diff --git a/TMessagesProj/build.gradle b/TMessagesProj/build.gradle index 7aa13b57d..446dfeae5 100644 --- a/TMessagesProj/build.gradle +++ b/TMessagesProj/build.gradle @@ -13,11 +13,10 @@ repositories { } dependencies { - compile 'com.android.support:support-v4:22.0.+' + compile 'com.android.support:support-v4:22.1.+' compile 'com.google.android.gms:play-services:3.2.+' compile 'net.hockeyapp.android:HockeySDK:3.5.+' compile 'com.googlecode.mp4parser:isoparser:1.0.+' - compile 'com.android.support:recyclerview-v7:+' } android { @@ -82,7 +81,7 @@ android { defaultConfig { minSdkVersion 8 targetSdkVersion 22 - versionCode 492 - versionName "2.7.0" + versionCode 521 + versionName "2.8.1" } } diff --git a/TMessagesProj/jni/Android.mk b/TMessagesProj/jni/Android.mk index f9b84ba96..19183707d 100755 --- a/TMessagesProj/jni/Android.mk +++ b/TMessagesProj/jni/Android.mk @@ -104,7 +104,7 @@ include $(BUILD_STATIC_LIBRARY) include $(CLEAR_VARS) LOCAL_PRELINK_MODULE := false LOCAL_STATIC_LIBRARIES := webp sqlite -LOCAL_MODULE := tmessages.7 +LOCAL_MODULE := tmessages.8 LOCAL_CFLAGS := -w -std=gnu99 -O2 -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 diff --git a/TMessagesProj/jni/image.c b/TMessagesProj/jni/image.c index 85a859007..a3a0fb862 100644 --- a/TMessagesProj/jni/image.c +++ b/TMessagesProj/jni/image.c @@ -288,7 +288,7 @@ METHODDEF(void) my_error_exit(j_common_ptr cinfo) { longjmp(myerr->setjmp_buffer, 1); } -JNIEXPORT void Java_org_telegram_messenger_Utilities_blurBitmap(JNIEnv *env, jclass class, jobject bitmap, int radius) { +JNIEXPORT void Java_org_telegram_messenger_Utilities_blurBitmap(JNIEnv *env, jclass class, jobject bitmap, int radius, int unpin) { if (!bitmap) { return; } @@ -312,7 +312,9 @@ JNIEXPORT void Java_org_telegram_messenger_Utilities_blurBitmap(JNIEnv *env, jcl } else { fastBlurMore(info.width, info.height, info.stride, pixels, radius); } - AndroidBitmap_unlockPixels(env, bitmap); + if (unpin) { + AndroidBitmap_unlockPixels(env, bitmap); + } } JNIEXPORT void Java_org_telegram_messenger_Utilities_calcCDT(JNIEnv *env, jclass class, jobject hsvBuffer, int width, int height, jobject buffer) { diff --git a/TMessagesProj/libs/armeabi-v7a/libtmessages.7.so b/TMessagesProj/libs/armeabi-v7a/libtmessages.8.so similarity index 92% rename from TMessagesProj/libs/armeabi-v7a/libtmessages.7.so rename to TMessagesProj/libs/armeabi-v7a/libtmessages.8.so index c086f4a98..29c750d68 100755 Binary files a/TMessagesProj/libs/armeabi-v7a/libtmessages.7.so and b/TMessagesProj/libs/armeabi-v7a/libtmessages.8.so differ diff --git a/TMessagesProj/libs/armeabi/libtmessages.7.so b/TMessagesProj/libs/armeabi/libtmessages.8.so similarity index 86% rename from TMessagesProj/libs/armeabi/libtmessages.7.so rename to TMessagesProj/libs/armeabi/libtmessages.8.so index 42ad265ae..ae7110a48 100755 Binary files a/TMessagesProj/libs/armeabi/libtmessages.7.so and b/TMessagesProj/libs/armeabi/libtmessages.8.so differ diff --git a/TMessagesProj/libs/x86/libtmessages.7.so b/TMessagesProj/libs/x86/libtmessages.8.so similarity index 99% rename from TMessagesProj/libs/x86/libtmessages.7.so rename to TMessagesProj/libs/x86/libtmessages.8.so index aaa768eac..2cf7dc77c 100755 Binary files a/TMessagesProj/libs/x86/libtmessages.7.so and b/TMessagesProj/libs/x86/libtmessages.8.so differ diff --git a/TMessagesProj/src/main/assets/emoji/emoji2.0x_0.jpg b/TMessagesProj/src/main/assets/emoji/emoji2.0x_0.jpg deleted file mode 100644 index 1cf58511e..000000000 Binary files a/TMessagesProj/src/main/assets/emoji/emoji2.0x_0.jpg and /dev/null differ diff --git a/TMessagesProj/src/main/assets/emoji/emoji2.0x_1.jpg b/TMessagesProj/src/main/assets/emoji/emoji2.0x_1.jpg deleted file mode 100644 index f5a3bd8a3..000000000 Binary files a/TMessagesProj/src/main/assets/emoji/emoji2.0x_1.jpg and /dev/null differ diff --git a/TMessagesProj/src/main/assets/emoji/emoji2.0x_2.jpg b/TMessagesProj/src/main/assets/emoji/emoji2.0x_2.jpg deleted file mode 100644 index d0db3e171..000000000 Binary files a/TMessagesProj/src/main/assets/emoji/emoji2.0x_2.jpg and /dev/null differ diff --git a/TMessagesProj/src/main/assets/emoji/emoji2.0x_3.jpg b/TMessagesProj/src/main/assets/emoji/emoji2.0x_3.jpg deleted file mode 100644 index 225f2b8c5..000000000 Binary files a/TMessagesProj/src/main/assets/emoji/emoji2.0x_3.jpg and /dev/null differ diff --git a/TMessagesProj/src/main/assets/emoji/emoji2.0x_4.jpg b/TMessagesProj/src/main/assets/emoji/emoji2.0x_4.jpg deleted file mode 100644 index 453875c0d..000000000 Binary files a/TMessagesProj/src/main/assets/emoji/emoji2.0x_4.jpg and /dev/null differ diff --git a/TMessagesProj/src/main/assets/emoji/emoji2.0x_a_0.jpg b/TMessagesProj/src/main/assets/emoji/emoji2.0x_a_0.jpg deleted file mode 100644 index 53416f240..000000000 Binary files a/TMessagesProj/src/main/assets/emoji/emoji2.0x_a_0.jpg and /dev/null differ diff --git a/TMessagesProj/src/main/assets/emoji/emoji2.0x_a_1.jpg b/TMessagesProj/src/main/assets/emoji/emoji2.0x_a_1.jpg deleted file mode 100644 index 8b7a562ea..000000000 Binary files a/TMessagesProj/src/main/assets/emoji/emoji2.0x_a_1.jpg and /dev/null differ diff --git a/TMessagesProj/src/main/assets/emoji/emoji2.0x_a_2.jpg b/TMessagesProj/src/main/assets/emoji/emoji2.0x_a_2.jpg deleted file mode 100644 index 48424203e..000000000 Binary files a/TMessagesProj/src/main/assets/emoji/emoji2.0x_a_2.jpg and /dev/null differ diff --git a/TMessagesProj/src/main/assets/emoji/emoji2.0x_a_3.jpg b/TMessagesProj/src/main/assets/emoji/emoji2.0x_a_3.jpg deleted file mode 100644 index 98cef882c..000000000 Binary files a/TMessagesProj/src/main/assets/emoji/emoji2.0x_a_3.jpg and /dev/null differ diff --git a/TMessagesProj/src/main/assets/emoji/emoji2.0x_a_4.jpg b/TMessagesProj/src/main/assets/emoji/emoji2.0x_a_4.jpg deleted file mode 100644 index 0f8980a81..000000000 Binary files a/TMessagesProj/src/main/assets/emoji/emoji2.0x_a_4.jpg and /dev/null differ diff --git a/TMessagesProj/src/main/assets/emoji/emoji3.0x_0.jpg b/TMessagesProj/src/main/assets/emoji/emoji3.0x_0.jpg deleted file mode 100644 index e09c30c59..000000000 Binary files a/TMessagesProj/src/main/assets/emoji/emoji3.0x_0.jpg and /dev/null differ diff --git a/TMessagesProj/src/main/assets/emoji/emoji3.0x_1.jpg b/TMessagesProj/src/main/assets/emoji/emoji3.0x_1.jpg deleted file mode 100644 index e0b0752ed..000000000 Binary files a/TMessagesProj/src/main/assets/emoji/emoji3.0x_1.jpg and /dev/null differ diff --git a/TMessagesProj/src/main/assets/emoji/emoji3.0x_2.jpg b/TMessagesProj/src/main/assets/emoji/emoji3.0x_2.jpg deleted file mode 100644 index ffd3c8480..000000000 Binary files a/TMessagesProj/src/main/assets/emoji/emoji3.0x_2.jpg and /dev/null differ diff --git a/TMessagesProj/src/main/assets/emoji/emoji3.0x_3.jpg b/TMessagesProj/src/main/assets/emoji/emoji3.0x_3.jpg deleted file mode 100644 index dd229d2a5..000000000 Binary files a/TMessagesProj/src/main/assets/emoji/emoji3.0x_3.jpg and /dev/null differ diff --git a/TMessagesProj/src/main/assets/emoji/emoji3.0x_4.jpg b/TMessagesProj/src/main/assets/emoji/emoji3.0x_4.jpg deleted file mode 100644 index 9127912ff..000000000 Binary files a/TMessagesProj/src/main/assets/emoji/emoji3.0x_4.jpg and /dev/null differ diff --git a/TMessagesProj/src/main/assets/emoji/emoji3.0x_a_0.jpg b/TMessagesProj/src/main/assets/emoji/emoji3.0x_a_0.jpg deleted file mode 100644 index e0e9339ec..000000000 Binary files a/TMessagesProj/src/main/assets/emoji/emoji3.0x_a_0.jpg and /dev/null differ diff --git a/TMessagesProj/src/main/assets/emoji/emoji3.0x_a_1.jpg b/TMessagesProj/src/main/assets/emoji/emoji3.0x_a_1.jpg deleted file mode 100644 index f5e831aec..000000000 Binary files a/TMessagesProj/src/main/assets/emoji/emoji3.0x_a_1.jpg and /dev/null differ diff --git a/TMessagesProj/src/main/assets/emoji/emoji3.0x_a_2.jpg b/TMessagesProj/src/main/assets/emoji/emoji3.0x_a_2.jpg deleted file mode 100644 index d04659d6a..000000000 Binary files a/TMessagesProj/src/main/assets/emoji/emoji3.0x_a_2.jpg and /dev/null differ diff --git a/TMessagesProj/src/main/assets/emoji/emoji3.0x_a_3.jpg b/TMessagesProj/src/main/assets/emoji/emoji3.0x_a_3.jpg deleted file mode 100644 index 1a3372391..000000000 Binary files a/TMessagesProj/src/main/assets/emoji/emoji3.0x_a_3.jpg and /dev/null differ diff --git a/TMessagesProj/src/main/assets/emoji/emoji3.0x_a_4.jpg b/TMessagesProj/src/main/assets/emoji/emoji3.0x_a_4.jpg deleted file mode 100644 index 7ce611f72..000000000 Binary files a/TMessagesProj/src/main/assets/emoji/emoji3.0x_a_4.jpg and /dev/null differ diff --git a/TMessagesProj/src/main/assets/emoji/v4_emoji2.0x_0.jpg b/TMessagesProj/src/main/assets/emoji/v4_emoji2.0x_0.jpg new file mode 100644 index 000000000..251747ff3 Binary files /dev/null and b/TMessagesProj/src/main/assets/emoji/v4_emoji2.0x_0.jpg differ diff --git a/TMessagesProj/src/main/assets/emoji/v4_emoji2.0x_1.jpg b/TMessagesProj/src/main/assets/emoji/v4_emoji2.0x_1.jpg new file mode 100644 index 000000000..21bc015ea Binary files /dev/null and b/TMessagesProj/src/main/assets/emoji/v4_emoji2.0x_1.jpg differ diff --git a/TMessagesProj/src/main/assets/emoji/v4_emoji2.0x_2.jpg b/TMessagesProj/src/main/assets/emoji/v4_emoji2.0x_2.jpg new file mode 100644 index 000000000..c1c6fd3b7 Binary files /dev/null and b/TMessagesProj/src/main/assets/emoji/v4_emoji2.0x_2.jpg differ diff --git a/TMessagesProj/src/main/assets/emoji/v4_emoji2.0x_3.jpg b/TMessagesProj/src/main/assets/emoji/v4_emoji2.0x_3.jpg new file mode 100644 index 000000000..bb398488c Binary files /dev/null and b/TMessagesProj/src/main/assets/emoji/v4_emoji2.0x_3.jpg differ diff --git a/TMessagesProj/src/main/assets/emoji/v4_emoji2.0x_4.jpg b/TMessagesProj/src/main/assets/emoji/v4_emoji2.0x_4.jpg new file mode 100644 index 000000000..a74e170f9 Binary files /dev/null and b/TMessagesProj/src/main/assets/emoji/v4_emoji2.0x_4.jpg differ diff --git a/TMessagesProj/src/main/assets/emoji/v4_emoji2.0x_a_0.jpg b/TMessagesProj/src/main/assets/emoji/v4_emoji2.0x_a_0.jpg new file mode 100644 index 000000000..2fe760d04 Binary files /dev/null and b/TMessagesProj/src/main/assets/emoji/v4_emoji2.0x_a_0.jpg differ diff --git a/TMessagesProj/src/main/assets/emoji/v4_emoji2.0x_a_1.jpg b/TMessagesProj/src/main/assets/emoji/v4_emoji2.0x_a_1.jpg new file mode 100644 index 000000000..776bf5887 Binary files /dev/null and b/TMessagesProj/src/main/assets/emoji/v4_emoji2.0x_a_1.jpg differ diff --git a/TMessagesProj/src/main/assets/emoji/v4_emoji2.0x_a_2.jpg b/TMessagesProj/src/main/assets/emoji/v4_emoji2.0x_a_2.jpg new file mode 100644 index 000000000..43a7aa378 Binary files /dev/null and b/TMessagesProj/src/main/assets/emoji/v4_emoji2.0x_a_2.jpg differ diff --git a/TMessagesProj/src/main/assets/emoji/v4_emoji2.0x_a_3.jpg b/TMessagesProj/src/main/assets/emoji/v4_emoji2.0x_a_3.jpg new file mode 100644 index 000000000..95d146fea Binary files /dev/null and b/TMessagesProj/src/main/assets/emoji/v4_emoji2.0x_a_3.jpg differ diff --git a/TMessagesProj/src/main/assets/emoji/v4_emoji2.0x_a_4.jpg b/TMessagesProj/src/main/assets/emoji/v4_emoji2.0x_a_4.jpg new file mode 100644 index 000000000..2be40c476 Binary files /dev/null and b/TMessagesProj/src/main/assets/emoji/v4_emoji2.0x_a_4.jpg differ diff --git a/TMessagesProj/src/main/assets/emoji/v4_emoji3.0x_0.jpg b/TMessagesProj/src/main/assets/emoji/v4_emoji3.0x_0.jpg new file mode 100644 index 000000000..bc1d03b45 Binary files /dev/null and b/TMessagesProj/src/main/assets/emoji/v4_emoji3.0x_0.jpg differ diff --git a/TMessagesProj/src/main/assets/emoji/v4_emoji3.0x_1.jpg b/TMessagesProj/src/main/assets/emoji/v4_emoji3.0x_1.jpg new file mode 100644 index 000000000..8288773c4 Binary files /dev/null and b/TMessagesProj/src/main/assets/emoji/v4_emoji3.0x_1.jpg differ diff --git a/TMessagesProj/src/main/assets/emoji/v4_emoji3.0x_2.jpg b/TMessagesProj/src/main/assets/emoji/v4_emoji3.0x_2.jpg new file mode 100644 index 000000000..feea12096 Binary files /dev/null and b/TMessagesProj/src/main/assets/emoji/v4_emoji3.0x_2.jpg differ diff --git a/TMessagesProj/src/main/assets/emoji/v4_emoji3.0x_3.jpg b/TMessagesProj/src/main/assets/emoji/v4_emoji3.0x_3.jpg new file mode 100644 index 000000000..0e3bcede0 Binary files /dev/null and b/TMessagesProj/src/main/assets/emoji/v4_emoji3.0x_3.jpg differ diff --git a/TMessagesProj/src/main/assets/emoji/v4_emoji3.0x_4.jpg b/TMessagesProj/src/main/assets/emoji/v4_emoji3.0x_4.jpg new file mode 100644 index 000000000..1f74e8359 Binary files /dev/null and b/TMessagesProj/src/main/assets/emoji/v4_emoji3.0x_4.jpg differ diff --git a/TMessagesProj/src/main/assets/emoji/v4_emoji3.0x_a_0.jpg b/TMessagesProj/src/main/assets/emoji/v4_emoji3.0x_a_0.jpg new file mode 100644 index 000000000..5218c3ca4 Binary files /dev/null and b/TMessagesProj/src/main/assets/emoji/v4_emoji3.0x_a_0.jpg differ diff --git a/TMessagesProj/src/main/assets/emoji/v4_emoji3.0x_a_1.jpg b/TMessagesProj/src/main/assets/emoji/v4_emoji3.0x_a_1.jpg new file mode 100644 index 000000000..e6192101a Binary files /dev/null and b/TMessagesProj/src/main/assets/emoji/v4_emoji3.0x_a_1.jpg differ diff --git a/TMessagesProj/src/main/assets/emoji/v4_emoji3.0x_a_2.jpg b/TMessagesProj/src/main/assets/emoji/v4_emoji3.0x_a_2.jpg new file mode 100644 index 000000000..58476054b Binary files /dev/null and b/TMessagesProj/src/main/assets/emoji/v4_emoji3.0x_a_2.jpg differ diff --git a/TMessagesProj/src/main/assets/emoji/v4_emoji3.0x_a_3.jpg b/TMessagesProj/src/main/assets/emoji/v4_emoji3.0x_a_3.jpg new file mode 100644 index 000000000..7e4ea184c Binary files /dev/null and b/TMessagesProj/src/main/assets/emoji/v4_emoji3.0x_a_3.jpg differ diff --git a/TMessagesProj/src/main/assets/emoji/v4_emoji3.0x_a_4.jpg b/TMessagesProj/src/main/assets/emoji/v4_emoji3.0x_a_4.jpg new file mode 100644 index 000000000..9bb3ea6cd Binary files /dev/null and b/TMessagesProj/src/main/assets/emoji/v4_emoji3.0x_a_4.jpg differ diff --git a/TMessagesProj/src/main/java/org/telegram/android/AndroidUtilities.java b/TMessagesProj/src/main/java/org/telegram/android/AndroidUtilities.java index b14c401b2..1e8111f08 100644 --- a/TMessagesProj/src/main/java/org/telegram/android/AndroidUtilities.java +++ b/TMessagesProj/src/main/java/org/telegram/android/AndroidUtilities.java @@ -45,10 +45,10 @@ import org.telegram.messenger.R; import org.telegram.messenger.TLRPC; import org.telegram.messenger.ApplicationLoader; import org.telegram.messenger.UserConfig; -import org.telegram.ui.AnimationCompat.AnimatorListenerAdapterProxy; -import org.telegram.ui.AnimationCompat.AnimatorSetProxy; -import org.telegram.ui.AnimationCompat.ObjectAnimatorProxy; -import org.telegram.ui.AnimationCompat.ViewProxy; +import org.telegram.android.AnimationCompat.AnimatorListenerAdapterProxy; +import org.telegram.android.AnimationCompat.AnimatorSetProxy; +import org.telegram.android.AnimationCompat.ObjectAnimatorProxy; +import org.telegram.android.AnimationCompat.ViewProxy; import org.telegram.ui.Components.ForegroundDetector; import org.telegram.ui.Components.NumberPicker; import org.telegram.ui.Components.TypefaceSpan; @@ -72,6 +72,7 @@ public class AndroidUtilities { public static Integer photoSize = null; public static DisplayMetrics displayMetrics = new DisplayMetrics(); public static int leftBaseline; + public static boolean usingHardwareInput; private static Boolean isTablet = null; static { @@ -227,21 +228,38 @@ public class AndroidUtilities { } public static int dp(float value) { + if (value == 0) { + return 0; + } return (int)Math.ceil(density * value); } + public static int compare(int lhs, int rhs) { + if (lhs == rhs) { + return 0; + } else if (lhs > rhs) { + return 1; + } + return -1; + } + public static float dpf2(float value) { + if (value == 0) { + return 0; + } return density * value; } public static void checkDisplaySize() { try { - WindowManager manager = (WindowManager)ApplicationLoader.applicationContext.getSystemService(Context.WINDOW_SERVICE); + Configuration configuration = ApplicationLoader.applicationContext.getResources().getConfiguration(); + usingHardwareInput = configuration.keyboard != Configuration.KEYBOARD_NOKEYS && configuration.hardKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_NO; + WindowManager manager = (WindowManager) ApplicationLoader.applicationContext.getSystemService(Context.WINDOW_SERVICE); if (manager != null) { Display display = manager.getDefaultDisplay(); if (display != null) { display.getMetrics(displayMetrics); - if(android.os.Build.VERSION.SDK_INT < 13) { + if (android.os.Build.VERSION.SDK_INT < 13) { displaySize.set(display.getWidth(), display.getHeight()); } else { display.getSize(displaySize); @@ -252,6 +270,38 @@ public class AndroidUtilities { } catch (Exception e) { FileLog.e("tmessages", e); } + + /* + keyboardHidden + public static final int KEYBOARDHIDDEN_NO = 1 + Constant for keyboardHidden, value corresponding to the keysexposed resource qualifier. + + public static final int KEYBOARDHIDDEN_UNDEFINED = 0 + Constant for keyboardHidden: a value indicating that no value has been set. + + public static final int KEYBOARDHIDDEN_YES = 2 + Constant for keyboardHidden, value corresponding to the keyshidden resource qualifier. + + hardKeyboardHidden + public static final int HARDKEYBOARDHIDDEN_NO = 1 + Constant for hardKeyboardHidden, value corresponding to the physical keyboard being exposed. + + public static final int HARDKEYBOARDHIDDEN_UNDEFINED = 0 + Constant for hardKeyboardHidden: a value indicating that no value has been set. + + public static final int HARDKEYBOARDHIDDEN_YES = 2 + Constant for hardKeyboardHidden, value corresponding to the physical keyboard being hidden. + + keyboard + public static final int KEYBOARD_12KEY = 3 + Constant for keyboard, value corresponding to the 12key resource qualifier. + + public static final int KEYBOARD_NOKEYS = 1 + Constant for keyboard, value corresponding to the nokeys resource qualifier. + + public static final int KEYBOARD_QWERTY = 2 + Constant for keyboard, value corresponding to the qwerty resource qualifier. + */ } public static float getPixelsInCM(float cm, boolean isX) { @@ -562,6 +612,9 @@ public class AndroidUtilities { if (start != -1) { stringBuilder.replace(start, start + 3, ""); end = stringBuilder.indexOf(""); + if (end == -1) { + end = stringBuilder.indexOf(""); + } stringBuilder.replace(end, end + 4, ""); bolds.add(start); bolds.add(end); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Animation/Animator10.java b/TMessagesProj/src/main/java/org/telegram/android/Animation/Animator10.java similarity index 99% rename from TMessagesProj/src/main/java/org/telegram/ui/Animation/Animator10.java rename to TMessagesProj/src/main/java/org/telegram/android/Animation/Animator10.java index 31a6d756b..a4661919f 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Animation/Animator10.java +++ b/TMessagesProj/src/main/java/org/telegram/android/Animation/Animator10.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.telegram.ui.Animation; +package org.telegram.android.Animation; import android.view.animation.Interpolator; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Animation/AnimatorListenerAdapter10.java b/TMessagesProj/src/main/java/org/telegram/android/Animation/AnimatorListenerAdapter10.java similarity index 96% rename from TMessagesProj/src/main/java/org/telegram/ui/Animation/AnimatorListenerAdapter10.java rename to TMessagesProj/src/main/java/org/telegram/android/Animation/AnimatorListenerAdapter10.java index 42349cd13..70b3aec39 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Animation/AnimatorListenerAdapter10.java +++ b/TMessagesProj/src/main/java/org/telegram/android/Animation/AnimatorListenerAdapter10.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.telegram.ui.Animation; +package org.telegram.android.Animation; public abstract class AnimatorListenerAdapter10 implements Animator10.AnimatorListener, Animator10.AnimatorPauseListener { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Animation/AnimatorSet10.java b/TMessagesProj/src/main/java/org/telegram/android/Animation/AnimatorSet10.java similarity index 99% rename from TMessagesProj/src/main/java/org/telegram/ui/Animation/AnimatorSet10.java rename to TMessagesProj/src/main/java/org/telegram/android/Animation/AnimatorSet10.java index 274dd6041..d9f63ef4b 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Animation/AnimatorSet10.java +++ b/TMessagesProj/src/main/java/org/telegram/android/Animation/AnimatorSet10.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.telegram.ui.Animation; +package org.telegram.android.Animation; import android.view.animation.Interpolator; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Animation/FloatEvaluator.java b/TMessagesProj/src/main/java/org/telegram/android/Animation/FloatEvaluator.java similarity index 95% rename from TMessagesProj/src/main/java/org/telegram/ui/Animation/FloatEvaluator.java rename to TMessagesProj/src/main/java/org/telegram/android/Animation/FloatEvaluator.java index 2591ebf74..856f4acd4 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Animation/FloatEvaluator.java +++ b/TMessagesProj/src/main/java/org/telegram/android/Animation/FloatEvaluator.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.telegram.ui.Animation; +package org.telegram.android.Animation; public class FloatEvaluator implements TypeEvaluator { public Float evaluate(float fraction, Number startValue, Number endValue) { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Animation/FloatKeyframeSet.java b/TMessagesProj/src/main/java/org/telegram/android/Animation/FloatKeyframeSet.java similarity index 98% rename from TMessagesProj/src/main/java/org/telegram/ui/Animation/FloatKeyframeSet.java rename to TMessagesProj/src/main/java/org/telegram/android/Animation/FloatKeyframeSet.java index 52d0da5fe..691b44b31 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Animation/FloatKeyframeSet.java +++ b/TMessagesProj/src/main/java/org/telegram/android/Animation/FloatKeyframeSet.java @@ -14,11 +14,11 @@ * limitations under the License. */ -package org.telegram.ui.Animation; +package org.telegram.android.Animation; import android.view.animation.Interpolator; -import org.telegram.ui.Animation.Keyframe.FloatKeyframe; +import org.telegram.android.Animation.Keyframe.FloatKeyframe; import java.util.ArrayList; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Animation/FloatProperty10.java b/TMessagesProj/src/main/java/org/telegram/android/Animation/FloatProperty10.java similarity index 95% rename from TMessagesProj/src/main/java/org/telegram/ui/Animation/FloatProperty10.java rename to TMessagesProj/src/main/java/org/telegram/android/Animation/FloatProperty10.java index 1a05b3bce..c02fb0f41 100755 --- a/TMessagesProj/src/main/java/org/telegram/ui/Animation/FloatProperty10.java +++ b/TMessagesProj/src/main/java/org/telegram/android/Animation/FloatProperty10.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.telegram.ui.Animation; +package org.telegram.android.Animation; public abstract class FloatProperty10 extends Property { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Animation/IntEvaluator.java b/TMessagesProj/src/main/java/org/telegram/android/Animation/IntEvaluator.java similarity index 95% rename from TMessagesProj/src/main/java/org/telegram/ui/Animation/IntEvaluator.java rename to TMessagesProj/src/main/java/org/telegram/android/Animation/IntEvaluator.java index cd3a19ebe..3f77f57f8 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Animation/IntEvaluator.java +++ b/TMessagesProj/src/main/java/org/telegram/android/Animation/IntEvaluator.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.telegram.ui.Animation; +package org.telegram.android.Animation; public class IntEvaluator implements TypeEvaluator { public Integer evaluate(float fraction, Integer startValue, Integer endValue) { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Animation/IntKeyframeSet.java b/TMessagesProj/src/main/java/org/telegram/android/Animation/IntKeyframeSet.java similarity index 98% rename from TMessagesProj/src/main/java/org/telegram/ui/Animation/IntKeyframeSet.java rename to TMessagesProj/src/main/java/org/telegram/android/Animation/IntKeyframeSet.java index 9d58863c8..3135614fc 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Animation/IntKeyframeSet.java +++ b/TMessagesProj/src/main/java/org/telegram/android/Animation/IntKeyframeSet.java @@ -14,11 +14,11 @@ * limitations under the License. */ -package org.telegram.ui.Animation; +package org.telegram.android.Animation; import android.view.animation.Interpolator; -import org.telegram.ui.Animation.Keyframe.IntKeyframe; +import org.telegram.android.Animation.Keyframe.IntKeyframe; import java.util.ArrayList; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Animation/IntProperty.java b/TMessagesProj/src/main/java/org/telegram/android/Animation/IntProperty.java similarity index 95% rename from TMessagesProj/src/main/java/org/telegram/ui/Animation/IntProperty.java rename to TMessagesProj/src/main/java/org/telegram/android/Animation/IntProperty.java index 07e72511a..701b88209 100755 --- a/TMessagesProj/src/main/java/org/telegram/ui/Animation/IntProperty.java +++ b/TMessagesProj/src/main/java/org/telegram/android/Animation/IntProperty.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.telegram.ui.Animation; +package org.telegram.android.Animation; public abstract class IntProperty extends Property { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Animation/Keyframe.java b/TMessagesProj/src/main/java/org/telegram/android/Animation/Keyframe.java similarity index 99% rename from TMessagesProj/src/main/java/org/telegram/ui/Animation/Keyframe.java rename to TMessagesProj/src/main/java/org/telegram/android/Animation/Keyframe.java index cb71460cd..2e4027392 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Animation/Keyframe.java +++ b/TMessagesProj/src/main/java/org/telegram/android/Animation/Keyframe.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.telegram.ui.Animation; +package org.telegram.android.Animation; import android.view.animation.Interpolator; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Animation/KeyframeSet.java b/TMessagesProj/src/main/java/org/telegram/android/Animation/KeyframeSet.java similarity index 97% rename from TMessagesProj/src/main/java/org/telegram/ui/Animation/KeyframeSet.java rename to TMessagesProj/src/main/java/org/telegram/android/Animation/KeyframeSet.java index 98a34ff67..38739e211 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Animation/KeyframeSet.java +++ b/TMessagesProj/src/main/java/org/telegram/android/Animation/KeyframeSet.java @@ -14,16 +14,16 @@ * limitations under the License. */ -package org.telegram.ui.Animation; +package org.telegram.android.Animation; import java.util.ArrayList; import java.util.Arrays; import android.util.Log; import android.view.animation.Interpolator; -import org.telegram.ui.Animation.Keyframe.IntKeyframe; -import org.telegram.ui.Animation.Keyframe.FloatKeyframe; -import org.telegram.ui.Animation.Keyframe.ObjectKeyframe; +import org.telegram.android.Animation.Keyframe.IntKeyframe; +import org.telegram.android.Animation.Keyframe.FloatKeyframe; +import org.telegram.android.Animation.Keyframe.ObjectKeyframe; class KeyframeSet { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Animation/NoSuchPropertyException.java b/TMessagesProj/src/main/java/org/telegram/android/Animation/NoSuchPropertyException.java similarity index 95% rename from TMessagesProj/src/main/java/org/telegram/ui/Animation/NoSuchPropertyException.java rename to TMessagesProj/src/main/java/org/telegram/android/Animation/NoSuchPropertyException.java index a3c59a4f4..bc00a4052 100755 --- a/TMessagesProj/src/main/java/org/telegram/ui/Animation/NoSuchPropertyException.java +++ b/TMessagesProj/src/main/java/org/telegram/android/Animation/NoSuchPropertyException.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.telegram.ui.Animation; +package org.telegram.android.Animation; public class NoSuchPropertyException extends RuntimeException { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Animation/ObjectAnimator10.java b/TMessagesProj/src/main/java/org/telegram/android/Animation/ObjectAnimator10.java similarity index 99% rename from TMessagesProj/src/main/java/org/telegram/ui/Animation/ObjectAnimator10.java rename to TMessagesProj/src/main/java/org/telegram/android/Animation/ObjectAnimator10.java index 9f7ea7369..d29f201c2 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Animation/ObjectAnimator10.java +++ b/TMessagesProj/src/main/java/org/telegram/android/Animation/ObjectAnimator10.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.telegram.ui.Animation; +package org.telegram.android.Animation; import android.view.View; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Animation/Property.java b/TMessagesProj/src/main/java/org/telegram/android/Animation/Property.java similarity index 97% rename from TMessagesProj/src/main/java/org/telegram/ui/Animation/Property.java rename to TMessagesProj/src/main/java/org/telegram/android/Animation/Property.java index 96beb2109..cd5248bb6 100755 --- a/TMessagesProj/src/main/java/org/telegram/ui/Animation/Property.java +++ b/TMessagesProj/src/main/java/org/telegram/android/Animation/Property.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.telegram.ui.Animation; +package org.telegram.android.Animation; public abstract class Property { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Animation/PropertyValuesHolder.java b/TMessagesProj/src/main/java/org/telegram/android/Animation/PropertyValuesHolder.java similarity index 99% rename from TMessagesProj/src/main/java/org/telegram/ui/Animation/PropertyValuesHolder.java rename to TMessagesProj/src/main/java/org/telegram/android/Animation/PropertyValuesHolder.java index 0420fb7b1..25dfa8385 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Animation/PropertyValuesHolder.java +++ b/TMessagesProj/src/main/java/org/telegram/android/Animation/PropertyValuesHolder.java @@ -14,9 +14,8 @@ * limitations under the License. */ -package org.telegram.ui.Animation; +package org.telegram.android.Animation; -import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.HashMap; import java.util.concurrent.locks.ReentrantReadWriteLock; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Animation/ReflectiveProperty.java b/TMessagesProj/src/main/java/org/telegram/android/Animation/ReflectiveProperty.java similarity index 99% rename from TMessagesProj/src/main/java/org/telegram/ui/Animation/ReflectiveProperty.java rename to TMessagesProj/src/main/java/org/telegram/android/Animation/ReflectiveProperty.java index ce487b4ff..7b4189822 100755 --- a/TMessagesProj/src/main/java/org/telegram/ui/Animation/ReflectiveProperty.java +++ b/TMessagesProj/src/main/java/org/telegram/android/Animation/ReflectiveProperty.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.telegram.ui.Animation; +package org.telegram.android.Animation; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Animation/TypeEvaluator.java b/TMessagesProj/src/main/java/org/telegram/android/Animation/TypeEvaluator.java similarity index 94% rename from TMessagesProj/src/main/java/org/telegram/ui/Animation/TypeEvaluator.java rename to TMessagesProj/src/main/java/org/telegram/android/Animation/TypeEvaluator.java index db5769e0c..d70e28400 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Animation/TypeEvaluator.java +++ b/TMessagesProj/src/main/java/org/telegram/android/Animation/TypeEvaluator.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.telegram.ui.Animation; +package org.telegram.android.Animation; public interface TypeEvaluator { T evaluate(float fraction, T startValue, T endValue); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Animation/ValueAnimator.java b/TMessagesProj/src/main/java/org/telegram/android/Animation/ValueAnimator.java similarity index 99% rename from TMessagesProj/src/main/java/org/telegram/ui/Animation/ValueAnimator.java rename to TMessagesProj/src/main/java/org/telegram/android/Animation/ValueAnimator.java index 168ff1dac..3b0c29130 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Animation/ValueAnimator.java +++ b/TMessagesProj/src/main/java/org/telegram/android/Animation/ValueAnimator.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.telegram.ui.Animation; +package org.telegram.android.Animation; import android.os.Looper; import android.util.AndroidRuntimeException; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Animation/View10.java b/TMessagesProj/src/main/java/org/telegram/android/Animation/View10.java similarity index 99% rename from TMessagesProj/src/main/java/org/telegram/ui/Animation/View10.java rename to TMessagesProj/src/main/java/org/telegram/android/Animation/View10.java index 4a89fe9c5..2cd59cb54 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Animation/View10.java +++ b/TMessagesProj/src/main/java/org/telegram/android/Animation/View10.java @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package org.telegram.ui.Animation; +package org.telegram.android.Animation; import android.graphics.Camera; import android.graphics.Matrix; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/AnimationCompat/AnimatorListenerAdapterProxy.java b/TMessagesProj/src/main/java/org/telegram/android/AnimationCompat/AnimatorListenerAdapterProxy.java similarity index 94% rename from TMessagesProj/src/main/java/org/telegram/ui/AnimationCompat/AnimatorListenerAdapterProxy.java rename to TMessagesProj/src/main/java/org/telegram/android/AnimationCompat/AnimatorListenerAdapterProxy.java index 2c416a110..4825db9ea 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/AnimationCompat/AnimatorListenerAdapterProxy.java +++ b/TMessagesProj/src/main/java/org/telegram/android/AnimationCompat/AnimatorListenerAdapterProxy.java @@ -6,14 +6,14 @@ * Copyright Nikolai Kudashov, 2013-2014. */ -package org.telegram.ui.AnimationCompat; +package org.telegram.android.AnimationCompat; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; -import org.telegram.ui.Animation.Animator10; -import org.telegram.ui.Animation.AnimatorListenerAdapter10; -import org.telegram.ui.Animation.View10; +import org.telegram.android.Animation.Animator10; +import org.telegram.android.Animation.AnimatorListenerAdapter10; +import org.telegram.android.Animation.View10; public class AnimatorListenerAdapterProxy { protected Object animatorListenerAdapter; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/AnimationCompat/AnimatorSetProxy.java b/TMessagesProj/src/main/java/org/telegram/android/AnimationCompat/AnimatorSetProxy.java similarity index 93% rename from TMessagesProj/src/main/java/org/telegram/ui/AnimationCompat/AnimatorSetProxy.java rename to TMessagesProj/src/main/java/org/telegram/android/AnimationCompat/AnimatorSetProxy.java index 50ac0c1d1..18c6b12da 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/AnimationCompat/AnimatorSetProxy.java +++ b/TMessagesProj/src/main/java/org/telegram/android/AnimationCompat/AnimatorSetProxy.java @@ -6,17 +6,17 @@ * Copyright Nikolai Kudashov, 2013-2014. */ -package org.telegram.ui.AnimationCompat; +package org.telegram.android.AnimationCompat; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; import android.view.animation.Interpolator; -import org.telegram.ui.Animation.Animator10; -import org.telegram.ui.Animation.AnimatorListenerAdapter10; -import org.telegram.ui.Animation.AnimatorSet10; -import org.telegram.ui.Animation.View10; +import org.telegram.android.Animation.Animator10; +import org.telegram.android.Animation.AnimatorListenerAdapter10; +import org.telegram.android.Animation.AnimatorSet10; +import org.telegram.android.Animation.View10; import java.lang.reflect.Array; import java.util.ArrayList; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/AnimationCompat/ObjectAnimatorProxy.java b/TMessagesProj/src/main/java/org/telegram/android/AnimationCompat/ObjectAnimatorProxy.java similarity index 94% rename from TMessagesProj/src/main/java/org/telegram/ui/AnimationCompat/ObjectAnimatorProxy.java rename to TMessagesProj/src/main/java/org/telegram/android/AnimationCompat/ObjectAnimatorProxy.java index c9fd8cf18..3210bbff9 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/AnimationCompat/ObjectAnimatorProxy.java +++ b/TMessagesProj/src/main/java/org/telegram/android/AnimationCompat/ObjectAnimatorProxy.java @@ -6,15 +6,15 @@ * Copyright Nikolai Kudashov, 2013-2014. */ -package org.telegram.ui.AnimationCompat; +package org.telegram.android.AnimationCompat; import android.animation.AnimatorListenerAdapter; import android.animation.ObjectAnimator; import android.view.animation.Interpolator; -import org.telegram.ui.Animation.AnimatorListenerAdapter10; -import org.telegram.ui.Animation.ObjectAnimator10; -import org.telegram.ui.Animation.View10; +import org.telegram.android.Animation.AnimatorListenerAdapter10; +import org.telegram.android.Animation.ObjectAnimator10; +import org.telegram.android.Animation.View10; public class ObjectAnimatorProxy { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/AnimationCompat/ViewProxy.java b/TMessagesProj/src/main/java/org/telegram/android/AnimationCompat/ViewProxy.java similarity index 98% rename from TMessagesProj/src/main/java/org/telegram/ui/AnimationCompat/ViewProxy.java rename to TMessagesProj/src/main/java/org/telegram/android/AnimationCompat/ViewProxy.java index 478398bee..e8dcf6ce8 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/AnimationCompat/ViewProxy.java +++ b/TMessagesProj/src/main/java/org/telegram/android/AnimationCompat/ViewProxy.java @@ -6,11 +6,11 @@ * Copyright Nikolai Kudashov, 2013-2014. */ -package org.telegram.ui.AnimationCompat; +package org.telegram.android.AnimationCompat; import android.view.View; -import org.telegram.ui.Animation.View10; +import org.telegram.android.Animation.View10; public class ViewProxy { diff --git a/TMessagesProj/src/main/java/org/telegram/android/ContactsController.java b/TMessagesProj/src/main/java/org/telegram/android/ContactsController.java index 84ac04327..da56c872d 100644 --- a/TMessagesProj/src/main/java/org/telegram/android/ContactsController.java +++ b/TMessagesProj/src/main/java/org/telegram/android/ContactsController.java @@ -14,6 +14,7 @@ import android.app.Activity; import android.content.ContentProviderOperation; import android.content.ContentProviderResult; import android.content.ContentResolver; +import android.content.ContentValues; import android.content.SharedPreferences; import android.database.Cursor; import android.net.Uri; @@ -37,7 +38,6 @@ import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; -import java.util.Locale; import java.util.concurrent.ConcurrentHashMap; public class ContactsController { @@ -168,7 +168,7 @@ public class ContactsController { if (!updatingInviteText && (inviteText == null || time + 86400 < (int)(System.currentTimeMillis() / 1000))) { updatingInviteText = true; TLRPC.TL_help_getInviteText req = new TLRPC.TL_help_getInviteText(); - req.lang_code = LocaleController.getLocaleString(Locale.getDefault()); + req.lang_code = LocaleController.getLocaleString(LocaleController.getInstance().getSystemDefaultLocale()); if (req.lang_code == null || req.lang_code.length() == 0) { req.lang_code = "en"; } @@ -202,7 +202,19 @@ public class ContactsController { public void checkAppAccount() { AccountManager am = AccountManager.get(ApplicationLoader.applicationContext); - Account[] accounts = am.getAccountsByType("org.telegram.account"); + Account[] accounts; + try { + accounts = am.getAccountsByType("org.telegram.account"); + if (accounts != null && accounts.length > 0) { + for (Account c : accounts) { + am.removeAccount(c, null, null); + } + } + } catch (Exception e) { + FileLog.e("tmessages", e); + } + + accounts = am.getAccountsByType("org.telegram.messenger"); boolean recreateAccount = false; if (UserConfig.isClientActivated()) { if (accounts.length == 1) { @@ -227,7 +239,7 @@ public class ContactsController { } if (UserConfig.isClientActivated()) { try { - currentAccount = new Account(UserConfig.getCurrentUser().phone, "org.telegram.account"); + currentAccount = new Account(UserConfig.getCurrentUser().phone, "org.telegram.messenger"); am.addAccountExplicitly(currentAccount, "", null); } catch (Exception e) { FileLog.e("tmessages", e); @@ -239,7 +251,7 @@ public class ContactsController { public void deleteAllAppAccounts() { try { AccountManager am = AccountManager.get(ApplicationLoader.applicationContext); - Account[] accounts = am.getAccountsByType("org.telegram.account"); + Account[] accounts = am.getAccountsByType("org.telegram.messenger"); for (Account c : accounts) { am.removeAccount(c, null, null); } @@ -1246,7 +1258,7 @@ public class ContactsController { private void performWriteContactsToPhoneBook() { final ArrayList contactsArray = new ArrayList<>(); contactsArray.addAll(contacts); - Utilities.photoBookQueue.postRunnable(new Runnable() { + Utilities.phoneBookQueue.postRunnable(new Runnable() { @Override public void run() { performWriteContactsToPhoneBookInternal(contactsArray); @@ -1303,7 +1315,7 @@ public class ContactsController { } for (final Integer uid : contactsTD) { - Utilities.photoBookQueue.postRunnable(new Runnable() { + Utilities.phoneBookQueue.postRunnable(new Runnable() { @Override public void run() { deleteContactFromPhoneBook(uid); @@ -1463,7 +1475,7 @@ public class ContactsController { builder = ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI); builder.withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0); builder.withValue(ContactsContract.Data.MIMETYPE, "vnd.android.cursor.item/vnd.org.telegram.messenger.android.profile"); - builder.withValue(ContactsContract.Data.DATA1, "+" + user.phone); + builder.withValue(ContactsContract.Data.DATA1, user.id); builder.withValue(ContactsContract.Data.DATA2, "Telegram Profile"); builder.withValue(ContactsContract.Data.DATA3, "+" + user.phone); builder.withValue(ContactsContract.Data.DATA4, user.id); @@ -1496,6 +1508,22 @@ public class ContactsController { } } + protected void markAsContacted(final String contactId) { + if (contactId == null) { + return; + } + Utilities.phoneBookQueue.postRunnable(new Runnable() { + @Override + public void run() { + Uri uri = Uri.parse(contactId); + ContentValues values = new ContentValues(); + values.put(ContactsContract.Contacts.LAST_TIME_CONTACTED, System.currentTimeMillis()); + ContentResolver cr = ApplicationLoader.applicationContext.getContentResolver(); + cr.update(uri, values, null, null); + } + }); + } + public void addContact(TLRPC.User user) { if (user == null || user.phone == null) { return; @@ -1533,7 +1561,7 @@ public class ContactsController { // } for (final TLRPC.User u : res.users) { - Utilities.photoBookQueue.postRunnable(new Runnable() { + Utilities.phoneBookQueue.postRunnable(new Runnable() { @Override public void run() { addContactToPhoneBook(u, true); @@ -1546,7 +1574,7 @@ public class ContactsController { MessagesStorage.getInstance().putContacts(arrayList, false); if (u.phone != null && u.phone.length() > 0) { - String name = formatName(u.first_name, u.last_name); + CharSequence name = formatName(u.first_name, u.last_name); MessagesStorage.getInstance().applyPhoneBookUpdates(u.phone, ""); Contact contact = contactsBookSPhones.get(u.phone); if (contact != null) { @@ -1599,7 +1627,7 @@ public class ContactsController { return; } MessagesStorage.getInstance().deleteContacts(uids); - Utilities.photoBookQueue.postRunnable(new Runnable() { + Utilities.phoneBookQueue.postRunnable(new Runnable() { @Override public void run() { for (TLRPC.User user : users) { @@ -1610,7 +1638,7 @@ public class ContactsController { for (TLRPC.User user : users) { if (user.phone != null && user.phone.length() > 0) { - String name = ContactsController.formatName(user.first_name, user.last_name); + CharSequence name = ContactsController.formatName(user.first_name, user.last_name); MessagesStorage.getInstance().applyPhoneBookUpdates(user.phone, ""); Contact contact = contactsBookSPhones.get(user.phone); if (contact != null) { @@ -1772,22 +1800,37 @@ public class ContactsController { } public static String formatName(String firstName, String lastName) { - String result = ""; + /*if ((firstName == null || firstName.length() == 0) && (lastName == null || lastName.length() == 0)) { + return LocaleController.getString("HiddenName", R.string.HiddenName); + }*/ + if (firstName != null) { + firstName = firstName.trim(); + } + if (lastName != null) { + lastName = lastName.trim(); + } + StringBuilder result = new StringBuilder((firstName != null ? firstName.length() : 0) + (lastName != null ? lastName.length() : 0) + 1); if (LocaleController.nameDisplayOrder == 1) { - result = firstName; - if (result == null || result.length() == 0) { - result = lastName; - } else if (result.length() != 0 && lastName != null && lastName.length() != 0) { - result += " " + lastName; + if (firstName != null && firstName.length() > 0) { + result.append(firstName); + if (lastName != null && lastName.length() > 0) { + result.append(" "); + result.append(lastName); + } + } else if (lastName != null && lastName.length() > 0) { + result.append(lastName); } } else { - result = lastName; - if (result == null || result.length() == 0) { - result = firstName; - } else if (result.length() != 0 && firstName != null && firstName.length() != 0) { - result += " " + firstName; + if (lastName != null && lastName.length() > 0) { + result.append(lastName); + if (firstName != null && firstName.length() > 0) { + result.append(" "); + result.append(firstName); + } + } else if (firstName != null && firstName.length() > 0) { + result.append(firstName); } } - return result.trim(); + return result.toString(); } } diff --git a/TMessagesProj/src/main/java/org/telegram/android/Emoji.java b/TMessagesProj/src/main/java/org/telegram/android/Emoji.java index 08eb51906..312c78488 100644 --- a/TMessagesProj/src/main/java/org/telegram/android/Emoji.java +++ b/TMessagesProj/src/main/java/org/telegram/android/Emoji.java @@ -33,7 +33,8 @@ import org.telegram.messenger.ApplicationLoader; public class Emoji { private static HashMap rects = new HashMap<>(); - private static int drawImgSize, bigImgSize; + private static int drawImgSize; + private static int bigImgSize; private static boolean inited = false; private static Paint placeholderPaint; private static Bitmap emojiBmp[] = new Bitmap[5]; @@ -193,19 +194,19 @@ public class Emoji { static { int emojiFullSize; if (AndroidUtilities.density <= 1.0f) { - emojiFullSize = 30; + emojiFullSize = 32; } else if (AndroidUtilities.density <= 1.5f) { - emojiFullSize = 45; + emojiFullSize = 48; } else if (AndroidUtilities.density <= 2.0f) { - emojiFullSize = 60; + emojiFullSize = 64; } else { - emojiFullSize = 90; + emojiFullSize = 96; } drawImgSize = AndroidUtilities.dp(20); if (AndroidUtilities.isTablet()) { bigImgSize = AndroidUtilities.dp(40); } else { - bigImgSize = AndroidUtilities.dp(30); + bigImgSize = AndroidUtilities.dp(32); } for (int j = 1; j < data.length; j++) { @@ -234,8 +235,26 @@ public class Emoji { scale = 3.0f; } - String imageName = String.format(Locale.US, "emoji%.01fx_%d.jpg", scale, page); - File imageFile = ApplicationLoader.applicationContext.getFileStreamPath(imageName); + String imageName; + File imageFile; + + try { + imageName = String.format(Locale.US, "emoji%.01fx_%d.jpg", scale, page); + imageFile = ApplicationLoader.applicationContext.getFileStreamPath(imageName); + if (imageFile.exists()) { + imageFile.delete(); + } + imageName = String.format(Locale.US, "emoji%.01fx_a_%d.jpg", scale, page); + imageFile = ApplicationLoader.applicationContext.getFileStreamPath(imageName); + if (imageFile.exists()) { + imageFile.delete(); + } + } catch (Exception e) { + FileLog.e("tmessages", e); + } + + imageName = String.format(Locale.US, "v4_emoji%.01fx_%d.jpg", scale, page); + imageFile = ApplicationLoader.applicationContext.getFileStreamPath(imageName); if (!imageFile.exists()) { InputStream is = ApplicationLoader.applicationContext.getAssets().open("emoji/" + imageName); Utilities.copyFile(is, imageFile); @@ -253,7 +272,7 @@ public class Emoji { final Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); Utilities.loadBitmap(imageFile.getAbsolutePath(), bitmap, imageResize, width, height, stride); - imageName = String.format(Locale.US, "emoji%.01fx_a_%d.jpg", scale, page); + imageName = String.format(Locale.US, "v4_emoji%.01fx_a_%d.jpg", scale, page); imageFile = ApplicationLoader.applicationContext.getFileStreamPath(imageName); if (!imageFile.exists()) { InputStream is = ApplicationLoader.applicationContext.getAssets().open("emoji/" + imageName); diff --git a/TMessagesProj/src/main/java/org/telegram/android/ImageLoader.java b/TMessagesProj/src/main/java/org/telegram/android/ImageLoader.java index fb6363697..2cc360353 100644 --- a/TMessagesProj/src/main/java/org/telegram/android/ImageLoader.java +++ b/TMessagesProj/src/main/java/org/telegram/android/ImageLoader.java @@ -70,6 +70,8 @@ public class ImageLoader { private DispatchQueue recycleQueue = new DispatchQueue("recycleQueue"); private ConcurrentHashMap fileProgresses = new ConcurrentHashMap<>(); private HashMap thumbGenerateTasks = new HashMap<>(); + private static byte[] bytes; + private static byte[] bytesThumb; private int currentHttpTasksCount = 0; private LinkedList httpFileLoadTasks = new LinkedList<>(); @@ -507,6 +509,7 @@ public class ImageLoader { } Long mediaId = null; + boolean mediaIsVideo = false; Bitmap image = null; File cacheFileFinal = cacheImage.finalFilePath; boolean canDeleteFile = true; @@ -537,18 +540,35 @@ public class ImageLoader { } } - if (image == null) { - if (isWebp) { - RandomAccessFile file = new RandomAccessFile(cacheFileFinal, "r"); - ByteBuffer buffer = file.getChannel().map(FileChannel.MapMode.READ_ONLY, 0, cacheFileFinal.length()); - image = Utilities.loadWebpImage(buffer, buffer.limit(), null); - file.close(); + BitmapFactory.Options opts = new BitmapFactory.Options(); + opts.inSampleSize = 1; + + if (!isWebp && Build.VERSION.SDK_INT > 10 && Build.VERSION.SDK_INT < 21) { + opts.inPurgeable = true; + } + + if (isWebp) { + RandomAccessFile file = new RandomAccessFile(cacheFileFinal, "r"); + ByteBuffer buffer = file.getChannel().map(FileChannel.MapMode.READ_ONLY, 0, cacheFileFinal.length()); + image = Utilities.loadWebpImage(buffer, buffer.limit(), null); + file.close(); + } else { + if (opts.inPurgeable) { + RandomAccessFile f = new RandomAccessFile(cacheFileFinal, "r"); + int len = (int) f.length(); + byte[] data = bytesThumb != null && bytesThumb.length >= len ? bytesThumb : null; + if (data == null) { + bytesThumb = data = new byte[len]; + } + f.readFully(data, 0, len); + image = BitmapFactory.decodeByteArray(data, 0, len, opts); } else { FileInputStream is = new FileInputStream(cacheFileFinal); - image = BitmapFactory.decodeStream(is, null, null); + image = BitmapFactory.decodeStream(is, null, opts); is.close(); } } + if (image == null) { if (canDeleteFile && (cacheFileFinal.length() == 0 || cacheImage.filter == null)) { cacheFileFinal.delete(); @@ -556,15 +576,18 @@ public class ImageLoader { } else { if (image != null) { if (blurType == 1) { - Utilities.blurBitmap(image, 3); + Utilities.blurBitmap(image, 3, opts.inPurgeable ? 0 : 1); } else if (blurType == 2) { - Utilities.blurBitmap(image, 1); + Utilities.blurBitmap(image, 1, opts.inPurgeable ? 0 : 1); } else if (blurType == 3) { - Utilities.blurBitmap(image, 7); - Utilities.blurBitmap(image, 7); - Utilities.blurBitmap(image, 7); + Utilities.blurBitmap(image, 7, opts.inPurgeable ? 0 : 1); + Utilities.blurBitmap(image, 7, opts.inPurgeable ? 0 : 1); + Utilities.blurBitmap(image, 7, opts.inPurgeable ? 0 : 1); } } + if (blurType == 0 && opts.inPurgeable) { + Utilities.pinBitmap(image); + } if (runtimeHack != null) { runtimeHack.trackFree(image.getRowBytes() * image.getHeight()); } @@ -579,6 +602,14 @@ public class ImageLoader { int idx = cacheImage.httpUrl.indexOf(":", 8); if (idx >= 0) { mediaId = Long.parseLong(cacheImage.httpUrl.substring(8, idx)); + mediaIsVideo = false; + } + canDeleteFile = false; + } else if (cacheImage.httpUrl.startsWith("vthumb://")) { + int idx = cacheImage.httpUrl.indexOf(":", 9); + if (idx >= 0) { + mediaId = Long.parseLong(cacheImage.httpUrl.substring(9, idx)); + mediaIsVideo = true; } canDeleteFile = false; } else if (!cacheImage.httpUrl.startsWith("http")) { @@ -604,35 +635,44 @@ public class ImageLoader { } BitmapFactory.Options opts = new BitmapFactory.Options(); + opts.inSampleSize = 1; float w_filter = 0; float h_filter = 0; boolean blur = false; if (cacheImage.filter != null) { String args[] = cacheImage.filter.split("_"); - w_filter = Float.parseFloat(args[0]) * AndroidUtilities.density; - h_filter = Float.parseFloat(args[1]) * AndroidUtilities.density; - if (args.length > 2) { + if (args.length >= 2) { + w_filter = Float.parseFloat(args[0]) * AndroidUtilities.density; + h_filter = Float.parseFloat(args[1]) * AndroidUtilities.density; + } + if (cacheImage.filter.contains("b")) { blur = true; } - opts.inJustDecodeBounds = true; + if (w_filter != 0 && h_filter != 0) { + opts.inJustDecodeBounds = true; - if (mediaId != null) { - MediaStore.Images.Thumbnails.getThumbnail(ApplicationLoader.applicationContext.getContentResolver(), mediaId, MediaStore.Images.Thumbnails.MINI_KIND, opts); - } else { - FileInputStream is = new FileInputStream(cacheFileFinal); - image = BitmapFactory.decodeStream(is, null, opts); - is.close(); - } + if (mediaId != null) { + if (mediaIsVideo) { + MediaStore.Video.Thumbnails.getThumbnail(ApplicationLoader.applicationContext.getContentResolver(), mediaId, MediaStore.Video.Thumbnails.MINI_KIND, opts); + } else { + MediaStore.Images.Thumbnails.getThumbnail(ApplicationLoader.applicationContext.getContentResolver(), mediaId, MediaStore.Images.Thumbnails.MINI_KIND, opts); + } + } else { + FileInputStream is = new FileInputStream(cacheFileFinal); + image = BitmapFactory.decodeStream(is, null, opts); + is.close(); + } - float photoW = opts.outWidth; - float photoH = opts.outHeight; - float scaleFactor = Math.max(photoW / w_filter, photoH / h_filter); - if (scaleFactor < 1) { - scaleFactor = 1; + float photoW = opts.outWidth; + float photoH = opts.outHeight; + float scaleFactor = Math.max(photoW / w_filter, photoH / h_filter); + if (scaleFactor < 1) { + scaleFactor = 1; + } + opts.inJustDecodeBounds = false; + opts.inSampleSize = (int) scaleFactor; } - opts.inJustDecodeBounds = false; - opts.inSampleSize = (int)scaleFactor; } synchronized (sync) { if (isCancelled) { @@ -645,13 +685,17 @@ public class ImageLoader { } else { opts.inPreferredConfig = Bitmap.Config.RGB_565; } - //if (Build.VERSION.SDK_INT < 21) { - // opts.inPurgeable = true; - //} + if (!isWebp && Build.VERSION.SDK_INT > 10 && Build.VERSION.SDK_INT < 21) { + opts.inPurgeable = true; + } opts.inDither = false; if (mediaId != null) { - image = MediaStore.Images.Thumbnails.getThumbnail(ApplicationLoader.applicationContext.getContentResolver(), mediaId, MediaStore.Images.Thumbnails.MINI_KIND, opts); + if (mediaIsVideo) { + image = MediaStore.Video.Thumbnails.getThumbnail(ApplicationLoader.applicationContext.getContentResolver(), mediaId, MediaStore.Video.Thumbnails.MINI_KIND, opts); + } else { + image = MediaStore.Images.Thumbnails.getThumbnail(ApplicationLoader.applicationContext.getContentResolver(), mediaId, MediaStore.Images.Thumbnails.MINI_KIND, opts); + } } if (image == null) { if (isWebp) { @@ -660,9 +704,20 @@ public class ImageLoader { image = Utilities.loadWebpImage(buffer, buffer.limit(), null); file.close(); } else { - FileInputStream is = new FileInputStream(cacheFileFinal); - image = BitmapFactory.decodeStream(is, null, opts); - is.close(); + if (opts.inPurgeable) { + RandomAccessFile f = new RandomAccessFile(cacheFileFinal, "r"); + int len = (int) f.length(); + byte[] data = bytes != null && bytes.length >= len ? bytes : null; + if (data == null) { + bytes = data = new byte[len]; + } + f.readFully(data, 0, len); + image = BitmapFactory.decodeByteArray(data, 0, len, opts); + } else { + FileInputStream is = new FileInputStream(cacheFileFinal); + image = BitmapFactory.decodeStream(is, null, opts); + is.close(); + } } } if (image == null) { @@ -670,12 +725,13 @@ public class ImageLoader { cacheFileFinal.delete(); } } else { + boolean blured = false; if (cacheImage.filter != null) { float bitmapW = image.getWidth(); float bitmapH = image.getHeight(); - if (bitmapW != w_filter && bitmapW > w_filter) { + if (!opts.inPurgeable && w_filter != 0 && bitmapW != w_filter && bitmapW > w_filter + 20) { float scaleFactor = bitmapW / w_filter; - Bitmap scaledBitmap = Bitmap.createScaledBitmap(image, (int)w_filter, (int)(bitmapH / scaleFactor), true); + Bitmap scaledBitmap = Bitmap.createScaledBitmap(image, (int) w_filter, (int) (bitmapH / scaleFactor), true); if (image != scaledBitmap) { image.recycle(); callGC(); @@ -683,9 +739,13 @@ public class ImageLoader { } } if (image != null && blur && bitmapH < 100 && bitmapW < 100) { - Utilities.blurBitmap(image, 3); + Utilities.blurBitmap(image, 3, opts.inPurgeable ? 0 : 1); + blured = true; } } + if (!blured && opts.inPurgeable) { + Utilities.pinBitmap(image); + } if (runtimeHack != null) { runtimeHack.trackFree(image.getRowBytes() * image.getHeight()); } @@ -753,7 +813,7 @@ public class ImageLoader { } try { Object res = trackAllocation.invoke(runtime, size); - return (res instanceof Boolean) ? (Boolean)res : true; + return (res instanceof Boolean) ? (Boolean) res : true; } catch (Exception e) { return false; } @@ -765,7 +825,7 @@ public class ImageLoader { } try { Object res = trackFree.invoke(runtime, size); - return (res instanceof Boolean) ? (Boolean)res : true; + return (res instanceof Boolean) ? (Boolean) res : true; } catch (Exception e) { return false; } @@ -778,8 +838,8 @@ public class ImageLoader { Method getRt = cl.getMethod("getRuntime", new Class[0]); Object[] objects = new Object[0]; runtime = getRt.invoke(null, objects); - trackAllocation = cl.getMethod("trackExternalAllocation", new Class[] {long.class}); - trackFree = cl.getMethod("trackExternalFree", new Class[] {long.class}); + trackAllocation = cl.getMethod("trackExternalAllocation", new Class[]{long.class}); + trackFree = cl.getMethod("trackExternalFree", new Class[]{long.class}); } catch (Exception e) { FileLog.e("tmessages", e); runtime = null; @@ -872,7 +932,7 @@ public class ImageLoader { @Override public void run() { for (ImageReceiver imgView : finalImageReceiverArray) { - imgView.setImageBitmapByKey(image, key, thumb); + imgView.setImageBitmapByKey(image, key, thumb, false); } } }); @@ -891,6 +951,7 @@ public class ImageLoader { } private static volatile ImageLoader Instance = null; + public static ImageLoader getInstance() { ImageLoader localInstance = Instance; if (localInstance == null) { @@ -915,12 +976,13 @@ public class ImageLoader { @Override protected int sizeOf(String key, BitmapDrawable bitmap) { Bitmap b = bitmap.getBitmap(); - if(Build.VERSION.SDK_INT < 12) { + if (Build.VERSION.SDK_INT < 12) { return b.getRowBytes() * b.getHeight(); } else { return b.getByteCount(); } } + @Override protected void entryRemoved(boolean evicted, String key, final BitmapDrawable oldBitmap, BitmapDrawable newBitmap) { if (ignoreRemoval != null && key != null && ignoreRemoval.equals(key)) { @@ -1193,7 +1255,7 @@ public class ImageLoader { recycleQueue.postRunnable(new Runnable() { @Override public void run() { - System.gc(); + //System.gc(); } }); } @@ -1296,17 +1358,21 @@ public class ImageLoader { return memCache.get(key); } - public void replaceImageInCache(final String oldKey, final String newKey) { + public void replaceImageInCache(final String oldKey, final String newKey, final TLRPC.FileLocation newLocation) { AndroidUtilities.runOnUIThread(new Runnable() { @Override public void run() { ArrayList arr = memCache.getFilterKeys(oldKey); if (arr != null) { for (String filter : arr) { - performReplace(oldKey + "@" + filter, newKey + "@" + filter); + String oldK = oldKey + "@" + filter; + String newK = newKey + "@" + filter; + performReplace(oldK, newK); + NotificationCenter.getInstance().postNotificationName(NotificationCenter.didReplacedPhotoInMemCache, oldK, newK, newLocation); } } else { performReplace(oldKey, newKey); + NotificationCenter.getInstance().postNotificationName(NotificationCenter.didReplacedPhotoInMemCache, oldKey, newKey, newLocation); } } }); @@ -1384,6 +1450,11 @@ public class ImageLoader { if (idx >= 0) { cacheFile = new File(httpLocation.substring(idx + 1)); } + } else if (httpLocation.startsWith("vthumb://")) { + int idx = httpLocation.indexOf(":", 9); + if (idx >= 0) { + cacheFile = new File(httpLocation.substring(idx + 1)); + } } else { cacheFile = new File(httpLocation); } @@ -1487,7 +1558,7 @@ public class ImageLoader { if (bitmapDrawable != null) { cancelLoadingForImageReceiver(imageReceiver, 0); if (!imageReceiver.isForcePreview()) { - imageReceiver.setImageBitmapByKey(bitmapDrawable, key, false); + imageReceiver.setImageBitmapByKey(bitmapDrawable, key, false, true); return; } } @@ -1497,7 +1568,7 @@ public class ImageLoader { if (thumbKey != null) { BitmapDrawable bitmapDrawable = memCache.get(thumbKey); if (bitmapDrawable != null) { - imageReceiver.setImageBitmapByKey(bitmapDrawable, thumbKey, true); + imageReceiver.setImageBitmapByKey(bitmapDrawable, thumbKey, true, true); cancelLoadingForImageReceiver(imageReceiver, 1); thumbSet = true; } @@ -1784,7 +1855,7 @@ public class ImageLoader { scaleFactor = 1; } bmOptions.inJustDecodeBounds = false; - bmOptions.inSampleSize = (int)scaleFactor; + bmOptions.inSampleSize = (int) scaleFactor; String exifPath = null; if (path != null) { @@ -1900,7 +1971,7 @@ public class ImageLoader { size.size = size.bytes.length; stream2.close(); } else { - size.size = (int)stream.getChannel().size(); + size.size = (int) stream.getChannel().size(); } stream.close(); if (scaledBitmap != bitmap) { @@ -1926,11 +1997,17 @@ public class ImageLoader { boolean scaleAnyway = false; float scaleFactor = Math.max(photoW / maxWidth, photoH / maxHeight); if (minWidth != 0 && minHeight != 0 && (photoW < minWidth || photoH < minHeight)) { - scaleFactor = Math.max(photoW / minWidth, photoH / minHeight); + if (photoW < minWidth && photoH > minHeight) { + scaleFactor = photoW / minWidth; + } else if (photoW > minWidth && photoH < minHeight) { + scaleFactor = photoH / minHeight; + } else { + scaleFactor = Math.max(photoW / minWidth, photoH / minHeight); + } scaleAnyway = true; } - int w = (int)(photoW / scaleFactor); - int h = (int)(photoH / scaleFactor); + int w = (int) (photoW / scaleFactor); + int h = (int) (photoH / scaleFactor); if (h == 0 || w == 0) { return null; } diff --git a/TMessagesProj/src/main/java/org/telegram/android/ImageReceiver.java b/TMessagesProj/src/main/java/org/telegram/android/ImageReceiver.java index 6f50bdc59..1d0eb8b4c 100644 --- a/TMessagesProj/src/main/java/org/telegram/android/ImageReceiver.java +++ b/TMessagesProj/src/main/java/org/telegram/android/ImageReceiver.java @@ -11,6 +11,7 @@ package org.telegram.android; import android.graphics.Bitmap; import android.graphics.BitmapShader; import android.graphics.Canvas; +import android.graphics.ColorFilter; import android.graphics.Matrix; import android.graphics.Paint; import android.graphics.PorterDuff; @@ -33,12 +34,25 @@ public class ImageReceiver implements NotificationCenter.NotificationCenterDeleg void didSetImage(ImageReceiver imageReceiver, boolean set, boolean thumb); } + private class SetImageBackup { + public TLObject fileLocation; + public String httpUrl; + public String filter; + public Drawable thumb; + public TLRPC.FileLocation thumbLocation; + public String thumbFilter; + public int size; + public boolean cacheOnly; + } + private View parentView; private Integer tag; private Integer thumbTag; private MessageObject parentMessageObject; private boolean canceledLoading; + private SetImageBackup setImageBackup; + private TLObject currentImageLocation; private String currentKey; private String currentThumbKey; @@ -66,11 +80,16 @@ public class ImageReceiver implements NotificationCenter.NotificationCenterDeleg private RectF roundRect; private RectF bitmapRect; private Matrix shaderMatrix; - private int alpha = 255; + private float overrideAlpha = 1.0f; private boolean isPressed; private int orientation; private boolean centerRotation; private ImageReceiverDelegate delegate; + private float currentAlpha; + private long lastUpdateAlphaTime; + private byte crossfadeAlpha = 1; + private boolean crossfadeWithThumb; + private ColorFilter colorFilter; public ImageReceiver() { @@ -106,6 +125,13 @@ public class ImageReceiver implements NotificationCenter.NotificationCenterDeleg } public void setImage(TLObject fileLocation, String httpUrl, String filter, Drawable thumb, TLRPC.FileLocation thumbLocation, String thumbFilter, int size, boolean cacheOnly) { + if (setImageBackup != null) { + setImageBackup.fileLocation = null; + setImageBackup.httpUrl = null; + setImageBackup.thumbLocation = null; + setImageBackup.thumb = null; + } + if ((fileLocation == null && httpUrl == null && thumbLocation == null) || (fileLocation != null && !(fileLocation instanceof TLRPC.TL_fileLocation) && !(fileLocation instanceof TLRPC.TL_fileEncryptedLocation) @@ -120,13 +146,14 @@ public class ImageReceiver implements NotificationCenter.NotificationCenterDeleg currentFilter = null; currentCacheOnly = false; staticThumb = thumb; + currentAlpha = 1; currentThumbLocation = null; currentSize = 0; currentImage = null; bitmapShader = null; ImageLoader.getInstance().cancelLoadingForImageReceiver(this, 0); if (parentView != null) { - parentView.invalidate(); + parentView.invalidate(imageX, imageY, imageX + imageW, imageY + imageH); } if (delegate != null) { delegate.didSetImage(this, currentImage != null || currentThumb != null || staticThumb != null, currentImage == null); @@ -134,6 +161,8 @@ public class ImageReceiver implements NotificationCenter.NotificationCenterDeleg return; } + + if (!(thumbLocation instanceof TLRPC.TL_fileLocation)) { thumbLocation = null; } @@ -187,6 +216,7 @@ public class ImageReceiver implements NotificationCenter.NotificationCenterDeleg currentThumbLocation = thumbLocation; staticThumb = thumb; bitmapShader = null; + currentAlpha = 1.0f; if (delegate != null) { delegate.didSetImage(this, currentImage != null || currentThumb != null || staticThumb != null, currentImage == null); @@ -194,10 +224,14 @@ public class ImageReceiver implements NotificationCenter.NotificationCenterDeleg ImageLoader.getInstance().loadImageForImageReceiver(this); if (parentView != null) { - parentView.invalidate(); + parentView.invalidate(imageX, imageY, imageX + imageW, imageY + imageH); } } + public void setColorFilter(ColorFilter filter) { + colorFilter = filter; + } + public void setDelegate(ImageReceiverDelegate delegate) { this.delegate = delegate; } @@ -239,11 +273,18 @@ public class ImageReceiver implements NotificationCenter.NotificationCenterDeleg currentSize = 0; currentCacheOnly = false; bitmapShader = null; + if (setImageBackup != null) { + setImageBackup.fileLocation = null; + setImageBackup.httpUrl = null; + setImageBackup.thumbLocation = null; + setImageBackup.thumb = null; + } + currentAlpha = 1; if (delegate != null) { delegate.didSetImage(this, currentImage != null || currentThumb != null || staticThumb != null, currentImage == null); } if (parentView != null) { - parentView.invalidate(); + parentView.invalidate(imageX, imageY, imageX + imageW, imageY + imageH); } } @@ -256,6 +297,212 @@ public class ImageReceiver implements NotificationCenter.NotificationCenterDeleg } } + public void onDetachedFromWindow() { + if (currentImageLocation != null || currentHttpUrl != null || currentThumbLocation != null || staticThumb != null) { + if (setImageBackup == null) { + setImageBackup = new SetImageBackup(); + } + setImageBackup.fileLocation = currentImageLocation; + setImageBackup.httpUrl = currentHttpUrl; + setImageBackup.filter = currentFilter; + setImageBackup.thumb = staticThumb; + setImageBackup.thumbLocation = currentThumbLocation; + setImageBackup.thumbFilter = currentThumbFilter; + setImageBackup.size = currentSize; + setImageBackup.cacheOnly = currentCacheOnly; + } + NotificationCenter.getInstance().removeObserver(this, NotificationCenter.didReplacedPhotoInMemCache); + clearImage(); + } + + public boolean onAttachedToWindow() { + NotificationCenter.getInstance().addObserver(this, NotificationCenter.didReplacedPhotoInMemCache); + if (setImageBackup != null && (setImageBackup.fileLocation != null || setImageBackup.httpUrl != null || setImageBackup.thumbLocation != null || setImageBackup.thumb != null)) { + setImage(setImageBackup.fileLocation, setImageBackup.httpUrl, setImageBackup.filter, setImageBackup.thumb, setImageBackup.thumbLocation, setImageBackup.thumbFilter, setImageBackup.size, setImageBackup.cacheOnly); + return true; + } + return false; + } + + private void drawDrawable(Canvas canvas, Drawable drawable, int alpha) { + if (drawable instanceof BitmapDrawable) { + BitmapDrawable bitmapDrawable = (BitmapDrawable) drawable; + + Paint paint = bitmapDrawable.getPaint(); + boolean hasFilter = paint != null && paint.getColorFilter() != null; + if (hasFilter && !isPressed) { + bitmapDrawable.setColorFilter(null); + hasFilter = false; + } else if (!hasFilter && isPressed) { + bitmapDrawable.setColorFilter(new PorterDuffColorFilter(0xffdddddd, PorterDuff.Mode.MULTIPLY)); + hasFilter = true; + } + if (colorFilter != null) { + bitmapDrawable.setColorFilter(colorFilter); + } + if (bitmapShader != null) { + drawRegion.set(imageX, imageY, imageX + imageW, imageY + imageH); + if (isVisible) { + roundRect.set(drawRegion); + shaderMatrix.reset(); + shaderMatrix.setRectToRect(bitmapRect, roundRect, Matrix.ScaleToFit.FILL); + bitmapShader.setLocalMatrix(shaderMatrix); + roundPaint.setAlpha(alpha); + canvas.drawRoundRect(roundRect, roundRadius, roundRadius, roundPaint); + } + } else { + int bitmapW; + int bitmapH; + int originalW = bitmapDrawable.getIntrinsicWidth(); + int originalH = bitmapDrawable.getIntrinsicHeight(); + if (orientation == 90 || orientation == 270) { + bitmapW = bitmapDrawable.getIntrinsicHeight(); + bitmapH = bitmapDrawable.getIntrinsicWidth(); + } else { + bitmapW = bitmapDrawable.getIntrinsicWidth(); + bitmapH = bitmapDrawable.getIntrinsicHeight(); + } + float scaleW = bitmapW / (float) imageW; + float scaleH = bitmapH / (float) imageH; + + if (isAspectFit) { + float scale = Math.max(scaleW, scaleH); + canvas.save(); + bitmapW /= scale; + bitmapH /= scale; + drawRegion.set(imageX + (imageW - bitmapW) / 2, imageY + (imageH - bitmapH) / 2, imageX + (imageW + bitmapW) / 2, imageY + (imageH + bitmapH) / 2); + bitmapDrawable.setBounds(drawRegion); + try { + bitmapDrawable.setAlpha(alpha); + bitmapDrawable.draw(canvas); + } catch (Exception e) { + if (bitmapDrawable == currentImage && currentKey != null) { + ImageLoader.getInstance().removeImage(currentKey); + currentKey = null; + } else if (bitmapDrawable == currentThumb && currentThumbKey != null) { + ImageLoader.getInstance().removeImage(currentThumbKey); + currentThumbKey = null; + } + setImage(currentImageLocation, currentHttpUrl, currentFilter, currentThumb, currentThumbLocation, currentThumbFilter, currentSize, currentCacheOnly); + FileLog.e("tmessages", e); + } + canvas.restore(); + } else { + if (Math.abs(scaleW - scaleH) > 0.00001f) { + canvas.save(); + canvas.clipRect(imageX, imageY, imageX + imageW, imageY + imageH); + + if (orientation != 0) { + if (centerRotation) { + canvas.rotate(orientation, imageW / 2, imageH / 2); + } else { + canvas.rotate(orientation, 0, 0); + } + } + + if (bitmapW / scaleH > imageW) { + bitmapW /= scaleH; + originalW /= scaleH; + drawRegion.set(imageX - (bitmapW - imageW) / 2, imageY, imageX + (bitmapW + imageW) / 2, imageY + imageH); + } else { + bitmapH /= scaleW; + originalH /= scaleW; + drawRegion.set(imageX, imageY - (bitmapH - imageH) / 2, imageX + imageW, imageY + (bitmapH + imageH) / 2); + } + if (orientation == 90 || orientation == 270) { + int width = (drawRegion.right - drawRegion.left) / 2; + int height = (drawRegion.bottom - drawRegion.top) / 2; + int centerX = (drawRegion.right + drawRegion.left) / 2; + int centerY = (drawRegion.top + drawRegion.bottom) / 2; + bitmapDrawable.setBounds(centerX - height, centerY - width, centerX + height, centerY + width); + } else { + bitmapDrawable.setBounds(drawRegion); + } + if (isVisible) { + try { + bitmapDrawable.setAlpha(alpha); + bitmapDrawable.draw(canvas); + } catch (Exception e) { + if (bitmapDrawable == currentImage && currentKey != null) { + ImageLoader.getInstance().removeImage(currentKey); + currentKey = null; + } else if (bitmapDrawable == currentThumb && currentThumbKey != null) { + ImageLoader.getInstance().removeImage(currentThumbKey); + currentThumbKey = null; + } + setImage(currentImageLocation, currentHttpUrl, currentFilter, currentThumb, currentThumbLocation, currentThumbFilter, currentSize, currentCacheOnly); + FileLog.e("tmessages", e); + } + } + + canvas.restore(); + } else { + canvas.save(); + if (orientation != 0) { + if (centerRotation) { + canvas.rotate(orientation, imageW / 2, imageH / 2); + } else { + canvas.rotate(orientation, 0, 0); + } + } + drawRegion.set(imageX, imageY, imageX + imageW, imageY + imageH); + if (orientation == 90 || orientation == 270) { + int width = (drawRegion.right - drawRegion.left) / 2; + int height = (drawRegion.bottom - drawRegion.top) / 2; + int centerX = (drawRegion.right + drawRegion.left) / 2; + int centerY = (drawRegion.top + drawRegion.bottom) / 2; + bitmapDrawable.setBounds(centerX - height, centerY - width, centerX + height, centerY + width); + } else { + bitmapDrawable.setBounds(drawRegion); + } + if (isVisible) { + try { + bitmapDrawable.setAlpha(alpha); + bitmapDrawable.draw(canvas); + } catch (Exception e) { + if (bitmapDrawable == currentImage && currentKey != null) { + ImageLoader.getInstance().removeImage(currentKey); + currentKey = null; + } else if (bitmapDrawable == currentThumb && currentThumbKey != null) { + ImageLoader.getInstance().removeImage(currentThumbKey); + currentThumbKey = null; + } + setImage(currentImageLocation, currentHttpUrl, currentFilter, currentThumb, currentThumbLocation, currentThumbFilter, currentSize, currentCacheOnly); + FileLog.e("tmessages", e); + } + } + canvas.restore(); + } + } + } + } else { + drawRegion.set(imageX, imageY, imageX + imageW, imageY + imageH); + drawable.setBounds(drawRegion); + if (isVisible) { + try { + drawable.setAlpha(alpha); + drawable.draw(canvas); + } catch (Exception e) { + FileLog.e("tmessages", e); + } + } + } + } + + private void checkAlphaAnimation() { + if (currentAlpha != 1) { + long currentTime = System.currentTimeMillis(); + currentAlpha += (currentTime - lastUpdateAlphaTime) / 150.0f; + if (currentAlpha > 1) { + currentAlpha = 1; + } + lastUpdateAlphaTime = System.currentTimeMillis(); + if (parentView != null) { + parentView.invalidate(imageX, imageY, imageX + imageW, imageY + imageH); + } + } + } + public boolean draw(Canvas canvas) { try { BitmapDrawable bitmapDrawable = null; @@ -267,161 +514,34 @@ public class ImageReceiver implements NotificationCenter.NotificationCenterDeleg bitmapDrawable = currentThumb; } if (bitmapDrawable != null) { - Paint paint = bitmapDrawable.getPaint(); - boolean hasFilter = paint != null && paint.getColorFilter() != null; - if (hasFilter && !isPressed) { - bitmapDrawable.setColorFilter(null); - hasFilter = false; - } else if (!hasFilter && isPressed) { - bitmapDrawable.setColorFilter(new PorterDuffColorFilter(0xffdddddd, PorterDuff.Mode.MULTIPLY)); - hasFilter = true; - } - if (bitmapShader != null) { - drawRegion.set(imageX, imageY, imageX + imageW, imageY + imageH); - if (isVisible) { - roundRect.set(drawRegion); - shaderMatrix.reset(); - shaderMatrix.setRectToRect(bitmapRect, roundRect, Matrix.ScaleToFit.FILL); - bitmapShader.setLocalMatrix(shaderMatrix); - canvas.drawRoundRect(roundRect, roundRadius, roundRadius, roundPaint); + if (crossfadeAlpha != 0) { + if (crossfadeWithThumb && currentAlpha != 1.0f) { + Drawable thumbDrawable = null; + if (bitmapDrawable == currentImage) { + if (staticThumb != null) { + thumbDrawable = staticThumb; + } else if (currentThumb != null) { + thumbDrawable = currentThumb; + } + } else if (bitmapDrawable == currentThumb) { + if (staticThumb != null) { + thumbDrawable = staticThumb; + } + } + if (thumbDrawable != null) { + drawDrawable(canvas, thumbDrawable, (int) (overrideAlpha * 255)); + } } + drawDrawable(canvas, bitmapDrawable, (int) (overrideAlpha * currentAlpha * 255)); } else { - int bitmapW; - int bitmapH; - int originalW = bitmapDrawable.getIntrinsicWidth(); - int originalH = bitmapDrawable.getIntrinsicHeight(); - if (orientation == 90 || orientation == 270) { - bitmapW = bitmapDrawable.getIntrinsicHeight(); - bitmapH = bitmapDrawable.getIntrinsicWidth(); - } else { - bitmapW = bitmapDrawable.getIntrinsicWidth(); - bitmapH = bitmapDrawable.getIntrinsicHeight(); - } - float scaleW = bitmapW / (float) imageW; - float scaleH = bitmapH / (float) imageH; - - if (isAspectFit) { - float scale = Math.max(scaleW, scaleH); - canvas.save(); - bitmapW /= scale; - bitmapH /= scale; - drawRegion.set(imageX + (imageW - bitmapW) / 2, imageY + (imageH - bitmapH) / 2, imageX + (imageW + bitmapW) / 2, imageY + (imageH + bitmapH) / 2); - bitmapDrawable.setBounds(drawRegion); - try { - bitmapDrawable.setAlpha(alpha); - bitmapDrawable.draw(canvas); - } catch (Exception e) { - if (bitmapDrawable == currentImage && currentKey != null) { - ImageLoader.getInstance().removeImage(currentKey); - currentKey = null; - } else if (bitmapDrawable == currentThumb && currentThumbKey != null) { - ImageLoader.getInstance().removeImage(currentThumbKey); - currentThumbKey = null; - } - setImage(currentImageLocation, currentHttpUrl, currentFilter, currentThumb, currentThumbLocation, currentThumbFilter, currentSize, currentCacheOnly); - FileLog.e("tmessages", e); - } - canvas.restore(); - } else { - if (Math.abs(scaleW - scaleH) > 0.00001f) { - canvas.save(); - canvas.clipRect(imageX, imageY, imageX + imageW, imageY + imageH); - - if (orientation != 0) { - if (centerRotation) { - canvas.rotate(orientation, imageW / 2, imageH / 2); - } else { - canvas.rotate(orientation, 0, 0); - } - } - - if (bitmapW / scaleH > imageW) { - bitmapW /= scaleH; - originalW /= scaleH; - drawRegion.set(imageX - (bitmapW - imageW) / 2, imageY, imageX + (bitmapW + imageW) / 2, imageY + imageH); - } else { - bitmapH /= scaleW; - originalH /= scaleW; - drawRegion.set(imageX, imageY - (bitmapH - imageH) / 2, imageX + imageW, imageY + (bitmapH + imageH) / 2); - } - if (orientation == 90 || orientation == 270) { - int width = (drawRegion.right - drawRegion.left) / 2; - int height = (drawRegion.bottom - drawRegion.top) / 2; - int centerX = (drawRegion.right + drawRegion.left) / 2; - int centerY = (drawRegion.top + drawRegion.bottom) / 2; - bitmapDrawable.setBounds(centerX - height, centerY - width, centerX + height, centerY + width); - } else { - bitmapDrawable.setBounds(drawRegion); - } - if (isVisible) { - try { - bitmapDrawable.setAlpha(alpha); - bitmapDrawable.draw(canvas); - } catch (Exception e) { - if (bitmapDrawable == currentImage && currentKey != null) { - ImageLoader.getInstance().removeImage(currentKey); - currentKey = null; - } else if (bitmapDrawable == currentThumb && currentThumbKey != null) { - ImageLoader.getInstance().removeImage(currentThumbKey); - currentThumbKey = null; - } - setImage(currentImageLocation, currentHttpUrl, currentFilter, currentThumb, currentThumbLocation, currentThumbFilter, currentSize, currentCacheOnly); - FileLog.e("tmessages", e); - } - } - - canvas.restore(); - } else { - canvas.save(); - if (orientation != 0) { - if (centerRotation) { - canvas.rotate(orientation, imageW / 2, imageH / 2); - } else { - canvas.rotate(orientation, 0, 0); - } - } - drawRegion.set(imageX, imageY, imageX + imageW, imageY + imageH); - if (orientation == 90 || orientation == 270) { - int width = (drawRegion.right - drawRegion.left) / 2; - int height = (drawRegion.bottom - drawRegion.top) / 2; - int centerX = (drawRegion.right + drawRegion.left) / 2; - int centerY = (drawRegion.top + drawRegion.bottom) / 2; - bitmapDrawable.setBounds(centerX - height, centerY - width, centerX + height, centerY + width); - } else { - bitmapDrawable.setBounds(drawRegion); - } - if (isVisible) { - try { - bitmapDrawable.setAlpha(alpha); - bitmapDrawable.draw(canvas); - } catch (Exception e) { - if (bitmapDrawable == currentImage && currentKey != null) { - ImageLoader.getInstance().removeImage(currentKey); - currentKey = null; - } else if (bitmapDrawable == currentThumb && currentThumbKey != null) { - ImageLoader.getInstance().removeImage(currentThumbKey); - currentThumbKey = null; - } - setImage(currentImageLocation, currentHttpUrl, currentFilter, currentThumb, currentThumbLocation, currentThumbFilter, currentSize, currentCacheOnly); - FileLog.e("tmessages", e); - } - } - canvas.restore(); - } - } + drawDrawable(canvas, bitmapDrawable, (int) (overrideAlpha * 255)); } + + checkAlphaAnimation(); return true; } else if (staticThumb != null) { - drawRegion.set(imageX, imageY, imageX + imageW, imageY + imageH); - staticThumb.setBounds(drawRegion); - if (isVisible) { - try { - staticThumb.setAlpha(alpha); - staticThumb.draw(canvas); - } catch (Exception e) { - FileLog.e("tmessages", e); - } - } + drawDrawable(canvas, staticThumb, 255); + checkAlphaAnimation(); return true; } } catch (Exception e) { @@ -457,7 +577,7 @@ public class ImageReceiver implements NotificationCenter.NotificationCenterDeleg } isVisible = value; if (invalidate && parentView != null) { - parentView.invalidate(); + parentView.invalidate(imageX, imageY, imageX + imageW, imageY + imageH); } } @@ -466,7 +586,11 @@ public class ImageReceiver implements NotificationCenter.NotificationCenterDeleg } public void setAlpha(float value) { - alpha = (int)(value * 255.0f); + overrideAlpha = value; + } + + public void setCrossfadeAlpha(byte value) { + crossfadeAlpha = value; } public boolean hasImage() { @@ -630,7 +754,7 @@ public class ImageReceiver implements NotificationCenter.NotificationCenterDeleg } } - protected void setImageBitmapByKey(BitmapDrawable bitmap, String key, boolean thumb) { + protected void setImageBitmapByKey(BitmapDrawable bitmap, String key, boolean thumb, boolean memCache) { if (bitmap == null || key == null) { return; } @@ -646,17 +770,38 @@ public class ImageReceiver implements NotificationCenter.NotificationCenterDeleg roundPaint.setShader(bitmapShader); bitmapRect.set(0, 0, object.getWidth(), object.getHeight()); } + + if (!memCache && !forcePreview) { + if (currentThumb == null && staticThumb == null || currentAlpha == 1.0f) { + currentAlpha = 0.0f; + lastUpdateAlphaTime = System.currentTimeMillis(); + crossfadeWithThumb = currentThumb != null || staticThumb != null; + } + } else { + currentAlpha = 1.0f; + } + if (parentView != null) { - parentView.invalidate(); + parentView.invalidate(imageX, imageY, imageX + imageW, imageY + imageH); } } else if (currentThumb == null && (currentImage == null || forcePreview)) { if (currentThumbKey == null || !key.equals(currentThumbKey)) { return; } ImageLoader.getInstance().incrementUseCount(currentThumbKey); + currentThumb = bitmap; + + if (!memCache && crossfadeAlpha != 2) { + currentAlpha = 0.0f; + lastUpdateAlphaTime = System.currentTimeMillis(); + crossfadeWithThumb = staticThumb != null && currentKey == null; + } else { + currentAlpha = 1.0f; + } + if (!(staticThumb instanceof BitmapDrawable) && parentView != null) { - parentView.invalidate(); + parentView.invalidate(imageX, imageY, imageX + imageW, imageY + imageH); } } @@ -721,7 +866,27 @@ public class ImageReceiver implements NotificationCenter.NotificationCenterDeleg staticThumb = null; } if (parentView != null) { - parentView.invalidate(); + parentView.invalidate(imageX, imageY, imageX + imageW, imageY + imageH); + } + } + } else if (id == NotificationCenter.didReplacedPhotoInMemCache) { + String oldKey = (String) args[0]; + if (currentKey != null && currentKey.equals(oldKey)) { + currentKey = (String) args[1]; + currentImageLocation = (TLRPC.FileLocation) args[2]; + } + if (currentThumbKey != null && currentThumbKey.equals(oldKey)) { + currentThumbKey = (String) args[1]; + currentThumbLocation = (TLRPC.FileLocation) args[2]; + } + if (setImageBackup != null) { + if (currentKey != null && currentKey.equals(oldKey)) { + currentKey = (String) args[1]; + currentImageLocation = (TLRPC.FileLocation) args[2]; + } + if (currentThumbKey != null && currentThumbKey.equals(oldKey)) { + currentThumbKey = (String) args[1]; + currentThumbLocation = (TLRPC.FileLocation) args[2]; } } } diff --git a/TMessagesProj/src/main/java/org/telegram/android/LocaleController.java b/TMessagesProj/src/main/java/org/telegram/android/LocaleController.java index 1d2094bc8..5e82e4070 100644 --- a/TMessagesProj/src/main/java/org/telegram/android/LocaleController.java +++ b/TMessagesProj/src/main/java/org/telegram/android/LocaleController.java @@ -309,6 +309,10 @@ public class LocaleController { } } + public Locale getSystemDefaultLocale() { + return systemDefaultLocale; + } + public static String getLocaleString(Locale locale) { if (locale == null) { return "en"; @@ -607,11 +611,12 @@ public class LocaleController { } public static String formatString(String key, int res, Object... args) { - String value = getInstance().localeValues.get(key); - if (value == null) { - value = ApplicationLoader.applicationContext.getString(res); - } try { + String value = getInstance().localeValues.get(key); + if (value == null) { + value = ApplicationLoader.applicationContext.getString(res); + } + if (getInstance().currentLocale != null) { return String.format(getInstance().currentLocale, value, args); } else { diff --git a/TMessagesProj/src/main/java/org/telegram/android/MediaController.java b/TMessagesProj/src/main/java/org/telegram/android/MediaController.java index 629268341..9a088bcd0 100644 --- a/TMessagesProj/src/main/java/org/telegram/android/MediaController.java +++ b/TMessagesProj/src/main/java/org/telegram/android/MediaController.java @@ -117,17 +117,27 @@ public class MediaController implements NotificationCenter.NotificationCenterDel MediaStore.Images.Media.ORIENTATION }; + private static final String[] projectionVideo = { + MediaStore.Video.Media._ID, + MediaStore.Video.Media.BUCKET_ID, + MediaStore.Video.Media.BUCKET_DISPLAY_NAME, + MediaStore.Video.Media.DATA, + MediaStore.Video.Media.DATE_TAKEN + }; + public static class AlbumEntry { public int bucketId; public String bucketName; public PhotoEntry coverPhoto; public ArrayList photos = new ArrayList<>(); public HashMap photosByIds = new HashMap<>(); + public boolean isVideo; - public AlbumEntry(int bucketId, String bucketName, PhotoEntry coverPhoto) { + public AlbumEntry(int bucketId, String bucketName, PhotoEntry coverPhoto, boolean isVideo) { this.bucketId = bucketId; this.bucketName = bucketName; this.coverPhoto = coverPhoto; + this.isVideo = isVideo; } public void addPhoto(PhotoEntry photoEntry) { @@ -144,13 +154,16 @@ public class MediaController implements NotificationCenter.NotificationCenterDel public int orientation; public String thumbPath; public String imagePath; + public boolean isVideo; + public CharSequence caption; - public PhotoEntry(int bucketId, int imageId, long dateTaken, String path, int orientation) { + public PhotoEntry(int bucketId, int imageId, long dateTaken, String path, int orientation, boolean isVideo) { this.bucketId = bucketId; this.imageId = imageId; this.dateTaken = dateTaken; this.path = path; this.orientation = orientation; + this.isVideo = isVideo; } } @@ -167,6 +180,7 @@ public class MediaController implements NotificationCenter.NotificationCenterDel public int date; public String thumbPath; public String imagePath; + public CharSequence caption; } public final static String MIME_TYPE = "video/avc"; @@ -178,6 +192,8 @@ public class MediaController implements NotificationCenter.NotificationCenterDel private final static int PROCESSOR_TYPE_TI = 5; private final Object videoConvertSync = new Object(); + private HashMap typingTimes = new HashMap<>(); + private SensorManager sensorManager; private Sensor proximitySensor; private boolean ignoreProximity; @@ -550,6 +566,7 @@ public class MediaController implements NotificationCenter.NotificationCenterDel videoDownloadQueue.clear(); downloadQueueKeys.clear(); videoConvertQueue.clear(); + typingTimes.clear(); cancelVideoConvert(null); } @@ -975,6 +992,29 @@ public class MediaController implements NotificationCenter.NotificationCenterDel } listenerInProgress = false; processLaterArrays(); + try { + ArrayList delayedMessages = SendMessagesHelper.getInstance().getDelayedMessages(fileName); + if (delayedMessages != null) { + for (SendMessagesHelper.DelayedMessage delayedMessage : delayedMessages) { + if (delayedMessage.encryptedChat == null) { + long dialog_id = delayedMessage.obj.getDialogId(); + Long lastTime = typingTimes.get(dialog_id); + if (lastTime == null || lastTime + 4000 < System.currentTimeMillis()) { + if (delayedMessage.videoLocation != null) { + MessagesController.getInstance().sendTyping(dialog_id, 5, 0); + } else if (delayedMessage.documentLocation != null) { + MessagesController.getInstance().sendTyping(dialog_id, 3, 0); + } else if (delayedMessage.location != null) { + MessagesController.getInstance().sendTyping(dialog_id, 4, 0); + } + typingTimes.put(dialog_id, System.currentTimeMillis()); + } + } + } + } + } catch (Exception e) { + FileLog.e("tmessages", e); + } } else if (id == NotificationCenter.messagesDeleted) { if (playingMessageObject != null) { ArrayList markAsDeletedMessages = (ArrayList)args[0]; @@ -1970,10 +2010,12 @@ public class MediaController implements NotificationCenter.NotificationCenterDel @Override public void run() { final ArrayList albumsSorted = new ArrayList<>(); + final ArrayList videoAlbumsSorted = new ArrayList<>(); HashMap albums = new HashMap<>(); AlbumEntry allPhotosAlbum = null; String cameraFolder = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM).getAbsolutePath() + "/" + "Camera/"; Integer cameraAlbumId = null; + Integer cameraAlbumVideoId = null; Cursor cursor = null; try { @@ -1998,10 +2040,10 @@ public class MediaController implements NotificationCenter.NotificationCenterDel continue; } - PhotoEntry photoEntry = new PhotoEntry(bucketId, imageId, dateTaken, path, orientation); + PhotoEntry photoEntry = new PhotoEntry(bucketId, imageId, dateTaken, path, orientation, false); if (allPhotosAlbum == null) { - allPhotosAlbum = new AlbumEntry(0, LocaleController.getString("AllPhotos", R.string.AllPhotos), photoEntry); + allPhotosAlbum = new AlbumEntry(0, LocaleController.getString("AllPhotos", R.string.AllPhotos), photoEntry, false); albumsSorted.add(0, allPhotosAlbum); } if (allPhotosAlbum != null) { @@ -2010,7 +2052,7 @@ public class MediaController implements NotificationCenter.NotificationCenterDel AlbumEntry albumEntry = albums.get(bucketId); if (albumEntry == null) { - albumEntry = new AlbumEntry(bucketId, bucketName, photoEntry); + albumEntry = new AlbumEntry(bucketId, bucketName, photoEntry, false); albums.put(bucketId, albumEntry); if (cameraAlbumId == null && cameraFolder != null && path != null && path.startsWith(cameraFolder)) { albumsSorted.add(0, albumEntry); @@ -2034,11 +2076,72 @@ public class MediaController implements NotificationCenter.NotificationCenterDel } } } + + try { + albums.clear(); + allPhotosAlbum = null; + cursor = MediaStore.Images.Media.query(ApplicationLoader.applicationContext.getContentResolver(), MediaStore.Video.Media.EXTERNAL_CONTENT_URI, projectionVideo, "", null, MediaStore.Video.Media.DATE_TAKEN + " DESC"); + if (cursor != null) { + int imageIdColumn = cursor.getColumnIndex(MediaStore.Video.Media._ID); + int bucketIdColumn = cursor.getColumnIndex(MediaStore.Video.Media.BUCKET_ID); + int bucketNameColumn = cursor.getColumnIndex(MediaStore.Video.Media.BUCKET_DISPLAY_NAME); + int dataColumn = cursor.getColumnIndex(MediaStore.Video.Media.DATA); + int dateColumn = cursor.getColumnIndex(MediaStore.Video.Media.DATE_TAKEN); + + while (cursor.moveToNext()) { + int imageId = cursor.getInt(imageIdColumn); + int bucketId = cursor.getInt(bucketIdColumn); + String bucketName = cursor.getString(bucketNameColumn); + String path = cursor.getString(dataColumn); + long dateTaken = cursor.getLong(dateColumn); + + if (path == null || path.length() == 0) { + continue; + } + + PhotoEntry photoEntry = new PhotoEntry(bucketId, imageId, dateTaken, path, 0, true); + + if (allPhotosAlbum == null) { + allPhotosAlbum = new AlbumEntry(0, LocaleController.getString("AllVideo", R.string.AllVideo), photoEntry, true); + videoAlbumsSorted.add(0, allPhotosAlbum); + } + if (allPhotosAlbum != null) { + allPhotosAlbum.addPhoto(photoEntry); + } + + AlbumEntry albumEntry = albums.get(bucketId); + if (albumEntry == null) { + albumEntry = new AlbumEntry(bucketId, bucketName, photoEntry, true); + albums.put(bucketId, albumEntry); + if (cameraAlbumVideoId == null && cameraFolder != null && path != null && path.startsWith(cameraFolder)) { + videoAlbumsSorted.add(0, albumEntry); + cameraAlbumVideoId = bucketId; + } else { + videoAlbumsSorted.add(albumEntry); + } + } + + albumEntry.addPhoto(photoEntry); + } + } + } catch (Exception e) { + FileLog.e("tmessages", e); + } finally { + if (cursor != null) { + try { + cursor.close(); + } catch (Exception e) { + FileLog.e("tmessages", e); + } + } + } + final Integer cameraAlbumIdFinal = cameraAlbumId; + final Integer cameraAlbumVideoIdFinal = cameraAlbumVideoId; AndroidUtilities.runOnUIThread(new Runnable() { @Override public void run() { - NotificationCenter.getInstance().postNotificationName(NotificationCenter.albumsDidLoaded, guid, albumsSorted, cameraAlbumIdFinal); + NotificationCenter.getInstance().postNotificationName(NotificationCenter.albumsDidLoaded, guid, albumsSorted, cameraAlbumIdFinal, videoAlbumsSorted, cameraAlbumVideoIdFinal); } }); } diff --git a/TMessagesProj/src/main/java/org/telegram/android/MessageObject.java b/TMessagesProj/src/main/java/org/telegram/android/MessageObject.java index 525c91eba..45673ea72 100644 --- a/TMessagesProj/src/main/java/org/telegram/android/MessageObject.java +++ b/TMessagesProj/src/main/java/org/telegram/android/MessageObject.java @@ -42,6 +42,7 @@ public class MessageObject { public TLRPC.Message messageOwner; public CharSequence messageText; public CharSequence linkDescription; + public CharSequence caption; public MessageObject replyMessageObject; public int type; public int contentType; @@ -52,7 +53,7 @@ public class MessageObject { public int audioProgressSec; public ArrayList photoThumbs; - private static TextPaint textPaint; + public static TextPaint textPaint; public int lastLineWidth; public int textWidth; public int textHeight; @@ -144,17 +145,35 @@ public class MessageObject { whoUser = MessagesController.getInstance().getUser(message.action.user_id); } if (whoUser != null && fromUser != null) { - if (isOut()) { - messageText = replaceWithLink(LocaleController.getString("ActionYouAddUser", R.string.ActionYouAddUser), "un2", whoUser); - } else if (message.action.user_id == UserConfig.getClientUserId()) { - messageText = replaceWithLink(LocaleController.getString("ActionAddUserYou", R.string.ActionAddUserYou), "un1", fromUser); + if (whoUser.id == fromUser.id) { + if (isOut()) { + messageText = LocaleController.getString("ActionAddUserSelf", R.string.ActionAddUserSelf).replace("un1", LocaleController.getString("FromYou", R.string.FromYou)); + } else { + messageText = replaceWithLink(LocaleController.getString("ActionAddUserSelf", R.string.ActionAddUserSelf), "un1", fromUser); + } } else { - messageText = replaceWithLink(LocaleController.getString("ActionAddUser", R.string.ActionAddUser), "un2", whoUser); - messageText = replaceWithLink(messageText, "un1", fromUser); + if (isOut()) { + messageText = replaceWithLink(LocaleController.getString("ActionYouAddUser", R.string.ActionYouAddUser), "un2", whoUser); + } else if (message.action.user_id == 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); + } } } else { messageText = LocaleController.getString("ActionAddUser", R.string.ActionAddUser).replace("un2", "").replace("un1", ""); } + } 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); + } + } else { + messageText = LocaleController.getString("ActionInviteUser", R.string.ActionInviteUser).replace("un1", ""); + } } else if (message.action instanceof TLRPC.TL_messageActionChatEditPhoto) { if (isOut()) { messageText = LocaleController.getString("ActionYouChangedPhoto", R.string.ActionYouChangedPhoto); @@ -279,7 +298,7 @@ public class MessageObject { messageText = LocaleController.getString("AttachPhoto", R.string.AttachPhoto); } else if (message.media instanceof TLRPC.TL_messageMediaVideo) { messageText = LocaleController.getString("AttachVideo", R.string.AttachVideo); - } else if (message.media instanceof TLRPC.TL_messageMediaGeo) { + } else if (message.media instanceof TLRPC.TL_messageMediaGeo || message.media instanceof TLRPC.TL_messageMediaVenue) { messageText = LocaleController.getString("AttachLocation", R.string.AttachLocation); } else if (message.media instanceof TLRPC.TL_messageMediaContact) { messageText = LocaleController.getString("AttachContact", R.string.AttachContact); @@ -314,7 +333,7 @@ public class MessageObject { contentType = type = 0; } else if (message.media instanceof TLRPC.TL_messageMediaPhoto) { contentType = type = 1; - } else if (message.media instanceof TLRPC.TL_messageMediaGeo) { + } else if (message.media instanceof TLRPC.TL_messageMediaGeo || message.media instanceof TLRPC.TL_messageMediaVenue) { contentType = 1; type = 4; } else if (message.media instanceof TLRPC.TL_messageMediaVideo) { @@ -374,6 +393,7 @@ public class MessageObject { monthKey = String.format("%d_%02d", dateYear, dateMonth); } + generateCaption(); if (generateLayout) { generateLayout(); } @@ -569,6 +589,41 @@ public class MessageObject { } } + public void generateCaption() { + if (caption != null) { + return; + } + if (messageOwner.media != null && messageOwner.media.caption != null && messageOwner.media.caption.length() > 0) { + caption = Emoji.replaceEmoji(messageOwner.media.caption, textPaint.getFontMetricsInt(), AndroidUtilities.dp(20)); + if (containsUrls(caption)) { + try { + Linkify.addLinks((Spannable) caption, Linkify.WEB_URLS); + } catch (Exception e) { + FileLog.e("tmessages", e); + } + addUsernamesAndHashtags(caption); + } + } + } + + private void addUsernamesAndHashtags(CharSequence charSequence) { + try { + Pattern pattern = Pattern.compile("(^|\\s)@[a-zA-Z\\d_]{5,32}|(^|\\s)#[\\w\\.]+"); + Matcher matcher = pattern.matcher(charSequence); + while (matcher.find()) { + int start = matcher.start(); + int end = matcher.end(); + if (charSequence.charAt(start) != '@' && charSequence.charAt(start) != '#') { + start++; + } + URLSpanNoUnderline url = new URLSpanNoUnderline(charSequence.subSequence(start, end).toString()); + ((Spannable) charSequence).setSpan(url, start, end, 0); + } + } catch (Exception e) { + FileLog.e("tmessages", e); + } + } + private void generateLayout() { if (type != 0 || messageOwner.to_id == null || messageText == null || messageText.length() == 0) { return; @@ -579,26 +634,19 @@ public class MessageObject { if (messageText instanceof Spannable && containsUrls(messageText)) { if (messageText.length() < 100) { - Linkify.addLinks((Spannable) messageText, Linkify.WEB_URLS | Linkify.PHONE_NUMBERS); - } else { - Linkify.addLinks((Spannable) messageText, Linkify.WEB_URLS); - } - - try { - Pattern pattern = Pattern.compile("(^|\\s)@[a-zA-Z\\d_]{5,32}|(^|\\s)#[\\w\\.]+"); - Matcher matcher = pattern.matcher(messageText); - while (matcher.find()) { - int start = matcher.start(); - int end = matcher.end(); - if (messageText.charAt(start) != '@' && messageText.charAt(start) != '#') { - start++; - } - URLSpanNoUnderline url = new URLSpanNoUnderline(messageText.subSequence(start, end).toString()); - ((Spannable) messageText).setSpan(url, start, end, 0); + try { + Linkify.addLinks((Spannable) messageText, Linkify.WEB_URLS | Linkify.PHONE_NUMBERS); + } catch (Exception e) { + FileLog.e("tmessages", e); + } + } else { + try { + Linkify.addLinks((Spannable) messageText, Linkify.WEB_URLS); + } catch (Exception e) { + FileLog.e("tmessages", e); } - } catch (Exception e) { - FileLog.e("tmessages", e); } + addUsernamesAndHashtags(messageText); } int maxWidth; @@ -763,10 +811,33 @@ public class MessageObject { return (messageOwner.flags & TLRPC.MESSAGE_FLAG_UNREAD) != 0; } + public boolean isContentUnread() { + return (messageOwner.flags & TLRPC.MESSAGE_FLAG_CONTENT_UNREAD) != 0; + } + public void setIsRead() { messageOwner.flags &= ~TLRPC.MESSAGE_FLAG_UNREAD; } + public int getUnradFlags() { + return getUnreadFlags(messageOwner); + } + + public static int getUnreadFlags(TLRPC.Message message) { + int flags = 0; + if ((message.flags & TLRPC.MESSAGE_FLAG_UNREAD) == 0) { + flags |= 1; + } + if ((message.flags & TLRPC.MESSAGE_FLAG_CONTENT_UNREAD) == 0) { + flags |= 2; + } + return flags; + } + + public void setContentIsRead() { + messageOwner.flags &= ~TLRPC.MESSAGE_FLAG_CONTENT_UNREAD; + } + public int getId() { return messageOwner.id; } @@ -782,18 +853,27 @@ public class MessageObject { messageOwner.media instanceof TLRPC.TL_messageMediaVideo); } - public static void setIsUnread(TLRPC.Message message, boolean unread) { - if (unread) { + public static void setUnreadFlags(TLRPC.Message message, int flag) { + if ((flag & 1) == 0) { message.flags |= TLRPC.MESSAGE_FLAG_UNREAD; } else { message.flags &= ~TLRPC.MESSAGE_FLAG_UNREAD; } + if ((flag & 2) == 0) { + message.flags |= TLRPC.MESSAGE_FLAG_CONTENT_UNREAD; + } else { + message.flags &= ~TLRPC.MESSAGE_FLAG_CONTENT_UNREAD; + } } public static boolean isUnread(TLRPC.Message message) { return (message.flags & TLRPC.MESSAGE_FLAG_UNREAD) != 0; } + public static boolean isContentUnread(TLRPC.Message message) { + return (message.flags & TLRPC.MESSAGE_FLAG_CONTENT_UNREAD) != 0; + } + public static boolean isOut(TLRPC.Message message) { return (message.flags & TLRPC.MESSAGE_FLAG_OUT) != 0; } diff --git a/TMessagesProj/src/main/java/org/telegram/android/MessagesController.java b/TMessagesProj/src/main/java/org/telegram/android/MessagesController.java index 6d2b29972..f4b2437b0 100644 --- a/TMessagesProj/src/main/java/org/telegram/android/MessagesController.java +++ b/TMessagesProj/src/main/java/org/telegram/android/MessagesController.java @@ -22,7 +22,6 @@ import org.telegram.messenger.FileLog; import org.telegram.messenger.R; import org.telegram.messenger.RPCRequest; import org.telegram.messenger.SerializedData; -import org.telegram.messenger.TLClassStore; import org.telegram.messenger.TLObject; import org.telegram.messenger.TLRPC; import org.telegram.messenger.UserConfig; @@ -34,7 +33,6 @@ import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; -import java.util.Locale; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Semaphore; @@ -45,13 +43,16 @@ public class MessagesController implements NotificationCenter.NotificationCenter private ConcurrentHashMap users = new ConcurrentHashMap<>(100, 1.0f, 2); private ConcurrentHashMap usersByUsernames = new ConcurrentHashMap<>(100, 1.0f, 2); + private HashMap exportedChats = new HashMap<>(); + public ArrayList dialogs = new ArrayList<>(); public ArrayList dialogsServerOnly = new ArrayList<>(); public ConcurrentHashMap dialogs_dict = new ConcurrentHashMap<>(100, 1.0f, 2); public HashMap dialogMessage = new HashMap<>(); public ConcurrentHashMap> printingUsers = new ConcurrentHashMap<>(20, 1.0f, 2); public HashMap printingStrings = new HashMap<>(); - public HashMap sendingTypings = new HashMap<>(); + public HashMap printingStringsTypes = new HashMap<>(); + public HashMap> sendingTypings = new HashMap<>(); public ConcurrentHashMap onlinePrivacy = new ConcurrentHashMap<>(20, 1.0f, 2); private int lastPrintingStringCount = 0; @@ -124,6 +125,7 @@ public class MessagesController implements NotificationCenter.NotificationCenter public static class PrintingUser { public long lastTime; public int userId; + public TLRPC.SendMessageAction action; } private static volatile MessagesController Instance = null; @@ -163,9 +165,9 @@ public class MessagesController implements NotificationCenter.NotificationCenter byte[] bytes = Base64.decode(disabledFeaturesString, Base64.DEFAULT); if (bytes != null) { SerializedData data = new SerializedData(bytes); - int count = data.readInt32(); + int count = data.readInt32(false); for (int a = 0; a < count; a++) { - TLRPC.TL_disabledFeature feature = (TLRPC.TL_disabledFeature) TLClassStore.Instance().TLdeserialize(data, data.readInt32()); + TLRPC.TL_disabledFeature feature = TLRPC.TL_disabledFeature.TLdeserialize(data, data.readInt32(false), false); if (feature != null && feature.feature != null && feature.description != null) { disabledFeatures.add(feature); } @@ -372,6 +374,7 @@ public class MessagesController implements NotificationCenter.NotificationCenter SecretChatHelper.getInstance().cleanUp(); dialogs_dict.clear(); + exportedChats.clear(); dialogs.clear(); dialogsServerOnly.clear(); users.clear(); @@ -380,6 +383,7 @@ public class MessagesController implements NotificationCenter.NotificationCenter dialogMessage.clear(); printingUsers.clear(); printingStrings.clear(); + printingStringsTypes.clear(); onlinePrivacy.clear(); totalDialogsCount = 0; lastPrintingStringCount = 0; @@ -463,6 +467,14 @@ public class MessagesController implements NotificationCenter.NotificationCenter return chat; } + public TLRPC.ExportedChatInvite getExportedInvite(int chat_id) { + return exportedChats.get(chat_id); + } + + public void putExportedInvite(int chat_id, TLRPC.TL_chatInviteExported invite) { + exportedChats.put(chat_id, invite); + } + public boolean putUser(TLRPC.User user, boolean fromCache) { if (user == null) { return false; @@ -564,7 +576,11 @@ public class MessagesController implements NotificationCenter.NotificationCenter } public void loadFullChat(final int chat_id, final int classGuid) { - if (loadingFullChats.contains(chat_id) || loadedFullChats.contains(chat_id)) { + loadFullChat(chat_id, classGuid, false); + } + + public void loadFullChat(final int chat_id, final int classGuid, boolean force) { + if (loadingFullChats.contains(chat_id) || !force && loadedFullChats.contains(chat_id)) { return; } loadingFullChats.add(chat_id); @@ -580,12 +596,13 @@ public class MessagesController implements NotificationCenter.NotificationCenter AndroidUtilities.runOnUIThread(new Runnable() { @Override public void run() { - loadingFullChats.remove((Integer)chat_id); + exportedChats.put(chat_id, res.full_chat.exported_invite); + loadingFullChats.remove((Integer) chat_id); loadedFullChats.add(chat_id); putUsers(res.users, false); putChats(res.chats, false); - NotificationCenter.getInstance().postNotificationName(NotificationCenter.chatInfoDidLoaded, chat_id, res.full_chat.participants); + NotificationCenter.getInstance().postNotificationName(NotificationCenter.chatInfoDidLoaded, chat_id, res.full_chat.participants, classGuid); } }); } else { @@ -617,7 +634,7 @@ public class MessagesController implements NotificationCenter.NotificationCenter AndroidUtilities.runOnUIThread(new Runnable() { @Override public void run() { - loadingFullUsers.remove((Integer)user.id); + loadingFullUsers.remove((Integer) user.id); loadedFullUsers.add(user.id); String names = user.first_name + user.last_name + user.username; TLRPC.TL_userFull userFull = (TLRPC.TL_userFull)response; @@ -1275,15 +1292,72 @@ public class MessagesController implements NotificationCenter.NotificationCenter } } - public void updatePrintingStrings() { + private String getUserNameForTyping(TLRPC.User user) { + if (user == null) { + return ""; + } + if (user.first_name != null && user.first_name.length() > 0) { + return user.first_name; + } else if (user.last_name != null && user.last_name.length() > 0) { + return user.last_name; + } + return ""; + } + + private void updatePrintingStrings() { final HashMap newPrintingStrings = new HashMap<>(); + final HashMap newPrintingStringsTypes = new HashMap<>(); ArrayList keys = new ArrayList<>(printingUsers.keySet()); - for (Long key : keys) { - if (key > 0 || key.intValue() == 0) { - newPrintingStrings.put(key, LocaleController.getString("Typing", R.string.Typing)); + for (HashMap.Entry> entry : printingUsers.entrySet()) { + long key = entry.getKey(); + ArrayList arr = entry.getValue(); + + int lower_id = (int) key; + + if (lower_id > 0 || lower_id == 0 || arr.size() == 1) { + PrintingUser pu = arr.get(0); + TLRPC.User user = getUser(pu.userId); + if (user == null) { + return; + } + if (pu.action instanceof TLRPC.TL_sendMessageUploadAudioAction || pu.action instanceof TLRPC.TL_sendMessageRecordAudioAction) { + if (lower_id < 0) { + newPrintingStrings.put(key, LocaleController.formatString("IsRecordingAudio", R.string.IsRecordingAudio, getUserNameForTyping(user))); + } else { + newPrintingStrings.put(key, LocaleController.getString("RecordingAudio", R.string.RecordingAudio)); + } + newPrintingStringsTypes.put(key, 1); + } else if (pu.action instanceof TLRPC.TL_sendMessageUploadVideoAction || pu.action instanceof TLRPC.TL_sendMessageRecordVideoAction) { + if (lower_id < 0) { + newPrintingStrings.put(key, LocaleController.formatString("IsSendingVideo", R.string.IsSendingVideo, getUserNameForTyping(user))); + } else { + newPrintingStrings.put(key, LocaleController.getString("SendingVideoStatus", R.string.SendingVideoStatus)); + } + newPrintingStringsTypes.put(key, 2); + } else if (pu.action instanceof TLRPC.TL_sendMessageUploadDocumentAction) { + if (lower_id < 0) { + newPrintingStrings.put(key, LocaleController.formatString("IsSendingFile", R.string.IsSendingFile, getUserNameForTyping(user))); + } else { + newPrintingStrings.put(key, LocaleController.getString("SendingFile", R.string.SendingFile)); + } + newPrintingStringsTypes.put(key, 2); + } else if (pu.action instanceof TLRPC.TL_sendMessageUploadPhotoAction) { + if (lower_id < 0) { + newPrintingStrings.put(key, LocaleController.formatString("IsSendingPhoto", R.string.IsSendingPhoto, getUserNameForTyping(user))); + } else { + newPrintingStrings.put(key, LocaleController.getString("SendingPhoto", R.string.SendingPhoto)); + } + newPrintingStringsTypes.put(key, 2); + } else { + if (lower_id < 0) { + newPrintingStrings.put(key, String.format("%s %s", getUserNameForTyping(user), LocaleController.getString("IsTyping", R.string.IsTyping))); + } else { + newPrintingStrings.put(key, LocaleController.getString("Typing", R.string.Typing)); + } + newPrintingStringsTypes.put(key, 0); + } } else { - ArrayList arr = printingUsers.get(key); int count = 0; String label = ""; for (PrintingUser pu : arr) { @@ -1292,11 +1366,7 @@ public class MessagesController implements NotificationCenter.NotificationCenter if (label.length() != 0) { label += ", "; } - if (user.first_name != null && user.first_name.length() > 0) { - label += user.first_name; - } else if (user.last_name != null && user.last_name.length() > 0) { - label += user.last_name; - } + label += getUserNameForTyping(user); count++; } if (count == 2) { @@ -1304,15 +1374,12 @@ public class MessagesController implements NotificationCenter.NotificationCenter } } if (label.length() != 0) { - if (count > 1) { - if (arr.size() > 2) { - newPrintingStrings.put(key, String.format("%s %s", label, LocaleController.formatPluralString("AndMoreTyping", arr.size() - 2))); - } else { - newPrintingStrings.put(key, String.format("%s %s", label, LocaleController.getString("AreTyping", R.string.AreTyping))); - } + if (arr.size() > 2) { + newPrintingStrings.put(key, String.format("%s %s", label, LocaleController.formatPluralString("AndMoreTyping", arr.size() - 2))); } else { - newPrintingStrings.put(key, String.format("%s %s", label, LocaleController.getString("IsTyping", R.string.IsTyping))); + newPrintingStrings.put(key, String.format("%s %s", label, LocaleController.getString("AreTyping", R.string.AreTyping))); } + newPrintingStringsTypes.put(key, 0); } } } @@ -1323,21 +1390,30 @@ public class MessagesController implements NotificationCenter.NotificationCenter @Override public void run() { printingStrings = newPrintingStrings; + printingStringsTypes = newPrintingStringsTypes; } }); } - public void cancelTyping(long dialog_id) { - sendingTypings.remove(dialog_id); + public void cancelTyping(int action, long dialog_id) { + HashMap typings = sendingTypings.get(action); + if (typings != null) { + typings.remove(dialog_id); + } } - public void sendTyping(final long dialog_id, int classGuid) { + public void sendTyping(final long dialog_id, final int action, int classGuid) { if (dialog_id == 0) { return; } - if (sendingTypings.get(dialog_id) != null) { + HashMap typings = sendingTypings.get(action); + if (typings != null && typings.get(dialog_id) != null) { return; } + if (typings == null) { + typings = new HashMap<>(); + sendingTypings.put(action, typings); + } int lower_part = (int)dialog_id; int high_id = (int)(dialog_id >> 32); if (lower_part != 0) { @@ -1364,21 +1440,41 @@ public class MessagesController implements NotificationCenter.NotificationCenter return; } } - req.action = new TLRPC.TL_sendMessageTypingAction(); - sendingTypings.put(dialog_id, true); + if (action == 0) { + req.action = new TLRPC.TL_sendMessageTypingAction(); + } else if (action == 1) { + req.action = new TLRPC.TL_sendMessageRecordAudioAction(); + } else if (action == 2) { + req.action = new TLRPC.TL_sendMessageCancelAction(); + } else if (action == 3) { + req.action = new TLRPC.TL_sendMessageUploadDocumentAction(); + } else if (action == 4) { + req.action = new TLRPC.TL_sendMessageUploadPhotoAction(); + } else if (action == 5) { + req.action = new TLRPC.TL_sendMessageUploadVideoAction(); + } + typings.put(dialog_id, true); long reqId = ConnectionsManager.getInstance().performRpc(req, new RPCRequest.RPCRequestDelegate() { @Override public void run(TLObject response, TLRPC.TL_error error) { AndroidUtilities.runOnUIThread(new Runnable() { @Override public void run() { - sendingTypings.remove(dialog_id); + HashMap typings = sendingTypings.get(action); + if (typings != null) { + typings.remove(dialog_id); + } } }); } }, true, RPCRequest.RPCRequestClassGeneric | RPCRequest.RPCRequestClassFailOnServerErrors); - ConnectionsManager.getInstance().bindRequestToGuid(reqId, classGuid); + if (classGuid != 0) { + ConnectionsManager.getInstance().bindRequestToGuid(reqId, classGuid); + } } else { + if (action != 0) { + return; + } TLRPC.EncryptedChat chat = getEncryptedChat(high_id); if (chat.auth_key != null && chat.auth_key.length > 1 && chat instanceof TLRPC.TL_encryptedChat) { TLRPC.TL_messages_setEncryptedTyping req = new TLRPC.TL_messages_setEncryptedTyping(); @@ -1386,14 +1482,24 @@ public class MessagesController implements NotificationCenter.NotificationCenter req.peer.chat_id = chat.id; req.peer.access_hash = chat.access_hash; req.typing = true; - sendingTypings.put(dialog_id, true); + typings.put(dialog_id, true); long reqId = ConnectionsManager.getInstance().performRpc(req, new RPCRequest.RPCRequestDelegate() { @Override public void run(TLObject response, TLRPC.TL_error error) { - sendingTypings.remove(dialog_id); + AndroidUtilities.runOnUIThread(new Runnable() { + @Override + public void run() { + HashMap typings = sendingTypings.get(action); + if (typings != null) { + typings.remove(dialog_id); + } + } + }); } }, true, RPCRequest.RPCRequestClassGeneric | RPCRequest.RPCRequestClassFailOnServerErrors); - ConnectionsManager.getInstance().bindRequestToGuid(reqId, classGuid); + if (classGuid != 0) { + ConnectionsManager.getInstance().bindRequestToGuid(reqId, classGuid); + } } } } @@ -1567,7 +1673,7 @@ public class MessagesController implements NotificationCenter.NotificationCenter currentDialog.unread_count = entry.getValue(); } } - NotificationCenter.getInstance().postNotificationName(NotificationCenter.dialogsNeedReload); + NotificationCenter.getInstance().postNotificationName(NotificationCenter.updateInterfaces, UPDATE_MASK_READ_DIALOG_MESSAGE); NotificationsController.getInstance().processDialogsUpdateRead(dialogsToUpdate); } }); @@ -1803,12 +1909,29 @@ public class MessagesController implements NotificationCenter.NotificationCenter dialogsEndReached = (dialogsRes.dialogs.size() == 0 || dialogsRes.dialogs.size() != count) && !isCache; NotificationCenter.getInstance().postNotificationName(NotificationCenter.dialogsNeedReload); + generateUpdateMessage(); } }); } }); } + public void markMessageContentAsRead(int mid) { + TLRPC.TL_messages_readMessageContents req = new TLRPC.TL_messages_readMessageContents(); + req.id.add(mid); + MessagesStorage.getInstance().markMessagesContentAsRead(req.id); + NotificationCenter.getInstance().postNotificationName(NotificationCenter.messagesReadContent, req.id); + ConnectionsManager.getInstance().performRpc(req, new RPCRequest.RPCRequestDelegate() { + @Override + public void run(TLObject response, TLRPC.TL_error error) { + if (error == null) { + TLRPC.TL_messages_affectedMessages res = (TLRPC.TL_messages_affectedMessages) response; + processNewDifferenceParams(-1, res.pts, -1, res.pts_count); + } + } + }); + } + public void markMessageAsRead(final long dialog_id, final long random_id, int ttl) { if (random_id == 0 || dialog_id == 0 || ttl <= 0) { return; @@ -2030,7 +2153,6 @@ public class MessagesController implements NotificationCenter.NotificationCenter public void run() { putUsers(updates.users, false); putChats(updates.chats, false); - TLRPC.Chat chat = null; if (updates.chats != null && !updates.chats.isEmpty()) { NotificationCenter.getInstance().postNotificationName(NotificationCenter.chatDidCreated, updates.chats.get(0).id); } else { @@ -2213,6 +2335,35 @@ public class MessagesController implements NotificationCenter.NotificationCenter }); } + public void generateUpdateMessage() { + Utilities.stageQueue.postRunnable(new Runnable() { + @Override + public void run() { + try { + String build = LocaleController.getString("updateBuild", R.string.updateBuild); + if (build != null) { + int version = Utilities.parseInt(build); + if (version <= UserConfig.lastUpdateVersion) { + return; + } + UserConfig.lastUpdateVersion = version; + UserConfig.saveConfig(false); + } + TLRPC.TL_updateServiceNotification update = new TLRPC.TL_updateServiceNotification(); + update.message = LocaleController.getString("updateText", R.string.updateText); + update.media = new TLRPC.TL_messageMediaEmpty(); + update.type = "update"; + update.popup = false; + ArrayList updates = new ArrayList<>(); + updates.add(update); + processUpdateArray(updates, null, null); + } catch (Exception e) { + FileLog.e("tmessages", e); + } + } + }); + } + public void registerForPush(final String regid) { if (regid == null || regid.length() == 0 || registeringForPush || UserConfig.getClientUserId() == 0) { return; @@ -2226,7 +2377,10 @@ public class MessagesController implements NotificationCenter.NotificationCenter req.token = regid; req.app_sandbox = false; try { - req.lang_code = LocaleController.getLocaleString(Locale.getDefault()); + req.lang_code = LocaleController.getLocaleString(LocaleController.getInstance().getSystemDefaultLocale()); + if (req.lang_code == null || req.lang_code.length() == 0) { + req.lang_code = "en"; + } req.device_model = Build.MANUFACTURER + Build.MODEL; if (req.device_model == null) { req.device_model = "Android unknown"; @@ -2359,7 +2513,7 @@ public class MessagesController implements NotificationCenter.NotificationCenter } else if (type == 2) { if (updates.qts <= MessagesStorage.lastQtsValue) { return 2; - } else if (MessagesStorage.lastQtsValue + 1 == updates.qts) { + } else if (MessagesStorage.lastQtsValue + updates.updates.size() == updates.qts) { return 0; } else { return 1; @@ -2375,14 +2529,7 @@ public class MessagesController implements NotificationCenter.NotificationCenter Collections.sort(updatesQueue, new Comparator() { @Override public int compare(TLRPC.Updates updates, TLRPC.Updates updates2) { - int seq1 = getUpdateSeq(updates); - int seq2 = getUpdateSeq(updates2); - if (seq1 == seq2) { - return 0; - } else if (seq1 > seq2) { - return 1; - } - return -1; + return AndroidUtilities.compare(getUpdateSeq(updates), getUpdateSeq(updates2)); } }); } else if (type == 1) { @@ -2390,12 +2537,7 @@ public class MessagesController implements NotificationCenter.NotificationCenter Collections.sort(updatesQueue, new Comparator() { @Override public int compare(TLRPC.Updates updates, TLRPC.Updates updates2) { - if (updates.pts == updates2.pts) { - return 0; - } else if (updates.pts > updates2.pts) { - return 1; - } - return -1; + return AndroidUtilities.compare(updates.pts, updates2.pts); } }); } else if (type == 2) { @@ -2403,12 +2545,7 @@ public class MessagesController implements NotificationCenter.NotificationCenter Collections.sort(updatesQueue, new Comparator() { @Override public int compare(TLRPC.Updates updates, TLRPC.Updates updates2) { - if (updates.qts == updates2.qts) { - return 0; - } else if (updates.qts > updates2.qts) { - return 1; - } - return -1; + return AndroidUtilities.compare(updates.qts, updates2.qts); } }); } @@ -2710,6 +2847,17 @@ public class MessagesController implements NotificationCenter.NotificationCenter }); } + private int getUpdateType(TLRPC.Update update) { + if (update instanceof TLRPC.TL_updateNewMessage || update instanceof TLRPC.TL_updateReadMessagesContents || update instanceof TLRPC.TL_updateReadHistoryInbox || + update instanceof TLRPC.TL_updateReadHistoryOutbox || update instanceof TLRPC.TL_updateDeleteMessages) { + return 0; + } else if (update instanceof TLRPC.TL_updateNewEncryptedMessage) { + return 1; + } else { + return 2; + } + } + public void processUpdates(final TLRPC.Updates updates, boolean fromQueue) { boolean needGetDiff = false; boolean needReceivedQueue = false; @@ -2719,11 +2867,12 @@ public class MessagesController implements NotificationCenter.NotificationCenter arr.add(updates.update); processUpdateArray(arr, null, null); } else if (updates instanceof TLRPC.TL_updateShortChatMessage || updates instanceof TLRPC.TL_updateShortMessage) { - TLRPC.User user = getUser(updates.user_id); + final int user_id = updates instanceof TLRPC.TL_updateShortChatMessage ? updates.from_id : updates.user_id; + TLRPC.User user = getUser(user_id); TLRPC.User user2 = null; if (user == null) { - user = MessagesStorage.getInstance().getUserSync(updates.user_id); + user = MessagesStorage.getInstance().getUserSync(user_id); putUser(user, true); } @@ -2763,13 +2912,13 @@ public class MessagesController implements NotificationCenter.NotificationCenter if ((updates.flags & TLRPC.MESSAGE_FLAG_OUT) != 0) { message.from_id = UserConfig.getClientUserId(); } else { - message.from_id = updates.user_id; + message.from_id = user_id; } message.to_id = new TLRPC.TL_peerUser(); - message.to_id.user_id = updates.user_id; - message.dialog_id = updates.user_id; + message.to_id.user_id = user_id; + message.dialog_id = user_id; } else { - message.from_id = updates.user_id; + message.from_id = user_id; message.to_id = new TLRPC.TL_peerChat(); message.to_id.chat_id = updates.chat_id; message.dialog_id = -updates.chat_id; @@ -2798,7 +2947,7 @@ public class MessagesController implements NotificationCenter.NotificationCenter if (printUpdate) { NotificationCenter.getInstance().postNotificationName(NotificationCenter.updateInterfaces, UPDATE_MASK_USER_PRINT); } - updateInterfaceWithMessages(updates.user_id, objArr); + updateInterfaceWithMessages(user_id, objArr); NotificationCenter.getInstance().postNotificationName(NotificationCenter.dialogsNeedReload); } }); @@ -2840,7 +2989,7 @@ public class MessagesController implements NotificationCenter.NotificationCenter if (updatesStartWaitTimePts == 0) { updatesStartWaitTimePts = System.currentTimeMillis(); } - FileLog.e("tmessages", "add short message to queue"); + FileLog.e("tmessages", "add to queue"); updatesQueuePts.add(updates); } else { needGetDiff = true; @@ -2849,56 +2998,92 @@ public class MessagesController implements NotificationCenter.NotificationCenter } } else if (updates instanceof TLRPC.TL_updatesCombined || updates instanceof TLRPC.TL_updates) { MessagesStorage.getInstance().putUsersAndChats(updates.users, updates.chats, true, true); - int lastQtsValue = MessagesStorage.lastQtsValue; + Collections.sort(updates.updates, new Comparator() { + @Override + public int compare(TLRPC.Update lhs, TLRPC.Update rhs) { + int ltype = getUpdateType(lhs); + int rtype = getUpdateType(rhs); + if (ltype != rtype) { + return AndroidUtilities.compare(ltype, rtype); + } else if (ltype == 0) { + return AndroidUtilities.compare(lhs.pts, rhs.pts); + } else if (ltype == 1) { + return AndroidUtilities.compare(lhs.qts, rhs.qts); + } + return 0; + } + }); for (int a = 0; a < updates.updates.size(); a++) { TLRPC.Update update = updates.updates.get(a); - if (update instanceof TLRPC.TL_updateNewMessage || update instanceof TLRPC.TL_updateReadMessages || update instanceof TLRPC.TL_updateReadHistoryInbox || - update instanceof TLRPC.TL_updateReadHistoryOutbox || update instanceof TLRPC.TL_updateDeleteMessages) { + if (getUpdateType(update) == 0) { TLRPC.TL_updates updatesNew = new TLRPC.TL_updates(); updatesNew.updates.add(update); updatesNew.pts = update.pts; updatesNew.pts_count = update.pts_count; - if (MessagesStorage.lastPtsValue + update.pts_count == update.pts) { + for (int b = a + 1; b < updates.updates.size(); b++) { + TLRPC.Update update2 = updates.updates.get(b); + if (getUpdateType(update2) == 0 && updatesNew.pts + update2.pts_count == update2.pts) { + updatesNew.updates.add(update2); + updatesNew.pts = update2.pts; + updatesNew.pts_count += update2.pts_count; + updates.updates.remove(b); + b--; + } else { + break; + } + } + if (MessagesStorage.lastPtsValue + updatesNew.pts_count == updatesNew.pts) { if (!processUpdateArray(updatesNew.updates, updates.users, updates.chats)) { FileLog.e("tmessages", "need get diff inner TL_updates, seq: " + MessagesStorage.lastSeqValue + " " + updates.seq); needGetDiff = true; } else { - MessagesStorage.lastPtsValue = update.pts; + MessagesStorage.lastPtsValue = updatesNew.pts; } - } else if (MessagesStorage.lastPtsValue != update.pts) { - FileLog.e("tmessages", update + " need get diff, pts: " + MessagesStorage.lastPtsValue + " " + update.pts + " count = " + update.pts_count); + } else if (MessagesStorage.lastPtsValue != updatesNew.pts) { + FileLog.e("tmessages", update + " need get diff, pts: " + MessagesStorage.lastPtsValue + " " + updatesNew.pts + " count = " + updatesNew.pts_count); if (gettingDifference || updatesStartWaitTimePts == 0 || updatesStartWaitTimePts != 0 && updatesStartWaitTimePts + 1500 > System.currentTimeMillis()) { if (updatesStartWaitTimePts == 0) { updatesStartWaitTimePts = System.currentTimeMillis(); } - FileLog.e("tmessages", "add short message to queue"); + FileLog.e("tmessages", "add to queue"); updatesQueuePts.add(updatesNew); } else { needGetDiff = true; } } - } else if (update instanceof TLRPC.TL_updateNewEncryptedMessage) { + } else if (getUpdateType(update) == 1) { TLRPC.TL_updates updatesNew = new TLRPC.TL_updates(); updatesNew.updates.add(update); updatesNew.qts = update.qts; - if (MessagesStorage.lastQtsValue == 0 || MessagesStorage.lastQtsValue + 1 == update.qts) { + for (int b = a + 1; b < updates.updates.size(); b++) { + TLRPC.Update update2 = updates.updates.get(b); + if (getUpdateType(update2) == 1 && updatesNew.qts + 1 == update2.qts) { + updatesNew.updates.add(update2); + updatesNew.qts = update2.qts; + updates.updates.remove(b); + b--; + } else { + break; + } + } + if (MessagesStorage.lastQtsValue == 0 || MessagesStorage.lastQtsValue + updatesNew.updates.size() == updatesNew.qts) { processUpdateArray(updatesNew.updates, updates.users, updates.chats); - MessagesStorage.lastQtsValue = update.qts; + MessagesStorage.lastQtsValue = updatesNew.qts; needReceivedQueue = true; - } else if (MessagesStorage.lastPtsValue != update.qts) { - FileLog.e("tmessages", update + " need get diff, qts: " + MessagesStorage.lastQtsValue + " " + update.qts); + } else if (MessagesStorage.lastPtsValue != updatesNew.qts) { + FileLog.e("tmessages", update + " need get diff, qts: " + MessagesStorage.lastQtsValue + " " + updatesNew.qts); if (gettingDifference || updatesStartWaitTimeQts == 0 || updatesStartWaitTimeQts != 0 && updatesStartWaitTimeQts + 1500 > System.currentTimeMillis()) { if (updatesStartWaitTimeQts == 0) { updatesStartWaitTimeQts = System.currentTimeMillis(); } - FileLog.e("tmessages", "add short message to queue"); + FileLog.e("tmessages", "add to queue"); updatesQueueQts.add(updatesNew); } else { needGetDiff = true; } } } else { - continue; + break; } updates.updates.remove(a); a--; @@ -2996,6 +3181,7 @@ public class MessagesController implements NotificationCenter.NotificationCenter final ArrayList messagesArr = new ArrayList<>(); final HashMap markAsReadMessagesInbox = new HashMap<>(); final HashMap markAsReadMessagesOutbox = new HashMap<>(); + final ArrayList markAsReadMessages = new ArrayList<>(); final HashMap markAsReadEncrypted = new HashMap<>(); final ArrayList deletedMessages = new ArrayList<>(); boolean printChanged = false; @@ -3078,8 +3264,8 @@ public class MessagesController implements NotificationCenter.NotificationCenter if (!obj.isOut() && obj.isUnread()) { pushMessages.add(obj); } - } else if (update instanceof TLRPC.TL_updateReadMessages) { - //markAsReadMessages.addAll(update.messages); disabled for now + } else if (update instanceof TLRPC.TL_updateReadMessagesContents) { + markAsReadMessages.addAll(update.messages); } else if (update instanceof TLRPC.TL_updateReadHistoryInbox) { TLRPC.Peer peer = ((TLRPC.TL_updateReadHistoryInbox) update).peer; if (peer.chat_id != 0) { @@ -3097,30 +3283,48 @@ public class MessagesController implements NotificationCenter.NotificationCenter } else if (update instanceof TLRPC.TL_updateDeleteMessages) { deletedMessages.addAll(update.messages); } else if (update instanceof TLRPC.TL_updateUserTyping || update instanceof TLRPC.TL_updateChatUserTyping) { - if (update.action instanceof TLRPC.TL_sendMessageTypingAction && update.user_id != UserConfig.getClientUserId()) { + if (update.user_id != UserConfig.getClientUserId()) { long uid = -update.chat_id; if (uid == 0) { uid = update.user_id; } ArrayList arr = printingUsers.get(uid); - if (arr == null) { - arr = new ArrayList<>(); - printingUsers.put(uid, arr); - } - boolean exist = false; - for (PrintingUser u : arr) { - if (u.userId == update.user_id) { - exist = true; - u.lastTime = currentTime; - break; + if (update.action instanceof TLRPC.TL_sendMessageCancelAction) { + if (arr != null) { + for (int a = 0; a < arr.size(); a++) { + PrintingUser pu = arr.get(a); + if (pu.userId == update.user_id) { + arr.remove(a); + printChanged = true; + break; + } + } + if (arr.isEmpty()) { + printingUsers.remove(uid); + } + } + } else { + if (arr == null) { + arr = new ArrayList<>(); + printingUsers.put(uid, arr); + } + boolean exist = false; + for (PrintingUser u : arr) { + if (u.userId == update.user_id) { + exist = true; + u.lastTime = currentTime; + u.action = update.action; + break; + } + } + if (!exist) { + PrintingUser newUser = new PrintingUser(); + newUser.userId = update.user_id; + newUser.lastTime = currentTime; + newUser.action = update.action; + arr.add(newUser); + printChanged = true; } - } - if (!exist) { - PrintingUser newUser = new PrintingUser(); - newUser.userId = update.user_id; - newUser.lastTime = currentTime; - arr.add(newUser); - printChanged = true; } onlinePrivacy.put(update.user_id, ConnectionsManager.getInstance().getCurrentTime()); } @@ -3181,8 +3385,6 @@ public class MessagesController implements NotificationCenter.NotificationCenter contactsIds.add(-update.user_id); } } - } else if (update instanceof TLRPC.TL_updateActivation) { - //DEPRECATED } else if (update instanceof TLRPC.TL_updateNewAuthorization) { AndroidUtilities.runOnUIThread(new Runnable() { @Override @@ -3247,6 +3449,7 @@ public class MessagesController implements NotificationCenter.NotificationCenter if (u.userId == update.user_id) { exist = true; u.lastTime = currentTime; + u.action = new TLRPC.TL_sendMessageTypingAction(); break; } } @@ -3254,6 +3457,7 @@ public class MessagesController implements NotificationCenter.NotificationCenter PrintingUser newUser = new PrintingUser(); newUser.userId = update.user_id; newUser.lastTime = currentTime; + newUser.action = new TLRPC.TL_sendMessageTypingAction(); arr.add(newUser); printChanged = true; } @@ -3304,7 +3508,7 @@ public class MessagesController implements NotificationCenter.NotificationCenter newMessage.local_id = newMessage.id = UserConfig.getNewMessageId(); UserConfig.saveConfig(false); newMessage.flags = TLRPC.MESSAGE_FLAG_UNREAD; - newMessage.date = update.date; + newMessage.date = ConnectionsManager.getInstance().getCurrentTime(); newMessage.from_id = 777000; newMessage.to_id = new TLRPC.TL_peerUser(); newMessage.to_id.user_id = UserConfig.getClientUserId(); @@ -3403,14 +3607,16 @@ public class MessagesController implements NotificationCenter.NotificationCenter } } else if (update instanceof TLRPC.TL_updateUserName) { if (currentUser != null) { + if (!(currentUser instanceof TLRPC.TL_userContact)) { + currentUser.first_name = update.first_name; + currentUser.last_name = update.last_name; + } if (currentUser.username != null && currentUser.username.length() > 0) { usersByUsernames.remove(currentUser.username); } if (update.username != null && update.username.length() > 0) { usersByUsernames.put(update.username, currentUser); } - currentUser.first_name = update.first_name; - currentUser.last_name = update.last_name; currentUser.username = update.username; } toDbUser.first_name = update.first_name; @@ -3427,7 +3633,7 @@ public class MessagesController implements NotificationCenter.NotificationCenter } else if (update instanceof TLRPC.TL_updateUserPhone) { if (currentUser != null) { currentUser.phone = update.phone; - Utilities.photoBookQueue.postRunnable(new Runnable() { + Utilities.phoneBookQueue.postRunnable(new Runnable() { @Override public void run() { ContactsController.getInstance().addContactToPhoneBook(currentUser, true); @@ -3563,7 +3769,7 @@ public class MessagesController implements NotificationCenter.NotificationCenter } if (!markAsReadEncrypted.isEmpty()) { for (HashMap.Entry entry : markAsReadEncrypted.entrySet()) { - NotificationCenter.getInstance().postNotificationName(NotificationCenter.messagesReadedEncrypted, entry.getKey(), entry.getValue()); + NotificationCenter.getInstance().postNotificationName(NotificationCenter.messagesReadEncrypted, entry.getKey(), entry.getValue()); long dialog_id = (long) (entry.getKey()) << 32; TLRPC.TL_dialog dialog = dialogs_dict.get(dialog_id); if (dialog != null) { @@ -3575,6 +3781,9 @@ public class MessagesController implements NotificationCenter.NotificationCenter } } } + if (!markAsReadMessages.isEmpty()) { + NotificationCenter.getInstance().postNotificationName(NotificationCenter.messagesReadContent, markAsReadMessages); + } if (!deletedMessages.isEmpty()) { NotificationCenter.getInstance().postNotificationName(NotificationCenter.messagesDeleted, deletedMessages); for (Integer id : deletedMessages) { @@ -3601,6 +3810,9 @@ public class MessagesController implements NotificationCenter.NotificationCenter } MessagesStorage.getInstance().markMessagesAsRead(markAsReadMessagesInbox, markAsReadMessagesOutbox, markAsReadEncrypted, true); } + if (!markAsReadMessages.isEmpty()) { + MessagesStorage.getInstance().markMessagesContentAsRead(markAsReadMessages); + } if (!deletedMessages.isEmpty()) { MessagesStorage.getInstance().markMessagesAsDeleted(deletedMessages, true); } @@ -3684,7 +3896,7 @@ public class MessagesController implements NotificationCenter.NotificationCenter NotificationCenter.getInstance().postNotificationName(NotificationCenter.didReceivedNewMessages, uid, messages); for (MessageObject message : messages) { - if (lastMessage == null || (!isEncryptedChat && message.getId() > lastMessage.getId() || isEncryptedChat && message.getId() < lastMessage.getId()) || message.messageOwner.date > lastMessage.messageOwner.date) { + if (lastMessage == null || (!isEncryptedChat && message.getId() > lastMessage.getId() || (isEncryptedChat || message.getId() < 0 && lastMessage.getId() < 0) && message.getId() < lastMessage.getId()) || message.messageOwner.date > lastMessage.messageOwner.date) { lastMessage = message; } } diff --git a/TMessagesProj/src/main/java/org/telegram/android/MessagesStorage.java b/TMessagesProj/src/main/java/org/telegram/android/MessagesStorage.java index d2b7981df..9f445c515 100644 --- a/TMessagesProj/src/main/java/org/telegram/android/MessagesStorage.java +++ b/TMessagesProj/src/main/java/org/telegram/android/MessagesStorage.java @@ -106,7 +106,7 @@ public class MessagesStorage { database.executeFast("CREATE TABLE messages(mid INTEGER PRIMARY KEY, uid INTEGER, read_state INTEGER, send_state INTEGER, date INTEGER, data BLOB, out INTEGER, ttl INTEGER, media INTEGER, replydata 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 dialogs(did INTEGER PRIMARY KEY, date INTEGER, unread_count INTEGER, last_mid INTEGER)").stepThis().dispose(); + database.executeFast("CREATE TABLE dialogs(did INTEGER PRIMARY KEY, date INTEGER, unread_count INTEGER, last_mid INTEGER, inbox_max INTEGER, outbox_max INTEGER)").stepThis().dispose(); database.executeFast("CREATE TABLE chat_settings(uid INTEGER PRIMARY KEY, participants BLOB)").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(); @@ -164,7 +164,7 @@ public class MessagesStorage { database.executeFast("CREATE TABLE keyvalue(id TEXT PRIMARY KEY, value TEXT)").stepThis().dispose(); //version - database.executeFast("PRAGMA user_version = 16").stepThis().dispose(); + database.executeFast("PRAGMA user_version = 17").stepThis().dispose(); } else { try { SQLiteCursor cursor = database.queryFinalized("SELECT seq, pts, date, qts, lsv, sg, pbytes FROM params WHERE id = 1"); @@ -195,7 +195,7 @@ public class MessagesStorage { } } int version = database.executeInt("PRAGMA user_version"); - if (version < 16) { + if (version < 17) { updateDbToLastVersion(version); } } @@ -295,7 +295,7 @@ public class MessagesStorage { if ((length = cursor.byteBufferValue(1, data.buffer)) != 0) { for (int a = 0; a < length / 4; a++) { state.requery(); - state.bindInteger(1, data.readInt32()); + state.bindInteger(1, data.readInt32(false)); state.bindInteger(2, date); state.step(); } @@ -385,6 +385,12 @@ public class MessagesStorage { database.executeFast("PRAGMA user_version = 16").stepThis().dispose(); version = 16; } + if (version == 16 && version < 17) { + database.executeFast("ALTER TABLE dialogs ADD COLUMN inbox_max INTEGER default 0").stepThis().dispose(); + database.executeFast("ALTER TABLE dialogs ADD COLUMN outbox_max INTEGER default 0").stepThis().dispose(); + database.executeFast("PRAGMA user_version = 17").stepThis().dispose(); + version = 17; + } } catch (Exception e) { FileLog.e("tmessages", e); } @@ -523,12 +529,12 @@ public class MessagesStorage { ArrayList chatIds = new ArrayList<>(); ArrayList encryptedChatIds = new ArrayList<>(); - cursor = database.queryFinalized("SELECT read_state, data, send_state, mid, date, uid FROM messages WHERE uid IN (" + ids.toString() + ") AND out = 0 AND read_state = 0 ORDER BY date DESC LIMIT 50"); + cursor = database.queryFinalized("SELECT read_state, data, send_state, mid, date, uid FROM messages WHERE uid IN (" + ids.toString() + ") AND out = 0 AND read_state IN(0,2) ORDER BY date DESC LIMIT 50"); while (cursor.next()) { ByteBufferDesc data = buffersStorage.getFreeBuffer(cursor.byteArrayLength(1)); if (data != null && cursor.byteBufferValue(1, data.buffer) != 0) { - TLRPC.Message message = (TLRPC.Message)TLClassStore.Instance().TLdeserialize(data, data.readInt32()); - MessageObject.setIsUnread(message, cursor.intValue(0) != 1); + TLRPC.Message message = TLRPC.Message.TLdeserialize(data, data.readInt32(false), false); + MessageObject.setUnreadFlags(message, cursor.intValue(0)); message.id = cursor.intValue(3); message.date = cursor.intValue(4); message.dialog_id = cursor.longValue(5); @@ -762,7 +768,7 @@ public class MessagesStorage { while (cursor.next()) { ByteBufferDesc data = buffersStorage.getFreeBuffer(cursor.byteArrayLength(0)); if (data != null && cursor.byteBufferValue(0, data.buffer) != 0) { - TLRPC.WallPaper wallPaper = (TLRPC.WallPaper) TLClassStore.Instance().TLdeserialize(data, data.readInt32()); + TLRPC.WallPaper wallPaper = TLRPC.WallPaper.TLdeserialize(data, data.readInt32(false), false); wallPapers.add(wallPaper); } buffersStorage.reuseFreeBuffer(data); @@ -876,7 +882,7 @@ public class MessagesStorage { while (cursor.next()) { ByteBufferDesc data = buffersStorage.getFreeBuffer(cursor.byteArrayLength(0)); if (data != null && cursor.byteBufferValue(0, data.buffer) != 0) { - TLRPC.Message message = (TLRPC.Message) TLClassStore.Instance().TLdeserialize(data, data.readInt32()); + TLRPC.Message message = TLRPC.Message.TLdeserialize(data, data.readInt32(false), false); if (message == null || message.media == null) { continue; } @@ -950,7 +956,7 @@ public class MessagesStorage { while (cursor.next()) { ByteBufferDesc data = buffersStorage.getFreeBuffer(cursor.byteArrayLength(0)); if (data != null && cursor.byteBufferValue(0, data.buffer) != 0) { - TLRPC.Photo photo = (TLRPC.Photo)TLClassStore.Instance().TLdeserialize(data, data.readInt32()); + TLRPC.Photo photo = TLRPC.Photo.TLdeserialize(data, data.readInt32(false), false); res.photos.add(photo); } buffersStorage.reuseFreeBuffer(data); @@ -1065,7 +1071,7 @@ public class MessagesStorage { StringBuilder mids = new StringBuilder(); SQLiteCursor cursor = null; if (random_ids == null) { - cursor = database.queryFinalized(String.format(Locale.US, "SELECT mid, ttl FROM messages WHERE uid = %d AND out = %d AND read_state = 1 AND ttl > 0 AND date <= %d AND send_state = 0 AND media != 1", ((long) chat_id) << 32, isOut, time)); + cursor = database.queryFinalized(String.format(Locale.US, "SELECT mid, ttl FROM messages WHERE uid = %d AND out = %d AND read_state != 0 AND ttl > 0 AND date <= %d AND send_state = 0 AND media != 1", ((long) chat_id) << 32, isOut, time)); } else { String ids = TextUtils.join(",", random_ids); cursor = database.queryFinalized(String.format(Locale.US, "SELECT m.mid, m.ttl FROM messages as m INNER JOIN randoms as r ON m.mid = r.mid WHERE r.random_id IN (%s)", ids)); @@ -1147,7 +1153,7 @@ public class MessagesStorage { cursor.dispose(); } else if (inbox != null && !inbox.isEmpty()) { for (HashMap.Entry entry : inbox.entrySet()) { - SQLiteCursor cursor = database.queryFinalized(String.format(Locale.US, "SELECT COUNT(mid) FROM messages WHERE uid = %d AND mid <= %d AND read_state = 0 AND out = 0", entry.getKey(), entry.getValue())); + SQLiteCursor cursor = database.queryFinalized(String.format(Locale.US, "SELECT COUNT(mid) FROM messages WHERE uid = %d AND mid <= %d AND read_state IN(0,2) AND out = 0", entry.getKey(), entry.getValue())); if (cursor.next()) { int count = cursor.intValue(0); if (count == 0) { @@ -1255,7 +1261,7 @@ public class MessagesStorage { if (cursor.next()) { ByteBufferDesc data = buffersStorage.getFreeBuffer(cursor.byteArrayLength(0)); if (data != null && cursor.byteBufferValue(0, data.buffer) != 0) { - info = (TLRPC.ChatParticipants)TLClassStore.Instance().TLdeserialize(data, data.readInt32()); + info = TLRPC.ChatParticipants.TLdeserialize(data, data.readInt32(false), false); } buffersStorage.reuseFreeBuffer(data); } @@ -1318,7 +1324,7 @@ public class MessagesStorage { if (cursor.next()) { ByteBufferDesc data = buffersStorage.getFreeBuffer(cursor.byteArrayLength(0)); if (data != null && cursor.byteBufferValue(0, data.buffer) != 0) { - info = (TLRPC.ChatParticipants)TLClassStore.Instance().TLdeserialize(data, data.readInt32()); + info = TLRPC.ChatParticipants.TLdeserialize(data, data.readInt32(false), false); } buffersStorage.reuseFreeBuffer(data); } @@ -1383,14 +1389,14 @@ public class MessagesStorage { int lower_id = (int)dialog_id; if (lower_id != 0) { - state = database.executeFast("UPDATE messages SET read_state = 1 WHERE uid = ? AND mid <= ? AND read_state = 0 AND out = 0"); + state = database.executeFast("UPDATE messages SET read_state = read_state | 1 WHERE uid = ? AND mid <= ? AND read_state IN(0,2) AND out = 0"); state.requery(); state.bindLong(1, dialog_id); state.bindInteger(2, max_id); state.step(); state.dispose(); } else { - state = database.executeFast("UPDATE messages SET read_state = 1 WHERE uid = ? AND date <= ? AND read_state = 0 AND out = 0"); + state = database.executeFast("UPDATE messages SET read_state = read_state | 1 WHERE uid = ? AND date <= ? AND read_state IN(0,2) AND out = 0"); state.requery(); state.bindLong(1, dialog_id); state.bindInteger(2, max_date); @@ -1613,9 +1619,9 @@ public class MessagesStorage { while (cursor.next()) { ByteBufferDesc data = buffersStorage.getFreeBuffer(cursor.byteArrayLength(1)); if (data != null && cursor.byteBufferValue(1, data.buffer) != 0) { - TLRPC.Message message = (TLRPC.Message)TLClassStore.Instance().TLdeserialize(data, data.readInt32()); + TLRPC.Message message = TLRPC.Message.TLdeserialize(data, data.readInt32(false), false); if (!messageHashMap.containsKey(message.id)) { - MessageObject.setIsUnread(message, cursor.intValue(0) != 1); + MessageObject.setUnreadFlags(message, cursor.intValue(0)); message.id = cursor.intValue(3); message.date = cursor.intValue(4); if (!cursor.isNull(5)) { @@ -1798,14 +1804,14 @@ public class MessagesStorage { } cursor.dispose(); - cursor = database.queryFinalized(String.format(Locale.US, "SELECT min(mid), max(date) FROM messages WHERE uid = %d AND out = 0 AND read_state = 0 AND mid > 0", dialog_id)); + cursor = database.queryFinalized(String.format(Locale.US, "SELECT min(mid), max(date) FROM messages WHERE uid = %d AND out = 0 AND read_state IN(0,2) AND mid > 0", dialog_id)); if (cursor.next()) { min_unread_id = cursor.intValue(0); max_unread_date = cursor.intValue(1); } cursor.dispose(); if (min_unread_id != 0) { - cursor = database.queryFinalized(String.format(Locale.US, "SELECT COUNT(*) FROM messages WHERE uid = %d AND mid >= %d AND out = 0 AND read_state = 0", dialog_id, min_unread_id)); + cursor = database.queryFinalized(String.format(Locale.US, "SELECT COUNT(*) FROM messages WHERE uid = %d AND mid >= %d AND out = 0 AND read_state IN(0,2)", dialog_id, min_unread_id)); if (cursor.next()) { count_unread = cursor.intValue(0); } @@ -1843,14 +1849,14 @@ public class MessagesStorage { } cursor.dispose(); - cursor = database.queryFinalized(String.format(Locale.US, "SELECT max(mid), max(date) FROM messages WHERE uid = %d AND out = 0 AND read_state = 0 AND mid < 0", dialog_id)); + cursor = database.queryFinalized(String.format(Locale.US, "SELECT max(mid), max(date) FROM messages WHERE uid = %d AND out = 0 AND read_state IN(0,2) AND mid < 0", dialog_id)); if (cursor.next()) { min_unread_id = cursor.intValue(0); max_unread_date = cursor.intValue(1); } cursor.dispose(); if (min_unread_id != 0) { - cursor = database.queryFinalized(String.format(Locale.US, "SELECT COUNT(*) FROM messages WHERE uid = %d AND mid <= %d AND out = 0 AND read_state = 0", dialog_id, min_unread_id)); + cursor = database.queryFinalized(String.format(Locale.US, "SELECT COUNT(*) FROM messages WHERE uid = %d AND mid <= %d AND out = 0 AND read_state IN(0,2)", dialog_id, min_unread_id)); if (cursor.next()) { count_unread = cursor.intValue(0); } @@ -1876,8 +1882,8 @@ public class MessagesStorage { while (cursor.next()) { ByteBufferDesc data = buffersStorage.getFreeBuffer(cursor.byteArrayLength(1)); if (data != null && cursor.byteBufferValue(1, data.buffer) != 0) { - TLRPC.Message message = (TLRPC.Message) TLClassStore.Instance().TLdeserialize(data, data.readInt32()); - MessageObject.setIsUnread(message, cursor.intValue(0) != 1); + TLRPC.Message message = TLRPC.Message.TLdeserialize(data, data.readInt32(false), false); + MessageObject.setUnreadFlags(message, cursor.intValue(0)); message.id = cursor.intValue(3); message.date = cursor.intValue(4); message.dialog_id = dialog_id; @@ -1900,7 +1906,7 @@ public class MessagesStorage { if (!cursor.isNull(6)) { ByteBufferDesc data2 = buffersStorage.getFreeBuffer(cursor.byteArrayLength(6)); if (data2 != null && cursor.byteBufferValue(6, data2.buffer) != 0) { - message.replyMessage = (TLRPC.Message) TLClassStore.Instance().TLdeserialize(data2, data2.readInt32()); + message.replyMessage = TLRPC.Message.TLdeserialize(data2, data2.readInt32(false), false); if (message.replyMessage != null) { fromUser.add(message.replyMessage.from_id); if (message.replyMessage.action != null && message.replyMessage.action.user_id != 0) { @@ -1987,7 +1993,7 @@ public class MessagesStorage { while (cursor.next()) { ByteBufferDesc data = buffersStorage.getFreeBuffer(cursor.byteArrayLength(0)); if (data != null && cursor.byteBufferValue(0, data.buffer) != 0) { - TLRPC.Message message = (TLRPC.Message) TLClassStore.Instance().TLdeserialize(data, data.readInt32()); + 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; @@ -2091,7 +2097,7 @@ public class MessagesStorage { if (cursor.next()) { ByteBufferDesc data = buffersStorage.getFreeBuffer(cursor.byteArrayLength(0)); if (data != null && cursor.byteBufferValue(0, data.buffer) != 0) { - TLObject file = TLClassStore.Instance().TLdeserialize(data, data.readInt32()); + TLObject file = TLClassStore.Instance().TLdeserialize(data, data.readInt32(false), false); if (file != null) { result.add(file); } @@ -2374,11 +2380,13 @@ public class MessagesStorage { buffersStorage.reuseFreeBuffer(data5); if (dialog != null) { - state = database.executeFast("REPLACE INTO dialogs(did, date, unread_count, last_mid) VALUES(?, ?, ?, ?)"); + state = database.executeFast("REPLACE INTO dialogs(did, date, unread_count, last_mid, inbox_max, outbox_max) VALUES(?, ?, ?, ?, ?, ?)"); state.bindLong(1, dialog.id); state.bindInteger(2, dialog.last_message_date); state.bindInteger(3, dialog.unread_count); state.bindInteger(4, dialog.top_message); + state.bindInteger(5, dialog.read_inbox_max_id); + state.bindInteger(6, 0); state.step(); state.dispose(); } @@ -2469,7 +2477,7 @@ public class MessagesStorage { try { ByteBufferDesc data = buffersStorage.getFreeBuffer(cursor.byteArrayLength(0)); if (data != null && cursor.byteBufferValue(0, data.buffer) != 0) { - TLRPC.User user = (TLRPC.User)TLClassStore.Instance().TLdeserialize(data, data.readInt32()); + TLRPC.User user = TLRPC.User.TLdeserialize(data, data.readInt32(false), false); if (user != null) { if (user.status != null) { user.status.expires = cursor.intValue(1); @@ -2494,7 +2502,7 @@ public class MessagesStorage { try { ByteBufferDesc data = buffersStorage.getFreeBuffer(cursor.byteArrayLength(0)); if (data != null && cursor.byteBufferValue(0, data.buffer) != 0) { - TLRPC.Chat chat = (TLRPC.Chat)TLClassStore.Instance().TLdeserialize(data, data.readInt32()); + TLRPC.Chat chat = TLRPC.Chat.TLdeserialize(data, data.readInt32(false), false); if (chat != null) { result.add(chat); } @@ -2511,13 +2519,12 @@ public class MessagesStorage { if (chatsToLoad == null || chatsToLoad.length() == 0 || result == null) { return; } - //use_count INTEGER, exchange_id INTEGER, key_date INTEGER, fprint INTEGER, fauthkey BLOB SQLiteCursor cursor = database.queryFinalized(String.format(Locale.US, "SELECT data, user, g, authkey, ttl, layer, seq_in, seq_out, use_count, exchange_id, key_date, fprint, fauthkey, khash FROM enc_chats WHERE uid IN(%s)", chatsToLoad)); while (cursor.next()) { try { ByteBufferDesc data = buffersStorage.getFreeBuffer(cursor.byteArrayLength(0)); if (data != null && cursor.byteBufferValue(0, data.buffer) != 0) { - TLRPC.EncryptedChat chat = (TLRPC.EncryptedChat)TLClassStore.Instance().TLdeserialize(data, data.readInt32()); + TLRPC.EncryptedChat chat = TLRPC.EncryptedChat.TLdeserialize(data, data.readInt32(false), false); if (chat != null) { chat.user_id = cursor.intValue(1); if (usersToLoad != null && !usersToLoad.contains(chat.user_id)) { @@ -2634,7 +2641,7 @@ public class MessagesStorage { downloadObject.id = cursor.longValue(0); ByteBufferDesc data = buffersStorage.getFreeBuffer(cursor.byteArrayLength(2)); if (data != null && cursor.byteBufferValue(2, data.buffer) != 0) { - downloadObject.object = TLClassStore.Instance().TLdeserialize(data, data.readInt32()); + downloadObject.object = TLClassStore.Instance().TLdeserialize(data, data.readInt32(false), false); } buffersStorage.reuseFreeBuffer(data); objects.add(downloadObject); @@ -2691,7 +2698,7 @@ public class MessagesStorage { int mid = cursor.intValue(0); ByteBufferDesc data = buffersStorage.getFreeBuffer(cursor.byteArrayLength(1)); if (data != null && cursor.byteBufferValue(1, data.buffer) != 0) { - TLRPC.Message message = (TLRPC.Message)TLClassStore.Instance().TLdeserialize(data, data.readInt32()); + TLRPC.Message message = TLRPC.Message.TLdeserialize(data, data.readInt32(false), false); if (message.media instanceof TLRPC.TL_messageMediaWebPage) { message.id = mid; message.media.webpage = webPages.get(message.media.webpage.id); @@ -2865,7 +2872,7 @@ public class MessagesStorage { state.bindInteger(1, messageId); state.bindLong(2, dialog_id); - state.bindInteger(3, (MessageObject.isUnread(message) ? 0 : 1)); + state.bindInteger(3, MessageObject.getUnreadFlags(message)); state.bindInteger(4, message.send_state); state.bindInteger(5, message.date); state.bindByteBuffer(6, data.buffer); @@ -2954,7 +2961,7 @@ public class MessagesStorage { state4.dispose(); state5.dispose(); - state = database.executeFast("REPLACE INTO dialogs(did, date, unread_count, last_mid) VALUES(?, ?, ?, ?)"); + state = database.executeFast("REPLACE INTO dialogs(did, date, unread_count, last_mid, inbox_max, outbox_max) VALUES(?, ?, ?, ?, ?, ?)"); for (HashMap.Entry pair : messagesMap.entrySet()) { Long key = pair.getKey(); @@ -2987,6 +2994,8 @@ public class MessagesStorage { } state.bindInteger(3, old_unread_count + unread_count); state.bindInteger(4, messageId); + state.bindInteger(5, 0); + state.bindInteger(6, 0); state.step(); } state.dispose(); @@ -3294,8 +3303,10 @@ public class MessagesStorage { TLRPC.User updateUser = usersDict.get(user.id); if (updateUser != null) { if (updateUser.first_name != null && updateUser.last_name != null) { - user.first_name = updateUser.first_name; - user.last_name = updateUser.last_name; + if (!(user instanceof TLRPC.TL_userContact)) { + user.first_name = updateUser.first_name; + user.last_name = updateUser.last_name; + } user.username = updateUser.username; } else if (updateUser.photo != null) { user.photo = updateUser.photo; @@ -3337,25 +3348,22 @@ public class MessagesStorage { } private void markMessagesAsReadInternal(HashMap inbox, HashMap outbox, HashMap encryptedMessages) { - if (Thread.currentThread().getId() != storageQueue.getId()) { - throw new RuntimeException("wrong db thread"); - } try { if (inbox != null) { for (HashMap.Entry entry : inbox.entrySet()) { - database.executeFast(String.format(Locale.US, "UPDATE messages SET read_state = 1 WHERE uid = %d AND mid <= %d AND read_state = 0 AND out = 0", entry.getKey(), entry.getValue())).stepThis().dispose(); + database.executeFast(String.format(Locale.US, "UPDATE messages SET read_state = read_state | 1 WHERE uid = %d AND mid <= %d AND read_state IN(0,2) AND out = 0", entry.getKey(), entry.getValue())).stepThis().dispose(); } } if (outbox != null) { for (HashMap.Entry entry : outbox.entrySet()) { - database.executeFast(String.format(Locale.US, "UPDATE messages SET read_state = 1 WHERE uid = %d AND mid <= %d AND read_state = 0 AND out = 1", entry.getKey(), entry.getValue())).stepThis().dispose(); + database.executeFast(String.format(Locale.US, "UPDATE messages SET read_state = read_state | 1 WHERE uid = %d AND mid <= %d AND read_state IN(0,2) AND out = 1", entry.getKey(), entry.getValue())).stepThis().dispose(); } } if (encryptedMessages != null && !encryptedMessages.isEmpty()) { for (HashMap.Entry entry : encryptedMessages.entrySet()) { long dialog_id = ((long)entry.getKey()) << 32; int max_date = entry.getValue(); - SQLitePreparedStatement state = database.executeFast("UPDATE messages SET read_state = 1 WHERE uid = ? AND date <= ? AND read_state = 0 AND out = 1"); + SQLitePreparedStatement state = database.executeFast("UPDATE messages SET read_state = read_state | 1 WHERE uid = ? AND date <= ? AND read_state IN(0,2) AND out = 1"); state.requery(); state.bindLong(1, dialog_id); state.bindInteger(2, max_date); @@ -3368,6 +3376,22 @@ public class MessagesStorage { } } + public void markMessagesContentAsRead(final ArrayList mids) { + if (mids == null || mids.isEmpty()) { + return; + } + storageQueue.postRunnable(new Runnable() { + @Override + public void run() { + try { + database.executeFast(String.format(Locale.US, "UPDATE messages SET read_state = read_state | 2 WHERE mid IN (%s)", TextUtils.join(",", mids))).stepThis().dispose(); + } catch (Exception e) { + FileLog.e("tmessages", e); + } + } + }); + } + public void markMessagesAsRead(final HashMap inbox, final HashMap outbox, final HashMap encryptedMessages, boolean useQueue) { if (useQueue) { storageQueue.postRunnable(new Runnable() { @@ -3433,7 +3457,7 @@ public class MessagesStorage { } ByteBufferDesc data = buffersStorage.getFreeBuffer(cursor.byteArrayLength(1)); if (data != null && cursor.byteBufferValue(1, data.buffer) != 0) { - TLRPC.Message message = (TLRPC.Message)TLClassStore.Instance().TLdeserialize(data, data.readInt32()); + TLRPC.Message message = TLRPC.Message.TLdeserialize(data, data.readInt32(false), false); if (message == null || message.media == null) { continue; } @@ -3527,8 +3551,8 @@ public class MessagesStorage { ByteBufferDesc data = buffersStorage.getFreeBuffer(cursor.byteArrayLength(4)); if (data != null && cursor.byteBufferValue(4, data.buffer) != 0) { - TLRPC.Message message = (TLRPC.Message)TLClassStore.Instance().TLdeserialize(data, data.readInt32()); - MessageObject.setIsUnread(message, cursor.intValue(5) != 1); + TLRPC.Message message = TLRPC.Message.TLdeserialize(data, data.readInt32(false), false); + MessageObject.setUnreadFlags(message, cursor.intValue(5)); message.id = cursor.intValue(6); message.send_state = cursor.intValue(7); int date = cursor.intValue(8); @@ -3667,7 +3691,7 @@ public class MessagesStorage { message.serializeToStream(data); state.bindInteger(1, message.id); state.bindLong(2, dialog_id); - state.bindInteger(3, (MessageObject.isUnread(message) ? 0 : 1)); + state.bindInteger(3, MessageObject.getUnreadFlags(message)); state.bindInteger(4, message.send_state); state.bindInteger(5, message.date); state.bindByteBuffer(6, data.buffer); @@ -3732,9 +3756,9 @@ public class MessagesStorage { ByteBufferDesc data = buffersStorage.getFreeBuffer(cursor.byteArrayLength(4)); if (data != null && cursor.byteBufferValue(4, data.buffer) != 0) { - TLRPC.Message message = (TLRPC.Message)TLClassStore.Instance().TLdeserialize(data, data.readInt32()); + TLRPC.Message message = TLRPC.Message.TLdeserialize(data, data.readInt32(false), false); if (message != null) { - MessageObject.setIsUnread(message, cursor.intValue(5) != 1); + MessageObject.setUnreadFlags(message, cursor.intValue(5)); message.id = cursor.intValue(6); int date = cursor.intValue(9); if (date != 0) { @@ -3831,7 +3855,7 @@ public class MessagesStorage { if (!dialogs.dialogs.isEmpty()) { SQLitePreparedStatement state = database.executeFast("REPLACE INTO messages VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, NULL)"); - SQLitePreparedStatement state2 = database.executeFast("REPLACE INTO dialogs(did, date, unread_count, last_mid) VALUES(?, ?, ?, ?)"); + SQLitePreparedStatement state2 = database.executeFast("REPLACE INTO dialogs(did, date, unread_count, last_mid, inbox_max, outbox_max) VALUES(?, ?, ?, ?, ?, ?)"); SQLitePreparedStatement state3 = database.executeFast("REPLACE INTO media_v2 VALUES(?, ?, ?, ?, ?)"); SQLitePreparedStatement state4 = database.executeFast("REPLACE INTO dialog_settings VALUES(?, ?)"); @@ -3850,7 +3874,7 @@ public class MessagesStorage { state.bindInteger(1, message.id); state.bindInteger(2, uid); - state.bindInteger(3, (MessageObject.isUnread(message) ? 0 : 1)); + state.bindInteger(3, MessageObject.getUnreadFlags(message)); state.bindInteger(4, message.send_state); state.bindInteger(5, message.date); state.bindByteBuffer(6, data.buffer); @@ -3863,6 +3887,8 @@ public class MessagesStorage { state2.bindInteger(2, message.date); state2.bindInteger(3, dialog.unread_count); state2.bindInteger(4, dialog.top_message); + state2.bindInteger(5, dialog.read_inbox_max_id); + state2.bindInteger(6, 0); state2.step(); state4.bindLong(1, uid); diff --git a/TMessagesProj/src/main/java/org/telegram/android/NativeLoader.java b/TMessagesProj/src/main/java/org/telegram/android/NativeLoader.java index 6eab0a11b..7ea11323e 100644 --- a/TMessagesProj/src/main/java/org/telegram/android/NativeLoader.java +++ b/TMessagesProj/src/main/java/org/telegram/android/NativeLoader.java @@ -23,7 +23,7 @@ import java.util.zip.ZipFile; public class NativeLoader { - private final static int LIB_VERSION = 7; + private final static int LIB_VERSION = 8; 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"; diff --git a/TMessagesProj/src/main/java/org/telegram/android/NotificationCenter.java b/TMessagesProj/src/main/java/org/telegram/android/NotificationCenter.java index 962de6977..a75a4f64e 100644 --- a/TMessagesProj/src/main/java/org/telegram/android/NotificationCenter.java +++ b/TMessagesProj/src/main/java/org/telegram/android/NotificationCenter.java @@ -32,7 +32,7 @@ public class NotificationCenter { public static final int mediaDidLoaded = totalEvents++; public static final int mediaCountDidLoaded = totalEvents++; public static final int encryptedChatUpdated = totalEvents++; - public static final int messagesReadedEncrypted = totalEvents++; + public static final int messagesReadEncrypted = totalEvents++; public static final int encryptedChatCreated = totalEvents++; public static final int userPhotosLoaded = totalEvents++; public static final int removeAllMessagesFromDialog = totalEvents++; @@ -56,6 +56,9 @@ public class NotificationCenter { public static final int newSessionReceived = totalEvents++; public static final int didReceivedWebpages = totalEvents++; public static final int didReceivedWebpagesInUpdates = totalEvents++; + public static final int stickersDidLoaded = totalEvents++; + public static final int didReplacedPhotoInMemCache = totalEvents++; + public static final int messagesReadContent = totalEvents++; public static final int httpFileDidLoaded = totalEvents++; public static final int httpFileDidFailedLoad = totalEvents++; diff --git a/TMessagesProj/src/main/java/org/telegram/android/NotificationsController.java b/TMessagesProj/src/main/java/org/telegram/android/NotificationsController.java index 1f31a687b..2192b3663 100644 --- a/TMessagesProj/src/main/java/org/telegram/android/NotificationsController.java +++ b/TMessagesProj/src/main/java/org/telegram/android/NotificationsController.java @@ -17,10 +17,10 @@ import android.content.Intent; import android.content.SharedPreferences; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; -import android.content.res.AssetFileDescriptor; +import android.graphics.Point; import android.graphics.drawable.BitmapDrawable; import android.media.AudioManager; -import android.media.MediaPlayer; +import android.media.SoundPool; import android.net.Uri; import android.os.Build; import android.os.SystemClock; @@ -54,7 +54,9 @@ public class NotificationsController { private DispatchQueue notificationsQueue = new DispatchQueue("notificationsQueue"); private ArrayList pushMessages = new ArrayList<>(); + private ArrayList delayedPushMessages = new ArrayList<>(); private HashMap pushMessagesDict = new HashMap<>(); + private HashMap smartNotificationsDialogs = new HashMap<>(); private NotificationManagerCompat notificationManager = null; private HashMap pushDialogs = new HashMap<>(); private HashMap wearNoticationsIds = new HashMap<>(); @@ -67,10 +69,14 @@ public class NotificationsController { private boolean notifyCheck = false; private int lastOnlineFromOtherDevice = 0; private boolean inChatSoundEnabled = true; + private int lastBadgeCount; private long lastSoundPlay; - private MediaPlayer mediaPlayerIn; - private MediaPlayer mediaPlayerOut; + //private MediaPlayer mediaPlayerIn; + //private MediaPlayer mediaPlayerOut; + private SoundPool soundPool; + private int soundIn; + private int soundOut; protected AudioManager audioManager; private static volatile NotificationsController Instance = null; @@ -110,6 +116,7 @@ public class NotificationsController { popupMessages.clear(); wearNoticationsIds.clear(); notifyCheck = false; + lastBadgeCount = 0; SharedPreferences preferences = ApplicationLoader.applicationContext.getSharedPreferences("Notifications", Context.MODE_PRIVATE); SharedPreferences.Editor editor = preferences.edit(); editor.clear(); @@ -187,7 +194,7 @@ public class NotificationsController { msg = LocaleController.formatString("NotificationMessageVideo", R.string.NotificationMessageVideo, ContactsController.formatName(user.first_name, user.last_name)); } else if (messageObject.messageOwner.media instanceof TLRPC.TL_messageMediaContact) { msg = LocaleController.formatString("NotificationMessageContact", R.string.NotificationMessageContact, ContactsController.formatName(user.first_name, user.last_name)); - } else if (messageObject.messageOwner.media instanceof TLRPC.TL_messageMediaGeo) { + } else if (messageObject.messageOwner.media instanceof TLRPC.TL_messageMediaGeo || messageObject.messageOwner.media instanceof TLRPC.TL_messageMediaVenue) { msg = LocaleController.formatString("NotificationMessageMap", R.string.NotificationMessageMap, ContactsController.formatName(user.first_name, user.last_name)); } else if (messageObject.messageOwner.media instanceof TLRPC.TL_messageMediaDocument) { if (messageObject.isSticker()) { @@ -214,8 +221,14 @@ public class NotificationsController { if (u2 == null) { return null; } - msg = LocaleController.formatString("NotificationGroupAddMember", R.string.NotificationGroupAddMember, ContactsController.formatName(user.first_name, user.last_name), chat.title, ContactsController.formatName(u2.first_name, u2.last_name)); + if (user.id == u2.id) { + msg = LocaleController.formatString("NotificationGroupAddSelf", R.string.NotificationGroupAddSelf, ContactsController.formatName(user.first_name, user.last_name), chat.title); + } else { + msg = LocaleController.formatString("NotificationGroupAddMember", R.string.NotificationGroupAddMember, ContactsController.formatName(user.first_name, user.last_name), chat.title, ContactsController.formatName(u2.first_name, u2.last_name)); + } } + } else if (messageObject.messageOwner.action instanceof TLRPC.TL_messageActionChatJoinedByLink) { + msg = LocaleController.formatString("NotificationInvitedToGroupByLink", R.string.NotificationInvitedToGroupByLink, ContactsController.formatName(user.first_name, user.last_name), chat.title); } else if (messageObject.messageOwner.action instanceof TLRPC.TL_messageActionChatEditTitle) { msg = LocaleController.formatString("NotificationEditedGroupName", R.string.NotificationEditedGroupName, ContactsController.formatName(user.first_name, user.last_name), messageObject.messageOwner.action.title); } else if (messageObject.messageOwner.action instanceof TLRPC.TL_messageActionChatEditPhoto || messageObject.messageOwner.action instanceof TLRPC.TL_messageActionChatDeletePhoto) { @@ -248,7 +261,7 @@ public class NotificationsController { msg = LocaleController.formatString("NotificationMessageGroupVideo", R.string.NotificationMessageGroupVideo, ContactsController.formatName(user.first_name, user.last_name), chat.title); } else if (messageObject.messageOwner.media instanceof TLRPC.TL_messageMediaContact) { msg = LocaleController.formatString("NotificationMessageGroupContact", R.string.NotificationMessageGroupContact, ContactsController.formatName(user.first_name, user.last_name), chat.title); - } else if (messageObject.messageOwner.media instanceof TLRPC.TL_messageMediaGeo) { + } else if (messageObject.messageOwner.media instanceof TLRPC.TL_messageMediaGeo || messageObject.messageOwner.media instanceof TLRPC.TL_messageMediaVenue) { msg = LocaleController.formatString("NotificationMessageGroupMap", R.string.NotificationMessageGroupMap, ContactsController.formatName(user.first_name, user.last_name), chat.title); } else if (messageObject.messageOwner.media instanceof TLRPC.TL_messageMediaDocument) { if (messageObject.isSticker()) { @@ -286,14 +299,14 @@ public class NotificationsController { private void scheduleNotificationDelay(boolean onlineReason) { try { - FileLog.e("tmessages", "delay notification start"); + FileLog.e("tmessages", "delay notification start, onlineReason = " + onlineReason); AlarmManager alarm = (AlarmManager) ApplicationLoader.applicationContext.getSystemService(Context.ALARM_SERVICE); PendingIntent pintent = PendingIntent.getService(ApplicationLoader.applicationContext, 0, new Intent(ApplicationLoader.applicationContext, NotificationDelay.class), 0); SharedPreferences preferences = ApplicationLoader.applicationContext.getSharedPreferences("Notifications", Activity.MODE_PRIVATE); if (onlineReason) { alarm.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime() + 3 * 1000, pintent); } else { - alarm.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime() + 500, pintent); + alarm.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime() + 1000, pintent); } } catch (Exception e) { FileLog.e("tmessages", e); @@ -302,7 +315,10 @@ public class NotificationsController { protected void notificationDelayReached() { FileLog.e("tmessages", "delay reached"); - showOrUpdateNotification(true); + if (!delayedPushMessages.isEmpty()) { + showOrUpdateNotification(true); + delayedPushMessages.clear(); + } } protected void repeatNotificationMaybe() { @@ -319,6 +335,7 @@ public class NotificationsController { AndroidUtilities.runOnUIThread(new Runnable() { @Override public void run() { + FileLog.e("tmessages", "set last online from other device = " + time); lastOnlineFromOtherDevice = time; } }); @@ -364,29 +381,47 @@ public class NotificationsController { boolean inAppPreview = false; boolean inAppPriority = false; int priority = 0; - int priority_override = 0; - int vibrate_override = 0; + int priorityOverride = 0; + int vibrateOverride = 0; SharedPreferences preferences = ApplicationLoader.applicationContext.getSharedPreferences("Notifications", Context.MODE_PRIVATE); - int notify_override = preferences.getInt("notify2_" + override_dialog_id, 0); - if (notify_override == 3) { - int mute_until = preferences.getInt("notifyuntil_" + override_dialog_id, 0); - if (mute_until >= ConnectionsManager.getInstance().getCurrentTime()) { - notify_override = 2; - } - } - if (!notifyAboutLast || notify_override == 2 || (!preferences.getBoolean("EnableAll", true) || chat_id != 0 && !preferences.getBoolean("EnableGroup", true)) && notify_override == 0) { + int notifyOverride = getNotifyOverride(preferences, override_dialog_id); + if (!notifyAboutLast || notifyOverride == 2 || (!preferences.getBoolean("EnableAll", true) || chat_id != 0 && !preferences.getBoolean("EnableGroup", true)) && notifyOverride == 0) { notifyDisabled = true; } + if (!notifyDisabled && dialog_id == override_dialog_id && chat != null) { + int notifyMaxCount = preferences.getInt("smart_max_count_" + dialog_id, 2); + int notifyDelay = preferences.getInt("smart_delay_" + dialog_id, 3 * 60); + if (notifyMaxCount != 0) { + Point dialogInfo = smartNotificationsDialogs.get(dialog_id); + if (dialogInfo == null) { + dialogInfo = new Point(1, (int) (System.currentTimeMillis() / 1000)); + smartNotificationsDialogs.put(dialog_id, dialogInfo); + } else { + int lastTime = dialogInfo.y; + if (lastTime + notifyDelay < System.currentTimeMillis() / 1000) { + dialogInfo.set(1, (int) (System.currentTimeMillis() / 1000)); + } else { + int count = dialogInfo.x; + if (count < notifyMaxCount) { + dialogInfo.set(count + 1, (int) (System.currentTimeMillis() / 1000)); + } else { + notifyDisabled = true; + } + } + } + } + } + String defaultPath = Settings.System.DEFAULT_NOTIFICATION_URI.getPath(); if (!notifyDisabled) { inAppSounds = preferences.getBoolean("EnableInAppSounds", true); inAppVibrate = preferences.getBoolean("EnableInAppVibrate", true); inAppPreview = preferences.getBoolean("EnableInAppPreview", true); inAppPriority = preferences.getBoolean("EnableInAppPriority", false); - vibrate_override = preferences.getInt("vibrate_" + dialog_id, 0); - priority_override = preferences.getInt("priority_" + dialog_id, 3); + vibrateOverride = preferences.getInt("vibrate_" + dialog_id, 0); + priorityOverride = preferences.getInt("priority_" + dialog_id, 3); boolean vibrateOnlyIfSilent = false; choosenSoundPath = preferences.getString("sound_path_" + dialog_id, null); @@ -413,16 +448,16 @@ public class NotificationsController { ledColor = preferences.getInt("color_" + dialog_id, 0); } - if (priority_override != 3) { - priority = priority_override; + if (priorityOverride != 3) { + priority = priorityOverride; } if (needVibrate == 4) { vibrateOnlyIfSilent = true; needVibrate = 0; } - if (needVibrate == 2 && (vibrate_override == 1 || vibrate_override == 3 || vibrate_override == 5) || needVibrate != 2 && vibrate_override == 2 || vibrate_override != 0) { - needVibrate = vibrate_override; + if (needVibrate == 2 && (vibrateOverride == 1 || vibrateOverride == 3 || vibrateOverride == 5) || needVibrate != 2 && vibrateOverride == 2 || vibrateOverride != 0) { + needVibrate = vibrateOverride; } if (!ApplicationLoader.mainInterfacePaused) { if (!inAppSounds) { @@ -524,9 +559,6 @@ public class NotificationsController { if (chat == null && user != null && user.phone != null && user.phone.length() > 0) { mBuilder.addPerson("tel:+" + user.phone); } - /*Bundle bundle = new Bundle(); - bundle.putString(NotificationCompat.EXTRA_PEOPLE, ); - mBuilder.setExtras()*/ String lastMessage = null; String lastMessageFull = null; @@ -558,7 +590,6 @@ public class NotificationsController { } if (i == 0) { lastMessageFull = message; - //lastMessage = getStringForMessage(pushMessages.get(i), true); lastMessage = lastMessageFull; } if (pushDialogs.size() == 1) { @@ -790,6 +821,7 @@ public class NotificationsController { } popupMessages.remove(messageObject); pushMessagesDict.remove(messageObject.getId()); + delayedPushMessages.remove(messageObject); pushMessages.remove(a); a--; } @@ -821,6 +853,7 @@ public class NotificationsController { personal_count--; } pushMessages.remove(a); + delayedPushMessages.remove(messageObject); popupMessages.remove(messageObject); pushMessagesDict.remove(messageObject.getId()); a--; @@ -847,14 +880,8 @@ public class NotificationsController { try { SharedPreferences preferences = ApplicationLoader.applicationContext.getSharedPreferences("Notifications", Context.MODE_PRIVATE); - int notify_override = preferences.getInt("notify2_" + openned_dialog_id, 0); - if (notify_override == 3) { - int mute_until = preferences.getInt("notifyuntil_" + openned_dialog_id, 0); - if (mute_until >= ConnectionsManager.getInstance().getCurrentTime()) { - notify_override = 2; - } - } - if (notify_override == 2) { + int notifyOverride = getNotifyOverride(preferences, openned_dialog_id); + if (notifyOverride == 2) { return; } notificationsQueue.postRunnable(new Runnable() { @@ -864,7 +891,14 @@ public class NotificationsController { return; } try { - if (mediaPlayerIn == null) { + if (soundPool == null) { + soundPool = new SoundPool(4, AudioManager.STREAM_SYSTEM, 0); + } + if (soundIn == 0) { + soundIn = soundPool.load(ApplicationLoader.applicationContext, R.raw.sound_in, 1); + } + soundPool.play(soundIn, 1.0f, 1.0f, 1, 0, 1.0f); + /*if (mediaPlayerIn == null) { AssetFileDescriptor assetFileDescriptor = ApplicationLoader.applicationContext.getResources().openRawResourceFd(R.raw.sound_in); if (assetFileDescriptor != null) { mediaPlayerIn = new MediaPlayer(); @@ -875,45 +909,18 @@ public class NotificationsController { mediaPlayerIn.prepare(); } } - mediaPlayerIn.start(); + try { + mediaPlayerIn.pause(); + mediaPlayerIn.seekTo(0); + } catch (Exception e) { + FileLog.e("tmessages", e); + } + mediaPlayerIn.start();*/ } catch (Exception e) { FileLog.e("tmessages", e); } } }); - /*String choosenSoundPath = null; - String defaultPath = Settings.System.DEFAULT_NOTIFICATION_URI.getPath(); - - choosenSoundPath = preferences.getString("sound_path_" + openned_dialog_id, null); - boolean isChat = (int)(openned_dialog_id) < 0; - if (isChat) { - if (choosenSoundPath != null && choosenSoundPath.equals(defaultPath)) { - choosenSoundPath = null; - } else if (choosenSoundPath == null) { - choosenSoundPath = preferences.getString("GroupSoundPath", defaultPath); - } - } else { - if (choosenSoundPath != null && choosenSoundPath.equals(defaultPath)) { - choosenSoundPath = null; - } else if (choosenSoundPath == null) { - choosenSoundPath = preferences.getString("GlobalSoundPath", defaultPath); - } - } - - if (choosenSoundPath != null && !choosenSoundPath.equals("NoSound")) { - if (lastMediaPlayerUri == null || !choosenSoundPath.equals(lastMediaPlayerUri)) { - lastMediaPlayerUri = choosenSoundPath; - mediaPlayer.reset(); - mediaPlayer.setAudioStreamType(AudioManager.STREAM_NOTIFICATION); - if (choosenSoundPath.equals(defaultPath)) { - mediaPlayer.setDataSource(ApplicationLoader.applicationContext, Settings.System.DEFAULT_NOTIFICATION_URI); - } else { - mediaPlayer.setDataSource(ApplicationLoader.applicationContext, Uri.parse(choosenSoundPath)); - } - mediaPlayer.prepare(); - } - mediaPlayer.start(); - }*/ } catch (Exception e) { FileLog.e("tmessages", e); } @@ -934,7 +941,14 @@ public class NotificationsController { @Override public void run() { try { - if (mediaPlayerOut == null) { + if (soundPool == null) { + soundPool = new SoundPool(4, AudioManager.STREAM_SYSTEM, 0); + } + if (soundOut == 0) { + soundOut = soundPool.load(ApplicationLoader.applicationContext, R.raw.sound_out, 1); + } + soundPool.play(soundOut, 1.0f, 1.0f, 1, 0, 1.0f); + /*if (mediaPlayerOut == null) { AssetFileDescriptor assetFileDescriptor = ApplicationLoader.applicationContext.getResources().openRawResourceFd(R.raw.sound_out); if (assetFileDescriptor != null) { mediaPlayerOut = new MediaPlayer(); @@ -945,7 +959,13 @@ public class NotificationsController { mediaPlayerOut.prepare(); } } - mediaPlayerOut.start(); + try { + mediaPlayerOut.pause(); + mediaPlayerOut.seekTo(0); + } catch (Exception e) { + FileLog.e("tmessages", e); + } + mediaPlayerOut.start();*/ } catch (Exception e) { FileLog.e("tmessages", e); } @@ -953,6 +973,17 @@ public class NotificationsController { }); } + private int getNotifyOverride(SharedPreferences preferences, long dialog_id) { + int notifyOverride = preferences.getInt("notify2_" + dialog_id, 0); + if (notifyOverride == 3) { + int muteUntil = preferences.getInt("notifyuntil_" + dialog_id, 0); + if (muteUntil >= ConnectionsManager.getInstance().getCurrentTime()) { + notifyOverride = 2; + } + } + return notifyOverride; + } + public void processNewMessages(ArrayList messageObjects, boolean isLast) { if (messageObjects.isEmpty()) { return; @@ -986,20 +1017,15 @@ public class NotificationsController { boolean isChat = (int)dialog_id < 0; popup = (int)dialog_id == 0 ? 0 : preferences.getInt(isChat ? "popupGroup" : "popupAll", 0); if (value == null) { - int notify_override = preferences.getInt("notify2_" + dialog_id, 0); - if (notify_override == 3) { - int mute_until = preferences.getInt("notifyuntil_" + dialog_id, 0); - if (mute_until >= ConnectionsManager.getInstance().getCurrentTime()) { - notify_override = 2; - } - } - value = !(notify_override == 2 || (!preferences.getBoolean("EnableAll", true) || isChat && !preferences.getBoolean("EnableGroup", true)) && notify_override == 0); + int notifyOverride = getNotifyOverride(preferences, dialog_id); + value = !(notifyOverride == 2 || (!preferences.getBoolean("EnableAll", true) || isChat && !preferences.getBoolean("EnableGroup", true)) && notifyOverride == 0); settingsCache.put(dialog_id, value); } if (value) { if (popup != 0) { popupMessages.add(0, messageObject); } + delayedPushMessages.add(messageObject); pushMessages.add(0, messageObject); pushMessagesDict.put(messageObject.getId(), messageObject); if (original_dialog_id != dialog_id) { @@ -1030,24 +1056,22 @@ public class NotificationsController { for (HashMap.Entry entry : dialogsToUpdate.entrySet()) { long dialog_id = entry.getKey(); - int notify_override = preferences.getInt("notify2_" + dialog_id, 0); - if (notify_override == 3) { - int mute_until = preferences.getInt("notifyuntil_" + dialog_id, 0); - if (mute_until >= ConnectionsManager.getInstance().getCurrentTime()) { - notify_override = 2; - } - } + int notifyOverride = getNotifyOverride(preferences, dialog_id); if (notifyCheck) { Integer override = pushDialogsOverrideMention.get(dialog_id); if (override != null && override == 1) { pushDialogsOverrideMention.put(dialog_id, 0); - notify_override = 1; + notifyOverride = 1; } } - boolean canAddValue = !(notify_override == 2 || (!preferences.getBoolean("EnableAll", true) || ((int)dialog_id < 0) && !preferences.getBoolean("EnableGroup", true)) && notify_override == 0); + boolean canAddValue = !(notifyOverride == 2 || (!preferences.getBoolean("EnableAll", true) || ((int)dialog_id < 0) && !preferences.getBoolean("EnableGroup", true)) && notifyOverride == 0); Integer currentCount = pushDialogs.get(dialog_id); Integer newCount = entry.getValue(); + if (newCount == 0) { + smartNotificationsDialogs.remove(dialog_id); + } + if (newCount < 0) { if (currentCount == null) { continue; @@ -1070,6 +1094,7 @@ public class NotificationsController { } pushMessages.remove(a); a--; + delayedPushMessages.remove(messageObject); pushMessagesDict.remove(messageObject.getId()); popupMessages.remove(messageObject); } @@ -1079,17 +1104,18 @@ public class NotificationsController { pushDialogs.put(dialog_id, newCount); } } - /*if (old_unread_count != total_unread_count) { TODO - if (lastOnlineFromOtherDevice > ConnectionsManager.getInstance().getCurrentTime()) { - showOrUpdateNotification(false); - scheduleNotificationDelay(true); - } else { - showOrUpdateNotification(notifyCheck); - } - }*/ if (old_unread_count != total_unread_count) { - showOrUpdateNotification(notifyCheck); + if (!notifyCheck) { + delayedPushMessages.clear(); + showOrUpdateNotification(notifyCheck); + } else { + showOrUpdateNotification(false); + scheduleNotificationDelay(lastOnlineFromOtherDevice > ConnectionsManager.getInstance().getCurrentTime()); + } } + /*if (old_unread_count != total_unread_count) { + showOrUpdateNotification(notifyCheck); + }*/ notifyCheck = false; if (preferences.getBoolean("badgeNumber", true)) { setBadge(ApplicationLoader.applicationContext, total_unread_count); @@ -1125,14 +1151,8 @@ public class NotificationsController { } Boolean value = settingsCache.get(dialog_id); if (value == null) { - int notify_override = preferences.getInt("notify2_" + dialog_id, 0); - if (notify_override == 3) { - int mute_until = preferences.getInt("notifyuntil_" + dialog_id, 0); - if (mute_until >= ConnectionsManager.getInstance().getCurrentTime()) { - notify_override = 2; - } - } - value = !(notify_override == 2 || (!preferences.getBoolean("EnableAll", true) || ((int) dialog_id < 0) && !preferences.getBoolean("EnableGroup", true)) && notify_override == 0); + int notifyOverride = getNotifyOverride(preferences, dialog_id); + 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) { @@ -1149,19 +1169,13 @@ public class NotificationsController { long dialog_id = entry.getKey(); Boolean value = settingsCache.get(dialog_id); if (value == null) { - int notify_override = preferences.getInt("notify2_" + dialog_id, 0); - if (notify_override == 3) { - int mute_until = preferences.getInt("notifyuntil_" + dialog_id, 0); - if (mute_until >= ConnectionsManager.getInstance().getCurrentTime()) { - notify_override = 2; - } - } + int notifyOverride = getNotifyOverride(preferences, dialog_id); Integer override = pushDialogsOverrideMention.get(dialog_id); if (override != null && override == 1) { pushDialogsOverrideMention.put(dialog_id, 0); - notify_override = 1; + notifyOverride = 1; } - value = !(notify_override == 2 || (!preferences.getBoolean("EnableAll", true) || ((int) dialog_id < 0) && !preferences.getBoolean("EnableGroup", true)) && notify_override == 0); + value = !(notifyOverride == 2 || (!preferences.getBoolean("EnableAll", true) || ((int) dialog_id < 0) && !preferences.getBoolean("EnableGroup", true)) && notifyOverride == 0); settingsCache.put(dialog_id, value); } if (!value) { @@ -1190,6 +1204,10 @@ public class NotificationsController { notificationsQueue.postRunnable(new Runnable() { @Override public void run() { + if (lastBadgeCount == count) { + return; + } + lastBadgeCount = count; try { ContentValues cv = new ContentValues(); cv.put("tag", "org.telegram.messenger/org.telegram.ui.LaunchActivity"); diff --git a/TMessagesProj/src/main/java/org/telegram/android/SecretChatHelper.java b/TMessagesProj/src/main/java/org/telegram/android/SecretChatHelper.java index 6a34ea267..2866763ac 100644 --- a/TMessagesProj/src/main/java/org/telegram/android/SecretChatHelper.java +++ b/TMessagesProj/src/main/java/org/telegram/android/SecretChatHelper.java @@ -542,7 +542,7 @@ public class SecretChatHelper { File cacheFile = new File(FileLoader.getInstance().getDirectory(FileLoader.MEDIA_DIR_CACHE), fileName + ".jpg"); File cacheFile2 = FileLoader.getPathToAttach(size); cacheFile.renameTo(cacheFile2); - ImageLoader.getInstance().replaceImageInCache(fileName, fileName2); + ImageLoader.getInstance().replaceImageInCache(fileName, fileName2, size.location); ArrayList arr = new ArrayList<>(); arr.add(newMsg); MessagesStorage.getInstance().putMessages(arr, false, true, false, 0); @@ -557,7 +557,7 @@ public class SecretChatHelper { newMsg.media.video.w = video.w; newMsg.media.video.h = video.h; newMsg.media.video.date = video.date; - newMsg.media.video.caption = ""; + newMsg.media.caption = video.caption != null ? video.caption : ""; newMsg.media.video.user_id = video.user_id; newMsg.media.video.size = file.size; newMsg.media.video.id = file.id; @@ -565,6 +565,7 @@ public class SecretChatHelper { newMsg.media.video.key = decryptedMessage.media.key; newMsg.media.video.iv = decryptedMessage.media.iv; newMsg.media.video.mime_type = video.mime_type; + newMsg.media.video.caption = video.caption != null ? video.caption : ""; if (newMsg.attachPath != null && newMsg.attachPath.startsWith(FileLoader.getInstance().getDirectory(FileLoader.MEDIA_DIR_CACHE).getAbsolutePath())) { File cacheFile = new File(newMsg.attachPath); @@ -893,10 +894,10 @@ public class SecretChatHelper { return null; } newMessage.media = new TLRPC.TL_messageMediaPhoto(); + newMessage.media.caption = ""; newMessage.media.photo = new TLRPC.TL_photo(); newMessage.media.photo.user_id = newMessage.from_id; newMessage.media.photo.date = newMessage.date; - newMessage.media.photo.caption = ""; newMessage.media.photo.geo = new TLRPC.TL_geoPointEmpty(); if (decryptedMessage.media.thumb.length != 0 && decryptedMessage.media.thumb.length <= 6000 && decryptedMessage.media.thumb_w <= 100 && decryptedMessage.media.thumb_h <= 100) { TLRPC.TL_photoCachedSize small = new TLRPC.TL_photoCachedSize(); @@ -926,6 +927,7 @@ public class SecretChatHelper { return null; } newMessage.media = new TLRPC.TL_messageMediaVideo(); + newMessage.media.caption = ""; newMessage.media.video = new TLRPC.TL_videoEncrypted(); if (decryptedMessage.media.thumb.length != 0 && decryptedMessage.media.thumb.length <= 6000 && decryptedMessage.media.thumb_w <= 100 && decryptedMessage.media.thumb_h <= 100) { newMessage.media.video.thumb = new TLRPC.TL_photoCachedSize(); @@ -943,7 +945,6 @@ public class SecretChatHelper { newMessage.media.video.w = decryptedMessage.media.w; newMessage.media.video.h = decryptedMessage.media.h; newMessage.media.video.date = date; - newMessage.media.video.caption = ""; newMessage.media.video.user_id = from_id; newMessage.media.video.size = file.size; newMessage.media.video.id = file.id; @@ -951,6 +952,7 @@ public class SecretChatHelper { newMessage.media.video.key = decryptedMessage.media.key; newMessage.media.video.iv = decryptedMessage.media.iv; newMessage.media.video.mime_type = decryptedMessage.media.mime_type; + newMessage.media.video.caption = ""; if (newMessage.ttl != 0) { newMessage.ttl = Math.max(newMessage.media.video.duration + 1, newMessage.ttl); } @@ -1300,7 +1302,7 @@ public class SecretChatHelper { ByteBufferDesc is = BuffersStorage.getInstance().getFreeBuffer(message.bytes.length); is.writeRaw(message.bytes); is.position(0); - long fingerprint = is.readInt64(); + long fingerprint = is.readInt64(false); byte[] keyToDecrypt = null; boolean new_key_used = false; if (chat.key_fingerprint == fingerprint) { @@ -1311,12 +1313,12 @@ public class SecretChatHelper { } if (keyToDecrypt != null) { - byte[] messageKey = is.readData(16); + byte[] messageKey = is.readData(16, false); MessageKeyData keyData = Utilities.generateMessageKeyData(keyToDecrypt, messageKey, false); Utilities.aesIgeEncryption(is.buffer, keyData.aesKey, keyData.aesIv, false, false, 24, is.limit() - 24); - int len = is.readInt32(); + int len = is.readInt32(false); if (len < 0 || len > is.limit() - 28) { return null; } @@ -1325,7 +1327,13 @@ public class SecretChatHelper { return null; } - TLObject object = TLClassStore.Instance().TLdeserialize(is, is.readInt32()); + TLObject object = null; + try { + object = TLClassStore.Instance().TLdeserialize(is, is.readInt32(true), true); + } catch (Exception e) { + FileLog.e("tmessages", e); + } + BuffersStorage.getInstance().reuseFreeBuffer(is); if (!new_key_used && AndroidUtilities.getPeerLayerVersion(chat.layer) >= 20) { chat.key_use_count_in++; diff --git a/TMessagesProj/src/main/java/org/telegram/android/SendMessagesHelper.java b/TMessagesProj/src/main/java/org/telegram/android/SendMessagesHelper.java index 35e6bedea..8cef20598 100644 --- a/TMessagesProj/src/main/java/org/telegram/android/SendMessagesHelper.java +++ b/TMessagesProj/src/main/java/org/telegram/android/SendMessagesHelper.java @@ -44,7 +44,7 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter private HashMap unsentMessages = new HashMap<>(); private HashMap sendingMessages = new HashMap<>(); - private class DelayedMessage { + protected class DelayedMessage { public TLObject sendRequest; public TLRPC.TL_decryptedMessage sendEncryptedRequest; public int type; @@ -458,8 +458,8 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter sendMessage(video, null, messageObject.messageOwner.attachPath, did, messageObject.replyMessageObject); } else if (messageObject.messageOwner.media.document instanceof TLRPC.TL_document) { sendMessage((TLRPC.TL_document) messageObject.messageOwner.media.document, null, messageObject.messageOwner.attachPath, did, messageObject.replyMessageObject); - } else if (messageObject.messageOwner.media.geo instanceof TLRPC.TL_geoPoint) { - sendMessage(messageObject.messageOwner.media.geo.lat, messageObject.messageOwner.media.geo._long, did, messageObject.replyMessageObject); + } else if (messageObject.messageOwner.media instanceof TLRPC.TL_messageMediaVenue || messageObject.messageOwner.media instanceof TLRPC.TL_messageMediaGeo) { + sendMessage(messageObject.messageOwner.media, did, messageObject.replyMessageObject); } else if (messageObject.messageOwner.media.phone_number != null) { TLRPC.User user = new TLRPC.TL_userContact(); user.phone = messageObject.messageOwner.media.phone_number; @@ -481,8 +481,53 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter } } + public void sendSticker(TLRPC.Document document, long peer, MessageObject replyingMessageObject) { + if (document == null) { + return; + } + if (((int) peer) == 0 && document.thumb instanceof TLRPC.TL_photoSize) { + File file = FileLoader.getPathToAttach(document.thumb, true); + if (file.exists()) { + try { + int len = (int) file.length(); + byte[] arr = new byte[(int) file.length()]; + RandomAccessFile reader = new RandomAccessFile(file, "r"); + reader.readFully(arr); + TLRPC.TL_document newDocument = new TLRPC.TL_document(); + newDocument.thumb = new TLRPC.TL_photoCachedSize(); + newDocument.thumb.location = document.thumb.location; + newDocument.thumb.size = document.thumb.size; + newDocument.thumb.w = document.thumb.w; + newDocument.thumb.h = document.thumb.h; + newDocument.thumb.type = document.thumb.type; + newDocument.thumb.bytes = arr; + + newDocument.id = document.id; + newDocument.access_hash = document.access_hash; + newDocument.date = document.date; + newDocument.mime_type = document.mime_type; + newDocument.size = document.size; + newDocument.dc_id = document.dc_id; + newDocument.attributes = document.attributes; + document = newDocument; + } catch (Exception e) { + FileLog.e("tmessages", e); + } + } + } + for (int a = 0; a < document.attributes.size(); a++) { + TLRPC.DocumentAttribute attribute = document.attributes.get(a); + if (attribute instanceof TLRPC.TL_documentAttributeSticker) { + document.attributes.remove(a); + document.attributes.add(new TLRPC.TL_documentAttributeSticker_old()); + break; + } + } + SendMessagesHelper.getInstance().sendMessage((TLRPC.TL_document) document, null, null, peer, replyingMessageObject); + } + public void sendMessage(TLRPC.User user, long peer, MessageObject reply_to_msg) { - sendMessage(null, null, null, null, null, null, user, null, null, null, peer, false, null, reply_to_msg, null, true); + sendMessage(null, null, null, null, null, user, null, null, null, peer, false, null, reply_to_msg, null, true); } public void sendMessage(ArrayList messages, long peer) { @@ -550,6 +595,9 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter ids.add(newMsg.fwd_msg_id); newMsg.date = ConnectionsManager.getInstance().getCurrentTime(); newMsg.flags |= TLRPC.MESSAGE_FLAG_UNREAD; + if (newMsg.media instanceof TLRPC.TL_messageMediaAudio) { + newMsg.flags |= TLRPC.MESSAGE_FLAG_CONTENT_UNREAD; + } newMsg.dialog_id = peer; newMsg.to_id = to_id; MessageObject newMsgObj = new MessageObject(newMsg, null, true); @@ -560,7 +608,7 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter putToSendingMessages(newMsg); if (arr.size() == 100 || a == messages.size() - 1) { - MessagesStorage.getInstance().putMessages(arr, false, true, false, 0); + MessagesStorage.getInstance().putMessages(new ArrayList<>(arr), false, true, false, 0); MessagesController.getInstance().updateInterfaceWithMessages(peer, objArr); NotificationCenter.getInstance().postNotificationName(NotificationCenter.dialogsNeedReload); UserConfig.saveConfig(false); @@ -655,38 +703,38 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter } public void sendMessage(MessageObject message) { - sendMessage(null, null, null, null, null, message, null, null, null, null, message.getDialogId(), true, message.messageOwner.attachPath, null, null, true); + sendMessage(null, null, null, null, message, null, null, null, null, message.getDialogId(), true, message.messageOwner.attachPath, null, null, true); } public void sendMessage(MessageObject message, long peer) { - sendMessage(null, null, null, null, null, message, null, null, null, null, peer, false, message.messageOwner.attachPath, null, null, true); + sendMessage(null, null, null, null, message, null, null, null, null, peer, false, message.messageOwner.attachPath, null, null, true); } public void sendMessage(TLRPC.TL_document document, String originalPath, String path, long peer, MessageObject reply_to_msg) { - sendMessage(null, null, null, null, null, null, null, document, null, originalPath, peer, false, path, reply_to_msg, null, true); + sendMessage(null, null, null, null, null, null, document, null, originalPath, peer, false, path, reply_to_msg, null, true); } public void sendMessage(String message, long peer, MessageObject reply_to_msg, TLRPC.WebPage webPage, boolean searchLinks) { - sendMessage(message, null, null, null, null, null, null, null, null, null, peer, false, null, reply_to_msg, webPage, searchLinks); + sendMessage(message, null, null, null, null, null, null, null, null, peer, false, null, reply_to_msg, webPage, searchLinks); } - public void sendMessage(double lat, double lon, long peer, MessageObject reply_to_msg) { - sendMessage(null, lat, lon, null, null, null, null, null, null, null, peer, false, null, reply_to_msg, null, true); + public void sendMessage(TLRPC.MessageMedia location, long peer, MessageObject reply_to_msg) { + sendMessage(null, location, null, null, null, null, null, null, null, peer, false, null, reply_to_msg, null, true); } public void sendMessage(TLRPC.TL_photo photo, String originalPath, String path, long peer, MessageObject reply_to_msg) { - sendMessage(null, null, null, photo, null, null, null, null, null, originalPath, peer, false, path, reply_to_msg, null, true); + sendMessage(null, null, photo, null, null, null, null, null, originalPath, peer, false, path, reply_to_msg, null, true); } public void sendMessage(TLRPC.TL_video video, String originalPath, String path, long peer, MessageObject reply_to_msg) { - sendMessage(null, null, null, null, video, null, null, null, null, originalPath, peer, false, path, reply_to_msg, null, true); + sendMessage(null, null, null, video, null, null, null, null, originalPath, peer, false, path, reply_to_msg, null, true); } public void sendMessage(TLRPC.TL_audio audio, String path, long peer, MessageObject reply_to_msg) { - sendMessage(null, null, null, null, null, null, null, null, audio, null, peer, false, path, reply_to_msg, null, true); + sendMessage(null, null, null, null, null, null, null, audio, null, peer, false, path, reply_to_msg, null, true); } - private void sendMessage(String message, Double lat, Double lon, TLRPC.TL_photo photo, TLRPC.TL_video video, MessageObject msgObj, TLRPC.User user, TLRPC.TL_document document, TLRPC.TL_audio audio, String originalPath, long peer, boolean retry, String path, MessageObject reply_to_msg, TLRPC.WebPage webPage, boolean searchLinks) { + private void sendMessage(String message, TLRPC.MessageMedia location, TLRPC.TL_photo photo, TLRPC.TL_video video, MessageObject msgObj, TLRPC.User user, TLRPC.TL_document document, TLRPC.TL_audio audio, String originalPath, long peer, boolean retry, String path, MessageObject reply_to_msg, TLRPC.WebPage webPage, boolean searchLinks) { if (peer == 0) { return; } @@ -713,8 +761,7 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter type = 0; } } else if (msgObj.type == 4) { - lat = newMsg.media.geo.lat; - lon = newMsg.media.geo._long; + location = newMsg.media; type = 1; } else if (msgObj.type == 1) { if (msgObj.isForwarded()) { @@ -760,16 +807,13 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter } type = 0; newMsg.message = message; - } else if (lat != null && lon != null) { + } else if (location != null) { if (encryptedChat != null && AndroidUtilities.getPeerLayerVersion(encryptedChat.layer) >= 17) { newMsg = new TLRPC.TL_message_secret(); } else { newMsg = new TLRPC.TL_message(); } - newMsg.media = new TLRPC.TL_messageMediaGeo(); - newMsg.media.geo = new TLRPC.TL_geoPoint(); - newMsg.media.geo.lat = lat; - newMsg.media.geo._long = lon; + newMsg.media = location; newMsg.message = ""; type = 1; } else if (photo != null) { @@ -779,6 +823,7 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter newMsg = new TLRPC.TL_message(); } newMsg.media = new TLRPC.TL_messageMediaPhoto(); + newMsg.media.caption = photo.caption != null ? photo.caption : ""; newMsg.media.photo = photo; type = 2; newMsg.message = "-1"; @@ -795,6 +840,7 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter newMsg = new TLRPC.TL_message(); } newMsg.media = new TLRPC.TL_messageMediaVideo(); + newMsg.media.caption = video.caption != null ? video.caption : ""; newMsg.media.video = video; newMsg.videoEditedInfo = video.videoEditedInfo; type = 3; @@ -868,6 +914,9 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter } newMsg.date = ConnectionsManager.getInstance().getCurrentTime(); newMsg.flags |= TLRPC.MESSAGE_FLAG_UNREAD; + if (encryptedChat == null && high_id != 1 && newMsg.media instanceof TLRPC.TL_messageMediaAudio) { + newMsg.flags |= TLRPC.MESSAGE_FLAG_CONTENT_UNREAD; + } newMsg.dialog_id = peer; if (reply_to_msg != null) { newMsg.flags |= TLRPC.MESSAGE_FLAG_REPLY; @@ -993,13 +1042,22 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter TLRPC.InputMedia inputMedia = null; DelayedMessage delayedMessage = null; if (type == 1) { - inputMedia = new TLRPC.TL_inputMediaGeoPoint(); + if (location instanceof TLRPC.TL_messageMediaVenue) { + inputMedia = new TLRPC.TL_inputMediaVenue(); + inputMedia.address = location.address; + inputMedia.title = location.title; + inputMedia.provider = location.provider; + inputMedia.venue_id = location.venue_id; + } else { + inputMedia = new TLRPC.TL_inputMediaGeoPoint(); + } inputMedia.geo_point = new TLRPC.TL_inputGeoPoint(); - inputMedia.geo_point.lat = lat; - inputMedia.geo_point._long = lon; + inputMedia.geo_point.lat = location.geo.lat; + inputMedia.geo_point._long = location.geo._long; } else if (type == 2) { if (photo.access_hash == 0) { inputMedia = new TLRPC.TL_inputMediaUploadedPhoto(); + inputMedia.caption = photo.caption != null ? photo.caption : ""; delayedMessage = new DelayedMessage(); delayedMessage.originalPath = originalPath; delayedMessage.type = 0; @@ -1012,6 +1070,7 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter } else { TLRPC.TL_inputMediaPhoto media = new TLRPC.TL_inputMediaPhoto(); media.id = new TLRPC.TL_inputPhoto(); + media.caption = photo.caption != null ? photo.caption : ""; media.id.id = photo.id; media.id.access_hash = photo.access_hash; inputMedia = media; @@ -1023,6 +1082,7 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter } else { inputMedia = new TLRPC.TL_inputMediaUploadedVideo(); } + inputMedia.caption = video.caption != null ? video.caption : ""; inputMedia.duration = video.duration; inputMedia.w = video.w; inputMedia.h = video.h; @@ -1036,6 +1096,7 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter } else { TLRPC.TL_inputMediaVideo media = new TLRPC.TL_inputMediaVideo(); media.id = new TLRPC.TL_inputVideo(); + media.caption = video.caption != null ? video.caption : ""; media.id.id = video.id; media.id.access_hash = video.access_hash; inputMedia = media; @@ -1161,8 +1222,8 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter reqSend.message = ""; if (type == 1) { reqSend.media = new TLRPC.TL_decryptedMessageMediaGeoPoint(); - reqSend.media.lat = lat; - reqSend.media._long = lon; + reqSend.media.lat = location.geo.lat; + reqSend.media._long = location.geo._long; SecretChatHelper.getInstance().performSendEncryptedRequest(reqSend, newMsgObj.messageOwner, encryptedChat, null, null); } else if (type == 2) { TLRPC.PhotoSize small = photo.sizes.get(0); @@ -1589,7 +1650,7 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter cacheFile2 = new File(FileLoader.getInstance().getDirectory(FileLoader.MEDIA_DIR_CACHE), fileName2 + ".jpg"); } cacheFile.renameTo(cacheFile2); - ImageLoader.getInstance().replaceImageInCache(fileName, fileName2); + ImageLoader.getInstance().replaceImageInCache(fileName, fileName2, size.location); size2.location = size.location; break; } @@ -1611,7 +1672,7 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter File cacheFile = new File(FileLoader.getInstance().getDirectory(FileLoader.MEDIA_DIR_CACHE), fileName + ".jpg"); File cacheFile2 = new File(FileLoader.getInstance().getDirectory(FileLoader.MEDIA_DIR_CACHE), fileName2 + ".jpg"); cacheFile.renameTo(cacheFile2); - ImageLoader.getInstance().replaceImageInCache(fileName, fileName2); + ImageLoader.getInstance().replaceImageInCache(fileName, fileName2, size.location); size2.location = size.location; } } @@ -1642,7 +1703,7 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter File cacheFile = new File(FileLoader.getInstance().getDirectory(FileLoader.MEDIA_DIR_CACHE), fileName + ".jpg"); File cacheFile2 = new File(FileLoader.getInstance().getDirectory(FileLoader.MEDIA_DIR_CACHE), fileName2 + ".jpg"); cacheFile.renameTo(cacheFile2); - ImageLoader.getInstance().replaceImageInCache(fileName, fileName2); + ImageLoader.getInstance().replaceImageInCache(fileName, fileName2, size.location); size2.location = size.location; } } else if (MessageObject.isStickerMessage(sentMessage) && size2.location != null) { @@ -1697,6 +1758,10 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter arrayList.add(message); } + protected ArrayList getDelayedMessages(String location) { + return delayedMessages.get(location); + } + protected long getNextRandomId() { long val = 0; while (val == 0) { @@ -1749,7 +1814,6 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter photo.user_id = UserConfig.getClientUserId(); photo.date = ConnectionsManager.getInstance().getCurrentTime(); photo.sizes = sizes; - photo.caption = ""; photo.geo = new TLRPC.TL_geoPointEmpty(); return photo; } @@ -1811,11 +1875,15 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter document.size = (int)f.length(); document.dc_id = 0; if (ext.length() != 0) { - String mimeType = myMime.getMimeTypeFromExtension(ext.toLowerCase()); - if (mimeType != null) { - document.mime_type = mimeType; + if (ext.toLowerCase().equals("webp")) { + document.mime_type = "image/webp"; } else { - document.mime_type = "application/octet-stream"; + String mimeType = myMime.getMimeTypeFromExtension(ext.toLowerCase()); + if (mimeType != null) { + document.mime_type = mimeType; + } else { + document.mime_type = "application/octet-stream"; + } } } else { document.mime_type = "application/octet-stream"; @@ -1929,9 +1997,10 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter }).start(); } - public static void prepareSendingPhoto(String imageFilePath, Uri imageUri, long dialog_id, MessageObject reply_to_msg) { + public static void prepareSendingPhoto(String imageFilePath, Uri imageUri, long dialog_id, MessageObject reply_to_msg, CharSequence caption) { ArrayList paths = null; ArrayList uris = null; + ArrayList captions = null; if (imageFilePath != null && imageFilePath.length() != 0) { paths = new ArrayList<>(); paths.add(imageFilePath); @@ -1940,7 +2009,11 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter uris = new ArrayList<>(); uris.add(imageUri); } - prepareSendingPhotos(paths, uris, dialog_id, reply_to_msg); + if (caption != null) { + captions = new ArrayList<>(); + captions.add(caption.toString()); + } + prepareSendingPhotos(paths, uris, dialog_id, reply_to_msg, captions); } public static void prepareSendingPhotosSearch(final ArrayList photos, final long dialog_id, final MessageObject reply_to_msg) { @@ -1951,7 +2024,8 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter @Override public void run() { boolean isEncrypted = (int)dialog_id == 0; - for (final MediaController.SearchImage searchImage : photos) { + for (int a = 0; a < photos.size(); a++) { + final MediaController.SearchImage searchImage = photos.get(a); if (searchImage.type == 1) { TLRPC.TL_document document = null; if (!isEncrypted) { @@ -2029,7 +2103,6 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter photo = new TLRPC.TL_photo(); photo.user_id = UserConfig.getClientUserId(); photo.date = ConnectionsManager.getInstance().getCurrentTime(); - photo.caption = ""; photo.geo = new TLRPC.TL_geoPointEmpty(); TLRPC.TL_photoSize photoSize = new TLRPC.TL_photoSize(); photoSize.w = searchImage.width; @@ -2042,6 +2115,9 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter } } if (photo != null) { + if (searchImage.caption != null) { + photo.caption = searchImage.caption.toString(); + } final String originalPathFinal = searchImage.imageUrl; final TLRPC.TL_photo photoFinal = photo; final boolean needDownloadHttpFinal = needDownloadHttp; @@ -2058,7 +2134,32 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter }).start(); } - public static void prepareSendingPhotos(ArrayList paths, ArrayList uris, final long dialog_id, final MessageObject reply_to_msg) { + private static String getTrimmedString(String src) { + String result = src.trim(); + if (result.length() == 0) { + return result; + } + while (src.startsWith("\n")) { + src = src.substring(1); + } + while (src.endsWith("\n")) { + src = src.substring(0, src.length() - 1); + } + return src; + } + + public static void prepareSendingText(String text, long dialog_id) { + text = getTrimmedString(text); + if (text.length() != 0) { + int count = (int) Math.ceil(text.length() / 4096.0f); + for (int a = 0; a < count; a++) { + String mess = text.substring(a * 4096, Math.min((a + 1) * 4096, text.length())); + SendMessagesHelper.getInstance().sendMessage(mess, dialog_id, null, null, true); + } + } + } + + public static void prepareSendingPhotos(ArrayList paths, ArrayList uris, final long dialog_id, final MessageObject reply_to_msg, final ArrayList captions) { if (paths == null && uris == null || paths != null && paths.isEmpty() || uris != null && uris.isEmpty()) { return; } @@ -2080,6 +2181,7 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter int count = !pathsCopy.isEmpty() ? pathsCopy.size() : urisCopy.size(); String path = null; Uri uri = null; + String extension = null; for (int a = 0; a < count; a++) { if (!pathsCopy.isEmpty()) { path = pathsCopy.get(a); @@ -2096,16 +2198,23 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter boolean isDocument = false; if (tempPath != null && (tempPath.endsWith(".gif") || tempPath.endsWith(".webp"))) { + if (tempPath.endsWith(".gif")) { + extension = "gif"; + } else { + extension = "webp"; + } isDocument = true; } else if (tempPath == null && uri != null) { if (MediaController.isGif(uri)) { isDocument = true; originalPath = uri.toString(); tempPath = MediaController.copyDocumentToCache(uri, "gif"); + extension = "gif"; } else if (MediaController.isWebp(uri)) { isDocument = true; originalPath = uri.toString(); tempPath = MediaController.copyDocumentToCache(uri, "webp"); + extension = "webp"; } } @@ -2134,6 +2243,9 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter photo = SendMessagesHelper.getInstance().generatePhotoSizes(path, uri); } if (photo != null) { + if (captions != null) { + photo.caption = captions.get(a); + } final String originalPathFinal = originalPath; final TLRPC.TL_photo photoFinal = photo; AndroidUtilities.runOnUIThread(new Runnable() { @@ -2147,7 +2259,7 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter } if (sendAsDocuments != null && !sendAsDocuments.isEmpty()) { for (int a = 0; a < sendAsDocuments.size(); a++) { - prepareSendingDocumentInternal(sendAsDocuments.get(a), sendAsDocumentsOriginal.get(a), null, "gif", dialog_id, reply_to_msg); + prepareSendingDocumentInternal(sendAsDocuments.get(a), sendAsDocumentsOriginal.get(a), null, extension, dialog_id, reply_to_msg); } } } @@ -2190,7 +2302,6 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter } else { video.thumb.type = "s"; } - video.caption = ""; video.mime_type = "video/mp4"; video.id = 0; UserConfig.saveConfig(false); diff --git a/TMessagesProj/src/main/java/org/telegram/android/query/ReplyMessageQuery.java b/TMessagesProj/src/main/java/org/telegram/android/query/ReplyMessageQuery.java index da9717dd1..bb01bdd5a 100644 --- a/TMessagesProj/src/main/java/org/telegram/android/query/ReplyMessageQuery.java +++ b/TMessagesProj/src/main/java/org/telegram/android/query/ReplyMessageQuery.java @@ -21,7 +21,6 @@ import org.telegram.messenger.ByteBufferDesc; import org.telegram.messenger.ConnectionsManager; import org.telegram.messenger.FileLog; import org.telegram.messenger.RPCRequest; -import org.telegram.messenger.TLClassStore; import org.telegram.messenger.TLObject; import org.telegram.messenger.TLRPC; @@ -65,7 +64,7 @@ public class ReplyMessageQuery { while (cursor.next()) { ByteBufferDesc data = MessagesStorage.getInstance().getBuffersStorage().getFreeBuffer(cursor.byteArrayLength(0)); if (data != null && cursor.byteBufferValue(0, data.buffer) != 0) { - TLRPC.Message message = (TLRPC.Message) TLClassStore.Instance().TLdeserialize(data, data.readInt32()); + 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; diff --git a/TMessagesProj/src/main/java/org/telegram/android/query/SharedMediaQuery.java b/TMessagesProj/src/main/java/org/telegram/android/query/SharedMediaQuery.java index aab9255e6..345b0e49d 100644 --- a/TMessagesProj/src/main/java/org/telegram/android/query/SharedMediaQuery.java +++ b/TMessagesProj/src/main/java/org/telegram/android/query/SharedMediaQuery.java @@ -20,7 +20,6 @@ import org.telegram.messenger.ByteBufferDesc; import org.telegram.messenger.ConnectionsManager; import org.telegram.messenger.FileLog; import org.telegram.messenger.RPCRequest; -import org.telegram.messenger.TLClassStore; import org.telegram.messenger.TLObject; import org.telegram.messenger.TLRPC; @@ -178,7 +177,7 @@ public class SharedMediaQuery { } final ArrayList objects = new ArrayList<>(); for (TLRPC.Message message : res.messages) { - objects.add(new MessageObject(message, usersLocal, false)); + objects.add(new MessageObject(message, usersLocal, true)); } AndroidUtilities.runOnUIThread(new Runnable() { @@ -324,7 +323,7 @@ public class SharedMediaQuery { while (cursor.next()) { ByteBufferDesc data = MessagesStorage.getInstance().getBuffersStorage().getFreeBuffer(cursor.byteArrayLength(0)); if (data != null && cursor.byteBufferValue(0, data.buffer) != 0) { - TLRPC.Message message = (TLRPC.Message) TLClassStore.Instance().TLdeserialize(data, data.readInt32()); + TLRPC.Message message = TLRPC.Message.TLdeserialize(data, data.readInt32(false), false); message.id = cursor.intValue(1); message.dialog_id = uid; if ((int)uid == 0) { diff --git a/TMessagesProj/src/main/java/org/telegram/android/query/StickersQuery.java b/TMessagesProj/src/main/java/org/telegram/android/query/StickersQuery.java new file mode 100644 index 000000000..6c8c45288 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/android/query/StickersQuery.java @@ -0,0 +1,182 @@ +/* + * This is the source code of Telegram for Android v. 2.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-2015. + */ + +package org.telegram.android.query; + +import org.telegram.SQLite.SQLiteCursor; +import org.telegram.SQLite.SQLitePreparedStatement; +import org.telegram.android.AndroidUtilities; +import org.telegram.android.MessagesStorage; +import org.telegram.android.NotificationCenter; +import org.telegram.messenger.ByteBufferDesc; +import org.telegram.messenger.ConnectionsManager; +import org.telegram.messenger.FileLog; +import org.telegram.messenger.RPCRequest; +import org.telegram.messenger.TLObject; +import org.telegram.messenger.TLRPC; +import org.telegram.messenger.Utilities; + +import java.util.ArrayList; +import java.util.HashMap; + +public class StickersQuery { + + private static String hash; + private static int loadDate; + private static ArrayList stickers = new ArrayList<>(); + private static HashMap> allStickers = new HashMap<>(); + private static boolean loadingStickers; + + public static void checkStickers() { + if (!loadingStickers && (allStickers.isEmpty() || loadDate < (System.currentTimeMillis() / 1000 - 60 * 60))) { + loadStickers(true); + } + } + + public static ArrayList getStickers() { + return stickers; + } + + private static void loadStickers(boolean cache) { + if (loadingStickers) { + return; + } + loadingStickers = true; + if (cache) { + MessagesStorage.getInstance().getStorageQueue().postRunnable(new Runnable() { + @Override + public void run() { + TLRPC.messages_AllStickers result = null; + int date = 0; + try { + SQLiteCursor cursor = MessagesStorage.getInstance().getDatabase().queryFinalized("SELECT data, date FROM stickers WHERE 1"); + ArrayList loadedUsers = new ArrayList<>(); + if (cursor.next()) { + ByteBufferDesc data = MessagesStorage.getInstance().getBuffersStorage().getFreeBuffer(cursor.byteArrayLength(0)); + if (data != null && cursor.byteBufferValue(0, data.buffer) != 0) { + result = TLRPC.messages_AllStickers.TLdeserialize(data, data.readInt32(false), false); + } + date = cursor.intValue(1); + MessagesStorage.getInstance().getBuffersStorage().reuseFreeBuffer(data); + } + cursor.dispose(); + } catch (Exception e) { + FileLog.e("tmessages", e); + } + processLoadedStickers(result, true, date); + } + }); + } else { + TLRPC.TL_messages_getAllStickers req = new TLRPC.TL_messages_getAllStickers(); + req.hash = hash; + if (req.hash == null) { + req.hash = ""; + } + ConnectionsManager.getInstance().performRpc(req, new RPCRequest.RPCRequestDelegate() { + @Override + public void run(final TLObject response, final TLRPC.TL_error error) { + AndroidUtilities.runOnUIThread(new Runnable() { + @Override + public void run() { + processLoadedStickers((TLRPC.messages_AllStickers) response, false, (int) (System.currentTimeMillis() / 1000)); + } + }); + } + }); + } + } + + private static void putStickersToCache(final TLRPC.TL_messages_allStickers stickers) { + MessagesStorage.getInstance().getStorageQueue().postRunnable(new Runnable() { + @Override + public void run() { + try { + SQLitePreparedStatement state = MessagesStorage.getInstance().getDatabase().executeFast("REPLACE INTO stickers VALUES(?, ?, ?)"); + state.requery(); + ByteBufferDesc data = MessagesStorage.getInstance().getBuffersStorage().getFreeBuffer(stickers.getObjectSize()); + stickers.serializeToStream(data); + state.bindInteger(1, 1); + state.bindByteBuffer(2, data.buffer); + state.bindInteger(3, (int) (System.currentTimeMillis() / 1000)); + state.step(); + MessagesStorage.getInstance().getBuffersStorage().reuseFreeBuffer(data); + state.dispose(); + } catch (Exception e) { + FileLog.e("tmessages", e); + } + } + }); + } + + private static void processLoadedStickers(final TLRPC.messages_AllStickers res, final boolean cache, final int date) { + AndroidUtilities.runOnUIThread(new Runnable() { + @Override + public void run() { + loadingStickers = false; + } + }); + Utilities.stageQueue.postRunnable(new Runnable() { + @Override + public void run() { + if ((res == null || date < (int) (System.currentTimeMillis() / 1000 - 60 * 60)) && cache) { + AndroidUtilities.runOnUIThread(new Runnable() { + @Override + public void run() { + loadStickers(false); + } + }); + if (res == null) { + return; + } + } + if (res instanceof TLRPC.TL_messages_allStickers) { + if (!cache) { + putStickersToCache((TLRPC.TL_messages_allStickers) res); + } + HashMap documents = new HashMap<>(); + for (TLRPC.Document document : res.documents) { + if (document == null) { + continue; + } + documents.put(document.id, document); + if (document.thumb != null && document.thumb.location != null) { + document.thumb.location.ext = "webp"; + } + } + final HashMap> result = new HashMap<>(); + for (TLRPC.TL_stickerPack stickerPack : res.packs) { + if (stickerPack != null && stickerPack.emoticon != null) { + stickerPack.emoticon = stickerPack.emoticon.replace("\uFE0F", ""); + ArrayList arrayList = result.get(stickerPack.emoticon); + for (Long id : stickerPack.documents) { + TLRPC.Document document = documents.get(id); + if (document != null) { + if (arrayList == null) { + arrayList = new ArrayList<>(); + result.put(stickerPack.emoticon, arrayList); + } + arrayList.add(document); + } + } + } + } + AndroidUtilities.runOnUIThread(new Runnable() { + @Override + public void run() { + allStickers = result; + stickers = res.documents; + hash = res.hash; + loadDate = date; + NotificationCenter.getInstance().postNotificationName(NotificationCenter.stickersDidLoaded); + } + }); + } + } + }); + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/android/support/util/SortedList.java b/TMessagesProj/src/main/java/org/telegram/android/support/util/SortedList.java new file mode 100644 index 000000000..688e032c8 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/android/support/util/SortedList.java @@ -0,0 +1,633 @@ +/* + * Copyright (C) 2014 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 android.support.v7.util; + +import java.lang.reflect.Array; + +/** + * 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 android.support.v7.widget.RecyclerView.Adapter + * RecyclerView.Adapter}. + *

+ * It keeps items ordered using the {@link Callback#compare(Object, Object)} method and uses + * binary search to retrieve items. If the sorting criteria of your items may change, make sure you + * call appropriate methods while editing them to avoid data inconsistencies. + *

+ * You can control the order of items and change notifications via the {@link Callback} parameter. + */ +@SuppressWarnings("unchecked") +public class SortedList { + + /** + * Used by {@link #indexOf(Object)} when he item cannot be found in the list. + */ + public static final int INVALID_POSITION = -1; + + private static final int MIN_CAPACITY = 10; + private static final int CAPACITY_GROWTH = MIN_CAPACITY; + private static final int INSERTION = 1; + private static final int DELETION = 1 << 1; + private static final int LOOKUP = 1 << 2; + T[] mData; + + /** + * The callback instance that controls the behavior of the SortedList and get notified when + * changes happen. + */ + private Callback mCallback; + + private BatchedCallback mBatchedCallback; + + private int mSize; + private final Class mTClass; + + /** + * Creates a new SortedList of type T. + * + * @param klass The class of the contents of the SortedList. + * @param callback The callback that controls the behavior of SortedList. + */ + public SortedList(Class klass, Callback callback) { + this(klass, callback, MIN_CAPACITY); + } + + /** + * Creates a new SortedList of type T. + * + * @param klass The class of the contents of the SortedList. + * @param callback The callback that controls the behavior of SortedList. + * @param initialCapacity The initial capacity to hold items. + */ + public SortedList(Class klass, Callback callback, int initialCapacity) { + mTClass = klass; + mData = (T[]) Array.newInstance(klass, initialCapacity); + mCallback = callback; + mSize = 0; + } + + /** + * The number of items in the list. + * + * @return The number of items in the list. + */ + public int size() { + return mSize; + } + + /** + * Adds the given item to the list. If this is a new item, SortedList calls + * {@link Callback#onInserted(int, int)}. + *

+ * If the item already exists in the list and its sorting criteria is not changed, it is + * replaced with the existing Item. SortedList uses + * {@link Callback#areItemsTheSame(Object, Object)} to check if two items are the same item + * and uses {@link Callback#areContentsTheSame(Object, Object)} to decide whether it should + * call {@link Callback#onChanged(int, int)} or not. In both cases, it always removes the + * reference to the old item and puts the new item into the backing array even if + * {@link Callback#areContentsTheSame(Object, Object)} returns false. + *

+ * If the sorting criteria of the item is changed, SortedList won't be able to find + * its duplicate in the list which will result in having a duplicate of the Item in the list. + * If you need to update sorting criteria of an item that already exists in the list, + * use {@link #updateItemAt(int, Object)}. You can find the index of the item using + * {@link #indexOf(Object)} before you update the object. + * + * @param item The item to be added into the list. + * @return The index of the newly added item. + * @see {@link Callback#compare(Object, Object)} + * @see {@link Callback#areItemsTheSame(Object, Object)} + * @see {@link Callback#areContentsTheSame(Object, Object)}} + */ + public int add(T item) { + return add(item, true); + } + + /** + * Batches adapter updates that happen between calling this method until calling + * {@link #endBatchedUpdates()}. For example, if you add multiple items in a loop + * and they are placed into consecutive indices, SortedList calls + * {@link Callback#onInserted(int, int)} only once with the proper item count. If an event + * cannot be merged with the previous event, the previous event is dispatched + * to the callback instantly. + *

+ * After running your data updates, you must call {@link #endBatchedUpdates()} + * which will dispatch any deferred data change event to the current callback. + *

+ * A sample implementation may look like this: + *

+     *     mSortedList.beginBatchedUpdates();
+     *     try {
+     *         mSortedList.add(item1)
+     *         mSortedList.add(item2)
+     *         mSortedList.remove(item3)
+     *         ...
+     *     } finally {
+     *         mSortedList.endBatchedUpdates();
+     *     }
+     * 
+ *

+ * Instead of using this method to batch calls, you can use a Callback that extends + * {@link BatchedCallback}. In that case, you must make sure that you are manually calling + * {@link BatchedCallback#dispatchLastEvent()} right after you complete your data changes. + * Failing to do so may create data inconsistencies with the Callback. + *

+ * If the current Callback in an instance of {@link BatchedCallback}, calling this method + * has no effect. + */ + public void beginBatchedUpdates() { + if (mCallback instanceof BatchedCallback) { + return; + } + if (mBatchedCallback == null) { + mBatchedCallback = new BatchedCallback(mCallback); + } + mCallback = mBatchedCallback; + } + + /** + * Ends the update transaction and dispatches any remaining event to the callback. + */ + public void endBatchedUpdates() { + if (mCallback instanceof BatchedCallback) { + ((BatchedCallback) mCallback).dispatchLastEvent(); + } + if (mCallback == mBatchedCallback) { + mCallback = mBatchedCallback.mWrappedCallback; + } + } + + private int add(T item, boolean notify) { + int index = findIndexOf(item, INSERTION); + if (index == INVALID_POSITION) { + index = 0; + } else if (index < mSize) { + T existing = mData[index]; + if (mCallback.areItemsTheSame(existing, item)) { + if (mCallback.areContentsTheSame(existing, item)) { + //no change but still replace the item + mData[index] = item; + return index; + } else { + mData[index] = item; + mCallback.onChanged(index, 1); + return index; + } + } + } + addToData(index, item); + if (notify) { + mCallback.onInserted(index, 1); + } + return index; + } + + /** + * Removes the provided item from the list and calls {@link Callback#onRemoved(int, int)}. + * + * @param item The item to be removed from the list. + * @return True if item is removed, false if item cannot be found in the list. + */ + public boolean remove(T item) { + return remove(item, true); + } + + /** + * Removes the item at the given index and calls {@link Callback#onRemoved(int, int)}. + * + * @param index The index of the item to be removed. + * @return The removed item. + */ + public T removeItemAt(int index) { + T item = get(index); + removeItemAtIndex(index, true); + return item; + } + + private boolean remove(T item, boolean notify) { + int index = findIndexOf(item, DELETION); + if (index == INVALID_POSITION) { + return false; + } + removeItemAtIndex(index, notify); + return true; + } + + private void removeItemAtIndex(int index, boolean notify) { + System.arraycopy(mData, index + 1, mData, index, mSize - index - 1); + mSize--; + mData[mSize] = null; + if (notify) { + mCallback.onRemoved(index, 1); + } + } + + /** + * Updates the item at the given index and calls {@link Callback#onChanged(int, int)} and/or + * {@link Callback#onMoved(int, int)} if necessary. + *

+ * You can use this method if you need to change an existing Item such that its position in the + * list may change. + *

+ * If the new object is a different object (get(index) != item) and + * {@link Callback#areContentsTheSame(Object, Object)} returns true, SortedList + * avoids calling {@link Callback#onChanged(int, int)} otherwise it calls + * {@link Callback#onChanged(int, int)}. + *

+ * If the new position of the item is different than the provided index, + * SortedList + * calls {@link Callback#onMoved(int, int)}. + * + * @param index The index of the item to replace + * @param item The item to replace the item at the given Index. + * @see #add(Object) + */ + public void updateItemAt(int index, T item) { + final T existing = get(index); + // assume changed if the same object is given back + boolean contentsChanged = existing == item || !mCallback.areContentsTheSame(existing, item); + if (existing != item) { + // different items, we can use comparison and may avoid lookup + final int cmp = mCallback.compare(existing, item); + if (cmp == 0) { + mData[index] = item; + if (contentsChanged) { + mCallback.onChanged(index, 1); + } + return; + } + } + if (contentsChanged) { + mCallback.onChanged(index, 1); + } + // TODO this done in 1 pass to avoid shifting twice. + removeItemAtIndex(index, false); + int newIndex = add(item, false); + if (index != newIndex) { + mCallback.onMoved(index, newIndex); + } + } + + /** + * This method can be used to recalculate the position of the item at the given index, without + * triggering an {@link Callback#onChanged(int, int)} callback. + *

+ * If you are editing objects in the list such that their position in the list may change but + * you don't want to trigger an onChange animation, you can use this method to re-position it. + * If the item changes position, SortedList will call {@link Callback#onMoved(int, int)} + * without + * calling {@link Callback#onChanged(int, int)}. + *

+ * A sample usage may look like: + * + *

+     *     final int position = mSortedList.indexOf(item);
+     *     item.incrementPriority(); // assume items are sorted by priority
+     *     mSortedList.recalculatePositionOfItemAt(position);
+     * 
+ * In the example above, because the sorting criteria of the item has been changed, + * mSortedList.indexOf(item) will not be able to find the item. This is why the code above + * first + * gets the position before editing the item, edits it and informs the SortedList that item + * should be repositioned. + * + * @param index The current index of the Item whose position should be re-calculated. + * @see #updateItemAt(int, Object) + * @see #add(Object) + */ + public void recalculatePositionOfItemAt(int index) { + // TODO can be improved + final T item = get(index); + removeItemAtIndex(index, false); + int newIndex = add(item, false); + if (index != newIndex) { + mCallback.onMoved(index, newIndex); + } + } + + /** + * Returns the item at the given index. + * + * @param index The index of the item to retrieve. + * @return The item at the given index. + * @throws java.lang.IndexOutOfBoundsException if provided index is negative or larger than the + * size of the list. + */ + public T get(int index) throws IndexOutOfBoundsException { + if (index >= mSize || index < 0) { + throw new IndexOutOfBoundsException("Asked to get item at " + index + " but size is " + + mSize); + } + return mData[index]; + } + + /** + * Returns the position of the provided item. + * + * @param item The item to query for position. + * @return The position of the provided item or {@link #INVALID_POSITION} if item is not in the + * list. + */ + public int indexOf(T item) { + return findIndexOf(item, LOOKUP); + } + + private int findIndexOf(T item, int reason) { + int left = 0; + int right = mSize; + while (left < right) { + final int middle = (left + right) / 2; + T myItem = mData[middle]; + final int cmp = mCallback.compare(myItem, item); + if (cmp < 0) { + left = middle + 1; + } else if (cmp == 0) { + if (mCallback.areItemsTheSame(myItem, item)) { + return middle; + } else { + int exact = linearEqualitySearch(item, middle, left, right); + if (reason == INSERTION) { + return exact == INVALID_POSITION ? middle : exact; + } else { + return exact; + } + } + } else { + right = middle; + } + } + return reason == INSERTION ? left : INVALID_POSITION; + } + + private int linearEqualitySearch(T item, int middle, int left, int right) { + // go left + for (int next = middle - 1; next >= left; next--) { + T nextItem = mData[next]; + int cmp = mCallback.compare(nextItem, item); + if (cmp != 0) { + break; + } + if (mCallback.areItemsTheSame(nextItem, item)) { + return next; + } + } + for (int next = middle + 1; next < right; next++) { + T nextItem = mData[next]; + int cmp = mCallback.compare(nextItem, item); + if (cmp != 0) { + break; + } + if (mCallback.areItemsTheSame(nextItem, item)) { + return next; + } + } + return INVALID_POSITION; + } + + private void addToData(int index, T item) { + if (index > mSize) { + throw new IndexOutOfBoundsException( + "cannot add item to " + index + " because size is " + mSize); + } + if (mSize == mData.length) { + // we are at the limit enlarge + T[] newData = (T[]) Array.newInstance(mTClass, mData.length + CAPACITY_GROWTH); + System.arraycopy(mData, 0, newData, 0, index); + newData[index] = item; + System.arraycopy(mData, index, newData, index + 1, mSize - index); + mData = newData; + } else { + // just shift, we fit + System.arraycopy(mData, index, mData, index + 1, mSize - index); + mData[index] = item; + } + mSize++; + } + + /** + * The class that controls the behavior of the {@link SortedList}. + *

+ * It defines how items should be sorted and how duplicates should be handled. + *

+ * SortedList calls the callback methods on this class to notify changes about the underlying + * data. + */ + public static abstract class Callback { + + /** + * Similar to {@link java.util.Comparator#compare(Object, Object)}, should compare two and + * return how they should be ordered. + * + * @param o1 The first object to compare. + * @param o2 The second object to compare. + * @return a negative integer, zero, or a positive integer as the + * first argument is less than, equal to, or greater than the + * second. + */ + abstract public int compare(T2 o1, T2 o2); + + /** + * Called by the SortedList when an item is inserted at the given position. + * + * @param position The position of the new item. + * @param count The number of items that have been added. + */ + abstract public void onInserted(int position, int count); + + /** + * Called by the SortedList when an item is removed from the given position. + * + * @param position The position of the item which has been removed. + * @param count The number of items which have been removed. + */ + abstract public void onRemoved(int position, int count); + + /** + * Called by the SortedList when an item changes its position in the list. + * + * @param fromPosition The previous position of the item before the move. + * @param toPosition The new position of the item. + */ + abstract public void onMoved(int fromPosition, int toPosition); + + /** + * Called by the SortedList when the item at the given position is updated. + * + * @param position The position of the item which has been updated. + * @param count The number of items which has changed. + */ + abstract public void onChanged(int position, int count); + + /** + * Called by the SortedList when it wants to check whether two items have the same data + * or not. SortedList uses this information to decide whether it should call + * {@link #onChanged(int, int)} or not. + *

+ * SortedList uses this method to check equality instead of {@link Object#equals(Object)} + * so + * that you can change its behavior depending on your UI. + *

+ * For example, if you are using 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. + * + * @param oldItem The previous representation of the object. + * @param newItem The new object that replaces the previous one. + * @return True if the contents of the items are the same or false if they are different. + */ + abstract public boolean areContentsTheSame(T2 oldItem, T2 newItem); + + /** + * Called by the SortedList to decide whether two object represent the same Item or not. + *

+ * For example, if your items have unique ids, this method should check their equality. + * + * @param item1 The first item to check. + * @param item2 The second item to check. + * @return True if the two items represent the same object or false if they are different. + */ + abstract public boolean areItemsTheSame(T2 item1, T2 item2); + } + + /** + * A callback implementation that can batch notify events dispatched by the SortedList. + *

+ * This class can be useful if you want to do multiple operations on a SortedList but don't + * want to dispatch each event one by one, which may result in a performance issue. + *

+ * For example, if you are going to add multiple items to a SortedList, BatchedCallback call + * convert individual onInserted(index, 1) calls into one + * onInserted(index, N) if items are added into consecutive indices. This change + * can help RecyclerView resolve changes much more easily. + *

+ * If consecutive changes in the SortedList are not suitable for batching, BatchingCallback + * dispatches them as soon as such case is detected. After your edits on the SortedList is + * complete, you must always call {@link BatchedCallback#dispatchLastEvent()} to flush + * all changes to the Callback. + */ + public static class BatchedCallback extends Callback { + + private final Callback mWrappedCallback; + static final int TYPE_NONE = 0; + static final int TYPE_ADD = 1; + static final int TYPE_REMOVE = 2; + static final int TYPE_CHANGE = 3; + static final int TYPE_MOVE = 4; + + int mLastEventType = TYPE_NONE; + int mLastEventPosition = -1; + int mLastEventCount = -1; + + /** + * Creates a new BatchedCallback that wraps the provided Callback. + * + * @param wrappedCallback The Callback which should received the data change callbacks. + * Other method calls (e.g. {@link #compare(Object, Object)} from + * the SortedList are directly forwarded to this Callback. + */ + public BatchedCallback(Callback wrappedCallback) { + mWrappedCallback = wrappedCallback; + } + + @Override + public int compare(T2 o1, T2 o2) { + return mWrappedCallback.compare(o1, o2); + } + + @Override + public void onInserted(int position, int count) { + if (mLastEventType == TYPE_ADD && position >= mLastEventPosition + && position <= mLastEventPosition + mLastEventCount) { + mLastEventCount += count; + mLastEventPosition = Math.min(position, mLastEventPosition); + return; + } + dispatchLastEvent(); + mLastEventPosition = position; + mLastEventCount = count; + mLastEventType = TYPE_ADD; + } + + @Override + public void onRemoved(int position, int count) { + if (mLastEventType == TYPE_REMOVE && mLastEventPosition == position) { + mLastEventCount += count; + return; + } + dispatchLastEvent(); + mLastEventPosition = position; + mLastEventCount = count; + mLastEventType = TYPE_REMOVE; + } + + @Override + public void onMoved(int fromPosition, int toPosition) { + dispatchLastEvent();//moves are not merged + mWrappedCallback.onMoved(fromPosition, toPosition); + } + + @Override + public void onChanged(int position, int count) { + if (mLastEventType == TYPE_CHANGE && + !(position > mLastEventPosition + mLastEventCount + || position + count < mLastEventPosition)) { + // take potential overlap into account + int previousEnd = mLastEventPosition + mLastEventCount; + mLastEventPosition = Math.min(position, mLastEventPosition); + mLastEventCount = Math.max(previousEnd, position + count) - mLastEventPosition; + return; + } + dispatchLastEvent(); + mLastEventPosition = position; + mLastEventCount = count; + mLastEventType = TYPE_CHANGE; + } + + @Override + public boolean areContentsTheSame(T2 oldItem, T2 newItem) { + return mWrappedCallback.areContentsTheSame(oldItem, newItem); + } + + @Override + public boolean areItemsTheSame(T2 item1, T2 item2) { + return mWrappedCallback.areItemsTheSame(item1, item2); + } + + + /** + * This method dispatches any pending event notifications to the wrapped Callback. + * You must always call this method after you are done with editing the SortedList. + */ + public void dispatchLastEvent() { + if (mLastEventType == TYPE_NONE) { + return; + } + switch (mLastEventType) { + case TYPE_ADD: + mWrappedCallback.onInserted(mLastEventPosition, mLastEventCount); + break; + case TYPE_REMOVE: + mWrappedCallback.onRemoved(mLastEventPosition, mLastEventCount); + break; + case TYPE_CHANGE: + mWrappedCallback.onChanged(mLastEventPosition, mLastEventCount); + break; + } + mLastEventType = TYPE_NONE; + } + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/android/support/widget/AdapterHelper.java b/TMessagesProj/src/main/java/org/telegram/android/support/widget/AdapterHelper.java new file mode 100644 index 000000000..3b4c28c1a --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/android/support/widget/AdapterHelper.java @@ -0,0 +1,733 @@ +/* + * Copyright (C) 2014 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.android.support.widget; + +import android.support.v4.util.Pools; +import android.util.Log; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import static org.telegram.android.support.widget.RecyclerView.*; + +/** + * Helper class that can enqueue and process adapter update operations. + *

+ * To support animations, RecyclerView presents an older version the Adapter to best represent + * previous state of the layout. Sometimes, this is not trivial when items are removed that were + * not laid out, in which case, RecyclerView has no way of providing that item's view for + * animations. + *

+ * AdapterHelper creates an UpdateOp for each adapter data change then pre-processes them. During + * pre processing, AdapterHelper finds out which UpdateOps can be deferred to second layout pass + * and which cannot. For the UpdateOps that cannot be deferred, AdapterHelper will change them + * according to previously deferred operation and dispatch them before the first layout pass. It + * also takes care of updating deferred UpdateOps since order of operations is changed by this + * process. + *

+ * Although operations may be forwarded to LayoutManager in different orders, resulting data set + * is guaranteed to be the consistent. + */ +class AdapterHelper implements OpReorderer.Callback { + + final static int POSITION_TYPE_INVISIBLE = 0; + + final static int POSITION_TYPE_NEW_OR_LAID_OUT = 1; + + private static final boolean DEBUG = false; + + private static final String TAG = "AHT"; + + private Pools.Pool mUpdateOpPool = new Pools.SimplePool(UpdateOp.POOL_SIZE); + + final ArrayList mPendingUpdates = new ArrayList(); + + final ArrayList mPostponedList = new ArrayList(); + + final Callback mCallback; + + Runnable mOnItemProcessedCallback; + + final boolean mDisableRecycler; + + final OpReorderer mOpReorderer; + + AdapterHelper(Callback callback) { + this(callback, false); + } + + AdapterHelper(Callback callback, boolean disableRecycler) { + mCallback = callback; + mDisableRecycler = disableRecycler; + mOpReorderer = new OpReorderer(this); + } + + AdapterHelper addUpdateOp(UpdateOp... ops) { + Collections.addAll(mPendingUpdates, ops); + return this; + } + + void reset() { + recycleUpdateOpsAndClearList(mPendingUpdates); + recycleUpdateOpsAndClearList(mPostponedList); + } + + void preProcess() { + mOpReorderer.reorderOps(mPendingUpdates); + final int count = mPendingUpdates.size(); + for (int i = 0; i < count; i++) { + UpdateOp op = mPendingUpdates.get(i); + switch (op.cmd) { + case UpdateOp.ADD: + applyAdd(op); + break; + case UpdateOp.REMOVE: + applyRemove(op); + break; + case UpdateOp.UPDATE: + applyUpdate(op); + break; + case UpdateOp.MOVE: + applyMove(op); + break; + } + if (mOnItemProcessedCallback != null) { + mOnItemProcessedCallback.run(); + } + } + mPendingUpdates.clear(); + } + + void consumePostponedUpdates() { + final int count = mPostponedList.size(); + for (int i = 0; i < count; i++) { + mCallback.onDispatchSecondPass(mPostponedList.get(i)); + } + recycleUpdateOpsAndClearList(mPostponedList); + } + + private void applyMove(UpdateOp op) { + // MOVE ops are pre-processed so at this point, we know that item is still in the adapter. + // otherwise, it would be converted into a REMOVE operation + postponeAndUpdateViewHolders(op); + } + + private void applyRemove(UpdateOp op) { + int tmpStart = op.positionStart; + int tmpCount = 0; + int tmpEnd = op.positionStart + op.itemCount; + int type = -1; + for (int position = op.positionStart; position < tmpEnd; position++) { + boolean typeChanged = false; + ViewHolder vh = mCallback.findViewHolder(position); + if (vh != null || canFindInPreLayout(position)) { + // If a ViewHolder exists or this is a newly added item, we can defer this update + // to post layout stage. + // * For existing ViewHolders, we'll fake its existence in the pre-layout phase. + // * For items that are added and removed in the same process cycle, they won't + // have any effect in pre-layout since their add ops are already deferred to + // post-layout pass. + if (type == POSITION_TYPE_INVISIBLE) { + // Looks like we have other updates that we cannot merge with this one. + // Create an UpdateOp and dispatch it to LayoutManager. + UpdateOp newOp = obtainUpdateOp(UpdateOp.REMOVE, tmpStart, tmpCount); + dispatchAndUpdateViewHolders(newOp); + typeChanged = true; + } + type = POSITION_TYPE_NEW_OR_LAID_OUT; + } else { + // This update cannot be recovered because we don't have a ViewHolder representing + // this position. Instead, post it to LayoutManager immediately + if (type == POSITION_TYPE_NEW_OR_LAID_OUT) { + // Looks like we have other updates that we cannot merge with this one. + // Create UpdateOp op and dispatch it to LayoutManager. + UpdateOp newOp = obtainUpdateOp(UpdateOp.REMOVE, tmpStart, tmpCount); + postponeAndUpdateViewHolders(newOp); + typeChanged = true; + } + type = POSITION_TYPE_INVISIBLE; + } + if (typeChanged) { + position -= tmpCount; // also equal to tmpStart + tmpEnd -= tmpCount; + tmpCount = 1; + } else { + tmpCount++; + } + } + if (tmpCount != op.itemCount) { // all 1 effect + recycleUpdateOp(op); + op = obtainUpdateOp(UpdateOp.REMOVE, tmpStart, tmpCount); + } + if (type == POSITION_TYPE_INVISIBLE) { + dispatchAndUpdateViewHolders(op); + } else { + postponeAndUpdateViewHolders(op); + } + } + + private void applyUpdate(UpdateOp op) { + int tmpStart = op.positionStart; + int tmpCount = 0; + int tmpEnd = op.positionStart + op.itemCount; + int type = -1; + for (int position = op.positionStart; position < tmpEnd; position++) { + ViewHolder vh = mCallback.findViewHolder(position); + if (vh != null || canFindInPreLayout(position)) { // deferred + if (type == POSITION_TYPE_INVISIBLE) { + UpdateOp newOp = obtainUpdateOp(UpdateOp.UPDATE, tmpStart, tmpCount); + dispatchAndUpdateViewHolders(newOp); + tmpCount = 0; + tmpStart = position; + } + type = POSITION_TYPE_NEW_OR_LAID_OUT; + } else { // applied + if (type == POSITION_TYPE_NEW_OR_LAID_OUT) { + UpdateOp newOp = obtainUpdateOp(UpdateOp.UPDATE, tmpStart, tmpCount); + postponeAndUpdateViewHolders(newOp); + tmpCount = 0; + tmpStart = position; + } + type = POSITION_TYPE_INVISIBLE; + } + tmpCount++; + } + if (tmpCount != op.itemCount) { // all 1 effect + recycleUpdateOp(op); + op = obtainUpdateOp(UpdateOp.UPDATE, tmpStart, tmpCount); + } + if (type == POSITION_TYPE_INVISIBLE) { + dispatchAndUpdateViewHolders(op); + } else { + postponeAndUpdateViewHolders(op); + } + } + + private void dispatchAndUpdateViewHolders(UpdateOp op) { + // tricky part. + // traverse all postpones and revert their changes on this op if necessary, apply updated + // dispatch to them since now they are after this op. + if (op.cmd == UpdateOp.ADD || op.cmd == UpdateOp.MOVE) { + throw new IllegalArgumentException("should not dispatch add or move for pre layout"); + } + if (DEBUG) { + Log.d(TAG, "dispatch (pre)" + op); + Log.d(TAG, "postponed state before:"); + for (UpdateOp updateOp : mPostponedList) { + Log.d(TAG, updateOp.toString()); + } + Log.d(TAG, "----"); + } + + // handle each pos 1 by 1 to ensure continuity. If it breaks, dispatch partial + // TODO Since move ops are pushed to end, we should not need this anymore + int tmpStart = updatePositionWithPostponed(op.positionStart, op.cmd); + if (DEBUG) { + Log.d(TAG, "pos:" + op.positionStart + ",updatedPos:" + tmpStart); + } + int tmpCnt = 1; + int offsetPositionForPartial = op.positionStart; + final int positionMultiplier; + switch (op.cmd) { + case UpdateOp.UPDATE: + positionMultiplier = 1; + break; + case UpdateOp.REMOVE: + positionMultiplier = 0; + break; + default: + throw new IllegalArgumentException("op should be remove or update." + op); + } + for (int p = 1; p < op.itemCount; p++) { + final int pos = op.positionStart + (positionMultiplier * p); + int updatedPos = updatePositionWithPostponed(pos, op.cmd); + if (DEBUG) { + Log.d(TAG, "pos:" + pos + ",updatedPos:" + updatedPos); + } + boolean continuous = false; + switch (op.cmd) { + case UpdateOp.UPDATE: + continuous = updatedPos == tmpStart + 1; + break; + case UpdateOp.REMOVE: + continuous = updatedPos == tmpStart; + break; + } + if (continuous) { + tmpCnt++; + } else { + // need to dispatch this separately + UpdateOp tmp = obtainUpdateOp(op.cmd, tmpStart, tmpCnt); + if (DEBUG) { + Log.d(TAG, "need to dispatch separately " + tmp); + } + dispatchFirstPassAndUpdateViewHolders(tmp, offsetPositionForPartial); + recycleUpdateOp(tmp); + if (op.cmd == UpdateOp.UPDATE) { + offsetPositionForPartial += tmpCnt; + } + tmpStart = updatedPos;// need to remove previously dispatched + tmpCnt = 1; + } + } + recycleUpdateOp(op); + if (tmpCnt > 0) { + UpdateOp tmp = obtainUpdateOp(op.cmd, tmpStart, tmpCnt); + if (DEBUG) { + Log.d(TAG, "dispatching:" + tmp); + } + dispatchFirstPassAndUpdateViewHolders(tmp, offsetPositionForPartial); + recycleUpdateOp(tmp); + } + if (DEBUG) { + Log.d(TAG, "post dispatch"); + Log.d(TAG, "postponed state after:"); + for (UpdateOp updateOp : mPostponedList) { + Log.d(TAG, updateOp.toString()); + } + Log.d(TAG, "----"); + } + } + + void dispatchFirstPassAndUpdateViewHolders(UpdateOp op, int offsetStart) { + mCallback.onDispatchFirstPass(op); + switch (op.cmd) { + case UpdateOp.REMOVE: + mCallback.offsetPositionsForRemovingInvisible(offsetStart, op.itemCount); + break; + case UpdateOp.UPDATE: + mCallback.markViewHoldersUpdated(offsetStart, op.itemCount); + break; + default: + throw new IllegalArgumentException("only remove and update ops can be dispatched" + + " in first pass"); + } + } + + private int updatePositionWithPostponed(int pos, int cmd) { + final int count = mPostponedList.size(); + for (int i = count - 1; i >= 0; i--) { + UpdateOp postponed = mPostponedList.get(i); + if (postponed.cmd == UpdateOp.MOVE) { + int start, end; + if (postponed.positionStart < postponed.itemCount) { + start = postponed.positionStart; + end = postponed.itemCount; + } else { + start = postponed.itemCount; + end = postponed.positionStart; + } + if (pos >= start && pos <= end) { + //i'm affected + if (start == postponed.positionStart) { + if (cmd == UpdateOp.ADD) { + postponed.itemCount++; + } else if (cmd == UpdateOp.REMOVE) { + postponed.itemCount--; + } + // op moved to left, move it right to revert + pos++; + } else { + if (cmd == UpdateOp.ADD) { + postponed.positionStart++; + } else if (cmd == UpdateOp.REMOVE) { + postponed.positionStart--; + } + // op was moved right, move left to revert + pos--; + } + } else if (pos < postponed.positionStart) { + // postponed MV is outside the dispatched OP. if it is before, offset + if (cmd == UpdateOp.ADD) { + postponed.positionStart++; + postponed.itemCount++; + } else if (cmd == UpdateOp.REMOVE) { + postponed.positionStart--; + postponed.itemCount--; + } + } + } else { + if (postponed.positionStart <= pos) { + if (postponed.cmd == UpdateOp.ADD) { + pos -= postponed.itemCount; + } else if (postponed.cmd == UpdateOp.REMOVE) { + pos += postponed.itemCount; + } + } else { + if (cmd == UpdateOp.ADD) { + postponed.positionStart++; + } else if (cmd == UpdateOp.REMOVE) { + postponed.positionStart--; + } + } + } + if (DEBUG) { + Log.d(TAG, "dispath (step" + i + ")"); + Log.d(TAG, "postponed state:" + i + ", pos:" + pos); + for (UpdateOp updateOp : mPostponedList) { + Log.d(TAG, updateOp.toString()); + } + Log.d(TAG, "----"); + } + } + for (int i = mPostponedList.size() - 1; i >= 0; i--) { + UpdateOp op = mPostponedList.get(i); + if (op.cmd == UpdateOp.MOVE) { + if (op.itemCount == op.positionStart || op.itemCount < 0) { + mPostponedList.remove(i); + recycleUpdateOp(op); + } + } else if (op.itemCount <= 0) { + mPostponedList.remove(i); + recycleUpdateOp(op); + } + } + return pos; + } + + private boolean canFindInPreLayout(int position) { + final int count = mPostponedList.size(); + for (int i = 0; i < count; i++) { + UpdateOp op = mPostponedList.get(i); + if (op.cmd == UpdateOp.MOVE) { + if (findPositionOffset(op.itemCount, i + 1) == position) { + return true; + } + } else if (op.cmd == UpdateOp.ADD) { + // TODO optimize. + final int end = op.positionStart + op.itemCount; + for (int pos = op.positionStart; pos < end; pos++) { + if (findPositionOffset(pos, i + 1) == position) { + return true; + } + } + } + } + return false; + } + + private void applyAdd(UpdateOp op) { + postponeAndUpdateViewHolders(op); + } + + private void postponeAndUpdateViewHolders(UpdateOp op) { + if (DEBUG) { + Log.d(TAG, "postponing " + op); + } + mPostponedList.add(op); + switch (op.cmd) { + case UpdateOp.ADD: + mCallback.offsetPositionsForAdd(op.positionStart, op.itemCount); + break; + case UpdateOp.MOVE: + mCallback.offsetPositionsForMove(op.positionStart, op.itemCount); + break; + case UpdateOp.REMOVE: + mCallback.offsetPositionsForRemovingLaidOutOrNewView(op.positionStart, + op.itemCount); + break; + case UpdateOp.UPDATE: + mCallback.markViewHoldersUpdated(op.positionStart, op.itemCount); + break; + default: + throw new IllegalArgumentException("Unknown update op type for " + op); + } + } + + boolean hasPendingUpdates() { + return mPendingUpdates.size() > 0; + } + + int findPositionOffset(int position) { + return findPositionOffset(position, 0); + } + + int findPositionOffset(int position, int firstPostponedItem) { + int count = mPostponedList.size(); + for (int i = firstPostponedItem; i < count; ++i) { + UpdateOp op = mPostponedList.get(i); + if (op.cmd == UpdateOp.MOVE) { + if (op.positionStart == position) { + position = op.itemCount; + } else { + if (op.positionStart < position) { + position--; // like a remove + } + if (op.itemCount <= position) { + position++; // like an add + } + } + } else if (op.positionStart <= position) { + if (op.cmd == UpdateOp.REMOVE) { + if (position < op.positionStart + op.itemCount) { + return -1; + } + position -= op.itemCount; + } else if (op.cmd == UpdateOp.ADD) { + position += op.itemCount; + } + } + } + return position; + } + + /** + * @return True if updates should be processed. + */ + boolean onItemRangeChanged(int positionStart, int itemCount) { + mPendingUpdates.add(obtainUpdateOp(UpdateOp.UPDATE, positionStart, itemCount)); + return mPendingUpdates.size() == 1; + } + + /** + * @return True if updates should be processed. + */ + boolean onItemRangeInserted(int positionStart, int itemCount) { + mPendingUpdates.add(obtainUpdateOp(UpdateOp.ADD, positionStart, itemCount)); + return mPendingUpdates.size() == 1; + } + + /** + * @return True if updates should be processed. + */ + boolean onItemRangeRemoved(int positionStart, int itemCount) { + mPendingUpdates.add(obtainUpdateOp(UpdateOp.REMOVE, positionStart, itemCount)); + return mPendingUpdates.size() == 1; + } + + /** + * @return True if updates should be processed. + */ + boolean onItemRangeMoved(int from, int to, int itemCount) { + if (from == to) { + 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)); + return mPendingUpdates.size() == 1; + } + + /** + * Skips pre-processing and applies all updates in one pass. + */ + void consumeUpdatesInOnePass() { + // we still consume postponed updates (if there is) in case there was a pre-process call + // w/o a matching consumePostponedUpdates. + consumePostponedUpdates(); + final int count = mPendingUpdates.size(); + for (int i = 0; i < count; i++) { + UpdateOp op = mPendingUpdates.get(i); + switch (op.cmd) { + case UpdateOp.ADD: + mCallback.onDispatchSecondPass(op); + mCallback.offsetPositionsForAdd(op.positionStart, op.itemCount); + break; + case UpdateOp.REMOVE: + mCallback.onDispatchSecondPass(op); + mCallback.offsetPositionsForRemovingInvisible(op.positionStart, op.itemCount); + break; + case UpdateOp.UPDATE: + mCallback.onDispatchSecondPass(op); + mCallback.markViewHoldersUpdated(op.positionStart, op.itemCount); + break; + case UpdateOp.MOVE: + mCallback.onDispatchSecondPass(op); + mCallback.offsetPositionsForMove(op.positionStart, op.itemCount); + break; + } + if (mOnItemProcessedCallback != null) { + mOnItemProcessedCallback.run(); + } + } + recycleUpdateOpsAndClearList(mPendingUpdates); + } + + public int applyPendingUpdatesToPosition(int position) { + final int size = mPendingUpdates.size(); + for (int i = 0; i < size; i ++) { + UpdateOp op = mPendingUpdates.get(i); + switch (op.cmd) { + case UpdateOp.ADD: + if (op.positionStart <= position) { + position += op.itemCount; + } + break; + case UpdateOp.REMOVE: + if (op.positionStart <= position) { + final int end = op.positionStart + op.itemCount; + if (end > position) { + return RecyclerView.NO_POSITION; + } + position -= op.itemCount; + } + break; + case UpdateOp.MOVE: + if (op.positionStart == position) { + position = op.itemCount;//position end + } else { + if (op.positionStart < position) { + position -= 1; + } + if (op.itemCount <= position) { + position += 1; + } + } + break; + } + } + return position; + } + + /** + * Queued operation to happen when child views are updated. + */ + static class UpdateOp { + + static final int ADD = 0; + + static final int REMOVE = 1; + + static final int UPDATE = 2; + + static final int MOVE = 3; + + static final int POOL_SIZE = 30; + + int cmd; + + int positionStart; + + // holds the target position if this is a MOVE + int itemCount; + + UpdateOp(int cmd, int positionStart, int itemCount) { + this.cmd = cmd; + this.positionStart = positionStart; + this.itemCount = itemCount; + } + + String cmdToString() { + switch (cmd) { + case ADD: + return "add"; + case REMOVE: + return "rm"; + case UPDATE: + return "up"; + case MOVE: + return "mv"; + } + return "??"; + } + + @Override + public String toString() { + return "[" + cmdToString() + ",s:" + positionStart + "c:" + itemCount + "]"; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + UpdateOp op = (UpdateOp) o; + + if (cmd != op.cmd) { + return false; + } + if (cmd == MOVE && Math.abs(itemCount - positionStart) == 1) { + // reverse of this is also true + if (itemCount == op.positionStart && positionStart == op.itemCount) { + return true; + } + } + if (itemCount != op.itemCount) { + return false; + } + if (positionStart != op.positionStart) { + return false; + } + + return true; + } + + @Override + public int hashCode() { + int result = cmd; + result = 31 * result + positionStart; + result = 31 * result + itemCount; + return result; + } + } + + @Override + public UpdateOp obtainUpdateOp(int cmd, int positionStart, int itemCount) { + UpdateOp op = mUpdateOpPool.acquire(); + if (op == null) { + op = new UpdateOp(cmd, positionStart, itemCount); + } else { + op.cmd = cmd; + op.positionStart = positionStart; + op.itemCount = itemCount; + } + return op; + } + + @Override + public void recycleUpdateOp(UpdateOp op) { + if (!mDisableRecycler) { + mUpdateOpPool.release(op); + } + } + + void recycleUpdateOpsAndClearList(List ops) { + final int count = ops.size(); + for (int i = 0; i < count; i++) { + recycleUpdateOp(ops.get(i)); + } + ops.clear(); + } + + /** + * Contract between AdapterHelper and RecyclerView. + */ + static interface Callback { + + ViewHolder findViewHolder(int position); + + void offsetPositionsForRemovingInvisible(int positionStart, int itemCount); + + void offsetPositionsForRemovingLaidOutOrNewView(int positionStart, int itemCount); + + void markViewHoldersUpdated(int positionStart, int itemCount); + + void onDispatchFirstPass(UpdateOp updateOp); + + void onDispatchSecondPass(UpdateOp updateOp); + + void offsetPositionsForAdd(int positionStart, int itemCount); + + void offsetPositionsForMove(int from, int to); + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/android/support/widget/ChildHelper.java b/TMessagesProj/src/main/java/org/telegram/android/support/widget/ChildHelper.java new file mode 100644 index 000000000..838bca966 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/android/support/widget/ChildHelper.java @@ -0,0 +1,487 @@ +/* + * Copyright (C) 2014 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.android.support.widget; + +import android.util.Log; +import android.view.View; +import android.view.ViewGroup; + +import java.util.ArrayList; +import java.util.List; + +/** + * Helper class to manage children. + *

+ * It wraps a RecyclerView and adds ability to hide some children. There are two sets of methods + * provided by this class. Regular methods are the ones that replicate ViewGroup methods + * like getChildAt, getChildCount etc. These methods ignore hidden children. + *

+ * When RecyclerView needs direct access to the view group children, it can call unfiltered + * methods like get getUnfilteredChildCount or getUnfilteredChildAt. + */ +class ChildHelper { + + private static final boolean DEBUG = false; + + private static final String TAG = "ChildrenHelper"; + + final Callback mCallback; + + final Bucket mBucket; + + final List mHiddenViews; + + ChildHelper(Callback callback) { + mCallback = callback; + mBucket = new Bucket(); + mHiddenViews = new ArrayList(); + } + + /** + * Adds a view to the ViewGroup + * + * @param child View to add. + * @param hidden If set to true, this item will be invisible from regular methods. + */ + void addView(View child, boolean hidden) { + addView(child, -1, hidden); + } + + /** + * Add a view to the ViewGroup at an index + * + * @param child View to add. + * @param index Index of the child from the regular perspective (excluding hidden views). + * ChildHelper offsets this index to actual ViewGroup index. + * @param hidden If set to true, this item will be invisible from regular methods. + */ + void addView(View child, int index, boolean hidden) { + final int offset; + if (index < 0) { + offset = mCallback.getChildCount(); + } else { + offset = getOffset(index); + } + mBucket.insert(offset, hidden); + if (hidden) { + mHiddenViews.add(child); + } + mCallback.addView(child, offset); + if (DEBUG) { + Log.d(TAG, "addViewAt " + index + ",h:" + hidden + ", " + this); + } + } + + private int getOffset(int index) { + if (index < 0) { + return -1; //anything below 0 won't work as diff will be undefined. + } + final int limit = mCallback.getChildCount(); + int offset = index; + while (offset < limit) { + final int removedBefore = mBucket.countOnesBefore(offset); + final int diff = index - (offset - removedBefore); + if (diff == 0) { + while (mBucket.get(offset)) { // ensure this offset is not hidden + offset ++; + } + return offset; + } else { + offset += diff; + } + } + return -1; + } + + /** + * Removes the provided View from underlying RecyclerView. + * + * @param view The view to remove. + */ + void removeView(View view) { + int index = mCallback.indexOfChild(view); + if (index < 0) { + return; + } + if (mBucket.remove(index)) { + mHiddenViews.remove(view); + } + mCallback.removeViewAt(index); + if (DEBUG) { + Log.d(TAG, "remove View off:" + index + "," + this); + } + } + + /** + * Removes the view at the provided index from RecyclerView. + * + * @param index Index of the child from the regular perspective (excluding hidden views). + * ChildHelper offsets this index to actual ViewGroup index. + */ + void removeViewAt(int index) { + final int offset = getOffset(index); + final View view = mCallback.getChildAt(offset); + if (view == null) { + return; + } + if (mBucket.remove(offset)) { + mHiddenViews.remove(view); + } + mCallback.removeViewAt(offset); + if (DEBUG) { + Log.d(TAG, "removeViewAt " + index + ", off:" + offset + ", " + this); + } + } + + /** + * Returns the child at provided index. + * + * @param index Index of the child to return in regular perspective. + */ + View getChildAt(int index) { + final int offset = getOffset(index); + return mCallback.getChildAt(offset); + } + + /** + * Removes all views from the ViewGroup including the hidden ones. + */ + void removeAllViewsUnfiltered() { + mBucket.reset(); + mHiddenViews.clear(); + mCallback.removeAllViews(); + if (DEBUG) { + Log.d(TAG, "removeAllViewsUnfiltered"); + } + } + + /** + * This can be used to find a disappearing view by position. + * + * @param position The adapter position of the item. + * @param type View type, can be {@link RecyclerView#INVALID_TYPE}. + * @return A hidden view with a valid ViewHolder that matches the position and type. + */ + View findHiddenNonRemovedView(int position, int type) { + final int count = mHiddenViews.size(); + 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)) { + return view; + } + } + return null; + } + + /** + * Attaches the provided view to the underlying ViewGroup. + * + * @param child Child to attach. + * @param index Index of the child to attach in regular perspective. + * @param layoutParams LayoutParams for the child. + * @param hidden If set to true, this item will be invisible to the regular methods. + */ + void attachViewToParent(View child, int index, ViewGroup.LayoutParams layoutParams, + boolean hidden) { + final int offset; + if (index < 0) { + offset = mCallback.getChildCount(); + } else { + offset = getOffset(index); + } + mBucket.insert(offset, hidden); + if (hidden) { + mHiddenViews.add(child); + } + mCallback.attachViewToParent(child, offset, layoutParams); + if (DEBUG) { + Log.d(TAG, "attach view to parent index:" + index + ",off:" + offset + "," + + "h:" + hidden + ", " + this); + } + } + + /** + * Returns the number of children that are not hidden. + * + * @return Number of children that are not hidden. + * @see #getChildAt(int) + */ + int getChildCount() { + return mCallback.getChildCount() - mHiddenViews.size(); + } + + /** + * Returns the total number of children. + * + * @return The total number of children including the hidden views. + * @see #getUnfilteredChildAt(int) + */ + int getUnfilteredChildCount() { + return mCallback.getChildCount(); + } + + /** + * Returns a child by ViewGroup offset. ChildHelper won't offset this index. + * + * @param index ViewGroup index of the child to return. + * @return The view in the provided index. + */ + View getUnfilteredChildAt(int index) { + return mCallback.getChildAt(index); + } + + /** + * Detaches the view at the provided index. + * + * @param index Index of the child to return in regular perspective. + */ + void detachViewFromParent(int index) { + final int offset = getOffset(index); + mBucket.remove(offset); + mCallback.detachViewFromParent(offset); + if (DEBUG) { + Log.d(TAG, "detach view from parent " + index + ", off:" + offset); + } + } + + /** + * Returns the index of the child in regular perspective. + * + * @param child The child whose index will be returned. + * @return The regular perspective index of the child or -1 if it does not exists. + */ + int indexOfChild(View child) { + final int index = mCallback.indexOfChild(child); + if (index == -1) { + return -1; + } + if (mBucket.get(index)) { + if (DEBUG) { + throw new IllegalArgumentException("cannot get index of a hidden child"); + } else { + return -1; + } + } + // reverse the index + return index - mBucket.countOnesBefore(index); + } + + /** + * Returns whether a View is visible to LayoutManager or not. + * + * @param view The child view to check. Should be a child of the Callback. + * @return True if the View is not visible to LayoutManager + */ + boolean isHidden(View view) { + return mHiddenViews.contains(view); + } + + /** + * Marks a child view as hidden. + * + * @param view The view to hide. + */ + void hide(View view) { + final int offset = mCallback.indexOfChild(view); + if (offset < 0) { + throw new IllegalArgumentException("view is not a child, cannot hide " + view); + } + if (DEBUG && mBucket.get(offset)) { + throw new RuntimeException("trying to hide same view twice, how come ? " + view); + } + mBucket.set(offset); + mHiddenViews.add(view); + if (DEBUG) { + Log.d(TAG, "hiding child " + view + " at offset " + offset+ ", " + this); + } + } + + @Override + public String toString() { + return mBucket.toString() + ", hidden list:" + mHiddenViews.size(); + } + + /** + * Removes a view from the ViewGroup if it is hidden. + * + * @param view The view to remove. + * @return True if the View is found and it is hidden. False otherwise. + */ + boolean removeViewIfHidden(View view) { + final int index = mCallback.indexOfChild(view); + if (index == -1) { + if (mHiddenViews.remove(view) && DEBUG) { + throw new IllegalStateException("view is in hidden list but not in view group"); + } + return true; + } + if (mBucket.get(index)) { + mBucket.remove(index); + if (!mHiddenViews.remove(view) && DEBUG) { + throw new IllegalStateException( + "removed a hidden view but it is not in hidden views list"); + } + mCallback.removeViewAt(index); + return true; + } + return false; + } + + /** + * Bitset implementation that provides methods to offset indices. + */ + static class Bucket { + + final static int BITS_PER_WORD = Long.SIZE; + + final static long LAST_BIT = 1L << (Long.SIZE - 1); + + long mData = 0; + + Bucket next; + + void set(int index) { + if (index >= BITS_PER_WORD) { + ensureNext(); + next.set(index - BITS_PER_WORD); + } else { + mData |= 1L << index; + } + } + + private void ensureNext() { + if (next == null) { + next = new Bucket(); + } + } + + void clear(int index) { + if (index >= BITS_PER_WORD) { + if (next != null) { + next.clear(index - BITS_PER_WORD); + } + } else { + mData &= ~(1L << index); + } + + } + + boolean get(int index) { + if (index >= BITS_PER_WORD) { + ensureNext(); + return next.get(index - BITS_PER_WORD); + } else { + return (mData & (1L << index)) != 0; + } + } + + void reset() { + mData = 0; + if (next != null) { + next.reset(); + } + } + + void insert(int index, boolean value) { + if (index >= BITS_PER_WORD) { + ensureNext(); + next.insert(index - BITS_PER_WORD, value); + } else { + final boolean lastBit = (mData & LAST_BIT) != 0; + long mask = (1L << index) - 1; + final long before = mData & mask; + final long after = ((mData & ~mask)) << 1; + mData = before | after; + if (value) { + set(index); + } else { + clear(index); + } + if (lastBit || next != null) { + ensureNext(); + next.insert(0, lastBit); + } + } + } + + boolean remove(int index) { + if (index >= BITS_PER_WORD) { + ensureNext(); + return next.remove(index - BITS_PER_WORD); + } else { + long mask = (1L << index); + final boolean value = (mData & mask) != 0; + mData &= ~mask; + mask = mask - 1; + final long before = mData & mask; + // cannot use >> because it adds one. + final long after = Long.rotateRight(mData & ~mask, 1); + mData = before | after; + if (next != null) { + if (next.get(0)) { + set(BITS_PER_WORD - 1); + } + next.remove(0); + } + return value; + } + } + + int countOnesBefore(int index) { + if (next == null) { + if (index >= BITS_PER_WORD) { + return Long.bitCount(mData); + } + return Long.bitCount(mData & ((1L << index) - 1)); + } + if (index < BITS_PER_WORD) { + return Long.bitCount(mData & ((1L << index) - 1)); + } else { + return next.countOnesBefore(index - BITS_PER_WORD) + Long.bitCount(mData); + } + } + + @Override + public String toString() { + return next == null ? Long.toBinaryString(mData) + : next.toString() + "xx" + Long.toBinaryString(mData); + } + } + + static interface Callback { + + int getChildCount(); + + void addView(View child, int index); + + int indexOfChild(View view); + + void removeViewAt(int index); + + View getChildAt(int offset); + + void removeAllViews(); + + RecyclerView.ViewHolder getChildViewHolder(View view); + + void attachViewToParent(View child, int index, ViewGroup.LayoutParams layoutParams); + + void detachViewFromParent(int offset); + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/android/support/widget/DefaultItemAnimator.java b/TMessagesProj/src/main/java/org/telegram/android/support/widget/DefaultItemAnimator.java new file mode 100644 index 000000000..bbbc20a54 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/android/support/widget/DefaultItemAnimator.java @@ -0,0 +1,631 @@ +/* + * Copyright (C) 2014 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.android.support.widget; + +import android.support.v4.view.ViewCompat; +import android.support.v4.view.ViewPropertyAnimatorCompat; +import android.support.v4.view.ViewPropertyAnimatorListener; +import org.telegram.android.support.widget.RecyclerView.ViewHolder; +import android.view.View; + +import java.util.ArrayList; +import java.util.List; + +/** + * This implementation of {@link RecyclerView.ItemAnimator} provides basic + * animations on remove, add, and move events that happen to the items in + * a RecyclerView. RecyclerView uses a DefaultItemAnimator by default. + * + * @see RecyclerView#setItemAnimator(RecyclerView.ItemAnimator) + */ +public class DefaultItemAnimator extends RecyclerView.ItemAnimator { + private static final boolean DEBUG = false; + + private ArrayList mPendingRemovals = new ArrayList(); + private ArrayList mPendingAdditions = new ArrayList(); + private ArrayList mPendingMoves = new ArrayList(); + private ArrayList mPendingChanges = new ArrayList(); + + private ArrayList> mAdditionsList = + new ArrayList>(); + private ArrayList> mMovesList = new ArrayList>(); + private ArrayList> mChangesList = new ArrayList>(); + + private ArrayList mAddAnimations = new ArrayList(); + private ArrayList mMoveAnimations = new ArrayList(); + private ArrayList mRemoveAnimations = new ArrayList(); + private ArrayList mChangeAnimations = new ArrayList(); + + private static class MoveInfo { + public ViewHolder holder; + public int fromX, fromY, toX, toY; + + private MoveInfo(ViewHolder holder, int fromX, int fromY, int toX, int toY) { + this.holder = holder; + this.fromX = fromX; + this.fromY = fromY; + this.toX = toX; + this.toY = toY; + } + } + + private static class ChangeInfo { + public ViewHolder oldHolder, newHolder; + public int fromX, fromY, toX, toY; + private ChangeInfo(ViewHolder oldHolder, ViewHolder newHolder) { + this.oldHolder = oldHolder; + this.newHolder = newHolder; + } + + private ChangeInfo(ViewHolder oldHolder, ViewHolder newHolder, + int fromX, int fromY, int toX, int toY) { + this(oldHolder, newHolder); + this.fromX = fromX; + this.fromY = fromY; + this.toX = toX; + this.toY = toY; + } + + @Override + public String toString() { + return "ChangeInfo{" + + "oldHolder=" + oldHolder + + ", newHolder=" + newHolder + + ", fromX=" + fromX + + ", fromY=" + fromY + + ", toX=" + toX + + ", toY=" + toY + + '}'; + } + } + + @Override + public void runPendingAnimations() { + boolean removalsPending = !mPendingRemovals.isEmpty(); + boolean movesPending = !mPendingMoves.isEmpty(); + boolean changesPending = !mPendingChanges.isEmpty(); + boolean additionsPending = !mPendingAdditions.isEmpty(); + if (!removalsPending && !movesPending && !additionsPending && !changesPending) { + // nothing to animate + return; + } + // First, remove stuff + for (ViewHolder holder : mPendingRemovals) { + animateRemoveImpl(holder); + } + mPendingRemovals.clear(); + // Next, move stuff + if (movesPending) { + final ArrayList moves = new ArrayList(); + moves.addAll(mPendingMoves); + mMovesList.add(moves); + mPendingMoves.clear(); + Runnable mover = new Runnable() { + @Override + public void run() { + for (MoveInfo moveInfo : moves) { + animateMoveImpl(moveInfo.holder, moveInfo.fromX, moveInfo.fromY, + moveInfo.toX, moveInfo.toY); + } + moves.clear(); + mMovesList.remove(moves); + } + }; + if (removalsPending) { + View view = moves.get(0).holder.itemView; + ViewCompat.postOnAnimationDelayed(view, mover, getRemoveDuration()); + } else { + mover.run(); + } + } + // Next, change stuff, to run in parallel with move animations + if (changesPending) { + final ArrayList changes = new ArrayList(); + changes.addAll(mPendingChanges); + mChangesList.add(changes); + mPendingChanges.clear(); + Runnable changer = new Runnable() { + @Override + public void run() { + for (ChangeInfo change : changes) { + animateChangeImpl(change); + } + changes.clear(); + mChangesList.remove(changes); + } + }; + if (removalsPending) { + ViewHolder holder = changes.get(0).oldHolder; + ViewCompat.postOnAnimationDelayed(holder.itemView, changer, getRemoveDuration()); + } else { + changer.run(); + } + } + // Next, add stuff + if (additionsPending) { + final ArrayList additions = new ArrayList(); + additions.addAll(mPendingAdditions); + mAdditionsList.add(additions); + mPendingAdditions.clear(); + Runnable adder = new Runnable() { + public void run() { + for (ViewHolder holder : additions) { + animateAddImpl(holder); + } + additions.clear(); + mAdditionsList.remove(additions); + } + }; + if (removalsPending || movesPending || changesPending) { + long removeDuration = removalsPending ? getRemoveDuration() : 0; + long moveDuration = movesPending ? getMoveDuration() : 0; + long changeDuration = changesPending ? getChangeDuration() : 0; + long totalDelay = removeDuration + Math.max(moveDuration, changeDuration); + View view = additions.get(0).itemView; + ViewCompat.postOnAnimationDelayed(view, adder, totalDelay); + } else { + adder.run(); + } + } + } + + @Override + public boolean animateRemove(final ViewHolder holder) { + endAnimation(holder); + mPendingRemovals.add(holder); + return true; + } + + private void animateRemoveImpl(final ViewHolder holder) { + final View view = holder.itemView; + final ViewPropertyAnimatorCompat animation = ViewCompat.animate(view); + mRemoveAnimations.add(holder); + animation.setDuration(getRemoveDuration()) + .alpha(0).setListener(new VpaListenerAdapter() { + @Override + public void onAnimationStart(View view) { + dispatchRemoveStarting(holder); + } + + @Override + public void onAnimationEnd(View view) { + animation.setListener(null); + ViewCompat.setAlpha(view, 1); + dispatchRemoveFinished(holder); + mRemoveAnimations.remove(holder); + dispatchFinishedWhenDone(); + } + }).start(); + } + + @Override + public boolean animateAdd(final ViewHolder holder) { + endAnimation(holder); + ViewCompat.setAlpha(holder.itemView, 0); + mPendingAdditions.add(holder); + return true; + } + + private void animateAddImpl(final ViewHolder holder) { + final View view = holder.itemView; + final ViewPropertyAnimatorCompat animation = ViewCompat.animate(view); + mAddAnimations.add(holder); + animation.alpha(1).setDuration(getAddDuration()). + setListener(new VpaListenerAdapter() { + @Override + public void onAnimationStart(View view) { + dispatchAddStarting(holder); + } + @Override + public void onAnimationCancel(View view) { + ViewCompat.setAlpha(view, 1); + } + + @Override + public void onAnimationEnd(View view) { + animation.setListener(null); + dispatchAddFinished(holder); + mAddAnimations.remove(holder); + dispatchFinishedWhenDone(); + } + }).start(); + } + + @Override + public boolean animateMove(final ViewHolder holder, int fromX, int fromY, + int toX, int toY) { + final View view = holder.itemView; + fromX += ViewCompat.getTranslationX(holder.itemView); + fromY += ViewCompat.getTranslationY(holder.itemView); + endAnimation(holder); + int deltaX = toX - fromX; + int deltaY = toY - fromY; + if (deltaX == 0 && deltaY == 0) { + dispatchMoveFinished(holder); + return false; + } + if (deltaX != 0) { + ViewCompat.setTranslationX(view, -deltaX); + } + if (deltaY != 0) { + ViewCompat.setTranslationY(view, -deltaY); + } + mPendingMoves.add(new MoveInfo(holder, fromX, fromY, toX, toY)); + return true; + } + + private void animateMoveImpl(final ViewHolder holder, int fromX, int fromY, int toX, int toY) { + final View view = holder.itemView; + final int deltaX = toX - fromX; + final int deltaY = toY - fromY; + if (deltaX != 0) { + ViewCompat.animate(view).translationX(0); + } + if (deltaY != 0) { + ViewCompat.animate(view).translationY(0); + } + // TODO: make EndActions end listeners instead, since end actions aren't called when + // vpas are canceled (and can't end them. why?) + // need listener functionality in VPACompat for this. Ick. + final ViewPropertyAnimatorCompat animation = ViewCompat.animate(view); + mMoveAnimations.add(holder); + animation.setDuration(getMoveDuration()).setListener(new VpaListenerAdapter() { + @Override + public void onAnimationStart(View view) { + dispatchMoveStarting(holder); + } + @Override + public void onAnimationCancel(View view) { + if (deltaX != 0) { + ViewCompat.setTranslationX(view, 0); + } + if (deltaY != 0) { + ViewCompat.setTranslationY(view, 0); + } + } + @Override + public void onAnimationEnd(View view) { + animation.setListener(null); + dispatchMoveFinished(holder); + mMoveAnimations.remove(holder); + dispatchFinishedWhenDone(); + } + }).start(); + } + + @Override + public boolean animateChange(ViewHolder oldHolder, ViewHolder newHolder, + int fromX, int fromY, int toX, int toY) { + final float prevTranslationX = ViewCompat.getTranslationX(oldHolder.itemView); + final float prevTranslationY = ViewCompat.getTranslationY(oldHolder.itemView); + final float prevAlpha = ViewCompat.getAlpha(oldHolder.itemView); + endAnimation(oldHolder); + int deltaX = (int) (toX - fromX - prevTranslationX); + int deltaY = (int) (toY - fromY - prevTranslationY); + // recover prev translation state after ending animation + ViewCompat.setTranslationX(oldHolder.itemView, prevTranslationX); + ViewCompat.setTranslationY(oldHolder.itemView, prevTranslationY); + ViewCompat.setAlpha(oldHolder.itemView, prevAlpha); + if (newHolder != null && newHolder.itemView != null) { + // carry over translation values + endAnimation(newHolder); + ViewCompat.setTranslationX(newHolder.itemView, -deltaX); + ViewCompat.setTranslationY(newHolder.itemView, -deltaY); + ViewCompat.setAlpha(newHolder.itemView, 0); + } + mPendingChanges.add(new ChangeInfo(oldHolder, newHolder, fromX, fromY, toX, toY)); + return true; + } + + private void animateChangeImpl(final ChangeInfo changeInfo) { + final ViewHolder holder = changeInfo.oldHolder; + final View view = holder == null ? null : holder.itemView; + final ViewHolder newHolder = changeInfo.newHolder; + final View newView = newHolder != null ? newHolder.itemView : null; + if (view != null) { + final ViewPropertyAnimatorCompat oldViewAnim = ViewCompat.animate(view).setDuration( + getChangeDuration()); + mChangeAnimations.add(changeInfo.oldHolder); + oldViewAnim.translationX(changeInfo.toX - changeInfo.fromX); + oldViewAnim.translationY(changeInfo.toY - changeInfo.fromY); + oldViewAnim.alpha(0).setListener(new VpaListenerAdapter() { + @Override + public void onAnimationStart(View view) { + dispatchChangeStarting(changeInfo.oldHolder, true); + } + + @Override + public void onAnimationEnd(View view) { + oldViewAnim.setListener(null); + ViewCompat.setAlpha(view, 1); + ViewCompat.setTranslationX(view, 0); + ViewCompat.setTranslationY(view, 0); + dispatchChangeFinished(changeInfo.oldHolder, true); + mChangeAnimations.remove(changeInfo.oldHolder); + dispatchFinishedWhenDone(); + } + }).start(); + } + if (newView != null) { + final ViewPropertyAnimatorCompat newViewAnimation = ViewCompat.animate(newView); + mChangeAnimations.add(changeInfo.newHolder); + newViewAnimation.translationX(0).translationY(0).setDuration(getChangeDuration()). + alpha(1).setListener(new VpaListenerAdapter() { + @Override + public void onAnimationStart(View view) { + dispatchChangeStarting(changeInfo.newHolder, false); + } + @Override + public void onAnimationEnd(View view) { + newViewAnimation.setListener(null); + ViewCompat.setAlpha(newView, 1); + ViewCompat.setTranslationX(newView, 0); + ViewCompat.setTranslationY(newView, 0); + dispatchChangeFinished(changeInfo.newHolder, false); + mChangeAnimations.remove(changeInfo.newHolder); + dispatchFinishedWhenDone(); + } + }).start(); + } + } + + private void endChangeAnimation(List infoList, ViewHolder item) { + for (int i = infoList.size() - 1; i >= 0; i--) { + ChangeInfo changeInfo = infoList.get(i); + if (endChangeAnimationIfNecessary(changeInfo, item)) { + if (changeInfo.oldHolder == null && changeInfo.newHolder == null) { + infoList.remove(changeInfo); + } + } + } + } + + private void endChangeAnimationIfNecessary(ChangeInfo changeInfo) { + if (changeInfo.oldHolder != null) { + endChangeAnimationIfNecessary(changeInfo, changeInfo.oldHolder); + } + if (changeInfo.newHolder != null) { + endChangeAnimationIfNecessary(changeInfo, changeInfo.newHolder); + } + } + private boolean endChangeAnimationIfNecessary(ChangeInfo changeInfo, ViewHolder item) { + boolean oldItem = false; + if (changeInfo.newHolder == item) { + changeInfo.newHolder = null; + } else if (changeInfo.oldHolder == item) { + changeInfo.oldHolder = null; + oldItem = true; + } else { + return false; + } + ViewCompat.setAlpha(item.itemView, 1); + ViewCompat.setTranslationX(item.itemView, 0); + ViewCompat.setTranslationY(item.itemView, 0); + dispatchChangeFinished(item, oldItem); + return true; + } + + @Override + public void endAnimation(ViewHolder item) { + final View view = item.itemView; + // this will trigger end callback which should set properties to their target values. + ViewCompat.animate(view).cancel(); + // TODO if some other animations are chained to end, how do we cancel them as well? + for (int i = mPendingMoves.size() - 1; i >= 0; i--) { + MoveInfo moveInfo = mPendingMoves.get(i); + if (moveInfo.holder == item) { + ViewCompat.setTranslationY(view, 0); + ViewCompat.setTranslationX(view, 0); + dispatchMoveFinished(item); + mPendingMoves.remove(i); + } + } + endChangeAnimation(mPendingChanges, item); + if (mPendingRemovals.remove(item)) { + ViewCompat.setAlpha(view, 1); + dispatchRemoveFinished(item); + } + if (mPendingAdditions.remove(item)) { + ViewCompat.setAlpha(view, 1); + dispatchAddFinished(item); + } + + for (int i = mChangesList.size() - 1; i >= 0; i--) { + ArrayList changes = mChangesList.get(i); + endChangeAnimation(changes, item); + if (changes.isEmpty()) { + mChangesList.remove(i); + } + } + for (int i = mMovesList.size() - 1; i >= 0; i--) { + ArrayList moves = mMovesList.get(i); + for (int j = moves.size() - 1; j >= 0; j--) { + MoveInfo moveInfo = moves.get(j); + if (moveInfo.holder == item) { + ViewCompat.setTranslationY(view, 0); + ViewCompat.setTranslationX(view, 0); + dispatchMoveFinished(item); + moves.remove(j); + if (moves.isEmpty()) { + mMovesList.remove(i); + } + break; + } + } + } + for (int i = mAdditionsList.size() - 1; i >= 0; i--) { + ArrayList additions = mAdditionsList.get(i); + if (additions.remove(item)) { + ViewCompat.setAlpha(view, 1); + dispatchAddFinished(item); + if (additions.isEmpty()) { + mAdditionsList.remove(i); + } + } + } + + // animations should be ended by the cancel above. + if (mRemoveAnimations.remove(item) && DEBUG) { + throw new IllegalStateException("after animation is cancelled, item should not be in " + + "mRemoveAnimations list"); + } + + if (mAddAnimations.remove(item) && DEBUG) { + throw new IllegalStateException("after animation is cancelled, item should not be in " + + "mAddAnimations list"); + } + + if (mChangeAnimations.remove(item) && DEBUG) { + throw new IllegalStateException("after animation is cancelled, item should not be in " + + "mChangeAnimations list"); + } + + if (mMoveAnimations.remove(item) && DEBUG) { + throw new IllegalStateException("after animation is cancelled, item should not be in " + + "mMoveAnimations list"); + } + dispatchFinishedWhenDone(); + } + + @Override + public boolean isRunning() { + return (!mPendingAdditions.isEmpty() || + !mPendingChanges.isEmpty() || + !mPendingMoves.isEmpty() || + !mPendingRemovals.isEmpty() || + !mMoveAnimations.isEmpty() || + !mRemoveAnimations.isEmpty() || + !mAddAnimations.isEmpty() || + !mChangeAnimations.isEmpty() || + !mMovesList.isEmpty() || + !mAdditionsList.isEmpty() || + !mChangesList.isEmpty()); + } + + /** + * Check the state of currently pending and running animations. If there are none + * pending/running, call {@link #dispatchAnimationsFinished()} to notify any + * listeners. + */ + private void dispatchFinishedWhenDone() { + if (!isRunning()) { + dispatchAnimationsFinished(); + } + } + + @Override + public void endAnimations() { + int count = mPendingMoves.size(); + for (int i = count - 1; i >= 0; i--) { + MoveInfo item = mPendingMoves.get(i); + View view = item.holder.itemView; + ViewCompat.setTranslationY(view, 0); + ViewCompat.setTranslationX(view, 0); + dispatchMoveFinished(item.holder); + mPendingMoves.remove(i); + } + count = mPendingRemovals.size(); + for (int i = count - 1; i >= 0; i--) { + ViewHolder item = mPendingRemovals.get(i); + dispatchRemoveFinished(item); + mPendingRemovals.remove(i); + } + count = mPendingAdditions.size(); + for (int i = count - 1; i >= 0; i--) { + ViewHolder item = mPendingAdditions.get(i); + View view = item.itemView; + ViewCompat.setAlpha(view, 1); + dispatchAddFinished(item); + mPendingAdditions.remove(i); + } + count = mPendingChanges.size(); + for (int i = count - 1; i >= 0; i--) { + endChangeAnimationIfNecessary(mPendingChanges.get(i)); + } + mPendingChanges.clear(); + if (!isRunning()) { + return; + } + + int listCount = mMovesList.size(); + for (int i = listCount - 1; i >= 0; i--) { + ArrayList moves = mMovesList.get(i); + count = moves.size(); + for (int j = count - 1; j >= 0; j--) { + MoveInfo moveInfo = moves.get(j); + ViewHolder item = moveInfo.holder; + View view = item.itemView; + ViewCompat.setTranslationY(view, 0); + ViewCompat.setTranslationX(view, 0); + dispatchMoveFinished(moveInfo.holder); + moves.remove(j); + if (moves.isEmpty()) { + mMovesList.remove(moves); + } + } + } + listCount = mAdditionsList.size(); + for (int i = listCount - 1; i >= 0; i--) { + ArrayList additions = mAdditionsList.get(i); + count = additions.size(); + for (int j = count - 1; j >= 0; j--) { + ViewHolder item = additions.get(j); + View view = item.itemView; + ViewCompat.setAlpha(view, 1); + dispatchAddFinished(item); + additions.remove(j); + if (additions.isEmpty()) { + mAdditionsList.remove(additions); + } + } + } + listCount = mChangesList.size(); + for (int i = listCount - 1; i >= 0; i--) { + ArrayList changes = mChangesList.get(i); + count = changes.size(); + for (int j = count - 1; j >= 0; j--) { + endChangeAnimationIfNecessary(changes.get(j)); + if (changes.isEmpty()) { + mChangesList.remove(changes); + } + } + } + + cancelAll(mRemoveAnimations); + cancelAll(mMoveAnimations); + cancelAll(mAddAnimations); + cancelAll(mChangeAnimations); + + dispatchAnimationsFinished(); + } + + void cancelAll(List viewHolders) { + for (int i = viewHolders.size() - 1; i >= 0; i--) { + ViewCompat.animate(viewHolders.get(i).itemView).cancel(); + } + } + + private static class VpaListenerAdapter implements ViewPropertyAnimatorListener { + @Override + public void onAnimationStart(View view) {} + + @Override + public void onAnimationEnd(View view) {} + + @Override + public void onAnimationCancel(View view) {} + }; +} diff --git a/TMessagesProj/src/main/java/org/telegram/android/support/widget/GridLayoutManager.java b/TMessagesProj/src/main/java/org/telegram/android/support/widget/GridLayoutManager.java new file mode 100644 index 000000000..790bf06f8 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/android/support/widget/GridLayoutManager.java @@ -0,0 +1,887 @@ +/* + * Copyright (C) 2014 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 languag`e governing permissions and + * limitations under the License. + */ +package org.telegram.android.support.widget; + +import android.content.Context; +import android.graphics.Rect; +import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat; +import android.util.AttributeSet; +import android.util.Log; +import android.util.SparseIntArray; +import android.view.View; +import android.view.ViewGroup; + +import java.util.Arrays; + +/** + * A {@link RecyclerView.LayoutManager} implementations that lays out items in a grid. + *

+ * By default, each item occupies 1 span. You can change it by providing a custom + * {@link SpanSizeLookup} instance via {@link #setSpanSizeLookup(SpanSizeLookup)}. + */ +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. + */ + boolean mPendingSpanCountChange = false; + int mSpanCount = DEFAULT_SPAN_COUNT; + /** + * Right borders for each span. + *

For i-th item start is {@link #mCachedBorders}[i-1] + 1 + * and end is {@link #mCachedBorders}[i]. + */ + int [] mCachedBorders; + /** + * Temporary array to keep views in layoutChunk method + */ + View[] mSet; + final SparseIntArray mPreLayoutSpanSizeCache = new SparseIntArray(); + final SparseIntArray mPreLayoutSpanIndexCache = new SparseIntArray(); + SpanSizeLookup mSpanSizeLookup = new DefaultSpanSizeLookup(); + // re-used variable to acquire decor insets from RecyclerView + final Rect mDecorInsets = new Rect(); + + /** + * Creates a vertical GridLayoutManager + * + * @param context Current context, will be used to access resources. + * @param spanCount The number of columns in the grid + */ + public GridLayoutManager(Context context, int spanCount) { + super(context); + setSpanCount(spanCount); + } + + /** + * @param context Current context, will be used to access resources. + * @param spanCount The number of columns or rows in the grid + * @param orientation Layout orientation. Should be {@link #HORIZONTAL} or {@link + * #VERTICAL}. + * @param reverseLayout When set to true, layouts from end to start. + */ + public GridLayoutManager(Context context, int spanCount, int orientation, + boolean reverseLayout) { + super(context, orientation, reverseLayout); + setSpanCount(spanCount); + } + + /** + * stackFromEnd is not supported by GridLayoutManager. Consider using + * {@link #setReverseLayout(boolean)}. + */ + @Override + public void setStackFromEnd(boolean stackFromEnd) { + if (stackFromEnd) { + throw new UnsupportedOperationException( + "GridLayoutManager does not support stack from end." + + " Consider using reverse layout"); + } + super.setStackFromEnd(false); + } + + @Override + public int getRowCountForAccessibility(RecyclerView.Recycler recycler, + RecyclerView.State state) { + if (mOrientation == HORIZONTAL) { + return mSpanCount; + } + if (state.getItemCount() < 1) { + return 0; + } + return getSpanGroupIndex(recycler, state, state.getItemCount() - 1); + } + + @Override + public int getColumnCountForAccessibility(RecyclerView.Recycler recycler, + RecyclerView.State state) { + if (mOrientation == VERTICAL) { + return mSpanCount; + } + if (state.getItemCount() < 1) { + return 0; + } + return getSpanGroupIndex(recycler, state, state.getItemCount() - 1); + } + + @Override + public void onInitializeAccessibilityNodeInfoForItem(RecyclerView.Recycler recycler, + RecyclerView.State state, View host, AccessibilityNodeInfoCompat info) { + ViewGroup.LayoutParams lp = host.getLayoutParams(); + if (!(lp instanceof LayoutParams)) { + super.onInitializeAccessibilityNodeInfoForItem(host, info); + return; + } + LayoutParams glp = (LayoutParams) lp; + int spanGroupIndex = getSpanGroupIndex(recycler, state, glp.getViewLayoutPosition()); + if (mOrientation == HORIZONTAL) { + info.setCollectionItemInfo(AccessibilityNodeInfoCompat.CollectionItemInfoCompat.obtain( + glp.getSpanIndex(), glp.getSpanSize(), + spanGroupIndex, 1, + mSpanCount > 1 && glp.getSpanSize() == mSpanCount, false)); + } else { // VERTICAL + info.setCollectionItemInfo(AccessibilityNodeInfoCompat.CollectionItemInfoCompat.obtain( + spanGroupIndex , 1, + glp.getSpanIndex(), glp.getSpanSize(), + mSpanCount > 1 && glp.getSpanSize() == mSpanCount, false)); + } + } + + @Override + public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) { + if (state.isPreLayout()) { + cachePreLayoutSpanMapping(); + } + super.onLayoutChildren(recycler, state); + if (DEBUG) { + validateChildOrder(); + } + clearPreLayoutSpanMappingCache(); + if (!state.isPreLayout()) { + mPendingSpanCountChange = false; + } + } + + private void clearPreLayoutSpanMappingCache() { + mPreLayoutSpanSizeCache.clear(); + mPreLayoutSpanIndexCache.clear(); + } + + private void cachePreLayoutSpanMapping() { + final int childCount = getChildCount(); + for (int i = 0; i < childCount; i++) { + final LayoutParams lp = (LayoutParams) getChildAt(i).getLayoutParams(); + final int viewPosition = lp.getViewLayoutPosition(); + mPreLayoutSpanSizeCache.put(viewPosition, lp.getSpanSize()); + mPreLayoutSpanIndexCache.put(viewPosition, lp.getSpanIndex()); + } + } + + @Override + public void onItemsAdded(RecyclerView recyclerView, int positionStart, int itemCount) { + mSpanSizeLookup.invalidateSpanIndexCache(); + } + + @Override + public void onItemsChanged(RecyclerView recyclerView) { + mSpanSizeLookup.invalidateSpanIndexCache(); + } + + @Override + public void onItemsRemoved(RecyclerView recyclerView, int positionStart, int itemCount) { + mSpanSizeLookup.invalidateSpanIndexCache(); + } + + @Override + public void onItemsUpdated(RecyclerView recyclerView, int positionStart, int itemCount) { + mSpanSizeLookup.invalidateSpanIndexCache(); + } + + @Override + public void onItemsMoved(RecyclerView recyclerView, int from, int to, int itemCount) { + mSpanSizeLookup.invalidateSpanIndexCache(); + } + + @Override + public RecyclerView.LayoutParams generateDefaultLayoutParams() { + return new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, + ViewGroup.LayoutParams.WRAP_CONTENT); + } + + @Override + public RecyclerView.LayoutParams generateLayoutParams(Context c, AttributeSet attrs) { + return new LayoutParams(c, attrs); + } + + @Override + public RecyclerView.LayoutParams generateLayoutParams(ViewGroup.LayoutParams lp) { + if (lp instanceof ViewGroup.MarginLayoutParams) { + return new LayoutParams((ViewGroup.MarginLayoutParams) lp); + } else { + return new LayoutParams(lp); + } + } + + @Override + public boolean checkLayoutParams(RecyclerView.LayoutParams lp) { + return lp instanceof LayoutParams; + } + + /** + * Sets the source to get the number of spans occupied by each item in the adapter. + * + * @param spanSizeLookup {@link SpanSizeLookup} instance to be used to query number of spans + * occupied by each item + */ + public void setSpanSizeLookup(SpanSizeLookup spanSizeLookup) { + mSpanSizeLookup = spanSizeLookup; + } + + /** + * Returns the current {@link SpanSizeLookup} used by the GridLayoutManager. + * + * @return The current {@link SpanSizeLookup} used by the GridLayoutManager. + */ + public SpanSizeLookup getSpanSizeLookup() { + return mSpanSizeLookup; + } + + private void updateMeasurements() { + int totalSpace; + if (getOrientation() == VERTICAL) { + totalSpace = getWidth() - getPaddingRight() - getPaddingLeft(); + } else { + totalSpace = getHeight() - getPaddingBottom() - getPaddingTop(); + } + calculateItemBorders(totalSpace); + } + + private void calculateItemBorders(int totalSpace) { + if (mCachedBorders == null || mCachedBorders.length != mSpanCount + 1 + || mCachedBorders[mCachedBorders.length - 1] != totalSpace) { + mCachedBorders = new int[mSpanCount + 1]; + } + mCachedBorders[0] = 0; + int sizePerSpan = totalSpace / mSpanCount; + int sizePerSpanRemainder = totalSpace % mSpanCount; + int consumedPixels = 0; + int additionalSize = 0; + for (int i = 1; i <= mSpanCount; i++) { + int itemSize = sizePerSpan; + additionalSize += sizePerSpanRemainder; + if (additionalSize > 0 && (mSpanCount - additionalSize) < sizePerSpanRemainder) { + itemSize += 1; + additionalSize -= mSpanCount; + } + consumedPixels += itemSize; + mCachedBorders[i] = consumedPixels; + } + } + + @Override + void onAnchorReady(RecyclerView.State state, AnchorInfo anchorInfo) { + super.onAnchorReady(state, anchorInfo); + updateMeasurements(); + if (state.getItemCount() > 0 && !state.isPreLayout()) { + ensureAnchorIsInFirstSpan(anchorInfo); + } + if (mSet == null || mSet.length != mSpanCount) { + mSet = new View[mSpanCount]; + } + } + + private void ensureAnchorIsInFirstSpan(AnchorInfo anchorInfo) { + int span = mSpanSizeLookup.getCachedSpanIndex(anchorInfo.mPosition, mSpanCount); + while (span > 0 && anchorInfo.mPosition > 0) { + anchorInfo.mPosition--; + span = mSpanSizeLookup.getCachedSpanIndex(anchorInfo.mPosition, mSpanCount); + } + } + + @Override + View findReferenceChild(int start, int end, int itemCount) { + ensureLayoutState(); + View invalidMatch = null; + View outOfBoundsMatch = null; + final int boundsStart = mOrientationHelper.getStartAfterPadding(); + final int boundsEnd = mOrientationHelper.getEndAfterPadding(); + final int diff = end > start ? 1 : -1; + for (int i = start; i != end; i += diff) { + final View view = getChildAt(i); + final int position = getPosition(view); + if (position >= 0 && position < itemCount) { + final int span = mSpanSizeLookup.getCachedSpanIndex(position, mSpanCount); + if (span != 0) { + continue; + } + if (((RecyclerView.LayoutParams) view.getLayoutParams()).isItemRemoved()) { + if (invalidMatch == null) { + invalidMatch = view; // removed item, least preferred + } + } else if (mOrientationHelper.getDecoratedStart(view) >= boundsEnd || + mOrientationHelper.getDecoratedEnd(view) < boundsStart) { + if (outOfBoundsMatch == null) { + outOfBoundsMatch = view; // item is not visible, less preferred + } + } else { + return view; + } + } + } + return outOfBoundsMatch != null ? outOfBoundsMatch : invalidMatch; + } + + private int getSpanGroupIndex(RecyclerView.Recycler recycler, RecyclerView.State state, + int viewPosition) { + if (!state.isPreLayout()) { + return mSpanSizeLookup.getSpanGroupIndex(viewPosition, mSpanCount); + } + final int adapterPosition = recycler.convertPreLayoutPositionToPostLayout(viewPosition); + if (adapterPosition == -1) { + if (DEBUG) { + throw new RuntimeException("Cannot find span group index for position " + + viewPosition); + } + Log.w(TAG, "Cannot find span size for pre layout position. " + viewPosition); + return 0; + } + return mSpanSizeLookup.getSpanGroupIndex(adapterPosition, mSpanCount); + } + + private int getSpanIndex(RecyclerView.Recycler recycler, RecyclerView.State state, int pos) { + if (!state.isPreLayout()) { + return mSpanSizeLookup.getCachedSpanIndex(pos, mSpanCount); + } + final int cached = mPreLayoutSpanIndexCache.get(pos, -1); + if (cached != -1) { + return cached; + } + final int adapterPosition = recycler.convertPreLayoutPositionToPostLayout(pos); + if (adapterPosition == -1) { + if (DEBUG) { + throw new RuntimeException("Cannot find span index for pre layout position. It is" + + " not cached, not in the adapter. Pos:" + pos); + } + Log.w(TAG, "Cannot find span size for pre layout position. It is" + + " not cached, not in the adapter. Pos:" + pos); + return 0; + } + return mSpanSizeLookup.getCachedSpanIndex(adapterPosition, mSpanCount); + } + + private int getSpanSize(RecyclerView.Recycler recycler, RecyclerView.State state, int pos) { + if (!state.isPreLayout()) { + return mSpanSizeLookup.getSpanSize(pos); + } + final int cached = mPreLayoutSpanSizeCache.get(pos, -1); + if (cached != -1) { + return cached; + } + final int adapterPosition = recycler.convertPreLayoutPositionToPostLayout(pos); + if (adapterPosition == -1) { + if (DEBUG) { + throw new RuntimeException("Cannot find span size for pre layout position. It is" + + " not cached, not in the adapter. Pos:" + pos); + } + Log.w(TAG, "Cannot find span size for pre layout position. It is" + + " not cached, not in the adapter. Pos:" + pos); + return 1; + } + return mSpanSizeLookup.getSpanSize(adapterPosition); + } + + @Override + void layoutChunk(RecyclerView.Recycler recycler, RecyclerView.State state, + LayoutState layoutState, LayoutChunkResult result) { + final boolean layingOutInPrimaryDirection = + layoutState.mItemDirection == LayoutState.ITEM_DIRECTION_TAIL; + int count = 0; + int consumedSpanCount = 0; + int remainingSpan = mSpanCount; + if (!layingOutInPrimaryDirection) { + int itemSpanIndex = getSpanIndex(recycler, state, layoutState.mCurrentPosition); + int itemSpanSize = getSpanSize(recycler, state, layoutState.mCurrentPosition); + remainingSpan = itemSpanIndex + itemSpanSize; + } + while (count < mSpanCount && layoutState.hasMore(state) && remainingSpan > 0) { + int pos = layoutState.mCurrentPosition; + final int spanSize = getSpanSize(recycler, state, pos); + if (spanSize > mSpanCount) { + throw new IllegalArgumentException("Item at position " + pos + " requires " + + spanSize + " spans but GridLayoutManager has only " + mSpanCount + + " spans."); + } + remainingSpan -= spanSize; + if (remainingSpan < 0) { + break; // item did not fit into this row or column + } + View view = layoutState.next(recycler); + if (view == null) { + break; + } + consumedSpanCount += spanSize; + mSet[count] = view; + count++; + } + + if (count == 0) { + result.mFinished = true; + return; + } + + int maxSize = 0; + + // we should assign spans before item decor offsets are calculated + assignSpans(recycler, state, count, consumedSpanCount, layingOutInPrimaryDirection); + for (int i = 0; i < count; i++) { + View view = mSet[i]; + if (layoutState.mScrapList == null) { + if (layingOutInPrimaryDirection) { + addView(view); + } else { + addView(view, 0); + } + } else { + if (layingOutInPrimaryDirection) { + addDisappearingView(view); + } else { + addDisappearingView(view, 0); + } + } + + final LayoutParams lp = (LayoutParams) view.getLayoutParams(); + final int spec = View.MeasureSpec.makeMeasureSpec( + mCachedBorders[lp.mSpanIndex + lp.mSpanSize] - + mCachedBorders[lp.mSpanIndex], + View.MeasureSpec.EXACTLY); + if (mOrientation == VERTICAL) { + measureChildWithDecorationsAndMargin(view, spec, getMainDirSpec(lp.height)); + } else { + measureChildWithDecorationsAndMargin(view, getMainDirSpec(lp.width), spec); + } + final int size = mOrientationHelper.getDecoratedMeasurement(view); + if (size > maxSize) { + maxSize = size; + } + } + + // views that did not measure the maxSize has to be re-measured + final int maxMeasureSpec = getMainDirSpec(maxSize); + 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); + if (mOrientation == VERTICAL) { + measureChildWithDecorationsAndMargin(view, spec, maxMeasureSpec); + } else { + measureChildWithDecorationsAndMargin(view, maxMeasureSpec, spec); + } + } + } + + result.mConsumed = maxSize; + + int left = 0, right = 0, top = 0, bottom = 0; + if (mOrientation == VERTICAL) { + if (layoutState.mLayoutDirection == LayoutState.LAYOUT_START) { + bottom = layoutState.mOffset; + top = bottom - maxSize; + } else { + top = layoutState.mOffset; + bottom = top + maxSize; + } + } else { + if (layoutState.mLayoutDirection == LayoutState.LAYOUT_START) { + right = layoutState.mOffset; + left = right - maxSize; + } else { + left = layoutState.mOffset; + right = left + maxSize; + } + } + for (int i = 0; i < count; i++) { + View view = mSet[i]; + LayoutParams params = (LayoutParams) view.getLayoutParams(); + if (mOrientation == VERTICAL) { + left = getPaddingLeft() + mCachedBorders[params.mSpanIndex]; + right = left + mOrientationHelper.getDecoratedMeasurementInOther(view); + } else { + top = getPaddingTop() + mCachedBorders[params.mSpanIndex]; + bottom = top + mOrientationHelper.getDecoratedMeasurementInOther(view); + } + // We calculate everything with View's bounding box (which includes decor and margins) + // To calculate correct layout position, we subtract margins. + layoutDecorated(view, left + params.leftMargin, top + params.topMargin, + right - params.rightMargin, bottom - params.bottomMargin); + if (DEBUG) { + Log.d(TAG, "laid out child at position " + getPosition(view) + ", with l:" + + (left + params.leftMargin) + ", t:" + (top + params.topMargin) + ", r:" + + (right - params.rightMargin) + ", b:" + (bottom - params.bottomMargin) + + ", span:" + params.mSpanIndex + ", spanSize:" + params.mSpanSize); + } + // Consume the available space if the view is not removed OR changed + if (params.isItemRemoved() || params.isItemChanged()) { + result.mIgnoreConsumed = true; + } + result.mFocusable |= view.isFocusable(); + } + 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); + } + } + + private void measureChildWithDecorationsAndMargin(View child, int widthSpec, int heightSpec) { + calculateItemDecorationsForChild(child, mDecorInsets); + RecyclerView.LayoutParams lp = (RecyclerView.LayoutParams) child.getLayoutParams(); + widthSpec = updateSpecWithExtra(widthSpec, lp.leftMargin + mDecorInsets.left, + lp.rightMargin + mDecorInsets.right); + heightSpec = updateSpecWithExtra(heightSpec, lp.topMargin + mDecorInsets.top, + lp.bottomMargin + mDecorInsets.bottom); + child.measure(widthSpec, heightSpec); + } + + private int updateSpecWithExtra(int spec, int startInset, int endInset) { + if (startInset == 0 && endInset == 0) { + return spec; + } + 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); + } + return spec; + } + + private void assignSpans(RecyclerView.Recycler recycler, RecyclerView.State state, int count, + int consumedSpanCount, boolean layingOutInPrimaryDirection) { + int span, spanDiff, start, end, diff; + // make sure we traverse from min position to max position + if (layingOutInPrimaryDirection) { + start = 0; + end = count; + diff = 1; + } else { + start = count - 1; + end = -1; + diff = -1; + } + if (mOrientation == VERTICAL && isLayoutRTL()) { // start from last span + span = mSpanCount - 1; + spanDiff = -1; + } else { + span = 0; + spanDiff = 1; + } + for (int i = start; i != end; i += diff) { + View view = mSet[i]; + LayoutParams params = (LayoutParams) view.getLayoutParams(); + params.mSpanSize = getSpanSize(recycler, state, getPosition(view)); + if (spanDiff == -1 && params.mSpanSize > 1) { + params.mSpanIndex = span - (params.mSpanSize - 1); + } else { + params.mSpanIndex = span; + } + span += spanDiff * params.mSpanSize; + } + } + + /** + * Returns the number of spans laid out by this grid. + * + * @return The number of spans + * @see #setSpanCount(int) + */ + public int getSpanCount() { + return mSpanCount; + } + + /** + * Sets the number of spans to be laid out. + *

+ * If {@link #getOrientation()} is {@link #VERTICAL}, this is the number of columns. + * If {@link #getOrientation()} is {@link #HORIZONTAL}, this is the number of rows. + * + * @param spanCount The total number of spans in the grid + * @see #getSpanCount() + */ + public void setSpanCount(int spanCount) { + if (spanCount == mSpanCount) { + return; + } + mPendingSpanCountChange = true; + if (spanCount < 1) { + throw new IllegalArgumentException("Span count should be at least 1. Provided " + + spanCount); + } + mSpanCount = spanCount; + mSpanSizeLookup.invalidateSpanIndexCache(); + } + + /** + * A helper class to provide the number of spans each item occupies. + *

+ * Default implementation sets each item to occupy exactly 1 span. + * + * @see GridLayoutManager#setSpanSizeLookup(SpanSizeLookup) + */ + public static abstract class SpanSizeLookup { + + final SparseIntArray mSpanIndexCache = new SparseIntArray(); + + private boolean mCacheSpanIndices = false; + + /** + * Returns the number of span occupied by the item at position. + * + * @param position The adapter position of the item + * @return The number of spans occupied by the item at the provided position + */ + abstract public int getSpanSize(int position); + + /** + * Sets whether the results of {@link #getSpanIndex(int, int)} method should be cached or + * not. By default these values are not cached. If you are not overriding + * {@link #getSpanIndex(int, int)}, you should set this to true for better performance. + * + * @param cacheSpanIndices Whether results of getSpanIndex should be cached or not. + */ + public void setSpanIndexCacheEnabled(boolean cacheSpanIndices) { + mCacheSpanIndices = cacheSpanIndices; + } + + /** + * Clears the span index cache. GridLayoutManager automatically calls this method when + * adapter changes occur. + */ + public void invalidateSpanIndexCache() { + mSpanIndexCache.clear(); + } + + /** + * Returns whether results of {@link #getSpanIndex(int, int)} method are cached or not. + * + * @return True if results of {@link #getSpanIndex(int, int)} are cached. + */ + public boolean isSpanIndexCacheEnabled() { + return mCacheSpanIndices; + } + + int getCachedSpanIndex(int position, int spanCount) { + if (!mCacheSpanIndices) { + return getSpanIndex(position, spanCount); + } + final int existing = mSpanIndexCache.get(position, -1); + if (existing != -1) { + return existing; + } + final int value = getSpanIndex(position, spanCount); + mSpanIndexCache.put(position, value); + return value; + } + + /** + * Returns the final span index of the provided position. + *

+ * If you have a faster way to calculate span index for your items, you should override + * this method. Otherwise, you should enable span index cache + * ({@link #setSpanIndexCacheEnabled(boolean)}) for better performance. When caching is + * disabled, default implementation traverses all items from 0 to + * position. When caching is enabled, it calculates from the closest cached + * value before the position. + *

+ * If you override this method, you need to make sure it is consistent with + * {@link #getSpanSize(int)}. GridLayoutManager does not call this method for + * each item. It is called only for the reference item and rest of the items + * are assigned to spans based on the reference item. For example, you cannot assign a + * position to span 2 while span 1 is empty. + *

+ * Note that span offsets always start with 0 and are not affected by RTL. + * + * @param position The position of the item + * @param spanCount The total number of spans in the grid + * @return The final span position of the item. Should be between 0 (inclusive) and + * spanCount(exclusive) + */ + public int getSpanIndex(int position, int spanCount) { + int positionSpanSize = getSpanSize(position); + if (positionSpanSize == spanCount) { + return 0; // quick return for full-span items + } + int span = 0; + int startPos = 0; + // If caching is enabled, try to jump + if (mCacheSpanIndices && mSpanIndexCache.size() > 0) { + int prevKey = findReferenceIndexFromCache(position); + if (prevKey >= 0) { + span = mSpanIndexCache.get(prevKey) + getSpanSize(prevKey); + startPos = prevKey + 1; + } + } + for (int i = startPos; i < position; i++) { + int size = getSpanSize(i); + span += size; + if (span == spanCount) { + span = 0; + } else if (span > spanCount) { + // did not fit, moving to next row / column + span = size; + } + } + if (span + positionSpanSize <= spanCount) { + return span; + } + return 0; + } + + int findReferenceIndexFromCache(int position) { + int lo = 0; + int hi = mSpanIndexCache.size() - 1; + + while (lo <= hi) { + final int mid = (lo + hi) >>> 1; + final int midVal = mSpanIndexCache.keyAt(mid); + if (midVal < position) { + lo = mid + 1; + } else { + hi = mid - 1; + } + } + int index = lo - 1; + if (index >= 0 && index < mSpanIndexCache.size()) { + return mSpanIndexCache.keyAt(index); + } + return -1; + } + + /** + * Returns the index of the group this position belongs. + *

+ * For example, if grid has 3 columns and each item occupies 1 span, span group index + * for item 1 will be 0, item 5 will be 1. + * + * @param adapterPosition The position in adapter + * @param spanCount The total number of spans in the grid + * @return The index of the span group including the item at the given adapter position + */ + public int getSpanGroupIndex(int adapterPosition, int spanCount) { + int span = 0; + int group = 0; + int positionSpanSize = getSpanSize(adapterPosition); + for (int i = 0; i < adapterPosition; i++) { + int size = getSpanSize(i); + span += size; + if (span == spanCount) { + span = 0; + group++; + } else if (span > spanCount) { + // did not fit, moving to next row / column + span = size; + group++; + } + } + if (span + positionSpanSize > spanCount) { + group++; + } + return group; + } + } + + @Override + public boolean supportsPredictiveItemAnimations() { + return mPendingSavedState == null && !mPendingSpanCountChange; + } + + /** + * Default implementation for {@link SpanSizeLookup}. Each item occupies 1 span. + */ + public static final class DefaultSpanSizeLookup extends SpanSizeLookup { + + @Override + public int getSpanSize(int position) { + return 1; + } + + @Override + public int getSpanIndex(int position, int spanCount) { + return position % spanCount; + } + } + + /** + * LayoutParams used by GridLayoutManager. + *

+ * Note that if the orientation is {@link #VERTICAL}, the width parameter is ignored and if the + * orientation is {@link #HORIZONTAL} the height parameter is ignored because child view is + * expected to fill all of the space given to it. + */ + public static class LayoutParams extends RecyclerView.LayoutParams { + + /** + * Span Id for Views that are not laid out yet. + */ + public static final int INVALID_SPAN_ID = -1; + + private int mSpanIndex = INVALID_SPAN_ID; + + private int mSpanSize = 0; + + public LayoutParams(Context c, AttributeSet attrs) { + super(c, attrs); + } + + public LayoutParams(int width, int height) { + super(width, height); + } + + public LayoutParams(ViewGroup.MarginLayoutParams source) { + super(source); + } + + public LayoutParams(ViewGroup.LayoutParams source) { + super(source); + } + + public LayoutParams(RecyclerView.LayoutParams source) { + super(source); + } + + /** + * Returns the current span index of this View. If the View is not laid out yet, the return + * value is undefined. + *

+ * Note that span index may change by whether the RecyclerView is RTL or not. For + * example, if the number of spans is 3 and layout is RTL, the rightmost item will have + * span index of 2. If the layout changes back to LTR, span index for this view will be 0. + * If the item was occupying 2 spans, span indices would be 1 and 0 respectively. + *

+ * If the View occupies multiple spans, span with the minimum index is returned. + * + * @return The span index of the View. + */ + public int getSpanIndex() { + return mSpanIndex; + } + + /** + * Returns the number of spans occupied by this View. If the View not laid out yet, the + * return value is undefined. + * + * @return The number of spans occupied by this View. + */ + public int getSpanSize() { + return mSpanSize; + } + } + +} diff --git a/TMessagesProj/src/main/java/org/telegram/android/support/widget/LayoutState.java b/TMessagesProj/src/main/java/org/telegram/android/support/widget/LayoutState.java new file mode 100644 index 000000000..c431b6149 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/android/support/widget/LayoutState.java @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2014 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 languag`e governing permissions and + * limitations under the License. + */ + +package org.telegram.android.support.widget; +import android.view.View; + +/** + * Helper class that keeps temporary state while {LayoutManager} is filling out the empty + * space. + */ +class LayoutState { + + final static String TAG = "LayoutState"; + + final static int LAYOUT_START = -1; + + final static int LAYOUT_END = 1; + + final static int INVALID_LAYOUT = Integer.MIN_VALUE; + + final static int ITEM_DIRECTION_HEAD = -1; + + final static int ITEM_DIRECTION_TAIL = 1; + + final static int SCOLLING_OFFSET_NaN = Integer.MIN_VALUE; + + /** + * Number of pixels that we should fill, in the layout direction. + */ + int mAvailable; + + /** + * Current position on the adapter to get the next item. + */ + int mCurrentPosition; + + /** + * Defines the direction in which the data adapter is traversed. + * Should be {@link #ITEM_DIRECTION_HEAD} or {@link #ITEM_DIRECTION_TAIL} + */ + int mItemDirection; + + /** + * Defines the direction in which the layout is filled. + * Should be {@link #LAYOUT_START} or {@link #LAYOUT_END} + */ + int mLayoutDirection; + + /** + * Used if you want to pre-layout items that are not yet visible. + * The difference with {@link #mAvailable} is that, when recycling, distance rendered for + * {@link #mExtra} is not considered not to recycle visible children. + */ + int mExtra = 0; + + /** + * @return true if there are more items in the data adapter + */ + boolean hasMore(RecyclerView.State state) { + return mCurrentPosition >= 0 && mCurrentPosition < state.getItemCount(); + } + + /** + * Gets the view for the next element that we should render. + * Also updates current item index to the next item, based on {@link #mItemDirection} + * + * @return The next element that we should render. + */ + View next(RecyclerView.Recycler recycler) { + final View view = recycler.getViewForPosition(mCurrentPosition); + mCurrentPosition += mItemDirection; + return view; + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/android/support/widget/LinearLayoutManager.java b/TMessagesProj/src/main/java/org/telegram/android/support/widget/LinearLayoutManager.java new file mode 100644 index 000000000..a52bbffe5 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/android/support/widget/LinearLayoutManager.java @@ -0,0 +1,2131 @@ +/* + * Copyright (C) 2014 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 languag`e governing permissions and + * limitations under the License. + */ + +package org.telegram.android.support.widget; + +import android.content.Context; +import android.graphics.PointF; +import android.os.Parcel; +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 android.util.Log; +import android.view.View; +import android.view.ViewGroup; +import android.view.accessibility.AccessibilityEvent; + +import java.util.List; + +import static org.telegram.android.support.widget.RecyclerView.NO_POSITION; + +/** + * A {@link android.support.v7.widget.RecyclerView.LayoutManager} implementation which provides + * similar functionality to {@link android.widget.ListView}. + */ +public class LinearLayoutManager extends RecyclerView.LayoutManager { + + private static final String TAG = "LinearLayoutManager"; + + private static final boolean DEBUG = false; + + public static final int HORIZONTAL = OrientationHelper.HORIZONTAL; + + public static final int VERTICAL = OrientationHelper.VERTICAL; + + public 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 = 0.33f; + + + /** + * Current orientation. Either {@link #HORIZONTAL} or {@link #VERTICAL} + */ + int mOrientation; + + /** + * Helper class that keeps temporary layout state. + * It does not keep state after layout is complete but we still keep a reference to re-use + * the same object. + */ + private LayoutState mLayoutState; + + /** + * Many calculations are made depending on orientation. To keep it clean, this interface + * helps {@link LinearLayoutManager} make those decisions. + * Based on {@link #mOrientation}, an implementation is lazily created in + * {@link #ensureLayoutState} method. + */ + OrientationHelper mOrientationHelper; + + /** + * We need to track this so that we can ignore current position when it changes. + */ + private boolean mLastStackFromEnd; + + + /** + * Defines if layout should be calculated from end to start. + * + * @see #mShouldReverseLayout + */ + private boolean mReverseLayout = false; + + /** + * This keeps the final value for how LayoutManager should start laying out views. + * It is calculated by checking {@link #getReverseLayout()} and View's layout direction. + * {@link #onLayoutChildren(RecyclerView.Recycler, RecyclerView.State)} is run. + */ + boolean mShouldReverseLayout = false; + + /** + * Works the same way as {@link android.widget.AbsListView#setStackFromBottom(boolean)} and + * it supports both orientations. + * see {@link android.widget.AbsListView#setStackFromBottom(boolean)} + */ + private boolean mStackFromEnd = false; + + /** + * Works the same way as {@link android.widget.AbsListView#setSmoothScrollbarEnabled(boolean)}. + * see {@link android.widget.AbsListView#setSmoothScrollbarEnabled(boolean)} + */ + private boolean mSmoothScrollbarEnabled = true; + + /** + * When LayoutManager needs to scroll to a position, it sets this variable and requests a + * layout which will check this variable and re-layout accordingly. + */ + int mPendingScrollPosition = NO_POSITION; + + /** + * Used to keep the offset value when {@link #scrollToPositionWithOffset(int, int)} is + * called. + */ + int mPendingScrollPositionOffset = INVALID_OFFSET; + + private boolean mRecycleChildrenOnDetach; + + SavedState mPendingSavedState = null; + + /** + * Re-used variable to keep anchor information on re-layout. + * Anchor position and coordinate defines the reference point for LLM while doing a layout. + * */ + final AnchorInfo mAnchorInfo; + + /** + * Creates a vertical LinearLayoutManager + * + * @param context Current context, will be used to access resources. + */ + public LinearLayoutManager(Context context) { + this(context, VERTICAL, false); + } + + /** + * @param context Current context, will be used to access resources. + * @param orientation Layout orientation. Should be {@link #HORIZONTAL} or {@link + * #VERTICAL}. + * @param reverseLayout When set to true, layouts from end to start. + */ + public LinearLayoutManager(Context context, int orientation, boolean reverseLayout) { + mAnchorInfo = new AnchorInfo(); + setOrientation(orientation); + setReverseLayout(reverseLayout); + } + + /** + * {@inheritDoc} + */ + @Override + public RecyclerView.LayoutParams generateDefaultLayoutParams() { + return new RecyclerView.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, + ViewGroup.LayoutParams.WRAP_CONTENT); + } + + /** + * Returns whether LayoutManager will recycle its children when it is detached from + * RecyclerView. + * + * @return true if LayoutManager will recycle its children when it is detached from + * RecyclerView. + */ + public boolean getRecycleChildrenOnDetach() { + return mRecycleChildrenOnDetach; + } + + /** + * Set whether LayoutManager will recycle its children when it is detached from + * RecyclerView. + *

+ * If you are using a {@link RecyclerView.RecycledViewPool}, it might be a good idea to set + * this flag to true so that views will be avilable to other RecyclerViews + * immediately. + *

+ * Note that, setting this flag will result in a performance drop if RecyclerView + * is restored. + * + * @param recycleChildrenOnDetach Whether children should be recycled in detach or not. + */ + public void setRecycleChildrenOnDetach(boolean recycleChildrenOnDetach) { + mRecycleChildrenOnDetach = recycleChildrenOnDetach; + } + + @Override + public void onDetachedFromWindow(RecyclerView view, RecyclerView.Recycler recycler) { + super.onDetachedFromWindow(view, recycler); + if (mRecycleChildrenOnDetach) { + removeAndRecycleAllViews(recycler); + recycler.clear(); + } + } + + @Override + public void onInitializeAccessibilityEvent(AccessibilityEvent event) { + super.onInitializeAccessibilityEvent(event); + if (getChildCount() > 0) { + final AccessibilityRecordCompat record = AccessibilityEventCompat + .asRecord(event); + record.setFromIndex(findFirstVisibleItemPosition()); + record.setToIndex(findLastVisibleItemPosition()); + } + } + + @Override + public Parcelable onSaveInstanceState() { + if (mPendingSavedState != null) { + return new SavedState(mPendingSavedState); + } + SavedState state = new SavedState(); + if (getChildCount() > 0) { + ensureLayoutState(); + boolean didLayoutFromEnd = mLastStackFromEnd ^ mShouldReverseLayout; + state.mAnchorLayoutFromEnd = didLayoutFromEnd; + if (didLayoutFromEnd) { + final View refChild = getChildClosestToEnd(); + state.mAnchorOffset = mOrientationHelper.getEndAfterPadding() - + mOrientationHelper.getDecoratedEnd(refChild); + state.mAnchorPosition = getPosition(refChild); + } else { + final View refChild = getChildClosestToStart(); + state.mAnchorPosition = getPosition(refChild); + state.mAnchorOffset = mOrientationHelper.getDecoratedStart(refChild) - + mOrientationHelper.getStartAfterPadding(); + } + } else { + state.invalidateAnchor(); + } + return state; + } + + @Override + public void onRestoreInstanceState(Parcelable state) { + if (state instanceof SavedState) { + mPendingSavedState = (SavedState) state; + requestLayout(); + if (DEBUG) { + Log.d(TAG, "loaded saved state"); + } + } else if (DEBUG) { + Log.d(TAG, "invalid saved state class"); + } + } + + /** + * @return true if {@link #getOrientation()} is {@link #HORIZONTAL} + */ + @Override + public boolean canScrollHorizontally() { + return mOrientation == HORIZONTAL; + } + + /** + * @return true if {@link #getOrientation()} is {@link #VERTICAL} + */ + @Override + public boolean canScrollVertically() { + return mOrientation == VERTICAL; + } + + /** + * Compatibility support for {@link android.widget.AbsListView#setStackFromBottom(boolean)} + */ + public void setStackFromEnd(boolean stackFromEnd) { + assertNotInLayoutOrScroll(null); + if (mStackFromEnd == stackFromEnd) { + return; + } + mStackFromEnd = stackFromEnd; + requestLayout(); + } + + public boolean getStackFromEnd() { + return mStackFromEnd; + } + + /** + * Returns the current orientaion of the layout. + * + * @return Current orientation. + * @see #mOrientation + * @see #setOrientation(int) + */ + public int getOrientation() { + return mOrientation; + } + + /** + * 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} + */ + public void setOrientation(int orientation) { + if (orientation != HORIZONTAL && orientation != VERTICAL) { + throw new IllegalArgumentException("invalid orientation:" + orientation); + } + assertNotInLayoutOrScroll(null); + if (orientation == mOrientation) { + return; + } + mOrientation = orientation; + mOrientationHelper = null; + requestLayout(); + } + + /** + * Calculates the view layout order. (e.g. from end to start or start to end) + * RTL layout support is applied automatically. So if layout is RTL and + * {@link #getReverseLayout()} is {@code true}, elements will be laid out starting from left. + */ + private void resolveShouldLayoutReverse() { + // A == B is the same result, but we rather keep it readable + if (mOrientation == VERTICAL || !isLayoutRTL()) { + mShouldReverseLayout = mReverseLayout; + } else { + mShouldReverseLayout = !mReverseLayout; + } + } + + /** + * Returns if views are laid out from the opposite direction of the layout. + * + * @return If layout is reversed or not. + * @see {@link #setReverseLayout(boolean)} + */ + public boolean getReverseLayout() { + return mReverseLayout; + } + + /** + * Used to reverse item traversal and layout order. + * This behaves similar to the layout change for RTL views. When set to true, first item is + * 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 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 + * {@link android.widget.AbsListView#setStackFromBottom(boolean)}, use + * {@link #setStackFromEnd(boolean)} + */ + public void setReverseLayout(boolean reverseLayout) { + assertNotInLayoutOrScroll(null); + if (reverseLayout == mReverseLayout) { + return; + } + mReverseLayout = reverseLayout; + requestLayout(); + } + + /** + * {@inheritDoc} + */ + @Override + public View findViewByPosition(int position) { + final int childCount = getChildCount(); + if (childCount == 0) { + return null; + } + final int firstChild = getPosition(getChildAt(0)); + final int viewPosition = position - firstChild; + if (viewPosition >= 0 && viewPosition < childCount) { + return getChildAt(viewPosition); + } + return null; + } + + /** + *

Returns the amount of extra space that should be laid out by LayoutManager. + * 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.

+ *

Laying out invisible elements will eventually come with performance cost. On the other + * hand, in places like smooth scrolling to an unknown location, this extra content helps + * LayoutManager to calculate a much smoother scrolling; which improves user experience.

+ *

You can also use this if you are trying to pre-layout your upcoming views.

+ * + * @return The extra space that should be laid out (in pixels). + */ + protected int getExtraLayoutSpace(RecyclerView.State state) { + if (state.hasTargetScrollPosition()) { + return mOrientationHelper.getTotalSpace(); + } else { + return 0; + } + } + + @Override + public void smoothScrollToPosition(RecyclerView recyclerView, RecyclerView.State state, + int position) { + LinearSmoothScroller linearSmoothScroller = + new LinearSmoothScroller(recyclerView.getContext()) { + @Override + public PointF computeScrollVectorForPosition(int targetPosition) { + return LinearLayoutManager.this + .computeScrollVectorForPosition(targetPosition); + } + }; + linearSmoothScroller.setTargetPosition(position); + startSmoothScroll(linearSmoothScroller); + } + + public PointF computeScrollVectorForPosition(int targetPosition) { + if (getChildCount() == 0) { + return null; + } + final int firstChildPos = getPosition(getChildAt(0)); + final int direction = targetPosition < firstChildPos != mShouldReverseLayout ? -1 : 1; + if (mOrientation == HORIZONTAL) { + return new PointF(direction, 0); + } else { + return new PointF(0, direction); + } + } + + /** + * {@inheritDoc} + */ + @Override + public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) { + // layout algorithm: + // 1) by checking children and other variables, find an anchor coordinate and an anchor + // item position. + // 2) fill towards start, stacking from bottom + // 3) fill towards end, stacking from top + // 4) scroll to fulfill requirements like stack from bottom. + // create layout state + if (DEBUG) { + Log.d(TAG, "is pre layout:" + state.isPreLayout()); + } + if (mPendingSavedState != null && mPendingSavedState.hasValidAnchor()) { + mPendingScrollPosition = mPendingSavedState.mAnchorPosition; + } + + ensureLayoutState(); + mLayoutState.mRecycle = false; + // resolve layout direction + resolveShouldLayoutReverse(); + + mAnchorInfo.reset(); + mAnchorInfo.mLayoutFromEnd = mShouldReverseLayout ^ mStackFromEnd; + // calculate anchor position and coordinate + updateAnchorInfoForLayout(state, mAnchorInfo); + if (DEBUG) { + Log.d(TAG, "Anchor info:" + mAnchorInfo); + } + + // LLM may decide to layout items for "extra" pixels to account for scrolling target, + // caching or predictive animations. + int extraForStart; + int extraForEnd; + final int extra = getExtraLayoutSpace(state); + // If the previous scroll delta was less than zero, the extra space should be laid out + // at the start. Otherwise, it should be at the end. + if (mLayoutState.mLastScrollDelta >= 0) { + extraForEnd = extra; + extraForStart = 0; + } else { + extraForStart = extra; + extraForEnd = 0; + } + extraForStart += mOrientationHelper.getStartAfterPadding(); + extraForEnd += mOrientationHelper.getEndPadding(); + if (state.isPreLayout() && mPendingScrollPosition != NO_POSITION && + mPendingScrollPositionOffset != INVALID_OFFSET) { + // if the child is visible and we are going to move it around, we should layout + // extra items in the opposite direction to make sure new items animate nicely + // instead of just fading in + final View existing = findViewByPosition(mPendingScrollPosition); + if (existing != null) { + final int current; + final int upcomingOffset; + if (mShouldReverseLayout) { + current = mOrientationHelper.getEndAfterPadding() - + mOrientationHelper.getDecoratedEnd(existing); + upcomingOffset = current - mPendingScrollPositionOffset; + } else { + current = mOrientationHelper.getDecoratedStart(existing) + - mOrientationHelper.getStartAfterPadding(); + upcomingOffset = mPendingScrollPositionOffset - current; + } + if (upcomingOffset > 0) { + extraForStart += upcomingOffset; + } else { + extraForEnd -= upcomingOffset; + } + } + } + int startOffset; + int endOffset; + onAnchorReady(state, mAnchorInfo); + detachAndScrapAttachedViews(recycler); + mLayoutState.mIsPreLayout = state.isPreLayout(); + if (mAnchorInfo.mLayoutFromEnd) { + // fill towards start + updateLayoutStateToFillStart(mAnchorInfo); + mLayoutState.mExtra = extraForStart; + fill(recycler, mLayoutState, state, false); + startOffset = mLayoutState.mOffset; + final int firstElement = mLayoutState.mCurrentPosition; + if (mLayoutState.mAvailable > 0) { + extraForEnd += mLayoutState.mAvailable; + } + // fill towards end + updateLayoutStateToFillEnd(mAnchorInfo); + mLayoutState.mExtra = extraForEnd; + mLayoutState.mCurrentPosition += mLayoutState.mItemDirection; + fill(recycler, mLayoutState, state, false); + endOffset = mLayoutState.mOffset; + + if (mLayoutState.mAvailable > 0) { + // end could not consume all. add more items towards start + extraForStart = mLayoutState.mAvailable; + updateLayoutStateToFillStart(firstElement, startOffset); + mLayoutState.mExtra = extraForStart; + fill(recycler, mLayoutState, state, false); + startOffset = mLayoutState.mOffset; + } + } else { + // fill towards end + updateLayoutStateToFillEnd(mAnchorInfo); + mLayoutState.mExtra = extraForEnd; + fill(recycler, mLayoutState, state, false); + endOffset = mLayoutState.mOffset; + final int lastElement = mLayoutState.mCurrentPosition; + if (mLayoutState.mAvailable > 0) { + extraForStart += mLayoutState.mAvailable; + } + // fill towards start + updateLayoutStateToFillStart(mAnchorInfo); + mLayoutState.mExtra = extraForStart; + mLayoutState.mCurrentPosition += mLayoutState.mItemDirection; + fill(recycler, mLayoutState, state, false); + startOffset = mLayoutState.mOffset; + + if (mLayoutState.mAvailable > 0) { + extraForEnd = mLayoutState.mAvailable; + // start could not consume all it should. add more items towards end + updateLayoutStateToFillEnd(lastElement, endOffset); + mLayoutState.mExtra = extraForEnd; + fill(recycler, mLayoutState, state, false); + endOffset = mLayoutState.mOffset; + } + } + + // changes may cause gaps on the UI, try to fix them. + // TODO we can probably avoid this if neither stackFromEnd/reverseLayout/RTL values have + // changed + if (getChildCount() > 0) { + // because layout from end may be changed by scroll to position + // we re-calculate it. + // find which side we should check for gaps. + if (mShouldReverseLayout ^ mStackFromEnd) { + int fixOffset = fixLayoutEndGap(endOffset, recycler, state, true); + startOffset += fixOffset; + endOffset += fixOffset; + fixOffset = fixLayoutStartGap(startOffset, recycler, state, false); + startOffset += fixOffset; + endOffset += fixOffset; + } else { + int fixOffset = fixLayoutStartGap(startOffset, recycler, state, true); + startOffset += fixOffset; + endOffset += fixOffset; + fixOffset = fixLayoutEndGap(endOffset, recycler, state, false); + startOffset += fixOffset; + endOffset += fixOffset; + } + } + layoutForPredictiveAnimations(recycler, state, startOffset, endOffset); + if (!state.isPreLayout()) { + mPendingScrollPosition = NO_POSITION; + mPendingScrollPositionOffset = INVALID_OFFSET; + mOrientationHelper.onLayoutComplete(); + } + mLastStackFromEnd = mStackFromEnd; + mPendingSavedState = null; // we don't need this anymore + if (DEBUG) { + validateChildOrder(); + } + } + + /** + * Method called when Anchor position is decided. Extending class can setup accordingly or + * even update anchor info if necessary. + * + * @param state + * @param anchorInfo Simple data structure to keep anchor point information for the next layout + */ + void onAnchorReady(RecyclerView.State state, AnchorInfo anchorInfo) { + } + + /** + * If necessary, layouts new items for predictive animations + */ + private void layoutForPredictiveAnimations(RecyclerView.Recycler recycler, + RecyclerView.State state, int startOffset, int endOffset) { + // If there are scrap children that we did not layout, we need to find where they did go + // and layout them accordingly so that animations can work as expected. + // This case may happen if new views are added or an existing view expands and pushes + // another view out of bounds. + if (!state.willRunPredictiveAnimations() || getChildCount() == 0 || state.isPreLayout() + || !supportsPredictiveItemAnimations()) { + return; + } + // to make the logic simpler, we calculate the size of children and call fill. + int scrapExtraStart = 0, scrapExtraEnd = 0; + final List scrapList = recycler.getScrapList(); + final int scrapSize = scrapList.size(); + final int firstChildPos = getPosition(getChildAt(0)); + for (int i = 0; i < scrapSize; i++) { + RecyclerView.ViewHolder scrap = scrapList.get(i); + if (scrap.isRemoved()) { + continue; + } + final int position = scrap.getLayoutPosition(); + final int direction = position < firstChildPos != mShouldReverseLayout + ? LayoutState.LAYOUT_START : LayoutState.LAYOUT_END; + if (direction == LayoutState.LAYOUT_START) { + scrapExtraStart += mOrientationHelper.getDecoratedMeasurement(scrap.itemView); + } else { + scrapExtraEnd += mOrientationHelper.getDecoratedMeasurement(scrap.itemView); + } + } + + if (DEBUG) { + Log.d(TAG, "for unused scrap, decided to add " + scrapExtraStart + + " towards start and " + scrapExtraEnd + " towards end"); + } + mLayoutState.mScrapList = scrapList; + if (scrapExtraStart > 0) { + View anchor = getChildClosestToStart(); + updateLayoutStateToFillStart(getPosition(anchor), startOffset); + mLayoutState.mExtra = scrapExtraStart; + mLayoutState.mAvailable = 0; + mLayoutState.assignPositionFromScrapList(); + fill(recycler, mLayoutState, state, false); + } + + if (scrapExtraEnd > 0) { + View anchor = getChildClosestToEnd(); + updateLayoutStateToFillEnd(getPosition(anchor), endOffset); + mLayoutState.mExtra = scrapExtraEnd; + mLayoutState.mAvailable = 0; + mLayoutState.assignPositionFromScrapList(); + fill(recycler, mLayoutState, state, false); + } + mLayoutState.mScrapList = null; + } + + private void updateAnchorInfoForLayout(RecyclerView.State state, AnchorInfo anchorInfo) { + if (updateAnchorFromPendingData(state, anchorInfo)) { + if (DEBUG) { + Log.d(TAG, "updated anchor info from pending information"); + } + return; + } + + if (updateAnchorFromChildren(state, anchorInfo)) { + if (DEBUG) { + Log.d(TAG, "updated anchor info from existing children"); + } + return; + } + if (DEBUG) { + Log.d(TAG, "deciding anchor info for fresh state"); + } + anchorInfo.assignCoordinateFromPadding(); + anchorInfo.mPosition = mStackFromEnd ? state.getItemCount() - 1 : 0; + } + + /** + * Finds an anchor child from existing Views. Most of the time, this is the view closest to + * start or end that has a valid position (e.g. not removed). + *

+ * If a child has focus, it is given priority. + */ + private boolean updateAnchorFromChildren(RecyclerView.State state, AnchorInfo anchorInfo) { + if (getChildCount() == 0) { + return false; + } + final View focused = getFocusedChild(); + if (focused != null && anchorInfo.isViewValidAsAnchor(focused, state)) { + anchorInfo.assignFromViewAndKeepVisibleRect(focused); + return true; + } + if (mLastStackFromEnd != mStackFromEnd) { + return false; + } + View referenceChild = anchorInfo.mLayoutFromEnd ? findReferenceChildClosestToEnd(state) + : findReferenceChildClosestToStart(state); + if (referenceChild != null) { + anchorInfo.assignFromView(referenceChild); + // If all visible views are removed in 1 pass, reference child might be out of bounds. + // If that is the case, offset it back to 0 so that we use these pre-layout children. + if (!state.isPreLayout() && supportsPredictiveItemAnimations()) { + // validate this child is at least partially visible. if not, offset it to start + final boolean notVisible = + mOrientationHelper.getDecoratedStart(referenceChild) >= mOrientationHelper + .getEndAfterPadding() + || mOrientationHelper.getDecoratedEnd(referenceChild) + < mOrientationHelper.getStartAfterPadding(); + if (notVisible) { + anchorInfo.mCoordinate = anchorInfo.mLayoutFromEnd + ? mOrientationHelper.getEndAfterPadding() + : mOrientationHelper.getStartAfterPadding(); + } + } + return true; + } + return false; + } + + /** + * If there is a pending scroll position or saved states, updates the anchor info from that + * data and returns true + */ + private boolean updateAnchorFromPendingData(RecyclerView.State state, AnchorInfo anchorInfo) { + if (state.isPreLayout() || mPendingScrollPosition == NO_POSITION) { + return false; + } + // validate scroll position + if (mPendingScrollPosition < 0 || mPendingScrollPosition >= state.getItemCount()) { + mPendingScrollPosition = NO_POSITION; + mPendingScrollPositionOffset = INVALID_OFFSET; + if (DEBUG) { + Log.e(TAG, "ignoring invalid scroll position " + mPendingScrollPosition); + } + return false; + } + + // if child is visible, try to make it a reference child and ensure it is fully visible. + // if child is not visible, align it depending on its virtual position. + anchorInfo.mPosition = mPendingScrollPosition; + if (mPendingSavedState != null && mPendingSavedState.hasValidAnchor()) { + // Anchor offset depends on how that child was laid out. Here, we update it + // according to our current view bounds + anchorInfo.mLayoutFromEnd = mPendingSavedState.mAnchorLayoutFromEnd; + if (anchorInfo.mLayoutFromEnd) { + anchorInfo.mCoordinate = mOrientationHelper.getEndAfterPadding() - + mPendingSavedState.mAnchorOffset; + } else { + anchorInfo.mCoordinate = mOrientationHelper.getStartAfterPadding() + + mPendingSavedState.mAnchorOffset; + } + return true; + } + + if (mPendingScrollPositionOffset == INVALID_OFFSET) { + View child = findViewByPosition(mPendingScrollPosition); + if (child != null) { + final int childSize = mOrientationHelper.getDecoratedMeasurement(child); + if (childSize > mOrientationHelper.getTotalSpace()) { + // item does not fit. fix depending on layout direction + anchorInfo.assignCoordinateFromPadding(); + return true; + } + final int startGap = mOrientationHelper.getDecoratedStart(child) + - mOrientationHelper.getStartAfterPadding(); + if (startGap < 0) { + anchorInfo.mCoordinate = mOrientationHelper.getStartAfterPadding(); + anchorInfo.mLayoutFromEnd = false; + return true; + } + final int endGap = mOrientationHelper.getEndAfterPadding() - + mOrientationHelper.getDecoratedEnd(child); + if (endGap < 0) { + anchorInfo.mCoordinate = mOrientationHelper.getEndAfterPadding(); + anchorInfo.mLayoutFromEnd = true; + return true; + } + anchorInfo.mCoordinate = anchorInfo.mLayoutFromEnd + ? (mOrientationHelper.getDecoratedEnd(child) + mOrientationHelper + .getTotalSpaceChange()) + : mOrientationHelper.getDecoratedStart(child); + } else { // item is not visible. + if (getChildCount() > 0) { + // get position of any child, does not matter + int pos = getPosition(getChildAt(0)); + anchorInfo.mLayoutFromEnd = mPendingScrollPosition < pos + == mShouldReverseLayout; + } + anchorInfo.assignCoordinateFromPadding(); + } + return true; + } + // override layout from end values for consistency + anchorInfo.mLayoutFromEnd = mShouldReverseLayout; + if (mShouldReverseLayout) { + anchorInfo.mCoordinate = mOrientationHelper.getEndAfterPadding() - + mPendingScrollPositionOffset; + } else { + anchorInfo.mCoordinate = mOrientationHelper.getStartAfterPadding() + + mPendingScrollPositionOffset; + } + return true; + } + + /** + * @return The final offset amount for children + */ + private int fixLayoutEndGap(int endOffset, RecyclerView.Recycler recycler, + RecyclerView.State state, boolean canOffsetChildren) { + int gap = mOrientationHelper.getEndAfterPadding() - endOffset; + int fixOffset = 0; + if (gap > 0) { + fixOffset = -scrollBy(-gap, recycler, state); + } else { + return 0; // nothing to fix + } + // move offset according to scroll amount + endOffset += fixOffset; + if (canOffsetChildren) { + // re-calculate gap, see if we could fix it + gap = mOrientationHelper.getEndAfterPadding() - endOffset; + if (gap > 0) { + mOrientationHelper.offsetChildren(gap); + return gap + fixOffset; + } + } + return fixOffset; + } + + /** + * @return The final offset amount for children + */ + private int fixLayoutStartGap(int startOffset, RecyclerView.Recycler recycler, + RecyclerView.State state, boolean canOffsetChildren) { + int gap = startOffset - mOrientationHelper.getStartAfterPadding(); + int fixOffset = 0; + if (gap > 0) { + // check if we should fix this gap. + fixOffset = -scrollBy(gap, recycler, state); + } else { + return 0; // nothing to fix + } + startOffset += fixOffset; + if (canOffsetChildren) { + // re-calculate gap, see if we could fix it + gap = startOffset - mOrientationHelper.getStartAfterPadding(); + if (gap > 0) { + mOrientationHelper.offsetChildren(-gap); + return fixOffset - gap; + } + } + return fixOffset; + } + + private void updateLayoutStateToFillEnd(AnchorInfo anchorInfo) { + updateLayoutStateToFillEnd(anchorInfo.mPosition, anchorInfo.mCoordinate); + } + + private void updateLayoutStateToFillEnd(int itemPosition, int offset) { + mLayoutState.mAvailable = mOrientationHelper.getEndAfterPadding() - offset; + mLayoutState.mItemDirection = mShouldReverseLayout ? LayoutState.ITEM_DIRECTION_HEAD : + LayoutState.ITEM_DIRECTION_TAIL; + mLayoutState.mCurrentPosition = itemPosition; + mLayoutState.mLayoutDirection = LayoutState.LAYOUT_END; + mLayoutState.mOffset = offset; + mLayoutState.mScrollingOffset = LayoutState.SCOLLING_OFFSET_NaN; + } + + private void updateLayoutStateToFillStart(AnchorInfo anchorInfo) { + updateLayoutStateToFillStart(anchorInfo.mPosition, anchorInfo.mCoordinate); + } + + private void updateLayoutStateToFillStart(int itemPosition, int offset) { + mLayoutState.mAvailable = offset - mOrientationHelper.getStartAfterPadding(); + mLayoutState.mCurrentPosition = itemPosition; + mLayoutState.mItemDirection = mShouldReverseLayout ? LayoutState.ITEM_DIRECTION_TAIL : + LayoutState.ITEM_DIRECTION_HEAD; + mLayoutState.mLayoutDirection = LayoutState.LAYOUT_START; + mLayoutState.mOffset = offset; + mLayoutState.mScrollingOffset = LayoutState.SCOLLING_OFFSET_NaN; + + } + + protected boolean isLayoutRTL() { + return getLayoutDirection() == ViewCompat.LAYOUT_DIRECTION_RTL; + } + + void ensureLayoutState() { + if (mLayoutState == null) { + mLayoutState = createLayoutState(); + } + if (mOrientationHelper == null) { + mOrientationHelper = OrientationHelper.createOrientationHelper(this, mOrientation); + } + } + + /** + * Test overrides this to plug some tracking and verification. + * + * @return A new LayoutState + */ + LayoutState createLayoutState() { + return new LayoutState(); + } + + /** + *

Scroll the RecyclerView to make the position visible.

+ * + *

RecyclerView will scroll the minimum amount that is necessary to make the + * target position visible. If you are looking for a similar behavior to + * {@link android.widget.ListView#setSelection(int)} or + * {@link android.widget.ListView#setSelectionFromTop(int, int)}, use + * {@link #scrollToPositionWithOffset(int, int)}.

+ * + *

Note that scroll position change will not be reflected until the next layout call.

+ * + * @param position Scroll to this adapter position + * @see #scrollToPositionWithOffset(int, int) + */ + @Override + public void scrollToPosition(int position) { + mPendingScrollPosition = position; + mPendingScrollPositionOffset = INVALID_OFFSET; + if (mPendingSavedState != null) { + mPendingSavedState.invalidateAnchor(); + } + requestLayout(); + } + + /** + * Scroll to the specified adapter position with the given offset from resolved layout + * start. Resolved layout start depends on {@link #getReverseLayout()}, + * {@link ViewCompat#getLayoutDirection(android.view.View)} and {@link #getStackFromEnd()}. + *

+ * For example, if layout is {@link #VERTICAL} and {@link #getStackFromEnd()} is true, calling + * scrollToPositionWithOffset(10, 20) will layout such that + * item[10]'s bottom is 20 pixels above the RecyclerView's bottom. + *

+ * Note that scroll position change will not be reflected until the next layout call. + * + *

+ * If you are just trying to make a position visible, use {@link #scrollToPosition(int)}. + * + * @param position Index (starting at 0) of the reference item. + * @param offset The distance (in pixels) between the start edge of the item view and + * start edge of the RecyclerView. + * @see #setReverseLayout(boolean) + * @see #scrollToPosition(int) + */ + public void scrollToPositionWithOffset(int position, int offset) { + mPendingScrollPosition = position; + mPendingScrollPositionOffset = offset; + if (mPendingSavedState != null) { + mPendingSavedState.invalidateAnchor(); + } + requestLayout(); + } + + + /** + * {@inheritDoc} + */ + @Override + public int scrollHorizontallyBy(int dx, RecyclerView.Recycler recycler, + RecyclerView.State state) { + if (mOrientation == VERTICAL) { + return 0; + } + return scrollBy(dx, recycler, state); + } + + /** + * {@inheritDoc} + */ + @Override + public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler, + RecyclerView.State state) { + if (mOrientation == HORIZONTAL) { + return 0; + } + return scrollBy(dy, recycler, state); + } + + @Override + public int computeHorizontalScrollOffset(RecyclerView.State state) { + return computeScrollOffset(state); + } + + @Override + public int computeVerticalScrollOffset(RecyclerView.State state) { + return computeScrollOffset(state); + } + + @Override + public int computeHorizontalScrollExtent(RecyclerView.State state) { + return computeScrollExtent(state); + } + + @Override + public int computeVerticalScrollExtent(RecyclerView.State state) { + return computeScrollExtent(state); + } + + @Override + public int computeHorizontalScrollRange(RecyclerView.State state) { + return computeScrollRange(state); + } + + @Override + public int computeVerticalScrollRange(RecyclerView.State state) { + return computeScrollRange(state); + } + + private int computeScrollOffset(RecyclerView.State state) { + if (getChildCount() == 0) { + return 0; + } + ensureLayoutState(); + return ScrollbarHelper.computeScrollOffset(state, mOrientationHelper, + findFirstVisibleChildClosestToStart(!mSmoothScrollbarEnabled, true), + findFirstVisibleChildClosestToEnd(!mSmoothScrollbarEnabled, true), + this, mSmoothScrollbarEnabled, mShouldReverseLayout); + } + + private int computeScrollExtent(RecyclerView.State state) { + if (getChildCount() == 0) { + return 0; + } + ensureLayoutState(); + return ScrollbarHelper.computeScrollExtent(state, mOrientationHelper, + findFirstVisibleChildClosestToStart(!mSmoothScrollbarEnabled, true), + findFirstVisibleChildClosestToEnd(!mSmoothScrollbarEnabled, true), + this, mSmoothScrollbarEnabled); + } + + private int computeScrollRange(RecyclerView.State state) { + if (getChildCount() == 0) { + return 0; + } + ensureLayoutState(); + return ScrollbarHelper.computeScrollRange(state, mOrientationHelper, + findFirstVisibleChildClosestToStart(!mSmoothScrollbarEnabled, true), + findFirstVisibleChildClosestToEnd(!mSmoothScrollbarEnabled, true), + this, mSmoothScrollbarEnabled); + } + + /** + * When smooth scrollbar is enabled, the position and size of the scrollbar thumb is computed + * based on the number of visible pixels in the visible items. This however assumes that all + * list items have similar or equal widths or heights (depending on list orientation). + * If you use a list in which items have different dimensions, the scrollbar will change + * appearance as the user scrolls through the list. To avoid this issue, you need to disable + * this property. + * + * When smooth scrollbar is disabled, the position and size of the scrollbar thumb is based + * solely on the number of items in the adapter and the position of the visible items inside + * the adapter. This provides a stable scrollbar as the user navigates through a list of items + * with varying widths / heights. + * + * @param enabled Whether or not to enable smooth scrollbar. + * + * @see #setSmoothScrollbarEnabled(boolean) + */ + public void setSmoothScrollbarEnabled(boolean enabled) { + mSmoothScrollbarEnabled = enabled; + } + + /** + * Returns the current state of the smooth scrollbar feature. It is enabled by default. + * + * @return True if smooth scrollbar is enabled, false otherwise. + * + * @see #setSmoothScrollbarEnabled(boolean) + */ + public boolean isSmoothScrollbarEnabled() { + return mSmoothScrollbarEnabled; + } + + private void updateLayoutState(int layoutDirection, int requiredSpace, + boolean canUseExistingSpace, RecyclerView.State state) { + mLayoutState.mExtra = getExtraLayoutSpace(state); + mLayoutState.mLayoutDirection = layoutDirection; + int fastScrollSpace; + if (layoutDirection == LayoutState.LAYOUT_END) { + mLayoutState.mExtra += mOrientationHelper.getEndPadding(); + // get the first child in the direction we are going + final View child = getChildClosestToEnd(); + // the direction in which we are traversing children + mLayoutState.mItemDirection = mShouldReverseLayout ? LayoutState.ITEM_DIRECTION_HEAD + : LayoutState.ITEM_DIRECTION_TAIL; + 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) + - mOrientationHelper.getEndAfterPadding(); + + } else { + final View child = getChildClosestToStart(); + mLayoutState.mExtra += mOrientationHelper.getStartAfterPadding(); + mLayoutState.mItemDirection = mShouldReverseLayout ? LayoutState.ITEM_DIRECTION_TAIL + : LayoutState.ITEM_DIRECTION_HEAD; + mLayoutState.mCurrentPosition = getPosition(child) + mLayoutState.mItemDirection; + mLayoutState.mOffset = mOrientationHelper.getDecoratedStart(child); + fastScrollSpace = -mOrientationHelper.getDecoratedStart(child) + + mOrientationHelper.getStartAfterPadding(); + } + mLayoutState.mAvailable = requiredSpace; + if (canUseExistingSpace) { + mLayoutState.mAvailable -= fastScrollSpace; + } + mLayoutState.mScrollingOffset = fastScrollSpace; + } + + int scrollBy(int dy, RecyclerView.Recycler recycler, RecyclerView.State state) { + if (getChildCount() == 0 || dy == 0) { + return 0; + } + mLayoutState.mRecycle = true; + ensureLayoutState(); + 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); + if (consumed < 0) { + if (DEBUG) { + Log.d(TAG, "Don't have any more elements to scroll"); + } + return 0; + } + final int scrolled = absDy > consumed ? layoutDirection * consumed : dy; + mOrientationHelper.offsetChildren(-scrolled); + if (DEBUG) { + Log.d(TAG, "scroll req: " + dy + " scrolled: " + scrolled); + } + mLayoutState.mLastScrollDelta = scrolled; + return scrolled; + } + + @Override + public void assertNotInLayoutOrScroll(String message) { + if (mPendingSavedState == null) { + super.assertNotInLayoutOrScroll(message); + } + } + + /** + * Recycles children between given indices. + * + * @param startIndex inclusive + * @param endIndex exclusive + */ + private void recycleChildren(RecyclerView.Recycler recycler, int startIndex, int endIndex) { + if (startIndex == endIndex) { + return; + } + if (DEBUG) { + Log.d(TAG, "Recycling " + Math.abs(startIndex - endIndex) + " items"); + } + if (endIndex > startIndex) { + for (int i = endIndex - 1; i >= startIndex; i--) { + removeAndRecycleViewAt(i, recycler); + } + } else { + for (int i = startIndex; i > endIndex; i--) { + removeAndRecycleViewAt(i, recycler); + } + } + } + + /** + * Recycles views that went out of bounds after scrolling towards the end of the layout. + * + * @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. + */ + private void recycleViewsFromStart(RecyclerView.Recycler recycler, int dt) { + if (dt < 0) { + if (DEBUG) { + Log.d(TAG, "Called recycle from start with a negative value. This might happen" + + " during layout changes but may be sign of a bug"); + } + return; + } + // ignore padding, ViewGroup may not clip children. + final int limit = dt; + final int childCount = getChildCount(); + if (mShouldReverseLayout) { + for (int i = childCount - 1; i >= 0; i--) { + View child = getChildAt(i); + if (mOrientationHelper.getDecoratedEnd(child) > limit) {// stop here + recycleChildren(recycler, childCount - 1, i); + return; + } + } + } else { + for (int i = 0; i < childCount; i++) { + View child = getChildAt(i); + if (mOrientationHelper.getDecoratedEnd(child) > limit) {// stop here + recycleChildren(recycler, 0, i); + return; + } + } + } + } + + + /** + * Recycles views that went out of bounds after scrolling towards the start of the layout. + * + * @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. + */ + private void recycleViewsFromEnd(RecyclerView.Recycler recycler, int dt) { + final int childCount = getChildCount(); + if (dt < 0) { + if (DEBUG) { + Log.d(TAG, "Called recycle from end with a negative value. This might happen" + + " during layout changes but may be sign of a bug"); + } + return; + } + final int limit = mOrientationHelper.getEnd() - dt; + if (mShouldReverseLayout) { + for (int i = 0; i < childCount; i++) { + View child = getChildAt(i); + if (mOrientationHelper.getDecoratedStart(child) < limit) {// stop here + recycleChildren(recycler, 0, i); + return; + } + } + } else { + for (int i = childCount - 1; i >= 0; i--) { + View child = getChildAt(i); + if (mOrientationHelper.getDecoratedStart(child) < limit) {// stop here + recycleChildren(recycler, childCount - 1, i); + return; + } + } + } + + } + + /** + * Helper method to call appropriate recycle method depending on current layout direction + * + * @param recycler Current recycler that is attached to RecyclerView + * @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(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) { + return; + } + if (layoutState.mLayoutDirection == LayoutState.LAYOUT_START) { + recycleViewsFromEnd(recycler, layoutState.mScrollingOffset); + } else { + recycleViewsFromStart(recycler, layoutState.mScrollingOffset); + } + } + + /** + * The magic functions :). Fills the given layout, defined by the layoutState. This is fairly + * 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 + * @param layoutState Configuration on how we should fill out the available space. + * @param state Context passed by the RecyclerView to control scroll steps. + * @param stopOnFocusable If true, filling stops in the first focusable new child + * @return Number of pixels that it added. Useful for scoll functions. + */ + int fill(RecyclerView.Recycler recycler, LayoutState layoutState, + RecyclerView.State state, boolean stopOnFocusable) { + // max offset we should set is mFastScroll + available + final int start = layoutState.mAvailable; + if (layoutState.mScrollingOffset != LayoutState.SCOLLING_OFFSET_NaN) { + // TODO ugly bug fix. should not happen + if (layoutState.mAvailable < 0) { + layoutState.mScrollingOffset += layoutState.mAvailable; + } + recycleByLayoutState(recycler, layoutState); + } + int remainingSpace = layoutState.mAvailable + layoutState.mExtra; + LayoutChunkResult layoutChunkResult = new LayoutChunkResult(); + while (remainingSpace > 0 && layoutState.hasMore(state)) { + layoutChunkResult.resetInternal(); + layoutChunk(recycler, state, layoutState, layoutChunkResult); + if (layoutChunkResult.mFinished) { + break; + } + layoutState.mOffset += layoutChunkResult.mConsumed * layoutState.mLayoutDirection; + /** + * Consume the available space if: + * * layoutChunk did not request to be ignored + * * OR we are laying out scrap children + * * OR we are not doing pre-layout + */ + if (!layoutChunkResult.mIgnoreConsumed || mLayoutState.mScrapList != null + || !state.isPreLayout()) { + layoutState.mAvailable -= layoutChunkResult.mConsumed; + // we keep a separate remaining space because mAvailable is important for recycling + remainingSpace -= layoutChunkResult.mConsumed; + } + + if (layoutState.mScrollingOffset != LayoutState.SCOLLING_OFFSET_NaN) { + layoutState.mScrollingOffset += layoutChunkResult.mConsumed; + if (layoutState.mAvailable < 0) { + layoutState.mScrollingOffset += layoutState.mAvailable; + } + recycleByLayoutState(recycler, layoutState); + } + if (stopOnFocusable && layoutChunkResult.mFocusable) { + break; + } + } + if (DEBUG) { + validateChildOrder(); + } + return start - layoutState.mAvailable; + } + + void layoutChunk(RecyclerView.Recycler recycler, RecyclerView.State state, + LayoutState layoutState, LayoutChunkResult result) { + View view = layoutState.next(recycler); + if (view == null) { + if (DEBUG && layoutState.mScrapList == null) { + throw new RuntimeException("received null view when unexpected"); + } + // if we are laying out views in scrap, this may return null which means there is + // no more items to layout. + result.mFinished = true; + return; + } + RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) view.getLayoutParams(); + if (layoutState.mScrapList == null) { + if (mShouldReverseLayout == (layoutState.mLayoutDirection + == LayoutState.LAYOUT_START)) { + addView(view); + } else { + addView(view, 0); + } + } else { + if (mShouldReverseLayout == (layoutState.mLayoutDirection + == LayoutState.LAYOUT_START)) { + addDisappearingView(view); + } else { + addDisappearingView(view, 0); + } + } + measureChildWithMargins(view, 0, 0); + result.mConsumed = mOrientationHelper.getDecoratedMeasurement(view); + int left, top, right, bottom; + if (mOrientation == VERTICAL) { + if (isLayoutRTL()) { + right = getWidth() - getPaddingRight(); + left = right - mOrientationHelper.getDecoratedMeasurementInOther(view); + } else { + left = getPaddingLeft(); + right = left + mOrientationHelper.getDecoratedMeasurementInOther(view); + } + if (layoutState.mLayoutDirection == LayoutState.LAYOUT_START) { + bottom = layoutState.mOffset; + top = layoutState.mOffset - result.mConsumed; + } else { + top = layoutState.mOffset; + bottom = layoutState.mOffset + result.mConsumed; + } + } else { + top = getPaddingTop(); + bottom = top + mOrientationHelper.getDecoratedMeasurementInOther(view); + + if (layoutState.mLayoutDirection == LayoutState.LAYOUT_START) { + right = layoutState.mOffset; + left = layoutState.mOffset - result.mConsumed; + } else { + left = layoutState.mOffset; + right = layoutState.mOffset + result.mConsumed; + } + } + // We calculate everything with View's bounding box (which includes decor and margins) + // To calculate correct layout position, we subtract margins. + layoutDecorated(view, left + params.leftMargin, top + params.topMargin, + right - params.rightMargin, bottom - params.bottomMargin); + if (DEBUG) { + Log.d(TAG, "laid out child at position " + getPosition(view) + ", with l:" + + (left + params.leftMargin) + ", t:" + (top + params.topMargin) + ", r:" + + (right - params.rightMargin) + ", b:" + (bottom - params.bottomMargin)); + } + // Consume the available space if the view is not removed OR changed + if (params.isItemRemoved() || params.isItemChanged()) { + result.mIgnoreConsumed = true; + } + result.mFocusable = view.isFocusable(); + } + + /** + * 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; + } + + } + + /** + * Convenience method to find the child closes to start. Caller should check it has enough + * children. + * + * @return The child closes to start of the layout from user's perspective. + */ + private View getChildClosestToStart() { + return getChildAt(mShouldReverseLayout ? getChildCount() - 1 : 0); + } + + /** + * Convenience method to find the child closes to end. Caller should check it has enough + * children. + * + * @return The child closes to end of the layout from user's perspective. + */ + private View getChildClosestToEnd() { + return getChildAt(mShouldReverseLayout ? 0 : getChildCount() - 1); + } + + /** + * Convenience method to find the visible child closes to start. Caller should check if it has + * enough children. + * + * @param completelyVisible Whether child should be completely visible or not + * @return The first visible child closest to start of the layout from user's perspective. + */ + private View findFirstVisibleChildClosestToStart(boolean completelyVisible, + boolean acceptPartiallyVisible) { + if (mShouldReverseLayout) { + return findOneVisibleChild(getChildCount() - 1, -1, completelyVisible, + acceptPartiallyVisible); + } else { + return findOneVisibleChild(0, getChildCount(), completelyVisible, + acceptPartiallyVisible); + } + } + + /** + * Convenience method to find the visible child closes to end. Caller should check if it has + * enough children. + * + * @param completelyVisible Whether child should be completely visible or not + * @return The first visible child closest to end of the layout from user's perspective. + */ + private View findFirstVisibleChildClosestToEnd(boolean completelyVisible, + boolean acceptPartiallyVisible) { + if (mShouldReverseLayout) { + return findOneVisibleChild(0, getChildCount(), completelyVisible, + acceptPartiallyVisible); + } else { + return findOneVisibleChild(getChildCount() - 1, -1, completelyVisible, + acceptPartiallyVisible); + } + } + + + /** + * Among the children that are suitable to be considered as an anchor child, returns the one + * closest to the end of the layout. + *

+ * Due to ambiguous adapter updates or children being removed, some children's positions may be + * invalid. This method is a best effort to find a position within adapter bounds if possible. + *

+ * It also prioritizes children that are within the visible bounds. + * @return A View that can be used an an anchor View. + */ + private View findReferenceChildClosestToEnd(RecyclerView.State state) { + return mShouldReverseLayout ? findFirstReferenceChild(state.getItemCount()) : + findLastReferenceChild(state.getItemCount()); + } + + /** + * Among the children that are suitable to be considered as an anchor child, returns the one + * closest to the start of the layout. + *

+ * Due to ambiguous adapter updates or children being removed, some children's positions may be + * invalid. This method is a best effort to find a position within adapter bounds if possible. + *

+ * It also prioritizes children that are within the visible bounds. + * + * @return A View that can be used an an anchor View. + */ + private View findReferenceChildClosestToStart(RecyclerView.State state) { + return mShouldReverseLayout ? findLastReferenceChild(state.getItemCount()) : + findFirstReferenceChild(state.getItemCount()); + } + + private View findFirstReferenceChild(int itemCount) { + return findReferenceChild(0, getChildCount(), itemCount); + } + + private View findLastReferenceChild(int itemCount) { + return findReferenceChild(getChildCount() - 1, -1, itemCount); + } + + // overridden by GridLayoutManager + View findReferenceChild(int start, int end, int itemCount) { + ensureLayoutState(); + View invalidMatch = null; + View outOfBoundsMatch = null; + final int boundsStart = mOrientationHelper.getStartAfterPadding(); + final int boundsEnd = mOrientationHelper.getEndAfterPadding(); + final int diff = end > start ? 1 : -1; + for (int i = start; i != end; i += diff) { + final View view = getChildAt(i); + final int position = getPosition(view); + if (position >= 0 && position < itemCount) { + if (((RecyclerView.LayoutParams) view.getLayoutParams()).isItemRemoved()) { + if (invalidMatch == null) { + invalidMatch = view; // removed item, least preferred + } + } else if (mOrientationHelper.getDecoratedStart(view) >= boundsEnd || + mOrientationHelper.getDecoratedEnd(view) < boundsStart) { + if (outOfBoundsMatch == null) { + outOfBoundsMatch = view; // item is not visible, less preferred + } + } else { + return view; + } + } + } + return outOfBoundsMatch != null ? outOfBoundsMatch : invalidMatch; + } + + /** + * Returns the adapter position of the first visible view. This position does not include + * adapter changes that were dispatched after the last layout pass. + *

+ * Note that, this value is not affected by layout orientation or item order traversal. + * ({@link #setReverseLayout(boolean)}). Views are sorted by their positions in the adapter, + * not in the layout. + *

+ * If RecyclerView has item decorators, they will be considered in calculations as well. + *

+ * LayoutManager may pre-cache some views that are not necessarily visible. Those views + * are ignored in this method. + * + * @return The adapter position of the first visible item or {@link RecyclerView#NO_POSITION} if + * there aren't any visible items. + * @see #findFirstCompletelyVisibleItemPosition() + * @see #findLastVisibleItemPosition() + */ + public int findFirstVisibleItemPosition() { + final View child = findOneVisibleChild(0, getChildCount(), false, true); + return child == null ? NO_POSITION : getPosition(child); + } + + /** + * Returns the adapter position of the first fully visible view. This position does not include + * adapter changes that were dispatched after the last layout pass. + *

+ * Note that bounds check is only performed in the current orientation. That means, if + * LayoutManager is horizontal, it will only check the view's left and right edges. + * + * @return The adapter position of the first fully visible item or + * {@link RecyclerView#NO_POSITION} if there aren't any visible items. + * @see #findFirstVisibleItemPosition() + * @see #findLastCompletelyVisibleItemPosition() + */ + public int findFirstCompletelyVisibleItemPosition() { + final View child = findOneVisibleChild(0, getChildCount(), true, false); + return child == null ? NO_POSITION : getPosition(child); + } + + /** + * Returns the adapter position of the last visible view. This position does not include + * adapter changes that were dispatched after the last layout pass. + *

+ * Note that, this value is not affected by layout orientation or item order traversal. + * ({@link #setReverseLayout(boolean)}). Views are sorted by their positions in the adapter, + * not in the layout. + *

+ * If RecyclerView has item decorators, they will be considered in calculations as well. + *

+ * LayoutManager may pre-cache some views that are not necessarily visible. Those views + * are ignored in this method. + * + * @return The adapter position of the last visible view or {@link RecyclerView#NO_POSITION} if + * there aren't any visible items. + * @see #findLastCompletelyVisibleItemPosition() + * @see #findFirstVisibleItemPosition() + */ + public int findLastVisibleItemPosition() { + final View child = findOneVisibleChild(getChildCount() - 1, -1, false, true); + return child == null ? NO_POSITION : getPosition(child); + } + + /** + * Returns the adapter position of the last fully visible view. This position does not include + * adapter changes that were dispatched after the last layout pass. + *

+ * Note that bounds check is only performed in the current orientation. That means, if + * LayoutManager is horizontal, it will only check the view's left and right edges. + * + * @return The adapter position of the last fully visible view or + * {@link RecyclerView#NO_POSITION} if there aren't any visible items. + * @see #findLastVisibleItemPosition() + * @see #findFirstCompletelyVisibleItemPosition() + */ + public int findLastCompletelyVisibleItemPosition() { + final View child = findOneVisibleChild(getChildCount() - 1, -1, true, false); + return child == null ? NO_POSITION : getPosition(child); + } + + View findOneVisibleChild(int fromIndex, int toIndex, boolean completelyVisible, + boolean acceptPartiallyVisible) { + ensureLayoutState(); + final int start = mOrientationHelper.getStartAfterPadding(); + final int end = mOrientationHelper.getEndAfterPadding(); + final int next = toIndex > fromIndex ? 1 : -1; + View partiallyVisible = null; + for (int i = fromIndex; i != toIndex; i+=next) { + final View child = getChildAt(i); + final int childStart = mOrientationHelper.getDecoratedStart(child); + final int childEnd = mOrientationHelper.getDecoratedEnd(child); + if (childStart < end && childEnd > start) { + if (completelyVisible) { + if (childStart >= start && childEnd <= end) { + return child; + } else if (acceptPartiallyVisible && partiallyVisible == null) { + partiallyVisible = child; + } + } else { + return child; + } + } + } + return partiallyVisible; + } + + @Override + public View onFocusSearchFailed(View focused, int focusDirection, + RecyclerView.Recycler recycler, RecyclerView.State state) { + resolveShouldLayoutReverse(); + if (getChildCount() == 0) { + return null; + } + + final int layoutDir = convertFocusDirectionToLayoutDirection(focusDirection); + if (layoutDir == LayoutState.INVALID_LAYOUT) { + return null; + } + ensureLayoutState(); + final View referenceChild; + if (layoutDir == LayoutState.LAYOUT_START) { + referenceChild = findReferenceChildClosestToStart(state); + } else { + referenceChild = findReferenceChildClosestToEnd(state); + } + if (referenceChild == null) { + if (DEBUG) { + Log.d(TAG, + "Cannot find a child with a valid position to be used for focus search."); + } + return null; + } + ensureLayoutState(); + final int maxScroll = (int) (MAX_SCROLL_FACTOR * mOrientationHelper.getTotalSpace()); + updateLayoutState(layoutDir, maxScroll, false, state); + mLayoutState.mScrollingOffset = LayoutState.SCOLLING_OFFSET_NaN; + mLayoutState.mRecycle = false; + fill(recycler, mLayoutState, state, true); + final View nextFocus; + if (layoutDir == LayoutState.LAYOUT_START) { + nextFocus = getChildClosestToStart(); + } else { + nextFocus = getChildClosestToEnd(); + } + if (nextFocus == referenceChild || !nextFocus.isFocusable()) { + return null; + } + return nextFocus; + } + + /** + * Used for debugging. + * Logs the internal representation of children to default logger. + */ + private void logChildren() { + Log.d(TAG, "internal representation of views on the screen"); + for (int i = 0; i < getChildCount(); i++) { + View child = getChildAt(i); + Log.d(TAG, "item " + getPosition(child) + ", coord:" + + mOrientationHelper.getDecoratedStart(child)); + } + Log.d(TAG, "=============="); + } + + /** + * Used for debugging. + * Validates that child views are laid out in correct order. This is important because rest of + * the algorithm relies on this constraint. + * + * In default layout, child 0 should be closest to screen position 0 and last child should be + * closest to position WIDTH or HEIGHT. + * In reverse layout, last child should be closes to screen position 0 and first child should + * be closest to position WIDTH or HEIGHT + */ + void validateChildOrder() { + Log.d(TAG, "validating child count " + getChildCount()); + if (getChildCount() < 1) { + return; + } + int lastPos = getPosition(getChildAt(0)); + int lastScreenLoc = mOrientationHelper.getDecoratedStart(getChildAt(0)); + if (mShouldReverseLayout) { + for (int i = 1; i < getChildCount(); i++) { + View child = getChildAt(i); + int pos = getPosition(child); + int screenLoc = mOrientationHelper.getDecoratedStart(child); + if (pos < lastPos) { + logChildren(); + throw new RuntimeException("detected invalid position. loc invalid? " + + (screenLoc < lastScreenLoc)); + } + if (screenLoc > lastScreenLoc) { + logChildren(); + throw new RuntimeException("detected invalid location"); + } + } + } else { + for (int i = 1; i < getChildCount(); i++) { + View child = getChildAt(i); + int pos = getPosition(child); + int screenLoc = mOrientationHelper.getDecoratedStart(child); + if (pos < lastPos) { + logChildren(); + throw new RuntimeException("detected invalid position. loc invalid? " + + (screenLoc < lastScreenLoc)); + } + if (screenLoc < lastScreenLoc) { + logChildren(); + throw new RuntimeException("detected invalid location"); + } + } + } + } + + @Override + public boolean supportsPredictiveItemAnimations() { + return mPendingSavedState == null && mLastStackFromEnd == mStackFromEnd; + } + + /** + * Helper class that keeps temporary state while {LayoutManager} is filling out the empty + * space. + */ + static class LayoutState { + + final static String TAG = "LinearLayoutManager#LayoutState"; + + final static int LAYOUT_START = -1; + + final static int LAYOUT_END = 1; + + final static int INVALID_LAYOUT = Integer.MIN_VALUE; + + final static int ITEM_DIRECTION_HEAD = -1; + + 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; + + /** + * Pixel offset where layout should start + */ + int mOffset; + + /** + * Number of pixels that we should fill, in the layout direction. + */ + int mAvailable; + + /** + * Current position on the adapter to get the next item. + */ + int mCurrentPosition; + + /** + * Defines the direction in which the data adapter is traversed. + * Should be {@link #ITEM_DIRECTION_HEAD} or {@link #ITEM_DIRECTION_TAIL} + */ + int mItemDirection; + + /** + * Defines the direction in which the layout is filled. + * Should be {@link #LAYOUT_START} or {@link #LAYOUT_END} + */ + int mLayoutDirection; + + /** + * Used when LayoutState is constructed in a scrolling state. + * It should be set the amount of scrolling we can make without creating a new view. + * Settings this is required for efficient view recycling. + */ + int mScrollingOffset; + + /** + * Used if you want to pre-layout items that are not yet visible. + * The difference with {@link #mAvailable} is that, when recycling, distance laid out for + * {@link #mExtra} is not considered to avoid recycling visible children. + */ + int mExtra = 0; + + /** + * Equal to {@link RecyclerView.State#isPreLayout()}. When consuming scrap, if this value + * is set to true, we skip removed views since they should not be laid out in post layout + * step. + */ + boolean mIsPreLayout = false; + + /** + * The most recent {@link #scrollBy(int, RecyclerView.Recycler, RecyclerView.State)} amount. + */ + int mLastScrollDelta; + + /** + * When LLM needs to layout particular views, it sets this list in which case, LayoutState + * will only return views from this list and return null if it cannot find an item. + */ + List mScrapList = null; + + /** + * @return true if there are more items in the data adapter + */ + boolean hasMore(RecyclerView.State state) { + return mCurrentPosition >= 0 && mCurrentPosition < state.getItemCount(); + } + + /** + * Gets the view for the next element that we should layout. + * Also updates current item index to the next item, based on {@link #mItemDirection} + * + * @return The next element that we should layout. + */ + View next(RecyclerView.Recycler recycler) { + if (mScrapList != null) { + return nextViewFromScrapList(); + } + final View view = recycler.getViewForPosition(mCurrentPosition); + mCurrentPosition += mItemDirection; + return view; + } + + /** + * Returns the next item from the scrap list. + *

+ * Upon finding a valid VH, sets current item position to VH.itemPosition + mItemDirection + * + * @return View if an item in the current position or direction exists if not null. + */ + private View nextViewFromScrapList() { + final int size = mScrapList.size(); + for (int i = 0; i < size; i++) { + final RecyclerView.ViewHolder viewHolder = mScrapList.get(i); + if (viewHolder.isRemoved()) { + continue; + } + if (mCurrentPosition == viewHolder.getLayoutPosition()) { + assignPositionFromScrapList(viewHolder); + return viewHolder.itemView; + } + } + return null; + } + + public void assignPositionFromScrapList() { + assignPositionFromScrapList(null); + } + + public void assignPositionFromScrapList(RecyclerView.ViewHolder ignore) { + RecyclerView.ViewHolder closest = nextViewHolderInLimitedList(ignore); + mCurrentPosition = closest == null ? RecyclerView.NO_POSITION : + closest.getLayoutPosition(); + } + + public RecyclerView.ViewHolder nextViewHolderInLimitedList(RecyclerView.ViewHolder ignore) { + int size = mScrapList.size(); + RecyclerView.ViewHolder closest = null; + int closestDistance = Integer.MAX_VALUE; + if (DEBUG && mIsPreLayout) { + throw new IllegalStateException("Scrap list cannot be used in pre layout"); + } + for (int i = 0; i < size; i++) { + RecyclerView.ViewHolder viewHolder = mScrapList.get(i); + if (viewHolder == ignore || viewHolder.isRemoved()) { + continue; + } + final int distance = (viewHolder.getLayoutPosition() - mCurrentPosition) * + mItemDirection; + if (distance < 0) { + continue; // item is not in current direction + } + if (distance < closestDistance) { + closest = viewHolder; + closestDistance = distance; + if (distance == 0) { + break; + } + } + } + return closest; + } + + void log() { + Log.d(TAG, "avail:" + mAvailable + ", ind:" + mCurrentPosition + ", dir:" + + mItemDirection + ", offset:" + mOffset + ", layoutDir:" + mLayoutDirection); + } + } + + static class SavedState implements Parcelable { + + int mAnchorPosition; + + int mAnchorOffset; + + boolean mAnchorLayoutFromEnd; + + public SavedState() { + + } + + SavedState(Parcel in) { + mAnchorPosition = in.readInt(); + mAnchorOffset = in.readInt(); + mAnchorLayoutFromEnd = in.readInt() == 1; + } + + public SavedState(SavedState other) { + mAnchorPosition = other.mAnchorPosition; + mAnchorOffset = other.mAnchorOffset; + mAnchorLayoutFromEnd = other.mAnchorLayoutFromEnd; + } + + boolean hasValidAnchor() { + return mAnchorPosition >= 0; + } + + void invalidateAnchor() { + mAnchorPosition = NO_POSITION; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(mAnchorPosition); + dest.writeInt(mAnchorOffset); + dest.writeInt(mAnchorLayoutFromEnd ? 1 : 0); + } + + public static final Parcelable.Creator CREATOR + = new Parcelable.Creator() { + @Override + public SavedState createFromParcel(Parcel in) { + return new SavedState(in); + } + + @Override + public SavedState[] newArray(int size) { + return new SavedState[size]; + } + }; + } + + /** + * Simple data class to keep Anchor information + */ + class AnchorInfo { + int mPosition; + int mCoordinate; + boolean mLayoutFromEnd; + void reset() { + mPosition = NO_POSITION; + mCoordinate = INVALID_OFFSET; + mLayoutFromEnd = false; + } + + /** + * assigns anchor coordinate from the RecyclerView's padding depending on current + * layoutFromEnd value + */ + void assignCoordinateFromPadding() { + mCoordinate = mLayoutFromEnd + ? mOrientationHelper.getEndAfterPadding() + : mOrientationHelper.getStartAfterPadding(); + } + + @Override + public String toString() { + return "AnchorInfo{" + + "mPosition=" + mPosition + + ", mCoordinate=" + mCoordinate + + ", mLayoutFromEnd=" + mLayoutFromEnd + + '}'; + } + + private boolean isViewValidAsAnchor(View child, RecyclerView.State state) { + RecyclerView.LayoutParams lp = (RecyclerView.LayoutParams) child.getLayoutParams(); + return !lp.isItemRemoved() && lp.getViewLayoutPosition() >= 0 + && lp.getViewLayoutPosition() < state.getItemCount(); + } + + public void assignFromViewAndKeepVisibleRect(View child) { + final int spaceChange = mOrientationHelper.getTotalSpaceChange(); + if (spaceChange >= 0) { + assignFromView(child); + return; + } + mPosition = getPosition(child); + if (mLayoutFromEnd) { + final int prevLayoutEnd = mOrientationHelper.getEndAfterPadding() - spaceChange; + final int childEnd = mOrientationHelper.getDecoratedEnd(child); + final int previousEndMargin = prevLayoutEnd - childEnd; + mCoordinate = mOrientationHelper.getEndAfterPadding() - previousEndMargin; + // ensure we did not push child's top out of bounds because of this + if (previousEndMargin > 0) {// we have room to shift bottom if necessary + final int childSize = mOrientationHelper.getDecoratedMeasurement(child); + final int estimatedChildStart = mCoordinate - childSize; + final int layoutStart = mOrientationHelper.getStartAfterPadding(); + final int previousStartMargin = mOrientationHelper.getDecoratedStart(child) - + layoutStart; + final int startReference = layoutStart + Math.min(previousStartMargin, 0); + final int startMargin = estimatedChildStart - startReference; + if (startMargin < 0) { + // offset to make top visible but not too much + mCoordinate += Math.min(previousEndMargin, -startMargin); + } + } + } else { + final int childStart = mOrientationHelper.getDecoratedStart(child); + final int startMargin = childStart - mOrientationHelper.getStartAfterPadding(); + mCoordinate = childStart; + if (startMargin > 0) { // we have room to fix end as well + final int estimatedEnd = childStart + + mOrientationHelper.getDecoratedMeasurement(child); + final int previousLayoutEnd = mOrientationHelper.getEndAfterPadding() - + spaceChange; + final int previousEndMargin = previousLayoutEnd - + mOrientationHelper.getDecoratedEnd(child); + final int endReference = mOrientationHelper.getEndAfterPadding() - + Math.min(0, previousEndMargin); + final int endMargin = endReference - estimatedEnd; + if (endMargin < 0) { + mCoordinate -= Math.min(startMargin, -endMargin); + } + } + } + } + + public void assignFromView(View child) { + if (mLayoutFromEnd) { + mCoordinate = mOrientationHelper.getDecoratedEnd(child) + + mOrientationHelper.getTotalSpaceChange(); + } else { + mCoordinate = mOrientationHelper.getDecoratedStart(child); + } + + mPosition = getPosition(child); + } + } + + protected static class LayoutChunkResult { + public int mConsumed; + public boolean mFinished; + public boolean mIgnoreConsumed; + public boolean mFocusable; + + void resetInternal() { + mConsumed = 0; + mFinished = false; + mIgnoreConsumed = false; + mFocusable = false; + } + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/android/support/widget/LinearSmoothScroller.java b/TMessagesProj/src/main/java/org/telegram/android/support/widget/LinearSmoothScroller.java new file mode 100644 index 000000000..419d9447d --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/android/support/widget/LinearSmoothScroller.java @@ -0,0 +1,338 @@ +/* + * Copyright (C) 2014 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.android.support.widget; + +import android.content.Context; +import android.graphics.PointF; +import android.util.DisplayMetrics; +import android.util.Log; +import android.view.View; +import android.view.animation.DecelerateInterpolator; +import android.view.animation.LinearInterpolator; + +/** + * {@link RecyclerView.SmoothScroller} implementation which uses + * {@link android.view.animation.LinearInterpolator} until the target position becames a child of + * the RecyclerView and then uses + * {@link android.view.animation.DecelerateInterpolator} to slowly approach to target position. + */ +abstract public class LinearSmoothScroller extends RecyclerView.SmoothScroller { + + private static final String TAG = "LinearSmoothScroller"; + + private static final boolean DEBUG = false; + + private static final float MILLISECONDS_PER_INCH = 25f; + + private static final int TARGET_SEEK_SCROLL_DISTANCE_PX = 10000; + + /** + * Align child view's left or top with parent view's left or top + * + * @see #calculateDtToFit(int, int, int, int, int) + * @see #calculateDxToMakeVisible(android.view.View, int) + * @see #calculateDyToMakeVisible(android.view.View, int) + */ + public static final int SNAP_TO_START = -1; + + /** + * Align child view's right or bottom with parent view's right or bottom + * + * @see #calculateDtToFit(int, int, int, int, int) + * @see #calculateDxToMakeVisible(android.view.View, int) + * @see #calculateDyToMakeVisible(android.view.View, int) + */ + public static final int SNAP_TO_END = 1; + + /** + *

Decides if the child should be snapped from start or end, depending on where it + * currently is in relation to its parent.

+ *

For instance, if the view is virtually on the left of RecyclerView, using + * {@code SNAP_TO_ANY} is the same as using {@code SNAP_TO_START}

+ * + * @see #calculateDtToFit(int, int, int, int, int) + * @see #calculateDxToMakeVisible(android.view.View, int) + * @see #calculateDyToMakeVisible(android.view.View, int) + */ + public static final int SNAP_TO_ANY = 0; + + // Trigger a scroll to a further distance than TARGET_SEEK_SCROLL_DISTANCE_PX so that if target + // view is not laid out until interim target position is reached, we can detect the case before + // scrolling slows down and reschedule another interim target scroll + private static final float TARGET_SEEK_EXTRA_SCROLL_RATIO = 1.2f; + + protected final LinearInterpolator mLinearInterpolator = new LinearInterpolator(); + + protected final DecelerateInterpolator mDecelerateInterpolator = new DecelerateInterpolator(); + + protected PointF mTargetVector; + + private final float MILLISECONDS_PER_PX; + + // Temporary variables to keep track of the interim scroll target. These values do not + // point to a real item position, rather point to an estimated location pixels. + protected int mInterimTargetDx = 0, mInterimTargetDy = 0; + + public LinearSmoothScroller(Context context) { + MILLISECONDS_PER_PX = calculateSpeedPerPixel(context.getResources().getDisplayMetrics()); + } + + /** + * {@inheritDoc} + */ + @Override + protected void onStart() { + + } + + /** + * {@inheritDoc} + */ + @Override + protected void onTargetFound(View targetView, RecyclerView.State state, Action action) { + final int dx = calculateDxToMakeVisible(targetView, getHorizontalSnapPreference()); + final int dy = calculateDyToMakeVisible(targetView, getVerticalSnapPreference()); + final int distance = (int) Math.sqrt(dx * dx + dy * dy); + final int time = calculateTimeForDeceleration(distance); + if (time > 0) { + action.update(-dx, -dy, time, mDecelerateInterpolator); + } + } + + /** + * {@inheritDoc} + */ + @Override + protected void onSeekTargetStep(int dx, int dy, RecyclerView.State state, Action action) { + if (getChildCount() == 0) { + stop(); + return; + } + if (DEBUG && mTargetVector != null + && ((mTargetVector.x * dx < 0 || mTargetVector.y * dy < 0))) { + throw new IllegalStateException("Scroll happened in the opposite direction" + + " of the target. Some calculations are wrong"); + } + mInterimTargetDx = clampApplyScroll(mInterimTargetDx, dx); + mInterimTargetDy = clampApplyScroll(mInterimTargetDy, dy); + + if (mInterimTargetDx == 0 && mInterimTargetDy == 0) { + updateActionForInterimTarget(action); + } // everything is valid, keep going + + } + + /** + * {@inheritDoc} + */ + @Override + protected void onStop() { + mInterimTargetDx = mInterimTargetDy = 0; + mTargetVector = null; + } + + /** + * Calculates the scroll speed. + * + * @param displayMetrics DisplayMetrics to be used for real dimension calculations + * @return The time (in ms) it should take for each pixel. For instance, if returned value is + * 2 ms, it means scrolling 1000 pixels with LinearInterpolation should take 2 seconds. + */ + protected float calculateSpeedPerPixel(DisplayMetrics displayMetrics) { + return MILLISECONDS_PER_INCH / displayMetrics.densityDpi; + } + + /** + *

Calculates the time for deceleration so that transition from LinearInterpolator to + * DecelerateInterpolator looks smooth.

+ * + * @param dx Distance to scroll + * @return Time for DecelerateInterpolator to smoothly traverse the distance when transitioning + * from LinearInterpolation + */ + protected int calculateTimeForDeceleration(int dx) { + // we want to cover same area with the linear interpolator for the first 10% of the + // interpolation. After that, deceleration will take control. + // area under curve (1-(1-x)^2) can be calculated as (1 - x/3) * x * x + // which gives 0.100028 when x = .3356 + // this is why we divide linear scrolling time with .3356 + return (int) Math.ceil(calculateTimeForScrolling(dx) / .3356); + } + + /** + * Calculates the time it should take to scroll the given distance (in pixels) + * + * @param dx Distance in pixels that we want to scroll + * @return Time in milliseconds + * @see #calculateSpeedPerPixel(android.util.DisplayMetrics) + */ + protected int calculateTimeForScrolling(int dx) { + // In a case where dx is very small, rounding may return 0 although dx > 0. + // To avoid that issue, ceil the result so that if dx > 0, we'll always return positive + // time. + return (int) Math.ceil(Math.abs(dx) * MILLISECONDS_PER_PX); + } + + /** + * When scrolling towards a child view, this method defines whether we should align the left + * or the right edge of the child with the parent RecyclerView. + * + * @return SNAP_TO_START, SNAP_TO_END or SNAP_TO_ANY; depending on the current target vector + * @see #SNAP_TO_START + * @see #SNAP_TO_END + * @see #SNAP_TO_ANY + */ + protected int getHorizontalSnapPreference() { + return mTargetVector == null || mTargetVector.x == 0 ? SNAP_TO_ANY : + mTargetVector.x > 0 ? SNAP_TO_END : SNAP_TO_START; + } + + /** + * When scrolling towards a child view, this method defines whether we should align the top + * or the bottom edge of the child with the parent RecyclerView. + * + * @return SNAP_TO_START, SNAP_TO_END or SNAP_TO_ANY; depending on the current target vector + * @see #SNAP_TO_START + * @see #SNAP_TO_END + * @see #SNAP_TO_ANY + */ + protected int getVerticalSnapPreference() { + return mTargetVector == null || mTargetVector.y == 0 ? SNAP_TO_ANY : + mTargetVector.y > 0 ? SNAP_TO_END : SNAP_TO_START; + } + + /** + * When the target scroll position is not a child of the RecyclerView, this method calculates + * a direction vector towards that child and triggers a smooth scroll. + * + * @see #computeScrollVectorForPosition(int) + */ + protected void updateActionForInterimTarget(Action action) { + // find an interim target position + PointF scrollVector = computeScrollVectorForPosition(getTargetPosition()); + if (scrollVector == null || (scrollVector.x == 0 && scrollVector.y == 0)) { + Log.e(TAG, "To support smooth scrolling, you should override \n" + + "LayoutManager#computeScrollVectorForPosition.\n" + + "Falling back to instant scroll"); + final int target = getTargetPosition(); + stop(); + instantScrollToPosition(target); + return; + } + normalize(scrollVector); + mTargetVector = scrollVector; + + mInterimTargetDx = (int) (TARGET_SEEK_SCROLL_DISTANCE_PX * scrollVector.x); + mInterimTargetDy = (int) (TARGET_SEEK_SCROLL_DISTANCE_PX * scrollVector.y); + final int time = calculateTimeForScrolling(TARGET_SEEK_SCROLL_DISTANCE_PX); + // To avoid UI hiccups, trigger a smooth scroll to a distance little further than the + // interim target. Since we track the distance travelled in onSeekTargetStep callback, it + // won't actually scroll more than what we need. + action.update((int) (mInterimTargetDx * TARGET_SEEK_EXTRA_SCROLL_RATIO) + , (int) (mInterimTargetDy * TARGET_SEEK_EXTRA_SCROLL_RATIO) + , (int) (time * TARGET_SEEK_EXTRA_SCROLL_RATIO), mLinearInterpolator); + } + + private int clampApplyScroll(int tmpDt, int dt) { + final int before = tmpDt; + tmpDt -= dt; + if (before * tmpDt <= 0) { // changed sign, reached 0 or was 0, reset + return 0; + } + return tmpDt; + } + + /** + * Helper method for {@link #calculateDxToMakeVisible(android.view.View, int)} and + * {@link #calculateDyToMakeVisible(android.view.View, int)} + */ + public int calculateDtToFit(int viewStart, int viewEnd, int boxStart, int boxEnd, int + snapPreference) { + switch (snapPreference) { + case SNAP_TO_START: + return boxStart - viewStart; + case SNAP_TO_END: + return boxEnd - viewEnd; + case SNAP_TO_ANY: + final int dtStart = boxStart - viewStart; + if (dtStart > 0) { + return dtStart; + } + final int dtEnd = boxEnd - viewEnd; + if (dtEnd < 0) { + return dtEnd; + } + break; + default: + throw new IllegalArgumentException("snap preference should be one of the" + + " constants defined in SmoothScroller, starting with SNAP_"); + } + return 0; + } + + /** + * Calculates the vertical scroll amount necessary to make the given view fully visible + * inside the RecyclerView. + * + * @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}. + * @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()) { + return 0; + } + final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) + view.getLayoutParams(); + final int top = layoutManager.getDecoratedTop(view) - params.topMargin; + final int bottom = layoutManager.getDecoratedBottom(view) + params.bottomMargin; + final int start = layoutManager.getPaddingTop(); + final int end = layoutManager.getHeight() - layoutManager.getPaddingBottom(); + return calculateDtToFit(top, bottom, start, end, snapPreference); + } + + /** + * Calculates the horizontal scroll amount necessary to make the given view fully visible + * inside the RecyclerView. + * + * @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} + * @return The vertical scroll amount necessary to make the view visible with the given + * snap preference. + */ + public int calculateDxToMakeVisible(View view, int snapPreference) { + final RecyclerView.LayoutManager layoutManager = getLayoutManager(); + if (!layoutManager.canScrollHorizontally()) { + return 0; + } + final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) + view.getLayoutParams(); + final int left = layoutManager.getDecoratedLeft(view) - params.leftMargin; + final int right = layoutManager.getDecoratedRight(view) + params.rightMargin; + final int start = layoutManager.getPaddingLeft(); + final int end = layoutManager.getWidth() - layoutManager.getPaddingRight(); + return calculateDtToFit(left, right, start, end, snapPreference); + } + + abstract public PointF computeScrollVectorForPosition(int targetPosition); +} diff --git a/TMessagesProj/src/main/java/org/telegram/android/support/widget/OpReorderer.java b/TMessagesProj/src/main/java/org/telegram/android/support/widget/OpReorderer.java new file mode 100644 index 000000000..3d56822f7 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/android/support/widget/OpReorderer.java @@ -0,0 +1,237 @@ +/* + * Copyright (C) 2014 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.android.support.widget; + +import java.util.List; + +import org.telegram.android.support.widget.AdapterHelper.UpdateOp; +import static org.telegram.android.support.widget.AdapterHelper.UpdateOp.ADD; +import static org.telegram.android.support.widget.AdapterHelper.UpdateOp.MOVE; +import static org.telegram.android.support.widget.AdapterHelper.UpdateOp.REMOVE; +import static org.telegram.android.support.widget.AdapterHelper.UpdateOp.UPDATE; + +class OpReorderer { + + final Callback mCallback; + + public OpReorderer(Callback callback) { + mCallback = callback; + } + + void reorderOps(List ops) { + // since move operations breaks continuity, their effects on ADD/RM are hard to handle. + // we push them to the end of the list so that they can be handled easily. + int badMove; + while ((badMove = getLastMoveOutOfOrder(ops)) != -1) { + swapMoveOp(ops, badMove, badMove + 1); + } + } + + private void swapMoveOp(List list, int badMove, int next) { + final UpdateOp moveOp = list.get(badMove); + final UpdateOp nextOp = list.get(next); + switch (nextOp.cmd) { + case REMOVE: + swapMoveRemove(list, badMove, moveOp, next, nextOp); + break; + case ADD: + swapMoveAdd(list, badMove, moveOp, next, nextOp); + break; + case UPDATE: + swapMoveUpdate(list, badMove, moveOp, next, nextOp); + break; + } + } + + void swapMoveRemove(List list, int movePos, UpdateOp moveOp, + int removePos, UpdateOp removeOp) { + UpdateOp extraRm = null; + // check if move is nulled out by remove + boolean revertedMove = false; + final boolean moveIsBackwards; + + if (moveOp.positionStart < moveOp.itemCount) { + moveIsBackwards = false; + if (removeOp.positionStart == moveOp.positionStart + && removeOp.itemCount == moveOp.itemCount - moveOp.positionStart) { + revertedMove = true; + } + } else { + moveIsBackwards = true; + if (removeOp.positionStart == moveOp.itemCount + 1 && + removeOp.itemCount == moveOp.positionStart - moveOp.itemCount) { + revertedMove = true; + } + } + + // going in reverse, first revert the effect of add + if (moveOp.itemCount < removeOp.positionStart) { + removeOp.positionStart--; + } else if (moveOp.itemCount < removeOp.positionStart + removeOp.itemCount) { + // move is removed. + removeOp.itemCount --; + moveOp.cmd = REMOVE; + moveOp.itemCount = 1; + if (removeOp.itemCount == 0) { + list.remove(removePos); + mCallback.recycleUpdateOp(removeOp); + } + // no need to swap, it is already a remove + return; + } + + // now affect of add is consumed. now apply effect of first remove + if (moveOp.positionStart <= removeOp.positionStart) { + removeOp.positionStart++; + } else if (moveOp.positionStart < removeOp.positionStart + removeOp.itemCount) { + final int remaining = removeOp.positionStart + removeOp.itemCount + - moveOp.positionStart; + extraRm = mCallback.obtainUpdateOp(REMOVE, moveOp.positionStart + 1, remaining); + removeOp.itemCount = moveOp.positionStart - removeOp.positionStart; + } + + // if effects of move is reverted by remove, we are done. + if (revertedMove) { + list.set(movePos, removeOp); + list.remove(removePos); + mCallback.recycleUpdateOp(moveOp); + return; + } + + // now find out the new locations for move actions + if (moveIsBackwards) { + if (extraRm != null) { + if (moveOp.positionStart > extraRm.positionStart) { + moveOp.positionStart -= extraRm.itemCount; + } + if (moveOp.itemCount > extraRm.positionStart) { + moveOp.itemCount -= extraRm.itemCount; + } + } + if (moveOp.positionStart > removeOp.positionStart) { + moveOp.positionStart -= removeOp.itemCount; + } + if (moveOp.itemCount > removeOp.positionStart) { + moveOp.itemCount -= removeOp.itemCount; + } + } else { + if (extraRm != null) { + if (moveOp.positionStart >= extraRm.positionStart) { + moveOp.positionStart -= extraRm.itemCount; + } + if (moveOp.itemCount >= extraRm.positionStart) { + moveOp.itemCount -= extraRm.itemCount; + } + } + if (moveOp.positionStart >= removeOp.positionStart) { + moveOp.positionStart -= removeOp.itemCount; + } + if (moveOp.itemCount >= removeOp.positionStart) { + moveOp.itemCount -= removeOp.itemCount; + } + } + + list.set(movePos, removeOp); + if (moveOp.positionStart != moveOp.itemCount) { + list.set(removePos, moveOp); + } else { + list.remove(removePos); + } + if (extraRm != null) { + list.add(movePos, extraRm); + } + } + + private void swapMoveAdd(List list, int move, UpdateOp moveOp, int add, + UpdateOp addOp) { + int offset = 0; + // going in reverse, first revert the effect of add + if (moveOp.itemCount < addOp.positionStart) { + offset--; + } + if (moveOp.positionStart < addOp.positionStart) { + offset++; + } + if (addOp.positionStart <= moveOp.positionStart) { + moveOp.positionStart += addOp.itemCount; + } + if (addOp.positionStart <= moveOp.itemCount) { + moveOp.itemCount += addOp.itemCount; + } + addOp.positionStart += offset; + list.set(move, addOp); + list.set(add, moveOp); + } + + void swapMoveUpdate(List list, int move, UpdateOp moveOp, int update, + UpdateOp updateOp) { + UpdateOp extraUp1 = null; + UpdateOp extraUp2 = null; + // going in reverse, first revert the effect of add + if (moveOp.itemCount < updateOp.positionStart) { + updateOp.positionStart--; + } else if (moveOp.itemCount < updateOp.positionStart + updateOp.itemCount) { + // moved item is updated. add an update for it + updateOp.itemCount--; + extraUp1 = mCallback.obtainUpdateOp(UPDATE, moveOp.positionStart, 1); + } + // now affect of add is consumed. now apply effect of first remove + if (moveOp.positionStart <= updateOp.positionStart) { + updateOp.positionStart++; + } else if (moveOp.positionStart < updateOp.positionStart + updateOp.itemCount) { + final int remaining = updateOp.positionStart + updateOp.itemCount + - moveOp.positionStart; + extraUp2 = mCallback.obtainUpdateOp(UPDATE, moveOp.positionStart + 1, remaining); + updateOp.itemCount -= remaining; + } + list.set(update, moveOp); + if (updateOp.itemCount > 0) { + list.set(move, updateOp); + } else { + list.remove(move); + mCallback.recycleUpdateOp(updateOp); + } + if (extraUp1 != null) { + list.add(move, extraUp1); + } + if (extraUp2 != null) { + list.add(move, extraUp2); + } + } + + private int getLastMoveOutOfOrder(List list) { + boolean foundNonMove = false; + for (int i = list.size() - 1; i >= 0; i--) { + final UpdateOp op1 = list.get(i); + if (op1.cmd == MOVE) { + if (foundNonMove) { + return i; + } + } else { + foundNonMove = true; + } + } + return -1; + } + + static interface Callback { + + UpdateOp obtainUpdateOp(int cmd, int startPosition, int itemCount); + + void recycleUpdateOp(UpdateOp op); + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/android/support/widget/OrientationHelper.java b/TMessagesProj/src/main/java/org/telegram/android/support/widget/OrientationHelper.java new file mode 100644 index 000000000..bfe380636 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/android/support/widget/OrientationHelper.java @@ -0,0 +1,338 @@ +/* + * Copyright (C) 2014 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.android.support.widget; + +import android.view.View; +import android.widget.LinearLayout; + +/** + * Helper class for LayoutManagers to abstract measurements depending on the View's orientation. + *

+ * It is developed to easily support vertical and horizontal orientations in a LayoutManager but + * can also be used to abstract calls around view bounds and child measurements with margins and + * decorations. + * + * @see #createHorizontalHelper(RecyclerView.LayoutManager) + * @see #createVerticalHelper(RecyclerView.LayoutManager) + */ +public abstract class OrientationHelper { + + private static final int INVALID_SIZE = Integer.MIN_VALUE; + + protected final RecyclerView.LayoutManager mLayoutManager; + + public static final int HORIZONTAL = LinearLayout.HORIZONTAL; + + public static final int VERTICAL = LinearLayout.VERTICAL; + + private int mLastTotalSpace = INVALID_SIZE; + + private OrientationHelper(RecyclerView.LayoutManager layoutManager) { + mLayoutManager = layoutManager; + } + + /** + * Call this method after onLayout method is complete if state is NOT pre-layout. + * This method records information like layout bounds that might be useful in the next layout + * calculations. + */ + public void onLayoutComplete() { + mLastTotalSpace = getTotalSpace(); + } + + /** + * Returns the layout space change between the previous layout pass and current layout pass. + *

+ * Make sure you call {@link #onLayoutComplete()} at the end of your LayoutManager's + * {@link RecyclerView.LayoutManager#onLayoutChildren(RecyclerView.Recycler, + * RecyclerView.State)} method. + * + * @return The difference between the current total space and previous layout's total space. + * @see #onLayoutComplete() + */ + public int getTotalSpaceChange() { + return INVALID_SIZE == mLastTotalSpace ? 0 : getTotalSpace() - mLastTotalSpace; + } + + /** + * Returns the start of the view including its decoration and margin. + *

+ * For example, for the horizontal helper, if a View's left is at pixel 20, has 2px left + * decoration and 3px left margin, returned value will be 15px. + * + * @param view The view element to check + * @return The first pixel of the element + * @see #getDecoratedEnd(android.view.View) + */ + public abstract int getDecoratedStart(View view); + + /** + * Returns the end of the view including its decoration and margin. + *

+ * For example, for the horizontal helper, if a View's right is at pixel 200, has 2px right + * decoration and 3px right margin, returned value will be 205. + * + * @param view The view element to check + * @return The last pixel of the element + * @see #getDecoratedStart(android.view.View) + */ + public abstract int getDecoratedEnd(View view); + + /** + * Returns the space occupied by this View in the current orientation including decorations and + * margins. + * + * @param view The view element to check + * @return Total space occupied by this view + * @see #getDecoratedMeasurementInOther(View) + */ + public abstract int getDecoratedMeasurement(View view); + + /** + * Returns the space occupied by this View in the perpendicular orientation including + * decorations and margins. + * + * @param view The view element to check + * @return Total space occupied by this view in the perpendicular orientation to current one + * @see #getDecoratedMeasurement(View) + */ + public abstract int getDecoratedMeasurementInOther(View view); + + /** + * Returns the start position of the layout after the start padding is added. + * + * @return The very first pixel we can draw. + */ + public abstract int getStartAfterPadding(); + + /** + * Returns the end position of the layout after the end padding is removed. + * + * @return The end boundary for this layout. + */ + public abstract int getEndAfterPadding(); + + /** + * Returns the end position of the layout without taking padding into account. + * + * @return The end boundary for this layout without considering padding. + */ + public abstract int getEnd(); + + /** + * Offsets all children's positions by the given amount. + * + * @param amount Value to add to each child's layout parameters + */ + public abstract void offsetChildren(int amount); + + /** + * Returns the total space to layout. This number is the difference between + * {@link #getEndAfterPadding()} and {@link #getStartAfterPadding()}. + * + * @return Total space to layout children + */ + public abstract int getTotalSpace(); + + /** + * Offsets the child in this orientation. + * + * @param view View to offset + * @param offset offset amount + */ + public abstract void offsetChild(View view, int offset); + + /** + * Returns the padding at the end of the layout. For horizontal helper, this is the right + * padding and for vertical helper, this is the bottom padding. This method does not check + * whether the layout is RTL or not. + * + * @return The padding at the end of the layout. + */ + public abstract int getEndPadding(); + + /** + * Creates an OrientationHelper for the given LayoutManager and orientation. + * + * @param layoutManager LayoutManager to attach to + * @param orientation Desired orientation. Should be {@link #HORIZONTAL} or {@link #VERTICAL} + * @return A new OrientationHelper + */ + public static OrientationHelper createOrientationHelper( + RecyclerView.LayoutManager layoutManager, int orientation) { + switch (orientation) { + case HORIZONTAL: + return createHorizontalHelper(layoutManager); + case VERTICAL: + return createVerticalHelper(layoutManager); + } + throw new IllegalArgumentException("invalid orientation"); + } + + /** + * Creates a horizontal OrientationHelper for the given LayoutManager. + * + * @param layoutManager The LayoutManager to attach to. + * @return A new OrientationHelper + */ + public static OrientationHelper createHorizontalHelper( + RecyclerView.LayoutManager layoutManager) { + return new OrientationHelper(layoutManager) { + @Override + public int getEndAfterPadding() { + return mLayoutManager.getWidth() - mLayoutManager.getPaddingRight(); + } + + @Override + public int getEnd() { + return mLayoutManager.getWidth(); + } + + @Override + public void offsetChildren(int amount) { + mLayoutManager.offsetChildrenHorizontal(amount); + } + + @Override + public int getStartAfterPadding() { + return mLayoutManager.getPaddingLeft(); + } + + @Override + public int getDecoratedMeasurement(View view) { + final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) + view.getLayoutParams(); + return mLayoutManager.getDecoratedMeasuredWidth(view) + params.leftMargin + + params.rightMargin; + } + + @Override + public int getDecoratedMeasurementInOther(View view) { + final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) + view.getLayoutParams(); + return mLayoutManager.getDecoratedMeasuredHeight(view) + params.topMargin + + params.bottomMargin; + } + + @Override + public int getDecoratedEnd(View view) { + final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) + view.getLayoutParams(); + return mLayoutManager.getDecoratedRight(view) + params.rightMargin; + } + + @Override + public int getDecoratedStart(View view) { + final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) + view.getLayoutParams(); + return mLayoutManager.getDecoratedLeft(view) - params.leftMargin; + } + + @Override + public int getTotalSpace() { + return mLayoutManager.getWidth() - mLayoutManager.getPaddingLeft() + - mLayoutManager.getPaddingRight(); + } + + @Override + public void offsetChild(View view, int offset) { + view.offsetLeftAndRight(offset); + } + + @Override + public int getEndPadding() { + return mLayoutManager.getPaddingRight(); + } + }; + } + + /** + * Creates a vertical OrientationHelper for the given LayoutManager. + * + * @param layoutManager The LayoutManager to attach to. + * @return A new OrientationHelper + */ + public static OrientationHelper createVerticalHelper(RecyclerView.LayoutManager layoutManager) { + return new OrientationHelper(layoutManager) { + @Override + public int getEndAfterPadding() { + return mLayoutManager.getHeight() - mLayoutManager.getPaddingBottom(); + } + + @Override + public int getEnd() { + return mLayoutManager.getHeight(); + } + + @Override + public void offsetChildren(int amount) { + mLayoutManager.offsetChildrenVertical(amount); + } + + @Override + public int getStartAfterPadding() { + return mLayoutManager.getPaddingTop(); + } + + @Override + public int getDecoratedMeasurement(View view) { + final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) + view.getLayoutParams(); + return mLayoutManager.getDecoratedMeasuredHeight(view) + params.topMargin + + params.bottomMargin; + } + + @Override + public int getDecoratedMeasurementInOther(View view) { + final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) + view.getLayoutParams(); + return mLayoutManager.getDecoratedMeasuredWidth(view) + params.leftMargin + + params.rightMargin; + } + + @Override + public int getDecoratedEnd(View view) { + final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) + view.getLayoutParams(); + return mLayoutManager.getDecoratedBottom(view) + params.bottomMargin; + } + + @Override + public int getDecoratedStart(View view) { + final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) + view.getLayoutParams(); + return mLayoutManager.getDecoratedTop(view) - params.topMargin; + } + + @Override + public int getTotalSpace() { + return mLayoutManager.getHeight() - mLayoutManager.getPaddingTop() + - mLayoutManager.getPaddingBottom(); + } + + @Override + public void offsetChild(View view, int offset) { + view.offsetTopAndBottom(offset); + } + + @Override + public int getEndPadding() { + return mLayoutManager.getPaddingBottom(); + } + }; + } +} \ No newline at end of file diff --git a/TMessagesProj/src/main/java/org/telegram/android/support/widget/PositionMap.java b/TMessagesProj/src/main/java/org/telegram/android/support/widget/PositionMap.java new file mode 100644 index 000000000..b97bd5a65 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/android/support/widget/PositionMap.java @@ -0,0 +1,460 @@ +/* + * Copyright (C) 2013 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.android.support.widget; + +import java.util.ArrayList; + +/** + * Like a SparseArray, but with the ability to offset key ranges for bulk insertions/deletions. + */ +class PositionMap implements Cloneable { + private static final Object DELETED = new Object(); + private boolean mGarbage = false; + + private int[] mKeys; + private Object[] mValues; + private int mSize; + + /** + * Creates a new SparseArray containing no mappings. + */ + public PositionMap() { + this(10); + } + + /** + * Creates a new PositionMap containing no mappings that will not + * require any additional memory allocation to store the specified + * number of mappings. If you supply an initial capacity of 0, the + * sparse array will be initialized with a light-weight representation + * not requiring any additional array allocations. + */ + public PositionMap(int initialCapacity) { + if (initialCapacity == 0) { + mKeys = ContainerHelpers.EMPTY_INTS; + mValues = ContainerHelpers.EMPTY_OBJECTS; + } else { + initialCapacity = idealIntArraySize(initialCapacity); + mKeys = new int[initialCapacity]; + mValues = new Object[initialCapacity]; + } + mSize = 0; + } + + @Override + @SuppressWarnings("unchecked") + public PositionMap clone() { + PositionMap clone = null; + try { + clone = (PositionMap) super.clone(); + clone.mKeys = mKeys.clone(); + clone.mValues = mValues.clone(); + } catch (CloneNotSupportedException cnse) { + /* ignore */ + } + return clone; + } + + /** + * Gets the Object mapped from the specified key, or null + * if no such mapping has been made. + */ + public E get(int key) { + return get(key, null); + } + + /** + * Gets the Object mapped from the specified key, or the specified Object + * if no such mapping has been made. + */ + @SuppressWarnings("unchecked") + public E get(int key, E valueIfKeyNotFound) { + int i = ContainerHelpers.binarySearch(mKeys, mSize, key); + + if (i < 0 || mValues[i] == DELETED) { + return valueIfKeyNotFound; + } else { + return (E) mValues[i]; + } + } + + /** + * Removes the mapping from the specified key, if there was any. + */ + public void delete(int key) { + int i = ContainerHelpers.binarySearch(mKeys, mSize, key); + + if (i >= 0) { + if (mValues[i] != DELETED) { + mValues[i] = DELETED; + mGarbage = true; + } + } + } + + /** + * Alias for {@link #delete(int)}. + */ + public void remove(int key) { + delete(key); + } + + /** + * Removes the mapping at the specified index. + */ + public void removeAt(int index) { + if (mValues[index] != DELETED) { + mValues[index] = DELETED; + mGarbage = true; + } + } + + /** + * Remove a range of mappings as a batch. + * + * @param index Index to begin at + * @param size Number of mappings to remove + */ + public void removeAtRange(int index, int size) { + final int end = Math.min(mSize, index + size); + for (int i = index; i < end; i++) { + removeAt(i); + } + } + + public void insertKeyRange(int keyStart, int count) { + + } + + public void removeKeyRange(ArrayList removedItems, int keyStart, int count) { + + } + + private void gc() { + // Log.e("SparseArray", "gc start with " + mSize); + + int n = mSize; + int o = 0; + int[] keys = mKeys; + Object[] values = mValues; + + for (int i = 0; i < n; i++) { + Object val = values[i]; + + if (val != DELETED) { + if (i != o) { + keys[o] = keys[i]; + values[o] = val; + values[i] = null; + } + + o++; + } + } + + mGarbage = false; + mSize = o; + + // Log.e("SparseArray", "gc end with " + mSize); + } + + /** + * Adds a mapping from the specified key to the specified value, + * replacing the previous mapping from the specified key if there + * was one. + */ + public void put(int key, E value) { + int i = ContainerHelpers.binarySearch(mKeys, mSize, key); + + if (i >= 0) { + mValues[i] = value; + } else { + i = ~i; + + if (i < mSize && mValues[i] == DELETED) { + mKeys[i] = key; + mValues[i] = value; + return; + } + + if (mGarbage && mSize >= mKeys.length) { + gc(); + + // Search again because indices may have changed. + i = ~ContainerHelpers.binarySearch(mKeys, mSize, key); + } + + if (mSize >= mKeys.length) { + int n = idealIntArraySize(mSize + 1); + + int[] nkeys = new int[n]; + Object[] nvalues = new Object[n]; + + // Log.e("SparseArray", "grow " + mKeys.length + " to " + n); + System.arraycopy(mKeys, 0, nkeys, 0, mKeys.length); + System.arraycopy(mValues, 0, nvalues, 0, mValues.length); + + mKeys = nkeys; + mValues = nvalues; + } + + if (mSize - i != 0) { + // Log.e("SparseArray", "move " + (mSize - i)); + System.arraycopy(mKeys, i, mKeys, i + 1, mSize - i); + System.arraycopy(mValues, i, mValues, i + 1, mSize - i); + } + + mKeys[i] = key; + mValues[i] = value; + mSize++; + } + } + + /** + * Returns the number of key-value mappings that this SparseArray + * currently stores. + */ + public int size() { + if (mGarbage) { + gc(); + } + + return mSize; + } + + /** + * Given an index in the range 0...size()-1, returns + * the key from the indexth key-value mapping that this + * SparseArray stores. + */ + public int keyAt(int index) { + if (mGarbage) { + gc(); + } + + return mKeys[index]; + } + + /** + * Given an index in the range 0...size()-1, returns + * the value from the indexth key-value mapping that this + * SparseArray stores. + */ + @SuppressWarnings("unchecked") + public E valueAt(int index) { + if (mGarbage) { + gc(); + } + + return (E) mValues[index]; + } + + /** + * Given an index in the range 0...size()-1, sets a new + * value for the indexth key-value mapping that this + * SparseArray stores. + */ + public void setValueAt(int index, E value) { + if (mGarbage) { + gc(); + } + + mValues[index] = value; + } + + /** + * Returns the index for which {@link #keyAt} would return the + * specified key, or a negative number if the specified + * key is not mapped. + */ + public int indexOfKey(int key) { + if (mGarbage) { + gc(); + } + + return ContainerHelpers.binarySearch(mKeys, mSize, key); + } + + /** + * Returns an index for which {@link #valueAt} would return the + * specified key, or a negative number if no keys map to the + * specified value. + *

Beware that this is a linear search, unlike lookups by key, + * and that multiple keys can map to the same value and this will + * find only one of them. + *

Note also that unlike most collections' {@code indexOf} methods, + * this method compares values using {@code ==} rather than {@code equals}. + */ + public int indexOfValue(E value) { + if (mGarbage) { + gc(); + } + + for (int i = 0; i < mSize; i++) + if (mValues[i] == value) + return i; + + return -1; + } + + /** + * Removes all key-value mappings from this SparseArray. + */ + public void clear() { + int n = mSize; + Object[] values = mValues; + + for (int i = 0; i < n; i++) { + values[i] = null; + } + + mSize = 0; + mGarbage = false; + } + + /** + * Puts a key/value pair into the array, optimizing for the case where + * the key is greater than all existing keys in the array. + */ + public void append(int key, E value) { + if (mSize != 0 && key <= mKeys[mSize - 1]) { + put(key, value); + return; + } + + if (mGarbage && mSize >= mKeys.length) { + gc(); + } + + int pos = mSize; + if (pos >= mKeys.length) { + int n = idealIntArraySize(pos + 1); + + int[] nkeys = new int[n]; + Object[] nvalues = new Object[n]; + + // Log.e("SparseArray", "grow " + mKeys.length + " to " + n); + System.arraycopy(mKeys, 0, nkeys, 0, mKeys.length); + System.arraycopy(mValues, 0, nvalues, 0, mValues.length); + + mKeys = nkeys; + mValues = nvalues; + } + + mKeys[pos] = key; + mValues[pos] = value; + mSize = pos + 1; + } + + /** + * {@inheritDoc} + * + *

This implementation composes a string by iterating over its mappings. If + * this map contains itself as a value, the string "(this Map)" + * will appear in its place. + */ + @Override + public String toString() { + if (size() <= 0) { + return "{}"; + } + + StringBuilder buffer = new StringBuilder(mSize * 28); + buffer.append('{'); + for (int i=0; i 0) { + buffer.append(", "); + } + int key = keyAt(i); + buffer.append(key); + buffer.append('='); + Object value = valueAt(i); + if (value != this) { + buffer.append(value); + } else { + buffer.append("(this Map)"); + } + } + buffer.append('}'); + return buffer.toString(); + } + + static int idealByteArraySize(int need) { + for (int i = 4; i < 32; i++) + if (need <= (1 << i) - 12) + return (1 << i) - 12; + + return need; + } + + static int idealBooleanArraySize(int need) { + return idealByteArraySize(need); + } + + static int idealShortArraySize(int need) { + return idealByteArraySize(need * 2) / 2; + } + + static int idealCharArraySize(int need) { + return idealByteArraySize(need * 2) / 2; + } + + static int idealIntArraySize(int need) { + return idealByteArraySize(need * 4) / 4; + } + + static int idealFloatArraySize(int need) { + return idealByteArraySize(need * 4) / 4; + } + + static int idealObjectArraySize(int need) { + return idealByteArraySize(need * 4) / 4; + } + + static int idealLongArraySize(int need) { + return idealByteArraySize(need * 8) / 8; + } + + static class ContainerHelpers { + static final boolean[] EMPTY_BOOLEANS = new boolean[0]; + static final int[] EMPTY_INTS = new int[0]; + static final long[] EMPTY_LONGS = new long[0]; + static final Object[] EMPTY_OBJECTS = new Object[0]; + + // This is Arrays.binarySearch(), but doesn't do any argument validation. + static int binarySearch(int[] array, int size, int value) { + int lo = 0; + int hi = size - 1; + + while (lo <= hi) { + final int mid = (lo + hi) >>> 1; + final int midVal = array[mid]; + + if (midVal < value) { + lo = mid + 1; + } else if (midVal > value) { + hi = mid - 1; + } else { + return mid; // value found + } + } + return ~lo; // value not present + } + } + +} diff --git a/TMessagesProj/src/main/java/org/telegram/android/support/widget/RecyclerView.java b/TMessagesProj/src/main/java/org/telegram/android/support/widget/RecyclerView.java new file mode 100644 index 000000000..ebb4e89ba --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/android/support/widget/RecyclerView.java @@ -0,0 +1,9319 @@ +/* + * Copyright (C) 2013 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.android.support.widget; + +import android.content.Context; +import android.database.Observable; +import android.graphics.Canvas; +import android.graphics.PointF; +import android.graphics.Rect; +import android.os.Build; +import android.os.Bundle; +import android.os.Parcel; +import android.os.Parcelable; +import android.support.annotation.Nullable; +import android.support.v4.util.ArrayMap; +import android.support.v4.view.InputDeviceCompat; +import android.support.v4.view.MotionEventCompat; +import android.support.v4.view.ScrollingView; +import android.support.v4.view.VelocityTrackerCompat; +import android.support.v4.view.ViewCompat; +import android.support.v4.view.ViewConfigurationCompat; +import android.support.v4.view.accessibility.AccessibilityEventCompat; +import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat; +import android.support.v4.view.accessibility.AccessibilityRecordCompat; +import android.support.v4.widget.EdgeEffectCompat; +import android.support.v4.widget.ScrollerCompat; +import static org.telegram.android.support.widget.AdapterHelper.UpdateOp; +import static org.telegram.android.support.widget.AdapterHelper.Callback; + +import android.util.AttributeSet; +import android.util.Log; +import android.util.SparseArray; +import android.util.SparseIntArray; +import android.util.TypedValue; +import android.view.FocusFinder; +import android.view.MotionEvent; +import android.view.VelocityTracker; +import android.view.View; +import android.view.ViewConfiguration; +import android.view.ViewGroup; +import android.view.ViewParent; +import android.view.accessibility.AccessibilityEvent; +import android.view.accessibility.AccessibilityManager; +import android.view.animation.Interpolator; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * A flexible view for providing a limited window into a large data set. + * + *

Glossary of terms:

+ * + *
    + *
  • Adapter: A subclass of {@link Adapter} responsible for providing views + * that represent items in a data set.
  • + *
  • Position: The position of a data item within an Adapter.
  • + *
  • Index: The index of an attached child view as used in a call to + * {@link ViewGroup#getChildAt}. Contrast with Position.
  • + *
  • Binding: The process of preparing a child view to display data corresponding + * to a position within the adapter.
  • + *
  • Recycle (view): A view previously used to display data for a specific adapter + * position may be placed in a cache for later reuse to display the same type of data again + * later. This can drastically improve performance by skipping initial layout inflation + * or construction.
  • + *
  • Scrap (view): A child view that has entered into a temporarily detached + * state during layout. Scrap views may be reused without becoming fully detached + * from the parent RecyclerView, either unmodified if no rebinding is required or modified + * by the adapter if the view was considered dirty.
  • + *
  • Dirty (view): A child view that must be rebound by the adapter before + * being displayed.
  • + *
+ * + *

Positions in RecyclerView:

+ *

+ * RecyclerView introduces an additional level of abstraction between the {@link Adapter} and + * {@link LayoutManager} to be able to detect data set changes in batches during a layout + * calculation. This saves LayoutManager from tracking adapter changes to calculate animations. + * It also helps with performance because all view bindings happen at the same time and unnecessary + * bindings are avoided. + *

+ * For this reason, there are two types of position related methods in RecyclerView: + *

    + *
  • layout position: Position of an item in the latest layout calculation. This is the + * position from the LayoutManager's perspective.
  • + *
  • adapter position: Position of an item in the adapter. This is the position from + * the Adapter's perspective.
  • + *
+ *

+ * These two positions are the same except the time between dispatching adapter.notify* + * events and calculating the updated layout. + *

+ * Methods that return or receive *LayoutPosition* use position as of the latest + * layout calculation (e.g. {@link ViewHolder#getLayoutPosition()}, + * {@link #findViewHolderForLayoutPosition(int)}). These positions include all changes until the + * last layout calculation. You can rely on these positions to be consistent with what user is + * currently seeing on the screen. For example, if you have a list of items on the screen and user + * asks for the 5th element, you should use these methods as they'll match what user + * is seeing. + *

+ * The other set of position related methods are in the form of + * *AdapterPosition*. (e.g. {@link ViewHolder#getAdapterPosition()}, + * {@link #findViewHolderForAdapterPosition(int)}) You should use these methods when you need to + * work with up-to-date adapter positions even if they may not have been reflected to layout yet. + * For example, if you want to access the item in the adapter on a ViewHolder click, you should use + * {@link ViewHolder#getAdapterPosition()}. Beware that these methods may not be able to calculate + * adapter positions if {@link Adapter#notifyDataSetChanged()} has been called and new layout has + * not yet been calculated. For this reasons, you should carefully handle {@link #NO_POSITION} or + * null results from these methods. + *

+ * When writing a {@link LayoutManager} you almost always want to use layout positions whereas when + * writing an {@link Adapter}, you probably want to use adapter positions. + */ +public class RecyclerView extends ViewGroup implements ScrollingView { + + private static final String TAG = "RecyclerView"; + + private static final boolean DEBUG = false; + + /** + * On Kitkat and JB MR2, there is a bug which prevents DisplayList from being invalidated if + * a View is two levels deep(wrt to ViewHolder.itemView). DisplayList can be invalidated by + * setting View's visibility to INVISIBLE when View is detached. On Kitkat and JB MR2, Recycler + * recursively traverses itemView and invalidates display list for each ViewGroup that matches + * this criteria. + */ + private static final boolean FORCE_INVALIDATE_DISPLAY_LIST = Build.VERSION.SDK_INT == 18 + || Build.VERSION.SDK_INT == 19 || Build.VERSION.SDK_INT == 20; + + private static final boolean DISPATCH_TEMP_DETACH = false; + public static final int HORIZONTAL = 0; + public static final int VERTICAL = 1; + + public static final int NO_POSITION = -1; + public static final long NO_ID = -1; + public static final int INVALID_TYPE = -1; + + /** + * Constant for use with {@link #setScrollingTouchSlop(int)}. Indicates + * that the RecyclerView should use the standard touch slop for smooth, + * continuous scrolling. + */ + public static final int TOUCH_SLOP_DEFAULT = 0; + + /** + * Constant for use with {@link #setScrollingTouchSlop(int)}. Indicates + * that the RecyclerView should use the standard touch slop for scrolling + * widgets that snap to a page or other coarse-grained barrier. + */ + public static final int TOUCH_SLOP_PAGING = 1; + + private static final int MAX_SCROLL_DURATION = 2000; + + /** + * RecyclerView is calculating a scroll. + * If there are too many of these in Systrace, some Views inside RecyclerView might be causing + * it. Try to avoid using EditText, focusable views or handle them with care. + */ + private static final String TRACE_SCROLL_TAG = "RV Scroll"; + + /** + * OnLayout has been called by the View system. + * If this shows up too many times in Systrace, make sure the children of RecyclerView do not + * update themselves directly. This will cause a full re-layout but when it happens via the + * Adapter notifyItemChanged, RecyclerView can avoid full layout calculation. + */ + private static final String TRACE_ON_LAYOUT_TAG = "RV OnLayout"; + + /** + * NotifyDataSetChanged or equal has been called. + * If this is taking a long time, try sending granular notify adapter changes instead of just + * calling notifyDataSetChanged or setAdapter / swapAdapter. Adding stable ids to your adapter + * might help. + */ + private static final String TRACE_ON_DATA_SET_CHANGE_LAYOUT_TAG = "RV FullInvalidate"; + + /** + * RecyclerView is doing a layout for partial adapter updates (we know what has changed) + * If this is taking a long time, you may have dispatched too many Adapter updates causing too + * many Views being rebind. Make sure all are necessary and also prefer using notify*Range + * methods. + */ + private static final String TRACE_HANDLE_ADAPTER_UPDATES_TAG = "RV PartialInvalidate"; + + /** + * RecyclerView is rebinding a View. + * If this is taking a lot of time, consider optimizing your layout or make sure you are not + * doing extra operations in onBindViewHolder call. + */ + private static final String TRACE_BIND_VIEW_TAG = "RV OnBindView"; + + /** + * RecyclerView is creating a new View. + * If too many of these present in Systrace: + * - There might be a problem in Recycling (e.g. custom Animations that set transient state and + * prevent recycling or ItemAnimator not implementing the contract properly. ({@link + * > Adapter#onFailedToRecycleView(ViewHolder)}) + * + * - There might be too many item view types. + * > Try merging them + * + * - There might be too many itemChange animations and not enough space in RecyclerPool. + * >Try increasing your pool size and item cache size. + */ + private static final String TRACE_CREATE_VIEW_TAG = "RV CreateView"; + + private final RecyclerViewDataObserver mObserver = new RecyclerViewDataObserver(); + + final Recycler mRecycler = new Recycler(); + + private SavedState mPendingSavedState; + + AdapterHelper mAdapterHelper; + + ChildHelper mChildHelper; + + /** + * Prior to L, there is no way to query this variable which is why we override the setter and + * track it here. + */ + private boolean mClipToPadding; + + /** + * Note: this Runnable is only ever posted if: + * 1) We've been through first layout + * 2) We know we have a fixed size (mHasFixedSize) + * 3) We're attached + */ + private final Runnable mUpdateChildViewsRunnable = new Runnable() { + public void run() { + if (!mFirstLayoutComplete) { + // a layout request will happen, we should not do layout here. + return; + } + if (mDataSetHasChangedAfterLayout) { + dispatchLayout(); + } else if (mAdapterHelper.hasPendingUpdates()) { + eatRequestLayout(); + mAdapterHelper.preProcess(); + if (!mLayoutRequestEaten) { + // We run this after pre-processing is complete so that ViewHolders have their + // final adapter positions. No need to run it if a layout is already requested. + rebindUpdatedViewHolders(); + } + resumeRequestLayout(true); + } + } + }; + + private final Rect mTempRect = new Rect(); + private Adapter mAdapter; + private LayoutManager mLayout; + private RecyclerListener mRecyclerListener; + private final ArrayList mItemDecorations = new ArrayList(); + private final ArrayList mOnItemTouchListeners = + new ArrayList(); + private OnItemTouchListener mActiveOnItemTouchListener; + private boolean mIsAttached; + private boolean mHasFixedSize; + private boolean mFirstLayoutComplete; + private boolean mEatRequestLayout; + private boolean mLayoutRequestEaten; + // binary OR of change events that were eaten during a layout or scroll. + private int mEatenAccessibilityChangeFlags; + private boolean mAdapterUpdateDuringMeasure; + private final boolean mPostUpdatesOnAnimation; + private final AccessibilityManager mAccessibilityManager; + + /** + * Set to true when an adapter data set changed notification is received. + * In that case, we cannot run any animations since we don't know what happened. + */ + private boolean mDataSetHasChangedAfterLayout = false; + + /** + * This variable is incremented during a dispatchLayout and/or scroll. + * Some methods should not be called during these periods (e.g. adapter data change). + * Doing so will create hard to find bugs so we better check it and throw an exception. + * + * @see #assertInLayoutOrScroll(String) + * @see #assertNotInLayoutOrScroll(String) + */ + private int mLayoutOrScrollCounter = 0; + + private EdgeEffectCompat mLeftGlow, mTopGlow, mRightGlow, mBottomGlow; + + ItemAnimator mItemAnimator = new DefaultItemAnimator(); + + private static final int INVALID_POINTER = -1; + + /** + * The RecyclerView is not currently scrolling. + * @see #getScrollState() + */ + public static final int SCROLL_STATE_IDLE = 0; + + /** + * The RecyclerView is currently being dragged by outside input such as user touch input. + * @see #getScrollState() + */ + public static final int SCROLL_STATE_DRAGGING = 1; + + /** + * The RecyclerView is currently animating to a final position while not under + * outside control. + * @see #getScrollState() + */ + public static final int SCROLL_STATE_SETTLING = 2; + + // Touch/scrolling handling + + private int mScrollState = SCROLL_STATE_IDLE; + private int mScrollPointerId = INVALID_POINTER; + private VelocityTracker mVelocityTracker; + private int mInitialTouchX; + private int mInitialTouchY; + private int mLastTouchX; + private int mLastTouchY; + private int mTouchSlop; + private final int mMinFlingVelocity; + private final int mMaxFlingVelocity; + // This value is used when handling generic motion events. + private float mScrollFactor = Float.MIN_VALUE; + + private final ViewFlinger mViewFlinger = new ViewFlinger(); + + final State mState = new State(); + + private OnScrollListener mScrollListener; + private List mScrollListeners; + + // For use in item animations + boolean mItemsAddedOrRemoved = false; + boolean mItemsChanged = false; + private ItemAnimator.ItemAnimatorListener mItemAnimatorListener = + new ItemAnimatorRestoreListener(); + private boolean mPostedAnimatorRunner = false; + private RecyclerViewAccessibilityDelegate mAccessibilityDelegate; + + // simple array to keep min and max child position during a layout calculation + // preserved not to create a new one in each layout pass + private final int[] mMinMaxLayoutPositions = new int[2]; + + private Runnable mItemAnimatorRunner = new Runnable() { + @Override + public void run() { + if (mItemAnimator != null) { + mItemAnimator.runPendingAnimations(); + } + mPostedAnimatorRunner = false; + } + }; + + private static final Interpolator sQuinticInterpolator = new Interpolator() { + public float getInterpolation(float t) { + t -= 1.0f; + return t * t * t * t * t + 1.0f; + } + }; + + public RecyclerView(Context context) { + this(context, null); + } + + public RecyclerView(Context context, @Nullable AttributeSet attrs) { + this(context, attrs, 0); + } + + public RecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + setFocusableInTouchMode(true); + final int version = Build.VERSION.SDK_INT; + mPostUpdatesOnAnimation = version >= 16; + + final ViewConfiguration vc = ViewConfiguration.get(context); + mTouchSlop = vc.getScaledTouchSlop(); + mMinFlingVelocity = vc.getScaledMinimumFlingVelocity(); + mMaxFlingVelocity = vc.getScaledMaximumFlingVelocity(); + setWillNotDraw(ViewCompat.getOverScrollMode(this) == ViewCompat.OVER_SCROLL_NEVER); + + mItemAnimator.setListener(mItemAnimatorListener); + initAdapterManager(); + initChildrenHelper(); + // If not explicitly specified this view is important for accessibility. + if (ViewCompat.getImportantForAccessibility(this) + == ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_AUTO) { + ViewCompat.setImportantForAccessibility(this, + ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_YES); + } + mAccessibilityManager = (AccessibilityManager) getContext() + .getSystemService(Context.ACCESSIBILITY_SERVICE); + setAccessibilityDelegateCompat(new RecyclerViewAccessibilityDelegate(this)); + } + + /** + * Returns the accessibility delegate compatibility implementation used by the RecyclerView. + * @return An instance of AccessibilityDelegateCompat used by RecyclerView + */ + public RecyclerViewAccessibilityDelegate getCompatAccessibilityDelegate() { + return mAccessibilityDelegate; + } + + /** + * Sets the accessibility delegate compatibility implementation used by RecyclerView. + * @param accessibilityDelegate The accessibility delegate to be used by RecyclerView. + */ + public void setAccessibilityDelegateCompat( + RecyclerViewAccessibilityDelegate accessibilityDelegate) { + mAccessibilityDelegate = accessibilityDelegate; + ViewCompat.setAccessibilityDelegate(this, mAccessibilityDelegate); + } + + private void initChildrenHelper() { + mChildHelper = new ChildHelper(new ChildHelper.Callback() { + @Override + public int getChildCount() { + return RecyclerView.this.getChildCount(); + } + + @Override + public void addView(View child, int index) { + RecyclerView.this.addView(child, index); + dispatchChildAttached(child); + } + + @Override + public int indexOfChild(View view) { + return RecyclerView.this.indexOfChild(view); + } + + @Override + public void removeViewAt(int index) { + final View child = RecyclerView.this.getChildAt(index); + if (child != null) { + dispatchChildDetached(child); + } + RecyclerView.this.removeViewAt(index); + } + + @Override + public View getChildAt(int offset) { + return RecyclerView.this.getChildAt(offset); + } + + @Override + public void removeAllViews() { + final int count = getChildCount(); + for (int i = 0; i < count; i ++) { + dispatchChildDetached(getChildAt(i)); + } + RecyclerView.this.removeAllViews(); + } + + @Override + public ViewHolder getChildViewHolder(View view) { + return getChildViewHolderInt(view); + } + + @Override + public void attachViewToParent(View child, int index, + ViewGroup.LayoutParams layoutParams) { + final ViewHolder vh = getChildViewHolderInt(child); + if (vh != null) { + if (!vh.isTmpDetached() && !vh.shouldIgnore()) { + throw new IllegalArgumentException("Called attach on a child which is not" + + " detached: " + vh); + } + if (DEBUG) { + Log.d(TAG, "reAttach " + vh); + } + vh.clearTmpDetachFlag(); + } + RecyclerView.this.attachViewToParent(child, index, layoutParams); + } + + @Override + public void detachViewFromParent(int offset) { + final View view = getChildAt(offset); + if (view != null) { + final ViewHolder vh = getChildViewHolderInt(view); + if (vh != null) { + if (vh.isTmpDetached() && !vh.shouldIgnore()) { + throw new IllegalArgumentException("called detach on an already" + + " detached child " + vh); + } + if (DEBUG) { + Log.d(TAG, "tmpDetach " + vh); + } + vh.addFlags(ViewHolder.FLAG_TMP_DETACHED); + } + } + RecyclerView.this.detachViewFromParent(offset); + } + }); + } + + void initAdapterManager() { + mAdapterHelper = new AdapterHelper(new Callback() { + @Override + public ViewHolder findViewHolder(int position) { + final ViewHolder vh = findViewHolderForPosition(position, true); + if (vh == null) { + return null; + } + // ensure it is not hidden because for adapter helper, the only thing matter is that + // LM thinks view is a child. + if (mChildHelper.isHidden(vh.itemView)) { + if (DEBUG) { + Log.d(TAG, "assuming view holder cannot be find because it is hidden"); + } + return null; + } + return vh; + } + + @Override + public void offsetPositionsForRemovingInvisible(int start, int count) { + offsetPositionRecordsForRemove(start, count, true); + mItemsAddedOrRemoved = true; + mState.mDeletedInvisibleItemCountSincePreviousLayout += count; + } + + @Override + public void offsetPositionsForRemovingLaidOutOrNewView(int positionStart, int itemCount) { + offsetPositionRecordsForRemove(positionStart, itemCount, false); + mItemsAddedOrRemoved = true; + } + + @Override + public void markViewHoldersUpdated(int positionStart, int itemCount) { + viewRangeUpdate(positionStart, itemCount); + mItemsChanged = true; + } + + @Override + public void onDispatchFirstPass(UpdateOp op) { + dispatchUpdate(op); + } + + void dispatchUpdate(UpdateOp op) { + switch (op.cmd) { + case UpdateOp.ADD: + mLayout.onItemsAdded(RecyclerView.this, op.positionStart, op.itemCount); + break; + case UpdateOp.REMOVE: + mLayout.onItemsRemoved(RecyclerView.this, op.positionStart, op.itemCount); + break; + case UpdateOp.UPDATE: + mLayout.onItemsUpdated(RecyclerView.this, op.positionStart, op.itemCount); + break; + case UpdateOp.MOVE: + mLayout.onItemsMoved(RecyclerView.this, op.positionStart, op.itemCount, 1); + break; + } + } + + @Override + public void onDispatchSecondPass(UpdateOp op) { + dispatchUpdate(op); + } + + @Override + public void offsetPositionsForAdd(int positionStart, int itemCount) { + offsetPositionRecordsForInsert(positionStart, itemCount); + mItemsAddedOrRemoved = true; + } + + @Override + public void offsetPositionsForMove(int from, int to) { + offsetPositionRecordsForMove(from, to); + // should we create mItemsMoved ? + mItemsAddedOrRemoved = true; + } + }); + } + + /** + * RecyclerView can perform several optimizations if it can know in advance that changes in + * adapter content cannot change the size of the RecyclerView itself. + * If your use of RecyclerView falls into this category, set this to true. + * + * @param hasFixedSize true if adapter changes cannot affect the size of the RecyclerView. + */ + public void setHasFixedSize(boolean hasFixedSize) { + mHasFixedSize = hasFixedSize; + } + + /** + * @return true if the app has specified that changes in adapter content cannot change + * the size of the RecyclerView itself. + */ + public boolean hasFixedSize() { + return mHasFixedSize; + } + + @Override + public void setClipToPadding(boolean clipToPadding) { + if (clipToPadding != mClipToPadding) { + invalidateGlows(); + } + mClipToPadding = clipToPadding; + super.setClipToPadding(clipToPadding); + if (mFirstLayoutComplete) { + requestLayout(); + } + } + + /** + * Configure the scrolling touch slop for a specific use case. + * + * Set up the RecyclerView's scrolling motion threshold based on common usages. + * Valid arguments are {@link #TOUCH_SLOP_DEFAULT} and {@link #TOUCH_SLOP_PAGING}. + * + * @param slopConstant One of the TOUCH_SLOP_ constants representing + * the intended usage of this RecyclerView + */ + public void setScrollingTouchSlop(int slopConstant) { + final ViewConfiguration vc = ViewConfiguration.get(getContext()); + switch (slopConstant) { + default: + Log.w(TAG, "setScrollingTouchSlop(): bad argument constant " + + slopConstant + "; using default value"); + // fall-through + case TOUCH_SLOP_DEFAULT: + mTouchSlop = vc.getScaledTouchSlop(); + break; + + case TOUCH_SLOP_PAGING: + mTouchSlop = ViewConfigurationCompat.getScaledPagingTouchSlop(vc); + break; + } + } + + /** + * Swaps the current adapter with the provided one. It is similar to + * {@link #setAdapter(Adapter)} but assumes existing adapter and the new adapter uses the same + * {@link ViewHolder} and does not clear the RecycledViewPool. + *

+ * Note that it still calls onAdapterChanged callbacks. + * + * @param adapter The new adapter to set, or null to set no adapter. + * @param removeAndRecycleExistingViews If set to true, RecyclerView will recycle all existing + * Views. If adapters have stable ids and/or you want to + * animate the disappearing views, you may prefer to set + * this to false. + * @see #setAdapter(Adapter) + */ + public void swapAdapter(Adapter adapter, boolean removeAndRecycleExistingViews) { + setAdapterInternal(adapter, true, removeAndRecycleExistingViews); + setDataSetChangedAfterLayout(); + requestLayout(); + } + /** + * Set a new adapter to provide child views on demand. + *

+ * When adapter is changed, all existing views are recycled back to the pool. If the pool has + * only one adapter, it will be cleared. + * + * @param adapter The new adapter to set, or null to set no adapter. + * @see #swapAdapter(Adapter, boolean) + */ + public void setAdapter(Adapter adapter) { + setAdapterInternal(adapter, false, true); + requestLayout(); + } + + /** + * Replaces the current adapter with the new one and triggers listeners. + * @param adapter The new adapter + * @param compatibleWithPrevious If true, the new adapter is using the same View Holders and + * item types with the current adapter (helps us avoid cache + * invalidation). + * @param removeAndRecycleViews If true, we'll remove and recycle all existing views. If + * compatibleWithPrevious is false, this parameter is ignored. + */ + private void setAdapterInternal(Adapter adapter, boolean compatibleWithPrevious, + boolean removeAndRecycleViews) { + if (mAdapter != null) { + mAdapter.unregisterAdapterDataObserver(mObserver); + mAdapter.onDetachedFromRecyclerView(this); + } + if (!compatibleWithPrevious || removeAndRecycleViews) { + // end all running animations + if (mItemAnimator != null) { + mItemAnimator.endAnimations(); + } + // Since animations are ended, mLayout.children should be equal to + // recyclerView.children. This may not be true if item animator's end does not work as + // expected. (e.g. not release children instantly). It is safer to use mLayout's child + // count. + if (mLayout != null) { + mLayout.removeAndRecycleAllViews(mRecycler); + mLayout.removeAndRecycleScrapInt(mRecycler); + } + // we should clear it here before adapters are swapped to ensure correct callbacks. + mRecycler.clear(); + } + mAdapterHelper.reset(); + final Adapter oldAdapter = mAdapter; + mAdapter = adapter; + if (adapter != null) { + adapter.registerAdapterDataObserver(mObserver); + adapter.onAttachedToRecyclerView(this); + } + if (mLayout != null) { + mLayout.onAdapterChanged(oldAdapter, mAdapter); + } + mRecycler.onAdapterChanged(oldAdapter, mAdapter, compatibleWithPrevious); + mState.mStructureChanged = true; + markKnownViewsInvalid(); + } + + /** + * Retrieves the previously set adapter or null if no adapter is set. + * + * @return The previously set adapter + * @see #setAdapter(Adapter) + */ + public Adapter getAdapter() { + return mAdapter; + } + + /** + * Register a listener that will be notified whenever a child view is recycled. + * + *

This listener will be called when a LayoutManager or the RecyclerView decides + * that a child view is no longer needed. If an application associates expensive + * or heavyweight data with item views, this may be a good place to release + * or free those resources.

+ * + * @param listener Listener to register, or null to clear + */ + public void setRecyclerListener(RecyclerListener listener) { + mRecyclerListener = listener; + } + + /** + *

Return the offset of the RecyclerView's text baseline from the its top + * boundary. If the LayoutManager of this RecyclerView does not support baseline alignment, + * this method returns -1.

+ * + * @return the offset of the baseline within the RecyclerView's bounds or -1 + * if baseline alignment is not supported + */ + @Override + public int getBaseline() { + if (mLayout != null) { + return mLayout.getBaseline(); + } else { + return super.getBaseline(); + } + } + + /** + * Set the {@link LayoutManager} that this RecyclerView will use. + * + *

In contrast to other adapter-backed views such as {@link android.widget.ListView} + * or {@link android.widget.GridView}, RecyclerView allows client code to provide custom + * layout arrangements for child views. These arrangements are controlled by the + * {@link LayoutManager}. A LayoutManager must be provided for RecyclerView to function.

+ * + *

Several default strategies are provided for common uses such as lists and grids.

+ * + * @param layout LayoutManager to use + */ + public void setLayoutManager(LayoutManager layout) { + if (layout == mLayout) { + return; + } + // TODO We should do this switch a dispachLayout pass and animate children. There is a good + // chance that LayoutManagers will re-use views. + if (mLayout != null) { + if (mIsAttached) { + mLayout.dispatchDetachedFromWindow(this, mRecycler); + } + mLayout.setRecyclerView(null); + } + mRecycler.clear(); + mChildHelper.removeAllViewsUnfiltered(); + mLayout = layout; + if (layout != null) { + if (layout.mRecyclerView != null) { + throw new IllegalArgumentException("LayoutManager " + layout + + " is already attached to a RecyclerView: " + layout.mRecyclerView); + } + mLayout.setRecyclerView(this); + if (mIsAttached) { + mLayout.dispatchAttachedToWindow(this); + } + } + requestLayout(); + } + + @Override + protected Parcelable onSaveInstanceState() { + SavedState state = new SavedState(super.onSaveInstanceState()); + if (mPendingSavedState != null) { + state.copyFrom(mPendingSavedState); + } else if (mLayout != null) { + state.mLayoutState = mLayout.onSaveInstanceState(); + } else { + state.mLayoutState = null; + } + + return state; + } + + @Override + protected void onRestoreInstanceState(Parcelable state) { + mPendingSavedState = (SavedState) state; + super.onRestoreInstanceState(mPendingSavedState.getSuperState()); + if (mLayout != null && mPendingSavedState.mLayoutState != null) { + mLayout.onRestoreInstanceState(mPendingSavedState.mLayoutState); + } + } + + /** + * Override to prevent freezing of any views created by the adapter. + */ + @Override + protected void dispatchSaveInstanceState(SparseArray container) { + dispatchFreezeSelfOnly(container); + } + + /** + * Override to prevent thawing of any views created by the adapter. + */ + @Override + protected void dispatchRestoreInstanceState(SparseArray container) { + dispatchThawSelfOnly(container); + } + + /** + * Adds a view to the animatingViews list. + * mAnimatingViews holds the child views that are currently being kept around + * purely for the purpose of being animated out of view. They are drawn as a regular + * part of the child list of the RecyclerView, but they are invisible to the LayoutManager + * as they are managed separately from the regular child views. + * @param viewHolder The ViewHolder to be removed + */ + private void addAnimatingView(ViewHolder viewHolder) { + final View view = viewHolder.itemView; + final boolean alreadyParented = view.getParent() == this; + mRecycler.unscrapView(getChildViewHolder(view)); + if (viewHolder.isTmpDetached()) { + // re-attach + mChildHelper.attachViewToParent(view, -1, view.getLayoutParams(), true); + } else if(!alreadyParented) { + mChildHelper.addView(view, true); + } else { + mChildHelper.hide(view); + } + } + + /** + * Removes a view from the animatingViews list. + * @param view The view to be removed + * @see #addAnimatingView(RecyclerView.ViewHolder) + * @return true if an animating view is removed + */ + private boolean removeAnimatingView(View view) { + eatRequestLayout(); + final boolean removed = mChildHelper.removeViewIfHidden(view); + if (removed) { + final ViewHolder viewHolder = getChildViewHolderInt(view); + mRecycler.unscrapView(viewHolder); + mRecycler.recycleViewHolderInternal(viewHolder); + if (DEBUG) { + Log.d(TAG, "after removing animated view: " + view + ", " + this); + } + } + resumeRequestLayout(false); + return removed; + } + + /** + * Return the {@link LayoutManager} currently responsible for + * layout policy for this RecyclerView. + * + * @return The currently bound LayoutManager + */ + public LayoutManager getLayoutManager() { + return mLayout; + } + + /** + * Retrieve this RecyclerView's {@link RecycledViewPool}. This method will never return null; + * if no pool is set for this view a new one will be created. See + * {@link #setRecycledViewPool(RecycledViewPool) setRecycledViewPool} for more information. + * + * @return The pool used to store recycled item views for reuse. + * @see #setRecycledViewPool(RecycledViewPool) + */ + public RecycledViewPool getRecycledViewPool() { + return mRecycler.getRecycledViewPool(); + } + + /** + * Recycled view pools allow multiple RecyclerViews to share a common pool of scrap views. + * This can be useful if you have multiple RecyclerViews with adapters that use the same + * view types, for example if you have several data sets with the same kinds of item views + * displayed by a {@link android.support.v4.view.ViewPager ViewPager}. + * + * @param pool Pool to set. If this parameter is null a new pool will be created and used. + */ + public void setRecycledViewPool(RecycledViewPool pool) { + mRecycler.setRecycledViewPool(pool); + } + + /** + * Sets a new {@link ViewCacheExtension} to be used by the Recycler. + * + * @param extension ViewCacheExtension to be used or null if you want to clear the existing one. + * + * @see {@link ViewCacheExtension#getViewForPositionAndType(Recycler, int, int)} + */ + public void setViewCacheExtension(ViewCacheExtension extension) { + mRecycler.setViewCacheExtension(extension); + } + + /** + * Set the number of offscreen views to retain before adding them to the potentially shared + * {@link #getRecycledViewPool() recycled view pool}. + * + *

The offscreen view cache stays aware of changes in the attached adapter, allowing + * a LayoutManager to reuse those views unmodified without needing to return to the adapter + * to rebind them.

+ * + * @param size Number of views to cache offscreen before returning them to the general + * recycled view pool + */ + public void setItemViewCacheSize(int size) { + mRecycler.setViewCacheSize(size); + } + + /** + * Return the current scrolling state of the RecyclerView. + * + * @return {@link #SCROLL_STATE_IDLE}, {@link #SCROLL_STATE_DRAGGING} or + * {@link #SCROLL_STATE_SETTLING} + */ + public int getScrollState() { + return mScrollState; + } + + private void setScrollState(int state) { + if (state == mScrollState) { + return; + } + if (DEBUG) { + Log.d(TAG, "setting scroll state to " + state + " from " + mScrollState, + new Exception()); + } + mScrollState = state; + if (state != SCROLL_STATE_SETTLING) { + stopScrollersInternal(); + } + dispatchOnScrollStateChanged(state); + } + + /** + * Add an {@link ItemDecoration} to this RecyclerView. Item decorations can + * affect both measurement and drawing of individual item views. + * + *

Item decorations are ordered. Decorations placed earlier in the list will + * be run/queried/drawn first for their effects on item views. Padding added to views + * will be nested; a padding added by an earlier decoration will mean further + * item decorations in the list will be asked to draw/pad within the previous decoration's + * given area.

+ * + * @param decor Decoration to add + * @param index Position in the decoration chain to insert this decoration at. If this value + * is negative the decoration will be added at the end. + */ + public void addItemDecoration(ItemDecoration decor, int index) { + if (mLayout != null) { + mLayout.assertNotInLayoutOrScroll("Cannot add item decoration during a scroll or" + + " layout"); + } + if (mItemDecorations.isEmpty()) { + setWillNotDraw(false); + } + if (index < 0) { + mItemDecorations.add(decor); + } else { + mItemDecorations.add(index, decor); + } + markItemDecorInsetsDirty(); + requestLayout(); + } + + /** + * Add an {@link ItemDecoration} to this RecyclerView. Item decorations can + * affect both measurement and drawing of individual item views. + * + *

Item decorations are ordered. Decorations placed earlier in the list will + * be run/queried/drawn first for their effects on item views. Padding added to views + * will be nested; a padding added by an earlier decoration will mean further + * item decorations in the list will be asked to draw/pad within the previous decoration's + * given area.

+ * + * @param decor Decoration to add + */ + public void addItemDecoration(ItemDecoration decor) { + addItemDecoration(decor, -1); + } + + /** + * Remove an {@link ItemDecoration} from this RecyclerView. + * + *

The given decoration will no longer impact the measurement and drawing of + * item views.

+ * + * @param decor Decoration to remove + * @see #addItemDecoration(ItemDecoration) + */ + public void removeItemDecoration(ItemDecoration decor) { + if (mLayout != null) { + mLayout.assertNotInLayoutOrScroll("Cannot remove item decoration during a scroll or" + + " layout"); + } + mItemDecorations.remove(decor); + if (mItemDecorations.isEmpty()) { + setWillNotDraw(ViewCompat.getOverScrollMode(this) == ViewCompat.OVER_SCROLL_NEVER); + } + markItemDecorInsetsDirty(); + requestLayout(); + } + + /** + * Set a listener that will be notified of any changes in scroll state or position. + * + * @param listener Listener to set or null to clear + * + * @deprecated Use {@link #addOnScrollListener(OnScrollListener)} and + * {@link #removeOnScrollListener(OnScrollListener)} + */ + @Deprecated + public void setOnScrollListener(OnScrollListener listener) { + mScrollListener = listener; + } + + /** + * Add a listener that will be notified of any changes in scroll state or position. + * + *

Components that add a listener should take care to remove it when finished. + * Other components that take ownership of a view may call {@link #clearOnScrollListeners()} + * to remove all attached listeners.

+ * + * @param listener listener to set or null to clear + */ + public void addOnScrollListener(OnScrollListener listener) { + if (mScrollListeners == null) { + mScrollListeners = new ArrayList(); + } + mScrollListeners.add(listener); + } + + /** + * Remove a listener that was notified of any changes in scroll state or position. + * + * @param listener listener to set or null to clear + */ + public void removeOnScrollListener(OnScrollListener listener) { + if (mScrollListeners != null) { + mScrollListeners.remove(listener); + } + } + + /** + * Remove all secondary listener that were notified of any changes in scroll state or position. + */ + public void clearOnScrollListeners() { + if (mScrollListeners != null) { + mScrollListeners.clear(); + } + } + + /** + * Convenience method to scroll to a certain position. + * + * RecyclerView does not implement scrolling logic, rather forwards the call to + * {@link android.support.v7.widget.RecyclerView.LayoutManager#scrollToPosition(int)} + * @param position Scroll to this adapter position + * @see android.support.v7.widget.RecyclerView.LayoutManager#scrollToPosition(int) + */ + public void scrollToPosition(int position) { + stopScroll(); + if (mLayout == null) { + Log.e(TAG, "Cannot scroll to position a LayoutManager set. " + + "Call setLayoutManager with a non-null argument."); + return; + } + mLayout.scrollToPosition(position); + awakenScrollBars(); + } + + /** + * Starts a smooth scroll to an adapter position. + *

+ * To support smooth scrolling, you must override + * {@link LayoutManager#smoothScrollToPosition(RecyclerView, State, int)} and create a + * {@link SmoothScroller}. + *

+ * {@link LayoutManager} is responsible for creating the actual scroll action. If you want to + * provide a custom smooth scroll logic, override + * {@link LayoutManager#smoothScrollToPosition(RecyclerView, State, int)} in your + * LayoutManager. + * + * @param position The adapter position to scroll to + * @see LayoutManager#smoothScrollToPosition(RecyclerView, State, int) + */ + public void smoothScrollToPosition(int position) { + if (mLayout == null) { + Log.e(TAG, "Cannot smooth scroll without a LayoutManager set. " + + "Call setLayoutManager with a non-null argument."); + return; + } + mLayout.smoothScrollToPosition(this, mState, position); + } + + @Override + public void scrollTo(int x, int y) { + throw new UnsupportedOperationException( + "RecyclerView does not support scrolling to an absolute position."); + } + + @Override + public void scrollBy(int x, int y) { + if (mLayout == null) { + Log.e(TAG, "Cannot scroll without a LayoutManager set. " + + "Call setLayoutManager with a non-null argument."); + return; + } + final boolean canScrollHorizontal = mLayout.canScrollHorizontally(); + final boolean canScrollVertical = mLayout.canScrollVertically(); + if (canScrollHorizontal || canScrollVertical) { + scrollByInternal(canScrollHorizontal ? x : 0, canScrollVertical ? y : 0, false, 0, 0); + } + } + + /** + * Helper method reflect data changes to the state. + *

+ * Adapter changes during a scroll may trigger a crash because scroll assumes no data change + * but data actually changed. + *

+ * This method consumes all deferred changes to avoid that case. + */ + private void consumePendingUpdateOperations() { + mUpdateChildViewsRunnable.run(); + } + + /** + * Does not perform bounds checking. Used by internal methods that have already validated input. + *

+ * It also reports any unused scroll request to the related EdgeEffect. + * + * @param x The amount of horizontal scroll request + * @param y The amount of vertical scroll request + * @param fromMotionEvent If request is originated from a MotionEvent, this should be set to + * true and motionX/motionY should be provided, false otherwise. + * @param motionX The x coordinate of the MotionEvent which triggered this scroll. Unused if + * fromMotionEvent is false. + * @param motionY The y coordinate of the MotionEvent which triggered this scroll. Unused if + * fromMotionEvent is false. + * + * @return Whether any scroll was consumed in either direction. + */ + boolean scrollByInternal(int x, int y, boolean fromMotionEvent, int motionX, int motionY) { + int overscrollX = 0, overscrollY = 0; + int hresult = 0, vresult = 0; + consumePendingUpdateOperations(); + if (mAdapter != null) { + eatRequestLayout(); + onEnterLayoutOrScroll(); + if (x != 0) { + hresult = mLayout.scrollHorizontallyBy(x, mRecycler, mState); + overscrollX = x - hresult; + } + if (y != 0) { + vresult = mLayout.scrollVerticallyBy(y, mRecycler, mState); + overscrollY = y - vresult; + } + if (supportsChangeAnimations()) { + // Fix up shadow views used by changing animations + int count = mChildHelper.getChildCount(); + for (int i = 0; i < count; i++) { + View view = mChildHelper.getChildAt(i); + ViewHolder holder = getChildViewHolder(view); + if (holder != null && holder.mShadowingHolder != null) { + ViewHolder shadowingHolder = holder.mShadowingHolder; + View shadowingView = shadowingHolder != null ? shadowingHolder.itemView : null; + if (shadowingView != null) { + int left = view.getLeft(); + int top = view.getTop(); + if (left != shadowingView.getLeft() || top != shadowingView.getTop()) { + shadowingView.layout(left, top, + left + shadowingView.getWidth(), + top + shadowingView.getHeight()); + } + } + } + } + } + onExitLayoutOrScroll(); + resumeRequestLayout(false); + } + if (!mItemDecorations.isEmpty()) { + invalidate(); + } + if (ViewCompat.getOverScrollMode(this) != ViewCompat.OVER_SCROLL_NEVER) { + if (fromMotionEvent) { + pullGlows(motionX, overscrollX, motionY, overscrollY); + } + considerReleasingGlowsOnScroll(x, y); + } + if (hresult != 0 || vresult != 0) { + dispatchOnScrolled(hresult, vresult); + } + if (!awakenScrollBars()) { + invalidate(); + } + return hresult != 0 || vresult != 0; + } + + /** + *

Compute the horizontal offset of the horizontal scrollbar's thumb within the horizontal + * range. This value is used to compute the length of the thumb within the scrollbar's track. + *

+ * + *

The range is expressed in arbitrary units that must be the same as the units used by + * {@link #computeHorizontalScrollRange()} and {@link #computeHorizontalScrollExtent()}.

+ * + *

Default implementation returns 0.

+ * + *

If you want to support scroll bars, override + * {@link RecyclerView.LayoutManager#computeHorizontalScrollOffset(RecyclerView.State)} in your + * LayoutManager.

+ * + * @return The horizontal offset of the scrollbar's thumb + * @see android.support.v7.widget.RecyclerView.LayoutManager#computeHorizontalScrollOffset + * (RecyclerView.Adapter) + */ + @Override + public int computeHorizontalScrollOffset() { + return mLayout.canScrollHorizontally() ? mLayout.computeHorizontalScrollOffset(mState) + : 0; + } + + /** + *

Compute the horizontal extent of the horizontal scrollbar's thumb within the + * horizontal range. This value is used to compute the length of the thumb within the + * scrollbar's track.

+ * + *

The range is expressed in arbitrary units that must be the same as the units used by + * {@link #computeHorizontalScrollRange()} and {@link #computeHorizontalScrollOffset()}.

+ * + *

Default implementation returns 0.

+ * + *

If you want to support scroll bars, override + * {@link RecyclerView.LayoutManager#computeHorizontalScrollExtent(RecyclerView.State)} in your + * LayoutManager.

+ * + * @return The horizontal extent of the scrollbar's thumb + * @see RecyclerView.LayoutManager#computeHorizontalScrollExtent(RecyclerView.State) + */ + @Override + public int computeHorizontalScrollExtent() { + return mLayout.canScrollHorizontally() ? mLayout.computeHorizontalScrollExtent(mState) : 0; + } + + /** + *

Compute the horizontal range that the horizontal scrollbar represents.

+ * + *

The range is expressed in arbitrary units that must be the same as the units used by + * {@link #computeHorizontalScrollExtent()} and {@link #computeHorizontalScrollOffset()}.

+ * + *

Default implementation returns 0.

+ * + *

If you want to support scroll bars, override + * {@link RecyclerView.LayoutManager#computeHorizontalScrollRange(RecyclerView.State)} in your + * LayoutManager.

+ * + * @return The total horizontal range represented by the vertical scrollbar + * @see RecyclerView.LayoutManager#computeHorizontalScrollRange(RecyclerView.State) + */ + @Override + public int computeHorizontalScrollRange() { + return mLayout.canScrollHorizontally() ? mLayout.computeHorizontalScrollRange(mState) : 0; + } + + /** + *

Compute the vertical offset of the vertical scrollbar's thumb within the vertical range. + * This value is used to compute the length of the thumb within the scrollbar's track.

+ * + *

The range is expressed in arbitrary units that must be the same as the units used by + * {@link #computeVerticalScrollRange()} and {@link #computeVerticalScrollExtent()}.

+ * + *

Default implementation returns 0.

+ * + *

If you want to support scroll bars, override + * {@link RecyclerView.LayoutManager#computeVerticalScrollOffset(RecyclerView.State)} in your + * LayoutManager.

+ * + * @return The vertical offset of the scrollbar's thumb + * @see android.support.v7.widget.RecyclerView.LayoutManager#computeVerticalScrollOffset + * (RecyclerView.Adapter) + */ + @Override + public int computeVerticalScrollOffset() { + return mLayout.canScrollVertically() ? mLayout.computeVerticalScrollOffset(mState) : 0; + } + + /** + *

Compute the vertical extent of the vertical scrollbar's thumb within the vertical range. + * This value is used to compute the length of the thumb within the scrollbar's track.

+ * + *

The range is expressed in arbitrary units that must be the same as the units used by + * {@link #computeVerticalScrollRange()} and {@link #computeVerticalScrollOffset()}.

+ * + *

Default implementation returns 0.

+ * + *

If you want to support scroll bars, override + * {@link RecyclerView.LayoutManager#computeVerticalScrollExtent(RecyclerView.State)} in your + * LayoutManager.

+ * + * @return The vertical extent of the scrollbar's thumb + * @see RecyclerView.LayoutManager#computeVerticalScrollExtent(RecyclerView.State) + */ + @Override + public int computeVerticalScrollExtent() { + return mLayout.canScrollVertically() ? mLayout.computeVerticalScrollExtent(mState) : 0; + } + + /** + *

Compute the vertical range that the vertical scrollbar represents.

+ * + *

The range is expressed in arbitrary units that must be the same as the units used by + * {@link #computeVerticalScrollExtent()} and {@link #computeVerticalScrollOffset()}.

+ * + *

Default implementation returns 0.

+ * + *

If you want to support scroll bars, override + * {@link RecyclerView.LayoutManager#computeVerticalScrollRange(RecyclerView.State)} in your + * LayoutManager.

+ * + * @return The total vertical range represented by the vertical scrollbar + * @see RecyclerView.LayoutManager#computeVerticalScrollRange(RecyclerView.State) + */ + @Override + public int computeVerticalScrollRange() { + return mLayout.canScrollVertically() ? mLayout.computeVerticalScrollRange(mState) : 0; + } + + + void eatRequestLayout() { + if (!mEatRequestLayout) { + mEatRequestLayout = true; + mLayoutRequestEaten = false; + } + } + + void resumeRequestLayout(boolean performLayoutChildren) { + if (mEatRequestLayout) { + if (performLayoutChildren && mLayoutRequestEaten && + mLayout != null && mAdapter != null) { + dispatchLayout(); + } + mEatRequestLayout = false; + mLayoutRequestEaten = false; + } + } + + /** + * Animate a scroll by the given amount of pixels along either axis. + * + * @param dx Pixels to scroll horizontally + * @param dy Pixels to scroll vertically + */ + public void smoothScrollBy(int dx, int dy) { + if (mLayout == null) { + Log.e(TAG, "Cannot smooth scroll without a LayoutManager set. " + + "Call setLayoutManager with a non-null argument."); + return; + } + if (!mLayout.canScrollHorizontally()) { + dx = 0; + } + if (!mLayout.canScrollVertically()) { + dy = 0; + } + if (dx != 0 || dy != 0) { + mViewFlinger.smoothScrollBy(dx, dy); + } + } + + /** + * Begin a standard fling with an initial velocity along each axis in pixels per second. + * If the velocity given is below the system-defined minimum this method will return false + * and no fling will occur. + * + * @param velocityX Initial horizontal velocity in pixels per second + * @param velocityY Initial vertical velocity in pixels per second + * @return true if the fling was started, false if the velocity was too low to fling or + * LayoutManager does not support scrolling in the axis fling is issued. + * + * @see LayoutManager#canScrollVertically() + * @see LayoutManager#canScrollHorizontally() + */ + public boolean fling(int velocityX, int velocityY) { + if (mLayout == null) { + Log.e(TAG, "Cannot fling without a LayoutManager set. " + + "Call setLayoutManager with a non-null argument."); + return false; + } + final boolean canScrollHorizontal = mLayout.canScrollHorizontally(); + final boolean canScrollVertical = mLayout.canScrollVertically(); + if (!canScrollHorizontal || Math.abs(velocityX) < mMinFlingVelocity) { + velocityX = 0; + } + if (!canScrollVertical || Math.abs(velocityY) < mMinFlingVelocity) { + velocityY = 0; + } + velocityX = Math.max(-mMaxFlingVelocity, Math.min(velocityX, mMaxFlingVelocity)); + velocityY = Math.max(-mMaxFlingVelocity, Math.min(velocityY, mMaxFlingVelocity)); + if (velocityX != 0 || velocityY != 0) { + mViewFlinger.fling(velocityX, velocityY); + return true; + } + return false; + } + + /** + * Stop any current scroll in progress, such as one started by + * {@link #smoothScrollBy(int, int)}, {@link #fling(int, int)} or a touch-initiated fling. + */ + public void stopScroll() { + setScrollState(SCROLL_STATE_IDLE); + stopScrollersInternal(); + } + + /** + * Similar to {@link #stopScroll()} but does not set the state. + */ + private void stopScrollersInternal() { + mViewFlinger.stop(); + if (mLayout != null) { + mLayout.stopSmoothScroller(); + } + } + + /** + * Apply a pull to relevant overscroll glow effects + */ + private void pullGlows(int x, int overscrollX, int y, int overscrollY) { + boolean invalidate = false; + if (overscrollX < 0) { + ensureLeftGlow(); + invalidate = mLeftGlow.onPull(-overscrollX / (float) getWidth(), + 1f - y / (float) getHeight()) || invalidate; + } else if (overscrollX > 0) { + ensureRightGlow(); + invalidate = mRightGlow.onPull(overscrollX / (float) getWidth(), + y / (float) getHeight()) || invalidate; + } + + if (overscrollY < 0) { + ensureTopGlow(); + invalidate = mTopGlow.onPull(-overscrollY / (float) getHeight(), + x / (float) getWidth()) || invalidate; + } else if (overscrollY > 0) { + ensureBottomGlow(); + invalidate = mBottomGlow.onPull(overscrollY / (float) getHeight(), + 1f - x / (float) getWidth()) || invalidate; + } + + if (invalidate || overscrollX != 0 || overscrollY != 0) { + ViewCompat.postInvalidateOnAnimation(this); + } + } + + private void releaseGlows() { + boolean needsInvalidate = false; + if (mLeftGlow != null) needsInvalidate = mLeftGlow.onRelease(); + if (mTopGlow != null) needsInvalidate |= mTopGlow.onRelease(); + if (mRightGlow != null) needsInvalidate |= mRightGlow.onRelease(); + if (mBottomGlow != null) needsInvalidate |= mBottomGlow.onRelease(); + if (needsInvalidate) { + ViewCompat.postInvalidateOnAnimation(this); + } + } + + private void considerReleasingGlowsOnScroll(int dx, int dy) { + boolean needsInvalidate = false; + if (mLeftGlow != null && !mLeftGlow.isFinished() && dx > 0) { + needsInvalidate = mLeftGlow.onRelease(); + } + if (mRightGlow != null && !mRightGlow.isFinished() && dx < 0) { + needsInvalidate |= mRightGlow.onRelease(); + } + if (mTopGlow != null && !mTopGlow.isFinished() && dy > 0) { + needsInvalidate |= mTopGlow.onRelease(); + } + if (mBottomGlow != null && !mBottomGlow.isFinished() && dy < 0) { + needsInvalidate |= mBottomGlow.onRelease(); + } + if (needsInvalidate) { + ViewCompat.postInvalidateOnAnimation(this); + } + } + + void absorbGlows(int velocityX, int velocityY) { + if (velocityX < 0) { + ensureLeftGlow(); + mLeftGlow.onAbsorb(-velocityX); + } else if (velocityX > 0) { + ensureRightGlow(); + mRightGlow.onAbsorb(velocityX); + } + + if (velocityY < 0) { + ensureTopGlow(); + mTopGlow.onAbsorb(-velocityY); + } else if (velocityY > 0) { + ensureBottomGlow(); + mBottomGlow.onAbsorb(velocityY); + } + + if (velocityX != 0 || velocityY != 0) { + ViewCompat.postInvalidateOnAnimation(this); + } + } + + void ensureLeftGlow() { + if (mLeftGlow != null) { + return; + } + mLeftGlow = new EdgeEffectCompat(getContext()); + if (mClipToPadding) { + mLeftGlow.setSize(getMeasuredHeight() - getPaddingTop() - getPaddingBottom(), + getMeasuredWidth() - getPaddingLeft() - getPaddingRight()); + } else { + mLeftGlow.setSize(getMeasuredHeight(), getMeasuredWidth()); + } + } + + void ensureRightGlow() { + if (mRightGlow != null) { + return; + } + mRightGlow = new EdgeEffectCompat(getContext()); + if (mClipToPadding) { + mRightGlow.setSize(getMeasuredHeight() - getPaddingTop() - getPaddingBottom(), + getMeasuredWidth() - getPaddingLeft() - getPaddingRight()); + } else { + mRightGlow.setSize(getMeasuredHeight(), getMeasuredWidth()); + } + } + + void ensureTopGlow() { + if (mTopGlow != null) { + return; + } + mTopGlow = new EdgeEffectCompat(getContext()); + if (mClipToPadding) { + mTopGlow.setSize(getMeasuredWidth() - getPaddingLeft() - getPaddingRight(), + getMeasuredHeight() - getPaddingTop() - getPaddingBottom()); + } else { + mTopGlow.setSize(getMeasuredWidth(), getMeasuredHeight()); + } + + } + + void ensureBottomGlow() { + if (mBottomGlow != null) { + return; + } + mBottomGlow = new EdgeEffectCompat(getContext()); + if (mClipToPadding) { + mBottomGlow.setSize(getMeasuredWidth() - getPaddingLeft() - getPaddingRight(), + getMeasuredHeight() - getPaddingTop() - getPaddingBottom()); + } else { + mBottomGlow.setSize(getMeasuredWidth(), getMeasuredHeight()); + } + } + + void invalidateGlows() { + mLeftGlow = mRightGlow = mTopGlow = mBottomGlow = null; + } + + // Focus handling + + @Override + public View focusSearch(View focused, int direction) { + View result = mLayout.onInterceptFocusSearch(focused, direction); + if (result != null) { + return result; + } + final FocusFinder ff = FocusFinder.getInstance(); + result = ff.findNextFocus(this, focused, direction); + if (result == null && mAdapter != null && mLayout != null) { + eatRequestLayout(); + result = mLayout.onFocusSearchFailed(focused, direction, mRecycler, mState); + resumeRequestLayout(false); + } + return result != null ? result : super.focusSearch(focused, direction); + } + + @Override + public void requestChildFocus(View child, View focused) { + if (!mLayout.onRequestChildFocus(this, mState, child, focused) && focused != null) { + mTempRect.set(0, 0, focused.getWidth(), focused.getHeight()); + + // get item decor offsets w/o refreshing. If they are invalid, there will be another + // layout pass to fix them, then it is LayoutManager's responsibility to keep focused + // View in viewport. + final ViewGroup.LayoutParams focusedLayoutParams = focused.getLayoutParams(); + if (focusedLayoutParams instanceof LayoutParams) { + // if focused child has item decors, use them. Otherwise, ignore. + final LayoutParams lp = (LayoutParams) focusedLayoutParams; + if (!lp.mInsetsDirty) { + final Rect insets = lp.mDecorInsets; + mTempRect.left -= insets.left; + mTempRect.right += insets.right; + mTempRect.top -= insets.top; + mTempRect.bottom += insets.bottom; + } + } + + offsetDescendantRectToMyCoords(focused, mTempRect); + offsetRectIntoDescendantCoords(child, mTempRect); + requestChildRectangleOnScreen(child, mTempRect, !mFirstLayoutComplete); + } + super.requestChildFocus(child, focused); + } + + @Override + public boolean requestChildRectangleOnScreen(View child, Rect rect, boolean immediate) { + return mLayout.requestChildRectangleOnScreen(this, child, rect, immediate); + } + + @Override + public void addFocusables(ArrayList views, int direction, int focusableMode) { + if (mLayout == null || !mLayout.onAddFocusables(this, views, direction, focusableMode)) { + super.addFocusables(views, direction, focusableMode); + } + } + + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + mLayoutOrScrollCounter = 0; + mIsAttached = true; + mFirstLayoutComplete = false; + if (mLayout != null) { + mLayout.dispatchAttachedToWindow(this); + } + mPostedAnimatorRunner = false; + } + + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + if (mItemAnimator != null) { + mItemAnimator.endAnimations(); + } + mFirstLayoutComplete = false; + + stopScroll(); + mIsAttached = false; + if (mLayout != null) { + mLayout.dispatchDetachedFromWindow(this, mRecycler); + } + removeCallbacks(mItemAnimatorRunner); + } + + /** + * Checks if RecyclerView is in the middle of a layout or scroll and throws an + * {@link IllegalStateException} if it is not. + * + * @param message The message for the exception. Can be null. + * @see #assertNotInLayoutOrScroll(String) + */ + void assertInLayoutOrScroll(String message) { + if (!isRunningLayoutOrScroll()) { + if (message == null) { + throw new IllegalStateException("Cannot call this method unless RecyclerView is " + + "computing a layout or scrolling"); + } + throw new IllegalStateException(message); + + } + } + + /** + * Checks if RecyclerView is in the middle of a layout or scroll and throws an + * {@link IllegalStateException} if it is. + * + * @param message The message for the exception. Can be null. + * @see #assertInLayoutOrScroll(String) + */ + void assertNotInLayoutOrScroll(String message) { + if (isRunningLayoutOrScroll()) { + if (message == null) { + throw new IllegalStateException("Cannot call this method while RecyclerView is " + + "computing a layout or scrolling"); + } + throw new IllegalStateException(message); + } + } + + /** + * Add an {@link OnItemTouchListener} to intercept touch events before they are dispatched + * to child views or this view's standard scrolling behavior. + * + *

Client code may use listeners to implement item manipulation behavior. Once a listener + * returns true from + * {@link OnItemTouchListener#onInterceptTouchEvent(RecyclerView, MotionEvent)} its + * {@link OnItemTouchListener#onTouchEvent(RecyclerView, MotionEvent)} method will be called + * for each incoming MotionEvent until the end of the gesture.

+ * + * @param listener Listener to add + */ + public void addOnItemTouchListener(OnItemTouchListener listener) { + mOnItemTouchListeners.add(listener); + } + + /** + * Remove an {@link OnItemTouchListener}. It will no longer be able to intercept touch events. + * + * @param listener Listener to remove + */ + public void removeOnItemTouchListener(OnItemTouchListener listener) { + mOnItemTouchListeners.remove(listener); + if (mActiveOnItemTouchListener == listener) { + mActiveOnItemTouchListener = null; + } + } + + private boolean dispatchOnItemTouchIntercept(MotionEvent e) { + final int action = e.getAction(); + if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_DOWN) { + mActiveOnItemTouchListener = null; + } + + final int listenerCount = mOnItemTouchListeners.size(); + for (int i = 0; i < listenerCount; i++) { + final OnItemTouchListener listener = mOnItemTouchListeners.get(i); + if (listener.onInterceptTouchEvent(this, e) && action != MotionEvent.ACTION_CANCEL) { + mActiveOnItemTouchListener = listener; + return true; + } + } + return false; + } + + private boolean dispatchOnItemTouch(MotionEvent e) { + final int action = e.getAction(); + if (mActiveOnItemTouchListener != null) { + if (action == MotionEvent.ACTION_DOWN) { + // Stale state from a previous gesture, we're starting a new one. Clear it. + mActiveOnItemTouchListener = null; + } else { + mActiveOnItemTouchListener.onTouchEvent(this, e); + if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) { + // Clean up for the next gesture. + mActiveOnItemTouchListener = null; + } + return true; + } + } + + // Listeners will have already received the ACTION_DOWN via dispatchOnItemTouchIntercept + // as called from onInterceptTouchEvent; skip it. + if (action != MotionEvent.ACTION_DOWN) { + final int listenerCount = mOnItemTouchListeners.size(); + for (int i = 0; i < listenerCount; i++) { + final OnItemTouchListener listener = mOnItemTouchListeners.get(i); + if (listener.onInterceptTouchEvent(this, e)) { + mActiveOnItemTouchListener = listener; + return true; + } + } + } + return false; + } + + @Override + public boolean onInterceptTouchEvent(MotionEvent e) { + if (dispatchOnItemTouchIntercept(e)) { + cancelTouch(); + return true; + } + + final boolean canScrollHorizontally = mLayout.canScrollHorizontally(); + final boolean canScrollVertically = mLayout.canScrollVertically(); + + if (mVelocityTracker == null) { + mVelocityTracker = VelocityTracker.obtain(); + } + mVelocityTracker.addMovement(e); + + final int action = MotionEventCompat.getActionMasked(e); + final int actionIndex = MotionEventCompat.getActionIndex(e); + + switch (action) { + case MotionEvent.ACTION_DOWN: + mScrollPointerId = MotionEventCompat.getPointerId(e, 0); + mInitialTouchX = mLastTouchX = (int) (e.getX() + 0.5f); + mInitialTouchY = mLastTouchY = (int) (e.getY() + 0.5f); + + if (mScrollState == SCROLL_STATE_SETTLING) { + getParent().requestDisallowInterceptTouchEvent(true); + setScrollState(SCROLL_STATE_DRAGGING); + } + break; + + case MotionEventCompat.ACTION_POINTER_DOWN: + mScrollPointerId = MotionEventCompat.getPointerId(e, actionIndex); + mInitialTouchX = mLastTouchX = (int) (MotionEventCompat.getX(e, actionIndex) + 0.5f); + mInitialTouchY = mLastTouchY = (int) (MotionEventCompat.getY(e, actionIndex) + 0.5f); + break; + + case MotionEvent.ACTION_MOVE: { + final int index = MotionEventCompat.findPointerIndex(e, mScrollPointerId); + if (index < 0) { + Log.e(TAG, "Error processing scroll; pointer index for id " + + mScrollPointerId + " not found. Did any MotionEvents get skipped?"); + return false; + } + + final int x = (int) (MotionEventCompat.getX(e, index) + 0.5f); + final int y = (int) (MotionEventCompat.getY(e, index) + 0.5f); + if (mScrollState != SCROLL_STATE_DRAGGING) { + final int dx = x - mInitialTouchX; + final int dy = y - mInitialTouchY; + boolean startScroll = false; + if (canScrollHorizontally && Math.abs(dx) > mTouchSlop) { + mLastTouchX = mInitialTouchX + mTouchSlop * (dx < 0 ? -1 : 1); + startScroll = true; + } + if (canScrollVertically && Math.abs(dy) > mTouchSlop) { + mLastTouchY = mInitialTouchY + mTouchSlop * (dy < 0 ? -1 : 1); + startScroll = true; + } + if (startScroll) { + setScrollState(SCROLL_STATE_DRAGGING); + } + } + } break; + + case MotionEventCompat.ACTION_POINTER_UP: { + onPointerUp(e); + } break; + + case MotionEvent.ACTION_UP: { + mVelocityTracker.clear(); + } break; + + case MotionEvent.ACTION_CANCEL: { + cancelTouch(); + } + } + return mScrollState == SCROLL_STATE_DRAGGING; + } + + @Override + public boolean onTouchEvent(MotionEvent e) { + if (dispatchOnItemTouch(e)) { + cancelTouch(); + return true; + } + + final boolean canScrollHorizontally = mLayout.canScrollHorizontally(); + final boolean canScrollVertically = mLayout.canScrollVertically(); + + if (mVelocityTracker == null) { + mVelocityTracker = VelocityTracker.obtain(); + } + mVelocityTracker.addMovement(e); + + final int action = MotionEventCompat.getActionMasked(e); + final int actionIndex = MotionEventCompat.getActionIndex(e); + + switch (action) { + case MotionEvent.ACTION_DOWN: { + mScrollPointerId = MotionEventCompat.getPointerId(e, 0); + mInitialTouchX = mLastTouchX = (int) (e.getX() + 0.5f); + mInitialTouchY = mLastTouchY = (int) (e.getY() + 0.5f); + } break; + + case MotionEventCompat.ACTION_POINTER_DOWN: { + mScrollPointerId = MotionEventCompat.getPointerId(e, actionIndex); + mInitialTouchX = mLastTouchX = (int) (MotionEventCompat.getX(e, actionIndex) + 0.5f); + mInitialTouchY = mLastTouchY = (int) (MotionEventCompat.getY(e, actionIndex) + 0.5f); + } break; + + case MotionEvent.ACTION_MOVE: { + final int index = MotionEventCompat.findPointerIndex(e, mScrollPointerId); + if (index < 0) { + Log.e(TAG, "Error processing scroll; pointer index for id " + + mScrollPointerId + " not found. Did any MotionEvents get skipped?"); + return false; + } + + final int x = (int) (MotionEventCompat.getX(e, index) + 0.5f); + final int y = (int) (MotionEventCompat.getY(e, index) + 0.5f); + if (mScrollState != SCROLL_STATE_DRAGGING) { + final int dx = x - mInitialTouchX; + final int dy = y - mInitialTouchY; + boolean startScroll = false; + if (canScrollHorizontally && Math.abs(dx) > mTouchSlop) { + mLastTouchX = mInitialTouchX + mTouchSlop * (dx < 0 ? -1 : 1); + startScroll = true; + } + if (canScrollVertically && Math.abs(dy) > mTouchSlop) { + mLastTouchY = mInitialTouchY + mTouchSlop * (dy < 0 ? -1 : 1); + startScroll = true; + } + if (startScroll) { + setScrollState(SCROLL_STATE_DRAGGING); + } + } + if (mScrollState == SCROLL_STATE_DRAGGING) { + final int dx = x - mLastTouchX; + final int dy = y - mLastTouchY; + if (scrollByInternal(canScrollHorizontally ? -dx : 0, + canScrollVertically ? -dy : 0, true, x, y)) { + getParent().requestDisallowInterceptTouchEvent(true); + } + } + mLastTouchX = x; + mLastTouchY = y; + } break; + + case MotionEventCompat.ACTION_POINTER_UP: { + onPointerUp(e); + } break; + + case MotionEvent.ACTION_UP: { + mVelocityTracker.computeCurrentVelocity(1000, mMaxFlingVelocity); + final float xvel = canScrollHorizontally ? + -VelocityTrackerCompat.getXVelocity(mVelocityTracker, mScrollPointerId) : 0; + final float yvel = canScrollVertically ? + -VelocityTrackerCompat.getYVelocity(mVelocityTracker, mScrollPointerId) : 0; + if (!((xvel != 0 || yvel != 0) && fling((int) xvel, (int) yvel))) { + setScrollState(SCROLL_STATE_IDLE); + } + mVelocityTracker.clear(); + releaseGlows(); + } break; + + case MotionEvent.ACTION_CANCEL: { + cancelTouch(); + } break; + } + + return true; + } + + private void cancelTouch() { + if (mVelocityTracker != null) { + mVelocityTracker.clear(); + } + releaseGlows(); + setScrollState(SCROLL_STATE_IDLE); + } + + private void onPointerUp(MotionEvent e) { + final int actionIndex = MotionEventCompat.getActionIndex(e); + if (MotionEventCompat.getPointerId(e, actionIndex) == mScrollPointerId) { + // Pick a new pointer to pick up the slack. + final int newIndex = actionIndex == 0 ? 1 : 0; + mScrollPointerId = MotionEventCompat.getPointerId(e, newIndex); + mInitialTouchX = mLastTouchX = (int) (MotionEventCompat.getX(e, newIndex) + 0.5f); + mInitialTouchY = mLastTouchY = (int) (MotionEventCompat.getY(e, newIndex) + 0.5f); + } + } + + // @Override + public boolean onGenericMotionEvent(MotionEvent event) { + if (mLayout == null) { + return false; + } + if ((MotionEventCompat.getSource(event) & InputDeviceCompat.SOURCE_CLASS_POINTER) != 0) { + if (event.getAction() == MotionEventCompat.ACTION_SCROLL) { + final float vScroll, hScroll; + if (mLayout.canScrollVertically()) { + vScroll = MotionEventCompat + .getAxisValue(event, MotionEventCompat.AXIS_VSCROLL); + } else { + vScroll = 0f; + } + if (mLayout.canScrollHorizontally()) { + hScroll = MotionEventCompat + .getAxisValue(event, MotionEventCompat.AXIS_HSCROLL); + } else { + hScroll = 0f; + } + + if (vScroll != 0 || hScroll != 0) { + final float scrollFactor = getScrollFactor(); + scrollBy((int) (hScroll * scrollFactor), (int) (vScroll * scrollFactor)); + } + } + } + return false; + } + + /** + * Ported from View.getVerticalScrollFactor. + */ + private float getScrollFactor() { + if (mScrollFactor == Float.MIN_VALUE) { + TypedValue outValue = new TypedValue(); + if (getContext().getTheme().resolveAttribute( + android.R.attr.listPreferredItemHeight, outValue, true)) { + mScrollFactor = outValue.getDimension( + getContext().getResources().getDisplayMetrics()); + } else { + return 0; //listPreferredItemHeight is not defined, no generic scrolling + } + + } + return mScrollFactor; + } + + @Override + protected void onMeasure(int widthSpec, int heightSpec) { + if (mAdapterUpdateDuringMeasure) { + eatRequestLayout(); + processAdapterUpdatesAndSetAnimationFlags(); + + if (mState.mRunPredictiveAnimations) { + // TODO: try to provide a better approach. + // When RV decides to run predictive animations, we need to measure in pre-layout + // state so that pre-layout pass results in correct layout. + // On the other hand, this will prevent the layout manager from resizing properly. + mState.mInPreLayout = true; + } else { + // consume remaining updates to provide a consistent state with the layout pass. + mAdapterHelper.consumeUpdatesInOnePass(); + mState.mInPreLayout = false; + } + mAdapterUpdateDuringMeasure = false; + resumeRequestLayout(false); + } + + if (mAdapter != null) { + mState.mItemCount = mAdapter.getItemCount(); + } else { + mState.mItemCount = 0; + } + if (mLayout == null) { + defaultOnMeasure(widthSpec, heightSpec); + } else { + mLayout.onMeasure(mRecycler, mState, widthSpec, heightSpec); + } + + mState.mInPreLayout = false; // clear + } + + /** + * Used when onMeasure is called before layout manager is set + */ + private void defaultOnMeasure(int widthSpec, int heightSpec) { + final int widthMode = MeasureSpec.getMode(widthSpec); + final int heightMode = MeasureSpec.getMode(heightSpec); + final int widthSize = MeasureSpec.getSize(widthSpec); + final int heightSize = MeasureSpec.getSize(heightSpec); + + int width = 0; + int height = 0; + + switch (widthMode) { + case MeasureSpec.EXACTLY: + case MeasureSpec.AT_MOST: + width = widthSize; + break; + case MeasureSpec.UNSPECIFIED: + default: + width = ViewCompat.getMinimumWidth(this); + break; + } + + switch (heightMode) { + case MeasureSpec.EXACTLY: + case MeasureSpec.AT_MOST: + height = heightSize; + break; + case MeasureSpec.UNSPECIFIED: + default: + height = ViewCompat.getMinimumHeight(this); + break; + } + + setMeasuredDimension(width, height); + } + + @Override + protected void onSizeChanged(int w, int h, int oldw, int oldh) { + super.onSizeChanged(w, h, oldw, oldh); + if (w != oldw || h != oldh) { + invalidateGlows(); + } + } + + /** + * Sets the {@link ItemAnimator} that will handle animations involving changes + * to the items in this RecyclerView. By default, RecyclerView instantiates and + * uses an instance of {@link DefaultItemAnimator}. Whether item animations are + * enabled for the RecyclerView depends on the ItemAnimator and whether + * the LayoutManager {@link LayoutManager#supportsPredictiveItemAnimations() + * supports item animations}. + * + * @param animator The ItemAnimator being set. If null, no animations will occur + * when changes occur to the items in this RecyclerView. + */ + public void setItemAnimator(ItemAnimator animator) { + if (mItemAnimator != null) { + mItemAnimator.endAnimations(); + mItemAnimator.setListener(null); + } + mItemAnimator = animator; + if (mItemAnimator != null) { + mItemAnimator.setListener(mItemAnimatorListener); + } + } + + private void onEnterLayoutOrScroll() { + mLayoutOrScrollCounter ++; + } + + private void onExitLayoutOrScroll() { + mLayoutOrScrollCounter --; + if (mLayoutOrScrollCounter < 1) { + if (DEBUG && mLayoutOrScrollCounter < 0) { + throw new IllegalStateException("layout or scroll counter cannot go below zero." + + "Some calls are not matching"); + } + mLayoutOrScrollCounter = 0; + dispatchContentChangedIfNecessary(); + } + } + + private void dispatchContentChangedIfNecessary() { + final int flags = mEatenAccessibilityChangeFlags; + mEatenAccessibilityChangeFlags = 0; + if (flags != 0 && mAccessibilityManager != null && mAccessibilityManager.isEnabled()) { + final AccessibilityEvent event = AccessibilityEvent.obtain(); + event.setEventType(AccessibilityEventCompat.TYPE_WINDOW_CONTENT_CHANGED); + AccessibilityEventCompat.setContentChangeTypes(event, flags); + sendAccessibilityEventUnchecked(event); + } + } + + boolean isRunningLayoutOrScroll() { + return mLayoutOrScrollCounter > 0; + } + + /** + * Returns true if an accessibility event should not be dispatched now. This happens when an + * accessibility request arrives while RecyclerView does not have a stable state which is very + * hard to handle for a LayoutManager. Instead, this method records necessary information about + * the event and dispatches a window change event after the critical section is finished. + * + * @return True if the accessibility event should be postponed. + */ + boolean shouldDeferAccessibilityEvent(AccessibilityEvent event) { + if (isRunningLayoutOrScroll()) { + int type = 0; + if (event != null) { + type = AccessibilityEventCompat.getContentChangeTypes(event); + } + if (type == 0) { + type = AccessibilityEventCompat.CONTENT_CHANGE_TYPE_UNDEFINED; + } + mEatenAccessibilityChangeFlags |= type; + return true; + } + return false; + } + + @Override + public void sendAccessibilityEventUnchecked(AccessibilityEvent event) { + if (shouldDeferAccessibilityEvent(event)) { + return; + } + super.sendAccessibilityEventUnchecked(event); + } + + /** + * Gets the current ItemAnimator for this RecyclerView. A null return value + * indicates that there is no animator and that item changes will happen without + * any animations. By default, RecyclerView instantiates and + * uses an instance of {@link DefaultItemAnimator}. + * + * @return ItemAnimator The current ItemAnimator. If null, no animations will occur + * when changes occur to the items in this RecyclerView. + */ + public ItemAnimator getItemAnimator() { + return mItemAnimator; + } + + private boolean supportsChangeAnimations() { + return mItemAnimator != null && mItemAnimator.getSupportsChangeAnimations(); + } + + /** + * Post a runnable to the next frame to run pending item animations. Only the first such + * request will be posted, governed by the mPostedAnimatorRunner flag. + */ + private void postAnimationRunner() { + if (!mPostedAnimatorRunner && mIsAttached) { + ViewCompat.postOnAnimation(this, mItemAnimatorRunner); + mPostedAnimatorRunner = true; + } + } + + private boolean predictiveItemAnimationsEnabled() { + return (mItemAnimator != null && mLayout.supportsPredictiveItemAnimations()); + } + + /** + * Consumes adapter updates and calculates which type of animations we want to run. + * Called in onMeasure and dispatchLayout. + *

+ * This method may process only the pre-layout state of updates or all of them. + */ + private void processAdapterUpdatesAndSetAnimationFlags() { + if (mDataSetHasChangedAfterLayout) { + // Processing these items have no value since data set changed unexpectedly. + // Instead, we just reset it. + mAdapterHelper.reset(); + markKnownViewsInvalid(); + mLayout.onItemsChanged(this); + } + // simple animations are a subset of advanced animations (which will cause a + // pre-layout step) + // If layout supports predictive animations, pre-process to decide if we want to run them + if (mItemAnimator != null && mLayout.supportsPredictiveItemAnimations()) { + mAdapterHelper.preProcess(); + } else { + mAdapterHelper.consumeUpdatesInOnePass(); + } + boolean animationTypeSupported = (mItemsAddedOrRemoved && !mItemsChanged) || + (mItemsAddedOrRemoved || (mItemsChanged && supportsChangeAnimations())); + mState.mRunSimpleAnimations = mFirstLayoutComplete && mItemAnimator != null && + (mDataSetHasChangedAfterLayout || animationTypeSupported || + mLayout.mRequestedSimpleAnimations) && + (!mDataSetHasChangedAfterLayout || mAdapter.hasStableIds()); + mState.mRunPredictiveAnimations = mState.mRunSimpleAnimations && + animationTypeSupported && !mDataSetHasChangedAfterLayout && + predictiveItemAnimationsEnabled(); + } + + /** + * Wrapper around layoutChildren() that handles animating changes caused by layout. + * Animations work on the assumption that there are five different kinds of items + * in play: + * PERSISTENT: items are visible before and after layout + * REMOVED: items were visible before layout and were removed by the app + * ADDED: items did not exist before layout and were added by the app + * DISAPPEARING: items exist in the data set before/after, but changed from + * visible to non-visible in the process of layout (they were moved off + * screen as a side-effect of other changes) + * APPEARING: items exist in the data set before/after, but changed from + * non-visible to visible in the process of layout (they were moved on + * screen as a side-effect of other changes) + * The overall approach figures out what items exist before/after layout and + * infers one of the five above states for each of the items. Then the animations + * are set up accordingly: + * PERSISTENT views are moved ({@link ItemAnimator#animateMove(ViewHolder, int, int, int, int)}) + * REMOVED views are removed ({@link ItemAnimator#animateRemove(ViewHolder)}) + * ADDED views are added ({@link ItemAnimator#animateAdd(ViewHolder)}) + * DISAPPEARING views are moved off screen + * APPEARING views are moved on screen + */ + void dispatchLayout() { + if (mAdapter == null) { + Log.e(TAG, "No adapter attached; skipping layout"); + return; + } + if (mLayout == null) { + Log.e(TAG, "No layout manager attached; skipping layout"); + return; + } + mState.mDisappearingViewsInLayoutPass.clear(); + eatRequestLayout(); + onEnterLayoutOrScroll(); + + processAdapterUpdatesAndSetAnimationFlags(); + + mState.mOldChangedHolders = mState.mRunSimpleAnimations && mItemsChanged + && supportsChangeAnimations() ? new ArrayMap() : null; + mItemsAddedOrRemoved = mItemsChanged = false; + ArrayMap appearingViewInitialBounds = null; + mState.mInPreLayout = mState.mRunPredictiveAnimations; + mState.mItemCount = mAdapter.getItemCount(); + findMinMaxChildLayoutPositions(mMinMaxLayoutPositions); + + if (mState.mRunSimpleAnimations) { + // Step 0: Find out where all non-removed items are, pre-layout + mState.mPreLayoutHolderMap.clear(); + mState.mPostLayoutHolderMap.clear(); + int count = mChildHelper.getChildCount(); + for (int i = 0; i < count; ++i) { + final ViewHolder holder = getChildViewHolderInt(mChildHelper.getChildAt(i)); + if (holder.shouldIgnore() || (holder.isInvalid() && !mAdapter.hasStableIds())) { + continue; + } + final View view = holder.itemView; + mState.mPreLayoutHolderMap.put(holder, new ItemHolderInfo(holder, + view.getLeft(), view.getTop(), view.getRight(), view.getBottom())); + } + } + if (mState.mRunPredictiveAnimations) { + // Step 1: run prelayout: This will use the old positions of items. The layout manager + // is expected to layout everything, even removed items (though not to add removed + // items back to the container). This gives the pre-layout position of APPEARING views + // which come into existence as part of the real layout. + + // Save old positions so that LayoutManager can run its mapping logic. + saveOldPositions(); + // processAdapterUpdatesAndSetAnimationFlags already run pre-layout animations. + if (mState.mOldChangedHolders != null) { + int count = mChildHelper.getChildCount(); + for (int i = 0; i < count; ++i) { + final ViewHolder holder = getChildViewHolderInt(mChildHelper.getChildAt(i)); + if (holder.isChanged() && !holder.isRemoved() && !holder.shouldIgnore()) { + long key = getChangedHolderKey(holder); + mState.mOldChangedHolders.put(key, holder); + mState.mPreLayoutHolderMap.remove(holder); + } + } + } + + final boolean didStructureChange = mState.mStructureChanged; + mState.mStructureChanged = false; + // temporarily disable flag because we are asking for previous layout + mLayout.onLayoutChildren(mRecycler, mState); + mState.mStructureChanged = didStructureChange; + + appearingViewInitialBounds = new ArrayMap(); + for (int i = 0; i < mChildHelper.getChildCount(); ++i) { + boolean found = false; + View child = mChildHelper.getChildAt(i); + if (getChildViewHolderInt(child).shouldIgnore()) { + continue; + } + for (int j = 0; j < mState.mPreLayoutHolderMap.size(); ++j) { + ViewHolder holder = mState.mPreLayoutHolderMap.keyAt(j); + if (holder.itemView == child) { + found = true; + break; + } + } + if (!found) { + appearingViewInitialBounds.put(child, new Rect(child.getLeft(), child.getTop(), + child.getRight(), child.getBottom())); + } + } + // we don't process disappearing list because they may re-appear in post layout pass. + clearOldPositions(); + mAdapterHelper.consumePostponedUpdates(); + } else { + clearOldPositions(); + // in case pre layout did run but we decided not to run predictive animations. + mAdapterHelper.consumeUpdatesInOnePass(); + if (mState.mOldChangedHolders != null) { + int count = mChildHelper.getChildCount(); + for (int i = 0; i < count; ++i) { + final ViewHolder holder = getChildViewHolderInt(mChildHelper.getChildAt(i)); + if (holder.isChanged() && !holder.isRemoved() && !holder.shouldIgnore()) { + long key = getChangedHolderKey(holder); + mState.mOldChangedHolders.put(key, holder); + mState.mPreLayoutHolderMap.remove(holder); + } + } + } + } + mState.mItemCount = mAdapter.getItemCount(); + mState.mDeletedInvisibleItemCountSincePreviousLayout = 0; + + // Step 2: Run layout + mState.mInPreLayout = false; + mLayout.onLayoutChildren(mRecycler, mState); + + mState.mStructureChanged = false; + mPendingSavedState = null; + + // onLayoutChildren may have caused client code to disable item animations; re-check + mState.mRunSimpleAnimations = mState.mRunSimpleAnimations && mItemAnimator != null; + + if (mState.mRunSimpleAnimations) { + // Step 3: Find out where things are now, post-layout + ArrayMap newChangedHolders = mState.mOldChangedHolders != null ? + new ArrayMap() : null; + int count = mChildHelper.getChildCount(); + for (int i = 0; i < count; ++i) { + ViewHolder holder = getChildViewHolderInt(mChildHelper.getChildAt(i)); + if (holder.shouldIgnore()) { + continue; + } + final View view = holder.itemView; + long key = getChangedHolderKey(holder); + if (newChangedHolders != null && mState.mOldChangedHolders.get(key) != null) { + newChangedHolders.put(key, holder); + } else { + mState.mPostLayoutHolderMap.put(holder, new ItemHolderInfo(holder, + view.getLeft(), view.getTop(), view.getRight(), view.getBottom())); + } + } + processDisappearingList(appearingViewInitialBounds); + // Step 4: Animate DISAPPEARING and REMOVED items + int preLayoutCount = mState.mPreLayoutHolderMap.size(); + for (int i = preLayoutCount - 1; i >= 0; i--) { + ViewHolder itemHolder = mState.mPreLayoutHolderMap.keyAt(i); + if (!mState.mPostLayoutHolderMap.containsKey(itemHolder)) { + ItemHolderInfo disappearingItem = mState.mPreLayoutHolderMap.valueAt(i); + mState.mPreLayoutHolderMap.removeAt(i); + + View disappearingItemView = disappearingItem.holder.itemView; + mRecycler.unscrapView(disappearingItem.holder); + animateDisappearance(disappearingItem); + } + } + // Step 5: Animate APPEARING and ADDED items + int postLayoutCount = mState.mPostLayoutHolderMap.size(); + if (postLayoutCount > 0) { + for (int i = postLayoutCount - 1; i >= 0; i--) { + ViewHolder itemHolder = mState.mPostLayoutHolderMap.keyAt(i); + ItemHolderInfo info = mState.mPostLayoutHolderMap.valueAt(i); + if ((mState.mPreLayoutHolderMap.isEmpty() || + !mState.mPreLayoutHolderMap.containsKey(itemHolder))) { + mState.mPostLayoutHolderMap.removeAt(i); + Rect initialBounds = (appearingViewInitialBounds != null) ? + appearingViewInitialBounds.get(itemHolder.itemView) : null; + animateAppearance(itemHolder, initialBounds, + info.left, info.top); + } + } + } + // Step 6: Animate PERSISTENT items + count = mState.mPostLayoutHolderMap.size(); + for (int i = 0; i < count; ++i) { + ViewHolder postHolder = mState.mPostLayoutHolderMap.keyAt(i); + ItemHolderInfo postInfo = mState.mPostLayoutHolderMap.valueAt(i); + ItemHolderInfo preInfo = mState.mPreLayoutHolderMap.get(postHolder); + if (preInfo != null && postInfo != null) { + if (preInfo.left != postInfo.left || preInfo.top != postInfo.top) { + postHolder.setIsRecyclable(false); + if (DEBUG) { + Log.d(TAG, "PERSISTENT: " + postHolder + + " with view " + postHolder.itemView); + } + if (mItemAnimator.animateMove(postHolder, + preInfo.left, preInfo.top, postInfo.left, postInfo.top)) { + postAnimationRunner(); + } + } + } + } + // Step 7: Animate CHANGING items + count = mState.mOldChangedHolders != null ? mState.mOldChangedHolders.size() : 0; + // traverse reverse in case view gets recycled while we are traversing the list. + for (int i = count - 1; i >= 0; i--) { + long key = mState.mOldChangedHolders.keyAt(i); + ViewHolder oldHolder = mState.mOldChangedHolders.get(key); + View oldView = oldHolder.itemView; + if (oldHolder.shouldIgnore()) { + continue; + } + // We probably don't need this check anymore since these views are removed from + // the list if they are recycled. + if (mRecycler.mChangedScrap != null && + mRecycler.mChangedScrap.contains(oldHolder)) { + animateChange(oldHolder, newChangedHolders.get(key)); + } else if (DEBUG) { + Log.e(TAG, "cannot find old changed holder in changed scrap :/" + oldHolder); + } + } + } + resumeRequestLayout(false); + mLayout.removeAndRecycleScrapInt(mRecycler); + mState.mPreviousLayoutItemCount = mState.mItemCount; + mDataSetHasChangedAfterLayout = false; + mState.mRunSimpleAnimations = false; + mState.mRunPredictiveAnimations = false; + onExitLayoutOrScroll(); + mLayout.mRequestedSimpleAnimations = false; + if (mRecycler.mChangedScrap != null) { + mRecycler.mChangedScrap.clear(); + } + mState.mOldChangedHolders = null; + + if (didChildRangeChange(mMinMaxLayoutPositions[0], mMinMaxLayoutPositions[1])) { + dispatchOnScrolled(0, 0); + } + } + + private void findMinMaxChildLayoutPositions(int[] into) { + final int count = mChildHelper.getChildCount(); + if (count == 0) { + into[0] = 0; + into[1] = 0; + return; + } + int minPositionPreLayout = Integer.MAX_VALUE; + int maxPositionPreLayout = Integer.MIN_VALUE; + for (int i = 0; i < count; ++i) { + final ViewHolder holder = getChildViewHolderInt(mChildHelper.getChildAt(i)); + if (holder.shouldIgnore()) { + continue; + } + final int pos = holder.getLayoutPosition(); + if (pos < minPositionPreLayout) { + minPositionPreLayout = pos; + } + if (pos > maxPositionPreLayout) { + maxPositionPreLayout = pos; + } + } + into[0] = minPositionPreLayout; + into[1] = maxPositionPreLayout; + } + + private boolean didChildRangeChange(int minPositionPreLayout, int maxPositionPreLayout) { + int count = mChildHelper.getChildCount(); + if (count == 0) { + return minPositionPreLayout != 0 || maxPositionPreLayout != 0; + } + for (int i = 0; i < count; ++i) { + final ViewHolder holder = getChildViewHolderInt(mChildHelper.getChildAt(i)); + if (holder.shouldIgnore()) { + continue; + } + final int pos = holder.getLayoutPosition(); + if (pos < minPositionPreLayout || pos > maxPositionPreLayout) { + return true; + } + } + return false; + } + + @Override + protected void removeDetachedView(View child, boolean animate) { + ViewHolder vh = getChildViewHolderInt(child); + if (vh != null) { + if (vh.isTmpDetached()) { + vh.clearTmpDetachFlag(); + } else if (!vh.shouldIgnore()) { + throw new IllegalArgumentException("Called removeDetachedView with a view which" + + " is not flagged as tmp detached." + vh); + } + } + dispatchChildDetached(child); + super.removeDetachedView(child, animate); + } + + /** + * Returns a unique key to be used while handling change animations. + * It might be child's position or stable id depending on the adapter type. + */ + long getChangedHolderKey(ViewHolder holder) { + return mAdapter.hasStableIds() ? holder.getItemId() : holder.mPosition; + } + + /** + * A LayoutManager may want to layout a view just to animate disappearance. + * This method handles those views and triggers remove animation on them. + */ + private void processDisappearingList(ArrayMap appearingViews) { + final List disappearingList = mState.mDisappearingViewsInLayoutPass; + for (int i = disappearingList.size() - 1; i >= 0; i --) { + View view = disappearingList.get(i); + ViewHolder vh = getChildViewHolderInt(view); + final ItemHolderInfo info = mState.mPreLayoutHolderMap.remove(vh); + if (!mState.isPreLayout()) { + mState.mPostLayoutHolderMap.remove(vh); + } + if (appearingViews.remove(view) != null) { + mLayout.removeAndRecycleView(view, mRecycler); + continue; + } + if (info != null) { + animateDisappearance(info); + } else { + // let it disappear from the position it becomes visible + animateDisappearance(new ItemHolderInfo(vh, view.getLeft(), view.getTop(), + view.getRight(), view.getBottom())); + } + } + disappearingList.clear(); + } + + private void animateAppearance(ViewHolder itemHolder, Rect beforeBounds, int afterLeft, + int afterTop) { + View newItemView = itemHolder.itemView; + if (beforeBounds != null && + (beforeBounds.left != afterLeft || beforeBounds.top != afterTop)) { + // slide items in if before/after locations differ + itemHolder.setIsRecyclable(false); + if (DEBUG) { + Log.d(TAG, "APPEARING: " + itemHolder + " with view " + newItemView); + } + if (mItemAnimator.animateMove(itemHolder, + beforeBounds.left, beforeBounds.top, + afterLeft, afterTop)) { + postAnimationRunner(); + } + } else { + if (DEBUG) { + Log.d(TAG, "ADDED: " + itemHolder + " with view " + newItemView); + } + itemHolder.setIsRecyclable(false); + if (mItemAnimator.animateAdd(itemHolder)) { + postAnimationRunner(); + } + } + } + + private void animateDisappearance(ItemHolderInfo disappearingItem) { + View disappearingItemView = disappearingItem.holder.itemView; + addAnimatingView(disappearingItem.holder); + int oldLeft = disappearingItem.left; + int oldTop = disappearingItem.top; + int newLeft = disappearingItemView.getLeft(); + int newTop = disappearingItemView.getTop(); + if (oldLeft != newLeft || oldTop != newTop) { + disappearingItem.holder.setIsRecyclable(false); + disappearingItemView.layout(newLeft, newTop, + newLeft + disappearingItemView.getWidth(), + newTop + disappearingItemView.getHeight()); + if (DEBUG) { + Log.d(TAG, "DISAPPEARING: " + disappearingItem.holder + + " with view " + disappearingItemView); + } + if (mItemAnimator.animateMove(disappearingItem.holder, oldLeft, oldTop, + newLeft, newTop)) { + postAnimationRunner(); + } + } else { + if (DEBUG) { + Log.d(TAG, "REMOVED: " + disappearingItem.holder + + " with view " + disappearingItemView); + } + disappearingItem.holder.setIsRecyclable(false); + if (mItemAnimator.animateRemove(disappearingItem.holder)) { + postAnimationRunner(); + } + } + } + + private void animateChange(ViewHolder oldHolder, ViewHolder newHolder) { + oldHolder.setIsRecyclable(false); + addAnimatingView(oldHolder); + oldHolder.mShadowedHolder = newHolder; + mRecycler.unscrapView(oldHolder); + if (DEBUG) { + Log.d(TAG, "CHANGED: " + oldHolder + " with view " + oldHolder.itemView); + } + final int fromLeft = oldHolder.itemView.getLeft(); + final int fromTop = oldHolder.itemView.getTop(); + final int toLeft, toTop; + if (newHolder == null || newHolder.shouldIgnore()) { + toLeft = fromLeft; + toTop = fromTop; + } else { + toLeft = newHolder.itemView.getLeft(); + toTop = newHolder.itemView.getTop(); + newHolder.setIsRecyclable(false); + newHolder.mShadowingHolder = oldHolder; + } + if(mItemAnimator.animateChange(oldHolder, newHolder, + fromLeft, fromTop, toLeft, toTop)) { + postAnimationRunner(); + } + } + + @Override + protected void onLayout(boolean changed, int l, int t, int r, int b) { + eatRequestLayout(); + dispatchLayout(); + resumeRequestLayout(false); + mFirstLayoutComplete = true; + } + + @Override + public void requestLayout() { + if (!mEatRequestLayout) { + super.requestLayout(); + } else { + mLayoutRequestEaten = true; + } + } + + void markItemDecorInsetsDirty() { + final int childCount = mChildHelper.getUnfilteredChildCount(); + for (int i = 0; i < childCount; i++) { + final View child = mChildHelper.getUnfilteredChildAt(i); + ((LayoutParams) child.getLayoutParams()).mInsetsDirty = true; + } + mRecycler.markItemDecorInsetsDirty(); + } + + @Override + public void draw(Canvas c) { + super.draw(c); + + final int count = mItemDecorations.size(); + for (int i = 0; i < count; i++) { + mItemDecorations.get(i).onDrawOver(c, this, mState); + } + // TODO If padding is not 0 and chilChildrenToPadding is false, to draw glows properly, we + // need find children closest to edges. Not sure if it is worth the effort. + boolean needsInvalidate = false; + if (mLeftGlow != null && !mLeftGlow.isFinished()) { + final int restore = c.save(); + final int padding = mClipToPadding ? getPaddingBottom() : 0; + c.rotate(270); + c.translate(-getHeight() + padding, 0); + needsInvalidate = mLeftGlow != null && mLeftGlow.draw(c); + c.restoreToCount(restore); + } + if (mTopGlow != null && !mTopGlow.isFinished()) { + final int restore = c.save(); + if (mClipToPadding) { + c.translate(getPaddingLeft(), getPaddingTop()); + } + needsInvalidate |= mTopGlow != null && mTopGlow.draw(c); + c.restoreToCount(restore); + } + if (mRightGlow != null && !mRightGlow.isFinished()) { + final int restore = c.save(); + final int width = getWidth(); + final int padding = mClipToPadding ? getPaddingTop() : 0; + c.rotate(90); + c.translate(-padding, -width); + needsInvalidate |= mRightGlow != null && mRightGlow.draw(c); + c.restoreToCount(restore); + } + if (mBottomGlow != null && !mBottomGlow.isFinished()) { + final int restore = c.save(); + c.rotate(180); + if (mClipToPadding) { + c.translate(-getWidth() + getPaddingRight(), -getHeight() + getPaddingBottom()); + } else { + c.translate(-getWidth(), -getHeight()); + } + needsInvalidate |= mBottomGlow != null && mBottomGlow.draw(c); + c.restoreToCount(restore); + } + + // If some views are animating, ItemDecorators are likely to move/change with them. + // Invalidate RecyclerView to re-draw decorators. This is still efficient because children's + // display lists are not invalidated. + if (!needsInvalidate && mItemAnimator != null && mItemDecorations.size() > 0 && + mItemAnimator.isRunning()) { + needsInvalidate = true; + } + + if (needsInvalidate) { + ViewCompat.postInvalidateOnAnimation(this); + } + } + + @Override + public void onDraw(Canvas c) { + super.onDraw(c); + + final int count = mItemDecorations.size(); + for (int i = 0; i < count; i++) { + mItemDecorations.get(i).onDraw(c, this, mState); + } + } + + @Override + protected boolean checkLayoutParams(ViewGroup.LayoutParams p) { + return p instanceof LayoutParams && mLayout.checkLayoutParams((LayoutParams) p); + } + + @Override + protected ViewGroup.LayoutParams generateDefaultLayoutParams() { + if (mLayout == null) { + throw new IllegalStateException("RecyclerView has no LayoutManager"); + } + return mLayout.generateDefaultLayoutParams(); + } + + @Override + public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) { + if (mLayout == null) { + throw new IllegalStateException("RecyclerView has no LayoutManager"); + } + return mLayout.generateLayoutParams(getContext(), attrs); + } + + @Override + protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) { + if (mLayout == null) { + throw new IllegalStateException("RecyclerView has no LayoutManager"); + } + return mLayout.generateLayoutParams(p); + } + + void saveOldPositions() { + final int childCount = mChildHelper.getUnfilteredChildCount(); + for (int i = 0; i < childCount; i++) { + final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i)); + if (DEBUG && holder.mPosition == -1 && !holder.isRemoved()) { + throw new IllegalStateException("view holder cannot have position -1 unless it" + + " is removed"); + } + if (!holder.shouldIgnore()) { + holder.saveOldPosition(); + } + } + } + + void clearOldPositions() { + final int childCount = mChildHelper.getUnfilteredChildCount(); + for (int i = 0; i < childCount; i++) { + final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i)); + if (!holder.shouldIgnore()) { + holder.clearOldPosition(); + } + } + mRecycler.clearOldPositions(); + } + + void offsetPositionRecordsForMove(int from, int to) { + final int childCount = mChildHelper.getUnfilteredChildCount(); + final int start, end, inBetweenOffset; + if (from < to) { + start = from; + end = to; + inBetweenOffset = -1; + } else { + start = to; + end = from; + inBetweenOffset = 1; + } + + for (int i = 0; i < childCount; i++) { + final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i)); + if (holder == null || holder.mPosition < start || holder.mPosition > end) { + continue; + } + if (DEBUG) { + Log.d(TAG, "offsetPositionRecordsForMove attached child " + i + " holder " + + holder); + } + if (holder.mPosition == from) { + holder.offsetPosition(to - from, false); + } else { + holder.offsetPosition(inBetweenOffset, false); + } + + mState.mStructureChanged = true; + } + mRecycler.offsetPositionRecordsForMove(from, to); + requestLayout(); + } + + void offsetPositionRecordsForInsert(int positionStart, int itemCount) { + final int childCount = mChildHelper.getUnfilteredChildCount(); + for (int i = 0; i < childCount; i++) { + final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i)); + if (holder != null && !holder.shouldIgnore() && holder.mPosition >= positionStart) { + if (DEBUG) { + Log.d(TAG, "offsetPositionRecordsForInsert attached child " + i + " holder " + + holder + " now at position " + (holder.mPosition + itemCount)); + } + holder.offsetPosition(itemCount, false); + mState.mStructureChanged = true; + } + } + mRecycler.offsetPositionRecordsForInsert(positionStart, itemCount); + requestLayout(); + } + + void offsetPositionRecordsForRemove(int positionStart, int itemCount, + boolean applyToPreLayout) { + final int positionEnd = positionStart + itemCount; + final int childCount = mChildHelper.getUnfilteredChildCount(); + for (int i = 0; i < childCount; i++) { + final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i)); + if (holder != null && !holder.shouldIgnore()) { + if (holder.mPosition >= positionEnd) { + if (DEBUG) { + Log.d(TAG, "offsetPositionRecordsForRemove attached child " + i + + " holder " + holder + " now at position " + + (holder.mPosition - itemCount)); + } + holder.offsetPosition(-itemCount, applyToPreLayout); + mState.mStructureChanged = true; + } else if (holder.mPosition >= positionStart) { + if (DEBUG) { + Log.d(TAG, "offsetPositionRecordsForRemove attached child " + i + + " holder " + holder + " now REMOVED"); + } + holder.flagRemovedAndOffsetPosition(positionStart - 1, -itemCount, + applyToPreLayout); + mState.mStructureChanged = true; + } + } + } + mRecycler.offsetPositionRecordsForRemove(positionStart, itemCount, applyToPreLayout); + requestLayout(); + } + + /** + * Rebind existing views for the given range, or create as needed. + * + * @param positionStart Adapter position to start at + * @param itemCount Number of views that must explicitly be rebound + */ + void viewRangeUpdate(int positionStart, int itemCount) { + final int childCount = mChildHelper.getUnfilteredChildCount(); + final int positionEnd = positionStart + itemCount; + + for (int i = 0; i < childCount; i++) { + final View child = mChildHelper.getUnfilteredChildAt(i); + final ViewHolder holder = getChildViewHolderInt(child); + if (holder == null || holder.shouldIgnore()) { + continue; + } + if (holder.mPosition >= positionStart && holder.mPosition < positionEnd) { + // We re-bind these view holders after pre-processing is complete so that + // ViewHolders have their final positions assigned. + holder.addFlags(ViewHolder.FLAG_UPDATE); + if (supportsChangeAnimations()) { + holder.addFlags(ViewHolder.FLAG_CHANGED); + } + // lp cannot be null since we get ViewHolder from it. + ((LayoutParams) child.getLayoutParams()).mInsetsDirty = true; + } + } + mRecycler.viewRangeUpdate(positionStart, itemCount); + } + + void rebindUpdatedViewHolders() { + final int childCount = mChildHelper.getChildCount(); + for (int i = 0; i < childCount; i++) { + final ViewHolder holder = getChildViewHolderInt(mChildHelper.getChildAt(i)); + // validate type is correct + if (holder == null || holder.shouldIgnore()) { + continue; + } + if (holder.isRemoved() || holder.isInvalid()) { + requestLayout(); + } else if (holder.needsUpdate()) { + final int type = mAdapter.getItemViewType(holder.mPosition); + if (holder.getItemViewType() == type) { + // Binding an attached view will request a layout if needed. + if (!holder.isChanged() || !supportsChangeAnimations()) { + mAdapter.bindViewHolder(holder, holder.mPosition); + } else { + // Don't rebind changed holders if change animations are enabled. + // We want the old contents for the animation and will get a new + // holder for the new contents. + requestLayout(); + } + } else { + // binding to a new view will need re-layout anyways. We can as well trigger + // it here so that it happens during layout + requestLayout(); + break; + } + } + } + } + + private void setDataSetChangedAfterLayout() { + if (mDataSetHasChangedAfterLayout) { + return; + } + mDataSetHasChangedAfterLayout = true; + final int childCount = mChildHelper.getUnfilteredChildCount(); + for (int i = 0; i < childCount; i++) { + final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i)); + if (holder != null && !holder.shouldIgnore()) { + holder.addFlags(ViewHolder.FLAG_ADAPTER_POSITION_UNKNOWN); + } + } + mRecycler.setAdapterPositionsAsUnknown(); + } + + /** + * Mark all known views as invalid. Used in response to a, "the whole world might have changed" + * data change event. + */ + void markKnownViewsInvalid() { + final int childCount = mChildHelper.getUnfilteredChildCount(); + for (int i = 0; i < childCount; i++) { + final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i)); + if (holder != null && !holder.shouldIgnore()) { + holder.addFlags(ViewHolder.FLAG_UPDATE | ViewHolder.FLAG_INVALID); + } + } + markItemDecorInsetsDirty(); + mRecycler.markKnownViewsInvalid(); + } + + /** + * Invalidates all ItemDecorations. If RecyclerView has item decorations, calling this method + * will trigger a {@link #requestLayout()} call. + */ + public void invalidateItemDecorations() { + if (mItemDecorations.size() == 0) { + return; + } + if (mLayout != null) { + mLayout.assertNotInLayoutOrScroll("Cannot invalidate item decorations during a scroll" + + " or layout"); + } + markItemDecorInsetsDirty(); + requestLayout(); + } + + /** + * Retrieve the {@link ViewHolder} for the given child view. + * + * @param child Child of this RecyclerView to query for its ViewHolder + * @return The child view's ViewHolder + */ + public ViewHolder getChildViewHolder(View child) { + final ViewParent parent = child.getParent(); + if (parent != null && parent != this) { + throw new IllegalArgumentException("View " + child + " is not a direct child of " + + this); + } + return getChildViewHolderInt(child); + } + + static ViewHolder getChildViewHolderInt(View child) { + if (child == null) { + return null; + } + return ((LayoutParams) child.getLayoutParams()).mViewHolder; + } + + /** + * @deprecated use {@link #getChildAdapterPosition(View)} or + * {@link #getChildLayoutPosition(View)}. + */ + @Deprecated + public int getChildPosition(View child) { + return getChildAdapterPosition(child); + } + + /** + * Return the adapter position that the given child view corresponds to. + * + * @param child Child View to query + * @return Adapter position corresponding to the given view or {@link #NO_POSITION} + */ + public int getChildAdapterPosition(View child) { + final ViewHolder holder = getChildViewHolderInt(child); + return holder != null ? holder.getAdapterPosition() : NO_POSITION; + } + + /** + * Return the adapter position of the given child view as of the latest completed layout pass. + *

+ * This position may not be equal to Item's adapter position if there are pending changes + * in the adapter which have not been reflected to the layout yet. + * + * @param child Child View to query + * @return Adapter position of the given View as of last layout pass or {@link #NO_POSITION} if + * the View is representing a removed item. + */ + public int getChildLayoutPosition(View child) { + final ViewHolder holder = getChildViewHolderInt(child); + return holder != null ? holder.getLayoutPosition() : NO_POSITION; + } + + /** + * Return the stable item id that the given child view corresponds to. + * + * @param child Child View to query + * @return Item id corresponding to the given view or {@link #NO_ID} + */ + public long getChildItemId(View child) { + if (mAdapter == null || !mAdapter.hasStableIds()) { + return NO_ID; + } + final ViewHolder holder = getChildViewHolderInt(child); + return holder != null ? holder.getItemId() : NO_ID; + } + + /** + * @deprecated use {@link #findViewHolderForLayoutPosition(int)} or + * {@link #findViewHolderForAdapterPosition(int)} + */ + @Deprecated + public ViewHolder findViewHolderForPosition(int position) { + return findViewHolderForPosition(position, false); + } + + /** + * Return the ViewHolder for the item in the given position of the data set as of the latest + * layout pass. + *

+ * This method checks only the children of RecyclerView. If the item at the given + * position is not laid out, it will not create a new one. + *

+ * Note that when Adapter contents change, ViewHolder positions are not updated until the + * next layout calculation. If there are pending adapter updates, the return value of this + * method may not match your adapter contents. You can use + * #{@link ViewHolder#getAdapterPosition()} to get the current adapter position of a ViewHolder. + * + * @param position The position of the item in the data set of the adapter + * @return The ViewHolder at position or null if there is no such item + */ + public ViewHolder findViewHolderForLayoutPosition(int position) { + return findViewHolderForPosition(position, false); + } + + /** + * Return the ViewHolder for the item in the given position of the data set. Unlike + * {@link #findViewHolderForLayoutPosition(int)} this method takes into account any pending + * adapter changes that may not be reflected to the layout yet. On the other hand, if + * {@link Adapter#notifyDataSetChanged()} has been called but the new layout has not been + * calculated yet, this method will return null since the new positions of views + * are unknown until the layout is calculated. + *

+ * This method checks only the children of RecyclerView. If the item at the given + * position is not laid out, it will not create a new one. + * + * @param position The position of the item in the data set of the adapter + * @return The ViewHolder at position or null if there is no such item + */ + public ViewHolder findViewHolderForAdapterPosition(int position) { + if (mDataSetHasChangedAfterLayout) { + return null; + } + final int childCount = mChildHelper.getUnfilteredChildCount(); + for (int i = 0; i < childCount; i++) { + final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i)); + if (holder != null && !holder.isRemoved() && getAdapterPositionFor(holder) == position) { + return holder; + } + } + return null; + } + + ViewHolder findViewHolderForPosition(int position, boolean checkNewPosition) { + final int childCount = mChildHelper.getUnfilteredChildCount(); + for (int i = 0; i < childCount; i++) { + final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i)); + if (holder != null && !holder.isRemoved()) { + if (checkNewPosition) { + if (holder.mPosition == position) { + return holder; + } + } else if (holder.getLayoutPosition() == position) { + return holder; + } + } + } + // This method should not query cached views. It creates a problem during adapter updates + // when we are dealing with already laid out views. Also, for the public method, it is more + // reasonable to return null if position is not laid out. + return null; + } + + /** + * Return the ViewHolder for the item with the given id. The RecyclerView must + * use an Adapter with {@link Adapter#setHasStableIds(boolean) stableIds} to + * return a non-null value. + *

+ * This method checks only the children of RecyclerView. If the item with the given + * id is not laid out, it will not create a new one. + * + * @param id The id for the requested item + * @return The ViewHolder with the given id or null if there is no such item + */ + public ViewHolder findViewHolderForItemId(long id) { + final int childCount = mChildHelper.getUnfilteredChildCount(); + for (int i = 0; i < childCount; i++) { + final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i)); + if (holder != null && holder.getItemId() == id) { + return holder; + } + } + // this method should not query cached views. They are not children so they + // should not be returned in this public method + return null; + } + + /** + * Find the topmost view under the given point. + * + * @param x Horizontal position in pixels to search + * @param y Vertical position in pixels to search + * @return The child view under (x, y) or null if no matching child is found + */ + public View findChildViewUnder(float x, float y) { + final int count = mChildHelper.getChildCount(); + for (int i = count - 1; i >= 0; i--) { + final View child = mChildHelper.getChildAt(i); + final float translationX = ViewCompat.getTranslationX(child); + final float translationY = ViewCompat.getTranslationY(child); + if (x >= child.getLeft() + translationX && + x <= child.getRight() + translationX && + y >= child.getTop() + translationY && + y <= child.getBottom() + translationY) { + return child; + } + } + return null; + } + + /** + * Offset the bounds of all child views by dy pixels. + * Useful for implementing simple scrolling in {@link LayoutManager LayoutManagers}. + * + * @param dy Vertical pixel offset to apply to the bounds of all child views + */ + public void offsetChildrenVertical(int dy) { + final int childCount = mChildHelper.getChildCount(); + for (int i = 0; i < childCount; i++) { + mChildHelper.getChildAt(i).offsetTopAndBottom(dy); + } + } + + /** + * Called when an item view is attached to this RecyclerView. + * + *

Subclasses of RecyclerView may want to perform extra bookkeeping or modifications + * of child views as they become attached. This will be called before a + * {@link LayoutManager} measures or lays out the view and is a good time to perform these + * changes.

+ * + * @param child Child view that is now attached to this RecyclerView and its associated window + */ + public void onChildAttachedToWindow(View child) { + } + + /** + * Called when an item view is detached from this RecyclerView. + * + *

Subclasses of RecyclerView may want to perform extra bookkeeping or modifications + * of child views as they become detached. This will be called as a + * {@link LayoutManager} fully detaches the child view from the parent and its window.

+ * + * @param child Child view that is now detached from this RecyclerView and its associated window + */ + public void onChildDetachedFromWindow(View child) { + } + + /** + * Offset the bounds of all child views by dx pixels. + * Useful for implementing simple scrolling in {@link LayoutManager LayoutManagers}. + * + * @param dx Horizontal pixel offset to apply to the bounds of all child views + */ + public void offsetChildrenHorizontal(int dx) { + final int childCount = mChildHelper.getChildCount(); + for (int i = 0; i < childCount; i++) { + mChildHelper.getChildAt(i).offsetLeftAndRight(dx); + } + } + + Rect getItemDecorInsetsForChild(View child) { + final LayoutParams lp = (LayoutParams) child.getLayoutParams(); + if (!lp.mInsetsDirty) { + return lp.mDecorInsets; + } + + final Rect insets = lp.mDecorInsets; + insets.set(0, 0, 0, 0); + final int decorCount = mItemDecorations.size(); + for (int i = 0; i < decorCount; i++) { + mTempRect.set(0, 0, 0, 0); + mItemDecorations.get(i).getItemOffsets(mTempRect, child, this, mState); + insets.left += mTempRect.left; + insets.top += mTempRect.top; + insets.right += mTempRect.right; + insets.bottom += mTempRect.bottom; + } + lp.mInsetsDirty = false; + return insets; + } + + /** + * Called when the scroll position of this RecyclerView changes. Subclasses should use + * this method to respond to scrolling within the adapter's data set instead of an explicit + * listener. + * + *

This method will always be invoked before listeners. If a subclass needs to perform + * any additional upkeep or bookkeeping after scrolling but before listeners run, + * this is a good place to do so.

+ * + *

This differs from {@link View#onScrollChanged(int, int, int, int)} in that it receives + * the distance scrolled in either direction within the adapter's data set instead of absolute + * scroll coordinates. Since RecyclerView cannot compute the absolute scroll position from + * any arbitrary point in the data set, onScrollChanged will always receive + * the current {@link View#getScrollX()} and {@link View#getScrollY()} values which + * do not correspond to the data set scroll position. However, some subclasses may choose + * to use these fields as special offsets.

+ * + * @param dx horizontal distance scrolled in pixels + * @param dy vertical distance scrolled in pixels + */ + public void onScrolled(int dx, int dy) { + // Do nothing + } + + void dispatchOnScrolled(int hresult, int vresult) { + // Pass the current scrollX/scrollY values; no actual change in these properties occurred + // but some general-purpose code may choose to respond to changes this way. + final int scrollX = getScrollX(); + final int scrollY = getScrollY(); + onScrollChanged(scrollX, scrollY, scrollX, scrollY); + + // Pass the real deltas to onScrolled, the RecyclerView-specific method. + onScrolled(hresult, vresult); + + // Invoke listeners last. Subclassed view methods always handle the event first. + // All internal state is consistent by the time listeners are invoked. + if (mScrollListener != null) { + mScrollListener.onScrolled(this, hresult, vresult); + } + if (mScrollListeners != null) { + for (int i = mScrollListeners.size() - 1; i >= 0; i--) { + mScrollListeners.get(i).onScrolled(this, hresult, vresult); + } + } + } + + /** + * Called when the scroll state of this RecyclerView changes. Subclasses should use this + * method to respond to state changes instead of an explicit listener. + * + *

This method will always be invoked before listeners, but after the LayoutManager + * responds to the scroll state change.

+ * + * @param state the new scroll state, one of {@link #SCROLL_STATE_IDLE}, + * {@link #SCROLL_STATE_DRAGGING} or {@link #SCROLL_STATE_SETTLING} + */ + public void onScrollStateChanged(int state) { + // Do nothing + } + + void dispatchOnScrollStateChanged(int state) { + // Let the LayoutManager go first; this allows it to bring any properties into + // a consistent state before the RecyclerView subclass responds. + if (mLayout != null) { + mLayout.onScrollStateChanged(state); + } + + // Let the RecyclerView subclass handle this event next; any LayoutManager property + // changes will be reflected by this time. + onScrollStateChanged(state); + + // Listeners go last. All other internal state is consistent by this point. + if (mScrollListener != null) { + mScrollListener.onScrollStateChanged(this, state); + } + if (mScrollListeners != null) { + for (int i = mScrollListeners.size() - 1; i >= 0; i--) { + mScrollListeners.get(i).onScrollStateChanged(this, state); + } + } + } + + /** + * Returns whether there are pending adapter updates which are not yet applied to the layout. + *

+ * If this method returns true, it means that what user is currently seeing may not + * reflect them adapter contents (depending on what has changed). + * You may use this information to defer or cancel some operations. + *

+ * This method returns true if RecyclerView has not yet calculated the first layout after it is + * attached to the Window or the Adapter has been replaced. + * + * @return True if there are some adapter updates which are not yet reflected to layout or false + * if layout is up to date. + */ + public boolean hasPendingAdapterUpdates() { + return !mFirstLayoutComplete || mDataSetHasChangedAfterLayout + || mAdapterHelper.hasPendingUpdates(); + } + + private class ViewFlinger implements Runnable { + private int mLastFlingX; + private int mLastFlingY; + private ScrollerCompat mScroller; + private Interpolator mInterpolator = sQuinticInterpolator; + + + // When set to true, postOnAnimation callbacks are delayed until the run method completes + private boolean mEatRunOnAnimationRequest = false; + + // Tracks if postAnimationCallback should be re-attached when it is done + private boolean mReSchedulePostAnimationCallback = false; + + public ViewFlinger() { + mScroller = ScrollerCompat.create(getContext(), sQuinticInterpolator); + } + + @Override + public void run() { + disableRunOnAnimationRequests(); + consumePendingUpdateOperations(); + // keep a local reference so that if it is changed during onAnimation method, it won't + // cause unexpected behaviors + final ScrollerCompat scroller = mScroller; + final SmoothScroller smoothScroller = mLayout.mSmoothScroller; + if (scroller.computeScrollOffset()) { + final int x = scroller.getCurrX(); + final int y = scroller.getCurrY(); + final int dx = x - mLastFlingX; + final int dy = y - mLastFlingY; + int hresult = 0; + int vresult = 0; + mLastFlingX = x; + mLastFlingY = y; + int overscrollX = 0, overscrollY = 0; + if (mAdapter != null) { + eatRequestLayout(); + onEnterLayoutOrScroll(); + if (dx != 0) { + hresult = mLayout.scrollHorizontallyBy(dx, mRecycler, mState); + overscrollX = dx - hresult; + } + if (dy != 0) { + vresult = mLayout.scrollVerticallyBy(dy, mRecycler, mState); + overscrollY = dy - vresult; + } + if (supportsChangeAnimations()) { + // Fix up shadow views used by changing animations + int count = mChildHelper.getChildCount(); + for (int i = 0; i < count; i++) { + View view = mChildHelper.getChildAt(i); + ViewHolder holder = getChildViewHolder(view); + if (holder != null && holder.mShadowingHolder != null) { + View shadowingView = holder.mShadowingHolder.itemView; + int left = view.getLeft(); + int top = view.getTop(); + if (left != shadowingView.getLeft() || + top != shadowingView.getTop()) { + shadowingView.layout(left, top, + left + shadowingView.getWidth(), + top + shadowingView.getHeight()); + } + } + } + } + + if (smoothScroller != null && !smoothScroller.isPendingInitialRun() && + smoothScroller.isRunning()) { + final int adapterSize = mState.getItemCount(); + if (adapterSize == 0) { + smoothScroller.stop(); + } else if (smoothScroller.getTargetPosition() >= adapterSize) { + smoothScroller.setTargetPosition(adapterSize - 1); + smoothScroller.onAnimation(dx - overscrollX, dy - overscrollY); + } else { + smoothScroller.onAnimation(dx - overscrollX, dy - overscrollY); + } + } + onExitLayoutOrScroll(); + resumeRequestLayout(false); + } + if (!mItemDecorations.isEmpty()) { + invalidate(); + } + if (ViewCompat.getOverScrollMode(RecyclerView.this) != + ViewCompat.OVER_SCROLL_NEVER) { + considerReleasingGlowsOnScroll(dx, dy); + } + if (overscrollX != 0 || overscrollY != 0) { + final int vel = (int) scroller.getCurrVelocity(); + + int velX = 0; + if (overscrollX != x) { + velX = overscrollX < 0 ? -vel : overscrollX > 0 ? vel : 0; + } + + int velY = 0; + if (overscrollY != y) { + velY = overscrollY < 0 ? -vel : overscrollY > 0 ? vel : 0; + } + + if (ViewCompat.getOverScrollMode(RecyclerView.this) != + ViewCompat.OVER_SCROLL_NEVER) { + absorbGlows(velX, velY); + } + if ((velX != 0 || overscrollX == x || scroller.getFinalX() == 0) && + (velY != 0 || overscrollY == y || scroller.getFinalY() == 0)) { + scroller.abortAnimation(); + } + } + if (hresult != 0 || vresult != 0) { + dispatchOnScrolled(hresult, vresult); + } + + if (!awakenScrollBars()) { + invalidate(); + } + + final boolean fullyConsumedVertical = dy != 0 && mLayout.canScrollVertically() + && vresult == dy; + final boolean fullyConsumedHorizontal = dx != 0 && mLayout.canScrollHorizontally() + && hresult == dx; + final boolean fullyConsumedAny = (dx == 0 && dy == 0) || fullyConsumedHorizontal + || fullyConsumedVertical; + + if (scroller.isFinished() || !fullyConsumedAny) { + setScrollState(SCROLL_STATE_IDLE); // setting state to idle will stop this. + } else { + postOnAnimation(); + } + } + // call this after the onAnimation is complete not to have inconsistent callbacks etc. + if (smoothScroller != null && smoothScroller.isPendingInitialRun()) { + smoothScroller.onAnimation(0, 0); + } + enableRunOnAnimationRequests(); + } + + private void disableRunOnAnimationRequests() { + mReSchedulePostAnimationCallback = false; + mEatRunOnAnimationRequest = true; + } + + private void enableRunOnAnimationRequests() { + mEatRunOnAnimationRequest = false; + if (mReSchedulePostAnimationCallback) { + postOnAnimation(); + } + } + + void postOnAnimation() { + if (mEatRunOnAnimationRequest) { + mReSchedulePostAnimationCallback = true; + } else { + removeCallbacks(this); + ViewCompat.postOnAnimation(RecyclerView.this, this); + } + } + + public void fling(int velocityX, int velocityY) { + setScrollState(SCROLL_STATE_SETTLING); + mLastFlingX = mLastFlingY = 0; + mScroller.fling(0, 0, velocityX, velocityY, + Integer.MIN_VALUE, Integer.MAX_VALUE, Integer.MIN_VALUE, Integer.MAX_VALUE); + postOnAnimation(); + } + + public void smoothScrollBy(int dx, int dy) { + smoothScrollBy(dx, dy, 0, 0); + } + + public void smoothScrollBy(int dx, int dy, int vx, int vy) { + smoothScrollBy(dx, dy, computeScrollDuration(dx, dy, vx, vy)); + } + + private float distanceInfluenceForSnapDuration(float f) { + f -= 0.5f; // center the values about 0. + f *= 0.3f * Math.PI / 2.0f; + return (float) Math.sin(f); + } + + private int computeScrollDuration(int dx, int dy, int vx, int vy) { + final int absDx = Math.abs(dx); + final int absDy = Math.abs(dy); + final boolean horizontal = absDx > absDy; + final int velocity = (int) Math.sqrt(vx * vx + vy * vy); + final int delta = (int) Math.sqrt(dx * dx + dy * dy); + final int containerSize = horizontal ? getWidth() : getHeight(); + final int halfContainerSize = containerSize / 2; + final float distanceRatio = Math.min(1.f, 1.f * delta / containerSize); + final float distance = halfContainerSize + halfContainerSize * + distanceInfluenceForSnapDuration(distanceRatio); + + final int duration; + if (velocity > 0) { + duration = 4 * Math.round(1000 * Math.abs(distance / velocity)); + } else { + float absDelta = (float) (horizontal ? absDx : absDy); + duration = (int) (((absDelta / containerSize) + 1) * 300); + } + return Math.min(duration, MAX_SCROLL_DURATION); + } + + public void smoothScrollBy(int dx, int dy, int duration) { + smoothScrollBy(dx, dy, duration, sQuinticInterpolator); + } + + public void smoothScrollBy(int dx, int dy, int duration, Interpolator interpolator) { + if (mInterpolator != interpolator) { + mInterpolator = interpolator; + mScroller = ScrollerCompat.create(getContext(), interpolator); + } + setScrollState(SCROLL_STATE_SETTLING); + mLastFlingX = mLastFlingY = 0; + mScroller.startScroll(0, 0, dx, dy, duration); + postOnAnimation(); + } + + public void stop() { + removeCallbacks(this); + mScroller.abortAnimation(); + } + + } + + private class RecyclerViewDataObserver extends AdapterDataObserver { + @Override + public void onChanged() { + assertNotInLayoutOrScroll(null); + if (mAdapter.hasStableIds()) { + // TODO Determine what actually changed. + // This is more important to implement now since this callback will disable all + // animations because we cannot rely on positions. + mState.mStructureChanged = true; + setDataSetChangedAfterLayout(); + } else { + mState.mStructureChanged = true; + setDataSetChangedAfterLayout(); + } + if (!mAdapterHelper.hasPendingUpdates()) { + requestLayout(); + } + } + + @Override + public void onItemRangeChanged(int positionStart, int itemCount) { + assertNotInLayoutOrScroll(null); + if (mAdapterHelper.onItemRangeChanged(positionStart, itemCount)) { + triggerUpdateProcessor(); + } + } + + @Override + public void onItemRangeInserted(int positionStart, int itemCount) { + assertNotInLayoutOrScroll(null); + if (mAdapterHelper.onItemRangeInserted(positionStart, itemCount)) { + triggerUpdateProcessor(); + } + } + + @Override + public void onItemRangeRemoved(int positionStart, int itemCount) { + assertNotInLayoutOrScroll(null); + if (mAdapterHelper.onItemRangeRemoved(positionStart, itemCount)) { + triggerUpdateProcessor(); + } + } + + @Override + public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) { + assertNotInLayoutOrScroll(null); + if (mAdapterHelper.onItemRangeMoved(fromPosition, toPosition, itemCount)) { + triggerUpdateProcessor(); + } + } + + void triggerUpdateProcessor() { + if (mPostUpdatesOnAnimation && mHasFixedSize && mIsAttached) { + ViewCompat.postOnAnimation(RecyclerView.this, mUpdateChildViewsRunnable); + } else { + mAdapterUpdateDuringMeasure = true; + requestLayout(); + } + } + } + + /** + * RecycledViewPool lets you share Views between multiple RecyclerViews. + *

+ * If you want to recycle views across RecyclerViews, create an instance of RecycledViewPool + * and use {@link RecyclerView#setRecycledViewPool(RecycledViewPool)}. + *

+ * RecyclerView automatically creates a pool for itself if you don't provide one. + * + */ + public static class RecycledViewPool { + private SparseArray> mScrap = + new SparseArray>(); + private SparseIntArray mMaxScrap = new SparseIntArray(); + private int mAttachCount = 0; + + private static final int DEFAULT_MAX_SCRAP = 5; + + public void clear() { + mScrap.clear(); + } + + public void setMaxRecycledViews(int viewType, int max) { + mMaxScrap.put(viewType, max); + final ArrayList scrapHeap = mScrap.get(viewType); + if (scrapHeap != null) { + while (scrapHeap.size() > max) { + scrapHeap.remove(scrapHeap.size() - 1); + } + } + } + + public ViewHolder getRecycledView(int viewType) { + final ArrayList scrapHeap = mScrap.get(viewType); + if (scrapHeap != null && !scrapHeap.isEmpty()) { + final int index = scrapHeap.size() - 1; + final ViewHolder scrap = scrapHeap.get(index); + scrapHeap.remove(index); + return scrap; + } + return null; + } + + int size() { + int count = 0; + for (int i = 0; i < mScrap.size(); i ++) { + ArrayList viewHolders = mScrap.valueAt(i); + if (viewHolders != null) { + count += viewHolders.size(); + } + } + return count; + } + + public void putRecycledView(ViewHolder scrap) { + final int viewType = scrap.getItemViewType(); + final ArrayList scrapHeap = getScrapHeapForType(viewType); + if (mMaxScrap.get(viewType) <= scrapHeap.size()) { + return; + } + scrap.resetInternal(); + scrapHeap.add(scrap); + } + + void attach(Adapter adapter) { + mAttachCount++; + } + + void detach() { + mAttachCount--; + } + + + /** + * Detaches the old adapter and attaches the new one. + *

+ * RecycledViewPool will clear its cache if it has only one adapter attached and the new + * adapter uses a different ViewHolder than the oldAdapter. + * + * @param oldAdapter The previous adapter instance. Will be detached. + * @param newAdapter The new adapter instance. Will be attached. + * @param compatibleWithPrevious True if both oldAdapter and newAdapter are using the same + * ViewHolder and view types. + */ + void onAdapterChanged(Adapter oldAdapter, Adapter newAdapter, + boolean compatibleWithPrevious) { + if (oldAdapter != null) { + detach(); + } + if (!compatibleWithPrevious && mAttachCount == 0) { + clear(); + } + if (newAdapter != null) { + attach(newAdapter); + } + } + + private ArrayList getScrapHeapForType(int viewType) { + ArrayList scrap = mScrap.get(viewType); + if (scrap == null) { + scrap = new ArrayList(); + mScrap.put(viewType, scrap); + if (mMaxScrap.indexOfKey(viewType) < 0) { + mMaxScrap.put(viewType, DEFAULT_MAX_SCRAP); + } + } + return scrap; + } + } + + /** + * A Recycler is responsible for managing scrapped or detached item views for reuse. + * + *

A "scrapped" view is a view that is still attached to its parent RecyclerView but + * that has been marked for removal or reuse.

+ * + *

Typical use of a Recycler by a {@link LayoutManager} will be to obtain views for + * an adapter's data set representing the data at a given position or item ID. + * If the view to be reused is considered "dirty" the adapter will be asked to rebind it. + * If not, the view can be quickly reused by the LayoutManager with no further work. + * Clean views that have not {@link android.view.View#isLayoutRequested() requested layout} + * may be repositioned by a LayoutManager without remeasurement.

+ */ + public final class Recycler { + final ArrayList mAttachedScrap = new ArrayList(); + private ArrayList mChangedScrap = null; + + final ArrayList mCachedViews = new ArrayList(); + + private final List + mUnmodifiableAttachedScrap = Collections.unmodifiableList(mAttachedScrap); + + private int mViewCacheMax = DEFAULT_CACHE_SIZE; + + private RecycledViewPool mRecyclerPool; + + private ViewCacheExtension mViewCacheExtension; + + private static final int DEFAULT_CACHE_SIZE = 2; + + /** + * Clear scrap views out of this recycler. Detached views contained within a + * recycled view pool will remain. + */ + public void clear() { + mAttachedScrap.clear(); + recycleAndClearCachedViews(); + } + + /** + * Set the maximum number of detached, valid views we should retain for later use. + * + * @param viewCount Number of views to keep before sending views to the shared pool + */ + public void setViewCacheSize(int viewCount) { + mViewCacheMax = viewCount; + // first, try the views that can be recycled + for (int i = mCachedViews.size() - 1; i >= 0 && mCachedViews.size() > viewCount; i--) { + recycleCachedViewAt(i); + } + } + + /** + * Returns an unmodifiable list of ViewHolders that are currently in the scrap list. + * + * @return List of ViewHolders in the scrap list. + */ + public List getScrapList() { + return mUnmodifiableAttachedScrap; + } + + /** + * Helper method for getViewForPosition. + *

+ * Checks whether a given view holder can be used for the provided position. + * + * @param holder ViewHolder + * @return true if ViewHolder matches the provided position, false otherwise + */ + boolean validateViewHolderForOffsetPosition(ViewHolder holder) { + // if it is a removed holder, nothing to verify since we cannot ask adapter anymore + // if it is not removed, verify the type and id. + if (holder.isRemoved()) { + return true; + } + if (holder.mPosition < 0 || holder.mPosition >= mAdapter.getItemCount()) { + throw new IndexOutOfBoundsException("Inconsistency detected. Invalid view holder " + + "adapter position" + holder); + } + if (!mState.isPreLayout()) { + // don't check type if it is pre-layout. + final int type = mAdapter.getItemViewType(holder.mPosition); + if (type != holder.getItemViewType()) { + return false; + } + } + if (mAdapter.hasStableIds()) { + return holder.getItemId() == mAdapter.getItemId(holder.mPosition); + } + return true; + } + + /** + * Binds the given View to the position. The View can be a View previously retrieved via + * {@link #getViewForPosition(int)} or created by + * {@link Adapter#onCreateViewHolder(ViewGroup, int)}. + *

+ * Generally, a LayoutManager should acquire its views via {@link #getViewForPosition(int)} + * and let the RecyclerView handle caching. This is a helper method for LayoutManager who + * wants to handle its own recycling logic. + *

+ * Note that, {@link #getViewForPosition(int)} already binds the View to the position so + * you don't need to call this method unless you want to bind this View to another position. + * + * @param view The view to update. + * @param position The position of the item to bind to this View. + */ + public void bindViewToPosition(View view, int position) { + ViewHolder holder = getChildViewHolderInt(view); + if (holder == null) { + throw new IllegalArgumentException("The view does not have a ViewHolder. You cannot" + + " pass arbitrary views to this method, they should be created by the " + + "Adapter"); + } + final int offsetPosition = mAdapterHelper.findPositionOffset(position); + if (offsetPosition < 0 || offsetPosition >= mAdapter.getItemCount()) { + throw new IndexOutOfBoundsException("Inconsistency detected. Invalid item " + + "position " + position + "(offset:" + offsetPosition + ")." + + "state:" + mState.getItemCount()); + } + holder.mOwnerRecyclerView = RecyclerView.this; + mAdapter.bindViewHolder(holder, offsetPosition); + attachAccessibilityDelegate(view); + if (mState.isPreLayout()) { + holder.mPreLayoutPosition = position; + } + + final ViewGroup.LayoutParams lp = holder.itemView.getLayoutParams(); + final LayoutParams rvLayoutParams; + if (lp == null) { + rvLayoutParams = (LayoutParams) generateDefaultLayoutParams(); + holder.itemView.setLayoutParams(rvLayoutParams); + } else if (!checkLayoutParams(lp)) { + rvLayoutParams = (LayoutParams) generateLayoutParams(lp); + holder.itemView.setLayoutParams(rvLayoutParams); + } else { + rvLayoutParams = (LayoutParams) lp; + } + + rvLayoutParams.mInsetsDirty = true; + rvLayoutParams.mViewHolder = holder; + rvLayoutParams.mPendingInvalidate = holder.itemView.getParent() == null; + } + + /** + * RecyclerView provides artificial position range (item count) in pre-layout state and + * automatically maps these positions to {@link Adapter} positions when + * {@link #getViewForPosition(int)} or {@link #bindViewToPosition(View, int)} is called. + *

+ * Usually, LayoutManager does not need to worry about this. However, in some cases, your + * LayoutManager may need to call some custom component with item positions in which + * case you need the actual adapter position instead of the pre layout position. You + * can use this method to convert a pre-layout position to adapter (post layout) position. + *

+ * Note that if the provided position belongs to a deleted ViewHolder, this method will + * return -1. + *

+ * Calling this method in post-layout state returns the same value back. + * + * @param position The pre-layout position to convert. Must be greater or equal to 0 and + * less than {@link State#getItemCount()}. + */ + public int convertPreLayoutPositionToPostLayout(int position) { + if (position < 0 || position >= mState.getItemCount()) { + throw new IndexOutOfBoundsException("invalid position " + position + ". State " + + "item count is " + mState.getItemCount()); + } + if (!mState.isPreLayout()) { + return position; + } + return mAdapterHelper.findPositionOffset(position); + } + + /** + * Obtain a view initialized for the given position. + * + * This method should be used by {@link LayoutManager} implementations to obtain + * views to represent data from an {@link Adapter}. + *

+ * The Recycler may reuse a scrap or detached view from a shared pool if one is + * available for the correct view type. If the adapter has not indicated that the + * data at the given position has changed, the Recycler will attempt to hand back + * a scrap view that was previously initialized for that data without rebinding. + * + * @param position Position to obtain a view for + * @return A view representing the data at position from adapter + */ + public View getViewForPosition(int position) { + return getViewForPosition(position, false); + } + + View getViewForPosition(int position, boolean dryRun) { + if (position < 0 || position >= mState.getItemCount()) { + throw new IndexOutOfBoundsException("Invalid item position " + position + + "(" + position + "). Item count:" + mState.getItemCount()); + } + boolean fromScrap = false; + ViewHolder holder = null; + // 0) If there is a changed scrap, try to find from there + if (mState.isPreLayout()) { + holder = getChangedScrapViewForPosition(position); + fromScrap = holder != null; + } + // 1) Find from scrap by position + if (holder == null) { + holder = getScrapViewForPosition(position, INVALID_TYPE, dryRun); + if (holder != null) { + if (!validateViewHolderForOffsetPosition(holder)) { + // recycle this scrap + if (!dryRun) { + // we would like to recycle this but need to make sure it is not used by + // animation logic etc. + holder.addFlags(ViewHolder.FLAG_INVALID); + if (holder.isScrap()) { + removeDetachedView(holder.itemView, false); + holder.unScrap(); + } else if (holder.wasReturnedFromScrap()) { + holder.clearReturnedFromScrapFlag(); + } + recycleViewHolderInternal(holder); + } + holder = null; + } else { + fromScrap = true; + } + } + } + if (holder == null) { + final int offsetPosition = mAdapterHelper.findPositionOffset(position); + if (offsetPosition < 0 || offsetPosition >= mAdapter.getItemCount()) { + throw new IndexOutOfBoundsException("Inconsistency detected. Invalid item " + + "position " + position + "(offset:" + offsetPosition + ")." + + "state:" + mState.getItemCount()); + } + + final int type = mAdapter.getItemViewType(offsetPosition); + // 2) Find from scrap via stable ids, if exists + if (mAdapter.hasStableIds()) { + holder = getScrapViewForId(mAdapter.getItemId(offsetPosition), type, dryRun); + if (holder != null) { + // update position + holder.mPosition = offsetPosition; + fromScrap = true; + } + } + if (holder == null && mViewCacheExtension != null) { + // We are NOT sending the offsetPosition because LayoutManager does not + // know it. + final View view = mViewCacheExtension + .getViewForPositionAndType(this, position, type); + if (view != null) { + holder = getChildViewHolder(view); + if (holder == null) { + throw new IllegalArgumentException("getViewForPositionAndType returned" + + " a view which does not have a ViewHolder"); + } else if (holder.shouldIgnore()) { + throw new IllegalArgumentException("getViewForPositionAndType returned" + + " a view that is ignored. You must call stopIgnoring before" + + " returning this view."); + } + } + } + if (holder == null) { // fallback to recycler + // try recycler. + // Head to the shared pool. + if (DEBUG) { + Log.d(TAG, "getViewForPosition(" + position + ") fetching from shared " + + "pool"); + } + holder = getRecycledViewPool().getRecycledView(type); + if (holder != null) { + holder.resetInternal(); + if (FORCE_INVALIDATE_DISPLAY_LIST) { + invalidateDisplayListInt(holder); + } + } + } + if (holder == null) { + holder = mAdapter.createViewHolder(RecyclerView.this, type); + if (DEBUG) { + Log.d(TAG, "getViewForPosition created new ViewHolder"); + } + } + } + boolean bound = false; + if (mState.isPreLayout() && holder.isBound()) { + // do not update unless we absolutely have to. + holder.mPreLayoutPosition = position; + } else if (!holder.isBound() || holder.needsUpdate() || holder.isInvalid()) { + if (DEBUG && holder.isRemoved()) { + throw new IllegalStateException("Removed holder should be bound and it should" + + " come here only in pre-layout. Holder: " + holder); + } + final int offsetPosition = mAdapterHelper.findPositionOffset(position); + holder.mOwnerRecyclerView = RecyclerView.this; + mAdapter.bindViewHolder(holder, offsetPosition); + attachAccessibilityDelegate(holder.itemView); + bound = true; + if (mState.isPreLayout()) { + holder.mPreLayoutPosition = position; + } + } + + final ViewGroup.LayoutParams lp = holder.itemView.getLayoutParams(); + final LayoutParams rvLayoutParams; + if (lp == null) { + rvLayoutParams = (LayoutParams) generateDefaultLayoutParams(); + holder.itemView.setLayoutParams(rvLayoutParams); + } else if (!checkLayoutParams(lp)) { + rvLayoutParams = (LayoutParams) generateLayoutParams(lp); + holder.itemView.setLayoutParams(rvLayoutParams); + } else { + rvLayoutParams = (LayoutParams) lp; + } + rvLayoutParams.mViewHolder = holder; + rvLayoutParams.mPendingInvalidate = fromScrap && bound; + return holder.itemView; + } + + private void attachAccessibilityDelegate(View itemView) { + if (mAccessibilityManager != null && mAccessibilityManager.isEnabled()) { + if (ViewCompat.getImportantForAccessibility(itemView) == + ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_AUTO) { + ViewCompat.setImportantForAccessibility(itemView, + ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_YES); + } + if (!ViewCompat.hasAccessibilityDelegate(itemView)) { + ViewCompat.setAccessibilityDelegate(itemView, + mAccessibilityDelegate.getItemDelegate()); + } + } + } + + private void invalidateDisplayListInt(ViewHolder holder) { + if (holder.itemView instanceof ViewGroup) { + invalidateDisplayListInt((ViewGroup) holder.itemView, false); + } + } + + private void invalidateDisplayListInt(ViewGroup viewGroup, boolean invalidateThis) { + for (int i = viewGroup.getChildCount() - 1; i >= 0; i--) { + final View view = viewGroup.getChildAt(i); + if (view instanceof ViewGroup) { + invalidateDisplayListInt((ViewGroup) view, true); + } + } + if (!invalidateThis) { + return; + } + // we need to force it to become invisible + if (viewGroup.getVisibility() == View.INVISIBLE) { + viewGroup.setVisibility(View.VISIBLE); + viewGroup.setVisibility(View.INVISIBLE); + } else { + final int visibility = viewGroup.getVisibility(); + viewGroup.setVisibility(View.INVISIBLE); + viewGroup.setVisibility(visibility); + } + } + + /** + * Recycle a detached view. The specified view will be added to a pool of views + * for later rebinding and reuse. + * + *

A view must be fully detached (removed from parent) before it may be recycled. If the + * View is scrapped, it will be removed from scrap list.

+ * + * @param view Removed view for recycling + * @see LayoutManager#removeAndRecycleView(View, Recycler) + */ + public void recycleView(View view) { + // This public recycle method tries to make view recycle-able since layout manager + // intended to recycle this view (e.g. even if it is in scrap or change cache) + ViewHolder holder = getChildViewHolderInt(view); + if (holder.isTmpDetached()) { + removeDetachedView(view, false); + } + if (holder.isScrap()) { + holder.unScrap(); + } else if (holder.wasReturnedFromScrap()){ + holder.clearReturnedFromScrapFlag(); + } + recycleViewHolderInternal(holder); + } + + /** + * Internally, use this method instead of {@link #recycleView(android.view.View)} to + * catch potential bugs. + * @param view + */ + void recycleViewInternal(View view) { + recycleViewHolderInternal(getChildViewHolderInt(view)); + } + + void recycleAndClearCachedViews() { + final int count = mCachedViews.size(); + for (int i = count - 1; i >= 0; i--) { + recycleCachedViewAt(i); + } + mCachedViews.clear(); + } + + /** + * Recycles a cached view and removes the view from the list. Views are added to cache + * if and only if they are recyclable, so this method does not check it again. + *

+ * A small exception to this rule is when the view does not have an animator reference + * but transient state is true (due to animations created outside ItemAnimator). In that + * case, adapter may choose to recycle it. From RecyclerView's perspective, the view is + * still recyclable since Adapter wants to do so. + * + * @param cachedViewIndex The index of the view in cached views list + */ + void recycleCachedViewAt(int cachedViewIndex) { + if (DEBUG) { + Log.d(TAG, "Recycling cached view at index " + cachedViewIndex); + } + ViewHolder viewHolder = mCachedViews.get(cachedViewIndex); + if (DEBUG) { + Log.d(TAG, "CachedViewHolder to be recycled: " + viewHolder); + } + addViewHolderToRecycledViewPool(viewHolder); + mCachedViews.remove(cachedViewIndex); + } + + /** + * internal implementation checks if view is scrapped or attached and throws an exception + * if so. + * Public version un-scraps before calling recycle. + */ + void recycleViewHolderInternal(ViewHolder holder) { + if (holder.isScrap() || holder.itemView.getParent() != null) { + throw new IllegalArgumentException( + "Scrapped or attached views may not be recycled. isScrap:" + + holder.isScrap() + " isAttached:" + + (holder.itemView.getParent() != null)); + } + + if (holder.isTmpDetached()) { + throw new IllegalArgumentException("Tmp detached view should be removed " + + "from RecyclerView before it can be recycled: " + holder); + } + + if (holder.shouldIgnore()) { + throw new IllegalArgumentException("Trying to recycle an ignored view holder. You" + + " should first call stopIgnoringView(view) before calling recycle."); + } + //noinspection unchecked + final boolean transientStatePreventsRecycling = holder + .doesTransientStatePreventRecycling(); + final boolean forceRecycle = mAdapter != null + && transientStatePreventsRecycling + && mAdapter.onFailedToRecycleView(holder); + boolean cached = false; + boolean recycled = false; + if (forceRecycle || holder.isRecyclable()) { + if (!holder.isInvalid() && !holder.isRemoved() && !holder.isChanged()) { + // Retire oldest cached view + final int cachedViewSize = mCachedViews.size(); + if (cachedViewSize == mViewCacheMax && cachedViewSize > 0) { + recycleCachedViewAt(0); + } + if (cachedViewSize < mViewCacheMax) { + mCachedViews.add(holder); + cached = true; + } + } + if (!cached) { + addViewHolderToRecycledViewPool(holder); + recycled = true; + } + } else if (DEBUG) { + Log.d(TAG, "trying to recycle a non-recycleable holder. Hopefully, it will " + + "re-visit here. We are still removing it from animation lists"); + } + // even if the holder is not removed, we still call this method so that it is removed + // from view holder lists. + mState.onViewRecycled(holder); + if (!cached && !recycled && transientStatePreventsRecycling) { + holder.mOwnerRecyclerView = null; + } + } + + void addViewHolderToRecycledViewPool(ViewHolder holder) { + ViewCompat.setAccessibilityDelegate(holder.itemView, null); + dispatchViewRecycled(holder); + holder.mOwnerRecyclerView = null; + getRecycledViewPool().putRecycledView(holder); + } + + /** + * Used as a fast path for unscrapping and recycling a view during a bulk operation. + * The caller must call {@link #clearScrap()} when it's done to update the recycler's + * internal bookkeeping. + */ + void quickRecycleScrapView(View view) { + final ViewHolder holder = getChildViewHolderInt(view); + holder.mScrapContainer = null; + holder.clearReturnedFromScrapFlag(); + recycleViewHolderInternal(holder); + } + + /** + * Mark an attached view as scrap. + * + *

"Scrap" views are still attached to their parent RecyclerView but are eligible + * for rebinding and reuse. Requests for a view for a given position may return a + * reused or rebound scrap view instance.

+ * + * @param view View to scrap + */ + void scrapView(View view) { + final ViewHolder holder = getChildViewHolderInt(view); + holder.setScrapContainer(this); + if (!holder.isChanged() || !supportsChangeAnimations()) { + if (holder.isInvalid() && !holder.isRemoved() && !mAdapter.hasStableIds()) { + throw new IllegalArgumentException("Called scrap view with an invalid view." + + " Invalid views cannot be reused from scrap, they should rebound from" + + " recycler pool."); + } + mAttachedScrap.add(holder); + } else { + if (mChangedScrap == null) { + mChangedScrap = new ArrayList(); + } + mChangedScrap.add(holder); + } + } + + /** + * Remove a previously scrapped view from the pool of eligible scrap. + * + *

This view will no longer be eligible for reuse until re-scrapped or + * until it is explicitly removed and recycled.

+ */ + void unscrapView(ViewHolder holder) { + if (!holder.isChanged() || !supportsChangeAnimations() || mChangedScrap == null) { + mAttachedScrap.remove(holder); + } else { + mChangedScrap.remove(holder); + } + holder.mScrapContainer = null; + holder.clearReturnedFromScrapFlag(); + } + + int getScrapCount() { + return mAttachedScrap.size(); + } + + View getScrapViewAt(int index) { + return mAttachedScrap.get(index).itemView; + } + + void clearScrap() { + mAttachedScrap.clear(); + } + + ViewHolder getChangedScrapViewForPosition(int position) { + // If pre-layout, check the changed scrap for an exact match. + final int changedScrapSize; + if (mChangedScrap == null || (changedScrapSize = mChangedScrap.size()) == 0) { + return null; + } + // find by position + for (int i = 0; i < changedScrapSize; i++) { + final ViewHolder holder = mChangedScrap.get(i); + if (!holder.wasReturnedFromScrap() && holder.getLayoutPosition() == position) { + holder.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP); + return holder; + } + } + // find by id + if (mAdapter.hasStableIds()) { + final int offsetPosition = mAdapterHelper.findPositionOffset(position); + if (offsetPosition > 0 && offsetPosition < mAdapter.getItemCount()) { + final long id = mAdapter.getItemId(offsetPosition); + for (int i = 0; i < changedScrapSize; i++) { + final ViewHolder holder = mChangedScrap.get(i); + if (!holder.wasReturnedFromScrap() && holder.getItemId() == id) { + holder.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP); + return holder; + } + } + } + } + return null; + } + + /** + * Returns a scrap view for the position. If type is not INVALID_TYPE, it also checks if + * ViewHolder's type matches the provided type. + * + * @param position Item position + * @param type View type + * @param dryRun Does a dry run, finds the ViewHolder but does not remove + * @return a ViewHolder that can be re-used for this position. + */ + ViewHolder getScrapViewForPosition(int position, int type, boolean dryRun) { + final int scrapCount = mAttachedScrap.size(); + + // Try first for an exact, non-invalid match from scrap. + for (int i = 0; i < scrapCount; i++) { + final ViewHolder holder = mAttachedScrap.get(i); + if (!holder.wasReturnedFromScrap() && holder.getLayoutPosition() == position + && !holder.isInvalid() && (mState.mInPreLayout || !holder.isRemoved())) { + if (type != INVALID_TYPE && holder.getItemViewType() != type) { + Log.e(TAG, "Scrap view for position " + position + " isn't dirty but has" + + " wrong view type! (found " + holder.getItemViewType() + + " but expected " + type + ")"); + break; + } + holder.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP); + return holder; + } + } + + if (!dryRun) { + View view = mChildHelper.findHiddenNonRemovedView(position, type); + if (view != null) { + // ending the animation should cause it to get recycled before we reuse it + mItemAnimator.endAnimation(getChildViewHolder(view)); + } + } + + // Search in our first-level recycled view cache. + final int cacheSize = mCachedViews.size(); + for (int i = 0; i < cacheSize; i++) { + final ViewHolder holder = mCachedViews.get(i); + // invalid view holders may be in cache if adapter has stable ids as they can be + // retrieved via getScrapViewForId + if (!holder.isInvalid() && holder.getLayoutPosition() == position) { + if (!dryRun) { + mCachedViews.remove(i); + } + if (DEBUG) { + Log.d(TAG, "getScrapViewForPosition(" + position + ", " + type + + ") found match in cache: " + holder); + } + return holder; + } + } + return null; + } + + ViewHolder getScrapViewForId(long id, int type, boolean dryRun) { + // Look in our attached views first + final int count = mAttachedScrap.size(); + for (int i = count - 1; i >= 0; i--) { + final ViewHolder holder = mAttachedScrap.get(i); + if (holder.getItemId() == id && !holder.wasReturnedFromScrap()) { + if (type == holder.getItemViewType()) { + holder.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP); + if (holder.isRemoved()) { + // this might be valid in two cases: + // > item is removed but we are in pre-layout pass + // >> do nothing. return as is. make sure we don't rebind + // > item is removed then added to another position and we are in + // post layout. + // >> remove removed and invalid flags, add update flag to rebind + // because item was invisible to us and we don't know what happened in + // between. + if (!mState.isPreLayout()) { + holder.setFlags(ViewHolder.FLAG_UPDATE, ViewHolder.FLAG_UPDATE | + ViewHolder.FLAG_INVALID | ViewHolder.FLAG_REMOVED); + } + } + return holder; + } else if (!dryRun) { + // Recycle this scrap. Type mismatch. + mAttachedScrap.remove(i); + removeDetachedView(holder.itemView, false); + quickRecycleScrapView(holder.itemView); + } + } + } + + // Search the first-level cache + final int cacheSize = mCachedViews.size(); + for (int i = cacheSize - 1; i >= 0; i--) { + final ViewHolder holder = mCachedViews.get(i); + if (holder.getItemId() == id) { + if (type == holder.getItemViewType()) { + if (!dryRun) { + mCachedViews.remove(i); + } + return holder; + } else if (!dryRun) { + recycleCachedViewAt(i); + } + } + } + return null; + } + + void dispatchViewRecycled(ViewHolder holder) { + if (mRecyclerListener != null) { + mRecyclerListener.onViewRecycled(holder); + } + if (mAdapter != null) { + mAdapter.onViewRecycled(holder); + } + if (mState != null) { + mState.onViewRecycled(holder); + } + if (DEBUG) Log.d(TAG, "dispatchViewRecycled: " + holder); + } + + void onAdapterChanged(Adapter oldAdapter, Adapter newAdapter, + boolean compatibleWithPrevious) { + clear(); + getRecycledViewPool().onAdapterChanged(oldAdapter, newAdapter, compatibleWithPrevious); + } + + void offsetPositionRecordsForMove(int from, int to) { + final int start, end, inBetweenOffset; + if (from < to) { + start = from; + end = to; + inBetweenOffset = -1; + } else { + start = to; + end = from; + inBetweenOffset = 1; + } + final int cachedCount = mCachedViews.size(); + for (int i = 0; i < cachedCount; i++) { + final ViewHolder holder = mCachedViews.get(i); + if (holder == null || holder.mPosition < start || holder.mPosition > end) { + continue; + } + if (holder.mPosition == from) { + holder.offsetPosition(to - from, false); + } else { + holder.offsetPosition(inBetweenOffset, false); + } + if (DEBUG) { + Log.d(TAG, "offsetPositionRecordsForMove cached child " + i + " holder " + + holder); + } + } + } + + void offsetPositionRecordsForInsert(int insertedAt, int count) { + final int cachedCount = mCachedViews.size(); + for (int i = 0; i < cachedCount; i++) { + final ViewHolder holder = mCachedViews.get(i); + if (holder != null && holder.getLayoutPosition() >= insertedAt) { + if (DEBUG) { + Log.d(TAG, "offsetPositionRecordsForInsert cached " + i + " holder " + + holder + " now at position " + (holder.mPosition + count)); + } + holder.offsetPosition(count, true); + } + } + } + + /** + * @param removedFrom Remove start index + * @param count Remove count + * @param applyToPreLayout If true, changes will affect ViewHolder's pre-layout position, if + * false, they'll be applied before the second layout pass + */ + void offsetPositionRecordsForRemove(int removedFrom, int count, boolean applyToPreLayout) { + final int removedEnd = removedFrom + count; + final int cachedCount = mCachedViews.size(); + for (int i = cachedCount - 1; i >= 0; i--) { + final ViewHolder holder = mCachedViews.get(i); + if (holder != null) { + if (holder.getLayoutPosition() >= removedEnd) { + if (DEBUG) { + Log.d(TAG, "offsetPositionRecordsForRemove cached " + i + + " holder " + holder + " now at position " + + (holder.mPosition - count)); + } + holder.offsetPosition(-count, applyToPreLayout); + } else if (holder.getLayoutPosition() >= removedFrom) { + // Item for this view was removed. Dump it from the cache. + holder.addFlags(ViewHolder.FLAG_REMOVED); + recycleCachedViewAt(i); + } + } + } + } + + void setViewCacheExtension(ViewCacheExtension extension) { + mViewCacheExtension = extension; + } + + void setRecycledViewPool(RecycledViewPool pool) { + if (mRecyclerPool != null) { + mRecyclerPool.detach(); + } + mRecyclerPool = pool; + if (pool != null) { + mRecyclerPool.attach(getAdapter()); + } + } + + RecycledViewPool getRecycledViewPool() { + if (mRecyclerPool == null) { + mRecyclerPool = new RecycledViewPool(); + } + return mRecyclerPool; + } + + void viewRangeUpdate(int positionStart, int itemCount) { + final int positionEnd = positionStart + itemCount; + final int cachedCount = mCachedViews.size(); + for (int i = 0; i < cachedCount; i++) { + final ViewHolder holder = mCachedViews.get(i); + if (holder == null) { + continue; + } + + final int pos = holder.getLayoutPosition(); + if (pos >= positionStart && pos < positionEnd) { + holder.addFlags(ViewHolder.FLAG_UPDATE); + // cached views should not be flagged as changed because this will cause them + // to animate when they are returned from cache. + } + } + } + + void setAdapterPositionsAsUnknown() { + final int cachedCount = mCachedViews.size(); + for (int i = 0; i < cachedCount; i++) { + final ViewHolder holder = mCachedViews.get(i); + if (holder != null) { + holder.addFlags(ViewHolder.FLAG_ADAPTER_POSITION_UNKNOWN); + } + } + } + + void markKnownViewsInvalid() { + if (mAdapter != null && mAdapter.hasStableIds()) { + final int cachedCount = mCachedViews.size(); + for (int i = 0; i < cachedCount; i++) { + final ViewHolder holder = mCachedViews.get(i); + if (holder != null) { + holder.addFlags(ViewHolder.FLAG_UPDATE | ViewHolder.FLAG_INVALID); + } + } + } else { + // we cannot re-use cached views in this case. Recycle them all + recycleAndClearCachedViews(); + } + } + + void clearOldPositions() { + final int cachedCount = mCachedViews.size(); + for (int i = 0; i < cachedCount; i++) { + final ViewHolder holder = mCachedViews.get(i); + holder.clearOldPosition(); + } + final int scrapCount = mAttachedScrap.size(); + for (int i = 0; i < scrapCount; i++) { + mAttachedScrap.get(i).clearOldPosition(); + } + if (mChangedScrap != null) { + final int changedScrapCount = mChangedScrap.size(); + for (int i = 0; i < changedScrapCount; i++) { + mChangedScrap.get(i).clearOldPosition(); + } + } + } + + void markItemDecorInsetsDirty() { + final int cachedCount = mCachedViews.size(); + for (int i = 0; i < cachedCount; i++) { + final ViewHolder holder = mCachedViews.get(i); + LayoutParams layoutParams = (LayoutParams) holder.itemView.getLayoutParams(); + if (layoutParams != null) { + layoutParams.mInsetsDirty = true; + } + } + } + } + + /** + * ViewCacheExtension is a helper class to provide an additional layer of view caching that can + * ben controlled by the developer. + *

+ * When {@link Recycler#getViewForPosition(int)} is called, Recycler checks attached scrap and + * first level cache to find a matching View. If it cannot find a suitable View, Recycler will + * call the {@link #getViewForPositionAndType(Recycler, int, int)} before checking + * {@link RecycledViewPool}. + *

+ * Note that, Recycler never sends Views to this method to be cached. It is developers + * responsibility to decide whether they want to keep their Views in this custom cache or let + * the default recycling policy handle it. + */ + public abstract static class ViewCacheExtension { + + /** + * Returns a View that can be binded to the given Adapter position. + *

+ * This method should not create a new View. Instead, it is expected to return + * an already created View that can be re-used for the given type and position. + * If the View is marked as ignored, it should first call + * {@link LayoutManager#stopIgnoringView(View)} before returning the View. + *

+ * RecyclerView will re-bind the returned View to the position if necessary. + * + * @param recycler The Recycler that can be used to bind the View + * @param position The adapter position + * @param type The type of the View, defined by adapter + * @return A View that is bound to the given position or NULL if there is no View to re-use + * @see LayoutManager#ignoreView(View) + */ + abstract public View getViewForPositionAndType(Recycler recycler, int position, int type); + } + + /** + * Base class for an Adapter + * + *

Adapters provide a binding from an app-specific data set to views that are displayed + * within a {@link RecyclerView}.

+ */ + public static abstract class Adapter { + private final AdapterDataObservable mObservable = new AdapterDataObservable(); + private boolean mHasStableIds = false; + + /** + * Called when RecyclerView needs a new {@link ViewHolder} of the given type to represent + * an item. + *

+ * This new ViewHolder should be constructed with a new View that can represent the items + * of the given type. You can either create a new View manually or inflate it from an XML + * layout file. + *

+ * The new ViewHolder will be used to display items of the adapter using + * {@link #onBindViewHolder(ViewHolder, int)}. Since it will be re-used to display different + * items in the data set, it is a good idea to cache references to sub views of the View to + * avoid unnecessary {@link View#findViewById(int)} calls. + * + * @param parent The ViewGroup into which the new View will be added after it is bound to + * an adapter position. + * @param viewType The view type of the new View. + * + * @return A new ViewHolder that holds a View of the given view type. + * @see #getItemViewType(int) + * @see #onBindViewHolder(ViewHolder, int) + */ + public abstract VH onCreateViewHolder(ViewGroup parent, int viewType); + + /** + * Called by RecyclerView to display the data at the specified position. This method + * should update the contents of the {@link ViewHolder#itemView} to reflect the item at + * the given position. + *

+ * Note that unlike {@link android.widget.ListView}, RecyclerView will not call this + * method again if the position of the item changes in the data set unless the item itself + * is invalidated or the new position cannot be determined. For this reason, you should only + * use the position parameter while acquiring the related data item inside this + * method and should not keep a copy of it. If you need the position of an item later on + * (e.g. in a click listener), use {@link ViewHolder#getAdapterPosition()} which will have + * the updated adapter position. + * + * @param holder The ViewHolder which should be updated to represent the contents of the + * item at the given position in the data set. + * @param position The position of the item within the adapter's data set. + */ + public abstract void onBindViewHolder(VH holder, int position); + + /** + * This method calls {@link #onCreateViewHolder(ViewGroup, int)} to create a new + * {@link ViewHolder} and initializes some private fields to be used by RecyclerView. + * + * @see #onCreateViewHolder(ViewGroup, int) + */ + public final VH createViewHolder(ViewGroup parent, int viewType) { + final VH holder = onCreateViewHolder(parent, viewType); + holder.mItemViewType = viewType; + return holder; + } + + /** + * This method internally calls {@link #onBindViewHolder(ViewHolder, int)} to update the + * {@link ViewHolder} contents with the item at the given position and also sets up some + * private fields to be used by RecyclerView. + * + * @see #onBindViewHolder(ViewHolder, int) + */ + public final void bindViewHolder(VH holder, int position) { + holder.mPosition = position; + if (hasStableIds()) { + holder.mItemId = getItemId(position); + } + holder.setFlags(ViewHolder.FLAG_BOUND, + ViewHolder.FLAG_BOUND | ViewHolder.FLAG_UPDATE | ViewHolder.FLAG_INVALID + | ViewHolder.FLAG_ADAPTER_POSITION_UNKNOWN); + onBindViewHolder(holder, position); + } + + /** + * Return the view type of the item at position for the purposes + * of view recycling. + * + *

The default implementation of this method returns 0, making the assumption of + * a single view type for the adapter. Unlike ListView adapters, types need not + * be contiguous. Consider using id resources to uniquely identify item view types. + * + * @param position position to query + * @return integer value identifying the type of the view needed to represent the item at + * position. Type codes need not be contiguous. + */ + public int getItemViewType(int position) { + return 0; + } + + /** + * Indicates whether each item in the data set can be represented with a unique identifier + * of type {@link java.lang.Long}. + * + * @param hasStableIds Whether items in data set have unique identifiers or not. + * @see #hasStableIds() + * @see #getItemId(int) + */ + public void setHasStableIds(boolean hasStableIds) { + if (hasObservers()) { + throw new IllegalStateException("Cannot change whether this adapter has " + + "stable IDs while the adapter has registered observers."); + } + mHasStableIds = hasStableIds; + } + + /** + * Return the stable ID for the item at position. If {@link #hasStableIds()} + * would return false this method should return {@link #NO_ID}. The default implementation + * of this method returns {@link #NO_ID}. + * + * @param position Adapter position to query + * @return the stable ID of the item at position + */ + public long getItemId(int position) { + return NO_ID; + } + + /** + * Returns the total number of items in the data set hold by the adapter. + * + * @return The total number of items in this adapter. + */ + public abstract int getItemCount(); + + /** + * Returns true if this adapter publishes a unique long value that can + * act as a key for the item at a given position in the data set. If that item is relocated + * in the data set, the ID returned for that item should be the same. + * + * @return true if this adapter's items have stable IDs + */ + public final boolean hasStableIds() { + return mHasStableIds; + } + + /** + * Called when a view created by this adapter has been recycled. + * + *

A view is recycled when a {@link LayoutManager} decides that it no longer + * needs to be attached to its parent {@link RecyclerView}. This can be because it has + * fallen out of visibility or a set of cached views represented by views still + * attached to the parent RecyclerView. If an item view has large or expensive data + * bound to it such as large bitmaps, this may be a good place to release those + * resources.

+ *

+ * RecyclerView calls this method right before clearing ViewHolder's internal data and + * sending it to RecycledViewPool. This way, if ViewHolder was holding valid information + * before being recycled, you can call {@link ViewHolder#getAdapterPosition()} to get + * its adapter position. + * + * @param holder The ViewHolder for the view being recycled + */ + public void onViewRecycled(VH holder) { + } + + /** + * Called by the RecyclerView if a ViewHolder created by this Adapter cannot be recycled + * due to its transient state. Upon receiving this callback, Adapter can clear the + * animation(s) that effect the View's transient state and return true so that + * the View can be recycled. Keep in mind that the View in question is already removed from + * the RecyclerView. + *

+ * In some cases, it is acceptable to recycle a View although it has transient state. Most + * of the time, this is a case where the transient state will be cleared in + * {@link #onBindViewHolder(ViewHolder, int)} call when View is rebound to a new position. + * For this reason, RecyclerView leaves the decision to the Adapter and uses the return + * value of this method to decide whether the View should be recycled or not. + *

+ * Note that when all animations are created by {@link RecyclerView.ItemAnimator}, you + * should never receive this callback because RecyclerView keeps those Views as children + * until their animations are complete. This callback is useful when children of the item + * views create animations which may not be easy to implement using an {@link ItemAnimator}. + *

+ * You should never fix this issue by calling + * holder.itemView.setHasTransientState(false); unless you've previously called + * holder.itemView.setHasTransientState(true);. Each + * View.setHasTransientState(true) call must be matched by a + * View.setHasTransientState(false) call, otherwise, the state of the View + * may become inconsistent. You should always prefer to end or cancel animations that are + * triggering the transient state instead of handling it manually. + * + * @param holder The ViewHolder containing the View that could not be recycled due to its + * transient state. + * @return True if the View should be recycled, false otherwise. Note that if this method + * returns true, RecyclerView will ignore the transient state of + * the View and recycle it regardless. If this method returns false, + * RecyclerView will check the View's transient state again before giving a final decision. + * Default implementation returns false. + */ + public boolean onFailedToRecycleView(VH holder) { + return false; + } + + /** + * Called when a view created by this adapter has been attached to a window. + * + *

This can be used as a reasonable signal that the view is about to be seen + * by the user. If the adapter previously freed any resources in + * {@link #onViewDetachedFromWindow(RecyclerView.ViewHolder) onViewDetachedFromWindow} + * those resources should be restored here.

+ * + * @param holder Holder of the view being attached + */ + public void onViewAttachedToWindow(VH holder) { + } + + /** + * Called when a view created by this adapter has been detached from its window. + * + *

Becoming detached from the window is not necessarily a permanent condition; + * the consumer of an Adapter's views may choose to cache views offscreen while they + * are not visible, attaching an detaching them as appropriate.

+ * + * @param holder Holder of the view being detached + */ + public void onViewDetachedFromWindow(VH holder) { + } + + /** + * Returns true if one or more observers are attached to this adapter. + * + * @return true if this adapter has observers + */ + public final boolean hasObservers() { + return mObservable.hasObservers(); + } + + /** + * Register a new observer to listen for data changes. + * + *

The adapter may publish a variety of events describing specific changes. + * Not all adapters may support all change types and some may fall back to a generic + * {@link android.support.v7.widget.RecyclerView.AdapterDataObserver#onChanged() + * "something changed"} event if more specific data is not available.

+ * + *

Components registering observers with an adapter are responsible for + * {@link #unregisterAdapterDataObserver(RecyclerView.AdapterDataObserver) + * unregistering} those observers when finished.

+ * + * @param observer Observer to register + * + * @see #unregisterAdapterDataObserver(RecyclerView.AdapterDataObserver) + */ + public void registerAdapterDataObserver(AdapterDataObserver observer) { + mObservable.registerObserver(observer); + } + + /** + * Unregister an observer currently listening for data changes. + * + *

The unregistered observer will no longer receive events about changes + * to the adapter.

+ * + * @param observer Observer to unregister + * + * @see #registerAdapterDataObserver(RecyclerView.AdapterDataObserver) + */ + public void unregisterAdapterDataObserver(AdapterDataObserver observer) { + mObservable.unregisterObserver(observer); + } + + /** + * Called by RecyclerView when it starts observing this Adapter. + *

+ * Keep in mind that same adapter may be observed by multiple RecyclerViews. + * + * @param recyclerView The RecyclerView instance which started observing this adapter. + * @see #onDetachedFromRecyclerView(RecyclerView) + */ + public void onAttachedToRecyclerView(RecyclerView recyclerView) { + } + + /** + * Called by RecyclerView when it stops observing this Adapter. + * + * @param recyclerView The RecyclerView instance which stopped observing this adapter. + * @see #onAttachedToRecyclerView(RecyclerView) + */ + public void onDetachedFromRecyclerView(RecyclerView recyclerView) { + } + + /** + * Notify any registered observers that the data set has changed. + * + *

There are two different classes of data change events, item changes and structural + * changes. Item changes are when a single item has its data updated but no positional + * changes have occurred. Structural changes are when items are inserted, removed or moved + * within the data set.

+ * + *

This event does not specify what about the data set has changed, forcing + * any observers to assume that all existing items and structure may no longer be valid. + * LayoutManagers will be forced to fully rebind and relayout all visible views.

+ * + *

RecyclerView will attempt to synthesize visible structural change events + * for adapters that report that they have {@link #hasStableIds() stable IDs} when + * this method is used. This can help for the purposes of animation and visual + * object persistence but individual item views will still need to be rebound + * and relaid out.

+ * + *

If you are writing an adapter it will always be more efficient to use the more + * specific change events if you can. Rely on notifyDataSetChanged() + * as a last resort.

+ * + * @see #notifyItemChanged(int) + * @see #notifyItemInserted(int) + * @see #notifyItemRemoved(int) + * @see #notifyItemRangeChanged(int, int) + * @see #notifyItemRangeInserted(int, int) + * @see #notifyItemRangeRemoved(int, int) + */ + public final void notifyDataSetChanged() { + mObservable.notifyChanged(); + } + + /** + * Notify any registered observers that the item at position has changed. + * + *

This is an item change event, not a structural change event. It indicates that any + * reflection of the data at position is out of date and should be updated. + * The item at position retains the same identity.

+ * + * @param position Position of the item that has changed + * + * @see #notifyItemRangeChanged(int, int) + */ + public final void notifyItemChanged(int position) { + mObservable.notifyItemRangeChanged(position, 1); + } + + /** + * Notify any registered observers that the itemCount items starting at + * position positionStart have changed. + * + *

This is an item change event, not a structural change event. It indicates that + * any reflection of the data in the given position range is out of date and should + * be updated. The items in the given range retain the same identity.

+ * + * @param positionStart Position of the first item that has changed + * @param itemCount Number of items that have changed + * + * @see #notifyItemChanged(int) + */ + public final void notifyItemRangeChanged(int positionStart, int itemCount) { + mObservable.notifyItemRangeChanged(positionStart, itemCount); + } + + /** + * Notify any registered observers that the item reflected at position + * has been newly inserted. The item previously at position is now at + * position position + 1. + * + *

This is a structural change event. Representations of other existing items in the + * data set are still considered up to date and will not be rebound, though their + * positions may be altered.

+ * + * @param position Position of the newly inserted item in the data set + * + * @see #notifyItemRangeInserted(int, int) + */ + public final void notifyItemInserted(int position) { + mObservable.notifyItemRangeInserted(position, 1); + } + + /** + * Notify any registered observers that the item reflected at fromPosition + * has been moved to toPosition. + * + *

This is a structural change event. Representations of other existing items in the + * data set are still considered up to date and will not be rebound, though their + * positions may be altered.

+ * + * @param fromPosition Previous position of the item. + * @param toPosition New position of the item. + */ + public final void notifyItemMoved(int fromPosition, int toPosition) { + mObservable.notifyItemMoved(fromPosition, toPosition); + } + + /** + * Notify any registered observers that the currently reflected itemCount + * items starting at positionStart have been newly inserted. The items + * previously located at positionStart and beyond can now be found starting + * at position positionStart + itemCount. + * + *

This is a structural change event. Representations of other existing items in the + * data set are still considered up to date and will not be rebound, though their positions + * may be altered.

+ * + * @param positionStart Position of the first item that was inserted + * @param itemCount Number of items inserted + * + * @see #notifyItemInserted(int) + */ + public final void notifyItemRangeInserted(int positionStart, int itemCount) { + mObservable.notifyItemRangeInserted(positionStart, itemCount); + } + + /** + * Notify any registered observers that the item previously located at position + * has been removed from the data set. The items previously located at and after + * position may now be found at oldPosition - 1. + * + *

This is a structural change event. Representations of other existing items in the + * data set are still considered up to date and will not be rebound, though their positions + * may be altered.

+ * + * @param position Position of the item that has now been removed + * + * @see #notifyItemRangeRemoved(int, int) + */ + public final void notifyItemRemoved(int position) { + mObservable.notifyItemRangeRemoved(position, 1); + } + + /** + * Notify any registered observers that the itemCount items previously + * located at positionStart have been removed from the data set. The items + * previously located at and after positionStart + itemCount may now be found + * at oldPosition - itemCount. + * + *

This is a structural change event. Representations of other existing items in the data + * set are still considered up to date and will not be rebound, though their positions + * may be altered.

+ * + * @param positionStart Previous position of the first item that was removed + * @param itemCount Number of items removed from the data set + */ + public final void notifyItemRangeRemoved(int positionStart, int itemCount) { + mObservable.notifyItemRangeRemoved(positionStart, itemCount); + } + } + + private void dispatchChildDetached(View child) { + if (mAdapter != null) { + mAdapter.onViewDetachedFromWindow(getChildViewHolderInt(child)); + } + onChildDetachedFromWindow(child); + } + + private void dispatchChildAttached(View child) { + if (mAdapter != null) { + mAdapter.onViewAttachedToWindow(getChildViewHolderInt(child)); + } + onChildAttachedToWindow(child); + } + + /** + * A LayoutManager is responsible for measuring and positioning item views + * within a RecyclerView as well as determining the policy for when to recycle + * item views that are no longer visible to the user. By changing the LayoutManager + * a RecyclerView can be used to implement a standard vertically scrolling list, + * a uniform grid, staggered grids, horizontally scrolling collections and more. Several stock + * layout managers are provided for general use. + */ + public static abstract class LayoutManager { + ChildHelper mChildHelper; + RecyclerView mRecyclerView; + + @Nullable + SmoothScroller mSmoothScroller; + + private boolean mRequestedSimpleAnimations = false; + + private boolean mIsAttachedToWindow = false; + + void setRecyclerView(RecyclerView recyclerView) { + if (recyclerView == null) { + mRecyclerView = null; + mChildHelper = null; + } else { + mRecyclerView = recyclerView; + mChildHelper = recyclerView.mChildHelper; + } + + } + + /** + * Calls {@code RecyclerView#requestLayout} on the underlying RecyclerView + */ + public void requestLayout() { + if(mRecyclerView != null) { + mRecyclerView.requestLayout(); + } + } + + /** + * Checks if RecyclerView is in the middle of a layout or scroll and throws an + * {@link IllegalStateException} if it is not. + * + * @param message The message for the exception. Can be null. + * @see #assertNotInLayoutOrScroll(String) + */ + public void assertInLayoutOrScroll(String message) { + if (mRecyclerView != null) { + mRecyclerView.assertInLayoutOrScroll(message); + } + } + + /** + * Checks if RecyclerView is in the middle of a layout or scroll and throws an + * {@link IllegalStateException} if it is. + * + * @param message The message for the exception. Can be null. + * @see #assertInLayoutOrScroll(String) + */ + public void assertNotInLayoutOrScroll(String message) { + if (mRecyclerView != null) { + mRecyclerView.assertNotInLayoutOrScroll(message); + } + } + + /** + * Returns whether this LayoutManager supports automatic item animations. + * A LayoutManager wishing to support item animations should obey certain + * rules as outlined in {@link #onLayoutChildren(Recycler, State)}. + * The default return value is false, so subclasses of LayoutManager + * will not get predictive item animations by default. + * + *

Whether item animations are enabled in a RecyclerView is determined both + * by the return value from this method and the + * {@link RecyclerView#setItemAnimator(ItemAnimator) ItemAnimator} set on the + * RecyclerView itself. If the RecyclerView has a non-null ItemAnimator but this + * method returns false, then simple item animations will be enabled, in which + * views that are moving onto or off of the screen are simply faded in/out. If + * the RecyclerView has a non-null ItemAnimator and this method returns true, + * then there will be two calls to {@link #onLayoutChildren(Recycler, State)} to + * setup up the information needed to more intelligently predict where appearing + * and disappearing views should be animated from/to.

+ * + * @return true if predictive item animations should be enabled, false otherwise + */ + public boolean supportsPredictiveItemAnimations() { + return false; + } + + void dispatchAttachedToWindow(RecyclerView view) { + mIsAttachedToWindow = true; + onAttachedToWindow(view); + } + + void dispatchDetachedFromWindow(RecyclerView view, Recycler recycler) { + mIsAttachedToWindow = false; + onDetachedFromWindow(view, recycler); + } + + /** + * Returns whether LayoutManager is currently attached to a RecyclerView which is attached + * to a window. + * + * @return True if this LayoutManager is controlling a RecyclerView and the RecyclerView + * is attached to window. + */ + public boolean isAttachedToWindow() { + return mIsAttachedToWindow; + } + + /** + * Causes the Runnable to execute on the next animation time step. + * The runnable will be run on the user interface thread. + *

+ * Calling this method when LayoutManager is not attached to a RecyclerView has no effect. + * + * @param action The Runnable that will be executed. + * + * @see #removeCallbacks + */ + public void postOnAnimation(Runnable action) { + if (mRecyclerView != null) { + ViewCompat.postOnAnimation(mRecyclerView, action); + } + } + + /** + * Removes the specified Runnable from the message queue. + *

+ * Calling this method when LayoutManager is not attached to a RecyclerView has no effect. + * + * @param action The Runnable to remove from the message handling queue + * + * @return true if RecyclerView could ask the Handler to remove the Runnable, + * false otherwise. When the returned value is true, the Runnable + * may or may not have been actually removed from the message queue + * (for instance, if the Runnable was not in the queue already.) + * + * @see #postOnAnimation + */ + public boolean removeCallbacks(Runnable action) { + if (mRecyclerView != null) { + return mRecyclerView.removeCallbacks(action); + } + return false; + } + /** + * Called when this LayoutManager is both attached to a RecyclerView and that RecyclerView + * is attached to a window. + * + *

Subclass implementations should always call through to the superclass implementation. + *

+ * + * @param view The RecyclerView this LayoutManager is bound to + */ + public void onAttachedToWindow(RecyclerView view) { + } + + /** + * @deprecated + * override {@link #onDetachedFromWindow(RecyclerView, Recycler)} + */ + @Deprecated + public void onDetachedFromWindow(RecyclerView view) { + + } + + /** + * Called when this LayoutManager is detached from its parent RecyclerView or when + * its parent RecyclerView is detached from its window. + * + *

Subclass implementations should always call through to the superclass implementation. + *

+ * + * @param view The RecyclerView this LayoutManager is bound to + * @param recycler The recycler to use if you prefer to recycle your children instead of + * keeping them around. + */ + public void onDetachedFromWindow(RecyclerView view, Recycler recycler) { + onDetachedFromWindow(view); + } + + /** + * Check if the RecyclerView is configured to clip child views to its padding. + * + * @return true if this RecyclerView clips children to its padding, false otherwise + */ + public boolean getClipToPadding() { + return mRecyclerView != null && mRecyclerView.mClipToPadding; + } + + /** + * Lay out all relevant child views from the given adapter. + * + * The LayoutManager is in charge of the behavior of item animations. By default, + * RecyclerView has a non-null {@link #getItemAnimator() ItemAnimator}, and simple + * item animations are enabled. This means that add/remove operations on the + * adapter will result in animations to add new or appearing items, removed or + * disappearing items, and moved items. If a LayoutManager returns false from + * {@link #supportsPredictiveItemAnimations()}, which is the default, and runs a + * normal layout operation during {@link #onLayoutChildren(Recycler, State)}, the + * RecyclerView will have enough information to run those animations in a simple + * way. For example, the default ItemAnimator, {@link DefaultItemAnimator}, will + * simple fade views in and out, whether they are actuall added/removed or whether + * they are moved on or off the screen due to other add/remove operations. + * + *

A LayoutManager wanting a better item animation experience, where items can be + * animated onto and off of the screen according to where the items exist when they + * are not on screen, then the LayoutManager should return true from + * {@link #supportsPredictiveItemAnimations()} and add additional logic to + * {@link #onLayoutChildren(Recycler, State)}. Supporting predictive animations + * means that {@link #onLayoutChildren(Recycler, State)} will be called twice; + * once as a "pre" layout step to determine where items would have been prior to + * a real layout, and again to do the "real" layout. In the pre-layout phase, + * items will remember their pre-layout positions to allow them to be laid out + * appropriately. Also, {@link LayoutParams#isItemRemoved() removed} items will + * be returned from the scrap to help determine correct placement of other items. + * These removed items should not be added to the child list, but should be used + * to help calculate correct positioning of other views, including views that + * were not previously onscreen (referred to as APPEARING views), but whose + * pre-layout offscreen position can be determined given the extra + * information about the pre-layout removed views.

+ * + *

The second layout pass is the real layout in which only non-removed views + * will be used. The only additional requirement during this pass is, if + * {@link #supportsPredictiveItemAnimations()} returns true, to note which + * views exist in the child list prior to layout and which are not there after + * layout (referred to as DISAPPEARING views), and to position/layout those views + * appropriately, without regard to the actual bounds of the RecyclerView. This allows + * the animation system to know the location to which to animate these disappearing + * views.

+ * + *

The default LayoutManager implementations for RecyclerView handle all of these + * requirements for animations already. Clients of RecyclerView can either use one + * of these layout managers directly or look at their implementations of + * onLayoutChildren() to see how they account for the APPEARING and + * DISAPPEARING views.

+ * + * @param recycler Recycler to use for fetching potentially cached views for a + * position + * @param state Transient state of RecyclerView + */ + public void onLayoutChildren(Recycler recycler, State state) { + Log.e(TAG, "You must override onLayoutChildren(Recycler recycler, State state) "); + } + + /** + * Create a default LayoutParams object for a child of the RecyclerView. + * + *

LayoutManagers will often want to use a custom LayoutParams type + * to store extra information specific to the layout. Client code should subclass + * {@link RecyclerView.LayoutParams} for this purpose.

+ * + *

Important: if you use your own custom LayoutParams type + * you must also override + * {@link #checkLayoutParams(LayoutParams)}, + * {@link #generateLayoutParams(android.view.ViewGroup.LayoutParams)} and + * {@link #generateLayoutParams(android.content.Context, android.util.AttributeSet)}.

+ * + * @return A new LayoutParams for a child view + */ + public abstract LayoutParams generateDefaultLayoutParams(); + + /** + * Determines the validity of the supplied LayoutParams object. + * + *

This should check to make sure that the object is of the correct type + * and all values are within acceptable ranges. The default implementation + * returns true for non-null params.

+ * + * @param lp LayoutParams object to check + * @return true if this LayoutParams object is valid, false otherwise + */ + public boolean checkLayoutParams(LayoutParams lp) { + return lp != null; + } + + /** + * Create a LayoutParams object suitable for this LayoutManager, copying relevant + * values from the supplied LayoutParams object if possible. + * + *

Important: if you use your own custom LayoutParams type + * you must also override + * {@link #checkLayoutParams(LayoutParams)}, + * {@link #generateLayoutParams(android.view.ViewGroup.LayoutParams)} and + * {@link #generateLayoutParams(android.content.Context, android.util.AttributeSet)}.

+ * + * @param lp Source LayoutParams object to copy values from + * @return a new LayoutParams object + */ + public LayoutParams generateLayoutParams(ViewGroup.LayoutParams lp) { + if (lp instanceof LayoutParams) { + return new LayoutParams((LayoutParams) lp); + } else if (lp instanceof MarginLayoutParams) { + return new LayoutParams((MarginLayoutParams) lp); + } else { + return new LayoutParams(lp); + } + } + + /** + * Create a LayoutParams object suitable for this LayoutManager from + * an inflated layout resource. + * + *

Important: if you use your own custom LayoutParams type + * you must also override + * {@link #checkLayoutParams(LayoutParams)}, + * {@link #generateLayoutParams(android.view.ViewGroup.LayoutParams)} and + * {@link #generateLayoutParams(android.content.Context, android.util.AttributeSet)}.

+ * + * @param c Context for obtaining styled attributes + * @param attrs AttributeSet describing the supplied arguments + * @return a new LayoutParams object + */ + public LayoutParams generateLayoutParams(Context c, AttributeSet attrs) { + return new LayoutParams(c, attrs); + } + + /** + * Scroll horizontally by dx pixels in screen coordinates and return the distance traveled. + * The default implementation does nothing and returns 0. + * + * @param dx distance to scroll by in pixels. X increases as scroll position + * approaches the right. + * @param recycler Recycler to use for fetching potentially cached views for a + * position + * @param state Transient state of RecyclerView + * @return The actual distance scrolled. The return value will be negative if dx was + * negative and scrolling proceeeded in that direction. + * Math.abs(result) may be less than dx if a boundary was reached. + */ + public int scrollHorizontallyBy(int dx, Recycler recycler, State state) { + return 0; + } + + /** + * Scroll vertically by dy pixels in screen coordinates and return the distance traveled. + * The default implementation does nothing and returns 0. + * + * @param dy distance to scroll in pixels. Y increases as scroll position + * approaches the bottom. + * @param recycler Recycler to use for fetching potentially cached views for a + * position + * @param state Transient state of RecyclerView + * @return The actual distance scrolled. The return value will be negative if dy was + * negative and scrolling proceeeded in that direction. + * Math.abs(result) may be less than dy if a boundary was reached. + */ + public int scrollVerticallyBy(int dy, Recycler recycler, State state) { + return 0; + } + + /** + * Query if horizontal scrolling is currently supported. The default implementation + * returns false. + * + * @return True if this LayoutManager can scroll the current contents horizontally + */ + public boolean canScrollHorizontally() { + return false; + } + + /** + * Query if vertical scrolling is currently supported. The default implementation + * returns false. + * + * @return True if this LayoutManager can scroll the current contents vertically + */ + public boolean canScrollVertically() { + return false; + } + + /** + * Scroll to the specified adapter position. + * + * Actual position of the item on the screen depends on the LayoutManager implementation. + * @param position Scroll to this adapter position. + */ + public void scrollToPosition(int position) { + if (DEBUG) { + Log.e(TAG, "You MUST implement scrollToPosition. It will soon become abstract"); + } + } + + /** + *

Smooth scroll to the specified adapter position.

+ *

To support smooth scrolling, override this method, create your {@link SmoothScroller} + * instance and call {@link #startSmoothScroll(SmoothScroller)}. + *

+ * @param recyclerView The RecyclerView to which this layout manager is attached + * @param state Current State of RecyclerView + * @param position Scroll to this adapter position. + */ + public void smoothScrollToPosition(RecyclerView recyclerView, State state, + int position) { + Log.e(TAG, "You must override smoothScrollToPosition to support smooth scrolling"); + } + + /** + *

Starts a smooth scroll using the provided SmoothScroller.

+ *

Calling this method will cancel any previous smooth scroll request.

+ * @param smoothScroller Unstance which defines how smooth scroll should be animated + */ + public void startSmoothScroll(SmoothScroller smoothScroller) { + if (mSmoothScroller != null && smoothScroller != mSmoothScroller + && mSmoothScroller.isRunning()) { + mSmoothScroller.stop(); + } + mSmoothScroller = smoothScroller; + mSmoothScroller.start(mRecyclerView, this); + } + + /** + * @return true if RecycylerView is currently in the state of smooth scrolling. + */ + public boolean isSmoothScrolling() { + return mSmoothScroller != null && mSmoothScroller.isRunning(); + } + + + /** + * Returns the resolved layout direction for this RecyclerView. + * + * @return {@link android.support.v4.view.ViewCompat#LAYOUT_DIRECTION_RTL} if the layout + * direction is RTL or returns + * {@link android.support.v4.view.ViewCompat#LAYOUT_DIRECTION_LTR} if the layout direction + * is not RTL. + */ + public int getLayoutDirection() { + return ViewCompat.getLayoutDirection(mRecyclerView); + } + + /** + * Ends all animations on the view created by the {@link ItemAnimator}. + * + * @param view The View for which the animations should be ended. + * @see RecyclerView.ItemAnimator#endAnimations() + */ + public void endAnimation(View view) { + if (mRecyclerView.mItemAnimator != null) { + mRecyclerView.mItemAnimator.endAnimation(getChildViewHolderInt(view)); + } + } + + /** + * To be called only during {@link #onLayoutChildren(Recycler, State)} to add a view + * to the layout that is known to be going away, either because it has been + * {@link Adapter#notifyItemRemoved(int) removed} or because it is actually not in the + * visible portion of the container but is being laid out in order to inform RecyclerView + * in how to animate the item out of view. + *

+ * Views added via this method are going to be invisible to LayoutManager after the + * dispatchLayout pass is complete. They cannot be retrieved via {@link #getChildAt(int)} + * or won't be included in {@link #getChildCount()} method. + * + * @param child View to add and then remove with animation. + */ + public void addDisappearingView(View child) { + addDisappearingView(child, -1); + } + + /** + * To be called only during {@link #onLayoutChildren(Recycler, State)} to add a view + * to the layout that is known to be going away, either because it has been + * {@link Adapter#notifyItemRemoved(int) removed} or because it is actually not in the + * visible portion of the container but is being laid out in order to inform RecyclerView + * in how to animate the item out of view. + *

+ * Views added via this method are going to be invisible to LayoutManager after the + * dispatchLayout pass is complete. They cannot be retrieved via {@link #getChildAt(int)} + * or won't be included in {@link #getChildCount()} method. + * + * @param child View to add and then remove with animation. + * @param index Index of the view. + */ + public void addDisappearingView(View child, int index) { + addViewInt(child, index, true); + } + + /** + * Add a view to the currently attached RecyclerView if needed. LayoutManagers should + * use this method to add views obtained from a {@link Recycler} using + * {@link Recycler#getViewForPosition(int)}. + * + * @param child View to add + */ + public void addView(View child) { + addView(child, -1); + } + + /** + * Add a view to the currently attached RecyclerView if needed. LayoutManagers should + * use this method to add views obtained from a {@link Recycler} using + * {@link Recycler#getViewForPosition(int)}. + * + * @param child View to add + * @param index Index to add child at + */ + public void addView(View child, int index) { + addViewInt(child, index, false); + } + + private void addViewInt(View child, int index, boolean disappearing) { + final ViewHolder holder = getChildViewHolderInt(child); + if (disappearing || holder.isRemoved()) { + // these views will be hidden at the end of the layout pass. + mRecyclerView.mState.addToDisappearingList(child); + } else { + // This may look like unnecessary but may happen if layout manager supports + // predictive layouts and adapter removed then re-added the same item. + // In this case, added version will be visible in the post layout (because add is + // deferred) but RV will still bind it to the same View. + // So if a View re-appears in post layout pass, remove it from disappearing list. + mRecyclerView.mState.removeFromDisappearingList(child); + } + final LayoutParams lp = (LayoutParams) child.getLayoutParams(); + if (holder.wasReturnedFromScrap() || holder.isScrap()) { + if (holder.isScrap()) { + holder.unScrap(); + } else { + holder.clearReturnedFromScrapFlag(); + } + mChildHelper.attachViewToParent(child, index, child.getLayoutParams(), false); + if (DISPATCH_TEMP_DETACH) { + ViewCompat.dispatchFinishTemporaryDetach(child); + } + } else if (child.getParent() == mRecyclerView) { // it was not a scrap but a valid child + // ensure in correct position + int currentIndex = mChildHelper.indexOfChild(child); + if (index == -1) { + index = mChildHelper.getChildCount(); + } + if (currentIndex == -1) { + throw new IllegalStateException("Added View has RecyclerView as parent but" + + " view is not a real child. Unfiltered index:" + + mRecyclerView.indexOfChild(child)); + } + if (currentIndex != index) { + mRecyclerView.mLayout.moveView(currentIndex, index); + } + } else { + mChildHelper.addView(child, index, false); + lp.mInsetsDirty = true; + if (mSmoothScroller != null && mSmoothScroller.isRunning()) { + mSmoothScroller.onChildAttachedToWindow(child); + } + } + if (lp.mPendingInvalidate) { + if (DEBUG) { + Log.d(TAG, "consuming pending invalidate on child " + lp.mViewHolder); + } + holder.itemView.invalidate(); + lp.mPendingInvalidate = false; + } + } + + /** + * Remove a view from the currently attached RecyclerView if needed. LayoutManagers should + * use this method to completely remove a child view that is no longer needed. + * LayoutManagers should strongly consider recycling removed views using + * {@link Recycler#recycleView(android.view.View)}. + * + * @param child View to remove + */ + public void removeView(View child) { + mChildHelper.removeView(child); + } + + /** + * Remove a view from the currently attached RecyclerView if needed. LayoutManagers should + * use this method to completely remove a child view that is no longer needed. + * LayoutManagers should strongly consider recycling removed views using + * {@link Recycler#recycleView(android.view.View)}. + * + * @param index Index of the child view to remove + */ + public void removeViewAt(int index) { + final View child = getChildAt(index); + if (child != null) { + mChildHelper.removeViewAt(index); + } + } + + /** + * Remove all views from the currently attached RecyclerView. This will not recycle + * any of the affected views; the LayoutManager is responsible for doing so if desired. + */ + public void removeAllViews() { + // Only remove non-animating views + final int childCount = getChildCount(); + for (int i = childCount - 1; i >= 0; i--) { + mChildHelper.removeViewAt(i); + } + } + + /** + * Returns offset of the RecyclerView's text baseline from the its top boundary. + * + * @return The offset of the RecyclerView's text baseline from the its top boundary; -1 if + * there is no baseline. + */ + public int getBaseline() { + return -1; + } + + /** + * Returns the adapter position of the item represented by the given View. This does not + * contain any adapter changes that might have happened after the last layout. + * + * @param view The view to query + * @return The adapter position of the item which is rendered by this View. + */ + public int getPosition(View view) { + return ((RecyclerView.LayoutParams) view.getLayoutParams()).getViewLayoutPosition(); + } + + /** + * Returns the View type defined by the adapter. + * + * @param view The view to query + * @return The type of the view assigned by the adapter. + */ + public int getItemViewType(View view) { + return getChildViewHolderInt(view).getItemViewType(); + } + + /** + * Finds the view which represents the given adapter position. + *

+ * This method traverses each child since it has no information about child order. + * Override this method to improve performance if your LayoutManager keeps data about + * child views. + *

+ * If a view is ignored via {@link #ignoreView(View)}, it is also ignored by this method. + * + * @param position Position of the item in adapter + * @return The child view that represents the given position or null if the position is not + * laid out + */ + public View findViewByPosition(int position) { + final int childCount = getChildCount(); + for (int i = 0; i < childCount; i++) { + View child = getChildAt(i); + ViewHolder vh = getChildViewHolderInt(child); + if (vh == null) { + continue; + } + if (vh.getLayoutPosition() == position && !vh.shouldIgnore() && + (mRecyclerView.mState.isPreLayout() || !vh.isRemoved())) { + return child; + } + } + return null; + } + + /** + * Temporarily detach a child view. + * + *

LayoutManagers may want to perform a lightweight detach operation to rearrange + * views currently attached to the RecyclerView. Generally LayoutManager implementations + * will want to use {@link #detachAndScrapView(android.view.View, RecyclerView.Recycler)} + * so that the detached view may be rebound and reused.

+ * + *

If a LayoutManager uses this method to detach a view, it must + * {@link #attachView(android.view.View, int, RecyclerView.LayoutParams) reattach} + * or {@link #removeDetachedView(android.view.View) fully remove} the detached view + * before the LayoutManager entry point method called by RecyclerView returns.

+ * + * @param child Child to detach + */ + public void detachView(View child) { + final int ind = mChildHelper.indexOfChild(child); + if (ind >= 0) { + detachViewInternal(ind, child); + } + } + + /** + * Temporarily detach a child view. + * + *

LayoutManagers may want to perform a lightweight detach operation to rearrange + * views currently attached to the RecyclerView. Generally LayoutManager implementations + * will want to use {@link #detachAndScrapView(android.view.View, RecyclerView.Recycler)} + * so that the detached view may be rebound and reused.

+ * + *

If a LayoutManager uses this method to detach a view, it must + * {@link #attachView(android.view.View, int, RecyclerView.LayoutParams) reattach} + * or {@link #removeDetachedView(android.view.View) fully remove} the detached view + * before the LayoutManager entry point method called by RecyclerView returns.

+ * + * @param index Index of the child to detach + */ + public void detachViewAt(int index) { + detachViewInternal(index, getChildAt(index)); + } + + private void detachViewInternal(int index, View view) { + if (DISPATCH_TEMP_DETACH) { + ViewCompat.dispatchStartTemporaryDetach(view); + } + mChildHelper.detachViewFromParent(index); + } + + /** + * Reattach a previously {@link #detachView(android.view.View) detached} view. + * This method should not be used to reattach views that were previously + * {@link #detachAndScrapView(android.view.View, RecyclerView.Recycler)} scrapped}. + * + * @param child Child to reattach + * @param index Intended child index for child + * @param lp LayoutParams for child + */ + public void attachView(View child, int index, LayoutParams lp) { + ViewHolder vh = getChildViewHolderInt(child); + if (vh.isRemoved()) { + mRecyclerView.mState.addToDisappearingList(child); + } else { + mRecyclerView.mState.removeFromDisappearingList(child); + } + mChildHelper.attachViewToParent(child, index, lp, vh.isRemoved()); + if (DISPATCH_TEMP_DETACH) { + ViewCompat.dispatchFinishTemporaryDetach(child); + } + } + + /** + * Reattach a previously {@link #detachView(android.view.View) detached} view. + * This method should not be used to reattach views that were previously + * {@link #detachAndScrapView(android.view.View, RecyclerView.Recycler)} scrapped}. + * + * @param child Child to reattach + * @param index Intended child index for child + */ + public void attachView(View child, int index) { + attachView(child, index, (LayoutParams) child.getLayoutParams()); + } + + /** + * Reattach a previously {@link #detachView(android.view.View) detached} view. + * This method should not be used to reattach views that were previously + * {@link #detachAndScrapView(android.view.View, RecyclerView.Recycler)} scrapped}. + * + * @param child Child to reattach + */ + public void attachView(View child) { + attachView(child, -1); + } + + /** + * Finish removing a view that was previously temporarily + * {@link #detachView(android.view.View) detached}. + * + * @param child Detached child to remove + */ + public void removeDetachedView(View child) { + mRecyclerView.removeDetachedView(child, false); + } + + /** + * Moves a View from one position to another. + * + * @param fromIndex The View's initial index + * @param toIndex The View's target index + */ + public void moveView(int fromIndex, int toIndex) { + View view = getChildAt(fromIndex); + if (view == null) { + throw new IllegalArgumentException("Cannot move a child from non-existing index:" + + fromIndex); + } + detachViewAt(fromIndex); + attachView(view, toIndex); + } + + /** + * Detach a child view and add it to a {@link Recycler Recycler's} scrap heap. + * + *

Scrapping a view allows it to be rebound and reused to show updated or + * different data.

+ * + * @param child Child to detach and scrap + * @param recycler Recycler to deposit the new scrap view into + */ + public void detachAndScrapView(View child, Recycler recycler) { + int index = mChildHelper.indexOfChild(child); + scrapOrRecycleView(recycler, index, child); + } + + /** + * Detach a child view and add it to a {@link Recycler Recycler's} scrap heap. + * + *

Scrapping a view allows it to be rebound and reused to show updated or + * different data.

+ * + * @param index Index of child to detach and scrap + * @param recycler Recycler to deposit the new scrap view into + */ + public void detachAndScrapViewAt(int index, Recycler recycler) { + final View child = getChildAt(index); + scrapOrRecycleView(recycler, index, child); + } + + /** + * Remove a child view and recycle it using the given Recycler. + * + * @param child Child to remove and recycle + * @param recycler Recycler to use to recycle child + */ + public void removeAndRecycleView(View child, Recycler recycler) { + removeView(child); + recycler.recycleView(child); + } + + /** + * Remove a child view and recycle it using the given Recycler. + * + * @param index Index of child to remove and recycle + * @param recycler Recycler to use to recycle child + */ + public void removeAndRecycleViewAt(int index, Recycler recycler) { + final View view = getChildAt(index); + removeViewAt(index); + recycler.recycleView(view); + } + + /** + * Return the current number of child views attached to the parent RecyclerView. + * This does not include child views that were temporarily detached and/or scrapped. + * + * @return Number of attached children + */ + public int getChildCount() { + return mChildHelper != null ? mChildHelper.getChildCount() : 0; + } + + /** + * Return the child view at the given index + * @param index Index of child to return + * @return Child view at index + */ + public View getChildAt(int index) { + return mChildHelper != null ? mChildHelper.getChildAt(index) : null; + } + + /** + * Return the width of the parent RecyclerView + * + * @return Width in pixels + */ + public int getWidth() { + return mRecyclerView != null ? mRecyclerView.getWidth() : 0; + } + + /** + * Return the height of the parent RecyclerView + * + * @return Height in pixels + */ + public int getHeight() { + return mRecyclerView != null ? mRecyclerView.getHeight() : 0; + } + + /** + * Return the left padding of the parent RecyclerView + * + * @return Padding in pixels + */ + public int getPaddingLeft() { + return mRecyclerView != null ? mRecyclerView.getPaddingLeft() : 0; + } + + /** + * Return the top padding of the parent RecyclerView + * + * @return Padding in pixels + */ + public int getPaddingTop() { + return mRecyclerView != null ? mRecyclerView.getPaddingTop() : 0; + } + + /** + * Return the right padding of the parent RecyclerView + * + * @return Padding in pixels + */ + public int getPaddingRight() { + return mRecyclerView != null ? mRecyclerView.getPaddingRight() : 0; + } + + /** + * Return the bottom padding of the parent RecyclerView + * + * @return Padding in pixels + */ + public int getPaddingBottom() { + return mRecyclerView != null ? mRecyclerView.getPaddingBottom() : 0; + } + + /** + * Return the start padding of the parent RecyclerView + * + * @return Padding in pixels + */ + public int getPaddingStart() { + return mRecyclerView != null ? ViewCompat.getPaddingStart(mRecyclerView) : 0; + } + + /** + * Return the end padding of the parent RecyclerView + * + * @return Padding in pixels + */ + public int getPaddingEnd() { + return mRecyclerView != null ? ViewCompat.getPaddingEnd(mRecyclerView) : 0; + } + + /** + * Returns true if the RecyclerView this LayoutManager is bound to has focus. + * + * @return True if the RecyclerView has focus, false otherwise. + * @see View#isFocused() + */ + public boolean isFocused() { + return mRecyclerView != null && mRecyclerView.isFocused(); + } + + /** + * Returns true if the RecyclerView this LayoutManager is bound to has or contains focus. + * + * @return true if the RecyclerView has or contains focus + * @see View#hasFocus() + */ + public boolean hasFocus() { + return mRecyclerView != null && mRecyclerView.hasFocus(); + } + + /** + * Returns the item View which has or contains focus. + * + * @return A direct child of RecyclerView which has focus or contains the focused child. + */ + public View getFocusedChild() { + if (mRecyclerView == null) { + return null; + } + final View focused = mRecyclerView.getFocusedChild(); + if (focused == null || mChildHelper.isHidden(focused)) { + return null; + } + return focused; + } + + /** + * Returns the number of items in the adapter bound to the parent RecyclerView. + *

+ * Note that this number is not necessarily equal to {@link State#getItemCount()}. In + * methods where State is available, you should use {@link State#getItemCount()} instead. + * For more details, check the documentation for {@link State#getItemCount()}. + * + * @return The number of items in the bound adapter + * @see State#getItemCount() + */ + public int getItemCount() { + final Adapter a = mRecyclerView != null ? mRecyclerView.getAdapter() : null; + return a != null ? a.getItemCount() : 0; + } + + /** + * Offset all child views attached to the parent RecyclerView by dx pixels along + * the horizontal axis. + * + * @param dx Pixels to offset by + */ + public void offsetChildrenHorizontal(int dx) { + if (mRecyclerView != null) { + mRecyclerView.offsetChildrenHorizontal(dx); + } + } + + /** + * Offset all child views attached to the parent RecyclerView by dy pixels along + * the vertical axis. + * + * @param dy Pixels to offset by + */ + public void offsetChildrenVertical(int dy) { + if (mRecyclerView != null) { + mRecyclerView.offsetChildrenVertical(dy); + } + } + + /** + * Flags a view so that it will not be scrapped or recycled. + *

+ * Scope of ignoring a child is strictly restricted to position tracking, scrapping and + * recyling. Methods like {@link #removeAndRecycleAllViews(Recycler)} will ignore the child + * whereas {@link #removeAllViews()} or {@link #offsetChildrenHorizontal(int)} will not + * ignore the child. + *

+ * Before this child can be recycled again, you have to call + * {@link #stopIgnoringView(View)}. + *

+ * You can call this method only if your LayoutManger is in onLayout or onScroll callback. + * + * @param view View to ignore. + * @see #stopIgnoringView(View) + */ + public void ignoreView(View view) { + if (view.getParent() != mRecyclerView || mRecyclerView.indexOfChild(view) == -1) { + // checking this because calling this method on a recycled or detached view may + // cause loss of state. + throw new IllegalArgumentException("View should be fully attached to be ignored"); + } + final ViewHolder vh = getChildViewHolderInt(view); + vh.addFlags(ViewHolder.FLAG_IGNORE); + mRecyclerView.mState.onViewIgnored(vh); + } + + /** + * View can be scrapped and recycled again. + *

+ * Note that calling this method removes all information in the view holder. + *

+ * You can call this method only if your LayoutManger is in onLayout or onScroll callback. + * + * @param view View to ignore. + */ + public void stopIgnoringView(View view) { + final ViewHolder vh = getChildViewHolderInt(view); + vh.stopIgnoring(); + vh.resetInternal(); + vh.addFlags(ViewHolder.FLAG_INVALID); + } + + /** + * Temporarily detach and scrap all currently attached child views. Views will be scrapped + * into the given Recycler. The Recycler may prefer to reuse scrap views before + * other views that were previously recycled. + * + * @param recycler Recycler to scrap views into + */ + public void detachAndScrapAttachedViews(Recycler recycler) { + final int childCount = getChildCount(); + for (int i = childCount - 1; i >= 0; i--) { + final View v = getChildAt(i); + scrapOrRecycleView(recycler, i, v); + } + } + + private void scrapOrRecycleView(Recycler recycler, int index, View view) { + final ViewHolder viewHolder = getChildViewHolderInt(view); + if (viewHolder.shouldIgnore()) { + if (DEBUG) { + Log.d(TAG, "ignoring view " + viewHolder); + } + return; + } + if (viewHolder.isInvalid() && !viewHolder.isRemoved() && !viewHolder.isChanged() && + !mRecyclerView.mAdapter.hasStableIds()) { + removeViewAt(index); + recycler.recycleViewHolderInternal(viewHolder); + } else { + detachViewAt(index); + recycler.scrapView(view); + } + } + + /** + * Recycles the scrapped views. + *

+ * When a view is detached and removed, it does not trigger a ViewGroup invalidate. This is + * the expected behavior if scrapped views are used for animations. Otherwise, we need to + * call remove and invalidate RecyclerView to ensure UI update. + * + * @param recycler Recycler + */ + void removeAndRecycleScrapInt(Recycler recycler) { + final int scrapCount = recycler.getScrapCount(); + // Loop backward, recycler might be changed by removeDetachedView() + for (int i = scrapCount - 1; i >= 0; i--) { + final View scrap = recycler.getScrapViewAt(i); + final ViewHolder vh = getChildViewHolderInt(scrap); + if (vh.shouldIgnore()) { + continue; + } + if (vh.isTmpDetached()) { + mRecyclerView.removeDetachedView(scrap, false); + } + recycler.quickRecycleScrapView(scrap); + } + recycler.clearScrap(); + if (scrapCount > 0) { + mRecyclerView.invalidate(); + } + } + + + /** + * Measure a child view using standard measurement policy, taking the padding + * of the parent RecyclerView and any added item decorations into account. + * + *

If the RecyclerView can be scrolled in either dimension the caller may + * pass 0 as the widthUsed or heightUsed parameters as they will be irrelevant.

+ * + * @param child Child view to measure + * @param widthUsed Width in pixels currently consumed by other views, if relevant + * @param heightUsed Height in pixels currently consumed by other views, if relevant + */ + public void measureChild(View child, int widthUsed, int heightUsed) { + final LayoutParams lp = (LayoutParams) child.getLayoutParams(); + + final Rect insets = mRecyclerView.getItemDecorInsetsForChild(child); + widthUsed += insets.left + insets.right; + heightUsed += insets.top + insets.bottom; + + final int widthSpec = getChildMeasureSpec(getWidth(), + getPaddingLeft() + getPaddingRight() + widthUsed, lp.width, + canScrollHorizontally()); + final int heightSpec = getChildMeasureSpec(getHeight(), + getPaddingTop() + getPaddingBottom() + heightUsed, lp.height, + canScrollVertically()); + child.measure(widthSpec, heightSpec); + } + + /** + * Measure a child view using standard measurement policy, taking the padding + * of the parent RecyclerView, any added item decorations and the child margins + * into account. + * + *

If the RecyclerView can be scrolled in either dimension the caller may + * pass 0 as the widthUsed or heightUsed parameters as they will be irrelevant.

+ * + * @param child Child view to measure + * @param widthUsed Width in pixels currently consumed by other views, if relevant + * @param heightUsed Height in pixels currently consumed by other views, if relevant + */ + public void measureChildWithMargins(View child, int widthUsed, int heightUsed) { + final LayoutParams lp = (LayoutParams) child.getLayoutParams(); + + final Rect insets = mRecyclerView.getItemDecorInsetsForChild(child); + widthUsed += insets.left + insets.right; + heightUsed += insets.top + insets.bottom; + + final int widthSpec = getChildMeasureSpec(getWidth(), + getPaddingLeft() + getPaddingRight() + + lp.leftMargin + lp.rightMargin + widthUsed, lp.width, + canScrollHorizontally()); + final int heightSpec = getChildMeasureSpec(getHeight(), + getPaddingTop() + getPaddingBottom() + + lp.topMargin + lp.bottomMargin + heightUsed, lp.height, + canScrollVertically()); + child.measure(widthSpec, heightSpec); + } + + /** + * Calculate a MeasureSpec value for measuring a child view in one dimension. + * + * @param parentSize Size of the parent view where the child will be placed + * @param padding Total space currently consumed by other elements of parent + * @param childDimension Desired size of the child view, or MATCH_PARENT/WRAP_CONTENT. + * Generally obtained from the child view's LayoutParams + * @param canScroll true if the parent RecyclerView can scroll in this dimension + * + * @return a MeasureSpec value for the child view + */ + public static int getChildMeasureSpec(int parentSize, int padding, int childDimension, + boolean canScroll) { + int size = Math.max(0, parentSize - padding); + int resultSize = 0; + int resultMode = 0; + + if (canScroll) { + if (childDimension >= 0) { + resultSize = childDimension; + resultMode = MeasureSpec.EXACTLY; + } else { + // MATCH_PARENT can't be applied since we can scroll in this dimension, wrap + // instead using UNSPECIFIED. + resultSize = 0; + resultMode = MeasureSpec.UNSPECIFIED; + } + } else { + if (childDimension >= 0) { + resultSize = childDimension; + resultMode = MeasureSpec.EXACTLY; + } else if (childDimension == LayoutParams.FILL_PARENT) { + resultSize = size; + resultMode = MeasureSpec.EXACTLY; + } else if (childDimension == LayoutParams.WRAP_CONTENT) { + resultSize = size; + resultMode = MeasureSpec.AT_MOST; + } + } + return MeasureSpec.makeMeasureSpec(resultSize, resultMode); + } + + /** + * Returns the measured width of the given child, plus the additional size of + * any insets applied by {@link ItemDecoration ItemDecorations}. + * + * @param child Child view to query + * @return child's measured width plus ItemDecoration insets + * + * @see View#getMeasuredWidth() + */ + public int getDecoratedMeasuredWidth(View child) { + final Rect insets = ((LayoutParams) child.getLayoutParams()).mDecorInsets; + return child.getMeasuredWidth() + insets.left + insets.right; + } + + /** + * Returns the measured height of the given child, plus the additional size of + * any insets applied by {@link ItemDecoration ItemDecorations}. + * + * @param child Child view to query + * @return child's measured height plus ItemDecoration insets + * + * @see View#getMeasuredHeight() + */ + public int getDecoratedMeasuredHeight(View child) { + final Rect insets = ((LayoutParams) child.getLayoutParams()).mDecorInsets; + return child.getMeasuredHeight() + insets.top + insets.bottom; + } + + /** + * Lay out the given child view within the RecyclerView using coordinates that + * include any current {@link ItemDecoration ItemDecorations}. + * + *

LayoutManagers should prefer working in sizes and coordinates that include + * item decoration insets whenever possible. This allows the LayoutManager to effectively + * ignore decoration insets within measurement and layout code. See the following + * methods:

+ *
    + *
  • {@link #measureChild(View, int, int)}
  • + *
  • {@link #measureChildWithMargins(View, int, int)}
  • + *
  • {@link #getDecoratedLeft(View)}
  • + *
  • {@link #getDecoratedTop(View)}
  • + *
  • {@link #getDecoratedRight(View)}
  • + *
  • {@link #getDecoratedBottom(View)}
  • + *
  • {@link #getDecoratedMeasuredWidth(View)}
  • + *
  • {@link #getDecoratedMeasuredHeight(View)}
  • + *
+ * + * @param child Child to lay out + * @param left Left edge, with item decoration insets included + * @param top Top edge, with item decoration insets included + * @param right Right edge, with item decoration insets included + * @param bottom Bottom edge, with item decoration insets included + * + * @see View#layout(int, int, int, int) + */ + public void layoutDecorated(View child, int left, int top, int right, int bottom) { + final Rect insets = ((LayoutParams) child.getLayoutParams()).mDecorInsets; + child.layout(left + insets.left, top + insets.top, right - insets.right, + bottom - insets.bottom); + } + + /** + * Returns the left edge of the given child view within its parent, offset by any applied + * {@link ItemDecoration ItemDecorations}. + * + * @param child Child to query + * @return Child left edge with offsets applied + * @see #getLeftDecorationWidth(View) + */ + public int getDecoratedLeft(View child) { + return child.getLeft() - getLeftDecorationWidth(child); + } + + /** + * Returns the top edge of the given child view within its parent, offset by any applied + * {@link ItemDecoration ItemDecorations}. + * + * @param child Child to query + * @return Child top edge with offsets applied + * @see #getTopDecorationHeight(View) + */ + public int getDecoratedTop(View child) { + return child.getTop() - getTopDecorationHeight(child); + } + + /** + * Returns the right edge of the given child view within its parent, offset by any applied + * {@link ItemDecoration ItemDecorations}. + * + * @param child Child to query + * @return Child right edge with offsets applied + * @see #getRightDecorationWidth(View) + */ + public int getDecoratedRight(View child) { + return child.getRight() + getRightDecorationWidth(child); + } + + /** + * Returns the bottom edge of the given child view within its parent, offset by any applied + * {@link ItemDecoration ItemDecorations}. + * + * @param child Child to query + * @return Child bottom edge with offsets applied + * @see #getBottomDecorationHeight(View) + */ + public int getDecoratedBottom(View child) { + return child.getBottom() + getBottomDecorationHeight(child); + } + + /** + * Calculates the item decor insets applied to the given child and updates the provided + * Rect instance with the inset values. + *
    + *
  • The Rect's left is set to the total width of left decorations.
  • + *
  • The Rect's top is set to the total height of top decorations.
  • + *
  • The Rect's right is set to the total width of right decorations.
  • + *
  • The Rect's bottom is set to total height of bottom decorations.
  • + *
+ *

+ * Note that item decorations are automatically calculated when one of the LayoutManager's + * measure child methods is called. If you need to measure the child with custom specs via + * {@link View#measure(int, int)}, you can use this method to get decorations. + * + * @param child The child view whose decorations should be calculated + * @param outRect The Rect to hold result values + */ + public void calculateItemDecorationsForChild(View child, Rect outRect) { + if (mRecyclerView == null) { + outRect.set(0, 0, 0, 0); + return; + } + Rect insets = mRecyclerView.getItemDecorInsetsForChild(child); + outRect.set(insets); + } + + /** + * Returns the total height of item decorations applied to child's top. + *

+ * Note that this value is not updated until the View is measured or + * {@link #calculateItemDecorationsForChild(View, Rect)} is called. + * + * @param child Child to query + * @return The total height of item decorations applied to the child's top. + * @see #getDecoratedTop(View) + * @see #calculateItemDecorationsForChild(View, Rect) + */ + public int getTopDecorationHeight(View child) { + return ((LayoutParams) child.getLayoutParams()).mDecorInsets.top; + } + + /** + * Returns the total height of item decorations applied to child's bottom. + *

+ * Note that this value is not updated until the View is measured or + * {@link #calculateItemDecorationsForChild(View, Rect)} is called. + * + * @param child Child to query + * @return The total height of item decorations applied to the child's bottom. + * @see #getDecoratedBottom(View) + * @see #calculateItemDecorationsForChild(View, Rect) + */ + public int getBottomDecorationHeight(View child) { + return ((LayoutParams) child.getLayoutParams()).mDecorInsets.bottom; + } + + /** + * Returns the total width of item decorations applied to child's left. + *

+ * Note that this value is not updated until the View is measured or + * {@link #calculateItemDecorationsForChild(View, Rect)} is called. + * + * @param child Child to query + * @return The total width of item decorations applied to the child's left. + * @see #getDecoratedLeft(View) + * @see #calculateItemDecorationsForChild(View, Rect) + */ + public int getLeftDecorationWidth(View child) { + return ((LayoutParams) child.getLayoutParams()).mDecorInsets.left; + } + + /** + * Returns the total width of item decorations applied to child's right. + *

+ * Note that this value is not updated until the View is measured or + * {@link #calculateItemDecorationsForChild(View, Rect)} is called. + * + * @param child Child to query + * @return The total width of item decorations applied to the child's right. + * @see #getDecoratedRight(View) + * @see #calculateItemDecorationsForChild(View, Rect) + */ + public int getRightDecorationWidth(View child) { + return ((LayoutParams) child.getLayoutParams()).mDecorInsets.right; + } + + /** + * Called when searching for a focusable view in the given direction has failed + * for the current content of the RecyclerView. + * + *

This is the LayoutManager's opportunity to populate views in the given direction + * to fulfill the request if it can. The LayoutManager should attach and return + * the view to be focused. The default implementation returns null.

+ * + * @param focused The currently focused view + * @param direction 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 + * @param recycler The recycler to use for obtaining views for currently offscreen items + * @param state Transient state of RecyclerView + * @return The chosen view to be focused + */ + @Nullable + public View onFocusSearchFailed(View focused, int direction, Recycler recycler, + State state) { + return null; + } + + /** + * This method gives a LayoutManager an opportunity to intercept the initial focus search + * before the default behavior of {@link FocusFinder} is used. If this method returns + * null FocusFinder will attempt to find a focusable child view. If it fails + * then {@link #onFocusSearchFailed(View, int, RecyclerView.Recycler, RecyclerView.State)} + * will be called to give the LayoutManager an opportunity to add new views for items + * that did not have attached views representing them. The LayoutManager should not add + * or remove views from this method. + * + * @param focused The currently focused view + * @param direction 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} + * @return A descendant view to focus or null to fall back to default behavior. + * The default implementation returns null. + */ + public View onInterceptFocusSearch(View focused, int direction) { + return null; + } + + /** + * Called when a child of the RecyclerView wants a particular rectangle to be positioned + * onto the screen. See {@link ViewParent#requestChildRectangleOnScreen(android.view.View, + * android.graphics.Rect, boolean)} for more details. + * + *

The base implementation will attempt to perform a standard programmatic scroll + * to bring the given rect into view, within the padded area of the RecyclerView.

+ * + * @param child The direct child making the request. + * @param rect The rectangle in the child's coordinates the child + * wishes to be on the screen. + * @param immediate True to forbid animated or delayed scrolling, + * false otherwise + * @return Whether the group scrolled to handle the operation + */ + public boolean requestChildRectangleOnScreen(RecyclerView parent, View child, Rect rect, + boolean immediate) { + final int parentLeft = getPaddingLeft(); + final int parentTop = getPaddingTop(); + final int parentRight = getWidth() - getPaddingRight(); + final int parentBottom = getHeight() - getPaddingBottom(); + final int childLeft = child.getLeft() + rect.left; + final int childTop = child.getTop() + rect.top; + final int childRight = childLeft + rect.width(); + final int childBottom = childTop + rect.height(); + + final int offScreenLeft = Math.min(0, childLeft - parentLeft); + final int offScreenTop = Math.min(0, childTop - parentTop); + final int offScreenRight = Math.max(0, childRight - parentRight); + final int offScreenBottom = Math.max(0, childBottom - parentBottom); + + // Favor the "start" layout direction over the end when bringing one side or the other + // of a large rect into view. If we decide to bring in end because start is already + // visible, limit the scroll such that start won't go out of bounds. + final int dx; + if (getLayoutDirection() == ViewCompat.LAYOUT_DIRECTION_RTL) { + dx = offScreenRight != 0 ? offScreenRight + : Math.max(offScreenLeft, childRight - parentRight); + } else { + dx = offScreenLeft != 0 ? offScreenLeft + : Math.min(childLeft - parentLeft, offScreenRight); + } + + // Favor bringing the top into view over the bottom. If top is already visible and + // we should scroll to make bottom visible, make sure top does not go out of bounds. + final int dy = offScreenTop != 0 ? offScreenTop + : Math.min(childTop - parentTop, offScreenBottom); + + if (dx != 0 || dy != 0) { + if (immediate) { + parent.scrollBy(dx, dy); + } else { + parent.smoothScrollBy(dx, dy); + } + return true; + } + return false; + } + + /** + * @deprecated Use {@link #onRequestChildFocus(RecyclerView, State, View, View)} + */ + @Deprecated + public boolean onRequestChildFocus(RecyclerView parent, View child, View focused) { + // eat the request if we are in the middle of a scroll or layout + return isSmoothScrolling() || parent.isRunningLayoutOrScroll(); + } + + /** + * Called when a descendant view of the RecyclerView requests focus. + * + *

A LayoutManager wishing to keep focused views aligned in a specific + * portion of the view may implement that behavior in an override of this method.

+ * + *

If the LayoutManager executes different behavior that should override the default + * behavior of scrolling the focused child on screen instead of running alongside it, + * this method should return true.

+ * + * @param parent The RecyclerView hosting this LayoutManager + * @param state Current state of RecyclerView + * @param child Direct child of the RecyclerView containing the newly focused view + * @param focused The newly focused view. This may be the same view as child or it may be + * null + * @return true if the default scroll behavior should be suppressed + */ + public boolean onRequestChildFocus(RecyclerView parent, State state, View child, + View focused) { + return onRequestChildFocus(parent, child, focused); + } + + /** + * Called if the RecyclerView this LayoutManager is bound to has a different adapter set. + * The LayoutManager may use this opportunity to clear caches and configure state such + * that it can relayout appropriately with the new data and potentially new view types. + * + *

The default implementation removes all currently attached views.

+ * + * @param oldAdapter The previous adapter instance. Will be null if there was previously no + * adapter. + * @param newAdapter The new adapter instance. Might be null if + * {@link #setAdapter(RecyclerView.Adapter)} is called with {@code null}. + */ + public void onAdapterChanged(Adapter oldAdapter, Adapter newAdapter) { + } + + /** + * Called to populate focusable views within the RecyclerView. + * + *

The LayoutManager implementation should return true if the default + * behavior of {@link ViewGroup#addFocusables(java.util.ArrayList, int)} should be + * suppressed.

+ * + *

The default implementation returns false to trigger RecyclerView + * to fall back to the default ViewGroup behavior.

+ * + * @param recyclerView The RecyclerView hosting this LayoutManager + * @param views List of output views. This method should add valid focusable views + * to this list. + * @param direction 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} + * @param focusableMode The type of focusables to be added. + * + * @return true to suppress the default behavior, false to add default focusables after + * this method returns. + * + * @see #FOCUSABLES_ALL + * @see #FOCUSABLES_TOUCH_MODE + */ + public boolean onAddFocusables(RecyclerView recyclerView, ArrayList views, + int direction, int focusableMode) { + return false; + } + + /** + * Called when {@link Adapter#notifyDataSetChanged()} is triggered instead of giving + * detailed information on what has actually changed. + * + * @param recyclerView + */ + public void onItemsChanged(RecyclerView recyclerView) { + } + + /** + * Called when items have been added to the adapter. The LayoutManager may choose to + * requestLayout if the inserted items would require refreshing the currently visible set + * of child views. (e.g. currently empty space would be filled by appended items, etc.) + * + * @param recyclerView + * @param positionStart + * @param itemCount + */ + public void onItemsAdded(RecyclerView recyclerView, int positionStart, int itemCount) { + } + + /** + * Called when items have been removed from the adapter. + * + * @param recyclerView + * @param positionStart + * @param itemCount + */ + public void onItemsRemoved(RecyclerView recyclerView, int positionStart, int itemCount) { + } + + /** + * Called when items have been changed in the adapter. + * + * @param recyclerView + * @param positionStart + * @param itemCount + */ + public void onItemsUpdated(RecyclerView recyclerView, int positionStart, int itemCount) { + } + + /** + * Called when an item is moved withing the adapter. + *

+ * Note that, an item may also change position in response to another ADD/REMOVE/MOVE + * operation. This callback is only called if and only if {@link Adapter#notifyItemMoved} + * is called. + * + * @param recyclerView + * @param from + * @param to + * @param itemCount + */ + public void onItemsMoved(RecyclerView recyclerView, int from, int to, int itemCount) { + + } + + + /** + *

Override this method if you want to support scroll bars.

+ * + *

Read {@link RecyclerView#computeHorizontalScrollExtent()} for details.

+ * + *

Default implementation returns 0.

+ * + * @param state Current state of RecyclerView + * @return The horizontal extent of the scrollbar's thumb + * @see RecyclerView#computeHorizontalScrollExtent() + */ + public int computeHorizontalScrollExtent(State state) { + return 0; + } + + /** + *

Override this method if you want to support scroll bars.

+ * + *

Read {@link RecyclerView#computeHorizontalScrollOffset()} for details.

+ * + *

Default implementation returns 0.

+ * + * @param state Current State of RecyclerView where you can find total item count + * @return The horizontal offset of the scrollbar's thumb + * @see RecyclerView#computeHorizontalScrollOffset() + */ + public int computeHorizontalScrollOffset(State state) { + return 0; + } + + /** + *

Override this method if you want to support scroll bars.

+ * + *

Read {@link RecyclerView#computeHorizontalScrollRange()} for details.

+ * + *

Default implementation returns 0.

+ * + * @param state Current State of RecyclerView where you can find total item count + * @return The total horizontal range represented by the vertical scrollbar + * @see RecyclerView#computeHorizontalScrollRange() + */ + public int computeHorizontalScrollRange(State state) { + return 0; + } + + /** + *

Override this method if you want to support scroll bars.

+ * + *

Read {@link RecyclerView#computeVerticalScrollExtent()} for details.

+ * + *

Default implementation returns 0.

+ * + * @param state Current state of RecyclerView + * @return The vertical extent of the scrollbar's thumb + * @see RecyclerView#computeVerticalScrollExtent() + */ + public int computeVerticalScrollExtent(State state) { + return 0; + } + + /** + *

Override this method if you want to support scroll bars.

+ * + *

Read {@link RecyclerView#computeVerticalScrollOffset()} for details.

+ * + *

Default implementation returns 0.

+ * + * @param state Current State of RecyclerView where you can find total item count + * @return The vertical offset of the scrollbar's thumb + * @see RecyclerView#computeVerticalScrollOffset() + */ + public int computeVerticalScrollOffset(State state) { + return 0; + } + + /** + *

Override this method if you want to support scroll bars.

+ * + *

Read {@link RecyclerView#computeVerticalScrollRange()} for details.

+ * + *

Default implementation returns 0.

+ * + * @param state Current State of RecyclerView where you can find total item count + * @return The total vertical range represented by the vertical scrollbar + * @see RecyclerView#computeVerticalScrollRange() + */ + public int computeVerticalScrollRange(State state) { + return 0; + } + + /** + * Measure the attached RecyclerView. Implementations must call + * {@link #setMeasuredDimension(int, int)} before returning. + * + *

The default implementation will handle EXACTLY measurements and respect + * the minimum width and height properties of the host RecyclerView if measured + * as UNSPECIFIED. AT_MOST measurements will be treated as EXACTLY and the RecyclerView + * will consume all available space.

+ * + * @param recycler Recycler + * @param state Transient state of RecyclerView + * @param widthSpec Width {@link android.view.View.MeasureSpec} + * @param heightSpec Height {@link android.view.View.MeasureSpec} + */ + public void onMeasure(Recycler recycler, State state, int widthSpec, int heightSpec) { + mRecyclerView.defaultOnMeasure(widthSpec, heightSpec); + } + + /** + * {@link View#setMeasuredDimension(int, int) Set the measured dimensions} of the + * host RecyclerView. + * + * @param widthSize Measured width + * @param heightSize Measured height + */ + public void setMeasuredDimension(int widthSize, int heightSize) { + mRecyclerView.setMeasuredDimension(widthSize, heightSize); + } + + /** + * @return The host RecyclerView's {@link View#getMinimumWidth()} + */ + public int getMinimumWidth() { + return ViewCompat.getMinimumWidth(mRecyclerView); + } + + /** + * @return The host RecyclerView's {@link View#getMinimumHeight()} + */ + public int getMinimumHeight() { + return ViewCompat.getMinimumHeight(mRecyclerView); + } + /** + *

Called when the LayoutManager should save its state. This is a good time to save your + * scroll position, configuration and anything else that may be required to restore the same + * layout state if the LayoutManager is recreated.

+ *

RecyclerView does NOT verify if the LayoutManager has changed between state save and + * restore. This will let you share information between your LayoutManagers but it is also + * your responsibility to make sure they use the same parcelable class.

+ * + * @return Necessary information for LayoutManager to be able to restore its state + */ + public Parcelable onSaveInstanceState() { + return null; + } + + + public void onRestoreInstanceState(Parcelable state) { + + } + + void stopSmoothScroller() { + if (mSmoothScroller != null) { + mSmoothScroller.stop(); + } + } + + private void onSmoothScrollerStopped(SmoothScroller smoothScroller) { + if (mSmoothScroller == smoothScroller) { + mSmoothScroller = null; + } + } + + /** + * RecyclerView calls this method to notify LayoutManager that scroll state has changed. + * + * @param state The new scroll state for RecyclerView + */ + public void onScrollStateChanged(int state) { + } + + /** + * Removes all views and recycles them using the given recycler. + *

+ * If you want to clean cached views as well, you should call {@link Recycler#clear()} too. + *

+ * If a View is marked as "ignored", it is not removed nor recycled. + * + * @param recycler Recycler to use to recycle children + * @see #removeAndRecycleView(View, Recycler) + * @see #removeAndRecycleViewAt(int, Recycler) + * @see #ignoreView(View) + */ + public void removeAndRecycleAllViews(Recycler recycler) { + for (int i = getChildCount() - 1; i >= 0; i--) { + final View view = getChildAt(i); + if (!getChildViewHolderInt(view).shouldIgnore()) { + removeAndRecycleViewAt(i, recycler); + } + } + } + + // called by accessibility delegate + void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfoCompat info) { + onInitializeAccessibilityNodeInfo(mRecyclerView.mRecycler, mRecyclerView.mState, info); + } + + /** + * Called by the AccessibilityDelegate when the information about the current layout should + * be populated. + *

+ * Default implementation adds a {@link + * android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.CollectionInfoCompat}. + *

+ * You should override + * {@link #getRowCountForAccessibility(RecyclerView.Recycler, RecyclerView.State)}, + * {@link #getColumnCountForAccessibility(RecyclerView.Recycler, RecyclerView.State)}, + * {@link #isLayoutHierarchical(RecyclerView.Recycler, RecyclerView.State)} and + * {@link #getSelectionModeForAccessibility(RecyclerView.Recycler, RecyclerView.State)} for + * more accurate accessibility information. + * + * @param recycler The Recycler that can be used to convert view positions into adapter + * positions + * @param state The current state of RecyclerView + * @param info The info that should be filled by the LayoutManager + * @see View#onInitializeAccessibilityNodeInfo( + *android.view.accessibility.AccessibilityNodeInfo) + * @see #getRowCountForAccessibility(RecyclerView.Recycler, RecyclerView.State) + * @see #getColumnCountForAccessibility(RecyclerView.Recycler, RecyclerView.State) + * @see #isLayoutHierarchical(RecyclerView.Recycler, RecyclerView.State) + * @see #getSelectionModeForAccessibility(RecyclerView.Recycler, RecyclerView.State) + */ + public void onInitializeAccessibilityNodeInfo(Recycler recycler, State state, + AccessibilityNodeInfoCompat info) { + if (ViewCompat.canScrollVertically(mRecyclerView, -1) || + ViewCompat.canScrollHorizontally(mRecyclerView, -1)) { + info.addAction(AccessibilityNodeInfoCompat.ACTION_SCROLL_BACKWARD); + info.setScrollable(true); + } + if (ViewCompat.canScrollVertically(mRecyclerView, 1) || + ViewCompat.canScrollHorizontally(mRecyclerView, 1)) { + info.addAction(AccessibilityNodeInfoCompat.ACTION_SCROLL_FORWARD); + info.setScrollable(true); + } + final AccessibilityNodeInfoCompat.CollectionInfoCompat collectionInfo + = AccessibilityNodeInfoCompat.CollectionInfoCompat + .obtain(getRowCountForAccessibility(recycler, state), + getColumnCountForAccessibility(recycler, state), + isLayoutHierarchical(recycler, state), + getSelectionModeForAccessibility(recycler, state)); + info.setCollectionInfo(collectionInfo); + } + + // called by accessibility delegate + public void onInitializeAccessibilityEvent(AccessibilityEvent event) { + onInitializeAccessibilityEvent(mRecyclerView.mRecycler, mRecyclerView.mState, event); + } + + /** + * Called by the accessibility delegate to initialize an accessibility event. + *

+ * Default implementation adds item count and scroll information to the event. + * + * @param recycler The Recycler that can be used to convert view positions into adapter + * positions + * @param state The current state of RecyclerView + * @param event The event instance to initialize + * @see View#onInitializeAccessibilityEvent(android.view.accessibility.AccessibilityEvent) + */ + public void onInitializeAccessibilityEvent(Recycler recycler, State state, + AccessibilityEvent event) { + final AccessibilityRecordCompat record = AccessibilityEventCompat + .asRecord(event); + if (mRecyclerView == null || record == null) { + return; + } + record.setScrollable(ViewCompat.canScrollVertically(mRecyclerView, 1) + || ViewCompat.canScrollVertically(mRecyclerView, -1) + || ViewCompat.canScrollHorizontally(mRecyclerView, -1) + || ViewCompat.canScrollHorizontally(mRecyclerView, 1)); + + if (mRecyclerView.mAdapter != null) { + record.setItemCount(mRecyclerView.mAdapter.getItemCount()); + } + } + + // called by accessibility delegate + void onInitializeAccessibilityNodeInfoForItem(View host, AccessibilityNodeInfoCompat info) { + final ViewHolder vh = getChildViewHolderInt(host); + // avoid trying to create accessibility node info for removed children + if (vh != null && !vh.isRemoved()) { + onInitializeAccessibilityNodeInfoForItem(mRecyclerView.mRecycler, + mRecyclerView.mState, host, info); + } + } + + /** + * Called by the AccessibilityDelegate when the accessibility information for a specific + * item should be populated. + *

+ * Default implementation adds basic positioning information about the item. + * + * @param recycler The Recycler that can be used to convert view positions into adapter + * positions + * @param state The current state of RecyclerView + * @param host The child for which accessibility node info should be populated + * @param info The info to fill out about the item + * @see android.widget.AbsListView#onInitializeAccessibilityNodeInfoForItem(View, int, + * android.view.accessibility.AccessibilityNodeInfo) + */ + public void onInitializeAccessibilityNodeInfoForItem(Recycler recycler, State state, + View host, AccessibilityNodeInfoCompat info) { + int rowIndexGuess = canScrollVertically() ? getPosition(host) : 0; + int columnIndexGuess = canScrollHorizontally() ? getPosition(host) : 0; + final AccessibilityNodeInfoCompat.CollectionItemInfoCompat itemInfo + = AccessibilityNodeInfoCompat.CollectionItemInfoCompat.obtain(rowIndexGuess, 1, + columnIndexGuess, 1, false, false); + info.setCollectionItemInfo(itemInfo); + } + + /** + * A LayoutManager can call this method to force RecyclerView to run simple animations in + * the next layout pass, even if there is not any trigger to do so. (e.g. adapter data + * change). + *

+ * Note that, calling this method will not guarantee that RecyclerView will run animations + * at all. For example, if there is not any {@link ItemAnimator} set, RecyclerView will + * not run any animations but will still clear this flag after the layout is complete. + * + */ + public void requestSimpleAnimationsInNextLayout() { + mRequestedSimpleAnimations = true; + } + + /** + * Returns the selection mode for accessibility. Should be + * {@link AccessibilityNodeInfoCompat.CollectionInfoCompat#SELECTION_MODE_NONE}, + * {@link AccessibilityNodeInfoCompat.CollectionInfoCompat#SELECTION_MODE_SINGLE} or + * {@link AccessibilityNodeInfoCompat.CollectionInfoCompat#SELECTION_MODE_MULTIPLE}. + *

+ * Default implementation returns + * {@link AccessibilityNodeInfoCompat.CollectionInfoCompat#SELECTION_MODE_NONE}. + * + * @param recycler The Recycler that can be used to convert view positions into adapter + * positions + * @param state The current state of RecyclerView + * @return Selection mode for accessibility. Default implementation returns + * {@link AccessibilityNodeInfoCompat.CollectionInfoCompat#SELECTION_MODE_NONE}. + */ + public int getSelectionModeForAccessibility(Recycler recycler, State state) { + return AccessibilityNodeInfoCompat.CollectionInfoCompat.SELECTION_MODE_NONE; + } + + /** + * Returns the number of rows for accessibility. + *

+ * Default implementation returns the number of items in the adapter if LayoutManager + * supports vertical scrolling or 1 if LayoutManager does not support vertical + * scrolling. + * + * @param recycler The Recycler that can be used to convert view positions into adapter + * positions + * @param state The current state of RecyclerView + * @return The number of rows in LayoutManager for accessibility. + */ + public int getRowCountForAccessibility(Recycler recycler, State state) { + if (mRecyclerView == null || mRecyclerView.mAdapter == null) { + return 1; + } + return canScrollVertically() ? mRecyclerView.mAdapter.getItemCount() : 1; + } + + /** + * Returns the number of columns for accessibility. + *

+ * Default implementation returns the number of items in the adapter if LayoutManager + * supports horizontal scrolling or 1 if LayoutManager does not support horizontal + * scrolling. + * + * @param recycler The Recycler that can be used to convert view positions into adapter + * positions + * @param state The current state of RecyclerView + * @return The number of rows in LayoutManager for accessibility. + */ + public int getColumnCountForAccessibility(Recycler recycler, State state) { + if (mRecyclerView == null || mRecyclerView.mAdapter == null) { + return 1; + } + return canScrollHorizontally() ? mRecyclerView.mAdapter.getItemCount() : 1; + } + + /** + * Returns whether layout is hierarchical or not to be used for accessibility. + *

+ * Default implementation returns false. + * + * @param recycler The Recycler that can be used to convert view positions into adapter + * positions + * @param state The current state of RecyclerView + * @return True if layout is hierarchical. + */ + public boolean isLayoutHierarchical(Recycler recycler, State state) { + return false; + } + + // called by accessibility delegate + boolean performAccessibilityAction(int action, Bundle args) { + return performAccessibilityAction(mRecyclerView.mRecycler, mRecyclerView.mState, + action, args); + } + + /** + * Called by AccessibilityDelegate when an action is requested from the RecyclerView. + * + * @param recycler The Recycler that can be used to convert view positions into adapter + * positions + * @param state The current state of RecyclerView + * @param action The action to perform + * @param args Optional action arguments + * @see View#performAccessibilityAction(int, android.os.Bundle) + */ + public boolean performAccessibilityAction(Recycler recycler, State state, int action, + Bundle args) { + if (mRecyclerView == null) { + return false; + } + int vScroll = 0, hScroll = 0; + switch (action) { + case AccessibilityNodeInfoCompat.ACTION_SCROLL_BACKWARD: + if (ViewCompat.canScrollVertically(mRecyclerView, -1)) { + vScroll = -(getHeight() - getPaddingTop() - getPaddingBottom()); + } + if (ViewCompat.canScrollHorizontally(mRecyclerView, -1)) { + hScroll = -(getWidth() - getPaddingLeft() - getPaddingRight()); + } + break; + case AccessibilityNodeInfoCompat.ACTION_SCROLL_FORWARD: + if (ViewCompat.canScrollVertically(mRecyclerView, 1)) { + vScroll = getHeight() - getPaddingTop() - getPaddingBottom(); + } + if (ViewCompat.canScrollHorizontally(mRecyclerView, 1)) { + hScroll = getWidth() - getPaddingLeft() - getPaddingRight(); + } + break; + } + if (vScroll == 0 && hScroll == 0) { + return false; + } + mRecyclerView.scrollBy(hScroll, vScroll); + return true; + } + + // called by accessibility delegate + boolean performAccessibilityActionForItem(View view, int action, Bundle args) { + return performAccessibilityActionForItem(mRecyclerView.mRecycler, mRecyclerView.mState, + view, action, args); + } + + /** + * Called by AccessibilityDelegate when an accessibility action is requested on one of the + * children of LayoutManager. + *

+ * Default implementation does not do anything. + * + * @param recycler The Recycler that can be used to convert view positions into adapter + * positions + * @param state The current state of RecyclerView + * @param view The child view on which the action is performed + * @param action The action to perform + * @param args Optional action arguments + * @return true if action is handled + * @see View#performAccessibilityAction(int, android.os.Bundle) + */ + public boolean performAccessibilityActionForItem(Recycler recycler, State state, View view, + int action, Bundle args) { + return false; + } + } + + /** + * An ItemDecoration allows the application to add a special drawing and layout offset + * to specific item views from the adapter's data set. This can be useful for drawing dividers + * between items, highlights, visual grouping boundaries and more. + * + *

All ItemDecorations are drawn in the order they were added, before the item + * views (in {@link ItemDecoration#onDraw(Canvas, RecyclerView, RecyclerView.State) onDraw()} + * and after the items (in {@link ItemDecoration#onDrawOver(Canvas, RecyclerView, + * RecyclerView.State)}.

+ */ + public static abstract class ItemDecoration { + /** + * Draw any appropriate decorations into the Canvas supplied to the RecyclerView. + * Any content drawn by this method will be drawn before the item views are drawn, + * and will thus appear underneath the views. + * + * @param c Canvas to draw into + * @param parent RecyclerView this ItemDecoration is drawing into + * @param state The current state of RecyclerView + */ + public void onDraw(Canvas c, RecyclerView parent, State state) { + onDraw(c, parent); + } + + /** + * @deprecated + * Override {@link #onDraw(Canvas, RecyclerView, RecyclerView.State)} + */ + @Deprecated + public void onDraw(Canvas c, RecyclerView parent) { + } + + /** + * Draw any appropriate decorations into the Canvas supplied to the RecyclerView. + * Any content drawn by this method will be drawn after the item views are drawn + * and will thus appear over the views. + * + * @param c Canvas to draw into + * @param parent RecyclerView this ItemDecoration is drawing into + * @param state The current state of RecyclerView. + */ + public void onDrawOver(Canvas c, RecyclerView parent, State state) { + onDrawOver(c, parent); + } + + /** + * @deprecated + * Override {@link #onDrawOver(Canvas, RecyclerView, RecyclerView.State)} + */ + @Deprecated + public void onDrawOver(Canvas c, RecyclerView parent) { + } + + + /** + * @deprecated + * Use {@link #getItemOffsets(Rect, View, RecyclerView, State)} + */ + @Deprecated + public void getItemOffsets(Rect outRect, int itemPosition, RecyclerView parent) { + outRect.set(0, 0, 0, 0); + } + + /** + * Retrieve any offsets for the given item. Each field of outRect specifies + * the number of pixels that the item view should be inset by, similar to padding or margin. + * The default implementation sets the bounds of outRect to 0 and returns. + * + *

+ * If this ItemDecoration does not affect the positioning of item views, it should set + * all four fields of outRect (left, top, right, bottom) to zero + * before returning. + * + *

+ * If you need to access Adapter for additional data, you can call + * {@link RecyclerView#getChildAdapterPosition(View)} to get the adapter position of the + * View. + * + * @param outRect Rect to receive the output. + * @param view The child view to decorate + * @param parent RecyclerView this ItemDecoration is decorating + * @param state The current state of RecyclerView. + */ + public void getItemOffsets(Rect outRect, View view, RecyclerView parent, State state) { + getItemOffsets(outRect, ((LayoutParams) view.getLayoutParams()).getViewLayoutPosition(), + parent); + } + } + + /** + * An OnItemTouchListener allows the application to intercept touch events in progress at the + * view hierarchy level of the RecyclerView before those touch events are considered for + * RecyclerView's own scrolling behavior. + * + *

This can be useful for applications that wish to implement various forms of gestural + * manipulation of item views within the RecyclerView. OnItemTouchListeners may intercept + * a touch interaction already in progress even if the RecyclerView is already handling that + * gesture stream itself for the purposes of scrolling.

+ */ + public interface OnItemTouchListener { + /** + * Silently observe and/or take over touch events sent to the RecyclerView + * before they are handled by either the RecyclerView itself or its child views. + * + *

The onInterceptTouchEvent methods of each attached OnItemTouchListener will be run + * in the order in which each listener was added, before any other touch processing + * by the RecyclerView itself or child views occurs.

+ * + * @param e MotionEvent describing the touch event. All coordinates are in + * the RecyclerView's coordinate system. + * @return true if this OnItemTouchListener wishes to begin intercepting touch events, false + * to continue with the current behavior and continue observing future events in + * the gesture. + */ + public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e); + + /** + * Process a touch event as part of a gesture that was claimed by returning true from + * a previous call to {@link #onInterceptTouchEvent}. + * + * @param e MotionEvent describing the touch event. All coordinates are in + * the RecyclerView's coordinate system. + */ + public void onTouchEvent(RecyclerView rv, MotionEvent e); + } + + /** + * An OnScrollListener can be set on a RecyclerView to receive messages + * when a scrolling event has occurred on that RecyclerView. + * + * @see RecyclerView#setOnScrollListener(OnScrollListener) and + * RecyclerView#addOnScrollListener(OnScrollListener) + * + * If you are planning to have several listeners at the same time, use + * RecyclerView#addOnScrollListener. If there will be only one listener at the time and you + * want your components to be able to easily replace the listener use + * RecyclerView#setOnScrollListener. + */ + abstract static public class OnScrollListener { + /** + * Callback method to be invoked when RecyclerView's scroll state changes. + * + * @param recyclerView The RecyclerView whose scroll state has changed. + * @param newState The updated scroll state. One of {@link #SCROLL_STATE_IDLE}, + * {@link #SCROLL_STATE_DRAGGING} or {@link #SCROLL_STATE_SETTLING}. + */ + public void onScrollStateChanged(RecyclerView recyclerView, int newState){} + + /** + * Callback method to be invoked when the RecyclerView has been scrolled. This will be + * called after the scroll has completed. + *

+ * This callback will also be called if visible item range changes after a layout + * calculation. In that case, dx and dy will be 0. + * + * @param recyclerView The RecyclerView which scrolled. + * @param dx The amount of horizontal scroll. + * @param dy The amount of vertical scroll. + */ + public void onScrolled(RecyclerView recyclerView, int dx, int dy){} + } + + /** + * A RecyclerListener can be set on a RecyclerView to receive messages whenever + * a view is recycled. + * + * @see RecyclerView#setRecyclerListener(RecyclerListener) + */ + public interface RecyclerListener { + + /** + * This method is called whenever the view in the ViewHolder is recycled. + * + * RecyclerView calls this method right before clearing ViewHolder's internal data and + * sending it to RecycledViewPool. This way, if ViewHolder was holding valid information + * before being recycled, you can call {@link ViewHolder#getAdapterPosition()} to get + * its adapter position. + * + * @param holder The ViewHolder containing the view that was recycled + */ + public void onViewRecycled(ViewHolder holder); + } + + /** + * A ViewHolder describes an item view and metadata about its place within the RecyclerView. + * + *

{@link Adapter} implementations should subclass ViewHolder and add fields for caching + * potentially expensive {@link View#findViewById(int)} results.

+ * + *

While {@link LayoutParams} belong to the {@link LayoutManager}, + * {@link ViewHolder ViewHolders} belong to the adapter. Adapters should feel free to use + * their own custom ViewHolder implementations to store data that makes binding view contents + * easier. Implementations should assume that individual item views will hold strong references + * to ViewHolder objects and that RecyclerView instances may hold + * strong references to extra off-screen item views for caching purposes

+ */ + public static abstract class ViewHolder { + public final View itemView; + int mPosition = NO_POSITION; + int mOldPosition = NO_POSITION; + long mItemId = NO_ID; + int mItemViewType = INVALID_TYPE; + int mPreLayoutPosition = NO_POSITION; + + // The item that this holder is shadowing during an item change event/animation + ViewHolder mShadowedHolder = null; + // The item that is shadowing this holder during an item change event/animation + ViewHolder mShadowingHolder = null; + + /** + * This ViewHolder has been bound to a position; mPosition, mItemId and mItemViewType + * are all valid. + */ + static final int FLAG_BOUND = 1 << 0; + + /** + * The data this ViewHolder's view reflects is stale and needs to be rebound + * by the adapter. mPosition and mItemId are consistent. + */ + static final int FLAG_UPDATE = 1 << 1; + + /** + * This ViewHolder's data is invalid. The identity implied by mPosition and mItemId + * are not to be trusted and may no longer match the item view type. + * This ViewHolder must be fully rebound to different data. + */ + static final int FLAG_INVALID = 1 << 2; + + /** + * This ViewHolder points at data that represents an item previously removed from the + * data set. Its view may still be used for things like outgoing animations. + */ + static final int FLAG_REMOVED = 1 << 3; + + /** + * This ViewHolder should not be recycled. This flag is set via setIsRecyclable() + * and is intended to keep views around during animations. + */ + static final int FLAG_NOT_RECYCLABLE = 1 << 4; + + /** + * This ViewHolder is returned from scrap which means we are expecting an addView call + * for this itemView. When returned from scrap, ViewHolder stays in the scrap list until + * the end of the layout pass and then recycled by RecyclerView if it is not added back to + * the RecyclerView. + */ + static final int FLAG_RETURNED_FROM_SCRAP = 1 << 5; + + /** + * This ViewHolder's contents have changed. This flag is used as an indication that + * change animations may be used, if supported by the ItemAnimator. + */ + static final int FLAG_CHANGED = 1 << 6; + + /** + * This ViewHolder is fully managed by the LayoutManager. We do not scrap, recycle or remove + * it unless LayoutManager is replaced. + * It is still fully visible to the LayoutManager. + */ + static final int FLAG_IGNORE = 1 << 7; + + /** + * When the View is detached form the parent, we set this flag so that we can take correct + * action when we need to remove it or add it back. + */ + static final int FLAG_TMP_DETACHED = 1 << 8; + + /** + * Set when we can no longer determine the adapter position of this ViewHolder until it is + * rebound to a new position. It is different than FLAG_INVALID because FLAG_INVALID is + * set even when the type does not match. Also, FLAG_ADAPTER_POSITION_UNKNOWN is set as soon + * as adapter notification arrives vs FLAG_INVALID is set lazily before layout is + * re-calculated. + */ + static final int FLAG_ADAPTER_POSITION_UNKNOWN = 1 << 9; + + private int mFlags; + + private int mIsRecyclableCount = 0; + + // If non-null, view is currently considered scrap and may be reused for other data by the + // scrap container. + private Recycler mScrapContainer = null; + + /** + * Is set when VH is bound from the adapter and cleaned right before it is sent to + * {@link RecycledViewPool}. + */ + RecyclerView mOwnerRecyclerView; + + public ViewHolder(View itemView) { + if (itemView == null) { + throw new IllegalArgumentException("itemView may not be null"); + } + this.itemView = itemView; + } + + void flagRemovedAndOffsetPosition(int mNewPosition, int offset, boolean applyToPreLayout) { + addFlags(ViewHolder.FLAG_REMOVED); + offsetPosition(offset, applyToPreLayout); + mPosition = mNewPosition; + } + + void offsetPosition(int offset, boolean applyToPreLayout) { + if (mOldPosition == NO_POSITION) { + mOldPosition = mPosition; + } + if (mPreLayoutPosition == NO_POSITION) { + mPreLayoutPosition = mPosition; + } + if (applyToPreLayout) { + mPreLayoutPosition += offset; + } + mPosition += offset; + if (itemView.getLayoutParams() != null) { + ((LayoutParams) itemView.getLayoutParams()).mInsetsDirty = true; + } + } + + void clearOldPosition() { + mOldPosition = NO_POSITION; + mPreLayoutPosition = NO_POSITION; + } + + void saveOldPosition() { + if (mOldPosition == NO_POSITION) { + mOldPosition = mPosition; + } + } + + boolean shouldIgnore() { + return (mFlags & FLAG_IGNORE) != 0; + } + + /** + * @deprecated This method is deprecated because its meaning is ambiguous due to the async + * handling of adapter updates. Please use {@link #getLayoutPosition()} or + * {@link #getAdapterPosition()} depending on your use case. + * + * @see #getLayoutPosition() + * @see #getAdapterPosition() + */ + @Deprecated + public final int getPosition() { + return mPreLayoutPosition == NO_POSITION ? mPosition : mPreLayoutPosition; + } + + /** + * Returns the position of the ViewHolder in terms of the latest layout pass. + *

+ * This position is mostly used by RecyclerView components to be consistent while + * RecyclerView lazily processes adapter updates. + *

+ * For performance and animation reasons, RecyclerView batches all adapter updates until the + * next layout pass. This may cause mismatches between the Adapter position of the item and + * the position it had in the latest layout calculations. + *

+ * LayoutManagers should always call this method while doing calculations based on item + * positions. All methods in {@link RecyclerView.LayoutManager}, {@link RecyclerView.State}, + * {@link RecyclerView.Recycler} that receive a position expect it to be the layout position + * of the item. + *

+ * If LayoutManager needs to call an external method that requires the adapter position of + * the item, it can use {@link #getAdapterPosition()} or + * {@link RecyclerView.Recycler#convertPreLayoutPositionToPostLayout(int)}. + * + * @return Returns the adapter position of the ViewHolder in the latest layout pass. + * @see #getAdapterPosition() + */ + public final int getLayoutPosition() { + return mPreLayoutPosition == NO_POSITION ? mPosition : mPreLayoutPosition; + } + + /** + * Returns the Adapter position of the item represented by this ViewHolder. + *

+ * Note that this might be different than the {@link #getLayoutPosition()} if there are + * pending adapter updates but a new layout pass has not happened yet. + *

+ * RecyclerView does not handle any adapter updates until the next layout traversal. This + * may create temporary inconsistencies between what user sees on the screen and what + * adapter contents have. This inconsistency is not important since it will be less than + * 16ms but it might be a problem if you want to use ViewHolder position to access the + * adapter. Sometimes, you may need to get the exact adapter position to do + * some actions in response to user events. In that case, you should use this method which + * will calculate the Adapter position of the ViewHolder. + *

+ * Note that if you've called {@link RecyclerView.Adapter#notifyDataSetChanged()}, until the + * next layout pass, the return value of this method will be {@link #NO_POSITION}. + * + * @return The adapter position of the item if it still exists in the adapter. + * {@link RecyclerView#NO_POSITION} if item has been removed from the adapter, + * {@link RecyclerView.Adapter#notifyDataSetChanged()} has been called after the last + * layout pass or the ViewHolder has already been recycled. + */ + public final int getAdapterPosition() { + if (mOwnerRecyclerView == null) { + return NO_POSITION; + } + return mOwnerRecyclerView.getAdapterPositionFor(this); + } + + /** + * When LayoutManager supports animations, RecyclerView tracks 3 positions for ViewHolders + * to perform animations. + *

+ * If a ViewHolder was laid out in the previous onLayout call, old position will keep its + * adapter index in the previous layout. + * + * @return The previous adapter index of the Item represented by this ViewHolder or + * {@link #NO_POSITION} if old position does not exists or cleared (pre-layout is + * complete). + */ + public final int getOldPosition() { + return mOldPosition; + } + + /** + * Returns The itemId represented by this ViewHolder. + * + * @return The the item's id if adapter has stable ids, {@link RecyclerView#NO_ID} + * otherwise + */ + public final long getItemId() { + return mItemId; + } + + /** + * @return The view type of this ViewHolder. + */ + public final int getItemViewType() { + return mItemViewType; + } + + boolean isScrap() { + return mScrapContainer != null; + } + + void unScrap() { + mScrapContainer.unscrapView(this); + } + + boolean wasReturnedFromScrap() { + return (mFlags & FLAG_RETURNED_FROM_SCRAP) != 0; + } + + void clearReturnedFromScrapFlag() { + mFlags = mFlags & ~FLAG_RETURNED_FROM_SCRAP; + } + + void clearTmpDetachFlag() { + mFlags = mFlags & ~FLAG_TMP_DETACHED; + } + + void stopIgnoring() { + mFlags = mFlags & ~FLAG_IGNORE; + } + + void setScrapContainer(Recycler recycler) { + mScrapContainer = recycler; + } + + boolean isInvalid() { + return (mFlags & FLAG_INVALID) != 0; + } + + boolean needsUpdate() { + return (mFlags & FLAG_UPDATE) != 0; + } + + boolean isChanged() { + return (mFlags & FLAG_CHANGED) != 0; + } + + boolean isBound() { + return (mFlags & FLAG_BOUND) != 0; + } + + boolean isRemoved() { + return (mFlags & FLAG_REMOVED) != 0; + } + + boolean hasAnyOfTheFlags(int flags) { + return (mFlags & flags) != 0; + } + + boolean isTmpDetached() { + return (mFlags & FLAG_TMP_DETACHED) != 0; + } + + boolean isAdapterPositionUnknown() { + return (mFlags & FLAG_ADAPTER_POSITION_UNKNOWN) != 0 || isInvalid(); + } + + void setFlags(int flags, int mask) { + mFlags = (mFlags & ~mask) | (flags & mask); + } + + void addFlags(int flags) { + mFlags |= flags; + } + + void resetInternal() { + mFlags = 0; + mPosition = NO_POSITION; + mOldPosition = NO_POSITION; + mItemId = NO_ID; + mPreLayoutPosition = NO_POSITION; + mIsRecyclableCount = 0; + mShadowedHolder = null; + mShadowingHolder = null; + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder("ViewHolder{" + + Integer.toHexString(hashCode()) + " position=" + mPosition + " id=" + mItemId + + ", oldPos=" + mOldPosition + ", pLpos:" + mPreLayoutPosition); + if (isScrap()) sb.append(" scrap"); + if (isInvalid()) sb.append(" invalid"); + if (!isBound()) sb.append(" unbound"); + if (needsUpdate()) sb.append(" update"); + if (isRemoved()) sb.append(" removed"); + if (shouldIgnore()) sb.append(" ignored"); + if (isChanged()) sb.append(" changed"); + if (isTmpDetached()) sb.append(" tmpDetached"); + if (!isRecyclable()) sb.append(" not recyclable(" + mIsRecyclableCount + ")"); + if (isAdapterPositionUnknown()) sb.append("undefined adapter position"); + + if (itemView.getParent() == null) sb.append(" no parent"); + sb.append("}"); + return sb.toString(); + } + + /** + * Informs the recycler whether this item can be recycled. Views which are not + * recyclable will not be reused for other items until setIsRecyclable() is + * later set to true. Calls to setIsRecyclable() should always be paired (one + * call to setIsRecyclabe(false) should always be matched with a later call to + * setIsRecyclable(true)). Pairs of calls may be nested, as the state is internally + * reference-counted. + * + * @param recyclable Whether this item is available to be recycled. Default value + * is true. + */ + public final void setIsRecyclable(boolean recyclable) { + mIsRecyclableCount = recyclable ? mIsRecyclableCount - 1 : mIsRecyclableCount + 1; + if (mIsRecyclableCount < 0) { + mIsRecyclableCount = 0; + if (DEBUG) { + throw new RuntimeException("isRecyclable decremented below 0: " + + "unmatched pair of setIsRecyable() calls for " + this); + } + Log.e(VIEW_LOG_TAG, "isRecyclable decremented below 0: " + + "unmatched pair of setIsRecyable() calls for " + this); + } else if (!recyclable && mIsRecyclableCount == 1) { + mFlags |= FLAG_NOT_RECYCLABLE; + } else if (recyclable && mIsRecyclableCount == 0) { + mFlags &= ~FLAG_NOT_RECYCLABLE; + } + if (DEBUG) { + Log.d(TAG, "setIsRecyclable val:" + recyclable + ":" + this); + } + } + + /** + * @see {@link #setIsRecyclable(boolean)} + * + * @return true if this item is available to be recycled, false otherwise. + */ + public final boolean isRecyclable() { + return (mFlags & FLAG_NOT_RECYCLABLE) == 0 && + !ViewCompat.hasTransientState(itemView); + } + + /** + * Returns whether we have animations referring to this view holder or not. + * This is similar to isRecyclable flag but does not check transient state. + */ + private boolean shouldBeKeptAsChild() { + return (mFlags & FLAG_NOT_RECYCLABLE) != 0; + } + + /** + * @return True if ViewHolder is not refenrenced by RecyclerView animations but has + * transient state which will prevent it from being recycled. + */ + private boolean doesTransientStatePreventRecycling() { + return (mFlags & FLAG_NOT_RECYCLABLE) == 0 && ViewCompat.hasTransientState(itemView); + } + } + + private int getAdapterPositionFor(ViewHolder viewHolder) { + if (viewHolder.hasAnyOfTheFlags( ViewHolder.FLAG_INVALID | + ViewHolder.FLAG_REMOVED | ViewHolder.FLAG_ADAPTER_POSITION_UNKNOWN) + || !viewHolder.isBound()) { + return RecyclerView.NO_POSITION; + } + return mAdapterHelper.applyPendingUpdatesToPosition(viewHolder.mPosition); + } + + /** + * {@link android.view.ViewGroup.MarginLayoutParams LayoutParams} subclass for children of + * {@link RecyclerView}. Custom {@link LayoutManager layout managers} are encouraged + * to create their own subclass of this LayoutParams class + * to store any additional required per-child view metadata about the layout. + */ + public static class LayoutParams extends android.view.ViewGroup.MarginLayoutParams { + ViewHolder mViewHolder; + final Rect mDecorInsets = new Rect(); + boolean mInsetsDirty = true; + // Flag is set to true if the view is bound while it is detached from RV. + // In this case, we need to manually call invalidate after view is added to guarantee that + // invalidation is populated through the View hierarchy + boolean mPendingInvalidate = false; + + public LayoutParams(Context c, AttributeSet attrs) { + super(c, attrs); + } + + public LayoutParams(int width, int height) { + super(width, height); + } + + public LayoutParams(MarginLayoutParams source) { + super(source); + } + + public LayoutParams(ViewGroup.LayoutParams source) { + super(source); + } + + public LayoutParams(LayoutParams source) { + super((ViewGroup.LayoutParams) source); + } + + /** + * Returns true if the view this LayoutParams is attached to needs to have its content + * updated from the corresponding adapter. + * + * @return true if the view should have its content updated + */ + public boolean viewNeedsUpdate() { + return mViewHolder.needsUpdate(); + } + + /** + * Returns true if the view this LayoutParams is attached to is now representing + * potentially invalid data. A LayoutManager should scrap/recycle it. + * + * @return true if the view is invalid + */ + public boolean isViewInvalid() { + return mViewHolder.isInvalid(); + } + + /** + * Returns true if the adapter data item corresponding to the view this LayoutParams + * is attached to has been removed from the data set. A LayoutManager may choose to + * treat it differently in order to animate its outgoing or disappearing state. + * + * @return true if the item the view corresponds to was removed from the data set + */ + public boolean isItemRemoved() { + return mViewHolder.isRemoved(); + } + + /** + * Returns true if the adapter data item corresponding to the view this LayoutParams + * is attached to has been changed in the data set. A LayoutManager may choose to + * treat it differently in order to animate its changing state. + * + * @return true if the item the view corresponds to was changed in the data set + */ + public boolean isItemChanged() { + return mViewHolder.isChanged(); + } + + /** + * @deprecated use {@link #getViewLayoutPosition()} or {@link #getViewAdapterPosition()} + */ + public int getViewPosition() { + return mViewHolder.getPosition(); + } + + /** + * Returns the adapter position that the view this LayoutParams is attached to corresponds + * to as of latest layout calculation. + * + * @return the adapter position this view as of latest layout pass + */ + public int getViewLayoutPosition() { + return mViewHolder.getLayoutPosition(); + } + + /** + * Returns the up-to-date adapter position that the view this LayoutParams is attached to + * corresponds to. + * + * @return the up-to-date adapter position this view. It may return + * {@link RecyclerView#NO_POSITION} if item represented by this View has been removed or + * its up-to-date position cannot be calculated. + */ + public int getViewAdapterPosition() { + return mViewHolder.getAdapterPosition(); + } + } + + /** + * Observer base class for watching changes to an {@link Adapter}. + * See {@link Adapter#registerAdapterDataObserver(AdapterDataObserver)}. + */ + public static abstract class AdapterDataObserver { + public void onChanged() { + // Do nothing + } + + public void onItemRangeChanged(int positionStart, int itemCount) { + // do nothing + } + + public void onItemRangeInserted(int positionStart, int itemCount) { + // do nothing + } + + public void onItemRangeRemoved(int positionStart, int itemCount) { + // do nothing + } + + public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) { + // do nothing + } + } + + /** + *

Base class for smooth scrolling. Handles basic tracking of the target view position and + * provides methods to trigger a programmatic scroll.

+ * + * @see LinearSmoothScroller + */ + public static abstract class SmoothScroller { + + private int mTargetPosition = RecyclerView.NO_POSITION; + + private RecyclerView mRecyclerView; + + private LayoutManager mLayoutManager; + + private boolean mPendingInitialRun; + + private boolean mRunning; + + private View mTargetView; + + private final Action mRecyclingAction; + + public SmoothScroller() { + mRecyclingAction = new Action(0, 0); + } + + /** + * Starts a smooth scroll for the given target position. + *

In each animation step, {@link RecyclerView} will check + * for the target view and call either + * {@link #onTargetFound(android.view.View, RecyclerView.State, SmoothScroller.Action)} or + * {@link #onSeekTargetStep(int, int, RecyclerView.State, SmoothScroller.Action)} until + * SmoothScroller is stopped.

+ * + *

Note that if RecyclerView finds the target view, it will automatically stop the + * SmoothScroller. This does not mean that scroll will stop, it only means it will + * stop calling SmoothScroller in each animation step.

+ */ + void start(RecyclerView recyclerView, LayoutManager layoutManager) { + mRecyclerView = recyclerView; + mLayoutManager = layoutManager; + if (mTargetPosition == RecyclerView.NO_POSITION) { + throw new IllegalArgumentException("Invalid target position"); + } + mRecyclerView.mState.mTargetPosition = mTargetPosition; + mRunning = true; + mPendingInitialRun = true; + mTargetView = findViewByPosition(getTargetPosition()); + onStart(); + mRecyclerView.mViewFlinger.postOnAnimation(); + } + + public void setTargetPosition(int targetPosition) { + mTargetPosition = targetPosition; + } + + /** + * @return The LayoutManager to which this SmoothScroller is attached + */ + public LayoutManager getLayoutManager() { + return mLayoutManager; + } + + /** + * Stops running the SmoothScroller in each animation callback. Note that this does not + * cancel any existing {@link Action} updated by + * {@link #onTargetFound(android.view.View, RecyclerView.State, SmoothScroller.Action)} or + * {@link #onSeekTargetStep(int, int, RecyclerView.State, SmoothScroller.Action)}. + */ + final protected void stop() { + if (!mRunning) { + return; + } + onStop(); + mRecyclerView.mState.mTargetPosition = RecyclerView.NO_POSITION; + mTargetView = null; + mTargetPosition = RecyclerView.NO_POSITION; + mPendingInitialRun = false; + mRunning = false; + // trigger a cleanup + mLayoutManager.onSmoothScrollerStopped(this); + // clear references to avoid any potential leak by a custom smooth scroller + mLayoutManager = null; + mRecyclerView = null; + } + + /** + * Returns true if SmoothScroller has been started but has not received the first + * animation + * callback yet. + * + * @return True if this SmoothScroller is waiting to start + */ + public boolean isPendingInitialRun() { + return mPendingInitialRun; + } + + + /** + * @return True if SmoothScroller is currently active + */ + public boolean isRunning() { + return mRunning; + } + + /** + * Returns the adapter position of the target item + * + * @return Adapter position of the target item or + * {@link RecyclerView#NO_POSITION} if no target view is set. + */ + public int getTargetPosition() { + return mTargetPosition; + } + + private void onAnimation(int dx, int dy) { + if (!mRunning || mTargetPosition == RecyclerView.NO_POSITION) { + stop(); + } + mPendingInitialRun = false; + if (mTargetView != null) { + // verify target position + if (getChildPosition(mTargetView) == mTargetPosition) { + onTargetFound(mTargetView, mRecyclerView.mState, mRecyclingAction); + mRecyclingAction.runIfNecessary(mRecyclerView); + stop(); + } else { + Log.e(TAG, "Passed over target position while smooth scrolling."); + mTargetView = null; + } + } + if (mRunning) { + onSeekTargetStep(dx, dy, mRecyclerView.mState, mRecyclingAction); + mRecyclingAction.runIfNecessary(mRecyclerView); + } + } + + /** + * @see RecyclerView#getChildLayoutPosition(android.view.View) + */ + public int getChildPosition(View view) { + return mRecyclerView.getChildLayoutPosition(view); + } + + /** + * @see RecyclerView.LayoutManager#getChildCount() + */ + public int getChildCount() { + return mRecyclerView.mLayout.getChildCount(); + } + + /** + * @see RecyclerView.LayoutManager#findViewByPosition(int) + */ + public View findViewByPosition(int position) { + return mRecyclerView.mLayout.findViewByPosition(position); + } + + /** + * @see RecyclerView#scrollToPosition(int) + */ + public void instantScrollToPosition(int position) { + mRecyclerView.scrollToPosition(position); + } + + protected void onChildAttachedToWindow(View child) { + if (getChildPosition(child) == getTargetPosition()) { + mTargetView = child; + if (DEBUG) { + Log.d(TAG, "smooth scroll target view has been attached"); + } + } + } + + /** + * Normalizes the vector. + * @param scrollVector The vector that points to the target scroll position + */ + protected void normalize(PointF scrollVector) { + final double magnitute = Math.sqrt(scrollVector.x * scrollVector.x + scrollVector.y * + scrollVector.y); + scrollVector.x /= magnitute; + scrollVector.y /= magnitute; + } + + /** + * Called when smooth scroll is started. This might be a good time to do setup. + */ + abstract protected void onStart(); + + /** + * Called when smooth scroller is stopped. This is a good place to cleanup your state etc. + * @see #stop() + */ + abstract protected void onStop(); + + /** + *

RecyclerView will call this method each time it scrolls until it can find the target + * position in the layout.

+ *

SmoothScroller should check dx, dy and if scroll should be changed, update the + * provided {@link Action} to define the next scroll.

+ * + * @param dx Last scroll amount horizontally + * @param dy Last scroll amount verticaully + * @param state Transient state of RecyclerView + * @param action If you want to trigger a new smooth scroll and cancel the previous one, + * update this object. + */ + abstract protected void onSeekTargetStep(int dx, int dy, State state, Action action); + + /** + * Called when the target position is laid out. This is the last callback SmoothScroller + * will receive and it should update the provided {@link Action} to define the scroll + * details towards the target view. + * @param targetView The view element which render the target position. + * @param state Transient state of RecyclerView + * @param action Action instance that you should update to define final scroll action + * towards the targetView + */ + abstract protected void onTargetFound(View targetView, State state, Action action); + + /** + * Holds information about a smooth scroll request by a {@link SmoothScroller}. + */ + public static class Action { + + public static final int UNDEFINED_DURATION = Integer.MIN_VALUE; + + private int mDx; + + private int mDy; + + private int mDuration; + + private Interpolator mInterpolator; + + private boolean changed = false; + + // we track this variable to inform custom implementer if they are updating the action + // in every animation callback + private int consecutiveUpdates = 0; + + /** + * @param dx Pixels to scroll horizontally + * @param dy Pixels to scroll vertically + */ + public Action(int dx, int dy) { + this(dx, dy, UNDEFINED_DURATION, null); + } + + /** + * @param dx Pixels to scroll horizontally + * @param dy Pixels to scroll vertically + * @param duration Duration of the animation in milliseconds + */ + public Action(int dx, int dy, int duration) { + this(dx, dy, duration, null); + } + + /** + * @param dx Pixels to scroll horizontally + * @param dy Pixels to scroll vertically + * @param duration Duration of the animation in milliseconds + * @param interpolator Interpolator to be used when calculating scroll position in each + * animation step + */ + public Action(int dx, int dy, int duration, Interpolator interpolator) { + mDx = dx; + mDy = dy; + mDuration = duration; + mInterpolator = interpolator; + } + private void runIfNecessary(RecyclerView recyclerView) { + if (changed) { + validate(); + if (mInterpolator == null) { + if (mDuration == UNDEFINED_DURATION) { + recyclerView.mViewFlinger.smoothScrollBy(mDx, mDy); + } else { + recyclerView.mViewFlinger.smoothScrollBy(mDx, mDy, mDuration); + } + } else { + recyclerView.mViewFlinger.smoothScrollBy(mDx, mDy, mDuration, mInterpolator); + } + consecutiveUpdates ++; + if (consecutiveUpdates > 10) { + // A new action is being set in every animation step. This looks like a bad + // implementation. Inform developer. + Log.e(TAG, "Smooth Scroll action is being updated too frequently. Make sure" + + " you are not changing it unless necessary"); + } + changed = false; + } else { + consecutiveUpdates = 0; + } + } + + private void validate() { + if (mInterpolator != null && mDuration < 1) { + throw new IllegalStateException("If you provide an interpolator, you must" + + " set a positive duration"); + } else if (mDuration < 1) { + throw new IllegalStateException("Scroll duration must be a positive number"); + } + } + + public int getDx() { + return mDx; + } + + public void setDx(int dx) { + changed = true; + mDx = dx; + } + + public int getDy() { + return mDy; + } + + public void setDy(int dy) { + changed = true; + mDy = dy; + } + + public int getDuration() { + return mDuration; + } + + public void setDuration(int duration) { + changed = true; + mDuration = duration; + } + + public Interpolator getInterpolator() { + return mInterpolator; + } + + /** + * Sets the interpolator to calculate scroll steps + * @param interpolator The interpolator to use. If you specify an interpolator, you must + * also set the duration. + * @see #setDuration(int) + */ + public void setInterpolator(Interpolator interpolator) { + changed = true; + mInterpolator = interpolator; + } + + /** + * Updates the action with given parameters. + * @param dx Pixels to scroll horizontally + * @param dy Pixels to scroll vertically + * @param duration Duration of the animation in milliseconds + * @param interpolator Interpolator to be used when calculating scroll position in each + * animation step + */ + public void update(int dx, int dy, int duration, Interpolator interpolator) { + mDx = dx; + mDy = dy; + mDuration = duration; + mInterpolator = interpolator; + changed = true; + } + } + } + + static class AdapterDataObservable extends Observable { + public boolean hasObservers() { + return !mObservers.isEmpty(); + } + + public void notifyChanged() { + // since onChanged() is implemented by the app, it could do anything, including + // removing itself from {@link mObservers} - and that could cause problems if + // an iterator is used on the ArrayList {@link mObservers}. + // to avoid such problems, just march thru the list in the reverse order. + for (int i = mObservers.size() - 1; i >= 0; i--) { + mObservers.get(i).onChanged(); + } + } + + public void notifyItemRangeChanged(int positionStart, int itemCount) { + // since onItemRangeChanged() is implemented by the app, it could do anything, including + // removing itself from {@link mObservers} - and that could cause problems if + // an iterator is used on the ArrayList {@link mObservers}. + // to avoid such problems, just march thru the list in the reverse order. + for (int i = mObservers.size() - 1; i >= 0; i--) { + mObservers.get(i).onItemRangeChanged(positionStart, itemCount); + } + } + + public void notifyItemRangeInserted(int positionStart, int itemCount) { + // since onItemRangeInserted() is implemented by the app, it could do anything, + // including removing itself from {@link mObservers} - and that could cause problems if + // an iterator is used on the ArrayList {@link mObservers}. + // to avoid such problems, just march thru the list in the reverse order. + for (int i = mObservers.size() - 1; i >= 0; i--) { + mObservers.get(i).onItemRangeInserted(positionStart, itemCount); + } + } + + public void notifyItemRangeRemoved(int positionStart, int itemCount) { + // since onItemRangeRemoved() is implemented by the app, it could do anything, including + // removing itself from {@link mObservers} - and that could cause problems if + // an iterator is used on the ArrayList {@link mObservers}. + // to avoid such problems, just march thru the list in the reverse order. + for (int i = mObservers.size() - 1; i >= 0; i--) { + mObservers.get(i).onItemRangeRemoved(positionStart, itemCount); + } + } + + public void notifyItemMoved(int fromPosition, int toPosition) { + for (int i = mObservers.size() - 1; i >= 0; i--) { + mObservers.get(i).onItemRangeMoved(fromPosition, toPosition, 1); + } + } + } + + static class SavedState extends android.view.View.BaseSavedState { + + Parcelable mLayoutState; + + /** + * called by CREATOR + */ + SavedState(Parcel in) { + super(in); + mLayoutState = in.readParcelable(LayoutManager.class.getClassLoader()); + } + + /** + * Called by onSaveInstanceState + */ + SavedState(Parcelable superState) { + super(superState); + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + super.writeToParcel(dest, flags); + dest.writeParcelable(mLayoutState, 0); + } + + private void copyFrom(SavedState other) { + mLayoutState = other.mLayoutState; + } + + public static final Parcelable.Creator CREATOR + = new Parcelable.Creator() { + @Override + public SavedState createFromParcel(Parcel in) { + return new SavedState(in); + } + + @Override + public SavedState[] newArray(int size) { + return new SavedState[size]; + } + }; + } + /** + *

Contains useful information about the current RecyclerView state like target scroll + * position or view focus. State object can also keep arbitrary data, identified by resource + * ids.

+ *

Often times, RecyclerView components will need to pass information between each other. + * To provide a well defined data bus between components, RecyclerView passes the same State + * object to component callbacks and these components can use it to exchange data.

+ *

If you implement custom components, you can use State's put/get/remove methods to pass + * data between your components without needing to manage their lifecycles.

+ */ + public static class State { + + private int mTargetPosition = RecyclerView.NO_POSITION; + ArrayMap mPreLayoutHolderMap = + new ArrayMap(); + ArrayMap mPostLayoutHolderMap = + new ArrayMap(); + // nullable + ArrayMap mOldChangedHolders = new ArrayMap(); + + // we use this like a set + final List mDisappearingViewsInLayoutPass = new ArrayList(); + + private SparseArray mData; + + /** + * Number of items adapter has. + */ + int mItemCount = 0; + + /** + * Number of items adapter had in the previous layout. + */ + private int mPreviousLayoutItemCount = 0; + + /** + * Number of items that were NOT laid out but has been deleted from the adapter after the + * previous layout. + */ + private int mDeletedInvisibleItemCountSincePreviousLayout = 0; + + private boolean mStructureChanged = false; + + private boolean mInPreLayout = false; + + private boolean mRunSimpleAnimations = false; + + private boolean mRunPredictiveAnimations = false; + + State reset() { + mTargetPosition = RecyclerView.NO_POSITION; + if (mData != null) { + mData.clear(); + } + mItemCount = 0; + mStructureChanged = false; + return this; + } + + public boolean isPreLayout() { + return mInPreLayout; + } + + /** + * Returns whether RecyclerView will run predictive animations in this layout pass + * or not. + * + * @return true if RecyclerView is calculating predictive animations to be run at the end + * of the layout pass. + */ + public boolean willRunPredictiveAnimations() { + return mRunPredictiveAnimations; + } + + /** + * Returns whether RecyclerView will run simple animations in this layout pass + * or not. + * + * @return true if RecyclerView is calculating simple animations to be run at the end of + * the layout pass. + */ + public boolean willRunSimpleAnimations() { + return mRunSimpleAnimations; + } + + /** + * Removes the mapping from the specified id, if there was any. + * @param resourceId Id of the resource you want to remove. It is suggested to use R.id.* to + * preserve cross functionality and avoid conflicts. + */ + public void remove(int resourceId) { + if (mData == null) { + return; + } + mData.remove(resourceId); + } + + /** + * Gets the Object mapped from the specified id, or null + * if no such data exists. + * + * @param resourceId Id of the resource you want to remove. It is suggested to use R.id.* + * to + * preserve cross functionality and avoid conflicts. + */ + public T get(int resourceId) { + if (mData == null) { + return null; + } + return (T) mData.get(resourceId); + } + + /** + * Adds a mapping from the specified id to the specified value, replacing the previous + * mapping from the specified key if there was one. + * + * @param resourceId Id of the resource you want to add. It is suggested to use R.id.* to + * preserve cross functionality and avoid conflicts. + * @param data The data you want to associate with the resourceId. + */ + public void put(int resourceId, Object data) { + if (mData == null) { + mData = new SparseArray(); + } + mData.put(resourceId, data); + } + + /** + * If scroll is triggered to make a certain item visible, this value will return the + * adapter index of that item. + * @return Adapter index of the target item or + * {@link RecyclerView#NO_POSITION} if there is no target + * position. + */ + public int getTargetScrollPosition() { + return mTargetPosition; + } + + /** + * Returns if current scroll has a target position. + * @return true if scroll is being triggered to make a certain position visible + * @see #getTargetScrollPosition() + */ + public boolean hasTargetScrollPosition() { + return mTargetPosition != RecyclerView.NO_POSITION; + } + + /** + * @return true if the structure of the data set has changed since the last call to + * onLayoutChildren, false otherwise + */ + public boolean didStructureChange() { + return mStructureChanged; + } + + /** + * Returns the total number of items that can be laid out. Note that this number is not + * necessarily equal to the number of items in the adapter, so you should always use this + * number for your position calculations and never access the adapter directly. + *

+ * RecyclerView listens for Adapter's notify events and calculates the effects of adapter + * data changes on existing Views. These calculations are used to decide which animations + * should be run. + *

+ * To support predictive animations, RecyclerView may rewrite or reorder Adapter changes to + * present the correct state to LayoutManager in pre-layout pass. + *

+ * For example, a newly added item is not included in pre-layout item count because + * pre-layout reflects the contents of the adapter before the item is added. Behind the + * scenes, RecyclerView offsets {@link Recycler#getViewForPosition(int)} calls such that + * LayoutManager does not know about the new item's existence in pre-layout. The item will + * be available in second layout pass and will be included in the item count. Similar + * adjustments are made for moved and removed items as well. + *

+ * You can get the adapter's item count via {@link LayoutManager#getItemCount()} method. + * + * @return The number of items currently available + * @see LayoutManager#getItemCount() + */ + public int getItemCount() { + return mInPreLayout ? + (mPreviousLayoutItemCount - mDeletedInvisibleItemCountSincePreviousLayout) : + mItemCount; + } + + void onViewRecycled(ViewHolder holder) { + mPreLayoutHolderMap.remove(holder); + mPostLayoutHolderMap.remove(holder); + if (mOldChangedHolders != null) { + removeFrom(mOldChangedHolders, holder); + } + mDisappearingViewsInLayoutPass.remove(holder.itemView); + // holder cannot be in new list. + } + + public void onViewIgnored(ViewHolder holder) { + onViewRecycled(holder); + } + + private void removeFrom(ArrayMap holderMap, ViewHolder holder) { + for (int i = holderMap.size() - 1; i >= 0; i --) { + if (holder == holderMap.valueAt(i)) { + holderMap.removeAt(i); + return; + } + } + } + + void removeFromDisappearingList(View child) { + mDisappearingViewsInLayoutPass.remove(child); + } + + void addToDisappearingList(View child) { + if (!mDisappearingViewsInLayoutPass.contains(child)) { + mDisappearingViewsInLayoutPass.add(child); + } + } + + @Override + public String toString() { + return "State{" + + "mTargetPosition=" + mTargetPosition + + ", mPreLayoutHolderMap=" + mPreLayoutHolderMap + + ", mPostLayoutHolderMap=" + mPostLayoutHolderMap + + ", mData=" + mData + + ", mItemCount=" + mItemCount + + ", mPreviousLayoutItemCount=" + mPreviousLayoutItemCount + + ", mDeletedInvisibleItemCountSincePreviousLayout=" + + mDeletedInvisibleItemCountSincePreviousLayout + + ", mStructureChanged=" + mStructureChanged + + ", mInPreLayout=" + mInPreLayout + + ", mRunSimpleAnimations=" + mRunSimpleAnimations + + ", mRunPredictiveAnimations=" + mRunPredictiveAnimations + + '}'; + } + } + + /** + * Internal listener that manages items after animations finish. This is how items are + * retained (not recycled) during animations, but allowed to be recycled afterwards. + * It depends on the contract with the ItemAnimator to call the appropriate dispatch*Finished() + * method on the animator's listener when it is done animating any item. + */ + private class ItemAnimatorRestoreListener implements ItemAnimator.ItemAnimatorListener { + + @Override + public void onRemoveFinished(ViewHolder item) { + item.setIsRecyclable(true); + if (!removeAnimatingView(item.itemView) && item.isTmpDetached()) { + removeDetachedView(item.itemView, false); + } + } + + @Override + public void onAddFinished(ViewHolder item) { + item.setIsRecyclable(true); + if (!item.shouldBeKeptAsChild()) { + removeAnimatingView(item.itemView); + } + } + + @Override + public void onMoveFinished(ViewHolder item) { + item.setIsRecyclable(true); + if (!item.shouldBeKeptAsChild()) { + removeAnimatingView(item.itemView); + } + } + + @Override + public void onChangeFinished(ViewHolder item) { + item.setIsRecyclable(true); + /** + * We check both shadowed and shadowing because a ViewHolder may get both roles at the + * same time. + * + * Assume this flow: + * item X is represented by VH_1. Then itemX changes, so we create VH_2 . + * RV sets the following and calls item animator: + * VH_1.shadowed = VH_2; + * VH_1.mChanged = true; + * VH_2.shadowing =VH_1; + * + * Then, before the first change finishes, item changes again so we create VH_3. + * RV sets the following and calls item animator: + * VH_2.shadowed = VH_3 + * VH_2.mChanged = true + * VH_3.shadowing = VH_2 + * + * Because VH_2 already has an animation, it will be cancelled. At this point VH_2 has + * both shadowing and shadowed fields set. Shadowing information is obsolete now + * because the first animation where VH_2 is newViewHolder is not valid anymore. + * We ended up in this case because VH_2 played both roles. On the other hand, + * we DO NOT want to clear its changed flag. + * + * If second change was simply reverting first change, we would find VH_1 in + * {@link Recycler#getScrapViewForPosition(int, int, boolean)} and recycle it before + * re-using + */ + if (item.mShadowedHolder != null && item.mShadowingHolder == null) { // old vh + item.mShadowedHolder = null; + item.setFlags(~ViewHolder.FLAG_CHANGED, item.mFlags); + } + // always null this because an OldViewHolder can never become NewViewHolder w/o being + // recycled. + item.mShadowingHolder = null; + if (!item.shouldBeKeptAsChild()) { + removeAnimatingView(item.itemView); + } + } + }; + + /** + * This class defines the animations that take place on items as changes are made + * to the adapter. + * + * Subclasses of ItemAnimator can be used to implement custom animations for actions on + * ViewHolder items. The RecyclerView will manage retaining these items while they + * are being animated, but implementors must call the appropriate "Starting" + * ({@link #dispatchRemoveStarting(ViewHolder)}, {@link #dispatchMoveStarting(ViewHolder)}, + * {@link #dispatchChangeStarting(ViewHolder, boolean)}, or + * {@link #dispatchAddStarting(ViewHolder)}) + * and "Finished" ({@link #dispatchRemoveFinished(ViewHolder)}, + * {@link #dispatchMoveFinished(ViewHolder)}, + * {@link #dispatchChangeFinished(ViewHolder, boolean)}, + * or {@link #dispatchAddFinished(ViewHolder)}) methods when each item animation is + * being started and ended. + * + *

By default, RecyclerView uses {@link DefaultItemAnimator}

+ * + * @see #setItemAnimator(ItemAnimator) + */ + public static abstract class ItemAnimator { + + private ItemAnimatorListener mListener = null; + private ArrayList mFinishedListeners = + new ArrayList(); + + private long mAddDuration = 120; + private long mRemoveDuration = 120; + private long mMoveDuration = 250; + private long mChangeDuration = 250; + + private boolean mSupportsChangeAnimations = true; + + /** + * Gets the current duration for which all move animations will run. + * + * @return The current move duration + */ + public long getMoveDuration() { + return mMoveDuration; + } + + /** + * Sets the duration for which all move animations will run. + * + * @param moveDuration The move duration + */ + public void setMoveDuration(long moveDuration) { + mMoveDuration = moveDuration; + } + + /** + * Gets the current duration for which all add animations will run. + * + * @return The current add duration + */ + public long getAddDuration() { + return mAddDuration; + } + + /** + * Sets the duration for which all add animations will run. + * + * @param addDuration The add duration + */ + public void setAddDuration(long addDuration) { + mAddDuration = addDuration; + } + + /** + * Gets the current duration for which all remove animations will run. + * + * @return The current remove duration + */ + public long getRemoveDuration() { + return mRemoveDuration; + } + + /** + * Sets the duration for which all remove animations will run. + * + * @param removeDuration The remove duration + */ + public void setRemoveDuration(long removeDuration) { + mRemoveDuration = removeDuration; + } + + /** + * Gets the current duration for which all change animations will run. + * + * @return The current change duration + */ + public long getChangeDuration() { + return mChangeDuration; + } + + /** + * Sets the duration for which all change animations will run. + * + * @param changeDuration The change duration + */ + public void setChangeDuration(long changeDuration) { + mChangeDuration = changeDuration; + } + + /** + * Returns whether this ItemAnimator supports animations of change events. + * + * @return true if change animations are supported, false otherwise + */ + 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 are 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. + * + * @see Adapter#notifyItemChanged(int) + * @see Adapter#notifyItemRangeChanged(int, int) + * + * @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. + */ + public void setSupportsChangeAnimations(boolean supportsChangeAnimations) { + mSupportsChangeAnimations = supportsChangeAnimations; + } + + /** + * Internal only: + * Sets the listener that must be called when the animator is finished + * animating the item (or immediately if no animation happens). This is set + * internally and is not intended to be set by external code. + * + * @param listener The listener that must be called. + */ + void setListener(ItemAnimatorListener listener) { + mListener = listener; + } + + /** + * Called when there are pending animations waiting to be started. This state + * is governed by the return values from {@link #animateAdd(ViewHolder) animateAdd()}, + * {@link #animateMove(ViewHolder, int, int, int, int) animateMove()}, and + * {@link #animateRemove(ViewHolder) animateRemove()}, which inform the + * RecyclerView that the ItemAnimator wants to be called later to start the + * associated animations. runPendingAnimations() will be scheduled to be run + * on the next frame. + */ + abstract public void runPendingAnimations(); + + /** + * 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()}. + * + *

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.

+ * + * @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()}. + * + *

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.

+ * + * @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)}. + *

+ * Implementers can choose whether and how to animate changes, but must always call + * {@link #dispatchChangeFinished(ViewHolder, boolean)} for each non-null ViewHolder, + * 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 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 + */ + public final void dispatchRemoveFinished(ViewHolder item) { + onRemoveFinished(item); + if (mListener != null) { + mListener.onRemoveFinished(item); + } + } + + /** + * Method to be called by subclasses when a move animation is done. + * + * @param item The item which has been moved + */ + public final void dispatchMoveFinished(ViewHolder item) { + onMoveFinished(item); + if (mListener != null) { + mListener.onMoveFinished(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); + if (mListener != null) { + mListener.onAddFinished(item); + } + } + + /** + * Method to be called by subclasses when a change animation is done. + * + * @see #animateChange(ViewHolder, ViewHolder, int, int, int, int) + * @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 dispatchChangeFinished(ViewHolder item, boolean oldItem) { + onChangeFinished(item, oldItem); + if (mListener != null) { + mListener.onChangeFinished(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); + } + + /** + * Method called when an animation on a view should be ended immediately. + * This could happen when other events, like scrolling, occur, so that + * animating views can be quickly put into their proper end locations. + * Implementations should ensure that any animations running on the item + * are canceled and affected properties are set to their end values. + * Also, appropriate dispatch methods (e.g., {@link #dispatchAddFinished(ViewHolder)} + * should be called since the animations are effectively done when this + * method is called. + * + * @param item The item for which an animation should be stopped. + */ + abstract public void endAnimation(ViewHolder item); + + /** + * Method called when all item animations should be ended immediately. + * This could happen when other events, like scrolling, occur, so that + * animating views can be quickly put into their proper end locations. + * Implementations should ensure that any animations running on any items + * are canceled and affected properties are set to their end values. + * Also, appropriate dispatch methods (e.g., {@link #dispatchAddFinished(ViewHolder)} + * should be called since the animations are effectively done when this + * method is called. + */ + abstract public void endAnimations(); + + /** + * Method which returns whether there are any item animations currently running. + * This method can be used to determine whether to delay other actions until + * animations end. + * + * @return true if there are any item animations currently running, false otherwise. + */ + abstract public boolean isRunning(); + + /** + * Like {@link #isRunning()}, this method returns whether there are any item + * animations currently running. Addtionally, the listener passed in will be called + * when there are no item animations running, either immediately (before the method + * returns) if no animations are currently running, or when the currently running + * animations are {@link #dispatchAnimationsFinished() finished}. + * + *

Note that the listener is transient - it is either called immediately and not + * stored at all, or stored only until it is called when running animations + * are finished sometime later.

+ * + * @param listener A listener to be called immediately if no animations are running + * or later when currently-running animations have finished. A null listener is + * equivalent to calling {@link #isRunning()}. + * @return true if there are any item animations currently running, false otherwise. + */ + public final boolean isRunning(ItemAnimatorFinishedListener listener) { + boolean running = isRunning(); + if (listener != null) { + if (!running) { + listener.onAnimationsFinished(); + } else { + mFinishedListeners.add(listener); + } + } + return running; + } + + /** + * The interface to be implemented by listeners to animation events from this + * ItemAnimator. This is used internally and is not intended for developers to + * create directly. + */ + interface ItemAnimatorListener { + void onRemoveFinished(ViewHolder item); + void onAddFinished(ViewHolder item); + void onMoveFinished(ViewHolder item); + void onChangeFinished(ViewHolder item); + } + + /** + * This method should be called by ItemAnimator implementations to notify + * any listeners that all pending and active item animations are finished. + */ + public final void dispatchAnimationsFinished() { + final int count = mFinishedListeners.size(); + for (int i = 0; i < count; ++i) { + mFinishedListeners.get(i).onAnimationsFinished(); + } + mFinishedListeners.clear(); + } + + /** + * This interface is used to inform listeners when all pending or running animations + * in an ItemAnimator are finished. This can be used, for example, to delay an action + * in a data set until currently-running animations are complete. + * + * @see #isRunning(ItemAnimatorFinishedListener) + */ + public interface ItemAnimatorFinishedListener { + void onAnimationsFinished(); + } + + /** + * 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. + */ + 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. + */ + 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. + */ + 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. + */ + 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) {} + + } + + /** + * Internal data structure that holds information about an item's bounds. + * This information is used in calculating item animations. + */ + private static class ItemHolderInfo { + ViewHolder holder; + int left, top, right, bottom; + + ItemHolderInfo(ViewHolder holder, int left, int top, int right, int bottom) { + this.holder = holder; + this.left = left; + this.top = top; + this.right = right; + this.bottom = bottom; + } + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/android/support/widget/RecyclerViewAccessibilityDelegate.java b/TMessagesProj/src/main/java/org/telegram/android/support/widget/RecyclerViewAccessibilityDelegate.java new file mode 100644 index 000000000..f1cd3b0b2 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/android/support/widget/RecyclerViewAccessibilityDelegate.java @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2014 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.android.support.widget; + +import android.os.Bundle; +import android.support.v4.view.AccessibilityDelegateCompat; +import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat; +import android.view.View; +import android.view.accessibility.AccessibilityEvent; + +/** + * The AccessibilityDelegate used by RecyclerView. + *

+ * This class handles basic accessibility actions and delegates them to LayoutManager. + */ +public class RecyclerViewAccessibilityDelegate extends AccessibilityDelegateCompat { + final RecyclerView mRecyclerView; + + + public RecyclerViewAccessibilityDelegate(RecyclerView recyclerView) { + mRecyclerView = recyclerView; + } + + private boolean shouldIgnore() { + return mRecyclerView.hasPendingAdapterUpdates(); + } + + @Override + public boolean performAccessibilityAction(View host, int action, Bundle args) { + if (super.performAccessibilityAction(host, action, args)) { + return true; + } + if (!shouldIgnore() && mRecyclerView.getLayoutManager() != null) { + return mRecyclerView.getLayoutManager().performAccessibilityAction(action, args); + } + + return false; + } + + @Override + public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfoCompat info) { + super.onInitializeAccessibilityNodeInfo(host, info); + info.setClassName(RecyclerView.class.getName()); + if (!shouldIgnore() && mRecyclerView.getLayoutManager() != null) { + mRecyclerView.getLayoutManager().onInitializeAccessibilityNodeInfo(info); + } + } + + @Override + public void onInitializeAccessibilityEvent(View host, AccessibilityEvent event) { + super.onInitializeAccessibilityEvent(host, event); + event.setClassName(RecyclerView.class.getName()); + if (host instanceof RecyclerView && !shouldIgnore()) { + RecyclerView rv = (RecyclerView) host; + if (rv.getLayoutManager() != null) { + rv.getLayoutManager().onInitializeAccessibilityEvent(event); + } + } + } + + AccessibilityDelegateCompat getItemDelegate() { + return mItemDelegate; + } + + final AccessibilityDelegateCompat mItemDelegate = new AccessibilityDelegateCompat() { + @Override + public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfoCompat info) { + super.onInitializeAccessibilityNodeInfo(host, info); + if (!shouldIgnore() && mRecyclerView.getLayoutManager() != null) { + mRecyclerView.getLayoutManager(). + onInitializeAccessibilityNodeInfoForItem(host, info); + } + } + + @Override + public boolean performAccessibilityAction(View host, int action, Bundle args) { + if (super.performAccessibilityAction(host, action, args)) { + return true; + } + if (!shouldIgnore() && mRecyclerView.getLayoutManager() != null) { + return mRecyclerView.getLayoutManager(). + performAccessibilityActionForItem(host, action, args); + } + return false; + } + }; +} diff --git a/TMessagesProj/src/main/java/org/telegram/android/support/widget/ScrollbarHelper.java b/TMessagesProj/src/main/java/org/telegram/android/support/widget/ScrollbarHelper.java new file mode 100644 index 000000000..9df5bba2f --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/android/support/widget/ScrollbarHelper.java @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2014 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.android.support.widget; + +import android.view.View; + +/** + * A helper class to do scroll offset calculations. + */ +class ScrollbarHelper { + + /** + * @param startChild View closest to start of the list. (top or left) + * @param endChild View closest to end of the list (bottom or right) + */ + static int computeScrollOffset(RecyclerView.State state, OrientationHelper orientation, + View startChild, View endChild, RecyclerView.LayoutManager lm, + boolean smoothScrollbarEnabled, boolean reverseLayout) { + if (lm.getChildCount() == 0 || state.getItemCount() == 0 || startChild == null || + endChild == null) { + return 0; + } + final int minPosition = Math.min(lm.getPosition(startChild), + lm.getPosition(endChild)); + final int maxPosition = Math.max(lm.getPosition(startChild), + lm.getPosition(endChild)); + final int itemsBefore = reverseLayout + ? Math.max(0, state.getItemCount() - maxPosition - 1) + : Math.max(0, minPosition); + if (!smoothScrollbarEnabled) { + return itemsBefore; + } + final int laidOutArea = Math.abs(orientation.getDecoratedEnd(endChild) - + orientation.getDecoratedStart(startChild)); + final int itemRange = Math.abs(lm.getPosition(startChild) - + lm.getPosition(endChild)) + 1; + final float avgSizePerRow = (float) laidOutArea / itemRange; + + return Math.round(itemsBefore * avgSizePerRow + (orientation.getStartAfterPadding() + - orientation.getDecoratedStart(startChild))); + } + + /** + * @param startChild View closest to start of the list. (top or left) + * @param endChild View closest to end of the list (bottom or right) + */ + static int computeScrollExtent(RecyclerView.State state, OrientationHelper orientation, + View startChild, View endChild, RecyclerView.LayoutManager lm, + boolean smoothScrollbarEnabled) { + if (lm.getChildCount() == 0 || state.getItemCount() == 0 || startChild == null || + endChild == null) { + return 0; + } + if (!smoothScrollbarEnabled) { + return Math.abs(lm.getPosition(startChild) - lm.getPosition(endChild)) + 1; + } + final int extend = orientation.getDecoratedEnd(endChild) + - orientation.getDecoratedStart(startChild); + return Math.min(orientation.getTotalSpace(), extend); + } + + /** + * @param startChild View closest to start of the list. (top or left) + * @param endChild View closest to end of the list (bottom or right) + */ + static int computeScrollRange(RecyclerView.State state, OrientationHelper orientation, + View startChild, View endChild, RecyclerView.LayoutManager lm, + boolean smoothScrollbarEnabled) { + if (lm.getChildCount() == 0 || state.getItemCount() == 0 || startChild == null || + endChild == null) { + return 0; + } + if (!smoothScrollbarEnabled) { + return state.getItemCount(); + } + // smooth scrollbar enabled. try to estimate better. + final int laidOutArea = orientation.getDecoratedEnd(endChild) - + orientation.getDecoratedStart(startChild); + final int laidOutRange = Math.abs(lm.getPosition(startChild) - + lm.getPosition(endChild)) + + 1; + // estimate a size for full list. + return (int) ((float) laidOutArea / laidOutRange * state.getItemCount()); + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/android/support/widget/StaggeredGridLayoutManager.java b/TMessagesProj/src/main/java/org/telegram/android/support/widget/StaggeredGridLayoutManager.java new file mode 100644 index 000000000..d28434a60 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/android/support/widget/StaggeredGridLayoutManager.java @@ -0,0 +1,2763 @@ +/* + * Copyright (C) 2014 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.android.support.widget; + +import android.content.Context; +import android.graphics.PointF; +import android.graphics.Rect; +import android.os.Parcel; +import android.os.Parcelable; +import android.support.v4.view.ViewCompat; +import android.support.v4.view.accessibility.AccessibilityEventCompat; +import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat; +import android.support.v4.view.accessibility.AccessibilityRecordCompat; +import android.util.AttributeSet; +import android.util.Log; +import android.view.View; +import android.view.ViewGroup; +import android.view.accessibility.AccessibilityEvent; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.BitSet; +import java.util.List; + +import static org.telegram.android.support.widget.LayoutState.LAYOUT_START; +import static org.telegram.android.support.widget.LayoutState.LAYOUT_END; +import static org.telegram.android.support.widget.LayoutState.ITEM_DIRECTION_HEAD; +import static org.telegram.android.support.widget.LayoutState.ITEM_DIRECTION_TAIL; +import static org.telegram.android.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. + *

+ * Staggered grids are likely to have gaps at the edges of the layout. To avoid these gaps, + * StaggeredGridLayoutManager can offset spans independently or move items between spans. You can + * control this behavior via {@link #setGapStrategy(int)}. + */ +public class StaggeredGridLayoutManager extends RecyclerView.LayoutManager { + + public static final String TAG = "StaggeredGridLayoutManager"; + + private static final boolean DEBUG = false; + + public static final int HORIZONTAL = OrientationHelper.HORIZONTAL; + + public static final int VERTICAL = OrientationHelper.VERTICAL; + + /** + * Does not do anything to hide gaps. + */ + public static final int GAP_HANDLING_NONE = 0; + + @Deprecated + public static final int GAP_HANDLING_LAZY = 1; + + /** + * When scroll state is changed to {@link RecyclerView#SCROLL_STATE_IDLE}, StaggeredGrid will + * check if there are gaps in the because of full span items. If it finds, it will re-layout + * and move items to correct positions with animations. + *

+ * For example, if LayoutManager ends up with the following layout due to adapter changes: + *

+     * AAA
+     * _BC
+     * DDD
+     * 
+ *

+ * It will animate to the following state: + *

+     * AAA
+     * BC_
+     * DDD
+     * 
+ */ + public static final int GAP_HANDLING_MOVE_ITEMS_BETWEEN_SPANS = 2; + + private static final int INVALID_OFFSET = Integer.MIN_VALUE; + + /** + * Number of spans + */ + private int mSpanCount = -1; + + private Span[] mSpans; + + /** + * Primary orientation is the layout's orientation, secondary orientation is the orientation + * for spans. Having both makes code much cleaner for calculations. + */ + OrientationHelper mPrimaryOrientation; + OrientationHelper mSecondaryOrientation; + + private int mOrientation; + + /** + * The width or height per span, depending on the orientation. + */ + private int mSizePerSpan; + + private LayoutState mLayoutState; + + private boolean mReverseLayout = false; + + /** + * Aggregated reverse layout value that takes RTL into account. + */ + boolean mShouldReverseLayout = false; + + /** + * Temporary variable used during fill method to check which spans needs to be filled. + */ + private BitSet mRemainingSpans; + + /** + * When LayoutManager needs to scroll to a position, it sets this variable and requests a + * layout which will check this variable and re-layout accordingly. + */ + int mPendingScrollPosition = NO_POSITION; + + /** + * Used to keep the offset value when {@link #scrollToPositionWithOffset(int, int)} is + * called. + */ + int mPendingScrollPositionOffset = INVALID_OFFSET; + + /** + * Keeps the mapping between the adapter positions and spans. This is necessary to provide + * a consistent experience when user scrolls the list. + */ + LazySpanLookup mLazySpanLookup = new LazySpanLookup(); + + /** + * how we handle gaps in UI. + */ + private int mGapStrategy = GAP_HANDLING_MOVE_ITEMS_BETWEEN_SPANS; + + /** + * Saved state needs this information to properly layout on restore. + */ + private boolean mLastLayoutFromEnd; + + /** + * Saved state and onLayout needs this information to re-layout properly + */ + private boolean mLastLayoutRTL; + + /** + * SavedState is not handled until a layout happens. This is where we keep it until next + * layout. + */ + private SavedState mPendingSavedState; + + /** + * Re-used measurement specs. updated by onLayout. + */ + private int mFullSizeSpec, mWidthSpec, mHeightSpec; + + /** + * Re-used rectangle to get child decor offsets. + */ + private final Rect mTmpRect = new Rect(); + + /** + * Re-used anchor info. + */ + private final AnchorInfo mAnchorInfo = new AnchorInfo(); + + /** + * If a full span item is invalid / or created in reverse direction; it may create gaps in + * the UI. While laying out, if such case is detected, we set this flag. + *

+ * After scrolling stops, we check this flag and if it is set, re-layout. + */ + private boolean mLaidOutInvalidFullSpan = false; + + /** + * Works the same way as {@link android.widget.AbsListView#setSmoothScrollbarEnabled(boolean)}. + * see {@link android.widget.AbsListView#setSmoothScrollbarEnabled(boolean)} + */ + private boolean mSmoothScrollbarEnabled = true; + + private final Runnable mCheckForGapsRunnable = new Runnable() { + @Override + public void run() { + checkForGaps(); + } + }; + + /** + * Creates a StaggeredGridLayoutManager with given parameters. + * + * @param spanCount If orientation is vertical, spanCount is number of columns. If + * orientation is horizontal, spanCount is number of rows. + * @param orientation {@link #VERTICAL} or {@link #HORIZONTAL} + */ + public StaggeredGridLayoutManager(int spanCount, int orientation) { + mOrientation = orientation; + setSpanCount(spanCount); + } + + /** + * Checks for gaps in the UI that may be caused by adapter changes. + *

+ * When a full span item is laid out in reverse direction, it sets a flag which we check when + * scroll is stopped (or re-layout happens) and re-layout after first valid item. + */ + private boolean checkForGaps() { + if (getChildCount() == 0 || mGapStrategy == GAP_HANDLING_NONE || !isAttachedToWindow()) { + return false; + } + final int minPos, maxPos; + if (mShouldReverseLayout) { + minPos = getLastChildPosition(); + maxPos = getFirstChildPosition(); + } else { + minPos = getFirstChildPosition(); + maxPos = getLastChildPosition(); + } + if (minPos == 0) { + View gapView = hasGapsToFix(); + if (gapView != null) { + mLazySpanLookup.clear(); + requestSimpleAnimationsInNextLayout(); + requestLayout(); + return true; + } + } + if (!mLaidOutInvalidFullSpan) { + return false; + } + int invalidGapDir = mShouldReverseLayout ? LAYOUT_START : LAYOUT_END; + final LazySpanLookup.FullSpanItem invalidFsi = mLazySpanLookup + .getFirstFullSpanItemInRange(minPos, maxPos + 1, invalidGapDir, true); + if (invalidFsi == null) { + mLaidOutInvalidFullSpan = false; + mLazySpanLookup.forceInvalidateAfter(maxPos + 1); + return false; + } + final LazySpanLookup.FullSpanItem validFsi = mLazySpanLookup + .getFirstFullSpanItemInRange(minPos, invalidFsi.mPosition, + invalidGapDir * -1, true); + if (validFsi == null) { + mLazySpanLookup.forceInvalidateAfter(invalidFsi.mPosition); + } else { + mLazySpanLookup.forceInvalidateAfter(validFsi.mPosition + 1); + } + requestSimpleAnimationsInNextLayout(); + requestLayout(); + return true; + } + + @Override + public void onScrollStateChanged(int state) { + if (state == RecyclerView.SCROLL_STATE_IDLE) { + checkForGaps(); + } + } + + @Override + public void onDetachedFromWindow(RecyclerView view, RecyclerView.Recycler recycler) { + removeCallbacks(mCheckForGapsRunnable); + for (int i = 0; i < mSpanCount; i++) { + mSpans[i].clear(); + } + } + + /** + * Checks for gaps if we've reached to the top of the list. + *

+ * Intermediate gaps created by full span items are tracked via mLaidOutInvalidFullSpan field. + */ + View hasGapsToFix() { + int startChildIndex = 0; + int endChildIndex = getChildCount() - 1; + BitSet mSpansToCheck = new BitSet(mSpanCount); + mSpansToCheck.set(0, mSpanCount, true); + + final int firstChildIndex, childLimit; + final int preferredSpanDir = mOrientation == VERTICAL && isLayoutRTL() ? 1 : -1; + + if (mShouldReverseLayout) { + firstChildIndex = endChildIndex; + childLimit = startChildIndex - 1; + } else { + firstChildIndex = startChildIndex; + childLimit = endChildIndex + 1; + } + final int nextChildDiff = firstChildIndex < childLimit ? 1 : -1; + for (int i = firstChildIndex; i != childLimit; i += nextChildDiff) { + View child = getChildAt(i); + LayoutParams lp = (LayoutParams) child.getLayoutParams(); + if (mSpansToCheck.get(lp.mSpan.mIndex)) { + if (checkSpanForGap(lp.mSpan)) { + return child; + } + mSpansToCheck.clear(lp.mSpan.mIndex); + } + if (lp.mFullSpan) { + continue; // quick reject + } + + if (i + nextChildDiff != childLimit) { + View nextChild = getChildAt(i + nextChildDiff); + boolean compareSpans = false; + if (mShouldReverseLayout) { + // ensure child's end is below nextChild's end + int myEnd = mPrimaryOrientation.getDecoratedEnd(child); + int nextEnd = mPrimaryOrientation.getDecoratedEnd(nextChild); + if (myEnd < nextEnd) { + return child;//i should have a better position + } else if (myEnd == nextEnd) { + compareSpans = true; + } + } else { + int myStart = mPrimaryOrientation.getDecoratedStart(child); + int nextStart = mPrimaryOrientation.getDecoratedStart(nextChild); + if (myStart > nextStart) { + return child;//i should have a better position + } else if (myStart == nextStart) { + compareSpans = true; + } + } + if (compareSpans) { + // equal, check span indices. + LayoutParams nextLp = (LayoutParams) nextChild.getLayoutParams(); + if (lp.mSpan.mIndex - nextLp.mSpan.mIndex < 0 != preferredSpanDir < 0) { + return child; + } + } + } + } + // everything looks good + return null; + } + + private boolean checkSpanForGap(Span span) { + if (mShouldReverseLayout) { + if (span.getEndLine() < mPrimaryOrientation.getEndAfterPadding()) { + return true; + } + } else if (span.getStartLine() > mPrimaryOrientation.getStartAfterPadding()) { + return true; + } + return false; + } + + /** + * Sets the number of spans for the layout. This will invalidate all of the span assignments + * for Views. + *

+ * Calling this method will automatically result in a new layout request unless the spanCount + * parameter is equal to current span count. + * + * @param spanCount Number of spans to layout + */ + public void setSpanCount(int spanCount) { + assertNotInLayoutOrScroll(null); + if (spanCount != mSpanCount) { + invalidateSpanAssignments(); + mSpanCount = spanCount; + mRemainingSpans = new BitSet(mSpanCount); + mSpans = new Span[mSpanCount]; + for (int i = 0; i < mSpanCount; i++) { + mSpans[i] = new Span(i); + } + requestLayout(); + } + } + + /** + * Sets the orientation of the layout. StaggeredGridLayoutManager will do its best to keep + * scroll position if this method is called after views are laid out. + * + * @param orientation {@link #HORIZONTAL} or {@link #VERTICAL} + */ + public void setOrientation(int orientation) { + if (orientation != HORIZONTAL && orientation != VERTICAL) { + throw new IllegalArgumentException("invalid orientation."); + } + assertNotInLayoutOrScroll(null); + if (orientation == mOrientation) { + return; + } + mOrientation = orientation; + if (mPrimaryOrientation != null && mSecondaryOrientation != null) { + // swap + OrientationHelper tmp = mPrimaryOrientation; + mPrimaryOrientation = mSecondaryOrientation; + mSecondaryOrientation = tmp; + } + requestLayout(); + } + + /** + * Sets whether LayoutManager should start laying out items from the end of the UI. The order + * items are traversed is not affected by this call. + *

+ * For vertical layout, if it is set to true, first item will be at the bottom of + * the list. + *

+ * 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 from LTR. + * + * @param reverseLayout Whether layout should be in reverse or not + */ + public void setReverseLayout(boolean reverseLayout) { + assertNotInLayoutOrScroll(null); + if (mPendingSavedState != null && mPendingSavedState.mReverseLayout != reverseLayout) { + mPendingSavedState.mReverseLayout = reverseLayout; + } + mReverseLayout = reverseLayout; + requestLayout(); + } + + /** + * Returns the current gap handling strategy for StaggeredGridLayoutManager. + *

+ * Staggered grid may have gaps in the layout due to changes in the adapter. To avoid gaps, + * StaggeredGridLayoutManager provides 2 options. Check {@link #GAP_HANDLING_NONE} and + * {@link #GAP_HANDLING_MOVE_ITEMS_BETWEEN_SPANS} for details. + *

+ * By default, StaggeredGridLayoutManager uses {@link #GAP_HANDLING_MOVE_ITEMS_BETWEEN_SPANS}. + * + * @return Current gap handling strategy. + * @see #setGapStrategy(int) + * @see #GAP_HANDLING_NONE + * @see #GAP_HANDLING_MOVE_ITEMS_BETWEEN_SPANS + */ + public int getGapStrategy() { + return mGapStrategy; + } + + /** + * Sets the gap handling strategy for StaggeredGridLayoutManager. If the gapStrategy parameter + * is different than the current strategy, calling this method will trigger a layout request. + * + * @param gapStrategy The new gap handling strategy. Should be + * {@link #GAP_HANDLING_MOVE_ITEMS_BETWEEN_SPANS} or {@link + * #GAP_HANDLING_NONE}. + * @see #getGapStrategy() + */ + public void setGapStrategy(int gapStrategy) { + assertNotInLayoutOrScroll(null); + if (gapStrategy == mGapStrategy) { + return; + } + if (gapStrategy != GAP_HANDLING_NONE && + gapStrategy != GAP_HANDLING_MOVE_ITEMS_BETWEEN_SPANS) { + throw new IllegalArgumentException("invalid gap strategy. Must be GAP_HANDLING_NONE " + + "or GAP_HANDLING_MOVE_ITEMS_BETWEEN_SPANS"); + } + mGapStrategy = gapStrategy; + requestLayout(); + } + + @Override + public void assertNotInLayoutOrScroll(String message) { + if (mPendingSavedState == null) { + super.assertNotInLayoutOrScroll(message); + } + } + + /** + * Returns the number of spans laid out by StaggeredGridLayoutManager. + * + * @return Number of spans in the layout + */ + public int getSpanCount() { + return mSpanCount; + } + + /** + * For consistency, StaggeredGridLayoutManager keeps a mapping between spans and items. + *

+ * If you need to cancel current assignments, you can call this method which will clear all + * assignments and request a new layout. + */ + public void invalidateSpanAssignments() { + mLazySpanLookup.clear(); + requestLayout(); + } + + private void ensureOrientationHelper() { + if (mPrimaryOrientation == null) { + mPrimaryOrientation = OrientationHelper.createOrientationHelper(this, mOrientation); + mSecondaryOrientation = OrientationHelper + .createOrientationHelper(this, 1 - mOrientation); + mLayoutState = new LayoutState(); + } + } + + /** + * Calculates the views' layout order. (e.g. from end to start or start to end) + * RTL layout support is applied automatically. So if layout is RTL and + * {@link #getReverseLayout()} is {@code true}, elements will be laid out starting from left. + */ + private void resolveShouldLayoutReverse() { + // A == B is the same result, but we rather keep it readable + if (mOrientation == VERTICAL || !isLayoutRTL()) { + mShouldReverseLayout = mReverseLayout; + } else { + mShouldReverseLayout = !mReverseLayout; + } + } + + boolean isLayoutRTL() { + return getLayoutDirection() == ViewCompat.LAYOUT_DIRECTION_RTL; + } + + /** + * Returns whether views are laid out in reverse order or not. + *

+ * Not that this value is not affected by RecyclerView's layout direction. + * + * @return True if layout is reversed, false otherwise + * @see #setReverseLayout(boolean) + */ + public boolean getReverseLayout() { + return mReverseLayout; + } + @Override + public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) { + ensureOrientationHelper(); + + final AnchorInfo anchorInfo = mAnchorInfo; + anchorInfo.reset(); + + if (mPendingSavedState != null) { + applyPendingSavedState(anchorInfo); + } else { + resolveShouldLayoutReverse(); + anchorInfo.mLayoutFromEnd = mShouldReverseLayout; + } + + updateAnchorInfoForLayout(state, anchorInfo); + + if (mPendingSavedState == null) { + if (anchorInfo.mLayoutFromEnd != mLastLayoutFromEnd || + isLayoutRTL() != mLastLayoutRTL) { + mLazySpanLookup.clear(); + anchorInfo.mInvalidateOffsets = true; + } + } + + if (getChildCount() > 0 && (mPendingSavedState == null || + mPendingSavedState.mSpanOffsetsSize < 1)) { + if (anchorInfo.mInvalidateOffsets) { + for (int i = 0; i < mSpanCount; i++) { + // Scroll to position is set, clear. + mSpans[i].clear(); + if (anchorInfo.mOffset != INVALID_OFFSET) { + mSpans[i].setLine(anchorInfo.mOffset); + } + } + } else { + for (int i = 0; i < mSpanCount; i++) { + mSpans[i].cacheReferenceLineAndClear(mShouldReverseLayout, anchorInfo.mOffset); + } + } + } + detachAndScrapAttachedViews(recycler); + mLaidOutInvalidFullSpan = false; + updateMeasureSpecs(); + if (anchorInfo.mLayoutFromEnd) { + // Layout start. + updateLayoutStateToFillStart(anchorInfo.mPosition, state); + fill(recycler, mLayoutState, state); + // Layout end. + updateLayoutStateToFillEnd(anchorInfo.mPosition, state); + mLayoutState.mCurrentPosition += mLayoutState.mItemDirection; + fill(recycler, mLayoutState, state); + } else { + // Layout end. + updateLayoutStateToFillEnd(anchorInfo.mPosition, state); + fill(recycler, mLayoutState, state); + // Layout start. + updateLayoutStateToFillStart(anchorInfo.mPosition, state); + mLayoutState.mCurrentPosition += mLayoutState.mItemDirection; + fill(recycler, mLayoutState, state); + } + + if (getChildCount() > 0) { + if (mShouldReverseLayout) { + fixEndGap(recycler, state, true); + fixStartGap(recycler, state, false); + } else { + fixStartGap(recycler, state, true); + fixEndGap(recycler, state, false); + } + } + + if (!state.isPreLayout()) { + final boolean needToCheckForGaps = mGapStrategy != GAP_HANDLING_NONE + && getChildCount() > 0 + && (mLaidOutInvalidFullSpan || hasGapsToFix() != null); + if (needToCheckForGaps) { + removeCallbacks(mCheckForGapsRunnable); + postOnAnimation(mCheckForGapsRunnable); + } + mPendingScrollPosition = NO_POSITION; + mPendingScrollPositionOffset = INVALID_OFFSET; + } + mLastLayoutFromEnd = anchorInfo.mLayoutFromEnd; + mLastLayoutRTL = isLayoutRTL(); + mPendingSavedState = null; // we don't need this anymore + } + + private void applyPendingSavedState(AnchorInfo anchorInfo) { + if (DEBUG) { + Log.d(TAG, "found saved state: " + mPendingSavedState); + } + if (mPendingSavedState.mSpanOffsetsSize > 0) { + if (mPendingSavedState.mSpanOffsetsSize == mSpanCount) { + for (int i = 0; i < mSpanCount; i++) { + mSpans[i].clear(); + int line = mPendingSavedState.mSpanOffsets[i]; + if (line != Span.INVALID_LINE) { + if (mPendingSavedState.mAnchorLayoutFromEnd) { + line += mPrimaryOrientation.getEndAfterPadding(); + } else { + line += mPrimaryOrientation.getStartAfterPadding(); + } + } + mSpans[i].setLine(line); + } + } else { + mPendingSavedState.invalidateSpanInfo(); + mPendingSavedState.mAnchorPosition = mPendingSavedState.mVisibleAnchorPosition; + } + } + mLastLayoutRTL = mPendingSavedState.mLastLayoutRTL; + setReverseLayout(mPendingSavedState.mReverseLayout); + resolveShouldLayoutReverse(); + + if (mPendingSavedState.mAnchorPosition != NO_POSITION) { + mPendingScrollPosition = mPendingSavedState.mAnchorPosition; + anchorInfo.mLayoutFromEnd = mPendingSavedState.mAnchorLayoutFromEnd; + } else { + anchorInfo.mLayoutFromEnd = mShouldReverseLayout; + } + if (mPendingSavedState.mSpanLookupSize > 1) { + mLazySpanLookup.mData = mPendingSavedState.mSpanLookup; + mLazySpanLookup.mFullSpanItems = mPendingSavedState.mFullSpanItems; + } + } + + void updateAnchorInfoForLayout(RecyclerView.State state, AnchorInfo anchorInfo) { + if (updateAnchorFromPendingData(state, anchorInfo)) { + return; + } + if (updateAnchorFromChildren(state, anchorInfo)) { + return; + } + if (DEBUG) { + Log.d(TAG, "Deciding anchor info from fresh state"); + } + anchorInfo.assignCoordinateFromPadding(); + anchorInfo.mPosition = 0; + } + + private boolean updateAnchorFromChildren(RecyclerView.State state, AnchorInfo anchorInfo) { + // We don't recycle views out of adapter order. This way, we can rely on the first or + // last child as the anchor position. + // Layout direction may change but we should select the child depending on the latest + // layout direction. Otherwise, we'll choose the wrong child. + anchorInfo.mPosition = mLastLayoutFromEnd + ? findLastReferenceChildPosition(state.getItemCount()) + : findFirstReferenceChildPosition(state.getItemCount()); + anchorInfo.mOffset = INVALID_OFFSET; + return true; + } + + boolean updateAnchorFromPendingData(RecyclerView.State state, AnchorInfo anchorInfo) { + // Validate scroll position if exists. + if (state.isPreLayout() || mPendingScrollPosition == NO_POSITION) { + return false; + } + // Validate it. + if (mPendingScrollPosition < 0 || mPendingScrollPosition >= state.getItemCount()) { + mPendingScrollPosition = NO_POSITION; + mPendingScrollPositionOffset = INVALID_OFFSET; + return false; + } + + if (mPendingSavedState == null || mPendingSavedState.mAnchorPosition == NO_POSITION + || mPendingSavedState.mSpanOffsetsSize < 1) { + // If item is visible, make it fully visible. + final View child = findViewByPosition(mPendingScrollPosition); + if (child != null) { + // Use regular anchor position, offset according to pending offset and target + // child + anchorInfo.mPosition = mShouldReverseLayout ? getLastChildPosition() + : getFirstChildPosition(); + + if (mPendingScrollPositionOffset != INVALID_OFFSET) { + if (anchorInfo.mLayoutFromEnd) { + final int target = mPrimaryOrientation.getEndAfterPadding() - + mPendingScrollPositionOffset; + anchorInfo.mOffset = target - mPrimaryOrientation.getDecoratedEnd(child); + } else { + final int target = mPrimaryOrientation.getStartAfterPadding() + + mPendingScrollPositionOffset; + anchorInfo.mOffset = target - mPrimaryOrientation.getDecoratedStart(child); + } + return true; + } + + // no offset provided. Decide according to the child location + final int childSize = mPrimaryOrientation.getDecoratedMeasurement(child); + if (childSize > mPrimaryOrientation.getTotalSpace()) { + // Item does not fit. Fix depending on layout direction. + anchorInfo.mOffset = anchorInfo.mLayoutFromEnd + ? mPrimaryOrientation.getEndAfterPadding() + : mPrimaryOrientation.getStartAfterPadding(); + return true; + } + + final int startGap = mPrimaryOrientation.getDecoratedStart(child) + - mPrimaryOrientation.getStartAfterPadding(); + if (startGap < 0) { + anchorInfo.mOffset = -startGap; + return true; + } + final int endGap = mPrimaryOrientation.getEndAfterPadding() - + mPrimaryOrientation.getDecoratedEnd(child); + if (endGap < 0) { + anchorInfo.mOffset = endGap; + return true; + } + // child already visible. just layout as usual + anchorInfo.mOffset = INVALID_OFFSET; + } else { + // Child is not visible. Set anchor coordinate depending on in which direction + // child will be visible. + anchorInfo.mPosition = mPendingScrollPosition; + if (mPendingScrollPositionOffset == INVALID_OFFSET) { + final int position = calculateScrollDirectionForPosition( + anchorInfo.mPosition); + anchorInfo.mLayoutFromEnd = position == LAYOUT_END; + anchorInfo.assignCoordinateFromPadding(); + } else { + anchorInfo.assignCoordinateFromPadding(mPendingScrollPositionOffset); + } + anchorInfo.mInvalidateOffsets = true; + } + } else { + anchorInfo.mOffset = INVALID_OFFSET; + anchorInfo.mPosition = mPendingScrollPosition; + } + return true; + } + + void updateMeasureSpecs() { + mSizePerSpan = mSecondaryOrientation.getTotalSpace() / mSpanCount; + 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); + } + } + + @Override + public boolean supportsPredictiveItemAnimations() { + return mPendingSavedState == null; + } + + /** + * Returns the adapter position of the first visible view for each span. + *

+ * Note that, this value is not affected by layout orientation or item order traversal. + * ({@link #setReverseLayout(boolean)}). Views are sorted by their positions in the adapter, + * not in the layout. + *

+ * If RecyclerView has item decorators, they will be considered in calculations as well. + *

+ * StaggeredGridLayoutManager may pre-cache some views that are not necessarily visible. Those + * views are ignored in this method. + * + * @param into An array to put the results into. If you don't provide any, LayoutManager will + * create a new one. + * @return The adapter position of the first visible item in each span. If a span does not have + * any items, {@link RecyclerView#NO_POSITION} is returned for that span. + * @see #findFirstCompletelyVisibleItemPositions(int[]) + * @see #findLastVisibleItemPositions(int[]) + */ + public int[] findFirstVisibleItemPositions(int[] into) { + if (into == null) { + into = new int[mSpanCount]; + } else if (into.length < mSpanCount) { + throw new IllegalArgumentException("Provided int[]'s size must be more than or equal" + + " to span count. Expected:" + mSpanCount + ", array size:" + into.length); + } + for (int i = 0; i < mSpanCount; i++) { + into[i] = mSpans[i].findFirstVisibleItemPosition(); + } + return into; + } + + /** + * Returns the adapter position of the first completely visible view for each span. + *

+ * Note that, this value is not affected by layout orientation or item order traversal. + * ({@link #setReverseLayout(boolean)}). Views are sorted by their positions in the adapter, + * not in the layout. + *

+ * If RecyclerView has item decorators, they will be considered in calculations as well. + *

+ * StaggeredGridLayoutManager may pre-cache some views that are not necessarily visible. Those + * views are ignored in this method. + * + * @param into An array to put the results into. If you don't provide any, LayoutManager will + * create a new one. + * @return The adapter position of the first fully visible item in each span. If a span does + * not have any items, {@link RecyclerView#NO_POSITION} is returned for that span. + * @see #findFirstVisibleItemPositions(int[]) + * @see #findLastCompletelyVisibleItemPositions(int[]) + */ + public int[] findFirstCompletelyVisibleItemPositions(int[] into) { + if (into == null) { + into = new int[mSpanCount]; + } else if (into.length < mSpanCount) { + throw new IllegalArgumentException("Provided int[]'s size must be more than or equal" + + " to span count. Expected:" + mSpanCount + ", array size:" + into.length); + } + for (int i = 0; i < mSpanCount; i++) { + into[i] = mSpans[i].findFirstCompletelyVisibleItemPosition(); + } + return into; + } + + /** + * Returns the adapter position of the last visible view for each span. + *

+ * Note that, this value is not affected by layout orientation or item order traversal. + * ({@link #setReverseLayout(boolean)}). Views are sorted by their positions in the adapter, + * not in the layout. + *

+ * If RecyclerView has item decorators, they will be considered in calculations as well. + *

+ * StaggeredGridLayoutManager may pre-cache some views that are not necessarily visible. Those + * views are ignored in this method. + * + * @param into An array to put the results into. If you don't provide any, LayoutManager will + * create a new one. + * @return The adapter position of the last visible item in each span. If a span does not have + * any items, {@link RecyclerView#NO_POSITION} is returned for that span. + * @see #findLastCompletelyVisibleItemPositions(int[]) + * @see #findFirstVisibleItemPositions(int[]) + */ + public int[] findLastVisibleItemPositions(int[] into) { + if (into == null) { + into = new int[mSpanCount]; + } else if (into.length < mSpanCount) { + throw new IllegalArgumentException("Provided int[]'s size must be more than or equal" + + " to span count. Expected:" + mSpanCount + ", array size:" + into.length); + } + for (int i = 0; i < mSpanCount; i++) { + into[i] = mSpans[i].findLastVisibleItemPosition(); + } + return into; + } + + /** + * Returns the adapter position of the last completely visible view for each span. + *

+ * Note that, this value is not affected by layout orientation or item order traversal. + * ({@link #setReverseLayout(boolean)}). Views are sorted by their positions in the adapter, + * not in the layout. + *

+ * If RecyclerView has item decorators, they will be considered in calculations as well. + *

+ * StaggeredGridLayoutManager may pre-cache some views that are not necessarily visible. Those + * views are ignored in this method. + * + * @param into An array to put the results into. If you don't provide any, LayoutManager will + * create a new one. + * @return The adapter position of the last fully visible item in each span. If a span does not + * have any items, {@link RecyclerView#NO_POSITION} is returned for that span. + * @see #findFirstCompletelyVisibleItemPositions(int[]) + * @see #findLastVisibleItemPositions(int[]) + */ + public int[] findLastCompletelyVisibleItemPositions(int[] into) { + if (into == null) { + into = new int[mSpanCount]; + } else if (into.length < mSpanCount) { + throw new IllegalArgumentException("Provided int[]'s size must be more than or equal" + + " to span count. Expected:" + mSpanCount + ", array size:" + into.length); + } + for (int i = 0; i < mSpanCount; i++) { + into[i] = mSpans[i].findLastCompletelyVisibleItemPosition(); + } + return into; + } + + @Override + public int computeHorizontalScrollOffset(RecyclerView.State state) { + return computeScrollOffset(state); + } + + private int computeScrollOffset(RecyclerView.State state) { + if (getChildCount() == 0) { + return 0; + } + ensureOrientationHelper(); + return ScrollbarHelper.computeScrollOffset(state, mPrimaryOrientation, + findFirstVisibleItemClosestToStart(!mSmoothScrollbarEnabled, true) + , findFirstVisibleItemClosestToEnd(!mSmoothScrollbarEnabled, true), + this, mSmoothScrollbarEnabled, mShouldReverseLayout); + } + + @Override + public int computeVerticalScrollOffset(RecyclerView.State state) { + return computeScrollOffset(state); + } + + @Override + public int computeHorizontalScrollExtent(RecyclerView.State state) { + return computeScrollExtent(state); + } + + private int computeScrollExtent(RecyclerView.State state) { + if (getChildCount() == 0) { + return 0; + } + ensureOrientationHelper(); + return ScrollbarHelper.computeScrollExtent(state, mPrimaryOrientation, + findFirstVisibleItemClosestToStart(!mSmoothScrollbarEnabled, true) + , findFirstVisibleItemClosestToEnd(!mSmoothScrollbarEnabled, true), + this, mSmoothScrollbarEnabled); + } + + @Override + public int computeVerticalScrollExtent(RecyclerView.State state) { + return computeScrollExtent(state); + } + + @Override + public int computeHorizontalScrollRange(RecyclerView.State state) { + return computeScrollRange(state); + } + + private int computeScrollRange(RecyclerView.State state) { + if (getChildCount() == 0) { + return 0; + } + ensureOrientationHelper(); + return ScrollbarHelper.computeScrollRange(state, mPrimaryOrientation, + findFirstVisibleItemClosestToStart(!mSmoothScrollbarEnabled, true) + , findFirstVisibleItemClosestToEnd(!mSmoothScrollbarEnabled, true), + this, mSmoothScrollbarEnabled); + } + + @Override + public int computeVerticalScrollRange(RecyclerView.State state) { + return computeScrollRange(state); + } + + private void measureChildWithDecorationsAndMargin(View child, LayoutParams lp) { + if (lp.mFullSpan) { + if (mOrientation == VERTICAL) { + measureChildWithDecorationsAndMargin(child, mFullSizeSpec, + getSpecForDimension(lp.height, mHeightSpec)); + } else { + measureChildWithDecorationsAndMargin(child, + getSpecForDimension(lp.width, mWidthSpec), mFullSizeSpec); + } + } else { + if (mOrientation == VERTICAL) { + measureChildWithDecorationsAndMargin(child, mWidthSpec, + getSpecForDimension(lp.height, mHeightSpec)); + } else { + measureChildWithDecorationsAndMargin(child, + getSpecForDimension(lp.width, mWidthSpec), mHeightSpec); + } + } + } + + 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) { + 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); + } + + private int updateSpecWithExtra(int spec, int startInset, int endInset) { + if (startInset == 0 && endInset == 0) { + return spec; + } + 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); + } + return spec; + } + + @Override + public void onRestoreInstanceState(Parcelable state) { + if (state instanceof SavedState) { + mPendingSavedState = (SavedState) state; + requestLayout(); + } else if (DEBUG) { + Log.d(TAG, "invalid saved state class"); + } + } + + @Override + public Parcelable onSaveInstanceState() { + if (mPendingSavedState != null) { + return new SavedState(mPendingSavedState); + } + SavedState state = new SavedState(); + state.mReverseLayout = mReverseLayout; + state.mAnchorLayoutFromEnd = mLastLayoutFromEnd; + state.mLastLayoutRTL = mLastLayoutRTL; + + if (mLazySpanLookup != null && mLazySpanLookup.mData != null) { + state.mSpanLookup = mLazySpanLookup.mData; + state.mSpanLookupSize = state.mSpanLookup.length; + state.mFullSpanItems = mLazySpanLookup.mFullSpanItems; + } else { + state.mSpanLookupSize = 0; + } + + if (getChildCount() > 0) { + ensureOrientationHelper(); + state.mAnchorPosition = mLastLayoutFromEnd ? getLastChildPosition() + : getFirstChildPosition(); + state.mVisibleAnchorPosition = findFirstVisibleItemPositionInt(); + state.mSpanOffsetsSize = mSpanCount; + state.mSpanOffsets = new int[mSpanCount]; + for (int i = 0; i < mSpanCount; i++) { + int line; + if (mLastLayoutFromEnd) { + line = mSpans[i].getEndLine(Span.INVALID_LINE); + if (line != Span.INVALID_LINE) { + line -= mPrimaryOrientation.getEndAfterPadding(); + } + } else { + line = mSpans[i].getStartLine(Span.INVALID_LINE); + if (line != Span.INVALID_LINE) { + line -= mPrimaryOrientation.getStartAfterPadding(); + } + } + state.mSpanOffsets[i] = line; + } + } else { + state.mAnchorPosition = NO_POSITION; + state.mVisibleAnchorPosition = NO_POSITION; + state.mSpanOffsetsSize = 0; + } + if (DEBUG) { + Log.d(TAG, "saved state:\n" + state); + } + return state; + } + + @Override + public void onInitializeAccessibilityNodeInfoForItem(RecyclerView.Recycler recycler, + RecyclerView.State state, View host, AccessibilityNodeInfoCompat info) { + ViewGroup.LayoutParams lp = host.getLayoutParams(); + if (!(lp instanceof LayoutParams)) { + super.onInitializeAccessibilityNodeInfoForItem(host, info); + return; + } + LayoutParams sglp = (LayoutParams) lp; + if (mOrientation == HORIZONTAL) { + info.setCollectionItemInfo(AccessibilityNodeInfoCompat.CollectionItemInfoCompat.obtain( + sglp.getSpanIndex(), sglp.mFullSpan ? mSpanCount : 1, + -1, -1, + sglp.mFullSpan, false)); + } else { // VERTICAL + info.setCollectionItemInfo(AccessibilityNodeInfoCompat.CollectionItemInfoCompat.obtain( + -1, -1, + sglp.getSpanIndex(), sglp.mFullSpan ? mSpanCount : 1, + sglp.mFullSpan, false)); + } + } + + @Override + public void onInitializeAccessibilityEvent(AccessibilityEvent event) { + super.onInitializeAccessibilityEvent(event); + if (getChildCount() > 0) { + final AccessibilityRecordCompat record = AccessibilityEventCompat + .asRecord(event); + final View start = findFirstVisibleItemClosestToStart(false, true); + final View end = findFirstVisibleItemClosestToEnd(false, true); + if (start == null || end == null) { + return; + } + final int startPos = getPosition(start); + final int endPos = getPosition(end); + if (startPos < endPos) { + record.setFromIndex(startPos); + record.setToIndex(endPos); + } else { + record.setFromIndex(endPos); + record.setToIndex(startPos); + } + } + } + + /** + * Finds the first fully visible child to be used as an anchor child if span count changes when + * state is restored. If no children is fully visible, returns a partially visible child instead + * of returning null. + */ + int findFirstVisibleItemPositionInt() { + final View first = mShouldReverseLayout ? findFirstVisibleItemClosestToEnd(true, true) : + findFirstVisibleItemClosestToStart(true, true); + return first == null ? NO_POSITION : getPosition(first); + } + + @Override + public int getRowCountForAccessibility(RecyclerView.Recycler recycler, + RecyclerView.State state) { + if (mOrientation == HORIZONTAL) { + return mSpanCount; + } + return super.getRowCountForAccessibility(recycler, state); + } + + @Override + public int getColumnCountForAccessibility(RecyclerView.Recycler recycler, + RecyclerView.State state) { + if (mOrientation == VERTICAL) { + return mSpanCount; + } + return super.getColumnCountForAccessibility(recycler, state); + } + + /** + * This is for internal use. Not necessarily the child closest to start but the first child + * we find that matches the criteria. + * This method does not do any sorting based on child's start coordinate, instead, it uses + * children order. + */ + View findFirstVisibleItemClosestToStart(boolean fullyVisible, boolean acceptPartiallyVisible) { + ensureOrientationHelper(); + final int boundsStart = mPrimaryOrientation.getStartAfterPadding(); + final int boundsEnd = mPrimaryOrientation.getEndAfterPadding(); + final int limit = getChildCount(); + View partiallyVisible = null; + for (int i = 0; i < limit; i++) { + final View child = getChildAt(i); + final int childStart = mPrimaryOrientation.getDecoratedStart(child); + final int childEnd = mPrimaryOrientation.getDecoratedEnd(child); + if(childEnd <= boundsStart || childStart >= boundsEnd) { + continue; // not visible at all + } + if (childStart >= boundsStart || !fullyVisible) { + // when checking for start, it is enough even if part of the child's top is visible + // as long as fully visible is not requested. + return child; + } + if (acceptPartiallyVisible && partiallyVisible == null) { + partiallyVisible = child; + } + } + return partiallyVisible; + } + + /** + * This is for internal use. Not necessarily the child closest to bottom but the first child + * we find that matches the criteria. + * This method does not do any sorting based on child's end coordinate, instead, it uses + * children order. + */ + View findFirstVisibleItemClosestToEnd(boolean fullyVisible, boolean acceptPartiallyVisible) { + ensureOrientationHelper(); + final int boundsStart = mPrimaryOrientation.getStartAfterPadding(); + final int boundsEnd = mPrimaryOrientation.getEndAfterPadding(); + View partiallyVisible = null; + for (int i = getChildCount() - 1; i >= 0; i--) { + final View child = getChildAt(i); + final int childStart = mPrimaryOrientation.getDecoratedStart(child); + final int childEnd = mPrimaryOrientation.getDecoratedEnd(child); + if(childEnd <= boundsStart || childStart >= boundsEnd) { + continue; // not visible at all + } + if (childEnd <= boundsEnd || !fullyVisible) { + // when checking for end, it is enough even if part of the child's bottom is visible + // as long as fully visible is not requested. + return child; + } + if (acceptPartiallyVisible && partiallyVisible == null) { + partiallyVisible = child; + } + } + return partiallyVisible; + } + + private void fixEndGap(RecyclerView.Recycler recycler, RecyclerView.State state, + boolean canOffsetChildren) { + final int maxEndLine = getMaxEnd(mPrimaryOrientation.getEndAfterPadding()); + int gap = mPrimaryOrientation.getEndAfterPadding() - maxEndLine; + int fixOffset; + if (gap > 0) { + fixOffset = -scrollBy(-gap, recycler, state); + } else { + return; // nothing to fix + } + gap -= fixOffset; + if (canOffsetChildren && gap > 0) { + mPrimaryOrientation.offsetChildren(gap); + } + } + + private void fixStartGap(RecyclerView.Recycler recycler, RecyclerView.State state, + boolean canOffsetChildren) { + final int minStartLine = getMinStart(mPrimaryOrientation.getStartAfterPadding()); + int gap = minStartLine - mPrimaryOrientation.getStartAfterPadding(); + int fixOffset; + if (gap > 0) { + fixOffset = scrollBy(gap, recycler, state); + } else { + return; // nothing to fix + } + gap -= fixOffset; + if (canOffsetChildren && gap > 0) { + mPrimaryOrientation.offsetChildren(-gap); + } + } + + private void updateLayoutStateToFillStart(int anchorPosition, RecyclerView.State state) { + mLayoutState.mAvailable = 0; + mLayoutState.mCurrentPosition = anchorPosition; + if (isSmoothScrolling()) { + final int targetPos = state.getTargetScrollPosition(); + if (mShouldReverseLayout == targetPos < anchorPosition) { + mLayoutState.mExtra = 0; + } else { + mLayoutState.mExtra = mPrimaryOrientation.getTotalSpace(); + } + } else { + mLayoutState.mExtra = 0; + } + mLayoutState.mLayoutDirection = LAYOUT_START; + mLayoutState.mItemDirection = mShouldReverseLayout ? ITEM_DIRECTION_TAIL + : ITEM_DIRECTION_HEAD; + } + + private void updateLayoutStateToFillEnd(int anchorPosition, RecyclerView.State state) { + mLayoutState.mAvailable = 0; + mLayoutState.mCurrentPosition = anchorPosition; + if (isSmoothScrolling()) { + final int targetPos = state.getTargetScrollPosition(); + if (mShouldReverseLayout == targetPos > anchorPosition) { + mLayoutState.mExtra = 0; + } else { + mLayoutState.mExtra = mPrimaryOrientation.getTotalSpace(); + } + } else { + mLayoutState.mExtra = 0; + } + mLayoutState.mLayoutDirection = LAYOUT_END; + mLayoutState.mItemDirection = mShouldReverseLayout ? ITEM_DIRECTION_HEAD + : ITEM_DIRECTION_TAIL; + } + + @Override + public void offsetChildrenHorizontal(int dx) { + super.offsetChildrenHorizontal(dx); + for (int i = 0; i < mSpanCount; i++) { + mSpans[i].onOffset(dx); + } + } + + @Override + public void offsetChildrenVertical(int dy) { + super.offsetChildrenVertical(dy); + for (int i = 0; i < mSpanCount; i++) { + mSpans[i].onOffset(dy); + } + } + + @Override + public void onItemsRemoved(RecyclerView recyclerView, int positionStart, int itemCount) { + handleUpdate(positionStart, itemCount, AdapterHelper.UpdateOp.REMOVE); + } + + @Override + public void onItemsAdded(RecyclerView recyclerView, int positionStart, int itemCount) { + handleUpdate(positionStart, itemCount, AdapterHelper.UpdateOp.ADD); + } + + @Override + public void onItemsChanged(RecyclerView recyclerView) { + mLazySpanLookup.clear(); + requestLayout(); + } + + @Override + public void onItemsMoved(RecyclerView recyclerView, int from, int to, int itemCount) { + handleUpdate(from, to, AdapterHelper.UpdateOp.MOVE); + } + + @Override + public void onItemsUpdated(RecyclerView recyclerView, int positionStart, int itemCount) { + handleUpdate(positionStart, itemCount, AdapterHelper.UpdateOp.UPDATE); + } + + /** + * Checks whether it should invalidate span assignments in response to an adapter change. + */ + private void handleUpdate(int positionStart, int itemCountOrToPosition, int cmd) { + int minPosition = mShouldReverseLayout ? getLastChildPosition() : getFirstChildPosition(); + final int affectedRangeEnd;// exclusive + final int affectedRangeStart;// inclusive + + if (cmd == AdapterHelper.UpdateOp.MOVE) { + if (positionStart < itemCountOrToPosition) { + affectedRangeEnd = itemCountOrToPosition + 1; + affectedRangeStart = positionStart; + } else { + affectedRangeEnd = positionStart + 1; + affectedRangeStart = itemCountOrToPosition; + } + } else { + affectedRangeStart = positionStart; + affectedRangeEnd = positionStart + itemCountOrToPosition; + } + + mLazySpanLookup.invalidateAfter(affectedRangeStart); + switch (cmd) { + case AdapterHelper.UpdateOp.ADD: + mLazySpanLookup.offsetForAddition(positionStart, itemCountOrToPosition); + break; + case AdapterHelper.UpdateOp.REMOVE: + mLazySpanLookup.offsetForRemoval(positionStart, itemCountOrToPosition); + break; + case AdapterHelper.UpdateOp.MOVE: + // TODO optimize + mLazySpanLookup.offsetForRemoval(positionStart, 1); + mLazySpanLookup.offsetForAddition(itemCountOrToPosition, 1); + break; + } + + if (affectedRangeEnd <= minPosition) { + return; + } + + int maxPosition = mShouldReverseLayout ? getFirstChildPosition() : getLastChildPosition(); + if (affectedRangeStart <= maxPosition) { + requestLayout(); + } + } + + private int fill(RecyclerView.Recycler recycler, LayoutState layoutState, + RecyclerView.State state) { + mRemainingSpans.set(0, mSpanCount, true); + // The target position we are trying to reach. + final int targetLine; + /* + * The line until which we can recycle, as long as we add views. + * Keep in mind, it is still the line in layout direction which means; to calculate the + * actual recycle line, we should subtract/add the size in orientation. + */ + final int recycleLine; + // Line of the furthest row. + if (layoutState.mLayoutDirection == LAYOUT_END) { + // ignore padding for recycler + recycleLine = mPrimaryOrientation.getEndAfterPadding() + mLayoutState.mAvailable; + targetLine = recycleLine + mLayoutState.mExtra + mPrimaryOrientation.getEndPadding(); + + } else { // LAYOUT_START + // ignore padding for recycler + recycleLine = mPrimaryOrientation.getStartAfterPadding() - mLayoutState.mAvailable; + targetLine = recycleLine - mLayoutState.mExtra - + mPrimaryOrientation.getStartAfterPadding(); + } + updateAllRemainingSpans(layoutState.mLayoutDirection, targetLine); + + // the default coordinate to add new view. + final int defaultNewViewLine = mShouldReverseLayout + ? mPrimaryOrientation.getEndAfterPadding() + : mPrimaryOrientation.getStartAfterPadding(); + + while (layoutState.hasMore(state) && !mRemainingSpans.isEmpty()) { + View view = layoutState.next(recycler); + LayoutParams lp = ((LayoutParams) view.getLayoutParams()); + final int position = lp.getViewLayoutPosition(); + final int spanIndex = mLazySpanLookup.getSpan(position); + Span currentSpan; + final boolean assignSpan = spanIndex == LayoutParams.INVALID_SPAN_ID; + if (assignSpan) { + currentSpan = lp.mFullSpan ? mSpans[0] : getNextSpan(layoutState); + mLazySpanLookup.setSpan(position, currentSpan); + if (DEBUG) { + Log.d(TAG, "assigned " + currentSpan.mIndex + " for " + position); + } + } else { + if (DEBUG) { + Log.d(TAG, "using " + spanIndex + " for pos " + position); + } + currentSpan = mSpans[spanIndex]; + } + // assign span before measuring so that item decorators can get updated span index + lp.mSpan = currentSpan; + if (layoutState.mLayoutDirection == LAYOUT_END) { + addView(view); + } else { + addView(view, 0); + } + measureChildWithDecorationsAndMargin(view, lp); + + final int start; + final int end; + if (layoutState.mLayoutDirection == LAYOUT_END) { + start = lp.mFullSpan ? getMaxEnd(defaultNewViewLine) + : currentSpan.getEndLine(defaultNewViewLine); + end = start + mPrimaryOrientation.getDecoratedMeasurement(view); + if (assignSpan && lp.mFullSpan) { + LazySpanLookup.FullSpanItem fullSpanItem; + fullSpanItem = createFullSpanItemFromEnd(start); + fullSpanItem.mGapDir = LAYOUT_START; + fullSpanItem.mPosition = position; + mLazySpanLookup.addFullSpanItem(fullSpanItem); + } + } else { + end = lp.mFullSpan ? getMinStart(defaultNewViewLine) + : currentSpan.getStartLine(defaultNewViewLine); + start = end - mPrimaryOrientation.getDecoratedMeasurement(view); + if (assignSpan && lp.mFullSpan) { + LazySpanLookup.FullSpanItem fullSpanItem; + fullSpanItem = createFullSpanItemFromStart(end); + fullSpanItem.mGapDir = LAYOUT_END; + fullSpanItem.mPosition = position; + mLazySpanLookup.addFullSpanItem(fullSpanItem); + } + } + + // check if this item may create gaps in the future + if (lp.mFullSpan && layoutState.mItemDirection == ITEM_DIRECTION_HEAD) { + if (assignSpan) { + mLaidOutInvalidFullSpan = true; + } else { + final boolean hasInvalidGap; + if (layoutState.mLayoutDirection == LAYOUT_END) { + hasInvalidGap = !areAllEndsEqual(); + } else { // layoutState.mLayoutDirection == LAYOUT_START + hasInvalidGap = !areAllStartsEqual(); + } + if (hasInvalidGap) { + final LazySpanLookup.FullSpanItem fullSpanItem = mLazySpanLookup + .getFullSpanItem(position); + if (fullSpanItem != null) { + fullSpanItem.mHasUnwantedGapAfter = true; + } + 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); + if (mOrientation == VERTICAL) { + layoutDecoratedWithMargins(view, otherStart, start, otherEnd, end); + } else { + layoutDecoratedWithMargins(view, start, otherStart, end, otherEnd); + } + + if (lp.mFullSpan) { + updateAllRemainingSpans(mLayoutState.mLayoutDirection, targetLine); + } else { + updateRemainingSpans(currentSpan, mLayoutState.mLayoutDirection, targetLine); + } + recycle(recycler, mLayoutState, currentSpan, recycleLine); + } + if (DEBUG) { + Log.d(TAG, "fill, " + getChildCount()); + } + if (mLayoutState.mLayoutDirection == LAYOUT_START) { + final int minStart = getMinStart(mPrimaryOrientation.getStartAfterPadding()); + return Math.max(0, mLayoutState.mAvailable + (recycleLine - minStart)); + } else { + final int max = getMaxEnd(mPrimaryOrientation.getEndAfterPadding()); + return Math.max(0, mLayoutState.mAvailable + (max - recycleLine)); + } + } + + private LazySpanLookup.FullSpanItem createFullSpanItemFromEnd(int newItemTop) { + LazySpanLookup.FullSpanItem fsi = new LazySpanLookup.FullSpanItem(); + fsi.mGapPerSpan = new int[mSpanCount]; + for (int i = 0; i < mSpanCount; i++) { + fsi.mGapPerSpan[i] = newItemTop - mSpans[i].getEndLine(newItemTop); + } + return fsi; + } + + private LazySpanLookup.FullSpanItem createFullSpanItemFromStart(int newItemBottom) { + LazySpanLookup.FullSpanItem fsi = new LazySpanLookup.FullSpanItem(); + fsi.mGapPerSpan = new int[mSpanCount]; + for (int i = 0; i < mSpanCount; i++) { + fsi.mGapPerSpan[i] = mSpans[i].getStartLine(newItemBottom) - newItemBottom; + } + return fsi; + } + + private void attachViewToSpans(View view, LayoutParams lp, LayoutState layoutState) { + if (layoutState.mLayoutDirection == LayoutState.LAYOUT_END) { + if (lp.mFullSpan) { + appendViewToAllSpans(view); + } else { + lp.mSpan.appendToSpan(view); + } + } else { + if (lp.mFullSpan) { + prependViewToAllSpans(view); + } else { + lp.mSpan.prependToSpan(view); + } + } + } + + private void recycle(RecyclerView.Recycler recycler, LayoutState layoutState, + Span updatedSpan, int recycleLine) { + if (layoutState.mLayoutDirection == LAYOUT_START) { + // calculate recycle line + int maxStart = getMaxStart(updatedSpan.getStartLine()); + recycleFromEnd(recycler, Math.max(recycleLine, maxStart) + + (mPrimaryOrientation.getEnd() - mPrimaryOrientation.getStartAfterPadding())); + } else { + // calculate recycle line + int minEnd = getMinEnd(updatedSpan.getEndLine()); + recycleFromStart(recycler, Math.min(recycleLine, minEnd) - + (mPrimaryOrientation.getEnd() - mPrimaryOrientation.getStartAfterPadding())); + } + } + + private void appendViewToAllSpans(View view) { + // traverse in reverse so that we end up assigning full span items to 0 + for (int i = mSpanCount - 1; i >= 0; i--) { + mSpans[i].appendToSpan(view); + } + } + + private void prependViewToAllSpans(View view) { + // traverse in reverse so that we end up assigning full span items to 0 + for (int i = mSpanCount - 1; i >= 0; i--) { + mSpans[i].prependToSpan(view); + } + } + + private void layoutDecoratedWithMargins(View child, int left, int top, int right, int bottom) { + LayoutParams lp = (LayoutParams) child.getLayoutParams(); + if (DEBUG) { + Log.d(TAG, "layout decorated pos: " + lp.getViewLayoutPosition() + ", span:" + + lp.getSpanIndex() + ", fullspan:" + lp.mFullSpan + + ". l:" + left + ",t:" + top + + ", r:" + right + ", b:" + bottom); + } + layoutDecorated(child, left + lp.leftMargin, top + lp.topMargin, right - lp.rightMargin + , bottom - lp.bottomMargin); + } + + private void updateAllRemainingSpans(int layoutDir, int targetLine) { + for (int i = 0; i < mSpanCount; i++) { + if (mSpans[i].mViews.isEmpty()) { + continue; + } + updateRemainingSpans(mSpans[i], layoutDir, targetLine); + } + } + + private void updateRemainingSpans(Span span, int layoutDir, int targetLine) { + final int deletedSize = span.getDeletedSize(); + if (layoutDir == LAYOUT_START) { + final int line = span.getStartLine(); + if (line + deletedSize < targetLine) { + mRemainingSpans.set(span.mIndex, false); + } + } else { + final int line = span.getEndLine(); + if (line - deletedSize > targetLine) { + mRemainingSpans.set(span.mIndex, false); + } + } + } + + private int getMaxStart(int def) { + int maxStart = mSpans[0].getStartLine(def); + for (int i = 1; i < mSpanCount; i++) { + final int spanStart = mSpans[i].getStartLine(def); + if (spanStart > maxStart) { + maxStart = spanStart; + } + } + return maxStart; + } + + private int getMinStart(int def) { + int minStart = mSpans[0].getStartLine(def); + for (int i = 1; i < mSpanCount; i++) { + final int spanStart = mSpans[i].getStartLine(def); + if (spanStart < minStart) { + minStart = spanStart; + } + } + return minStart; + } + + boolean areAllEndsEqual() { + int end = mSpans[0].getEndLine(Span.INVALID_LINE); + for (int i = 1; i < mSpanCount; i++) { + if (mSpans[i].getEndLine(Span.INVALID_LINE) != end) { + return false; + } + } + return true; + } + + boolean areAllStartsEqual() { + int start = mSpans[0].getStartLine(Span.INVALID_LINE); + for (int i = 1; i < mSpanCount; i++) { + if (mSpans[i].getStartLine(Span.INVALID_LINE) != start) { + return false; + } + } + return true; + } + + private int getMaxEnd(int def) { + int maxEnd = mSpans[0].getEndLine(def); + for (int i = 1; i < mSpanCount; i++) { + final int spanEnd = mSpans[i].getEndLine(def); + if (spanEnd > maxEnd) { + maxEnd = spanEnd; + } + } + return maxEnd; + } + + private int getMinEnd(int def) { + int minEnd = mSpans[0].getEndLine(def); + for (int i = 1; i < mSpanCount; i++) { + final int spanEnd = mSpans[i].getEndLine(def); + if (spanEnd < minEnd) { + minEnd = spanEnd; + } + } + return minEnd; + } + + private void recycleFromStart(RecyclerView.Recycler recycler, int line) { + if (DEBUG) { + Log.d(TAG, "recycling from start for line " + line); + } + while (getChildCount() > 0) { + View child = getChildAt(0); + if (mPrimaryOrientation.getDecoratedEnd(child) < line) { + LayoutParams lp = (LayoutParams) child.getLayoutParams(); + if (lp.mFullSpan) { + for (int j = 0; j < mSpanCount; j++) { + mSpans[j].popStart(); + } + } else { + lp.mSpan.popStart(); + } + removeAndRecycleView(child, recycler); + } else { + return;// done + } + } + } + + private void recycleFromEnd(RecyclerView.Recycler recycler, int line) { + final int childCount = getChildCount(); + int i; + for (i = childCount - 1; i >= 0; i--) { + View child = getChildAt(i); + if (mPrimaryOrientation.getDecoratedStart(child) > line) { + LayoutParams lp = (LayoutParams) child.getLayoutParams(); + if (lp.mFullSpan) { + for (int j = 0; j < mSpanCount; j++) { + mSpans[j].popEnd(); + } + } else { + lp.mSpan.popEnd(); + } + removeAndRecycleView(child, recycler); + } else { + return;// done + } + } + } + + /** + * @return True if last span is the first one we want to fill + */ + private boolean preferLastSpan(int layoutDir) { + if (mOrientation == HORIZONTAL) { + return (layoutDir == LAYOUT_START) != mShouldReverseLayout; + } + return ((layoutDir == LAYOUT_START) == mShouldReverseLayout) == isLayoutRTL(); + } + + /** + * Finds the span for the next view. + */ + private Span getNextSpan(LayoutState layoutState) { + final boolean preferLastSpan = preferLastSpan(layoutState.mLayoutDirection); + final int startIndex, endIndex, diff; + if (preferLastSpan) { + startIndex = mSpanCount - 1; + endIndex = -1; + diff = -1; + } else { + startIndex = 0; + endIndex = mSpanCount; + diff = 1; + } + if (layoutState.mLayoutDirection == LAYOUT_END) { + Span min = null; + int minLine = Integer.MAX_VALUE; + final int defaultLine = mPrimaryOrientation.getStartAfterPadding(); + for (int i = startIndex; i != endIndex; i += diff) { + final Span other = mSpans[i]; + int otherLine = other.getEndLine(defaultLine); + if (otherLine < minLine) { + min = other; + minLine = otherLine; + } + } + return min; + } else { + Span max = null; + int maxLine = Integer.MIN_VALUE; + final int defaultLine = mPrimaryOrientation.getEndAfterPadding(); + for (int i = startIndex; i != endIndex; i += diff) { + final Span other = mSpans[i]; + int otherLine = other.getStartLine(defaultLine); + if (otherLine > maxLine) { + max = other; + maxLine = otherLine; + } + } + return max; + } + } + + @Override + public boolean canScrollVertically() { + return mOrientation == VERTICAL; + } + + @Override + public boolean canScrollHorizontally() { + return mOrientation == HORIZONTAL; + } + + @Override + public int scrollHorizontallyBy(int dx, RecyclerView.Recycler recycler, + RecyclerView.State state) { + return scrollBy(dx, recycler, state); + } + + @Override + public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler, + RecyclerView.State state) { + return scrollBy(dy, recycler, state); + } + + private int calculateScrollDirectionForPosition(int position) { + if (getChildCount() == 0) { + return mShouldReverseLayout ? LAYOUT_END : LAYOUT_START; + } + final int firstChildPos = getFirstChildPosition(); + return position < firstChildPos != mShouldReverseLayout ? LAYOUT_START : LAYOUT_END; + } + + @Override + public void smoothScrollToPosition(RecyclerView recyclerView, RecyclerView.State state, + int position) { + LinearSmoothScroller scroller = new LinearSmoothScroller(recyclerView.getContext()) { + @Override + public PointF computeScrollVectorForPosition(int targetPosition) { + final int direction = calculateScrollDirectionForPosition(targetPosition); + if (direction == 0) { + return null; + } + if (mOrientation == HORIZONTAL) { + return new PointF(direction, 0); + } else { + return new PointF(0, direction); + } + } + }; + scroller.setTargetPosition(position); + startSmoothScroll(scroller); + } + + @Override + public void scrollToPosition(int position) { + if (mPendingSavedState != null && mPendingSavedState.mAnchorPosition != position) { + mPendingSavedState.invalidateAnchorPositionInfo(); + } + mPendingScrollPosition = position; + mPendingScrollPositionOffset = INVALID_OFFSET; + requestLayout(); + } + + /** + * Scroll to the specified adapter position with the given offset from layout start. + *

+ * Note that scroll position change will not be reflected until the next layout call. + *

+ * If you are just trying to make a position visible, use {@link #scrollToPosition(int)}. + * + * @param position Index (starting at 0) of the reference item. + * @param offset The distance (in pixels) between the start edge of the item view and + * start edge of the RecyclerView. + * @see #setReverseLayout(boolean) + * @see #scrollToPosition(int) + */ + public void scrollToPositionWithOffset(int position, int offset) { + if (mPendingSavedState != null) { + mPendingSavedState.invalidateAnchorPositionInfo(); + } + mPendingScrollPosition = position; + mPendingScrollPositionOffset = offset; + requestLayout(); + } + + int scrollBy(int dt, RecyclerView.Recycler recycler, RecyclerView.State state) { + ensureOrientationHelper(); + final int referenceChildPosition; + if (dt > 0) { // layout towards end + mLayoutState.mLayoutDirection = LAYOUT_END; + mLayoutState.mItemDirection = mShouldReverseLayout ? ITEM_DIRECTION_HEAD + : ITEM_DIRECTION_TAIL; + referenceChildPosition = getLastChildPosition(); + } else { + mLayoutState.mLayoutDirection = LAYOUT_START; + mLayoutState.mItemDirection = mShouldReverseLayout ? ITEM_DIRECTION_TAIL + : ITEM_DIRECTION_HEAD; + referenceChildPosition = getFirstChildPosition(); + } + mLayoutState.mCurrentPosition = referenceChildPosition + mLayoutState.mItemDirection; + final int absDt = Math.abs(dt); + mLayoutState.mAvailable = absDt; + mLayoutState.mExtra = isSmoothScrolling() ? mPrimaryOrientation.getTotalSpace() : 0; + int consumed = fill(recycler, mLayoutState, state); + final int totalScroll; + if (absDt < consumed) { + totalScroll = dt; + } else if (dt < 0) { + totalScroll = -consumed; + } else { // dt > 0 + totalScroll = consumed; + } + if (DEBUG) { + Log.d(TAG, "asked " + dt + " scrolled" + totalScroll); + } + + mPrimaryOrientation.offsetChildren(-totalScroll); + // always reset this if we scroll for a proper save instance state + mLastLayoutFromEnd = mShouldReverseLayout; + return totalScroll; + } + + private int getLastChildPosition() { + final int childCount = getChildCount(); + return childCount == 0 ? 0 : getPosition(getChildAt(childCount - 1)); + } + + private int getFirstChildPosition() { + final int childCount = getChildCount(); + return childCount == 0 ? 0 : getPosition(getChildAt(0)); + } + + /** + * Finds the first View that can be used as an anchor View. + * + * @return Position of the View or 0 if it cannot find any such View. + */ + private int findFirstReferenceChildPosition(int itemCount) { + final int limit = getChildCount(); + for (int i = 0; i < limit; i++) { + final View view = getChildAt(i); + final int position = getPosition(view); + if (position >= 0 && position < itemCount) { + return position; + } + } + return 0; + } + + /** + * Finds the last View that can be used as an anchor View. + * + * @return Position of the View or 0 if it cannot find any such View. + */ + private int findLastReferenceChildPosition(int itemCount) { + for (int i = getChildCount() - 1; i >= 0; i--) { + final View view = getChildAt(i); + final int position = getPosition(view); + if (position >= 0 && position < itemCount) { + return position; + } + } + return 0; + } + + @Override + public RecyclerView.LayoutParams generateDefaultLayoutParams() { + return new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, + ViewGroup.LayoutParams.WRAP_CONTENT); + } + + @Override + public RecyclerView.LayoutParams generateLayoutParams(Context c, AttributeSet attrs) { + return new LayoutParams(c, attrs); + } + + @Override + public RecyclerView.LayoutParams generateLayoutParams(ViewGroup.LayoutParams lp) { + if (lp instanceof ViewGroup.MarginLayoutParams) { + return new LayoutParams((ViewGroup.MarginLayoutParams) lp); + } else { + return new LayoutParams(lp); + } + } + + @Override + public boolean checkLayoutParams(RecyclerView.LayoutParams lp) { + return lp instanceof LayoutParams; + } + + public int getOrientation() { + return mOrientation; + } + + + /** + * LayoutParams used by StaggeredGridLayoutManager. + *

+ * Note that if the orientation is {@link #VERTICAL}, the width parameter is ignored and if the + * orientation is {@link #HORIZONTAL} the height parameter is ignored because child view is + * expected to fill all of the space given to it. + */ + public static class LayoutParams extends RecyclerView.LayoutParams { + + /** + * Span Id for Views that are not laid out yet. + */ + public static final int INVALID_SPAN_ID = -1; + + // Package scope to be able to access from tests. + Span mSpan; + + boolean mFullSpan; + + public LayoutParams(Context c, AttributeSet attrs) { + super(c, attrs); + } + + public LayoutParams(int width, int height) { + super(width, height); + } + + public LayoutParams(ViewGroup.MarginLayoutParams source) { + super(source); + } + + public LayoutParams(ViewGroup.LayoutParams source) { + super(source); + } + + public LayoutParams(RecyclerView.LayoutParams source) { + super(source); + } + + /** + * When set to true, the item will layout using all span area. That means, if orientation + * is vertical, the view will have full width; if orientation is horizontal, the view will + * have full height. + * + * @param fullSpan True if this item should traverse all spans. + * @see #isFullSpan() + */ + public void setFullSpan(boolean fullSpan) { + mFullSpan = fullSpan; + } + + /** + * Returns whether this View occupies all available spans or just one. + * + * @return True if the View occupies all spans or false otherwise. + * @see #setFullSpan(boolean) + */ + public boolean isFullSpan() { + return mFullSpan; + } + + /** + * Returns the Span index to which this View is assigned. + * + * @return The Span index of the View. If View is not yet assigned to any span, returns + * {@link #INVALID_SPAN_ID}. + */ + public final int getSpanIndex() { + if (mSpan == null) { + return INVALID_SPAN_ID; + } + return mSpan.mIndex; + } + } + + // Package scoped to access from tests. + class Span { + + static final int INVALID_LINE = Integer.MIN_VALUE; + private ArrayList mViews = new ArrayList(); + int mCachedStart = INVALID_LINE; + int mCachedEnd = INVALID_LINE; + int mDeletedSize = 0; + final int mIndex; + + private Span(int index) { + mIndex = index; + } + + int getStartLine(int def) { + if (mCachedStart != INVALID_LINE) { + return mCachedStart; + } + if (mViews.size() == 0) { + return def; + } + calculateCachedStart(); + return mCachedStart; + } + + void calculateCachedStart() { + final View startView = mViews.get(0); + final LayoutParams lp = getLayoutParams(startView); + mCachedStart = mPrimaryOrientation.getDecoratedStart(startView); + if (lp.mFullSpan) { + LazySpanLookup.FullSpanItem fsi = mLazySpanLookup + .getFullSpanItem(lp.getViewLayoutPosition()); + if (fsi != null && fsi.mGapDir == LAYOUT_START) { + mCachedStart -= fsi.getGapForSpan(mIndex); + } + } + } + + // Use this one when default value does not make sense and not having a value means a bug. + int getStartLine() { + if (mCachedStart != INVALID_LINE) { + return mCachedStart; + } + calculateCachedStart(); + return mCachedStart; + } + + int getEndLine(int def) { + if (mCachedEnd != INVALID_LINE) { + return mCachedEnd; + } + final int size = mViews.size(); + if (size == 0) { + return def; + } + calculateCachedEnd(); + return mCachedEnd; + } + + void calculateCachedEnd() { + final View endView = mViews.get(mViews.size() - 1); + final LayoutParams lp = getLayoutParams(endView); + mCachedEnd = mPrimaryOrientation.getDecoratedEnd(endView); + if (lp.mFullSpan) { + LazySpanLookup.FullSpanItem fsi = mLazySpanLookup + .getFullSpanItem(lp.getViewLayoutPosition()); + if (fsi != null && fsi.mGapDir == LAYOUT_END) { + mCachedEnd += fsi.getGapForSpan(mIndex); + } + } + } + + // Use this one when default value does not make sense and not having a value means a bug. + int getEndLine() { + if (mCachedEnd != INVALID_LINE) { + return mCachedEnd; + } + calculateCachedEnd(); + return mCachedEnd; + } + + void prependToSpan(View view) { + LayoutParams lp = getLayoutParams(view); + lp.mSpan = this; + mViews.add(0, view); + mCachedStart = INVALID_LINE; + if (mViews.size() == 1) { + mCachedEnd = INVALID_LINE; + } + if (lp.isItemRemoved() || lp.isItemChanged()) { + mDeletedSize += mPrimaryOrientation.getDecoratedMeasurement(view); + } + } + + void appendToSpan(View view) { + LayoutParams lp = getLayoutParams(view); + lp.mSpan = this; + mViews.add(view); + mCachedEnd = INVALID_LINE; + if (mViews.size() == 1) { + mCachedStart = INVALID_LINE; + } + if (lp.isItemRemoved() || lp.isItemChanged()) { + mDeletedSize += mPrimaryOrientation.getDecoratedMeasurement(view); + } + } + + // Useful method to preserve positions on a re-layout. + void cacheReferenceLineAndClear(boolean reverseLayout, int offset) { + int reference; + if (reverseLayout) { + reference = getEndLine(INVALID_LINE); + } else { + reference = getStartLine(INVALID_LINE); + } + clear(); + if (reference == INVALID_LINE) { + return; + } + if ((reverseLayout && reference < mPrimaryOrientation.getEndAfterPadding()) || + (!reverseLayout && reference > mPrimaryOrientation.getStartAfterPadding())) { + return; + } + if (offset != INVALID_OFFSET) { + reference += offset; + } + mCachedStart = mCachedEnd = reference; + } + + void clear() { + mViews.clear(); + invalidateCache(); + mDeletedSize = 0; + } + + void invalidateCache() { + mCachedStart = INVALID_LINE; + mCachedEnd = INVALID_LINE; + } + + void setLine(int line) { + mCachedEnd = mCachedStart = line; + } + + void popEnd() { + final int size = mViews.size(); + View end = mViews.remove(size - 1); + final LayoutParams lp = getLayoutParams(end); + lp.mSpan = null; + if (lp.isItemRemoved() || lp.isItemChanged()) { + mDeletedSize -= mPrimaryOrientation.getDecoratedMeasurement(end); + } + if (size == 1) { + mCachedStart = INVALID_LINE; + } + mCachedEnd = INVALID_LINE; + } + + void popStart() { + View start = mViews.remove(0); + final LayoutParams lp = getLayoutParams(start); + lp.mSpan = null; + if (mViews.size() == 0) { + mCachedEnd = INVALID_LINE; + } + if (lp.isItemRemoved() || lp.isItemChanged()) { + mDeletedSize -= mPrimaryOrientation.getDecoratedMeasurement(start); + } + mCachedStart = INVALID_LINE; + } + + public int getDeletedSize() { + return mDeletedSize; + } + + LayoutParams getLayoutParams(View view) { + return (LayoutParams) view.getLayoutParams(); + } + + void onOffset(int dt) { + if (mCachedStart != INVALID_LINE) { + mCachedStart += dt; + } + if (mCachedEnd != INVALID_LINE) { + mCachedEnd += dt; + } + } + + // 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) + : findOneVisibleChild(0, mViews.size(), false); + } + + public int findFirstCompletelyVisibleItemPosition() { + return mReverseLayout + ? findOneVisibleChild(mViews.size() - 1, -1, true) + : findOneVisibleChild(0, mViews.size(), true); + } + + public int findLastVisibleItemPosition() { + return mReverseLayout + ? findOneVisibleChild(0, mViews.size(), false) + : findOneVisibleChild(mViews.size() - 1, -1, false); + } + + public int findLastCompletelyVisibleItemPosition() { + return mReverseLayout + ? findOneVisibleChild(0, mViews.size(), true) + : findOneVisibleChild(mViews.size() - 1, -1, true); + } + + int findOneVisibleChild(int fromIndex, int toIndex, boolean completelyVisible) { + final int start = mPrimaryOrientation.getStartAfterPadding(); + final int end = mPrimaryOrientation.getEndAfterPadding(); + final int next = toIndex > fromIndex ? 1 : -1; + for (int i = fromIndex; i != toIndex; i += next) { + final View child = mViews.get(i); + final int childStart = mPrimaryOrientation.getDecoratedStart(child); + final int childEnd = mPrimaryOrientation.getDecoratedEnd(child); + if (childStart < end && childEnd > start) { + if (completelyVisible) { + if (childStart >= start && childEnd <= end) { + return getPosition(child); + } + } else { + return getPosition(child); + } + } + } + return NO_POSITION; + } + } + + /** + * An array of mappings from adapter position to span. + * This only grows when a write happens and it grows up to the size of the adapter. + */ + static class LazySpanLookup { + + private static final int MIN_SIZE = 10; + int[] mData; + List mFullSpanItems; + + + /** + * Invalidates everything after this position, including full span information + */ + int forceInvalidateAfter(int position) { + if (mFullSpanItems != null) { + for (int i = mFullSpanItems.size() - 1; i >= 0; i--) { + FullSpanItem fsi = mFullSpanItems.get(i); + if (fsi.mPosition >= position) { + mFullSpanItems.remove(i); + } + } + } + return invalidateAfter(position); + } + + /** + * returns end position for invalidation. + */ + int invalidateAfter(int position) { + if (mData == null) { + return RecyclerView.NO_POSITION; + } + if (position >= mData.length) { + return RecyclerView.NO_POSITION; + } + int endPosition = invalidateFullSpansAfter(position); + if (endPosition == RecyclerView.NO_POSITION) { + Arrays.fill(mData, position, mData.length, LayoutParams.INVALID_SPAN_ID); + return mData.length; + } else { + // just invalidate items in between + Arrays.fill(mData, position, endPosition + 1, LayoutParams.INVALID_SPAN_ID); + return endPosition + 1; + } + } + + int getSpan(int position) { + if (mData == null || position >= mData.length) { + return LayoutParams.INVALID_SPAN_ID; + } else { + return mData[position]; + } + } + + void setSpan(int position, Span span) { + ensureSize(position); + mData[position] = span.mIndex; + } + + int sizeForPosition(int position) { + int len = mData.length; + while (len <= position) { + len *= 2; + } + return len; + } + + void ensureSize(int position) { + if (mData == null) { + mData = new int[Math.max(position, MIN_SIZE) + 1]; + Arrays.fill(mData, LayoutParams.INVALID_SPAN_ID); + } else if (position >= mData.length) { + int[] old = mData; + mData = new int[sizeForPosition(position)]; + System.arraycopy(old, 0, mData, 0, old.length); + Arrays.fill(mData, old.length, mData.length, LayoutParams.INVALID_SPAN_ID); + } + } + + void clear() { + if (mData != null) { + Arrays.fill(mData, LayoutParams.INVALID_SPAN_ID); + } + mFullSpanItems = null; + } + + void offsetForRemoval(int positionStart, int itemCount) { + if (mData == null || positionStart >= mData.length) { + return; + } + ensureSize(positionStart + itemCount); + System.arraycopy(mData, positionStart + itemCount, mData, positionStart, + mData.length - positionStart - itemCount); + Arrays.fill(mData, mData.length - itemCount, mData.length, + LayoutParams.INVALID_SPAN_ID); + offsetFullSpansForRemoval(positionStart, itemCount); + } + + private void offsetFullSpansForRemoval(int positionStart, int itemCount) { + if (mFullSpanItems == null) { + return; + } + final int end = positionStart + itemCount; + for (int i = mFullSpanItems.size() - 1; i >= 0; i--) { + FullSpanItem fsi = mFullSpanItems.get(i); + if (fsi.mPosition < positionStart) { + continue; + } + if (fsi.mPosition < end) { + mFullSpanItems.remove(i); + } else { + fsi.mPosition -= itemCount; + } + } + } + + void offsetForAddition(int positionStart, int itemCount) { + if (mData == null || positionStart >= mData.length) { + return; + } + ensureSize(positionStart + itemCount); + System.arraycopy(mData, positionStart, mData, positionStart + itemCount, + mData.length - positionStart - itemCount); + Arrays.fill(mData, positionStart, positionStart + itemCount, + LayoutParams.INVALID_SPAN_ID); + offsetFullSpansForAddition(positionStart, itemCount); + } + + private void offsetFullSpansForAddition(int positionStart, int itemCount) { + if (mFullSpanItems == null) { + return; + } + for (int i = mFullSpanItems.size() - 1; i >= 0; i--) { + FullSpanItem fsi = mFullSpanItems.get(i); + if (fsi.mPosition < positionStart) { + continue; + } + fsi.mPosition += itemCount; + } + } + + /** + * Returns when invalidation should end. e.g. hitting a full span position. + * Returned position SHOULD BE invalidated. + */ + private int invalidateFullSpansAfter(int position) { + if (mFullSpanItems == null) { + return RecyclerView.NO_POSITION; + } + final FullSpanItem item = getFullSpanItem(position); + // if there is an fsi at this position, get rid of it. + if (item != null) { + mFullSpanItems.remove(item); + } + int nextFsiIndex = -1; + final int count = mFullSpanItems.size(); + for (int i = 0; i < count; i++) { + FullSpanItem fsi = mFullSpanItems.get(i); + if (fsi.mPosition >= position) { + nextFsiIndex = i; + break; + } + } + if (nextFsiIndex != -1) { + FullSpanItem fsi = mFullSpanItems.get(nextFsiIndex); + mFullSpanItems.remove(nextFsiIndex); + return fsi.mPosition; + } + return RecyclerView.NO_POSITION; + } + + public void addFullSpanItem(FullSpanItem fullSpanItem) { + if (mFullSpanItems == null) { + mFullSpanItems = new ArrayList(); + } + final int size = mFullSpanItems.size(); + for (int i = 0; i < size; i++) { + FullSpanItem other = mFullSpanItems.get(i); + if (other.mPosition == fullSpanItem.mPosition) { + if (DEBUG) { + throw new IllegalStateException("two fsis for same position"); + } else { + mFullSpanItems.remove(i); + } + } + if (other.mPosition >= fullSpanItem.mPosition) { + mFullSpanItems.add(i, fullSpanItem); + return; + } + } + // if it is not added to a position. + mFullSpanItems.add(fullSpanItem); + } + + public FullSpanItem getFullSpanItem(int position) { + if (mFullSpanItems == null) { + return null; + } + for (int i = mFullSpanItems.size() - 1; i >= 0; i--) { + final FullSpanItem fsi = mFullSpanItems.get(i); + if (fsi.mPosition == position) { + return fsi; + } + } + return null; + } + + /** + * @param minPos inclusive + * @param maxPos exclusive + * @param gapDir if not 0, returns FSIs on in that direction + * @param hasUnwantedGapAfter If true, when full span item has unwanted gaps, it will be + * returned even if its gap direction does not match. + */ + public FullSpanItem getFirstFullSpanItemInRange(int minPos, int maxPos, int gapDir, + boolean hasUnwantedGapAfter) { + if (mFullSpanItems == null) { + return null; + } + final int limit = mFullSpanItems.size(); + for (int i = 0; i < limit; i++) { + FullSpanItem fsi = mFullSpanItems.get(i); + if (fsi.mPosition >= maxPos) { + return null; + } + if (fsi.mPosition >= minPos + && (gapDir == 0 || fsi.mGapDir == gapDir || + (hasUnwantedGapAfter && fsi.mHasUnwantedGapAfter))) { + return fsi; + } + } + return null; + } + + /** + * We keep information about full span items because they may create gaps in the UI. + */ + static class FullSpanItem implements Parcelable { + + int mPosition; + int mGapDir; + int[] mGapPerSpan; + // A full span may be laid out in primary direction but may have gaps due to + // invalidation of views after it. This is recorded during a reverse scroll and if + // view is still on the screen after scroll stops, we have to recalculate layout + boolean mHasUnwantedGapAfter; + + public FullSpanItem(Parcel in) { + mPosition = in.readInt(); + mGapDir = in.readInt(); + mHasUnwantedGapAfter = in.readInt() == 1; + int spanCount = in.readInt(); + if (spanCount > 0) { + mGapPerSpan = new int[spanCount]; + in.readIntArray(mGapPerSpan); + } + } + + public FullSpanItem() { + } + + int getGapForSpan(int spanIndex) { + return mGapPerSpan == null ? 0 : mGapPerSpan[spanIndex]; + } + + public void invalidateSpanGaps() { + mGapPerSpan = null; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(mPosition); + dest.writeInt(mGapDir); + dest.writeInt(mHasUnwantedGapAfter ? 1 : 0); + if (mGapPerSpan != null && mGapPerSpan.length > 0) { + dest.writeInt(mGapPerSpan.length); + dest.writeIntArray(mGapPerSpan); + } else { + dest.writeInt(0); + } + } + + @Override + public String toString() { + return "FullSpanItem{" + + "mPosition=" + mPosition + + ", mGapDir=" + mGapDir + + ", mHasUnwantedGapAfter=" + mHasUnwantedGapAfter + + ", mGapPerSpan=" + Arrays.toString(mGapPerSpan) + + '}'; + } + + public static final Parcelable.Creator CREATOR + = new Parcelable.Creator() { + @Override + public FullSpanItem createFromParcel(Parcel in) { + return new FullSpanItem(in); + } + + @Override + public FullSpanItem[] newArray(int size) { + return new FullSpanItem[size]; + } + }; + } + } + + static class SavedState implements Parcelable { + + int mAnchorPosition; + int mVisibleAnchorPosition; // Replacement for span info when spans are invalidated + int mSpanOffsetsSize; + int[] mSpanOffsets; + int mSpanLookupSize; + int[] mSpanLookup; + List mFullSpanItems; + boolean mReverseLayout; + boolean mAnchorLayoutFromEnd; + boolean mLastLayoutRTL; + + public SavedState() { + } + + SavedState(Parcel in) { + mAnchorPosition = in.readInt(); + mVisibleAnchorPosition = in.readInt(); + mSpanOffsetsSize = in.readInt(); + if (mSpanOffsetsSize > 0) { + mSpanOffsets = new int[mSpanOffsetsSize]; + in.readIntArray(mSpanOffsets); + } + + mSpanLookupSize = in.readInt(); + if (mSpanLookupSize > 0) { + mSpanLookup = new int[mSpanLookupSize]; + in.readIntArray(mSpanLookup); + } + mReverseLayout = in.readInt() == 1; + mAnchorLayoutFromEnd = in.readInt() == 1; + mLastLayoutRTL = in.readInt() == 1; + mFullSpanItems = in.readArrayList( + LazySpanLookup.FullSpanItem.class.getClassLoader()); + } + + public SavedState(SavedState other) { + mSpanOffsetsSize = other.mSpanOffsetsSize; + mAnchorPosition = other.mAnchorPosition; + mVisibleAnchorPosition = other.mVisibleAnchorPosition; + mSpanOffsets = other.mSpanOffsets; + mSpanLookupSize = other.mSpanLookupSize; + mSpanLookup = other.mSpanLookup; + mReverseLayout = other.mReverseLayout; + mAnchorLayoutFromEnd = other.mAnchorLayoutFromEnd; + mLastLayoutRTL = other.mLastLayoutRTL; + mFullSpanItems = other.mFullSpanItems; + } + + void invalidateSpanInfo() { + mSpanOffsets = null; + mSpanOffsetsSize = 0; + mSpanLookupSize = 0; + mSpanLookup = null; + mFullSpanItems = null; + } + + void invalidateAnchorPositionInfo() { + mSpanOffsets = null; + mSpanOffsetsSize = 0; + mAnchorPosition = NO_POSITION; + mVisibleAnchorPosition = NO_POSITION; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(mAnchorPosition); + dest.writeInt(mVisibleAnchorPosition); + dest.writeInt(mSpanOffsetsSize); + if (mSpanOffsetsSize > 0) { + dest.writeIntArray(mSpanOffsets); + } + dest.writeInt(mSpanLookupSize); + if (mSpanLookupSize > 0) { + dest.writeIntArray(mSpanLookup); + } + dest.writeInt(mReverseLayout ? 1 : 0); + dest.writeInt(mAnchorLayoutFromEnd ? 1 : 0); + dest.writeInt(mLastLayoutRTL ? 1 : 0); + dest.writeList(mFullSpanItems); + } + + public static final Parcelable.Creator CREATOR + = new Parcelable.Creator() { + @Override + public SavedState createFromParcel(Parcel in) { + return new SavedState(in); + } + + @Override + public SavedState[] newArray(int size) { + return new SavedState[size]; + } + }; + } + + /** + * Data class to hold the information about an anchor position which is used in onLayout call. + */ + private class AnchorInfo { + + int mPosition; + int mOffset; + boolean mLayoutFromEnd; + boolean mInvalidateOffsets; + + void reset() { + mPosition = NO_POSITION; + mOffset = INVALID_OFFSET; + mLayoutFromEnd = false; + mInvalidateOffsets = false; + } + + void assignCoordinateFromPadding() { + mOffset = mLayoutFromEnd ? mPrimaryOrientation.getEndAfterPadding() + : mPrimaryOrientation.getStartAfterPadding(); + } + + void assignCoordinateFromPadding(int addedDistance) { + if (mLayoutFromEnd) { + mOffset = mPrimaryOrientation.getEndAfterPadding() - addedDistance; + } else { + mOffset = mPrimaryOrientation.getStartAfterPadding() + addedDistance; + } + } + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/android/support/widget/util/SortedListAdapterCallback.java b/TMessagesProj/src/main/java/org/telegram/android/support/widget/util/SortedListAdapterCallback.java new file mode 100644 index 000000000..44ddd667e --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/android/support/widget/util/SortedListAdapterCallback.java @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2014 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.android.support.widget.util; + +import android.support.v7.util.SortedList; +import org.telegram.android.support.widget.RecyclerView; + +/** + * A {@link SortedList.Callback} implementation that can bind a {@link SortedList} to a + * {@link RecyclerView.Adapter}. + */ +public abstract class SortedListAdapterCallback extends SortedList.Callback { + + final RecyclerView.Adapter mAdapter; + + /** + * Creates a {@link SortedList.Callback} that will forward data change events to the provided + * Adapter. + * + * @param adapter The Adapter instance which should receive events from the SortedList. + */ + public SortedListAdapterCallback(RecyclerView.Adapter adapter) { + mAdapter = adapter; + } + + @Override + public void onInserted(int position, int count) { + mAdapter.notifyItemRangeInserted(position, count); + } + + @Override + public void onRemoved(int position, int count) { + mAdapter.notifyItemRangeRemoved(position, count); + } + + @Override + public void onMoved(int fromPosition, int toPosition) { + mAdapter.notifyItemMoved(fromPosition, toPosition); + } + + @Override + public void onChanged(int position, int count) { + mAdapter.notifyItemRangeChanged(position, count); + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/AbsSerializedData.java b/TMessagesProj/src/main/java/org/telegram/messenger/AbsSerializedData.java index 0f0bb480c..1e24cd4e1 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/AbsSerializedData.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/AbsSerializedData.java @@ -20,17 +20,19 @@ public abstract class AbsSerializedData { public abstract void writeByteArray(byte[] b, int offset, int count); public abstract void writeByteArray(byte[] b); public abstract void writeDouble(double d); - public abstract int readInt32(); - public abstract int readInt32(boolean[] error); - public abstract boolean readBool(); - public abstract long readInt64(); - public abstract long readInt64(boolean[] error); - public abstract void readRaw(byte[] b); - public abstract byte[] readData(int count); - public abstract String readString(); - public abstract byte[] readByteArray(); - public abstract ByteBufferDesc readByteBuffer(); public abstract void writeByteBuffer(ByteBufferDesc buffer); - public abstract double readDouble(); + + public abstract int readInt32(boolean exception); + public abstract boolean readBool(boolean exception); + public abstract long readInt64(boolean exception); + public abstract void readRaw(byte[] b, boolean exception); + public abstract byte[] readData(int count, boolean exception); + public abstract String readString(boolean exception); + public abstract byte[] readByteArray(boolean exception); + public abstract ByteBufferDesc readByteBuffer(boolean exception); + public abstract double readDouble(boolean exception); + public abstract int length(); + public abstract void skip(int count); + public abstract int getPosition(); } diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/ApplicationLoader.java b/TMessagesProj/src/main/java/org/telegram/messenger/ApplicationLoader.java index 481be8415..70a9b6f1d 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/ApplicationLoader.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/ApplicationLoader.java @@ -17,8 +17,6 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.SharedPreferences; -import android.content.pm.PackageInfo; -import android.content.pm.PackageManager; import android.content.res.Configuration; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; @@ -273,8 +271,7 @@ public class ApplicationLoader extends Application { return ""; } int registeredVersion = prefs.getInt(PROPERTY_APP_VERSION, Integer.MIN_VALUE); - int currentVersion = getAppVersion(); - if (registeredVersion != currentVersion) { + if (registeredVersion != BuildVars.BUILD_VERSION) { FileLog.d("tmessages", "App version changed."); return ""; } @@ -285,15 +282,6 @@ public class ApplicationLoader extends Application { return getSharedPreferences(ApplicationLoader.class.getSimpleName(), Context.MODE_PRIVATE); } - public static int getAppVersion() { - try { - PackageInfo packageInfo = applicationContext.getPackageManager().getPackageInfo(applicationContext.getPackageName(), 0); - return packageInfo.versionCode; - } catch (PackageManager.NameNotFoundException e) { - throw new RuntimeException("Could not get package name: " + e); - } - } - private void registerInBackground() { AsyncTask task = new AsyncTask() { @Override @@ -354,7 +342,7 @@ public class ApplicationLoader extends Application { private void storeRegistrationId(Context context, String regId) { final SharedPreferences prefs = getGCMPreferences(context); - int appVersion = getAppVersion(); + int appVersion = BuildVars.BUILD_VERSION; FileLog.e("tmessages", "Saving regId on app version " + appVersion); SharedPreferences.Editor editor = prefs.edit(); editor.putString(PROPERTY_REG_ID, regId); diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/BuildVars.java b/TMessagesProj/src/main/java/org/telegram/messenger/BuildVars.java index d2b91b67a..9507d40e1 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/BuildVars.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/BuildVars.java @@ -10,10 +10,14 @@ package org.telegram.messenger; public class BuildVars { public static boolean DEBUG_VERSION = false; + public static int BUILD_VERSION = 521; 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"; public static String GCM_SENDER_ID = "760348033672"; public static String SEND_LOGS_EMAIL = "email@gmail.com"; public static String BING_SEARCH_KEY = ""; //obtain your own KEY at https://www.bing.com/dev/en-us/dev-center + public static String FOURSQUARE_API_KEY = ""; //obtain your own KEY at https://developer.foursquare.com/ + public static String FOURSQUARE_API_ID = ""; //obtain your own API_ID at https://developer.foursquare.com/ + public static String FOURSQUARE_API_VERSION = "20150326"; } diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/ByteArrayOutputStreamExpand.java b/TMessagesProj/src/main/java/org/telegram/messenger/ByteArrayOutputStreamExpand.java new file mode 100644 index 000000000..b82131fef --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/messenger/ByteArrayOutputStreamExpand.java @@ -0,0 +1,81 @@ +/* + * This is the source code of Telegram for Android v. 2.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-2015. + */ + +package org.telegram.messenger; + +import java.io.OutputStream; + +public class ByteArrayOutputStreamExpand extends OutputStream { + + protected byte[] buf; + protected int count; + + public ByteArrayOutputStreamExpand() { + buf = new byte[32]; + } + + public ByteArrayOutputStreamExpand(int size) { + if (size >= 0) { + buf = new byte[size]; + } else { + throw new IllegalArgumentException("size < 0"); + } + } + + private void expand(int i) { + if (count + i <= buf.length) { + return; + } + + byte[] newbuf = new byte[count + i]; + System.arraycopy(buf, 0, newbuf, 0, count); + buf = newbuf; + } + + public synchronized void reset() { + count = 0; + } + + public int size() { + return count; + } + + public byte[] toByteArray() { + return buf; + } + + @Override + public String toString() { + return new String(buf, 0, count); + } + + @Override + public void write(byte[] buffer, int offset, int len) { + checkOffsetAndCount(buffer.length, offset, len); + if (len == 0) { + return; + } + expand(len); + System.arraycopy(buffer, offset, buf, this.count, len); + this.count += len; + } + + @Override + public void write(int oneByte) { + if (count == buf.length) { + expand(1); + } + buf[count++] = (byte) oneByte; + } + + public void checkOffsetAndCount(int arrayLength, int offset, int count) { + if ((offset | count) < 0 || offset > arrayLength || arrayLength - offset < count) { + throw new ArrayIndexOutOfBoundsException("length=" + arrayLength + "; regionStart=" + offset + "; regionLength=" + count); + } + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/ByteBufferDesc.java b/TMessagesProj/src/main/java/org/telegram/messenger/ByteBufferDesc.java index f36049bc8..ca366b0b1 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/ByteBufferDesc.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/ByteBufferDesc.java @@ -109,7 +109,7 @@ public class ByteBufferDesc extends AbsSerializedData { } else { len += b.length; } - } catch (Exception x) { + } catch (Exception e) { FileLog.e("tmessages", "write raw error"); } } @@ -121,7 +121,7 @@ public class ByteBufferDesc extends AbsSerializedData { } else { len += count; } - } catch (Exception x) { + } catch (Exception e) { FileLog.e("tmessages", "write raw error"); } } @@ -145,7 +145,7 @@ public class ByteBufferDesc extends AbsSerializedData { public void writeString(String s) { try { writeByteArray(s.getBytes("UTF-8")); - } catch(Exception x) { + } catch(Exception e) { FileLog.e("tmessages", "write string error"); } } @@ -182,7 +182,7 @@ public class ByteBufferDesc extends AbsSerializedData { } i++; } - } catch (Exception x) { + } catch (Exception e) { FileLog.e("tmessages", "write byte array error"); } } @@ -219,7 +219,7 @@ public class ByteBufferDesc extends AbsSerializedData { } i++; } - } catch (Exception x) { + } catch (Exception e) { FileLog.e("tmessages", "write byte array error"); } } @@ -227,7 +227,7 @@ public class ByteBufferDesc extends AbsSerializedData { public void writeDouble(double d) { try { writeInt64(Double.doubleToRawLongBits(d)); - } catch(Exception x) { + } catch(Exception e) { FileLog.e("tmessages", "write double error"); } } @@ -280,72 +280,92 @@ public class ByteBufferDesc extends AbsSerializedData { } } - public int readInt32() { - return readInt32(null); + public int getIntFromByte(byte b) { + return b >= 0 ? b : ((int)b) + 256; } - public int readInt32(boolean[] error) { + public int length() { + if (!justCalc) { + return buffer.position(); + } + return len; + } + + public void skip(int count) { + if (count == 0) { + return; + } + if (!justCalc) { + buffer.position(buffer.position() + count); + } else { + len += count; + } + } + + public int getPosition() { + return buffer.position(); + } + + public int readInt32(boolean exception) { try { - int i = buffer.getInt(); - if (error != null) { - error[0] = false; + return buffer.getInt(); + } catch (Exception e) { + if (exception) { + throw new RuntimeException("read int32 error", e); + } else { + FileLog.e("tmessages", "read int32 error"); } - return i; - } catch (Exception x) { - if (error != null) { - error[0] = true; - } - FileLog.e("tmessages", "read int32 error"); } return 0; } - public boolean readBool() { - int consructor = readInt32(); + public boolean readBool(boolean exception) { + int consructor = readInt32(exception); if (consructor == 0x997275b5) { return true; } else if (consructor == 0xbc799737) { return false; } - FileLog.e("tmessages", "Not bool value!"); + if (exception) { + throw new RuntimeException("Not bool value!"); + } else { + FileLog.e("tmessages", "Not bool value!"); + } return false; } - public long readInt64() { - return readInt64(null); - } - - public long readInt64(boolean[] error) { + public long readInt64(boolean exception) { try { - long i = buffer.getLong(); - if (error != null) { - error[0] = false; + return buffer.getLong(); + } catch (Exception e) { + if (exception) { + throw new RuntimeException("read int64 error", e); + } else { + FileLog.e("tmessages", "read int64 error"); } - return i; - } catch (Exception x) { - if (error != null) { - error[0] = true; - } - FileLog.e("tmessages", "read int64 error"); } return 0; } - public void readRaw(byte[] b) { + public void readRaw(byte[] b, boolean exception) { try { buffer.get(b); - } catch (Exception x) { - FileLog.e("tmessages", "read raw error"); + } catch (Exception e) { + if (exception) { + throw new RuntimeException("read raw error", e); + } else { + FileLog.e("tmessages", "read raw error"); + } } } - public byte[] readData(int count) { + public byte[] readData(int count, boolean exception) { byte[] arr = new byte[count]; - readRaw(arr); + readRaw(arr, exception); return arr; } - public String readString() { + public String readString(boolean exception) { try { int sl = 1; int l = getIntFromByte(buffer.get()); @@ -361,17 +381,17 @@ public class ByteBufferDesc extends AbsSerializedData { i++; } return new String(b, "UTF-8"); - } catch (Exception x) { - FileLog.e("tmessages", "read string error"); + } catch (Exception e) { + if (exception) { + throw new RuntimeException("read string error", e); + } else { + FileLog.e("tmessages", "read string error"); + } } return null; } - public int getIntFromByte(byte b) { - return b >= 0 ? b : ((int)b) + 256; - } - - public byte[] readByteArray() { + public byte[] readByteArray(boolean exception) { try { int sl = 1; int l = getIntFromByte(buffer.get()); @@ -387,13 +407,17 @@ public class ByteBufferDesc extends AbsSerializedData { i++; } return b; - } catch (Exception x) { - FileLog.e("tmessages", "read byte array error"); + } catch (Exception e) { + if (exception) { + throw new RuntimeException("read byte array error", e); + } else { + FileLog.e("tmessages", "read byte array error"); + } } return null; } - public ByteBufferDesc readByteBuffer() { + public ByteBufferDesc readByteBuffer(boolean exception) { try { int sl = 1; int l = getIntFromByte(buffer.get()); @@ -415,25 +439,26 @@ public class ByteBufferDesc extends AbsSerializedData { i++; } return b; - } catch (Exception x) { - FileLog.e("tmessages", "read byte array error"); + } catch (Exception e) { + if (exception) { + throw new RuntimeException("read byte array error", e); + } else { + FileLog.e("tmessages", "read byte array error"); + } } return null; } - public double readDouble() { + public double readDouble(boolean exception) { try { - return Double.longBitsToDouble(readInt64()); - } catch(Exception x) { - FileLog.e("tmessages", "read double error"); + return Double.longBitsToDouble(readInt64(exception)); + } catch(Exception e) { + if (exception) { + throw new RuntimeException("read double error", e); + } else { + FileLog.e("tmessages", "read double error"); + } } return 0; } - - public int length() { - if (!justCalc) { - return buffer.position(); - } - return len; - } } diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/ConnectionsManager.java b/TMessagesProj/src/main/java/org/telegram/messenger/ConnectionsManager.java index 3d8fa2a10..b0a32b642 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/ConnectionsManager.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/ConnectionsManager.java @@ -65,7 +65,6 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. private boolean updatingDcSettings = false; private int updatingDcStartTime = 0; private int lastDcUpdateTime = 0; - private int currentAppVersion = 0; private long pushSessionId; private boolean registeringForPush = false; @@ -85,6 +84,7 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. private PowerManager.WakeLock wakeLock = null; private static volatile ConnectionsManager Instance = null; + public static ConnectionsManager getInstance() { ConnectionsManager localInstance = Instance; if (localInstance == null) { @@ -185,7 +185,7 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. boolean notFound = true; for (Action actor : actionQueue) { if (actor instanceof HandshakeAction) { - HandshakeAction eactor = (HandshakeAction)actor; + HandshakeAction eactor = (HandshakeAction) actor; if (eactor.datacenter.datacenterId == datacenter.datacenterId) { notFound = false; break; @@ -206,7 +206,6 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. }; public ConnectionsManager() { - currentAppVersion = ApplicationLoader.getAppVersion(); lastOutgoingMessageId = 0; movingToDatacenterId = DEFAULT_DATACENTER_ID; loadSession(); @@ -218,7 +217,7 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. Utilities.stageQueue.postRunnable(stageRunnable, 1000); try { - PowerManager pm = (PowerManager)ApplicationLoader.applicationContext.getSystemService(Context.POWER_SERVICE); + PowerManager pm = (PowerManager) ApplicationLoader.applicationContext.getSystemService(Context.POWER_SERVICE); wakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "lock"); wakeLock.setReferenceCounted(false); } catch (Exception e) { @@ -352,20 +351,20 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. if (configFile.exists()) { try { SerializedData data = new SerializedData(configFile); - isTestBackend = data.readInt32(); - int version = data.readInt32(); + isTestBackend = data.readInt32(false); + int version = data.readInt32(false); sessionsToDestroy.clear(); - int count = data.readInt32(); + int count = data.readInt32(false); for (int a = 0; a < count; a++) { - sessionsToDestroy.add(data.readInt64()); + sessionsToDestroy.add(data.readInt64(false)); } - timeDifference = data.readInt32(); - count = data.readInt32(); + timeDifference = data.readInt32(false); + count = data.readInt32(false); for (int a = 0; a < count; a++) { Datacenter datacenter = new Datacenter(data, 0); datacenters.put(datacenter.datacenterId, datacenter); } - currentDatacenterId = data.readInt32(); + currentDatacenterId = data.readInt32(false); data.cleanup(); } catch (Exception e) { UserConfig.clearConfig(); @@ -385,9 +384,9 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. byte[] sessionsBytes = Base64.decode(sessionsString, Base64.DEFAULT); if (sessionsBytes != null) { SerializedData data = new SerializedData(sessionsBytes); - int count = data.readInt32(); + int count = data.readInt32(false); for (int a = 0; a < count; a++) { - sessionsToDestroy.add(data.readInt64()); + sessionsToDestroy.add(data.readInt64(false)); } data.cleanup(); } @@ -402,7 +401,7 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. byte[] datacentersBytes = Base64.decode(datacentersString, Base64.DEFAULT); if (datacentersBytes != null) { SerializedData data = new SerializedData(datacentersBytes); - int count = data.readInt32(); + int count = data.readInt32(false); for (int a = 0; a < count; a++) { Datacenter datacenter = new Datacenter(data, 1); datacenters.put(datacenter.datacenterId, datacenter); @@ -625,7 +624,7 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. } long generateMessageId() { - long messageId = (long)((((double)System.currentTimeMillis() + ((double)timeDifference) * 1000) * 4294967296.0) / 1000.0); + long messageId = (long) ((((double) System.currentTimeMillis() + ((double) timeDifference) * 1000) * 4294967296.0) / 1000.0); if (messageId <= lastOutgoingMessageId) { messageId = lastOutgoingMessageId + 1; } @@ -637,13 +636,14 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. } long getTimeFromMsgId(long messageId) { - return (long)(messageId / 4294967296.0 * 1000); + return (long) (messageId / 4294967296.0 * 1000); } //================================================================================ // Requests manage //================================================================================ int lastClassGuid = 1; + public int generateClassGuid() { int guid = lastClassGuid++; requestsByGuids.put(guid, new ArrayList()); @@ -756,7 +756,7 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. if (updatingDcSettings) { return; } - updatingDcStartTime = (int)(System.currentTimeMillis() / 1000); + updatingDcStartTime = (int) (System.currentTimeMillis() / 1000); updatingDcSettings = true; TLRPC.TL_help_getConfig getConfig = new TLRPC.TL_help_getConfig(); @@ -767,12 +767,12 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. return; } if (error == null) { - TLRPC.TL_config config = (TLRPC.TL_config)response; + TLRPC.TL_config config = (TLRPC.TL_config) response; int updateIn = config.expires - getCurrentTime(); if (updateIn <= 0) { updateIn = 120; } - lastDcUpdateTime = (int)(System.currentTimeMillis() / 1000) - DC_UPDATE_TIME + updateIn; + lastDcUpdateTime = (int) (System.currentTimeMillis() / 1000) - DC_UPDATE_TIME + updateIn; ArrayList datacentersArr = new ArrayList<>(); HashMap datacenterMap = new HashMap<>(); for (TLRPC.TL_dcOption datacenterDesc : config.dc_options) { @@ -813,14 +813,17 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. private TLObject wrapInLayer(TLObject object, int datacenterId, RPCRequest request) { if (object.layer() > 0) { Datacenter datacenter = datacenterWithId(datacenterId); - if (datacenter == null || datacenter.lastInitVersion != currentAppVersion) { + if (datacenter == null || datacenter.lastInitVersion != BuildVars.BUILD_VERSION) { registerForPush(); request.initRequest = true; TLRPC.initConnection invoke = new TLRPC.initConnection(); invoke.query = object; invoke.api_id = BuildVars.APP_ID; try { - invoke.lang_code = LocaleController.getLocaleString(Locale.getDefault()); + invoke.lang_code = LocaleController.getLocaleString(LocaleController.getInstance().getSystemDefaultLocale()); + if (invoke.lang_code == null || invoke.lang_code.length() == 0) { + invoke.lang_code = "en"; + } invoke.device_model = Build.MANUFACTURER + Build.MODEL; if (invoke.device_model == null) { invoke.device_model = "Android unknown"; @@ -962,7 +965,7 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. public static boolean isNetworkOnline() { try { - ConnectivityManager cm = (ConnectivityManager)ApplicationLoader.applicationContext.getSystemService(Context.CONNECTIVITY_SERVICE); + ConnectivityManager cm = (ConnectivityManager) ApplicationLoader.applicationContext.getSystemService(Context.CONNECTIVITY_SERVICE); NetworkInfo netInfo = cm.getActiveNetworkInfo(); if (netInfo != null && (netInfo.isConnectedOrConnecting() || netInfo.isAvailable())) { return true; @@ -974,11 +977,11 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. return true; } else { netInfo = cm.getNetworkInfo(ConnectivityManager.TYPE_WIFI); - if(netInfo != null && netInfo.isConnectedOrConnecting()) { + if (netInfo != null && netInfo.isConnectedOrConnecting()) { return true; } } - } catch(Exception e) { + } catch (Exception e) { FileLog.e("tmessages", e); return true; } @@ -987,12 +990,12 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. public static boolean isRoaming() { try { - ConnectivityManager cm = (ConnectivityManager)ApplicationLoader.applicationContext.getSystemService(Context.CONNECTIVITY_SERVICE); + ConnectivityManager cm = (ConnectivityManager) ApplicationLoader.applicationContext.getSystemService(Context.CONNECTIVITY_SERVICE); NetworkInfo netInfo = cm.getActiveNetworkInfo(); if (netInfo != null) { return netInfo.isRoaming(); } - } catch(Exception e) { + } catch (Exception e) { FileLog.e("tmessages", e); } return false; @@ -1000,19 +1003,19 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. public static boolean isConnectedToWiFi() { try { - ConnectivityManager cm = (ConnectivityManager)ApplicationLoader.applicationContext.getSystemService(Context.CONNECTIVITY_SERVICE); + ConnectivityManager cm = (ConnectivityManager) ApplicationLoader.applicationContext.getSystemService(Context.CONNECTIVITY_SERVICE); NetworkInfo netInfo = cm.getNetworkInfo(ConnectivityManager.TYPE_WIFI); if (netInfo != null && netInfo.getState() == NetworkInfo.State.CONNECTED) { return true; } - } catch(Exception e) { + } catch (Exception e) { FileLog.e("tmessages", e); } return false; } public int getCurrentTime() { - return (int)(System.currentTimeMillis() / 1000) + timeDifference; + return (int) (System.currentTimeMillis() / 1000) + timeDifference; } public int getTimeDifference() { @@ -1033,7 +1036,7 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. genericConnection = defaultDatacenter.getGenericConnection(this); } - int currentTime = (int)(System.currentTimeMillis() / 1000); + int currentTime = (int) (System.currentTimeMillis() / 1000); for (int i = 0; i < runningRequests.size(); i++) { RPCRequest request = runningRequests.get(i); @@ -1062,7 +1065,7 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. } Datacenter requestDatacenter = datacenterWithId(datacenterId); - if (!request.initRequest && requestDatacenter.lastInitVersion != currentAppVersion) { + if (!request.initRequest && requestDatacenter.lastInitVersion != BuildVars.BUILD_VERSION) { request.rpcRequest = wrapInLayer(request.rawRequest, requestDatacenter.datacenterId, request); ByteBufferDesc os = new ByteBufferDesc(true); request.rpcRequest.serializeToStream(os); @@ -1093,7 +1096,7 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. connection = requestDatacenter.getGenericConnection(this); } else if ((request.flags & RPCRequest.RPCRequestClassDownloadMedia) != 0) { connection = requestDatacenter.getDownloadConnection(this); - } else if ((request.flags & RPCRequest.RPCRequestClassUploadMedia) != 0 ) { + } else if ((request.flags & RPCRequest.RPCRequestClassUploadMedia) != 0) { connection = requestDatacenter.getUploadConnection(this); } @@ -1203,7 +1206,7 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. continue; } if (System.currentTimeMillis() / 1000 - lastDestroySessionRequestTime > 2.0) { - lastDestroySessionRequestTime = (int)(System.currentTimeMillis() / 1000); + lastDestroySessionRequestTime = (int) (System.currentTimeMillis() / 1000); TLRPC.TL_destroy_session destroySession = new TLRPC.TL_destroy_session(); destroySession.session_id = it; destroyingSessions.add(it); @@ -1277,7 +1280,7 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. } Datacenter requestDatacenter = datacenterWithId(datacenterId); - if (!request.initRequest && requestDatacenter.lastInitVersion != currentAppVersion) { + if (!request.initRequest && requestDatacenter.lastInitVersion != BuildVars.BUILD_VERSION) { request.rpcRequest = wrapInLayer(request.rawRequest, requestDatacenter.datacenterId, request); } @@ -1363,7 +1366,7 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. request.runningMessageId = messageId; request.runningMessageSeqNo = networkMessage.protoMessage.seqno; request.serializedLength = requestLength; - request.runningStartTime = (int)(System.currentTimeMillis() / 1000); + request.runningStartTime = (int) (System.currentTimeMillis() / 1000); request.transportChannelToken = connection.channelToken; if (request.requiresCompletion) { runningRequests.add(request); @@ -1486,7 +1489,7 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. boolean notFound = true; for (Action actor : actionQueue) { if (actor instanceof HandshakeAction) { - HandshakeAction eactor = (HandshakeAction)actor; + HandshakeAction eactor = (HandshakeAction) actor; if (eactor.datacenter.datacenterId == num) { notFound = false; break; @@ -1506,7 +1509,7 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. boolean notFound = true; for (Action actor : actionQueue) { if (actor instanceof ExportAuthorizationAction) { - ExportAuthorizationAction eactor = (ExportAuthorizationAction)actor; + ExportAuthorizationAction eactor = (ExportAuthorizationAction) actor; if (eactor.datacenter.datacenterId == num) { notFound = false; break; @@ -1554,7 +1557,7 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. } ArrayList messages = new ArrayList<>(); - if(messageList != null) { + if (messageList != null) { messages.addAll(messageList); } @@ -1638,11 +1641,11 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. if (BuildVars.DEBUG_VERSION) { if (message.body instanceof TLRPC.invokeWithLayer) { - FileLog.d("tmessages", connection.getSissionId() + ":DC" + datacenter.datacenterId + "> Send message (" + message.seqno + ", " + message.msg_id + "): " + ((TLRPC.invokeWithLayer)message.body).query); + FileLog.d("tmessages", connection.getSissionId() + ":DC" + datacenter.datacenterId + "> Send message (" + message.seqno + ", " + message.msg_id + "): " + ((TLRPC.invokeWithLayer) message.body).query); } else if (message.body instanceof TLRPC.initConnection) { - TLRPC.initConnection r = (TLRPC.initConnection)message.body; + TLRPC.initConnection r = (TLRPC.initConnection) message.body; if (r.query instanceof TLRPC.invokeWithLayer) { - FileLog.d("tmessages", connection.getSissionId() + ":DC" + datacenter.datacenterId + "> Send message (" + message.seqno + ", " + message.msg_id + "): " + ((TLRPC.invokeWithLayer)r.query).query); + FileLog.d("tmessages", connection.getSissionId() + ":DC" + datacenter.datacenterId + "> Send message (" + message.seqno + ", " + message.msg_id + "): " + ((TLRPC.invokeWithLayer) r.query).query); } else { FileLog.d("tmessages", connection.getSissionId() + ":DC" + datacenter.datacenterId + "> Send message (" + message.seqno + ", " + message.msg_id + "): " + r.query); } @@ -1652,7 +1655,7 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. } long msg_time = getTimeFromMsgId(message.msg_id); - long currentTime = System.currentTimeMillis() + ((long)timeDifference) * 1000; + long currentTime = System.currentTimeMillis() + ((long) timeDifference) * 1000; if (msg_time < currentTime - 30000 || msg_time > currentTime + 25000) { FileLog.d("tmessages", "wrap in messages continaer"); @@ -1678,11 +1681,11 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. containerMessages.add(message); if (BuildVars.DEBUG_VERSION) { if (message.body instanceof TLRPC.invokeWithLayer) { - FileLog.d("tmessages", connection.getSissionId() + ":DC" + datacenter.datacenterId + "> Send message (" + message.seqno + ", " + message.msg_id + "): " + ((TLRPC.invokeWithLayer)message.body).query); + FileLog.d("tmessages", connection.getSissionId() + ":DC" + datacenter.datacenterId + "> Send message (" + message.seqno + ", " + message.msg_id + "): " + ((TLRPC.invokeWithLayer) message.body).query); } else if (message.body instanceof TLRPC.initConnection) { - TLRPC.initConnection r = (TLRPC.initConnection)message.body; + TLRPC.initConnection r = (TLRPC.initConnection) message.body; if (r.query instanceof TLRPC.invokeWithLayer) { - FileLog.d("tmessages", connection.getSissionId() + ":DC" + datacenter.datacenterId + "> Send message (" + message.seqno + ", " + message.msg_id + "): " + ((TLRPC.invokeWithLayer)r.query).query); + FileLog.d("tmessages", connection.getSissionId() + ":DC" + datacenter.datacenterId + "> Send message (" + message.seqno + ", " + message.msg_id + "): " + ((TLRPC.invokeWithLayer) r.query).query); } else { FileLog.d("tmessages", connection.getSissionId() + ":DC" + datacenter.datacenterId + "> Send message (" + message.seqno + ", " + message.msg_id + "): " + r.query); } @@ -1722,7 +1725,7 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. if (quickAckId != null) { SerializedData data = new SerializedData(messageKeyFull); - quickAckId.add(data.readInt32() & 0x7fffffff); + quickAckId.add(data.readInt32(false) & 0x7fffffff); data.cleanup(); } @@ -1782,7 +1785,7 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. performRpc(getFutureSalts, new RPCRequest.RPCRequestDelegate() { @Override public void run(TLObject response, TLRPC.TL_error error) { - TLRPC.TL_futuresalts res = (TLRPC.TL_futuresalts)response; + TLRPC.TL_futuresalts res = (TLRPC.TL_futuresalts) response; if (error == null) { int currentTime = getCurrentTime(); datacenter.mergeServerSalts(currentTime, res.salts); @@ -1837,7 +1840,10 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. req.token = "" + pushSessionId; req.app_sandbox = false; try { - req.lang_code = LocaleController.getLocaleString(Locale.getDefault()); + req.lang_code = LocaleController.getLocaleString(LocaleController.getInstance().getSystemDefaultLocale()); + if (req.lang_code == null || req.lang_code.length() == 0) { + req.lang_code = "en"; + } req.device_model = Build.MANUFACTURER + Build.MODEL; if (req.device_model == null) { req.device_model = "Android unknown"; @@ -1896,7 +1902,7 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. Datacenter datacenter = datacenterWithId(connection.getDatacenterId()); if (message instanceof TLRPC.TL_new_session_created) { - TLRPC.TL_new_session_created newSession = (TLRPC.TL_new_session_created)message; + TLRPC.TL_new_session_created newSession = (TLRPC.TL_new_session_created) message; if (!connection.isSessionProcessed(newSession.unique_id)) { FileLog.d("tmessages", "New session:"); @@ -1941,7 +1947,7 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. timeDifference = (int)((time - currentTime) / 1000 - currentPingTime / 2.0); }*/ - TLRPC.TL_msg_container messageContainer = (TLRPC.TL_msg_container)message; + TLRPC.TL_msg_container messageContainer = (TLRPC.TL_msg_container) message; for (TLRPC.TL_protoMessage innerMessage : messageContainer.messages) { long innerMessageId = innerMessage.msg_id; if (innerMessage.seqno % 2 != 0) { @@ -1989,7 +1995,7 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. sendingPushPing = false; } } else if (message instanceof TLRPC.TL_futuresalts) { - TLRPC.TL_futuresalts futureSalts = (TLRPC.TL_futuresalts)message; + TLRPC.TL_futuresalts futureSalts = (TLRPC.TL_futuresalts) message; long requestMid = futureSalts.req_msg_id; for (RPCRequest request : runningRequests) { if (request.respondsToMessageId(requestMid)) { @@ -2007,7 +2013,7 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. } } } else if (message instanceof TLRPC.DestroySessionRes) { - TLRPC.DestroySessionRes res = (TLRPC.DestroySessionRes)message; + TLRPC.DestroySessionRes res = (TLRPC.DestroySessionRes) message; ArrayList lst = new ArrayList<>(); lst.addAll(sessionsToDestroy); destroyingSessions.remove(res.session_id); @@ -2019,18 +2025,18 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. } } } else if (message instanceof TLRPC.TL_rpc_result) { - TLRPC.TL_rpc_result resultContainer = (TLRPC.TL_rpc_result)message; + TLRPC.TL_rpc_result resultContainer = (TLRPC.TL_rpc_result) message; long resultMid = resultContainer.req_msg_id; boolean ignoreResult = false; FileLog.d("tmessages", "object in rpc_result is " + resultContainer.result); if (resultContainer.result instanceof TLRPC.RpcError) { - String errorMessage = ((TLRPC.RpcError)resultContainer.result).error_message; - FileLog.e("tmessages", String.format("***** RPC error %d: %s", ((TLRPC.RpcError)resultContainer.result).error_code, errorMessage)); + String errorMessage = ((TLRPC.RpcError) resultContainer.result).error_message; + FileLog.e("tmessages", String.format("***** RPC error %d: %s", ((TLRPC.RpcError) resultContainer.result).error_code, errorMessage)); int migrateToDatacenterId = DEFAULT_DATACENTER_ID; - if (((TLRPC.RpcError)resultContainer.result).error_code == 303) { + if (((TLRPC.RpcError) resultContainer.result).error_code == 303) { ArrayList migrateErrors = new ArrayList<>(); migrateErrors.add("NETWORK_MIGRATE_"); migrateErrors.add("PHONE_MIGRATE_"); @@ -2079,21 +2085,16 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. boolean discardResponse = false; boolean isError = false; + boolean allowInitConnection = true; + if (request.completionBlock != null) { TLRPC.TL_error implicitError = null; if (resultContainer.result instanceof TLRPC.TL_gzip_packed) { - TLRPC.TL_gzip_packed packet = (TLRPC.TL_gzip_packed)resultContainer.result; - TLObject uncomressed = Utilities.decompress(packet.packed_data, request.rawRequest); - if (uncomressed == null) { - System.gc(); - uncomressed = Utilities.decompress(packet.packed_data, request.rawRequest); - } - if (uncomressed == null) { - throw new RuntimeException("failed to decomress responce for " + request.rawRequest); - } - resultContainer.result = uncomressed; + TLRPC.TL_gzip_packed packet = (TLRPC.TL_gzip_packed) resultContainer.result; + resultContainer.result = Utilities.decompress(packet.packed_data, request.rawRequest, true); } if (resultContainer.result instanceof TLRPC.RpcError) { + allowInitConnection = false; String errorMessage = ((TLRPC.RpcError) resultContainer.result).error_message; FileLog.e("tmessages", String.format("***** RPC error %d: %s", ((TLRPC.RpcError) resultContainer.result).error_code, errorMessage)); @@ -2106,6 +2107,7 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. request.runningMinStartTime = request.runningStartTime + delay; request.confirmed = false; } + request.serverFailureCount++; } else if (errorCode == 420) { if ((request.flags & RPCRequest.RPCRequestClassFailOnServerErrors) == 0) { @@ -2135,20 +2137,21 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. discardResponse = true; request.wait = true; - request.runningMinStartTime = (int)(System.currentTimeMillis() / 1000 + waitTime); + request.runningMinStartTime = (int) (System.currentTimeMillis() / 1000 + waitTime); request.confirmed = false; } } implicitError = new TLRPC.TL_error(); - implicitError.code = ((TLRPC.RpcError)resultContainer.result).error_code; - implicitError.text = ((TLRPC.RpcError)resultContainer.result).error_message; + implicitError.code = ((TLRPC.RpcError) resultContainer.result).error_code; + implicitError.text = ((TLRPC.RpcError) resultContainer.result).error_message; } else if (!(resultContainer.result instanceof TLRPC.TL_error)) { - if (request.rawRequest == null || resultContainer.result == null || !request.rawRequest.responseClass().isAssignableFrom(resultContainer.result.getClass())) { + if (request.rawRequest == null || resultContainer.result == null) { + allowInitConnection = false; if (request.rawRequest == null) { FileLog.e("tmessages", "rawRequest is null"); } else { - FileLog.e("tmessages", "***** RPC error: invalid response class " + resultContainer.result + " (" + request.rawRequest.responseClass() + " expected)"); + FileLog.e("tmessages", "***** RPC error: invalid response class " + resultContainer.result + " (for request " + request.rawRequest + ")"); } implicitError = new TLRPC.TL_error(); implicitError.code = -1000; @@ -2161,6 +2164,7 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. isError = true; request.completionBlock.run(null, implicitError != null ? implicitError : (TLRPC.TL_error) resultContainer.result); } else { + request.completionBlock.run(resultContainer.result, null); if (resultContainer.result instanceof TLRPC.updates_Difference) { pushMessagesReceived = true; AndroidUtilities.runOnUIThread(new Runnable() { @@ -2173,24 +2177,14 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. } }); } - request.completionBlock.run(resultContainer.result, null); } } if (implicitError != null && implicitError.code == 401) { + allowInitConnection = false; isError = true; if (implicitError.text != null && implicitError.text.contains("SESSION_PASSWORD_NEEDED")) { - /*UserConfig.setWaitingForPasswordEnter(true); TODO - UserConfig.saveConfig(false); - if (UserConfig.isClientActivated()) { - discardResponse = true; - AndroidUtilities.runOnUIThread(new Runnable() { - @Override - public void run() { - NotificationCenter.getInstance().postNotificationName(NotificationCenter.needPasswordEnter); - } - }); - }*/ + //ignore this error } else if (datacenter.datacenterId == currentDatacenterId || datacenter.datacenterId == movingToDatacenterId) { if ((request.flags & RPCRequest.RPCRequestClassGeneric) != 0 && UserConfig.isClientActivated()) { UserConfig.clearConfig(); @@ -2214,9 +2208,9 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. } if (!discardResponse) { - if (request.initRequest && !isError) { - if (datacenter.lastInitVersion != currentAppVersion) { - datacenter.lastInitVersion = currentAppVersion; + if (allowInitConnection && request.initRequest && !isError) { + if (datacenter.lastInitVersion != BuildVars.BUILD_VERSION) { + datacenter.lastInitVersion = BuildVars.BUILD_VERSION; saveSession(); FileLog.e("tmessages", "init connection completed"); } else { @@ -2254,7 +2248,7 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. } else if (message instanceof TLRPC.TL_ping) { } else if (message instanceof TLRPC.TL_bad_msg_notification) { - TLRPC.TL_bad_msg_notification badMsgNotification = (TLRPC.TL_bad_msg_notification)message; + TLRPC.TL_bad_msg_notification badMsgNotification = (TLRPC.TL_bad_msg_notification) message; FileLog.e("tmessages", String.format("***** Bad message: %d", badMsgNotification.error_code)); @@ -2267,7 +2261,7 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. if (realId != 0) { long time = getTimeFromMsgId(messageId); long currentTime = System.currentTimeMillis(); - timeDifference = (int)((time - currentTime) / 1000 - currentPingTime / 2.0); + timeDifference = (int) ((time - currentTime) / 1000 - currentPingTime / 2.0); } datacenter.recreateSessions(); @@ -2283,7 +2277,7 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. if (messageId != 0) { long time = getTimeFromMsgId(messageId); long currentTime = System.currentTimeMillis(); - timeDifference = (int)((time - currentTime) / 1000 - currentPingTime / 2.0); + timeDifference = (int) ((time - currentTime) / 1000 - currentPingTime / 2.0); lastOutgoingMessageId = Math.max(messageId, lastOutgoingMessageId); } @@ -2316,7 +2310,7 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. processRequestQueue(RPCRequest.RPCRequestClassTransportMask, datacenter.datacenterId); } } else if (message instanceof TLRPC.MsgDetailedInfo) { - TLRPC.MsgDetailedInfo detailedInfo = (TLRPC.MsgDetailedInfo)message; + TLRPC.MsgDetailedInfo detailedInfo = (TLRPC.MsgDetailedInfo) message; boolean requestResend = false; boolean confirm = true; @@ -2327,8 +2321,8 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. if (request.completed) { break; } - if (request.lastResendTime == 0 || request.lastResendTime + 60 < (int)(System.currentTimeMillis() / 1000)) { - request.lastResendTime = (int)(System.currentTimeMillis() / 1000); + if (request.lastResendTime == 0 || request.lastResendTime + 60 < (int) (System.currentTimeMillis() / 1000)) { + request.lastResendTime = (int) (System.currentTimeMillis() / 1000); requestResend = true; } else { confirm = false; @@ -2356,9 +2350,11 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. connection.addMessageToConfirm(detailedInfo.answer_msg_id); } } else if (message instanceof TLRPC.TL_gzip_packed) { - TLRPC.TL_gzip_packed packet = (TLRPC.TL_gzip_packed)message; - TLObject result = Utilities.decompress(packet.packed_data, getRequestWithMessageId(messageId)); - processMessage(result, messageId, messageSeqNo, messageSalt, connection, innerMsgId, containerMessageId); + TLRPC.TL_gzip_packed packet = (TLRPC.TL_gzip_packed) message; + TLObject result = Utilities.decompress(packet.packed_data, getRequestWithMessageId(messageId), true); + if (result != null) { + processMessage(result, messageId, messageSeqNo, messageSalt, connection, innerMsgId, containerMessageId); + } } else if (message instanceof TLRPC.Updates) { if ((connection.transportRequestClass & RPCRequest.RPCRequestClassPush) != 0) { FileLog.e("tmessages", "received internal push"); @@ -2375,6 +2371,7 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. resumeNetworkInternal(); } else { pushMessagesReceived = true; + MessagesController.getInstance().processUpdates((TLRPC.Updates) message, false); AndroidUtilities.runOnUIThread(new Runnable() { @Override public void run() { @@ -2384,7 +2381,6 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. } } }); - MessagesController.getInstance().processUpdates((TLRPC.Updates) message, false); } } else { FileLog.e("tmessages", "***** Error: unknown message class " + message); @@ -2464,7 +2460,7 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. } if (BuildVars.DEBUG_VERSION) { try { - ConnectivityManager cm = (ConnectivityManager)ApplicationLoader.applicationContext.getSystemService(Context.CONNECTIVITY_SERVICE); + ConnectivityManager cm = (ConnectivityManager) ApplicationLoader.applicationContext.getSystemService(Context.CONNECTIVITY_SERVICE); NetworkInfo[] networkInfos = cm.getAllNetworkInfo(); for (int a = 0; a < 2; a++) { if (a >= networkInfos.length) { @@ -2491,7 +2487,7 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. FileLog.e("tmessages", "push connection closed"); if (BuildVars.DEBUG_VERSION) { try { - ConnectivityManager cm = (ConnectivityManager)ApplicationLoader.applicationContext.getSystemService(Context.CONNECTIVITY_SERVICE); + ConnectivityManager cm = (ConnectivityManager) ApplicationLoader.applicationContext.getSystemService(Context.CONNECTIVITY_SERVICE); NetworkInfo[] networkInfos = cm.getAllNetworkInfo(); for (int a = 0; a < 2; a++) { if (a >= networkInfos.length) { @@ -2577,7 +2573,7 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. } } if (length == 4) { - int error = data.readInt32(); + int error = data.readInt32(false); FileLog.e("tmessages", "mtproto error = " + error); connection.suspendConnection(true); connection.connect(); @@ -2585,22 +2581,20 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. } Datacenter datacenter = datacenterWithId(connection.getDatacenterId()); - long keyId = data.readInt64(); + long keyId = data.readInt64(false); if (keyId == 0) { - long messageId = data.readInt64(); + long messageId = data.readInt64(false); if (connection.isMessageIdProcessed(messageId)) { finishUpdatingState(connection); return; } - int messageLength = data.readInt32(); - int constructor = data.readInt32(); + int messageLength = data.readInt32(false); - TLObject object = TLClassStore.Instance().TLdeserialize(data, constructor, getRequestWithMessageId(messageId)); + TLObject message = deserialize(getRequestWithMessageId(messageId), data, true); - processMessage(object, messageId, 0, 0, connection, 0, 0); - - if (object != null) { + if (message != null) { + processMessage(message, messageId, 0, 0, connection, 0, 0); connection.addProcessedMessageId(messageId); } } else { @@ -2612,13 +2606,13 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. return; } - byte[] messageKey = data.readData(16); + byte[] messageKey = data.readData(16, false); MessageKeyData keyData = Utilities.generateMessageKeyData(datacenter.authKey, messageKey, true); Utilities.aesIgeEncryption(data.buffer, keyData.aesKey, keyData.aesIv, false, false, data.position(), length - 24); - long messageServerSalt = data.readInt64(); - long messageSessionId = data.readInt64(); + long messageServerSalt = data.readInt64(false); + long messageSessionId = data.readInt64(false); if (messageSessionId != connection.getSissionId()) { FileLog.e("tmessages", String.format("***** Error: invalid message session ID (%d instead of %d)", messageSessionId, connection.getSissionId())); @@ -2628,9 +2622,9 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. boolean doNotProcess = false; - long messageId = data.readInt64(); - int messageSeqNo = data.readInt32(); - int messageLength = data.readInt32(); + long messageId = data.readInt64(false); + int messageSeqNo = data.readInt32(false); + int messageLength = data.readInt32(false); if (connection.isMessageIdProcessed(messageId)) { doNotProcess = true; @@ -2654,12 +2648,8 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. } if (!doNotProcess) { - int constructor = data.readInt32(); - TLObject message = TLClassStore.Instance().TLdeserialize(data, constructor, getRequestWithMessageId(messageId)); - - if (message == null) { - FileLog.e("tmessages", "***** Error parsing message: " + constructor); - } else { + TLObject message = deserialize(getRequestWithMessageId(messageId), data, true); + if (message != null) { FileLog.d("tmessages", "received object " + message); processMessage(message, messageId, messageSeqNo, messageServerSalt, connection, 0, 0); connection.addProcessedMessageId(messageId); @@ -2680,6 +2670,33 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. } } + protected TLObject deserialize(TLObject request, AbsSerializedData data, boolean exception) { + int constructor = data.readInt32(exception); + TLObject message = null; + try { + message = TLClassStore.Instance().TLdeserialize(data, constructor, request, exception); + } catch (Exception e) { + FileLog.e("tmessages", e); + } + if (message == null) { + if (request != null) { + try { + message = request.deserializeResponse(data, constructor, exception); + } catch (Exception e) { + FileLog.e("tmessages", e); + } + if (message == null) { + FileLog.e("tmessages", String.format(Locale.US, "***** Error parsing message: %x", constructor)); + } + } else { + FileLog.d("tmessages", String.format(Locale.US, "***** Not found request to parse message: %x", constructor)); + } + } else if (message instanceof TLRPC.TL_rpc_result && ((TLRPC.TL_rpc_result) message).result == null) { + message = null; + } + return message; + } + public TLObject getRequestWithMessageId(long msgId) { for (RPCRequest request : runningRequests) { if (msgId == request.runningMessageId) { @@ -2712,7 +2729,7 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. @Override public void run(TLObject response, TLRPC.TL_error error) { if (error == null) { - movingAuthorization = (TLRPC.TL_auth_exportedAuthorization)response; + movingAuthorization = (TLRPC.TL_auth_exportedAuthorization) response; authorizeOnMovingDatacenter(); } else { Utilities.stageQueue.postRunnable(new Runnable() { @@ -2797,12 +2814,12 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. @Override public void ActionDidFinishExecution(final Action action, HashMap params) { if (action instanceof HandshakeAction) { - HandshakeAction eactor = (HandshakeAction)action; + HandshakeAction eactor = (HandshakeAction) action; eactor.datacenter.connection.delegate = this; saveSession(); if (eactor.datacenter.datacenterId == currentDatacenterId || eactor.datacenter.datacenterId == movingToDatacenterId) { - timeDifference = (Integer)params.get("timeDifference"); + timeDifference = (Integer) params.get("timeDifference"); eactor.datacenter.recreateSessions(); clearRequestsForRequestClass(RPCRequest.RPCRequestClassGeneric, eactor.datacenter); @@ -2811,7 +2828,7 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. } processRequestQueue(RPCRequest.RPCRequestClassTransportMask, eactor.datacenter.datacenterId); } else if (action instanceof ExportAuthorizationAction) { - ExportAuthorizationAction eactor = (ExportAuthorizationAction)action; + ExportAuthorizationAction eactor = (ExportAuthorizationAction) action; Datacenter datacenter = eactor.datacenter; datacenter.authorized = true; diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/Datacenter.java b/TMessagesProj/src/main/java/org/telegram/messenger/Datacenter.java index 61c63d2aa..04ffccfab 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/Datacenter.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/Datacenter.java @@ -45,64 +45,64 @@ public class Datacenter { public Datacenter(SerializedData data, int version) { if (version == 0) { - datacenterId = data.readInt32(); - String address = data.readString(); + datacenterId = data.readInt32(false); + String address = data.readString(false); addresses.add(address); - int port = data.readInt32(); + int port = data.readInt32(false); ports.put(address, port); - int len = data.readInt32(); + int len = data.readInt32(false); if (len != 0) { - authKey = data.readData(len); + authKey = data.readData(len, false); } - len = data.readInt32(); + len = data.readInt32(false); if (len != 0) { - authKeyId = data.readInt64(); + authKeyId = data.readInt64(false); } - authorized = data.readInt32() != 0; - len = data.readInt32(); + authorized = data.readInt32(false) != 0; + len = data.readInt32(false); for (int a = 0; a < len; a++) { ServerSalt salt = new ServerSalt(); - salt.validSince = data.readInt32(); - salt.validUntil = data.readInt32(); - salt.value = data.readInt64(); + salt.validSince = data.readInt32(false); + salt.validUntil = data.readInt32(false); + salt.value = data.readInt64(false); if (authServerSaltSet == null) { authServerSaltSet = new ArrayList<>(); } authServerSaltSet.add(salt); } } else if (version == 1) { - int currentVersion = data.readInt32(); + int currentVersion = data.readInt32(false); if (currentVersion == 2 || currentVersion == 3 || currentVersion == 4) { - datacenterId = data.readInt32(); + datacenterId = data.readInt32(false); if (currentVersion >= 3) { - lastInitVersion = data.readInt32(); + lastInitVersion = data.readInt32(false); } - int len = data.readInt32(); + int len = data.readInt32(false); for (int a = 0; a < len; a++) { - String address = data.readString(); + String address = data.readString(false); addresses.add(address); - ports.put(address, data.readInt32()); + ports.put(address, data.readInt32(false)); } - len = data.readInt32(); + len = data.readInt32(false); if (len != 0) { - authKey = data.readData(len); + authKey = data.readData(len, false); } if (currentVersion == 4) { - authKeyId = data.readInt64(); + authKeyId = data.readInt64(false); } else { - len = data.readInt32(); + len = data.readInt32(false); if (len != 0) { - authKeyId = data.readInt64(); + authKeyId = data.readInt64(false); } } - authorized = data.readInt32() != 0; - len = data.readInt32(); + authorized = data.readInt32(false) != 0; + len = data.readInt32(false); for (int a = 0; a < len; a++) { ServerSalt salt = new ServerSalt(); - salt.validSince = data.readInt32(); - salt.validUntil = data.readInt32(); - salt.value = data.readInt64(); + salt.validSince = data.readInt32(false); + salt.validUntil = data.readInt32(false); + salt.value = data.readInt64(false); if (authServerSaltSet == null) { authServerSaltSet = new ArrayList<>(); } diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/FileLoader.java b/TMessagesProj/src/main/java/org/telegram/messenger/FileLoader.java index af3ae922c..8dbed3e4c 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/FileLoader.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/FileLoader.java @@ -631,6 +631,10 @@ public class FileLoader { } public static TLRPC.PhotoSize getClosestPhotoSizeWithSize(ArrayList sizes, int side) { + return getClosestPhotoSizeWithSize(sizes, side, false); + } + + public static TLRPC.PhotoSize getClosestPhotoSizeWithSize(ArrayList sizes, int side, boolean byMinSide) { if (sizes == null || sizes.isEmpty()) { return null; } @@ -640,10 +644,18 @@ public class FileLoader { if (obj == null) { continue; } - int currentSide = obj.w >= obj.h ? obj.w : obj.h; - if (closestObject == null || side > 100 && closestObject.location != null && closestObject.location.dc_id == Integer.MIN_VALUE || obj instanceof TLRPC.TL_photoCachedSize || currentSide <= side && lastSide < currentSide) { - closestObject = obj; - lastSide = currentSide; + if (byMinSide) { + int currentSide = obj.h >= obj.w ? obj.w : obj.h; + if (closestObject == null || side > 100 && closestObject.location != null && closestObject.location.dc_id == Integer.MIN_VALUE || obj instanceof TLRPC.TL_photoCachedSize || side > lastSide && lastSide < currentSide) { + closestObject = obj; + lastSide = currentSide; + } + } else { + int currentSide = obj.w >= obj.h ? obj.w : obj.h; + if (closestObject == null || side > 100 && closestObject.location != null && closestObject.location.dc_id == Integer.MIN_VALUE || obj instanceof TLRPC.TL_photoCachedSize || currentSide <= side && lastSide < currentSide) { + closestObject = obj; + lastSide = currentSide; + } } } return closestObject; diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/HandshakeAction.java b/TMessagesProj/src/main/java/org/telegram/messenger/HandshakeAction.java index dc5f426d1..cf64c98e0 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/HandshakeAction.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/HandshakeAction.java @@ -340,7 +340,7 @@ public class HandshakeAction extends Action implements TcpConnection.TcpConnecti Utilities.aesIgeEncryption(answerWithHash.buffer, tmpAesKey.toByteArray(), tmpAesIv.toByteArray(), false, false, 0, serverDhParams.encrypted_answer.length); byte[] answerHash = new byte[20]; - answerWithHash.readRaw(answerHash); + answerWithHash.readRaw(answerHash, false); boolean hashVerified = false; for (int i = 0; i < 16; i++) { @@ -358,8 +358,8 @@ public class HandshakeAction extends Action implements TcpConnection.TcpConnecti return; } - int constructor = answerWithHash.readInt32(); - TLRPC.TL_server_DH_inner_data dhInnerData = (TLRPC.TL_server_DH_inner_data)TLClassStore.Instance().TLdeserialize(answerWithHash, constructor); + int constructor = answerWithHash.readInt32(false); + TLRPC.TL_server_DH_inner_data dhInnerData = TLRPC.TL_server_DH_inner_data.TLdeserialize(answerWithHash, constructor, false); BuffersStorage.getInstance().reuseFreeBuffer(answerWithHash); if (!(dhInnerData instanceof TLRPC.TL_server_DH_inner_data)) { @@ -627,17 +627,17 @@ public class HandshakeAction extends Action implements TcpConnection.TcpConnecti @Override public void tcpConnectionReceivedData(TcpConnection connection, ByteBufferDesc data, int length) { - long keyId = data.readInt64(); + long keyId = data.readInt64(false); if (keyId == 0) { - long messageId = data.readInt64(); + long messageId = data.readInt64(false); if (processedMessageIds.contains(messageId)) { FileLog.d("tmessages", String.format("===== Duplicate message id %d received, ignoring", messageId)); return; } - int messageLength = data.readInt32(); + int messageLength = data.readInt32(false); - int constructor = data.readInt32(); - TLObject object = TLClassStore.Instance().TLdeserialize(data, constructor); + int constructor = data.readInt32(false); + TLObject object = TLClassStore.Instance().TLdeserialize(data, constructor, false); if (object != null) { processedMessageIds.add(messageId); diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/SerializedData.java b/TMessagesProj/src/main/java/org/telegram/messenger/SerializedData.java index 40847dabb..f01309425 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/SerializedData.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/SerializedData.java @@ -47,6 +47,7 @@ public class SerializedData extends AbsSerializedData { isOut = false; inbuf = new ByteArrayInputStream(data); in = new DataInputStream(inbuf); + len = 0; } public void cleanup() { @@ -131,17 +132,6 @@ public class SerializedData extends AbsSerializedData { } } - public boolean readBool() { - int consructor = readInt32(); - if (consructor == 0x997275b5) { - return true; - } else if (consructor == 0xbc799737) { - return false; - } - FileLog.e("tmessages", "Not bool value!"); - return false; - } - public void writeBool(boolean value) { if (!justCalc) { if (value) { @@ -173,52 +163,6 @@ public class SerializedData extends AbsSerializedData { } } - public int readInt32() { - return readInt32(null); - } - - public int readInt32(boolean[] error) { - try { - int i = 0; - for(int j = 0; j < 4; j++) { - i |= (in.read() << (j * 8)); - } - if (error != null) { - error[0] = false; - } - return i; - } catch(Exception x) { - if (error != null) { - error[0] = true; - } - FileLog.e("tmessages", "read int32 error"); - } - return 0; - } - - public long readInt64() { - return readInt64(null); - } - - public long readInt64(boolean[] error) { - try { - long i = 0; - for(int j = 0; j < 8; j++) { - i |= ((long)in.read() << (j * 8)); - } - if (error != null) { - error[0] = false; - } - return i; - } catch (Exception x) { - if (error != null) { - error[0] = true; - } - FileLog.e("tmessages", "read int64 error"); - } - return 0; - } - public void writeRaw(byte[] b) { try { if (!justCalc) { @@ -226,7 +170,7 @@ public class SerializedData extends AbsSerializedData { } else { len += b.length; } - } catch (Exception x) { + } catch (Exception e) { FileLog.e("tmessages", "write raw error"); } } @@ -238,7 +182,7 @@ public class SerializedData extends AbsSerializedData { } else { len += count; } - } catch (Exception x) { + } catch (Exception e) { FileLog.e("tmessages", "write raw error"); } } @@ -246,7 +190,7 @@ public class SerializedData extends AbsSerializedData { public void writeByte(int i) { try { if (!justCalc) { - out.writeByte((byte)i); + out.writeByte((byte) i); } else { len += 1; } @@ -267,68 +211,6 @@ public class SerializedData extends AbsSerializedData { } } - public void readRaw(byte[] b) { - try { - in.read(b); - } catch (Exception x) { - FileLog.e("tmessages", "read raw error"); - } - } - - public byte[] readData(int count) { - byte[] arr = new byte[count]; - readRaw(arr); - return arr; - } - - public String readString() { - try { - int sl = 1; - int l = in.read(); - if(l >= 254) { - l = in.read() | (in.read() << 8) | (in.read() << 16); - sl = 4; - } - byte[] b = new byte[l]; - in.read(b); - int i=sl; - while((l + i) % 4 != 0) { - in.read(); - i++; - } - return new String(b, "UTF-8"); - } catch (Exception x) { - FileLog.e("tmessages", "read string error"); - } - return null; - } - - public byte[] readByteArray() { - try { - int sl = 1; - int l = in.read(); - if (l >= 254) { - l = in.read() | (in.read() << 8) | (in.read() << 16); - sl = 4; - } - byte[] b = new byte[l]; - in.read(b); - int i = sl; - while((l + i) % 4 != 0) { - in.read(); - i++; - } - return b; - } catch (Exception x) { - FileLog.e("tmessages", "read byte array error"); - } - return null; - } - - public ByteBufferDesc readByteBuffer() { - throw new RuntimeException("SerializedData don't support readByteBuffer"); - } - public void writeByteArray(byte[] b) { try { if (b.length <= 253) { @@ -361,7 +243,7 @@ public class SerializedData extends AbsSerializedData { } i++; } - } catch (Exception x) { + } catch (Exception e) { FileLog.e("tmessages", "write byte array error"); } } @@ -369,7 +251,7 @@ public class SerializedData extends AbsSerializedData { public void writeString(String s) { try { writeByteArray(s.getBytes("UTF-8")); - } catch(Exception x) { + } catch(Exception e) { FileLog.e("tmessages", "write string error"); } } @@ -406,24 +288,15 @@ public class SerializedData extends AbsSerializedData { } i++; } - } catch (Exception x) { + } catch (Exception e) { FileLog.e("tmessages", "write byte array error"); } } - public double readDouble() { - try { - return Double.longBitsToDouble(readInt64()); - } catch(Exception x) { - FileLog.e("tmessages", "read double error"); - } - return 0; - } - public void writeDouble(double d) { try { writeInt64(Double.doubleToRawLongBits(d)); - } catch(Exception x) { + } catch(Exception e) { FileLog.e("tmessages", "write double error"); } } @@ -444,4 +317,172 @@ public class SerializedData extends AbsSerializedData { public byte[] toByteArray() { return outbuf.toByteArray(); } + + public void skip(int count) { + if (count == 0) { + return; + } + if (!justCalc) { + if (in != null) { + try { + in.skipBytes(count); + } catch (Exception e) { + FileLog.e("tmessages", e); + } + } + } else { + len += count; + } + } + + public int getPosition() { + return len; + } + + public boolean readBool(boolean exception) { + int consructor = readInt32(exception); + if (consructor == 0x997275b5) { + return true; + } else if (consructor == 0xbc799737) { + return false; + } + if (exception) { + throw new RuntimeException("Not bool value!"); + } else { + FileLog.e("tmessages", "Not bool value!"); + } + return false; + } + + public void readRaw(byte[] b, boolean exception) { + try { + in.read(b); + len += b.length; + } catch (Exception e) { + if (exception) { + throw new RuntimeException("read raw error", e); + } else { + FileLog.e("tmessages", "read raw error"); + } + } + } + + public byte[] readData(int count, boolean exception) { + byte[] arr = new byte[count]; + readRaw(arr, exception); + return arr; + } + + public String readString(boolean exception) { + try { + int sl = 1; + int l = in.read(); + len++; + if(l >= 254) { + l = in.read() | (in.read() << 8) | (in.read() << 16); + len += 3; + sl = 4; + } + byte[] b = new byte[l]; + in.read(b); + len++; + int i=sl; + while((l + i) % 4 != 0) { + in.read(); + len++; + i++; + } + return new String(b, "UTF-8"); + } catch (Exception e) { + if (exception) { + throw new RuntimeException("read string error", e); + } else { + FileLog.e("tmessages", "read string error"); + } + } + return null; + } + + public byte[] readByteArray(boolean exception) { + try { + int sl = 1; + int l = in.read(); + len++; + if (l >= 254) { + l = in.read() | (in.read() << 8) | (in.read() << 16); + len += 3; + sl = 4; + } + byte[] b = new byte[l]; + in.read(b); + len++; + int i = sl; + while((l + i) % 4 != 0) { + in.read(); + len++; + i++; + } + return b; + } catch (Exception e) { + if (exception) { + throw new RuntimeException("read byte array error", e); + } else { + FileLog.e("tmessages", "read byte array error"); + } + } + return null; + } + + public ByteBufferDesc readByteBuffer(boolean exception) { + throw new RuntimeException("SerializedData don't support readByteBuffer"); + } + + public double readDouble(boolean exception) { + try { + return Double.longBitsToDouble(readInt64(exception)); + } catch(Exception e) { + if (exception) { + throw new RuntimeException("read double error", e); + } else { + FileLog.e("tmessages", "read double error"); + } + } + return 0; + } + + public int readInt32(boolean exception) { + try { + int i = 0; + for(int j = 0; j < 4; j++) { + i |= (in.read() << (j * 8)); + len++; + } + return i; + } catch(Exception e) { + if (exception) { + throw new RuntimeException("read int32 error", e); + } else { + FileLog.e("tmessages", "read int32 error"); + } + } + return 0; + } + + public long readInt64(boolean exception) { + try { + long i = 0; + for(int j = 0; j < 8; j++) { + i |= ((long)in.read() << (j * 8)); + len++; + } + return i; + } catch (Exception e) { + if (exception) { + throw new RuntimeException("read int64 error", e); + } else { + FileLog.e("tmessages", "read int64 error"); + } + } + return 0; + } } diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/TLClassStore.java b/TMessagesProj/src/main/java/org/telegram/messenger/TLClassStore.java index c3e862ad6..8b433f284 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/TLClassStore.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/TLClassStore.java @@ -16,415 +16,86 @@ public class TLClassStore { public TLClassStore () { classStore = new HashMap<>(); - classStore.put(TLRPC.TL_chatPhotoEmpty.constructor, TLRPC.TL_chatPhotoEmpty.class); - classStore.put(TLRPC.TL_chatPhoto.constructor, TLRPC.TL_chatPhoto.class); classStore.put(TLRPC.TL_futuresalts.constructor, TLRPC.TL_futuresalts.class); - classStore.put(TLRPC.TL_bad_msg_notification.constructor, TLRPC.TL_bad_msg_notification.class); - classStore.put(TLRPC.TL_bad_server_salt.constructor, TLRPC.TL_bad_server_salt.class); + classStore.put(TLRPC.TL_msg_new_detailed_info.constructor, TLRPC.TL_msg_new_detailed_info.class); + classStore.put(TLRPC.TL_msg_detailed_info.constructor, TLRPC.TL_msg_detailed_info.class); classStore.put(TLRPC.TL_error.constructor, TLRPC.TL_error.class); - classStore.put(TLRPC.TL_messages_sentEncryptedMessage.constructor, TLRPC.TL_messages_sentEncryptedMessage.class); - classStore.put(TLRPC.TL_messages_sentEncryptedFile.constructor, TLRPC.TL_messages_sentEncryptedFile.class); - classStore.put(TLRPC.TL_notifyAll.constructor, TLRPC.TL_notifyAll.class); - classStore.put(TLRPC.TL_notifyChats.constructor, TLRPC.TL_notifyChats.class); - classStore.put(TLRPC.TL_notifyUsers.constructor, TLRPC.TL_notifyUsers.class); - classStore.put(TLRPC.TL_notifyPeer.constructor, TLRPC.TL_notifyPeer.class); - classStore.put(TLRPC.TL_auth_checkedPhone.constructor, TLRPC.TL_auth_checkedPhone.class); - classStore.put(TLRPC.TL_msgs_ack.constructor, TLRPC.TL_msgs_ack.class); - classStore.put(TLRPC.TL_messages_chatFull.constructor, TLRPC.TL_messages_chatFull.class); - classStore.put(TLRPC.TL_documentAttributeAnimated.constructor, TLRPC.TL_documentAttributeAnimated.class); - classStore.put(TLRPC.TL_documentAttributeAudio.constructor, TLRPC.TL_documentAttributeAudio.class); - classStore.put(TLRPC.TL_documentAttributeFilename.constructor, TLRPC.TL_documentAttributeFilename.class); - classStore.put(TLRPC.TL_documentAttributeVideo.constructor, TLRPC.TL_documentAttributeVideo.class); - classStore.put(TLRPC.TL_documentAttributeSticker.constructor, TLRPC.TL_documentAttributeSticker.class); - classStore.put(TLRPC.TL_documentAttributeImageSize.constructor, TLRPC.TL_documentAttributeImageSize.class); - classStore.put(TLRPC.TL_rpc_result.constructor, TLRPC.TL_rpc_result.class); - classStore.put(TLRPC.TL_contactStatus.constructor, TLRPC.TL_contactStatus.class); classStore.put(TLRPC.TL_auth_authorization.constructor, TLRPC.TL_auth_authorization.class); - classStore.put(TLRPC.TL_messages_messages.constructor, TLRPC.TL_messages_messages.class); - classStore.put(TLRPC.TL_messages_messagesSlice.constructor, TLRPC.TL_messages_messagesSlice.class); - classStore.put(TLRPC.TL_rpc_answer_unknown.constructor, TLRPC.TL_rpc_answer_unknown.class); - classStore.put(TLRPC.TL_rpc_answer_dropped.constructor, TLRPC.TL_rpc_answer_dropped.class); - classStore.put(TLRPC.TL_rpc_answer_dropped_running.constructor, TLRPC.TL_rpc_answer_dropped_running.class); - classStore.put(TLRPC.TL_contacts_link.constructor, TLRPC.TL_contacts_link.class); - classStore.put(TLRPC.TL_peerUser.constructor, TLRPC.TL_peerUser.class); - classStore.put(TLRPC.TL_peerChat.constructor, TLRPC.TL_peerChat.class); - classStore.put(TLRPC.TL_encryptedFile.constructor, TLRPC.TL_encryptedFile.class); - classStore.put(TLRPC.TL_encryptedFileEmpty.constructor, TLRPC.TL_encryptedFileEmpty.class); - classStore.put(TLRPC.TL_destroy_session_ok.constructor, TLRPC.TL_destroy_session_ok.class); - classStore.put(TLRPC.TL_destroy_session_none.constructor, TLRPC.TL_destroy_session_none.class); - classStore.put(TLRPC.TL_updates_differenceEmpty.constructor, TLRPC.TL_updates_differenceEmpty.class); - classStore.put(TLRPC.TL_updates_differenceSlice.constructor, TLRPC.TL_updates_differenceSlice.class); - classStore.put(TLRPC.TL_updates_difference.constructor, TLRPC.TL_updates_difference.class); - classStore.put(TLRPC.TL_geoPointEmpty.constructor, TLRPC.TL_geoPointEmpty.class); - classStore.put(TLRPC.TL_geoPoint.constructor, TLRPC.TL_geoPoint.class); - classStore.put(TLRPC.TL_privacyKeyStatusTimestamp.constructor, TLRPC.TL_privacyKeyStatusTimestamp.class); - classStore.put(TLRPC.TL_account_privacyRules.constructor, TLRPC.TL_account_privacyRules.class); - classStore.put(TLRPC.TL_help_appUpdate.constructor, TLRPC.TL_help_appUpdate.class); - classStore.put(TLRPC.TL_help_noAppUpdate.constructor, TLRPC.TL_help_noAppUpdate.class); - classStore.put(TLRPC.TL_messageEmpty.constructor, TLRPC.TL_messageEmpty.class); - classStore.put(TLRPC.TL_message.constructor, TLRPC.TL_message.class); - classStore.put(TLRPC.TL_messageService.constructor, TLRPC.TL_messageService.class); - classStore.put(TLRPC.TL_inputPhoneContact.constructor, TLRPC.TL_inputPhoneContact.class); - classStore.put(TLRPC.TL_sendMessageGeoLocationAction.constructor, TLRPC.TL_sendMessageGeoLocationAction.class); - classStore.put(TLRPC.TL_sendMessageChooseContactAction.constructor, TLRPC.TL_sendMessageChooseContactAction.class); - classStore.put(TLRPC.TL_sendMessageTypingAction.constructor, TLRPC.TL_sendMessageTypingAction.class); - classStore.put(TLRPC.TL_sendMessageUploadDocumentAction.constructor, TLRPC.TL_sendMessageUploadDocumentAction.class); - classStore.put(TLRPC.TL_sendMessageRecordVideoAction.constructor, TLRPC.TL_sendMessageRecordVideoAction.class); - classStore.put(TLRPC.TL_sendMessageUploadPhotoAction.constructor, TLRPC.TL_sendMessageUploadPhotoAction.class); - classStore.put(TLRPC.TL_sendMessageUploadVideoAction.constructor, TLRPC.TL_sendMessageUploadVideoAction.class); - classStore.put(TLRPC.TL_sendMessageUploadAudioAction.constructor, TLRPC.TL_sendMessageUploadAudioAction.class); - classStore.put(TLRPC.TL_sendMessageCancelAction.constructor, TLRPC.TL_sendMessageCancelAction.class); - classStore.put(TLRPC.TL_sendMessageRecordAudioAction.constructor, TLRPC.TL_sendMessageRecordAudioAction.class); - classStore.put(TLRPC.TL_invokeAfterMsg.constructor, TLRPC.TL_invokeAfterMsg.class); - classStore.put(TLRPC.TL_messageMediaVideo.constructor, TLRPC.TL_messageMediaVideo.class); - classStore.put(TLRPC.TL_messageMediaPhoto.constructor, TLRPC.TL_messageMediaPhoto.class); - classStore.put(TLRPC.TL_messageMediaDocument.constructor, TLRPC.TL_messageMediaDocument.class); - classStore.put(TLRPC.TL_messageMediaGeo.constructor, TLRPC.TL_messageMediaGeo.class); - classStore.put(TLRPC.TL_messageMediaEmpty.constructor, TLRPC.TL_messageMediaEmpty.class); - classStore.put(TLRPC.TL_messageMediaAudio.constructor, TLRPC.TL_messageMediaAudio.class); - classStore.put(TLRPC.TL_messageMediaContact.constructor, TLRPC.TL_messageMediaContact.class); - classStore.put(TLRPC.TL_messageMediaUnsupported.constructor, TLRPC.TL_messageMediaUnsupported.class); - classStore.put(TLRPC.TL_auth_sentAppCode.constructor, TLRPC.TL_auth_sentAppCode.class); - classStore.put(TLRPC.TL_auth_sentCode.constructor, TLRPC.TL_auth_sentCode.class); - classStore.put(TLRPC.TL_peerNotifySettingsEmpty.constructor, TLRPC.TL_peerNotifySettingsEmpty.class); - classStore.put(TLRPC.TL_peerNotifySettings.constructor, TLRPC.TL_peerNotifySettings.class); - classStore.put(TLRPC.TL_msg_resend_req.constructor, TLRPC.TL_msg_resend_req.class); - classStore.put(TLRPC.TL_http_wait.constructor, TLRPC.TL_http_wait.class); - classStore.put(TLRPC.TL_contacts_blocked.constructor, TLRPC.TL_contacts_blocked.class); - classStore.put(TLRPC.TL_contacts_blockedSlice.constructor, TLRPC.TL_contacts_blockedSlice.class); - classStore.put(TLRPC.TL_inputGeoPoint.constructor, TLRPC.TL_inputGeoPoint.class); - classStore.put(TLRPC.TL_inputGeoPointEmpty.constructor, TLRPC.TL_inputGeoPointEmpty.class); - classStore.put(TLRPC.TL_help_inviteText.constructor, TLRPC.TL_help_inviteText.class); - classStore.put(TLRPC.TL_messages_dhConfigNotModified.constructor, TLRPC.TL_messages_dhConfigNotModified.class); - classStore.put(TLRPC.TL_messages_dhConfig.constructor, TLRPC.TL_messages_dhConfig.class); - classStore.put(TLRPC.TL_audioEmpty.constructor, TLRPC.TL_audioEmpty.class); - classStore.put(TLRPC.TL_audio.constructor, TLRPC.TL_audio.class); - classStore.put(TLRPC.TL_destroy_sessions_res.constructor, TLRPC.TL_destroy_sessions_res.class); - classStore.put(TLRPC.TL_privacyValueAllowUsers.constructor, TLRPC.TL_privacyValueAllowUsers.class); - classStore.put(TLRPC.TL_privacyValueDisallowAll.constructor, TLRPC.TL_privacyValueDisallowAll.class); - classStore.put(TLRPC.TL_privacyValueAllowContacts.constructor, TLRPC.TL_privacyValueAllowContacts.class); - classStore.put(TLRPC.TL_privacyValueDisallowContacts.constructor, TLRPC.TL_privacyValueDisallowContacts.class); - classStore.put(TLRPC.TL_privacyValueAllowAll.constructor, TLRPC.TL_privacyValueAllowAll.class); - classStore.put(TLRPC.TL_privacyValueDisallowUsers.constructor, TLRPC.TL_privacyValueDisallowUsers.class); - classStore.put(TLRPC.TL_contacts_contacts.constructor, TLRPC.TL_contacts_contacts.class); - classStore.put(TLRPC.TL_contacts_contactsNotModified.constructor, TLRPC.TL_contacts_contactsNotModified.class); - classStore.put(TLRPC.TL_inputPrivacyKeyStatusTimestamp.constructor, TLRPC.TL_inputPrivacyKeyStatusTimestamp.class); - classStore.put(TLRPC.TL_photos_photos.constructor, TLRPC.TL_photos_photos.class); - classStore.put(TLRPC.TL_photos_photosSlice.constructor, TLRPC.TL_photos_photosSlice.class); - classStore.put(TLRPC.TL_chatFull.constructor, TLRPC.TL_chatFull.class); - classStore.put(TLRPC.TL_msgs_all_info.constructor, TLRPC.TL_msgs_all_info.class); - classStore.put(TLRPC.TL_inputPeerNotifySettings.constructor, TLRPC.TL_inputPeerNotifySettings.class); - classStore.put(TLRPC.TL_null.constructor, TLRPC.TL_null.class); - classStore.put(TLRPC.TL_inputUserSelf.constructor, TLRPC.TL_inputUserSelf.class); - classStore.put(TLRPC.TL_inputUserForeign.constructor, TLRPC.TL_inputUserForeign.class); - classStore.put(TLRPC.TL_inputUserEmpty.constructor, TLRPC.TL_inputUserEmpty.class); - classStore.put(TLRPC.TL_inputUserContact.constructor, TLRPC.TL_inputUserContact.class); - classStore.put(TLRPC.TL_p_q_inner_data.constructor, TLRPC.TL_p_q_inner_data.class); - classStore.put(TLRPC.TL_msgs_state_req.constructor, TLRPC.TL_msgs_state_req.class); - classStore.put(TLRPC.TL_boolTrue.constructor, TLRPC.TL_boolTrue.class); - classStore.put(TLRPC.TL_boolFalse.constructor, TLRPC.TL_boolFalse.class); - classStore.put(TLRPC.TL_auth_exportedAuthorization.constructor, TLRPC.TL_auth_exportedAuthorization.class); - classStore.put(TLRPC.TL_inputNotifyChats.constructor, TLRPC.TL_inputNotifyChats.class); - classStore.put(TLRPC.TL_inputNotifyPeer.constructor, TLRPC.TL_inputNotifyPeer.class); - classStore.put(TLRPC.TL_inputNotifyUsers.constructor, TLRPC.TL_inputNotifyUsers.class); - classStore.put(TLRPC.TL_inputNotifyGeoChatPeer.constructor, TLRPC.TL_inputNotifyGeoChatPeer.class); - classStore.put(TLRPC.TL_inputNotifyAll.constructor, TLRPC.TL_inputNotifyAll.class); - classStore.put(TLRPC.TL_inputAudioFileLocation.constructor, TLRPC.TL_inputAudioFileLocation.class); - classStore.put(TLRPC.TL_inputEncryptedFileLocation.constructor, TLRPC.TL_inputEncryptedFileLocation.class); - classStore.put(TLRPC.TL_inputVideoFileLocation.constructor, TLRPC.TL_inputVideoFileLocation.class); - classStore.put(TLRPC.TL_inputDocumentFileLocation.constructor, TLRPC.TL_inputDocumentFileLocation.class); - classStore.put(TLRPC.TL_inputFileLocation.constructor, TLRPC.TL_inputFileLocation.class); - classStore.put(TLRPC.TL_photos_photo.constructor, TLRPC.TL_photos_photo.class); - classStore.put(TLRPC.TL_userContact.constructor, TLRPC.TL_userContact.class); - classStore.put(TLRPC.TL_userRequest.constructor, TLRPC.TL_userRequest.class); - classStore.put(TLRPC.TL_userForeign.constructor, TLRPC.TL_userForeign.class); - classStore.put(TLRPC.TL_userDeleted.constructor, TLRPC.TL_userDeleted.class); - classStore.put(TLRPC.TL_userSelf.constructor, TLRPC.TL_userSelf.class); - classStore.put(TLRPC.TL_userEmpty.constructor, TLRPC.TL_userEmpty.class); - classStore.put(TLRPC.TL_geoChatMessage.constructor, TLRPC.TL_geoChatMessage.class); - classStore.put(TLRPC.TL_geoChatMessageService.constructor, TLRPC.TL_geoChatMessageService.class); - classStore.put(TLRPC.TL_geoChatMessageEmpty.constructor, TLRPC.TL_geoChatMessageEmpty.class); - classStore.put(TLRPC.TL_pong.constructor, TLRPC.TL_pong.class); - classStore.put(TLRPC.TL_messageActionChatEditPhoto.constructor, TLRPC.TL_messageActionChatEditPhoto.class); - classStore.put(TLRPC.TL_messageActionChatDeleteUser.constructor, TLRPC.TL_messageActionChatDeleteUser.class); - classStore.put(TLRPC.TL_messageActionChatDeletePhoto.constructor, TLRPC.TL_messageActionChatDeletePhoto.class); - classStore.put(TLRPC.TL_messageActionChatAddUser.constructor, TLRPC.TL_messageActionChatAddUser.class); - classStore.put(TLRPC.TL_messageActionChatCreate.constructor, TLRPC.TL_messageActionChatCreate.class); - classStore.put(TLRPC.TL_messageActionEmpty.constructor, TLRPC.TL_messageActionEmpty.class); - classStore.put(TLRPC.TL_messageActionChatEditTitle.constructor, TLRPC.TL_messageActionChatEditTitle.class); - classStore.put(TLRPC.TL_messageActionGeoChatCreate.constructor, TLRPC.TL_messageActionGeoChatCreate.class); - classStore.put(TLRPC.TL_messageActionGeoChatCheckin.constructor, TLRPC.TL_messageActionGeoChatCheckin.class); classStore.put(TLRPC.TL_dh_gen_retry.constructor, TLRPC.TL_dh_gen_retry.class); classStore.put(TLRPC.TL_dh_gen_fail.constructor, TLRPC.TL_dh_gen_fail.class); classStore.put(TLRPC.TL_dh_gen_ok.constructor, TLRPC.TL_dh_gen_ok.class); - classStore.put(TLRPC.TL_peerNotifyEventsEmpty.constructor, TLRPC.TL_peerNotifyEventsEmpty.class); - classStore.put(TLRPC.TL_peerNotifyEventsAll.constructor, TLRPC.TL_peerNotifyEventsAll.class); - classStore.put(TLRPC.TL_chatLocated.constructor, TLRPC.TL_chatLocated.class); - classStore.put(TLRPC.TL_decryptedMessageService.constructor, TLRPC.TL_decryptedMessageService.class); - classStore.put(TLRPC.TL_decryptedMessage.constructor, TLRPC.TL_decryptedMessage.class); - classStore.put(TLRPC.TL_inputPeerNotifyEventsAll.constructor, TLRPC.TL_inputPeerNotifyEventsAll.class); - classStore.put(TLRPC.TL_inputPeerNotifyEventsEmpty.constructor, TLRPC.TL_inputPeerNotifyEventsEmpty.class); - classStore.put(TLRPC.TL_client_DH_inner_data.constructor, TLRPC.TL_client_DH_inner_data.class); - classStore.put(TLRPC.TL_video.constructor, TLRPC.TL_video.class); - classStore.put(TLRPC.TL_videoEmpty.constructor, TLRPC.TL_videoEmpty.class); - classStore.put(TLRPC.TL_contactBlocked.constructor, TLRPC.TL_contactBlocked.class); - classStore.put(TLRPC.TL_inputDocumentEmpty.constructor, TLRPC.TL_inputDocumentEmpty.class); - classStore.put(TLRPC.TL_inputDocument.constructor, TLRPC.TL_inputDocument.class); - classStore.put(TLRPC.TL_inputAppEvent.constructor, TLRPC.TL_inputAppEvent.class); - classStore.put(TLRPC.TL_messages_affectedHistory.constructor, TLRPC.TL_messages_affectedHistory.class); - classStore.put(TLRPC.TL_documentEmpty.constructor, TLRPC.TL_documentEmpty.class); - classStore.put(TLRPC.TL_document.constructor, TLRPC.TL_document.class); - classStore.put(TLRPC.TL_inputPrivacyValueDisallowUsers.constructor, TLRPC.TL_inputPrivacyValueDisallowUsers.class); - classStore.put(TLRPC.TL_inputPrivacyValueDisallowAll.constructor, TLRPC.TL_inputPrivacyValueDisallowAll.class); - classStore.put(TLRPC.TL_inputPrivacyValueDisallowContacts.constructor, TLRPC.TL_inputPrivacyValueDisallowContacts.class); - classStore.put(TLRPC.TL_inputPrivacyValueAllowAll.constructor, TLRPC.TL_inputPrivacyValueAllowAll.class); - classStore.put(TLRPC.TL_inputPrivacyValueAllowContacts.constructor, TLRPC.TL_inputPrivacyValueAllowContacts.class); - classStore.put(TLRPC.TL_inputPrivacyValueAllowUsers.constructor, TLRPC.TL_inputPrivacyValueAllowUsers.class); - classStore.put(TLRPC.TL_inputMediaContact.constructor, TLRPC.TL_inputMediaContact.class); - classStore.put(TLRPC.TL_inputMediaUploadedThumbDocument.constructor, TLRPC.TL_inputMediaUploadedThumbDocument.class); - classStore.put(TLRPC.TL_inputMediaAudio.constructor, TLRPC.TL_inputMediaAudio.class); - classStore.put(TLRPC.TL_inputMediaDocument.constructor, TLRPC.TL_inputMediaDocument.class); - classStore.put(TLRPC.TL_inputMediaVideo.constructor, TLRPC.TL_inputMediaVideo.class); - classStore.put(TLRPC.TL_inputMediaGeoPoint.constructor, TLRPC.TL_inputMediaGeoPoint.class); - classStore.put(TLRPC.TL_inputMediaEmpty.constructor, TLRPC.TL_inputMediaEmpty.class); - classStore.put(TLRPC.TL_inputMediaUploadedThumbVideo.constructor, TLRPC.TL_inputMediaUploadedThumbVideo.class); - classStore.put(TLRPC.TL_inputMediaUploadedPhoto.constructor, TLRPC.TL_inputMediaUploadedPhoto.class); - classStore.put(TLRPC.TL_inputMediaUploadedAudio.constructor, TLRPC.TL_inputMediaUploadedAudio.class); - classStore.put(TLRPC.TL_inputMediaUploadedVideo.constructor, TLRPC.TL_inputMediaUploadedVideo.class); - classStore.put(TLRPC.TL_inputMediaUploadedDocument.constructor, TLRPC.TL_inputMediaUploadedDocument.class); - classStore.put(TLRPC.TL_inputMediaPhoto.constructor, TLRPC.TL_inputMediaPhoto.class); - classStore.put(TLRPC.TL_geochats_messagesSlice.constructor, TLRPC.TL_geochats_messagesSlice.class); - classStore.put(TLRPC.TL_geochats_messages.constructor, TLRPC.TL_geochats_messages.class); - classStore.put(TLRPC.TL_messages_sentMessage.constructor, TLRPC.TL_messages_sentMessage.class); - classStore.put(TLRPC.TL_messages_sentMessageLink.constructor, TLRPC.TL_messages_sentMessageLink.class); - classStore.put(TLRPC.TL_encryptedMessageService.constructor, TLRPC.TL_encryptedMessageService.class); - classStore.put(TLRPC.TL_encryptedMessage.constructor, TLRPC.TL_encryptedMessage.class); - classStore.put(TLRPC.TL_contactSuggested.constructor, TLRPC.TL_contactSuggested.class); - classStore.put(TLRPC.TL_server_DH_params_fail.constructor, TLRPC.TL_server_DH_params_fail.class); - classStore.put(TLRPC.TL_server_DH_params_ok.constructor, TLRPC.TL_server_DH_params_ok.class); - classStore.put(TLRPC.TL_userStatusOffline.constructor, TLRPC.TL_userStatusOffline.class); - classStore.put(TLRPC.TL_userStatusLastWeek.constructor, TLRPC.TL_userStatusLastWeek.class); - classStore.put(TLRPC.TL_userStatusEmpty.constructor, TLRPC.TL_userStatusEmpty.class); - classStore.put(TLRPC.TL_userStatusLastMonth.constructor, TLRPC.TL_userStatusLastMonth.class); - classStore.put(TLRPC.TL_userStatusOnline.constructor, TLRPC.TL_userStatusOnline.class); - classStore.put(TLRPC.TL_userStatusRecently.constructor, TLRPC.TL_userStatusRecently.class); - classStore.put(TLRPC.TL_msg_copy.constructor, TLRPC.TL_msg_copy.class); - classStore.put(TLRPC.TL_contacts_importedContacts.constructor, TLRPC.TL_contacts_importedContacts.class); - classStore.put(TLRPC.TL_disabledFeature.constructor, TLRPC.TL_disabledFeature.class); + classStore.put(TLRPC.TL_server_DH_inner_data.constructor, TLRPC.TL_server_DH_inner_data.class); + classStore.put(TLRPC.TL_msgs_ack.constructor, TLRPC.TL_msgs_ack.class); classStore.put(TLRPC.TL_futureSalt.constructor, TLRPC.TL_futureSalt.class); - classStore.put(TLRPC.TL_updateEncryptedMessagesRead.constructor, TLRPC.TL_updateEncryptedMessagesRead.class); - classStore.put(TLRPC.TL_updateContactLink.constructor, TLRPC.TL_updateContactLink.class); - classStore.put(TLRPC.TL_updateReadMessages.constructor, TLRPC.TL_updateReadMessages.class); - classStore.put(TLRPC.TL_updateChatParticipantDelete.constructor, TLRPC.TL_updateChatParticipantDelete.class); - classStore.put(TLRPC.TL_updateServiceNotification.constructor, TLRPC.TL_updateServiceNotification.class); - classStore.put(TLRPC.TL_updateNotifySettings.constructor, TLRPC.TL_updateNotifySettings.class); - classStore.put(TLRPC.TL_updateUserTyping.constructor, TLRPC.TL_updateUserTyping.class); - classStore.put(TLRPC.TL_updateChatUserTyping.constructor, TLRPC.TL_updateChatUserTyping.class); - classStore.put(TLRPC.TL_updateUserName.constructor, TLRPC.TL_updateUserName.class); - classStore.put(TLRPC.TL_updateNewEncryptedMessage.constructor, TLRPC.TL_updateNewEncryptedMessage.class); - classStore.put(TLRPC.TL_updateNewMessage.constructor, TLRPC.TL_updateNewMessage.class); - classStore.put(TLRPC.TL_updateMessageID.constructor, TLRPC.TL_updateMessageID.class); - classStore.put(TLRPC.TL_updateDeleteMessages.constructor, TLRPC.TL_updateDeleteMessages.class); - classStore.put(TLRPC.TL_updateEncryptedChatTyping.constructor, TLRPC.TL_updateEncryptedChatTyping.class); - classStore.put(TLRPC.TL_updateDcOptions.constructor, TLRPC.TL_updateDcOptions.class); - classStore.put(TLRPC.TL_updateChatParticipants.constructor, TLRPC.TL_updateChatParticipants.class); - classStore.put(TLRPC.TL_updatePrivacy.constructor, TLRPC.TL_updatePrivacy.class); - classStore.put(TLRPC.TL_updateEncryption.constructor, TLRPC.TL_updateEncryption.class); - classStore.put(TLRPC.TL_updateUserBlocked.constructor, TLRPC.TL_updateUserBlocked.class); - classStore.put(TLRPC.TL_updateActivation.constructor, TLRPC.TL_updateActivation.class); - classStore.put(TLRPC.TL_updateNewAuthorization.constructor, TLRPC.TL_updateNewAuthorization.class); - classStore.put(TLRPC.TL_updateNewGeoChatMessage.constructor, TLRPC.TL_updateNewGeoChatMessage.class); - classStore.put(TLRPC.TL_updateUserPhoto.constructor, TLRPC.TL_updateUserPhoto.class); - classStore.put(TLRPC.TL_updateContactRegistered.constructor, TLRPC.TL_updateContactRegistered.class); - classStore.put(TLRPC.TL_updateChatParticipantAdd.constructor, TLRPC.TL_updateChatParticipantAdd.class); - classStore.put(TLRPC.TL_updateUserStatus.constructor, TLRPC.TL_updateUserStatus.class); - classStore.put(TLRPC.TL_contacts_suggested.constructor, TLRPC.TL_contacts_suggested.class); + classStore.put(TLRPC.TL_msg_resend_req.constructor, TLRPC.TL_msg_resend_req.class); classStore.put(TLRPC.TL_rpc_error.constructor, TLRPC.TL_rpc_error.class); classStore.put(TLRPC.TL_rpc_req_error.constructor, TLRPC.TL_rpc_req_error.class); - classStore.put(TLRPC.TL_inputEncryptedFile.constructor, TLRPC.TL_inputEncryptedFile.class); - classStore.put(TLRPC.TL_inputEncryptedFileBigUploaded.constructor, TLRPC.TL_inputEncryptedFileBigUploaded.class); - classStore.put(TLRPC.TL_inputEncryptedFileEmpty.constructor, TLRPC.TL_inputEncryptedFileEmpty.class); - classStore.put(TLRPC.TL_inputEncryptedFileUploaded.constructor, TLRPC.TL_inputEncryptedFileUploaded.class); - classStore.put(TLRPC.TL_decryptedMessageActionFlushHistory.constructor, TLRPC.TL_decryptedMessageActionFlushHistory.class); - classStore.put(TLRPC.TL_decryptedMessageActionResend.constructor, TLRPC.TL_decryptedMessageActionResend.class); - classStore.put(TLRPC.TL_decryptedMessageActionNotifyLayer.constructor, TLRPC.TL_decryptedMessageActionNotifyLayer.class); - classStore.put(TLRPC.TL_decryptedMessageActionSetMessageTTL.constructor, TLRPC.TL_decryptedMessageActionSetMessageTTL.class); - classStore.put(TLRPC.TL_decryptedMessageActionDeleteMessages.constructor, TLRPC.TL_decryptedMessageActionDeleteMessages.class); - classStore.put(TLRPC.TL_decryptedMessageActionTyping.constructor, TLRPC.TL_decryptedMessageActionTyping.class); - classStore.put(TLRPC.TL_decryptedMessageActionReadMessages.constructor, TLRPC.TL_decryptedMessageActionReadMessages.class); - classStore.put(TLRPC.TL_decryptedMessageActionScreenshotMessages.constructor, TLRPC.TL_decryptedMessageActionScreenshotMessages.class); - classStore.put(TLRPC.TL_server_DH_inner_data.constructor, TLRPC.TL_server_DH_inner_data.class); + classStore.put(TLRPC.TL_decryptedMessageService.constructor, TLRPC.TL_decryptedMessageService.class); + classStore.put(TLRPC.TL_decryptedMessage.constructor, TLRPC.TL_decryptedMessage.class); + classStore.put(TLRPC.TL_bad_msg_notification.constructor, TLRPC.TL_bad_msg_notification.class); + classStore.put(TLRPC.TL_bad_server_salt.constructor, TLRPC.TL_bad_server_salt.class); classStore.put(TLRPC.TL_new_session_created.constructor, TLRPC.TL_new_session_created.class); - classStore.put(TLRPC.TL_account_password.constructor, TLRPC.TL_account_password.class); - classStore.put(TLRPC.TL_account_noPassword.constructor, TLRPC.TL_account_noPassword.class); - classStore.put(TLRPC.TL_userProfilePhotoEmpty.constructor, TLRPC.TL_userProfilePhotoEmpty.class); - classStore.put(TLRPC.TL_userProfilePhoto.constructor, TLRPC.TL_userProfilePhoto.class); - classStore.put(TLRPC.TL_photo.constructor, TLRPC.TL_photo.class); - classStore.put(TLRPC.TL_photoEmpty.constructor, TLRPC.TL_photoEmpty.class); - classStore.put(TLRPC.TL_encryptedChatWaiting.constructor, TLRPC.TL_encryptedChatWaiting.class); - classStore.put(TLRPC.TL_encryptedChatEmpty.constructor, TLRPC.TL_encryptedChatEmpty.class); - classStore.put(TLRPC.TL_encryptedChatDiscarded.constructor, TLRPC.TL_encryptedChatDiscarded.class); - classStore.put(TLRPC.TL_encryptedChat.constructor, TLRPC.TL_encryptedChat.class); - classStore.put(TLRPC.TL_encryptedChatRequested.constructor, TLRPC.TL_encryptedChatRequested.class); - classStore.put(TLRPC.TL_geochats_statedMessage.constructor, TLRPC.TL_geochats_statedMessage.class); - classStore.put(TLRPC.TL_contact.constructor, TLRPC.TL_contact.class); - classStore.put(TLRPC.TL_config.constructor, TLRPC.TL_config.class); - classStore.put(TLRPC.TL_inputAudio.constructor, TLRPC.TL_inputAudio.class); - classStore.put(TLRPC.TL_inputAudioEmpty.constructor, TLRPC.TL_inputAudioEmpty.class); - classStore.put(TLRPC.TL_help_support.constructor, TLRPC.TL_help_support.class); - classStore.put(TLRPC.TL_messages_chats.constructor, TLRPC.TL_messages_chats.class); - classStore.put(TLRPC.TL_contacts_found.constructor, TLRPC.TL_contacts_found.class); - classStore.put(TLRPC.TL_chatParticipants.constructor, TLRPC.TL_chatParticipants.class); - classStore.put(TLRPC.TL_chatParticipantsForbidden.constructor, TLRPC.TL_chatParticipantsForbidden.class); - classStore.put(TLRPC.TL_decryptedMessageMediaDocument.constructor, TLRPC.TL_decryptedMessageMediaDocument.class); - classStore.put(TLRPC.TL_decryptedMessageMediaGeoPoint.constructor, TLRPC.TL_decryptedMessageMediaGeoPoint.class); - classStore.put(TLRPC.TL_decryptedMessageMediaAudio.constructor, TLRPC.TL_decryptedMessageMediaAudio.class); - classStore.put(TLRPC.TL_decryptedMessageMediaVideo.constructor, TLRPC.TL_decryptedMessageMediaVideo.class); - classStore.put(TLRPC.TL_decryptedMessageMediaContact.constructor, TLRPC.TL_decryptedMessageMediaContact.class); - classStore.put(TLRPC.TL_decryptedMessageMediaEmpty.constructor, TLRPC.TL_decryptedMessageMediaEmpty.class); - classStore.put(TLRPC.TL_decryptedMessageMediaPhoto.constructor, TLRPC.TL_decryptedMessageMediaPhoto.class); - classStore.put(TLRPC.TL_chatParticipant.constructor, TLRPC.TL_chatParticipant.class); - classStore.put(TLRPC.TL_chatForbidden.constructor, TLRPC.TL_chatForbidden.class); - classStore.put(TLRPC.TL_geoChat.constructor, TLRPC.TL_geoChat.class); - classStore.put(TLRPC.TL_chatEmpty.constructor, TLRPC.TL_chatEmpty.class); - classStore.put(TLRPC.TL_chat.constructor, TLRPC.TL_chat.class); - classStore.put(TLRPC.TL_storage_fileUnknown.constructor, TLRPC.TL_storage_fileUnknown.class); - classStore.put(TLRPC.TL_storage_fileMp4.constructor, TLRPC.TL_storage_fileMp4.class); - classStore.put(TLRPC.TL_storage_fileWebp.constructor, TLRPC.TL_storage_fileWebp.class); - classStore.put(TLRPC.TL_storage_filePng.constructor, TLRPC.TL_storage_filePng.class); - classStore.put(TLRPC.TL_storage_fileGif.constructor, TLRPC.TL_storage_fileGif.class); - classStore.put(TLRPC.TL_storage_filePdf.constructor, TLRPC.TL_storage_filePdf.class); - classStore.put(TLRPC.TL_storage_fileMp3.constructor, TLRPC.TL_storage_fileMp3.class); - classStore.put(TLRPC.TL_storage_fileJpeg.constructor, TLRPC.TL_storage_fileJpeg.class); - classStore.put(TLRPC.TL_storage_fileMov.constructor, TLRPC.TL_storage_fileMov.class); - classStore.put(TLRPC.TL_storage_filePartial.constructor, TLRPC.TL_storage_filePartial.class); - classStore.put(TLRPC.TL_inputMessagesFilterVideo.constructor, TLRPC.TL_inputMessagesFilterVideo.class); - classStore.put(TLRPC.TL_inputMessagesFilterEmpty.constructor, TLRPC.TL_inputMessagesFilterEmpty.class); - classStore.put(TLRPC.TL_inputMessagesFilterPhotos.constructor, TLRPC.TL_inputMessagesFilterPhotos.class); - classStore.put(TLRPC.TL_inputMessagesFilterPhotoVideo.constructor, TLRPC.TL_inputMessagesFilterPhotoVideo.class); - classStore.put(TLRPC.TL_inputMessagesFilterDocument.constructor, TLRPC.TL_inputMessagesFilterDocument.class); - classStore.put(TLRPC.TL_inputMessagesFilterAudio.constructor, TLRPC.TL_inputMessagesFilterAudio.class); - classStore.put(TLRPC.TL_msgs_state_info.constructor, TLRPC.TL_msgs_state_info.class); - classStore.put(TLRPC.TL_upload_file.constructor, TLRPC.TL_upload_file.class); - classStore.put(TLRPC.TL_dialog.constructor, TLRPC.TL_dialog.class); - classStore.put(TLRPC.TL_fileLocation.constructor, TLRPC.TL_fileLocation.class); - classStore.put(TLRPC.TL_fileLocationUnavailable.constructor, TLRPC.TL_fileLocationUnavailable.class); - classStore.put(TLRPC.TL_messages_messageEmpty.constructor, TLRPC.TL_messages_messageEmpty.class); - classStore.put(TLRPC.TL_messages_message.constructor, TLRPC.TL_messages_message.class); - classStore.put(TLRPC.TL_geochats_located.constructor, TLRPC.TL_geochats_located.class); - classStore.put(TLRPC.TL_inputGeoChat.constructor, TLRPC.TL_inputGeoChat.class); - classStore.put(TLRPC.TL_protoMessage.constructor, TLRPC.TL_protoMessage.class); - classStore.put(TLRPC.TL_photoSize.constructor, TLRPC.TL_photoSize.class); - classStore.put(TLRPC.TL_photoSizeEmpty.constructor, TLRPC.TL_photoSizeEmpty.class); - classStore.put(TLRPC.TL_photoCachedSize.constructor, TLRPC.TL_photoCachedSize.class); - classStore.put(TLRPC.TL_contactFound.constructor, TLRPC.TL_contactFound.class); - classStore.put(TLRPC.TL_inputFileBig.constructor, TLRPC.TL_inputFileBig.class); - classStore.put(TLRPC.TL_inputFile.constructor, TLRPC.TL_inputFile.class); - classStore.put(TLRPC.TL_userFull.constructor, TLRPC.TL_userFull.class); - classStore.put(TLRPC.TL_updates_state.constructor, TLRPC.TL_updates_state.class); classStore.put(TLRPC.TL_resPQ.constructor, TLRPC.TL_resPQ.class); + classStore.put(TLRPC.TL_config.constructor, TLRPC.TL_config.class); + classStore.put(TLRPC.TL_msg_copy.constructor, TLRPC.TL_msg_copy.class); + classStore.put(TLRPC.TL_pong.constructor, TLRPC.TL_pong.class); + classStore.put(TLRPC.TL_rpc_answer_unknown.constructor, TLRPC.TL_rpc_answer_unknown.class); + classStore.put(TLRPC.TL_rpc_answer_dropped.constructor, TLRPC.TL_rpc_answer_dropped.class); + classStore.put(TLRPC.TL_rpc_answer_dropped_running.constructor, TLRPC.TL_rpc_answer_dropped_running.class); + classStore.put(TLRPC.TL_rpc_result.constructor, TLRPC.TL_rpc_result.class); + classStore.put(TLRPC.TL_auth_exportedAuthorization.constructor, TLRPC.TL_auth_exportedAuthorization.class); + classStore.put(TLRPC.TL_destroy_session_ok.constructor, TLRPC.TL_destroy_session_ok.class); + classStore.put(TLRPC.TL_destroy_session_none.constructor, TLRPC.TL_destroy_session_none.class); + classStore.put(TLRPC.TL_msgs_state_req.constructor, TLRPC.TL_msgs_state_req.class); + classStore.put(TLRPC.TL_server_DH_params_fail.constructor, TLRPC.TL_server_DH_params_fail.class); + classStore.put(TLRPC.TL_server_DH_params_ok.constructor, TLRPC.TL_server_DH_params_ok.class); + classStore.put(TLRPC.TL_protoMessage.constructor, TLRPC.TL_protoMessage.class); + classStore.put(TLRPC.TL_msgs_all_info.constructor, TLRPC.TL_msgs_all_info.class); + classStore.put(TLRPC.TL_p_q_inner_data.constructor, TLRPC.TL_p_q_inner_data.class); classStore.put(TLRPC.TL_updateShortChatMessage.constructor, TLRPC.TL_updateShortChatMessage.class); classStore.put(TLRPC.TL_updates.constructor, TLRPC.TL_updates.class); classStore.put(TLRPC.TL_updateShortMessage.constructor, TLRPC.TL_updateShortMessage.class); classStore.put(TLRPC.TL_updateShort.constructor, TLRPC.TL_updateShort.class); classStore.put(TLRPC.TL_updatesCombined.constructor, TLRPC.TL_updatesCombined.class); classStore.put(TLRPC.TL_updatesTooLong.constructor, TLRPC.TL_updatesTooLong.class); - classStore.put(TLRPC.TL_wallPaper.constructor, TLRPC.TL_wallPaper.class); - classStore.put(TLRPC.TL_wallPaperSolid.constructor, TLRPC.TL_wallPaperSolid.class); - classStore.put(TLRPC.TL_msg_new_detailed_info.constructor, TLRPC.TL_msg_new_detailed_info.class); - classStore.put(TLRPC.TL_msg_detailed_info.constructor, TLRPC.TL_msg_detailed_info.class); - classStore.put(TLRPC.TL_inputEncryptedChat.constructor, TLRPC.TL_inputEncryptedChat.class); - classStore.put(TLRPC.TL_inputChatPhoto.constructor, TLRPC.TL_inputChatPhoto.class); - classStore.put(TLRPC.TL_inputChatPhotoEmpty.constructor, TLRPC.TL_inputChatPhotoEmpty.class); - classStore.put(TLRPC.TL_inputChatUploadedPhoto.constructor, TLRPC.TL_inputChatUploadedPhoto.class); - classStore.put(TLRPC.TL_inputVideoEmpty.constructor, TLRPC.TL_inputVideoEmpty.class); - classStore.put(TLRPC.TL_inputVideo.constructor, TLRPC.TL_inputVideo.class); - classStore.put(TLRPC.TL_nearestDc.constructor, TLRPC.TL_nearestDc.class); - classStore.put(TLRPC.TL_inputPhotoEmpty.constructor, TLRPC.TL_inputPhotoEmpty.class); - classStore.put(TLRPC.TL_inputPhoto.constructor, TLRPC.TL_inputPhoto.class); - classStore.put(TLRPC.TL_importedContact.constructor, TLRPC.TL_importedContact.class); - classStore.put(TLRPC.TL_accountDaysTTL.constructor, TLRPC.TL_accountDaysTTL.class); - classStore.put(TLRPC.TL_stickerPack.constructor, TLRPC.TL_stickerPack.class); - classStore.put(TLRPC.TL_messages_allStickers.constructor, TLRPC.TL_messages_allStickers.class); - classStore.put(TLRPC.TL_messages_allStickersNotModified.constructor, TLRPC.TL_messages_allStickersNotModified.class); - classStore.put(TLRPC.TL_inputPeerContact.constructor, TLRPC.TL_inputPeerContact.class); - classStore.put(TLRPC.TL_inputPeerChat.constructor, TLRPC.TL_inputPeerChat.class); - classStore.put(TLRPC.TL_inputPeerEmpty.constructor, TLRPC.TL_inputPeerEmpty.class); - classStore.put(TLRPC.TL_inputPeerSelf.constructor, TLRPC.TL_inputPeerSelf.class); - classStore.put(TLRPC.TL_inputPeerForeign.constructor, TLRPC.TL_inputPeerForeign.class); - classStore.put(TLRPC.TL_dcOption.constructor, TLRPC.TL_dcOption.class); + classStore.put(TLRPC.TL_msgs_state_info.constructor, TLRPC.TL_msgs_state_info.class); classStore.put(TLRPC.TL_decryptedMessageLayer.constructor, TLRPC.TL_decryptedMessageLayer.class); - classStore.put(TLRPC.TL_inputPhotoCropAuto.constructor, TLRPC.TL_inputPhotoCropAuto.class); - classStore.put(TLRPC.TL_inputPhotoCrop.constructor, TLRPC.TL_inputPhotoCrop.class); - classStore.put(TLRPC.TL_messages_dialogs.constructor, TLRPC.TL_messages_dialogs.class); - classStore.put(TLRPC.TL_messages_dialogsSlice.constructor, TLRPC.TL_messages_dialogsSlice.class); - classStore.put(TLRPC.TL_account_sentChangePhoneCode.constructor, TLRPC.TL_account_sentChangePhoneCode.class); - classStore.put(TLRPC.TL_updateUserPhone.constructor, TLRPC.TL_updateUserPhone.class); - classStore.put(TLRPC.TL_decryptedMessageActionRequestKey.constructor, TLRPC.TL_decryptedMessageActionRequestKey.class); - classStore.put(TLRPC.TL_decryptedMessageActionAcceptKey.constructor, TLRPC.TL_decryptedMessageActionAcceptKey.class); - classStore.put(TLRPC.TL_decryptedMessageActionCommitKey.constructor, TLRPC.TL_decryptedMessageActionCommitKey.class); - classStore.put(TLRPC.TL_decryptedMessageActionAbortKey.constructor, TLRPC.TL_decryptedMessageActionAbortKey.class); - classStore.put(TLRPC.TL_decryptedMessageActionNoop.constructor, TLRPC.TL_decryptedMessageActionNoop.class); - classStore.put(TLRPC.TL_decryptedMessageMediaExternalDocument.constructor, TLRPC.TL_decryptedMessageMediaExternalDocument.class); - classStore.put(TLRPC.TL_updateReadHistoryInbox.constructor, TLRPC.TL_updateReadHistoryInbox.class); - classStore.put(TLRPC.TL_updateReadHistoryOutbox.constructor, TLRPC.TL_updateReadHistoryOutbox.class); - classStore.put(TLRPC.TL_contactLinkUnknown.constructor, TLRPC.TL_contactLinkUnknown.class); - classStore.put(TLRPC.TL_contactLinkNone.constructor, TLRPC.TL_contactLinkNone.class); - classStore.put(TLRPC.TL_contactLinkHasPhone.constructor, TLRPC.TL_contactLinkHasPhone.class); - classStore.put(TLRPC.TL_contactLinkContact.constructor, TLRPC.TL_contactLinkContact.class); - classStore.put(TLRPC.TL_messages_affectedMessages.constructor, TLRPC.TL_messages_affectedMessages.class); - classStore.put(TLRPC.TL_updateWebPage.constructor, TLRPC.TL_updateWebPage.class); - classStore.put(TLRPC.TL_webPagePending.constructor, TLRPC.TL_webPagePending.class); - classStore.put(TLRPC.TL_webPageEmpty.constructor, TLRPC.TL_webPageEmpty.class); - classStore.put(TLRPC.TL_webPage.constructor, TLRPC.TL_webPage.class); - classStore.put(TLRPC.TL_messageMediaWebPage.constructor, TLRPC.TL_messageMediaWebPage.class); - classStore.put(TLRPC.TL_authorization.constructor, TLRPC.TL_authorization.class); - classStore.put(TLRPC.TL_account_authorizations.constructor, TLRPC.TL_account_authorizations.class); - classStore.put(TLRPC.TL_account_passwordSettings.constructor, TLRPC.TL_account_passwordSettings.class); - classStore.put(TLRPC.TL_account_passwordInputSettings.constructor, TLRPC.TL_account_passwordInputSettings.class); - classStore.put(TLRPC.TL_auth_passwordRecovery.constructor, TLRPC.TL_auth_passwordRecovery.class); - classStore.put(TLRPC.TL_messages_getWebPagePreview.constructor, TLRPC.TL_messages_getWebPagePreview.class); - - classStore.put(TLRPC.TL_messageMediaUnsupported_old.constructor, TLRPC.TL_messageMediaUnsupported_old.class); - classStore.put(TLRPC.TL_userSelf_old2.constructor, TLRPC.TL_userSelf_old2.class); - classStore.put(TLRPC.TL_msg_container.constructor, TLRPC.TL_msg_container.class); - classStore.put(TLRPC.TL_fileEncryptedLocation.constructor, TLRPC.TL_fileEncryptedLocation.class); - classStore.put(TLRPC.TL_messageActionTTLChange.constructor, TLRPC.TL_messageActionTTLChange.class); - classStore.put(TLRPC.TL_videoEncrypted.constructor, TLRPC.TL_videoEncrypted.class); - classStore.put(TLRPC.TL_documentEncrypted.constructor, TLRPC.TL_documentEncrypted.class); - classStore.put(TLRPC.TL_audioEncrypted.constructor, TLRPC.TL_audioEncrypted.class); + classStore.put(TLRPC.TL_http_wait.constructor, TLRPC.TL_http_wait.class); classStore.put(TLRPC.TL_gzip_packed.constructor, TLRPC.TL_gzip_packed.class); - classStore.put(TLRPC.Vector.constructor, TLRPC.Vector.class); - classStore.put(TLRPC.TL_userProfilePhotoOld.constructor, TLRPC.TL_userProfilePhotoOld.class); - classStore.put(TLRPC.TL_messageActionUserUpdatedPhoto.constructor, TLRPC.TL_messageActionUserUpdatedPhoto.class); - classStore.put(TLRPC.TL_messageActionUserJoined.constructor, TLRPC.TL_messageActionUserJoined.class); - classStore.put(TLRPC.TL_messageActionLoginUnknownLocation.constructor, TLRPC.TL_messageActionLoginUnknownLocation.class); - classStore.put(TLRPC.TL_encryptedChat_old.constructor, TLRPC.TL_encryptedChat_old.class); - classStore.put(TLRPC.TL_encryptedChatRequested_old.constructor, TLRPC.TL_encryptedChatRequested_old.class); - classStore.put(TLRPC.TL_decryptedMessageMediaVideo_old.constructor, TLRPC.TL_decryptedMessageMediaVideo_old.class); - classStore.put(TLRPC.TL_decryptedMessageMediaAudio_old.constructor, TLRPC.TL_decryptedMessageMediaAudio_old.class); - classStore.put(TLRPC.TL_audio_old.constructor, TLRPC.TL_audio_old.class); - classStore.put(TLRPC.TL_video_old.constructor, TLRPC.TL_video_old.class); - classStore.put(TLRPC.TL_messageActionCreatedBroadcastList.constructor, TLRPC.TL_messageActionCreatedBroadcastList.class); - classStore.put(TLRPC.TL_messageForwarded_old.constructor, TLRPC.TL_messageForwarded_old.class); - classStore.put(TLRPC.TL_message_old.constructor, TLRPC.TL_message_old.class); - classStore.put(TLRPC.TL_messageService_old.constructor, TLRPC.TL_messageService_old.class); classStore.put(TLRPC.TL_decryptedMessageService_old.constructor, TLRPC.TL_decryptedMessageService_old.class); classStore.put(TLRPC.TL_decryptedMessage_old.constructor, TLRPC.TL_decryptedMessage_old.class); classStore.put(TLRPC.TL_message_secret.constructor, TLRPC.TL_message_secret.class); - classStore.put(TLRPC.TL_userSelf_old.constructor, TLRPC.TL_userSelf_old.class); - classStore.put(TLRPC.TL_userContact_old.constructor, TLRPC.TL_userContact_old.class); - classStore.put(TLRPC.TL_userRequest_old.constructor, TLRPC.TL_userRequest_old.class); - classStore.put(TLRPC.TL_userForeign_old.constructor, TLRPC.TL_userForeign_old.class); - classStore.put(TLRPC.TL_userDeleted_old.constructor, TLRPC.TL_userDeleted_old.class); classStore.put(TLRPC.TL_messageEncryptedAction.constructor, TLRPC.TL_messageEncryptedAction.class); classStore.put(TLRPC.TL_decryptedMessageHolder.constructor, TLRPC.TL_decryptedMessageHolder.class); + classStore.put(TLRPC.TL_client_DH_inner_data.constructor, TLRPC.TL_client_DH_inner_data.class); + classStore.put(TLRPC.TL_null.constructor, TLRPC.TL_null.class); + classStore.put(TLRPC.TL_destroy_sessions_res.constructor, TLRPC.TL_destroy_sessions_res.class); + classStore.put(TLRPC.TL_msg_container.constructor, TLRPC.TL_msg_container.class); + + + classStore.put(TLRPC.TL_video.constructor, TLRPC.TL_video.class); + classStore.put(TLRPC.TL_videoEmpty.constructor, TLRPC.TL_videoEmpty.class); + classStore.put(TLRPC.TL_video_old2.constructor, TLRPC.TL_video_old2.class); + classStore.put(TLRPC.TL_video_old.constructor, TLRPC.TL_video_old.class); + classStore.put(TLRPC.TL_videoEncrypted.constructor, TLRPC.TL_videoEncrypted.class); + + classStore.put(TLRPC.TL_audio.constructor, TLRPC.TL_audio.class); + classStore.put(TLRPC.TL_audioEncrypted.constructor, TLRPC.TL_audioEncrypted.class); + classStore.put(TLRPC.TL_audioEmpty.constructor, TLRPC.TL_audioEmpty.class); + classStore.put(TLRPC.TL_audio_old.constructor, TLRPC.TL_audio_old.class); + + classStore.put(TLRPC.TL_document.constructor, TLRPC.TL_document.class); + classStore.put(TLRPC.TL_documentEmpty.constructor, TLRPC.TL_documentEmpty.class); classStore.put(TLRPC.TL_documentEncrypted_old.constructor, TLRPC.TL_documentEncrypted_old.class); + classStore.put(TLRPC.TL_documentEncrypted.constructor, TLRPC.TL_documentEncrypted.class); classStore.put(TLRPC.TL_document_old.constructor, TLRPC.TL_document_old.class); - classStore.put(TLRPC.TL_config_old.constructor, TLRPC.TL_config_old.class); - classStore.put(TLRPC.TL_messageForwarded_old2.constructor, TLRPC.TL_messageForwarded_old2.class); - classStore.put(TLRPC.TL_message_old2.constructor, TLRPC.TL_message_old2.class); - classStore.put(TLRPC.TL_documentAttributeSticker_old.constructor, TLRPC.TL_documentAttributeSticker_old.class); + + classStore.put(TLRPC.TL_photo.constructor, TLRPC.TL_photo.class); + classStore.put(TLRPC.TL_photoEmpty.constructor, TLRPC.TL_photoEmpty.class); + classStore.put(TLRPC.TL_photoSize.constructor, TLRPC.TL_photoSize.class); + classStore.put(TLRPC.TL_photoSizeEmpty.constructor, TLRPC.TL_photoSizeEmpty.class); + classStore.put(TLRPC.TL_photoCachedSize.constructor, TLRPC.TL_photoCachedSize.class); + classStore.put(TLRPC.TL_photo_old.constructor, TLRPC.TL_photo_old.class); } static TLClassStore store = null; @@ -436,46 +107,27 @@ public class TLClassStore { return store; } - public TLObject TLdeserialize(AbsSerializedData stream, int constructor) { + public TLObject TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { try { - return TLdeserialize(stream, constructor, null); + return TLdeserialize(stream, constructor, null, exception); } catch (Exception e) { return null; } } - public TLObject TLdeserialize(AbsSerializedData stream, int constructor, TLObject request) { + public TLObject TLdeserialize(AbsSerializedData stream, int constructor, TLObject request, boolean exception) { Class objClass = classStore.get(constructor); if (objClass != null) { try { TLObject response = (TLObject)objClass.newInstance(); - if (response instanceof TLRPC.Vector) { - if (request != null) { - request.parseVector((TLRPC.Vector)response, stream); - } else { - int size = stream.readInt32(); - for (int a = 0; a < size; a++) { - ((TLRPC.Vector)response).objects.add(stream.readInt32()); - } - } - } else { - response.readParams(stream); - } + response.readParams(stream, exception); return response; - } catch (IllegalAccessException e) { - FileLog.e("tmessages", "can't create class"); - return null; - } catch (InstantiationException e2) { + } catch (Throwable e) { FileLog.e("tmessages", "can't create class"); return null; } } else { - FileLog.e("tmessages", String.format("unknown class %x", constructor)); - if (BuildVars.DEBUG_VERSION) { - throw new RuntimeException(String.format("unknown class %x", constructor)); - } else { - return null; - } + return null; } } } diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/TLObject.java b/TMessagesProj/src/main/java/org/telegram/messenger/TLObject.java index 8ee8bc002..17c493070 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/TLObject.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/TLObject.java @@ -11,15 +11,15 @@ package org.telegram.messenger; public class TLObject { public boolean disableFree = false; - public TLObject () { + public TLObject() { } - public void readParams(AbsSerializedData stream) { + public void readParams(AbsSerializedData stream, boolean exception) { } - public byte[] serialize () { + public byte[] serialize() { return null; } @@ -27,16 +27,12 @@ public class TLObject { } - public Class responseClass () { - return this.getClass(); + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return null; } - public int layer () { - return 11; - } - - public void parseVector(TLRPC.Vector vector, AbsSerializedData data) { - + public int layer() { + return 11; } public void freeResources() { diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/TLRPC.java b/TMessagesProj/src/main/java/org/telegram/messenger/TLRPC.java index 0789e6cbe..cd093459a 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/TLRPC.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/TLRPC.java @@ -19,2100 +19,37 @@ public class TLRPC { public static final int MESSAGE_FLAG_FWD = 4; public static final int MESSAGE_FLAG_REPLY = 8; public static final int MESSAGE_FLAG_MENTION = 16; - public static final int LAYER = 27; + public static final int MESSAGE_FLAG_CONTENT_UNREAD = 32; + public static final int LAYER = 28; - public static class ChatPhoto extends TLObject { - public FileLocation photo_small; - public FileLocation photo_big; - } + public static class TL_inputEncryptedChat extends TLObject { + public static int constructor = 0xf141b5e1; - public static class TL_chatPhotoEmpty extends ChatPhoto { - public static int constructor = 0x37c1011c; - - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - } - } - - public static class TL_chatPhoto extends ChatPhoto { - public static int constructor = 0x6153276a; - - - public void readParams(AbsSerializedData stream) { - photo_small = (FileLocation)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - photo_big = (FileLocation)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - photo_small.serializeToStream(stream); - photo_big.serializeToStream(stream); - } - } - - public static class BadMsgNotification extends TLObject { - public long bad_msg_id; - public int bad_msg_seqno; - public int error_code; - public long new_server_salt; - } - - public static class TL_bad_msg_notification extends BadMsgNotification { - public static int constructor = 0xa7eff811; - - - public void readParams(AbsSerializedData stream) { - bad_msg_id = stream.readInt64(); - bad_msg_seqno = stream.readInt32(); - error_code = stream.readInt32(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt64(bad_msg_id); - stream.writeInt32(bad_msg_seqno); - stream.writeInt32(error_code); - } - } - - public static class TL_bad_server_salt extends BadMsgNotification { - public static int constructor = 0xedab447b; - - - public void readParams(AbsSerializedData stream) { - bad_msg_id = stream.readInt64(); - bad_msg_seqno = stream.readInt32(); - error_code = stream.readInt32(); - new_server_salt = stream.readInt64(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt64(bad_msg_id); - stream.writeInt32(bad_msg_seqno); - stream.writeInt32(error_code); - stream.writeInt64(new_server_salt); - } - } - - public static class TL_error extends TLObject { - public static int constructor = 0xc4b9f9bb; - - public int code; - public String text; - - public void readParams(AbsSerializedData stream) { - code = stream.readInt32(); - text = stream.readString(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(code); - stream.writeString(text); - } - } - - public static class messages_SentEncryptedMessage extends TLObject { - public int date; - public EncryptedFile file; - } - - public static class TL_messages_sentEncryptedMessage extends messages_SentEncryptedMessage { - public static int constructor = 0x560f8935; - - - public void readParams(AbsSerializedData stream) { - date = stream.readInt32(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(date); - } - } - - public static class TL_messages_sentEncryptedFile extends messages_SentEncryptedMessage { - public static int constructor = 0x9493ff32; - - - public void readParams(AbsSerializedData stream) { - date = stream.readInt32(); - file = (EncryptedFile)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(date); - file.serializeToStream(stream); - } - } - - public static class NotifyPeer extends TLObject { - public Peer peer; - } - - public static class TL_notifyAll extends NotifyPeer { - public static int constructor = 0x74d07c60; - - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - } - } - - public static class TL_notifyChats extends NotifyPeer { - public static int constructor = 0xc007cec3; - - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - } - } - - public static class TL_notifyUsers extends NotifyPeer { - public static int constructor = 0xb4c83b4c; - - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - } - } - - public static class TL_notifyPeer extends NotifyPeer { - public static int constructor = 0x9fd40bd8; - - - public void readParams(AbsSerializedData stream) { - peer = (Peer)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - peer.serializeToStream(stream); - } - } - - public static class TL_auth_checkedPhone extends TLObject { - public static int constructor = 0x811ea28e; - - public boolean phone_registered; - - public void readParams(AbsSerializedData stream) { - phone_registered = stream.readBool(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeBool(phone_registered); - } - } - - public static class TL_msgs_ack extends TLObject { - public static int constructor = 0x62d6b459; - - public ArrayList msg_ids = new ArrayList<>(); - - public void readParams(AbsSerializedData stream) { - stream.readInt32(); - int count = stream.readInt32(); - for (int a = 0; a < count; a++) { - msg_ids.add(stream.readInt64()); - } - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(0x1cb5c415); - int count = msg_ids.size(); - stream.writeInt32(count); - for (Long msg_id : msg_ids) { - stream.writeInt64(msg_id); - } - } - } - - public static class TL_messages_chatFull extends TLObject { - public static int constructor = 0xe5d7d19c; - - public TL_chatFull full_chat; - public ArrayList chats = new ArrayList<>(); - public ArrayList users = new ArrayList<>(); - - public void readParams(AbsSerializedData stream) { - full_chat = (TL_chatFull)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - stream.readInt32(); - int count = stream.readInt32(); - for (int a = 0; a < count; a++) { - chats.add((Chat)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32())); - } - stream.readInt32(); - count = stream.readInt32(); - for (int a = 0; a < count; a++) { - users.add((User)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32())); - } - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - full_chat.serializeToStream(stream); - stream.writeInt32(0x1cb5c415); - int count = chats.size(); - stream.writeInt32(count); - for (int a = 0; a < count; a++) { - chats.get(a).serializeToStream(stream); - } - stream.writeInt32(0x1cb5c415); - count = users.size(); - stream.writeInt32(count); - for (int a = 0; a < count; a++) { - users.get(a).serializeToStream(stream); - } - } - } - - public static class TL_account_passwordSettings extends TLObject { - public static int constructor = 0xb7b72ab3; - - public String email; - - public void readParams(AbsSerializedData stream) { - email = stream.readString(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeString(email); - } - } - - public static class DocumentAttribute extends TLObject { - public int duration; - public String file_name; - public String alt; - public int w; - public int h; - } - - public static class TL_documentAttributeAnimated extends DocumentAttribute { - public static int constructor = 0x11b58939; - - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - } - } - - public static class TL_documentAttributeAudio extends DocumentAttribute { - public static int constructor = 0x51448e5; - - - public void readParams(AbsSerializedData stream) { - duration = stream.readInt32(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(duration); - } - } - - public static class TL_documentAttributeFilename extends DocumentAttribute { - public static int constructor = 0x15590068; - - - public void readParams(AbsSerializedData stream) { - file_name = stream.readString(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeString(file_name); - } - } - - public static class TL_documentAttributeVideo extends DocumentAttribute { - public static int constructor = 0x5910cccb; - - - public void readParams(AbsSerializedData stream) { - duration = stream.readInt32(); - w = stream.readInt32(); - h = stream.readInt32(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(duration); - stream.writeInt32(w); - stream.writeInt32(h); - } - } - - public static class TL_documentAttributeSticker extends DocumentAttribute { - public static int constructor = 0x994c9882; - - - public void readParams(AbsSerializedData stream) { - alt = stream.readString(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeString(alt); - } - } - - public static class TL_documentAttributeImageSize extends DocumentAttribute { - public static int constructor = 0x6c37c15c; - - - public void readParams(AbsSerializedData stream) { - w = stream.readInt32(); - h = stream.readInt32(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(w); - stream.writeInt32(h); - } - } - - public static class TL_contactStatus extends TLObject { - public static int constructor = 0xd3680c61; - - public int user_id; - public UserStatus status; - - public void readParams(AbsSerializedData stream) { - user_id = stream.readInt32(); - status = (UserStatus)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(user_id); - status.serializeToStream(stream); - } - } - - public static class TL_auth_authorization extends TLObject { - public static int constructor = 0xf6b673a4; - - public int expires; - public User user; - - public void readParams(AbsSerializedData stream) { - expires = stream.readInt32(); - user = (User)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(expires); - user.serializeToStream(stream); - } - } - - public static class messages_Messages extends TLObject { - public ArrayList messages = new ArrayList<>(); - public ArrayList chats = new ArrayList<>(); - public ArrayList users = new ArrayList<>(); - public int count; - } - - public static class TL_messages_messages extends messages_Messages { - public static int constructor = 0x8c718e87; - - - public void readParams(AbsSerializedData stream) { - stream.readInt32(); - int count = stream.readInt32(); - for (int a = 0; a < count; a++) { - messages.add((Message)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32())); - } - stream.readInt32(); - count = stream.readInt32(); - for (int a = 0; a < count; a++) { - chats.add((Chat)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32())); - } - stream.readInt32(); - count = stream.readInt32(); - for (int a = 0; a < count; a++) { - users.add((User)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32())); - } - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(0x1cb5c415); - int count = messages.size(); - stream.writeInt32(count); - for (int a = 0; a < count; a++) { - messages.get(a).serializeToStream(stream); - } - stream.writeInt32(0x1cb5c415); - count = chats.size(); - stream.writeInt32(count); - for (int a = 0; a < count; a++) { - chats.get(a).serializeToStream(stream); - } - stream.writeInt32(0x1cb5c415); - count = users.size(); - stream.writeInt32(count); - for (int a = 0; a < count; a++) { - users.get(a).serializeToStream(stream); - } - } - } - - public static class TL_messages_messagesSlice extends messages_Messages { - public static int constructor = 0xb446ae3; - - - public void readParams(AbsSerializedData stream) { - count = stream.readInt32(); - stream.readInt32(); - int count = stream.readInt32(); - for (int a = 0; a < count; a++) { - messages.add((Message)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32())); - } - stream.readInt32(); - count = stream.readInt32(); - for (int a = 0; a < count; a++) { - chats.add((Chat)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32())); - } - stream.readInt32(); - count = stream.readInt32(); - for (int a = 0; a < count; a++) { - users.add((User)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32())); - } - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(count); - stream.writeInt32(0x1cb5c415); - int count = messages.size(); - stream.writeInt32(count); - for (int a = 0; a < count; a++) { - messages.get(a).serializeToStream(stream); - } - stream.writeInt32(0x1cb5c415); - count = chats.size(); - stream.writeInt32(count); - for (int a = 0; a < count; a++) { - chats.get(a).serializeToStream(stream); - } - stream.writeInt32(0x1cb5c415); - count = users.size(); - stream.writeInt32(count); - for (int a = 0; a < count; a++) { - users.get(a).serializeToStream(stream); - } - } - } - - public static class RpcDropAnswer extends TLObject { - public long msg_id; - public int seq_no; - public int bytes; - } - - public static class TL_rpc_answer_unknown extends RpcDropAnswer { - public static int constructor = 0x5e2ad36e; - - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - } - } - - public static class TL_rpc_answer_dropped extends RpcDropAnswer { - public static int constructor = 0xa43ad8b7; - - - public void readParams(AbsSerializedData stream) { - msg_id = stream.readInt64(); - seq_no = stream.readInt32(); - bytes = stream.readInt32(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt64(msg_id); - stream.writeInt32(seq_no); - stream.writeInt32(bytes); - } - } - - public static class TL_rpc_answer_dropped_running extends RpcDropAnswer { - public static int constructor = 0xcd78e586; - - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - } - } - - public static class TL_contacts_link extends TLObject { - public static int constructor = 0x3ace484c; - - public ContactLink my_link; - public ContactLink foreign_link; - public User user; - - public void readParams(AbsSerializedData stream) { - my_link = (ContactLink)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - foreign_link = (ContactLink)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - user = (User)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - my_link.serializeToStream(stream); - foreign_link.serializeToStream(stream); - user.serializeToStream(stream); - } - } - - public static class Peer extends TLObject { - public int user_id; public int chat_id; - } + public long access_hash; - public static class TL_peerUser extends Peer { - public static int constructor = 0x9db1bc6d; - - - public void readParams(AbsSerializedData stream) { - user_id = stream.readInt32(); + public static TL_inputEncryptedChat TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + if (TL_inputEncryptedChat.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_inputEncryptedChat", constructor)); + } else { + return null; + } + } + TL_inputEncryptedChat result = new TL_inputEncryptedChat(); + result.readParams(stream, exception); + return result; } - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(user_id); - } - } - - public static class TL_peerChat extends Peer { - public static int constructor = 0xbad0e5bb; - - - public void readParams(AbsSerializedData stream) { - chat_id = stream.readInt32(); + public void readParams(AbsSerializedData stream, boolean exception) { + chat_id = stream.readInt32(exception); + access_hash = stream.readInt64(exception); } public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(chat_id); - } - } - - public static class EncryptedFile extends TLObject { - public long id; - public long access_hash; - public int size; - public int dc_id; - public int key_fingerprint; - } - - public static class TL_encryptedFile extends EncryptedFile { - public static int constructor = 0x4a70994c; - - - public void readParams(AbsSerializedData stream) { - id = stream.readInt64(); - access_hash = stream.readInt64(); - size = stream.readInt32(); - dc_id = stream.readInt32(); - key_fingerprint = stream.readInt32(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt64(id); stream.writeInt64(access_hash); - stream.writeInt32(size); - stream.writeInt32(dc_id); - stream.writeInt32(key_fingerprint); - } - } - - public static class TL_encryptedFileEmpty extends EncryptedFile { - public static int constructor = 0xc21f497e; - - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - } - } - - public static class TL_messages_affectedMessages extends TLObject { - public static int constructor = 0x84d19185; - - public int pts; - public int pts_count; - - public void readParams(AbsSerializedData stream) { - pts = stream.readInt32(); - pts_count = stream.readInt32(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(pts); - stream.writeInt32(pts_count); - } - } - - public static class TL_authorization extends TLObject { - public static int constructor = 0x7bf2e6f6; - - public long hash; - public int flags; - public String device_model; - public String platform; - public String system_version; - public int api_id; - public String app_name; - public String app_version; - public int date_created; - public int date_active; - public String ip; - public String country; - public String region; - - public void readParams(AbsSerializedData stream) { - hash = stream.readInt64(); - flags = stream.readInt32(); - device_model = stream.readString(); - platform = stream.readString(); - system_version = stream.readString(); - api_id = stream.readInt32(); - app_name = stream.readString(); - app_version = stream.readString(); - date_created = stream.readInt32(); - date_active = stream.readInt32(); - ip = stream.readString(); - country = stream.readString(); - region = stream.readString(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt64(hash); - stream.writeInt32(flags); - stream.writeString(device_model); - stream.writeString(platform); - stream.writeString(system_version); - stream.writeInt32(api_id); - stream.writeString(app_name); - stream.writeString(app_version); - stream.writeInt32(date_created); - stream.writeInt32(date_active); - stream.writeString(ip); - stream.writeString(country); - stream.writeString(region); - } - } - - public static class DestroySessionRes extends TLObject { - public long session_id; - } - - public static class TL_destroy_session_ok extends DestroySessionRes { - public static int constructor = 0xe22045fc; - - - public void readParams(AbsSerializedData stream) { - session_id = stream.readInt64(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt64(session_id); - } - } - - public static class TL_destroy_session_none extends DestroySessionRes { - public static int constructor = 0x62d350c9; - - - public void readParams(AbsSerializedData stream) { - session_id = stream.readInt64(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt64(session_id); - } - } - - public static class updates_Difference extends TLObject { - public int date; - public int seq; - public ArrayList new_messages = new ArrayList<>(); - public ArrayList new_encrypted_messages = new ArrayList<>(); - public ArrayList other_updates = new ArrayList<>(); - public ArrayList chats = new ArrayList<>(); - public ArrayList users = new ArrayList<>(); - public TL_updates_state intermediate_state; - public TL_updates_state state; - } - - public static class TL_updates_differenceEmpty extends updates_Difference { - public static int constructor = 0x5d75a138; - - - public void readParams(AbsSerializedData stream) { - date = stream.readInt32(); - seq = stream.readInt32(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(date); - stream.writeInt32(seq); - } - } - - public static class TL_updates_differenceSlice extends updates_Difference { - public static int constructor = 0xa8fb1981; - - - public void readParams(AbsSerializedData stream) { - stream.readInt32(); - int count = stream.readInt32(); - for (int a = 0; a < count; a++) { - new_messages.add((Message)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32())); - } - stream.readInt32(); - count = stream.readInt32(); - for (int a = 0; a < count; a++) { - new_encrypted_messages.add((EncryptedMessage)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32())); - } - stream.readInt32(); - count = stream.readInt32(); - for (int a = 0; a < count; a++) { - other_updates.add((Update)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32())); - } - stream.readInt32(); - count = stream.readInt32(); - for (int a = 0; a < count; a++) { - chats.add((Chat)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32())); - } - stream.readInt32(); - count = stream.readInt32(); - for (int a = 0; a < count; a++) { - users.add((User)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32())); - } - intermediate_state = (TL_updates_state)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(0x1cb5c415); - int count = new_messages.size(); - stream.writeInt32(count); - for (int a = 0; a < count; a++) { - new_messages.get(a).serializeToStream(stream); - } - stream.writeInt32(0x1cb5c415); - count = new_encrypted_messages.size(); - stream.writeInt32(count); - for (int a = 0; a < count; a++) { - new_encrypted_messages.get(a).serializeToStream(stream); - } - stream.writeInt32(0x1cb5c415); - count = other_updates.size(); - stream.writeInt32(count); - for (int a = 0; a < count; a++) { - other_updates.get(a).serializeToStream(stream); - } - stream.writeInt32(0x1cb5c415); - count = chats.size(); - stream.writeInt32(count); - for (int a = 0; a < count; a++) { - chats.get(a).serializeToStream(stream); - } - stream.writeInt32(0x1cb5c415); - count = users.size(); - stream.writeInt32(count); - for (int a = 0; a < count; a++) { - users.get(a).serializeToStream(stream); - } - intermediate_state.serializeToStream(stream); - } - } - - public static class TL_updates_difference extends updates_Difference { - public static int constructor = 0xf49ca0; - - - public void readParams(AbsSerializedData stream) { - stream.readInt32(); - int count = stream.readInt32(); - for (int a = 0; a < count; a++) { - new_messages.add((Message)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32())); - } - stream.readInt32(); - count = stream.readInt32(); - for (int a = 0; a < count; a++) { - new_encrypted_messages.add((EncryptedMessage)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32())); - } - stream.readInt32(); - count = stream.readInt32(); - for (int a = 0; a < count; a++) { - other_updates.add((Update)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32())); - } - stream.readInt32(); - count = stream.readInt32(); - for (int a = 0; a < count; a++) { - chats.add((Chat)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32())); - } - stream.readInt32(); - count = stream.readInt32(); - for (int a = 0; a < count; a++) { - users.add((User)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32())); - } - state = (TL_updates_state)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(0x1cb5c415); - int count = new_messages.size(); - stream.writeInt32(count); - for (int a = 0; a < count; a++) { - new_messages.get(a).serializeToStream(stream); - } - stream.writeInt32(0x1cb5c415); - count = new_encrypted_messages.size(); - stream.writeInt32(count); - for (int a = 0; a < count; a++) { - new_encrypted_messages.get(a).serializeToStream(stream); - } - stream.writeInt32(0x1cb5c415); - count = other_updates.size(); - stream.writeInt32(count); - for (int a = 0; a < count; a++) { - other_updates.get(a).serializeToStream(stream); - } - stream.writeInt32(0x1cb5c415); - count = chats.size(); - stream.writeInt32(count); - for (int a = 0; a < count; a++) { - chats.get(a).serializeToStream(stream); - } - stream.writeInt32(0x1cb5c415); - count = users.size(); - stream.writeInt32(count); - for (int a = 0; a < count; a++) { - users.get(a).serializeToStream(stream); - } - state.serializeToStream(stream); - } - } - - public static class GeoPoint extends TLObject { - public double _long; - public double lat; - } - - public static class TL_geoPointEmpty extends GeoPoint { - public static int constructor = 0x1117dd5f; - - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - } - } - - public static class TL_geoPoint extends GeoPoint { - public static int constructor = 0x2049d70c; - - - public void readParams(AbsSerializedData stream) { - _long = stream.readDouble(); - lat = stream.readDouble(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeDouble(_long); - stream.writeDouble(lat); - } - } - - public static class TL_privacyKeyStatusTimestamp extends TLObject { - public static int constructor = 0xbc2eab30; - - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - } - } - - public static class TL_account_privacyRules extends TLObject { - public static int constructor = 0x554abb6f; - - public ArrayList rules = new ArrayList<>(); - public ArrayList users = new ArrayList<>(); - - public void readParams(AbsSerializedData stream) { - stream.readInt32(); - int count = stream.readInt32(); - for (int a = 0; a < count; a++) { - rules.add((PrivacyRule)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32())); - } - stream.readInt32(); - count = stream.readInt32(); - for (int a = 0; a < count; a++) { - users.add((User)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32())); - } - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(0x1cb5c415); - int count = rules.size(); - stream.writeInt32(count); - for (int a = 0; a < count; a++) { - rules.get(a).serializeToStream(stream); - } - stream.writeInt32(0x1cb5c415); - count = users.size(); - stream.writeInt32(count); - for (int a = 0; a < count; a++) { - users.get(a).serializeToStream(stream); - } - } - } - - public static class help_AppUpdate extends TLObject { - public int id; - public boolean critical; - public String url; - public String text; - } - - public static class TL_help_appUpdate extends help_AppUpdate { - public static int constructor = 0x8987f311; - - - public void readParams(AbsSerializedData stream) { - id = stream.readInt32(); - critical = stream.readBool(); - url = stream.readString(); - text = stream.readString(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(id); - stream.writeBool(critical); - stream.writeString(url); - stream.writeString(text); - } - } - - public static class TL_help_noAppUpdate extends help_AppUpdate { - public static int constructor = 0xc45a6536; - - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - } - } - - public static class TL_messageEmpty extends Message { - public static int constructor = 0x83e5de54; - - - public void readParams(AbsSerializedData stream) { - id = stream.readInt32(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(id); - } - } - - public static class TL_inputPhoneContact extends TLObject { - public static int constructor = 0xf392b7f4; - - public long client_id; - public String phone; - public String first_name; - public String last_name; - - public void readParams(AbsSerializedData stream) { - client_id = stream.readInt64(); - phone = stream.readString(); - first_name = stream.readString(); - last_name = stream.readString(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt64(client_id); - stream.writeString(phone); - stream.writeString(first_name); - stream.writeString(last_name); - } - } - - public static class SendMessageAction extends TLObject { - } - - public static class TL_sendMessageGeoLocationAction extends SendMessageAction { - public static int constructor = 0x176f8ba1; - - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - } - } - - public static class TL_sendMessageChooseContactAction extends SendMessageAction { - public static int constructor = 0x628cbc6f; - - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - } - } - - public static class TL_sendMessageTypingAction extends SendMessageAction { - public static int constructor = 0x16bf744e; - - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - } - } - - public static class TL_sendMessageUploadDocumentAction extends SendMessageAction { - public static int constructor = 0x8faee98e; - - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - } - } - - public static class TL_sendMessageRecordVideoAction extends SendMessageAction { - public static int constructor = 0xa187d66f; - - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - } - } - - public static class TL_sendMessageUploadPhotoAction extends SendMessageAction { - public static int constructor = 0x990a3c1a; - - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - } - } - - public static class TL_sendMessageUploadVideoAction extends SendMessageAction { - public static int constructor = 0x92042ff7; - - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - } - } - - public static class TL_sendMessageUploadAudioAction extends SendMessageAction { - public static int constructor = 0xe6ac8a6f; - - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - } - } - - public static class TL_sendMessageCancelAction extends SendMessageAction { - public static int constructor = 0xfd5ec8f5; - - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - } - } - - public static class TL_sendMessageRecordAudioAction extends SendMessageAction { - public static int constructor = 0xd52f73f7; - - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - } - } - - public static class TL_invokeAfterMsg extends TLObject { - public static int constructor = 0xcb9f372d; - - public long msg_id; - public TLObject query; - - public void readParams(AbsSerializedData stream) { - msg_id = stream.readInt64(); - query = TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt64(msg_id); - query.serializeToStream(stream); - } - } - - public static class MessageMedia extends TLObject { - public Video video; - public Photo photo; - public WebPage webpage; - public Document document; - public GeoPoint geo; - public Audio audio; - public String phone_number; - public String first_name; - public String last_name; - public int user_id; - public byte[] bytes; - } - - public static class TL_messageMediaVideo extends MessageMedia { - public static int constructor = 0xa2d24290; - - - public void readParams(AbsSerializedData stream) { - video = (Video)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - video.serializeToStream(stream); - } - } - - public static class TL_messageMediaPhoto extends MessageMedia { - public static int constructor = 0xc8c45a2a; - - - public void readParams(AbsSerializedData stream) { - photo = (Photo)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - photo.serializeToStream(stream); - } - } - - public static class TL_messageMediaWebPage extends MessageMedia { - public static int constructor = 0xa32dd600; - - - public void readParams(AbsSerializedData stream) { - webpage = (WebPage)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - webpage.serializeToStream(stream); - } - } - - public static class TL_messageMediaDocument extends MessageMedia { - public static int constructor = 0x2fda2204; - - - public void readParams(AbsSerializedData stream) { - document = (Document)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - document.serializeToStream(stream); - } - } - - public static class TL_messageMediaGeo extends MessageMedia { - public static int constructor = 0x56e0d474; - - - public void readParams(AbsSerializedData stream) { - geo = (GeoPoint)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - geo.serializeToStream(stream); - } - } - - public static class TL_messageMediaEmpty extends MessageMedia { - public static int constructor = 0x3ded6320; - - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - } - } - - public static class TL_messageMediaAudio extends MessageMedia { - public static int constructor = 0xc6b68300; - - - public void readParams(AbsSerializedData stream) { - audio = (Audio)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - audio.serializeToStream(stream); - } - } - - public static class TL_messageMediaContact extends MessageMedia { - public static int constructor = 0x5e7d2f39; - - - public void readParams(AbsSerializedData stream) { - phone_number = stream.readString(); - first_name = stream.readString(); - last_name = stream.readString(); - user_id = stream.readInt32(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeString(phone_number); - stream.writeString(first_name); - stream.writeString(last_name); - stream.writeInt32(user_id); - } - } - - public static class TL_messageMediaUnsupported extends MessageMedia { - public static int constructor = 0x9f84f49e; - - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - } - } - - public static class auth_SentCode extends TLObject { - public boolean phone_registered; - public String phone_code_hash; - public int send_call_timeout; - public boolean is_password; - } - - public static class TL_auth_sentAppCode extends auth_SentCode { - public static int constructor = 0xe325edcf; - - - public void readParams(AbsSerializedData stream) { - phone_registered = stream.readBool(); - phone_code_hash = stream.readString(); - send_call_timeout = stream.readInt32(); - is_password = stream.readBool(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeBool(phone_registered); - stream.writeString(phone_code_hash); - stream.writeInt32(send_call_timeout); - stream.writeBool(is_password); - } - } - - public static class TL_auth_sentCode extends auth_SentCode { - public static int constructor = 0xefed51d9; - - - public void readParams(AbsSerializedData stream) { - phone_registered = stream.readBool(); - phone_code_hash = stream.readString(); - send_call_timeout = stream.readInt32(); - is_password = stream.readBool(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeBool(phone_registered); - stream.writeString(phone_code_hash); - stream.writeInt32(send_call_timeout); - stream.writeBool(is_password); - } - } - - public static class PeerNotifySettings extends TLObject { - public int mute_until; - public String sound; - public boolean show_previews; - public int events_mask; - } - - public static class TL_peerNotifySettingsEmpty extends PeerNotifySettings { - public static int constructor = 0x70a68512; - - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - } - } - - public static class TL_peerNotifySettings extends PeerNotifySettings { - public static int constructor = 0x8d5e11ee; - - - public void readParams(AbsSerializedData stream) { - mute_until = stream.readInt32(); - sound = stream.readString(); - show_previews = stream.readBool(); - events_mask = stream.readInt32(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(mute_until); - stream.writeString(sound); - stream.writeBool(show_previews); - stream.writeInt32(events_mask); - } - } - - public static class TL_msg_resend_req extends TLObject { - public static int constructor = 0x7d861a08; - - public ArrayList msg_ids = new ArrayList<>(); - - public void readParams(AbsSerializedData stream) { - stream.readInt32(); - int count = stream.readInt32(); - for (int a = 0; a < count; a++) { - msg_ids.add(stream.readInt64()); - } - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(0x1cb5c415); - int count = msg_ids.size(); - stream.writeInt32(count); - for (Long msg_id : msg_ids) { - stream.writeInt64(msg_id); - } - } - } - - public static class TL_http_wait extends TLObject { - public static int constructor = 0x9299359f; - - public int max_delay; - public int wait_after; - public int max_wait; - - public void readParams(AbsSerializedData stream) { - max_delay = stream.readInt32(); - wait_after = stream.readInt32(); - max_wait = stream.readInt32(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(max_delay); - stream.writeInt32(wait_after); - stream.writeInt32(max_wait); - } - } - - public static class contacts_Blocked extends TLObject { - public ArrayList blocked = new ArrayList<>(); - public ArrayList users = new ArrayList<>(); - public int count; - } - - public static class TL_contacts_blocked extends contacts_Blocked { - public static int constructor = 0x1c138d15; - - - public void readParams(AbsSerializedData stream) { - stream.readInt32(); - int count = stream.readInt32(); - for (int a = 0; a < count; a++) { - blocked.add((TL_contactBlocked)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32())); - } - stream.readInt32(); - count = stream.readInt32(); - for (int a = 0; a < count; a++) { - users.add((User)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32())); - } - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(0x1cb5c415); - int count = blocked.size(); - stream.writeInt32(count); - for (int a = 0; a < count; a++) { - blocked.get(a).serializeToStream(stream); - } - stream.writeInt32(0x1cb5c415); - count = users.size(); - stream.writeInt32(count); - for (int a = 0; a < count; a++) { - users.get(a).serializeToStream(stream); - } - } - } - - public static class TL_contacts_blockedSlice extends contacts_Blocked { - public static int constructor = 0x900802a1; - - - public void readParams(AbsSerializedData stream) { - count = stream.readInt32(); - stream.readInt32(); - int count = stream.readInt32(); - for (int a = 0; a < count; a++) { - blocked.add((TL_contactBlocked)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32())); - } - stream.readInt32(); - count = stream.readInt32(); - for (int a = 0; a < count; a++) { - users.add((User)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32())); - } - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(count); - stream.writeInt32(0x1cb5c415); - int count = blocked.size(); - stream.writeInt32(count); - for (int a = 0; a < count; a++) { - blocked.get(a).serializeToStream(stream); - } - stream.writeInt32(0x1cb5c415); - count = users.size(); - stream.writeInt32(count); - for (int a = 0; a < count; a++) { - users.get(a).serializeToStream(stream); - } - } - } - - public static class InputGeoPoint extends TLObject { - public double lat; - public double _long; - } - - public static class TL_inputGeoPoint extends InputGeoPoint { - public static int constructor = 0xf3b7acc9; - - - public void readParams(AbsSerializedData stream) { - lat = stream.readDouble(); - _long = stream.readDouble(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeDouble(lat); - stream.writeDouble(_long); - } - } - - public static class TL_inputGeoPointEmpty extends InputGeoPoint { - public static int constructor = 0xe4c123d6; - - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - } - } - - public static class TL_help_inviteText extends TLObject { - public static int constructor = 0x18cb9f78; - - public String message; - - public void readParams(AbsSerializedData stream) { - message = stream.readString(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeString(message); - } - } - - public static class messages_DhConfig extends TLObject { - public byte[] random; - public int g; - public byte[] p; - public int version; - } - - public static class TL_messages_dhConfigNotModified extends messages_DhConfig { - public static int constructor = 0xc0e24635; - - - public void readParams(AbsSerializedData stream) { - random = stream.readByteArray(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeByteArray(random); - } - } - - public static class TL_messages_dhConfig extends messages_DhConfig { - public static int constructor = 0x2c221edd; - - - public void readParams(AbsSerializedData stream) { - g = stream.readInt32(); - p = stream.readByteArray(); - version = stream.readInt32(); - random = stream.readByteArray(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(g); - stream.writeByteArray(p); - stream.writeInt32(version); - stream.writeByteArray(random); - } - } - - public static class TL_audioEmpty extends Audio { - public static int constructor = 0x586988d8; - - - public void readParams(AbsSerializedData stream) { - id = stream.readInt64(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt64(id); - } - } - - public static class TL_audio extends Audio { - public static int constructor = 0xc7ac6496; - - - public void readParams(AbsSerializedData stream) { - id = stream.readInt64(); - access_hash = stream.readInt64(); - user_id = stream.readInt32(); - date = stream.readInt32(); - duration = stream.readInt32(); - mime_type = stream.readString(); - size = stream.readInt32(); - dc_id = stream.readInt32(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt64(id); - stream.writeInt64(access_hash); - stream.writeInt32(user_id); - stream.writeInt32(date); - stream.writeInt32(duration); - stream.writeString(mime_type); - stream.writeInt32(size); - stream.writeInt32(dc_id); - } - } - - public static class TL_destroy_sessions_res extends TLObject { - public static int constructor = 0xfb95abcd; - - public ArrayList destroy_results = new ArrayList<>(); - - public void readParams(AbsSerializedData stream) { - int count = stream.readInt32(); - for (int a = 0; a < count; a++) { - destroy_results.add((DestroySessionRes)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32())); - } - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - int count = destroy_results.size(); - stream.writeInt32(count); - for (DestroySessionRes destroy_result : destroy_results) { - destroy_result.serializeToStream(stream); - } - } - } - - public static class PrivacyRule extends TLObject { - public ArrayList users = new ArrayList<>(); - } - - public static class TL_privacyValueAllowUsers extends PrivacyRule { - public static int constructor = 0x4d5bbe0c; - - - public void readParams(AbsSerializedData stream) { - stream.readInt32(); - int count = stream.readInt32(); - for (int a = 0; a < count; a++) { - users.add(stream.readInt32()); - } - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(0x1cb5c415); - int count = users.size(); - stream.writeInt32(count); - for (Integer user : users) { - stream.writeInt32(user); - } - } - } - - public static class TL_privacyValueDisallowAll extends PrivacyRule { - public static int constructor = 0x8b73e763; - - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - } - } - - public static class TL_privacyValueAllowContacts extends PrivacyRule { - public static int constructor = 0xfffe1bac; - - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - } - } - - public static class TL_privacyValueDisallowContacts extends PrivacyRule { - public static int constructor = 0xf888fa1a; - - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - } - } - - public static class TL_privacyValueAllowAll extends PrivacyRule { - public static int constructor = 0x65427b82; - - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - } - } - - public static class TL_privacyValueDisallowUsers extends PrivacyRule { - public static int constructor = 0xc7f49b7; - - - public void readParams(AbsSerializedData stream) { - stream.readInt32(); - int count = stream.readInt32(); - for (int a = 0; a < count; a++) { - users.add(stream.readInt32()); - } - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(0x1cb5c415); - int count = users.size(); - stream.writeInt32(count); - for (Integer user : users) { - stream.writeInt32(user); - } - } - } - - public static class contacts_Contacts extends TLObject { - public ArrayList contacts = new ArrayList<>(); - public ArrayList users = new ArrayList<>(); - } - - public static class TL_contacts_contacts extends contacts_Contacts { - public static int constructor = 0x6f8b8cb2; - - - public void readParams(AbsSerializedData stream) { - stream.readInt32(); - int count = stream.readInt32(); - for (int a = 0; a < count; a++) { - contacts.add((TL_contact)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32())); - } - stream.readInt32(); - count = stream.readInt32(); - for (int a = 0; a < count; a++) { - users.add((User)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32())); - } - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(0x1cb5c415); - int count = contacts.size(); - stream.writeInt32(count); - for (int a = 0; a < count; a++) { - contacts.get(a).serializeToStream(stream); - } - stream.writeInt32(0x1cb5c415); - count = users.size(); - stream.writeInt32(count); - for (int a = 0; a < count; a++) { - users.get(a).serializeToStream(stream); - } - } - } - - public static class TL_contacts_contactsNotModified extends contacts_Contacts { - public static int constructor = 0xb74ba9d2; - - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - } - } - - public static class TL_inputPrivacyKeyStatusTimestamp extends TLObject { - public static int constructor = 0x4f96cb18; - - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - } - } - - public static class photos_Photos extends TLObject { - public ArrayList photos = new ArrayList<>(); - public ArrayList users = new ArrayList<>(); - public int count; - } - - public static class TL_photos_photos extends photos_Photos { - public static int constructor = 0x8dca6aa5; - - - public void readParams(AbsSerializedData stream) { - stream.readInt32(); - int count = stream.readInt32(); - for (int a = 0; a < count; a++) { - photos.add((Photo)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32())); - } - stream.readInt32(); - count = stream.readInt32(); - for (int a = 0; a < count; a++) { - users.add((User)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32())); - } - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(0x1cb5c415); - int count = photos.size(); - stream.writeInt32(count); - for (int a = 0; a < count; a++) { - photos.get(a).serializeToStream(stream); - } - stream.writeInt32(0x1cb5c415); - count = users.size(); - stream.writeInt32(count); - for (int a = 0; a < count; a++) { - users.get(a).serializeToStream(stream); - } - } - } - - public static class TL_photos_photosSlice extends photos_Photos { - public static int constructor = 0x15051f54; - - - public void readParams(AbsSerializedData stream) { - count = stream.readInt32(); - stream.readInt32(); - int count = stream.readInt32(); - for (int a = 0; a < count; a++) { - photos.add((Photo)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32())); - } - stream.readInt32(); - count = stream.readInt32(); - for (int a = 0; a < count; a++) { - users.add((User)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32())); - } - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(count); - stream.writeInt32(0x1cb5c415); - int count = photos.size(); - stream.writeInt32(count); - for (int a = 0; a < count; a++) { - photos.get(a).serializeToStream(stream); - } - stream.writeInt32(0x1cb5c415); - count = users.size(); - stream.writeInt32(count); - for (int a = 0; a < count; a++) { - users.get(a).serializeToStream(stream); - } - } - } - - public static class TL_chatFull extends TLObject { - public static int constructor = 0x630e61be; - - public int id; - public ChatParticipants participants; - public Photo chat_photo; - public PeerNotifySettings notify_settings; - - public void readParams(AbsSerializedData stream) { - id = stream.readInt32(); - participants = (ChatParticipants)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - chat_photo = (Photo)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - notify_settings = (PeerNotifySettings)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(id); - participants.serializeToStream(stream); - chat_photo.serializeToStream(stream); - notify_settings.serializeToStream(stream); - } - } - - public static class TL_msgs_all_info extends TLObject { - public static int constructor = 0x8cc0d131; - - public ArrayList msg_ids = new ArrayList<>(); - public String info; - - public void readParams(AbsSerializedData stream) { - stream.readInt32(); - int count = stream.readInt32(); - for (int a = 0; a < count; a++) { - msg_ids.add(stream.readInt64()); - } - info = stream.readString(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(0x1cb5c415); - int count = msg_ids.size(); - stream.writeInt32(count); - for (Long msg_id : msg_ids) { - stream.writeInt64(msg_id); - } - stream.writeString(info); - } - } - - public static class TL_inputPeerNotifySettings extends TLObject { - public static int constructor = 0x46a2ce98; - - public int mute_until; - public String sound; - public boolean show_previews; - public int events_mask; - - public void readParams(AbsSerializedData stream) { - mute_until = stream.readInt32(); - sound = stream.readString(); - show_previews = stream.readBool(); - events_mask = stream.readInt32(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(mute_until); - stream.writeString(sound); - stream.writeBool(show_previews); - stream.writeInt32(events_mask); - } - } - - public static class TL_null extends TLObject { - public static int constructor = 0x56730bcc; - - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - } - } - - public static class InputUser extends TLObject { - public int user_id; - public long access_hash; - } - - public static class TL_inputUserSelf extends InputUser { - public static int constructor = 0xf7c1b13f; - - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - } - } - - public static class TL_inputUserForeign extends InputUser { - public static int constructor = 0x655e74ff; - - - public void readParams(AbsSerializedData stream) { - user_id = stream.readInt32(); - access_hash = stream.readInt64(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(user_id); - stream.writeInt64(access_hash); - } - } - - public static class TL_inputUserEmpty extends InputUser { - public static int constructor = 0xb98886cf; - - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - } - } - - public static class TL_inputUserContact extends InputUser { - public static int constructor = 0x86e94f65; - - - public void readParams(AbsSerializedData stream) { - user_id = stream.readInt32(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(user_id); - } - } - - public static class TL_p_q_inner_data extends TLObject { - public static int constructor = 0x83c95aec; - - public byte[] pq; - public byte[] p; - public byte[] q; - public byte[] nonce; - public byte[] server_nonce; - public byte[] new_nonce; - - public void readParams(AbsSerializedData stream) { - pq = stream.readByteArray(); - p = stream.readByteArray(); - q = stream.readByteArray(); - nonce = stream.readData(16); - server_nonce = stream.readData(16); - new_nonce = stream.readData(32); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeByteArray(pq); - stream.writeByteArray(p); - stream.writeByteArray(q); - stream.writeRaw(nonce); - stream.writeRaw(server_nonce); - stream.writeRaw(new_nonce); - } - } - - public static class TL_msgs_state_req extends TLObject { - public static int constructor = 0xda69fb52; - - public ArrayList msg_ids = new ArrayList<>(); - - public void readParams(AbsSerializedData stream) { - stream.readInt32(); - int count = stream.readInt32(); - for (int a = 0; a < count; a++) { - msg_ids.add(stream.readInt64()); - } - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(0x1cb5c415); - int count = msg_ids.size(); - stream.writeInt32(count); - for (Long msg_id : msg_ids) { - stream.writeInt64(msg_id); - } - } - } - - public static class Bool extends TLObject { - } - - public static class TL_boolTrue extends Bool { - public static int constructor = 0x997275b5; - - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - } - } - - public static class TL_boolFalse extends Bool { - public static int constructor = 0xbc799737; - - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - } - } - - public static class TL_auth_exportedAuthorization extends TLObject { - public static int constructor = 0xdf969c2d; - - public int id; - public byte[] bytes; - - public void readParams(AbsSerializedData stream) { - id = stream.readInt32(); - bytes = stream.readByteArray(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(id); - stream.writeByteArray(bytes); } } @@ -2133,15 +70,37 @@ public class TLRPC { public int embed_height; public int duration; public String author; + + public static WebPage TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + WebPage result = null; + switch(constructor) { + case 0xc586da1c: + result = new TL_webPagePending(); + break; + case 0xeb1477e8: + result = new TL_webPageEmpty(); + break; + case 0xa31ea0b5: + result = new TL_webPage(); + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in WebPage", constructor)); + } + if (result != null) { + result.readParams(stream, exception); + } + return result; + } } public static class TL_webPagePending extends WebPage { public static int constructor = 0xc586da1c; - public void readParams(AbsSerializedData stream) { - id = stream.readInt64(); - date = stream.readInt32(); + public void readParams(AbsSerializedData stream, boolean exception) { + id = stream.readInt64(exception); + date = stream.readInt32(exception); } public void serializeToStream(AbsSerializedData stream) { @@ -2155,8 +114,8 @@ public class TLRPC { public static int constructor = 0xeb1477e8; - public void readParams(AbsSerializedData stream) { - id = stream.readInt64(); + public void readParams(AbsSerializedData stream, boolean exception) { + id = stream.readInt64(exception); } public void serializeToStream(AbsSerializedData stream) { @@ -2165,57 +124,47 @@ public class TLRPC { } } - public static class TL_auth_passwordRecovery extends TLObject { - public static int constructor = 0x137948a5; - - public String email_pattern; - - public void readParams(AbsSerializedData stream) { - email_pattern = stream.readString(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeString(email_pattern); - } - } - public static class TL_webPage extends WebPage { public static int constructor = 0xa31ea0b5; - public void readParams(AbsSerializedData stream) { - flags = stream.readInt32(); - id = stream.readInt64(); - url = stream.readString(); - display_url = stream.readString(); + + public void readParams(AbsSerializedData stream, boolean exception) { + flags = stream.readInt32(exception); + id = stream.readInt64(exception); + url = stream.readString(exception); + display_url = stream.readString(exception); if ((flags & 1) != 0) { - type = stream.readString(); + type = stream.readString(exception); } if ((flags & 2) != 0) { - site_name = stream.readString(); + site_name = stream.readString(exception); } if ((flags & 4) != 0) { - title = stream.readString(); + title = stream.readString(exception); } if ((flags & 8) != 0) { - description = stream.readString(); + description = stream.readString(exception); } if ((flags & 16) != 0) { - photo = (Photo) TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); + photo = Photo.TLdeserialize(stream, stream.readInt32(exception), exception); } if ((flags & 32) != 0) { - embed_url = stream.readString(); - embed_type = stream.readString(); + embed_url = stream.readString(exception); + } + if ((flags & 32) != 0) { + embed_type = stream.readString(exception); } if ((flags & 64) != 0) { - embed_width = stream.readInt32(); - embed_height = stream.readInt32(); + embed_width = stream.readInt32(exception); + } + if ((flags & 64) != 0) { + embed_height = stream.readInt32(exception); } if ((flags & 128) != 0) { - duration = stream.readInt32(); + duration = stream.readInt32(exception); } if ((flags & 256) != 0) { - author = stream.readString(); + author = stream.readString(exception); } } @@ -2242,10 +191,14 @@ public class TLRPC { } if ((flags & 32) != 0) { stream.writeString(embed_url); + } + if ((flags & 32) != 0) { stream.writeString(embed_type); } if ((flags & 64) != 0) { stream.writeInt32(embed_width); + } + if ((flags & 64) != 0) { stream.writeInt32(embed_height); } if ((flags & 128) != 0) { @@ -2257,479 +210,196 @@ public class TLRPC { } } - public static class InputNotifyPeer extends TLObject { - } + public static class TL_inputPeerNotifySettings extends TLObject { + public static int constructor = 0x46a2ce98; - public static class TL_inputNotifyChats extends InputNotifyPeer { - public static int constructor = 0x4a95e84e; + public int mute_until; + public String sound; + public boolean show_previews; + public int events_mask; - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - } - } - - public static class TL_inputNotifyPeer extends InputNotifyPeer { - public static int constructor = 0xb8bc5b0c; - - public InputPeer peer; - - public void readParams(AbsSerializedData stream) { - peer = (InputPeer)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - peer.serializeToStream(stream); - } - } - - public static class TL_inputNotifyUsers extends InputNotifyPeer { - public static int constructor = 0x193b4417; - - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - } - } - - public static class TL_inputNotifyGeoChatPeer extends InputNotifyPeer { - public static int constructor = 0x4d8ddec8; - - public TL_inputGeoChat peer; - - public void readParams(AbsSerializedData stream) { - peer = (TL_inputGeoChat)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - peer.serializeToStream(stream); - } - } - - public static class TL_inputNotifyAll extends InputNotifyPeer { - public static int constructor = 0xa429b886; - - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - } - } - - public static class InputFileLocation extends TLObject { - public long id; - public long access_hash; - public long volume_id; - public int local_id; - public long secret; - } - - public static class TL_inputAudioFileLocation extends InputFileLocation { - public static int constructor = 0x74dc404d; - - - public void readParams(AbsSerializedData stream) { - id = stream.readInt64(); - access_hash = stream.readInt64(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt64(id); - stream.writeInt64(access_hash); - } - } - - public static class TL_inputEncryptedFileLocation extends InputFileLocation { - public static int constructor = 0xf5235d55; - - - public void readParams(AbsSerializedData stream) { - id = stream.readInt64(); - access_hash = stream.readInt64(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt64(id); - stream.writeInt64(access_hash); - } - } - - public static class TL_inputVideoFileLocation extends InputFileLocation { - public static int constructor = 0x3d0364ec; - - - public void readParams(AbsSerializedData stream) { - id = stream.readInt64(); - access_hash = stream.readInt64(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt64(id); - stream.writeInt64(access_hash); - } - } - - public static class TL_inputDocumentFileLocation extends InputFileLocation { - public static int constructor = 0x4e45abe9; - - - public void readParams(AbsSerializedData stream) { - id = stream.readInt64(); - access_hash = stream.readInt64(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt64(id); - stream.writeInt64(access_hash); - } - } - - public static class TL_inputFileLocation extends InputFileLocation { - public static int constructor = 0x14637196; - - - public void readParams(AbsSerializedData stream) { - volume_id = stream.readInt64(); - local_id = stream.readInt32(); - secret = stream.readInt64(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt64(volume_id); - stream.writeInt32(local_id); - stream.writeInt64(secret); - } - } - - public static class TL_photos_photo extends TLObject { - public static int constructor = 0x20212ca8; - - public Photo photo; - public ArrayList users = new ArrayList<>(); - - public void readParams(AbsSerializedData stream) { - photo = (Photo)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - stream.readInt32(); - int count = stream.readInt32(); - for (int a = 0; a < count; a++) { - users.add((User) TLClassStore.Instance().TLdeserialize(stream, stream.readInt32())); + public static TL_inputPeerNotifySettings TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + if (TL_inputPeerNotifySettings.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_inputPeerNotifySettings", constructor)); + } else { + return null; + } } + TL_inputPeerNotifySettings result = new TL_inputPeerNotifySettings(); + result.readParams(stream, exception); + return result; + } + + public void readParams(AbsSerializedData stream, boolean exception) { + mute_until = stream.readInt32(exception); + sound = stream.readString(exception); + show_previews = stream.readBool(exception); + events_mask = stream.readInt32(exception); } public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); - photo.serializeToStream(stream); - stream.writeInt32(0x1cb5c415); - int count = users.size(); - stream.writeInt32(count); - for (User user : users) { - user.serializeToStream(stream); - } + stream.writeInt32(mute_until); + stream.writeString(sound); + stream.writeBool(show_previews); + stream.writeInt32(events_mask); } } - public static class TL_userContact extends User { - public static int constructor = 0xcab35e18; + public static class TL_inputEncryptedFile extends InputEncryptedFile { + public static int constructor = 0x5a17b5e5; - public void readParams(AbsSerializedData stream) { - id = stream.readInt32(); - first_name = stream.readString(); - last_name = stream.readString(); - username = stream.readString(); - access_hash = stream.readInt64(); - phone = stream.readString(); - photo = (UserProfilePhoto)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - status = (UserStatus)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); + public void readParams(AbsSerializedData stream, boolean exception) { + id = stream.readInt64(exception); + access_hash = stream.readInt64(exception); } public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); - stream.writeInt32(id); - stream.writeString(first_name); - stream.writeString(last_name); - stream.writeString(username); + stream.writeInt64(id); stream.writeInt64(access_hash); - stream.writeString(phone); - photo.serializeToStream(stream); - status.serializeToStream(stream); } } - public static class TL_userRequest extends User { - public static int constructor = 0xd9ccc4ef; + public static class TL_inputEncryptedFileBigUploaded extends InputEncryptedFile { + public static int constructor = 0x2dc173c8; - public void readParams(AbsSerializedData stream) { - id = stream.readInt32(); - first_name = stream.readString(); - last_name = stream.readString(); - username = stream.readString(); - access_hash = stream.readInt64(); - phone = stream.readString(); - photo = (UserProfilePhoto)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - status = (UserStatus)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); + public void readParams(AbsSerializedData stream, boolean exception) { + id = stream.readInt64(exception); + parts = stream.readInt32(exception); + key_fingerprint = stream.readInt32(exception); } public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); - stream.writeInt32(id); - stream.writeString(first_name); - stream.writeString(last_name); - stream.writeString(username); - stream.writeInt64(access_hash); - stream.writeString(phone); - photo.serializeToStream(stream); - status.serializeToStream(stream); + stream.writeInt64(id); + stream.writeInt32(parts); + stream.writeInt32(key_fingerprint); } } - public static class TL_userForeign extends User { - public static int constructor = 0x75cf7a8; + public static class TL_inputEncryptedFileEmpty extends InputEncryptedFile { + public static int constructor = 0x1837c364; - public void readParams(AbsSerializedData stream) { - id = stream.readInt32(); - first_name = stream.readString(); - last_name = stream.readString(); - username = stream.readString(); - access_hash = stream.readInt64(); - photo = (UserProfilePhoto)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - status = (UserStatus)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class TL_inputEncryptedFileUploaded extends InputEncryptedFile { + public static int constructor = 0x64bd0306; + + + public void readParams(AbsSerializedData stream, boolean exception) { + id = stream.readInt64(exception); + parts = stream.readInt32(exception); + md5_checksum = stream.readString(exception); + key_fingerprint = stream.readInt32(exception); } public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); - stream.writeInt32(id); - stream.writeString(first_name); - stream.writeString(last_name); - stream.writeString(username); - stream.writeInt64(access_hash); - photo.serializeToStream(stream); - status.serializeToStream(stream); + stream.writeInt64(id); + stream.writeInt32(parts); + stream.writeString(md5_checksum); + stream.writeInt32(key_fingerprint); } } - public static class TL_userDeleted extends User { - public static int constructor = 0xd6016d7a; - - - public void readParams(AbsSerializedData stream) { - id = stream.readInt32(); - first_name = stream.readString(); - last_name = stream.readString(); - username = stream.readString(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(id); - stream.writeString(first_name); - stream.writeString(last_name); - stream.writeString(username); - } - } - - public static class TL_userSelf extends User { - public static int constructor = 0x1c60e608; - - - public void readParams(AbsSerializedData stream) { - id = stream.readInt32(); - first_name = stream.readString(); - last_name = stream.readString(); - username = stream.readString(); - phone = stream.readString(); - photo = (UserProfilePhoto)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - status = (UserStatus)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(id); - stream.writeString(first_name); - stream.writeString(last_name); - stream.writeString(username); - stream.writeString(phone); - photo.serializeToStream(stream); - status.serializeToStream(stream); - } - } - - public static class GeoChatMessage extends TLObject { - public int chat_id; - public int id; - public int from_id; - public int date; - public String message; - public MessageMedia media; - public MessageAction action; - } - - public static class TL_geoChatMessage extends GeoChatMessage { - public static int constructor = 0x4505f8e1; - - - public void readParams(AbsSerializedData stream) { - chat_id = stream.readInt32(); - id = stream.readInt32(); - from_id = stream.readInt32(); - date = stream.readInt32(); - message = stream.readString(); - media = (MessageMedia)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(chat_id); - stream.writeInt32(id); - stream.writeInt32(from_id); - stream.writeInt32(date); - stream.writeString(message); - media.serializeToStream(stream); - } - } - - public static class TL_geoChatMessageService extends GeoChatMessage { - public static int constructor = 0xd34fa24e; - - - public void readParams(AbsSerializedData stream) { - chat_id = stream.readInt32(); - id = stream.readInt32(); - from_id = stream.readInt32(); - date = stream.readInt32(); - action = (MessageAction)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(chat_id); - stream.writeInt32(id); - stream.writeInt32(from_id); - stream.writeInt32(date); - action.serializeToStream(stream); - } - } - - public static class TL_geoChatMessageEmpty extends GeoChatMessage { - public static int constructor = 0x60311a9b; - - - public void readParams(AbsSerializedData stream) { - chat_id = stream.readInt32(); - id = stream.readInt32(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(chat_id); - stream.writeInt32(id); - } - } - - public static class TL_pong extends TLObject { - public static int constructor = 0x347773c5; - + public static class MsgDetailedInfo extends TLObject { + public long answer_msg_id; + public int bytes; + public int status; public long msg_id; - public long ping_id; - public void readParams(AbsSerializedData stream) { - msg_id = stream.readInt64(); - ping_id = stream.readInt64(); + public static MsgDetailedInfo TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + MsgDetailedInfo result = null; + switch(constructor) { + case 0x809db6df: + result = new TL_msg_new_detailed_info(); + break; + case 0x276d3ec6: + result = new TL_msg_detailed_info(); + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in MsgDetailedInfo", constructor)); + } + if (result != null) { + result.readParams(stream, exception); + } + return result; + } + } + + public static class TL_msg_new_detailed_info extends MsgDetailedInfo { + public static int constructor = 0x809db6df; + + + public void readParams(AbsSerializedData stream, boolean exception) { + answer_msg_id = stream.readInt64(exception); + bytes = stream.readInt32(exception); + status = stream.readInt32(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt64(answer_msg_id); + stream.writeInt32(bytes); + stream.writeInt32(status); + } + } + + public static class TL_msg_detailed_info extends MsgDetailedInfo { + public static int constructor = 0x276d3ec6; + + + public void readParams(AbsSerializedData stream, boolean exception) { + msg_id = stream.readInt64(exception); + answer_msg_id = stream.readInt64(exception); + bytes = stream.readInt32(exception); + status = stream.readInt32(exception); } public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt64(msg_id); - stream.writeInt64(ping_id); + stream.writeInt64(answer_msg_id); + stream.writeInt32(bytes); + stream.writeInt32(status); } } - public static class TL_messageActionChatEditPhoto extends MessageAction { - public static int constructor = 0x7fcb13a8; + public static class GeoPoint extends TLObject { + public double _long; + public double lat; - - public void readParams(AbsSerializedData stream) { - photo = (Photo)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - photo.serializeToStream(stream); - } - } - - public static class TL_messageActionChatDeleteUser extends MessageAction { - public static int constructor = 0xb2ae9b0c; - - - public void readParams(AbsSerializedData stream) { - user_id = stream.readInt32(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(user_id); - } - } - - public static class TL_messageActionChatDeletePhoto extends MessageAction { - public static int constructor = 0x95e3fbef; - - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - } - } - - public static class TL_messageActionChatAddUser extends MessageAction { - public static int constructor = 0x5e3cfc4b; - - - public void readParams(AbsSerializedData stream) { - user_id = stream.readInt32(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(user_id); - } - } - - public static class TL_messageActionChatCreate extends MessageAction { - public static int constructor = 0xa6638b9a; - - - public void readParams(AbsSerializedData stream) { - title = stream.readString(); - stream.readInt32(); - int count = stream.readInt32(); - for (int a = 0; a < count; a++) { - users.add(stream.readInt32()); + public static GeoPoint TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + GeoPoint result = null; + switch(constructor) { + case 0x1117dd5f: + result = new TL_geoPointEmpty(); + break; + case 0x2049d70c: + result = new TL_geoPoint(); + break; } - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeString(title); - stream.writeInt32(0x1cb5c415); - int count = users.size(); - stream.writeInt32(count); - for (Integer user : users) { - stream.writeInt32(user); + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in GeoPoint", constructor)); } + if (result != null) { + result.readParams(stream, exception); + } + return result; } } - public static class TL_messageActionEmpty extends MessageAction { - public static int constructor = 0xb6aef7b0; + public static class TL_geoPointEmpty extends GeoPoint { + public static int constructor = 0x1117dd5f; public void serializeToStream(AbsSerializedData stream) { @@ -2737,307 +407,171 @@ public class TLRPC { } } - public static class TL_messageActionChatEditTitle extends MessageAction { - public static int constructor = 0xb5a1ce5a; + public static class TL_geoPoint extends GeoPoint { + public static int constructor = 0x2049d70c; - public void readParams(AbsSerializedData stream) { - title = stream.readString(); + public void readParams(AbsSerializedData stream, boolean exception) { + _long = stream.readDouble(exception); + lat = stream.readDouble(exception); } public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); - stream.writeString(title); + stream.writeDouble(_long); + stream.writeDouble(lat); } } - public static class TL_messageActionGeoChatCreate extends MessageAction { - public static int constructor = 0x6f038ebc; + public static class TL_accountDaysTTL extends TLObject { + public static int constructor = 0xb8d0afdf; + public int days; - public void readParams(AbsSerializedData stream) { - title = stream.readString(); - address = stream.readString(); + public static TL_accountDaysTTL TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + if (TL_accountDaysTTL.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_accountDaysTTL", constructor)); + } else { + return null; + } + } + TL_accountDaysTTL result = new TL_accountDaysTTL(); + result.readParams(stream, exception); + return result; + } + + public void readParams(AbsSerializedData stream, boolean exception) { + days = stream.readInt32(exception); } public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); - stream.writeString(title); - stream.writeString(address); + stream.writeInt32(days); } } - public static class TL_messageActionGeoChatCheckin extends MessageAction { - public static int constructor = 0xc7d53de; + public static class TL_error extends TLObject { + public static int constructor = 0xc4b9f9bb; + public int code; + public String text; - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); + public static TL_error TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + if (TL_error.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_error", constructor)); + } else { + return null; + } + } + TL_error result = new TL_error(); + result.readParams(stream, exception); + return result; } - } - public static class Set_client_DH_params_answer extends TLObject { - public byte[] nonce; - public byte[] server_nonce; - public byte[] new_nonce_hash2; - public byte[] new_nonce_hash3; - public byte[] new_nonce_hash1; - } - - public static class TL_dh_gen_retry extends Set_client_DH_params_answer { - public static int constructor = 0x46dc1fb9; - - - public void readParams(AbsSerializedData stream) { - nonce = stream.readData(16); - server_nonce = stream.readData(16); - new_nonce_hash2 = stream.readData(16); + public void readParams(AbsSerializedData stream, boolean exception) { + code = stream.readInt32(exception); + text = stream.readString(exception); } public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); - stream.writeRaw(nonce); - stream.writeRaw(server_nonce); - stream.writeRaw(new_nonce_hash2); + stream.writeInt32(code); + stream.writeString(text); } } - public static class TL_dh_gen_fail extends Set_client_DH_params_answer { - public static int constructor = 0xa69dae02; + public static class TL_messageService extends Message { + public static int constructor = 0x1d86f70e; - - public void readParams(AbsSerializedData stream) { - nonce = stream.readData(16); - server_nonce = stream.readData(16); - new_nonce_hash3 = stream.readData(16); + public void readParams(AbsSerializedData stream, boolean exception) { + flags = stream.readInt32(exception); + id = stream.readInt32(exception); + from_id = stream.readInt32(exception); + to_id = Peer.TLdeserialize(stream, stream.readInt32(exception), exception); + date = stream.readInt32(exception); + action = MessageAction.TLdeserialize(stream, stream.readInt32(exception), exception); } public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); - stream.writeRaw(nonce); - stream.writeRaw(server_nonce); - stream.writeRaw(new_nonce_hash3); - } - } - - public static class TL_dh_gen_ok extends Set_client_DH_params_answer { - public static int constructor = 0x3bcbf734; - - - public void readParams(AbsSerializedData stream) { - nonce = stream.readData(16); - server_nonce = stream.readData(16); - new_nonce_hash1 = stream.readData(16); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeRaw(nonce); - stream.writeRaw(server_nonce); - stream.writeRaw(new_nonce_hash1); - } - } - - public static class PeerNotifyEvents extends TLObject { - } - - public static class TL_peerNotifyEventsEmpty extends PeerNotifyEvents { - public static int constructor = 0xadd53cb3; - - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - } - } - - public static class TL_peerNotifyEventsAll extends PeerNotifyEvents { - public static int constructor = 0x6d1ded88; - - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - } - } - - public static class TL_chatLocated extends TLObject { - public static int constructor = 0x3631cf4c; - - public int chat_id; - public int distance; - - public void readParams(AbsSerializedData stream) { - chat_id = stream.readInt32(); - distance = stream.readInt32(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(chat_id); - stream.writeInt32(distance); - } - } - - public static class DecryptedMessage extends TLObject { - public long random_id; - public byte[] random_bytes; - public DecryptedMessageAction action; - public int ttl; - public String message; - public DecryptedMessageMedia media; - } - - public static class TL_decryptedMessageService extends DecryptedMessage { - public static int constructor = 0x73164160; - - - public void readParams(AbsSerializedData stream) { - random_id = stream.readInt64(); - action = (DecryptedMessageAction)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt64(random_id); + stream.writeInt32(flags); + stream.writeInt32(id); + stream.writeInt32(from_id); + to_id.serializeToStream(stream); + stream.writeInt32(date); action.serializeToStream(stream); } } - public static class TL_decryptedMessage extends DecryptedMessage { - public static int constructor = 0x204d3878; + public static class TL_messageEmpty extends Message { + public static int constructor = 0x83e5de54; - public void readParams(AbsSerializedData stream) { - random_id = stream.readInt64(); - ttl = stream.readInt32(); - message = stream.readString(); - media = (DecryptedMessageMedia)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); + public void readParams(AbsSerializedData stream, boolean exception) { + id = stream.readInt32(exception); } public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); - stream.writeInt64(random_id); - stream.writeInt32(ttl); - stream.writeString(message); - media.serializeToStream(stream); + stream.writeInt32(id); } } - public static class InputPeerNotifyEvents extends TLObject { - } - - public static class TL_inputPeerNotifyEventsAll extends InputPeerNotifyEvents { - public static int constructor = 0xe86a2c74; - - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - } - } - - public static class TL_inputPeerNotifyEventsEmpty extends InputPeerNotifyEvents { - public static int constructor = 0xf03064d8; - - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - } - } - - public static class TL_client_DH_inner_data extends TLObject { - public static int constructor = 0x6643b654; - - public byte[] nonce; - public byte[] server_nonce; - public long retry_id; - public byte[] g_b; - - public void readParams(AbsSerializedData stream) { - nonce = stream.readData(16); - server_nonce = stream.readData(16); - retry_id = stream.readInt64(); - g_b = stream.readByteArray(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeRaw(nonce); - stream.writeRaw(server_nonce); - stream.writeInt64(retry_id); - stream.writeByteArray(g_b); - } - } - - public static class TL_video extends Video { - public static int constructor = 0x388fa391; - - - public void readParams(AbsSerializedData stream) { - id = stream.readInt64(); - access_hash = stream.readInt64(); - user_id = stream.readInt32(); - date = stream.readInt32(); - caption = stream.readString(); - duration = stream.readInt32(); - mime_type = stream.readString(); - size = stream.readInt32(); - thumb = (PhotoSize)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - dc_id = stream.readInt32(); - w = stream.readInt32(); - h = stream.readInt32(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt64(id); - stream.writeInt64(access_hash); - stream.writeInt32(user_id); - stream.writeInt32(date); - stream.writeString(caption); - stream.writeInt32(duration); - stream.writeString(mime_type); - stream.writeInt32(size); - thumb.serializeToStream(stream); - stream.writeInt32(dc_id); - stream.writeInt32(w); - stream.writeInt32(h); - } - } - - public static class TL_videoEmpty extends Video { - public static int constructor = 0xc10658a8; - - - public void readParams(AbsSerializedData stream) { - id = stream.readInt64(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt64(id); - } - } - - public static class TL_contactBlocked extends TLObject { - public static int constructor = 0x561bc879; + public static class TL_contactStatus extends TLObject { + public static int constructor = 0xd3680c61; public int user_id; - public int date; + public UserStatus status; - public void readParams(AbsSerializedData stream) { - user_id = stream.readInt32(); - date = stream.readInt32(); + public static TL_contactStatus TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + if (TL_contactStatus.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_contactStatus", constructor)); + } else { + return null; + } + } + TL_contactStatus result = new TL_contactStatus(); + result.readParams(stream, exception); + return result; + } + + public void readParams(AbsSerializedData stream, boolean exception) { + user_id = stream.readInt32(exception); + status = UserStatus.TLdeserialize(stream, stream.readInt32(exception), exception); } public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(user_id); - stream.writeInt32(date); + status.serializeToStream(stream); } } public static class InputDocument extends TLObject { public long id; public long access_hash; + + public static InputDocument TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + InputDocument result = null; + switch(constructor) { + case 0x72f0eaae: + result = new TL_inputDocumentEmpty(); + break; + case 0x18798952: + result = new TL_inputDocument(); + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in InputDocument", constructor)); + } + if (result != null) { + result.readParams(stream, exception); + } + return result; + } } public static class TL_inputDocumentEmpty extends InputDocument { @@ -3053,9 +587,9 @@ public class TLRPC { public static int constructor = 0x18798952; - public void readParams(AbsSerializedData stream) { - id = stream.readInt64(); - access_hash = stream.readInt64(); + public void readParams(AbsSerializedData stream, boolean exception) { + id = stream.readInt64(exception); + access_hash = stream.readInt64(exception); } public void serializeToStream(AbsSerializedData stream) { @@ -3065,93 +599,239 @@ public class TLRPC { } } - public static class TL_inputAppEvent extends TLObject { - public static int constructor = 0x770656a8; + public static class TL_auth_authorization extends TLObject { + public static int constructor = 0xf6b673a4; - public double time; - public String type; - public long peer; - public String data; + public int expires; + public User user; - public void readParams(AbsSerializedData stream) { - time = stream.readDouble(); - type = stream.readString(); - peer = stream.readInt64(); - data = stream.readString(); + public static TL_auth_authorization TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + if (TL_auth_authorization.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_auth_authorization", constructor)); + } else { + return null; + } + } + TL_auth_authorization result = new TL_auth_authorization(); + result.readParams(stream, exception); + return result; + } + + public void readParams(AbsSerializedData stream, boolean exception) { + expires = stream.readInt32(exception); + user = User.TLdeserialize(stream, stream.readInt32(exception), exception); } public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); - stream.writeDouble(time); - stream.writeString(type); - stream.writeInt64(peer); - stream.writeString(data); + stream.writeInt32(expires); + user.serializeToStream(stream); } } - public static class TL_messages_affectedHistory extends TLObject { - public static int constructor = 0xb45c69d1; + public static class Set_client_DH_params_answer extends TLObject { + public byte[] nonce; + public byte[] server_nonce; + public byte[] new_nonce_hash2; + public byte[] new_nonce_hash3; + public byte[] new_nonce_hash1; - public int pts; - public int pts_count; - public int offset; + public static Set_client_DH_params_answer TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + Set_client_DH_params_answer result = null; + switch(constructor) { + case 0x46dc1fb9: + result = new TL_dh_gen_retry(); + break; + case 0xa69dae02: + result = new TL_dh_gen_fail(); + break; + case 0x3bcbf734: + result = new TL_dh_gen_ok(); + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in Set_client_DH_params_answer", constructor)); + } + if (result != null) { + result.readParams(stream, exception); + } + return result; + } + } - public void readParams(AbsSerializedData stream) { - pts = stream.readInt32(); - pts_count = stream.readInt32(); - offset = stream.readInt32(); + public static class TL_dh_gen_retry extends Set_client_DH_params_answer { + public static int constructor = 0x46dc1fb9; + + + public void readParams(AbsSerializedData stream, boolean exception) { + nonce = stream.readData(16, exception); + server_nonce = stream.readData(16, exception); + new_nonce_hash2 = stream.readData(16, exception); } public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); - stream.writeInt32(pts); - stream.writeInt32(pts_count); - stream.writeInt32(offset); + stream.writeRaw(nonce); + stream.writeRaw(server_nonce); + stream.writeRaw(new_nonce_hash2); } } - public static class TL_documentEmpty extends Document { - public static int constructor = 0x36f8c871; + public static class TL_dh_gen_fail extends Set_client_DH_params_answer { + public static int constructor = 0xa69dae02; - public void readParams(AbsSerializedData stream) { - id = stream.readInt64(); + public void readParams(AbsSerializedData stream, boolean exception) { + nonce = stream.readData(16, exception); + server_nonce = stream.readData(16, exception); + new_nonce_hash3 = stream.readData(16, exception); } public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); - stream.writeInt64(id); + stream.writeRaw(nonce); + stream.writeRaw(server_nonce); + stream.writeRaw(new_nonce_hash3); } } - public static class TL_document extends Document { - public static int constructor = 0xf9a39f4f; + public static class TL_dh_gen_ok extends Set_client_DH_params_answer { + public static int constructor = 0x3bcbf734; - public void readParams(AbsSerializedData stream) { - id = stream.readInt64(); - access_hash = stream.readInt64(); - date = stream.readInt32(); - mime_type = stream.readString(); - size = stream.readInt32(); - thumb = (PhotoSize)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - dc_id = stream.readInt32(); - stream.readInt32(); - int count = stream.readInt32(); + public void readParams(AbsSerializedData stream, boolean exception) { + nonce = stream.readData(16, exception); + server_nonce = stream.readData(16, exception); + new_nonce_hash1 = stream.readData(16, exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeRaw(nonce); + stream.writeRaw(server_nonce); + stream.writeRaw(new_nonce_hash1); + } + } + + public static class InputMedia extends TLObject { + public String phone_number; + public String first_name; + public String last_name; + public InputFile file; + public InputFile thumb; + public String mime_type; + public ArrayList attributes = new ArrayList<>(); + public String caption; + public InputGeoPoint geo_point; + public int duration; + public int w; + public int h; + public String title; + public String address; + public String provider; + public String venue_id; + + public static InputMedia TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + InputMedia result = null; + switch(constructor) { + case 0xa6e45987: + result = new TL_inputMediaContact(); + break; + case 0x41481486: + result = new TL_inputMediaUploadedThumbDocument(); + break; + case 0x89938781: + result = new TL_inputMediaAudio(); + break; + case 0xd184e841: + result = new TL_inputMediaDocument(); + break; + case 0x936a4ebd: + result = new TL_inputMediaVideo(); + break; + case 0xf9c44144: + result = new TL_inputMediaGeoPoint(); + break; + case 0x9664f57f: + result = new TL_inputMediaEmpty(); + break; + case 0x96fb97dc: + result = new TL_inputMediaUploadedThumbVideo(); + break; + case 0xf7aff1c0: + result = new TL_inputMediaUploadedPhoto(); + break; + case 0x2827a81a: + result = new TL_inputMediaVenue(); + break; + case 0x4e498cab: + result = new TL_inputMediaUploadedAudio(); + break; + case 0xe13fd4bc: + result = new TL_inputMediaUploadedVideo(); + break; + case 0xffe76b78: + result = new TL_inputMediaUploadedDocument(); + break; + case 0xe9bfb4f3: + result = new TL_inputMediaPhoto(); + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in InputMedia", constructor)); + } + if (result != null) { + result.readParams(stream, exception); + } + return result; + } + } + + public static class TL_inputMediaContact extends InputMedia { + public static int constructor = 0xa6e45987; + + + public void readParams(AbsSerializedData stream, boolean exception) { + phone_number = stream.readString(exception); + first_name = stream.readString(exception); + last_name = stream.readString(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeString(phone_number); + stream.writeString(first_name); + stream.writeString(last_name); + } + } + + public static class TL_inputMediaUploadedThumbDocument extends InputMedia { + public static int constructor = 0x41481486; + + + public void readParams(AbsSerializedData stream, boolean exception) { + file = InputFile.TLdeserialize(stream, stream.readInt32(exception), exception); + thumb = InputFile.TLdeserialize(stream, stream.readInt32(exception), exception); + mime_type = stream.readString(exception); + int magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + int count = stream.readInt32(exception); for (int a = 0; a < count; a++) { - attributes.add((DocumentAttribute)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32())); + attributes.add(DocumentAttribute.TLdeserialize(stream, stream.readInt32(exception), exception)); } } public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); - stream.writeInt64(id); - stream.writeInt64(access_hash); - stream.writeInt32(date); - stream.writeString(mime_type); - stream.writeInt32(size); + file.serializeToStream(stream); thumb.serializeToStream(stream); - stream.writeInt32(dc_id); + stream.writeString(mime_type); stream.writeInt32(0x1cb5c415); int count = attributes.size(); stream.writeInt32(count); @@ -3161,11 +841,69 @@ public class TLRPC { } } - public static class ContactLink extends TLObject { + public static class TL_inputMediaAudio extends InputMedia { + public static int constructor = 0x89938781; + + public InputAudio id; + + public void readParams(AbsSerializedData stream, boolean exception) { + id = InputAudio.TLdeserialize(stream, stream.readInt32(exception), exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + id.serializeToStream(stream); + } } - public static class TL_contactLinkNone extends ContactLink { - public static int constructor = 0xfeedd3ad; + public static class TL_inputMediaDocument extends InputMedia { + public static int constructor = 0xd184e841; + + public InputDocument id; + + public void readParams(AbsSerializedData stream, boolean exception) { + id = InputDocument.TLdeserialize(stream, stream.readInt32(exception), exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + id.serializeToStream(stream); + } + } + + public static class TL_inputMediaVideo extends InputMedia { + public static int constructor = 0x936a4ebd; + + public InputVideo id; + + public void readParams(AbsSerializedData stream, boolean exception) { + id = InputVideo.TLdeserialize(stream, stream.readInt32(exception), exception); + caption = stream.readString(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + id.serializeToStream(stream); + stream.writeString(caption); + } + } + + public static class TL_inputMediaGeoPoint extends InputMedia { + public static int constructor = 0xf9c44144; + + + public void readParams(AbsSerializedData stream, boolean exception) { + geo_point = InputGeoPoint.TLdeserialize(stream, stream.readInt32(exception), exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + geo_point.serializeToStream(stream); + } + } + + public static class TL_inputMediaEmpty extends InputMedia { + public static int constructor = 0x9664f57f; public void serializeToStream(AbsSerializedData stream) { @@ -3173,46 +911,507 @@ public class TLRPC { } } - public static class TL_contactLinkContact extends ContactLink { - public static int constructor = 0xd502c2d0; + public static class TL_inputMediaUploadedThumbVideo extends InputMedia { + public static int constructor = 0x96fb97dc; + public void readParams(AbsSerializedData stream, boolean exception) { + file = InputFile.TLdeserialize(stream, stream.readInt32(exception), exception); + thumb = InputFile.TLdeserialize(stream, stream.readInt32(exception), exception); + duration = stream.readInt32(exception); + w = stream.readInt32(exception); + h = stream.readInt32(exception); + caption = stream.readString(exception); + } + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); + file.serializeToStream(stream); + thumb.serializeToStream(stream); + stream.writeInt32(duration); + stream.writeInt32(w); + stream.writeInt32(h); + stream.writeString(caption); } } - public static class TL_contactLinkHasPhone extends ContactLink { - public static int constructor = 0x268f3f59; + public static class TL_inputMediaUploadedPhoto extends InputMedia { + public static int constructor = 0xf7aff1c0; + public void readParams(AbsSerializedData stream, boolean exception) { + file = InputFile.TLdeserialize(stream, stream.readInt32(exception), exception); + caption = stream.readString(exception); + } + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); + file.serializeToStream(stream); + stream.writeString(caption); } } - public static class TL_contactLinkUnknown extends ContactLink { - public static int constructor = 0x5f4f9247; + public static class TL_inputMediaVenue extends InputMedia { + public static int constructor = 0x2827a81a; + public void readParams(AbsSerializedData stream, boolean exception) { + geo_point = InputGeoPoint.TLdeserialize(stream, stream.readInt32(exception), exception); + title = stream.readString(exception); + address = stream.readString(exception); + provider = stream.readString(exception); + venue_id = stream.readString(exception); + } + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); + geo_point.serializeToStream(stream); + stream.writeString(title); + stream.writeString(address); + stream.writeString(provider); + stream.writeString(venue_id); + } + } + + public static class TL_inputMediaUploadedAudio extends InputMedia { + public static int constructor = 0x4e498cab; + + + public void readParams(AbsSerializedData stream, boolean exception) { + file = InputFile.TLdeserialize(stream, stream.readInt32(exception), exception); + duration = stream.readInt32(exception); + mime_type = stream.readString(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + file.serializeToStream(stream); + stream.writeInt32(duration); + stream.writeString(mime_type); + } + } + + public static class TL_inputMediaUploadedVideo extends InputMedia { + public static int constructor = 0xe13fd4bc; + + + public void readParams(AbsSerializedData stream, boolean exception) { + file = InputFile.TLdeserialize(stream, stream.readInt32(exception), exception); + duration = stream.readInt32(exception); + w = stream.readInt32(exception); + h = stream.readInt32(exception); + caption = stream.readString(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + file.serializeToStream(stream); + stream.writeInt32(duration); + stream.writeInt32(w); + stream.writeInt32(h); + stream.writeString(caption); + } + } + + public static class TL_inputMediaUploadedDocument extends InputMedia { + public static int constructor = 0xffe76b78; + + + public void readParams(AbsSerializedData stream, boolean exception) { + file = InputFile.TLdeserialize(stream, stream.readInt32(exception), exception); + mime_type = stream.readString(exception); + int magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + int count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + attributes.add(DocumentAttribute.TLdeserialize(stream, stream.readInt32(exception), exception)); + } + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + file.serializeToStream(stream); + stream.writeString(mime_type); + stream.writeInt32(0x1cb5c415); + int count = attributes.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + attributes.get(a).serializeToStream(stream); + } + } + } + + public static class TL_inputMediaPhoto extends InputMedia { + public static int constructor = 0xe9bfb4f3; + + public InputPhoto id; + + public void readParams(AbsSerializedData stream, boolean exception) { + id = InputPhoto.TLdeserialize(stream, stream.readInt32(exception), exception); + caption = stream.readString(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + id.serializeToStream(stream); + stream.writeString(caption); + } + } + + public static class TL_geochats_statedMessage extends TLObject { + public static int constructor = 0x17b1578b; + + public GeoChatMessage message; + public ArrayList chats = new ArrayList<>(); + public ArrayList users = new ArrayList<>(); + public int seq; + + public static TL_geochats_statedMessage TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + if (TL_geochats_statedMessage.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_geochats_statedMessage", constructor)); + } else { + return null; + } + } + TL_geochats_statedMessage result = new TL_geochats_statedMessage(); + result.readParams(stream, exception); + return result; + } + + public void readParams(AbsSerializedData stream, boolean exception) { + message = GeoChatMessage.TLdeserialize(stream, stream.readInt32(exception), exception); + int magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + int count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + chats.add(Chat.TLdeserialize(stream, stream.readInt32(exception), exception)); + } + magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + users.add(User.TLdeserialize(stream, stream.readInt32(exception), exception)); + } + seq = stream.readInt32(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + message.serializeToStream(stream); + stream.writeInt32(0x1cb5c415); + int count = chats.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + chats.get(a).serializeToStream(stream); + } + stream.writeInt32(0x1cb5c415); + count = users.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + users.get(a).serializeToStream(stream); + } + stream.writeInt32(seq); + } + } + + public static class messages_Dialogs extends TLObject { + public ArrayList dialogs = new ArrayList<>(); + public ArrayList messages = new ArrayList<>(); + public ArrayList chats = new ArrayList<>(); + public ArrayList users = new ArrayList<>(); + public int count; + + public static messages_Dialogs TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + messages_Dialogs result = null; + switch(constructor) { + case 0x15ba6c40: + result = new TL_messages_dialogs(); + break; + case 0x71e094f3: + result = new TL_messages_dialogsSlice(); + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in messages_Dialogs", constructor)); + } + if (result != null) { + result.readParams(stream, exception); + } + return result; + } + } + + public static class TL_messages_dialogs extends messages_Dialogs { + public static int constructor = 0x15ba6c40; + + + public void readParams(AbsSerializedData stream, boolean exception) { + int magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + int count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + dialogs.add(TL_dialog.TLdeserialize(stream, stream.readInt32(exception), exception)); + } + magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + messages.add(Message.TLdeserialize(stream, stream.readInt32(exception), exception)); + } + magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + chats.add(Chat.TLdeserialize(stream, stream.readInt32(exception), exception)); + } + magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + users.add(User.TLdeserialize(stream, stream.readInt32(exception), exception)); + } + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(0x1cb5c415); + int count = dialogs.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + dialogs.get(a).serializeToStream(stream); + } + stream.writeInt32(0x1cb5c415); + count = messages.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + messages.get(a).serializeToStream(stream); + } + stream.writeInt32(0x1cb5c415); + count = chats.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + chats.get(a).serializeToStream(stream); + } + stream.writeInt32(0x1cb5c415); + count = users.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + users.get(a).serializeToStream(stream); + } + } + } + + public static class TL_messages_dialogsSlice extends messages_Dialogs { + public static int constructor = 0x71e094f3; + + + public void readParams(AbsSerializedData stream, boolean exception) { + count = stream.readInt32(exception); + int magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + int count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + dialogs.add(TL_dialog.TLdeserialize(stream, stream.readInt32(exception), exception)); + } + magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + messages.add(Message.TLdeserialize(stream, stream.readInt32(exception), exception)); + } + magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + chats.add(Chat.TLdeserialize(stream, stream.readInt32(exception), exception)); + } + magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + users.add(User.TLdeserialize(stream, stream.readInt32(exception), exception)); + } + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(count); + stream.writeInt32(0x1cb5c415); + int count = dialogs.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + dialogs.get(a).serializeToStream(stream); + } + stream.writeInt32(0x1cb5c415); + count = messages.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + messages.get(a).serializeToStream(stream); + } + stream.writeInt32(0x1cb5c415); + count = chats.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + chats.get(a).serializeToStream(stream); + } + stream.writeInt32(0x1cb5c415); + count = users.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + users.get(a).serializeToStream(stream); + } + } + } + + public static class TL_server_DH_inner_data extends TLObject { + public static int constructor = 0xb5890dba; + + public byte[] nonce; + public byte[] server_nonce; + public int g; + public byte[] dh_prime; + public byte[] g_a; + public int server_time; + + public static TL_server_DH_inner_data TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + if (TL_server_DH_inner_data.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_server_DH_inner_data", constructor)); + } else { + return null; + } + } + TL_server_DH_inner_data result = new TL_server_DH_inner_data(); + result.readParams(stream, exception); + return result; + } + + public void readParams(AbsSerializedData stream, boolean exception) { + nonce = stream.readData(16, exception); + server_nonce = stream.readData(16, exception); + g = stream.readInt32(exception); + dh_prime = stream.readByteArray(exception); + g_a = stream.readByteArray(exception); + server_time = stream.readInt32(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeRaw(nonce); + stream.writeRaw(server_nonce); + stream.writeInt32(g); + stream.writeByteArray(dh_prime); + stream.writeByteArray(g_a); + stream.writeInt32(server_time); } } public static class InputPrivacyRule extends TLObject { public ArrayList users = new ArrayList<>(); + + public static InputPrivacyRule TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + InputPrivacyRule result = null; + switch(constructor) { + case 0x90110467: + result = new TL_inputPrivacyValueDisallowUsers(); + break; + case 0xd66b66c9: + result = new TL_inputPrivacyValueDisallowAll(); + break; + case 0xba52007: + result = new TL_inputPrivacyValueDisallowContacts(); + break; + case 0x184b35ce: + result = new TL_inputPrivacyValueAllowAll(); + break; + case 0xd09e07b: + result = new TL_inputPrivacyValueAllowContacts(); + break; + case 0x131cc67f: + result = new TL_inputPrivacyValueAllowUsers(); + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in InputPrivacyRule", constructor)); + } + if (result != null) { + result.readParams(stream, exception); + } + return result; + } } public static class TL_inputPrivacyValueDisallowUsers extends InputPrivacyRule { public static int constructor = 0x90110467; - public void readParams(AbsSerializedData stream) { - stream.readInt32(); - int count = stream.readInt32(); + public void readParams(AbsSerializedData stream, boolean exception) { + int magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + int count = stream.readInt32(exception); for (int a = 0; a < count; a++) { - users.add((InputUser)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32())); + users.add(InputUser.TLdeserialize(stream, stream.readInt32(exception), exception)); } } @@ -3221,8 +1420,8 @@ public class TLRPC { stream.writeInt32(0x1cb5c415); int count = users.size(); stream.writeInt32(count); - for (InputUser user : users) { - user.serializeToStream(stream); + for (int a = 0; a < count; a++) { + users.get(a).serializeToStream(stream); } } } @@ -3267,11 +1466,17 @@ public class TLRPC { public static int constructor = 0x131cc67f; - public void readParams(AbsSerializedData stream) { - stream.readInt32(); - int count = stream.readInt32(); + public void readParams(AbsSerializedData stream, boolean exception) { + int magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + int count = stream.readInt32(exception); for (int a = 0; a < count; a++) { - users.add((InputUser)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32())); + users.add(InputUser.TLdeserialize(stream, stream.readInt32(exception), exception)); } } @@ -3280,34 +1485,954 @@ public class TLRPC { stream.writeInt32(0x1cb5c415); int count = users.size(); stream.writeInt32(count); - for (InputUser user : users) { - user.serializeToStream(stream); + for (int a = 0; a < count; a++) { + users.get(a).serializeToStream(stream); } } } - public static class InputMedia extends TLObject { - public String phone_number; - public String first_name; - public String last_name; - public InputFile file; - public InputFile thumb; - public String mime_type; - public ArrayList attributes = new ArrayList<>(); - public InputGeoPoint geo_point; - public int duration; - public int w; - public int h; + public static class TL_contacts_link extends TLObject { + public static int constructor = 0x3ace484c; + + public ContactLink my_link; + public ContactLink foreign_link; + public User user; + + public static TL_contacts_link TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + if (TL_contacts_link.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_contacts_link", constructor)); + } else { + return null; + } + } + TL_contacts_link result = new TL_contacts_link(); + result.readParams(stream, exception); + return result; + } + + public void readParams(AbsSerializedData stream, boolean exception) { + my_link = ContactLink.TLdeserialize(stream, stream.readInt32(exception), exception); + foreign_link = ContactLink.TLdeserialize(stream, stream.readInt32(exception), exception); + user = User.TLdeserialize(stream, stream.readInt32(exception), exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + my_link.serializeToStream(stream); + foreign_link.serializeToStream(stream); + user.serializeToStream(stream); + } } - public static class TL_inputMediaContact extends InputMedia { - public static int constructor = 0xa6e45987; + public static class photos_Photos extends TLObject { + public ArrayList photos = new ArrayList<>(); + public ArrayList users = new ArrayList<>(); + public int count; + + public static photos_Photos TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + photos_Photos result = null; + switch(constructor) { + case 0x8dca6aa5: + result = new TL_photos_photos(); + break; + case 0x15051f54: + result = new TL_photos_photosSlice(); + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in photos_Photos", constructor)); + } + if (result != null) { + result.readParams(stream, exception); + } + return result; + } + } + + public static class TL_photos_photos extends photos_Photos { + public static int constructor = 0x8dca6aa5; - public void readParams(AbsSerializedData stream) { - phone_number = stream.readString(); - first_name = stream.readString(); - last_name = stream.readString(); + public void readParams(AbsSerializedData stream, boolean exception) { + int magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + int count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + photos.add(Photo.TLdeserialize(stream, stream.readInt32(exception), exception)); + } + magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + users.add(User.TLdeserialize(stream, stream.readInt32(exception), exception)); + } + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(0x1cb5c415); + int count = photos.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + photos.get(a).serializeToStream(stream); + } + stream.writeInt32(0x1cb5c415); + count = users.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + users.get(a).serializeToStream(stream); + } + } + } + + public static class TL_photos_photosSlice extends photos_Photos { + public static int constructor = 0x15051f54; + + + public void readParams(AbsSerializedData stream, boolean exception) { + count = stream.readInt32(exception); + int magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + int count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + photos.add(Photo.TLdeserialize(stream, stream.readInt32(exception), exception)); + } + magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + users.add(User.TLdeserialize(stream, stream.readInt32(exception), exception)); + } + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(count); + stream.writeInt32(0x1cb5c415); + int count = photos.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + photos.get(a).serializeToStream(stream); + } + stream.writeInt32(0x1cb5c415); + count = users.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + users.get(a).serializeToStream(stream); + } + } + } + + public static class TL_msgs_ack extends TLObject { + public static int constructor = 0x62d6b459; + + public ArrayList msg_ids = new ArrayList<>(); + + public static TL_msgs_ack TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + if (TL_msgs_ack.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_msgs_ack", constructor)); + } else { + return null; + } + } + TL_msgs_ack result = new TL_msgs_ack(); + result.readParams(stream, exception); + return result; + } + + public void readParams(AbsSerializedData stream, boolean exception) { + int magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + int count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + msg_ids.add(stream.readInt64(exception)); + } + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(0x1cb5c415); + int count = msg_ids.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + stream.writeInt64(msg_ids.get(a)); + } + } + } + + public static class TL_userStatusLastWeek extends UserStatus { + public static int constructor = 0x7bf09fc; + + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class TL_userStatusEmpty extends UserStatus { + public static int constructor = 0x9d05049; + + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class TL_userStatusLastMonth extends UserStatus { + public static int constructor = 0x77ebc742; + + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class TL_userStatusOnline extends UserStatus { + public static int constructor = 0xedb93949; + + + public void readParams(AbsSerializedData stream, boolean exception) { + expires = stream.readInt32(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(expires); + } + } + + public static class TL_userStatusRecently extends UserStatus { + public static int constructor = 0xe26f42f1; + + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class TL_userFull extends TLObject { + public static int constructor = 0x771095da; + + public User user; + public TL_contacts_link link; + public Photo profile_photo; + public PeerNotifySettings notify_settings; + public boolean blocked; + public String real_first_name; + public String real_last_name; + + public static TL_userFull TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + if (TL_userFull.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_userFull", constructor)); + } else { + return null; + } + } + TL_userFull result = new TL_userFull(); + result.readParams(stream, exception); + return result; + } + + public void readParams(AbsSerializedData stream, boolean exception) { + user = User.TLdeserialize(stream, stream.readInt32(exception), exception); + link = TL_contacts_link.TLdeserialize(stream, stream.readInt32(exception), exception); + profile_photo = Photo.TLdeserialize(stream, stream.readInt32(exception), exception); + notify_settings = PeerNotifySettings.TLdeserialize(stream, stream.readInt32(exception), exception); + blocked = stream.readBool(exception); + real_first_name = stream.readString(exception); + real_last_name = stream.readString(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + user.serializeToStream(stream); + link.serializeToStream(stream); + profile_photo.serializeToStream(stream); + notify_settings.serializeToStream(stream); + stream.writeBool(blocked); + stream.writeString(real_first_name); + stream.writeString(real_last_name); + } + } + + public static class TL_msg_resend_req extends TLObject { + public static int constructor = 0x7d861a08; + + public ArrayList msg_ids = new ArrayList<>(); + + public static TL_msg_resend_req TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + if (TL_msg_resend_req.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_msg_resend_req", constructor)); + } else { + return null; + } + } + TL_msg_resend_req result = new TL_msg_resend_req(); + result.readParams(stream, exception); + return result; + } + + public void readParams(AbsSerializedData stream, boolean exception) { + int magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + int count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + msg_ids.add(stream.readInt64(exception)); + } + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(0x1cb5c415); + int count = msg_ids.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + stream.writeInt64(msg_ids.get(a)); + } + } + } + + public static class TL_contact extends TLObject { + public static int constructor = 0xf911c994; + + public int user_id; + public boolean mutual; + + public static TL_contact TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + if (TL_contact.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_contact", constructor)); + } else { + return null; + } + } + TL_contact result = new TL_contact(); + result.readParams(stream, exception); + return result; + } + + public void readParams(AbsSerializedData stream, boolean exception) { + user_id = stream.readInt32(exception); + mutual = stream.readBool(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(user_id); + stream.writeBool(mutual); + } + } + + public static class TL_chatLocated extends TLObject { + public static int constructor = 0x3631cf4c; + + public int chat_id; + public int distance; + + public static TL_chatLocated TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + if (TL_chatLocated.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_chatLocated", constructor)); + } else { + return null; + } + } + TL_chatLocated result = new TL_chatLocated(); + result.readParams(stream, exception); + return result; + } + + public void readParams(AbsSerializedData stream, boolean exception) { + chat_id = stream.readInt32(exception); + distance = stream.readInt32(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(chat_id); + stream.writeInt32(distance); + } + } + + public static class RpcError extends TLObject { + public int error_code; + public String error_message; + public long query_id; + + public static RpcError TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + RpcError result = null; + switch(constructor) { + case 0x2144ca19: + result = new TL_rpc_error(); + break; + case 0x7ae432f5: + result = new TL_rpc_req_error(); + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in RpcError", constructor)); + } + if (result != null) { + result.readParams(stream, exception); + } + return result; + } + } + + public static class TL_rpc_error extends RpcError { + public static int constructor = 0x2144ca19; + + + public void readParams(AbsSerializedData stream, boolean exception) { + error_code = stream.readInt32(exception); + error_message = stream.readString(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(error_code); + stream.writeString(error_message); + } + } + + public static class TL_rpc_req_error extends RpcError { + public static int constructor = 0x7ae432f5; + + + public void readParams(AbsSerializedData stream, boolean exception) { + query_id = stream.readInt64(exception); + error_code = stream.readInt32(exception); + error_message = stream.readString(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt64(query_id); + stream.writeInt32(error_code); + stream.writeString(error_message); + } + } + + public static class TL_privacyKeyStatusTimestamp extends TLObject { + public static int constructor = 0xbc2eab30; + + + public static TL_privacyKeyStatusTimestamp TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + if (TL_privacyKeyStatusTimestamp.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_privacyKeyStatusTimestamp", constructor)); + } else { + return null; + } + } + TL_privacyKeyStatusTimestamp result = new TL_privacyKeyStatusTimestamp(); + result.readParams(stream, exception); + return result; + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class ChatParticipants extends TLObject { + public int chat_id; + public int admin_id; + public ArrayList participants = new ArrayList<>(); + public int version; + + public static ChatParticipants TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + ChatParticipants result = null; + switch(constructor) { + case 0x7841b415: + result = new TL_chatParticipants(); + break; + case 0xfd2bb8a: + result = new TL_chatParticipantsForbidden(); + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in ChatParticipants", constructor)); + } + if (result != null) { + result.readParams(stream, exception); + } + return result; + } + } + + public static class TL_chatParticipants extends ChatParticipants { + public static int constructor = 0x7841b415; + + + public void readParams(AbsSerializedData stream, boolean exception) { + chat_id = stream.readInt32(exception); + admin_id = stream.readInt32(exception); + int magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + int count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + participants.add(TL_chatParticipant.TLdeserialize(stream, stream.readInt32(exception), exception)); + } + version = stream.readInt32(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(chat_id); + stream.writeInt32(admin_id); + stream.writeInt32(0x1cb5c415); + int count = participants.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + participants.get(a).serializeToStream(stream); + } + stream.writeInt32(version); + } + } + + public static class TL_chatParticipantsForbidden extends ChatParticipants { + public static int constructor = 0xfd2bb8a; + + + public void readParams(AbsSerializedData stream, boolean exception) { + chat_id = stream.readInt32(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(chat_id); + } + } + + public static class TL_auth_passwordRecovery extends TLObject { + public static int constructor = 0x137948a5; + + public String email_pattern; + + public static TL_auth_passwordRecovery TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + if (TL_auth_passwordRecovery.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_auth_passwordRecovery", constructor)); + } else { + return null; + } + } + TL_auth_passwordRecovery result = new TL_auth_passwordRecovery(); + result.readParams(stream, exception); + return result; + } + + public void readParams(AbsSerializedData stream, boolean exception) { + email_pattern = stream.readString(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeString(email_pattern); + } + } + + public static class TL_decryptedMessageService extends DecryptedMessage { + public static int constructor = 0x73164160; + + + public void readParams(AbsSerializedData stream, boolean exception) { + random_id = stream.readInt64(exception); + action = DecryptedMessageAction.TLdeserialize(stream, stream.readInt32(exception), exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt64(random_id); + action.serializeToStream(stream); + } + } + + public static class TL_decryptedMessage extends DecryptedMessage { + public static int constructor = 0x204d3878; + + + public void readParams(AbsSerializedData stream, boolean exception) { + random_id = stream.readInt64(exception); + ttl = stream.readInt32(exception); + message = stream.readString(exception); + media = DecryptedMessageMedia.TLdeserialize(stream, stream.readInt32(exception), exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt64(random_id); + stream.writeInt32(ttl); + stream.writeString(message); + media.serializeToStream(stream); + } + } + + public static class messages_Messages extends TLObject { + public ArrayList messages = new ArrayList<>(); + public ArrayList chats = new ArrayList<>(); + public ArrayList users = new ArrayList<>(); + public int count; + + public static messages_Messages TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + messages_Messages result = null; + switch(constructor) { + case 0x8c718e87: + result = new TL_messages_messages(); + break; + case 0xb446ae3: + result = new TL_messages_messagesSlice(); + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in messages_Messages", constructor)); + } + if (result != null) { + result.readParams(stream, exception); + } + return result; + } + } + + public static class TL_messages_messages extends messages_Messages { + public static int constructor = 0x8c718e87; + + + public void readParams(AbsSerializedData stream, boolean exception) { + int magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + int count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + messages.add(Message.TLdeserialize(stream, stream.readInt32(exception), exception)); + } + magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + chats.add(Chat.TLdeserialize(stream, stream.readInt32(exception), exception)); + } + magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + users.add(User.TLdeserialize(stream, stream.readInt32(exception), exception)); + } + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(0x1cb5c415); + int count = messages.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + messages.get(a).serializeToStream(stream); + } + stream.writeInt32(0x1cb5c415); + count = chats.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + chats.get(a).serializeToStream(stream); + } + stream.writeInt32(0x1cb5c415); + count = users.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + users.get(a).serializeToStream(stream); + } + } + } + + public static class TL_messages_messagesSlice extends messages_Messages { + public static int constructor = 0xb446ae3; + + + public void readParams(AbsSerializedData stream, boolean exception) { + count = stream.readInt32(exception); + int magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + int count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + messages.add(Message.TLdeserialize(stream, stream.readInt32(exception), exception)); + } + magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + chats.add(Chat.TLdeserialize(stream, stream.readInt32(exception), exception)); + } + magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + users.add(User.TLdeserialize(stream, stream.readInt32(exception), exception)); + } + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(count); + stream.writeInt32(0x1cb5c415); + int count = messages.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + messages.get(a).serializeToStream(stream); + } + stream.writeInt32(0x1cb5c415); + count = chats.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + chats.get(a).serializeToStream(stream); + } + stream.writeInt32(0x1cb5c415); + count = users.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + users.get(a).serializeToStream(stream); + } + } + } + + public static class BadMsgNotification extends TLObject { + public long bad_msg_id; + public int bad_msg_seqno; + public int error_code; + public long new_server_salt; + + public static BadMsgNotification TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + BadMsgNotification result = null; + switch(constructor) { + case 0xa7eff811: + result = new TL_bad_msg_notification(); + break; + case 0xedab447b: + result = new TL_bad_server_salt(); + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in BadMsgNotification", constructor)); + } + if (result != null) { + result.readParams(stream, exception); + } + return result; + } + } + + public static class TL_bad_msg_notification extends BadMsgNotification { + public static int constructor = 0xa7eff811; + + + public void readParams(AbsSerializedData stream, boolean exception) { + bad_msg_id = stream.readInt64(exception); + bad_msg_seqno = stream.readInt32(exception); + error_code = stream.readInt32(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt64(bad_msg_id); + stream.writeInt32(bad_msg_seqno); + stream.writeInt32(error_code); + } + } + + public static class TL_bad_server_salt extends BadMsgNotification { + public static int constructor = 0xedab447b; + + + public void readParams(AbsSerializedData stream, boolean exception) { + bad_msg_id = stream.readInt64(exception); + bad_msg_seqno = stream.readInt32(exception); + error_code = stream.readInt32(exception); + new_server_salt = stream.readInt64(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt64(bad_msg_id); + stream.writeInt32(bad_msg_seqno); + stream.writeInt32(error_code); + stream.writeInt64(new_server_salt); + } + } + + public static class TL_decryptedMessageMediaDocument extends DecryptedMessageMedia { + public static int constructor = 0xb095434b; + + + public void readParams(AbsSerializedData stream, boolean exception) { + thumb = stream.readByteArray(exception); + thumb_w = stream.readInt32(exception); + thumb_h = stream.readInt32(exception); + file_name = stream.readString(exception); + mime_type = stream.readString(exception); + size = stream.readInt32(exception); + key = stream.readByteArray(exception); + iv = stream.readByteArray(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeByteArray(thumb); + stream.writeInt32(thumb_w); + stream.writeInt32(thumb_h); + stream.writeString(file_name); + stream.writeString(mime_type); + stream.writeInt32(size); + stream.writeByteArray(key); + stream.writeByteArray(iv); + } + } + + public static class TL_decryptedMessageMediaGeoPoint extends DecryptedMessageMedia { + public static int constructor = 0x35480a59; + + + public void readParams(AbsSerializedData stream, boolean exception) { + lat = stream.readDouble(exception); + _long = stream.readDouble(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeDouble(lat); + stream.writeDouble(_long); + } + } + + public static class TL_decryptedMessageMediaAudio extends DecryptedMessageMedia { + public static int constructor = 0x57e0a9cb; + + + public void readParams(AbsSerializedData stream, boolean exception) { + duration = stream.readInt32(exception); + mime_type = stream.readString(exception); + size = stream.readInt32(exception); + key = stream.readByteArray(exception); + iv = stream.readByteArray(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(duration); + stream.writeString(mime_type); + stream.writeInt32(size); + stream.writeByteArray(key); + stream.writeByteArray(iv); + } + } + + public static class TL_decryptedMessageMediaVideo extends DecryptedMessageMedia { + public static int constructor = 0x524a415d; + + + public void readParams(AbsSerializedData stream, boolean exception) { + thumb = stream.readByteArray(exception); + thumb_w = stream.readInt32(exception); + thumb_h = stream.readInt32(exception); + duration = stream.readInt32(exception); + mime_type = stream.readString(exception); + w = stream.readInt32(exception); + h = stream.readInt32(exception); + size = stream.readInt32(exception); + key = stream.readByteArray(exception); + iv = stream.readByteArray(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeByteArray(thumb); + stream.writeInt32(thumb_w); + stream.writeInt32(thumb_h); + stream.writeInt32(duration); + stream.writeString(mime_type); + stream.writeInt32(w); + stream.writeInt32(h); + stream.writeInt32(size); + stream.writeByteArray(key); + stream.writeByteArray(iv); + } + } + + public static class TL_decryptedMessageMediaContact extends DecryptedMessageMedia { + public static int constructor = 0x588a0a97; + + + public void readParams(AbsSerializedData stream, boolean exception) { + phone_number = stream.readString(exception); + first_name = stream.readString(exception); + last_name = stream.readString(exception); + user_id = stream.readInt32(exception); } public void serializeToStream(AbsSerializedData stream) { @@ -3315,99 +2440,12 @@ public class TLRPC { stream.writeString(phone_number); stream.writeString(first_name); stream.writeString(last_name); + stream.writeInt32(user_id); } } - public static class TL_inputMediaUploadedThumbDocument extends InputMedia { - public static int constructor = 0x41481486; - - - public void readParams(AbsSerializedData stream) { - file = (InputFile)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - thumb = (InputFile)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - mime_type = stream.readString(); - stream.readInt32(); - int count = stream.readInt32(); - for (int a = 0; a < count; a++) { - attributes.add((DocumentAttribute)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32())); - } - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - file.serializeToStream(stream); - thumb.serializeToStream(stream); - stream.writeString(mime_type); - stream.writeInt32(0x1cb5c415); - int count = attributes.size(); - stream.writeInt32(count); - for (int a = 0; a < count; a++) { - attributes.get(a).serializeToStream(stream); - } - } - } - - public static class TL_inputMediaAudio extends InputMedia { - public static int constructor = 0x89938781; - - public InputAudio id; - - public void readParams(AbsSerializedData stream) { - id = (InputAudio)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - id.serializeToStream(stream); - } - } - - public static class TL_inputMediaDocument extends InputMedia { - public static int constructor = 0xd184e841; - - public InputDocument id; - - public void readParams(AbsSerializedData stream) { - id = (InputDocument)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - id.serializeToStream(stream); - } - } - - public static class TL_inputMediaVideo extends InputMedia { - public static int constructor = 0x7f023ae6; - - public InputVideo id; - - public void readParams(AbsSerializedData stream) { - id = (InputVideo)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - id.serializeToStream(stream); - } - } - - public static class TL_inputMediaGeoPoint extends InputMedia { - public static int constructor = 0xf9c44144; - - - public void readParams(AbsSerializedData stream) { - geo_point = (InputGeoPoint)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - geo_point.serializeToStream(stream); - } - } - - public static class TL_inputMediaEmpty extends InputMedia { - public static int constructor = 0x9664f57f; + public static class TL_decryptedMessageMediaEmpty extends DecryptedMessageMedia { + public static int constructor = 0x89f5c4a; public void serializeToStream(AbsSerializedData stream) { @@ -3415,123 +2453,5226 @@ public class TLRPC { } } - public static class TL_inputMediaUploadedThumbVideo extends InputMedia { - public static int constructor = 0x9912dabf; + public static class TL_decryptedMessageMediaPhoto extends DecryptedMessageMedia { + public static int constructor = 0x32798a8c; - public void readParams(AbsSerializedData stream) { - file = (InputFile)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - thumb = (InputFile)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - duration = stream.readInt32(); - w = stream.readInt32(); - h = stream.readInt32(); - mime_type = stream.readString(); + public void readParams(AbsSerializedData stream, boolean exception) { + thumb = stream.readByteArray(exception); + thumb_w = stream.readInt32(exception); + thumb_h = stream.readInt32(exception); + w = stream.readInt32(exception); + h = stream.readInt32(exception); + size = stream.readInt32(exception); + key = stream.readByteArray(exception); + iv = stream.readByteArray(exception); } public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); - file.serializeToStream(stream); - thumb.serializeToStream(stream); - stream.writeInt32(duration); + stream.writeByteArray(thumb); + stream.writeInt32(thumb_w); + stream.writeInt32(thumb_h); stream.writeInt32(w); stream.writeInt32(h); - stream.writeString(mime_type); + stream.writeInt32(size); + stream.writeByteArray(key); + stream.writeByteArray(iv); } } - public static class TL_inputMediaUploadedPhoto extends InputMedia { - public static int constructor = 0x2dc53a7d; + public static class TL_userContact extends User { + public static int constructor = 0xcab35e18; - public void readParams(AbsSerializedData stream) { - file = (InputFile)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); + public void readParams(AbsSerializedData stream, boolean exception) { + id = stream.readInt32(exception); + first_name = stream.readString(exception); + last_name = stream.readString(exception); + username = stream.readString(exception); + access_hash = stream.readInt64(exception); + phone = stream.readString(exception); + photo = UserProfilePhoto.TLdeserialize(stream, stream.readInt32(exception), exception); + status = UserStatus.TLdeserialize(stream, stream.readInt32(exception), exception); } public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); - file.serializeToStream(stream); + stream.writeInt32(id); + stream.writeString(first_name); + stream.writeString(last_name); + stream.writeString(username); + stream.writeInt64(access_hash); + stream.writeString(phone); + photo.serializeToStream(stream); + status.serializeToStream(stream); } } - public static class TL_inputMediaUploadedAudio extends InputMedia { - public static int constructor = 0x4e498cab; + public static class TL_userRequest extends User { + public static int constructor = 0xd9ccc4ef; - public void readParams(AbsSerializedData stream) { - file = (InputFile)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - duration = stream.readInt32(); - mime_type = stream.readString(); + public void readParams(AbsSerializedData stream, boolean exception) { + id = stream.readInt32(exception); + first_name = stream.readString(exception); + last_name = stream.readString(exception); + username = stream.readString(exception); + access_hash = stream.readInt64(exception); + phone = stream.readString(exception); + photo = UserProfilePhoto.TLdeserialize(stream, stream.readInt32(exception), exception); + status = UserStatus.TLdeserialize(stream, stream.readInt32(exception), exception); } public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); - file.serializeToStream(stream); - stream.writeInt32(duration); - stream.writeString(mime_type); + stream.writeInt32(id); + stream.writeString(first_name); + stream.writeString(last_name); + stream.writeString(username); + stream.writeInt64(access_hash); + stream.writeString(phone); + photo.serializeToStream(stream); + status.serializeToStream(stream); } } - public static class TL_inputMediaUploadedVideo extends InputMedia { - public static int constructor = 0x133ad6f6; + public static class TL_userForeign extends User { + public static int constructor = 0x75cf7a8; - public void readParams(AbsSerializedData stream) { - file = (InputFile)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - duration = stream.readInt32(); - w = stream.readInt32(); - h = stream.readInt32(); - mime_type = stream.readString(); + public void readParams(AbsSerializedData stream, boolean exception) { + id = stream.readInt32(exception); + first_name = stream.readString(exception); + last_name = stream.readString(exception); + username = stream.readString(exception); + access_hash = stream.readInt64(exception); + photo = UserProfilePhoto.TLdeserialize(stream, stream.readInt32(exception), exception); + status = UserStatus.TLdeserialize(stream, stream.readInt32(exception), exception); } public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); - file.serializeToStream(stream); - stream.writeInt32(duration); - stream.writeInt32(w); - stream.writeInt32(h); - stream.writeString(mime_type); + stream.writeInt32(id); + stream.writeString(first_name); + stream.writeString(last_name); + stream.writeString(username); + stream.writeInt64(access_hash); + photo.serializeToStream(stream); + status.serializeToStream(stream); } } - public static class TL_inputMediaUploadedDocument extends InputMedia { - public static int constructor = 0xffe76b78; + public static class TL_userDeleted extends User { + public static int constructor = 0xd6016d7a; - public void readParams(AbsSerializedData stream) { - file = (InputFile)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - mime_type = stream.readString(); - stream.readInt32(); - int count = stream.readInt32(); + public void readParams(AbsSerializedData stream, boolean exception) { + id = stream.readInt32(exception); + first_name = stream.readString(exception); + last_name = stream.readString(exception); + username = stream.readString(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(id); + stream.writeString(first_name); + stream.writeString(last_name); + stream.writeString(username); + } + } + + public static class TL_userSelf extends User { + public static int constructor = 0x1c60e608; + + + public void readParams(AbsSerializedData stream, boolean exception) { + id = stream.readInt32(exception); + first_name = stream.readString(exception); + last_name = stream.readString(exception); + username = stream.readString(exception); + phone = stream.readString(exception); + photo = UserProfilePhoto.TLdeserialize(stream, stream.readInt32(exception), exception); + status = UserStatus.TLdeserialize(stream, stream.readInt32(exception), exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(id); + stream.writeString(first_name); + stream.writeString(last_name); + stream.writeString(username); + stream.writeString(phone); + photo.serializeToStream(stream); + status.serializeToStream(stream); + } + } + + public static class TL_messageMediaVideo extends MessageMedia { + public static int constructor = 0x5bcf1675; + + + public void readParams(AbsSerializedData stream, boolean exception) { + video = Video.TLdeserialize(stream, stream.readInt32(exception), exception); + caption = stream.readString(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + video.serializeToStream(stream); + stream.writeString(caption); + } + } + + public static class TL_messageMediaPhoto extends MessageMedia { + public static int constructor = 0x3d8ce53d; + + + public void readParams(AbsSerializedData stream, boolean exception) { + photo = Photo.TLdeserialize(stream, stream.readInt32(exception), exception); + caption = stream.readString(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + photo.serializeToStream(stream); + stream.writeString(caption); + } + } + + public static class TL_messageMediaWebPage extends MessageMedia { + public static int constructor = 0xa32dd600; + + + public void readParams(AbsSerializedData stream, boolean exception) { + webpage = WebPage.TLdeserialize(stream, stream.readInt32(exception), exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + webpage.serializeToStream(stream); + } + } + + public static class TL_messageMediaDocument extends MessageMedia { + public static int constructor = 0x2fda2204; + + + public void readParams(AbsSerializedData stream, boolean exception) { + document = Document.TLdeserialize(stream, stream.readInt32(exception), exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + document.serializeToStream(stream); + } + } + + public static class TL_messageMediaGeo extends MessageMedia { + public static int constructor = 0x56e0d474; + + + public void readParams(AbsSerializedData stream, boolean exception) { + geo = GeoPoint.TLdeserialize(stream, stream.readInt32(exception), exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + geo.serializeToStream(stream); + } + } + + public static class TL_messageMediaEmpty extends MessageMedia { + public static int constructor = 0x3ded6320; + + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class TL_messageMediaAudio extends MessageMedia { + public static int constructor = 0xc6b68300; + + + public void readParams(AbsSerializedData stream, boolean exception) { + audio = Audio.TLdeserialize(stream, stream.readInt32(exception), exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + audio.serializeToStream(stream); + } + } + + public static class TL_messageMediaVenue extends MessageMedia { + public static int constructor = 0x7912b71f; + + + public void readParams(AbsSerializedData stream, boolean exception) { + geo = GeoPoint.TLdeserialize(stream, stream.readInt32(exception), exception); + title = stream.readString(exception); + address = stream.readString(exception); + provider = stream.readString(exception); + venue_id = stream.readString(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + geo.serializeToStream(stream); + stream.writeString(title); + stream.writeString(address); + stream.writeString(provider); + stream.writeString(venue_id); + } + } + + public static class TL_messageMediaContact extends MessageMedia { + public static int constructor = 0x5e7d2f39; + + + public void readParams(AbsSerializedData stream, boolean exception) { + phone_number = stream.readString(exception); + first_name = stream.readString(exception); + last_name = stream.readString(exception); + user_id = stream.readInt32(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeString(phone_number); + stream.writeString(first_name); + stream.writeString(last_name); + stream.writeInt32(user_id); + } + } + + public static class TL_messageMediaUnsupported extends MessageMedia { + public static int constructor = 0x9f84f49e; + + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class TL_nearestDc extends TLObject { + public static int constructor = 0x8e1a1775; + + public String country; + public int this_dc; + public int nearest_dc; + + public static TL_nearestDc TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + if (TL_nearestDc.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_nearestDc", constructor)); + } else { + return null; + } + } + TL_nearestDc result = new TL_nearestDc(); + result.readParams(stream, exception); + return result; + } + + public void readParams(AbsSerializedData stream, boolean exception) { + country = stream.readString(exception); + this_dc = stream.readInt32(exception); + nearest_dc = stream.readInt32(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeString(country); + stream.writeInt32(this_dc); + stream.writeInt32(nearest_dc); + } + } + + public static class TL_contactFound extends TLObject { + public static int constructor = 0xea879f95; + + public int user_id; + + public static TL_contactFound TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + if (TL_contactFound.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_contactFound", constructor)); + } else { + return null; + } + } + TL_contactFound result = new TL_contactFound(); + result.readParams(stream, exception); + return result; + } + + public void readParams(AbsSerializedData stream, boolean exception) { + user_id = stream.readInt32(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(user_id); + } + } + + public static class TL_new_session_created extends TLObject { + public static int constructor = 0x9ec20908; + + public long first_msg_id; + public long unique_id; + public long server_salt; + + public static TL_new_session_created TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + if (TL_new_session_created.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_new_session_created", constructor)); + } else { + return null; + } + } + TL_new_session_created result = new TL_new_session_created(); + result.readParams(stream, exception); + return result; + } + + public void readParams(AbsSerializedData stream, boolean exception) { + first_msg_id = stream.readInt64(exception); + unique_id = stream.readInt64(exception); + server_salt = stream.readInt64(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt64(first_msg_id); + stream.writeInt64(unique_id); + stream.writeInt64(server_salt); + } + } + + public static class TL_contacts_suggested extends TLObject { + public static int constructor = 0x5649dcc5; + + public ArrayList results = new ArrayList<>(); + public ArrayList users = new ArrayList<>(); + + public static TL_contacts_suggested TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + if (TL_contacts_suggested.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_contacts_suggested", constructor)); + } else { + return null; + } + } + TL_contacts_suggested result = new TL_contacts_suggested(); + result.readParams(stream, exception); + return result; + } + + public void readParams(AbsSerializedData stream, boolean exception) { + int magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + int count = stream.readInt32(exception); for (int a = 0; a < count; a++) { - attributes.add((DocumentAttribute)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32())); + results.add(TL_contactSuggested.TLdeserialize(stream, stream.readInt32(exception), exception)); + } + magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + users.add(User.TLdeserialize(stream, stream.readInt32(exception), exception)); } } public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); - file.serializeToStream(stream); - stream.writeString(mime_type); stream.writeInt32(0x1cb5c415); - int count = attributes.size(); + int count = results.size(); stream.writeInt32(count); for (int a = 0; a < count; a++) { - attributes.get(a).serializeToStream(stream); + results.get(a).serializeToStream(stream); + } + stream.writeInt32(0x1cb5c415); + count = users.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + users.get(a).serializeToStream(stream); } } } - public static class TL_inputMediaPhoto extends InputMedia { - public static int constructor = 0x8f2ab2ec; + public static class WallPaper extends TLObject { + public int id; + public String title; + public ArrayList sizes = new ArrayList<>(); + public int color; + public int bg_color; + public static WallPaper TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + WallPaper result = null; + switch(constructor) { + case 0xccb03657: + result = new TL_wallPaper(); + break; + case 0x63117f24: + result = new TL_wallPaperSolid(); + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in WallPaper", constructor)); + } + if (result != null) { + result.readParams(stream, exception); + } + return result; + } + } + + public static class TL_wallPaper extends WallPaper { + public static int constructor = 0xccb03657; + + + public void readParams(AbsSerializedData stream, boolean exception) { + id = stream.readInt32(exception); + title = stream.readString(exception); + int magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + int count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + sizes.add(PhotoSize.TLdeserialize(stream, stream.readInt32(exception), exception)); + } + color = stream.readInt32(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(id); + stream.writeString(title); + stream.writeInt32(0x1cb5c415); + int count = sizes.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + sizes.get(a).serializeToStream(stream); + } + stream.writeInt32(color); + } + } + + public static class TL_wallPaperSolid extends WallPaper { + public static int constructor = 0x63117f24; + + + public void readParams(AbsSerializedData stream, boolean exception) { + id = stream.readInt32(exception); + title = stream.readString(exception); + bg_color = stream.readInt32(exception); + color = stream.readInt32(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(id); + stream.writeString(title); + stream.writeInt32(bg_color); + stream.writeInt32(color); + } + } + + public static class NotifyPeer extends TLObject { + public Peer peer; + + public static NotifyPeer TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + NotifyPeer result = null; + switch(constructor) { + case 0x74d07c60: + result = new TL_notifyAll(); + break; + case 0xc007cec3: + result = new TL_notifyChats(); + break; + case 0xb4c83b4c: + result = new TL_notifyUsers(); + break; + case 0x9fd40bd8: + result = new TL_notifyPeer(); + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in NotifyPeer", constructor)); + } + if (result != null) { + result.readParams(stream, exception); + } + return result; + } + } + + public static class TL_notifyAll extends NotifyPeer { + public static int constructor = 0x74d07c60; + + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class TL_notifyChats extends NotifyPeer { + public static int constructor = 0xc007cec3; + + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class TL_notifyUsers extends NotifyPeer { + public static int constructor = 0xb4c83b4c; + + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class TL_notifyPeer extends NotifyPeer { + public static int constructor = 0x9fd40bd8; + + + public void readParams(AbsSerializedData stream, boolean exception) { + peer = Peer.TLdeserialize(stream, stream.readInt32(exception), exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + peer.serializeToStream(stream); + } + } + + public static class TL_inputPrivacyKeyStatusTimestamp extends TLObject { + public static int constructor = 0x4f96cb18; + + + public static TL_inputPrivacyKeyStatusTimestamp TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + if (TL_inputPrivacyKeyStatusTimestamp.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_inputPrivacyKeyStatusTimestamp", constructor)); + } else { + return null; + } + } + TL_inputPrivacyKeyStatusTimestamp result = new TL_inputPrivacyKeyStatusTimestamp(); + result.readParams(stream, exception); + return result; + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class TL_account_sentChangePhoneCode extends TLObject { + public static int constructor = 0xa4f58c4c; + + public String phone_code_hash; + public int send_call_timeout; + + public static TL_account_sentChangePhoneCode TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + if (TL_account_sentChangePhoneCode.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_account_sentChangePhoneCode", constructor)); + } else { + return null; + } + } + TL_account_sentChangePhoneCode result = new TL_account_sentChangePhoneCode(); + result.readParams(stream, exception); + return result; + } + + public void readParams(AbsSerializedData stream, boolean exception) { + phone_code_hash = stream.readString(exception); + send_call_timeout = stream.readInt32(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeString(phone_code_hash); + stream.writeInt32(send_call_timeout); + } + } + + public static class ChatInvite extends TLObject { + public String title; + public Chat chat; + + public static ChatInvite TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + ChatInvite result = null; + switch(constructor) { + case 0xce917dcd: + result = new TL_chatInvite(); + break; + case 0x5a686d7c: + result = new TL_chatInviteAlready(); + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in ChatInvite", constructor)); + } + if (result != null) { + result.readParams(stream, exception); + } + return result; + } + } + + public static class TL_chatInvite extends ChatInvite { + public static int constructor = 0xce917dcd; + + + public void readParams(AbsSerializedData stream, boolean exception) { + title = stream.readString(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeString(title); + } + } + + public static class TL_chatInviteAlready extends ChatInvite { + public static int constructor = 0x5a686d7c; + + + public void readParams(AbsSerializedData stream, boolean exception) { + chat = Chat.TLdeserialize(stream, stream.readInt32(exception), exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + chat.serializeToStream(stream); + } + } + + public static class TL_photos_photo extends TLObject { + public static int constructor = 0x20212ca8; + + public Photo photo; + public ArrayList users = new ArrayList<>(); + + public static TL_photos_photo TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + if (TL_photos_photo.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_photos_photo", constructor)); + } else { + return null; + } + } + TL_photos_photo result = new TL_photos_photo(); + result.readParams(stream, exception); + return result; + } + + public void readParams(AbsSerializedData stream, boolean exception) { + photo = Photo.TLdeserialize(stream, stream.readInt32(exception), exception); + int magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + int count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + users.add(User.TLdeserialize(stream, stream.readInt32(exception), exception)); + } + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + photo.serializeToStream(stream); + stream.writeInt32(0x1cb5c415); + int count = users.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + users.get(a).serializeToStream(stream); + } + } + } + + public static class TL_resPQ extends TLObject { + public static int constructor = 0x05162463; + + public byte[] nonce; + public byte[] server_nonce; + public byte[] pq; + public ArrayList server_public_key_fingerprints = new ArrayList<>(); + + public static TL_resPQ TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + if (TL_resPQ.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_resPQ", constructor)); + } else { + return null; + } + } + TL_resPQ result = new TL_resPQ(); + result.readParams(stream, exception); + return result; + } + + public void readParams(AbsSerializedData stream, boolean exception) { + nonce = stream.readData(16, exception); + server_nonce = stream.readData(16, exception); + pq = stream.readByteArray(exception); + int magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + int count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + server_public_key_fingerprints.add(stream.readInt64(exception)); + } + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeRaw(nonce); + stream.writeRaw(server_nonce); + stream.writeByteArray(pq); + stream.writeInt32(0x1cb5c415); + int count = server_public_key_fingerprints.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + stream.writeInt64(server_public_key_fingerprints.get(a)); + } + } + } + + public static class messages_SentEncryptedMessage extends TLObject { + public int date; + public EncryptedFile file; + + public static messages_SentEncryptedMessage TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + messages_SentEncryptedMessage result = null; + switch(constructor) { + case 0x560f8935: + result = new TL_messages_sentEncryptedMessage(); + break; + case 0x9493ff32: + result = new TL_messages_sentEncryptedFile(); + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in messages_SentEncryptedMessage", constructor)); + } + if (result != null) { + result.readParams(stream, exception); + } + return result; + } + } + + public static class TL_messages_sentEncryptedMessage extends messages_SentEncryptedMessage { + public static int constructor = 0x560f8935; + + + public void readParams(AbsSerializedData stream, boolean exception) { + date = stream.readInt32(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(date); + } + } + + public static class TL_messages_sentEncryptedFile extends messages_SentEncryptedMessage { + public static int constructor = 0x9493ff32; + + + public void readParams(AbsSerializedData stream, boolean exception) { + date = stream.readInt32(exception); + file = EncryptedFile.TLdeserialize(stream, stream.readInt32(exception), exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(date); + file.serializeToStream(stream); + } + } + + public static class Update extends TLObject { + public int chat_id; + public int max_date; + public int date; + public int user_id; + public ContactLink my_link; + public ContactLink foreign_link; + public int max_id; + public int pts; + public int pts_count; + public int version; + public WebPage webpage; + public String type; + public MessageMedia media; + public boolean popup; + public PeerNotifySettings notify_settings; + public SendMessageAction action; + public String first_name; + public String last_name; + public String username; + public int qts; + public int id; + public long random_id; + public ArrayList dc_options = new ArrayList<>(); + public ArrayList messages = new ArrayList<>(); + public ChatParticipants participants; + public TL_privacyKeyStatusTimestamp key; + public ArrayList rules = new ArrayList<>(); + public EncryptedChat chat; + public boolean blocked; + public String phone; + public long auth_key_id; + public String device; + public String location; + public UserProfilePhoto photo; + public boolean previous; + public int inviter_id; + public UserStatus status; + public NotifyPeer peer; + + public static Update TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + Update result = null; + switch(constructor) { + case 0x38fe25b7: + result = new TL_updateEncryptedMessagesRead(); + break; + case 0x9d2e67c5: + result = new TL_updateContactLink(); + break; + case 0x9961fd5c: + result = new TL_updateReadHistoryInbox(); + break; + case 0x2f2f21bf: + result = new TL_updateReadHistoryOutbox(); + break; + case 0x6e5f8c22: + result = new TL_updateChatParticipantDelete(); + break; + case 0x2cc36971: + result = new TL_updateWebPage(); + break; + case 0x382dd3e4: + result = new TL_updateServiceNotification(); + break; + case 0xbec268ef: + result = new TL_updateNotifySettings(); + break; + case 0x5c486927: + result = new TL_updateUserTyping(); + break; + case 0x9a65ea1f: + result = new TL_updateChatUserTyping(); + break; + case 0xa7332b73: + result = new TL_updateUserName(); + break; + case 0x12bcbd9a: + result = new TL_updateNewEncryptedMessage(); + break; + case 0x1f2b0afd: + result = new TL_updateNewMessage(); + break; + case 0x4e90bfd6: + result = new TL_updateMessageID(); + break; + case 0x8e5e9873: + result = new TL_updateDcOptions(); + break; + case 0x1710f156: + result = new TL_updateEncryptedChatTyping(); + break; + case 0xa20db0e5: + result = new TL_updateDeleteMessages(); + break; + case 0x68c13933: + result = new TL_updateReadMessagesContents(); + break; + case 0x7761198: + result = new TL_updateChatParticipants(); + break; + case 0xee3b272a: + result = new TL_updatePrivacy(); + break; + case 0xb4a2e88d: + result = new TL_updateEncryption(); + break; + case 0x80ece81a: + result = new TL_updateUserBlocked(); + break; + case 0x12b9417b: + result = new TL_updateUserPhone(); + break; + case 0x8f06529a: + result = new TL_updateNewAuthorization(); + break; + case 0x5a68e3f7: + result = new TL_updateNewGeoChatMessage(); + break; + case 0x95313b0c: + result = new TL_updateUserPhoto(); + break; + case 0x2575bbb9: + result = new TL_updateContactRegistered(); + break; + case 0x3a0eeb22: + result = new TL_updateChatParticipantAdd(); + break; + case 0x1bfbd823: + result = new TL_updateUserStatus(); + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in Update", constructor)); + } + if (result != null) { + result.readParams(stream, exception); + } + return result; + } + } + + public static class TL_updateEncryptedMessagesRead extends Update { + public static int constructor = 0x38fe25b7; + + + public void readParams(AbsSerializedData stream, boolean exception) { + chat_id = stream.readInt32(exception); + max_date = stream.readInt32(exception); + date = stream.readInt32(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(chat_id); + stream.writeInt32(max_date); + stream.writeInt32(date); + } + } + + public static class TL_updateContactLink extends Update { + public static int constructor = 0x9d2e67c5; + + + public void readParams(AbsSerializedData stream, boolean exception) { + user_id = stream.readInt32(exception); + my_link = ContactLink.TLdeserialize(stream, stream.readInt32(exception), exception); + foreign_link = ContactLink.TLdeserialize(stream, stream.readInt32(exception), exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(user_id); + my_link.serializeToStream(stream); + foreign_link.serializeToStream(stream); + } + } + + public static class TL_updateReadHistoryInbox extends Update { + public static int constructor = 0x9961fd5c; + + public Peer peer; + + public void readParams(AbsSerializedData stream, boolean exception) { + peer = Peer.TLdeserialize(stream, stream.readInt32(exception), exception); + max_id = stream.readInt32(exception); + pts = stream.readInt32(exception); + pts_count = stream.readInt32(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + peer.serializeToStream(stream); + stream.writeInt32(max_id); + stream.writeInt32(pts); + stream.writeInt32(pts_count); + } + } + + public static class TL_updateReadHistoryOutbox extends Update { + public static int constructor = 0x2f2f21bf; + + public Peer peer; + + public void readParams(AbsSerializedData stream, boolean exception) { + peer = Peer.TLdeserialize(stream, stream.readInt32(exception), exception); + max_id = stream.readInt32(exception); + pts = stream.readInt32(exception); + pts_count = stream.readInt32(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + peer.serializeToStream(stream); + stream.writeInt32(max_id); + stream.writeInt32(pts); + stream.writeInt32(pts_count); + } + } + + public static class TL_updateChatParticipantDelete extends Update { + public static int constructor = 0x6e5f8c22; + + + public void readParams(AbsSerializedData stream, boolean exception) { + chat_id = stream.readInt32(exception); + user_id = stream.readInt32(exception); + version = stream.readInt32(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(chat_id); + stream.writeInt32(user_id); + stream.writeInt32(version); + } + } + + public static class TL_updateWebPage extends Update { + public static int constructor = 0x2cc36971; + + + public void readParams(AbsSerializedData stream, boolean exception) { + webpage = WebPage.TLdeserialize(stream, stream.readInt32(exception), exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + webpage.serializeToStream(stream); + } + } + + public static class TL_updateServiceNotification extends Update { + public static int constructor = 0x382dd3e4; + + public String message; + + public void readParams(AbsSerializedData stream, boolean exception) { + type = stream.readString(exception); + message = stream.readString(exception); + media = MessageMedia.TLdeserialize(stream, stream.readInt32(exception), exception); + popup = stream.readBool(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeString(type); + stream.writeString(message); + media.serializeToStream(stream); + stream.writeBool(popup); + } + } + + public static class TL_updateNotifySettings extends Update { + public static int constructor = 0xbec268ef; + + public void readParams(AbsSerializedData stream, boolean exception) { + peer = NotifyPeer.TLdeserialize(stream, stream.readInt32(exception), exception); + notify_settings = PeerNotifySettings.TLdeserialize(stream, stream.readInt32(exception), exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + peer.serializeToStream(stream); + notify_settings.serializeToStream(stream); + } + } + + public static class TL_updateUserTyping extends Update { + public static int constructor = 0x5c486927; + + + public void readParams(AbsSerializedData stream, boolean exception) { + user_id = stream.readInt32(exception); + action = SendMessageAction.TLdeserialize(stream, stream.readInt32(exception), exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(user_id); + action.serializeToStream(stream); + } + } + + public static class TL_updateChatUserTyping extends Update { + public static int constructor = 0x9a65ea1f; + + + public void readParams(AbsSerializedData stream, boolean exception) { + chat_id = stream.readInt32(exception); + user_id = stream.readInt32(exception); + action = SendMessageAction.TLdeserialize(stream, stream.readInt32(exception), exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(chat_id); + stream.writeInt32(user_id); + action.serializeToStream(stream); + } + } + + public static class TL_updateUserName extends Update { + public static int constructor = 0xa7332b73; + + + public void readParams(AbsSerializedData stream, boolean exception) { + user_id = stream.readInt32(exception); + first_name = stream.readString(exception); + last_name = stream.readString(exception); + username = stream.readString(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(user_id); + stream.writeString(first_name); + stream.writeString(last_name); + stream.writeString(username); + } + } + + public static class TL_updateNewEncryptedMessage extends Update { + public static int constructor = 0x12bcbd9a; + + public EncryptedMessage message; + + public void readParams(AbsSerializedData stream, boolean exception) { + message = EncryptedMessage.TLdeserialize(stream, stream.readInt32(exception), exception); + qts = stream.readInt32(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + message.serializeToStream(stream); + stream.writeInt32(qts); + } + } + + public static class TL_updateNewMessage extends Update { + public static int constructor = 0x1f2b0afd; + + public Message message; + + public void readParams(AbsSerializedData stream, boolean exception) { + message = Message.TLdeserialize(stream, stream.readInt32(exception), exception); + pts = stream.readInt32(exception); + pts_count = stream.readInt32(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + message.serializeToStream(stream); + stream.writeInt32(pts); + stream.writeInt32(pts_count); + } + } + + public static class TL_updateMessageID extends Update { + public static int constructor = 0x4e90bfd6; + + + public void readParams(AbsSerializedData stream, boolean exception) { + id = stream.readInt32(exception); + random_id = stream.readInt64(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(id); + stream.writeInt64(random_id); + } + } + + public static class TL_updateDcOptions extends Update { + public static int constructor = 0x8e5e9873; + + + public void readParams(AbsSerializedData stream, boolean exception) { + int magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + int count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + dc_options.add(TL_dcOption.TLdeserialize(stream, stream.readInt32(exception), exception)); + } + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(0x1cb5c415); + int count = dc_options.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + dc_options.get(a).serializeToStream(stream); + } + } + } + + public static class TL_updateEncryptedChatTyping extends Update { + public static int constructor = 0x1710f156; + + + public void readParams(AbsSerializedData stream, boolean exception) { + chat_id = stream.readInt32(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(chat_id); + } + } + + public static class TL_updateDeleteMessages extends Update { + public static int constructor = 0xa20db0e5; + + + public void readParams(AbsSerializedData stream, boolean exception) { + int magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + int count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + messages.add(stream.readInt32(exception)); + } + pts = stream.readInt32(exception); + pts_count = stream.readInt32(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(0x1cb5c415); + int count = messages.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + stream.writeInt32(messages.get(a)); + } + stream.writeInt32(pts); + stream.writeInt32(pts_count); + } + } + + public static class TL_updateReadMessagesContents extends Update { + public static int constructor = 0x68c13933; + + + public void readParams(AbsSerializedData stream, boolean exception) { + int magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + int count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + messages.add(stream.readInt32(exception)); + } + pts = stream.readInt32(exception); + pts_count = stream.readInt32(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(0x1cb5c415); + int count = messages.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + stream.writeInt32(messages.get(a)); + } + stream.writeInt32(pts); + stream.writeInt32(pts_count); + } + } + + public static class TL_updateChatParticipants extends Update { + public static int constructor = 0x7761198; + + + public void readParams(AbsSerializedData stream, boolean exception) { + participants = ChatParticipants.TLdeserialize(stream, stream.readInt32(exception), exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + participants.serializeToStream(stream); + } + } + + public static class TL_updatePrivacy extends Update { + public static int constructor = 0xee3b272a; + + + public void readParams(AbsSerializedData stream, boolean exception) { + key = TL_privacyKeyStatusTimestamp.TLdeserialize(stream, stream.readInt32(exception), exception); + int magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + int count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + rules.add(PrivacyRule.TLdeserialize(stream, stream.readInt32(exception), exception)); + } + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + key.serializeToStream(stream); + stream.writeInt32(0x1cb5c415); + int count = rules.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + rules.get(a).serializeToStream(stream); + } + } + } + + public static class TL_updateEncryption extends Update { + public static int constructor = 0xb4a2e88d; + + + public void readParams(AbsSerializedData stream, boolean exception) { + chat = EncryptedChat.TLdeserialize(stream, stream.readInt32(exception), exception); + date = stream.readInt32(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + chat.serializeToStream(stream); + stream.writeInt32(date); + } + } + + public static class TL_updateUserBlocked extends Update { + public static int constructor = 0x80ece81a; + + + public void readParams(AbsSerializedData stream, boolean exception) { + user_id = stream.readInt32(exception); + blocked = stream.readBool(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(user_id); + stream.writeBool(blocked); + } + } + + public static class TL_updateUserPhone extends Update { + public static int constructor = 0x12b9417b; + + + public void readParams(AbsSerializedData stream, boolean exception) { + user_id = stream.readInt32(exception); + phone = stream.readString(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(user_id); + stream.writeString(phone); + } + } + + public static class TL_updateNewAuthorization extends Update { + public static int constructor = 0x8f06529a; + + + public void readParams(AbsSerializedData stream, boolean exception) { + auth_key_id = stream.readInt64(exception); + date = stream.readInt32(exception); + device = stream.readString(exception); + location = stream.readString(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt64(auth_key_id); + stream.writeInt32(date); + stream.writeString(device); + stream.writeString(location); + } + } + + public static class TL_updateNewGeoChatMessage extends Update { + public static int constructor = 0x5a68e3f7; + + public GeoChatMessage message; + + public void readParams(AbsSerializedData stream, boolean exception) { + message = GeoChatMessage.TLdeserialize(stream, stream.readInt32(exception), exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + message.serializeToStream(stream); + } + } + + public static class TL_updateUserPhoto extends Update { + public static int constructor = 0x95313b0c; + + + public void readParams(AbsSerializedData stream, boolean exception) { + user_id = stream.readInt32(exception); + date = stream.readInt32(exception); + photo = UserProfilePhoto.TLdeserialize(stream, stream.readInt32(exception), exception); + previous = stream.readBool(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(user_id); + stream.writeInt32(date); + photo.serializeToStream(stream); + stream.writeBool(previous); + } + } + + public static class TL_updateContactRegistered extends Update { + public static int constructor = 0x2575bbb9; + + + public void readParams(AbsSerializedData stream, boolean exception) { + user_id = stream.readInt32(exception); + date = stream.readInt32(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(user_id); + stream.writeInt32(date); + } + } + + public static class TL_updateChatParticipantAdd extends Update { + public static int constructor = 0x3a0eeb22; + + + public void readParams(AbsSerializedData stream, boolean exception) { + chat_id = stream.readInt32(exception); + user_id = stream.readInt32(exception); + inviter_id = stream.readInt32(exception); + version = stream.readInt32(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(chat_id); + stream.writeInt32(user_id); + stream.writeInt32(inviter_id); + stream.writeInt32(version); + } + } + + public static class TL_updateUserStatus extends Update { + public static int constructor = 0x1bfbd823; + + + public void readParams(AbsSerializedData stream, boolean exception) { + user_id = stream.readInt32(exception); + status = UserStatus.TLdeserialize(stream, stream.readInt32(exception), exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(user_id); + status.serializeToStream(stream); + } + } + + public static class TL_config extends TLObject { + public static int constructor = 0x4e32b894; + + public int date; + public int expires; + public boolean test_mode; + public int this_dc; + public ArrayList dc_options = new ArrayList<>(); + public int chat_size_max; + public int broadcast_size_max; + public int forwarded_count_max; + public int online_update_period_ms; + public int offline_blur_timeout_ms; + public int offline_idle_timeout_ms; + public int online_cloud_timeout_ms; + public int notify_cloud_delay_ms; + public int notify_default_delay_ms; + public int chat_big_size; + public int push_chat_period_ms; + public int push_chat_limit; + public ArrayList disabled_features = new ArrayList<>(); + + public static TL_config TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + if (TL_config.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_config", constructor)); + } else { + return null; + } + } + TL_config result = new TL_config(); + result.readParams(stream, exception); + return result; + } + + public void readParams(AbsSerializedData stream, boolean exception) { + date = stream.readInt32(exception); + expires = stream.readInt32(exception); + test_mode = stream.readBool(exception); + this_dc = stream.readInt32(exception); + int magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + int count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + dc_options.add(TL_dcOption.TLdeserialize(stream, stream.readInt32(exception), exception)); + } + chat_size_max = stream.readInt32(exception); + broadcast_size_max = stream.readInt32(exception); + forwarded_count_max = stream.readInt32(exception); + online_update_period_ms = stream.readInt32(exception); + offline_blur_timeout_ms = stream.readInt32(exception); + offline_idle_timeout_ms = stream.readInt32(exception); + online_cloud_timeout_ms = stream.readInt32(exception); + notify_cloud_delay_ms = stream.readInt32(exception); + notify_default_delay_ms = stream.readInt32(exception); + chat_big_size = stream.readInt32(exception); + push_chat_period_ms = stream.readInt32(exception); + push_chat_limit = stream.readInt32(exception); + magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + disabled_features.add(TL_disabledFeature.TLdeserialize(stream, stream.readInt32(exception), exception)); + } + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(date); + stream.writeInt32(expires); + stream.writeBool(test_mode); + stream.writeInt32(this_dc); + stream.writeInt32(0x1cb5c415); + int count = dc_options.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + dc_options.get(a).serializeToStream(stream); + } + stream.writeInt32(chat_size_max); + stream.writeInt32(broadcast_size_max); + stream.writeInt32(forwarded_count_max); + stream.writeInt32(online_update_period_ms); + stream.writeInt32(offline_blur_timeout_ms); + stream.writeInt32(offline_idle_timeout_ms); + stream.writeInt32(online_cloud_timeout_ms); + stream.writeInt32(notify_cloud_delay_ms); + stream.writeInt32(notify_default_delay_ms); + stream.writeInt32(chat_big_size); + stream.writeInt32(push_chat_period_ms); + stream.writeInt32(push_chat_limit); + stream.writeInt32(0x1cb5c415); + count = disabled_features.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + disabled_features.get(a).serializeToStream(stream); + } + } + } + + public static class InputAudio extends TLObject { + public long id; + public long access_hash; + + public static InputAudio TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + InputAudio result = null; + switch(constructor) { + case 0x77d440ff: + result = new TL_inputAudio(); + break; + case 0xd95adc84: + result = new TL_inputAudioEmpty(); + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in InputAudio", constructor)); + } + if (result != null) { + result.readParams(stream, exception); + } + return result; + } + } + + public static class TL_inputAudio extends InputAudio { + public static int constructor = 0x77d440ff; + + + public void readParams(AbsSerializedData stream, boolean exception) { + id = stream.readInt64(exception); + access_hash = stream.readInt64(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt64(id); + stream.writeInt64(access_hash); + } + } + + public static class TL_inputAudioEmpty extends InputAudio { + public static int constructor = 0xd95adc84; + + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class PeerNotifySettings extends TLObject { + public int mute_until; + public String sound; + public boolean show_previews; + public int events_mask; + + public static PeerNotifySettings TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + PeerNotifySettings result = null; + switch(constructor) { + case 0x70a68512: + result = new TL_peerNotifySettingsEmpty(); + break; + case 0x8d5e11ee: + result = new TL_peerNotifySettings(); + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in PeerNotifySettings", constructor)); + } + if (result != null) { + result.readParams(stream, exception); + } + return result; + } + } + + public static class TL_peerNotifySettingsEmpty extends PeerNotifySettings { + public static int constructor = 0x70a68512; + + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class TL_peerNotifySettings extends PeerNotifySettings { + public static int constructor = 0x8d5e11ee; + + + public void readParams(AbsSerializedData stream, boolean exception) { + mute_until = stream.readInt32(exception); + sound = stream.readString(exception); + show_previews = stream.readBool(exception); + events_mask = stream.readInt32(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(mute_until); + stream.writeString(sound); + stream.writeBool(show_previews); + stream.writeInt32(events_mask); + } + } + + public static class GeoChatMessage extends TLObject { + public int chat_id; + public int id; + public int from_id; + public int date; + public String message; + public MessageMedia media; + public MessageAction action; + + public static GeoChatMessage TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + GeoChatMessage result = null; + switch(constructor) { + case 0x4505f8e1: + result = new TL_geoChatMessage(); + break; + case 0xd34fa24e: + result = new TL_geoChatMessageService(); + break; + case 0x60311a9b: + result = new TL_geoChatMessageEmpty(); + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in GeoChatMessage", constructor)); + } + if (result != null) { + result.readParams(stream, exception); + } + return result; + } + } + + public static class TL_geoChatMessage extends GeoChatMessage { + public static int constructor = 0x4505f8e1; + + + public void readParams(AbsSerializedData stream, boolean exception) { + chat_id = stream.readInt32(exception); + id = stream.readInt32(exception); + from_id = stream.readInt32(exception); + date = stream.readInt32(exception); + message = stream.readString(exception); + media = MessageMedia.TLdeserialize(stream, stream.readInt32(exception), exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(chat_id); + stream.writeInt32(id); + stream.writeInt32(from_id); + stream.writeInt32(date); + stream.writeString(message); + media.serializeToStream(stream); + } + } + + public static class TL_geoChatMessageService extends GeoChatMessage { + public static int constructor = 0xd34fa24e; + + + public void readParams(AbsSerializedData stream, boolean exception) { + chat_id = stream.readInt32(exception); + id = stream.readInt32(exception); + from_id = stream.readInt32(exception); + date = stream.readInt32(exception); + action = MessageAction.TLdeserialize(stream, stream.readInt32(exception), exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(chat_id); + stream.writeInt32(id); + stream.writeInt32(from_id); + stream.writeInt32(date); + action.serializeToStream(stream); + } + } + + public static class TL_geoChatMessageEmpty extends GeoChatMessage { + public static int constructor = 0x60311a9b; + + + public void readParams(AbsSerializedData stream, boolean exception) { + chat_id = stream.readInt32(exception); + id = stream.readInt32(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(chat_id); + stream.writeInt32(id); + } + } + + public static class messages_SentMessage extends TLObject { + public int id; + public int date; + public MessageMedia media; + public int pts; + public int pts_count; + public ArrayList links = new ArrayList<>(); + public int seq; + + public static messages_SentMessage TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + messages_SentMessage result = null; + switch(constructor) { + case 0x4c3d47f3: + result = new TL_messages_sentMessage(); + break; + case 0x35a1a663: + result = new TL_messages_sentMessageLink(); + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in messages_SentMessage", constructor)); + } + if (result != null) { + result.readParams(stream, exception); + } + return result; + } + } + + public static class TL_messages_sentMessage extends messages_SentMessage { + public static int constructor = 0x4c3d47f3; + + + public void readParams(AbsSerializedData stream, boolean exception) { + id = stream.readInt32(exception); + date = stream.readInt32(exception); + media = MessageMedia.TLdeserialize(stream, stream.readInt32(exception), exception); + pts = stream.readInt32(exception); + pts_count = stream.readInt32(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(id); + stream.writeInt32(date); + media.serializeToStream(stream); + stream.writeInt32(pts); + stream.writeInt32(pts_count); + } + } + + public static class TL_messages_sentMessageLink extends messages_SentMessage { + public static int constructor = 0x35a1a663; + + + public void readParams(AbsSerializedData stream, boolean exception) { + id = stream.readInt32(exception); + date = stream.readInt32(exception); + media = MessageMedia.TLdeserialize(stream, stream.readInt32(exception), exception); + pts = stream.readInt32(exception); + pts_count = stream.readInt32(exception); + int magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + int count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + links.add(TL_contacts_link.TLdeserialize(stream, stream.readInt32(exception), exception)); + } + seq = stream.readInt32(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(id); + stream.writeInt32(date); + media.serializeToStream(stream); + stream.writeInt32(pts); + stream.writeInt32(pts_count); + stream.writeInt32(0x1cb5c415); + int count = links.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + links.get(a).serializeToStream(stream); + } + stream.writeInt32(seq); + } + } + + public static class TL_contactSuggested extends TLObject { + public static int constructor = 0x3de191a1; + + public int user_id; + public int mutual_contacts; + + public static TL_contactSuggested TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + if (TL_contactSuggested.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_contactSuggested", constructor)); + } else { + return null; + } + } + TL_contactSuggested result = new TL_contactSuggested(); + result.readParams(stream, exception); + return result; + } + + public void readParams(AbsSerializedData stream, boolean exception) { + user_id = stream.readInt32(exception); + mutual_contacts = stream.readInt32(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(user_id); + stream.writeInt32(mutual_contacts); + } + } + + public static class InputChatPhoto extends TLObject { public InputPhoto id; + public InputPhotoCrop crop; + public InputFile file; - public void readParams(AbsSerializedData stream) { - id = (InputPhoto)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); + public static InputChatPhoto TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + InputChatPhoto result = null; + switch(constructor) { + case 0xb2e1bf08: + result = new TL_inputChatPhoto(); + break; + case 0x1ca48f57: + result = new TL_inputChatPhotoEmpty(); + break; + case 0x94254732: + result = new TL_inputChatUploadedPhoto(); + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in InputChatPhoto", constructor)); + } + if (result != null) { + result.readParams(stream, exception); + } + return result; + } + } + + public static class TL_inputChatPhoto extends InputChatPhoto { + public static int constructor = 0xb2e1bf08; + + + public void readParams(AbsSerializedData stream, boolean exception) { + id = InputPhoto.TLdeserialize(stream, stream.readInt32(exception), exception); + crop = InputPhotoCrop.TLdeserialize(stream, stream.readInt32(exception), exception); } public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); id.serializeToStream(stream); + crop.serializeToStream(stream); + } + } + + public static class TL_inputChatPhotoEmpty extends InputChatPhoto { + public static int constructor = 0x1ca48f57; + + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class TL_inputChatUploadedPhoto extends InputChatPhoto { + public static int constructor = 0x94254732; + + + public void readParams(AbsSerializedData stream, boolean exception) { + file = InputFile.TLdeserialize(stream, stream.readInt32(exception), exception); + crop = InputPhotoCrop.TLdeserialize(stream, stream.readInt32(exception), exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + file.serializeToStream(stream); + crop.serializeToStream(stream); + } + } + + public static class InputPeer extends TLObject { + public int user_id; + public int chat_id; + public long access_hash; + + public static InputPeer TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + InputPeer result = null; + switch(constructor) { + case 0x1023dbe8: + result = new TL_inputPeerContact(); + break; + case 0x179be863: + result = new TL_inputPeerChat(); + break; + case 0x7f3b18ea: + result = new TL_inputPeerEmpty(); + break; + case 0x7da07ec9: + result = new TL_inputPeerSelf(); + break; + case 0x9b447325: + result = new TL_inputPeerForeign(); + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in InputPeer", constructor)); + } + if (result != null) { + result.readParams(stream, exception); + } + return result; + } + } + + public static class TL_inputPeerContact extends InputPeer { + public static int constructor = 0x1023dbe8; + + + public void readParams(AbsSerializedData stream, boolean exception) { + user_id = stream.readInt32(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(user_id); + } + } + + public static class TL_inputPeerChat extends InputPeer { + public static int constructor = 0x179be863; + + + public void readParams(AbsSerializedData stream, boolean exception) { + chat_id = stream.readInt32(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(chat_id); + } + } + + public static class TL_inputPeerEmpty extends InputPeer { + public static int constructor = 0x7f3b18ea; + + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class TL_inputPeerSelf extends InputPeer { + public static int constructor = 0x7da07ec9; + + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class TL_inputPeerForeign extends InputPeer { + public static int constructor = 0x9b447325; + + + public void readParams(AbsSerializedData stream, boolean exception) { + user_id = stream.readInt32(exception); + access_hash = stream.readInt64(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(user_id); + stream.writeInt64(access_hash); + } + } + + public static class TL_msg_copy extends TLObject { + public static int constructor = 0xe06046b2; + + public TL_protoMessage orig_message; + + public static TL_msg_copy TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + if (TL_msg_copy.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_msg_copy", constructor)); + } else { + return null; + } + } + TL_msg_copy result = new TL_msg_copy(); + result.readParams(stream, exception); + return result; + } + + public void readParams(AbsSerializedData stream, boolean exception) { + orig_message = TL_protoMessage.TLdeserialize(stream, stream.readInt32(exception), exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + orig_message.serializeToStream(stream); + } + } + + public static class TL_fileLocation extends FileLocation { + public static int constructor = 0x53d69076; + + + public void readParams(AbsSerializedData stream, boolean exception) { + dc_id = stream.readInt32(exception); + volume_id = stream.readInt64(exception); + local_id = stream.readInt32(exception); + secret = stream.readInt64(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(dc_id); + stream.writeInt64(volume_id); + stream.writeInt32(local_id); + stream.writeInt64(secret); + } + } + + public static class TL_fileLocationUnavailable extends FileLocation { + public static int constructor = 0x7c596b46; + + + public void readParams(AbsSerializedData stream, boolean exception) { + volume_id = stream.readInt64(exception); + local_id = stream.readInt32(exception); + secret = stream.readInt64(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt64(volume_id); + stream.writeInt32(local_id); + stream.writeInt64(secret); + } + } + + public static class TL_pong extends TLObject { + public static int constructor = 0x347773c5; + + public long msg_id; + public long ping_id; + + public static TL_pong TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + if (TL_pong.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_pong", constructor)); + } else { + return null; + } + } + TL_pong result = new TL_pong(); + result.readParams(stream, exception); + return result; + } + + public void readParams(AbsSerializedData stream, boolean exception) { + msg_id = stream.readInt64(exception); + ping_id = stream.readInt64(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt64(msg_id); + stream.writeInt64(ping_id); + } + } + + public static class TL_inputAppEvent extends TLObject { + public static int constructor = 0x770656a8; + + public double time; + public String type; + public long peer; + public String data; + + public static TL_inputAppEvent TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + if (TL_inputAppEvent.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_inputAppEvent", constructor)); + } else { + return null; + } + } + TL_inputAppEvent result = new TL_inputAppEvent(); + result.readParams(stream, exception); + return result; + } + + public void readParams(AbsSerializedData stream, boolean exception) { + time = stream.readDouble(exception); + type = stream.readString(exception); + peer = stream.readInt64(exception); + data = stream.readString(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeDouble(time); + stream.writeString(type); + stream.writeInt64(peer); + stream.writeString(data); + } + } + + public static class TL_messages_chatFull extends TLObject { + public static int constructor = 0xe5d7d19c; + + public TL_chatFull full_chat; + public ArrayList chats = new ArrayList<>(); + public ArrayList users = new ArrayList<>(); + + public static TL_messages_chatFull TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + if (TL_messages_chatFull.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_messages_chatFull", constructor)); + } else { + return null; + } + } + TL_messages_chatFull result = new TL_messages_chatFull(); + result.readParams(stream, exception); + return result; + } + + public void readParams(AbsSerializedData stream, boolean exception) { + full_chat = TL_chatFull.TLdeserialize(stream, stream.readInt32(exception), exception); + int magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + int count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + chats.add(Chat.TLdeserialize(stream, stream.readInt32(exception), exception)); + } + magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + users.add(User.TLdeserialize(stream, stream.readInt32(exception), exception)); + } + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + full_chat.serializeToStream(stream); + stream.writeInt32(0x1cb5c415); + int count = chats.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + chats.get(a).serializeToStream(stream); + } + stream.writeInt32(0x1cb5c415); + count = users.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + users.get(a).serializeToStream(stream); + } + } + } + + public static class InputNotifyPeer extends TLObject { + + public static InputNotifyPeer TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + InputNotifyPeer result = null; + switch(constructor) { + case 0x4a95e84e: + result = new TL_inputNotifyChats(); + break; + case 0xb8bc5b0c: + result = new TL_inputNotifyPeer(); + break; + case 0x193b4417: + result = new TL_inputNotifyUsers(); + break; + case 0x4d8ddec8: + result = new TL_inputNotifyGeoChatPeer(); + break; + case 0xa429b886: + result = new TL_inputNotifyAll(); + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in InputNotifyPeer", constructor)); + } + if (result != null) { + result.readParams(stream, exception); + } + return result; + } + } + + public static class TL_inputNotifyChats extends InputNotifyPeer { + public static int constructor = 0x4a95e84e; + + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class TL_inputNotifyPeer extends InputNotifyPeer { + public static int constructor = 0xb8bc5b0c; + + public InputPeer peer; + + public void readParams(AbsSerializedData stream, boolean exception) { + peer = InputPeer.TLdeserialize(stream, stream.readInt32(exception), exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + peer.serializeToStream(stream); + } + } + + public static class TL_inputNotifyUsers extends InputNotifyPeer { + public static int constructor = 0x193b4417; + + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class TL_inputNotifyGeoChatPeer extends InputNotifyPeer { + public static int constructor = 0x4d8ddec8; + + public TL_inputGeoChat peer; + + public void readParams(AbsSerializedData stream, boolean exception) { + peer = TL_inputGeoChat.TLdeserialize(stream, stream.readInt32(exception), exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + peer.serializeToStream(stream); + } + } + + public static class TL_inputNotifyAll extends InputNotifyPeer { + public static int constructor = 0xa429b886; + + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class TL_messages_affectedHistory extends TLObject { + public static int constructor = 0xb45c69d1; + + public int pts; + public int pts_count; + public int offset; + + public static TL_messages_affectedHistory TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + if (TL_messages_affectedHistory.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_messages_affectedHistory", constructor)); + } else { + return null; + } + } + TL_messages_affectedHistory result = new TL_messages_affectedHistory(); + result.readParams(stream, exception); + return result; + } + + public void readParams(AbsSerializedData stream, boolean exception) { + pts = stream.readInt32(exception); + pts_count = stream.readInt32(exception); + offset = stream.readInt32(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(pts); + stream.writeInt32(pts_count); + stream.writeInt32(offset); + } + } + + public static class RpcDropAnswer extends TLObject { + public long msg_id; + public int seq_no; + public int bytes; + + public static RpcDropAnswer TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + RpcDropAnswer result = null; + switch(constructor) { + case 0x5e2ad36e: + result = new TL_rpc_answer_unknown(); + break; + case 0xa43ad8b7: + result = new TL_rpc_answer_dropped(); + break; + case 0xcd78e586: + result = new TL_rpc_answer_dropped_running(); + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in RpcDropAnswer", constructor)); + } + if (result != null) { + result.readParams(stream, exception); + } + return result; + } + } + + public static class TL_rpc_answer_unknown extends RpcDropAnswer { + public static int constructor = 0x5e2ad36e; + + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class TL_rpc_answer_dropped extends RpcDropAnswer { + public static int constructor = 0xa43ad8b7; + + + public void readParams(AbsSerializedData stream, boolean exception) { + msg_id = stream.readInt64(exception); + seq_no = stream.readInt32(exception); + bytes = stream.readInt32(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt64(msg_id); + stream.writeInt32(seq_no); + stream.writeInt32(bytes); + } + } + + public static class TL_rpc_answer_dropped_running extends RpcDropAnswer { + public static int constructor = 0xcd78e586; + + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class InputVideo extends TLObject { + public long id; + public long access_hash; + + public static InputVideo TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + InputVideo result = null; + switch(constructor) { + case 0x5508ec75: + result = new TL_inputVideoEmpty(); + break; + case 0xee579652: + result = new TL_inputVideo(); + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in InputVideo", constructor)); + } + if (result != null) { + result.readParams(stream, exception); + } + return result; + } + } + + public static class TL_inputVideoEmpty extends InputVideo { + public static int constructor = 0x5508ec75; + + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class TL_inputVideo extends InputVideo { + public static int constructor = 0xee579652; + + + public void readParams(AbsSerializedData stream, boolean exception) { + id = stream.readInt64(exception); + access_hash = stream.readInt64(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt64(id); + stream.writeInt64(access_hash); + } + } + + public static class messages_DhConfig extends TLObject { + public byte[] random; + public int g; + public byte[] p; + public int version; + + public static messages_DhConfig TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + messages_DhConfig result = null; + switch(constructor) { + case 0xc0e24635: + result = new TL_messages_dhConfigNotModified(); + break; + case 0x2c221edd: + result = new TL_messages_dhConfig(); + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in messages_DhConfig", constructor)); + } + if (result != null) { + result.readParams(stream, exception); + } + return result; + } + } + + public static class TL_messages_dhConfigNotModified extends messages_DhConfig { + public static int constructor = 0xc0e24635; + + + public void readParams(AbsSerializedData stream, boolean exception) { + random = stream.readByteArray(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeByteArray(random); + } + } + + public static class TL_messages_dhConfig extends messages_DhConfig { + public static int constructor = 0x2c221edd; + + + public void readParams(AbsSerializedData stream, boolean exception) { + g = stream.readInt32(exception); + p = stream.readByteArray(exception); + version = stream.readInt32(exception); + random = stream.readByteArray(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(g); + stream.writeByteArray(p); + stream.writeInt32(version); + stream.writeByteArray(random); + } + } + + public static class Peer extends TLObject { + public int user_id; + public int chat_id; + + public static Peer TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + Peer result = null; + switch(constructor) { + case 0x9db1bc6d: + result = new TL_peerUser(); + break; + case 0xbad0e5bb: + result = new TL_peerChat(); + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in Peer", constructor)); + } + if (result != null) { + result.readParams(stream, exception); + } + return result; + } + } + + public static class TL_peerUser extends Peer { + public static int constructor = 0x9db1bc6d; + + + public void readParams(AbsSerializedData stream, boolean exception) { + user_id = stream.readInt32(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(user_id); + } + } + + public static class TL_peerChat extends Peer { + public static int constructor = 0xbad0e5bb; + + + public void readParams(AbsSerializedData stream, boolean exception) { + chat_id = stream.readInt32(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(chat_id); + } + } + + public static class TL_dcOption extends TLObject { + public static int constructor = 0x2ec2a43c; + + public int id; + public String hostname; + public String ip_address; + public int port; + + public static TL_dcOption TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + if (TL_dcOption.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_dcOption", constructor)); + } else { + return null; + } + } + TL_dcOption result = new TL_dcOption(); + result.readParams(stream, exception); + return result; + } + + public void readParams(AbsSerializedData stream, boolean exception) { + id = stream.readInt32(exception); + hostname = stream.readString(exception); + ip_address = stream.readString(exception); + port = stream.readInt32(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(id); + stream.writeString(hostname); + stream.writeString(ip_address); + stream.writeInt32(port); + } + } + + public static class InputFile extends TLObject { + public long id; + public int parts; + public String name; + public String md5_checksum; + + public static InputFile TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + InputFile result = null; + switch(constructor) { + case 0xfa4f0bb5: + result = new TL_inputFileBig(); + break; + case 0xf52ff27f: + result = new TL_inputFile(); + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in InputFile", constructor)); + } + if (result != null) { + result.readParams(stream, exception); + } + return result; + } + } + + public static class TL_inputFileBig extends InputFile { + public static int constructor = 0xfa4f0bb5; + + + public void readParams(AbsSerializedData stream, boolean exception) { + id = stream.readInt64(exception); + parts = stream.readInt32(exception); + name = stream.readString(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt64(id); + stream.writeInt32(parts); + stream.writeString(name); + } + } + + public static class TL_inputFile extends InputFile { + public static int constructor = 0xf52ff27f; + + + public void readParams(AbsSerializedData stream, boolean exception) { + id = stream.readInt64(exception); + parts = stream.readInt32(exception); + name = stream.readString(exception); + md5_checksum = stream.readString(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt64(id); + stream.writeInt32(parts); + stream.writeString(name); + stream.writeString(md5_checksum); + } + } + + public static class TL_account_passwordInputSettings extends TLObject { + public static int constructor = 0xbcfc532c; + + public int flags; + public byte[] new_salt; + public byte[] new_password_hash; + public String hint; + public String email; + + public static TL_account_passwordInputSettings TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + if (TL_account_passwordInputSettings.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_account_passwordInputSettings", constructor)); + } else { + return null; + } + } + TL_account_passwordInputSettings result = new TL_account_passwordInputSettings(); + result.readParams(stream, exception); + return result; + } + + public void readParams(AbsSerializedData stream, boolean exception) { + flags = stream.readInt32(exception); + if ((flags & 1) != 0) { + new_salt = stream.readByteArray(exception); + } + if ((flags & 1) != 0) { + new_password_hash = stream.readByteArray(exception); + } + if ((flags & 1) != 0) { + hint = stream.readString(exception); + } + if ((flags & 2) != 0) { + email = stream.readString(exception); + } + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(flags); + if ((flags & 1) != 0) { + stream.writeByteArray(new_salt); + } + if ((flags & 1) != 0) { + stream.writeByteArray(new_password_hash); + } + if ((flags & 1) != 0) { + stream.writeString(hint); + } + if ((flags & 2) != 0) { + stream.writeString(email); + } + } + } + + public static class InputUser extends TLObject { + public int user_id; + public long access_hash; + + public static InputUser TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + InputUser result = null; + switch(constructor) { + case 0xf7c1b13f: + result = new TL_inputUserSelf(); + break; + case 0x655e74ff: + result = new TL_inputUserForeign(); + break; + case 0xb98886cf: + result = new TL_inputUserEmpty(); + break; + case 0x86e94f65: + result = new TL_inputUserContact(); + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in InputUser", constructor)); + } + if (result != null) { + result.readParams(stream, exception); + } + return result; + } + } + + public static class TL_inputUserSelf extends InputUser { + public static int constructor = 0xf7c1b13f; + + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class TL_inputUserForeign extends InputUser { + public static int constructor = 0x655e74ff; + + + public void readParams(AbsSerializedData stream, boolean exception) { + user_id = stream.readInt32(exception); + access_hash = stream.readInt64(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(user_id); + stream.writeInt64(access_hash); + } + } + + public static class TL_inputUserEmpty extends InputUser { + public static int constructor = 0xb98886cf; + + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class TL_inputUserContact extends InputUser { + public static int constructor = 0x86e94f65; + + + public void readParams(AbsSerializedData stream, boolean exception) { + user_id = stream.readInt32(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(user_id); + } + } + + public static class TL_chatParticipant extends TLObject { + public static int constructor = 0xc8d7493e; + + public int user_id; + public int inviter_id; + public int date; + + public static TL_chatParticipant TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + if (TL_chatParticipant.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_chatParticipant", constructor)); + } else { + return null; + } + } + TL_chatParticipant result = new TL_chatParticipant(); + result.readParams(stream, exception); + return result; + } + + public void readParams(AbsSerializedData stream, boolean exception) { + user_id = stream.readInt32(exception); + inviter_id = stream.readInt32(exception); + date = stream.readInt32(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(user_id); + stream.writeInt32(inviter_id); + stream.writeInt32(date); + } + } + + public static class EncryptedFile extends TLObject { + public long id; + public long access_hash; + public int size; + public int dc_id; + public int key_fingerprint; + + public static EncryptedFile TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + EncryptedFile result = null; + switch(constructor) { + case 0x4a70994c: + result = new TL_encryptedFile(); + break; + case 0xc21f497e: + result = new TL_encryptedFileEmpty(); + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in EncryptedFile", constructor)); + } + if (result != null) { + result.readParams(stream, exception); + } + return result; + } + } + + public static class TL_encryptedFile extends EncryptedFile { + public static int constructor = 0x4a70994c; + + + public void readParams(AbsSerializedData stream, boolean exception) { + id = stream.readInt64(exception); + access_hash = stream.readInt64(exception); + size = stream.readInt32(exception); + dc_id = stream.readInt32(exception); + key_fingerprint = stream.readInt32(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt64(id); + stream.writeInt64(access_hash); + stream.writeInt32(size); + stream.writeInt32(dc_id); + stream.writeInt32(key_fingerprint); + } + } + + public static class TL_encryptedFileEmpty extends EncryptedFile { + public static int constructor = 0xc21f497e; + + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class TL_account_privacyRules extends TLObject { + public static int constructor = 0x554abb6f; + + public ArrayList rules = new ArrayList<>(); + public ArrayList users = new ArrayList<>(); + + public static TL_account_privacyRules TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + if (TL_account_privacyRules.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_account_privacyRules", constructor)); + } else { + return null; + } + } + TL_account_privacyRules result = new TL_account_privacyRules(); + result.readParams(stream, exception); + return result; + } + + public void readParams(AbsSerializedData stream, boolean exception) { + int magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + int count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + rules.add(PrivacyRule.TLdeserialize(stream, stream.readInt32(exception), exception)); + } + magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + users.add(User.TLdeserialize(stream, stream.readInt32(exception), exception)); + } + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(0x1cb5c415); + int count = rules.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + rules.get(a).serializeToStream(stream); + } + stream.writeInt32(0x1cb5c415); + count = users.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + users.get(a).serializeToStream(stream); + } + } + } + + public static class TL_auth_exportedAuthorization extends TLObject { + public static int constructor = 0xdf969c2d; + + public int id; + public byte[] bytes; + + public static TL_auth_exportedAuthorization TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + if (TL_auth_exportedAuthorization.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_auth_exportedAuthorization", constructor)); + } else { + return null; + } + } + TL_auth_exportedAuthorization result = new TL_auth_exportedAuthorization(); + result.readParams(stream, exception); + return result; + } + + public void readParams(AbsSerializedData stream, boolean exception) { + id = stream.readInt32(exception); + bytes = stream.readByteArray(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(id); + stream.writeByteArray(bytes); + } + } + + public static class InputFileLocation extends TLObject { + public long id; + public long access_hash; + public long volume_id; + public int local_id; + public long secret; + + public static InputFileLocation TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + InputFileLocation result = null; + switch(constructor) { + case 0x74dc404d: + result = new TL_inputAudioFileLocation(); + break; + case 0xf5235d55: + result = new TL_inputEncryptedFileLocation(); + break; + case 0x3d0364ec: + result = new TL_inputVideoFileLocation(); + break; + case 0x4e45abe9: + result = new TL_inputDocumentFileLocation(); + break; + case 0x14637196: + result = new TL_inputFileLocation(); + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in InputFileLocation", constructor)); + } + if (result != null) { + result.readParams(stream, exception); + } + return result; + } + } + + public static class TL_inputAudioFileLocation extends InputFileLocation { + public static int constructor = 0x74dc404d; + + + public void readParams(AbsSerializedData stream, boolean exception) { + id = stream.readInt64(exception); + access_hash = stream.readInt64(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt64(id); + stream.writeInt64(access_hash); + } + } + + public static class TL_inputEncryptedFileLocation extends InputFileLocation { + public static int constructor = 0xf5235d55; + + + public void readParams(AbsSerializedData stream, boolean exception) { + id = stream.readInt64(exception); + access_hash = stream.readInt64(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt64(id); + stream.writeInt64(access_hash); + } + } + + public static class TL_inputVideoFileLocation extends InputFileLocation { + public static int constructor = 0x3d0364ec; + + + public void readParams(AbsSerializedData stream, boolean exception) { + id = stream.readInt64(exception); + access_hash = stream.readInt64(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt64(id); + stream.writeInt64(access_hash); + } + } + + public static class TL_inputDocumentFileLocation extends InputFileLocation { + public static int constructor = 0x4e45abe9; + + + public void readParams(AbsSerializedData stream, boolean exception) { + id = stream.readInt64(exception); + access_hash = stream.readInt64(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt64(id); + stream.writeInt64(access_hash); + } + } + + public static class TL_inputFileLocation extends InputFileLocation { + public static int constructor = 0x14637196; + + + public void readParams(AbsSerializedData stream, boolean exception) { + volume_id = stream.readInt64(exception); + local_id = stream.readInt32(exception); + secret = stream.readInt64(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt64(volume_id); + stream.writeInt32(local_id); + stream.writeInt64(secret); + } + } + + public static class TL_chatFull extends TLObject { + public static int constructor = 0xcade0791; + + public int id; + public ChatParticipants participants; + public Photo chat_photo; + public PeerNotifySettings notify_settings; + public ExportedChatInvite exported_invite; + + public static TL_chatFull TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + if (TL_chatFull.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_chatFull", constructor)); + } else { + return null; + } + } + TL_chatFull result = new TL_chatFull(); + result.readParams(stream, exception); + return result; + } + + public void readParams(AbsSerializedData stream, boolean exception) { + id = stream.readInt32(exception); + participants = ChatParticipants.TLdeserialize(stream, stream.readInt32(exception), exception); + chat_photo = Photo.TLdeserialize(stream, stream.readInt32(exception), exception); + notify_settings = PeerNotifySettings.TLdeserialize(stream, stream.readInt32(exception), exception); + exported_invite = ExportedChatInvite.TLdeserialize(stream, stream.readInt32(exception), exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(id); + participants.serializeToStream(stream); + chat_photo.serializeToStream(stream); + notify_settings.serializeToStream(stream); + exported_invite.serializeToStream(stream); + } + } + + public static class InputGeoPoint extends TLObject { + public double lat; + public double _long; + + public static InputGeoPoint TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + InputGeoPoint result = null; + switch(constructor) { + case 0xf3b7acc9: + result = new TL_inputGeoPoint(); + break; + case 0xe4c123d6: + result = new TL_inputGeoPointEmpty(); + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in InputGeoPoint", constructor)); + } + if (result != null) { + result.readParams(stream, exception); + } + return result; + } + } + + public static class TL_inputGeoPoint extends InputGeoPoint { + public static int constructor = 0xf3b7acc9; + + + public void readParams(AbsSerializedData stream, boolean exception) { + lat = stream.readDouble(exception); + _long = stream.readDouble(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeDouble(lat); + stream.writeDouble(_long); + } + } + + public static class TL_inputGeoPointEmpty extends InputGeoPoint { + public static int constructor = 0xe4c123d6; + + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class TL_photo extends Photo { + public static int constructor = 0xc3838076; + + + public void readParams(AbsSerializedData stream, boolean exception) { + id = stream.readInt64(exception); + access_hash = stream.readInt64(exception); + user_id = stream.readInt32(exception); + date = stream.readInt32(exception); + geo = GeoPoint.TLdeserialize(stream, stream.readInt32(exception), exception); + int magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + int count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + sizes.add(PhotoSize.TLdeserialize(stream, stream.readInt32(exception), exception)); + } + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt64(id); + stream.writeInt64(access_hash); + stream.writeInt32(user_id); + stream.writeInt32(date); + geo.serializeToStream(stream); + stream.writeInt32(0x1cb5c415); + int count = sizes.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + sizes.get(a).serializeToStream(stream); + } + } + } + + public static class TL_photoEmpty extends Photo { + public static int constructor = 0x2331b22d; + + + public void readParams(AbsSerializedData stream, boolean exception) { + id = stream.readInt64(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt64(id); + } + } + + public static class TL_help_support extends TLObject { + public static int constructor = 0x17c6b5f6; + + public String phone_number; + public User user; + + public static TL_help_support TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + if (TL_help_support.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_help_support", constructor)); + } else { + return null; + } + } + TL_help_support result = new TL_help_support(); + result.readParams(stream, exception); + return result; + } + + public void readParams(AbsSerializedData stream, boolean exception) { + phone_number = stream.readString(exception); + user = User.TLdeserialize(stream, stream.readInt32(exception), exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeString(phone_number); + user.serializeToStream(stream); + } + } + + public static class TL_inputPhoneContact extends TLObject { + public static int constructor = 0xf392b7f4; + + public long client_id; + public String phone; + public String first_name; + public String last_name; + + public static TL_inputPhoneContact TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + if (TL_inputPhoneContact.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_inputPhoneContact", constructor)); + } else { + return null; + } + } + TL_inputPhoneContact result = new TL_inputPhoneContact(); + result.readParams(stream, exception); + return result; + } + + public void readParams(AbsSerializedData stream, boolean exception) { + client_id = stream.readInt64(exception); + phone = stream.readString(exception); + first_name = stream.readString(exception); + last_name = stream.readString(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt64(client_id); + stream.writeString(phone); + stream.writeString(first_name); + stream.writeString(last_name); + } + } + + public static class Bool extends TLObject { + + public static Bool TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + Bool result = null; + switch(constructor) { + case 0x997275b5: + result = new TL_boolTrue(); + break; + case 0xbc799737: + result = new TL_boolFalse(); + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in Bool", constructor)); + } + if (result != null) { + result.readParams(stream, exception); + } + return result; + } + } + + public static class TL_boolTrue extends Bool { + public static int constructor = 0x997275b5; + + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class TL_boolFalse extends Bool { + public static int constructor = 0xbc799737; + + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class EncryptedMessage extends TLObject { + public long random_id; + public int chat_id; + public int date; + public byte[] bytes; + public EncryptedFile file; + + public static EncryptedMessage TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + EncryptedMessage result = null; + switch(constructor) { + case 0x23734b06: + result = new TL_encryptedMessageService(); + break; + case 0xed18c118: + result = new TL_encryptedMessage(); + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in EncryptedMessage", constructor)); + } + if (result != null) { + result.readParams(stream, exception); + } + return result; + } + } + + public static class TL_encryptedMessageService extends EncryptedMessage { + public static int constructor = 0x23734b06; + + + public void readParams(AbsSerializedData stream, boolean exception) { + random_id = stream.readInt64(exception); + chat_id = stream.readInt32(exception); + date = stream.readInt32(exception); + bytes = stream.readByteArray(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt64(random_id); + stream.writeInt32(chat_id); + stream.writeInt32(date); + stream.writeByteArray(bytes); + } + } + + public static class TL_encryptedMessage extends EncryptedMessage { + public static int constructor = 0xed18c118; + + + public void readParams(AbsSerializedData stream, boolean exception) { + random_id = stream.readInt64(exception); + chat_id = stream.readInt32(exception); + date = stream.readInt32(exception); + bytes = stream.readByteArray(exception); + file = EncryptedFile.TLdeserialize(stream, stream.readInt32(exception), exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt64(random_id); + stream.writeInt32(chat_id); + stream.writeInt32(date); + stream.writeByteArray(bytes); + file.serializeToStream(stream); + } + } + + public static class TL_messages_messageEmpty extends TLObject { + public static int constructor = 0x3f4e0648; + + + public static TL_messages_messageEmpty TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + if (TL_messages_messageEmpty.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_messages_messageEmpty", constructor)); + } else { + return null; + } + } + TL_messages_messageEmpty result = new TL_messages_messageEmpty(); + result.readParams(stream, exception); + return result; + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class DestroySessionRes extends TLObject { + public long session_id; + + public static DestroySessionRes TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + DestroySessionRes result = null; + switch(constructor) { + case 0xe22045fc: + result = new TL_destroy_session_ok(); + break; + case 0x62d350c9: + result = new TL_destroy_session_none(); + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in DestroySessionRes", constructor)); + } + if (result != null) { + result.readParams(stream, exception); + } + return result; + } + } + + public static class TL_destroy_session_ok extends DestroySessionRes { + public static int constructor = 0xe22045fc; + + + public void readParams(AbsSerializedData stream, boolean exception) { + session_id = stream.readInt64(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt64(session_id); + } + } + + public static class TL_destroy_session_none extends DestroySessionRes { + public static int constructor = 0x62d350c9; + + + public void readParams(AbsSerializedData stream, boolean exception) { + session_id = stream.readInt64(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt64(session_id); + } + } + + public static class PhotoSize extends TLObject { + public String type; + public FileLocation location; + public int w; + public int h; + public int size; + public byte[] bytes; + + public static PhotoSize TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + PhotoSize result = null; + switch(constructor) { + case 0x77bfb61b: + result = new TL_photoSize(); + break; + case 0xe17e23c: + result = new TL_photoSizeEmpty(); + break; + case 0xe9a734fa: + result = new TL_photoCachedSize(); + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in PhotoSize", constructor)); + } + if (result != null) { + result.readParams(stream, exception); + } + return result; + } + } + + public static class TL_photoSize extends PhotoSize { + public static int constructor = 0x77bfb61b; + + + public void readParams(AbsSerializedData stream, boolean exception) { + type = stream.readString(exception); + location = FileLocation.TLdeserialize(stream, stream.readInt32(exception), exception); + w = stream.readInt32(exception); + h = stream.readInt32(exception); + size = stream.readInt32(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeString(type); + location.serializeToStream(stream); + stream.writeInt32(w); + stream.writeInt32(h); + stream.writeInt32(size); + } + } + + public static class TL_photoSizeEmpty extends PhotoSize { + public static int constructor = 0xe17e23c; + + + public void readParams(AbsSerializedData stream, boolean exception) { + type = stream.readString(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeString(type); + } + } + + public static class TL_photoCachedSize extends PhotoSize { + public static int constructor = 0xe9a734fa; + + + public void readParams(AbsSerializedData stream, boolean exception) { + type = stream.readString(exception); + location = FileLocation.TLdeserialize(stream, stream.readInt32(exception), exception); + w = stream.readInt32(exception); + h = stream.readInt32(exception); + bytes = stream.readByteArray(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeString(type); + location.serializeToStream(stream); + stream.writeInt32(w); + stream.writeInt32(h); + stream.writeByteArray(bytes); + } + } + + public static class TL_messageActionChatEditPhoto extends MessageAction { + public static int constructor = 0x7fcb13a8; + + + public void readParams(AbsSerializedData stream, boolean exception) { + photo = Photo.TLdeserialize(stream, stream.readInt32(exception), exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + photo.serializeToStream(stream); + } + } + + public static class TL_messageActionChatDeleteUser extends MessageAction { + public static int constructor = 0xb2ae9b0c; + + + public void readParams(AbsSerializedData stream, boolean exception) { + user_id = stream.readInt32(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(user_id); + } + } + + public static class TL_messageActionChatJoinedByLink extends MessageAction { + public static int constructor = 0xf89cf5e8; + + + public void readParams(AbsSerializedData stream, boolean exception) { + inviter_id = stream.readInt32(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(inviter_id); + } + } + + public static class TL_messageActionChatDeletePhoto extends MessageAction { + public static int constructor = 0x95e3fbef; + + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class TL_messageActionChatAddUser extends MessageAction { + public static int constructor = 0x5e3cfc4b; + + + public void readParams(AbsSerializedData stream, boolean exception) { + user_id = stream.readInt32(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(user_id); + } + } + + public static class TL_messageActionChatCreate extends MessageAction { + public static int constructor = 0xa6638b9a; + + + public void readParams(AbsSerializedData stream, boolean exception) { + title = stream.readString(exception); + int magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + int count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + users.add(stream.readInt32(exception)); + } + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeString(title); + stream.writeInt32(0x1cb5c415); + int count = users.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + stream.writeInt32(users.get(a)); + } + } + } + + public static class TL_messageActionEmpty extends MessageAction { + public static int constructor = 0xb6aef7b0; + + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class TL_messageActionChatEditTitle extends MessageAction { + public static int constructor = 0xb5a1ce5a; + + + public void readParams(AbsSerializedData stream, boolean exception) { + title = stream.readString(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeString(title); + } + } + + public static class TL_messageActionGeoChatCreate extends MessageAction { + public static int constructor = 0x6f038ebc; + + + public void readParams(AbsSerializedData stream, boolean exception) { + title = stream.readString(exception); + address = stream.readString(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeString(title); + stream.writeString(address); + } + } + + public static class TL_messageActionGeoChatCheckin extends MessageAction { + public static int constructor = 0xc7d53de; + + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class DecryptedMessageAction extends TLObject { + public int ttl_seconds; + public int layer; + public ArrayList random_ids = new ArrayList<>(); + public long exchange_id; + public long key_fingerprint; + public SendMessageAction action; + public byte[] g_b; + public int start_seq_no; + public int end_seq_no; + public byte[] g_a; + + public static DecryptedMessageAction TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + DecryptedMessageAction result = null; + switch(constructor) { + case 0xa1733aec: + result = new TL_decryptedMessageActionSetMessageTTL(); + break; + case 0xf3048883: + result = new TL_decryptedMessageActionNotifyLayer(); + break; + case 0x65614304: + result = new TL_decryptedMessageActionDeleteMessages(); + break; + case 0xec2e0b9b: + result = new TL_decryptedMessageActionCommitKey(); + break; + case 0xdd05ec6b: + result = new TL_decryptedMessageActionAbortKey(); + break; + case 0x6719e45c: + result = new TL_decryptedMessageActionFlushHistory(); + break; + case 0xccb27641: + result = new TL_decryptedMessageActionTyping(); + break; + case 0x6fe1735b: + result = new TL_decryptedMessageActionAcceptKey(); + break; + case 0xc4f40be: + result = new TL_decryptedMessageActionReadMessages(); + break; + case 0x511110b0: + result = new TL_decryptedMessageActionResend(); + break; + case 0xf3c9611b: + result = new TL_decryptedMessageActionRequestKey(); + break; + case 0x8ac1f475: + result = new TL_decryptedMessageActionScreenshotMessages(); + break; + case 0xa82fdd63: + result = new TL_decryptedMessageActionNoop(); + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in DecryptedMessageAction", constructor)); + } + if (result != null) { + result.readParams(stream, exception); + } + return result; + } + } + + public static class TL_decryptedMessageActionSetMessageTTL extends DecryptedMessageAction { + public static int constructor = 0xa1733aec; + + + public void readParams(AbsSerializedData stream, boolean exception) { + ttl_seconds = stream.readInt32(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(ttl_seconds); + } + } + + public static class TL_decryptedMessageActionNotifyLayer extends DecryptedMessageAction { + public static int constructor = 0xf3048883; + + + public void readParams(AbsSerializedData stream, boolean exception) { + layer = stream.readInt32(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(layer); + } + } + + public static class TL_decryptedMessageActionDeleteMessages extends DecryptedMessageAction { + public static int constructor = 0x65614304; + + + public void readParams(AbsSerializedData stream, boolean exception) { + int magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + int count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + random_ids.add(stream.readInt64(exception)); + } + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(0x1cb5c415); + int count = random_ids.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + stream.writeInt64(random_ids.get(a)); + } + } + } + + public static class TL_decryptedMessageActionCommitKey extends DecryptedMessageAction { + public static int constructor = 0xec2e0b9b; + + + public void readParams(AbsSerializedData stream, boolean exception) { + exchange_id = stream.readInt64(exception); + key_fingerprint = stream.readInt64(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt64(exchange_id); + stream.writeInt64(key_fingerprint); + } + } + + public static class TL_decryptedMessageActionAbortKey extends DecryptedMessageAction { + public static int constructor = 0xdd05ec6b; + + + public void readParams(AbsSerializedData stream, boolean exception) { + exchange_id = stream.readInt64(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt64(exchange_id); + } + } + + public static class TL_decryptedMessageActionFlushHistory extends DecryptedMessageAction { + public static int constructor = 0x6719e45c; + + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class TL_decryptedMessageActionTyping extends DecryptedMessageAction { + public static int constructor = 0xccb27641; + + + public void readParams(AbsSerializedData stream, boolean exception) { + action = SendMessageAction.TLdeserialize(stream, stream.readInt32(exception), exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + action.serializeToStream(stream); + } + } + + public static class TL_decryptedMessageActionAcceptKey extends DecryptedMessageAction { + public static int constructor = 0x6fe1735b; + + + public void readParams(AbsSerializedData stream, boolean exception) { + exchange_id = stream.readInt64(exception); + g_b = stream.readByteArray(exception); + key_fingerprint = stream.readInt64(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt64(exchange_id); + stream.writeByteArray(g_b); + stream.writeInt64(key_fingerprint); + } + } + + public static class TL_decryptedMessageActionReadMessages extends DecryptedMessageAction { + public static int constructor = 0xc4f40be; + + + public void readParams(AbsSerializedData stream, boolean exception) { + int magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + int count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + random_ids.add(stream.readInt64(exception)); + } + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(0x1cb5c415); + int count = random_ids.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + stream.writeInt64(random_ids.get(a)); + } + } + } + + public static class TL_decryptedMessageActionResend extends DecryptedMessageAction { + public static int constructor = 0x511110b0; + + + public void readParams(AbsSerializedData stream, boolean exception) { + start_seq_no = stream.readInt32(exception); + end_seq_no = stream.readInt32(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(start_seq_no); + stream.writeInt32(end_seq_no); + } + } + + public static class TL_decryptedMessageActionRequestKey extends DecryptedMessageAction { + public static int constructor = 0xf3c9611b; + + + public void readParams(AbsSerializedData stream, boolean exception) { + exchange_id = stream.readInt64(exception); + g_a = stream.readByteArray(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt64(exchange_id); + stream.writeByteArray(g_a); + } + } + + public static class TL_decryptedMessageActionScreenshotMessages extends DecryptedMessageAction { + public static int constructor = 0x8ac1f475; + + + public void readParams(AbsSerializedData stream, boolean exception) { + int magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + int count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + random_ids.add(stream.readInt64(exception)); + } + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(0x1cb5c415); + int count = random_ids.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + stream.writeInt64(random_ids.get(a)); + } + } + } + + public static class TL_decryptedMessageActionNoop extends DecryptedMessageAction { + public static int constructor = 0xa82fdd63; + + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class TL_inputGeoChat extends TLObject { + public static int constructor = 0x74d456fa; + + public int chat_id; + public long access_hash; + + public static TL_inputGeoChat TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + if (TL_inputGeoChat.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_inputGeoChat", constructor)); + } else { + return null; + } + } + TL_inputGeoChat result = new TL_inputGeoChat(); + result.readParams(stream, exception); + return result; + } + + public void readParams(AbsSerializedData stream, boolean exception) { + chat_id = stream.readInt32(exception); + access_hash = stream.readInt64(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(chat_id); + stream.writeInt64(access_hash); + } + } + + public static class TL_msgs_state_req extends TLObject { + public static int constructor = 0xda69fb52; + + public ArrayList msg_ids = new ArrayList<>(); + + public static TL_msgs_state_req TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + if (TL_msgs_state_req.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_msgs_state_req", constructor)); + } else { + return null; + } + } + TL_msgs_state_req result = new TL_msgs_state_req(); + result.readParams(stream, exception); + return result; + } + + public void readParams(AbsSerializedData stream, boolean exception) { + int magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + int count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + msg_ids.add(stream.readInt64(exception)); + } + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(0x1cb5c415); + int count = msg_ids.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + stream.writeInt64(msg_ids.get(a)); + } + } + } + + public static class TL_importedContact extends TLObject { + public static int constructor = 0xd0028438; + + public int user_id; + public long client_id; + + public static TL_importedContact TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + if (TL_importedContact.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_importedContact", constructor)); + } else { + return null; + } + } + TL_importedContact result = new TL_importedContact(); + result.readParams(stream, exception); + return result; + } + + public void readParams(AbsSerializedData stream, boolean exception) { + user_id = stream.readInt32(exception); + client_id = stream.readInt64(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(user_id); + stream.writeInt64(client_id); + } + } + + public static class auth_SentCode extends TLObject { + public boolean phone_registered; + public String phone_code_hash; + public int send_call_timeout; + public boolean is_password; + + public static auth_SentCode TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + auth_SentCode result = null; + switch(constructor) { + case 0xe325edcf: + result = new TL_auth_sentAppCode(); + break; + case 0xefed51d9: + result = new TL_auth_sentCode(); + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in auth_SentCode", constructor)); + } + if (result != null) { + result.readParams(stream, exception); + } + return result; + } + } + + public static class TL_auth_sentAppCode extends auth_SentCode { + public static int constructor = 0xe325edcf; + + + public void readParams(AbsSerializedData stream, boolean exception) { + phone_registered = stream.readBool(exception); + phone_code_hash = stream.readString(exception); + send_call_timeout = stream.readInt32(exception); + is_password = stream.readBool(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeBool(phone_registered); + stream.writeString(phone_code_hash); + stream.writeInt32(send_call_timeout); + stream.writeBool(is_password); + } + } + + public static class TL_auth_sentCode extends auth_SentCode { + public static int constructor = 0xefed51d9; + + + public void readParams(AbsSerializedData stream, boolean exception) { + phone_registered = stream.readBool(exception); + phone_code_hash = stream.readString(exception); + send_call_timeout = stream.readInt32(exception); + is_password = stream.readBool(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeBool(phone_registered); + stream.writeString(phone_code_hash); + stream.writeInt32(send_call_timeout); + stream.writeBool(is_password); + } + } + + public static class TL_help_inviteText extends TLObject { + public static int constructor = 0x18cb9f78; + + public String message; + + public static TL_help_inviteText TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + if (TL_help_inviteText.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_help_inviteText", constructor)); + } else { + return null; + } + } + TL_help_inviteText result = new TL_help_inviteText(); + result.readParams(stream, exception); + return result; + } + + public void readParams(AbsSerializedData stream, boolean exception) { + message = stream.readString(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeString(message); + } + } + + public static class messages_AllStickers extends TLObject { + public String hash; + public ArrayList packs = new ArrayList<>(); + public ArrayList documents = new ArrayList<>(); + + public static messages_AllStickers TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + messages_AllStickers result = null; + switch(constructor) { + case 0xdcef3102: + result = new TL_messages_allStickers(); + break; + case 0xe86602c3: + result = new TL_messages_allStickersNotModified(); + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in messages_AllStickers", constructor)); + } + if (result != null) { + result.readParams(stream, exception); + } + return result; + } + } + + public static class TL_messages_allStickers extends messages_AllStickers { + public static int constructor = 0xdcef3102; + + + public void readParams(AbsSerializedData stream, boolean exception) { + hash = stream.readString(exception); + int magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + int count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + packs.add(TL_stickerPack.TLdeserialize(stream, stream.readInt32(exception), exception)); + } + magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + documents.add(Document.TLdeserialize(stream, stream.readInt32(exception), exception)); + } + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeString(hash); + stream.writeInt32(0x1cb5c415); + int count = packs.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + packs.get(a).serializeToStream(stream); + } + stream.writeInt32(0x1cb5c415); + count = documents.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + documents.get(a).serializeToStream(stream); + } + } + } + + public static class TL_messages_allStickersNotModified extends messages_AllStickers { + public static int constructor = 0xe86602c3; + + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class TL_auth_checkedPhone extends TLObject { + public static int constructor = 0x811ea28e; + + public boolean phone_registered; + + public static TL_auth_checkedPhone TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + if (TL_auth_checkedPhone.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_auth_checkedPhone", constructor)); + } else { + return null; + } + } + TL_auth_checkedPhone result = new TL_auth_checkedPhone(); + result.readParams(stream, exception); + return result; + } + + public void readParams(AbsSerializedData stream, boolean exception) { + phone_registered = stream.readBool(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeBool(phone_registered); + } + } + + public static class TL_userProfilePhotoEmpty extends UserProfilePhoto { + public static int constructor = 0x4f11bae1; + + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class TL_userProfilePhoto extends UserProfilePhoto { + public static int constructor = 0xd559d8c8; + + + public void readParams(AbsSerializedData stream, boolean exception) { + photo_id = stream.readInt64(exception); + photo_small = FileLocation.TLdeserialize(stream, stream.readInt32(exception), exception); + photo_big = FileLocation.TLdeserialize(stream, stream.readInt32(exception), exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt64(photo_id); + photo_small.serializeToStream(stream); + photo_big.serializeToStream(stream); + } + } + + public static class TL_authorization extends TLObject { + public static int constructor = 0x7bf2e6f6; + + public long hash; + public int flags; + public String device_model; + public String platform; + public String system_version; + public int api_id; + public String app_name; + public String app_version; + public int date_created; + public int date_active; + public String ip; + public String country; + public String region; + + public static TL_authorization TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + if (TL_authorization.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_authorization", constructor)); + } else { + return null; + } + } + TL_authorization result = new TL_authorization(); + result.readParams(stream, exception); + return result; + } + + public void readParams(AbsSerializedData stream, boolean exception) { + hash = stream.readInt64(exception); + flags = stream.readInt32(exception); + device_model = stream.readString(exception); + platform = stream.readString(exception); + system_version = stream.readString(exception); + api_id = stream.readInt32(exception); + app_name = stream.readString(exception); + app_version = stream.readString(exception); + date_created = stream.readInt32(exception); + date_active = stream.readInt32(exception); + ip = stream.readString(exception); + country = stream.readString(exception); + region = stream.readString(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt64(hash); + stream.writeInt32(flags); + stream.writeString(device_model); + stream.writeString(platform); + stream.writeString(system_version); + stream.writeInt32(api_id); + stream.writeString(app_name); + stream.writeString(app_version); + stream.writeInt32(date_created); + stream.writeInt32(date_active); + stream.writeString(ip); + stream.writeString(country); + stream.writeString(region); + } + } + + public static class Server_DH_Params extends TLObject { + public byte[] nonce; + public byte[] server_nonce; + public byte[] new_nonce_hash; + public byte[] encrypted_answer; + + public static Server_DH_Params TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + Server_DH_Params result = null; + switch(constructor) { + case 0x79cb045d: + result = new TL_server_DH_params_fail(); + break; + case 0xd0e8075c: + result = new TL_server_DH_params_ok(); + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in Server_DH_Params", constructor)); + } + if (result != null) { + result.readParams(stream, exception); + } + return result; + } + } + + public static class TL_server_DH_params_fail extends Server_DH_Params { + public static int constructor = 0x79cb045d; + + + public void readParams(AbsSerializedData stream, boolean exception) { + nonce = stream.readData(16, exception); + server_nonce = stream.readData(16, exception); + new_nonce_hash = stream.readData(16, exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeRaw(nonce); + stream.writeRaw(server_nonce); + stream.writeRaw(new_nonce_hash); + } + } + + public static class TL_server_DH_params_ok extends Server_DH_Params { + public static int constructor = 0xd0e8075c; + + + public void readParams(AbsSerializedData stream, boolean exception) { + nonce = stream.readData(16, exception); + server_nonce = stream.readData(16, exception); + encrypted_answer = stream.readByteArray(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeRaw(nonce); + stream.writeRaw(server_nonce); + stream.writeByteArray(encrypted_answer); + } + } + + public static class TL_protoMessage extends TLObject { + public static int constructor = 0x5bb8e511; + + public long msg_id; + public int seqno; + public int bytes; + public TLObject body; + + public static TL_protoMessage TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + if (TL_protoMessage.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_protoMessage", constructor)); + } else { + return null; + } + } + TL_protoMessage result = new TL_protoMessage(); + result.readParams(stream, exception); + return result; + } + + public void readParams(AbsSerializedData stream, boolean exception) { + msg_id = stream.readInt64(exception); + seqno = stream.readInt32(exception); + bytes = stream.readInt32(exception); + body = TLClassStore.Instance().TLdeserialize(stream, stream.readInt32(exception), exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt64(msg_id); + stream.writeInt32(seqno); + stream.writeInt32(bytes); + body.serializeToStream(stream); + } + } + + public static class TL_geochats_located extends TLObject { + public static int constructor = 0x48feb267; + + public ArrayList results = new ArrayList<>(); + public ArrayList messages = new ArrayList<>(); + public ArrayList chats = new ArrayList<>(); + public ArrayList users = new ArrayList<>(); + + public static TL_geochats_located TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + if (TL_geochats_located.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_geochats_located", constructor)); + } else { + return null; + } + } + TL_geochats_located result = new TL_geochats_located(); + result.readParams(stream, exception); + return result; + } + + public void readParams(AbsSerializedData stream, boolean exception) { + int magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + int count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + results.add(TL_chatLocated.TLdeserialize(stream, stream.readInt32(exception), exception)); + } + magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + messages.add(GeoChatMessage.TLdeserialize(stream, stream.readInt32(exception), exception)); + } + magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + chats.add(Chat.TLdeserialize(stream, stream.readInt32(exception), exception)); + } + magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + users.add(User.TLdeserialize(stream, stream.readInt32(exception), exception)); + } + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(0x1cb5c415); + int count = results.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + results.get(a).serializeToStream(stream); + } + stream.writeInt32(0x1cb5c415); + count = messages.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + messages.get(a).serializeToStream(stream); + } + stream.writeInt32(0x1cb5c415); + count = chats.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + chats.get(a).serializeToStream(stream); + } + stream.writeInt32(0x1cb5c415); + count = users.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + users.get(a).serializeToStream(stream); + } + } + } + + public static class TL_msgs_all_info extends TLObject { + public static int constructor = 0x8cc0d131; + + public ArrayList msg_ids = new ArrayList<>(); + public String info; + + public static TL_msgs_all_info TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + if (TL_msgs_all_info.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_msgs_all_info", constructor)); + } else { + return null; + } + } + TL_msgs_all_info result = new TL_msgs_all_info(); + result.readParams(stream, exception); + return result; + } + + public void readParams(AbsSerializedData stream, boolean exception) { + int magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + int count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + msg_ids.add(stream.readInt64(exception)); + } + info = stream.readString(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(0x1cb5c415); + int count = msg_ids.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + stream.writeInt64(msg_ids.get(a)); + } + stream.writeString(info); + } + } + + public static class contacts_Blocked extends TLObject { + public ArrayList blocked = new ArrayList<>(); + public ArrayList users = new ArrayList<>(); + public int count; + + public static contacts_Blocked TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + contacts_Blocked result = null; + switch(constructor) { + case 0x1c138d15: + result = new TL_contacts_blocked(); + break; + case 0x900802a1: + result = new TL_contacts_blockedSlice(); + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in contacts_Blocked", constructor)); + } + if (result != null) { + result.readParams(stream, exception); + } + return result; + } + } + + public static class TL_contacts_blocked extends contacts_Blocked { + public static int constructor = 0x1c138d15; + + + public void readParams(AbsSerializedData stream, boolean exception) { + int magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + int count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + blocked.add(TL_contactBlocked.TLdeserialize(stream, stream.readInt32(exception), exception)); + } + magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + users.add(User.TLdeserialize(stream, stream.readInt32(exception), exception)); + } + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(0x1cb5c415); + int count = blocked.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + blocked.get(a).serializeToStream(stream); + } + stream.writeInt32(0x1cb5c415); + count = users.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + users.get(a).serializeToStream(stream); + } + } + } + + public static class TL_contacts_blockedSlice extends contacts_Blocked { + public static int constructor = 0x900802a1; + + + public void readParams(AbsSerializedData stream, boolean exception) { + count = stream.readInt32(exception); + int magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + int count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + blocked.add(TL_contactBlocked.TLdeserialize(stream, stream.readInt32(exception), exception)); + } + magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + users.add(User.TLdeserialize(stream, stream.readInt32(exception), exception)); + } + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(count); + stream.writeInt32(0x1cb5c415); + int count = blocked.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + blocked.get(a).serializeToStream(stream); + } + stream.writeInt32(0x1cb5c415); + count = users.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + users.get(a).serializeToStream(stream); + } + } + } + + public static class TL_encryptedChatWaiting extends EncryptedChat { + public static int constructor = 0x3bf703dc; + + + public void readParams(AbsSerializedData stream, boolean exception) { + id = stream.readInt32(exception); + access_hash = stream.readInt64(exception); + date = stream.readInt32(exception); + admin_id = stream.readInt32(exception); + participant_id = stream.readInt32(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(id); + stream.writeInt64(access_hash); + stream.writeInt32(date); + stream.writeInt32(admin_id); + stream.writeInt32(participant_id); + } + } + + public static class TL_encryptedChatEmpty extends EncryptedChat { + public static int constructor = 0xab7ec0a0; + + + public void readParams(AbsSerializedData stream, boolean exception) { + id = stream.readInt32(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(id); + } + } + + public static class TL_encryptedChatDiscarded extends EncryptedChat { + public static int constructor = 0x13d6dd27; + + + public void readParams(AbsSerializedData stream, boolean exception) { + id = stream.readInt32(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(id); + } + } + + public static class TL_encryptedChat extends EncryptedChat { + public static int constructor = 0xfa56ce36; + + + public void readParams(AbsSerializedData stream, boolean exception) { + id = stream.readInt32(exception); + access_hash = stream.readInt64(exception); + date = stream.readInt32(exception); + admin_id = stream.readInt32(exception); + participant_id = stream.readInt32(exception); + g_a_or_b = stream.readByteArray(exception); + key_fingerprint = stream.readInt64(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(id); + stream.writeInt64(access_hash); + stream.writeInt32(date); + stream.writeInt32(admin_id); + stream.writeInt32(participant_id); + stream.writeByteArray(g_a_or_b); + stream.writeInt64(key_fingerprint); + } + } + + public static class TL_encryptedChatRequested extends EncryptedChat { + public static int constructor = 0xc878527e; + + + public void readParams(AbsSerializedData stream, boolean exception) { + id = stream.readInt32(exception); + access_hash = stream.readInt64(exception); + date = stream.readInt32(exception); + admin_id = stream.readInt32(exception); + participant_id = stream.readInt32(exception); + g_a = stream.readByteArray(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(id); + stream.writeInt64(access_hash); + stream.writeInt32(date); + stream.writeInt32(admin_id); + stream.writeInt32(participant_id); + stream.writeByteArray(g_a); + } + } + + public static class help_AppUpdate extends TLObject { + public int id; + public boolean critical; + public String url; + public String text; + + public static help_AppUpdate TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + help_AppUpdate result = null; + switch(constructor) { + case 0x8987f311: + result = new TL_help_appUpdate(); + break; + case 0xc45a6536: + result = new TL_help_noAppUpdate(); + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in help_AppUpdate", constructor)); + } + if (result != null) { + result.readParams(stream, exception); + } + return result; + } + } + + public static class TL_help_appUpdate extends help_AppUpdate { + public static int constructor = 0x8987f311; + + + public void readParams(AbsSerializedData stream, boolean exception) { + id = stream.readInt32(exception); + critical = stream.readBool(exception); + url = stream.readString(exception); + text = stream.readString(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(id); + stream.writeBool(critical); + stream.writeString(url); + stream.writeString(text); + } + } + + public static class TL_help_noAppUpdate extends help_AppUpdate { + public static int constructor = 0xc45a6536; + + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class messages_Stickers extends TLObject { + public String hash; + public ArrayList stickers = new ArrayList<>(); + + public static messages_Stickers TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + messages_Stickers result = null; + switch(constructor) { + case 0xf1749a22: + result = new TL_messages_stickersNotModified(); + break; + case 0x8a8ecd32: + result = new TL_messages_stickers(); + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in messages_Stickers", constructor)); + } + if (result != null) { + result.readParams(stream, exception); + } + return result; + } + } + + public static class TL_messages_stickersNotModified extends messages_Stickers { + public static int constructor = 0xf1749a22; + + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class TL_messages_stickers extends messages_Stickers { + public static int constructor = 0x8a8ecd32; + + + public void readParams(AbsSerializedData stream, boolean exception) { + hash = stream.readString(exception); + int magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + int count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + stickers.add(Document.TLdeserialize(stream, stream.readInt32(exception), exception)); + } + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeString(hash); + stream.writeInt32(0x1cb5c415); + int count = stickers.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + stickers.get(a).serializeToStream(stream); + } + } + } + + public static class TL_video extends Video { + public static int constructor = 0xee9f4a4d; + + + public void readParams(AbsSerializedData stream, boolean exception) { + id = stream.readInt64(exception); + access_hash = stream.readInt64(exception); + user_id = stream.readInt32(exception); + date = stream.readInt32(exception); + duration = stream.readInt32(exception); + size = stream.readInt32(exception); + thumb = PhotoSize.TLdeserialize(stream, stream.readInt32(exception), exception); + dc_id = stream.readInt32(exception); + w = stream.readInt32(exception); + h = stream.readInt32(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt64(id); + stream.writeInt64(access_hash); + stream.writeInt32(user_id); + stream.writeInt32(date); + stream.writeInt32(duration); + stream.writeInt32(size); + thumb.serializeToStream(stream); + stream.writeInt32(dc_id); + stream.writeInt32(w); + stream.writeInt32(h); + } + } + + public static class TL_videoEmpty extends Video { + public static int constructor = 0xc10658a8; + + + public void readParams(AbsSerializedData stream, boolean exception) { + id = stream.readInt64(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt64(id); + } + } + + public static class TL_messages_affectedMessages extends TLObject { + public static int constructor = 0x84d19185; + + public int pts; + public int pts_count; + + public static TL_messages_affectedMessages TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + if (TL_messages_affectedMessages.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_messages_affectedMessages", constructor)); + } else { + return null; + } + } + TL_messages_affectedMessages result = new TL_messages_affectedMessages(); + result.readParams(stream, exception); + return result; + } + + public void readParams(AbsSerializedData stream, boolean exception) { + pts = stream.readInt32(exception); + pts_count = stream.readInt32(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(pts); + stream.writeInt32(pts_count); } } @@ -3540,28 +7681,65 @@ public class TLRPC { public ArrayList messages = new ArrayList<>(); public ArrayList chats = new ArrayList<>(); public ArrayList users = new ArrayList<>(); + + public static geochats_Messages TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + geochats_Messages result = null; + switch(constructor) { + case 0xbc5863e8: + result = new TL_geochats_messagesSlice(); + break; + case 0xd1526db1: + result = new TL_geochats_messages(); + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in geochats_Messages", constructor)); + } + if (result != null) { + result.readParams(stream, exception); + } + return result; + } } public static class TL_geochats_messagesSlice extends geochats_Messages { public static int constructor = 0xbc5863e8; - public void readParams(AbsSerializedData stream) { - count = stream.readInt32(); - stream.readInt32(); - int count = stream.readInt32(); - for (int a = 0; a < count; a++) { - messages.add((GeoChatMessage)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32())); + public void readParams(AbsSerializedData stream, boolean exception) { + count = stream.readInt32(exception); + int magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; } - stream.readInt32(); - count = stream.readInt32(); + int count = stream.readInt32(exception); for (int a = 0; a < count; a++) { - chats.add((Chat)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32())); + messages.add(GeoChatMessage.TLdeserialize(stream, stream.readInt32(exception), exception)); } - stream.readInt32(); - count = stream.readInt32(); + magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + count = stream.readInt32(exception); for (int a = 0; a < count; a++) { - users.add((User)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32())); + chats.add(Chat.TLdeserialize(stream, stream.readInt32(exception), exception)); + } + magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + users.add(User.TLdeserialize(stream, stream.readInt32(exception), exception)); } } @@ -3593,21 +7771,39 @@ public class TLRPC { public static int constructor = 0xd1526db1; - public void readParams(AbsSerializedData stream) { - stream.readInt32(); - int count = stream.readInt32(); - for (int a = 0; a < count; a++) { - messages.add((GeoChatMessage)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32())); + public void readParams(AbsSerializedData stream, boolean exception) { + int magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; } - stream.readInt32(); - count = stream.readInt32(); + int count = stream.readInt32(exception); for (int a = 0; a < count; a++) { - chats.add((Chat)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32())); + messages.add(GeoChatMessage.TLdeserialize(stream, stream.readInt32(exception), exception)); } - stream.readInt32(); - count = stream.readInt32(); + magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + count = stream.readInt32(exception); for (int a = 0; a < count; a++) { - users.add((User)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32())); + chats.add(Chat.TLdeserialize(stream, stream.readInt32(exception), exception)); + } + magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + users.add(User.TLdeserialize(stream, stream.readInt32(exception), exception)); } } @@ -3634,243 +7830,48 @@ public class TLRPC { } } - public static class messages_SentMessage extends TLObject { - public int id; - public int date; - public MessageMedia media; - public int pts; - public int pts_count; - public ArrayList links = new ArrayList<>(); - public int seq; - } + public static class TL_stickerPack extends TLObject { + public static int constructor = 0x12b299d4; - public static class TL_messages_sentMessage extends messages_SentMessage { - public static int constructor = 0x4c3d47f3; + public String emoticon; + public ArrayList documents = new ArrayList<>(); - - public void readParams(AbsSerializedData stream) { - id = stream.readInt32(); - date = stream.readInt32(); - media = (MessageMedia)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - pts = stream.readInt32(); - pts_count = stream.readInt32(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(id); - stream.writeInt32(date); - media.serializeToStream(stream); - stream.writeInt32(pts); - stream.writeInt32(pts_count); - } - } - - public static class TL_messages_sentMessageLink extends messages_SentMessage { - public static int constructor = 0x35a1a663; - - - public void readParams(AbsSerializedData stream) { - id = stream.readInt32(); - date = stream.readInt32(); - media = (MessageMedia)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - pts = stream.readInt32(); - pts_count = stream.readInt32(); - stream.readInt32(); - int count = stream.readInt32(); - for (int a = 0; a < count; a++) { - links.add((TL_contacts_link)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32())); + public static TL_stickerPack TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + if (TL_stickerPack.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_stickerPack", constructor)); + } else { + return null; + } + } + TL_stickerPack result = new TL_stickerPack(); + result.readParams(stream, exception); + return result; + } + + public void readParams(AbsSerializedData stream, boolean exception) { + emoticon = stream.readString(exception); + int magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + int count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + documents.add(stream.readInt64(exception)); } - seq = stream.readInt32(); } public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); - stream.writeInt32(id); - stream.writeInt32(date); - media.serializeToStream(stream); - stream.writeInt32(pts); - stream.writeInt32(pts_count); + stream.writeString(emoticon); stream.writeInt32(0x1cb5c415); - int count = links.size(); + int count = documents.size(); stream.writeInt32(count); for (int a = 0; a < count; a++) { - links.get(a).serializeToStream(stream); - } - stream.writeInt32(seq); - } - } - - public static class EncryptedMessage extends TLObject { - public long random_id; - public int chat_id; - public int date; - public byte[] bytes; - public EncryptedFile file; - } - - public static class TL_encryptedMessageService extends EncryptedMessage { - public static int constructor = 0x23734b06; - - - public void readParams(AbsSerializedData stream) { - random_id = stream.readInt64(); - chat_id = stream.readInt32(); - date = stream.readInt32(); - bytes = stream.readByteArray(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt64(random_id); - stream.writeInt32(chat_id); - stream.writeInt32(date); - stream.writeByteArray(bytes); - } - } - - public static class TL_encryptedMessage extends EncryptedMessage { - public static int constructor = 0xed18c118; - - - public void readParams(AbsSerializedData stream) { - random_id = stream.readInt64(); - chat_id = stream.readInt32(); - date = stream.readInt32(); - bytes = stream.readByteArray(); - file = (EncryptedFile)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt64(random_id); - stream.writeInt32(chat_id); - stream.writeInt32(date); - stream.writeByteArray(bytes); - file.serializeToStream(stream); - } - } - - public static class TL_contactSuggested extends TLObject { - public static int constructor = 0x3de191a1; - - public int user_id; - public int mutual_contacts; - - public void readParams(AbsSerializedData stream) { - user_id = stream.readInt32(); - mutual_contacts = stream.readInt32(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(user_id); - stream.writeInt32(mutual_contacts); - } - } - - public static class Server_DH_Params extends TLObject { - public byte[] nonce; - public byte[] server_nonce; - public byte[] new_nonce_hash; - public byte[] encrypted_answer; - } - - public static class TL_server_DH_params_fail extends Server_DH_Params { - public static int constructor = 0x79cb045d; - - - public void readParams(AbsSerializedData stream) { - nonce = stream.readData(16); - server_nonce = stream.readData(16); - new_nonce_hash = stream.readData(16); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeRaw(nonce); - stream.writeRaw(server_nonce); - stream.writeRaw(new_nonce_hash); - } - } - - public static class TL_server_DH_params_ok extends Server_DH_Params { - public static int constructor = 0xd0e8075c; - - - public void readParams(AbsSerializedData stream) { - nonce = stream.readData(16); - server_nonce = stream.readData(16); - encrypted_answer = stream.readByteArray(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeRaw(nonce); - stream.writeRaw(server_nonce); - stream.writeByteArray(encrypted_answer); - } - } - - public static class TL_msg_copy extends TLObject { - public static int constructor = 0xe06046b2; - - public TL_protoMessage orig_message; - - public void readParams(AbsSerializedData stream) { - orig_message = (TL_protoMessage)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - orig_message.serializeToStream(stream); - } - } - - public static class TL_contacts_importedContacts extends TLObject { - public static int constructor = 0xad524315; - - public ArrayList imported = new ArrayList<>(); - public ArrayList retry_contacts = new ArrayList<>(); - public ArrayList users = new ArrayList<>(); - - public void readParams(AbsSerializedData stream) { - stream.readInt32(); - int count = stream.readInt32(); - for (int a = 0; a < count; a++) { - imported.add((TL_importedContact)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32())); - } - stream.readInt32(); - count = stream.readInt32(); - for (int a = 0; a < count; a++) { - retry_contacts.add(stream.readInt64()); - } - stream.readInt32(); - count = stream.readInt32(); - for (int a = 0; a < count; a++) { - users.add((User)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32())); - } - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(0x1cb5c415); - int count = imported.size(); - stream.writeInt32(count); - for (int a = 0; a < count; a++) { - imported.get(a).serializeToStream(stream); - } - stream.writeInt32(0x1cb5c415); - count = retry_contacts.size(); - stream.writeInt32(count); - for (int a = 0; a < count; a++) { - stream.writeInt64(retry_contacts.get(a)); - } - stream.writeInt32(0x1cb5c415); - count = users.size(); - stream.writeInt32(count); - for (int a = 0; a < count; a++) { - users.get(a).serializeToStream(stream); + stream.writeInt64(documents.get(a)); } } } @@ -3881,9 +7882,22 @@ public class TLRPC { public String feature; public String description; - public void readParams(AbsSerializedData stream) { - feature = stream.readString(); - description = stream.readString(); + public static TL_disabledFeature TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + if (TL_disabledFeature.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_disabledFeature", constructor)); + } else { + return null; + } + } + TL_disabledFeature result = new TL_disabledFeature(); + result.readParams(stream, exception); + return result; + } + + public void readParams(AbsSerializedData stream, boolean exception) { + feature = stream.readString(exception); + description = stream.readString(exception); } public void serializeToStream(AbsSerializedData stream) { @@ -3893,2216 +7907,50 @@ public class TLRPC { } } - public static class TL_futureSalt extends TLObject { - public static int constructor = 0x0949d9dc; - - public int valid_since; - public int valid_until; - public long salt; - - public void readParams(AbsSerializedData stream) { - valid_since = stream.readInt32(); - valid_until = stream.readInt32(); - salt = stream.readInt64(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(valid_since); - stream.writeInt32(valid_until); - stream.writeInt64(salt); - } - } - - public static class Update extends TLObject { - public int chat_id; - public int max_date; - public int date; - public int user_id; - public ContactLink my_link; - public ContactLink foreign_link; - public ArrayList messages = new ArrayList<>(); - public int pts; - public int pts_count; - public int max_id; - public int version; - public WebPage webpage; - public String type; - public MessageMedia media; - public boolean popup; - public NotifyPeer peer; - public PeerNotifySettings notify_settings; - public SendMessageAction action; - public String first_name; - public String last_name; - public String username; - public String phone; - public int qts; - public int id; - public long random_id; - public ArrayList dc_options = new ArrayList<>(); - public ChatParticipants participants; - public TL_privacyKeyStatusTimestamp key; - public ArrayList rules = new ArrayList<>(); - public EncryptedChat chat; - public boolean blocked; - public long auth_key_id; - public String device; - public String location; - public UserProfilePhoto photo; - public boolean previous; - public int inviter_id; - public UserStatus status; - } - - public static class TL_updateEncryptedMessagesRead extends Update { - public static int constructor = 0x38fe25b7; - - - public void readParams(AbsSerializedData stream) { - chat_id = stream.readInt32(); - max_date = stream.readInt32(); - date = stream.readInt32(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(chat_id); - stream.writeInt32(max_date); - stream.writeInt32(date); - } - } - - public static class TL_updateContactLink extends Update { - public static int constructor = 0x9d2e67c5; - - - public void readParams(AbsSerializedData stream) { - user_id = stream.readInt32(); - my_link = (ContactLink)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - foreign_link = (ContactLink)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(user_id); - my_link.serializeToStream(stream); - foreign_link.serializeToStream(stream); - } - } - - public static class TL_updateReadMessages extends Update { - public static int constructor = 0x2e5ab668; - - - public void readParams(AbsSerializedData stream) { - stream.readInt32(); - int count = stream.readInt32(); - for (int a = 0; a < count; a++) { - messages.add(stream.readInt32()); - } - pts = stream.readInt32(); - pts_count = stream.readInt32(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(0x1cb5c415); - int count = messages.size(); - stream.writeInt32(count); - for (int a = 0; a < count; a++) { - stream.writeInt32(messages.get(a)); - } - stream.writeInt32(pts); - stream.writeInt32(pts_count); - } - } - - public static class TL_updateReadHistoryInbox extends Update { - public static int constructor = 0x9961fd5c; - - public Peer peer; - - public void readParams(AbsSerializedData stream) { - peer = (Peer)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - max_id = stream.readInt32(); - pts = stream.readInt32(); - pts_count = stream.readInt32(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - peer.serializeToStream(stream); - stream.writeInt32(max_id); - stream.writeInt32(pts); - stream.writeInt32(pts_count); - } - } - - public static class TL_updateChatParticipantDelete extends Update { - public static int constructor = 0x6e5f8c22; - - - public void readParams(AbsSerializedData stream) { - chat_id = stream.readInt32(); - user_id = stream.readInt32(); - version = stream.readInt32(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(chat_id); - stream.writeInt32(user_id); - stream.writeInt32(version); - } - } - - public static class TL_updateReadHistoryOutbox extends Update { - public static int constructor = 0x2f2f21bf; - - public Peer peer; - - public void readParams(AbsSerializedData stream) { - peer = (Peer)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - max_id = stream.readInt32(); - pts = stream.readInt32(); - pts_count = stream.readInt32(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - peer.serializeToStream(stream); - stream.writeInt32(max_id); - stream.writeInt32(pts); - stream.writeInt32(pts_count); - } - } - - public static class TL_updateWebPage extends Update { - public static int constructor = 0x2cc36971; - - - public void readParams(AbsSerializedData stream) { - webpage = (WebPage)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - webpage.serializeToStream(stream); - } - } - - public static class TL_updateServiceNotification extends Update { - public static int constructor = 0x382dd3e4; - - public String message; - - public void readParams(AbsSerializedData stream) { - type = stream.readString(); - message = stream.readString(); - media = (MessageMedia)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - popup = stream.readBool(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeString(type); - stream.writeString(message); - media.serializeToStream(stream); - stream.writeBool(popup); - } - } - - public static class TL_updateNotifySettings extends Update { - public static int constructor = 0xbec268ef; - - - public void readParams(AbsSerializedData stream) { - peer = (NotifyPeer)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - notify_settings = (PeerNotifySettings)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - peer.serializeToStream(stream); - notify_settings.serializeToStream(stream); - } - } - - public static class TL_updateUserTyping extends Update { - public static int constructor = 0x5c486927; - - - public void readParams(AbsSerializedData stream) { - user_id = stream.readInt32(); - action = (SendMessageAction)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(user_id); - action.serializeToStream(stream); - } - } - - public static class TL_updateChatUserTyping extends Update { - public static int constructor = 0x9a65ea1f; - - - public void readParams(AbsSerializedData stream) { - chat_id = stream.readInt32(); - user_id = stream.readInt32(); - action = (SendMessageAction) TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(chat_id); - stream.writeInt32(user_id); - action.serializeToStream(stream); - } - } - - public static class TL_updateUserName extends Update { - public static int constructor = 0xa7332b73; - - - public void readParams(AbsSerializedData stream) { - user_id = stream.readInt32(); - first_name = stream.readString(); - last_name = stream.readString(); - username = stream.readString(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(user_id); - stream.writeString(first_name); - stream.writeString(last_name); - stream.writeString(username); - } - } - - public static class TL_updateNewEncryptedMessage extends Update { - public static int constructor = 0x12bcbd9a; - - public EncryptedMessage message; - - public void readParams(AbsSerializedData stream) { - message = (EncryptedMessage)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - qts = stream.readInt32(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - message.serializeToStream(stream); - stream.writeInt32(qts); - } - } - - public static class TL_updateNewMessage extends Update { - public static int constructor = 0x1f2b0afd; - - public Message message; - - public void readParams(AbsSerializedData stream) { - message = (Message)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - pts = stream.readInt32(); - pts_count = stream.readInt32(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - message.serializeToStream(stream); - stream.writeInt32(pts); - stream.writeInt32(pts_count); - } - } - - public static class TL_updateMessageID extends Update { - public static int constructor = 0x4e90bfd6; - - - public void readParams(AbsSerializedData stream) { - id = stream.readInt32(); - random_id = stream.readInt64(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(id); - stream.writeInt64(random_id); - } - } - - public static class TL_updateDeleteMessages extends Update { - public static int constructor = 0xa20db0e5; - - public void readParams(AbsSerializedData stream) { - stream.readInt32(); - int count = stream.readInt32(); - for (int a = 0; a < count; a++) { - messages.add(stream.readInt32()); - } - pts = stream.readInt32(); - pts_count = stream.readInt32(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(0x1cb5c415); - int count = messages.size(); - stream.writeInt32(count); - for (int a = 0; a < count; a++) { - stream.writeInt32(messages.get(a)); - } - stream.writeInt32(pts); - stream.writeInt32(pts_count); - } - } - - public static class TL_updateEncryptedChatTyping extends Update { - public static int constructor = 0x1710f156; - - - public void readParams(AbsSerializedData stream) { - chat_id = stream.readInt32(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(chat_id); - } - } - - public static class TL_updateDcOptions extends Update { - public static int constructor = 0x8e5e9873; - - - public void readParams(AbsSerializedData stream) { - stream.readInt32(); - int count = stream.readInt32(); - for (int a = 0; a < count; a++) { - dc_options.add((TL_dcOption)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32())); - } - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(0x1cb5c415); - int count = dc_options.size(); - stream.writeInt32(count); - for (TL_dcOption dc_option : dc_options) { - dc_option.serializeToStream(stream); - } - } - } - - public static class TL_updateChatParticipants extends Update { - public static int constructor = 0x7761198; - - - public void readParams(AbsSerializedData stream) { - participants = (ChatParticipants)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - participants.serializeToStream(stream); - } - } - - public static class TL_updateUserPhone extends Update { - public static int constructor = 0x12b9417b; - - - public void readParams(AbsSerializedData stream) { - user_id = stream.readInt32(); - phone = stream.readString(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(user_id); - stream.writeString(phone); - } - } - - public static class TL_updatePrivacy extends Update { - public static int constructor = 0xee3b272a; - - - public void readParams(AbsSerializedData stream) { - key = (TL_privacyKeyStatusTimestamp)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - stream.readInt32(); - int count = stream.readInt32(); - for (int a = 0; a < count; a++) { - rules.add((PrivacyRule)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32())); - } - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - key.serializeToStream(stream); - stream.writeInt32(0x1cb5c415); - int count = rules.size(); - stream.writeInt32(count); - for (PrivacyRule rule : rules) { - rule.serializeToStream(stream); - } - } - } - - public static class TL_updateEncryption extends Update { - public static int constructor = 0xb4a2e88d; - - - public void readParams(AbsSerializedData stream) { - chat = (EncryptedChat)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - date = stream.readInt32(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - chat.serializeToStream(stream); - stream.writeInt32(date); - } - } - - public static class TL_updateUserBlocked extends Update { - public static int constructor = 0x80ece81a; - - - public void readParams(AbsSerializedData stream) { - user_id = stream.readInt32(); - blocked = stream.readBool(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(user_id); - stream.writeBool(blocked); - } - } - - public static class TL_updateActivation extends Update { - public static int constructor = 0x6f690963; - - - public void readParams(AbsSerializedData stream) { - user_id = stream.readInt32(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(user_id); - } - } - - public static class TL_updateNewAuthorization extends Update { - public static int constructor = 0x8f06529a; - - - public void readParams(AbsSerializedData stream) { - auth_key_id = stream.readInt64(); - date = stream.readInt32(); - device = stream.readString(); - location = stream.readString(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt64(auth_key_id); - stream.writeInt32(date); - stream.writeString(device); - stream.writeString(location); - } - } - - public static class TL_updateNewGeoChatMessage extends Update { - public static int constructor = 0x5a68e3f7; - - public GeoChatMessage message; - - public void readParams(AbsSerializedData stream) { - message = (GeoChatMessage)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - message.serializeToStream(stream); - } - } - - public static class TL_updateUserPhoto extends Update { - public static int constructor = 0x95313b0c; - - - public void readParams(AbsSerializedData stream) { - user_id = stream.readInt32(); - date = stream.readInt32(); - photo = (UserProfilePhoto)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - previous = stream.readBool(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(user_id); - stream.writeInt32(date); - photo.serializeToStream(stream); - stream.writeBool(previous); - } - } - - public static class TL_updateContactRegistered extends Update { - public static int constructor = 0x2575bbb9; - - - public void readParams(AbsSerializedData stream) { - user_id = stream.readInt32(); - date = stream.readInt32(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(user_id); - stream.writeInt32(date); - } - } - - public static class TL_updateChatParticipantAdd extends Update { - public static int constructor = 0x3a0eeb22; - - - public void readParams(AbsSerializedData stream) { - chat_id = stream.readInt32(); - user_id = stream.readInt32(); - inviter_id = stream.readInt32(); - version = stream.readInt32(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(chat_id); - stream.writeInt32(user_id); - stream.writeInt32(inviter_id); - stream.writeInt32(version); - } - } - - public static class TL_updateUserStatus extends Update { - public static int constructor = 0x1bfbd823; - - - public void readParams(AbsSerializedData stream) { - user_id = stream.readInt32(); - status = (UserStatus)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(user_id); - status.serializeToStream(stream); - } - } - - public static class TL_contacts_suggested extends TLObject { - public static int constructor = 0x5649dcc5; - - public ArrayList results = new ArrayList<>(); - public ArrayList users = new ArrayList<>(); - - public void readParams(AbsSerializedData stream) { - stream.readInt32(); - int count = stream.readInt32(); - for (int a = 0; a < count; a++) { - results.add((TL_contactSuggested)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32())); - } - stream.readInt32(); - count = stream.readInt32(); - for (int a = 0; a < count; a++) { - users.add((User)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32())); - } - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(0x1cb5c415); - int count = results.size(); - stream.writeInt32(count); - for (int a = 0; a < count; a++) { - results.get(a).serializeToStream(stream); - } - stream.writeInt32(0x1cb5c415); - count = users.size(); - stream.writeInt32(count); - for (int a = 0; a < count; a++) { - users.get(a).serializeToStream(stream); - } - } - } - - public static class RpcError extends TLObject { - public int error_code; - public String error_message; - public long query_id; - } - - public static class TL_rpc_error extends RpcError { - public static int constructor = 0x2144ca19; - - - public void readParams(AbsSerializedData stream) { - error_code = stream.readInt32(); - error_message = stream.readString(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(error_code); - stream.writeString(error_message); - } - } - - public static class TL_rpc_req_error extends RpcError { - public static int constructor = 0x7ae432f5; - - - public void readParams(AbsSerializedData stream) { - query_id = stream.readInt64(); - error_code = stream.readInt32(); - error_message = stream.readString(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt64(query_id); - stream.writeInt32(error_code); - stream.writeString(error_message); - } - } - - public static class TL_inputEncryptedFile extends InputEncryptedFile { - public static int constructor = 0x5a17b5e5; - - - public void readParams(AbsSerializedData stream) { - id = stream.readInt64(); - access_hash = stream.readInt64(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt64(id); - stream.writeInt64(access_hash); - } - } - - public static class TL_inputEncryptedFileBigUploaded extends InputEncryptedFile { - public static int constructor = 0x2dc173c8; - - - public void readParams(AbsSerializedData stream) { - id = stream.readInt64(); - parts = stream.readInt32(); - key_fingerprint = stream.readInt32(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt64(id); - stream.writeInt32(parts); - stream.writeInt32(key_fingerprint); - } - } - - public static class TL_inputEncryptedFileEmpty extends InputEncryptedFile { - public static int constructor = 0x1837c364; - - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - } - } - - public static class TL_inputEncryptedFileUploaded extends InputEncryptedFile { - public static int constructor = 0x64bd0306; - - - public void readParams(AbsSerializedData stream) { - id = stream.readInt64(); - parts = stream.readInt32(); - md5_checksum = stream.readString(); - key_fingerprint = stream.readInt32(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt64(id); - stream.writeInt32(parts); - stream.writeString(md5_checksum); - stream.writeInt32(key_fingerprint); - } - } - - public static class DecryptedMessageAction extends TLObject { - public int start_seq_no; - public int end_seq_no; - public int ttl_seconds; - public int layer; - public ArrayList random_ids = new ArrayList<>(); - public long exchange_id; - public long key_fingerprint; - public byte[] g_b; - public SendMessageAction action; - public byte[] g_a; - } - - public static class TL_decryptedMessageActionSetMessageTTL extends DecryptedMessageAction { - public static int constructor = 0xa1733aec; - - public void readParams(AbsSerializedData stream) { - ttl_seconds = stream.readInt32(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(ttl_seconds); - } - } - - public static class TL_decryptedMessageActionFlushHistory extends DecryptedMessageAction { - public static int constructor = 0x6719e45c; - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - } - } - - public static class TL_decryptedMessageActionAcceptKey extends DecryptedMessageAction { - public static int constructor = 0x6fe1735b; - - - public void readParams(AbsSerializedData stream) { - exchange_id = stream.readInt64(); - g_b = stream.readByteArray(); - key_fingerprint = stream.readInt64(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt64(exchange_id); - stream.writeByteArray(g_b); - stream.writeInt64(key_fingerprint); - } - } - - public static class TL_decryptedMessageActionResend extends DecryptedMessageAction { - public static int constructor = 0x511110b0; - - - public void readParams(AbsSerializedData stream) { - start_seq_no = stream.readInt32(); - end_seq_no = stream.readInt32(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(start_seq_no); - stream.writeInt32(end_seq_no); - } - } - - public static class TL_decryptedMessageActionNotifyLayer extends DecryptedMessageAction { - public static int constructor = 0xf3048883; - - - public void readParams(AbsSerializedData stream) { - layer = stream.readInt32(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(layer); - } - } - - public static class TL_decryptedMessageActionReadMessages extends DecryptedMessageAction { - public static int constructor = 0xc4f40be; - - - public void readParams(AbsSerializedData stream) { - stream.readInt32(); - int count = stream.readInt32(); - for (int a = 0; a < count; a++) { - random_ids.add(stream.readInt64()); - } - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(0x1cb5c415); - int count = random_ids.size(); - stream.writeInt32(count); - for (Long random_id : random_ids) { - stream.writeInt64(random_id); - } - } - } - - public static class TL_decryptedMessageActionRequestKey extends DecryptedMessageAction { - public static int constructor = 0xf3c9611b; - - - public void readParams(AbsSerializedData stream) { - exchange_id = stream.readInt64(); - g_a = stream.readByteArray(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt64(exchange_id); - stream.writeByteArray(g_a); - } - } - - public static class TL_decryptedMessageActionTyping extends DecryptedMessageAction { - public static int constructor = 0xccb27641; - - - public void readParams(AbsSerializedData stream) { - action = (SendMessageAction)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - action.serializeToStream(stream); - } - } - - public static class TL_server_DH_inner_data extends TLObject { - public static int constructor = 0xb5890dba; - - public byte[] nonce; - public byte[] server_nonce; - public int g; - public byte[] dh_prime; - public byte[] g_a; - public int server_time; - - public void readParams(AbsSerializedData stream) { - nonce = stream.readData(16); - server_nonce = stream.readData(16); - g = stream.readInt32(); - dh_prime = stream.readByteArray(); - g_a = stream.readByteArray(); - server_time = stream.readInt32(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeRaw(nonce); - stream.writeRaw(server_nonce); - stream.writeInt32(g); - stream.writeByteArray(dh_prime); - stream.writeByteArray(g_a); - stream.writeInt32(server_time); - } - } - - public static class TL_new_session_created extends TLObject { - public static int constructor = 0x9ec20908; - - public long first_msg_id; - public long unique_id; - public long server_salt; - - public void readParams(AbsSerializedData stream) { - first_msg_id = stream.readInt64(); - unique_id = stream.readInt64(); - server_salt = stream.readInt64(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt64(first_msg_id); - stream.writeInt64(unique_id); - stream.writeInt64(server_salt); - } - } - - public static class messages_AllStickers extends TLObject { - public String hash; - public ArrayList packs = new ArrayList<>(); - public ArrayList documents = new ArrayList<>(); - } - - public static class TL_messages_allStickers extends messages_AllStickers { - public static int constructor = 0xdcef3102; - - - public void readParams(AbsSerializedData stream) { - hash = stream.readString(); - stream.readInt32(); - int count = stream.readInt32(); - for (int a = 0; a < count; a++) { - packs.add((TL_stickerPack)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32())); - } - stream.readInt32(); - count = stream.readInt32(); - for (int a = 0; a < count; a++) { - documents.add((Document)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32())); - } - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeString(hash); - stream.writeInt32(0x1cb5c415); - int count = packs.size(); - stream.writeInt32(count); - for (int a = 0; a < count; a++) { - packs.get(a).serializeToStream(stream); - } - stream.writeInt32(0x1cb5c415); - count = documents.size(); - stream.writeInt32(count); - for (int a = 0; a < count; a++) { - documents.get(a).serializeToStream(stream); - } - } - } - - public static class TL_messages_allStickersNotModified extends messages_AllStickers { - public static int constructor = 0xe86602c3; - - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - } - } - - public static class account_Password extends TLObject { - public byte[] current_salt; - public byte[] new_salt; - public String hint; - public boolean has_recovery; - public String email_unconfirmed_pattern; - } - - public static class TL_account_password extends account_Password { - public static int constructor = 0x7c18141c; - - - public void readParams(AbsSerializedData stream) { - current_salt = stream.readByteArray(); - new_salt = stream.readByteArray(); - hint = stream.readString(); - has_recovery = stream.readBool(); - email_unconfirmed_pattern = stream.readString(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeByteArray(current_salt); - stream.writeByteArray(new_salt); - stream.writeString(hint); - stream.writeBool(has_recovery); - stream.writeString(email_unconfirmed_pattern); - } - } - - public static class TL_account_noPassword extends account_Password { - public static int constructor = 0x96dabc18; - - - public void readParams(AbsSerializedData stream) { - new_salt = stream.readByteArray(); - email_unconfirmed_pattern = stream.readString(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeByteArray(new_salt); - stream.writeString(email_unconfirmed_pattern); - } - } - - public static class UserProfilePhoto extends TLObject { - public long photo_id; - public FileLocation photo_small; - public FileLocation photo_big; - } - - public static class TL_userProfilePhotoEmpty extends UserProfilePhoto { - public static int constructor = 0x4f11bae1; - - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - } - } - - public static class TL_userProfilePhoto extends UserProfilePhoto { - public static int constructor = 0xd559d8c8; - - - public void readParams(AbsSerializedData stream) { - photo_id = stream.readInt64(); - photo_small = (FileLocation)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - photo_big = (FileLocation)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt64(photo_id); - photo_small.serializeToStream(stream); - photo_big.serializeToStream(stream); - } - } - - public static class Photo extends TLObject { - public long id; - public long access_hash; - public int user_id; - public int date; - public String caption; - public GeoPoint geo; - public ArrayList sizes = new ArrayList<>(); - } - - public static class TL_photo extends Photo { - public static int constructor = 0x22b56751; - - - public void readParams(AbsSerializedData stream) { - id = stream.readInt64(); - access_hash = stream.readInt64(); - user_id = stream.readInt32(); - date = stream.readInt32(); - caption = stream.readString(); - geo = (GeoPoint)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - stream.readInt32(); - int count = stream.readInt32(); - for (int a = 0; a < count; a++) { - PhotoSize size = (PhotoSize)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - if (size != null) { - sizes.add(size); - } - } - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt64(id); - stream.writeInt64(access_hash); - stream.writeInt32(user_id); - stream.writeInt32(date); - stream.writeString(caption); - geo.serializeToStream(stream); - stream.writeInt32(0x1cb5c415); - int count = sizes.size(); - stream.writeInt32(count); - for (PhotoSize size : sizes) { - size.serializeToStream(stream); - } - } - } - - public static class TL_photoEmpty extends Photo { - public static int constructor = 0x2331b22d; - - - public void readParams(AbsSerializedData stream) { - id = stream.readInt64(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt64(id); - } - } - - public static class TL_encryptedChatWaiting extends EncryptedChat { - public static int constructor = 0x3bf703dc; - - - public void readParams(AbsSerializedData stream) { - id = stream.readInt32(); - access_hash = stream.readInt64(); - date = stream.readInt32(); - admin_id = stream.readInt32(); - participant_id = stream.readInt32(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(id); - stream.writeInt64(access_hash); - stream.writeInt32(date); - stream.writeInt32(admin_id); - stream.writeInt32(participant_id); - } - } - - public static class TL_encryptedChatEmpty extends EncryptedChat { - public static int constructor = 0xab7ec0a0; - - - public void readParams(AbsSerializedData stream) { - id = stream.readInt32(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(id); - } - } - - public static class TL_encryptedChatDiscarded extends EncryptedChat { - public static int constructor = 0x13d6dd27; - - - public void readParams(AbsSerializedData stream) { - id = stream.readInt32(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(id); - } - } - - public static class TL_encryptedChat extends EncryptedChat { - public static int constructor = 0xfa56ce36; - - - public void readParams(AbsSerializedData stream) { - id = stream.readInt32(); - access_hash = stream.readInt64(); - date = stream.readInt32(); - admin_id = stream.readInt32(); - participant_id = stream.readInt32(); - g_a_or_b = stream.readByteArray(); - key_fingerprint = stream.readInt64(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(id); - stream.writeInt64(access_hash); - stream.writeInt32(date); - stream.writeInt32(admin_id); - stream.writeInt32(participant_id); - stream.writeByteArray(g_a_or_b); - stream.writeInt64(key_fingerprint); - } - } - - public static class TL_encryptedChatRequested extends EncryptedChat { - public static int constructor = 0xc878527e; - - - public void readParams(AbsSerializedData stream) { - id = stream.readInt32(); - access_hash = stream.readInt64(); - date = stream.readInt32(); - admin_id = stream.readInt32(); - participant_id = stream.readInt32(); - g_a = stream.readByteArray(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(id); - stream.writeInt64(access_hash); - stream.writeInt32(date); - stream.writeInt32(admin_id); - stream.writeInt32(participant_id); - stream.writeByteArray(g_a); - } - } - - public static class TL_geochats_statedMessage extends TLObject { - public static int constructor = 0x17b1578b; - - public GeoChatMessage message; - public ArrayList chats = new ArrayList<>(); - public ArrayList users = new ArrayList<>(); - public int seq; - - public void readParams(AbsSerializedData stream) { - message = (GeoChatMessage)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - stream.readInt32(); - int count = stream.readInt32(); - for (int a = 0; a < count; a++) { - chats.add((Chat)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32())); - } - stream.readInt32(); - count = stream.readInt32(); - for (int a = 0; a < count; a++) { - users.add((User)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32())); - } - seq = stream.readInt32(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - message.serializeToStream(stream); - stream.writeInt32(0x1cb5c415); - int count = chats.size(); - stream.writeInt32(count); - for (int a = 0; a < count; a++) { - chats.get(a).serializeToStream(stream); - } - stream.writeInt32(0x1cb5c415); - count = users.size(); - stream.writeInt32(count); - for (int a = 0; a < count; a++) { - users.get(a).serializeToStream(stream); - } - stream.writeInt32(seq); - } - } - - public static class TL_contact extends TLObject { - public static int constructor = 0xf911c994; - - public int user_id; - public boolean mutual; - - public void readParams(AbsSerializedData stream) { - user_id = stream.readInt32(); - mutual = stream.readBool(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(user_id); - stream.writeBool(mutual); - } - } - - public static class TL_config extends TLObject { - public static int constructor = 0x68bac247; - - public int date; - public int expires; - public boolean test_mode; - public int this_dc; - public ArrayList dc_options = new ArrayList<>(); - public int chat_size_max; - public int broadcast_size_max; - public int forwarded_count_max; - public int online_update_period_ms; - public int offline_blur_timeout_ms; - public int offline_idle_timeout_ms; - public int online_cloud_timeout_ms; - public int notify_cloud_delay_ms; - public int notify_default_delay_ms; - public int chat_big_size; - public ArrayList disabled_features = new ArrayList<>(); - - public void readParams(AbsSerializedData stream) { - date = stream.readInt32(); - expires = stream.readInt32(); - test_mode = stream.readBool(); - this_dc = stream.readInt32(); - stream.readInt32(); - int count = stream.readInt32(); - for (int a = 0; a < count; a++) { - dc_options.add((TL_dcOption)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32())); - } - chat_size_max = stream.readInt32(); - broadcast_size_max = stream.readInt32(); - forwarded_count_max = stream.readInt32(); - online_update_period_ms = stream.readInt32(); - offline_blur_timeout_ms = stream.readInt32(); - offline_idle_timeout_ms = stream.readInt32(); - online_cloud_timeout_ms = stream.readInt32(); - notify_cloud_delay_ms = stream.readInt32(); - notify_default_delay_ms = stream.readInt32(); - chat_big_size = stream.readInt32(); - stream.readInt32(); - count = stream.readInt32(); - for (int a = 0; a < count; a++) { - disabled_features.add((TL_disabledFeature)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32())); - } - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(date); - stream.writeInt32(expires); - stream.writeBool(test_mode); - stream.writeInt32(this_dc); - stream.writeInt32(0x1cb5c415); - int count = dc_options.size(); - stream.writeInt32(count); - for (int a = 0; a < count; a++) { - dc_options.get(a).serializeToStream(stream); - } - stream.writeInt32(chat_size_max); - stream.writeInt32(broadcast_size_max); - stream.writeInt32(forwarded_count_max); - stream.writeInt32(online_update_period_ms); - stream.writeInt32(offline_blur_timeout_ms); - stream.writeInt32(offline_idle_timeout_ms); - stream.writeInt32(online_cloud_timeout_ms); - stream.writeInt32(notify_cloud_delay_ms); - stream.writeInt32(notify_default_delay_ms); - stream.writeInt32(chat_big_size); - stream.writeInt32(0x1cb5c415); - count = disabled_features.size(); - stream.writeInt32(count); - for (int a = 0; a < count; a++) { - disabled_features.get(a).serializeToStream(stream); - } - } - } - - public static class TL_help_support extends TLObject { - public static int constructor = 0x17c6b5f6; - - public String phone_number; - public User user; - - public void readParams(AbsSerializedData stream) { - phone_number = stream.readString(); - user = (User)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeString(phone_number); - user.serializeToStream(stream); - } - } - - public static class TL_help_getSupport extends TLObject { - public static int constructor = 0x9cdf08cd; - - public Class responseClass () { - return TL_help_support.class; - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - } - } - - public static class TL_auth_sendSms extends TLObject { - public static int constructor = 0xda9f3e8; - - public String phone_number; - public String phone_code_hash; - - public Class responseClass () { - return Bool.class; - } - - public void readParams(AbsSerializedData stream) { - phone_number = stream.readString(); - phone_code_hash = stream.readString(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeString(phone_number); - stream.writeString(phone_code_hash); - } - } - - public static class TL_messages_readMessageContents extends TLObject { - public static int constructor = 0x36a73f77; - - public ArrayList id = new ArrayList<>(); - - public Class responseClass () { - return TL_messages_affectedMessages.class; - } - - public void readParams(AbsSerializedData stream) { - stream.readInt32(); - int count = stream.readInt32(); - for (int a = 0; a < count; a++) { - id.add(stream.readInt32()); - } - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(0x1cb5c415); - int count = id.size(); - stream.writeInt32(count); - for (int a = 0; a < count; a++) { - stream.writeInt32(id.get(a)); - } - } - } - - public static class TL_account_getPrivacy extends TLObject { - public static int constructor = 0xdadbc950; - - public TL_inputPrivacyKeyStatusTimestamp key; - - public Class responseClass () { - return TL_account_privacyRules.class; - } - - public void readParams(AbsSerializedData stream) { - key = (TL_inputPrivacyKeyStatusTimestamp)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - key.serializeToStream(stream); - } - } - - public static class TL_account_setPrivacy extends TLObject { - public static int constructor = 0xc9f81ce8; - - public TL_inputPrivacyKeyStatusTimestamp key; - public ArrayList rules = new ArrayList<>(); - - public Class responseClass () { - return TL_account_privacyRules.class; - } - - public void readParams(AbsSerializedData stream) { - key = (TL_inputPrivacyKeyStatusTimestamp)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - stream.readInt32(); - int count = stream.readInt32(); - for (int a = 0; a < count; a++) { - rules.add((InputPrivacyRule)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32())); - } - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - key.serializeToStream(stream); - stream.writeInt32(0x1cb5c415); - int count = rules.size(); - stream.writeInt32(count); - for (InputPrivacyRule rule : rules) { - rule.serializeToStream(stream); - } - } - } - - public static class TL_account_deleteAccount extends TLObject { - public static int constructor = 0x418d4e0b; - - public String reason; - - public Class responseClass () { - return Bool.class; - } - - public void readParams(AbsSerializedData stream) { - reason = stream.readString(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeString(reason); - } - } - - public static class TL_account_getAccountTTL extends TLObject { - public static int constructor = 0x8fc711d; - - - public Class responseClass () { - return TL_accountDaysTTL.class; - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - } - } - - public static class TL_account_setAccountTTL extends TLObject { - public static int constructor = 0x2442485e; - - public TL_accountDaysTTL ttl; - - public Class responseClass () { - return Bool.class; - } - - public void readParams(AbsSerializedData stream) { - ttl = (TL_accountDaysTTL)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - ttl.serializeToStream(stream); - } - } - - public static class TL_account_sendChangePhoneCode extends TLObject { - public static int constructor = 0xa407a8f4; - - public String phone_number; - - public Class responseClass() { - return TL_account_sentChangePhoneCode.class; - } - - public void readParams(AbsSerializedData stream) { - phone_number = stream.readString(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeString(phone_number); - } - } - - public static class TL_account_changePhone extends TLObject { - public static int constructor = 0x70c32edb; - - public String phone_number; - public String phone_code_hash; - public String phone_code; - - public Class responseClass () { - return User.class; - } - - public void readParams(AbsSerializedData stream) { - phone_number = stream.readString(); - phone_code_hash = stream.readString(); - phone_code = stream.readString(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeString(phone_number); - stream.writeString(phone_code_hash); - stream.writeString(phone_code); - } - } - - public static class TL_account_updateDeviceLocked extends TLObject { - public static int constructor = 0x38df3532; - - public int period; - - public Class responseClass () { - return Bool.class; - } - - public void readParams(AbsSerializedData stream) { - period = stream.readInt32(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(period); - } - } - - public static class TL_account_getAuthorizations extends TLObject { - public static int constructor = 0xe320c158; - - - public Class responseClass () { - return TL_account_authorizations.class; - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - } - } - - public static class TL_account_resetAuthorization extends TLObject { - public static int constructor = 0xdf77f3bc; - - public long hash; - - public Class responseClass () { - return Bool.class; - } - - public void readParams(AbsSerializedData stream) { - hash = stream.readInt64(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt64(hash); - } - } - - public static class TL_messages_getAllStickers extends TLObject { - public static int constructor = 0xaa3bc868; - - public String hash; - - public Class responseClass () { - return messages_AllStickers.class; - } - - public void readParams(AbsSerializedData stream) { - hash = stream.readString(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeString(hash); - } - } - - public static class TL_messages_getWebPagePreview extends TLObject { - public static int constructor = 0x25223e24; - - public String message; - - public Class responseClass () { - return MessageMedia.class; - } - - public void readParams(AbsSerializedData stream) { - message = stream.readString(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeString(message); - } - } - - public static class TL_account_checkUsername extends TLObject { - public static int constructor = 0x2714d86c; - - public String username; - - public Class responseClass () { - return Bool.class; - } - - public void readParams(AbsSerializedData stream) { - username = stream.readString(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeString(username); - } - } - - public static class TL_account_updateUsername extends TLObject { - public static int constructor = 0x3e0bdd7c; - - public String username; - - public Class responseClass () { - return User.class; - } - - public void readParams(AbsSerializedData stream) { - username = stream.readString(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeString(username); - } - } - - public static class TL_contacts_resolveUsername extends TLObject { - public static int constructor = 0xbf0131c; - - public String username; - - public Class responseClass () { - return User.class; - } - - public void readParams(AbsSerializedData stream) { - username = stream.readString(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeString(username); - } - } - - public static class InputAudio extends TLObject { - public long id; - public long access_hash; - } - - public static class TL_inputAudio extends InputAudio { - public static int constructor = 0x77d440ff; - - - public void readParams(AbsSerializedData stream) { - id = stream.readInt64(); - access_hash = stream.readInt64(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt64(id); - stream.writeInt64(access_hash); - } - } - - public static class TL_inputAudioEmpty extends InputAudio { - public static int constructor = 0xd95adc84; - - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - } - } - - public static class TL_messages_chats extends TLObject { - public static int constructor = 0x64ff9fd5; - - public ArrayList chats = new ArrayList<>(); - - public void readParams(AbsSerializedData stream) { - stream.readInt32(); - int count = stream.readInt32(); - for (int a = 0; a < count; a++) { - chats.add((Chat)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32())); - } - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(0x1cb5c415); - int count = chats.size(); - stream.writeInt32(count); - for (int a = 0; a < count; a++) { - chats.get(a).serializeToStream(stream); - } - } - } - - public static class TL_contacts_found extends TLObject { - public static int constructor = 0x566000e; - - public ArrayList results = new ArrayList<>(); - public ArrayList users = new ArrayList<>(); - - public void readParams(AbsSerializedData stream) { - stream.readInt32(); - int count = stream.readInt32(); - for (int a = 0; a < count; a++) { - results.add((TL_contactFound)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32())); - } - stream.readInt32(); - count = stream.readInt32(); - for (int a = 0; a < count; a++) { - users.add((User)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32())); - } - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(0x1cb5c415); - int count = results.size(); - stream.writeInt32(count); - for (int a = 0; a < count; a++) { - results.get(a).serializeToStream(stream); - } - stream.writeInt32(0x1cb5c415); - count = users.size(); - stream.writeInt32(count); - for (int a = 0; a < count; a++) { - users.get(a).serializeToStream(stream); - } - } - } - - public static class ChatParticipants extends TLObject { - public int chat_id; - public int admin_id; - public ArrayList participants = new ArrayList<>(); - public int version; - } - - public static class TL_chatParticipants extends ChatParticipants { - public static int constructor = 0x7841b415; - - - public void readParams(AbsSerializedData stream) { - chat_id = stream.readInt32(); - admin_id = stream.readInt32(); - stream.readInt32(); - int count = stream.readInt32(); - for (int a = 0; a < count; a++) { - participants.add((TL_chatParticipant)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32())); - } - version = stream.readInt32(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(chat_id); - stream.writeInt32(admin_id); - stream.writeInt32(0x1cb5c415); - int count = participants.size(); - stream.writeInt32(count); - for (TL_chatParticipant participant : participants) { - participant.serializeToStream(stream); - } - stream.writeInt32(version); - } - } - - public static class TL_chatParticipantsForbidden extends ChatParticipants { - public static int constructor = 0xfd2bb8a; - - - public void readParams(AbsSerializedData stream) { - chat_id = stream.readInt32(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(chat_id); - } - } - - public static class DecryptedMessageMedia extends TLObject { - public long id; - public long access_hash; - public int date; - public PhotoSize thumbImage; - public byte[] thumb; - public int thumb_w; - public int thumb_h; - public String file_name; - public String mime_type; - public int size; - public int dc_id; - public ArrayList attributes = new ArrayList<>(); - public byte[] key; - public byte[] iv; - public double lat; - public double _long; - public int duration; - public int w; - public int h; - public String phone_number; - public String first_name; - public String last_name; - public int user_id; - } - - public static class TL_decryptedMessageMediaExternalDocument extends DecryptedMessageMedia { - public static int constructor = 0xfa95b0dd; - - public void readParams(AbsSerializedData stream) { - id = stream.readInt64(); - access_hash = stream.readInt64(); - date = stream.readInt32(); - mime_type = stream.readString(); - size = stream.readInt32(); - thumbImage = (PhotoSize)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - dc_id = stream.readInt32(); - stream.readInt32(); - int count = stream.readInt32(); - for (int a = 0; a < count; a++) { - attributes.add((DocumentAttribute)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32())); - } - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt64(id); - stream.writeInt64(access_hash); - stream.writeInt32(date); - stream.writeString(mime_type); - stream.writeInt32(size); - thumbImage.serializeToStream(stream); - stream.writeInt32(dc_id); - stream.writeInt32(0x1cb5c415); - int count = attributes.size(); - stream.writeInt32(count); - for (int a = 0; a < count; a++) { - attributes.get(a).serializeToStream(stream); - } - } - } - - public static class TL_decryptedMessageMediaDocument extends DecryptedMessageMedia { - public static int constructor = 0xb095434b; - - - public void readParams(AbsSerializedData stream) { - thumb = stream.readByteArray(); - thumb_w = stream.readInt32(); - thumb_h = stream.readInt32(); - file_name = stream.readString(); - mime_type = stream.readString(); - size = stream.readInt32(); - key = stream.readByteArray(); - iv = stream.readByteArray(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeByteArray(thumb); - stream.writeInt32(thumb_w); - stream.writeInt32(thumb_h); - stream.writeString(file_name); - stream.writeString(mime_type); - stream.writeInt32(size); - stream.writeByteArray(key); - stream.writeByteArray(iv); - } - } - - public static class TL_decryptedMessageMediaGeoPoint extends DecryptedMessageMedia { - public static int constructor = 0x35480a59; - - - public void readParams(AbsSerializedData stream) { - lat = stream.readDouble(); - _long = stream.readDouble(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeDouble(lat); - stream.writeDouble(_long); - } - } - - public static class TL_decryptedMessageMediaAudio extends DecryptedMessageMedia { - public static int constructor = 0x57e0a9cb; - - - public void readParams(AbsSerializedData stream) { - duration = stream.readInt32(); - mime_type = stream.readString(); - size = stream.readInt32(); - key = stream.readByteArray(); - iv = stream.readByteArray(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(duration); - stream.writeString(mime_type); - stream.writeInt32(size); - stream.writeByteArray(key); - stream.writeByteArray(iv); - } - } - - public static class TL_decryptedMessageMediaVideo extends DecryptedMessageMedia { - public static int constructor = 0x524a415d; - - - public void readParams(AbsSerializedData stream) { - thumb = stream.readByteArray(); - thumb_w = stream.readInt32(); - thumb_h = stream.readInt32(); - duration = stream.readInt32(); - mime_type = stream.readString(); - w = stream.readInt32(); - h = stream.readInt32(); - size = stream.readInt32(); - key = stream.readByteArray(); - iv = stream.readByteArray(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeByteArray(thumb); - stream.writeInt32(thumb_w); - stream.writeInt32(thumb_h); - stream.writeInt32(duration); - stream.writeString(mime_type); - stream.writeInt32(w); - stream.writeInt32(h); - stream.writeInt32(size); - stream.writeByteArray(key); - stream.writeByteArray(iv); - } - } - - public static class TL_decryptedMessageMediaContact extends DecryptedMessageMedia { - public static int constructor = 0x588a0a97; - - - public void readParams(AbsSerializedData stream) { - phone_number = stream.readString(); - first_name = stream.readString(); - last_name = stream.readString(); - user_id = stream.readInt32(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeString(phone_number); - stream.writeString(first_name); - stream.writeString(last_name); - stream.writeInt32(user_id); - } - } - - public static class TL_decryptedMessageMediaEmpty extends DecryptedMessageMedia { - public static int constructor = 0x89f5c4a; - - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - } - } - - public static class TL_decryptedMessageMediaPhoto extends DecryptedMessageMedia { - public static int constructor = 0x32798a8c; - - - public void readParams(AbsSerializedData stream) { - thumb = stream.readByteArray(); - thumb_w = stream.readInt32(); - thumb_h = stream.readInt32(); - w = stream.readInt32(); - h = stream.readInt32(); - size = stream.readInt32(); - key = stream.readByteArray(); - iv = stream.readByteArray(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeByteArray(thumb); - stream.writeInt32(thumb_w); - stream.writeInt32(thumb_h); - stream.writeInt32(w); - stream.writeInt32(h); - stream.writeInt32(size); - stream.writeByteArray(key); - stream.writeByteArray(iv); - } - } - - public static class TL_chatParticipant extends TLObject { - public static int constructor = 0xc8d7493e; - - public int user_id; - public int inviter_id; - public int date; - - public void readParams(AbsSerializedData stream) { - user_id = stream.readInt32(); - inviter_id = stream.readInt32(); - date = stream.readInt32(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(user_id); - stream.writeInt32(inviter_id); - stream.writeInt32(date); - } - } - - public static class Chat extends TLObject { - public int id; - public String title; - public int date; - public long access_hash; - public String address; - public String venue; - public GeoPoint geo; - public ChatPhoto photo; - public int participants_count; - public boolean checked_in; - public int version; - public boolean left; - } - - public static class TL_chatForbidden extends Chat { - public static int constructor = 0xfb0ccc41; - - - public void readParams(AbsSerializedData stream) { - id = stream.readInt32(); - title = stream.readString(); - date = stream.readInt32(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(id); - stream.writeString(title); - stream.writeInt32(date); - } - } - - public static class TL_geoChat extends Chat { - public static int constructor = 0x75eaea5a; - - - public void readParams(AbsSerializedData stream) { - id = stream.readInt32(); - access_hash = stream.readInt64(); - title = stream.readString(); - address = stream.readString(); - venue = stream.readString(); - geo = (GeoPoint)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - photo = (ChatPhoto)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - participants_count = stream.readInt32(); - date = stream.readInt32(); - checked_in = stream.readBool(); - version = stream.readInt32(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(id); - stream.writeInt64(access_hash); - stream.writeString(title); - stream.writeString(address); - stream.writeString(venue); - geo.serializeToStream(stream); - photo.serializeToStream(stream); - stream.writeInt32(participants_count); - stream.writeInt32(date); - stream.writeBool(checked_in); - stream.writeInt32(version); - } - } - public static class TL_chat extends Chat { - public static int constructor = 0x6e9c9bc7; - - - public void readParams(AbsSerializedData stream) { - id = stream.readInt32(); - title = stream.readString(); - photo = (ChatPhoto)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - participants_count = stream.readInt32(); - date = stream.readInt32(); - left = stream.readBool(); - version = stream.readInt32(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(id); - stream.writeString(title); - photo.serializeToStream(stream); - stream.writeInt32(participants_count); - stream.writeInt32(date); - stream.writeBool(left); - stream.writeInt32(version); - } - } - public static class storage_FileType extends TLObject { + + public static storage_FileType TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + storage_FileType result = null; + switch(constructor) { + case 0xaa963b05: + result = new TL_storage_fileUnknown(); + break; + case 0xb3cea0e4: + result = new TL_storage_fileMp4(); + break; + case 0x1081464c: + result = new TL_storage_fileWebp(); + break; + case 0xa4f63c0: + result = new TL_storage_filePng(); + break; + case 0xcae1aadf: + result = new TL_storage_fileGif(); + break; + case 0xae1e508d: + result = new TL_storage_filePdf(); + break; + case 0x528a0677: + result = new TL_storage_fileMp3(); + break; + case 0x7efe0e: + result = new TL_storage_fileJpeg(); + break; + case 0x4b09ebbc: + result = new TL_storage_fileMov(); + break; + case 0x40bc6f52: + result = new TL_storage_filePartial(); + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in storage_FileType", constructor)); + } + if (result != null) { + result.readParams(stream, exception); + } + return result; + } } public static class TL_storage_fileUnknown extends storage_FileType { @@ -6114,6 +7962,15 @@ public class TLRPC { } } + public static class TL_storage_fileMp4 extends storage_FileType { + public static int constructor = 0xb3cea0e4; + + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + public static class TL_storage_fileWebp extends storage_FileType { public static int constructor = 0x1081464c; @@ -6150,15 +8007,6 @@ public class TLRPC { } } - public static class TL_storage_fileMov extends storage_FileType { - public static int constructor = 0x4b09ebbc; - - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - } - } - public static class TL_storage_fileMp3 extends storage_FileType { public static int constructor = 0x528a0677; @@ -6177,6 +8025,15 @@ public class TLRPC { } } + public static class TL_storage_fileMov extends storage_FileType { + public static int constructor = 0x4b09ebbc; + + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + public static class TL_storage_filePartial extends storage_FileType { public static int constructor = 0x40bc6f52; @@ -6186,522 +8043,255 @@ public class TLRPC { } } - public static class TL_storage_fileMp4 extends storage_FileType { - public static int constructor = 0xb3cea0e4; + public static class TL_account_authorizations extends TLObject { + public static int constructor = 0x1250abde; + public ArrayList authorizations = new ArrayList<>(); - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - } - } - - public static class MessagesFilter extends TLObject { - } - - public static class TL_inputMessagesFilterVideo extends MessagesFilter { - public static int constructor = 0x9fc00e65; - - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - } - } - - public static class TL_inputMessagesFilterEmpty extends MessagesFilter { - public static int constructor = 0x57e2f66c; - - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - } - } - - public static class TL_inputMessagesFilterPhotos extends MessagesFilter { - public static int constructor = 0x9609a51c; - - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - } - } - - public static class TL_inputMessagesFilterPhotoVideo extends MessagesFilter { - public static int constructor = 0x56e9f0e4; - - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - } - } - - public static class TL_inputMessagesFilterDocument extends MessagesFilter { - public static int constructor = 0x9eddf188; - - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - } - } - - public static class TL_inputMessagesFilterAudio extends MessagesFilter { - public static int constructor = 0xcfc87522; - - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - } - } - - public static class TL_msgs_state_info extends TLObject { - public static int constructor = 0x04deb57d; - - public long req_msg_id; - public String info; - - public void readParams(AbsSerializedData stream) { - req_msg_id = stream.readInt64(); - info = stream.readString(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt64(req_msg_id); - stream.writeString(info); - } - } - - public static class TL_fileLocation extends FileLocation { - public static int constructor = 0x53d69076; - - - public void readParams(AbsSerializedData stream) { - dc_id = stream.readInt32(); - volume_id = stream.readInt64(); - local_id = stream.readInt32(); - secret = stream.readInt64(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(dc_id); - stream.writeInt64(volume_id); - stream.writeInt32(local_id); - stream.writeInt64(secret); - } - } - - public static class TL_fileLocationUnavailable extends FileLocation { - public static int constructor = 0x7c596b46; - - - public void readParams(AbsSerializedData stream) { - volume_id = stream.readInt64(); - local_id = stream.readInt32(); - secret = stream.readInt64(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt64(volume_id); - stream.writeInt32(local_id); - stream.writeInt64(secret); - } - } - - public static class messages_Message extends TLObject { - public Message message; - public ArrayList chats = new ArrayList<>(); - public ArrayList users = new ArrayList<>(); - } - - public static class TL_messages_messageEmpty extends messages_Message { - public static int constructor = 0x3f4e0648; - - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - } - } - - public static class TL_messages_message extends messages_Message { - public static int constructor = 0xff90c417; - - - public void readParams(AbsSerializedData stream) { - message = (Message)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - stream.readInt32(); - int count = stream.readInt32(); - for (int a = 0; a < count; a++) { - chats.add((Chat)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32())); - } - stream.readInt32(); - count = stream.readInt32(); - for (int a = 0; a < count; a++) { - users.add((User)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32())); + public static TL_account_authorizations TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + if (TL_account_authorizations.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_account_authorizations", constructor)); + } else { + return null; + } } + TL_account_authorizations result = new TL_account_authorizations(); + result.readParams(stream, exception); + return result; } - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - message.serializeToStream(stream); - stream.writeInt32(0x1cb5c415); - int count = chats.size(); - stream.writeInt32(count); - for (int a = 0; a < count; a++) { - chats.get(a).serializeToStream(stream); + public void readParams(AbsSerializedData stream, boolean exception) { + int magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; } - stream.writeInt32(0x1cb5c415); - count = users.size(); - stream.writeInt32(count); + int count = stream.readInt32(exception); for (int a = 0; a < count; a++) { - users.get(a).serializeToStream(stream); - } - } - } - - public static class TL_geochats_located extends TLObject { - public static int constructor = 0x48feb267; - - public ArrayList results = new ArrayList<>(); - public ArrayList messages = new ArrayList<>(); - public ArrayList chats = new ArrayList<>(); - public ArrayList users = new ArrayList<>(); - - public void readParams(AbsSerializedData stream) { - stream.readInt32(); - int count = stream.readInt32(); - for (int a = 0; a < count; a++) { - results.add((TL_chatLocated)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32())); - } - stream.readInt32(); - count = stream.readInt32(); - for (int a = 0; a < count; a++) { - messages.add((GeoChatMessage)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32())); - } - stream.readInt32(); - count = stream.readInt32(); - for (int a = 0; a < count; a++) { - chats.add((Chat)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32())); - } - stream.readInt32(); - count = stream.readInt32(); - for (int a = 0; a < count; a++) { - users.add((User)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32())); + authorizations.add(TL_authorization.TLdeserialize(stream, stream.readInt32(exception), exception)); } } public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(0x1cb5c415); - int count = results.size(); + int count = authorizations.size(); stream.writeInt32(count); for (int a = 0; a < count; a++) { - results.get(a).serializeToStream(stream); - } - stream.writeInt32(0x1cb5c415); - count = messages.size(); - stream.writeInt32(count); - for (int a = 0; a < count; a++) { - messages.get(a).serializeToStream(stream); - } - stream.writeInt32(0x1cb5c415); - count = chats.size(); - stream.writeInt32(count); - for (int a = 0; a < count; a++) { - chats.get(a).serializeToStream(stream); - } - stream.writeInt32(0x1cb5c415); - count = users.size(); - stream.writeInt32(count); - for (int a = 0; a < count; a++) { - users.get(a).serializeToStream(stream); + authorizations.get(a).serializeToStream(stream); } } } - public static class TL_inputGeoChat extends TLObject { - public static int constructor = 0x74d456fa; + public static class TL_p_q_inner_data extends TLObject { + public static int constructor = 0x83c95aec; - public int chat_id; + public byte[] pq; + public byte[] p; + public byte[] q; + public byte[] nonce; + public byte[] server_nonce; + public byte[] new_nonce; + + public static TL_p_q_inner_data TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + if (TL_p_q_inner_data.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_p_q_inner_data", constructor)); + } else { + return null; + } + } + TL_p_q_inner_data result = new TL_p_q_inner_data(); + result.readParams(stream, exception); + return result; + } + + public void readParams(AbsSerializedData stream, boolean exception) { + pq = stream.readByteArray(exception); + p = stream.readByteArray(exception); + q = stream.readByteArray(exception); + nonce = stream.readData(16, exception); + server_nonce = stream.readData(16, exception); + new_nonce = stream.readData(32, exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeByteArray(pq); + stream.writeByteArray(p); + stream.writeByteArray(q); + stream.writeRaw(nonce); + stream.writeRaw(server_nonce); + stream.writeRaw(new_nonce); + } + } + + public static class InputPhoto extends TLObject { + public long id; public long access_hash; - public void readParams(AbsSerializedData stream) { - chat_id = stream.readInt32(); - access_hash = stream.readInt64(); + public static InputPhoto TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + InputPhoto result = null; + switch(constructor) { + case 0x1cd7bf0d: + result = new TL_inputPhotoEmpty(); + break; + case 0xfb95c6c4: + result = new TL_inputPhoto(); + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in InputPhoto", constructor)); + } + if (result != null) { + result.readParams(stream, exception); + } + return result; + } + } + + public static class TL_inputPhotoEmpty extends InputPhoto { + public static int constructor = 0x1cd7bf0d; + + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class TL_inputPhoto extends InputPhoto { + public static int constructor = 0xfb95c6c4; + + + public void readParams(AbsSerializedData stream, boolean exception) { + id = stream.readInt64(exception); + access_hash = stream.readInt64(exception); } public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); - stream.writeInt32(chat_id); + stream.writeInt64(id); stream.writeInt64(access_hash); } } - public static class TL_protoMessage extends TLObject { - public static int constructor = 0x5bb8e511; + public static class contacts_Contacts extends TLObject { + public ArrayList contacts = new ArrayList<>(); + public ArrayList users = new ArrayList<>(); - public long msg_id; - public int seqno; - public int bytes; - public TLObject body; + public static contacts_Contacts TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + contacts_Contacts result = null; + switch(constructor) { + case 0xb74ba9d2: + result = new TL_contacts_contactsNotModified(); + break; + case 0x6f8b8cb2: + result = new TL_contacts_contacts(); + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in contacts_Contacts", constructor)); + } + if (result != null) { + result.readParams(stream, exception); + } + return result; + } + } - public void readParams(AbsSerializedData stream) { - msg_id = stream.readInt64(); - seqno = stream.readInt32(); - bytes = stream.readInt32(); - body = TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); + public static class TL_contacts_contactsNotModified extends contacts_Contacts { + public static int constructor = 0xb74ba9d2; + + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class TL_contacts_contacts extends contacts_Contacts { + public static int constructor = 0x6f8b8cb2; + + + public void readParams(AbsSerializedData stream, boolean exception) { + int magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + int count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + contacts.add(TL_contact.TLdeserialize(stream, stream.readInt32(exception), exception)); + } + magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + users.add(User.TLdeserialize(stream, stream.readInt32(exception), exception)); + } } public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); - stream.writeInt64(msg_id); - stream.writeInt32(seqno); - stream.writeInt32(bytes); - body.serializeToStream(stream); + stream.writeInt32(0x1cb5c415); + int count = contacts.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + contacts.get(a).serializeToStream(stream); + } + stream.writeInt32(0x1cb5c415); + count = users.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + users.get(a).serializeToStream(stream); + } } } - public static class PhotoSize extends TLObject { - public String type; - public FileLocation location; - public int w; - public int h; - public int size; - public byte[] bytes; - } - - public static class TL_photoSize extends PhotoSize { - public static int constructor = 0x77bfb61b; - - - public void readParams(AbsSerializedData stream) { - type = stream.readString(); - location = (FileLocation)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - w = stream.readInt32(); - h = stream.readInt32(); - size = stream.readInt32(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeString(type); - location.serializeToStream(stream); - stream.writeInt32(w); - stream.writeInt32(h); - stream.writeInt32(size); - } - } - - public static class TL_photoSizeEmpty extends PhotoSize { - public static int constructor = 0xe17e23c; - - - public void readParams(AbsSerializedData stream) { - type = stream.readString(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeString(type); - } - } - - public static class TL_photoCachedSize extends PhotoSize { - public static int constructor = 0xe9a734fa; - - - public void readParams(AbsSerializedData stream) { - type = stream.readString(); - location = (FileLocation)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - w = stream.readInt32(); - h = stream.readInt32(); - bytes = stream.readByteArray(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeString(type); - location.serializeToStream(stream); - stream.writeInt32(w); - stream.writeInt32(h); - stream.writeByteArray(bytes); - } - } - - public static class TL_contactFound extends TLObject { - public static int constructor = 0xea879f95; + public static class TL_contactBlocked extends TLObject { + public static int constructor = 0x561bc879; public int user_id; + public int date; - public void readParams(AbsSerializedData stream) { - user_id = stream.readInt32(); + public static TL_contactBlocked TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + if (TL_contactBlocked.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_contactBlocked", constructor)); + } else { + return null; + } + } + TL_contactBlocked result = new TL_contactBlocked(); + result.readParams(stream, exception); + return result; + } + + public void readParams(AbsSerializedData stream, boolean exception) { + user_id = stream.readInt32(exception); + date = stream.readInt32(exception); } public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(user_id); - } - } - - public static class TL_account_sentChangePhoneCode extends TLObject { - public static int constructor = 0xa4f58c4c; - - public String phone_code_hash; - public int send_call_timeout; - - public void readParams(AbsSerializedData stream) { - phone_code_hash = stream.readString(); - send_call_timeout = stream.readInt32(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeString(phone_code_hash); - stream.writeInt32(send_call_timeout); - } - } - - public static class InputFile extends TLObject { - public long id; - public int parts; - public String name; - public String md5_checksum; - } - - public static class TL_inputFileBig extends InputFile { - public static int constructor = 0xfa4f0bb5; - - - public void readParams(AbsSerializedData stream) { - id = stream.readInt64(); - parts = stream.readInt32(); - name = stream.readString(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt64(id); - stream.writeInt32(parts); - stream.writeString(name); - } - } - - public static class TL_inputFile extends InputFile { - public static int constructor = 0xf52ff27f; - - - public void readParams(AbsSerializedData stream) { - id = stream.readInt64(); - parts = stream.readInt32(); - name = stream.readString(); - md5_checksum = stream.readString(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt64(id); - stream.writeInt32(parts); - stream.writeString(name); - stream.writeString(md5_checksum); - } - } - - public static class TL_userFull extends TLObject { - public static int constructor = 0x771095da; - - public User user; - public TL_contacts_link link; - public Photo profile_photo; - public PeerNotifySettings notify_settings; - public boolean blocked; - public String real_first_name; - public String real_last_name; - - public void readParams(AbsSerializedData stream) { - user = (User)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - link = (TL_contacts_link)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - profile_photo = (Photo)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - notify_settings = (PeerNotifySettings)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - blocked = stream.readBool(); - real_first_name = stream.readString(); - real_last_name = stream.readString(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - user.serializeToStream(stream); - link.serializeToStream(stream); - profile_photo.serializeToStream(stream); - notify_settings.serializeToStream(stream); - stream.writeBool(blocked); - stream.writeString(real_first_name); - stream.writeString(real_last_name); - } - } - - public static class TL_updates_state extends TLObject { - public static int constructor = 0xa56c2a3e; - - public int pts; - public int qts; - public int date; - public int seq; - public int unread_count; - - public void readParams(AbsSerializedData stream) { - pts = stream.readInt32(); - qts = stream.readInt32(); - date = stream.readInt32(); - seq = stream.readInt32(); - unread_count = stream.readInt32(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(pts); - stream.writeInt32(qts); stream.writeInt32(date); - stream.writeInt32(seq); - stream.writeInt32(unread_count); - } - } - - public static class TL_resPQ extends TLObject { - public static int constructor = 0x05162463; - - public byte[] nonce; - public byte[] server_nonce; - public byte[] pq; - public ArrayList server_public_key_fingerprints = new ArrayList<>(); - - public void readParams(AbsSerializedData stream) { - nonce = stream.readData(16); - server_nonce = stream.readData(16); - pq = stream.readByteArray(); - stream.readInt32(); - int count = stream.readInt32(); - for (int a = 0; a < count; a++) { - server_public_key_fingerprints.add(stream.readInt64()); - } - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeRaw(nonce); - stream.writeRaw(server_nonce); - stream.writeByteArray(pq); - stream.writeInt32(0x1cb5c415); - int count = server_public_key_fingerprints.size(); - stream.writeInt32(count); - for (Long server_public_key_fingerprint : server_public_key_fingerprints) { - stream.writeInt64(server_public_key_fingerprint); - } } } public static class Updates extends TLObject { public int flags; public int id; + public int from_id; public int chat_id; public String message; public int pts; @@ -6718,27 +8308,60 @@ public class TLRPC { public Update update; public int seq_start; public int qts; + + public static Updates TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + Updates result = null; + switch(constructor) { + case 0x52238b3c: + result = new TL_updateShortChatMessage(); + break; + case 0x74ae4240: + result = new TL_updates(); + break; + case 0xed5c2127: + result = new TL_updateShortMessage(); + break; + case 0x78d4dec1: + result = new TL_updateShort(); + break; + case 0x725b04c3: + result = new TL_updatesCombined(); + break; + case 0xe317af7e: + result = new TL_updatesTooLong(); + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in Updates", constructor)); + } + if (result != null) { + result.readParams(stream, exception); + } + return result; + } } public static class TL_updateShortChatMessage extends Updates { public static int constructor = 0x52238b3c; - public void readParams(AbsSerializedData stream) { - flags = stream.readInt32(); - id = stream.readInt32(); - user_id = stream.readInt32(); - chat_id = stream.readInt32(); - message = stream.readString(); - pts = stream.readInt32(); - pts_count = stream.readInt32(); - date = stream.readInt32(); - if ((flags & MESSAGE_FLAG_FWD) != 0) { - fwd_from_id = stream.readInt32(); - fwd_date = stream.readInt32(); + public void readParams(AbsSerializedData stream, boolean exception) { + flags = stream.readInt32(exception); + id = stream.readInt32(exception); + from_id = stream.readInt32(exception); + chat_id = stream.readInt32(exception); + message = stream.readString(exception); + pts = stream.readInt32(exception); + pts_count = stream.readInt32(exception); + date = stream.readInt32(exception); + if ((flags & 4) != 0) { + fwd_from_id = stream.readInt32(exception); } - if ((flags & MESSAGE_FLAG_REPLY) != 0) { - reply_to_msg_id = stream.readInt32(); + if ((flags & 4) != 0) { + fwd_date = stream.readInt32(exception); + } + if ((flags & 8) != 0) { + reply_to_msg_id = stream.readInt32(exception); } } @@ -6746,17 +8369,19 @@ public class TLRPC { stream.writeInt32(constructor); stream.writeInt32(flags); stream.writeInt32(id); - stream.writeInt32(user_id); + stream.writeInt32(from_id); stream.writeInt32(chat_id); stream.writeString(message); stream.writeInt32(pts); stream.writeInt32(pts_count); stream.writeInt32(date); - if ((flags & MESSAGE_FLAG_FWD) != 0) { + if ((flags & 4) != 0) { stream.writeInt32(fwd_from_id); + } + if ((flags & 4) != 0) { stream.writeInt32(fwd_date); } - if ((flags & MESSAGE_FLAG_REPLY) != 0) { + if ((flags & 8) != 0) { stream.writeInt32(reply_to_msg_id); } } @@ -6766,24 +8391,42 @@ public class TLRPC { public static int constructor = 0x74ae4240; - public void readParams(AbsSerializedData stream) { - stream.readInt32(); - int count = stream.readInt32(); - for (int a = 0; a < count; a++) { - updates.add((Update)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32())); + public void readParams(AbsSerializedData stream, boolean exception) { + int magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; } - stream.readInt32(); - count = stream.readInt32(); + int count = stream.readInt32(exception); for (int a = 0; a < count; a++) { - users.add((User)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32())); + updates.add(Update.TLdeserialize(stream, stream.readInt32(exception), exception)); } - stream.readInt32(); - count = stream.readInt32(); + magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + count = stream.readInt32(exception); for (int a = 0; a < count; a++) { - chats.add((Chat)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32())); + users.add(User.TLdeserialize(stream, stream.readInt32(exception), exception)); } - date = stream.readInt32(); - seq = stream.readInt32(); + magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + chats.add(Chat.TLdeserialize(stream, stream.readInt32(exception), exception)); + } + date = stream.readInt32(exception); + seq = stream.readInt32(exception); } public void serializeToStream(AbsSerializedData stream) { @@ -6815,20 +8458,22 @@ public class TLRPC { public static int constructor = 0xed5c2127; - public void readParams(AbsSerializedData stream) { - flags = stream.readInt32(); - id = stream.readInt32(); - user_id = stream.readInt32(); - message = stream.readString(); - pts = stream.readInt32(); - pts_count = stream.readInt32(); - date = stream.readInt32(); - if ((flags & MESSAGE_FLAG_FWD) != 0) { - fwd_from_id = stream.readInt32(); - fwd_date = stream.readInt32(); + public void readParams(AbsSerializedData stream, boolean exception) { + flags = stream.readInt32(exception); + id = stream.readInt32(exception); + user_id = stream.readInt32(exception); + message = stream.readString(exception); + pts = stream.readInt32(exception); + pts_count = stream.readInt32(exception); + date = stream.readInt32(exception); + if ((flags & 4) != 0) { + fwd_from_id = stream.readInt32(exception); } - if ((flags & MESSAGE_FLAG_REPLY) != 0) { - reply_to_msg_id = stream.readInt32(); + if ((flags & 4) != 0) { + fwd_date = stream.readInt32(exception); + } + if ((flags & 8) != 0) { + reply_to_msg_id = stream.readInt32(exception); } } @@ -6841,11 +8486,13 @@ public class TLRPC { stream.writeInt32(pts); stream.writeInt32(pts_count); stream.writeInt32(date); - if ((flags & MESSAGE_FLAG_FWD) != 0) { + if ((flags & 4) != 0) { stream.writeInt32(fwd_from_id); + } + if ((flags & 4) != 0) { stream.writeInt32(fwd_date); } - if ((flags & MESSAGE_FLAG_REPLY) != 0) { + if ((flags & 8) != 0) { stream.writeInt32(reply_to_msg_id); } } @@ -6855,9 +8502,9 @@ public class TLRPC { public static int constructor = 0x78d4dec1; - public void readParams(AbsSerializedData stream) { - update = (Update)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - date = stream.readInt32(); + public void readParams(AbsSerializedData stream, boolean exception) { + update = Update.TLdeserialize(stream, stream.readInt32(exception), exception); + date = stream.readInt32(exception); } public void serializeToStream(AbsSerializedData stream) { @@ -6871,25 +8518,43 @@ public class TLRPC { public static int constructor = 0x725b04c3; - public void readParams(AbsSerializedData stream) { - stream.readInt32(); - int count = stream.readInt32(); - for (int a = 0; a < count; a++) { - updates.add((Update)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32())); + public void readParams(AbsSerializedData stream, boolean exception) { + int magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; } - stream.readInt32(); - count = stream.readInt32(); + int count = stream.readInt32(exception); for (int a = 0; a < count; a++) { - users.add((User)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32())); + updates.add(Update.TLdeserialize(stream, stream.readInt32(exception), exception)); } - stream.readInt32(); - count = stream.readInt32(); + magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + count = stream.readInt32(exception); for (int a = 0; a < count; a++) { - chats.add((Chat)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32())); + users.add(User.TLdeserialize(stream, stream.readInt32(exception), exception)); } - date = stream.readInt32(); - seq_start = stream.readInt32(); - seq = stream.readInt32(); + magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + chats.add(Chat.TLdeserialize(stream, stream.readInt32(exception), exception)); + } + date = stream.readInt32(exception); + seq_start = stream.readInt32(exception); + seq = stream.readInt32(exception); } public void serializeToStream(AbsSerializedData stream) { @@ -6927,438 +8592,371 @@ public class TLRPC { } } - public static class WallPaper extends TLObject { - public int id; - public String title; - public ArrayList sizes = new ArrayList<>(); - public int color; - public int bg_color; + public static class InputPeerNotifyEvents extends TLObject { + + public static InputPeerNotifyEvents TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + InputPeerNotifyEvents result = null; + switch(constructor) { + case 0xe86a2c74: + result = new TL_inputPeerNotifyEventsAll(); + break; + case 0xf03064d8: + result = new TL_inputPeerNotifyEventsEmpty(); + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in InputPeerNotifyEvents", constructor)); + } + if (result != null) { + result.readParams(stream, exception); + } + return result; + } } - public static class TL_wallPaper extends WallPaper { - public static int constructor = 0xccb03657; + public static class TL_inputPeerNotifyEventsAll extends InputPeerNotifyEvents { + public static int constructor = 0xe86a2c74; - public void readParams(AbsSerializedData stream) { - id = stream.readInt32(); - title = stream.readString(); - stream.readInt32(); - int count = stream.readInt32(); + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class TL_inputPeerNotifyEventsEmpty extends InputPeerNotifyEvents { + public static int constructor = 0xf03064d8; + + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class TL_msgs_state_info extends TLObject { + public static int constructor = 0x04deb57d; + + public long req_msg_id; + public String info; + + public static TL_msgs_state_info TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + if (TL_msgs_state_info.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_msgs_state_info", constructor)); + } else { + return null; + } + } + TL_msgs_state_info result = new TL_msgs_state_info(); + result.readParams(stream, exception); + return result; + } + + public void readParams(AbsSerializedData stream, boolean exception) { + req_msg_id = stream.readInt64(exception); + info = stream.readString(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt64(req_msg_id); + stream.writeString(info); + } + } + + public static class updates_Difference extends TLObject { + public int date; + public int seq; + public ArrayList new_messages = new ArrayList<>(); + public ArrayList new_encrypted_messages = new ArrayList<>(); + public ArrayList other_updates = new ArrayList<>(); + public ArrayList chats = new ArrayList<>(); + public ArrayList users = new ArrayList<>(); + public TL_updates_state intermediate_state; + public TL_updates_state state; + + public static updates_Difference TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + updates_Difference result = null; + switch(constructor) { + case 0x5d75a138: + result = new TL_updates_differenceEmpty(); + break; + case 0xa8fb1981: + result = new TL_updates_differenceSlice(); + break; + case 0xf49ca0: + result = new TL_updates_difference(); + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in updates_Difference", constructor)); + } + if (result != null) { + result.readParams(stream, exception); + } + return result; + } + } + + public static class TL_updates_differenceEmpty extends updates_Difference { + public static int constructor = 0x5d75a138; + + + public void readParams(AbsSerializedData stream, boolean exception) { + date = stream.readInt32(exception); + seq = stream.readInt32(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(date); + stream.writeInt32(seq); + } + } + + public static class TL_updates_differenceSlice extends updates_Difference { + public static int constructor = 0xa8fb1981; + + + public void readParams(AbsSerializedData stream, boolean exception) { + int magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + int count = stream.readInt32(exception); for (int a = 0; a < count; a++) { - sizes.add((PhotoSize)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32())); + new_messages.add(Message.TLdeserialize(stream, stream.readInt32(exception), exception)); } - color = stream.readInt32(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(id); - stream.writeString(title); - stream.writeInt32(0x1cb5c415); - int count = sizes.size(); - stream.writeInt32(count); - for (PhotoSize size : sizes) { - size.serializeToStream(stream); + magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; } - stream.writeInt32(color); - } - } - - public static class TL_wallPaperSolid extends WallPaper { - public static int constructor = 0x63117f24; - - - public void readParams(AbsSerializedData stream) { - id = stream.readInt32(); - title = stream.readString(); - bg_color = stream.readInt32(); - color = stream.readInt32(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(id); - stream.writeString(title); - stream.writeInt32(bg_color); - stream.writeInt32(color); - } - } - - public static class MsgDetailedInfo extends TLObject { - public long answer_msg_id; - public int bytes; - public int status; - public long msg_id; - } - - public static class TL_msg_new_detailed_info extends MsgDetailedInfo { - public static int constructor = 0x809db6df; - - - public void readParams(AbsSerializedData stream) { - answer_msg_id = stream.readInt64(); - bytes = stream.readInt32(); - status = stream.readInt32(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt64(answer_msg_id); - stream.writeInt32(bytes); - stream.writeInt32(status); - } - } - - public static class TL_msg_detailed_info extends MsgDetailedInfo { - public static int constructor = 0x276d3ec6; - - - public void readParams(AbsSerializedData stream) { - msg_id = stream.readInt64(); - answer_msg_id = stream.readInt64(); - bytes = stream.readInt32(); - status = stream.readInt32(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt64(msg_id); - stream.writeInt64(answer_msg_id); - stream.writeInt32(bytes); - stream.writeInt32(status); - } - } - - public static class TL_stickerPack extends TLObject { - public static int constructor = 0x12b299d4; - - public String emoticon; - public ArrayList documents = new ArrayList<>(); - - public void readParams(AbsSerializedData stream) { - emoticon = stream.readString(); - stream.readInt32(); - int count = stream.readInt32(); + count = stream.readInt32(exception); for (int a = 0; a < count; a++) { - documents.add(stream.readInt64()); + new_encrypted_messages.add(EncryptedMessage.TLdeserialize(stream, stream.readInt32(exception), exception)); } + magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + other_updates.add(Update.TLdeserialize(stream, stream.readInt32(exception), exception)); + } + magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + chats.add(Chat.TLdeserialize(stream, stream.readInt32(exception), exception)); + } + magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + users.add(User.TLdeserialize(stream, stream.readInt32(exception), exception)); + } + intermediate_state = TL_updates_state.TLdeserialize(stream, stream.readInt32(exception), exception); } public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); - stream.writeString(emoticon); stream.writeInt32(0x1cb5c415); - int count = documents.size(); + int count = new_messages.size(); stream.writeInt32(count); for (int a = 0; a < count; a++) { - stream.writeInt64(documents.get(a)); + new_messages.get(a).serializeToStream(stream); } - } - } - - public static class TL_inputEncryptedChat extends TLObject { - public static int constructor = 0xf141b5e1; - - public int chat_id; - public long access_hash; - - public void readParams(AbsSerializedData stream) { - chat_id = stream.readInt32(); - access_hash = stream.readInt64(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(chat_id); - stream.writeInt64(access_hash); - } - } - - public static class InputChatPhoto extends TLObject { - public InputPhoto id; - public InputPhotoCrop crop; - public InputFile file; - } - - public static class TL_inputChatPhoto extends InputChatPhoto { - public static int constructor = 0xb2e1bf08; - - - public void readParams(AbsSerializedData stream) { - id = (InputPhoto)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - crop = (InputPhotoCrop)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - id.serializeToStream(stream); - crop.serializeToStream(stream); - } - } - - public static class TL_inputChatPhotoEmpty extends InputChatPhoto { - public static int constructor = 0x1ca48f57; - - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - } - } - - public static class TL_inputChatUploadedPhoto extends InputChatPhoto { - public static int constructor = 0x94254732; - - - public void readParams(AbsSerializedData stream) { - file = (InputFile)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - crop = (InputPhotoCrop)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - file.serializeToStream(stream); - crop.serializeToStream(stream); - } - } - - public static class InputVideo extends TLObject { - public long id; - public long access_hash; - } - - public static class TL_inputVideoEmpty extends InputVideo { - public static int constructor = 0x5508ec75; - - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - } - } - - public static class TL_inputVideo extends InputVideo { - public static int constructor = 0xee579652; - - - public void readParams(AbsSerializedData stream) { - id = stream.readInt64(); - access_hash = stream.readInt64(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt64(id); - stream.writeInt64(access_hash); - } - } - - public static class TL_nearestDc extends TLObject { - public static int constructor = 0x8e1a1775; - - public String country; - public int this_dc; - public int nearest_dc; - - public void readParams(AbsSerializedData stream) { - country = stream.readString(); - this_dc = stream.readInt32(); - nearest_dc = stream.readInt32(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeString(country); - stream.writeInt32(this_dc); - stream.writeInt32(nearest_dc); - } - } - - public static class InputPhoto extends TLObject { - public long id; - public long access_hash; - } - - public static class TL_inputPhotoEmpty extends InputPhoto { - public static int constructor = 0x1cd7bf0d; - - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - } - } - - public static class TL_inputPhoto extends InputPhoto { - public static int constructor = 0xfb95c6c4; - - - public void readParams(AbsSerializedData stream) { - id = stream.readInt64(); - access_hash = stream.readInt64(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt64(id); - stream.writeInt64(access_hash); - } - } - - public static class TL_importedContact extends TLObject { - public static int constructor = 0xd0028438; - - public int user_id; - public long client_id; - - public void readParams(AbsSerializedData stream) { - user_id = stream.readInt32(); - client_id = stream.readInt64(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(user_id); - stream.writeInt64(client_id); - } - } - - public static class TL_accountDaysTTL extends TLObject { - public static int constructor = 0xb8d0afdf; - - public int days; - - public void readParams(AbsSerializedData stream) { - days = stream.readInt32(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(days); - } - } - - public static class InputPeer extends TLObject { - public int user_id; - public int chat_id; - public long access_hash; - } - - public static class TL_inputPeerContact extends InputPeer { - public static int constructor = 0x1023dbe8; - - - public void readParams(AbsSerializedData stream) { - user_id = stream.readInt32(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(user_id); - } - } - - public static class TL_inputPeerChat extends InputPeer { - public static int constructor = 0x179be863; - - - public void readParams(AbsSerializedData stream) { - chat_id = stream.readInt32(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(chat_id); - } - } - - public static class TL_inputPeerEmpty extends InputPeer { - public static int constructor = 0x7f3b18ea; - - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - } - } - - public static class TL_inputPeerSelf extends InputPeer { - public static int constructor = 0x7da07ec9; - - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - } - } - - public static class TL_inputPeerForeign extends InputPeer { - public static int constructor = 0x9b447325; - - - public void readParams(AbsSerializedData stream) { - user_id = stream.readInt32(); - access_hash = stream.readInt64(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(user_id); - stream.writeInt64(access_hash); - } - } - - public static class TL_account_passwordInputSettings extends TLObject { - public static int constructor = 0xbcfc532c; - - public int flags; - public byte[] new_salt; - public byte[] new_password_hash; - public String hint; - public String email; - - public void readParams(AbsSerializedData stream) { - flags = stream.readInt32(); - if ((flags & 1) != 0) { - new_salt = stream.readByteArray(); - new_password_hash = stream.readByteArray(); - hint = stream.readString(); + stream.writeInt32(0x1cb5c415); + count = new_encrypted_messages.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + new_encrypted_messages.get(a).serializeToStream(stream); } - if ((flags & 2) != 0) { - email = stream.readString(); + stream.writeInt32(0x1cb5c415); + count = other_updates.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + other_updates.get(a).serializeToStream(stream); } - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(flags); - if ((flags & 1) != 0) { - stream.writeByteArray(new_salt); - stream.writeByteArray(new_password_hash); - stream.writeString(hint); + stream.writeInt32(0x1cb5c415); + count = chats.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + chats.get(a).serializeToStream(stream); } - if ((flags & 2) != 0) { - stream.writeString(email); + stream.writeInt32(0x1cb5c415); + count = users.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + users.get(a).serializeToStream(stream); } + intermediate_state.serializeToStream(stream); } } - public static class TL_dcOption extends TLObject { - public static int constructor = 0x2ec2a43c; + public static class TL_updates_difference extends updates_Difference { + public static int constructor = 0xf49ca0; - public int id; - public String hostname; - public String ip_address; - public int port; - public void readParams(AbsSerializedData stream) { - id = stream.readInt32(); - hostname = stream.readString(); - ip_address = stream.readString(); - port = stream.readInt32(); + public void readParams(AbsSerializedData stream, boolean exception) { + int magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + int count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + new_messages.add(Message.TLdeserialize(stream, stream.readInt32(exception), exception)); + } + magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + new_encrypted_messages.add(EncryptedMessage.TLdeserialize(stream, stream.readInt32(exception), exception)); + } + magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + other_updates.add(Update.TLdeserialize(stream, stream.readInt32(exception), exception)); + } + magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + chats.add(Chat.TLdeserialize(stream, stream.readInt32(exception), exception)); + } + magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + users.add(User.TLdeserialize(stream, stream.readInt32(exception), exception)); + } + state = TL_updates_state.TLdeserialize(stream, stream.readInt32(exception), exception); } public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); - stream.writeInt32(id); - stream.writeString(hostname); - stream.writeString(ip_address); - stream.writeInt32(port); + stream.writeInt32(0x1cb5c415); + int count = new_messages.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + new_messages.get(a).serializeToStream(stream); + } + stream.writeInt32(0x1cb5c415); + count = new_encrypted_messages.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + new_encrypted_messages.get(a).serializeToStream(stream); + } + stream.writeInt32(0x1cb5c415); + count = other_updates.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + other_updates.get(a).serializeToStream(stream); + } + stream.writeInt32(0x1cb5c415); + count = chats.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + chats.get(a).serializeToStream(stream); + } + stream.writeInt32(0x1cb5c415); + count = users.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + users.get(a).serializeToStream(stream); + } + state.serializeToStream(stream); + } + } + + public static class ChatPhoto extends TLObject { + public FileLocation photo_small; + public FileLocation photo_big; + + public static ChatPhoto TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + ChatPhoto result = null; + switch(constructor) { + case 0x37c1011c: + result = new TL_chatPhotoEmpty(); + break; + case 0x6153276a: + result = new TL_chatPhoto(); + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in ChatPhoto", constructor)); + } + if (result != null) { + result.readParams(stream, exception); + } + return result; + } + } + + public static class TL_chatPhotoEmpty extends ChatPhoto { + public static int constructor = 0x37c1011c; + + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class TL_chatPhoto extends ChatPhoto { + public static int constructor = 0x6153276a; + + + public void readParams(AbsSerializedData stream, boolean exception) { + photo_small = FileLocation.TLdeserialize(stream, stream.readInt32(exception), exception); + photo_big = FileLocation.TLdeserialize(stream, stream.readInt32(exception), exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + photo_small.serializeToStream(stream); + photo_big.serializeToStream(stream); } } @@ -7371,12 +8969,25 @@ public class TLRPC { public int out_seq_no; public DecryptedMessage message; - public void readParams(AbsSerializedData stream) { - random_bytes = stream.readByteArray(); - layer = stream.readInt32(); - in_seq_no = stream.readInt32(); - out_seq_no = stream.readInt32(); - message = (DecryptedMessage)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); + public static TL_decryptedMessageLayer TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + if (TL_decryptedMessageLayer.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_decryptedMessageLayer", constructor)); + } else { + return null; + } + } + TL_decryptedMessageLayer result = new TL_decryptedMessageLayer(); + result.readParams(stream, exception); + return result; + } + + public void readParams(AbsSerializedData stream, boolean exception) { + random_bytes = stream.readByteArray(exception); + layer = stream.readInt32(exception); + in_seq_no = stream.readInt32(exception); + out_seq_no = stream.readInt32(exception); + message = DecryptedMessage.TLdeserialize(stream, stream.readInt32(exception), exception); } public void serializeToStream(AbsSerializedData stream) { @@ -7389,10 +9000,397 @@ public class TLRPC { } } + public static class TL_audioEmpty extends Audio { + public static int constructor = 0x586988d8; + + + public void readParams(AbsSerializedData stream, boolean exception) { + id = stream.readInt64(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt64(id); + } + } + + public static class TL_audio extends Audio { + public static int constructor = 0xc7ac6496; + + + public void readParams(AbsSerializedData stream, boolean exception) { + id = stream.readInt64(exception); + access_hash = stream.readInt64(exception); + user_id = stream.readInt32(exception); + date = stream.readInt32(exception); + duration = stream.readInt32(exception); + mime_type = stream.readString(exception); + size = stream.readInt32(exception); + dc_id = stream.readInt32(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt64(id); + stream.writeInt64(access_hash); + stream.writeInt32(user_id); + stream.writeInt32(date); + stream.writeInt32(duration); + stream.writeString(mime_type); + stream.writeInt32(size); + stream.writeInt32(dc_id); + } + } + + public static class TL_contacts_found extends TLObject { + public static int constructor = 0x566000e; + + public ArrayList results = new ArrayList<>(); + public ArrayList users = new ArrayList<>(); + + public static TL_contacts_found TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + if (TL_contacts_found.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_contacts_found", constructor)); + } else { + return null; + } + } + TL_contacts_found result = new TL_contacts_found(); + result.readParams(stream, exception); + return result; + } + + public void readParams(AbsSerializedData stream, boolean exception) { + int magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + int count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + results.add(TL_contactFound.TLdeserialize(stream, stream.readInt32(exception), exception)); + } + magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + users.add(User.TLdeserialize(stream, stream.readInt32(exception), exception)); + } + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(0x1cb5c415); + int count = results.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + results.get(a).serializeToStream(stream); + } + stream.writeInt32(0x1cb5c415); + count = users.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + users.get(a).serializeToStream(stream); + } + } + } + + public static class TL_documentEmpty extends Document { + public static int constructor = 0x36f8c871; + + + public void readParams(AbsSerializedData stream, boolean exception) { + id = stream.readInt64(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt64(id); + } + } + + public static class TL_document extends Document { + public static int constructor = 0xf9a39f4f; + + + public void readParams(AbsSerializedData stream, boolean exception) { + id = stream.readInt64(exception); + access_hash = stream.readInt64(exception); + date = stream.readInt32(exception); + mime_type = stream.readString(exception); + size = stream.readInt32(exception); + thumb = PhotoSize.TLdeserialize(stream, stream.readInt32(exception), exception); + dc_id = stream.readInt32(exception); + int magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + int count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + attributes.add(DocumentAttribute.TLdeserialize(stream, stream.readInt32(exception), exception)); + } + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt64(id); + stream.writeInt64(access_hash); + stream.writeInt32(date); + stream.writeString(mime_type); + stream.writeInt32(size); + thumb.serializeToStream(stream); + stream.writeInt32(dc_id); + stream.writeInt32(0x1cb5c415); + int count = attributes.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + attributes.get(a).serializeToStream(stream); + } + } + } + + public static class MessagesFilter extends TLObject { + + public static MessagesFilter TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + MessagesFilter result = null; + switch(constructor) { + case 0x9eddf188: + result = new TL_inputMessagesFilterDocument(); + break; + case 0x9fc00e65: + result = new TL_inputMessagesFilterVideo(); + break; + case 0x9609a51c: + result = new TL_inputMessagesFilterPhotos(); + break; + case 0xd95e73bb: + result = new TL_inputMessagesFilterPhotoVideoDocuments(); + break; + case 0xcfc87522: + result = new TL_inputMessagesFilterAudio(); + break; + case 0x57e2f66c: + result = new TL_inputMessagesFilterEmpty(); + break; + case 0x56e9f0e4: + result = new TL_inputMessagesFilterPhotoVideo(); + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in MessagesFilter", constructor)); + } + if (result != null) { + result.readParams(stream, exception); + } + return result; + } + } + + public static class TL_inputMessagesFilterDocument extends MessagesFilter { + public static int constructor = 0x9eddf188; + + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class TL_inputMessagesFilterVideo extends MessagesFilter { + public static int constructor = 0x9fc00e65; + + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class TL_inputMessagesFilterPhotos extends MessagesFilter { + public static int constructor = 0x9609a51c; + + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class TL_inputMessagesFilterPhotoVideoDocuments extends MessagesFilter { + public static int constructor = 0xd95e73bb; + + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class TL_inputMessagesFilterAudio extends MessagesFilter { + public static int constructor = 0xcfc87522; + + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class TL_inputMessagesFilterEmpty extends MessagesFilter { + public static int constructor = 0x57e2f66c; + + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class TL_inputMessagesFilterPhotoVideo extends MessagesFilter { + public static int constructor = 0x56e9f0e4; + + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class TL_account_passwordSettings extends TLObject { + public static int constructor = 0xb7b72ab3; + + public String email; + + public static TL_account_passwordSettings TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + if (TL_account_passwordSettings.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_account_passwordSettings", constructor)); + } else { + return null; + } + } + TL_account_passwordSettings result = new TL_account_passwordSettings(); + result.readParams(stream, exception); + return result; + } + + public void readParams(AbsSerializedData stream, boolean exception) { + email = stream.readString(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeString(email); + } + } + + public static class ExportedChatInvite extends TLObject { + public String link; + + public static ExportedChatInvite TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + ExportedChatInvite result = null; + switch(constructor) { + case 0xfc2e05bc: + result = new TL_chatInviteExported(); + break; + case 0x69df3769: + result = new TL_chatInviteEmpty(); + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in ExportedChatInvite", constructor)); + } + if (result != null) { + result.readParams(stream, exception); + } + return result; + } + } + + public static class TL_chatInviteExported extends ExportedChatInvite { + public static int constructor = 0xfc2e05bc; + + + public void readParams(AbsSerializedData stream, boolean exception) { + link = stream.readString(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeString(link); + } + } + + public static class TL_chatInviteEmpty extends ExportedChatInvite { + public static int constructor = 0x69df3769; + + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class TL_http_wait extends TLObject { + public static int constructor = 0x9299359f; + + public int max_delay; + public int wait_after; + public int max_wait; + + public static TL_http_wait TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + if (TL_http_wait.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_http_wait", constructor)); + } else { + return null; + } + } + TL_http_wait result = new TL_http_wait(); + result.readParams(stream, exception); + return result; + } + + public void readParams(AbsSerializedData stream, boolean exception) { + max_delay = stream.readInt32(exception); + wait_after = stream.readInt32(exception); + max_wait = stream.readInt32(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(max_delay); + stream.writeInt32(wait_after); + stream.writeInt32(max_wait); + } + } + public static class InputPhotoCrop extends TLObject { public double crop_left; public double crop_top; public double crop_width; + + public static InputPhotoCrop TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + InputPhotoCrop result = null; + switch(constructor) { + case 0xade6b004: + result = new TL_inputPhotoCropAuto(); + break; + case 0xd9915325: + result = new TL_inputPhotoCrop(); + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in InputPhotoCrop", constructor)); + } + if (result != null) { + result.readParams(stream, exception); + } + return result; + } } public static class TL_inputPhotoCropAuto extends InputPhotoCrop { @@ -7408,10 +9406,10 @@ public class TLRPC { public static int constructor = 0xd9915325; - public void readParams(AbsSerializedData stream) { - crop_left = stream.readDouble(); - crop_top = stream.readDouble(); - crop_width = stream.readDouble(); + public void readParams(AbsSerializedData stream, boolean exception) { + crop_left = stream.readDouble(exception); + crop_top = stream.readDouble(exception); + crop_width = stream.readDouble(exception); } public void serializeToStream(AbsSerializedData stream) { @@ -7422,60 +9420,659 @@ public class TLRPC { } } - public static class messages_Dialogs extends TLObject { - public ArrayList dialogs = new ArrayList<>(); - public ArrayList messages = new ArrayList<>(); + public static class Chat extends TLObject { + public int id; + public String title; + public int date; + public long access_hash; + public String address; + public String venue; + public GeoPoint geo; + public ChatPhoto photo; + public int participants_count; + public boolean checked_in; + public int version; + public boolean left; + + public static Chat TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + Chat result = null; + switch(constructor) { + case 0xfb0ccc41: + result = new TL_chatForbidden(); + break; + case 0x75eaea5a: + result = new TL_geoChat(); + break; + case 0x9ba2d800: + result = new TL_chatEmpty(); + break; + case 0x6e9c9bc7: + result = new TL_chat(); + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in Chat", constructor)); + } + if (result != null) { + result.readParams(stream, exception); + } + return result; + } + } + + public static class TL_chatForbidden extends Chat { + public static int constructor = 0xfb0ccc41; + + + public void readParams(AbsSerializedData stream, boolean exception) { + id = stream.readInt32(exception); + title = stream.readString(exception); + date = stream.readInt32(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(id); + stream.writeString(title); + stream.writeInt32(date); + } + } + + public static class TL_geoChat extends Chat { + public static int constructor = 0x75eaea5a; + + + public void readParams(AbsSerializedData stream, boolean exception) { + id = stream.readInt32(exception); + access_hash = stream.readInt64(exception); + title = stream.readString(exception); + address = stream.readString(exception); + venue = stream.readString(exception); + geo = GeoPoint.TLdeserialize(stream, stream.readInt32(exception), exception); + photo = ChatPhoto.TLdeserialize(stream, stream.readInt32(exception), exception); + participants_count = stream.readInt32(exception); + date = stream.readInt32(exception); + checked_in = stream.readBool(exception); + version = stream.readInt32(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(id); + stream.writeInt64(access_hash); + stream.writeString(title); + stream.writeString(address); + stream.writeString(venue); + geo.serializeToStream(stream); + photo.serializeToStream(stream); + stream.writeInt32(participants_count); + stream.writeInt32(date); + stream.writeBool(checked_in); + stream.writeInt32(version); + } + } + + public static class TL_chat extends Chat { + public static int constructor = 0x6e9c9bc7; + + + public void readParams(AbsSerializedData stream, boolean exception) { + id = stream.readInt32(exception); + title = stream.readString(exception); + photo = ChatPhoto.TLdeserialize(stream, stream.readInt32(exception), exception); + participants_count = stream.readInt32(exception); + date = stream.readInt32(exception); + left = stream.readBool(exception); + version = stream.readInt32(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(id); + stream.writeString(title); + photo.serializeToStream(stream); + stream.writeInt32(participants_count); + stream.writeInt32(date); + stream.writeBool(left); + stream.writeInt32(version); + } + } + + public static class TL_messages_chats extends TLObject { + public static int constructor = 0x64ff9fd5; + public ArrayList chats = new ArrayList<>(); + + public static TL_messages_chats TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + if (TL_messages_chats.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_messages_chats", constructor)); + } else { + return null; + } + } + TL_messages_chats result = new TL_messages_chats(); + result.readParams(stream, exception); + return result; + } + + public void readParams(AbsSerializedData stream, boolean exception) { + int magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + int count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + chats.add(Chat.TLdeserialize(stream, stream.readInt32(exception), exception)); + } + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(0x1cb5c415); + int count = chats.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + chats.get(a).serializeToStream(stream); + } + } + } + + public static class ContactLink extends TLObject { + + public static ContactLink TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + ContactLink result = null; + switch(constructor) { + case 0xfeedd3ad: + result = new TL_contactLinkNone(); + break; + case 0xd502c2d0: + result = new TL_contactLinkContact(); + break; + case 0x268f3f59: + result = new TL_contactLinkHasPhone(); + break; + case 0x5f4f9247: + result = new TL_contactLinkUnknown(); + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in ContactLink", constructor)); + } + if (result != null) { + result.readParams(stream, exception); + } + return result; + } + } + + public static class TL_contactLinkNone extends ContactLink { + public static int constructor = 0xfeedd3ad; + + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class TL_contactLinkContact extends ContactLink { + public static int constructor = 0xd502c2d0; + + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class TL_contactLinkHasPhone extends ContactLink { + public static int constructor = 0x268f3f59; + + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class TL_contactLinkUnknown extends ContactLink { + public static int constructor = 0x5f4f9247; + + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class PeerNotifyEvents extends TLObject { + + public static PeerNotifyEvents TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + PeerNotifyEvents result = null; + switch(constructor) { + case 0xadd53cb3: + result = new TL_peerNotifyEventsEmpty(); + break; + case 0x6d1ded88: + result = new TL_peerNotifyEventsAll(); + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in PeerNotifyEvents", constructor)); + } + if (result != null) { + result.readParams(stream, exception); + } + return result; + } + } + + public static class TL_peerNotifyEventsEmpty extends PeerNotifyEvents { + public static int constructor = 0xadd53cb3; + + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class TL_peerNotifyEventsAll extends PeerNotifyEvents { + public static int constructor = 0x6d1ded88; + + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class TL_sendMessageRecordAudioAction extends SendMessageAction { + public static int constructor = 0xd52f73f7; + + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class TL_sendMessageUploadAudioAction extends SendMessageAction { + public static int constructor = 0xf351d7ab; + + + public void readParams(AbsSerializedData stream, boolean exception) { + progress = stream.readInt32(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(progress); + } + } + + public static class TL_sendMessageUploadPhotoAction extends SendMessageAction { + public static int constructor = 0xd1d34a26; + + + public void readParams(AbsSerializedData stream, boolean exception) { + progress = stream.readInt32(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(progress); + } + } + + public static class TL_sendMessageUploadVideoAction extends SendMessageAction { + public static int constructor = 0xe9763aec; + + + public void readParams(AbsSerializedData stream, boolean exception) { + progress = stream.readInt32(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(progress); + } + } + + public static class TL_sendMessageCancelAction extends SendMessageAction { + public static int constructor = 0xfd5ec8f5; + + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class TL_sendMessageGeoLocationAction extends SendMessageAction { + public static int constructor = 0x176f8ba1; + + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class TL_sendMessageChooseContactAction extends SendMessageAction { + public static int constructor = 0x628cbc6f; + + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class TL_sendMessageTypingAction extends SendMessageAction { + public static int constructor = 0x16bf744e; + + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class TL_sendMessageUploadDocumentAction extends SendMessageAction { + public static int constructor = 0xaa0cd9e4; + + + public void readParams(AbsSerializedData stream, boolean exception) { + progress = stream.readInt32(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(progress); + } + } + + public static class TL_sendMessageRecordVideoAction extends SendMessageAction { + public static int constructor = 0xa187d66f; + + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class TL_client_DH_inner_data extends TLObject { + public static int constructor = 0x6643b654; + + public byte[] nonce; + public byte[] server_nonce; + public long retry_id; + public byte[] g_b; + + public static TL_client_DH_inner_data TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + if (TL_client_DH_inner_data.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_client_DH_inner_data", constructor)); + } else { + return null; + } + } + TL_client_DH_inner_data result = new TL_client_DH_inner_data(); + result.readParams(stream, exception); + return result; + } + + public void readParams(AbsSerializedData stream, boolean exception) { + nonce = stream.readData(16, exception); + server_nonce = stream.readData(16, exception); + retry_id = stream.readInt64(exception); + g_b = stream.readByteArray(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeRaw(nonce); + stream.writeRaw(server_nonce); + stream.writeInt64(retry_id); + stream.writeByteArray(g_b); + } + } + + public static class TL_null extends TLObject { + public static int constructor = 0x56730bcc; + + + public static TL_null TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + if (TL_null.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_null", constructor)); + } else { + return null; + } + } + TL_null result = new TL_null(); + result.readParams(stream, exception); + return result; + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class account_Password extends TLObject { + public byte[] current_salt; + public byte[] new_salt; + public String hint; + public boolean has_recovery; + public String email_unconfirmed_pattern; + + public static account_Password TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + account_Password result = null; + switch(constructor) { + case 0x7c18141c: + result = new TL_account_password(); + break; + case 0x96dabc18: + result = new TL_account_noPassword(); + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in account_Password", constructor)); + } + if (result != null) { + result.readParams(stream, exception); + } + return result; + } + } + + public static class TL_account_password extends account_Password { + public static int constructor = 0x7c18141c; + + + public void readParams(AbsSerializedData stream, boolean exception) { + current_salt = stream.readByteArray(exception); + new_salt = stream.readByteArray(exception); + hint = stream.readString(exception); + has_recovery = stream.readBool(exception); + email_unconfirmed_pattern = stream.readString(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeByteArray(current_salt); + stream.writeByteArray(new_salt); + stream.writeString(hint); + stream.writeBool(has_recovery); + stream.writeString(email_unconfirmed_pattern); + } + } + + public static class TL_account_noPassword extends account_Password { + public static int constructor = 0x96dabc18; + + + public void readParams(AbsSerializedData stream, boolean exception) { + new_salt = stream.readByteArray(exception); + email_unconfirmed_pattern = stream.readString(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeByteArray(new_salt); + stream.writeString(email_unconfirmed_pattern); + } + } + + public static class TL_documentAttributeAnimated extends DocumentAttribute { + public static int constructor = 0x11b58939; + + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class TL_documentAttributeImageSize extends DocumentAttribute { + public static int constructor = 0x6c37c15c; + + + public void readParams(AbsSerializedData stream, boolean exception) { + w = stream.readInt32(exception); + h = stream.readInt32(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(w); + stream.writeInt32(h); + } + } + + public static class TL_documentAttributeFilename extends DocumentAttribute { + public static int constructor = 0x15590068; + + + public void readParams(AbsSerializedData stream, boolean exception) { + file_name = stream.readString(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeString(file_name); + } + } + + public static class TL_documentAttributeVideo extends DocumentAttribute { + public static int constructor = 0x5910cccb; + + + public void readParams(AbsSerializedData stream, boolean exception) { + duration = stream.readInt32(exception); + w = stream.readInt32(exception); + h = stream.readInt32(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(duration); + stream.writeInt32(w); + stream.writeInt32(h); + } + } + + public static class TL_documentAttributeSticker extends DocumentAttribute { + public static int constructor = 0x994c9882; + + + public void readParams(AbsSerializedData stream, boolean exception) { + alt = stream.readString(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeString(alt); + } + } + + public static class TL_documentAttributeAudio extends DocumentAttribute { + public static int constructor = 0x51448e5; + + + public void readParams(AbsSerializedData stream, boolean exception) { + duration = stream.readInt32(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(duration); + } + } + + public static class TL_contacts_importedContacts extends TLObject { + public static int constructor = 0xad524315; + + public ArrayList imported = new ArrayList<>(); + public ArrayList retry_contacts = new ArrayList<>(); public ArrayList users = new ArrayList<>(); - public int count; - } - public static class TL_messages_dialogs extends messages_Dialogs { - public static int constructor = 0x15ba6c40; + public static TL_contacts_importedContacts TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + if (TL_contacts_importedContacts.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_contacts_importedContacts", constructor)); + } else { + return null; + } + } + TL_contacts_importedContacts result = new TL_contacts_importedContacts(); + result.readParams(stream, exception); + return result; + } - - public void readParams(AbsSerializedData stream) { - stream.readInt32(); - int count = stream.readInt32(); - for (int a = 0; a < count; a++) { - dialogs.add((TL_dialog)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32())); + public void readParams(AbsSerializedData stream, boolean exception) { + int magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; } - stream.readInt32(); - count = stream.readInt32(); + int count = stream.readInt32(exception); for (int a = 0; a < count; a++) { - messages.add((Message)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32())); + imported.add(TL_importedContact.TLdeserialize(stream, stream.readInt32(exception), exception)); } - stream.readInt32(); - count = stream.readInt32(); - for (int a = 0; a < count; a++) { - chats.add((Chat)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32())); + magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; } - stream.readInt32(); - count = stream.readInt32(); + count = stream.readInt32(exception); for (int a = 0; a < count; a++) { - users.add((User)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32())); + retry_contacts.add(stream.readInt64(exception)); + } + magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + users.add(User.TLdeserialize(stream, stream.readInt32(exception), exception)); } } public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(0x1cb5c415); - int count = dialogs.size(); + int count = imported.size(); stream.writeInt32(count); for (int a = 0; a < count; a++) { - dialogs.get(a).serializeToStream(stream); + imported.get(a).serializeToStream(stream); } stream.writeInt32(0x1cb5c415); - count = messages.size(); + count = retry_contacts.size(); stream.writeInt32(count); for (int a = 0; a < count; a++) { - messages.get(a).serializeToStream(stream); - } - stream.writeInt32(0x1cb5c415); - count = chats.size(); - stream.writeInt32(count); - for (int a = 0; a < count; a++) { - chats.get(a).serializeToStream(stream); + stream.writeInt64(retry_contacts.get(a)); } stream.writeInt32(0x1cb5c415); count = users.size(); @@ -7486,99 +10083,248 @@ public class TLRPC { } } - public static class TL_messages_dialogsSlice extends messages_Dialogs { - public static int constructor = 0x71e094f3; + public static class PrivacyRule extends TLObject { + public ArrayList users = new ArrayList<>(); + + public static PrivacyRule TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + PrivacyRule result = null; + switch(constructor) { + case 0x4d5bbe0c: + result = new TL_privacyValueAllowUsers(); + break; + case 0x8b73e763: + result = new TL_privacyValueDisallowAll(); + break; + case 0xfffe1bac: + result = new TL_privacyValueAllowContacts(); + break; + case 0xf888fa1a: + result = new TL_privacyValueDisallowContacts(); + break; + case 0x65427b82: + result = new TL_privacyValueAllowAll(); + break; + case 0xc7f49b7: + result = new TL_privacyValueDisallowUsers(); + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in PrivacyRule", constructor)); + } + if (result != null) { + result.readParams(stream, exception); + } + return result; + } + } + + public static class TL_privacyValueAllowUsers extends PrivacyRule { + public static int constructor = 0x4d5bbe0c; - public void readParams(AbsSerializedData stream) { - count = stream.readInt32(); - stream.readInt32(); - int count = stream.readInt32(); - for (int a = 0; a < count; a++) { - dialogs.add((TL_dialog)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32())); + public void readParams(AbsSerializedData stream, boolean exception) { + int magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; } - stream.readInt32(); - count = stream.readInt32(); + int count = stream.readInt32(exception); for (int a = 0; a < count; a++) { - messages.add((Message)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32())); - } - stream.readInt32(); - count = stream.readInt32(); - for (int a = 0; a < count; a++) { - chats.add((Chat)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32())); - } - stream.readInt32(); - count = stream.readInt32(); - for (int a = 0; a < count; a++) { - users.add((User)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32())); + users.add(stream.readInt32(exception)); } } public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); - stream.writeInt32(count); stream.writeInt32(0x1cb5c415); - int count = dialogs.size(); + int count = users.size(); stream.writeInt32(count); for (int a = 0; a < count; a++) { - dialogs.get(a).serializeToStream(stream); - } - stream.writeInt32(0x1cb5c415); - count = messages.size(); - stream.writeInt32(count); - for (int a = 0; a < count; a++) { - messages.get(a).serializeToStream(stream); - } - stream.writeInt32(0x1cb5c415); - count = chats.size(); - stream.writeInt32(count); - for (int a = 0; a < count; a++) { - chats.get(a).serializeToStream(stream); - } - stream.writeInt32(0x1cb5c415); - count = users.size(); - stream.writeInt32(count); - for (int a = 0; a < count; a++) { - users.get(a).serializeToStream(stream); + stream.writeInt32(users.get(a)); } } } - public static class TL_account_authorizations extends TLObject { - public static int constructor = 0x1250abde; + public static class TL_privacyValueDisallowAll extends PrivacyRule { + public static int constructor = 0x8b73e763; - public ArrayList authorizations = new ArrayList<>(); - public void readParams(AbsSerializedData stream) { - stream.readInt32(); - int count = stream.readInt32(); + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class TL_privacyValueAllowContacts extends PrivacyRule { + public static int constructor = 0xfffe1bac; + + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class TL_privacyValueDisallowContacts extends PrivacyRule { + public static int constructor = 0xf888fa1a; + + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class TL_privacyValueAllowAll extends PrivacyRule { + public static int constructor = 0x65427b82; + + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class TL_privacyValueDisallowUsers extends PrivacyRule { + public static int constructor = 0xc7f49b7; + + + public void readParams(AbsSerializedData stream, boolean exception) { + int magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + int count = stream.readInt32(exception); for (int a = 0; a < count; a++) { - authorizations.add((TL_authorization)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32())); + users.add(stream.readInt32(exception)); } } public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(0x1cb5c415); - int count = authorizations.size(); + int count = users.size(); stream.writeInt32(count); for (int a = 0; a < count; a++) { - authorizations.get(a).serializeToStream(stream); + stream.writeInt32(users.get(a)); } } } + public static class TL_updates_state extends TLObject { + public static int constructor = 0xa56c2a3e; + + public int pts; + public int qts; + public int date; + public int seq; + public int unread_count; + + public static TL_updates_state TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + if (TL_updates_state.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_updates_state", constructor)); + } else { + return null; + } + } + TL_updates_state result = new TL_updates_state(); + result.readParams(stream, exception); + return result; + } + + public void readParams(AbsSerializedData stream, boolean exception) { + pts = stream.readInt32(exception); + qts = stream.readInt32(exception); + date = stream.readInt32(exception); + seq = stream.readInt32(exception); + unread_count = stream.readInt32(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(pts); + stream.writeInt32(qts); + stream.writeInt32(date); + stream.writeInt32(seq); + stream.writeInt32(unread_count); + } + } + + public static class TL_destroy_sessions_res extends TLObject { + public static int constructor = 0xfb95abcd; + + public ArrayList destroy_results = new ArrayList<>(); + + public static TL_destroy_sessions_res TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + if (TL_destroy_sessions_res.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_destroy_sessions_res", constructor)); + } else { + return null; + } + } + TL_destroy_sessions_res result = new TL_destroy_sessions_res(); + result.readParams(stream, exception); + return result; + } + + public void readParams(AbsSerializedData stream, boolean exception) { + int count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + destroy_results.add(DestroySessionRes.TLdeserialize(stream, stream.readInt32(exception), exception)); + } + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + int count = destroy_results.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + destroy_results.get(a).serializeToStream(stream); + } + } + } + + public static class TL_receivedNotifyMessage extends TLObject { + public static int constructor = 0xa384b779; + + public int id; + public int flags; + + public static TL_receivedNotifyMessage TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + if (TL_receivedNotifyMessage.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_receivedNotifyMessage", constructor)); + } else { + return null; + } + } + TL_receivedNotifyMessage result = new TL_receivedNotifyMessage(); + result.readParams(stream, exception); + return result; + } + + public void readParams(AbsSerializedData stream, boolean exception) { + id = stream.readInt32(exception); + flags = stream.readInt32(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(id); + stream.writeInt32(flags); + } + } + public static class TL_req_pq extends TLObject { public static int constructor = 0x60469778; public byte[] nonce; - public Class responseClass () { - return TL_resPQ.class; - } - - public void readParams(AbsSerializedData stream) { - nonce = stream.readData(16); + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return TL_resPQ.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -7597,17 +10343,8 @@ public class TLRPC { public long public_key_fingerprint; public byte[] encrypted_data; - public Class responseClass () { - return Server_DH_Params.class; - } - - public void readParams(AbsSerializedData stream) { - nonce = stream.readData(16); - server_nonce = stream.readData(16); - p = stream.readByteArray(); - q = stream.readByteArray(); - public_key_fingerprint = stream.readInt64(); - encrypted_data = stream.readByteArray(); + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return Server_DH_Params.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -7626,12 +10363,8 @@ public class TLRPC { public String phone_number; - public Class responseClass () { - return TL_auth_checkedPhone.class; - } - - public void readParams(AbsSerializedData stream) { - phone_number = stream.readString(); + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return TL_auth_checkedPhone.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -7649,16 +10382,8 @@ public class TLRPC { public String api_hash; public String lang_code; - public Class responseClass () { - return TL_auth_sentCode.class; - } - - public void readParams(AbsSerializedData stream) { - phone_number = stream.readString(); - sms_type = stream.readInt32(); - api_id = stream.readInt32(); - api_hash = stream.readString(); - lang_code = stream.readString(); + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return auth_SentCode.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -7677,13 +10402,8 @@ public class TLRPC { public String phone_number; public String phone_code_hash; - public Class responseClass () { - return Bool.class; - } - - public void readParams(AbsSerializedData stream) { - phone_number = stream.readString(); - phone_code_hash = stream.readString(); + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return Bool.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -7702,16 +10422,8 @@ public class TLRPC { public String first_name; public String last_name; - public Class responseClass () { - return TL_auth_authorization.class; - } - - public void readParams(AbsSerializedData stream) { - phone_number = stream.readString(); - phone_code_hash = stream.readString(); - phone_code = stream.readString(); - first_name = stream.readString(); - last_name = stream.readString(); + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return TL_auth_authorization.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -7731,14 +10443,8 @@ public class TLRPC { public String phone_code_hash; public String phone_code; - public Class responseClass () { - return TL_auth_authorization.class; - } - - public void readParams(AbsSerializedData stream) { - phone_number = stream.readString(); - phone_code_hash = stream.readString(); - phone_code = stream.readString(); + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return TL_auth_authorization.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -7753,8 +10459,8 @@ public class TLRPC { public static int constructor = 0x5717da40; - public Class responseClass () { - return Bool.class; + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return Bool.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -7766,8 +10472,8 @@ public class TLRPC { public static int constructor = 0x9fab0d1a; - public Class responseClass () { - return Bool.class; + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return Bool.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -7781,17 +10487,8 @@ public class TLRPC { public ArrayList phone_numbers = new ArrayList<>(); public String message; - public Class responseClass () { - return Bool.class; - } - - public void readParams(AbsSerializedData stream) { - stream.readInt32(); - int count = stream.readInt32(); - for (int a = 0; a < count; a++) { - phone_numbers.add(stream.readString()); - } - message = stream.readString(); + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return Bool.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -7799,8 +10496,8 @@ public class TLRPC { stream.writeInt32(0x1cb5c415); int count = phone_numbers.size(); stream.writeInt32(count); - for (String phone_number : phone_numbers) { - stream.writeString(phone_number); + for (int a = 0; a < count; a++) { + stream.writeString(phone_numbers.get(a)); } stream.writeString(message); } @@ -7811,12 +10508,8 @@ public class TLRPC { public int dc_id; - public Class responseClass () { - return TL_auth_exportedAuthorization.class; - } - - public void readParams(AbsSerializedData stream) { - dc_id = stream.readInt32(); + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return TL_auth_exportedAuthorization.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -7831,13 +10524,8 @@ public class TLRPC { public int id; public byte[] bytes; - public Class responseClass () { - return TL_auth_authorization.class; - } - - public void readParams(AbsSerializedData stream) { - id = stream.readInt32(); - bytes = stream.readByteArray(); + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return TL_auth_authorization.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -7847,6 +10535,27 @@ public class TLRPC { } } + public static class TL_auth_bindTempAuthKey extends TLObject { + public static int constructor = 0xcdd42a05; + + public long perm_auth_key_id; + public long nonce; + public int expires_at; + public byte[] encrypted_message; + + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return Bool.TLdeserialize(stream, constructor, exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt64(perm_auth_key_id); + stream.writeInt64(nonce); + stream.writeInt32(expires_at); + stream.writeByteArray(encrypted_message); + } + } + public static class TL_account_registerDevice extends TLObject { public static int constructor = 0x446c712c; @@ -7858,18 +10567,8 @@ public class TLRPC { public boolean app_sandbox; public String lang_code; - public Class responseClass () { - return Bool.class; - } - - public void readParams(AbsSerializedData stream) { - token_type = stream.readInt32(); - token = stream.readString(); - device_model = stream.readString(); - system_version = stream.readString(); - app_version = stream.readString(); - app_sandbox = stream.readBool(); - lang_code = stream.readString(); + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return Bool.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -7890,13 +10589,8 @@ public class TLRPC { public int token_type; public String token; - public Class responseClass () { - return Bool.class; - } - - public void readParams(AbsSerializedData stream) { - token_type = stream.readInt32(); - token = stream.readString(); + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return Bool.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -7912,13 +10606,8 @@ public class TLRPC { public InputNotifyPeer peer; public TL_inputPeerNotifySettings settings; - public Class responseClass () { - return Bool.class; - } - - public void readParams(AbsSerializedData stream) { - peer = (InputNotifyPeer)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - settings = (TL_inputPeerNotifySettings)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return Bool.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -7933,12 +10622,8 @@ public class TLRPC { public InputNotifyPeer peer; - public Class responseClass () { - return PeerNotifySettings.class; - } - - public void readParams(AbsSerializedData stream) { - peer = (InputNotifyPeer)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return PeerNotifySettings.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -7951,8 +10636,8 @@ public class TLRPC { public static int constructor = 0xdb7e1747; - public Class responseClass () { - return Bool.class; + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return Bool.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -7966,13 +10651,8 @@ public class TLRPC { public String first_name; public String last_name; - public Class responseClass () { - return User.class; - } - - public void readParams(AbsSerializedData stream) { - first_name = stream.readString(); - last_name = stream.readString(); + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return User.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -7987,12 +10667,8 @@ public class TLRPC { public boolean offline; - public Class responseClass () { - return Bool.class; - } - - public void readParams(AbsSerializedData stream) { - offline = stream.readBool(); + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return Bool.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -8001,17 +10677,36 @@ public class TLRPC { } } + public static class TL_account_getWallPapers extends TLObject { + public static int constructor = 0xc04cfac2; + + + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + Vector vector = new Vector(); + int size = stream.readInt32(exception); + for (int a = 0; a < size; a++) { + vector.objects.add(WallPaper.TLdeserialize(stream, stream.readInt32(exception), exception)); + } + return vector; + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + public static class TL_users_getUsers extends TLObject { public static int constructor = 0xd91a548; public ArrayList id = new ArrayList<>(); - public void readParams(AbsSerializedData stream) { - stream.readInt32(); - int count = stream.readInt32(); - for (int a = 0; a < count; a++) { - id.add((InputUser)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32())); + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + Vector vector = new Vector(); + int size = stream.readInt32(exception); + for (int a = 0; a < size; a++) { + vector.objects.add(User.TLdeserialize(stream, stream.readInt32(exception), exception)); } + return vector; } public void serializeToStream(AbsSerializedData stream) { @@ -8019,8 +10714,8 @@ public class TLRPC { stream.writeInt32(0x1cb5c415); int count = id.size(); stream.writeInt32(count); - for (InputUser anId : id) { - anId.serializeToStream(stream); + for (int a = 0; a < count; a++) { + id.get(a).serializeToStream(stream); } } } @@ -8030,12 +10725,8 @@ public class TLRPC { public InputUser id; - public Class responseClass () { - return TL_userFull.class; - } - - public void readParams(AbsSerializedData stream) { - id = (InputUser)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return TL_userFull.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -8047,17 +10738,14 @@ public class TLRPC { public static class TL_contacts_getStatuses extends TLObject { public static int constructor = 0xc4a353ee; - public ArrayList id = new ArrayList<>(); - public Class responseClass () { - return Vector.class; - } - - public void parseVector(Vector vector, AbsSerializedData data) { - int size = data.readInt32(); + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + Vector vector = new Vector(); + int size = stream.readInt32(exception); for (int a = 0; a < size; a++) { - vector.objects.add(TLClassStore.Instance().TLdeserialize(data, data.readInt32())); + vector.objects.add(TL_contactStatus.TLdeserialize(stream, stream.readInt32(exception), exception)); } + return vector; } public void serializeToStream(AbsSerializedData stream) { @@ -8070,12 +10758,8 @@ public class TLRPC { public String hash; - public Class responseClass () { - return contacts_Contacts.class; - } - - public void readParams(AbsSerializedData stream) { - hash = stream.readString(); + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return contacts_Contacts.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -8090,17 +10774,8 @@ public class TLRPC { public ArrayList contacts = new ArrayList<>(); public boolean replace; - public Class responseClass () { - return TL_contacts_importedContacts.class; - } - - public void readParams(AbsSerializedData stream) { - stream.readInt32(); - int count = stream.readInt32(); - for (int a = 0; a < count; a++) { - contacts.add((TL_inputPhoneContact)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32())); - } - replace = stream.readBool(); + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return TL_contacts_importedContacts.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -8108,46 +10783,20 @@ public class TLRPC { stream.writeInt32(0x1cb5c415); int count = contacts.size(); stream.writeInt32(count); - for (TL_inputPhoneContact contact : contacts) { - contact.serializeToStream(stream); + for (int a = 0; a < count; a++) { + contacts.get(a).serializeToStream(stream); } stream.writeBool(replace); } } - public static class TL_contacts_search extends TLObject { - public static int constructor = 0x11f812d8; - - public String q; - public int limit; - - public Class responseClass () { - return TL_contacts_found.class; - } - - public void readParams(AbsSerializedData stream) { - q = stream.readString(); - limit = stream.readInt32(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeString(q); - stream.writeInt32(limit); - } - } - public static class TL_contacts_getSuggested extends TLObject { public static int constructor = 0xcd773428; public int limit; - public Class responseClass () { - return TL_contacts_suggested.class; - } - - public void readParams(AbsSerializedData stream) { - limit = stream.readInt32(); + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return TL_contacts_suggested.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -8161,12 +10810,8 @@ public class TLRPC { public InputUser id; - public Class responseClass () { - return TL_contacts_link.class; - } - - public void readParams(AbsSerializedData stream) { - id = (InputUser)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return TL_contacts_link.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -8180,16 +10825,8 @@ public class TLRPC { public ArrayList id = new ArrayList<>(); - public Class responseClass () { - return Bool.class; - } - - public void readParams(AbsSerializedData stream) { - stream.readInt32(); - int count = stream.readInt32(); - for (int a = 0; a < count; a++) { - id.add((InputUser)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32())); - } + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return Bool.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -8197,8 +10834,8 @@ public class TLRPC { stream.writeInt32(0x1cb5c415); int count = id.size(); stream.writeInt32(count); - for (InputUser anId : id) { - anId.serializeToStream(stream); + for (int a = 0; a < count; a++) { + id.get(a).serializeToStream(stream); } } } @@ -8208,12 +10845,8 @@ public class TLRPC { public InputUser id; - public Class responseClass () { - return Bool.class; - } - - public void readParams(AbsSerializedData stream) { - id = (InputUser)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return Bool.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -8227,12 +10860,8 @@ public class TLRPC { public InputUser id; - public Class responseClass () { - return Bool.class; - } - - public void readParams(AbsSerializedData stream) { - id = (InputUser)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return Bool.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -8247,13 +10876,8 @@ public class TLRPC { public int offset; public int limit; - public Class responseClass () { - return contacts_Blocked.class; - } - - public void readParams(AbsSerializedData stream) { - offset = stream.readInt32(); - limit = stream.readInt32(); + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return contacts_Blocked.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -8263,21 +10887,51 @@ public class TLRPC { } } + public static class TL_contacts_exportCard extends TLObject { + public static int constructor = 0x84e53737; + + + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + Vector vector = new Vector(); + int size = stream.readInt32(exception); + for (int a = 0; a < size; a++) { + vector.objects.add(stream.readInt32(exception)); + } + return vector; + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class TL_contacts_importCard extends TLObject { + public static int constructor = 0x4fe196fe; + + public ArrayList export_card = new ArrayList<>(); + + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return User.TLdeserialize(stream, constructor, exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(0x1cb5c415); + int count = export_card.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + stream.writeInt32(export_card.get(a)); + } + } + } + public static class TL_messages_getMessages extends TLObject { public static int constructor = 0x4222fa74; public ArrayList id = new ArrayList<>(); - public Class responseClass () { - return messages_Messages.class; - } - - public void readParams(AbsSerializedData stream) { - stream.readInt32(); - int count = stream.readInt32(); - for (int a = 0; a < count; a++) { - id.add(stream.readInt32()); - } + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return messages_Messages.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -8285,8 +10939,8 @@ public class TLRPC { stream.writeInt32(0x1cb5c415); int count = id.size(); stream.writeInt32(count); - for (Integer anId : id) { - stream.writeInt32(anId); + for (int a = 0; a < count; a++) { + stream.writeInt32(id.get(a)); } } } @@ -8298,14 +10952,8 @@ public class TLRPC { public int max_id; public int limit; - public Class responseClass () { - return messages_Dialogs.class; - } - - public void readParams(AbsSerializedData stream) { - offset = stream.readInt32(); - max_id = stream.readInt32(); - limit = stream.readInt32(); + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return messages_Dialogs.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -8324,15 +10972,8 @@ public class TLRPC { public int max_id; public int limit; - public Class responseClass () { - return messages_Messages.class; - } - - public void readParams(AbsSerializedData stream) { - peer = (InputPeer)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - offset = stream.readInt32(); - max_id = stream.readInt32(); - limit = stream.readInt32(); + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return messages_Messages.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -8356,19 +10997,8 @@ public class TLRPC { public int max_id; public int limit; - public Class responseClass () { - return messages_Messages.class; - } - - public void readParams(AbsSerializedData stream) { - peer = (InputPeer)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - q = stream.readString(); - filter = (MessagesFilter)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - min_date = stream.readInt32(); - max_date = stream.readInt32(); - offset = stream.readInt32(); - max_id = stream.readInt32(); - limit = stream.readInt32(); + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return messages_Messages.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -8391,14 +11021,8 @@ public class TLRPC { public int max_id; public int offset; - public Class responseClass () { - return TL_messages_affectedHistory.class; - } - - public void readParams(AbsSerializedData stream) { - peer = (InputPeer)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - max_id = stream.readInt32(); - offset = stream.readInt32(); + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return TL_messages_affectedHistory.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -8415,13 +11039,8 @@ public class TLRPC { public InputPeer peer; public int offset; - public Class responseClass () { - return TL_messages_affectedHistory.class; - } - - public void readParams(AbsSerializedData stream) { - peer = (InputPeer)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - offset = stream.readInt32(); + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return TL_messages_affectedHistory.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -8431,19 +11050,54 @@ public class TLRPC { } } + public static class TL_messages_deleteMessages extends TLObject { + public static int constructor = 0xa5f18925; + + public ArrayList id = new ArrayList<>(); + + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return TL_messages_affectedMessages.TLdeserialize(stream, constructor, exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(0x1cb5c415); + int count = id.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + stream.writeInt32(id.get(a)); + } + } + } + + public static class TL_messages_receivedMessages extends TLObject { + public static int constructor = 0x5a954c0; + + public int max_id; + + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + Vector vector = new Vector(); + int size = stream.readInt32(exception); + for (int a = 0; a < size; a++) { + vector.objects.add(TL_receivedNotifyMessage.TLdeserialize(stream, stream.readInt32(exception), exception)); + } + return vector; + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(max_id); + } + } + public static class TL_messages_setTyping extends TLObject { public static int constructor = 0xa3825e50; public InputPeer peer; public SendMessageAction action; - public Class responseClass () { - return Bool.class; - } - - public void readParams(AbsSerializedData stream) { - peer = (InputPeer)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - action = (SendMessageAction)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return Bool.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -8462,18 +11116,8 @@ public class TLRPC { public String message; public long random_id; - public Class responseClass () { - return messages_SentMessage.class; - } - - public void readParams(AbsSerializedData stream) { - flags = stream.readInt32(); - peer = (InputPeer)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - if ((flags & 1) != 0) { - reply_to_msg_id = stream.readInt32(); - } - message = stream.readString(); - random_id = stream.readInt64(); + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return messages_SentMessage.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -8497,18 +11141,8 @@ public class TLRPC { public InputMedia media; public long random_id; - public Class responseClass () { - return Updates.class; - } - - public void readParams(AbsSerializedData stream) { - flags = stream.readInt32(); - peer = (InputPeer)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - if ((flags & 1) != 0) { - reply_to_msg_id = stream.readInt32(); - } - media = (InputMedia)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - random_id = stream.readInt64(); + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return Updates.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -8530,22 +11164,8 @@ public class TLRPC { public ArrayList id = new ArrayList<>(); public ArrayList random_id = new ArrayList<>(); - public Class responseClass () { - return Updates.class; - } - - public void readParams(AbsSerializedData stream) { - peer = (InputPeer)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - stream.readInt32(); - int count = stream.readInt32(); - for (int a = 0; a < count; a++) { - id.add(stream.readInt32()); - } - stream.readInt32(); - count = stream.readInt32(); - for (int a = 0; a < count; a++) { - random_id.add(stream.readInt64()); - } + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return Updates.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -8571,16 +11191,8 @@ public class TLRPC { public ArrayList id = new ArrayList<>(); - public Class responseClass () { - return TL_messages_chats.class; - } - - public void readParams(AbsSerializedData stream) { - stream.readInt32(); - int count = stream.readInt32(); - for (int a = 0; a < count; a++) { - id.add(stream.readInt32()); - } + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return TL_messages_chats.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -8588,8 +11200,8 @@ public class TLRPC { stream.writeInt32(0x1cb5c415); int count = id.size(); stream.writeInt32(count); - for (Integer anId : id) { - stream.writeInt32(anId); + for (int a = 0; a < count; a++) { + stream.writeInt32(id.get(a)); } } } @@ -8599,12 +11211,8 @@ public class TLRPC { public int chat_id; - public Class responseClass () { - return TL_messages_chatFull.class; - } - - public void readParams(AbsSerializedData stream) { - chat_id = stream.readInt32(); + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return TL_messages_chatFull.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -8619,13 +11227,8 @@ public class TLRPC { public int chat_id; public String title; - public Class responseClass () { - return Updates.class; - } - - public void readParams(AbsSerializedData stream) { - chat_id = stream.readInt32(); - title = stream.readString(); + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return Updates.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -8641,13 +11244,8 @@ public class TLRPC { public int chat_id; public InputChatPhoto photo; - public Class responseClass () { - return Updates.class; - } - - public void readParams(AbsSerializedData stream) { - chat_id = stream.readInt32(); - photo = (InputChatPhoto)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return Updates.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -8664,14 +11262,8 @@ public class TLRPC { public InputUser user_id; public int fwd_limit; - public Class responseClass () { - return Updates.class; - } - - public void readParams(AbsSerializedData stream) { - chat_id = stream.readInt32(); - user_id = (InputUser)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - fwd_limit = stream.readInt32(); + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return Updates.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -8688,13 +11280,8 @@ public class TLRPC { public int chat_id; public InputUser user_id; - public Class responseClass () { - return Updates.class; - } - - public void readParams(AbsSerializedData stream) { - chat_id = stream.readInt32(); - user_id = (InputUser)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return Updates.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -8710,17 +11297,8 @@ public class TLRPC { public ArrayList users = new ArrayList<>(); public String title; - public Class responseClass () { - return Updates.class; - } - - public void readParams(AbsSerializedData stream) { - stream.readInt32(); - int count = stream.readInt32(); - for (int a = 0; a < count; a++) { - users.add((InputUser)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32())); - } - title = stream.readString(); + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return Updates.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -8739,8 +11317,8 @@ public class TLRPC { public static int constructor = 0xedd4882a; - public Class responseClass () { - return TL_updates_state.class; + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return TL_updates_state.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -8755,14 +11333,8 @@ public class TLRPC { public int date; public int qts; - public Class responseClass () { - return updates_Difference.class; - } - - public void readParams(AbsSerializedData stream) { - pts = stream.readInt32(); - date = stream.readInt32(); - qts = stream.readInt32(); + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return updates_Difference.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -8779,13 +11351,8 @@ public class TLRPC { public InputPhoto id; public InputPhotoCrop crop; - public Class responseClass () { - return UserProfilePhoto.class; - } - - public void readParams(AbsSerializedData stream) { - id = (InputPhoto)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - crop = (InputPhotoCrop)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return UserProfilePhoto.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -8795,32 +11362,6 @@ public class TLRPC { } } - public static class TL_photos_deletePhotos extends TLObject { - public static int constructor = 0x87cf7f2f; - - public ArrayList id = new ArrayList<>(); - - public Class responseClass () { - return Vector.class; - } - - public void parseVector(Vector vector, AbsSerializedData data) { - int size = data.readInt32(); - for (int a = 0; a < size; a++) { - vector.objects.add(data.readInt64()); - } - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(0x1cb5c415); - stream.writeInt32(id.size()); - for (InputPhoto inputPhoto : id) { - inputPhoto.serializeToStream(stream); - } - } - } - public static class TL_photos_uploadProfilePhoto extends TLObject { public static int constructor = 0xd50f9c88; @@ -8829,15 +11370,8 @@ public class TLRPC { public InputGeoPoint geo_point; public InputPhotoCrop crop; - public Class responseClass () { - return TL_photos_photo.class; - } - - public void readParams(AbsSerializedData stream) { - file = (InputFile)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - caption = stream.readString(); - geo_point = (InputGeoPoint)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - crop = (InputPhotoCrop)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return TL_photos_photo.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -8849,6 +11383,31 @@ public class TLRPC { } } + public static class TL_photos_deletePhotos extends TLObject { + public static int constructor = 0x87cf7f2f; + + public ArrayList id = new ArrayList<>(); + + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + Vector vector = new Vector(); + int size = stream.readInt32(exception); + for (int a = 0; a < size; a++) { + vector.objects.add(stream.readInt64(exception)); + } + return vector; + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(0x1cb5c415); + int count = id.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + id.get(a).serializeToStream(stream); + } + } + } + public static class TL_upload_getFile extends TLObject { public static int constructor = 0xe3a6cfb5; @@ -8856,14 +11415,8 @@ public class TLRPC { public int offset; public int limit; - public Class responseClass() { - return TL_upload_file.class; - } - - public void readParams(AbsSerializedData stream) { - location = (InputFileLocation)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - offset = stream.readInt32(); - limit = stream.readInt32(); + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return TL_upload_file.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -8878,8 +11431,8 @@ public class TLRPC { public static int constructor = 0xc4f9186b; - public Class responseClass () { - return TL_config.class; + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return TL_config.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -8891,8 +11444,8 @@ public class TLRPC { public static int constructor = 0x1fb33026; - public Class responseClass () { - return TL_nearestDc.class; + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return TL_nearestDc.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -8908,15 +11461,8 @@ public class TLRPC { public String app_version; public String lang_code; - public Class responseClass () { - return help_AppUpdate.class; - } - - public void readParams(AbsSerializedData stream) { - device_model = stream.readString(); - system_version = stream.readString(); - app_version = stream.readString(); - lang_code = stream.readString(); + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return help_AppUpdate.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -8933,16 +11479,8 @@ public class TLRPC { public ArrayList events = new ArrayList<>(); - public Class responseClass () { - return Bool.class; - } - - public void readParams(AbsSerializedData stream) { - stream.readInt32(); - int count = stream.readInt32(); - for (int a = 0; a < count; a++) { - events.add((TL_inputAppEvent)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32())); - } + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return Bool.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -8950,8 +11488,8 @@ public class TLRPC { stream.writeInt32(0x1cb5c415); int count = events.size(); stream.writeInt32(count); - for (TL_inputAppEvent event : events) { - event.serializeToStream(stream); + for (int a = 0; a < count; a++) { + events.get(a).serializeToStream(stream); } } } @@ -8961,12 +11499,8 @@ public class TLRPC { public String lang_code; - public Class responseClass () { - return TL_help_inviteText.class; - } - - public void readParams(AbsSerializedData stream) { - lang_code = stream.readString(); + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return TL_help_inviteText.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -8983,15 +11517,8 @@ public class TLRPC { public int max_id; public int limit; - public Class responseClass () { - return photos_Photos.class; - } - - public void readParams(AbsSerializedData stream) { - user_id = (InputUser)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - offset = stream.readInt32(); - max_id = stream.readInt32(); - limit = stream.readInt32(); + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return photos_Photos.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -9010,14 +11537,8 @@ public class TLRPC { public int id; public long random_id; - public Class responseClass () { - return Updates.class; - } - - public void readParams(AbsSerializedData stream) { - peer = (InputPeer)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - id = stream.readInt32(); - random_id = stream.readInt64(); + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return Updates.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -9036,23 +11557,8 @@ public class TLRPC { public String message; public InputMedia media; - public Class responseClass () { - return Updates.class; - } - - public void readParams(AbsSerializedData stream) { - stream.readInt32(); - int count = stream.readInt32(); - for (int a = 0; a < count; a++) { - contacts.add((InputUser)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32())); - } - stream.readInt32(); - count = stream.readInt32(); - for (int a = 0; a < count; a++) { - random_id.add(stream.readInt64()); - } - message = stream.readString(); - media = (InputMedia)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return Updates.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -9081,14 +11587,8 @@ public class TLRPC { public int radius; public int limit; - public Class responseClass () { - return TL_geochats_located.class; - } - - public void readParams(AbsSerializedData stream) { - geo_point = (InputGeoPoint)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - radius = stream.readInt32(); - limit = stream.readInt32(); + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return TL_geochats_located.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -9105,13 +11605,8 @@ public class TLRPC { public int offset; public int limit; - public Class responseClass () { - return geochats_Messages.class; - } - - public void readParams(AbsSerializedData stream) { - offset = stream.readInt32(); - limit = stream.readInt32(); + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return geochats_Messages.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -9126,12 +11621,8 @@ public class TLRPC { public TL_inputGeoChat peer; - public Class responseClass () { - return TL_geochats_statedMessage.class; - } - - public void readParams(AbsSerializedData stream) { - peer = (TL_inputGeoChat)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return TL_geochats_statedMessage.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -9145,12 +11636,8 @@ public class TLRPC { public TL_inputGeoChat peer; - public Class responseClass() { - return TL_messages_chatFull.class; - } - - public void readParams(AbsSerializedData stream) { - peer = (TL_inputGeoChat)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return TL_messages_chatFull.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -9166,14 +11653,8 @@ public class TLRPC { public String title; public String address; - public Class responseClass () { - return TL_geochats_statedMessage.class; - } - - public void readParams(AbsSerializedData stream) { - peer = (TL_inputGeoChat)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - title = stream.readString(); - address = stream.readString(); + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return TL_geochats_statedMessage.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -9190,13 +11671,8 @@ public class TLRPC { public TL_inputGeoChat peer; public InputChatPhoto photo; - public Class responseClass () { - return TL_geochats_statedMessage.class; - } - - public void readParams(AbsSerializedData stream) { - peer = (TL_inputGeoChat)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - photo = (InputChatPhoto)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return TL_geochats_statedMessage.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -9218,19 +11694,8 @@ public class TLRPC { public int max_id; public int limit; - public Class responseClass () { - return geochats_Messages.class; - } - - public void readParams(AbsSerializedData stream) { - peer = (TL_inputGeoChat)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - q = stream.readString(); - filter = (MessagesFilter)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - min_date = stream.readInt32(); - max_date = stream.readInt32(); - offset = stream.readInt32(); - max_id = stream.readInt32(); - limit = stream.readInt32(); + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return geochats_Messages.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -9254,15 +11719,8 @@ public class TLRPC { public int max_id; public int limit; - public Class responseClass () { - return geochats_Messages.class; - } - - public void readParams(AbsSerializedData stream) { - peer = (TL_inputGeoChat)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - offset = stream.readInt32(); - max_id = stream.readInt32(); - limit = stream.readInt32(); + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return geochats_Messages.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -9280,13 +11738,8 @@ public class TLRPC { public TL_inputGeoChat peer; public boolean typing; - public Class responseClass () { - return Bool.class; - } - - public void readParams(AbsSerializedData stream) { - peer = (TL_inputGeoChat)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - typing = stream.readBool(); + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return Bool.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -9303,14 +11756,8 @@ public class TLRPC { public String message; public long random_id; - public Class responseClass () { - return TL_geochats_statedMessage.class; - } - - public void readParams(AbsSerializedData stream) { - peer = (TL_inputGeoChat)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - message = stream.readString(); - random_id = stream.readInt64(); + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return TL_geochats_statedMessage.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -9328,14 +11775,8 @@ public class TLRPC { public InputMedia media; public long random_id; - public Class responseClass () { - return TL_geochats_statedMessage.class; - } - - public void readParams(AbsSerializedData stream) { - peer = (TL_inputGeoChat)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - media = (InputMedia)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - random_id = stream.readInt64(); + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return TL_geochats_statedMessage.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -9354,15 +11795,8 @@ public class TLRPC { public String address; public String venue; - public Class responseClass () { - return TL_geochats_statedMessage.class; - } - - public void readParams(AbsSerializedData stream) { - title = stream.readString(); - geo_point = (InputGeoPoint)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - address = stream.readString(); - venue = stream.readString(); + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return TL_geochats_statedMessage.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -9380,13 +11814,8 @@ public class TLRPC { public int version; public int random_length; - public Class responseClass () { - return messages_DhConfig.class; - } - - public void readParams(AbsSerializedData stream) { - version = stream.readInt32(); - random_length = stream.readInt32(); + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return messages_DhConfig.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -9403,14 +11832,8 @@ public class TLRPC { public int random_id; public byte[] g_a; - public Class responseClass () { - return EncryptedChat.class; - } - - public void readParams(AbsSerializedData stream) { - user_id = (InputUser)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - random_id = stream.readInt32(); - g_a = stream.readByteArray(); + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return EncryptedChat.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -9428,14 +11851,8 @@ public class TLRPC { public byte[] g_b; public long key_fingerprint; - public Class responseClass () { - return EncryptedChat.class; - } - - public void readParams(AbsSerializedData stream) { - peer = (TL_inputEncryptedChat)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - g_b = stream.readByteArray(); - key_fingerprint = stream.readInt64(); + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return EncryptedChat.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -9451,12 +11868,8 @@ public class TLRPC { public int chat_id; - public Class responseClass () { - return Bool.class; - } - - public void readParams(AbsSerializedData stream) { - chat_id = stream.readInt32(); + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return Bool.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -9471,13 +11884,8 @@ public class TLRPC { public TL_inputEncryptedChat peer; public boolean typing; - public Class responseClass() { - return Bool.class; - } - - public void readParams(AbsSerializedData stream) { - peer = (TL_inputEncryptedChat)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - typing = stream.readBool(); + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return Bool.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -9493,13 +11901,8 @@ public class TLRPC { public TL_inputEncryptedChat peer; public int max_date; - public Class responseClass () { - return Bool.class; - } - - public void readParams(AbsSerializedData stream) { - peer = (TL_inputEncryptedChat)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - max_date = stream.readInt32(); + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return Bool.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -9509,21 +11912,63 @@ public class TLRPC { } } - public static class TL_messages_deleteMessages extends TLObject { - public static int constructor = 0xa5f18925; + public static class TL_messages_receivedQueue extends TLObject { + public static int constructor = 0x55a5bb66; + + public int max_qts; + + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + Vector vector = new Vector(); + int size = stream.readInt32(exception); + for (int a = 0; a < size; a++) { + vector.objects.add(stream.readInt64(exception)); + } + return vector; + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(max_qts); + } + } + + public static class TL_help_getSupport extends TLObject { + public static int constructor = 0x9cdf08cd; + + + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return TL_help_support.TLdeserialize(stream, constructor, exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class TL_auth_sendSms extends TLObject { + public static int constructor = 0xda9f3e8; + + public String phone_number; + public String phone_code_hash; + + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return Bool.TLdeserialize(stream, constructor, exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeString(phone_number); + stream.writeString(phone_code_hash); + } + } + + public static class TL_messages_readMessageContents extends TLObject { + public static int constructor = 0x36a73f77; public ArrayList id = new ArrayList<>(); - public Class responseClass () { - return TL_messages_affectedMessages.class; - } - - public void readParams(AbsSerializedData stream) { - stream.readInt32(); - int count = stream.readInt32(); - for (int a = 0; a < count; a++) { - id.add(stream.readInt32()); - } + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return TL_messages_affectedMessages.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -9537,12 +11982,278 @@ public class TLRPC { } } + public static class TL_account_checkUsername extends TLObject { + public static int constructor = 0x2714d86c; + + public String username; + + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return Bool.TLdeserialize(stream, constructor, exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeString(username); + } + } + + public static class TL_account_updateUsername extends TLObject { + public static int constructor = 0x3e0bdd7c; + + public String username; + + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return User.TLdeserialize(stream, constructor, exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeString(username); + } + } + + public static class TL_contacts_search extends TLObject { + public static int constructor = 0x11f812d8; + + public String q; + public int limit; + + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return TL_contacts_found.TLdeserialize(stream, constructor, exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeString(q); + stream.writeInt32(limit); + } + } + + public static class TL_account_getPrivacy extends TLObject { + public static int constructor = 0xdadbc950; + + public TL_inputPrivacyKeyStatusTimestamp key; + + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return TL_account_privacyRules.TLdeserialize(stream, constructor, exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + key.serializeToStream(stream); + } + } + + public static class TL_account_setPrivacy extends TLObject { + public static int constructor = 0xc9f81ce8; + + public TL_inputPrivacyKeyStatusTimestamp key; + public ArrayList rules = new ArrayList<>(); + + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return TL_account_privacyRules.TLdeserialize(stream, constructor, exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + key.serializeToStream(stream); + stream.writeInt32(0x1cb5c415); + int count = rules.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + rules.get(a).serializeToStream(stream); + } + } + } + + public static class TL_account_deleteAccount extends TLObject { + public static int constructor = 0x418d4e0b; + + public String reason; + + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return Bool.TLdeserialize(stream, constructor, exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeString(reason); + } + } + + public static class TL_account_getAccountTTL extends TLObject { + public static int constructor = 0x8fc711d; + + + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return TL_accountDaysTTL.TLdeserialize(stream, constructor, exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class TL_account_setAccountTTL extends TLObject { + public static int constructor = 0x2442485e; + + public TL_accountDaysTTL ttl; + + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return Bool.TLdeserialize(stream, constructor, exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + ttl.serializeToStream(stream); + } + } + + public static class TL_contacts_resolveUsername extends TLObject { + public static int constructor = 0xbf0131c; + + public String username; + + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return User.TLdeserialize(stream, constructor, exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeString(username); + } + } + + public static class TL_account_sendChangePhoneCode extends TLObject { + public static int constructor = 0xa407a8f4; + + public String phone_number; + + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return TL_account_sentChangePhoneCode.TLdeserialize(stream, constructor, exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeString(phone_number); + } + } + + public static class TL_account_changePhone extends TLObject { + public static int constructor = 0x70c32edb; + + public String phone_number; + public String phone_code_hash; + public String phone_code; + + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return User.TLdeserialize(stream, constructor, exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeString(phone_number); + stream.writeString(phone_code_hash); + stream.writeString(phone_code); + } + } + + public static class TL_messages_getStickers extends TLObject { + public static int constructor = 0xae22e045; + + public String emoticon; + public String hash; + + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return messages_Stickers.TLdeserialize(stream, constructor, exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeString(emoticon); + stream.writeString(hash); + } + } + + public static class TL_messages_getAllStickers extends TLObject { + public static int constructor = 0xaa3bc868; + + public String hash; + + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return messages_AllStickers.TLdeserialize(stream, constructor, exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeString(hash); + } + } + + public static class TL_account_updateDeviceLocked extends TLObject { + public static int constructor = 0x38df3532; + + public int period; + + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return Bool.TLdeserialize(stream, constructor, exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(period); + } + } + + public static class TL_messages_getWebPagePreview extends TLObject { + public static int constructor = 0x25223e24; + + public String message; + + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return MessageMedia.TLdeserialize(stream, constructor, exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeString(message); + } + } + + public static class TL_account_getAuthorizations extends TLObject { + public static int constructor = 0xe320c158; + + + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return TL_account_authorizations.TLdeserialize(stream, constructor, exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class TL_account_resetAuthorization extends TLObject { + public static int constructor = 0xdf77f3bc; + + public long hash; + + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return Bool.TLdeserialize(stream, constructor, exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt64(hash); + } + } + public static class TL_account_getPassword extends TLObject { public static int constructor = 0x548a30f5; - public Class responseClass () { - return account_Password.class; + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return account_Password.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -9555,12 +12266,8 @@ public class TLRPC { public byte[] current_password_hash; - public Class responseClass () { - return TL_account_passwordSettings.class; - } - - public void readParams(AbsSerializedData stream) { - current_password_hash = stream.readByteArray(); + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return TL_account_passwordSettings.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -9575,13 +12282,8 @@ public class TLRPC { public byte[] current_password_hash; public TL_account_passwordInputSettings new_settings; - public Class responseClass () { - return Bool.class; - } - - public void readParams(AbsSerializedData stream) { - current_password_hash = stream.readByteArray(); - new_settings = (TL_account_passwordInputSettings)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return Bool.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -9596,12 +12298,8 @@ public class TLRPC { public byte[] password_hash; - public Class responseClass () { - return TL_auth_authorization.class; - } - - public void readParams(AbsSerializedData stream) { - password_hash = stream.readByteArray(); + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return TL_auth_authorization.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -9614,8 +12312,8 @@ public class TLRPC { public static int constructor = 0xd897bc66; - public Class responseClass () { - return TL_auth_passwordRecovery.class; + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return TL_auth_passwordRecovery.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -9628,12 +12326,8 @@ public class TLRPC { public String code; - public Class responseClass () { - return TL_auth_authorization.class; - } - - public void readParams(AbsSerializedData stream) { - code = stream.readString(); + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return TL_auth_authorization.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -9642,765 +12336,323 @@ public class TLRPC { } } + public static class TL_messages_exportChatInvite extends TLObject { + public static int constructor = 0x7d885289; + + public int chat_id; + + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return ExportedChatInvite.TLdeserialize(stream, constructor, exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(chat_id); + } + } + + public static class TL_messages_checkChatInvite extends TLObject { + public static int constructor = 0x3eadb1bb; + + public String hash; + + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return ChatInvite.TLdeserialize(stream, constructor, exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeString(hash); + } + } + + public static class TL_messages_importChatInvite extends TLObject { + public static int constructor = 0x6c50051c; + + public String hash; + + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return Updates.TLdeserialize(stream, constructor, exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeString(hash); + } + } + //manually created - public static class TL_documentAttributeSticker_old extends TL_documentAttributeSticker { - public static int constructor = 0xfb0a5727; - - public void readParams(AbsSerializedData stream) { - - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - } - } - - public static class TL_messageMediaUnsupported_old extends TL_messageMediaUnsupported { - public static int constructor = 0x29632a36; - - - public void readParams(AbsSerializedData stream) { - bytes = stream.readByteArray(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeByteArray(bytes); - } - } - - public static class TL_config_old extends TL_config { - public static int constructor = 0x2e54dd74; - - public void readParams(AbsSerializedData stream) { - date = stream.readInt32(); - test_mode = stream.readBool(); - this_dc = stream.readInt32(); - stream.readInt32(); - int count = stream.readInt32(); - for (int a = 0; a < count; a++) { - dc_options.add((TL_dcOption) TLClassStore.Instance().TLdeserialize(stream, stream.readInt32())); - } - chat_size_max = stream.readInt32(); - broadcast_size_max = stream.readInt32(); - expires = (int) (System.currentTimeMillis() / 1000) + 3600; - chat_big_size = 10; - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(date); - stream.writeBool(test_mode); - stream.writeInt32(this_dc); - stream.writeInt32(0x1cb5c415); - int count = dc_options.size(); - stream.writeInt32(count); - for (TL_dcOption dc_option : dc_options) { - dc_option.serializeToStream(stream); - } - stream.writeInt32(chat_size_max); - stream.writeInt32(broadcast_size_max); - } - } - - public static class TL_document_old extends TL_document { - public static int constructor = 0x9efc6326; - - - public void readParams(AbsSerializedData stream) { - id = stream.readInt64(); - access_hash = stream.readInt64(); - stream.readInt32(); - date = stream.readInt32(); - TL_documentAttributeFilename fileName = new TL_documentAttributeFilename(); - fileName.file_name = stream.readString(); - attributes.add(fileName); - mime_type = stream.readString(); - size = stream.readInt32(); - thumb = (PhotoSize)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - dc_id = stream.readInt32(); - } - } - - public static class TL_decryptedMessageHolder extends TLObject { - public static int constructor = 0x555555F9; - - public long random_id; + //Photo start + public static class Photo extends TLObject { + public long id; + public long access_hash; + public int user_id; public int date; - public TL_decryptedMessageLayer layer; - public EncryptedFile file; - public boolean new_key_used; + public GeoPoint geo; + public ArrayList sizes = new ArrayList<>(); + public String caption; //custom - public void readParams(AbsSerializedData stream) { - random_id = stream.readInt64(); - date = stream.readInt32(); - layer = (TL_decryptedMessageLayer)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - if (stream.readBool()) { - file = (EncryptedFile) TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); + public static Photo TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + Photo result = null; + switch(constructor) { + case 0xc3838076: + result = new TL_photo(); + break; + case 0x2331b22d: + result = new TL_photoEmpty(); + break; + case 0x22b56751: + result = new TL_photo_old(); //custom + break; } - new_key_used = stream.readBool(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt64(random_id); - stream.writeInt32(date); - layer.serializeToStream(stream); - stream.writeBool(file != null); - if (file != null) { - file.serializeToStream(stream); - } - stream.writeBool(new_key_used); - } - } - - public static class TL_messages_sendEncryptedService extends TLObject { - public static int constructor = 0x32d439a4; - - public TL_inputEncryptedChat peer; - public long random_id; - public ByteBufferDesc data; - - public Class responseClass () { - return messages_SentEncryptedMessage.class; - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - peer.serializeToStream(stream); - stream.writeInt64(random_id); - stream.writeByteBuffer(data); - } - - @Override - public void freeResources() { - if (disableFree) { - return; - } - if (data != null) { - BuffersStorage.getInstance().reuseFreeBuffer(data); - data = null; - } - } - } - - public static class TL_userDeleted_old extends TL_userDeleted { - public static int constructor = 0xb29ad7cc; - - - public void readParams(AbsSerializedData stream) { - id = stream.readInt32(); - first_name = stream.readString(); - last_name = stream.readString(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(id); - stream.writeString(first_name); - stream.writeString(last_name); - } - } - - public static class TL_userForeign_old extends TL_userForeign { - public static int constructor = 0x5214c89d; - - - public void readParams(AbsSerializedData stream) { - id = stream.readInt32(); - first_name = stream.readString(); - last_name = stream.readString(); - access_hash = stream.readInt64(); - photo = (UserProfilePhoto)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - status = (UserStatus)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(id); - stream.writeString(first_name); - stream.writeString(last_name); - stream.writeInt64(access_hash); - photo.serializeToStream(stream); - status.serializeToStream(stream); - } - } - - public static class TL_userRequest_old extends TL_userRequest { - public static int constructor = 0x22e8ceb0; - - - public void readParams(AbsSerializedData stream) { - id = stream.readInt32(); - first_name = stream.readString(); - last_name = stream.readString(); - access_hash = stream.readInt64(); - phone = stream.readString(); - photo = (UserProfilePhoto)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - status = (UserStatus)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(id); - stream.writeString(first_name); - stream.writeString(last_name); - stream.writeInt64(access_hash); - stream.writeString(phone); - photo.serializeToStream(stream); - status.serializeToStream(stream); - } - } - - public static class TL_userContact_old extends TL_userContact { - public static int constructor = 0xf2fb8319; - - - public void readParams(AbsSerializedData stream) { - id = stream.readInt32(); - first_name = stream.readString(); - last_name = stream.readString(); - access_hash = stream.readInt64(); - phone = stream.readString(); - photo = (UserProfilePhoto)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - status = (UserStatus)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(id); - stream.writeString(first_name); - stream.writeString(last_name); - stream.writeInt64(access_hash); - stream.writeString(phone); - photo.serializeToStream(stream); - status.serializeToStream(stream); - } - } - - public static class TL_userSelf_old2 extends TL_userSelf { - public static int constructor = 0x7007b451; - - - public void readParams(AbsSerializedData stream) { - id = stream.readInt32(); - first_name = stream.readString(); - last_name = stream.readString(); - username = stream.readString(); - phone = stream.readString(); - photo = (UserProfilePhoto)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - status = (UserStatus)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - inactive = stream.readBool(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(id); - stream.writeString(first_name); - stream.writeString(last_name); - stream.writeString(username); - stream.writeString(phone); - photo.serializeToStream(stream); - status.serializeToStream(stream); - stream.writeBool(inactive); - } - } - - public static class TL_userSelf_old extends TL_userSelf { - public static int constructor = 0x720535ec; - - - public void readParams(AbsSerializedData stream) { - id = stream.readInt32(); - first_name = stream.readString(); - last_name = stream.readString(); - phone = stream.readString(); - photo = (UserProfilePhoto)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - status = (UserStatus)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - inactive = stream.readBool(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(id); - stream.writeString(first_name); - stream.writeString(last_name); - stream.writeString(phone); - photo.serializeToStream(stream); - status.serializeToStream(stream); - stream.writeBool(inactive); - } - } - - public static class TL_set_client_DH_params extends TLObject { - public static int constructor = 0xf5045f1f; - - public byte[] nonce; - public byte[] server_nonce; - public ByteBufferDesc encrypted_data; - - public Class responseClass () { - return Set_client_DH_params_answer.class; - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeRaw(nonce); - stream.writeRaw(server_nonce); - stream.writeByteBuffer(encrypted_data); - } - - @Override - public void freeResources() { - if (disableFree) { - return; - } - if (encrypted_data != null) { - BuffersStorage.getInstance().reuseFreeBuffer(encrypted_data); - encrypted_data = null; - } - } - } - - public static class TL_messages_sendEncrypted extends TLObject { - public static int constructor = 0xa9776773; - - public TL_inputEncryptedChat peer; - public long random_id; - public ByteBufferDesc data; - - public Class responseClass () { - return messages_SentEncryptedMessage.class; - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - peer.serializeToStream(stream); - stream.writeInt64(random_id); - stream.writeByteBuffer(data); - } - - @Override - public void freeResources() { - if (disableFree) { - return; - } - if (data != null) { - BuffersStorage.getInstance().reuseFreeBuffer(data); - data = null; - } - } - } - - public static class TL_decryptedMessageService_old extends TL_decryptedMessageService { - public static int constructor = 0xaa48327d; - - - public void readParams(AbsSerializedData stream) { - random_id = stream.readInt64(); - random_bytes = stream.readByteArray(); - action = (DecryptedMessageAction)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt64(random_id); - stream.writeByteArray(random_bytes); - action.serializeToStream(stream); - } - } - - public static class TL_decryptedMessage_old extends TL_decryptedMessage { - public static int constructor = 0x1f814f1f; - - - public void readParams(AbsSerializedData stream) { - random_id = stream.readInt64(); - random_bytes = stream.readByteArray(); - message = stream.readString(); - media = (DecryptedMessageMedia)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt64(random_id); - stream.writeByteArray(random_bytes); - stream.writeString(message); - media.serializeToStream(stream); - } - } - - public static class TL_messages_sendEncryptedFile extends TLObject { - public static int constructor = 0x9a901b66; - - public TL_inputEncryptedChat peer; - public long random_id; - public ByteBufferDesc data; - public InputEncryptedFile file; - - public Class responseClass () { - return messages_SentEncryptedMessage.class; - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - peer.serializeToStream(stream); - stream.writeInt64(random_id); - stream.writeByteBuffer(data); - file.serializeToStream(stream); - } - - @Override - public void freeResources() { - if (disableFree) { - return; - } - if (data != null) { - BuffersStorage.getInstance().reuseFreeBuffer(data); - data = null; - } - } - } - - public static class UserStatus extends TLObject { - public int expires; - } - - public static class TL_userStatusLastWeek extends UserStatus { - public static int constructor = 0x7bf09fc; - - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - } - } - - public static class TL_userStatusEmpty extends UserStatus { - public static int constructor = 0x9d05049; - - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - } - } - - public static class TL_userStatusLastMonth extends UserStatus { - public static int constructor = 0x77ebc742; - - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - } - } - - public static class TL_userStatusOnline extends UserStatus { - public static int constructor = 0xedb93949; - - - public void readParams(AbsSerializedData stream) { - expires = stream.readInt32(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(expires); - } - } - - public static class TL_userStatusRecently extends UserStatus { - public static int constructor = 0xe26f42f1; - - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - } - } - - public static class TL_userStatusOffline extends UserStatus { - public static int constructor = 0x8c703f; - - - public void readParams(AbsSerializedData stream) { - expires = stream.readInt32(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(expires); - } - } - - public static class TL_upload_file extends TLObject { - public static int constructor = 0x96a18d5; - - public storage_FileType type; - public int mtime; - public ByteBufferDesc bytes; - - public void readParams(AbsSerializedData stream) { - type = (storage_FileType)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - mtime = stream.readInt32(); - bytes = stream.readByteBuffer(); - } - - @Override - public void freeResources() { - if (disableFree) { - return; - } - if (bytes != null) { - BuffersStorage.getInstance().reuseFreeBuffer(bytes); - bytes = null; - } - } - } - - public static class TL_messages_receivedQueue extends TLObject { - public static int constructor = 0x55a5bb66; - - public int max_qts; - - public Class responseClass () { - return Vector.class; - } - - public void parseVector(Vector vector, AbsSerializedData data) { - int size = data.readInt32(); - for (int a = 0; a < size; a++) { - vector.objects.add(data.readInt64()); - } - } - - public void readParams(AbsSerializedData stream) { - max_qts = stream.readInt32(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(max_qts); - } - } - - public static class TL_account_getWallPapers extends TLObject { - public static int constructor = 0xc04cfac2; - - public Class responseClass () { - return Vector.class; - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - } - - public void parseVector(Vector vector, AbsSerializedData data) { - int size = data.readInt32(); - for (int a = 0; a < size; a++) { - vector.objects.add(TLClassStore.Instance().TLdeserialize(data, data.readInt32())); - } - } - } - - public static class TL_get_future_salts extends TLObject { - public static int constructor = 0xb921bd04; - - public int num; - - public int layer () { - return 0; - } - - public Class responseClass () { - return TL_futuresalts.class; - } - - public void readParams(AbsSerializedData stream) { - num = stream.readInt32(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(num); - } - } - - public static class TL_rpc_drop_answer extends TLObject { - public static int constructor = 0x58e4a740; - - public long req_msg_id; - - public int layer () { - return 0; - } - - public Class responseClass() { - return RpcDropAnswer.class; - } - - public void readParams(AbsSerializedData stream) { - req_msg_id = stream.readInt64(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt64(req_msg_id); - } - } - - public static class TL_msg_container extends TLObject { - public ArrayList messages; - - public static int constructor = 0x73f1f8dc; - - public void readParams(AbsSerializedData stream) { - messages = new ArrayList<>(); - int count = stream.readInt32(); - for (int a = 0; a < count; a++) { - TL_protoMessage message = new TL_protoMessage(); - message.msg_id = stream.readInt64(); - message.seqno = stream.readInt32(); - message.bytes = stream.readInt32(); - int constructor = stream.readInt32(); - TLObject request = ConnectionsManager.getInstance().getRequestWithMessageId(message.msg_id); - message.body = TLClassStore.Instance().TLdeserialize(stream, constructor, request); - messages.add(message); - } - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(messages.size()); - for (TLObject obj : messages) { - TL_protoMessage proto = (TL_protoMessage)obj; - stream.writeInt64(proto.msg_id); - stream.writeInt32(proto.seqno); - stream.writeInt32(proto.bytes); - proto.body.serializeToStream(stream); - } - } - } - - public static class TL_rpc_result extends TLObject { - public static int constructor = 0xf35c6d01; - - public long req_msg_id; - public TLObject result; - - public void readParams(AbsSerializedData stream) { - req_msg_id = stream.readInt64(); - TLObject request = ConnectionsManager.getInstance().getRequestWithMessageId(req_msg_id); - result = TLClassStore.Instance().TLdeserialize(stream, stream.readInt32(), request); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt64(req_msg_id); - result.serializeToStream(stream); - } - - @Override - public void freeResources() { - if (disableFree) { - return; + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in Photo", constructor)); } if (result != null) { - result.freeResources(); + result.readParams(stream, exception); } + return result; } } - public static class TL_futuresalts extends TLObject { - public static int constructor = 0xae500895; + public static class TL_photo_old extends TL_photo { + public static int constructor = 0x22b56751; - public long req_msg_id; - public int now; - public ArrayList salts = new ArrayList<>(); - public void readParams(AbsSerializedData stream) { - req_msg_id = stream.readInt64(); - now = stream.readInt32(); - int count = stream.readInt32(); + public void readParams(AbsSerializedData stream, boolean exception) { + id = stream.readInt64(exception); + access_hash = stream.readInt64(exception); + user_id = stream.readInt32(exception); + date = stream.readInt32(exception); + caption = stream.readString(exception); + geo = GeoPoint.TLdeserialize(stream, stream.readInt32(exception), exception); + stream.readInt32(exception); + int count = stream.readInt32(exception); for (int a = 0; a < count; a++) { - TL_futureSalt salt = new TL_futureSalt(); - salt.readParams(stream); - salts.add(salt); + sizes.add(PhotoSize.TLdeserialize(stream, stream.readInt32(exception), exception)); } } public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); - stream.writeInt64(req_msg_id); - stream.writeInt32(now); - int count = salts.size(); + stream.writeInt64(id); + stream.writeInt64(access_hash); + stream.writeInt32(user_id); + stream.writeInt32(date); + stream.writeString(caption); + geo.serializeToStream(stream); + stream.writeInt32(0x1cb5c415); + int count = sizes.size(); stream.writeInt32(count); - for (TL_futureSalt salt : salts) { - salt.serializeToStream(stream); + for (int a = 0; a < count; a++) { + sizes.get(a).serializeToStream(stream); } } } + //Photo end - public static class TL_gzip_packed extends TLObject { - public static int constructor = 0x3072cfa1; + //EncryptedChat start + public static class EncryptedChat extends TLObject { + public int id; + public long access_hash; + public int date; + public int admin_id; + public int participant_id; + public byte[] g_a_or_b; + public long key_fingerprint; + public byte[] g_a; + public byte[] a_or_b; //custom + public byte[] auth_key; //custom + public int user_id; //custom + public int ttl; //custom + public int layer; //custom + public int seq_in; //custom + public int seq_out; //custom + public byte[] key_hash; //custom + public short key_use_count_in; //custom + public short key_use_count_out; //custom + public long exchange_id; //custom + public int key_create_date; //custom + public long future_key_fingerprint; //custom + public byte[] future_auth_key; //custom - public byte[] packed_data; - - public void readParams(AbsSerializedData stream) { - packed_data = stream.readByteArray(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeByteArray(packed_data); + public static EncryptedChat TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + EncryptedChat result = null; + switch(constructor) { + case 0x3bf703dc: + result = new TL_encryptedChatWaiting(); + break; + case 0xab7ec0a0: + result = new TL_encryptedChatEmpty(); + break; + case 0x13d6dd27: + result = new TL_encryptedChatDiscarded(); + break; + case 0xfa56ce36: + result = new TL_encryptedChat(); + break; + case 0xc878527e: + result = new TL_encryptedChatRequested(); + break; + case 0x6601d14f: + result = new TL_encryptedChat_old(); //custom + break; + case 0xfda9a7b7: + result = new TL_encryptedChatRequested_old(); //custom + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in EncryptedChat", constructor)); + } + if (result != null) { + result.readParams(stream, exception); + } + return result; } } + public static class TL_encryptedChat_old extends TL_encryptedChat { + public static int constructor = 0x6601d14f; + + + public void readParams(AbsSerializedData stream, boolean exception) { + id = stream.readInt32(exception); + access_hash = stream.readInt64(exception); + date = stream.readInt32(exception); + admin_id = stream.readInt32(exception); + participant_id = stream.readInt32(exception); + g_a_or_b = stream.readByteArray(exception); + stream.readByteArray(exception); + key_fingerprint = stream.readInt64(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(TL_encryptedChat.constructor); + stream.writeInt32(id); + stream.writeInt64(access_hash); + stream.writeInt32(date); + stream.writeInt32(admin_id); + stream.writeInt32(participant_id); + stream.writeByteArray(g_a_or_b); + stream.writeInt64(key_fingerprint); + } + } + + public static class TL_encryptedChatRequested_old extends EncryptedChat { + public static int constructor = 0xfda9a7b7; + + + public void readParams(AbsSerializedData stream, boolean exception) { + id = stream.readInt32(exception); + access_hash = stream.readInt64(exception); + date = stream.readInt32(exception); + admin_id = stream.readInt32(exception); + participant_id = stream.readInt32(exception); + g_a = stream.readByteArray(exception); + stream.readByteArray(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(TL_encryptedChatRequested.constructor); + stream.writeInt32(id); + stream.writeInt64(access_hash); + stream.writeInt32(date); + stream.writeInt32(admin_id); + stream.writeInt32(participant_id); + stream.writeByteArray(g_a); + } + } + //EncryptedChat end + + //Message start public static class Message extends TLObject { - public int flags; public int id; - public int fwd_from_id; - public int fwd_date; public int from_id; public Peer to_id; public int date; + public MessageAction action; + public int fwd_from_id; + public int fwd_date; + public int reply_to_msg_id; public String message; public MessageMedia media; - public int reply_to_msg_id; - public MessageAction action; - public int send_state = 0; - public int fwd_msg_id = 0; - public String attachPath = ""; - public long random_id; - public int local_id = 0; - public long dialog_id; - public int ttl; - public int destroyTime; - public int layer; - public int seq_in; - public int seq_out; - public TLRPC.Message replyMessage; - public VideoEditedInfo videoEditedInfo = null; + public int flags; + public int send_state = 0; //custom + public int fwd_msg_id = 0; //custom + public String attachPath = ""; //custom + public long random_id; //custom + public int local_id = 0; //custom + public long dialog_id; //custom + public int ttl; //custom + public int destroyTime; //custom + public int layer; //custom + public int seq_in; //custom + public int seq_out; //custom + public TLRPC.Message replyMessage; //custom + public VideoEditedInfo videoEditedInfo = null; //custom + + public static Message TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + Message result = null; + switch(constructor) { + case 0x1d86f70e: + result = new TL_messageService(); + break; + case 0xa7ab1991: + result = new TL_message(); + break; + case 0x83e5de54: + result = new TL_messageEmpty(); + break; + case 0xa367e716: + result = new TL_messageForwarded_old2(); //custom + break; + case 0x5f46804: + result = new TL_messageForwarded_old(); //custom + break; + case 0x567699b3: + result = new TL_message_old2(); //custom + break; + case 0x9f8d60bb: + result = new TL_messageService_old(); //custom + break; + case 0x22eb6aba: + result = new TL_message_old(); //custom + break; + case 0x555555F8: + result = new TL_message_secret(); //custom + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in Message", constructor)); + } + if (result != null) { + result.readParams(stream, exception); + } + return result; + } } public static class TL_messageForwarded_old2 extends Message { public static int constructor = 0xa367e716; - public void readParams(AbsSerializedData stream) { - flags = stream.readInt32(); - id = stream.readInt32(); - fwd_from_id = stream.readInt32(); - fwd_date = stream.readInt32(); - from_id = stream.readInt32(); - to_id = (Peer)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - date = stream.readInt32(); - message = stream.readString(); + public void readParams(AbsSerializedData stream, boolean exception) { + flags = stream.readInt32(exception); + id = stream.readInt32(exception); + fwd_from_id = stream.readInt32(exception); + fwd_date = stream.readInt32(exception); + from_id = stream.readInt32(exception); + to_id = Peer.TLdeserialize(stream, stream.readInt32(exception), exception); + date = stream.readInt32(exception); + message = stream.readString(exception); flags |= MESSAGE_FLAG_FWD; - media = (MessageMedia)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); + media = MessageMedia.TLdeserialize(stream, stream.readInt32(exception), exception); if (id < 0) { - fwd_msg_id = stream.readInt32(); + fwd_msg_id = stream.readInt32(exception); } if (id < 0 || (media != null && !(media instanceof TL_messageMediaEmpty) && message != null && message.length() != 0 && message.startsWith("-1"))) { - attachPath = stream.readString(); + attachPath = stream.readString(exception); } if (id < 0 && message.length() > 6 && media instanceof TL_messageMediaVideo) { videoEditedInfo = new VideoEditedInfo(); @@ -10429,31 +12681,32 @@ public class TLRPC { public static class TL_message extends Message { public static int constructor = 0xa7ab1991; - - public void readParams(AbsSerializedData stream) { - flags = stream.readInt32(); - id = stream.readInt32(); - from_id = stream.readInt32(); - to_id = (Peer)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - if ((flags & MESSAGE_FLAG_FWD) != 0) { - fwd_from_id = stream.readInt32(); - fwd_date = stream.readInt32(); + public void readParams(AbsSerializedData stream, boolean exception) { + flags = stream.readInt32(exception); + id = stream.readInt32(exception); + from_id = stream.readInt32(exception); + to_id = Peer.TLdeserialize(stream, stream.readInt32(exception), exception); + if ((flags & 4) != 0) { + fwd_from_id = stream.readInt32(exception); } - if ((flags & MESSAGE_FLAG_REPLY) != 0) { - reply_to_msg_id = stream.readInt32(); + if ((flags & 4) != 0) { + fwd_date = stream.readInt32(exception); } - date = stream.readInt32(); - message = stream.readString(); - media = (MessageMedia)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); + if ((flags & 8) != 0) { + reply_to_msg_id = stream.readInt32(exception); + } + date = stream.readInt32(exception); + message = stream.readString(exception); + media = MessageMedia.TLdeserialize(stream, stream.readInt32(exception), exception); if (id < 0 || (media != null && !(media instanceof TL_messageMediaEmpty) && message != null && message.length() != 0 && message.startsWith("-1"))) { - attachPath = stream.readString(); + attachPath = stream.readString(exception); } if (id < 0 && message.length() > 6 && media instanceof TL_messageMediaVideo) { videoEditedInfo = new VideoEditedInfo(); videoEditedInfo.parseString(message); } if ((flags & MESSAGE_FLAG_FWD) != 0 && id < 0) { - fwd_msg_id = stream.readInt32(); + fwd_msg_id = stream.readInt32(exception); } } @@ -10463,11 +12716,13 @@ public class TLRPC { stream.writeInt32(id); stream.writeInt32(from_id); to_id.serializeToStream(stream); - if ((flags & MESSAGE_FLAG_FWD) != 0) { + if ((flags & 4) != 0) { stream.writeInt32(fwd_from_id); + } + if ((flags & 4) != 0) { stream.writeInt32(fwd_date); } - if ((flags & MESSAGE_FLAG_REPLY) != 0) { + if ((flags & 8) != 0) { stream.writeInt32(reply_to_msg_id); } stream.writeInt32(date); @@ -10484,16 +12739,16 @@ public class TLRPC { public static int constructor = 0x567699b3; - public void readParams(AbsSerializedData stream) { - flags = stream.readInt32(); - id = stream.readInt32(); - from_id = stream.readInt32(); - to_id = (Peer)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - date = stream.readInt32(); - message = stream.readString(); - media = (MessageMedia)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); + public void readParams(AbsSerializedData stream, boolean exception) { + flags = stream.readInt32(exception); + id = stream.readInt32(exception); + from_id = stream.readInt32(exception); + to_id = Peer.TLdeserialize(stream, stream.readInt32(exception), exception); + date = stream.readInt32(exception); + message = stream.readString(exception); + media = MessageMedia.TLdeserialize(stream, stream.readInt32(exception), exception); if (id < 0 || (media != null && !(media instanceof TL_messageMediaEmpty) && message != null && message.length() != 0 && message.startsWith("-1"))) { - attachPath = stream.readString(); + attachPath = stream.readString(exception); } if (id < 0 && message.length() > 6 && media instanceof TL_messageMediaVideo) { videoEditedInfo = new VideoEditedInfo(); @@ -10514,42 +12769,18 @@ public class TLRPC { } } - public static class TL_messageService extends Message { - public static int constructor = 0x1d86f70e; - - - public void readParams(AbsSerializedData stream) { - flags = stream.readInt32(); - id = stream.readInt32(); - from_id = stream.readInt32(); - to_id = (Peer)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - date = stream.readInt32(); - action = (MessageAction)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(flags); - stream.writeInt32(id); - stream.writeInt32(from_id); - to_id.serializeToStream(stream); - stream.writeInt32(date); - action.serializeToStream(stream); - } - } - public static class TL_messageService_old extends TL_messageService { public static int constructor = 0x9f8d60bb; - public void readParams(AbsSerializedData stream) { - id = stream.readInt32(); - from_id = stream.readInt32(); - to_id = (Peer)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - flags |= stream.readBool() ? MESSAGE_FLAG_OUT : 0; - flags |= stream.readBool() ? MESSAGE_FLAG_UNREAD : 0; - date = stream.readInt32(); - action = (MessageAction)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); + public void readParams(AbsSerializedData stream, boolean exception) { + id = stream.readInt32(exception); + from_id = stream.readInt32(exception); + to_id = Peer.TLdeserialize(stream, stream.readInt32(exception), exception); + flags |= stream.readBool(exception) ? MESSAGE_FLAG_OUT : 0; + flags |= stream.readBool(exception) ? MESSAGE_FLAG_UNREAD : 0; + date = stream.readInt32(exception); + action = MessageAction.TLdeserialize(stream, stream.readInt32(exception), exception); } public void serializeToStream(AbsSerializedData stream) { @@ -10568,23 +12799,23 @@ public class TLRPC { public static int constructor = 0x5f46804; - public void readParams(AbsSerializedData stream) { - id = stream.readInt32(); - fwd_from_id = stream.readInt32(); - fwd_date = stream.readInt32(); - from_id = stream.readInt32(); - to_id = (Peer)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - flags |= stream.readBool() ? MESSAGE_FLAG_OUT : 0; - flags |= stream.readBool() ? MESSAGE_FLAG_UNREAD : 0; + public void readParams(AbsSerializedData stream, boolean exception) { + id = stream.readInt32(exception); + fwd_from_id = stream.readInt32(exception); + fwd_date = stream.readInt32(exception); + from_id = stream.readInt32(exception); + to_id = Peer.TLdeserialize(stream, stream.readInt32(exception), exception); + flags |= stream.readBool(exception) ? MESSAGE_FLAG_OUT : 0; + flags |= stream.readBool(exception) ? MESSAGE_FLAG_UNREAD : 0; flags |= MESSAGE_FLAG_FWD; - date = stream.readInt32(); - message = stream.readString(); - media = (MessageMedia)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); + date = stream.readInt32(exception); + message = stream.readString(exception); + media = MessageMedia.TLdeserialize(stream, stream.readInt32(exception), exception); if (id < 0) { - fwd_msg_id = stream.readInt32(); + fwd_msg_id = stream.readInt32(exception); } if (id < 0 || (media != null && !(media instanceof TL_messageMediaEmpty) && message != null && message.length() != 0 && message.startsWith("-1"))) { - attachPath = stream.readString(); + attachPath = stream.readString(exception); } if (id < 0 && message.length() > 6 && media instanceof TL_messageMediaVideo) { videoEditedInfo = new VideoEditedInfo(); @@ -10614,17 +12845,17 @@ public class TLRPC { public static class TL_message_old extends TL_message { public static int constructor = 0x22eb6aba; - public void readParams(AbsSerializedData stream) { - id = stream.readInt32(); - from_id = stream.readInt32(); - to_id = (Peer)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - flags |= stream.readBool() ? MESSAGE_FLAG_OUT : 0; - flags |= stream.readBool() ? MESSAGE_FLAG_UNREAD : 0; - date = stream.readInt32(); - message = stream.readString(); - media = (MessageMedia)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); + public void readParams(AbsSerializedData stream, boolean exception) { + id = stream.readInt32(exception); + from_id = stream.readInt32(exception); + to_id = Peer.TLdeserialize(stream, stream.readInt32(exception), exception); + flags |= stream.readBool(exception) ? MESSAGE_FLAG_OUT : 0; + flags |= stream.readBool(exception) ? MESSAGE_FLAG_UNREAD : 0; + date = stream.readInt32(exception); + message = stream.readString(exception); + media = MessageMedia.TLdeserialize(stream, stream.readInt32(exception), exception); if (id < 0 || (media != null && !(media instanceof TL_messageMediaEmpty) && message != null && message.length() != 0 && message.startsWith("-1"))) { - attachPath = stream.readString(); + attachPath = stream.readString(exception); } if (id < 0 && message.length() > 6 && media instanceof TL_messageMediaVideo) { videoEditedInfo = new VideoEditedInfo(); @@ -10649,17 +12880,17 @@ public class TLRPC { public static class TL_message_secret extends TL_message { public static int constructor = 0x555555F8; - public void readParams(AbsSerializedData stream) { - flags = stream.readInt32(); - id = stream.readInt32(); - ttl = stream.readInt32(); - from_id = stream.readInt32(); - to_id = (Peer)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - date = stream.readInt32(); - message = stream.readString(); - media = (MessageMedia)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); + public void readParams(AbsSerializedData stream, boolean exception) { + flags = stream.readInt32(exception); + id = stream.readInt32(exception); + ttl = stream.readInt32(exception); + from_id = stream.readInt32(exception); + to_id = Peer.TLdeserialize(stream, stream.readInt32(exception), exception); + date = stream.readInt32(exception); + message = stream.readString(exception); + media = MessageMedia.TLdeserialize(stream, stream.readInt32(exception), exception); if (id < 0 || (media != null && !(media instanceof TL_messageMediaEmpty) && message != null && message.length() != 0 && message.startsWith("-1"))) { - attachPath = stream.readString(); + attachPath = stream.readString(exception); } if (id < 0 && message.length() > 6 && media instanceof TL_messageMediaVideo) { videoEditedInfo = new VideoEditedInfo(); @@ -10680,12 +12911,294 @@ public class TLRPC { stream.writeString(attachPath); } } + //Message end - public static class Vector extends TLObject { - public static int constructor = 0x1cb5c415; - public ArrayList objects = new ArrayList<>(); + //MessageAction start + public static class MessageAction extends TLObject { + public Photo photo; + public int user_id; + public int inviter_id; + public String title; + public ArrayList users = new ArrayList<>(); + public String address; + public int ttl; + public DecryptedMessageAction encryptedAction; + public UserProfilePhoto newUserPhoto; + + public static MessageAction TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + MessageAction result = null; + switch(constructor) { + case 0x7fcb13a8: + result = new TL_messageActionChatEditPhoto(); + break; + case 0xb2ae9b0c: + result = new TL_messageActionChatDeleteUser(); + break; + case 0xf89cf5e8: + result = new TL_messageActionChatJoinedByLink(); + break; + case 0x95e3fbef: + result = new TL_messageActionChatDeletePhoto(); + break; + case 0x5e3cfc4b: + result = new TL_messageActionChatAddUser(); + break; + case 0xa6638b9a: + result = new TL_messageActionChatCreate(); + break; + case 0xb6aef7b0: + result = new TL_messageActionEmpty(); + break; + case 0xb5a1ce5a: + result = new TL_messageActionChatEditTitle(); + break; + case 0x6f038ebc: + result = new TL_messageActionGeoChatCreate(); + break; + case 0xc7d53de: + result = new TL_messageActionGeoChatCheckin(); + break; + case 0x55555552: + result = new TL_messageActionTTLChange(); //custom + break; + case 0x55555557: + result = new TL_messageActionCreatedBroadcastList(); //custom + break; + case 0x55555551: + result = new TL_messageActionUserUpdatedPhoto(); //custom + break; + case 0x55555550: + result = new TL_messageActionUserJoined(); //custom + break; + case 0x555555F5: + result = new TL_messageActionLoginUnknownLocation(); //custom + break; + case 0x555555F7: + result = new TL_messageEncryptedAction(); //custom + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in MessageAction", constructor)); + } + if (result != null) { + result.readParams(stream, exception); + } + return result; + } } + public static class TL_messageActionTTLChange extends MessageAction { + public static int constructor = 0x55555552; + + public void readParams(AbsSerializedData stream, boolean exception) { + ttl = stream.readInt32(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(ttl); + } + } + + public static class TL_messageActionCreatedBroadcastList extends MessageAction { + public static int constructor = 0x55555557; + + public void readParams(AbsSerializedData stream, boolean exception) { + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class TL_messageActionUserUpdatedPhoto extends MessageAction { + public static int constructor = 0x55555551; + + public void readParams(AbsSerializedData stream, boolean exception) { + newUserPhoto = UserProfilePhoto.TLdeserialize(stream, stream.readInt32(exception), exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + newUserPhoto.serializeToStream(stream); + } + } + + public static class TL_messageActionUserJoined extends MessageAction { + public static int constructor = 0x55555550; + + public void readParams(AbsSerializedData stream, boolean exception) { + + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class TL_messageActionLoginUnknownLocation extends MessageAction { + public static int constructor = 0x555555F5; + + public void readParams(AbsSerializedData stream, boolean exception) { + title = stream.readString(exception); + address = stream.readString(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeString(title); + stream.writeString(address); + } + } + + public static class TL_messageEncryptedAction extends MessageAction { + public static int constructor = 0x555555F7; + + public void readParams(AbsSerializedData stream, boolean exception) { + encryptedAction = DecryptedMessageAction.TLdeserialize(stream, stream.readInt32(exception), exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + encryptedAction.serializeToStream(stream); + } + } + //MessageAction end + + //InputEncryptedFile start + public static class InputEncryptedFile extends TLObject { + public long id; + public long access_hash; + public int parts; + public int key_fingerprint; + public String md5_checksum; + public byte[] key; //custom + public byte[] iv; //custom + + public static InputEncryptedFile TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + InputEncryptedFile result = null; + switch(constructor) { + case 0x5a17b5e5: + result = new TL_inputEncryptedFile(); + break; + case 0x2dc173c8: + result = new TL_inputEncryptedFileBigUploaded(); + break; + case 0x1837c364: + result = new TL_inputEncryptedFileEmpty(); + break; + case 0x64bd0306: + result = new TL_inputEncryptedFileUploaded(); + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in InputEncryptedFile", constructor)); + } + if (result != null) { + result.readParams(stream, exception); + } + return result; + } + } + //InputEncryptedFile end + + //UserStatus start + public static class UserStatus extends TLObject { + public int expires; + + public static UserStatus TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + UserStatus result = null; + switch(constructor) { + case 0x8c703f: + result = new TL_userStatusOffline(); + break; + case 0x7bf09fc: + result = new TL_userStatusLastWeek(); + break; + case 0x9d05049: + result = new TL_userStatusEmpty(); + break; + case 0x77ebc742: + result = new TL_userStatusLastMonth(); + break; + case 0xedb93949: + result = new TL_userStatusOnline(); + break; + case 0xe26f42f1: + result = new TL_userStatusRecently(); + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in UserStatus", constructor)); + } + if (result != null) { + result.readParams(stream, exception); + } + return result; + } + } + + public static class TL_userStatusOffline extends UserStatus { + public static int constructor = 0x8c703f; + + + public void readParams(AbsSerializedData stream, boolean exception) { + expires = stream.readInt32(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(expires); + } + } + //UserStatus end + + //TL_dialog start + public static class TL_dialog extends TLObject { + public static int constructor = 0xc1dd804a; + + public Peer peer; + public int top_message; + public int read_inbox_max_id; + public int unread_count; + public PeerNotifySettings notify_settings; + public int last_message_date; //custom + public long id; //custom + public int last_read; //custom + + public static TL_dialog TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + if (TL_dialog.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_dialog", constructor)); + } else { + return null; + } + } + TL_dialog result = new TL_dialog(); + result.readParams(stream, exception); + return result; + } + + public void readParams(AbsSerializedData stream, boolean exception) { + peer = Peer.TLdeserialize(stream, stream.readInt32(exception), exception); + top_message = stream.readInt32(exception); + read_inbox_max_id = stream.readInt32(exception); + unread_count = stream.readInt32(exception); + notify_settings = PeerNotifySettings.TLdeserialize(stream, stream.readInt32(exception), exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + peer.serializeToStream(stream); + stream.writeInt32(top_message); + stream.writeInt32(read_inbox_max_id); + stream.writeInt32(unread_count); + notify_settings.serializeToStream(stream); + } + } + //TL_dialog end + + //User start public static class User extends TLObject { public int id; public String first_name; @@ -10695,15 +13208,63 @@ public class TLRPC { public String phone; public UserProfilePhoto photo; public UserStatus status; - public boolean inactive; + + public static User TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + User result = null; + switch(constructor) { + case 0xcab35e18: + result = new TL_userContact(); + break; + case 0xd9ccc4ef: + result = new TL_userRequest(); + break; + case 0x75cf7a8: + result = new TL_userForeign(); + break; + case 0xd6016d7a: + result = new TL_userDeleted(); + break; + case 0x1c60e608: + result = new TL_userSelf(); + break; + case 0x200250ba: + result = new TL_userEmpty(); + break; + case 0xb29ad7cc: + result = new TL_userDeleted_old(); //custom + break; + case 0x5214c89d: + result = new TL_userForeign_old(); //custom + break; + case 0x22e8ceb0: + result = new TL_userRequest_old(); //custom + break; + case 0xf2fb8319: + result = new TL_userContact_old(); //custom + break; + case 0x7007b451: + result = new TL_userSelf_old2(); //custom + break; + case 0x720535ec: + result = new TL_userSelf_old(); //custom + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in User", constructor)); + } + if (result != null) { + result.readParams(stream, exception); + } + return result; + } } public static class TL_userEmpty extends User { public static int constructor = 0x200250ba; - public void readParams(AbsSerializedData stream) { - id = stream.readInt32(); + public void readParams(AbsSerializedData stream, boolean exception) { + id = stream.readInt32(exception); first_name = "DELETED"; last_name = ""; @@ -10717,224 +13278,305 @@ public class TLRPC { } } - public static class TL_chatEmpty extends Chat { - public static int constructor = 0x9ba2d800; + public static class TL_userDeleted_old extends TL_userDeleted { + public static int constructor = 0xb29ad7cc; - public void readParams(AbsSerializedData stream) { - id = stream.readInt32(); - - title = "DELETED"; + public void readParams(AbsSerializedData stream, boolean exception) { + id = stream.readInt32(exception); + first_name = stream.readString(exception); + last_name = stream.readString(exception); } public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(id); + stream.writeString(first_name); + stream.writeString(last_name); } } - public static class TL_userProfilePhotoOld extends UserProfilePhoto { - public static int constructor = 0x990d1493; + public static class TL_userForeign_old extends TL_userForeign { + public static int constructor = 0x5214c89d; - public void readParams(AbsSerializedData stream) { - photo_small = (FileLocation)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - photo_big = (FileLocation)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); + public void readParams(AbsSerializedData stream, boolean exception) { + id = stream.readInt32(exception); + first_name = stream.readString(exception); + last_name = stream.readString(exception); + access_hash = stream.readInt64(exception); + photo = UserProfilePhoto.TLdeserialize(stream, stream.readInt32(exception), exception); + status = UserStatus.TLdeserialize(stream, stream.readInt32(exception), exception); } public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); - photo_small.serializeToStream(stream); - photo_big.serializeToStream(stream); + stream.writeInt32(id); + stream.writeString(first_name); + stream.writeString(last_name); + stream.writeInt64(access_hash); + photo.serializeToStream(stream); + status.serializeToStream(stream); } } - public static class TL_ping extends TLObject { - public static int constructor = 0x7abe77ec; + public static class TL_userRequest_old extends TL_userRequest { + public static int constructor = 0x22e8ceb0; - public long ping_id; - public Class responseClass () { - return TL_pong.class; - } - - public int layer () { - return 0; - } - - public void readParams(AbsSerializedData stream) { - ping_id = stream.readInt64(); + public void readParams(AbsSerializedData stream, boolean exception) { + id = stream.readInt32(exception); + first_name = stream.readString(exception); + last_name = stream.readString(exception); + access_hash = stream.readInt64(exception); + phone = stream.readString(exception); + photo = UserProfilePhoto.TLdeserialize(stream, stream.readInt32(exception), exception); + status = UserStatus.TLdeserialize(stream, stream.readInt32(exception), exception); } public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); - stream.writeInt64(ping_id); + stream.writeInt32(id); + stream.writeString(first_name); + stream.writeString(last_name); + stream.writeInt64(access_hash); + stream.writeString(phone); + photo.serializeToStream(stream); + status.serializeToStream(stream); } } - public static class TL_ping_delay_disconnect extends TLObject { - public static int constructor = 0xf3427b8c; + public static class TL_userContact_old extends TL_userContact { + public static int constructor = 0xf2fb8319; - public long ping_id; - public int disconnect_delay; - public Class responseClass () { - return TL_pong.class; - } - - public int layer () { - return 0; - } - - public void readParams(AbsSerializedData stream) { - ping_id = stream.readInt64(); - disconnect_delay = stream.readInt32(); + public void readParams(AbsSerializedData stream, boolean exception) { + id = stream.readInt32(exception); + first_name = stream.readString(exception); + last_name = stream.readString(exception); + access_hash = stream.readInt64(exception); + phone = stream.readString(exception); + photo = UserProfilePhoto.TLdeserialize(stream, stream.readInt32(exception), exception); + status = UserStatus.TLdeserialize(stream, stream.readInt32(exception), exception); } public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); - stream.writeInt64(ping_id); - stream.writeInt32(disconnect_delay); + stream.writeInt32(id); + stream.writeString(first_name); + stream.writeString(last_name); + stream.writeInt64(access_hash); + stream.writeString(phone); + photo.serializeToStream(stream); + status.serializeToStream(stream); } } - public static class TL_destroy_session extends TLObject { - public static int constructor = 0xe7512126; + public static class TL_userSelf_old2 extends TL_userSelf { + public static int constructor = 0x7007b451; - public long session_id; - public Class responseClass () { - return DestroySessionRes.class; - } - - public int layer () { - return 0; - } - - public void readParams(AbsSerializedData stream) { - session_id = stream.readInt64(); + public void readParams(AbsSerializedData stream, boolean exception) { + id = stream.readInt32(exception); + first_name = stream.readString(exception); + last_name = stream.readString(exception); + username = stream.readString(exception); + phone = stream.readString(exception); + photo = UserProfilePhoto.TLdeserialize(stream, stream.readInt32(exception), exception); + status = UserStatus.TLdeserialize(stream, stream.readInt32(exception), exception); + stream.readBool(exception); } public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); - stream.writeInt64(session_id); + stream.writeInt32(id); + stream.writeString(first_name); + stream.writeString(last_name); + stream.writeString(username); + stream.writeString(phone); + photo.serializeToStream(stream); + status.serializeToStream(stream); + stream.writeBool(false); } } - public static class TL_destroy_sessions extends TLObject { - public static int constructor = 0xa13dc52f; + public static class TL_userSelf_old extends TL_userSelf { + public static int constructor = 0x720535ec; - public ArrayList session_ids = new ArrayList<>(); - public Class responseClass () { - return TL_destroy_sessions_res.class; - } - - public int layer () { - return 0; - } - - public void readParams(AbsSerializedData stream) { - int count = stream.readInt32(); - for (int a = 0; a < count; a++) { - session_ids.add(stream.readInt64()); - } + public void readParams(AbsSerializedData stream, boolean exception) { + id = stream.readInt32(exception); + first_name = stream.readString(exception); + last_name = stream.readString(exception); + phone = stream.readString(exception); + photo = UserProfilePhoto.TLdeserialize(stream, stream.readInt32(exception), exception); + status = UserStatus.TLdeserialize(stream, stream.readInt32(exception), exception); + stream.readBool(exception); } public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); - int count = session_ids.size(); - stream.writeInt32(count); - for (Long session_id : session_ids) { - stream.writeInt64(session_id); - } + stream.writeInt32(id); + stream.writeString(first_name); + stream.writeString(last_name); + stream.writeString(phone); + photo.serializeToStream(stream); + status.serializeToStream(stream); + stream.writeBool(false); } } + //User end - public static class TL_dialog extends TLObject { - public static int constructor = 0xc1dd804a; - - public Peer peer; - public int top_message; - public int unread_count; - public int read_inbox_max_id; - public PeerNotifySettings notify_settings; - public int last_message_date; + //Video start + public static class Video extends TLObject { public long id; - public int last_read; - - public void readParams(AbsSerializedData stream) { - peer = (Peer)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - top_message = stream.readInt32(); - read_inbox_max_id = stream.readInt32(); - unread_count = stream.readInt32(); - notify_settings = (PeerNotifySettings)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - peer.serializeToStream(stream); - stream.writeInt32(top_message); - stream.writeInt32(read_inbox_max_id); - stream.writeInt32(unread_count); - notify_settings.serializeToStream(stream); - } - } - - public static class EncryptedChat extends TLObject { - public int id; public long access_hash; - public int date; - public int admin_id; - public int participant_id; - public byte[] g_a_or_b; - public long key_fingerprint; - public byte[] g_a; - public byte[] a_or_b; - public byte[] auth_key; public int user_id; - public int ttl; - public int layer; - public int seq_in; - public int seq_out; - public byte[] key_hash; - public short key_use_count_in; - public short key_use_count_out; - public long exchange_id; - public int key_create_date; - public long future_key_fingerprint; - public byte[] future_auth_key; - } - - public static class FileLocation extends TLObject { + public int date; + public int duration; + public int size; + public PhotoSize thumb; public int dc_id; - public long volume_id; - public int local_id; - public long secret; - public String ext; - public byte[] key; - public byte[] iv; + public int w; + public int h; + public String caption; + public String mime_type; + public byte[] key; //custom + public byte[] iv; //custom + public VideoEditedInfo videoEditedInfo = null; //custom + + public static Video TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + Video result = null; + switch(constructor) { + case 0xee9f4a4d: + result = new TL_video(); + break; + case 0xc10658a8: + result = new TL_videoEmpty(); + break; + case 0x5a04a49f: + result = new TL_video_old(); //custom + break; + case 0x55555553: + result = new TL_videoEncrypted(); //custom + break; + case 0x388fa391: + result = new TL_video_old2(); //custom + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in Video", constructor)); + } + if (result != null) { + result.readParams(stream, exception); + } + return result; + } } - public static class TL_fileEncryptedLocation extends FileLocation { - public static int constructor = 0x55555554; + public static class TL_video_old extends TL_video { + public static int constructor = 0x5a04a49f; - public void readParams(AbsSerializedData stream) { - dc_id = stream.readInt32(); - volume_id = stream.readInt64(); - local_id = stream.readInt32(); - secret = stream.readInt64(); - key = stream.readByteArray(); - iv = stream.readByteArray(); + public void readParams(AbsSerializedData stream, boolean exception) { + id = stream.readInt64(exception); + access_hash = stream.readInt64(exception); + user_id = stream.readInt32(exception); + date = stream.readInt32(exception); + caption = stream.readString(exception); + duration = stream.readInt32(exception); + size = stream.readInt32(exception); + thumb = PhotoSize.TLdeserialize(stream, stream.readInt32(exception), exception); + dc_id = stream.readInt32(exception); + w = stream.readInt32(exception); + h = stream.readInt32(exception); } public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); + stream.writeInt64(id); + stream.writeInt64(access_hash); + stream.writeInt32(user_id); + stream.writeInt32(date); + stream.writeString(caption); + stream.writeInt32(duration); + stream.writeInt32(size); + thumb.serializeToStream(stream); stream.writeInt32(dc_id); - stream.writeInt64(volume_id); - stream.writeInt32(local_id); - stream.writeInt64(secret); + stream.writeInt32(w); + stream.writeInt32(h); + } + } + + public static class TL_video_old2 extends TL_video { + public static int constructor = 0x388fa391; + + + public void readParams(AbsSerializedData stream, boolean exception) { + id = stream.readInt64(exception); + access_hash = stream.readInt64(exception); + user_id = stream.readInt32(exception); + date = stream.readInt32(exception); + caption = stream.readString(exception); + duration = stream.readInt32(exception); + mime_type = stream.readString(exception); + size = stream.readInt32(exception); + thumb = PhotoSize.TLdeserialize(stream, stream.readInt32(exception), exception); + dc_id = stream.readInt32(exception); + w = stream.readInt32(exception); + h = stream.readInt32(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt64(id); + stream.writeInt64(access_hash); + stream.writeInt32(user_id); + stream.writeInt32(date); + stream.writeString(caption); + stream.writeInt32(duration); + stream.writeString(mime_type); + stream.writeInt32(size); + thumb.serializeToStream(stream); + stream.writeInt32(dc_id); + stream.writeInt32(w); + stream.writeInt32(h); + } + } + + public static class TL_videoEncrypted extends TL_video { + public static int constructor = 0x55555553; + + + public void readParams(AbsSerializedData stream, boolean exception) { + id = stream.readInt64(exception); + access_hash = stream.readInt64(exception); + user_id = stream.readInt32(exception); + date = stream.readInt32(exception); + caption = stream.readString(exception); + duration = stream.readInt32(exception); + size = stream.readInt32(exception); + thumb = PhotoSize.TLdeserialize(stream, stream.readInt32(exception), exception); + dc_id = stream.readInt32(exception); + w = stream.readInt32(exception); + h = stream.readInt32(exception); + key = stream.readByteArray(exception); + iv = stream.readByteArray(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt64(id); + stream.writeInt64(access_hash); + stream.writeInt32(user_id); + stream.writeInt32(date); + stream.writeString(caption); + stream.writeInt32(duration); + stream.writeInt32(size); + thumb.serializeToStream(stream); + stream.writeInt32(dc_id); + stream.writeInt32(w); + stream.writeInt32(h); stream.writeByteArray(key); stream.writeByteArray(iv); } @@ -10979,25 +13621,9 @@ public class TLRPC { } } } + //Video end - public static class Video extends TLObject { - public long id; - public long access_hash; - public int user_id; - public int date; - public String caption; - public int duration; - public String mime_type; - public int size; - public PhotoSize thumb; - public int dc_id; - public int w; - public int h; - public byte[] key; - public byte[] iv; - public VideoEditedInfo videoEditedInfo = null; - } - + //Document start public static class Document extends TLObject { public long id; public long access_hash; @@ -11007,76 +13633,56 @@ public class TLRPC { public PhotoSize thumb; public int dc_id; public ArrayList attributes = new ArrayList<>(); - public byte[] key; - public byte[] iv; - } + public byte[] key; //custom + public byte[] iv; //custom - public static class Audio extends TLObject { - public long id; - public long access_hash; - public int user_id; - public int date; - public int duration; - public String mime_type; - public int size; - public int dc_id; - public byte[] key; - public byte[] iv; - } - - public static class MessageAction extends TLObject { - public Photo photo; - public UserProfilePhoto newUserPhoto; - public int user_id; - public String title; - public ArrayList users = new ArrayList<>(); - public String address; - public int ttl; - public DecryptedMessageAction encryptedAction; - } - - public static class TL_messageActionTTLChange extends MessageAction { - public static int constructor = 0x55555552; - - public void readParams(AbsSerializedData stream) { - ttl = stream.readInt32(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(ttl); - } - } - - public static class TL_messageActionCreatedBroadcastList extends MessageAction { - public static int constructor = 0x55555557; - - public void readParams(AbsSerializedData stream) { - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); + public static Document TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + Document result = null; + switch(constructor) { + case 0x36f8c871: + result = new TL_documentEmpty(); + break; + case 0xf9a39f4f: + result = new TL_document(); + break; + case 0x55555558: + result = new TL_documentEncrypted(); //custom + break; + case 0x55555556: + result = new TL_documentEncrypted_old(); //custom + break; + case 0x9efc6326: + result = new TL_document_old(); //custom + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in Document", constructor)); + } + if (result != null) { + result.readParams(stream, exception); + } + return result; } } public static class TL_documentEncrypted extends TL_document { public static int constructor = 0x55555558; - public void readParams(AbsSerializedData stream) { - id = stream.readInt64(); - access_hash = stream.readInt64(); - date = stream.readInt32(); - mime_type = stream.readString(); - size = stream.readInt32(); - thumb = (PhotoSize)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - dc_id = stream.readInt32(); - stream.readInt32(); - int count = stream.readInt32(); + public void readParams(AbsSerializedData stream, boolean exception) { + id = stream.readInt64(exception); + access_hash = stream.readInt64(exception); + date = stream.readInt32(exception); + mime_type = stream.readString(exception); + size = stream.readInt32(exception); + thumb = PhotoSize.TLdeserialize(stream, stream.readInt32(exception), exception); + dc_id = stream.readInt32(exception); + stream.readInt32(exception); + int count = stream.readInt32(exception); for (int a = 0; a < count; a++) { - attributes.add((DocumentAttribute)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32())); + attributes.add(DocumentAttribute.TLdeserialize(stream, stream.readInt32(exception), exception)); } - key = stream.readByteArray(); - iv = stream.readByteArray(); + key = stream.readByteArray(exception); + iv = stream.readByteArray(exception); } public void serializeToStream(AbsSerializedData stream) { @@ -11103,58 +13709,79 @@ public class TLRPC { public static int constructor = 0x55555556; - public void readParams(AbsSerializedData stream) { - id = stream.readInt64(); - access_hash = stream.readInt64(); - stream.readInt32(); - date = stream.readInt32(); + public void readParams(AbsSerializedData stream, boolean exception) { + id = stream.readInt64(exception); + access_hash = stream.readInt64(exception); + stream.readInt32(exception); + date = stream.readInt32(exception); TL_documentAttributeFilename fileName = new TL_documentAttributeFilename(); - fileName.file_name = stream.readString(); + fileName.file_name = stream.readString(exception); attributes.add(fileName); - mime_type = stream.readString(); - size = stream.readInt32(); - thumb = (PhotoSize)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - dc_id = stream.readInt32(); - key = stream.readByteArray(); - iv = stream.readByteArray(); + mime_type = stream.readString(exception); + size = stream.readInt32(exception); + thumb = PhotoSize.TLdeserialize(stream, stream.readInt32(exception), exception); + dc_id = stream.readInt32(exception); + key = stream.readByteArray(exception); + iv = stream.readByteArray(exception); } } - public static class TL_videoEncrypted extends TL_video { - public static int constructor = 0x55555553; + public static class TL_document_old extends TL_document { + public static int constructor = 0x9efc6326; - public void readParams(AbsSerializedData stream) { - id = stream.readInt64(); - access_hash = stream.readInt64(); - user_id = stream.readInt32(); - date = stream.readInt32(); - caption = stream.readString(); - duration = stream.readInt32(); - size = stream.readInt32(); - thumb = (PhotoSize)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - dc_id = stream.readInt32(); - w = stream.readInt32(); - h = stream.readInt32(); - key = stream.readByteArray(); - iv = stream.readByteArray(); + public void readParams(AbsSerializedData stream, boolean exception) { + id = stream.readInt64(exception); + access_hash = stream.readInt64(exception); + stream.readInt32(exception); + date = stream.readInt32(exception); + TL_documentAttributeFilename fileName = new TL_documentAttributeFilename(); + fileName.file_name = stream.readString(exception); + attributes.add(fileName); + mime_type = stream.readString(exception); + size = stream.readInt32(exception); + thumb = PhotoSize.TLdeserialize(stream, stream.readInt32(exception), exception); + dc_id = stream.readInt32(exception); } + } + //Document end - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt64(id); - stream.writeInt64(access_hash); - stream.writeInt32(user_id); - stream.writeInt32(date); - stream.writeString(caption); - stream.writeInt32(duration); - stream.writeInt32(size); - thumb.serializeToStream(stream); - stream.writeInt32(dc_id); - stream.writeInt32(w); - stream.writeInt32(h); - stream.writeByteArray(key); - stream.writeByteArray(iv); + //Audio start + public static class Audio extends TLObject { + public long id; + public long access_hash; + public int user_id; + public int date; + public int duration; + public String mime_type; + public int size; + public int dc_id; + public byte[] key; + public byte[] iv; + + public static Audio TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + Audio result = null; + switch(constructor) { + case 0x586988d8: + result = new TL_audioEmpty(); + break; + case 0xc7ac6496: + result = new TL_audio(); + break; + case 0x555555F6: + result = new TL_audioEncrypted(); //custom + break; + case 0x427425e7: + result = new TL_audio_old(); //custom + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in Audio", constructor)); + } + if (result != null) { + result.readParams(stream, exception); + } + return result; } } @@ -11162,16 +13789,16 @@ public class TLRPC { public static int constructor = 0x555555F6; - public void readParams(AbsSerializedData stream) { - id = stream.readInt64(); - access_hash = stream.readInt64(); - user_id = stream.readInt32(); - date = stream.readInt32(); - duration = stream.readInt32(); - size = stream.readInt32(); - dc_id = stream.readInt32(); - key = stream.readByteArray(); - iv = stream.readByteArray(); + public void readParams(AbsSerializedData stream, boolean exception) { + id = stream.readInt64(exception); + access_hash = stream.readInt64(exception); + user_id = stream.readInt32(exception); + date = stream.readInt32(exception); + duration = stream.readInt32(exception); + size = stream.readInt32(exception); + dc_id = stream.readInt32(exception); + key = stream.readByteArray(exception); + iv = stream.readByteArray(exception); } public void serializeToStream(AbsSerializedData stream) { @@ -11188,23 +13815,235 @@ public class TLRPC { } } - public static class TL_messageActionUserUpdatedPhoto extends MessageAction { - public static int constructor = 0x55555551; + public static class TL_audio_old extends TL_audio { + public static int constructor = 0x427425e7; - public void readParams(AbsSerializedData stream) { - newUserPhoto = (UserProfilePhoto)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); + + public void readParams(AbsSerializedData stream, boolean exception) { + id = stream.readInt64(exception); + access_hash = stream.readInt64(exception); + user_id = stream.readInt32(exception); + date = stream.readInt32(exception); + duration = stream.readInt32(exception); + size = stream.readInt32(exception); + dc_id = stream.readInt32(exception); } public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); - newUserPhoto.serializeToStream(stream); + stream.writeInt64(id); + stream.writeInt64(access_hash); + stream.writeInt32(user_id); + stream.writeInt32(date); + stream.writeInt32(duration); + stream.writeInt32(size); + stream.writeInt32(dc_id); + } + } + //Audio end + + //SendMessageAction start + public static class SendMessageAction extends TLObject { + public int progress; + + public static SendMessageAction TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + SendMessageAction result = null; + switch(constructor) { + case 0xd52f73f7: + result = new TL_sendMessageRecordAudioAction(); + break; + case 0xf351d7ab: + result = new TL_sendMessageUploadAudioAction(); + break; + case 0xd1d34a26: + result = new TL_sendMessageUploadPhotoAction(); + break; + case 0xe9763aec: + result = new TL_sendMessageUploadVideoAction(); + break; + case 0xfd5ec8f5: + result = new TL_sendMessageCancelAction(); + break; + case 0x176f8ba1: + result = new TL_sendMessageGeoLocationAction(); + break; + case 0x628cbc6f: + result = new TL_sendMessageChooseContactAction(); + break; + case 0x16bf744e: + result = new TL_sendMessageTypingAction(); + break; + case 0xaa0cd9e4: + result = new TL_sendMessageUploadDocumentAction(); + break; + case 0xa187d66f: + result = new TL_sendMessageRecordVideoAction(); + break; + case 0x92042ff7: + result = new TL_sendMessageUploadVideoAction_old(); //custom + break; + case 0xe6ac8a6f: + result = new TL_sendMessageUploadAudioAction_old(); //custom + break; + case 0x990a3c1a: + result = new TL_sendMessageUploadPhotoAction_old(); //custom + break; + case 0x8faee98e: + result = new TL_sendMessageUploadDocumentAction_old(); //custom + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in SendMessageAction", constructor)); + } + if (result != null) { + result.readParams(stream, exception); + } + return result; } } - public static class TL_messageActionUserJoined extends MessageAction { - public static int constructor = 0x55555550; + public static class TL_sendMessageUploadDocumentAction_old extends TL_sendMessageUploadDocumentAction { + public static int constructor = 0x8faee98e; - public void readParams(AbsSerializedData stream) { + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class TL_sendMessageUploadVideoAction_old extends TL_sendMessageUploadVideoAction { + public static int constructor = 0x92042ff7; + + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class TL_sendMessageUploadPhotoAction_old extends TL_sendMessageUploadPhotoAction { + public static int constructor = 0x990a3c1a; + + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class TL_sendMessageUploadAudioAction_old extends TL_sendMessageUploadAudioAction { + public static int constructor = 0xe6ac8a6f; + + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + //SendMessageAction end + + //FileLocation start + public static class FileLocation extends TLObject { + public int dc_id; + public long volume_id; + public int local_id; + public long secret; + public String ext; //custom + public byte[] key; //custom + public byte[] iv; //custom + + public static FileLocation TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + FileLocation result = null; + switch(constructor) { + case 0x53d69076: + result = new TL_fileLocation(); + break; + case 0x7c596b46: + result = new TL_fileLocationUnavailable(); + break; + case 0x55555554: + result = new TL_fileEncryptedLocation(); //custom + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in FileLocation", constructor)); + } + if (result != null) { + result.readParams(stream, exception); + } + return result; + } + } + + public static class TL_fileEncryptedLocation extends FileLocation { + public static int constructor = 0x55555554; + + + public void readParams(AbsSerializedData stream, boolean exception) { + dc_id = stream.readInt32(exception); + volume_id = stream.readInt64(exception); + local_id = stream.readInt32(exception); + secret = stream.readInt64(exception); + key = stream.readByteArray(exception); + iv = stream.readByteArray(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(dc_id); + stream.writeInt64(volume_id); + stream.writeInt32(local_id); + stream.writeInt64(secret); + stream.writeByteArray(key); + stream.writeByteArray(iv); + } + } + //FileLocation end + + //DocumentAttribute start + public static class DocumentAttribute extends TLObject { + public int w; + public int h; + public String file_name; + public int duration; + public String alt; + + public static DocumentAttribute TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + DocumentAttribute result = null; + switch(constructor) { + case 0x11b58939: + result = new TL_documentAttributeAnimated(); + break; + case 0x6c37c15c: + result = new TL_documentAttributeImageSize(); + break; + case 0x15590068: + result = new TL_documentAttributeFilename(); + break; + case 0x5910cccb: + result = new TL_documentAttributeVideo(); + break; + case 0x994c9882: + result = new TL_documentAttributeSticker(); + break; + case 0x51448e5: + result = new TL_documentAttributeAudio(); + break; + case 0xfb0a5727: + result = new TL_documentAttributeSticker_old(); //custom + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in DocumentAttribute", constructor)); + } + if (result != null) { + result.readParams(stream, exception); + } + return result; + } + } + + public static class TL_documentAttributeSticker_old extends TL_documentAttributeSticker { + public static int constructor = 0xfb0a5727; + + public void readParams(AbsSerializedData stream, boolean exception) { } @@ -11212,22 +14051,649 @@ public class TLRPC { stream.writeInt32(constructor); } } + //DocumentAttribute end - public static class TL_messageActionLoginUnknownLocation extends MessageAction { - public static int constructor = 0x555555F5; + //MessageMedia start + public static class MessageMedia extends TLObject { + public Video video; + public String caption; + public Photo photo; + public WebPage webpage; + public Document document; + public GeoPoint geo; + public Audio audio; + public String title; + public String address; + public String provider; + public String venue_id; + public String phone_number; + public String first_name; + public String last_name; + public int user_id; + public byte[] bytes; //custom - public void readParams(AbsSerializedData stream) { - title = stream.readString(); - address = stream.readString(); + public static MessageMedia TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + MessageMedia result = null; + switch(constructor) { + case 0x5bcf1675: + result = new TL_messageMediaVideo(); + break; + case 0x3d8ce53d: + result = new TL_messageMediaPhoto(); + break; + case 0xa32dd600: + result = new TL_messageMediaWebPage(); + break; + case 0x2fda2204: + result = new TL_messageMediaDocument(); + break; + case 0x56e0d474: + result = new TL_messageMediaGeo(); + break; + case 0x3ded6320: + result = new TL_messageMediaEmpty(); + break; + case 0xc6b68300: + result = new TL_messageMediaAudio(); + break; + case 0x7912b71f: + result = new TL_messageMediaVenue(); + break; + case 0x5e7d2f39: + result = new TL_messageMediaContact(); + break; + case 0x9f84f49e: + result = new TL_messageMediaUnsupported(); + break; + case 0xa2d24290: + result = new TL_messageMediaVideo_old(); //custom + break; + case 0xc8c45a2a: + result = new TL_messageMediaPhoto_old(); //custom + break; + case 0x29632a36: + result = new TL_messageMediaUnsupported_old(); //custom + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in MessageMedia", constructor)); + } + if (result != null) { + result.readParams(stream, exception); + } + return result; + } + } + + public static class TL_messageMediaUnsupported_old extends TL_messageMediaUnsupported { + public static int constructor = 0x29632a36; + + + public void readParams(AbsSerializedData stream, boolean exception) { + bytes = stream.readByteArray(exception); } public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); - stream.writeString(title); - stream.writeString(address); + stream.writeByteArray(bytes); } } + public static class TL_messageMediaPhoto_old extends TL_messageMediaPhoto { + public static int constructor = 0xc8c45a2a; + + + public void readParams(AbsSerializedData stream, boolean exception) { + photo = Photo.TLdeserialize(stream, stream.readInt32(exception), exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + photo.serializeToStream(stream); + } + } + + public static class TL_messageMediaVideo_old extends TL_messageMediaVideo { + public static int constructor = 0xa2d24290; + + + public void readParams(AbsSerializedData stream, boolean exception) { + video = Video.TLdeserialize(stream, stream.readInt32(exception), exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + video.serializeToStream(stream); + } + } + //MessageMedia end + + //DecryptedMessage start + public static class DecryptedMessage extends TLObject { + public long random_id; + public DecryptedMessageAction action; + public int ttl; + public String message; + public DecryptedMessageMedia media; + public byte[] random_bytes; //custom + + public static DecryptedMessage TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + DecryptedMessage result = null; + switch(constructor) { + case 0x73164160: + result = new TL_decryptedMessageService(); + break; + case 0x204d3878: + result = new TL_decryptedMessage(); + break; + case 0xaa48327d: + result = new TL_decryptedMessageService_old(); //custom + break; + case 0x1f814f1f: + result = new TL_decryptedMessage_old(); //custom + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in DecryptedMessage", constructor)); + } + if (result != null) { + result.readParams(stream, exception); + } + return result; + } + } + + public static class TL_decryptedMessageService_old extends TL_decryptedMessageService { + public static int constructor = 0xaa48327d; + + + public void readParams(AbsSerializedData stream, boolean exception) { + random_id = stream.readInt64(exception); + random_bytes = stream.readByteArray(exception); + action = DecryptedMessageAction.TLdeserialize(stream, stream.readInt32(exception), exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt64(random_id); + stream.writeByteArray(random_bytes); + action.serializeToStream(stream); + } + } + + public static class TL_decryptedMessage_old extends TL_decryptedMessage { + public static int constructor = 0x1f814f1f; + + + public void readParams(AbsSerializedData stream, boolean exception) { + random_id = stream.readInt64(exception); + random_bytes = stream.readByteArray(exception); + message = stream.readString(exception); + media = DecryptedMessageMedia.TLdeserialize(stream, stream.readInt32(exception), exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt64(random_id); + stream.writeByteArray(random_bytes); + stream.writeString(message); + media.serializeToStream(stream); + } + } + //DecryptedMessage end + + //Chat start + public static class TL_chatEmpty extends Chat { + public static int constructor = 0x9ba2d800; + + + public void readParams(AbsSerializedData stream, boolean exception) { + id = stream.readInt32(exception); + + title = "DELETED"; + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(id); + } + } + //Chat end + + //UserProfilePhoto start + public static class UserProfilePhoto extends TLObject { + public long photo_id; + public FileLocation photo_small; + public FileLocation photo_big; + + public static UserProfilePhoto TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + UserProfilePhoto result = null; + switch(constructor) { + case 0x4f11bae1: + result = new TL_userProfilePhotoEmpty(); + break; + case 0xd559d8c8: + result = new TL_userProfilePhoto(); + break; + case 0x990d1493: + result = new TL_userProfilePhotoOld(); + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in UserProfilePhoto", constructor)); + } + if (result != null) { + result.readParams(stream, exception); + } + return result; + } + } + + public static class TL_userProfilePhotoOld extends UserProfilePhoto { + public static int constructor = 0x990d1493; + + + public void readParams(AbsSerializedData stream, boolean exception) { + photo_small = FileLocation.TLdeserialize(stream, stream.readInt32(exception), exception); + photo_big = FileLocation.TLdeserialize(stream, stream.readInt32(exception), exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + photo_small.serializeToStream(stream); + photo_big.serializeToStream(stream); + } + } + //UserProfilePhoto end + + //DecryptedMessageMedia start + public static class DecryptedMessageMedia extends TLObject { + public long id; + public long access_hash; + public int date; + public String mime_type; + public int size; + public int dc_id; + public ArrayList attributes = new ArrayList<>(); + public int thumb_w; + public int thumb_h; + public String file_name; + public byte[] key; + public byte[] iv; + public double lat; + public double _long; + public int duration; + public int w; + public int h; + public String phone_number; + public String first_name; + public String last_name; + public int user_id; + public byte[] thumb; //custom + public PhotoSize thumbImage; //custom + + public static DecryptedMessageMedia TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + DecryptedMessageMedia result = null; + switch(constructor) { + case 0xfa95b0dd: + result = new TL_decryptedMessageMediaExternalDocument(); + break; + case 0xb095434b: + result = new TL_decryptedMessageMediaDocument(); + break; + case 0x35480a59: + result = new TL_decryptedMessageMediaGeoPoint(); + break; + case 0x57e0a9cb: + result = new TL_decryptedMessageMediaAudio(); + break; + case 0x524a415d: + result = new TL_decryptedMessageMediaVideo(); + break; + case 0x588a0a97: + result = new TL_decryptedMessageMediaContact(); + break; + case 0x89f5c4a: + result = new TL_decryptedMessageMediaEmpty(); + break; + case 0x32798a8c: + result = new TL_decryptedMessageMediaPhoto(); + break; + case 0x4cee6ef3: + result = new TL_decryptedMessageMediaVideo_old(); //custom + break; + case 0x6080758f: + result = new TL_decryptedMessageMediaAudio_old(); //custom + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in DecryptedMessageMedia", constructor)); + } + if (result != null) { + result.readParams(stream, exception); + } + return result; + } + } + + public static class TL_decryptedMessageMediaExternalDocument extends DecryptedMessageMedia { + public static int constructor = 0xfa95b0dd; + + + public void readParams(AbsSerializedData stream, boolean exception) { + id = stream.readInt64(exception); + access_hash = stream.readInt64(exception); + date = stream.readInt32(exception); + mime_type = stream.readString(exception); + size = stream.readInt32(exception); + thumbImage = PhotoSize.TLdeserialize(stream, stream.readInt32(exception), exception); + dc_id = stream.readInt32(exception); + int magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + int count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + attributes.add(DocumentAttribute.TLdeserialize(stream, stream.readInt32(exception), exception)); + } + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt64(id); + stream.writeInt64(access_hash); + stream.writeInt32(date); + stream.writeString(mime_type); + stream.writeInt32(size); + thumbImage.serializeToStream(stream); + stream.writeInt32(dc_id); + stream.writeInt32(0x1cb5c415); + int count = attributes.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + attributes.get(a).serializeToStream(stream); + } + } + } + + public static class TL_decryptedMessageMediaVideo_old extends TL_decryptedMessageMediaVideo { + public static int constructor = 0x4cee6ef3; + + + public void readParams(AbsSerializedData stream, boolean exception) { + thumb = stream.readByteArray(exception); + thumb_w = stream.readInt32(exception); + thumb_h = stream.readInt32(exception); + duration = stream.readInt32(exception); + w = stream.readInt32(exception); + h = stream.readInt32(exception); + size = stream.readInt32(exception); + key = stream.readByteArray(exception); + iv = stream.readByteArray(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeByteArray(thumb); + stream.writeInt32(thumb_w); + stream.writeInt32(thumb_h); + stream.writeInt32(duration); + stream.writeInt32(w); + stream.writeInt32(h); + stream.writeInt32(size); + stream.writeByteArray(key); + stream.writeByteArray(iv); + } + } + + public static class TL_decryptedMessageMediaAudio_old extends TL_decryptedMessageMediaAudio { + public static int constructor = 0x6080758f; + + + public void readParams(AbsSerializedData stream, boolean exception) { + duration = stream.readInt32(exception); + size = stream.readInt32(exception); + key = stream.readByteArray(exception); + iv = stream.readByteArray(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(duration); + stream.writeInt32(size); + stream.writeByteArray(key); + stream.writeByteArray(iv); + } + } + //DecryptedMessageMedia end + + //functions memory optimize + public static class TL_upload_saveFilePart extends TLObject { + public static int constructor = 0xb304a621; + + public long file_id; + public int file_part; + public ByteBufferDesc bytes; + + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return Bool.TLdeserialize(stream, constructor, exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt64(file_id); + stream.writeInt32(file_part); + stream.writeByteBuffer(bytes); + } + + @Override + public void freeResources() { + if (disableFree) { + return; + } + if (bytes != null) { + BuffersStorage.getInstance().reuseFreeBuffer(bytes); + bytes = null; + } + } + } + + public static class TL_upload_saveBigFilePart extends TLObject { + public static int constructor = 0xde7b673d; + + public long file_id; + public int file_part; + public int file_total_parts; + public ByteBufferDesc bytes; + + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return Bool.TLdeserialize(stream, constructor, exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt64(file_id); + stream.writeInt32(file_part); + stream.writeInt32(file_total_parts); + stream.writeByteBuffer(bytes); + } + + @Override + public void freeResources() { + if (disableFree) { + return; + } + if (bytes != null) { + BuffersStorage.getInstance().reuseFreeBuffer(bytes); + bytes = null; + } + } + } + + public static class TL_upload_file extends TLObject { + public static int constructor = 0x96a18d5; + + public storage_FileType type; + public int mtime; + public ByteBufferDesc bytes; + + public static TL_upload_file TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + if (TL_upload_file.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_upload_file", constructor)); + } else { + return null; + } + } + TL_upload_file result = new TL_upload_file(); + result.readParams(stream, exception); + return result; + } + + public void readParams(AbsSerializedData stream, boolean exception) { + type = storage_FileType.TLdeserialize(stream, stream.readInt32(exception), exception); + mtime = stream.readInt32(exception); + bytes = stream.readByteBuffer(exception); + } + + @Override + public void freeResources() { + if (disableFree) { + return; + } + if (bytes != null) { + BuffersStorage.getInstance().reuseFreeBuffer(bytes); + bytes = null; + } + } + } + + public static class TL_messages_sendEncryptedFile extends TLObject { + public static int constructor = 0x9a901b66; + + public TL_inputEncryptedChat peer; + public long random_id; + public ByteBufferDesc data; + public InputEncryptedFile file; + + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return messages_SentEncryptedMessage.TLdeserialize(stream, constructor, exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + peer.serializeToStream(stream); + stream.writeInt64(random_id); + stream.writeByteBuffer(data); + file.serializeToStream(stream); + } + + @Override + public void freeResources() { + if (disableFree) { + return; + } + if (data != null) { + BuffersStorage.getInstance().reuseFreeBuffer(data); + data = null; + } + } + } + + public static class TL_messages_sendEncrypted extends TLObject { + public static int constructor = 0xa9776773; + + public TL_inputEncryptedChat peer; + public long random_id; + public ByteBufferDesc data; + + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return messages_SentEncryptedMessage.TLdeserialize(stream, constructor, exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + peer.serializeToStream(stream); + stream.writeInt64(random_id); + stream.writeByteBuffer(data); + } + + @Override + public void freeResources() { + if (disableFree) { + return; + } + if (data != null) { + BuffersStorage.getInstance().reuseFreeBuffer(data); + data = null; + } + } + } + + public static class TL_messages_sendEncryptedService extends TLObject { + public static int constructor = 0x32d439a4; + + public TL_inputEncryptedChat peer; + public long random_id; + public ByteBufferDesc data; + + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return messages_SentEncryptedMessage.TLdeserialize(stream, constructor, exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + peer.serializeToStream(stream); + stream.writeInt64(random_id); + stream.writeByteBuffer(data); + } + + @Override + public void freeResources() { + if (disableFree) { + return; + } + if (data != null) { + BuffersStorage.getInstance().reuseFreeBuffer(data); + data = null; + } + } + } + + public static class TL_set_client_DH_params extends TLObject { + public static int constructor = 0xf5045f1f; + + public byte[] nonce; + public byte[] server_nonce; + public ByteBufferDesc encrypted_data; + + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return Set_client_DH_params_answer.TLdeserialize(stream, constructor, exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeRaw(nonce); + stream.writeRaw(server_nonce); + stream.writeByteBuffer(encrypted_data); + } + + @Override + public void freeResources() { + if (disableFree) { + return; + } + if (encrypted_data != null) { + BuffersStorage.getInstance().reuseFreeBuffer(encrypted_data); + encrypted_data = null; + } + } + } + + //functions public static class invokeWithLayer extends TLObject { public static int constructor = 0xda9b0d0d; @@ -11262,194 +14728,231 @@ public class TLRPC { } } - public static class TL_encryptedChat_old extends TL_encryptedChat { - public static int constructor = 0x6601d14f; + public static class TL_destroy_sessions extends TLObject { + public static int constructor = 0xa13dc52f; + public ArrayList session_ids = new ArrayList<>(); - public void readParams(AbsSerializedData stream) { - id = stream.readInt32(); - access_hash = stream.readInt64(); - date = stream.readInt32(); - admin_id = stream.readInt32(); - participant_id = stream.readInt32(); - g_a_or_b = stream.readByteArray(); - stream.readByteArray(); - key_fingerprint = stream.readInt64(); + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return TL_destroy_sessions_res.TLdeserialize(stream, constructor, exception); + } + + public int layer() { + return 0; + } + + public void readParams(AbsSerializedData stream, boolean exception) { + int count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + session_ids.add(stream.readInt64(exception)); + } } public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(TL_encryptedChat.constructor); - stream.writeInt32(id); - stream.writeInt64(access_hash); - stream.writeInt32(date); - stream.writeInt32(admin_id); - stream.writeInt32(participant_id); - stream.writeByteArray(g_a_or_b); - stream.writeInt64(key_fingerprint); + stream.writeInt32(constructor); + int count = session_ids.size(); + stream.writeInt32(count); + for (Long session_id : session_ids) { + stream.writeInt64(session_id); + } } } - public static class TL_encryptedChatRequested_old extends EncryptedChat { - public static int constructor = 0xfda9a7b7; + public static class TL_ping extends TLObject { + public static int constructor = 0x7abe77ec; + public long ping_id; - public void readParams(AbsSerializedData stream) { - id = stream.readInt32(); - access_hash = stream.readInt64(); - date = stream.readInt32(); - admin_id = stream.readInt32(); - participant_id = stream.readInt32(); - g_a = stream.readByteArray(); - stream.readByteArray(); + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return TL_pong.TLdeserialize(stream, constructor, exception); + } + + public int layer() { + return 0; + } + + public void readParams(AbsSerializedData stream, boolean exception) { + ping_id = stream.readInt64(exception); } public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(TL_encryptedChatRequested.constructor); - stream.writeInt32(id); - stream.writeInt64(access_hash); - stream.writeInt32(date); - stream.writeInt32(admin_id); - stream.writeInt32(participant_id); - stream.writeByteArray(g_a); + stream.writeInt32(constructor); + stream.writeInt64(ping_id); } } - public static class TL_decryptedMessageActionDeleteMessages extends DecryptedMessageAction { - public static int constructor = 0x65614304; + public static class TL_ping_delay_disconnect extends TLObject { + public static int constructor = 0xf3427b8c; + public long ping_id; + public int disconnect_delay; - public void readParams(AbsSerializedData stream) { - boolean[] error = new boolean[1]; - stream.readInt32(error); - if (error[0]) { - return; - } - int count = stream.readInt32(error); - if (error[0]) { - return; - } - for (long a = 0; a < count; a++) { - random_ids.add(stream.readInt64(error)); - if (error[0]) { - return; + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return TL_pong.TLdeserialize(stream, constructor, exception); + } + + public int layer() { + return 0; + } + + public void readParams(AbsSerializedData stream, boolean exception) { + ping_id = stream.readInt64(exception); + disconnect_delay = stream.readInt32(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt64(ping_id); + stream.writeInt32(disconnect_delay); + } + } + + public static class TL_destroy_session extends TLObject { + public static int constructor = 0xe7512126; + + public long session_id; + + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return DestroySessionRes.TLdeserialize(stream, constructor, exception); + } + + public int layer() { + return 0; + } + + public void readParams(AbsSerializedData stream, boolean exception) { + session_id = stream.readInt64(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt64(session_id); + } + } + + public static class TL_invokeAfterMsg extends TLObject { + public static int constructor = 0xcb9f372d; + + public long msg_id; + public TLObject query; + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt64(msg_id); + query.serializeToStream(stream); + } + } + + public static class TL_get_future_salts extends TLObject { + public static int constructor = 0xb921bd04; + + public int num; + + public int layer() { + return 0; + } + + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return TL_futuresalts.TLdeserialize(stream, constructor, exception); + } + + public void readParams(AbsSerializedData stream, boolean exception) { + num = stream.readInt32(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(num); + } + } + + public static class TL_rpc_drop_answer extends TLObject { + public static int constructor = 0x58e4a740; + + public long req_msg_id; + + public int layer() { + return 0; + } + + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return RpcDropAnswer.TLdeserialize(stream, constructor, exception); + } + + public void readParams(AbsSerializedData stream, boolean exception) { + req_msg_id = stream.readInt64(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt64(req_msg_id); + } + } + + public static class TL_msg_container extends TLObject { + public ArrayList messages; + + public static int constructor = 0x73f1f8dc; + + public void readParams(AbsSerializedData stream, boolean exception) { + messages = new ArrayList<>(); + int count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + TL_protoMessage message = new TL_protoMessage(); + message.msg_id = stream.readInt64(exception); + message.seqno = stream.readInt32(exception); + message.bytes = stream.readInt32(exception); + int position = stream.getPosition(); + message.body = ConnectionsManager.getInstance().deserialize(ConnectionsManager.getInstance().getRequestWithMessageId(message.msg_id), stream, exception); + if (message.body == null) { + stream.skip(message.bytes - (stream.getPosition() - position)); + } else { + messages.add(message); } } } public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); - stream.writeInt32(0x1cb5c415); - int count = random_ids.size(); - stream.writeInt32(count); - for (Long value : random_ids) { - stream.writeInt64(value); + stream.writeInt32(messages.size()); + for (TLObject obj : messages) { + TL_protoMessage proto = (TL_protoMessage) obj; + stream.writeInt64(proto.msg_id); + stream.writeInt32(proto.seqno); + stream.writeInt32(proto.bytes); + proto.body.serializeToStream(stream); } } } - public static class TL_decryptedMessageActionCommitKey extends DecryptedMessageAction { - public static int constructor = 0xec2e0b9b; + public static class TL_rpc_result extends TLObject { + public static int constructor = 0xf35c6d01; + public long req_msg_id; + public TLObject result; - public void readParams(AbsSerializedData stream) { - exchange_id = stream.readInt64(); - key_fingerprint = stream.readInt64(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt64(exchange_id); - stream.writeInt64(key_fingerprint); - } - } - - public static class TL_decryptedMessageActionAbortKey extends DecryptedMessageAction { - public static int constructor = 0xdd05ec6b; - - - public void readParams(AbsSerializedData stream) { - exchange_id = stream.readInt64(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt64(exchange_id); - } - } - - public static class TL_decryptedMessageActionNoop extends DecryptedMessageAction { - public static int constructor = 0xa82fdd63; - - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - } - } - - public static class TL_decryptedMessageActionScreenshotMessages extends DecryptedMessageAction { - public static int constructor = 0x8ac1f475; - - - public void readParams(AbsSerializedData stream) { - boolean[] error = new boolean[1]; - stream.readInt32(error); - if (error[0]) { - return; - } - int count = stream.readInt32(error); - if (error[0]) { - return; - } - for (long a = 0; a < count; a++) { - random_ids.add(stream.readInt64(error)); - if (error[0]) { - return; + public static TL_rpc_result TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + if (TL_rpc_result.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_rpc_result", constructor)); + } else { + return null; } } + TL_rpc_result result = new TL_rpc_result(); + result.readParams(stream, exception); + return result; + } + + public void readParams(AbsSerializedData stream, boolean exception) { + req_msg_id = stream.readInt64(exception); + result = ConnectionsManager.getInstance().deserialize(ConnectionsManager.getInstance().getRequestWithMessageId(req_msg_id), stream, exception); } public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); - stream.writeInt32(0x1cb5c415); - int count = random_ids.size(); - stream.writeInt32(count); - for (Long value : random_ids) { - stream.writeInt64(value); - } - } - } - - public static class TL_messageEncryptedAction extends MessageAction { - public static int constructor = 0x555555F7; - - public void readParams(AbsSerializedData stream) { - encryptedAction = (DecryptedMessageAction)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - encryptedAction.serializeToStream(stream); - } - } - - public static class TL_upload_saveBigFilePart extends TLObject { - public static int constructor = 0xde7b673d; - - public long file_id; - public int file_part; - public int file_total_parts; - public ByteBufferDesc bytes; - - public Class responseClass () { - return Bool.class; - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt64(file_id); - stream.writeInt32(file_part); - stream.writeInt32(file_total_parts); - stream.writeByteBuffer(bytes); + stream.writeInt64(req_msg_id); + result.serializeToStream(stream); } @Override @@ -11457,160 +14960,125 @@ public class TLRPC { if (disableFree) { return; } - if (bytes != null) { - BuffersStorage.getInstance().reuseFreeBuffer(bytes); - bytes = null; + if (result != null) { + result.freeResources(); } } } - public static class TL_upload_saveFilePart extends TLObject { - public static int constructor = 0xb304a621; + public static class TL_futureSalt extends TLObject { + public static int constructor = 0x0949d9dc; - public long file_id; - public int file_part; - public ByteBufferDesc bytes; + public int valid_since; + public int valid_until; + public long salt; - public Class responseClass () { - return Bool.class; + public void readParams(AbsSerializedData stream, boolean exception) { + valid_since = stream.readInt32(exception); + valid_until = stream.readInt32(exception); + salt = stream.readInt64(exception); } public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); - stream.writeInt64(file_id); - stream.writeInt32(file_part); - stream.writeByteBuffer(bytes); + stream.writeInt32(valid_since); + stream.writeInt32(valid_until); + stream.writeInt64(salt); + } + } + + public static class TL_futuresalts extends TLObject { + public static int constructor = 0xae500895; + + public long req_msg_id; + public int now; + public ArrayList salts = new ArrayList<>(); + + public static TL_futuresalts TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + if (TL_futuresalts.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_futuresalts", constructor)); + } else { + return null; + } + } + TL_futuresalts result = new TL_futuresalts(); + result.readParams(stream, exception); + return result; } - @Override - public void freeResources() { - if (disableFree) { - return; + public void readParams(AbsSerializedData stream, boolean exception) { + req_msg_id = stream.readInt64(exception); + now = stream.readInt32(exception); + int count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + TL_futureSalt salt = new TL_futureSalt(); + salt.readParams(stream, exception); + salts.add(salt); } - if (bytes != null) { - BuffersStorage.getInstance().reuseFreeBuffer(bytes); - bytes = null; + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt64(req_msg_id); + stream.writeInt32(now); + int count = salts.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + salts.get(a).serializeToStream(stream); } } } - public static class InputEncryptedFile extends TLObject { - public long id; - public long access_hash; - public int parts; - public int key_fingerprint; - public String md5_checksum; - public byte[] key; - public byte[] iv; - } + public static class TL_gzip_packed extends TLObject { + public static int constructor = 0x3072cfa1; - public static class TL_decryptedMessageMediaVideo_old extends TL_decryptedMessageMediaVideo { - public static int constructor = 0x4cee6ef3; + public byte[] packed_data; - - public void readParams(AbsSerializedData stream) { - thumb = stream.readByteArray(); - thumb_w = stream.readInt32(); - thumb_h = stream.readInt32(); - duration = stream.readInt32(); - w = stream.readInt32(); - h = stream.readInt32(); - size = stream.readInt32(); - key = stream.readByteArray(); - iv = stream.readByteArray(); + public void readParams(AbsSerializedData stream, boolean exception) { + packed_data = stream.readByteArray(exception); } public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); - stream.writeByteArray(thumb); - stream.writeInt32(thumb_w); - stream.writeInt32(thumb_h); - stream.writeInt32(duration); - stream.writeInt32(w); - stream.writeInt32(h); - stream.writeInt32(size); - stream.writeByteArray(key); - stream.writeByteArray(iv); + stream.writeByteArray(packed_data); } } - public static class TL_decryptedMessageMediaAudio_old extends TL_decryptedMessageMediaAudio { - public static int constructor = 0x6080758f; + public static class Vector extends TLObject { + public static int constructor = 0x1cb5c415; + public ArrayList objects = new ArrayList<>(); + } + public static class TL_decryptedMessageHolder extends TLObject { + public static int constructor = 0x555555F9; - public void readParams(AbsSerializedData stream) { - duration = stream.readInt32(); - size = stream.readInt32(); - key = stream.readByteArray(); - iv = stream.readByteArray(); + public long random_id; + public int date; + public TL_decryptedMessageLayer layer; + public EncryptedFile file; + public boolean new_key_used; + + public void readParams(AbsSerializedData stream, boolean exception) { + random_id = stream.readInt64(exception); + date = stream.readInt32(exception); + layer = TL_decryptedMessageLayer.TLdeserialize(stream, stream.readInt32(exception), exception); + if (stream.readBool(exception)) { + file = EncryptedFile.TLdeserialize(stream, stream.readInt32(exception), exception); + } + new_key_used = stream.readBool(exception); } public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); - stream.writeInt32(duration); - stream.writeInt32(size); - stream.writeByteArray(key); - stream.writeByteArray(iv); - } - } - - public static class TL_audio_old extends TL_audio { - public static int constructor = 0x427425e7; - - - public void readParams(AbsSerializedData stream) { - id = stream.readInt64(); - access_hash = stream.readInt64(); - user_id = stream.readInt32(); - date = stream.readInt32(); - duration = stream.readInt32(); - size = stream.readInt32(); - dc_id = stream.readInt32(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt64(id); - stream.writeInt64(access_hash); - stream.writeInt32(user_id); + stream.writeInt64(random_id); stream.writeInt32(date); - stream.writeInt32(duration); - stream.writeInt32(size); - stream.writeInt32(dc_id); - } - } - - public static class TL_video_old extends TL_video { - public static int constructor = 0x5a04a49f; - - - public void readParams(AbsSerializedData stream) { - id = stream.readInt64(); - access_hash = stream.readInt64(); - user_id = stream.readInt32(); - date = stream.readInt32(); - caption = stream.readString(); - duration = stream.readInt32(); - size = stream.readInt32(); - thumb = (PhotoSize)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - dc_id = stream.readInt32(); - w = stream.readInt32(); - h = stream.readInt32(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt64(id); - stream.writeInt64(access_hash); - stream.writeInt32(user_id); - stream.writeInt32(date); - stream.writeString(caption); - stream.writeInt32(duration); - stream.writeInt32(size); - thumb.serializeToStream(stream); - stream.writeInt32(dc_id); - stream.writeInt32(w); - stream.writeInt32(h); + layer.serializeToStream(stream); + stream.writeBool(file != null); + if (file != null) { + file.serializeToStream(stream); + } + stream.writeBool(new_key_used); } } } diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/UserConfig.java b/TMessagesProj/src/main/java/org/telegram/messenger/UserConfig.java index 42e44a7ca..2ae522fb4 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/UserConfig.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/UserConfig.java @@ -36,6 +36,7 @@ public class UserConfig { public static int autoLockIn = 60 * 60; public static int lastPauseTime = 0; public static boolean isWaitingForPasscodeEnter = false; + public static int lastUpdateVersion; public static int getNewMessageId() { int id; @@ -71,6 +72,7 @@ public class UserConfig { editor.putInt("passcodeType", passcodeType); editor.putInt("autoLockIn", autoLockIn); editor.putInt("lastPauseTime", lastPauseTime); + editor.putInt("lastUpdateVersion", lastUpdateVersion); if (currentUser != null) { if (withFile) { @@ -123,28 +125,28 @@ public class UserConfig { if (configFile.exists()) { try { SerializedData data = new SerializedData(configFile); - int ver = data.readInt32(); + int ver = data.readInt32(false); if (ver == 1) { - int constructor = data.readInt32(); - currentUser = (TLRPC.TL_userSelf)TLClassStore.Instance().TLdeserialize(data, constructor); - MessagesStorage.lastDateValue = data.readInt32(); - MessagesStorage.lastPtsValue = data.readInt32(); - MessagesStorage.lastSeqValue = data.readInt32(); - registeredForPush = data.readBool(); - pushString = data.readString(); - lastSendMessageId = data.readInt32(); - lastLocalId = data.readInt32(); - contactsHash = data.readString(); - importHash = data.readString(); - saveIncomingPhotos = data.readBool(); + int constructor = data.readInt32(false); + currentUser = TLRPC.TL_userSelf.TLdeserialize(data, constructor, false); + MessagesStorage.lastDateValue = data.readInt32(false); + MessagesStorage.lastPtsValue = data.readInt32(false); + MessagesStorage.lastSeqValue = data.readInt32(false); + registeredForPush = data.readBool(false); + pushString = data.readString(false); + lastSendMessageId = data.readInt32(false); + lastLocalId = data.readInt32(false); + contactsHash = data.readString(false); + importHash = data.readString(false); + saveIncomingPhotos = data.readBool(false); contactsVersion = 0; - MessagesStorage.lastQtsValue = data.readInt32(); - MessagesStorage.lastSecretVersion = data.readInt32(); - int val = data.readInt32(); + MessagesStorage.lastQtsValue = data.readInt32(false); + MessagesStorage.lastSecretVersion = data.readInt32(false); + int val = data.readInt32(false); if (val == 1) { - MessagesStorage.secretPBytes = data.readByteArray(); + MessagesStorage.secretPBytes = data.readByteArray(false); } - MessagesStorage.secretG = data.readInt32(); + MessagesStorage.secretG = data.readInt32(false); Utilities.stageQueue.postRunnable(new Runnable() { @Override public void run() { @@ -152,8 +154,8 @@ public class UserConfig { } }); } else if (ver == 2) { - int constructor = data.readInt32(); - currentUser = (TLRPC.TL_userSelf)TLClassStore.Instance().TLdeserialize(data, constructor); + int constructor = data.readInt32(false); + currentUser = TLRPC.TL_userSelf.TLdeserialize(data, constructor, false); SharedPreferences preferences = ApplicationLoader.applicationContext.getSharedPreferences("userconfing", Context.MODE_PRIVATE); registeredForPush = preferences.getBoolean("registeredForPush", false); @@ -199,12 +201,13 @@ public class UserConfig { passcodeType = preferences.getInt("passcodeType", 0); autoLockIn = preferences.getInt("autoLockIn", 60 * 60); lastPauseTime = preferences.getInt("lastPauseTime", 0); + lastUpdateVersion = preferences.getInt("lastUpdateVersion", 511); String user = preferences.getString("user", null); if (user != null) { byte[] userBytes = Base64.decode(user, Base64.DEFAULT); if (userBytes != null) { SerializedData data = new SerializedData(userBytes); - currentUser = (TLRPC.TL_userSelf)TLClassStore.Instance().TLdeserialize(data, data.readInt32()); + currentUser = TLRPC.TL_userSelf.TLdeserialize(data, data.readInt32(false), false); data.cleanup(); } } @@ -229,6 +232,7 @@ public class UserConfig { autoLockIn = 60 * 60; lastPauseTime = 0; isWaitingForPasscodeEnter = false; + lastUpdateVersion = BuildVars.BUILD_VERSION; saveConfig(true); } } diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/Utilities.java b/TMessagesProj/src/main/java/org/telegram/messenger/Utilities.java index 3a42f6c6f..2b1ac08eb 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/Utilities.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/Utilities.java @@ -59,6 +59,9 @@ public class Utilities { public static Pattern pattern = Pattern.compile("[0-9]+"); public static SecureRandom random = new SecureRandom(); + private static byte[] decompressBuffer; + private static ByteArrayOutputStreamExpand decompressStream; + public static ArrayList goodPrimes = new ArrayList<>(); public static class TPFactorizedValue { @@ -68,7 +71,7 @@ public class Utilities { public static volatile DispatchQueue stageQueue = new DispatchQueue("stageQueue"); public static volatile DispatchQueue globalQueue = new DispatchQueue("globalQueue"); public static volatile DispatchQueue searchQueue = new DispatchQueue("searchQueue"); - public static volatile DispatchQueue photoBookQueue = new DispatchQueue("photoBookQueue"); + public static volatile DispatchQueue phoneBookQueue = new DispatchQueue("photoBookQueue"); final protected static char[] hexArray = "0123456789ABCDEF".toCharArray(); @@ -93,9 +96,9 @@ public class Utilities { byte[] bytes = Base64.decode(primes, Base64.DEFAULT); if (bytes != null) { SerializedData data = new SerializedData(bytes); - int count = data.readInt32(); + int count = data.readInt32(false); for (int a = 0; a < count; a++) { - goodPrimes.add(data.readString()); + goodPrimes.add(data.readString(false)); } data.cleanup(); } @@ -110,7 +113,7 @@ public class Utilities { public native static long doPQNative(long _what); public native static void loadBitmap(String path, Bitmap bitmap, int scale, int width, int height, int stride); public native static int pinBitmap(Bitmap bitmap); - public native static void blurBitmap(Object bitmap, int radius); + public native static void blurBitmap(Object bitmap, int radius, int unpin); public native static void calcCDT(ByteBuffer hsvBuffer, int width, int height, ByteBuffer buffer); public native static Bitmap loadWebpImage(ByteBuffer buffer, int len, BitmapFactory.Options options); public native static Bitmap loadBpgImage(ByteBuffer buffer, int len, BitmapFactory.Options options); @@ -412,17 +415,21 @@ public class Utilities { return keyData; } - public static TLObject decompress(byte[] data, TLObject parentObject) { - final int BUFFER_SIZE = 512; + public static TLObject decompress(byte[] data, TLObject parentObject, boolean exception) { + final int BUFFER_SIZE = 16384; ByteArrayInputStream is = new ByteArrayInputStream(data); GZIPInputStream gis; + SerializedData stream = null; try { + if (decompressBuffer == null) { + decompressBuffer = new byte[BUFFER_SIZE]; + decompressStream = new ByteArrayOutputStreamExpand(BUFFER_SIZE); + } + decompressStream.reset(); gis = new GZIPInputStream(is, BUFFER_SIZE); - ByteArrayOutputStream bytesOutput = new ByteArrayOutputStream(); - data = new byte[BUFFER_SIZE]; int bytesRead; - while ((bytesRead = gis.read(data)) != -1) { - bytesOutput.write(data, 0, bytesRead); + while ((bytesRead = gis.read(decompressBuffer)) != -1) { + decompressStream.write(decompressBuffer, 0, bytesRead); } try { gis.close(); @@ -434,18 +441,15 @@ public class Utilities { } catch (Exception e) { FileLog.e("tmessages", e); } - SerializedData stream = new SerializedData(bytesOutput.toByteArray()); - try { - bytesOutput.close(); - } catch (Exception e) { - FileLog.e("tmessages", e); - } - TLObject object = TLClassStore.Instance().TLdeserialize(stream, stream.readInt32(), parentObject); - stream.cleanup(); - return object; + stream = new SerializedData(decompressStream.toByteArray()); } catch (IOException e) { FileLog.e("tmessages", e); } + if (stream != null) { + TLObject object = ConnectionsManager.getInstance().deserialize(parentObject, stream, exception); + stream.cleanup(); + return object; + } return null; } @@ -692,7 +696,7 @@ public class Utilities { if (query.startsWith(" ")) { builder.append(" "); } - query.trim(); + query = query.trim(); builder.append(AndroidUtilities.replaceTags("" + query + "")); lastIndex = end; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/ActionBar.java b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/ActionBar.java index 8c94548bf..03942a068 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/ActionBar.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/ActionBar.java @@ -24,6 +24,7 @@ import android.widget.TextView; import org.telegram.android.AndroidUtilities; import org.telegram.messenger.R; +import org.telegram.ui.Components.LayoutHelper; public class ActionBar extends FrameLayout { @@ -62,8 +63,8 @@ public class ActionBar extends FrameLayout { titleFrameLayout = new FrameLayout(context); addView(titleFrameLayout); FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams)titleFrameLayout.getLayoutParams(); - layoutParams.width = LayoutParams.WRAP_CONTENT; - layoutParams.height = LayoutParams.FILL_PARENT; + layoutParams.width = LayoutHelper.WRAP_CONTENT; + layoutParams.height = LayoutHelper.MATCH_PARENT; layoutParams.gravity = Gravity.TOP | Gravity.LEFT; titleFrameLayout.setLayoutParams(layoutParams); titleFrameLayout.setPadding(0, 0, AndroidUtilities.dp(4), 0); @@ -97,8 +98,8 @@ public class ActionBar extends FrameLayout { } layoutParams = (LayoutParams) titleTextView.getLayoutParams(); - layoutParams.width = LayoutParams.WRAP_CONTENT; - layoutParams.height = LayoutParams.WRAP_CONTENT; + layoutParams.width = LayoutHelper.WRAP_CONTENT; + layoutParams.height = LayoutHelper.WRAP_CONTENT; layoutParams.gravity = Gravity.TOP | Gravity.LEFT; titleTextView.setLayoutParams(layoutParams); titleTextView.measure(width, height); @@ -112,8 +113,8 @@ public class ActionBar extends FrameLayout { } layoutParams = (LayoutParams) subTitleTextView.getLayoutParams(); - layoutParams.width = LayoutParams.WRAP_CONTENT; - layoutParams.height = LayoutParams.WRAP_CONTENT; + layoutParams.width = LayoutHelper.WRAP_CONTENT; + layoutParams.height = LayoutHelper.WRAP_CONTENT; layoutParams.gravity = Gravity.TOP | Gravity.LEFT; subTitleTextView.setLayoutParams(layoutParams); subTitleTextView.measure(width, height); @@ -133,7 +134,7 @@ public class ActionBar extends FrameLayout { if (titleTextView != null && titleTextView.getVisibility() == VISIBLE) { layoutParams = (LayoutParams) titleTextView.getLayoutParams(); - layoutParams.width = LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; layoutParams.height = titleTextView.getMeasuredHeight(); int y; if (subTitleTextView != null && subTitleTextView.getVisibility() == VISIBLE) { @@ -146,7 +147,7 @@ public class ActionBar extends FrameLayout { } if (subTitleTextView != null && subTitleTextView.getVisibility() == VISIBLE) { layoutParams = (LayoutParams) subTitleTextView.getLayoutParams(); - layoutParams.width = LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; layoutParams.height = subTitleTextView.getMeasuredHeight(); layoutParams.setMargins(x, height / 2 + (height / 2 - subTitleTextView.getMeasuredHeight()) / 2 - offset, 0, 0); subTitleTextView.setLayoutParams(layoutParams); @@ -163,7 +164,7 @@ public class ActionBar extends FrameLayout { return; } FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams)menu.getLayoutParams(); - layoutParams.width = isSearchFieldVisible ? LayoutParams.MATCH_PARENT : LayoutParams.WRAP_CONTENT; + layoutParams.width = isSearchFieldVisible ? LayoutHelper.MATCH_PARENT : LayoutHelper.WRAP_CONTENT; layoutParams.height = height; layoutParams.leftMargin = isSearchFieldVisible ? AndroidUtilities.dp(AndroidUtilities.isTablet() ? 74 : 66) : 0; layoutParams.topMargin = occupyStatusBar ? AndroidUtilities.statusBarHeight : 0; @@ -277,14 +278,19 @@ public class ActionBar extends FrameLayout { } public void setTitle(CharSequence value) { + boolean created = false; if (value != null && titleTextView == null) { createTitleTextView(); + created = true; } if (titleTextView != null) { lastTitle = value; titleTextView.setVisibility(value != null && !isSearchFieldVisible ? VISIBLE : INVISIBLE); titleTextView.setText(value); positionTitle(getMeasuredWidth(), getMeasuredHeight()); + if (!created) { + titleTextView.setText(value); + } } } @@ -301,11 +307,11 @@ public class ActionBar extends FrameLayout { return subTitleTextView.getCompoundDrawables()[0]; } - public CharSequence getTitle() { + public String getTitle() { if (titleTextView == null) { return null; } - return titleTextView.getText(); + return titleTextView.getText().toString(); } public ActionBarMenu createMenu() { @@ -315,8 +321,8 @@ public class ActionBar extends FrameLayout { menu = new ActionBarMenu(getContext(), this); addView(menu); FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams)menu.getLayoutParams(); - layoutParams.height = LayoutParams.FILL_PARENT; - layoutParams.width = LayoutParams.WRAP_CONTENT; + layoutParams.height = LayoutHelper.MATCH_PARENT; + layoutParams.width = LayoutHelper.WRAP_CONTENT; layoutParams.gravity = Gravity.RIGHT; menu.setLayoutParams(layoutParams); return menu; @@ -331,8 +337,8 @@ public class ActionBar extends FrameLayout { View view = li.inflate(resourceId, null); addView(view); FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams)view.getLayoutParams(); - layoutParams.width = LayoutParams.FILL_PARENT; - layoutParams.height = LayoutParams.FILL_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; + layoutParams.height = LayoutHelper.MATCH_PARENT; layoutParams.topMargin = occupyStatusBar ? AndroidUtilities.statusBarHeight : 0; view.setLayoutParams(layoutParams); } @@ -346,8 +352,8 @@ public class ActionBar extends FrameLayout { addView(actionMode); actionMode.setPadding(0, occupyStatusBar ? AndroidUtilities.statusBarHeight : 0, 0, 0); FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams)actionMode.getLayoutParams(); - layoutParams.height = LayoutParams.FILL_PARENT; - layoutParams.width = LayoutParams.FILL_PARENT; + layoutParams.height = LayoutHelper.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; layoutParams.gravity = Gravity.RIGHT; actionMode.setLayoutParams(layoutParams); actionMode.setVisibility(INVISIBLE); @@ -358,7 +364,7 @@ public class ActionBar extends FrameLayout { addView(actionModeTop); layoutParams = (FrameLayout.LayoutParams)actionModeTop.getLayoutParams(); layoutParams.height = AndroidUtilities.statusBarHeight; - layoutParams.width = LayoutParams.FILL_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; layoutParams.gravity = Gravity.TOP | Gravity.LEFT; actionModeTop.setLayoutParams(layoutParams); actionModeTop.setVisibility(INVISIBLE); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/ActionBarLayout.java b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/ActionBarLayout.java index c49768f8a..2a7f75c4f 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/ActionBarLayout.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/ActionBarLayout.java @@ -30,10 +30,11 @@ import android.widget.LinearLayout; import org.telegram.android.AndroidUtilities; import org.telegram.android.NotificationCenter; import org.telegram.messenger.R; -import org.telegram.ui.AnimationCompat.AnimatorListenerAdapterProxy; -import org.telegram.ui.AnimationCompat.AnimatorSetProxy; -import org.telegram.ui.AnimationCompat.ObjectAnimatorProxy; -import org.telegram.ui.AnimationCompat.ViewProxy; +import org.telegram.android.AnimationCompat.AnimatorListenerAdapterProxy; +import org.telegram.android.AnimationCompat.AnimatorSetProxy; +import org.telegram.android.AnimationCompat.ObjectAnimatorProxy; +import org.telegram.android.AnimationCompat.ViewProxy; +import org.telegram.ui.Components.LayoutHelper; import java.util.ArrayList; @@ -108,6 +109,8 @@ public class ActionBarLayout extends FrameLayout { private ActionBar currentActionBar; private AnimatorSetProxy currentAnimation; + private DecelerateInterpolator decelerateInterpolator = new DecelerateInterpolator(1.5f); + private AccelerateDecelerateInterpolator accelerateDecelerateInterpolator = new AccelerateDecelerateInterpolator(); public float innerTranslationX; @@ -128,6 +131,9 @@ public class ActionBarLayout extends FrameLayout { private View backgroundView; private boolean removeActionBarExtraHeight; + private float animationProgress = 0.0f; + private long lastFrameTime; + private String titleOverlayText; private ActionBarLayoutDelegate delegate = null; @@ -151,16 +157,16 @@ public class ActionBarLayout extends FrameLayout { containerViewBack = new LinearLayoutContainer(parentActivity); addView(containerViewBack); FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) containerViewBack.getLayoutParams(); - layoutParams.width = LayoutParams.MATCH_PARENT; - layoutParams.height = LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; + layoutParams.height = LayoutHelper.MATCH_PARENT; layoutParams.gravity = Gravity.TOP | Gravity.LEFT; containerViewBack.setLayoutParams(layoutParams); containerView = new LinearLayoutContainer(parentActivity); addView(containerView); layoutParams = (FrameLayout.LayoutParams) containerView.getLayoutParams(); - layoutParams.width = LayoutParams.MATCH_PARENT; - layoutParams.height = LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; + layoutParams.height = LayoutHelper.MATCH_PARENT; layoutParams.gravity = Gravity.TOP | Gravity.LEFT; containerView.setLayoutParams(layoutParams); @@ -347,8 +353,8 @@ public class ActionBarLayout extends FrameLayout { } containerViewBack.addView(fragmentView); ViewGroup.LayoutParams layoutParams = fragmentView.getLayoutParams(); - layoutParams.width = FrameLayout.LayoutParams.MATCH_PARENT; - layoutParams.height = FrameLayout.LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; + layoutParams.height = LayoutHelper.MATCH_PARENT; fragmentView.setLayoutParams(layoutParams); if (!lastFragment.hasOwnBackground && fragmentView.getBackground() == null) { fragmentView.setBackgroundColor(0xffffffff); @@ -380,7 +386,7 @@ public class ActionBarLayout extends FrameLayout { int dx = Math.max(0, (int) (ev.getX() - startedTrackingX)); int dy = Math.abs((int) ev.getY() - startedTrackingY); velocityTracker.addMovement(ev); - if (maybeStartTracking && !startedTracking && dx >= AndroidUtilities.getPixelsInCM(0.3f, true) && Math.abs(dx) / 3 > dy) { + if (maybeStartTracking && !startedTracking && dx >= AndroidUtilities.getPixelsInCM(0.4f, true) && Math.abs(dx) / 3 > dy) { prepareForMoving(ev); } else if (startedTracking) { if (!beginTrackingSent) { @@ -399,10 +405,10 @@ public class ActionBarLayout extends FrameLayout { velocityTracker = VelocityTracker.obtain(); } velocityTracker.computeCurrentVelocity(1000); - if (!startedTracking) { + if (!startedTracking && fragmentsStack.get(fragmentsStack.size() - 1).swipeBackEnabled) { float velX = velocityTracker.getXVelocity(); float velY = velocityTracker.getYVelocity(); - if (velX >= 3500 && velX > velY) { + if (velX >= 3500 && velX > Math.abs(velY)) { prepareForMoving(ev); if (!beginTrackingSent) { if (((Activity) getContext()).getCurrentFocus() != null) { @@ -550,6 +556,44 @@ public class ActionBarLayout extends FrameLayout { return presentFragment(fragment, removeLast, false, true); } + private void startLayoutAnimation(final boolean open, final boolean first) { + if (first) { + animationProgress = 0.0f; + lastFrameTime = System.nanoTime() / 1000000; + } + AndroidUtilities.runOnUIThread(new Runnable() { + @Override + public void run() { + if (first) { + transitionAnimationStartTime = System.currentTimeMillis(); + } + long newTime = System.nanoTime() / 1000000; + long dt = newTime - lastFrameTime; + if (dt > 18) { + dt = 18; + } + lastFrameTime = newTime; + animationProgress += dt / 150.0f; + if (animationProgress > 1.0f) { + animationProgress = 1.0f; + } + float interpolated = decelerateInterpolator.getInterpolation(animationProgress); + if (open) { + ViewProxy.setAlpha(containerView, interpolated); + ViewProxy.setTranslationX(containerView, AndroidUtilities.dp(48) * (1.0f - interpolated)); + } else { + ViewProxy.setAlpha(containerViewBack, 1.0f - interpolated); + ViewProxy.setTranslationX(containerViewBack, AndroidUtilities.dp(48) * interpolated); + } + if (animationProgress < 1) { + startLayoutAnimation(open, false); + } else { + onAnimationEndCheck(false); + } + } + }); + } + public boolean presentFragment(final BaseFragment fragment, final boolean removeLast, boolean forceWithoutAnimation, boolean check) { if (checkTransitionAnimation() || delegate != null && check && !delegate.needPresentFragment(fragment, removeLast, forceWithoutAnimation, this) || !fragment.onFragmentCreate()) { return false; @@ -586,8 +630,8 @@ public class ActionBarLayout extends FrameLayout { containerViewBack.addView(fragmentView); ViewGroup.LayoutParams layoutParams = fragmentView.getLayoutParams(); - layoutParams.width = FrameLayout.LayoutParams.MATCH_PARENT; - layoutParams.height = FrameLayout.LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; + layoutParams.height = LayoutHelper.MATCH_PARENT; fragmentView.setLayoutParams(layoutParams); fragmentsStack.add(fragment); fragment.onResume(); @@ -615,6 +659,14 @@ public class ActionBarLayout extends FrameLayout { if (useAlphaAnimations && fragmentsStack.size() == 1) { presentFragmentInternalRemoveOld(removeLast, currentFragment); + transitionAnimationStartTime = System.currentTimeMillis(); + transitionAnimationInProgress = true; + onOpenAnimationEndRunnable = new Runnable() { + @Override + public void run() { + fragment.onOpenAnimationEnd(); + } + }; ArrayList animators = new ArrayList<>(); animators.add(ObjectAnimatorProxy.ofFloat(this, "alpha", 0.0f, 1.0f)); if (backgroundView != null) { @@ -624,7 +676,7 @@ public class ActionBarLayout extends FrameLayout { currentAnimation = new AnimatorSetProxy(); currentAnimation.playTogether(animators); - currentAnimation.setInterpolator(new AccelerateDecelerateInterpolator()); + currentAnimation.setInterpolator(accelerateDecelerateInterpolator); currentAnimation.setDuration(200); currentAnimation.addListener(new AnimatorListenerAdapterProxy() { @Override @@ -651,11 +703,12 @@ public class ActionBarLayout extends FrameLayout { }; ViewProxy.setAlpha(containerView, 0.0f); ViewProxy.setTranslationX(containerView, 48.0f); - currentAnimation = new AnimatorSetProxy(); + startLayoutAnimation(true, true); + /*currentAnimation = new AnimatorSetProxy(); currentAnimation.playTogether( ObjectAnimatorProxy.ofFloat(containerView, "alpha", 0.0f, 1.0f), ObjectAnimatorProxy.ofFloat(containerView, "translationX", AndroidUtilities.dp(48), 0)); - currentAnimation.setInterpolator(new DecelerateInterpolator(1.5f)); + currentAnimation.setInterpolator(decelerateInterpolator); currentAnimation.setDuration(200); currentAnimation.addListener(new AnimatorListenerAdapterProxy() { @Override @@ -673,7 +726,7 @@ public class ActionBarLayout extends FrameLayout { onAnimationEndCheck(false); } }); - currentAnimation.start(); + currentAnimation.start();*/ } } else { if (backgroundView != null) { @@ -771,8 +824,8 @@ public class ActionBarLayout extends FrameLayout { } containerView.addView(fragmentView); ViewGroup.LayoutParams layoutParams = fragmentView.getLayoutParams(); - layoutParams.width = FrameLayout.LayoutParams.MATCH_PARENT; - layoutParams.height = FrameLayout.LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; + layoutParams.height = LayoutHelper.MATCH_PARENT; fragmentView.setLayoutParams(layoutParams); previousFragment.onResume(); currentActionBar = previousFragment.actionBar; @@ -794,12 +847,13 @@ public class ActionBarLayout extends FrameLayout { ViewProxy.setTranslationX(containerViewBack, 0); } }; + startLayoutAnimation(false, true); - currentAnimation = new AnimatorSetProxy(); + /*currentAnimation = new AnimatorSetProxy(); currentAnimation.playTogether( ObjectAnimatorProxy.ofFloat(containerViewBack, "alpha", 1.0f, 0.0f), ObjectAnimatorProxy.ofFloat(containerViewBack, "translationX", 0, AndroidUtilities.dp(48))); - currentAnimation.setInterpolator(new DecelerateInterpolator(1.5f)); + currentAnimation.setInterpolator(decelerateInterpolator); currentAnimation.setDuration(200); currentAnimation.addListener(new AnimatorListenerAdapterProxy() { @Override @@ -817,7 +871,7 @@ public class ActionBarLayout extends FrameLayout { onAnimationEndCheck(false); } }); - currentAnimation.start(); + currentAnimation.start();*/ } } else { if (useAlphaAnimations) { @@ -846,7 +900,7 @@ public class ActionBarLayout extends FrameLayout { currentAnimation = new AnimatorSetProxy(); currentAnimation.playTogether(animators); - currentAnimation.setInterpolator(new AccelerateDecelerateInterpolator()); + currentAnimation.setInterpolator(accelerateDecelerateInterpolator); currentAnimation.setDuration(200); currentAnimation.addListener(new AnimatorListenerAdapterProxy() { @Override @@ -903,8 +957,8 @@ public class ActionBarLayout extends FrameLayout { } containerView.addView(fragmentView); ViewGroup.LayoutParams layoutParams = fragmentView.getLayoutParams(); - layoutParams.width = FrameLayout.LayoutParams.MATCH_PARENT; - layoutParams.height = FrameLayout.LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; + layoutParams.height = LayoutHelper.MATCH_PARENT; fragmentView.setLayoutParams(layoutParams); previousFragment.onResume(); currentActionBar = previousFragment.actionBar; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/ActionBarMenu.java b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/ActionBarMenu.java index 3a63dd188..ef85bd22f 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/ActionBarMenu.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/ActionBarMenu.java @@ -13,10 +13,10 @@ import android.graphics.drawable.Drawable; import android.util.AttributeSet; import android.view.LayoutInflater; import android.view.View; -import android.widget.FrameLayout; import android.widget.LinearLayout; import org.telegram.android.AndroidUtilities; +import org.telegram.ui.Components.LayoutHelper; public class ActionBarMenu extends LinearLayout { @@ -46,7 +46,7 @@ public class ActionBarMenu extends LinearLayout { view.setTag(id); addView(view); LinearLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams)view.getLayoutParams(); - layoutParams.height = FrameLayout.LayoutParams.FILL_PARENT; + layoutParams.height = LayoutHelper.MATCH_PARENT; view.setBackgroundResource(parentActionBar.itemsBackgroundResourceId); view.setLayoutParams(layoutParams); view.setOnClickListener(new OnClickListener() { @@ -84,7 +84,7 @@ public class ActionBarMenu extends LinearLayout { } addView(menuItem); LinearLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams)menuItem.getLayoutParams(); - layoutParams.height = FrameLayout.LayoutParams.MATCH_PARENT; + layoutParams.height = LayoutHelper.MATCH_PARENT; layoutParams.width = width; menuItem.setLayoutParams(layoutParams); menuItem.setOnClickListener(new OnClickListener() { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/ActionBarMenuItem.java b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/ActionBarMenuItem.java index e8991c227..afbf6b807 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/ActionBarMenuItem.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/ActionBarMenuItem.java @@ -33,8 +33,9 @@ import android.widget.TextView; import org.telegram.android.AndroidUtilities; import org.telegram.android.LocaleController; import org.telegram.messenger.R; -import org.telegram.ui.AnimationCompat.ViewProxy; +import org.telegram.android.AnimationCompat.ViewProxy; import org.telegram.ui.Components.FrameLayoutFixed; +import org.telegram.ui.Components.LayoutHelper; import java.lang.reflect.Field; @@ -74,8 +75,8 @@ public class ActionBarMenuItem extends FrameLayoutFixed { iconView.setScaleType(ImageView.ScaleType.CENTER); addView(iconView); LayoutParams layoutParams = (LayoutParams) iconView.getLayoutParams(); - layoutParams.width = LayoutParams.MATCH_PARENT; - layoutParams.height = LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; + layoutParams.height = LayoutHelper.MATCH_PARENT; iconView.setLayoutParams(layoutParams); } @@ -216,7 +217,7 @@ public class ActionBarMenuItem extends FrameLayoutFixed { if (LocaleController.isRTL) { layoutParams.gravity = Gravity.RIGHT; } - layoutParams.width = LinearLayout.LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; layoutParams.height = AndroidUtilities.dp(48); textView.setLayoutParams(layoutParams); textView.setOnClickListener(new OnClickListener() { @@ -249,7 +250,7 @@ public class ActionBarMenuItem extends FrameLayoutFixed { return; } if (popupWindow == null) { - popupWindow = new ActionBarPopupWindow(popupLayout, FrameLayout.LayoutParams.WRAP_CONTENT, FrameLayout.LayoutParams.WRAP_CONTENT); + popupWindow = new ActionBarPopupWindow(popupLayout, LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT); //popupWindow.setBackgroundDrawable(new BitmapDrawable()); popupWindow.setAnimationStyle(R.style.PopupAnimation); popupWindow.setOutsideTouchable(true); @@ -362,7 +363,7 @@ public class ActionBarMenuItem extends FrameLayoutFixed { LinearLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams)searchContainer.getLayoutParams(); layoutParams.weight = 1; layoutParams.width = 0; - layoutParams.height = LinearLayout.LayoutParams.MATCH_PARENT; + layoutParams.height = LayoutHelper.MATCH_PARENT; layoutParams.leftMargin = AndroidUtilities.dp(6); searchContainer.setLayoutParams(layoutParams); searchContainer.setVisibility(GONE); @@ -446,7 +447,7 @@ public class ActionBarMenuItem extends FrameLayoutFixed { } searchContainer.addView(searchField); FrameLayout.LayoutParams layoutParams2 = (FrameLayout.LayoutParams) searchField.getLayoutParams(); - layoutParams2.width = FrameLayout.LayoutParams.MATCH_PARENT; + layoutParams2.width = LayoutHelper.MATCH_PARENT; layoutParams2.gravity = Gravity.CENTER_VERTICAL; layoutParams2.height = AndroidUtilities.dp(36); layoutParams2.rightMargin = AndroidUtilities.dp(48); @@ -466,7 +467,7 @@ public class ActionBarMenuItem extends FrameLayoutFixed { layoutParams2 = (FrameLayout.LayoutParams) clearButton.getLayoutParams(); layoutParams2.width = AndroidUtilities.dp(48); layoutParams2.gravity = Gravity.CENTER_VERTICAL | Gravity.RIGHT; - layoutParams2.height = FrameLayout.LayoutParams.MATCH_PARENT; + layoutParams2.height = LayoutHelper.MATCH_PARENT; clearButton.setLayoutParams(layoutParams2); } isSearchField = value; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/BaseFragment.java b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/BaseFragment.java index b6592a2d9..2388b7b48 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/BaseFragment.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/BaseFragment.java @@ -204,7 +204,7 @@ public class BaseFragment { } public AlertDialog showAlertDialog(AlertDialog.Builder builder) { - if (parentLayout == null || parentLayout.checkTransitionAnimation() || parentLayout.animationInProgress || parentLayout.startedTracking) { + if (parentLayout == null || parentLayout.animationInProgress || parentLayout.startedTracking || parentLayout.checkTransitionAnimation()) { return null; } try { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/DrawerLayoutContainer.java b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/DrawerLayoutContainer.java index aa136bf16..3596048d4 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/DrawerLayoutContainer.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/DrawerLayoutContainer.java @@ -26,9 +26,9 @@ import android.widget.ListView; import org.telegram.android.AndroidUtilities; import org.telegram.messenger.R; -import org.telegram.ui.AnimationCompat.AnimatorListenerAdapterProxy; -import org.telegram.ui.AnimationCompat.AnimatorSetProxy; -import org.telegram.ui.AnimationCompat.ObjectAnimatorProxy; +import org.telegram.android.AnimationCompat.AnimatorListenerAdapterProxy; +import org.telegram.android.AnimationCompat.AnimatorSetProxy; +import org.telegram.android.AnimationCompat.ObjectAnimatorProxy; public class DrawerLayoutContainer extends FrameLayout { @@ -57,6 +57,7 @@ public class DrawerLayoutContainer extends FrameLayout { private float drawerPosition = 0; private boolean drawerOpened = false; + private boolean allowDrawContent = true; public DrawerLayoutContainer(Context context) { super(context); @@ -150,7 +151,7 @@ public class DrawerLayoutContainer extends FrameLayout { if (drawerLayout.getVisibility() != newVisibility) { drawerLayout.setVisibility(newVisibility); } - setScrimOpacity(drawerPosition / (float)drawerLayout.getMeasuredWidth()); + setScrimOpacity(drawerPosition / (float) drawerLayout.getMeasuredWidth()); } public float getDrawerPosition() { @@ -276,6 +277,13 @@ public class DrawerLayoutContainer extends FrameLayout { return drawerOpened; } + public void setAllowDrawContent(boolean value) { + if (allowDrawContent != value) { + allowDrawContent = value; + invalidate(); + } + } + public boolean onTouchEvent(MotionEvent ev) { if (!parentActionBarLayout.checkTransitionAnimation()) { if (drawerOpened && ev != null && ev.getX() > drawerPosition && !startedTracking) { @@ -301,7 +309,7 @@ public class DrawerLayoutContainer extends FrameLayout { float dx = (int) (ev.getX() - startedTrackingX); float dy = Math.abs((int) ev.getY() - startedTrackingY); velocityTracker.addMovement(ev); - if (maybeStartTracking && !startedTracking && (dx > 0 && dx / 3.0f > Math.abs(dy) && Math.abs(dx) >= AndroidUtilities.getPixelsInCM(0.2f, true) || dx < 0 && Math.abs(dx) >= Math.abs(dy) && Math.abs(dx) >= AndroidUtilities.getPixelsInCM(0.3f, true))) { + if (maybeStartTracking && !startedTracking && (dx > 0 && dx / 3.0f > Math.abs(dy) && Math.abs(dx) >= AndroidUtilities.getPixelsInCM(0.2f, true) || dx < 0 && Math.abs(dx) >= Math.abs(dy) && Math.abs(dx) >= AndroidUtilities.getPixelsInCM(0.4f, true))) { prepareForDrawerOpen(ev); startedTrackingX = (int) ev.getX(); requestDisallowInterceptTouchEvent(true); @@ -445,6 +453,9 @@ public class DrawerLayoutContainer extends FrameLayout { @Override protected boolean drawChild(Canvas canvas, View child, long drawingTime) { + if (!allowDrawContent) { + return false; + } final int height = getHeight(); final boolean drawingContent = child != drawerLayout; int clipLeft = 0, clipRight = getWidth(); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Adapters/BaseLocationAdapter.java b/TMessagesProj/src/main/java/org/telegram/ui/Adapters/BaseLocationAdapter.java new file mode 100644 index 000000000..81d26e865 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Adapters/BaseLocationAdapter.java @@ -0,0 +1,222 @@ +/* + * This is the source code of Telegram for Android v. 2.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-2015. + */ + +package org.telegram.ui.Adapters; + +import android.location.Location; + +import org.json.JSONArray; +import org.json.JSONObject; +import org.telegram.android.AndroidUtilities; +import org.telegram.android.volley.Request; +import org.telegram.android.volley.RequestQueue; +import org.telegram.android.volley.Response; +import org.telegram.android.volley.VolleyError; +import org.telegram.android.volley.toolbox.JsonObjectRequest; +import org.telegram.android.volley.toolbox.Volley; +import org.telegram.messenger.ApplicationLoader; +import org.telegram.messenger.BuildVars; +import org.telegram.messenger.FileLog; +import org.telegram.messenger.TLRPC; + +import java.net.URLEncoder; +import java.util.ArrayList; +import java.util.Locale; +import java.util.Timer; +import java.util.TimerTask; + +public class BaseLocationAdapter extends BaseFragmentAdapter { + + public interface BaseLocationAdapterDelegate { + void didLoadedSearchResult(ArrayList places); + } + + private RequestQueue requestQueue; + protected boolean searching; + protected ArrayList places = new ArrayList<>(); + protected ArrayList iconUrls = new ArrayList<>(); + private Location lastSearchLocation; + private BaseLocationAdapterDelegate delegate; + private Timer searchTimer; + + public BaseLocationAdapter() { + requestQueue = Volley.newRequestQueue(ApplicationLoader.applicationContext); + } + + public void destroy() { + if (requestQueue != null) { + requestQueue.cancelAll("search"); + requestQueue.stop(); + } + } + + public void setDelegate(BaseLocationAdapterDelegate delegate) { + this.delegate = delegate; + } + + public void searchDelayed(final String query, final Location coordinate) { + if (query == null || query.length() == 0) { + places.clear(); + notifyDataSetChanged(); + } else { + try { + if (searchTimer != null) { + searchTimer.cancel(); + } + } catch (Exception e) { + FileLog.e("tmessages", e); + } + searchTimer = new Timer(); + searchTimer.schedule(new TimerTask() { + @Override + public void run() { + try { + searchTimer.cancel(); + searchTimer = null; + } catch (Exception e) { + FileLog.e("tmessages", e); + } + AndroidUtilities.runOnUIThread(new Runnable() { + @Override + public void run() { + lastSearchLocation = null; + searchGooglePlacesWithQuery(query, coordinate); + } + }); + } + }, 200, 500); + } + } + + public void searchGooglePlacesWithQuery(final String query, final Location coordinate) { + if (lastSearchLocation != null && coordinate.distanceTo(lastSearchLocation) < 200) { + return; + } + lastSearchLocation = coordinate; + if (searching) { + searching = false; + requestQueue.cancelAll("search"); + } + try { + searching = true; + String url = String.format(Locale.US, "https://api.foursquare.com/v2/venues/search/?v=%s&locale=en&limit=25&client_id=%s&client_secret=%s&ll=%s", BuildVars.FOURSQUARE_API_VERSION, BuildVars.FOURSQUARE_API_ID, BuildVars.FOURSQUARE_API_KEY, String.format(Locale.US, "%f,%f", coordinate.getLatitude(), coordinate.getLongitude())); + if (query != null && query.length() > 0) { + url += "&query=" + URLEncoder.encode(query, "UTF-8"); + } + JsonObjectRequest jsonObjReq = new JsonObjectRequest(Request.Method.GET, url, null, + new Response.Listener() { + @Override + public void onResponse(JSONObject response) { + try { + places.clear(); + iconUrls.clear(); + /* + GOOGLE MAPS + JSONArray result = response.getJSONArray("results"); + + for (int a = 0; a < result.length(); a++) { + try { + JSONObject object = result.getJSONObject(a); + JSONObject location = object.getJSONObject("geometry").getJSONObject("location"); + TLRPC.TL_messageMediaVenue venue = new TLRPC.TL_messageMediaVenue(); + venue.geo = new TLRPC.TL_geoPoint(); + venue.geo.lat = location.getDouble("lat"); + venue.geo._long = location.getDouble("lng"); + if (object.has("vicinity")) { + venue.address = object.getString("vicinity").trim(); + } else { + venue.address = String.format(Locale.US, "%f,%f", venue.geo.lat, venue.geo._long); + } + if (object.has("name")) { + venue.title = object.getString("name").trim(); + } + venue.venue_id = object.getString("place_id"); + venue.provider = "google"; + places.add(venue); + } catch (Exception e) { + FileLog.e("tmessages", e); + } + } + */ + JSONArray result = response.getJSONObject("response").getJSONArray("venues"); + + for (int a = 0; a < result.length(); a++) { + try { + JSONObject object = result.getJSONObject(a); + String iconUrl = null; + if (object.has("categories")) { + JSONArray categories = object.getJSONArray("categories"); + if (categories.length() > 0) { + JSONObject category = categories.getJSONObject(0); + if (category.has("icon")) { + JSONObject icon = category.getJSONObject("icon"); + iconUrl = String.format(Locale.US, "%s64%s", icon.getString("prefix"), icon.getString("suffix")); + } + } + } + iconUrls.add(iconUrl); + + JSONObject location = object.getJSONObject("location"); + TLRPC.TL_messageMediaVenue venue = new TLRPC.TL_messageMediaVenue(); + venue.geo = new TLRPC.TL_geoPoint(); + venue.geo.lat = location.getDouble("lat"); + venue.geo._long = location.getDouble("lng"); + if (location.has("address")) { + venue.address = location.getString("address"); + } else if (location.has("city")) { + venue.address = location.getString("city"); + } else if (location.has("state")) { + venue.address = location.getString("state"); + } else if (location.has("country")) { + venue.address = location.getString("country"); + } else { + venue.address = String.format(Locale.US, "%f,%f", venue.geo.lat, venue.geo._long); + } + if (object.has("name")) { + venue.title = object.getString("name"); + } + venue.venue_id = object.getString("id"); + venue.provider = "foursquare"; + places.add(venue); + } catch (Exception e) { + FileLog.e("tmessages", e); + } + } + } catch (Exception e) { + FileLog.e("tmessages", e); + } + searching = false; + notifyDataSetChanged(); + if (delegate != null) { + delegate.didLoadedSearchResult(places); + } + } + }, + new Response.ErrorListener() { + @Override + public void onErrorResponse(VolleyError error) { + FileLog.e("tmessages", "Error: " + error.getMessage()); + searching = false; + notifyDataSetChanged(); + if (delegate != null) { + delegate.didLoadedSearchResult(places); + } + } + }); + jsonObjReq.setTag("search"); + requestQueue.add(jsonObjReq); + } catch (Exception e) { + FileLog.e("tmessages", e); + searching = false; + if (delegate != null) { + delegate.didLoadedSearchResult(places); + } + } + notifyDataSetChanged(); + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Adapters/ChatActivityAdapter.java b/TMessagesProj/src/main/java/org/telegram/ui/Adapters/ChatActivityAdapter.java new file mode 100644 index 000000000..0fd986614 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Adapters/ChatActivityAdapter.java @@ -0,0 +1,396 @@ +/* + * This is the source code of Telegram for Android v. 2.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-2015. + */ + +package org.telegram.ui.Adapters; + +public class ChatActivityAdapter { + + /*private Context mContext; + + public ChatAdapter(Context context) { + mContext = context; + } + + @Override + public boolean areAllItemsEnabled() { + return true; + } + + @Override + public boolean isEnabled(int i) { + return true; + } + + @Override + public int getCount() { + int count = messages.size(); + if (count != 0) { + if (!endReached) { + count++; + } + if (!forward_end_reached) { + count++; + } + } + return count; + } + + @Override + public Object getItem(int i) { + return null; + } + + @Override + public long getItemId(int i) { + return i; + } + + @Override + public boolean hasStableIds() { + return true; + } + + @Override + public View getView(int i, View view, ViewGroup viewGroup) { + int offset = 1; + if ((!endReached || !forward_end_reached) && messages.size() != 0) { + if (!endReached) { + offset = 0; + } + if (i == 0 && !endReached || !forward_end_reached && i == (messages.size() + 1 - offset)) { + View progressBar = null; + if (view == null) { + LayoutInflater li = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE); + view = li.inflate(R.layout.chat_loading_layout, viewGroup, false); + progressBar = view.findViewById(R.id.progressLayout); + if (ApplicationLoader.isCustomTheme()) { + progressBar.setBackgroundResource(R.drawable.system_loader2); + } else { + progressBar.setBackgroundResource(R.drawable.system_loader1); + } + } else { + progressBar = view.findViewById(R.id.progressLayout); + } + progressBar.setVisibility(loadsCount > 1 ? View.VISIBLE : View.INVISIBLE); + + return view; + } + } + final MessageObject message = messages.get(messages.size() - i - offset); + int type = message.contentType; + if (view == null) { + if (type == 0) { + view = new ChatMessageCell(mContext); + } + if (type == 1) { + view = new ChatMediaCell(mContext); + } else if (type == 2) { + view = new ChatAudioCell(mContext); + } else if (type == 3) { + view = new ChatContactCell(mContext); + } else if (type == 6) { + LayoutInflater li = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE); + view = li.inflate(R.layout.chat_unread_layout, viewGroup, false); + } else if (type == 4) { + view = new ChatActionCell(mContext); + } + + if (view instanceof ChatBaseCell) { + ((ChatBaseCell) view).setDelegate(new ChatBaseCell.ChatBaseCellDelegate() { + @Override + public void didPressedUserAvatar(ChatBaseCell cell, TLRPC.User user) { + if (actionBar.isActionModeShowed()) { + processRowSelect(cell); + return; + } + if (user != null && user.id != UserConfig.getClientUserId()) { + Bundle args = new Bundle(); + args.putInt("user_id", user.id); + presentFragment(new ProfileActivity(args)); + } + } + + @Override + public void didPressedCancelSendButton(ChatBaseCell cell) { + MessageObject message = cell.getMessageObject(); + if (message.messageOwner.send_state != 0) { + SendMessagesHelper.getInstance().cancelSendingMessage(message); + } + } + + @Override + public void didLongPressed(ChatBaseCell cell) { + createMenu(cell, false); + } + + @Override + public boolean canPerformActions() { + return actionBar != null && !actionBar.isActionModeShowed(); + } + + @Override + public void didPressUrl(String url) { + if (url.startsWith("@")) { + openProfileWithUsername(url.substring(1)); + } else if (url.startsWith("#")) { + MessagesActivity fragment = new MessagesActivity(null); + fragment.setSearchString(url); + presentFragment(fragment); + } + } + + @Override + public void didPressReplyMessage(ChatBaseCell cell, int id) { + scrollToMessageId(id, cell.getMessageObject().getId(), true); + } + }); + if (view instanceof ChatMediaCell) { + ((ChatMediaCell) view).setAllowedToSetPhoto(openAnimationEnded); + ((ChatMediaCell) view).setMediaDelegate(new ChatMediaCell.ChatMediaCellDelegate() { + @Override + public void didClickedImage(ChatMediaCell cell) { + MessageObject message = cell.getMessageObject(); + if (message.isSendError()) { + createMenu(cell, false); + return; + } else if (message.isSending()) { + return; + } + if (message.type == 1) { + PhotoViewer.getInstance().setParentActivity(getParentActivity()); + PhotoViewer.getInstance().openPhoto(message, ChatActivity.this); + } else if (message.type == 3) { + sendSecretMessageRead(message); + try { + File f = null; + if (message.messageOwner.attachPath != null && message.messageOwner.attachPath.length() != 0) { + f = new File(message.messageOwner.attachPath); + } + if (f == null || f != null && !f.exists()) { + f = FileLoader.getPathToMessage(message.messageOwner); + } + Intent intent = new Intent(Intent.ACTION_VIEW); + intent.setDataAndType(Uri.fromFile(f), "video/mp4"); + getParentActivity().startActivityForResult(intent, 500); + } catch (Exception e) { + alertUserOpenError(message); + } + } else if (message.type == 4) { + if (!isGoogleMapsInstalled()) { + return; + } + LocationActivity fragment = new LocationActivity(); + fragment.setMessageObject(message); + presentFragment(fragment); + } else if (message.type == 9) { + File f = null; + String fileName = message.getFileName(); + if (message.messageOwner.attachPath != null && message.messageOwner.attachPath.length() != 0) { + f = new File(message.messageOwner.attachPath); + } + if (f == null || f != null && !f.exists()) { + f = FileLoader.getPathToMessage(message.messageOwner); + } + if (f != null && f.exists()) { + String realMimeType = null; + try { + Intent intent = new Intent(Intent.ACTION_VIEW); + if (message.type == 8 || message.type == 9) { + MimeTypeMap myMime = MimeTypeMap.getSingleton(); + int idx = fileName.lastIndexOf("."); + if (idx != -1) { + String ext = fileName.substring(idx + 1); + realMimeType = myMime.getMimeTypeFromExtension(ext.toLowerCase()); + if (realMimeType == null) { + realMimeType = message.messageOwner.media.document.mime_type; + if (realMimeType == null || realMimeType.length() == 0) { + realMimeType = null; + } + } + if (realMimeType != null) { + intent.setDataAndType(Uri.fromFile(f), realMimeType); + } else { + intent.setDataAndType(Uri.fromFile(f), "text/plain"); + } + } else { + intent.setDataAndType(Uri.fromFile(f), "text/plain"); + } + } + if (realMimeType != null) { + try { + getParentActivity().startActivityForResult(intent, 500); + } catch (Exception e) { + intent.setDataAndType(Uri.fromFile(f), "text/plain"); + getParentActivity().startActivityForResult(intent, 500); + } + } else { + getParentActivity().startActivityForResult(intent, 500); + } + } catch (Exception e) { + alertUserOpenError(message); + } + } + } + } + + @Override + public void didPressedOther(ChatMediaCell cell) { + createMenu(cell, true); + } + }); + } else if (view instanceof ChatContactCell) { + ((ChatContactCell) view).setContactDelegate(new ChatContactCell.ChatContactCellDelegate() { + @Override + public void didClickAddButton(ChatContactCell cell, TLRPC.User user) { + if (actionBar.isActionModeShowed()) { + processRowSelect(cell); + return; + } + MessageObject messageObject = cell.getMessageObject(); + Bundle args = new Bundle(); + args.putInt("user_id", messageObject.messageOwner.media.user_id); + args.putString("phone", messageObject.messageOwner.media.phone_number); + args.putBoolean("addContact", true); + presentFragment(new ContactAddActivity(args)); + } + + @Override + public void didClickPhone(ChatContactCell cell) { + if (actionBar.isActionModeShowed()) { + processRowSelect(cell); + return; + } + final MessageObject messageObject = cell.getMessageObject(); + if (getParentActivity() == null || messageObject.messageOwner.media.phone_number == null || messageObject.messageOwner.media.phone_number.length() == 0) { + return; + } + AlertDialog.Builder builder = new AlertDialog.Builder(getParentActivity()); + builder.setItems(new CharSequence[]{LocaleController.getString("Copy", R.string.Copy), LocaleController.getString("Call", R.string.Call)}, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialogInterface, int i) { + if (i == 1) { + try { + Intent intent = new Intent(Intent.ACTION_DIAL, Uri.parse("tel:" + messageObject.messageOwner.media.phone_number)); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + getParentActivity().startActivityForResult(intent, 500); + } catch (Exception e) { + FileLog.e("tmessages", e); + } + } else if (i == 0) { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) { + android.text.ClipboardManager clipboard = (android.text.ClipboardManager) ApplicationLoader.applicationContext.getSystemService(Context.CLIPBOARD_SERVICE); + clipboard.setText(messageObject.messageOwner.media.phone_number); + } else { + android.content.ClipboardManager clipboard = (android.content.ClipboardManager) ApplicationLoader.applicationContext.getSystemService(Context.CLIPBOARD_SERVICE); + android.content.ClipData clip = android.content.ClipData.newPlainText("label", messageObject.messageOwner.media.phone_number); + clipboard.setPrimaryClip(clip); + } + } + } + } + ); + showAlertDialog(builder); + } + }); + } + } else if (view instanceof ChatActionCell) { + ((ChatActionCell) view).setDelegate(new ChatActionCell.ChatActionCellDelegate() { + @Override + public void didClickedImage(ChatActionCell cell) { + MessageObject message = cell.getMessageObject(); + PhotoViewer.getInstance().setParentActivity(getParentActivity()); + PhotoViewer.getInstance().openPhoto(message, ChatActivity.this); + } + + @Override + public void didLongPressed(ChatActionCell cell) { + createMenu(cell, false); + } + + @Override + public void needOpenUserProfile(int uid) { + if (uid != UserConfig.getClientUserId()) { + Bundle args = new Bundle(); + args.putInt("user_id", uid); + presentFragment(new ProfileActivity(args)); + } + } + }); + } + } + + boolean selected = false; + boolean disableSelection = false; + if (actionBar.isActionModeShowed()) { + if (selectedMessagesIds.containsKey(message.getId())) { + view.setBackgroundColor(0x6633b5e5); + selected = true; + } else { + view.setBackgroundColor(0); + } + disableSelection = true; + } else { + view.setBackgroundColor(0); + } + + if (view instanceof ChatBaseCell) { + ChatBaseCell baseCell = (ChatBaseCell) view; + baseCell.isChat = currentChat != null; + baseCell.setMessageObject(message); + baseCell.setCheckPressed(!disableSelection, disableSelection && selected); + if (view instanceof ChatAudioCell && MediaController.getInstance().canDownloadMedia(MediaController.AUTODOWNLOAD_MASK_AUDIO)) { + ((ChatAudioCell) view).downloadAudioIfNeed(); + } + baseCell.setHighlighted(highlightMessageId != Integer.MAX_VALUE && message.getId() == highlightMessageId); + } else if (view instanceof ChatActionCell) { + ChatActionCell actionCell = (ChatActionCell) view; + actionCell.setMessageObject(message); + } + if (type == 6) { + TextView messageTextView = (TextView) view.findViewById(R.id.chat_message_text); + messageTextView.setText(LocaleController.formatPluralString("NewMessages", unread_to_load)); + } + + return view; + } + + @Override + public int getItemViewType(int i) { + int offset = 1; + if (!endReached && messages.size() != 0) { + offset = 0; + if (i == 0) { + return 5; + } + } + if (!forward_end_reached && i == (messages.size() + 1 - offset)) { + return 5; + } + MessageObject message = messages.get(messages.size() - i - offset); + return message.contentType; + } + + @Override + public int getViewTypeCount() { + return 7; + } + + @Override + public boolean isEmpty() { + int count = messages.size(); + if (count != 0) { + if (!endReached) { + count++; + } + if (!forward_end_reached) { + count++; + } + } + return count == 0; + }*/ +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Adapters/ContactsAdapter.java b/TMessagesProj/src/main/java/org/telegram/ui/Adapters/ContactsAdapter.java index cd50fac42..147ff456b 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Adapters/ContactsAdapter.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Adapters/ContactsAdapter.java @@ -19,7 +19,7 @@ import org.telegram.messenger.TLRPC; import org.telegram.android.ContactsController; import org.telegram.android.MessagesController; import org.telegram.messenger.R; -import org.telegram.ui.AnimationCompat.ViewProxy; +import org.telegram.android.AnimationCompat.ViewProxy; import org.telegram.ui.Cells.DividerCell; import org.telegram.ui.Cells.GreySectionCell; import org.telegram.ui.Cells.LetterSectionCell; @@ -37,12 +37,14 @@ public class ContactsAdapter extends BaseSectionsAdapter { private HashMap ignoreUsers; private HashMap checkedMap; private boolean scrolling; + private boolean isAdmin; - public ContactsAdapter(Context context, boolean arg1, boolean arg2, HashMap arg3) { + public ContactsAdapter(Context context, boolean arg1, boolean arg2, HashMap arg3, boolean arg4) { mContext = context; onlyUsers = arg1; needPhonebook = arg2; ignoreUsers = arg3; + isAdmin = arg4; } public void setCheckedMap(HashMap map) { @@ -55,7 +57,7 @@ public class ContactsAdapter extends BaseSectionsAdapter { @Override public Object getItem(int section, int position) { - if (onlyUsers) { + if (onlyUsers && !isAdmin) { if (section < ContactsController.getInstance().sortedUsersSectionsArray.size()) { ArrayList arr = ContactsController.getInstance().usersSectionsDict.get(ContactsController.getInstance().sortedUsersSectionsArray.get(section)); if (position < arr.size()) { @@ -84,12 +86,12 @@ public class ContactsAdapter extends BaseSectionsAdapter { @Override public boolean isRowEnabled(int section, int row) { - if (onlyUsers) { + if (onlyUsers && !isAdmin) { ArrayList arr = ContactsController.getInstance().usersSectionsDict.get(ContactsController.getInstance().sortedUsersSectionsArray.get(section)); return row < arr.size(); } else { if (section == 0) { - if (needPhonebook) { + if (needPhonebook || isAdmin) { if (row == 1) { return false; } @@ -113,6 +115,9 @@ public class ContactsAdapter extends BaseSectionsAdapter { if (!onlyUsers) { count++; } + if (isAdmin) { + count++; + } if (needPhonebook) { count++; } @@ -121,7 +126,7 @@ public class ContactsAdapter extends BaseSectionsAdapter { @Override public int getCountForSection(int section) { - if (onlyUsers) { + if (onlyUsers && !isAdmin) { if (section < ContactsController.getInstance().sortedUsersSectionsArray.size()) { ArrayList arr = ContactsController.getInstance().usersSectionsDict.get(ContactsController.getInstance().sortedUsersSectionsArray.get(section)); int count = arr.size(); @@ -132,7 +137,7 @@ public class ContactsAdapter extends BaseSectionsAdapter { } } else { if (section == 0) { - if (needPhonebook) { + if (needPhonebook || isAdmin) { return 2; } else { return 4; @@ -157,7 +162,7 @@ public class ContactsAdapter extends BaseSectionsAdapter { if (convertView == null) { convertView = new LetterSectionCell(mContext); } - if (onlyUsers) { + if (onlyUsers && !isAdmin) { if (section < ContactsController.getInstance().sortedUsersSectionsArray.size()) { ((LetterSectionCell) convertView).setLetter(ContactsController.getInstance().sortedUsersSectionsArray.get(section)); } else { @@ -195,6 +200,8 @@ public class ContactsAdapter extends BaseSectionsAdapter { TextCell actionCell = (TextCell) convertView; if (needPhonebook) { actionCell.setTextAndIcon(LocaleController.getString("InviteFriends", R.string.InviteFriends), R.drawable.menu_invite); + } else if (isAdmin) { + actionCell.setTextAndIcon(LocaleController.getString("InviteToGroupByLink", R.string.InviteToGroupByLink), R.drawable.menu_invite); } else { if (position == 0) { actionCell.setTextAndIcon(LocaleController.getString("NewGroup", R.string.NewGroup), R.drawable.menu_newgroup); @@ -222,7 +229,7 @@ public class ContactsAdapter extends BaseSectionsAdapter { ((UserCell) convertView).setStatusColors(0xffa8a8a8, 0xff3b84c0); } - ArrayList arr = ContactsController.getInstance().usersSectionsDict.get(ContactsController.getInstance().sortedUsersSectionsArray.get(section - (onlyUsers ? 0 : 1))); + ArrayList arr = ContactsController.getInstance().usersSectionsDict.get(ContactsController.getInstance().sortedUsersSectionsArray.get(section - (onlyUsers && !isAdmin ? 0 : 1))); TLRPC.User user = MessagesController.getInstance().getUser(arr.get(position).user_id); ((UserCell)convertView).setData(user, null, null, 0); if (checkedMap != null) { @@ -241,12 +248,12 @@ public class ContactsAdapter extends BaseSectionsAdapter { @Override public int getItemViewType(int section, int position) { - if (onlyUsers) { + if (onlyUsers && !isAdmin) { ArrayList arr = ContactsController.getInstance().usersSectionsDict.get(ContactsController.getInstance().sortedUsersSectionsArray.get(section)); return position < arr.size() ? 0 : 4; } else { if (section == 0) { - if (needPhonebook) { + if (needPhonebook || isAdmin) { if (position == 1) { return 3; } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Adapters/DialogsSearchAdapter.java b/TMessagesProj/src/main/java/org/telegram/ui/Adapters/DialogsSearchAdapter.java index 9a426aeb2..03497da97 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Adapters/DialogsSearchAdapter.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Adapters/DialogsSearchAdapter.java @@ -25,10 +25,8 @@ import org.telegram.messenger.ConnectionsManager; import org.telegram.messenger.FileLog; import org.telegram.messenger.R; import org.telegram.messenger.RPCRequest; -import org.telegram.messenger.TLClassStore; import org.telegram.messenger.TLObject; import org.telegram.messenger.TLRPC; -import org.telegram.messenger.UserConfig; import org.telegram.messenger.Utilities; import org.telegram.ui.Cells.DialogCell; import org.telegram.ui.Cells.GreySectionCell; @@ -242,20 +240,18 @@ public class DialogsSearchAdapter extends BaseSearchAdapter { if (found != 0) { ByteBufferDesc data = MessagesStorage.getInstance().getBuffersStorage().getFreeBuffer(cursor.byteArrayLength(0)); if (data != null && cursor.byteBufferValue(0, data.buffer) != 0) { - TLRPC.User user = (TLRPC.User) TLClassStore.Instance().TLdeserialize(data, data.readInt32()); - if (user.id != UserConfig.getClientUserId()) { - DialogSearchResult dialogSearchResult = dialogsResult.get((long) user.id); - if (user.status != null) { - user.status.expires = cursor.intValue(1); - } - if (found == 1) { - dialogSearchResult.name = Utilities.generateSearchName(user.first_name, user.last_name, q); - } else { - dialogSearchResult.name = Utilities.generateSearchName("@" + user.username, null, "@" + q); - } - dialogSearchResult.object = user; - resultCount++; + TLRPC.User user = TLRPC.User.TLdeserialize(data, data.readInt32(false), false); + DialogSearchResult dialogSearchResult = dialogsResult.get((long) user.id); + if (user.status != null) { + user.status.expires = cursor.intValue(1); } + if (found == 1) { + dialogSearchResult.name = Utilities.generateSearchName(user.first_name, user.last_name, q); + } else { + dialogSearchResult.name = Utilities.generateSearchName("@" + user.username, null, "@" + q); + } + dialogSearchResult.object = user; + resultCount++; } MessagesStorage.getInstance().getBuffersStorage().reuseFreeBuffer(data); break; @@ -277,7 +273,7 @@ public class DialogsSearchAdapter extends BaseSearchAdapter { if (name.startsWith(q) || name.contains(" " + q) || tName != null && (tName.startsWith(q) || tName.contains(" " + q))) { ByteBufferDesc data = MessagesStorage.getInstance().getBuffersStorage().getFreeBuffer(cursor.byteArrayLength(0)); if (data != null && cursor.byteBufferValue(0, data.buffer) != 0) { - TLRPC.Chat chat = (TLRPC.Chat) TLClassStore.Instance().TLdeserialize(data, data.readInt32()); + TLRPC.Chat chat = TLRPC.Chat.TLdeserialize(data, data.readInt32(false), false); long dialog_id; if (chat.id > 0) { dialog_id = -chat.id; @@ -323,7 +319,7 @@ public class DialogsSearchAdapter extends BaseSearchAdapter { ByteBufferDesc data = MessagesStorage.getInstance().getBuffersStorage().getFreeBuffer(cursor.byteArrayLength(0)); ByteBufferDesc data2 = MessagesStorage.getInstance().getBuffersStorage().getFreeBuffer(cursor.byteArrayLength(6)); if (data != null && cursor.byteBufferValue(0, data.buffer) != 0 && cursor.byteBufferValue(6, data2.buffer) != 0) { - TLRPC.EncryptedChat chat = (TLRPC.EncryptedChat) TLClassStore.Instance().TLdeserialize(data, data.readInt32()); + TLRPC.EncryptedChat chat = TLRPC.EncryptedChat.TLdeserialize(data, data.readInt32(false), false); DialogSearchResult dialogSearchResult = dialogsResult.get((long) chat.id << 32); chat.user_id = cursor.intValue(2); @@ -342,7 +338,7 @@ public class DialogsSearchAdapter extends BaseSearchAdapter { chat.future_auth_key = cursor.byteArrayValue(15); chat.key_hash = cursor.byteArrayValue(16); - TLRPC.User user = (TLRPC.User) TLClassStore.Instance().TLdeserialize(data2, data2.readInt32()); + TLRPC.User user = TLRPC.User.TLdeserialize(data2, data2.readInt32(false), false); if (user.status != null) { user.status.expires = cursor.intValue(7); } @@ -417,18 +413,16 @@ public class DialogsSearchAdapter extends BaseSearchAdapter { if (found != 0) { ByteBufferDesc data = MessagesStorage.getInstance().getBuffersStorage().getFreeBuffer(cursor.byteArrayLength(0)); if (data != null && cursor.byteBufferValue(0, data.buffer) != 0) { - TLRPC.User user = (TLRPC.User) TLClassStore.Instance().TLdeserialize(data, data.readInt32()); - if (user.id != UserConfig.getClientUserId()) { - if (user.status != null) { - user.status.expires = cursor.intValue(1); - } - if (found == 1) { - resultArrayNames.add(Utilities.generateSearchName(user.first_name, user.last_name, q)); - } else { - resultArrayNames.add(Utilities.generateSearchName("@" + user.username, null, "@" + q)); - } - resultArray.add(user); + TLRPC.User user = TLRPC.User.TLdeserialize(data, data.readInt32(false), false); + if (user.status != null) { + user.status.expires = cursor.intValue(1); } + if (found == 1) { + resultArrayNames.add(Utilities.generateSearchName(user.first_name, user.last_name, q)); + } else { + resultArrayNames.add(Utilities.generateSearchName("@" + user.username, null, "@" + q)); + } + resultArray.add(user); } MessagesStorage.getInstance().getBuffersStorage().reuseFreeBuffer(data); break; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Adapters/LocationActivityAdapter.java b/TMessagesProj/src/main/java/org/telegram/ui/Adapters/LocationActivityAdapter.java new file mode 100644 index 000000000..73d7686c1 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Adapters/LocationActivityAdapter.java @@ -0,0 +1,167 @@ +/* + * This is the source code of Telegram for Android v. 2.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-2015. + */ + +package org.telegram.ui.Adapters; + +import android.content.Context; +import android.location.Location; +import android.view.View; +import android.view.ViewGroup; + +import org.telegram.android.LocaleController; +import org.telegram.messenger.R; +import org.telegram.messenger.TLRPC; +import org.telegram.ui.Cells.EmptyCell; +import org.telegram.ui.Cells.GreySectionCell; +import org.telegram.ui.Cells.LocationCell; +import org.telegram.ui.Cells.LocationLoadingCell; +import org.telegram.ui.Cells.LocationPoweredCell; +import org.telegram.ui.Cells.SendLocationCell; + +import java.util.Locale; + +public class LocationActivityAdapter extends BaseLocationAdapter { + + private Context mContext; + private int overScrollHeight; + private SendLocationCell sendLocationCell; + private Location gpsLocation; + private Location customLocation; + + public LocationActivityAdapter(Context context) { + super(); + mContext = context; + } + + public void setOverScrollHeight(int value) { + overScrollHeight = value; + } + + public void setGpsLocation(Location location) { + gpsLocation = location; + updateCell(); + } + + public void setCustomLocation(Location location) { + customLocation = location; + updateCell(); + } + + private void updateCell() { + if (sendLocationCell != null) { + if (customLocation != null) { + sendLocationCell.setText(LocaleController.getString("SendSelectedLocation", R.string.SendSelectedLocation), String.format(Locale.US, "(%f,%f)", customLocation.getLatitude(), customLocation.getLongitude())); + } else { + if (gpsLocation != null) { + sendLocationCell.setText(LocaleController.getString("SendLocation", R.string.SendLocation), LocaleController.formatString("AccurateTo", R.string.AccurateTo, LocaleController.formatPluralString("Meters", (int) gpsLocation.getAccuracy()))); + } else { + sendLocationCell.setText(LocaleController.getString("SendLocation", R.string.SendLocation), LocaleController.getString("Loading", R.string.Loading)); + } + } + } + } + + @Override + public int getCount() { + if (searching || !searching && places.isEmpty()) { + return 4; + } + return 3 + places.size() + (places.isEmpty() ? 0 : 1); + } + + @Override + public boolean isEmpty() { + return false; + } + + @Override + public View getView(int i, View view, ViewGroup viewGroup) { + if (i == 0) { + if (view == null) { + view = new EmptyCell(mContext); + } + ((EmptyCell) view).setHeight(overScrollHeight); + } else if (i == 1) { + if (view == null) { + view = new SendLocationCell(mContext); + } + sendLocationCell = (SendLocationCell) view; + updateCell(); + return view; + } else if (i == 2) { + if (view == null) { + view = new GreySectionCell(mContext); + } + ((GreySectionCell) view).setText(LocaleController.getString("NearbyPlaces", R.string.NearbyPlaces)); + } else if (searching || !searching && places.isEmpty()) { + if (view == null) { + view = new LocationLoadingCell(mContext); + } + ((LocationLoadingCell) view).setLoading(searching); + } else if (i == places.size() + 3) { + if (view == null) { + view = new LocationPoweredCell(mContext); + } + } else { + if (view == null) { + view = new LocationCell(mContext); + } + ((LocationCell) view).setLocation(places.get(i - 3), iconUrls.get(i - 3), true); + } + return view; + } + + @Override + public TLRPC.TL_messageMediaVenue getItem(int i) { + if (i > 2 && i < places.size() + 3) { + return places.get(i - 3); + } + return null; + } + + @Override + public long getItemId(int i) { + return i; + } + + @Override + public int getItemViewType(int position) { + if (position == 0) { + return 0; + } else if (position == 1) { + return 1; + } else if (position == 2) { + return 2; + } else if (searching || !searching && places.isEmpty()) { + return 4; + } else if (position == places.size() + 3) { + return 5; + } + return 3; + } + + @Override + public int getViewTypeCount() { + return 6; + } + + @Override + public boolean areAllItemsEnabled() { + return false; + } + + @Override + public boolean isEnabled(int position) { + return !(position == 2 || position == 0 || position == 3 && (searching || !searching && places.isEmpty()) || position == places.size() + 3); + } + + @Override + public boolean hasStableIds() { + return true; + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Adapters/LocationActivitySearchAdapter.java b/TMessagesProj/src/main/java/org/telegram/ui/Adapters/LocationActivitySearchAdapter.java new file mode 100644 index 000000000..2da45562e --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Adapters/LocationActivitySearchAdapter.java @@ -0,0 +1,83 @@ +/* + * This is the source code of Telegram for Android v. 2.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-2015. + */ + +package org.telegram.ui.Adapters; + +import android.content.Context; +import android.view.View; +import android.view.ViewGroup; + +import org.telegram.messenger.TLRPC; +import org.telegram.ui.Cells.LocationCell; + +public class LocationActivitySearchAdapter extends BaseLocationAdapter { + + private Context mContext; + + public LocationActivitySearchAdapter(Context context) { + super(); + mContext = context; + } + + @Override + public int getCount() { + return places.size(); + } + + @Override + public boolean isEmpty() { + return places.isEmpty(); + } + + @Override + public View getView(int i, View view, ViewGroup viewGroup) { + if (view == null) { + view = new LocationCell(mContext); + } + ((LocationCell) view).setLocation(places.get(i), iconUrls.get(i), i != places.size() - 1); + return view; + } + + @Override + public TLRPC.TL_messageMediaVenue getItem(int i) { + if (i >= 0 && i < places.size()) { + return places.get(i); + } + return null; + } + + @Override + public long getItemId(int i) { + return i; + } + + @Override + public int getItemViewType(int position) { + return 0; + } + + @Override + public int getViewTypeCount() { + return 4; + } + + @Override + public boolean areAllItemsEnabled() { + return false; + } + + @Override + public boolean isEnabled(int position) { + return true; + } + + @Override + public boolean hasStableIds() { + return true; + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Adapters/MentionsAdapter.java b/TMessagesProj/src/main/java/org/telegram/ui/Adapters/MentionsAdapter.java index 6c500386e..312038997 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Adapters/MentionsAdapter.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Adapters/MentionsAdapter.java @@ -39,10 +39,12 @@ public class MentionsAdapter extends BaseSearchAdapter { private int lastPosition; private ArrayList messages; private boolean needUsernames = true; + private boolean isDarkTheme; - public MentionsAdapter(Context context, MentionsAdapterDelegate delegate) { + public MentionsAdapter(Context context, boolean isDarkTheme, MentionsAdapterDelegate delegate) { mContext = context; this.delegate = delegate; + this.isDarkTheme = isDarkTheme; } public void setChatInfo(TLRPC.ChatParticipants chatParticipants) { @@ -261,6 +263,7 @@ public class MentionsAdapter extends BaseSearchAdapter { public View getView(int i, View view, ViewGroup viewGroup) { if (view == null) { view = new MentionCell(mContext); + ((MentionCell) view).setIsDarkTheme(isDarkTheme); } if (searchResultUsernames != null) { ((MentionCell) view).setUser(searchResultUsernames.get(i)); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Adapters/StickersAdapter.java b/TMessagesProj/src/main/java/org/telegram/ui/Adapters/StickersAdapter.java index ef6f61c0f..2acda7a59 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Adapters/StickersAdapter.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Adapters/StickersAdapter.java @@ -9,7 +9,6 @@ package org.telegram.ui.Adapters; import android.content.Context; -import android.support.v7.widget.RecyclerView; import android.view.View; import android.view.ViewGroup; @@ -18,12 +17,12 @@ import org.telegram.SQLite.SQLitePreparedStatement; import org.telegram.android.AndroidUtilities; import org.telegram.android.MessagesStorage; import org.telegram.android.NotificationCenter; +import org.telegram.android.support.widget.RecyclerView; import org.telegram.messenger.ByteBufferDesc; import org.telegram.messenger.ConnectionsManager; import org.telegram.messenger.FileLoader; import org.telegram.messenger.FileLog; import org.telegram.messenger.RPCRequest; -import org.telegram.messenger.TLClassStore; import org.telegram.messenger.TLObject; import org.telegram.messenger.TLRPC; import org.telegram.messenger.Utilities; @@ -108,7 +107,7 @@ public class StickersAdapter extends RecyclerView.Adapter implements Notificatio if (cursor.next()) { ByteBufferDesc data = MessagesStorage.getInstance().getBuffersStorage().getFreeBuffer(cursor.byteArrayLength(0)); if (data != null && cursor.byteBufferValue(0, data.buffer) != 0) { - result = (TLRPC.messages_AllStickers) TLClassStore.Instance().TLdeserialize(data, data.readInt32()); + result = TLRPC.messages_AllStickers.TLdeserialize(data, data.readInt32(false), false); } date = cursor.intValue(1); MessagesStorage.getInstance().getBuffersStorage().reuseFreeBuffer(data); @@ -301,7 +300,6 @@ public class StickersAdapter extends RecyclerView.Adapter implements Notificatio @Override public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int i) { - Holder holder = (Holder) viewHolder; int side = 0; if (i == 0) { if (stickers.size() == 1) { @@ -312,6 +310,6 @@ public class StickersAdapter extends RecyclerView.Adapter implements Notificatio } else if (i == stickers.size() - 1) { side = 1; } - ((StickerCell) holder.itemView).setSticker(stickers.get(i), side); + ((StickerCell) viewHolder.itemView).setSticker(stickers.get(i), side); } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/BlockedUsersActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/BlockedUsersActivity.java index 5982e413b..b0c4d1434 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/BlockedUsersActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/BlockedUsersActivity.java @@ -36,6 +36,7 @@ import org.telegram.ui.Cells.UserCell; import org.telegram.ui.ActionBar.ActionBar; import org.telegram.ui.ActionBar.ActionBarMenu; import org.telegram.ui.ActionBar.BaseFragment; +import org.telegram.ui.Components.LayoutHelper; public class BlockedUsersActivity extends BaseFragment implements NotificationCenter.NotificationCenterDelegate, ContactsActivity.ContactsActivityDelegate { @@ -99,8 +100,8 @@ public class BlockedUsersActivity extends BaseFragment implements NotificationCe emptyTextView.setText(LocaleController.getString("NoBlocked", R.string.NoBlocked)); frameLayout.addView(emptyTextView); FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) emptyTextView.getLayoutParams(); - layoutParams.width = FrameLayout.LayoutParams.MATCH_PARENT; - layoutParams.height = FrameLayout.LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; + layoutParams.height = LayoutHelper.MATCH_PARENT; layoutParams.gravity = Gravity.TOP; emptyTextView.setLayoutParams(layoutParams); emptyTextView.setOnTouchListener(new View.OnTouchListener() { @@ -113,15 +114,15 @@ public class BlockedUsersActivity extends BaseFragment implements NotificationCe progressView = new FrameLayout(context); frameLayout.addView(progressView); layoutParams = (FrameLayout.LayoutParams) progressView.getLayoutParams(); - layoutParams.width = FrameLayout.LayoutParams.MATCH_PARENT; - layoutParams.height = FrameLayout.LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; + layoutParams.height = LayoutHelper.MATCH_PARENT; progressView.setLayoutParams(layoutParams); ProgressBar progressBar = new ProgressBar(context); progressView.addView(progressBar); layoutParams = (FrameLayout.LayoutParams) progressView.getLayoutParams(); - layoutParams.width = FrameLayout.LayoutParams.WRAP_CONTENT; - layoutParams.height = FrameLayout.LayoutParams.WRAP_CONTENT; + layoutParams.width = LayoutHelper.WRAP_CONTENT; + layoutParams.height = LayoutHelper.WRAP_CONTENT; layoutParams.gravity = Gravity.CENTER; progressView.setLayoutParams(layoutParams); @@ -136,8 +137,8 @@ public class BlockedUsersActivity extends BaseFragment implements NotificationCe } frameLayout.addView(listView); layoutParams = (FrameLayout.LayoutParams) listView.getLayoutParams(); - layoutParams.width = FrameLayout.LayoutParams.MATCH_PARENT; - layoutParams.height = FrameLayout.LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; + layoutParams.height = LayoutHelper.MATCH_PARENT; listView.setLayoutParams(layoutParams); listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/AddMemberCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/AddMemberCell.java new file mode 100644 index 000000000..a4cff7ad0 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/AddMemberCell.java @@ -0,0 +1,44 @@ +/* + * This is the source code of Telegram for Android v. 2.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-2015. + */ + +package org.telegram.ui.Cells; + +import android.content.Context; +import android.view.Gravity; +import android.widget.FrameLayout; +import android.widget.ImageView; + +import org.telegram.android.AndroidUtilities; +import org.telegram.android.LocaleController; +import org.telegram.messenger.R; +import org.telegram.ui.Components.LayoutHelper; +import org.telegram.ui.Components.SimpleTextView; + +public class AddMemberCell extends FrameLayout { + + public AddMemberCell(Context context) { + super(context); + + ImageView imageView = new ImageView(context); + imageView.setImageResource(R.drawable.addmember); + imageView.setScaleType(ImageView.ScaleType.CENTER); + addView(imageView, LayoutHelper.createFrame(48, 48, (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP, LocaleController.isRTL ? 0 : 68, 8, LocaleController.isRTL ? 68 : 0, 0)); + + SimpleTextView textView = new SimpleTextView(context); + textView.setTextColor(0xff212121); + textView.setTextSize(17); + textView.setText(LocaleController.getString("AddMember", R.string.AddMember)); + textView.setGravity((LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP); + addView(textView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 20, (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP, LocaleController.isRTL ? 28 : 129, 22.5f, LocaleController.isRTL ? 129 : 28, 0)); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(64), MeasureSpec.EXACTLY)); + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatActionCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatActionCell.java index 2fcaed609..ce31697b2 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatActionCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatActionCell.java @@ -28,9 +28,9 @@ import org.telegram.android.MessagesController; import org.telegram.messenger.ApplicationLoader; import org.telegram.messenger.FileLoader; import org.telegram.messenger.FileLog; -import org.telegram.messenger.R; import org.telegram.messenger.TLRPC; import org.telegram.messenger.UserConfig; +import org.telegram.ui.Components.ResourceLoader; import org.telegram.ui.PhotoViewer; import org.telegram.ui.Components.AvatarDrawable; @@ -42,8 +42,6 @@ public class ChatActionCell extends BaseCell { void needOpenUserProfile(int uid); } - private static Drawable backgroundBlack; - private static Drawable backgroundBlue; private static TextPaint textPaint; private URLSpan pressedLink; @@ -65,10 +63,7 @@ public class ChatActionCell extends BaseCell { public ChatActionCell(Context context) { super(context); - if (backgroundBlack == null) { - backgroundBlack = getResources().getDrawable(R.drawable.system_black); - backgroundBlue = getResources().getDrawable(R.drawable.system_blue); - + if (textPaint == null) { textPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG); textPaint.setColor(0xffffffff); textPaint.linkColor = 0xffffffff; @@ -264,9 +259,9 @@ public class ChatActionCell extends BaseCell { Drawable backgroundDrawable = null; if (ApplicationLoader.isCustomTheme()) { - backgroundDrawable = backgroundBlack; + backgroundDrawable = ResourceLoader.backgroundBlack; } else { - backgroundDrawable = backgroundBlue; + backgroundDrawable = ResourceLoader.backgroundBlue; } backgroundDrawable.setBounds(textX - AndroidUtilities.dp(5), AndroidUtilities.dp(5), textX + textWidth + AndroidUtilities.dp(5), AndroidUtilities.dp(9) + textHeight); backgroundDrawable.draw(canvas); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatAudioCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatAudioCell.java index 7a3f1a6b4..09bc6fcd1 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatAudioCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatAudioCell.java @@ -10,6 +10,7 @@ package org.telegram.ui.Cells; import android.content.Context; import android.graphics.Canvas; +import android.graphics.Paint; import android.graphics.drawable.Drawable; import android.text.Layout; import android.text.StaticLayout; @@ -19,27 +20,21 @@ import android.view.SoundEffectConstants; import org.telegram.android.AndroidUtilities; import org.telegram.android.ImageLoader; +import org.telegram.android.MessagesController; import org.telegram.messenger.FileLoader; import org.telegram.android.MediaController; -import org.telegram.messenger.TLRPC; -import org.telegram.android.MessagesController; -import org.telegram.messenger.R; import org.telegram.android.MessageObject; -import org.telegram.android.ImageReceiver; -import org.telegram.ui.Components.AvatarDrawable; import org.telegram.ui.Components.ProgressView; +import org.telegram.ui.Components.ResourceLoader; import org.telegram.ui.Components.SeekBar; import java.io.File; public class ChatAudioCell extends ChatBaseCell implements SeekBar.SeekBarDelegate, MediaController.FileDownloadProgressListener { - private static Drawable[][] statesDrawable = new Drawable[8][2]; private static TextPaint timePaint; + private static Paint circlePaint; - private ImageReceiver avatarImage; - private AvatarDrawable avatarDrawable; - private boolean needAvatarImage = false; private SeekBar seekBar; private ProgressView progressView; private int seekBarX; @@ -50,62 +45,42 @@ public class ChatAudioCell extends ChatBaseCell implements SeekBar.SeekBarDelega private int buttonY; private boolean buttonPressed = false; - private boolean avatarPressed = false; - private StaticLayout timeLayout; private int timeX; + private int timeWidth; private String lastTimeString = null; private int TAG; - public TLRPC.User audioUser; - private TLRPC.FileLocation currentPhoto; - public ChatAudioCell(Context context) { super(context); TAG = MediaController.getInstance().generateObserverTag(); - avatarImage = new ImageReceiver(this); - avatarImage.setRoundRadius(AndroidUtilities.dp(25)); seekBar = new SeekBar(context); seekBar.delegate = this; progressView = new ProgressView(); - avatarDrawable = new AvatarDrawable(); + drawForwardedName = true; if (timePaint == null) { - statesDrawable[0][0] = getResources().getDrawable(R.drawable.play1); - statesDrawable[0][1] = getResources().getDrawable(R.drawable.play1_pressed); - statesDrawable[1][0] = getResources().getDrawable(R.drawable.pause1); - statesDrawable[1][1] = getResources().getDrawable(R.drawable.pause1_pressed); - statesDrawable[2][0] = getResources().getDrawable(R.drawable.audioload1); - statesDrawable[2][1] = getResources().getDrawable(R.drawable.audioload1_pressed); - statesDrawable[3][0] = getResources().getDrawable(R.drawable.audiocancel1); - statesDrawable[3][1] = getResources().getDrawable(R.drawable.audiocancel1_pressed); - - statesDrawable[4][0] = getResources().getDrawable(R.drawable.play2); - statesDrawable[4][1] = getResources().getDrawable(R.drawable.play2_pressed); - statesDrawable[5][0] = getResources().getDrawable(R.drawable.pause2); - statesDrawable[5][1] = getResources().getDrawable(R.drawable.pause2_pressed); - statesDrawable[6][0] = getResources().getDrawable(R.drawable.audioload2); - statesDrawable[6][1] = getResources().getDrawable(R.drawable.audioload2_pressed); - statesDrawable[7][0] = getResources().getDrawable(R.drawable.audiocancel2); - statesDrawable[7][1] = getResources().getDrawable(R.drawable.audiocancel2_pressed); - timePaint = new TextPaint(TextPaint.ANTI_ALIAS_FLAG); timePaint.setTextSize(AndroidUtilities.dp(12)); + + circlePaint = new Paint(Paint.ANTI_ALIAS_FLAG); } } @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); - if (avatarImage != null) { - avatarImage.clearImage(); - currentPhoto = null; - } MediaController.getInstance().removeLoadingFileObserver(this); } + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + updateButtonState(); + } + @Override public boolean onTouchEvent(MotionEvent event) { float x = event.getX(); @@ -123,9 +98,6 @@ public class ChatAudioCell extends ChatBaseCell implements SeekBar.SeekBarDelega buttonPressed = true; invalidate(); result = true; - } else if (needAvatarImage && avatarImage.isInsideImage(x, y)) { - avatarPressed = true; - result = true; } } else if (buttonPressed) { if (event.getAction() == MotionEvent.ACTION_UP) { @@ -142,20 +114,6 @@ public class ChatAudioCell extends ChatBaseCell implements SeekBar.SeekBarDelega invalidate(); } } - } else if (avatarPressed) { - if (event.getAction() == MotionEvent.ACTION_UP) { - avatarPressed = false; - playSoundEffect(SoundEffectConstants.CLICK); - if (delegate != null) { - delegate.didPressedUserAvatar(this, audioUser); - } - } else if (event.getAction() == MotionEvent.ACTION_CANCEL) { - avatarPressed = false; - } else if (event.getAction() == MotionEvent.ACTION_MOVE) { - if (!avatarImage.isInsideImage(x, y)) { - avatarPressed = false; - } - } } if (!result) { result = super.onTouchEvent(event); @@ -168,6 +126,9 @@ public class ChatAudioCell extends ChatBaseCell implements SeekBar.SeekBarDelega private void didPressedButton() { if (buttonState == 0) { boolean result = MediaController.getInstance().playAudio(currentMessageObject); + if (!currentMessageObject.isOut() && currentMessageObject.isContentUnread()) { + MessagesController.getInstance().markMessageContentAsRead(currentMessageObject.getId()); + } if (result) { buttonState = 1; invalidate(); @@ -212,7 +173,7 @@ public class ChatAudioCell extends ChatBaseCell implements SeekBar.SeekBarDelega } String timeString = String.format("%02d:%02d", duration / 60, duration % 60); if (lastTimeString == null || lastTimeString != null && !lastTimeString.equals(timeString)) { - int timeWidth = (int)Math.ceil(timePaint.measureText(timeString)); + timeWidth = (int)Math.ceil(timePaint.measureText(timeString)); timeLayout = new StaticLayout(timeString, timePaint, timeWidth, Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false); } invalidate(); @@ -227,29 +188,36 @@ public class ChatAudioCell extends ChatBaseCell implements SeekBar.SeekBarDelega } public void updateButtonState() { - String fileName = currentMessageObject.getFileName(); - File cacheFile = FileLoader.getPathToMessage(currentMessageObject.messageOwner); - if (cacheFile.exists()) { - MediaController.getInstance().removeLoadingFileObserver(this); - boolean playing = MediaController.getInstance().isPlayingAudio(currentMessageObject); - if (!playing || playing && MediaController.getInstance().isAudioPaused()) { - buttonState = 0; - } else { - buttonState = 1; - } - progressView.setProgress(0); + if (currentMessageObject == null) { + return; + } + if (currentMessageObject.isOut() && currentMessageObject.isSending()) { + buttonState = 4; } else { - MediaController.getInstance().addLoadingFileObserver(fileName, this); - if (!FileLoader.getInstance().isLoadingFile(fileName)) { - buttonState = 2; + String fileName = currentMessageObject.getFileName(); + File cacheFile = FileLoader.getPathToMessage(currentMessageObject.messageOwner); + if (cacheFile.exists()) { + MediaController.getInstance().removeLoadingFileObserver(this); + boolean playing = MediaController.getInstance().isPlayingAudio(currentMessageObject); + if (!playing || playing && MediaController.getInstance().isAudioPaused()) { + buttonState = 0; + } else { + buttonState = 1; + } progressView.setProgress(0); } else { - buttonState = 3; - Float progress = ImageLoader.getInstance().getFileProgress(fileName); - if (progress != null) { - progressView.setProgress(progress); - } else { + MediaController.getInstance().addLoadingFileObserver(fileName, this); + if (!FileLoader.getInstance().isLoadingFile(fileName)) { + buttonState = 2; progressView.setProgress(0); + } else { + buttonState = 3; + Float progress = ImageLoader.getInstance().getFileProgress(fileName); + if (progress != null) { + progressView.setProgress(progress); + } else { + progressView.setProgress(0); + } } } } @@ -297,65 +265,39 @@ public class ChatAudioCell extends ChatBaseCell implements SeekBar.SeekBarDelega @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int width = MeasureSpec.getSize(widthMeasureSpec); - setMeasuredDimension(width, AndroidUtilities.dp(68) + namesOffset); + setMeasuredDimension(width, AndroidUtilities.dp(66) + namesOffset); } @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); - int x; - if (currentMessageObject.isOut()) { - x = layoutWidth - backgroundWidth + AndroidUtilities.dp(8); - seekBarX = layoutWidth - backgroundWidth + AndroidUtilities.dp(97); - buttonX = layoutWidth - backgroundWidth + AndroidUtilities.dp(67); - timeX = layoutWidth - backgroundWidth + AndroidUtilities.dp(71); + seekBarX = layoutWidth - backgroundWidth + AndroidUtilities.dp(55); + buttonX = layoutWidth - backgroundWidth + AndroidUtilities.dp(13); + timeX = layoutWidth - backgroundWidth + AndroidUtilities.dp(66); } else { if (isChat) { - x = AndroidUtilities.dp(69); - seekBarX = AndroidUtilities.dp(158); - buttonX = AndroidUtilities.dp(128); - timeX = AndroidUtilities.dp(132); + seekBarX = AndroidUtilities.dp(116); + buttonX = AndroidUtilities.dp(74); + timeX = AndroidUtilities.dp(127); } else { - x = AndroidUtilities.dp(16); - seekBarX = AndroidUtilities.dp(106); - buttonX = AndroidUtilities.dp(76); - timeX = AndroidUtilities.dp(80); + seekBarX = AndroidUtilities.dp(64); + buttonX = AndroidUtilities.dp(22); + timeX = AndroidUtilities.dp(75); } } - int diff = 0; - if (needAvatarImage) { - avatarImage.setImageCoords(x, AndroidUtilities.dp(9) + namesOffset, AndroidUtilities.dp(50), AndroidUtilities.dp(50)); - } else { - diff = AndroidUtilities.dp(56); - seekBarX -= diff; - buttonX -= diff; - timeX -= diff; - } - seekBar.width = backgroundWidth - AndroidUtilities.dp(112) + diff; + seekBar.width = backgroundWidth - AndroidUtilities.dp(70); seekBar.height = AndroidUtilities.dp(30); - progressView.width = backgroundWidth - AndroidUtilities.dp(136) + diff; + progressView.width = backgroundWidth - AndroidUtilities.dp(94); progressView.height = AndroidUtilities.dp(30); - seekBarY = AndroidUtilities.dp(13) + namesOffset; - buttonY = AndroidUtilities.dp(10) + namesOffset; + seekBarY = AndroidUtilities.dp(11) + namesOffset; + buttonY = AndroidUtilities.dp(13) + namesOffset; updateProgress(); } - @Override - protected boolean isUserDataChanged() { - TLRPC.User newUser = MessagesController.getInstance().getUser(currentMessageObject.messageOwner.media.audio.user_id); - TLRPC.FileLocation newPhoto = null; - - if (avatarImage != null && newUser != null && newUser.photo != null) { - newPhoto = newUser.photo.photo_small; - } - - return currentPhoto == null && newPhoto != null || currentPhoto != null && newPhoto == null || currentPhoto != null && newPhoto != null && (currentPhoto.local_id != newPhoto.local_id || currentPhoto.volume_id != newPhoto.volume_id) || super.isUserDataChanged(); - } - @Override public void setMessageObject(MessageObject messageObject) { if (currentMessageObject != messageObject || isUserDataChanged()) { @@ -369,23 +311,6 @@ public class ChatAudioCell extends ChatBaseCell implements SeekBar.SeekBarDelega if (uid == 0) { uid = messageObject.messageOwner.from_id; } - needAvatarImage = !(messageObject.messageOwner.to_id != null && messageObject.messageOwner.to_id.chat_id != 0 && !messageObject.isOut() && messageObject.messageOwner.media.audio.user_id == messageObject.messageOwner.from_id); - audioUser = MessagesController.getInstance().getUser(uid); - - if (needAvatarImage) { - if (audioUser != null) { - if (audioUser.photo != null) { - currentPhoto = audioUser.photo.photo_small; - } else { - currentPhoto = null; - } - avatarDrawable.setInfo(audioUser); - } else { - avatarDrawable.setInfo(uid, null, null, false); - currentPhoto = null; - } - avatarImage.setImage(currentPhoto, "50_50", avatarDrawable, false); - } if (messageObject.isOut()) { seekBar.type = 0; @@ -408,10 +333,6 @@ public class ChatAudioCell extends ChatBaseCell implements SeekBar.SeekBarDelega return; } - if (needAvatarImage) { - avatarImage.draw(canvas); - } - canvas.save(); if (buttonState == 0 || buttonState == 1) { canvas.translate(seekBarX, seekBarY); @@ -423,22 +344,25 @@ public class ChatAudioCell extends ChatBaseCell implements SeekBar.SeekBarDelega canvas.restore(); int state = buttonState; - if (!currentMessageObject.isOut()) { - state += 4; - timePaint.setColor(0xffa1aab3); - } else { + if (currentMessageObject.isOut()) { timePaint.setColor(0xff70b15c); + circlePaint.setColor(0xff87bf78); + } else { + state += 5; + timePaint.setColor(0xffa1aab3); + circlePaint.setColor(0xff4195e5); } - Drawable buttonDrawable = statesDrawable[state][buttonPressed ? 1 : 0]; - int side = AndroidUtilities.dp(36); - int x = (side - buttonDrawable.getIntrinsicWidth()) / 2; - int y = (side - buttonDrawable.getIntrinsicHeight()) / 2; - setDrawableBounds(buttonDrawable, x + buttonX, y + buttonY); + Drawable buttonDrawable = ResourceLoader.audioStatesDrawable[state][buttonPressed ? 1 : 0]; + setDrawableBounds(buttonDrawable, buttonX, buttonY); buttonDrawable.draw(canvas); canvas.save(); - canvas.translate(timeX, AndroidUtilities.dp(45) + namesOffset); + canvas.translate(timeX, AndroidUtilities.dp(42) + namesOffset); timeLayout.draw(canvas); canvas.restore(); + + if (currentMessageObject.isContentUnread()) { + canvas.drawCircle(timeX + timeWidth + AndroidUtilities.dp(8), AndroidUtilities.dp(49.5f) + namesOffset, AndroidUtilities.dp(3), circlePaint); + } } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatBaseCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatBaseCell.java index a56e53678..72589d5d0 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatBaseCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatBaseCell.java @@ -13,11 +13,13 @@ import android.content.Context; import android.content.res.Configuration; import android.graphics.Canvas; import android.graphics.Paint; +import android.graphics.Path; import android.graphics.drawable.Drawable; import android.text.Layout; import android.text.StaticLayout; import android.text.TextPaint; import android.text.TextUtils; +import android.text.style.ClickableSpan; import android.view.MotionEvent; import android.view.SoundEffectConstants; @@ -34,6 +36,8 @@ import org.telegram.messenger.R; import org.telegram.android.MessageObject; import org.telegram.android.ImageReceiver; import org.telegram.ui.Components.AvatarDrawable; +import org.telegram.ui.Components.ResourceLoader; +import org.telegram.ui.Components.StaticLayoutEx; public class ChatBaseCell extends BaseCell { @@ -46,6 +50,46 @@ public class ChatBaseCell extends BaseCell { boolean canPerformActions(); } + protected class MyPath extends Path { + + private StaticLayout currentLayout; + private int currentLine; + private float lastTop = -1; + + public void setCurrentLayout(StaticLayout layout, int start) { + currentLayout = layout; + currentLine = layout.getLineForOffset(start); + lastTop = -1; + } + + @Override + public void addRect(float left, float top, float right, float bottom, Direction dir) { + if (lastTop == -1) { + lastTop = top; + } else if (lastTop != top) { + lastTop = top; + currentLine++; + } + float lineRight = currentLayout.getLineRight(currentLine); + float lineLeft = currentLayout.getLineLeft(currentLine); + if (left >= lineRight) { + return; + } + if (right > lineRight) { + right = lineRight; + } + if (left < lineLeft) { + left = lineLeft; + } + super.addRect(left, top, right, bottom, dir); + } + } + + protected ClickableSpan pressedLink; + protected boolean linkPreviewPressed; + protected MyPath urlPath = new MyPath(); + protected static Paint urlPaint; + public boolean isChat = false; protected boolean isPressed = false; protected boolean forwardName = false; @@ -57,26 +101,6 @@ public class ChatBaseCell extends BaseCell { protected boolean drawBackground = true; protected MessageObject currentMessageObject; - private static Drawable backgroundDrawableIn; - private static Drawable backgroundDrawableInSelected; - private static Drawable backgroundDrawableOut; - private static Drawable backgroundDrawableOutSelected; - private static Drawable backgroundMediaDrawableIn; - private static Drawable backgroundMediaDrawableInSelected; - private static Drawable backgroundMediaDrawableOut; - private static Drawable backgroundMediaDrawableOutSelected; - private static Drawable checkDrawable; - private static Drawable halfCheckDrawable; - private static Drawable clockDrawable; - private static Drawable broadcastDrawable; - private static Drawable checkMediaDrawable; - private static Drawable halfCheckMediaDrawable; - private static Drawable clockMediaDrawable; - private static Drawable broadcastMediaDrawable; - private static Drawable errorDrawable; - private static Drawable backgroundBlack; - private static Drawable backgroundBlue; - protected static Drawable mediaBackgroundDrawable; private static TextPaint timePaintIn; private static TextPaint timePaintOut; private static TextPaint timeMediaPaint; @@ -144,28 +168,7 @@ public class ChatBaseCell extends BaseCell { public ChatBaseCell(Context context) { super(context); - if (backgroundDrawableIn == null) { - backgroundDrawableIn = getResources().getDrawable(R.drawable.msg_in); - backgroundDrawableInSelected = getResources().getDrawable(R.drawable.msg_in_selected); - backgroundDrawableOut = getResources().getDrawable(R.drawable.msg_out); - backgroundDrawableOutSelected = getResources().getDrawable(R.drawable.msg_out_selected); - backgroundMediaDrawableIn = getResources().getDrawable(R.drawable.msg_in_photo); - backgroundMediaDrawableInSelected = getResources().getDrawable(R.drawable.msg_in_photo_selected); - backgroundMediaDrawableOut = getResources().getDrawable(R.drawable.msg_out_photo); - backgroundMediaDrawableOutSelected = getResources().getDrawable(R.drawable.msg_out_photo_selected); - checkDrawable = getResources().getDrawable(R.drawable.msg_check); - halfCheckDrawable = getResources().getDrawable(R.drawable.msg_halfcheck); - clockDrawable = getResources().getDrawable(R.drawable.msg_clock); - checkMediaDrawable = getResources().getDrawable(R.drawable.msg_check_w); - halfCheckMediaDrawable = getResources().getDrawable(R.drawable.msg_halfcheck_w); - clockMediaDrawable = getResources().getDrawable(R.drawable.msg_clock_photo); - errorDrawable = getResources().getDrawable(R.drawable.msg_warning); - mediaBackgroundDrawable = getResources().getDrawable(R.drawable.phototime); - broadcastDrawable = getResources().getDrawable(R.drawable.broadcast3); - broadcastMediaDrawable = getResources().getDrawable(R.drawable.broadcast4); - backgroundBlack = getResources().getDrawable(R.drawable.system_black); - backgroundBlue = getResources().getDrawable(R.drawable.system_blue); - + if (timePaintIn == null) { timePaintIn = new TextPaint(TextPaint.ANTI_ALIAS_FLAG); timePaintIn.setTextSize(AndroidUtilities.dp(12)); timePaintIn.setColor(0xffa1aab3); @@ -203,10 +206,15 @@ public class ChatBaseCell extends BaseCell { @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); - avatarImage.clearImage(); - replyImageReceiver.clearImage(); - currentPhoto = null; - currentReplyPhoto = null; + avatarImage.onDetachedFromWindow(); + replyImageReceiver.onDetachedFromWindow(); + } + + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + avatarImage.onAttachedToWindow(); + replyImageReceiver.onAttachedToWindow(); } @Override @@ -215,6 +223,14 @@ public class ChatBaseCell extends BaseCell { invalidate(); } + protected void resetPressedLink() { + if (pressedLink != null) { + pressedLink = null; + } + linkPreviewPressed = false; + invalidate(); + } + public void setDelegate(ChatBaseCellDelegate delegate) { this.delegate = delegate; } @@ -289,6 +305,20 @@ public class ChatBaseCell extends BaseCell { return currentForwardNameString == null && newNameString != null || currentForwardNameString != null && newNameString == null || currentForwardNameString != null && newNameString != null && !currentForwardNameString.equals(newNameString); } + protected void measureTime(MessageObject messageObject) { + if (!media) { + if (messageObject.isOut()) { + currentTimePaint = timePaintOut; + } else { + currentTimePaint = timePaintIn; + } + } else { + currentTimePaint = timeMediaPaint; + } + currentTimeString = LocaleController.formatterDay.format((long) (messageObject.messageOwner.date) * 1000); + timeWidth = (int)Math.ceil(currentTimePaint.measureText(currentTimeString)); + } + public void setMessageObject(MessageObject messageObject) { currentMessageObject = messageObject; last_send_state = messageObject.messageOwner.send_state; @@ -363,7 +393,7 @@ public class ChatBaseCell extends BaseCell { CharSequence str = TextUtils.ellipsize(currentForwardNameString.replace("\n", " "), forwardNamePaint, forwardedNameWidth - AndroidUtilities.dp(40), TextUtils.TruncateAt.END); str = AndroidUtilities.replaceTags(String.format("%s\n%s %s", LocaleController.getString("ForwardedMessage", R.string.ForwardedMessage), LocaleController.getString("From", R.string.From), str)); - forwardedNameLayout = new StaticLayout(str, forwardNamePaint, forwardedNameWidth, Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false); + forwardedNameLayout = StaticLayoutEx.createStaticLayout(str, forwardNamePaint, forwardedNameWidth, Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false, TextUtils.TruncateAt.END, forwardedNameWidth, 2); if (forwardedNameLayout.getLineCount() > 1) { forwardedNameWidth = Math.max((int) Math.ceil(forwardedNameLayout.getLineWidth(0)), (int) Math.ceil(forwardedNameLayout.getLineWidth(1))); namesOffset += AndroidUtilities.dp(36); @@ -627,30 +657,30 @@ public class ChatBaseCell extends BaseCell { if (currentMessageObject.isOut()) { if (isPressed() && isCheckPressed || !isCheckPressed && isPressed || isHighlighted) { if (!media) { - currentBackgroundDrawable = backgroundDrawableOutSelected; + currentBackgroundDrawable = ResourceLoader.backgroundDrawableOutSelected; } else { - currentBackgroundDrawable = backgroundMediaDrawableOutSelected; + currentBackgroundDrawable = ResourceLoader.backgroundMediaDrawableOutSelected; } } else { if (!media) { - currentBackgroundDrawable = backgroundDrawableOut; + currentBackgroundDrawable = ResourceLoader.backgroundDrawableOut; } else { - currentBackgroundDrawable = backgroundMediaDrawableOut; + currentBackgroundDrawable = ResourceLoader.backgroundMediaDrawableOut; } } setDrawableBounds(currentBackgroundDrawable, layoutWidth - backgroundWidth - (!media ? 0 : AndroidUtilities.dp(9)), AndroidUtilities.dp(1), backgroundWidth, layoutHeight - AndroidUtilities.dp(2)); } else { if (isPressed() && isCheckPressed || !isCheckPressed && isPressed || isHighlighted) { if (!media) { - currentBackgroundDrawable = backgroundDrawableInSelected; + currentBackgroundDrawable = ResourceLoader.backgroundDrawableInSelected; } else { - currentBackgroundDrawable = backgroundMediaDrawableInSelected; + currentBackgroundDrawable = ResourceLoader.backgroundMediaDrawableInSelected; } } else { if (!media) { - currentBackgroundDrawable = backgroundDrawableIn; + currentBackgroundDrawable = ResourceLoader.backgroundDrawableIn; } else { - currentBackgroundDrawable = backgroundMediaDrawableIn; + currentBackgroundDrawable = ResourceLoader.backgroundMediaDrawableIn; } } if (isChat) { @@ -703,9 +733,9 @@ public class ChatBaseCell extends BaseCell { } Drawable back; if (ApplicationLoader.isCustomTheme()) { - back = backgroundBlack; + back = ResourceLoader.backgroundBlack; } else { - back = backgroundBlue; + back = ResourceLoader.backgroundBlue; } replyStartY = layoutHeight - AndroidUtilities.dp(58); back.setBounds(replyStartX - AndroidUtilities.dp(7), replyStartY - AndroidUtilities.dp(6), replyStartX - AndroidUtilities.dp(7) + backWidth, replyStartY + AndroidUtilities.dp(41)); @@ -755,10 +785,10 @@ public class ChatBaseCell extends BaseCell { } } - if (drawTime) { + if (drawTime || !media) { if (media) { - setDrawableBounds(mediaBackgroundDrawable, timeX - AndroidUtilities.dp(3), layoutHeight - AndroidUtilities.dp(27.5f), timeWidth + AndroidUtilities.dp(6 + (currentMessageObject.isOut() ? 20 : 0)), AndroidUtilities.dp(16.5f)); - mediaBackgroundDrawable.draw(canvas); + setDrawableBounds(ResourceLoader.mediaBackgroundDrawable, timeX - AndroidUtilities.dp(3), layoutHeight - AndroidUtilities.dp(27.5f), timeWidth + AndroidUtilities.dp(6 + (currentMessageObject.isOut() ? 20 : 0)), AndroidUtilities.dp(16.5f)); + ResourceLoader.mediaBackgroundDrawable.draw(canvas); canvas.save(); canvas.translate(timeX, layoutHeight - AndroidUtilities.dp(12.0f) - timeLayout.getHeight()); @@ -802,58 +832,58 @@ public class ChatBaseCell extends BaseCell { if (drawClock) { if (!media) { - setDrawableBounds(clockDrawable, layoutWidth - AndroidUtilities.dp(18.5f) - clockDrawable.getIntrinsicWidth(), layoutHeight - AndroidUtilities.dp(8.5f) - clockDrawable.getIntrinsicHeight()); - clockDrawable.draw(canvas); + setDrawableBounds(ResourceLoader.clockDrawable, layoutWidth - AndroidUtilities.dp(18.5f) - ResourceLoader.clockDrawable.getIntrinsicWidth(), layoutHeight - AndroidUtilities.dp(8.5f) - ResourceLoader.clockDrawable.getIntrinsicHeight()); + ResourceLoader.clockDrawable.draw(canvas); } else { - setDrawableBounds(clockMediaDrawable, layoutWidth - AndroidUtilities.dp(22.0f) - clockMediaDrawable.getIntrinsicWidth(), layoutHeight - AndroidUtilities.dp(13.0f) - clockMediaDrawable.getIntrinsicHeight()); - clockMediaDrawable.draw(canvas); + setDrawableBounds(ResourceLoader.clockMediaDrawable, layoutWidth - AndroidUtilities.dp(22.0f) - ResourceLoader.clockMediaDrawable.getIntrinsicWidth(), layoutHeight - AndroidUtilities.dp(13.0f) - ResourceLoader.clockMediaDrawable.getIntrinsicHeight()); + ResourceLoader.clockMediaDrawable.draw(canvas); } } if (isBroadcast) { if (drawCheck1 || drawCheck2) { if (!media) { - setDrawableBounds(broadcastDrawable, layoutWidth - AndroidUtilities.dp(20.5f) - broadcastDrawable.getIntrinsicWidth(), layoutHeight - AndroidUtilities.dp(8.0f) - broadcastDrawable.getIntrinsicHeight()); - broadcastDrawable.draw(canvas); + setDrawableBounds(ResourceLoader.broadcastDrawable, layoutWidth - AndroidUtilities.dp(20.5f) - ResourceLoader.broadcastDrawable.getIntrinsicWidth(), layoutHeight - AndroidUtilities.dp(8.0f) - ResourceLoader.broadcastDrawable.getIntrinsicHeight()); + ResourceLoader.broadcastDrawable.draw(canvas); } else { - setDrawableBounds(broadcastMediaDrawable, layoutWidth - AndroidUtilities.dp(24.0f) - broadcastMediaDrawable.getIntrinsicWidth(), layoutHeight - AndroidUtilities.dp(13.0f) - broadcastMediaDrawable.getIntrinsicHeight()); - broadcastMediaDrawable.draw(canvas); + setDrawableBounds(ResourceLoader.broadcastMediaDrawable, layoutWidth - AndroidUtilities.dp(24.0f) - ResourceLoader.broadcastMediaDrawable.getIntrinsicWidth(), layoutHeight - AndroidUtilities.dp(13.0f) - ResourceLoader.broadcastMediaDrawable.getIntrinsicHeight()); + ResourceLoader.broadcastMediaDrawable.draw(canvas); } } } else { if (drawCheck2) { if (!media) { if (drawCheck1) { - setDrawableBounds(checkDrawable, layoutWidth - AndroidUtilities.dp(22.5f) - checkDrawable.getIntrinsicWidth(), layoutHeight - AndroidUtilities.dp(8.0f) - checkDrawable.getIntrinsicHeight()); + setDrawableBounds(ResourceLoader.checkDrawable, layoutWidth - AndroidUtilities.dp(22.5f) - ResourceLoader.checkDrawable.getIntrinsicWidth(), layoutHeight - AndroidUtilities.dp(8.0f) - ResourceLoader.checkDrawable.getIntrinsicHeight()); } else { - setDrawableBounds(checkDrawable, layoutWidth - AndroidUtilities.dp(18.5f) - checkDrawable.getIntrinsicWidth(), layoutHeight - AndroidUtilities.dp(8.0f) - checkDrawable.getIntrinsicHeight()); + setDrawableBounds(ResourceLoader.checkDrawable, layoutWidth - AndroidUtilities.dp(18.5f) - ResourceLoader.checkDrawable.getIntrinsicWidth(), layoutHeight - AndroidUtilities.dp(8.0f) - ResourceLoader.checkDrawable.getIntrinsicHeight()); } - checkDrawable.draw(canvas); + ResourceLoader.checkDrawable.draw(canvas); } else { if (drawCheck1) { - setDrawableBounds(checkMediaDrawable, layoutWidth - AndroidUtilities.dp(26.0f) - checkMediaDrawable.getIntrinsicWidth(), layoutHeight - AndroidUtilities.dp(13.0f) - checkMediaDrawable.getIntrinsicHeight()); + setDrawableBounds(ResourceLoader.checkMediaDrawable, layoutWidth - AndroidUtilities.dp(26.0f) - ResourceLoader.checkMediaDrawable.getIntrinsicWidth(), layoutHeight - AndroidUtilities.dp(13.0f) - ResourceLoader.checkMediaDrawable.getIntrinsicHeight()); } else { - setDrawableBounds(checkMediaDrawable, layoutWidth - AndroidUtilities.dp(22.0f) - checkMediaDrawable.getIntrinsicWidth(), layoutHeight - AndroidUtilities.dp(13.0f) - checkMediaDrawable.getIntrinsicHeight()); + setDrawableBounds(ResourceLoader.checkMediaDrawable, layoutWidth - AndroidUtilities.dp(22.0f) - ResourceLoader.checkMediaDrawable.getIntrinsicWidth(), layoutHeight - AndroidUtilities.dp(13.0f) - ResourceLoader.checkMediaDrawable.getIntrinsicHeight()); } - checkMediaDrawable.draw(canvas); + ResourceLoader.checkMediaDrawable.draw(canvas); } } if (drawCheck1) { if (!media) { - setDrawableBounds(halfCheckDrawable, layoutWidth - AndroidUtilities.dp(18) - halfCheckDrawable.getIntrinsicWidth(), layoutHeight - AndroidUtilities.dp(8.0f) - halfCheckDrawable.getIntrinsicHeight()); - halfCheckDrawable.draw(canvas); + setDrawableBounds(ResourceLoader.halfCheckDrawable, layoutWidth - AndroidUtilities.dp(18) - ResourceLoader.halfCheckDrawable.getIntrinsicWidth(), layoutHeight - AndroidUtilities.dp(8.0f) - ResourceLoader.halfCheckDrawable.getIntrinsicHeight()); + ResourceLoader.halfCheckDrawable.draw(canvas); } else { - setDrawableBounds(halfCheckMediaDrawable, layoutWidth - AndroidUtilities.dp(20.5f) - halfCheckMediaDrawable.getIntrinsicWidth(), layoutHeight - AndroidUtilities.dp(13.0f) - halfCheckMediaDrawable.getIntrinsicHeight()); - halfCheckMediaDrawable.draw(canvas); + setDrawableBounds(ResourceLoader.halfCheckMediaDrawable, layoutWidth - AndroidUtilities.dp(20.5f) - ResourceLoader.halfCheckMediaDrawable.getIntrinsicWidth(), layoutHeight - AndroidUtilities.dp(13.0f) - ResourceLoader.halfCheckMediaDrawable.getIntrinsicHeight()); + ResourceLoader.halfCheckMediaDrawable.draw(canvas); } } } if (drawError) { if (!media) { - setDrawableBounds(errorDrawable, layoutWidth - AndroidUtilities.dp(18) - errorDrawable.getIntrinsicWidth(), layoutHeight - AndroidUtilities.dp(6.5f) - errorDrawable.getIntrinsicHeight()); - errorDrawable.draw(canvas); + setDrawableBounds(ResourceLoader.errorDrawable, layoutWidth - AndroidUtilities.dp(18) - ResourceLoader.errorDrawable.getIntrinsicWidth(), layoutHeight - AndroidUtilities.dp(6.5f) - ResourceLoader.errorDrawable.getIntrinsicHeight()); + ResourceLoader.errorDrawable.draw(canvas); } else { - setDrawableBounds(errorDrawable, layoutWidth - AndroidUtilities.dp(20.5f) - errorDrawable.getIntrinsicWidth(), layoutHeight - AndroidUtilities.dp(12.5f) - errorDrawable.getIntrinsicHeight()); - errorDrawable.draw(canvas); + setDrawableBounds(ResourceLoader.errorDrawable, layoutWidth - AndroidUtilities.dp(20.5f) - ResourceLoader.errorDrawable.getIntrinsicWidth(), layoutHeight - AndroidUtilities.dp(12.5f) - ResourceLoader.errorDrawable.getIntrinsicHeight()); + ResourceLoader.errorDrawable.draw(canvas); } } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatMediaCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatMediaCell.java index a2a6f9245..69cd4052c 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatMediaCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatMediaCell.java @@ -16,9 +16,11 @@ import android.graphics.RectF; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.text.Layout; +import android.text.Spannable; import android.text.StaticLayout; import android.text.TextPaint; import android.text.TextUtils; +import android.text.style.ClickableSpan; import android.view.MotionEvent; import android.view.SoundEffectConstants; @@ -29,11 +31,15 @@ import org.telegram.android.SendMessagesHelper; import org.telegram.messenger.ConnectionsManager; import org.telegram.messenger.FileLoader; import org.telegram.android.MediaController; +import org.telegram.messenger.FileLog; import org.telegram.messenger.R; import org.telegram.messenger.TLRPC; import org.telegram.messenger.Utilities; import org.telegram.android.MessageObject; import org.telegram.ui.Components.RadialProgress; +import org.telegram.ui.Components.ResourceLoader; +import org.telegram.ui.Components.StaticLayoutEx; +import org.telegram.ui.Components.URLSpanNoUnderline; import org.telegram.ui.PhotoViewer; import org.telegram.ui.Components.GifDrawable; import org.telegram.android.ImageReceiver; @@ -45,21 +51,17 @@ public class ChatMediaCell extends ChatBaseCell implements MediaController.FileD public interface ChatMediaCellDelegate { void didClickedImage(ChatMediaCell cell); + void didPressedOther(ChatMediaCell cell); } - private static Drawable placeholderDocInDrawable; - private static Drawable placeholderDocOutDrawable; - private static Drawable videoIconDrawable; - private static Drawable docMenuInDrawable; - private static Drawable docMenuOutDrawable; - private static Drawable[] buttonStatesDrawables = new Drawable[8]; - private static Drawable[][] buttonStatesDrawablesDoc = new Drawable[3][2]; private static TextPaint infoPaint; private static MessageObject lastDownloadedGifMessage = null; private static TextPaint namePaint; private static Paint docBackPaint; private static Paint deleteProgressPaint; + private static TextPaint locationTitlePaint; + private static TextPaint locationAddressPaint; private GifDrawable gifDrawable = null; private RadialProgress radialProgress; @@ -73,6 +75,7 @@ public class ChatMediaCell extends ChatBaseCell implements MediaController.FileD private ImageReceiver photoImage; private boolean photoNotSet = false; private boolean cancelLoading = false; + private int additionHeight; private boolean allowedToSetPhoto = true; @@ -97,30 +100,14 @@ public class ChatMediaCell extends ChatBaseCell implements MediaController.FileD private ChatMediaCellDelegate mediaDelegate = null; private RectF deleteProgressRect = new RectF(); + private int captionX; + private int captionY; + private int captionHeight; + public ChatMediaCell(Context context) { super(context); - if (placeholderDocInDrawable == null) { - placeholderDocInDrawable = getResources().getDrawable(R.drawable.doc_blue); - placeholderDocOutDrawable = getResources().getDrawable(R.drawable.doc_green); - buttonStatesDrawables[0] = getResources().getDrawable(R.drawable.photoload); - buttonStatesDrawables[1] = getResources().getDrawable(R.drawable.photocancel); - buttonStatesDrawables[2] = getResources().getDrawable(R.drawable.photogif); - buttonStatesDrawables[3] = getResources().getDrawable(R.drawable.playvideo); - buttonStatesDrawables[4] = getResources().getDrawable(R.drawable.photopause); - buttonStatesDrawables[5] = getResources().getDrawable(R.drawable.burn); - buttonStatesDrawables[6] = getResources().getDrawable(R.drawable.circle); - buttonStatesDrawables[7] = getResources().getDrawable(R.drawable.photocheck); - buttonStatesDrawablesDoc[0][0] = getResources().getDrawable(R.drawable.docload_b); - buttonStatesDrawablesDoc[1][0] = getResources().getDrawable(R.drawable.doccancel_b); - buttonStatesDrawablesDoc[2][0] = getResources().getDrawable(R.drawable.docpause_b); - buttonStatesDrawablesDoc[0][1] = getResources().getDrawable(R.drawable.docload_g); - buttonStatesDrawablesDoc[1][1] = getResources().getDrawable(R.drawable.doccancel_g); - buttonStatesDrawablesDoc[2][1] = getResources().getDrawable(R.drawable.docpause_g); - videoIconDrawable = getResources().getDrawable(R.drawable.ic_video); - docMenuInDrawable = getResources().getDrawable(R.drawable.doc_actions_b); - docMenuOutDrawable = getResources().getDrawable(R.drawable.doc_actions_g); - + if (infoPaint == null) { infoPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG); infoPaint.setTextSize(AndroidUtilities.dp(12)); @@ -132,6 +119,13 @@ public class ChatMediaCell extends ChatBaseCell implements MediaController.FileD deleteProgressPaint = new Paint(Paint.ANTI_ALIAS_FLAG); deleteProgressPaint.setColor(0xffe4e2e0); + + locationTitlePaint = new TextPaint(Paint.ANTI_ALIAS_FLAG); + locationTitlePaint.setTextSize(AndroidUtilities.dp(14)); + locationTitlePaint.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); + + locationAddressPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG); + locationAddressPaint.setTextSize(AndroidUtilities.dp(14)); } TAG = MediaController.getInstance().generateObserverTag(); @@ -156,12 +150,7 @@ public class ChatMediaCell extends ChatBaseCell implements MediaController.FileD @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); - if (photoImage != null) { - photoImage.clearImage(); - currentPhotoObject = null; - currentPhotoObjectThumb = null; - } - currentUrl = null; + photoImage.onDetachedFromWindow(); if (gifDrawable != null) { MediaController.getInstance().clearGifDrawable(this); gifDrawable = null; @@ -169,6 +158,14 @@ public class ChatMediaCell extends ChatBaseCell implements MediaController.FileD MediaController.getInstance().removeLoadingFileObserver(this); } + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + if (photoImage.onAttachedToWindow()) { + updateButtonState(false); + } + } + @Override public boolean onTouchEvent(MotionEvent event) { float x = event.getX(); @@ -176,6 +173,78 @@ public class ChatMediaCell extends ChatBaseCell implements MediaController.FileD boolean result = false; int side = AndroidUtilities.dp(48); + if (currentMessageObject.caption instanceof Spannable && !isPressed) { + if (event.getAction() == MotionEvent.ACTION_DOWN || (linkPreviewPressed || pressedLink != null) && event.getAction() == MotionEvent.ACTION_UP) { + if (nameLayout != null && x >= captionX && x <= captionX + backgroundWidth && y >= captionY && y <= captionY + captionHeight) { + if (event.getAction() == MotionEvent.ACTION_DOWN) { + resetPressedLink(); + try { + int x2 = (int) (x - captionX); + int y2 = (int) (y - captionY); + final int line = nameLayout.getLineForVertical(y2); + final int off = nameLayout.getOffsetForHorizontal(line, x2); + + final float left = nameLayout.getLineLeft(line); + if (left <= x2 && left + nameLayout.getLineWidth(line) >= x2) { + Spannable buffer = (Spannable) currentMessageObject.caption; + ClickableSpan[] link = buffer.getSpans(off, off, ClickableSpan.class); + if (link.length != 0) { + resetPressedLink(); + pressedLink = link[0]; + linkPreviewPressed = true; + result = true; + try { + int start = buffer.getSpanStart(pressedLink); + urlPath.setCurrentLayout(nameLayout, start); + nameLayout.getSelectionPath(start, buffer.getSpanEnd(pressedLink), urlPath); + } catch (Exception e) { + FileLog.e("tmessages", e); + } + } else { + resetPressedLink(); + } + } else { + resetPressedLink(); + } + } catch (Exception e) { + resetPressedLink(); + FileLog.e("tmessages", e); + } + } else if (linkPreviewPressed) { + try { + if (pressedLink instanceof URLSpanNoUnderline) { + String url = ((URLSpanNoUnderline) pressedLink).getURL(); + if (url.startsWith("@") || url.startsWith("#")) { + if (delegate != null) { + delegate.didPressUrl(url); + } + } + } else { + pressedLink.onClick(this); + } + } catch (Exception e) { + FileLog.e("tmessages", e); + } + resetPressedLink(); + result = true; + } + } else { + resetPressedLink(); + } + } else if (event.getAction() == MotionEvent.ACTION_CANCEL) { + resetPressedLink(); + } + + if (result && event.getAction() == MotionEvent.ACTION_DOWN) { + startCheckLongPress(); + } + if (event.getAction() != MotionEvent.ACTION_DOWN && event.getAction() != MotionEvent.ACTION_MOVE) { + cancelCheckLongPress(); + } + if (result) { + return result; + } + } if (event.getAction() == MotionEvent.ACTION_DOWN) { if (delegate == null || delegate.canPerformActions()) { if (buttonState != -1 && x >= buttonX && x <= buttonX + side && y >= buttonY && y <= buttonY + side) { @@ -314,20 +383,20 @@ public class ChatMediaCell extends ChatBaseCell implements MediaController.FileD Drawable currentButtonDrawable = null; if (currentMessageObject.type == 9 && gifDrawable == null) { if (buttonState == 1 && !currentMessageObject.isSending()) { - return buttonStatesDrawablesDoc[2][currentMessageObject.isOut() ? 1 : 0]; + return ResourceLoader.buttonStatesDrawablesDoc[2][currentMessageObject.isOut() ? 1 : 0]; } else { - return buttonStatesDrawablesDoc[buttonState][currentMessageObject.isOut() ? 1 : 0]; + return ResourceLoader.buttonStatesDrawablesDoc[buttonState][currentMessageObject.isOut() ? 1 : 0]; } } else { if (buttonState == 1 && !currentMessageObject.isSending()) { - return buttonStatesDrawables[4]; + return ResourceLoader.buttonStatesDrawables[4]; } else { - return buttonStatesDrawables[buttonState]; + return ResourceLoader.buttonStatesDrawables[buttonState]; } } } else if (buttonState == -1) { if (currentMessageObject.type == 9 && gifDrawable == null) { - return currentMessageObject.isOut() ? placeholderDocOutDrawable : placeholderDocInDrawable; + return currentMessageObject.isOut() ? ResourceLoader.placeholderDocOutDrawable : ResourceLoader.placeholderDocInDrawable; } } return null; @@ -393,7 +462,7 @@ public class ChatMediaCell extends ChatBaseCell implements MediaController.FileD } double lat = object.messageOwner.media.geo.lat; double lon = object.messageOwner.media.geo._long; - String url = String.format(Locale.US, "https://maps.googleapis.com/maps/api/staticmap?center=%f,%f&zoom=13&size=100x100&maptype=roadmap&scale=%d&markers=color:red|size:big|%f,%f&sensor=false", lat, lon, Math.min(2, (int)Math.ceil(AndroidUtilities.density)), lat, lon); + String url = String.format(Locale.US, "https://maps.googleapis.com/maps/api/staticmap?center=%f,%f&zoom=13&size=100x100&maptype=roadmap&scale=%d&markers=color:red|size:big|%f,%f&sensor=false", lat, lon, Math.min(2, (int) Math.ceil(AndroidUtilities.density)), lat, lon); if (!url.equals(currentUrl)) { return true; } @@ -410,10 +479,12 @@ public class ChatMediaCell extends ChatBaseCell implements MediaController.FileD @Override public void setMessageObject(MessageObject messageObject) { - media = messageObject.type != 9; boolean dataChanged = currentMessageObject == messageObject && (isUserDataChanged() || photoNotSet); if (currentMessageObject != messageObject || isPhotoDataChanged(messageObject) || dataChanged) { + media = messageObject.type != 9; cancelLoading = false; + additionHeight = 0; + resetPressedLink(); buttonState = -1; gifDrawable = null; @@ -437,9 +508,12 @@ public class ChatMediaCell extends ChatBaseCell implements MediaController.FileD } if (currentNameString == null || !currentNameString.equals(name)) { currentNameString = name; - nameWidth = Math.min(maxWidth, (int) Math.ceil(namePaint.measureText(currentNameString))); - CharSequence str = TextUtils.ellipsize(currentNameString, namePaint, nameWidth, TextUtils.TruncateAt.END); - nameLayout = new StaticLayout(str, namePaint, nameWidth, Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false); + nameLayout = StaticLayoutEx.createStaticLayout(currentNameString, namePaint, maxWidth, Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false, TextUtils.TruncateAt.END, maxWidth, 1); + if (nameLayout.getLineCount() > 0) { + nameWidth = Math.min(maxWidth, (int) Math.ceil(nameLayout.getLineWidth(0))); + } else { + nameWidth = maxWidth; + } } String fileName = messageObject.getFileName(); @@ -481,7 +555,7 @@ public class ChatMediaCell extends ChatBaseCell implements MediaController.FileD String str = String.format("%d:%02d, %s", minutes, seconds, Utilities.formatFileSize(messageObject.messageOwner.media.video.size)); if (currentInfoString == null || !currentInfoString.equals(str)) { currentInfoString = str; - infoOffset = videoIconDrawable.getIntrinsicWidth() + AndroidUtilities.dp(4); + infoOffset = ResourceLoader.videoIconDrawable.getIntrinsicWidth() + AndroidUtilities.dp(4); infoWidth = (int) Math.ceil(infoPaint.measureText(currentInfoString)); infoLayout = new StaticLayout(currentInfoString, infoPaint, infoWidth, Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false); } @@ -509,17 +583,46 @@ public class ChatMediaCell extends ChatBaseCell implements MediaController.FileD photoImage.setImageBitmap((BitmapDrawable) null); } } else if (messageObject.type == 4) { //geo - photoWidth = AndroidUtilities.dp(200); - photoHeight = AndroidUtilities.dp(100); - backgroundWidth = photoWidth + AndroidUtilities.dp(12); - double lat = messageObject.messageOwner.media.geo.lat; double lon = messageObject.messageOwner.media.geo._long; - currentUrl = String.format(Locale.US, "https://maps.googleapis.com/maps/api/staticmap?center=%f,%f&zoom=13&size=200x100&maptype=roadmap&scale=%d&markers=color:red|size:big|%f,%f&sensor=false", lat, lon, Math.min(2, (int)Math.ceil(AndroidUtilities.density)), lat, lon); + + if (messageObject.messageOwner.media.title != null && messageObject.messageOwner.media.title.length() > 0) { + int maxWidth = (AndroidUtilities.isTablet() ? AndroidUtilities.getMinTabletSide() : Math.min(AndroidUtilities.displaySize.x, AndroidUtilities.displaySize.y)) - AndroidUtilities.dp((isChat && !messageObject.isOut() ? 102 : 40) + 86 + 24); + nameLayout = StaticLayoutEx.createStaticLayout(messageObject.messageOwner.media.title, locationTitlePaint, maxWidth, Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false, TextUtils.TruncateAt.END, maxWidth - AndroidUtilities.dp(4), 3); + int lineCount = nameLayout.getLineCount(); + if (messageObject.messageOwner.media.address != null && messageObject.messageOwner.media.address.length() > 0) { + infoLayout = StaticLayoutEx.createStaticLayout(messageObject.messageOwner.media.address, locationAddressPaint, maxWidth, Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false, TextUtils.TruncateAt.END, maxWidth - AndroidUtilities.dp(4), Math.min(3, 4 - lineCount)); + } else { + infoLayout = null; + } + + media = false; + measureTime(messageObject); + photoWidth = AndroidUtilities.dp(86); + photoHeight = AndroidUtilities.dp(86); + maxWidth = timeWidth + AndroidUtilities.dp(messageObject.isOut() ? 29 : 9); + for (int a = 0; a < lineCount; a++) { + maxWidth = (int) Math.max(maxWidth, nameLayout.getLineWidth(a) + AndroidUtilities.dp(16)); + } + if (infoLayout != null) { + lineCount = infoLayout.getLineCount(); + for (int a = 0; a < infoLayout.getLineCount(); a++) { + maxWidth = (int) Math.max(maxWidth, infoLayout.getLineWidth(a) + AndroidUtilities.dp(16)); + } + } + backgroundWidth = photoWidth + AndroidUtilities.dp(21) + maxWidth; + currentUrl = String.format(Locale.US, "https://maps.googleapis.com/maps/api/staticmap?center=%f,%f&zoom=13&size=72x72&maptype=roadmap&scale=%d&markers=color:red|size:big|%f,%f&sensor=false", lat, lon, Math.min(2, (int) Math.ceil(AndroidUtilities.density)), lat, lon); + } else { + photoWidth = AndroidUtilities.dp(200); + photoHeight = AndroidUtilities.dp(100); + backgroundWidth = photoWidth + AndroidUtilities.dp(12); + currentUrl = String.format(Locale.US, "https://maps.googleapis.com/maps/api/staticmap?center=%f,%f&zoom=13&size=200x100&maptype=roadmap&scale=%d&markers=color:red|size:big|%f,%f&sensor=false", lat, lon, Math.min(2, (int) Math.ceil(AndroidUtilities.density)), lat, lon); + } + photoImage.setNeedsQualityThumb(false); photoImage.setShouldGenerateQualityThumb(false); photoImage.setParentMessageObject(null); - photoImage.setImage(currentUrl, null, null, 0); + photoImage.setImage(currentUrl, null, messageObject.isOut() ? ResourceLoader.geoOutDrawable : ResourceLoader.geoInDrawable, 0); } else if (messageObject.type == 13) { //webp drawBackground = false; for (TLRPC.DocumentAttribute attribute : messageObject.messageOwner.media.document.attributes) { @@ -542,11 +645,11 @@ public class ChatMediaCell extends ChatBaseCell implements MediaController.FileD } if (photoHeight > maxHeight) { photoWidth *= maxHeight / photoHeight; - photoHeight = (int)maxHeight; + photoHeight = (int) maxHeight; } if (photoWidth > maxWidth) { photoHeight *= maxWidth / photoWidth; - photoWidth = (int)maxWidth; + photoWidth = (int) maxWidth; } backgroundWidth = photoWidth + AndroidUtilities.dp(12); currentPhotoObjectThumb = FileLoader.getClosestPhotoSizeWithSize(messageObject.photoThumbs, 80); @@ -602,10 +705,16 @@ public class ChatMediaCell extends ChatBaseCell implements MediaController.FileD } //8 - gif, 1 - photo, 3 - video + if (messageObject.caption != null) { + media = false; + } currentPhotoObject = FileLoader.getClosestPhotoSizeWithSize(messageObject.photoThumbs, AndroidUtilities.getPhotoSize()); if (currentPhotoObject != null) { + if (currentPhotoObject == currentPhotoObjectThumb) { + currentPhotoObjectThumb = null; + } boolean noSize = false; if (messageObject.type == 3 || messageObject.type == 8) { noSize = true; @@ -640,6 +749,7 @@ public class ChatMediaCell extends ChatBaseCell implements MediaController.FileD w = (int) (currentPhotoObject.w / hScale); } } + measureTime(messageObject); int timeWidthTotal = timeWidth + AndroidUtilities.dp(14 + (messageObject.isOut() ? 20 : 0)); if (w < timeWidthTotal) { w = timeWidthTotal; @@ -656,6 +766,20 @@ public class ChatMediaCell extends ChatBaseCell implements MediaController.FileD photoWidth = w; photoHeight = h; backgroundWidth = w + AndroidUtilities.dp(12); + if (!media) { + backgroundWidth += AndroidUtilities.dp(9); + } + if (messageObject.caption != null) { + nameLayout = new StaticLayout(messageObject.caption, MessageObject.textPaint, photoWidth - AndroidUtilities.dp(10), Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false); + if (nameLayout.getLineCount() > 0) { + captionHeight = nameLayout.getHeight(); + additionHeight += captionHeight + AndroidUtilities.dp(9); + float lastLineWidth = nameLayout.getLineWidth(nameLayout.getLineCount() - 1) + nameLayout.getLineLeft(nameLayout.getLineCount() - 1); + if (photoWidth - AndroidUtilities.dp(8) - lastLineWidth < timeWidthTotal) { + additionHeight += AndroidUtilities.dp(14); + } + } + } currentPhotoFilter = String.format(Locale.US, "%d_%d", (int) (w / AndroidUtilities.density), (int) (h / AndroidUtilities.density)); if (messageObject.photoThumbs.size() > 1 || messageObject.type == 3 || messageObject.type == 8) { @@ -697,7 +821,7 @@ public class ChatMediaCell extends ChatBaseCell implements MediaController.FileD photoImage.setImage(null, null, currentPhotoObject.location, currentPhotoFilter, 0, false); } } else { - photoImage.setImageBitmap((Bitmap)null); + photoImage.setImageBitmap((Bitmap) null); } } super.setMessageObject(messageObject); @@ -793,7 +917,7 @@ public class ChatMediaCell extends ChatBaseCell implements MediaController.FileD @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec), photoHeight + AndroidUtilities.dp(14) + namesOffset); + setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec), photoHeight + AndroidUtilities.dp(14) + namesOffset + additionHeight); } @Override @@ -816,8 +940,8 @@ public class ChatMediaCell extends ChatBaseCell implements MediaController.FileD } photoImage.setImageCoords(x, AndroidUtilities.dp(7) + namesOffset, photoWidth, photoHeight); int size = AndroidUtilities.dp(48); - buttonX = (int)(x + (photoWidth - size) / 2.0f); - buttonY = (int)(AndroidUtilities.dp(7) + (photoHeight - size) / 2.0f) + namesOffset; + buttonX = (int) (x + (photoWidth - size) / 2.0f); + buttonY = (int) (AndroidUtilities.dp(7) + (photoHeight - size) / 2.0f) + namesOffset; radialProgress.setProgressRect(buttonX, buttonY, buttonX + AndroidUtilities.dp(48), buttonY + AndroidUtilities.dp(48)); deleteProgressRect.set(buttonX + AndroidUtilities.dp(3), buttonY + AndroidUtilities.dp(3), buttonX + AndroidUtilities.dp(45), buttonY + AndroidUtilities.dp(45)); @@ -834,7 +958,7 @@ public class ChatMediaCell extends ChatBaseCell implements MediaController.FileD if (currentInfoString == null || !currentInfoString.equals(str)) { currentInfoString = str; infoOffset = 0; - infoWidth = (int)Math.ceil(infoPaint.measureText(currentInfoString)); + infoWidth = (int) Math.ceil(infoPaint.measureText(currentInfoString)); CharSequence str2 = TextUtils.ellipsize(currentInfoString, infoPaint, infoWidth, TextUtils.TruncateAt.END); infoLayout = new StaticLayout(str2, infoPaint, infoWidth, Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false); invalidate(); @@ -878,11 +1002,11 @@ public class ChatMediaCell extends ChatBaseCell implements MediaController.FileD if (currentMessageObject.isOut()) { infoPaint.setColor(0xff70b15c); docBackPaint.setColor(0xffdaf5c3); - menuDrawable = docMenuOutDrawable; + menuDrawable = ResourceLoader.docMenuOutDrawable; } else { infoPaint.setColor(0xffa1adbb); docBackPaint.setColor(0xffebf0f5); - menuDrawable = docMenuInDrawable; + menuDrawable = ResourceLoader.docMenuInDrawable; } setDrawableBounds(menuDrawable, photoImage.getImageX() + backgroundWidth - AndroidUtilities.dp(44), AndroidUtilities.dp(10) + namesOffset); @@ -891,15 +1015,15 @@ public class ChatMediaCell extends ChatBaseCell implements MediaController.FileD if (buttonState >= 0 && buttonState < 4) { if (!imageDrawn) { if (buttonState == 1 && !currentMessageObject.isSending()) { - radialProgress.swapBackground(buttonStatesDrawablesDoc[2][currentMessageObject.isOut() ? 1 : 0]); + radialProgress.swapBackground(ResourceLoader.buttonStatesDrawablesDoc[2][currentMessageObject.isOut() ? 1 : 0]); } else { - radialProgress.swapBackground(buttonStatesDrawablesDoc[buttonState][currentMessageObject.isOut() ? 1 : 0]); + radialProgress.swapBackground(ResourceLoader.buttonStatesDrawablesDoc[buttonState][currentMessageObject.isOut() ? 1 : 0]); } } else { if (buttonState == 1 && !currentMessageObject.isSending()) { - radialProgress.swapBackground(buttonStatesDrawables[4]); + radialProgress.swapBackground(ResourceLoader.buttonStatesDrawables[4]); } else { - radialProgress.swapBackground(buttonStatesDrawables[buttonState]); + radialProgress.swapBackground(ResourceLoader.buttonStatesDrawables[buttonState]); } } } @@ -930,16 +1054,16 @@ public class ChatMediaCell extends ChatBaseCell implements MediaController.FileD drawable = 6; } } - setDrawableBounds(buttonStatesDrawables[drawable], buttonX, buttonY); - buttonStatesDrawables[drawable].setAlpha((int)(255 * (1.0f - radialProgress.getAlpha()))); - buttonStatesDrawables[drawable].draw(canvas); + setDrawableBounds(ResourceLoader.buttonStatesDrawables[drawable], buttonX, buttonY); + ResourceLoader.buttonStatesDrawables[drawable].setAlpha((int) (255 * (1.0f - radialProgress.getAlpha()))); + ResourceLoader.buttonStatesDrawables[drawable].draw(canvas); if (!currentMessageObject.isOut() && currentMessageObject.messageOwner.destroyTime != 0) { long msTime = System.currentTimeMillis() + ConnectionsManager.getInstance().getTimeDifference() * 1000; - float progress = Math.max(0, (long)currentMessageObject.messageOwner.destroyTime * 1000 - msTime) / (currentMessageObject.messageOwner.ttl * 1000.0f); + float progress = Math.max(0, (long) currentMessageObject.messageOwner.destroyTime * 1000 - msTime) / (currentMessageObject.messageOwner.ttl * 1000.0f); canvas.drawArc(deleteProgressRect, -90, -360 * progress, true, deleteProgressPaint); if (progress != 0) { int offset = AndroidUtilities.dp(2); - invalidate((int)deleteProgressRect.left - offset, (int)deleteProgressRect.top - offset, (int)deleteProgressRect.right + offset * 2, (int)deleteProgressRect.bottom + offset * 2); + invalidate((int) deleteProgressRect.left - offset, (int) deleteProgressRect.top - offset, (int) deleteProgressRect.right + offset * 2, (int) deleteProgressRect.bottom + offset * 2); } updateSecretTimeText(); } @@ -947,7 +1071,48 @@ public class ChatMediaCell extends ChatBaseCell implements MediaController.FileD radialProgress.onDraw(canvas); - if (nameLayout != null) { + if (currentMessageObject.type == 1 || currentMessageObject.type == 3) { + if (nameLayout != null) { + canvas.save(); + canvas.translate(captionX = photoImage.getImageX() + AndroidUtilities.dp(5), captionY = photoImage.getImageY() + photoHeight + AndroidUtilities.dp(6)); + if (pressedLink != null) { + canvas.drawPath(urlPath, urlPaint); + } + nameLayout.draw(canvas); + canvas.restore(); + } + if (infoLayout != null && (buttonState == 1 || buttonState == 0 || buttonState == 3 || currentMessageObject.isSecretPhoto())) { + infoPaint.setColor(0xffffffff); + setDrawableBounds(ResourceLoader.mediaBackgroundDrawable, photoImage.getImageX() + AndroidUtilities.dp(4), photoImage.getImageY() + AndroidUtilities.dp(4), infoWidth + AndroidUtilities.dp(8) + infoOffset, AndroidUtilities.dp(16.5f)); + ResourceLoader.mediaBackgroundDrawable.draw(canvas); + + if (currentMessageObject.type == 3) { + setDrawableBounds(ResourceLoader.videoIconDrawable, photoImage.getImageX() + AndroidUtilities.dp(8), photoImage.getImageY() + AndroidUtilities.dp(7.5f)); + ResourceLoader.videoIconDrawable.draw(canvas); + } + + canvas.save(); + canvas.translate(photoImage.getImageX() + AndroidUtilities.dp(8) + infoOffset, photoImage.getImageY() + AndroidUtilities.dp(5.5f)); + infoLayout.draw(canvas); + canvas.restore(); + } + } else if (currentMessageObject.type == 4) { + if (nameLayout != null) { + locationAddressPaint.setColor(currentMessageObject.isOut() ? 0xff70b15c : 0xff999999); + + canvas.save(); + canvas.translate(photoImage.getImageX() + photoImage.getImageWidth() + AndroidUtilities.dp(10), photoImage.getImageY() + AndroidUtilities.dp(3)); + nameLayout.draw(canvas); + canvas.restore(); + + if (infoLayout != null) { + canvas.save(); + canvas.translate(photoImage.getImageX() + photoImage.getImageWidth() + AndroidUtilities.dp(10), photoImage.getImageY() + AndroidUtilities.dp(nameLayout.getLineCount() * 16 + 5)); + infoLayout.draw(canvas); + canvas.restore(); + } + } + } else if (nameLayout != null) { canvas.save(); canvas.translate(photoImage.getImageX() + photoImage.getImageWidth() + AndroidUtilities.dp(10), photoImage.getImageY() + AndroidUtilities.dp(8)); nameLayout.draw(canvas); @@ -959,20 +1124,6 @@ public class ChatMediaCell extends ChatBaseCell implements MediaController.FileD infoLayout.draw(canvas); canvas.restore(); } - } else if (infoLayout != null && (buttonState == 1 || buttonState == 0 || buttonState == 3 || currentMessageObject.isSecretPhoto())) { - infoPaint.setColor(0xffffffff); - setDrawableBounds(mediaBackgroundDrawable, photoImage.getImageX() + AndroidUtilities.dp(4), photoImage.getImageY() + AndroidUtilities.dp(4), infoWidth + AndroidUtilities.dp(8) + infoOffset, AndroidUtilities.dp(16.5f)); - mediaBackgroundDrawable.draw(canvas); - - if (currentMessageObject.type == 3) { - setDrawableBounds(videoIconDrawable, photoImage.getImageX() + AndroidUtilities.dp(8), photoImage.getImageY() + AndroidUtilities.dp(7.5f)); - videoIconDrawable.draw(canvas); - } - - canvas.save(); - canvas.translate(photoImage.getImageX() + AndroidUtilities.dp(8) + infoOffset, photoImage.getImageY() + AndroidUtilities.dp(5.5f)); - infoLayout.draw(canvas); - canvas.restore(); } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatMessageCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatMessageCell.java index 9098aaf1d..00a9477a4 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatMessageCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatMessageCell.java @@ -12,7 +12,6 @@ import android.content.Context; import android.content.Intent; import android.graphics.Canvas; import android.graphics.Paint; -import android.graphics.Path; import android.graphics.drawable.Drawable; import android.net.Uri; import android.provider.Browser; @@ -33,6 +32,7 @@ import org.telegram.messenger.FileLog; import org.telegram.android.MessageObject; import org.telegram.messenger.R; import org.telegram.messenger.TLRPC; +import org.telegram.ui.Components.ResourceLoader; import org.telegram.ui.Components.StaticLayoutEx; import org.telegram.ui.Components.URLSpanNoUnderline; @@ -42,11 +42,7 @@ public class ChatMessageCell extends ChatBaseCell { private int textX, textY; private int totalHeight = 0; - private ClickableSpan pressedLink; private int linkBlockNum; - private MyPath urlPath = new MyPath(); - private boolean linkPreviewPressed; - private static Paint urlPaint; private int lastVisibleBlockNum = 0; private int firstVisibleBlockNum = 0; @@ -58,7 +54,6 @@ public class ChatMessageCell extends ChatBaseCell { private boolean hasLinkPreview; private int linkPreviewHeight; private boolean isInstagram; - private int smallImageX; private int descriptionY; private int durationWidth; private StaticLayout siteNameLayout; @@ -67,47 +62,9 @@ public class ChatMessageCell extends ChatBaseCell { private StaticLayout durationLayout; private StaticLayout authorLayout; private static TextPaint durationPaint; - private TLRPC.PhotoSize currentPhotoObject; - private TLRPC.PhotoSize currentPhotoObjectThumb; - private boolean imageCleared; private static Drawable igvideoDrawable; - private class MyPath extends Path { - - private StaticLayout currentLayout; - private int currentLine; - private float lastTop = -1; - - public void setCurrentLayout(StaticLayout layout, int start) { - currentLayout = layout; - currentLine = layout.getLineForOffset(start); - lastTop = -1; - } - - @Override - public void addRect(float left, float top, float right, float bottom, Direction dir) { - if (lastTop == -1) { - lastTop = top; - } else if (lastTop != top) { - lastTop = top; - currentLine++; - } - float lineRight = currentLayout.getLineRight(currentLine); - float lineLeft = currentLayout.getLineLeft(currentLine); - if (left >= lineRight) { - return; - } - if (right > lineRight) { - right = lineRight; - } - if (left < lineLeft) { - left = lineLeft; - } - super.addRect(left, top, right, bottom, dir); - } - } - public ChatMessageCell(Context context) { super(context); drawForwardedName = true; @@ -118,35 +75,27 @@ public class ChatMessageCell extends ChatBaseCell { } } - private void resetPressedLink() { - if (pressedLink != null) { - pressedLink = null; - } - linkPreviewPressed = false; - invalidate(); - } - @Override public boolean onTouchEvent(MotionEvent event) { boolean result = false; if (currentMessageObject != null && currentMessageObject.textLayoutBlocks != null && !currentMessageObject.textLayoutBlocks.isEmpty() && currentMessageObject.messageText instanceof Spannable && !isPressed) { if (event.getAction() == MotionEvent.ACTION_DOWN || (linkPreviewPressed || pressedLink != null) && event.getAction() == MotionEvent.ACTION_UP) { - int x = (int)event.getX(); - int y = (int)event.getY(); + int x = (int) event.getX(); + int y = (int) event.getY(); if (x >= textX && y >= textY && x <= textX + currentMessageObject.textWidth && y <= textY + currentMessageObject.textHeight) { y -= textY; int blockNum = Math.max(0, y / currentMessageObject.blockHeight); if (blockNum < currentMessageObject.textLayoutBlocks.size()) { try { MessageObject.TextLayoutBlock block = currentMessageObject.textLayoutBlocks.get(blockNum); - x -= textX - (int)Math.ceil(block.textXOffset); + x -= textX - (int) Math.ceil(block.textXOffset); y -= block.textYOffset; final int line = block.textLayout.getLineForVertical(y); final int off = block.textLayout.getOffsetForHorizontal(line, x) + block.charactersOffset; final float left = block.textLayout.getLineLeft(line); if (left <= x && left + block.textLayout.getLineWidth(line) >= x) { - Spannable buffer = (Spannable)currentMessageObject.messageText; + Spannable buffer = (Spannable) currentMessageObject.messageText; ClickableSpan[] link = buffer.getSpans(off, off, ClickableSpan.class); if (link.length != 0) { if (event.getAction() == MotionEvent.ACTION_DOWN) { @@ -318,7 +267,7 @@ public class ChatMessageCell extends ChatBaseCell { pos--; if (stringBuilder.charAt(pos + addedChars) == ' ') { stringBuilder.replace(pos + addedChars, pos + addedChars + 1, "\n"); - } else { + } else if (stringBuilder.charAt(pos + addedChars) != '\n') { stringBuilder.insert(pos + addedChars, "\n"); addedChars++; } @@ -331,7 +280,7 @@ public class ChatMessageCell extends ChatBaseCell { @Override protected boolean isUserDataChanged() { - if (imageCleared || !hasLinkPreview && currentMessageObject.messageOwner.media != null && currentMessageObject.messageOwner.media.webpage instanceof TLRPC.TL_webPage) { + if (!hasLinkPreview && currentMessageObject.messageOwner.media != null && currentMessageObject.messageOwner.media.webpage instanceof TLRPC.TL_webPage) { return true; } //suppress warning @@ -341,14 +290,13 @@ public class ChatMessageCell extends ChatBaseCell { @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); - if (linkImageView != null) { - linkImageView.clearImage(); - if (currentPhotoObject != null) { - imageCleared = true; - currentPhotoObject = null; - currentPhotoObjectThumb = null; - } - } + linkImageView.onDetachedFromWindow(); + } + + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + linkImageView.onAttachedToWindow(); } @Override @@ -363,16 +311,12 @@ public class ChatMessageCell extends ChatBaseCell { resetPressedLink(); linkPreviewPressed = false; linkPreviewHeight = 0; - smallImageX = 0; isInstagram = false; durationLayout = null; descriptionLayout = null; titleLayout = null; siteNameLayout = null; authorLayout = null; - currentPhotoObject = null; - imageCleared = false; - currentPhotoObjectThumb = null; int maxWidth; if (AndroidUtilities.isTablet()) { @@ -425,13 +369,19 @@ public class ChatMessageCell extends ChatBaseCell { linkPreviewMaxWidth = Math.min(AndroidUtilities.displaySize.x, AndroidUtilities.displaySize.y) - AndroidUtilities.dp(80); } } + + TLRPC.TL_webPage webPage = (TLRPC.TL_webPage) messageObject.messageOwner.media.webpage; + + if (webPage.site_name != null && webPage.photo != null && webPage.site_name.toLowerCase().equals("instagram")) { + linkPreviewMaxWidth = Math.max(AndroidUtilities.displaySize.y / 3, currentMessageObject.textWidth); + } + int additinalWidth = AndroidUtilities.dp(10); int restLinesCount = 3; int additionalHeight = 0; linkPreviewMaxWidth -= additinalWidth; hasLinkPreview = true; - TLRPC.TL_webPage webPage = (TLRPC.TL_webPage) messageObject.messageOwner.media.webpage; if (currentMessageObject.photoThumbs == null && webPage.photo != null) { currentMessageObject.generateThumbs(true); @@ -475,7 +425,6 @@ public class ChatMessageCell extends ChatBaseCell { for (int a = 0; a < titleLayout.getLineCount(); a++) { int width = (int) Math.ceil(titleLayout.getLineWidth(a)); if (a < restLines) { - smallImageX = Math.max(smallImageX, width); width += AndroidUtilities.dp(48 + 2); } maxChildWidth = Math.max(maxChildWidth, width + additinalWidth); @@ -527,7 +476,6 @@ public class ChatMessageCell extends ChatBaseCell { for (int a = 0; a < descriptionLayout.getLineCount(); a++) { int width = (int) Math.ceil(descriptionLayout.getLineWidth(a)); if (a < restLines) { - smallImageX = Math.max(smallImageX, width); width += AndroidUtilities.dp(48 + 2); } maxChildWidth = Math.max(maxChildWidth, width + additinalWidth); @@ -544,8 +492,8 @@ public class ChatMessageCell extends ChatBaseCell { isSmallImage = false; } int maxPhotoWidth = smallImage ? AndroidUtilities.dp(48) : linkPreviewMaxWidth; - currentPhotoObject = FileLoader.getClosestPhotoSizeWithSize(messageObject.photoThumbs, maxPhotoWidth); - currentPhotoObjectThumb = FileLoader.getClosestPhotoSizeWithSize(messageObject.photoThumbs, 80); + TLRPC.PhotoSize currentPhotoObject = FileLoader.getClosestPhotoSizeWithSize(messageObject.photoThumbs, maxPhotoWidth, true); + TLRPC.PhotoSize currentPhotoObjectThumb = FileLoader.getClosestPhotoSizeWithSize(messageObject.photoThumbs, 80); if (currentPhotoObjectThumb == currentPhotoObject) { currentPhotoObjectThumb = null; } @@ -571,8 +519,10 @@ public class ChatMessageCell extends ChatBaseCell { float scale = width / (float) maxPhotoWidth; width /= scale; height /= scale; - if (height > AndroidUtilities.displaySize.y / 3) { - height = AndroidUtilities.displaySize.y / 3; + if (webPage.site_name != null && !webPage.site_name.toLowerCase().equals("instagram")) { + if (height > AndroidUtilities.displaySize.y / 3) { + height = AndroidUtilities.displaySize.y / 3; + } } } if (isSmallImage) { @@ -755,7 +705,7 @@ public class ChatMessageCell extends ChatBaseCell { } if (isSmallImage) { - linkImageView.setImageCoords(textX + smallImageX + AndroidUtilities.dp(12), smallImageStartY, linkImageView.getImageWidth(), linkImageView.getImageHeight()); + linkImageView.setImageCoords(textX + backgroundWidth - AndroidUtilities.dp(77), smallImageStartY, linkImageView.getImageWidth(), linkImageView.getImageHeight()); } else { linkImageView.setImageCoords(textX + AndroidUtilities.dp(10), linkPreviewY, linkImageView.getImageWidth(), linkImageView.getImageHeight()); } @@ -771,8 +721,8 @@ public class ChatMessageCell extends ChatBaseCell { if (durationLayout != null) { int x = linkImageView.getImageX() + linkImageView.getImageWidth() - AndroidUtilities.dp(8) - durationWidth; int y = linkImageView.getImageY() + linkImageView.getImageHeight() - AndroidUtilities.dp(19); - mediaBackgroundDrawable.setBounds(x - AndroidUtilities.dp(4), y - AndroidUtilities.dp(1.5f), x + durationWidth + AndroidUtilities.dp(4), y + AndroidUtilities.dp(14.5f)); - mediaBackgroundDrawable.draw(canvas); + ResourceLoader.mediaBackgroundDrawable.setBounds(x - AndroidUtilities.dp(4), y - AndroidUtilities.dp(1.5f), x + durationWidth + AndroidUtilities.dp(4), y + AndroidUtilities.dp(14.5f)); + ResourceLoader.mediaBackgroundDrawable.draw(canvas); canvas.save(); canvas.translate(x, y); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/DialogCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/DialogCell.java index 85f79f6bf..10f96292c 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/DialogCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/DialogCell.java @@ -197,9 +197,13 @@ public class DialogCell extends BaseCell { @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); - if (avatarImage != null) { - avatarImage.clearImage(); - } + avatarImage.onDetachedFromWindow(); + } + + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + avatarImage.onAttachedToWindow(); } @Override @@ -341,23 +345,36 @@ public class DialogCell extends BaseCell { } } checkMessage = false; - if (message.messageOwner.media != null && !message.isMediaEmpty()) { - currentMessagePaint = messagePrintingPaint; - messageString = Emoji.replaceEmoji(AndroidUtilities.replaceTags(String.format("%s: %s", name, message.messageText)), messagePaint.getFontMetricsInt(), AndroidUtilities.dp(20)); + if (message.caption != null) { + String mess = message.caption.toString(); + if (mess.length() > 150) { + mess = mess.substring(0, 150); + } + mess = mess.replace("\n", " "); + messageString = Emoji.replaceEmoji(AndroidUtilities.replaceTags(String.format("%s: %s", name, mess)), messagePaint.getFontMetricsInt(), AndroidUtilities.dp(20)); } else { - if (message.messageOwner.message != null) { - String mess = message.messageOwner.message; - if (mess.length() > 150) { - mess = mess.substring(0, 150); + if (message.messageOwner.media != null && !message.isMediaEmpty()) { + currentMessagePaint = messagePrintingPaint; + messageString = Emoji.replaceEmoji(AndroidUtilities.replaceTags(String.format("%s: %s", name, message.messageText)), messagePaint.getFontMetricsInt(), AndroidUtilities.dp(20)); + } else { + if (message.messageOwner.message != null) { + String mess = message.messageOwner.message; + if (mess.length() > 150) { + mess = mess.substring(0, 150); + } + mess = mess.replace("\n", " "); + messageString = Emoji.replaceEmoji(AndroidUtilities.replaceTags(String.format("%s: %s", name, mess)), messagePaint.getFontMetricsInt(), AndroidUtilities.dp(20)); } - mess = mess.replace("\n", " "); - messageString = Emoji.replaceEmoji(AndroidUtilities.replaceTags(String.format("%s: %s", name, mess.replace("<", "<").replace(">", ">"))), messagePaint.getFontMetricsInt(), AndroidUtilities.dp(20)); } } } else { - messageString = message.messageText; - if (message.messageOwner.media != null && !message.isMediaEmpty()) { - currentMessagePaint = messagePrintingPaint; + if (message.caption != null) { + messageString = message.caption; + } else { + messageString = message.messageText; + if (message.messageOwner.media != null && !message.isMediaEmpty()) { + currentMessagePaint = messagePrintingPaint; + } } } } @@ -635,10 +652,12 @@ public class DialogCell extends BaseCell { if (mask != 0) { boolean continueUpdate = false; - if (isDialogCell && (mask & MessagesController.UPDATE_MASK_USER_PRINT) != 0) { - CharSequence printString = MessagesController.getInstance().printingStrings.get(currentDialogId); - if (lastPrintString != null && printString == null || lastPrintString == null && printString != null || lastPrintString != null && printString != null && !lastPrintString.equals(printString)) { - continueUpdate = true; + if (isDialogCell) { + if ((mask & MessagesController.UPDATE_MASK_USER_PRINT) != 0) { + CharSequence printString = MessagesController.getInstance().printingStrings.get(currentDialogId); + if (lastPrintString != null && printString == null || lastPrintString == null && printString != null || lastPrintString != null && printString != null && !lastPrintString.equals(printString)) { + continueUpdate = true; + } } } if (!continueUpdate && (mask & MessagesController.UPDATE_MASK_AVATAR) != 0) { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/DrawerActionCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/DrawerActionCell.java index 2a4d708fc..3e127c02c 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/DrawerActionCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/DrawerActionCell.java @@ -15,6 +15,7 @@ import android.widget.FrameLayout; import android.widget.TextView; import org.telegram.android.AndroidUtilities; +import org.telegram.ui.Components.LayoutHelper; public class DrawerActionCell extends FrameLayout { @@ -34,8 +35,8 @@ public class DrawerActionCell extends FrameLayout { textView.setCompoundDrawablePadding(AndroidUtilities.dp(34)); addView(textView); LayoutParams layoutParams = (LayoutParams) textView.getLayoutParams(); - layoutParams.width = LayoutParams.MATCH_PARENT; - layoutParams.height = LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; + layoutParams.height = LayoutHelper.MATCH_PARENT; layoutParams.gravity = Gravity.LEFT; layoutParams.leftMargin = AndroidUtilities.dp(14); layoutParams.rightMargin = AndroidUtilities.dp(16); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/DrawerProfileCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/DrawerProfileCell.java index 706323fd6..afe12f393 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/DrawerProfileCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/DrawerProfileCell.java @@ -31,6 +31,7 @@ import org.telegram.messenger.R; import org.telegram.messenger.TLRPC; import org.telegram.ui.Components.AvatarDrawable; import org.telegram.ui.Components.BackupImageView; +import org.telegram.ui.Components.LayoutHelper; public class DrawerProfileCell extends FrameLayout { @@ -52,7 +53,7 @@ public class DrawerProfileCell extends FrameLayout { shadowView.setImageResource(R.drawable.bottom_shadow); addView(shadowView); LayoutParams layoutParams = (FrameLayout.LayoutParams) shadowView.getLayoutParams(); - layoutParams.width = LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; layoutParams.height = AndroidUtilities.dp(70); layoutParams.gravity = Gravity.LEFT | Gravity.BOTTOM; shadowView.setLayoutParams(layoutParams); @@ -78,8 +79,8 @@ public class DrawerProfileCell extends FrameLayout { nameTextView.setGravity(Gravity.LEFT); addView(nameTextView); layoutParams = (FrameLayout.LayoutParams) nameTextView.getLayoutParams(); - layoutParams.width = LayoutParams.MATCH_PARENT; - layoutParams.height = LayoutParams.WRAP_CONTENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; + layoutParams.height = LayoutHelper.WRAP_CONTENT; layoutParams.gravity = Gravity.LEFT | Gravity.BOTTOM; layoutParams.leftMargin = AndroidUtilities.dp(16); layoutParams.bottomMargin = AndroidUtilities.dp(28); @@ -95,8 +96,8 @@ public class DrawerProfileCell extends FrameLayout { phoneTextView.setGravity(Gravity.LEFT); addView(phoneTextView); layoutParams = (FrameLayout.LayoutParams) phoneTextView.getLayoutParams(); - layoutParams.width = LayoutParams.MATCH_PARENT; - layoutParams.height = LayoutParams.WRAP_CONTENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; + layoutParams.height = LayoutHelper.WRAP_CONTENT; layoutParams.gravity = Gravity.LEFT | Gravity.BOTTOM; layoutParams.leftMargin = AndroidUtilities.dp(16); layoutParams.bottomMargin = AndroidUtilities.dp(9); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/EmptyCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/EmptyCell.java index fd06f4e8e..0458b98f2 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/EmptyCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/EmptyCell.java @@ -11,8 +11,6 @@ package org.telegram.ui.Cells; import android.content.Context; import android.widget.FrameLayout; -import org.telegram.android.AndroidUtilities; - public class EmptyCell extends FrameLayout { int cellHeight; @@ -33,6 +31,6 @@ public class EmptyCell extends FrameLayout { @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(cellHeight), MeasureSpec.EXACTLY)); + super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(cellHeight, MeasureSpec.EXACTLY)); } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/GreySectionCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/GreySectionCell.java index 633e02c8e..43a5132e8 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/GreySectionCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/GreySectionCell.java @@ -16,6 +16,7 @@ import android.widget.TextView; import org.telegram.android.AndroidUtilities; import org.telegram.android.LocaleController; +import org.telegram.ui.Components.LayoutHelper; public class GreySectionCell extends FrameLayout { private TextView textView; @@ -32,8 +33,8 @@ public class GreySectionCell extends FrameLayout { textView.setGravity((LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.CENTER_VERTICAL); addView(textView); FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams)textView.getLayoutParams(); - layoutParams.width = FrameLayout.LayoutParams.MATCH_PARENT; - layoutParams.height = FrameLayout.LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; + layoutParams.height = LayoutHelper.MATCH_PARENT; layoutParams.leftMargin = AndroidUtilities.dp(16); layoutParams.rightMargin = AndroidUtilities.dp(16); layoutParams.gravity = LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/HeaderCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/HeaderCell.java index fcfd8b2e0..dd4a8eb20 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/HeaderCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/HeaderCell.java @@ -17,6 +17,7 @@ import android.widget.TextView; import org.telegram.android.AndroidUtilities; import org.telegram.android.LocaleController; +import org.telegram.ui.Components.LayoutHelper; public class HeaderCell extends FrameLayout { @@ -30,8 +31,8 @@ public class HeaderCell extends FrameLayout { textView.setGravity((LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.CENTER_VERTICAL); addView(textView); FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams)textView.getLayoutParams(); - layoutParams.width = FrameLayout.LayoutParams.MATCH_PARENT; - layoutParams.height = FrameLayout.LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; + layoutParams.height = LayoutHelper.MATCH_PARENT; layoutParams.leftMargin = AndroidUtilities.dp(17); layoutParams.rightMargin = AndroidUtilities.dp(17); layoutParams.topMargin = AndroidUtilities.dp(15); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/LetterSectionCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/LetterSectionCell.java index 40e5b3a1f..18ac212bb 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/LetterSectionCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/LetterSectionCell.java @@ -16,6 +16,7 @@ import android.widget.FrameLayout; import android.widget.TextView; import org.telegram.android.AndroidUtilities; +import org.telegram.ui.Components.LayoutHelper; public class LetterSectionCell extends FrameLayout { @@ -32,8 +33,8 @@ public class LetterSectionCell extends FrameLayout { textView.setGravity(Gravity.CENTER); addView(textView); FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams)textView.getLayoutParams(); - layoutParams.width = FrameLayout.LayoutParams.MATCH_PARENT; - layoutParams.height = FrameLayout.LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; + layoutParams.height = LayoutHelper.MATCH_PARENT; textView.setLayoutParams(layoutParams); } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/LoadingCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/LoadingCell.java index a98c5a0c2..aad139aa8 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/LoadingCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/LoadingCell.java @@ -14,6 +14,7 @@ import android.widget.FrameLayout; import android.widget.ProgressBar; import org.telegram.android.AndroidUtilities; +import org.telegram.ui.Components.LayoutHelper; public class LoadingCell extends FrameLayout { @@ -23,8 +24,8 @@ public class LoadingCell extends FrameLayout { ProgressBar progressBar = new ProgressBar(context); addView(progressBar); LayoutParams layoutParams = (FrameLayout.LayoutParams) progressBar.getLayoutParams(); - layoutParams.width = LayoutParams.WRAP_CONTENT; - layoutParams.height = LayoutParams.WRAP_CONTENT; + layoutParams.width = LayoutHelper.WRAP_CONTENT; + layoutParams.height = LayoutHelper.WRAP_CONTENT; layoutParams.gravity = Gravity.CENTER; progressBar.setLayoutParams(layoutParams); } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/LocationCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/LocationCell.java new file mode 100644 index 000000000..015083117 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/LocationCell.java @@ -0,0 +1,91 @@ +/* + * This is the source code of Telegram for Android v. 2.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-2015. + */ + +package org.telegram.ui.Cells; + +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.PorterDuff; +import android.graphics.PorterDuffColorFilter; +import android.text.TextUtils; +import android.util.TypedValue; +import android.view.Gravity; +import android.widget.FrameLayout; +import android.widget.TextView; + +import org.telegram.android.AndroidUtilities; +import org.telegram.android.LocaleController; +import org.telegram.messenger.R; +import org.telegram.messenger.TLRPC; +import org.telegram.ui.Components.BackupImageView; +import org.telegram.ui.Components.LayoutHelper; + +public class LocationCell extends FrameLayout { + + private TextView nameTextView; + private TextView addressTextView; + private BackupImageView imageView; + private boolean needDivider; + private static Paint paint; + + public LocationCell(Context context) { + super(context); + + if (paint == null) { + paint = new Paint(); + paint.setColor(0xffd9d9d9); + paint.setStrokeWidth(1); + } + + imageView = new BackupImageView(context); + imageView.setBackgroundResource(R.drawable.round_grey); + imageView.setSize(AndroidUtilities.dp(30), AndroidUtilities.dp(30)); + imageView.getImageReceiver().setColorFilter(new PorterDuffColorFilter(0xff999999, PorterDuff.Mode.MULTIPLY)); + addView(imageView, LayoutHelper.createFrame(40, 40, Gravity.TOP | (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT), LocaleController.isRTL ? 0 : 17, 8, LocaleController.isRTL ? 17 : 0, 0)); + + nameTextView = new TextView(context); + nameTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16); + nameTextView.setMaxLines(1); + nameTextView.setEllipsize(TextUtils.TruncateAt.END); + nameTextView.setSingleLine(true); + nameTextView.setTextColor(0xff212121); + nameTextView.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); + nameTextView.setGravity(LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT); + addView(nameTextView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.TOP | (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT), (LocaleController.isRTL ? 16 : 72), 5, (LocaleController.isRTL ? 72 : 16), 0)); + + addressTextView = new TextView(context); + addressTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); + addressTextView.setMaxLines(1); + addressTextView.setEllipsize(TextUtils.TruncateAt.END); + addressTextView.setSingleLine(true); + addressTextView.setTextColor(0xff999999); + addressTextView.setGravity(LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT); + addView(addressTextView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.TOP | (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT), (LocaleController.isRTL ? 16 : 72), 30, (LocaleController.isRTL ? 72 : 16), 0)); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(56) + (needDivider ? 1 : 0), MeasureSpec.EXACTLY)); + } + + public void setLocation(TLRPC.TL_messageMediaVenue location, String icon, boolean divider) { + needDivider = divider; + nameTextView.setText(location.title); + addressTextView.setText(location.address); + imageView.setImage(icon, null, null); + setWillNotDraw(!divider); + } + + @Override + protected void onDraw(Canvas canvas) { + if (needDivider) { + canvas.drawLine(AndroidUtilities.dp(72), getHeight() - 1, getWidth(), getHeight() - 1, paint); + } + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/LocationLoadingCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/LocationLoadingCell.java new file mode 100644 index 000000000..0b917d844 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/LocationLoadingCell.java @@ -0,0 +1,50 @@ +/* + * This is the source code of Telegram for Android v. 2.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-2015. + */ + +package org.telegram.ui.Cells; + +import android.content.Context; +import android.util.TypedValue; +import android.view.Gravity; +import android.widget.FrameLayout; +import android.widget.ProgressBar; +import android.widget.TextView; + +import org.telegram.android.AndroidUtilities; +import org.telegram.android.LocaleController; +import org.telegram.messenger.R; +import org.telegram.ui.Components.LayoutHelper; + +public class LocationLoadingCell extends FrameLayout { + + private ProgressBar progressBar; + private TextView textView; + + public LocationLoadingCell(Context context) { + super(context); + + progressBar = new ProgressBar(context); + addView(progressBar, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER)); + + textView = new TextView(context); + textView.setTextColor(0xff999999); + textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16); + textView.setText(LocaleController.getString("NoResult", R.string.NoResult)); + addView(textView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER)); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec((int) (AndroidUtilities.dp(56) * 2.5f), MeasureSpec.EXACTLY)); + } + + public void setLoading(boolean value) { + progressBar.setVisibility(value ? VISIBLE : INVISIBLE); + textView.setVisibility(value ? INVISIBLE : VISIBLE); + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/LocationPoweredCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/LocationPoweredCell.java new file mode 100644 index 000000000..3009f6094 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/LocationPoweredCell.java @@ -0,0 +1,53 @@ +/* + * This is the source code of Telegram for Android v. 2.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-2015. + */ + +package org.telegram.ui.Cells; + +import android.content.Context; +import android.util.TypedValue; +import android.view.Gravity; +import android.widget.FrameLayout; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.TextView; + +import org.telegram.android.AndroidUtilities; +import org.telegram.messenger.R; +import org.telegram.ui.Components.LayoutHelper; + +public class LocationPoweredCell extends FrameLayout { + + public LocationPoweredCell(Context context) { + super(context); + + LinearLayout linearLayout = new LinearLayout(context); + addView(linearLayout, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER)); + + TextView textView = new TextView(context); + textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16); + textView.setTextColor(0xff999999); + textView.setText("Powered by"); + linearLayout.addView(textView, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT)); + + ImageView imageView = new ImageView(context); + imageView.setImageResource(R.drawable.foursquare); + imageView.setPadding(0, AndroidUtilities.dp(2), 0, 0); + linearLayout.addView(imageView, LayoutHelper.createLinear(35, LayoutHelper.WRAP_CONTENT)); + + textView = new TextView(context); + textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16); + textView.setTextColor(0xff999999); + textView.setText("Foursquare"); + linearLayout.addView(textView, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT)); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(56), MeasureSpec.EXACTLY)); + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/MentionCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/MentionCell.java index 67111a811..05cd5b6b9 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/MentionCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/MentionCell.java @@ -20,6 +20,7 @@ import org.telegram.android.ContactsController; import org.telegram.messenger.TLRPC; import org.telegram.ui.Components.AvatarDrawable; import org.telegram.ui.Components.BackupImageView; +import org.telegram.ui.Components.LayoutHelper; public class MentionCell extends LinearLayout { @@ -55,8 +56,8 @@ public class MentionCell extends LinearLayout { addView(nameTextView); layoutParams = (LayoutParams) nameTextView.getLayoutParams(); layoutParams.leftMargin = AndroidUtilities.dp(12); - layoutParams.width = LayoutParams.WRAP_CONTENT; - layoutParams.height = LayoutParams.WRAP_CONTENT; + layoutParams.width = LayoutHelper.WRAP_CONTENT; + layoutParams.height = LayoutHelper.WRAP_CONTENT; layoutParams.gravity = Gravity.CENTER_VERTICAL; nameTextView.setLayoutParams(layoutParams); @@ -69,8 +70,8 @@ public class MentionCell extends LinearLayout { addView(usernameTextView); layoutParams = (LayoutParams) usernameTextView.getLayoutParams(); layoutParams.leftMargin = AndroidUtilities.dp(12); - layoutParams.width = LayoutParams.WRAP_CONTENT; - layoutParams.height = LayoutParams.WRAP_CONTENT; + layoutParams.width = LayoutHelper.WRAP_CONTENT; + layoutParams.height = LayoutHelper.WRAP_CONTENT; layoutParams.gravity = Gravity.CENTER_VERTICAL; usernameTextView.setLayoutParams(layoutParams); } @@ -104,4 +105,14 @@ public class MentionCell extends LinearLayout { usernameTextView.setVisibility(INVISIBLE); nameTextView.setText(text); } + + public void setIsDarkTheme(boolean isDarkTheme) { + if (isDarkTheme) { + nameTextView.setTextColor(0xffffffff); + usernameTextView.setTextColor(0xff999999); + } else { + nameTextView.setTextColor(0xff000000); + usernameTextView.setTextColor(0xff999999); + } + } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/PhotoEditToolCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/PhotoEditToolCell.java index 25d67fe46..f723a5371 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/PhotoEditToolCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/PhotoEditToolCell.java @@ -17,6 +17,7 @@ import android.widget.TextView; import org.telegram.android.AndroidUtilities; import org.telegram.ui.Components.FrameLayoutFixed; +import org.telegram.ui.Components.LayoutHelper; public class PhotoEditToolCell extends FrameLayoutFixed { @@ -31,8 +32,8 @@ public class PhotoEditToolCell extends FrameLayoutFixed { iconImage.setScaleType(ImageView.ScaleType.CENTER); addView(iconImage); LayoutParams layoutParams = (LayoutParams) iconImage.getLayoutParams(); - layoutParams.width = LayoutParams.MATCH_PARENT; - layoutParams.height = LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; + layoutParams.height = LayoutHelper.MATCH_PARENT; layoutParams.bottomMargin = AndroidUtilities.dp(12); iconImage.setLayoutParams(layoutParams); @@ -46,8 +47,8 @@ public class PhotoEditToolCell extends FrameLayoutFixed { nameTextView.setEllipsize(TextUtils.TruncateAt.END); addView(nameTextView); layoutParams = (LayoutParams) nameTextView.getLayoutParams(); - layoutParams.width = LayoutParams.MATCH_PARENT; - layoutParams.height = LayoutParams.WRAP_CONTENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; + layoutParams.height = LayoutHelper.WRAP_CONTENT; layoutParams.gravity = Gravity.LEFT | Gravity.BOTTOM; layoutParams.leftMargin = AndroidUtilities.dp(4); layoutParams.rightMargin = AndroidUtilities.dp(4); @@ -59,8 +60,8 @@ public class PhotoEditToolCell extends FrameLayoutFixed { valueTextView.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); addView(valueTextView); layoutParams = (LayoutParams) valueTextView.getLayoutParams(); - layoutParams.width = LayoutParams.WRAP_CONTENT; - layoutParams.height = LayoutParams.WRAP_CONTENT; + layoutParams.width = LayoutHelper.WRAP_CONTENT; + layoutParams.height = LayoutHelper.WRAP_CONTENT; layoutParams.gravity = Gravity.LEFT | Gravity.TOP; layoutParams.leftMargin = AndroidUtilities.dp(57); layoutParams.topMargin = AndroidUtilities.dp(3); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/PhotoPickerAlbumsCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/PhotoPickerAlbumsCell.java index 16540b5da..d469e9503 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/PhotoPickerAlbumsCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/PhotoPickerAlbumsCell.java @@ -23,6 +23,7 @@ import org.telegram.android.MediaController; import org.telegram.messenger.R; import org.telegram.ui.Components.BackupImageView; import org.telegram.ui.Components.FrameLayoutFixed; +import org.telegram.ui.Components.LayoutHelper; public class PhotoPickerAlbumsCell extends FrameLayoutFixed { @@ -48,8 +49,8 @@ public class PhotoPickerAlbumsCell extends FrameLayoutFixed { imageView = new BackupImageView(context); addView(imageView); LayoutParams layoutParams = (LayoutParams) imageView.getLayoutParams(); - layoutParams.width = LayoutParams.MATCH_PARENT; - layoutParams.height = LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; + layoutParams.height = LayoutHelper.MATCH_PARENT; imageView.setLayoutParams(layoutParams); LinearLayout linearLayout = new LinearLayout(context); @@ -57,7 +58,7 @@ public class PhotoPickerAlbumsCell extends FrameLayoutFixed { linearLayout.setBackgroundColor(0x7f000000); addView(linearLayout); layoutParams = (LayoutParams) linearLayout.getLayoutParams(); - layoutParams.width = LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; layoutParams.height = AndroidUtilities.dp(28); layoutParams.gravity = Gravity.BOTTOM; linearLayout.setLayoutParams(layoutParams); @@ -72,7 +73,7 @@ public class PhotoPickerAlbumsCell extends FrameLayoutFixed { linearLayout.addView(nameTextView); LinearLayout.LayoutParams layoutParams1 = (LinearLayout.LayoutParams) nameTextView.getLayoutParams(); layoutParams1.width = 0; - layoutParams1.height = LinearLayout.LayoutParams.MATCH_PARENT; + layoutParams1.height = LayoutHelper.MATCH_PARENT; layoutParams1.leftMargin = AndroidUtilities.dp(8); layoutParams1.weight = 1; nameTextView.setLayoutParams(layoutParams1); @@ -86,8 +87,8 @@ public class PhotoPickerAlbumsCell extends FrameLayoutFixed { countTextView.setGravity(Gravity.CENTER_VERTICAL); linearLayout.addView(countTextView); layoutParams1 = (LinearLayout.LayoutParams) countTextView.getLayoutParams(); - layoutParams1.width = LinearLayout.LayoutParams.WRAP_CONTENT; - layoutParams1.height = LinearLayout.LayoutParams.MATCH_PARENT; + layoutParams1.width = LayoutHelper.WRAP_CONTENT; + layoutParams1.height = LayoutHelper.MATCH_PARENT; layoutParams1.leftMargin = AndroidUtilities.dp(4); layoutParams1.rightMargin = AndroidUtilities.dp(4); countTextView.setLayoutParams(layoutParams1); @@ -96,8 +97,8 @@ public class PhotoPickerAlbumsCell extends FrameLayoutFixed { selector.setBackgroundResource(R.drawable.list_selector); addView(selector); layoutParams = (LayoutParams) selector.getLayoutParams(); - layoutParams.width = LayoutParams.MATCH_PARENT; - layoutParams.height = LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; + layoutParams.height = LayoutHelper.MATCH_PARENT; selector.setLayoutParams(layoutParams); } @@ -149,7 +150,11 @@ public class PhotoPickerAlbumsCell extends FrameLayoutFixed { albumView.imageView.setOrientation(0, true); if (albumEntry.coverPhoto != null && albumEntry.coverPhoto.path != null) { albumView.imageView.setOrientation(albumEntry.coverPhoto.orientation, true); - albumView.imageView.setImage("thumb://" + albumEntry.coverPhoto.imageId + ":" + albumEntry.coverPhoto.path, null, getContext().getResources().getDrawable(R.drawable.nophotos)); + if (albumEntry.coverPhoto.isVideo) { + albumView.imageView.setImage("vthumb://" + albumEntry.coverPhoto.imageId + ":" + albumEntry.coverPhoto.path, null, getContext().getResources().getDrawable(R.drawable.nophotos)); + } else { + albumView.imageView.setImage("thumb://" + albumEntry.coverPhoto.imageId + ":" + albumEntry.coverPhoto.path, null, getContext().getResources().getDrawable(R.drawable.nophotos)); + } } else { albumView.imageView.setImageResource(R.drawable.nophotos); } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/PhotoPickerPhotoCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/PhotoPickerPhotoCell.java index f00434920..40005597d 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/PhotoPickerPhotoCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/PhotoPickerPhotoCell.java @@ -16,6 +16,7 @@ import org.telegram.android.AndroidUtilities; import org.telegram.messenger.R; import org.telegram.ui.Components.BackupImageView; import org.telegram.ui.Components.CheckBox; +import org.telegram.ui.Components.LayoutHelper; public class PhotoPickerPhotoCell extends FrameLayout { @@ -30,8 +31,8 @@ public class PhotoPickerPhotoCell extends FrameLayout { photoImage = new BackupImageView(context); addView(photoImage); LayoutParams layoutParams = (LayoutParams) photoImage.getLayoutParams(); - layoutParams.width = LayoutParams.MATCH_PARENT; - layoutParams.height = LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; + layoutParams.height = LayoutHelper.MATCH_PARENT; photoImage.setLayoutParams(layoutParams); checkFrame = new FrameLayout(context); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/PhotoPickerSearchCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/PhotoPickerSearchCell.java index e24419ed1..bc3ce4f10 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/PhotoPickerSearchCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/PhotoPickerSearchCell.java @@ -23,6 +23,7 @@ import android.widget.TextView; import org.telegram.android.AndroidUtilities; import org.telegram.android.LocaleController; import org.telegram.messenger.R; +import org.telegram.ui.Components.LayoutHelper; public class PhotoPickerSearchCell extends LinearLayout { @@ -46,8 +47,8 @@ public class PhotoPickerSearchCell extends LinearLayout { selector.setBackgroundResource(R.drawable.list_selector); addView(selector); FrameLayout.LayoutParams layoutParams1 = (FrameLayout.LayoutParams) selector.getLayoutParams(); - layoutParams1.width = LayoutParams.MATCH_PARENT; - layoutParams1.height = LayoutParams.MATCH_PARENT; + layoutParams1.width = LayoutHelper.MATCH_PARENT; + layoutParams1.height = LayoutHelper.MATCH_PARENT; selector.setLayoutParams(layoutParams1); imageView = new ImageView(context); @@ -68,8 +69,8 @@ public class PhotoPickerSearchCell extends LinearLayout { textView1.setEllipsize(TextUtils.TruncateAt.END); addView(textView1); layoutParams1 = (FrameLayout.LayoutParams) textView1.getLayoutParams(); - layoutParams1.width = LayoutParams.MATCH_PARENT; - layoutParams1.height = LayoutParams.WRAP_CONTENT; + layoutParams1.width = LayoutHelper.MATCH_PARENT; + layoutParams1.height = LayoutHelper.WRAP_CONTENT; layoutParams1.gravity = Gravity.TOP | Gravity.LEFT; layoutParams1.rightMargin = AndroidUtilities.dp(4); layoutParams1.leftMargin = AndroidUtilities.dp(51); @@ -85,8 +86,8 @@ public class PhotoPickerSearchCell extends LinearLayout { textView2.setEllipsize(TextUtils.TruncateAt.END); addView(textView2); layoutParams1 = (FrameLayout.LayoutParams) textView2.getLayoutParams(); - layoutParams1.width = LayoutParams.MATCH_PARENT; - layoutParams1.height = LayoutParams.WRAP_CONTENT; + layoutParams1.width = LayoutHelper.MATCH_PARENT; + layoutParams1.height = LayoutHelper.WRAP_CONTENT; layoutParams1.gravity = Gravity.TOP | Gravity.LEFT; layoutParams1.leftMargin = AndroidUtilities.dp(51); layoutParams1.rightMargin = AndroidUtilities.dp(4); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/ProfileSearchCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/ProfileSearchCell.java index b6ce6a2f8..80bfa4f28 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/ProfileSearchCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/ProfileSearchCell.java @@ -117,10 +117,13 @@ public class ProfileSearchCell extends BaseCell { @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); - if (avatarImage != null) { - avatarImage.clearImage(); - lastAvatar = null; - } + avatarImage.onDetachedFromWindow(); + } + + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + avatarImage.onAttachedToWindow(); } @Override diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/SendLocationCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/SendLocationCell.java new file mode 100644 index 000000000..46a691978 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/SendLocationCell.java @@ -0,0 +1,57 @@ +/* + * This is the source code of Telegram for Android v. 2.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-2015. + */ + +package org.telegram.ui.Cells; + +import android.content.Context; +import android.view.Gravity; +import android.widget.FrameLayout; +import android.widget.ImageView; + +import org.telegram.android.AndroidUtilities; +import org.telegram.android.LocaleController; +import org.telegram.messenger.R; +import org.telegram.ui.Components.LayoutHelper; +import org.telegram.ui.Components.SimpleTextView; + +public class SendLocationCell extends FrameLayout { + + private SimpleTextView accurateTextView; + private SimpleTextView titleTextView; + + public SendLocationCell(Context context) { + super(context); + + ImageView imageView = new ImageView(context); + imageView.setImageResource(R.drawable.pin); + addView(imageView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.TOP | (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT), LocaleController.isRTL ? 0 : 17, 13, LocaleController.isRTL ? 17 : 0, 0)); + + titleTextView = new SimpleTextView(context); + titleTextView.setTextSize(16); + titleTextView.setTextColor(0xff377aae); + titleTextView.setGravity(LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT); + titleTextView.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); + addView(titleTextView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 20, Gravity.TOP | (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT), LocaleController.isRTL ? 16 : 73, 12, LocaleController.isRTL ? 73 : 16, 0)); + + accurateTextView = new SimpleTextView(context); + accurateTextView.setTextSize(14); + accurateTextView.setTextColor(0xff999999); + accurateTextView.setGravity(LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT); + addView(accurateTextView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 20, Gravity.TOP | (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT), LocaleController.isRTL ? 16 : 73, 37, LocaleController.isRTL ? 73 : 16, 0)); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(66), MeasureSpec.EXACTLY)); + } + + public void setText(String title, String text) { + titleTextView.setText(title); + accurateTextView.setText(text); + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/SessionCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/SessionCell.java index 924a851f7..a76d1e389 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/SessionCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/SessionCell.java @@ -22,6 +22,7 @@ import org.telegram.android.AndroidUtilities; import org.telegram.android.LocaleController; import org.telegram.messenger.R; import org.telegram.messenger.TLRPC; +import org.telegram.ui.Components.LayoutHelper; import java.util.Locale; @@ -48,7 +49,7 @@ public class SessionCell extends FrameLayout { linearLayout.setWeightSum(1); addView(linearLayout); LayoutParams layoutParams = (LayoutParams) linearLayout.getLayoutParams(); - layoutParams.width = LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; layoutParams.height = AndroidUtilities.dp(30); layoutParams.leftMargin = AndroidUtilities.dp(17); layoutParams.rightMargin = AndroidUtilities.dp(17); @@ -80,7 +81,7 @@ public class SessionCell extends FrameLayout { LinearLayout.LayoutParams layoutParams2 = (LinearLayout.LayoutParams) nameTextView.getLayoutParams(); layoutParams2.width = 0; - layoutParams2.height = LayoutParams.MATCH_PARENT; + layoutParams2.height = LayoutHelper.MATCH_PARENT; layoutParams2.weight = 1; if (LocaleController.isRTL) { layoutParams2.leftMargin = AndroidUtilities.dp(10); @@ -91,8 +92,8 @@ public class SessionCell extends FrameLayout { nameTextView.setLayoutParams(layoutParams2); layoutParams2 = (LinearLayout.LayoutParams) onlineTextView.getLayoutParams(); - layoutParams2.width = LayoutParams.WRAP_CONTENT; - layoutParams2.height = LayoutParams.MATCH_PARENT; + layoutParams2.width = LayoutHelper.WRAP_CONTENT; + layoutParams2.height = LayoutHelper.MATCH_PARENT; layoutParams2.topMargin = AndroidUtilities.dp(2); layoutParams2.gravity = (LocaleController.isRTL ? Gravity.LEFT : Gravity.RIGHT) | Gravity.TOP; onlineTextView.setLayoutParams(layoutParams2); @@ -107,8 +108,8 @@ public class SessionCell extends FrameLayout { detailTextView.setGravity((LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP); addView(detailTextView); layoutParams = (LayoutParams) detailTextView.getLayoutParams(); - layoutParams.width = LayoutParams.MATCH_PARENT; - layoutParams.height = LayoutParams.WRAP_CONTENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; + layoutParams.height = LayoutHelper.WRAP_CONTENT; layoutParams.leftMargin = AndroidUtilities.dp(17); layoutParams.rightMargin = AndroidUtilities.dp(17); layoutParams.topMargin = AndroidUtilities.dp(36); @@ -125,8 +126,8 @@ public class SessionCell extends FrameLayout { detailExTextView.setGravity((LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP); addView(detailExTextView); layoutParams = (LayoutParams) detailExTextView.getLayoutParams(); - layoutParams.width = LayoutParams.MATCH_PARENT; - layoutParams.height = LayoutParams.WRAP_CONTENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; + layoutParams.height = LayoutHelper.WRAP_CONTENT; layoutParams.leftMargin = AndroidUtilities.dp(17); layoutParams.rightMargin = AndroidUtilities.dp(17); layoutParams.topMargin = AndroidUtilities.dp(59); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/SharedDocumentCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/SharedDocumentCell.java index a41172b06..92f83da3d 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/SharedDocumentCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/SharedDocumentCell.java @@ -31,6 +31,7 @@ import org.telegram.messenger.TLRPC; import org.telegram.messenger.Utilities; import org.telegram.ui.Components.BackupImageView; import org.telegram.ui.Components.CheckBox; +import org.telegram.ui.Components.LayoutHelper; import org.telegram.ui.Components.LineProgressView; import java.io.File; @@ -98,7 +99,7 @@ public class SharedDocumentCell extends FrameLayout implements MediaController.F addView(extTextView); layoutParams = (LayoutParams) extTextView.getLayoutParams(); layoutParams.width = AndroidUtilities.dp(32); - layoutParams.height = LayoutParams.WRAP_CONTENT; + layoutParams.height = LayoutHelper.WRAP_CONTENT; layoutParams.topMargin = AndroidUtilities.dp(22); layoutParams.leftMargin = LocaleController.isRTL ? 0 : AndroidUtilities.dp(16); layoutParams.rightMargin = LocaleController.isRTL ? AndroidUtilities.dp(16) : 0; @@ -134,8 +135,8 @@ public class SharedDocumentCell extends FrameLayout implements MediaController.F nameTextView.setGravity((LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.CENTER_VERTICAL); addView(nameTextView); layoutParams = (LayoutParams) nameTextView.getLayoutParams(); - layoutParams.width = LayoutParams.MATCH_PARENT; - layoutParams.height = LayoutParams.WRAP_CONTENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; + layoutParams.height = LayoutHelper.WRAP_CONTENT; layoutParams.topMargin = AndroidUtilities.dp(5); layoutParams.leftMargin = LocaleController.isRTL ? AndroidUtilities.dp(8) : AndroidUtilities.dp(72); layoutParams.rightMargin = LocaleController.isRTL ? AndroidUtilities.dp(72) : AndroidUtilities.dp(8); @@ -146,8 +147,8 @@ public class SharedDocumentCell extends FrameLayout implements MediaController.F statusImageView.setVisibility(INVISIBLE); addView(statusImageView); layoutParams = (LayoutParams) statusImageView.getLayoutParams(); - layoutParams.width = LayoutParams.WRAP_CONTENT; - layoutParams.height = LayoutParams.WRAP_CONTENT; + layoutParams.width = LayoutHelper.WRAP_CONTENT; + layoutParams.height = LayoutHelper.WRAP_CONTENT; layoutParams.topMargin = AndroidUtilities.dp(35); layoutParams.leftMargin = LocaleController.isRTL ? AndroidUtilities.dp(8) : AndroidUtilities.dp(72); layoutParams.rightMargin = LocaleController.isRTL ? AndroidUtilities.dp(72) : AndroidUtilities.dp(8); @@ -164,8 +165,8 @@ public class SharedDocumentCell extends FrameLayout implements MediaController.F dateTextView.setGravity((LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.CENTER_VERTICAL); addView(dateTextView); layoutParams = (LayoutParams) dateTextView.getLayoutParams(); - layoutParams.width = LayoutParams.MATCH_PARENT; - layoutParams.height = LayoutParams.WRAP_CONTENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; + layoutParams.height = LayoutHelper.WRAP_CONTENT; layoutParams.topMargin = AndroidUtilities.dp(30); layoutParams.leftMargin = LocaleController.isRTL ? AndroidUtilities.dp(8) : AndroidUtilities.dp(72); layoutParams.rightMargin = LocaleController.isRTL ? AndroidUtilities.dp(72) : AndroidUtilities.dp(8); @@ -175,7 +176,7 @@ public class SharedDocumentCell extends FrameLayout implements MediaController.F progressView = new LineProgressView(context); addView(progressView); layoutParams = (LayoutParams) progressView.getLayoutParams(); - layoutParams.width = LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; layoutParams.height = AndroidUtilities.dp(2); layoutParams.topMargin = AndroidUtilities.dp(54); layoutParams.leftMargin = LocaleController.isRTL ? 0 : AndroidUtilities.dp(72); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/SharedMediaSectionCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/SharedMediaSectionCell.java index 4b7d29c58..7c39b0bf8 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/SharedMediaSectionCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/SharedMediaSectionCell.java @@ -16,6 +16,7 @@ import android.widget.TextView; import org.telegram.android.AndroidUtilities; import org.telegram.android.LocaleController; +import org.telegram.ui.Components.LayoutHelper; public class SharedMediaSectionCell extends FrameLayout { @@ -31,8 +32,8 @@ public class SharedMediaSectionCell extends FrameLayout { textView.setGravity((LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.CENTER_VERTICAL); addView(textView); FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams)textView.getLayoutParams(); - layoutParams.width = FrameLayout.LayoutParams.MATCH_PARENT; - layoutParams.height = FrameLayout.LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; + layoutParams.height = LayoutHelper.MATCH_PARENT; layoutParams.leftMargin = AndroidUtilities.dp(13); layoutParams.rightMargin = AndroidUtilities.dp(13); layoutParams.gravity = LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/SharedPhotoVideoCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/SharedPhotoVideoCell.java index 7b6085d45..954632263 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/SharedPhotoVideoCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/SharedPhotoVideoCell.java @@ -27,6 +27,7 @@ import org.telegram.messenger.TLRPC; import org.telegram.ui.Components.BackupImageView; import org.telegram.ui.Components.CheckBox; import org.telegram.ui.Components.FrameLayoutFixed; +import org.telegram.ui.Components.LayoutHelper; import org.telegram.ui.PhotoViewer; public class SharedPhotoVideoCell extends FrameLayoutFixed { @@ -57,63 +58,36 @@ public class SharedPhotoVideoCell extends FrameLayoutFixed { imageView = new BackupImageView(context); imageView.getImageReceiver().setNeedsQualityThumb(true); imageView.getImageReceiver().setShouldGenerateQualityThumb(true); - addView(imageView); - LayoutParams layoutParams = (LayoutParams) imageView.getLayoutParams(); - layoutParams.width = LayoutParams.MATCH_PARENT; - layoutParams.height = LayoutParams.MATCH_PARENT; - imageView.setLayoutParams(layoutParams); + addView(imageView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT)); videoInfoContainer = new LinearLayout(context); videoInfoContainer.setOrientation(LinearLayout.HORIZONTAL); videoInfoContainer.setBackgroundResource(R.drawable.phototime); videoInfoContainer.setPadding(AndroidUtilities.dp(3), 0, AndroidUtilities.dp(3), 0); videoInfoContainer.setGravity(Gravity.CENTER_VERTICAL); - addView(videoInfoContainer); - layoutParams = (LayoutParams) videoInfoContainer.getLayoutParams(); - layoutParams.width = LayoutParams.MATCH_PARENT; - layoutParams.height = AndroidUtilities.dp(16); - layoutParams.gravity = Gravity.BOTTOM | Gravity.LEFT; - videoInfoContainer.setLayoutParams(layoutParams); + addView(videoInfoContainer, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 16, Gravity.BOTTOM | Gravity.LEFT)); ImageView imageView1 = new ImageView(context); imageView1.setImageResource(R.drawable.ic_video); videoInfoContainer.addView(imageView1); LinearLayout.LayoutParams layoutParams1 = (LinearLayout.LayoutParams) imageView1.getLayoutParams(); - layoutParams1.width = LinearLayout.LayoutParams.WRAP_CONTENT; - layoutParams1.height = LinearLayout.LayoutParams.WRAP_CONTENT; + layoutParams1.width = LayoutHelper.WRAP_CONTENT; + layoutParams1.height = LayoutHelper.WRAP_CONTENT; imageView1.setLayoutParams(layoutParams1); videoTextView = new TextView(context); videoTextView.setTextColor(0xffffffff); videoTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 12); videoTextView.setGravity(Gravity.CENTER_VERTICAL); - videoInfoContainer.addView(videoTextView); - layoutParams1 = (LinearLayout.LayoutParams) videoTextView.getLayoutParams(); - layoutParams1.width = LinearLayout.LayoutParams.WRAP_CONTENT; - layoutParams1.height = LinearLayout.LayoutParams.WRAP_CONTENT; - layoutParams1.leftMargin = AndroidUtilities.dp(4); - layoutParams1.gravity = Gravity.CENTER_VERTICAL; - layoutParams1.bottomMargin = AndroidUtilities.dp(1); - videoTextView.setLayoutParams(layoutParams1); + videoInfoContainer.addView(videoTextView, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER_VERTICAL, 4, 0, 0, 1)); selector = new View(context); selector.setBackgroundResource(R.drawable.list_selector); - addView(selector); - layoutParams = (LayoutParams) selector.getLayoutParams(); - layoutParams.width = LayoutParams.MATCH_PARENT; - layoutParams.height = LayoutParams.MATCH_PARENT; - selector.setLayoutParams(layoutParams); + addView(selector, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT)); checkBox = new CheckBox(context, R.drawable.round_check2); checkBox.setVisibility(INVISIBLE); - addView(checkBox); - layoutParams = (LayoutParams) checkBox.getLayoutParams(); - layoutParams.width = AndroidUtilities.dp(22); - layoutParams.height = AndroidUtilities.dp(22); - layoutParams.gravity = Gravity.RIGHT | Gravity.TOP; - layoutParams.topMargin = AndroidUtilities.dp(6); - layoutParams.rightMargin = AndroidUtilities.dp(6); - checkBox.setLayoutParams(layoutParams); + addView(checkBox, LayoutHelper.createFrame(22, 22, Gravity.RIGHT | Gravity.TOP, 6, 0, 6, 0)); } @Override diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/StickerEmojiCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/StickerEmojiCell.java new file mode 100644 index 000000000..72b8db653 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/StickerEmojiCell.java @@ -0,0 +1,53 @@ +/* + * This is the source code of Telegram for Android v. 2.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-2015. + */ + +package org.telegram.ui.Cells; + +import android.content.Context; +import android.graphics.drawable.Drawable; +import android.view.Gravity; +import android.widget.FrameLayout; + +import org.telegram.messenger.TLRPC; +import org.telegram.ui.Components.BackupImageView; +import org.telegram.ui.Components.LayoutHelper; + +public class StickerEmojiCell extends FrameLayout { + + private BackupImageView imageView; + private TLRPC.Document sticker; + + public StickerEmojiCell(Context context) { + super(context); + + imageView = new BackupImageView(context); + imageView.setAspectFit(true); + addView(imageView, LayoutHelper.createFrame(66, 66, Gravity.CENTER)); + } + + @Override + public void setPressed(boolean pressed) { + if (imageView.getImageReceiver().getPressed() != pressed) { + imageView.getImageReceiver().setPressed(pressed); + imageView.invalidate(); + } + super.setPressed(pressed); + } + + public TLRPC.Document getSticker() { + return sticker; + } + + public void setSticker(TLRPC.Document document) { + if (document != null) { + sticker = document; + document.thumb.location.ext = "webp"; + imageView.setImage(document.thumb.location, null, (Drawable) null); + } + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/TextBlockCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/TextBlockCell.java new file mode 100644 index 000000000..15f7b3d13 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/TextBlockCell.java @@ -0,0 +1,60 @@ +/* + * This is the source code of Telegram for Android v. 2.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-2015. + */ + +package org.telegram.ui.Cells; + +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.util.TypedValue; +import android.view.Gravity; +import android.widget.FrameLayout; +import android.widget.TextView; + +import org.telegram.android.LocaleController; +import org.telegram.ui.Components.LayoutHelper; + +public class TextBlockCell extends FrameLayout { + + private TextView textView; + private static Paint paint; + private boolean needDivider; + + public TextBlockCell(Context context) { + super(context); + + if (paint == null) { + paint = new Paint(); + paint.setColor(0xffd9d9d9); + paint.setStrokeWidth(1); + } + + textView = new TextView(context); + 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)); + } + + public void setTextColor(int color) { + textView.setTextColor(color); + } + + public void setText(String text, boolean divider) { + textView.setText(text); + needDivider = divider; + setWillNotDraw(!divider); + } + + @Override + protected void onDraw(Canvas canvas) { + if (needDivider) { + canvas.drawLine(getPaddingLeft(), getHeight() - 1, getWidth() - getPaddingRight(), getHeight() - 1, paint); + } + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/TextCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/TextCell.java index d86331002..e6dfa544b 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/TextCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/TextCell.java @@ -19,6 +19,7 @@ import android.widget.TextView; import org.telegram.android.AndroidUtilities; import org.telegram.android.LocaleController; +import org.telegram.ui.Components.LayoutHelper; public class TextCell extends FrameLayout { @@ -40,8 +41,8 @@ public class TextCell extends FrameLayout { textView.setGravity((LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.CENTER_VERTICAL); addView(textView); LayoutParams layoutParams = (LayoutParams) textView.getLayoutParams(); - layoutParams.width = LayoutParams.MATCH_PARENT; - layoutParams.height = LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; + layoutParams.height = LayoutHelper.MATCH_PARENT; layoutParams.leftMargin = AndroidUtilities.dp(LocaleController.isRTL ? 16 : 71); layoutParams.rightMargin = AndroidUtilities.dp(LocaleController.isRTL ? 71 : 16); layoutParams.gravity = LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT; @@ -56,8 +57,8 @@ public class TextCell extends FrameLayout { valueTextView.setGravity((LocaleController.isRTL ? Gravity.LEFT : Gravity.RIGHT) | Gravity.CENTER_VERTICAL); addView(valueTextView); layoutParams = (LayoutParams) valueTextView.getLayoutParams(); - layoutParams.width = LayoutParams.WRAP_CONTENT; - layoutParams.height = LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.WRAP_CONTENT; + layoutParams.height = LayoutHelper.MATCH_PARENT; layoutParams.leftMargin = AndroidUtilities.dp(LocaleController.isRTL ? 24 : 0); layoutParams.rightMargin = AndroidUtilities.dp(LocaleController.isRTL ? 0 : 24); layoutParams.gravity = LocaleController.isRTL ? Gravity.LEFT : Gravity.RIGHT; @@ -67,8 +68,8 @@ public class TextCell extends FrameLayout { imageView.setScaleType(ImageView.ScaleType.CENTER); addView(imageView); layoutParams = (LayoutParams) imageView.getLayoutParams(); - layoutParams.width = LayoutParams.WRAP_CONTENT; - layoutParams.height = LayoutParams.WRAP_CONTENT; + layoutParams.width = LayoutHelper.WRAP_CONTENT; + layoutParams.height = LayoutHelper.WRAP_CONTENT; layoutParams.leftMargin = AndroidUtilities.dp(LocaleController.isRTL ? 0 : 16); layoutParams.rightMargin = AndroidUtilities.dp(LocaleController.isRTL ? 16 : 0); layoutParams.gravity = (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.CENTER_VERTICAL; @@ -78,8 +79,8 @@ public class TextCell extends FrameLayout { valueImageView.setScaleType(ImageView.ScaleType.CENTER); addView(valueImageView); layoutParams = (LayoutParams) valueImageView.getLayoutParams(); - layoutParams.width = LayoutParams.WRAP_CONTENT; - layoutParams.height = LayoutParams.WRAP_CONTENT; + layoutParams.width = LayoutHelper.WRAP_CONTENT; + layoutParams.height = LayoutHelper.WRAP_CONTENT; layoutParams.leftMargin = AndroidUtilities.dp(LocaleController.isRTL ? 24 : 0); layoutParams.rightMargin = AndroidUtilities.dp(LocaleController.isRTL ? 0 : 24); layoutParams.gravity = (LocaleController.isRTL ? Gravity.LEFT : Gravity.RIGHT) | Gravity.CENTER_VERTICAL; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/TextCheckCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/TextCheckCell.java index 4e5da0292..6734071ec 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/TextCheckCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/TextCheckCell.java @@ -19,6 +19,7 @@ import android.widget.TextView; import org.telegram.android.AndroidUtilities; import org.telegram.android.LocaleController; import org.telegram.ui.Components.FrameLayoutFixed; +import org.telegram.ui.Components.LayoutHelper; import org.telegram.ui.Components.Switch; public class TextCheckCell extends FrameLayoutFixed { @@ -46,8 +47,8 @@ public class TextCheckCell extends FrameLayoutFixed { textView.setGravity((LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.CENTER_VERTICAL); addView(textView); LayoutParams layoutParams = (LayoutParams) textView.getLayoutParams(); - layoutParams.width = LayoutParams.MATCH_PARENT; - layoutParams.height = LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; + layoutParams.height = LayoutHelper.MATCH_PARENT; layoutParams.leftMargin = AndroidUtilities.dp(17); layoutParams.rightMargin = AndroidUtilities.dp(17); layoutParams.gravity = LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT; @@ -60,8 +61,8 @@ public class TextCheckCell extends FrameLayoutFixed { checkBox.setClickable(false); addView(checkBox); layoutParams = (LayoutParams) checkBox.getLayoutParams(); - layoutParams.width = LayoutParams.WRAP_CONTENT; - layoutParams.height = LayoutParams.WRAP_CONTENT; + layoutParams.width = LayoutHelper.WRAP_CONTENT; + layoutParams.height = LayoutHelper.WRAP_CONTENT; layoutParams.leftMargin = AndroidUtilities.dp(14); layoutParams.rightMargin = AndroidUtilities.dp(14); layoutParams.gravity = (LocaleController.isRTL ? Gravity.LEFT : Gravity.RIGHT) | Gravity.CENTER_VERTICAL; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/TextColorCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/TextColorCell.java index 59b6c9e61..348e5b7b5 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/TextColorCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/TextColorCell.java @@ -22,6 +22,7 @@ import android.widget.TextView; import org.telegram.android.AndroidUtilities; import org.telegram.android.LocaleController; import org.telegram.messenger.R; +import org.telegram.ui.Components.LayoutHelper; public class TextColorCell extends FrameLayout { private TextView textView; @@ -48,8 +49,8 @@ public class TextColorCell extends FrameLayout { textView.setGravity((LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.CENTER_VERTICAL); addView(textView); LayoutParams layoutParams = (LayoutParams) textView.getLayoutParams(); - layoutParams.width = LayoutParams.MATCH_PARENT; - layoutParams.height = LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; + layoutParams.height = LayoutHelper.MATCH_PARENT; layoutParams.leftMargin = AndroidUtilities.dp(17); layoutParams.rightMargin = AndroidUtilities.dp(17); layoutParams.gravity = LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/TextDetailCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/TextDetailCell.java index 513daf871..e44654ef0 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/TextDetailCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/TextDetailCell.java @@ -18,6 +18,7 @@ import android.widget.TextView; import org.telegram.android.AndroidUtilities; import org.telegram.android.LocaleController; +import org.telegram.ui.Components.LayoutHelper; public class TextDetailCell extends FrameLayout { @@ -37,8 +38,8 @@ public class TextDetailCell extends FrameLayout { textView.setGravity(LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT); addView(textView); FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) textView.getLayoutParams(); - layoutParams.width = LayoutParams.WRAP_CONTENT; - layoutParams.height = LayoutParams.WRAP_CONTENT; + layoutParams.width = LayoutHelper.WRAP_CONTENT; + layoutParams.height = LayoutHelper.WRAP_CONTENT; layoutParams.topMargin = AndroidUtilities.dp(10); layoutParams.leftMargin = AndroidUtilities.dp(LocaleController.isRTL ? 16 : 71); layoutParams.rightMargin = AndroidUtilities.dp(LocaleController.isRTL ? 71 : 16); @@ -54,8 +55,8 @@ public class TextDetailCell extends FrameLayout { valueTextView.setGravity(LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT); addView(valueTextView); layoutParams = (FrameLayout.LayoutParams) valueTextView.getLayoutParams(); - layoutParams.width = LayoutParams.WRAP_CONTENT; - layoutParams.height = LayoutParams.WRAP_CONTENT; + layoutParams.width = LayoutHelper.WRAP_CONTENT; + layoutParams.height = LayoutHelper.WRAP_CONTENT; layoutParams.topMargin = AndroidUtilities.dp(35); layoutParams.leftMargin = AndroidUtilities.dp(LocaleController.isRTL ? 16 : 71); layoutParams.rightMargin = AndroidUtilities.dp(LocaleController.isRTL ? 71 : 16); @@ -66,8 +67,8 @@ public class TextDetailCell extends FrameLayout { imageView.setScaleType(ImageView.ScaleType.CENTER); addView(imageView); layoutParams = (LayoutParams) imageView.getLayoutParams(); - layoutParams.width = LayoutParams.WRAP_CONTENT; - layoutParams.height = LayoutParams.WRAP_CONTENT; + layoutParams.width = LayoutHelper.WRAP_CONTENT; + layoutParams.height = LayoutHelper.WRAP_CONTENT; layoutParams.leftMargin = AndroidUtilities.dp(LocaleController.isRTL ? 0 : 16); layoutParams.rightMargin = AndroidUtilities.dp(LocaleController.isRTL ? 16 : 0); layoutParams.gravity = (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.CENTER_VERTICAL; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/TextDetailSettingsCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/TextDetailSettingsCell.java index 4d62db658..1ac52592a 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/TextDetailSettingsCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/TextDetailSettingsCell.java @@ -18,6 +18,7 @@ import android.widget.TextView; import org.telegram.android.AndroidUtilities; import org.telegram.android.LocaleController; import org.telegram.ui.Components.FrameLayoutFixed; +import org.telegram.ui.Components.LayoutHelper; public class TextDetailSettingsCell extends FrameLayoutFixed { @@ -45,8 +46,8 @@ public class TextDetailSettingsCell extends FrameLayoutFixed { textView.setGravity((LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.CENTER_VERTICAL); addView(textView); LayoutParams layoutParams = (LayoutParams) textView.getLayoutParams(); - layoutParams.width = LayoutParams.WRAP_CONTENT; - layoutParams.height = LayoutParams.WRAP_CONTENT; + layoutParams.width = LayoutHelper.WRAP_CONTENT; + layoutParams.height = LayoutHelper.WRAP_CONTENT; layoutParams.topMargin = AndroidUtilities.dp(10); layoutParams.leftMargin = AndroidUtilities.dp(17); layoutParams.rightMargin = AndroidUtilities.dp(17); @@ -63,8 +64,8 @@ public class TextDetailSettingsCell extends FrameLayoutFixed { valueTextView.setPadding(0, 0, 0, 0); addView(valueTextView); layoutParams = (LayoutParams) valueTextView.getLayoutParams(); - layoutParams.width = LayoutParams.WRAP_CONTENT; - layoutParams.height = LayoutParams.WRAP_CONTENT; + layoutParams.width = LayoutHelper.WRAP_CONTENT; + layoutParams.height = LayoutHelper.WRAP_CONTENT; layoutParams.topMargin = AndroidUtilities.dp(35); layoutParams.leftMargin = AndroidUtilities.dp(17); layoutParams.rightMargin = AndroidUtilities.dp(17); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/TextInfoCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/TextInfoCell.java index 5125a3c72..236cff202 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/TextInfoCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/TextInfoCell.java @@ -15,6 +15,7 @@ import android.widget.FrameLayout; import android.widget.TextView; import org.telegram.android.AndroidUtilities; +import org.telegram.ui.Components.LayoutHelper; public class TextInfoCell extends FrameLayout { @@ -30,8 +31,8 @@ public class TextInfoCell extends FrameLayout { textView.setPadding(0, AndroidUtilities.dp(19), 0, AndroidUtilities.dp(19)); addView(textView); LayoutParams layoutParams = (LayoutParams) textView.getLayoutParams(); - layoutParams.width = LayoutParams.WRAP_CONTENT; - layoutParams.height = LayoutParams.WRAP_CONTENT; + layoutParams.width = LayoutHelper.WRAP_CONTENT; + layoutParams.height = LayoutHelper.WRAP_CONTENT; layoutParams.leftMargin = AndroidUtilities.dp(17); layoutParams.rightMargin = AndroidUtilities.dp(17); layoutParams.gravity = Gravity.CENTER; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/TextInfoPrivacyCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/TextInfoPrivacyCell.java index 6c8bcffa6..8ba0f7232 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/TextInfoPrivacyCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/TextInfoPrivacyCell.java @@ -16,6 +16,7 @@ import android.widget.TextView; import org.telegram.android.AndroidUtilities; import org.telegram.android.LocaleController; +import org.telegram.ui.Components.LayoutHelper; public class TextInfoPrivacyCell extends FrameLayout { @@ -31,8 +32,8 @@ public class TextInfoPrivacyCell extends FrameLayout { textView.setPadding(0, AndroidUtilities.dp(10), 0, AndroidUtilities.dp(17)); addView(textView); LayoutParams layoutParams = (LayoutParams) textView.getLayoutParams(); - layoutParams.width = LayoutParams.WRAP_CONTENT; - layoutParams.height = LayoutParams.WRAP_CONTENT; + layoutParams.width = LayoutHelper.WRAP_CONTENT; + layoutParams.height = LayoutHelper.WRAP_CONTENT; layoutParams.leftMargin = AndroidUtilities.dp(17); layoutParams.rightMargin = AndroidUtilities.dp(17); layoutParams.gravity = LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/TextSettingsCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/TextSettingsCell.java index e3c3ffb58..b9b295e55 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/TextSettingsCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/TextSettingsCell.java @@ -20,6 +20,7 @@ import android.widget.TextView; import org.telegram.android.AndroidUtilities; import org.telegram.android.LocaleController; +import org.telegram.ui.Components.LayoutHelper; public class TextSettingsCell extends FrameLayout { @@ -48,8 +49,8 @@ public class TextSettingsCell extends FrameLayout { textView.setGravity((LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.CENTER_VERTICAL); addView(textView); LayoutParams layoutParams = (LayoutParams) textView.getLayoutParams(); - layoutParams.width = LayoutParams.MATCH_PARENT; - layoutParams.height = LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; + layoutParams.height = LayoutHelper.MATCH_PARENT; layoutParams.leftMargin = AndroidUtilities.dp(17); layoutParams.rightMargin = AndroidUtilities.dp(17); layoutParams.gravity = LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT; @@ -65,8 +66,8 @@ public class TextSettingsCell extends FrameLayout { valueTextView.setGravity((LocaleController.isRTL ? Gravity.LEFT : Gravity.RIGHT) | Gravity.CENTER_VERTICAL); addView(valueTextView); layoutParams = (LayoutParams) valueTextView.getLayoutParams(); - layoutParams.width = LayoutParams.WRAP_CONTENT; - layoutParams.height = LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.WRAP_CONTENT; + layoutParams.height = LayoutHelper.MATCH_PARENT; layoutParams.leftMargin = AndroidUtilities.dp(17); layoutParams.rightMargin = AndroidUtilities.dp(17); layoutParams.gravity = LocaleController.isRTL ? Gravity.LEFT : Gravity.RIGHT; @@ -77,8 +78,8 @@ public class TextSettingsCell extends FrameLayout { valueImageView.setVisibility(INVISIBLE); addView(valueImageView); layoutParams = (LayoutParams) valueImageView.getLayoutParams(); - layoutParams.width = LayoutParams.WRAP_CONTENT; - layoutParams.height = LayoutParams.WRAP_CONTENT; + layoutParams.width = LayoutHelper.WRAP_CONTENT; + layoutParams.height = LayoutHelper.WRAP_CONTENT; layoutParams.leftMargin = AndroidUtilities.dp(LocaleController.isRTL ? 17 : 0); layoutParams.rightMargin = AndroidUtilities.dp(LocaleController.isRTL ? 0 : 17); layoutParams.gravity = (LocaleController.isRTL ? Gravity.LEFT : Gravity.RIGHT) | Gravity.CENTER_VERTICAL; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/UserCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/UserCell.java index 909be3b41..116721b01 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/UserCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/UserCell.java @@ -9,12 +9,9 @@ package org.telegram.ui.Cells; import android.content.Context; -import android.text.TextUtils; -import android.util.TypedValue; import android.view.Gravity; import android.widget.FrameLayout; import android.widget.ImageView; -import android.widget.TextView; import org.telegram.android.AndroidUtilities; import org.telegram.android.ContactsController; @@ -27,12 +24,14 @@ import org.telegram.messenger.UserConfig; import org.telegram.ui.Components.AvatarDrawable; import org.telegram.ui.Components.BackupImageView; import org.telegram.ui.Components.CheckBox; +import org.telegram.ui.Components.LayoutHelper; +import org.telegram.ui.Components.SimpleTextView; public class UserCell extends FrameLayout { private BackupImageView avatarImageView; - private TextView nameTextView; - private TextView statusTextView; + private SimpleTextView nameTextView; + private SimpleTextView statusTextView; private ImageView imageView; private CheckBox checkBox; @@ -53,76 +52,31 @@ public class UserCell extends FrameLayout { public UserCell(Context context, int padding) { super(context); - avatarImageView = new BackupImageView(context); - avatarImageView.setRoundRadius(AndroidUtilities.dp(24)); - addView(avatarImageView); - LayoutParams layoutParams = (LayoutParams) avatarImageView.getLayoutParams(); - layoutParams.width = AndroidUtilities.dp(48); - layoutParams.height = AndroidUtilities.dp(48); - layoutParams.gravity = LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT; - layoutParams.leftMargin = LocaleController.isRTL ? 0 : AndroidUtilities.dp(7 + padding); - layoutParams.rightMargin = LocaleController.isRTL ? AndroidUtilities.dp(7 + padding) : 0; - layoutParams.topMargin = AndroidUtilities.dp(8); - avatarImageView.setLayoutParams(layoutParams); avatarDrawable = new AvatarDrawable(); - nameTextView = new TextView(context); - nameTextView.setTextColor(0xff212121); - nameTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 17); - nameTextView.setLines(1); - nameTextView.setMaxLines(1); - nameTextView.setSingleLine(true); - nameTextView.setEllipsize(TextUtils.TruncateAt.END); - nameTextView.setGravity((LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.CENTER_VERTICAL); - addView(nameTextView); - layoutParams = (LayoutParams) nameTextView.getLayoutParams(); - layoutParams.width = LayoutParams.WRAP_CONTENT; - layoutParams.height = LayoutParams.WRAP_CONTENT; - layoutParams.leftMargin = AndroidUtilities.dp(LocaleController.isRTL ? 16 : (68 + padding)); - layoutParams.rightMargin = AndroidUtilities.dp(LocaleController.isRTL ? (68 + padding) : 16); - layoutParams.topMargin = AndroidUtilities.dp(10.5f); - layoutParams.gravity = LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT; - nameTextView.setLayoutParams(layoutParams); + avatarImageView = new BackupImageView(context); + avatarImageView.setRoundRadius(AndroidUtilities.dp(24)); + addView(avatarImageView, LayoutHelper.createFrame(48, 48, (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP, LocaleController.isRTL ? 0 : 7 + padding, 8, LocaleController.isRTL ? 7 + padding : 0, 0)); - statusTextView = new TextView(context); - statusTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); - statusTextView.setLines(1); - statusTextView.setMaxLines(1); - statusTextView.setSingleLine(true); - statusTextView.setEllipsize(TextUtils.TruncateAt.END); - statusTextView.setGravity((LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.CENTER_VERTICAL); - addView(statusTextView); - layoutParams = (LayoutParams) statusTextView.getLayoutParams(); - layoutParams.width = LayoutParams.WRAP_CONTENT; - layoutParams.height = LayoutParams.WRAP_CONTENT; - layoutParams.leftMargin = AndroidUtilities.dp(LocaleController.isRTL ? 16 : (68 + padding)); - layoutParams.rightMargin = AndroidUtilities.dp(LocaleController.isRTL ? (68 + padding) : 16); - layoutParams.topMargin = AndroidUtilities.dp(33.5f); - layoutParams.gravity = LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT; - statusTextView.setLayoutParams(layoutParams); + nameTextView = new SimpleTextView(context); + 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)); + + statusTextView = new SimpleTextView(context); + statusTextView.setTextSize(14); + statusTextView.setGravity((LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP); + addView(statusTextView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 20, (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP, LocaleController.isRTL ? 28 : (68 + padding), 34.5f, LocaleController.isRTL ? (68 + padding) : 28, 0)); imageView = new ImageView(context); imageView.setScaleType(ImageView.ScaleType.CENTER); - addView(imageView); - layoutParams = (LayoutParams) imageView.getLayoutParams(); - layoutParams.width = LayoutParams.WRAP_CONTENT; - layoutParams.height = LayoutParams.WRAP_CONTENT; - layoutParams.leftMargin = AndroidUtilities.dp(LocaleController.isRTL ? 0 : 16); - layoutParams.rightMargin = AndroidUtilities.dp(LocaleController.isRTL ? 16 : 0); - layoutParams.gravity = (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.CENTER_VERTICAL; - imageView.setLayoutParams(layoutParams); + imageView.setVisibility(GONE); + addView(imageView, LayoutHelper.createFrame(LayoutParams.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.CENTER_VERTICAL, LocaleController.isRTL ? 0 : 16, 0, LocaleController.isRTL ? 16 : 0, 0)); checkBox = new CheckBox(context, R.drawable.round_check2); checkBox.setVisibility(INVISIBLE); - addView(checkBox); - layoutParams = (LayoutParams) checkBox.getLayoutParams(); - layoutParams.width = AndroidUtilities.dp(22); - layoutParams.height = AndroidUtilities.dp(22); - layoutParams.topMargin = AndroidUtilities.dp(38); - layoutParams.leftMargin = LocaleController.isRTL ? 0 : AndroidUtilities.dp(37 + padding); - layoutParams.rightMargin = LocaleController.isRTL ? AndroidUtilities.dp(37 + padding) : 0; - layoutParams.gravity = (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT); - checkBox.setLayoutParams(layoutParams); + 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)); } public void setData(TLRPC.User user, CharSequence name, CharSequence status, int resId) { @@ -149,12 +103,6 @@ public class UserCell extends FrameLayout { checkBox.setChecked(checked, animated); } - @Override - protected void onDetachedFromWindow() { - super.onDetachedFromWindow(); - lastAvatar = null; - } - @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(64), MeasureSpec.EXACTLY)); @@ -170,6 +118,7 @@ public class UserCell extends FrameLayout { return; } TLRPC.FileLocation photo = null; + String newName = null; if (currentUser.photo != null) { photo = currentUser.photo.photo_small; } @@ -190,9 +139,9 @@ public class UserCell extends FrameLayout { continueUpdate = true; } } - if (!continueUpdate && (mask & MessagesController.UPDATE_MASK_NAME) != 0) { - String newName = currentUser.first_name + currentUser.last_name; - if (newName == null || !newName.equals(lastName)) { + if (!continueUpdate && currentName == null && lastName != null && (mask & MessagesController.UPDATE_MASK_NAME) != 0) { + newName = ContactsController.formatName(currentUser.first_name, currentUser.last_name); + if (!newName.equals(lastName)) { continueUpdate = true; } } @@ -207,29 +156,31 @@ public class UserCell extends FrameLayout { } else { lastStatus = 0; } - lastName = currentUser.first_name + currentUser.last_name; - lastAvatar = photo; if (currentName != null) { + lastName = null; nameTextView.setText(currentName); } else { - nameTextView.setText(ContactsController.formatName(currentUser.first_name, currentUser.last_name)); + lastName = newName == null ? ContactsController.formatName(currentUser.first_name, currentUser.last_name) : newName; + nameTextView.setText(lastName); } if (currrntStatus != null) { - statusTextView.setText(currrntStatus); statusTextView.setTextColor(statusColor); + statusTextView.setText(currrntStatus); } else { if (currentUser.id == UserConfig.getClientUserId() || currentUser.status != null && currentUser.status.expires > ConnectionsManager.getInstance().getCurrentTime()) { - statusTextView.setText(LocaleController.getString("Online", R.string.Online)); statusTextView.setTextColor(statusOnlineColor); + statusTextView.setText(LocaleController.getString("Online", R.string.Online)); } else { - statusTextView.setText(LocaleController.formatUserStatus(currentUser)); statusTextView.setTextColor(statusColor); + statusTextView.setText(LocaleController.formatUserStatus(currentUser)); } } - imageView.setVisibility(currentDrawable == 0 ? INVISIBLE : VISIBLE); - imageView.setImageResource(currentDrawable); + if (imageView.getVisibility() == VISIBLE && currentDrawable == 0 || imageView.getVisibility() == GONE && currentDrawable != 0) { + imageView.setVisibility(currentDrawable == 0 ? GONE : VISIBLE); + imageView.setImageResource(currentDrawable); + } avatarImageView.setImage(photo, "50_50", avatarDrawable); } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ChangeChatNameActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/ChangeChatNameActivity.java index eb94acb69..66d3b748e 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ChangeChatNameActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ChangeChatNameActivity.java @@ -34,6 +34,7 @@ import org.telegram.messenger.R; import org.telegram.ui.ActionBar.ActionBar; import org.telegram.ui.ActionBar.ActionBarMenu; import org.telegram.ui.ActionBar.BaseFragment; +import org.telegram.ui.Components.LayoutHelper; public class ChangeChatNameActivity extends BaseFragment { @@ -118,7 +119,7 @@ public class ChangeChatNameActivity extends BaseFragment { layoutParams.height = AndroidUtilities.dp(36); layoutParams.leftMargin = AndroidUtilities.dp(24); layoutParams.rightMargin = AndroidUtilities.dp(24); - layoutParams.width = LinearLayout.LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; firstNameField.setLayoutParams(layoutParams); if (chat_id > 0) { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ChangeNameActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/ChangeNameActivity.java index 4c672106e..490aa76dc 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ChangeNameActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ChangeNameActivity.java @@ -38,6 +38,7 @@ import org.telegram.messenger.UserConfig; import org.telegram.ui.ActionBar.ActionBar; import org.telegram.ui.ActionBar.ActionBarMenu; import org.telegram.ui.ActionBar.BaseFragment; +import org.telegram.ui.Components.LayoutHelper; public class ChangeNameActivity extends BaseFragment { @@ -103,7 +104,7 @@ public class ChangeNameActivity extends BaseFragment { layoutParams.height = AndroidUtilities.dp(36); layoutParams.leftMargin = AndroidUtilities.dp(24); layoutParams.rightMargin = AndroidUtilities.dp(24); - layoutParams.width = LinearLayout.LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; firstNameField.setLayoutParams(layoutParams); firstNameField.setOnEditorActionListener(new TextView.OnEditorActionListener() { @Override @@ -135,7 +136,7 @@ public class ChangeNameActivity extends BaseFragment { layoutParams.height = AndroidUtilities.dp(36); layoutParams.leftMargin = AndroidUtilities.dp(24); layoutParams.rightMargin = AndroidUtilities.dp(24); - layoutParams.width = LinearLayout.LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; lastNameField.setLayoutParams(layoutParams); lastNameField.setOnEditorActionListener(new TextView.OnEditorActionListener() { @Override diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ChangePhoneActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/ChangePhoneActivity.java index 01e54b550..0841e4a7c 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ChangePhoneActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ChangePhoneActivity.java @@ -53,10 +53,11 @@ import org.telegram.messenger.UserConfig; import org.telegram.ui.ActionBar.ActionBar; import org.telegram.ui.ActionBar.ActionBarMenu; import org.telegram.ui.ActionBar.BaseFragment; -import org.telegram.ui.AnimationCompat.AnimatorListenerAdapterProxy; -import org.telegram.ui.AnimationCompat.AnimatorSetProxy; -import org.telegram.ui.AnimationCompat.ObjectAnimatorProxy; -import org.telegram.ui.AnimationCompat.ViewProxy; +import org.telegram.android.AnimationCompat.AnimatorListenerAdapterProxy; +import org.telegram.android.AnimationCompat.AnimatorSetProxy; +import org.telegram.android.AnimationCompat.ObjectAnimatorProxy; +import org.telegram.android.AnimationCompat.ViewProxy; +import org.telegram.ui.Components.LayoutHelper; import org.telegram.ui.Components.SlideView; import org.telegram.ui.Components.TypefaceSpan; @@ -133,8 +134,8 @@ public class ChangePhoneActivity extends BaseFragment { views[0].setVisibility(View.VISIBLE); frameLayout.addView(views[0]); FrameLayout.LayoutParams layoutParams1 = (FrameLayout.LayoutParams) views[0].getLayoutParams(); - layoutParams1.width = FrameLayout.LayoutParams.MATCH_PARENT; - layoutParams1.height = FrameLayout.LayoutParams.WRAP_CONTENT; + layoutParams1.width = LayoutHelper.MATCH_PARENT; + layoutParams1.height = LayoutHelper.WRAP_CONTENT; layoutParams1.leftMargin = AndroidUtilities.dp(16); layoutParams1.rightMargin = AndroidUtilities.dp(16); layoutParams1.topMargin = AndroidUtilities.dp(30); @@ -145,8 +146,8 @@ public class ChangePhoneActivity extends BaseFragment { views[1].setVisibility(View.GONE); frameLayout.addView(views[1]); layoutParams1 = (FrameLayout.LayoutParams) views[1].getLayoutParams(); - layoutParams1.width = FrameLayout.LayoutParams.MATCH_PARENT; - layoutParams1.height = FrameLayout.LayoutParams.MATCH_PARENT; + layoutParams1.width = LayoutHelper.MATCH_PARENT; + layoutParams1.height = LayoutHelper.MATCH_PARENT; layoutParams1.leftMargin = AndroidUtilities.dp(16); layoutParams1.rightMargin = AndroidUtilities.dp(16); layoutParams1.topMargin = AndroidUtilities.dp(30); @@ -307,7 +308,7 @@ public class ChangePhoneActivity extends BaseFragment { countryButton.setBackgroundResource(R.drawable.spinner_states); addView(countryButton); LayoutParams layoutParams = (LayoutParams) countryButton.getLayoutParams(); - layoutParams.width = LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; layoutParams.height = AndroidUtilities.dp(36); layoutParams.leftMargin = AndroidUtilities.dp(20); layoutParams.rightMargin = AndroidUtilities.dp(20); @@ -333,7 +334,7 @@ public class ChangePhoneActivity extends BaseFragment { view.setBackgroundColor(0xffdbdbdb); addView(view); layoutParams = (LayoutParams) view.getLayoutParams(); - layoutParams.width = LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; layoutParams.height = 1; layoutParams.leftMargin = AndroidUtilities.dp(24); layoutParams.rightMargin = AndroidUtilities.dp(24); @@ -344,8 +345,8 @@ public class ChangePhoneActivity extends BaseFragment { linearLayout.setOrientation(HORIZONTAL); addView(linearLayout); layoutParams = (LayoutParams) linearLayout.getLayoutParams(); - layoutParams.width = LayoutParams.MATCH_PARENT; - layoutParams.height = LayoutParams.WRAP_CONTENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; + layoutParams.height = LayoutHelper.WRAP_CONTENT; layoutParams.topMargin = AndroidUtilities.dp(20); linearLayout.setLayoutParams(layoutParams); @@ -355,8 +356,8 @@ public class ChangePhoneActivity extends BaseFragment { textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 18); linearLayout.addView(textView); layoutParams = (LayoutParams) textView.getLayoutParams(); - layoutParams.width = LayoutParams.WRAP_CONTENT; - layoutParams.height = LayoutParams.WRAP_CONTENT; + layoutParams.width = LayoutHelper.WRAP_CONTENT; + layoutParams.height = LayoutHelper.WRAP_CONTENT; layoutParams.leftMargin = AndroidUtilities.dp(24); textView.setLayoutParams(layoutParams); @@ -447,7 +448,7 @@ public class ChangePhoneActivity extends BaseFragment { phoneField.setImeOptions(EditorInfo.IME_ACTION_NEXT | EditorInfo.IME_FLAG_NO_EXTRACT_UI); linearLayout.addView(phoneField); layoutParams = (LayoutParams) phoneField.getLayoutParams(); - layoutParams.width = LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; layoutParams.height = AndroidUtilities.dp(36); layoutParams.rightMargin = AndroidUtilities.dp(24); phoneField.setLayoutParams(layoutParams); @@ -517,8 +518,8 @@ public class ChangePhoneActivity extends BaseFragment { textView.setLineSpacing(AndroidUtilities.dp(2), 1.0f); addView(textView); layoutParams = (LayoutParams) textView.getLayoutParams(); - layoutParams.width = LayoutParams.WRAP_CONTENT; - layoutParams.height = LayoutParams.WRAP_CONTENT; + layoutParams.width = LayoutHelper.WRAP_CONTENT; + layoutParams.height = LayoutHelper.WRAP_CONTENT; layoutParams.leftMargin = AndroidUtilities.dp(24); layoutParams.rightMargin = AndroidUtilities.dp(24); layoutParams.topMargin = AndroidUtilities.dp(28); @@ -747,8 +748,8 @@ public class ChangePhoneActivity extends BaseFragment { confirmTextView.setLineSpacing(AndroidUtilities.dp(2), 1.0f); addView(confirmTextView); LayoutParams layoutParams = (LayoutParams) confirmTextView.getLayoutParams(); - layoutParams.width = LayoutParams.WRAP_CONTENT; - layoutParams.height = LayoutParams.WRAP_CONTENT; + layoutParams.width = LayoutHelper.WRAP_CONTENT; + layoutParams.height = LayoutHelper.WRAP_CONTENT; layoutParams.gravity = Gravity.LEFT; layoutParams.leftMargin = AndroidUtilities.dp(24); layoutParams.rightMargin = AndroidUtilities.dp(24); @@ -766,7 +767,7 @@ public class ChangePhoneActivity extends BaseFragment { codeField.setPadding(0, 0, 0, 0); addView(codeField); layoutParams = (LayoutParams) codeField.getLayoutParams(); - layoutParams.width = LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; layoutParams.height = AndroidUtilities.dp(36); layoutParams.gravity = Gravity.CENTER_HORIZONTAL; layoutParams.topMargin = AndroidUtilities.dp(20); @@ -791,8 +792,8 @@ public class ChangePhoneActivity extends BaseFragment { timeText.setGravity(Gravity.LEFT); addView(timeText); layoutParams = (LayoutParams) timeText.getLayoutParams(); - layoutParams.width = LayoutParams.WRAP_CONTENT; - layoutParams.height = LayoutParams.WRAP_CONTENT; + layoutParams.width = LayoutHelper.WRAP_CONTENT; + layoutParams.height = LayoutHelper.WRAP_CONTENT; layoutParams.gravity = Gravity.LEFT; layoutParams.topMargin = AndroidUtilities.dp(30); layoutParams.leftMargin = AndroidUtilities.dp(24); @@ -803,8 +804,8 @@ public class ChangePhoneActivity extends BaseFragment { linearLayout.setGravity(Gravity.BOTTOM | Gravity.CENTER_VERTICAL); addView(linearLayout); layoutParams = (LayoutParams) linearLayout.getLayoutParams(); - layoutParams.width = LayoutParams.MATCH_PARENT; - layoutParams.height = LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; + layoutParams.height = LayoutHelper.MATCH_PARENT; linearLayout.setLayoutParams(layoutParams); TextView wrongNumber = new TextView(context); @@ -815,8 +816,8 @@ public class ChangePhoneActivity extends BaseFragment { wrongNumber.setPadding(0, AndroidUtilities.dp(24), 0, 0); linearLayout.addView(wrongNumber); layoutParams = (LayoutParams) wrongNumber.getLayoutParams(); - layoutParams.width = LayoutParams.WRAP_CONTENT; - layoutParams.height = LayoutParams.WRAP_CONTENT; + 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); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ChangePhoneHelpActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/ChangePhoneHelpActivity.java index 3da860185..362d91294 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ChangePhoneHelpActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ChangePhoneHelpActivity.java @@ -31,6 +31,7 @@ import org.telegram.messenger.TLRPC; import org.telegram.messenger.UserConfig; import org.telegram.ui.ActionBar.ActionBar; import org.telegram.ui.ActionBar.BaseFragment; +import org.telegram.ui.Components.LayoutHelper; public class ChangePhoneHelpActivity extends BaseFragment { @@ -70,8 +71,8 @@ public class ChangePhoneHelpActivity extends BaseFragment { ScrollView scrollView = new ScrollView(context); relativeLayout.addView(scrollView); RelativeLayout.LayoutParams layoutParams3 = (RelativeLayout.LayoutParams) scrollView.getLayoutParams(); - layoutParams3.width = RelativeLayout.LayoutParams.MATCH_PARENT; - layoutParams3.height = RelativeLayout.LayoutParams.WRAP_CONTENT; + layoutParams3.width = LayoutHelper.MATCH_PARENT; + layoutParams3.height = LayoutHelper.WRAP_CONTENT; layoutParams3.addRule(RelativeLayout.CENTER_VERTICAL, RelativeLayout.TRUE); scrollView.setLayoutParams(layoutParams3); @@ -88,8 +89,8 @@ public class ChangePhoneHelpActivity extends BaseFragment { imageView.setImageResource(R.drawable.phone_change); linearLayout.addView(imageView); LinearLayout.LayoutParams layoutParams2 = (LinearLayout.LayoutParams) imageView.getLayoutParams(); - layoutParams2.width = LinearLayout.LayoutParams.WRAP_CONTENT; - layoutParams2.height = LinearLayout.LayoutParams.WRAP_CONTENT; + layoutParams2.width = LayoutHelper.WRAP_CONTENT; + layoutParams2.height = LayoutHelper.WRAP_CONTENT; layoutParams2.gravity = Gravity.CENTER_HORIZONTAL; imageView.setLayoutParams(layoutParams2); @@ -106,8 +107,8 @@ public class ChangePhoneHelpActivity extends BaseFragment { } linearLayout.addView(textView); layoutParams2 = (LinearLayout.LayoutParams) textView.getLayoutParams(); - layoutParams2.width = LinearLayout.LayoutParams.WRAP_CONTENT; - layoutParams2.height = LinearLayout.LayoutParams.WRAP_CONTENT; + layoutParams2.width = LayoutHelper.WRAP_CONTENT; + layoutParams2.height = LayoutHelper.WRAP_CONTENT; layoutParams2.gravity = Gravity.CENTER_HORIZONTAL; layoutParams2.leftMargin = AndroidUtilities.dp(20); layoutParams2.rightMargin = AndroidUtilities.dp(20); @@ -123,8 +124,8 @@ public class ChangePhoneHelpActivity extends BaseFragment { textView.setPadding(0, AndroidUtilities.dp(10), 0, AndroidUtilities.dp(10)); linearLayout.addView(textView); layoutParams2 = (LinearLayout.LayoutParams) textView.getLayoutParams(); - layoutParams2.width = LinearLayout.LayoutParams.WRAP_CONTENT; - layoutParams2.height = LinearLayout.LayoutParams.WRAP_CONTENT; + layoutParams2.width = LayoutHelper.WRAP_CONTENT; + layoutParams2.height = LayoutHelper.WRAP_CONTENT; layoutParams2.gravity = Gravity.CENTER_HORIZONTAL; layoutParams2.leftMargin = AndroidUtilities.dp(20); layoutParams2.rightMargin = AndroidUtilities.dp(20); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ChangeUsernameActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/ChangeUsernameActivity.java index e0169de83..6e1d20ca8 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ChangeUsernameActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ChangeUsernameActivity.java @@ -15,7 +15,6 @@ import android.content.Context; import android.content.DialogInterface; import android.content.SharedPreferences; import android.text.Editable; -import android.text.Html; import android.text.InputType; import android.text.TextWatcher; import android.util.TypedValue; @@ -46,6 +45,7 @@ import org.telegram.messenger.UserConfig; import org.telegram.ui.ActionBar.ActionBar; import org.telegram.ui.ActionBar.ActionBarMenu; import org.telegram.ui.ActionBar.BaseFragment; +import org.telegram.ui.Components.LayoutHelper; import java.util.ArrayList; @@ -125,7 +125,7 @@ public class ChangeUsernameActivity extends BaseFragment { layoutParams.height = AndroidUtilities.dp(36); layoutParams.leftMargin = AndroidUtilities.dp(24); layoutParams.rightMargin = AndroidUtilities.dp(24); - layoutParams.width = LinearLayout.LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; firstNameField.setLayoutParams(layoutParams); if (user != null && user.username != null && user.username.length() > 0) { @@ -139,8 +139,8 @@ public class ChangeUsernameActivity extends BaseFragment { ((LinearLayout) fragmentView).addView(checkTextView); layoutParams = (LinearLayout.LayoutParams) checkTextView.getLayoutParams(); layoutParams.topMargin = AndroidUtilities.dp(12); - layoutParams.width = LinearLayout.LayoutParams.WRAP_CONTENT; - layoutParams.height = LinearLayout.LayoutParams.WRAP_CONTENT; + layoutParams.width = LayoutHelper.WRAP_CONTENT; + layoutParams.height = LayoutHelper.WRAP_CONTENT; layoutParams.gravity = LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT; layoutParams.leftMargin = AndroidUtilities.dp(24); layoutParams.rightMargin = AndroidUtilities.dp(24); @@ -154,8 +154,8 @@ public class ChangeUsernameActivity extends BaseFragment { ((LinearLayout) fragmentView).addView(helpTextView); layoutParams = (LinearLayout.LayoutParams) helpTextView.getLayoutParams(); layoutParams.topMargin = AndroidUtilities.dp(10); - layoutParams.width = LinearLayout.LayoutParams.WRAP_CONTENT; - layoutParams.height = LinearLayout.LayoutParams.WRAP_CONTENT; + layoutParams.width = LayoutHelper.WRAP_CONTENT; + layoutParams.height = LayoutHelper.WRAP_CONTENT; layoutParams.gravity = LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT; layoutParams.leftMargin = AndroidUtilities.dp(24); layoutParams.rightMargin = AndroidUtilities.dp(24); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ChatActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/ChatActivity.java index 801c41d59..11eb1f3f1 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ChatActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ChatActivity.java @@ -26,7 +26,6 @@ import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.provider.MediaStore; -import android.support.v7.widget.LinearLayoutManager; import android.text.TextUtils; import android.util.Base64; import android.util.SparseArray; @@ -39,7 +38,6 @@ import android.view.ViewGroup; import android.view.ViewTreeObserver; import android.view.WindowManager; import android.webkit.MimeTypeMap; -import android.widget.AbsListView; import android.widget.AdapterView; import android.widget.FrameLayout; import android.widget.ImageView; @@ -60,11 +58,12 @@ import org.telegram.android.NotificationsController; import org.telegram.android.SecretChatHelper; import org.telegram.android.SendMessagesHelper; import org.telegram.android.query.ReplyMessageQuery; +import org.telegram.android.support.widget.LinearLayoutManager; +import org.telegram.android.support.widget.RecyclerView; import org.telegram.messenger.ApplicationLoader; import org.telegram.messenger.FileLoader; import org.telegram.messenger.RPCRequest; import org.telegram.messenger.SerializedData; -import org.telegram.messenger.TLClassStore; import org.telegram.messenger.TLObject; import org.telegram.messenger.TLRPC; import org.telegram.android.ContactsController; @@ -76,36 +75,38 @@ import org.telegram.android.NotificationCenter; import org.telegram.messenger.R; import org.telegram.messenger.UserConfig; import org.telegram.messenger.Utilities; -import org.telegram.ui.Adapters.BaseFragmentAdapter; import org.telegram.ui.Adapters.MentionsAdapter; import org.telegram.ui.Adapters.StickersAdapter; -import org.telegram.ui.AnimationCompat.AnimatorListenerAdapterProxy; -import org.telegram.ui.AnimationCompat.AnimatorSetProxy; -import org.telegram.ui.AnimationCompat.ObjectAnimatorProxy; -import org.telegram.ui.AnimationCompat.ViewProxy; +import org.telegram.android.AnimationCompat.AnimatorListenerAdapterProxy; +import org.telegram.android.AnimationCompat.AnimatorSetProxy; +import org.telegram.android.AnimationCompat.ObjectAnimatorProxy; +import org.telegram.android.AnimationCompat.ViewProxy; import org.telegram.ui.Cells.ChatActionCell; import org.telegram.ui.Cells.ChatAudioCell; import org.telegram.ui.Cells.ChatBaseCell; import org.telegram.ui.Cells.ChatContactCell; import org.telegram.ui.Cells.ChatMediaCell; -import org.telegram.ui.Cells.ChatMessageCell; import org.telegram.ui.ActionBar.ActionBar; import org.telegram.ui.ActionBar.ActionBarMenu; import org.telegram.ui.ActionBar.ActionBarMenuItem; +import org.telegram.ui.Cells.ChatMessageCell; import org.telegram.ui.Components.AvatarDrawable; import org.telegram.ui.Components.BackupImageView; import org.telegram.ui.ActionBar.BaseFragment; import org.telegram.ui.Components.ChatActivityEnterView; import org.telegram.android.ImageReceiver; import org.telegram.ui.Components.FrameLayoutFixed; -import org.telegram.ui.Components.LayoutListView; +import org.telegram.ui.Components.LayoutHelper; +import org.telegram.ui.Components.RecordStatusDrawable; +import org.telegram.ui.Components.RecyclerExListView; import org.telegram.ui.Components.RecyclerListView; +import org.telegram.ui.Components.ResourceLoader; +import org.telegram.ui.Components.SendingFileExDrawable; import org.telegram.ui.Components.SizeNotifierRelativeLayout; import org.telegram.ui.Components.TimerDrawable; import org.telegram.ui.Components.TypingDotsDrawable; import java.io.File; -import java.io.RandomAccessFile; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; @@ -114,14 +115,16 @@ import java.util.concurrent.Semaphore; public class ChatActivity extends BaseFragment implements NotificationCenter.NotificationCenterDelegate, MessagesActivity.MessagesActivityDelegate, PhotoViewer.PhotoViewerProvider { - private TLRPC.Chat currentChat; - private TLRPC.User currentUser; - private TLRPC.EncryptedChat currentEncryptedChat; + protected TLRPC.Chat currentChat; + protected TLRPC.User currentUser; + protected TLRPC.EncryptedChat currentEncryptedChat; private boolean userBlocked = false; + private ArrayList chatMessageCellsCache = new ArrayList<>(); + private ArrayList chatMediaCellsCache = new ArrayList<>(); + private FrameLayout progressView; private FrameLayout bottomOverlay; - private ChatAdapter chatAdapter; private ChatActivityEnterView chatActivityEnterView; private ImageView timeItem; private View timeItem2; @@ -130,11 +133,15 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not private ActionBarMenuItem attachItem; private ActionBarMenuItem headerItem; private TextView addContactItem; - private LayoutListView chatListView; + private RecyclerExListView chatListView; + private LinearLayoutManager chatLayoutManager; + private ChatActivityAdapter chatAdapter; private BackupImageView avatarImageView; private TextView bottomOverlayChatText; private FrameLayout bottomOverlayChat; private TypingDotsDrawable typingDotsDrawable; + private RecordStatusDrawable recordStatusDrawable; + private SendingFileExDrawable sendingFileDrawable; private FrameLayout emptyViewContainer; private ArrayList actionModeViews = new ArrayList<>(); private TextView nameTextView; @@ -164,6 +171,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not private MessageObject forwaringMessage; private MessageObject replyingMessageObject; private boolean paused = true; + private boolean wasPaused = false; private boolean readWhenResume = false; private TLRPC.FileLocation replyImageLocation; private long linkSearchRequestId; @@ -185,7 +193,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not private HashMap messagesDict = new HashMap<>(); private HashMap> messagesByDays = new HashMap<>(); - private ArrayList messages = new ArrayList<>(); + protected ArrayList messages = new ArrayList<>(); private int maxMessageId = Integer.MAX_VALUE; private int minMessageId = Integer.MIN_VALUE; private int maxDate = Integer.MIN_VALUE; @@ -214,11 +222,14 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not private String currentPicturePath; - private TLRPC.ChatParticipants info = null; + private Rect scrollRect = new Rect(); + + protected TLRPC.ChatParticipants info = null; private int onlineCount = -1; private CharSequence lastPrintString; private String lastStatus; + private int lastStatusDrawable; private long chatEnterTime = 0; private long chatLeaveTime = 0; @@ -247,19 +258,18 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not private final static int id_chat_compose_panel = 1000; - AdapterView.OnItemLongClickListener onItemLongClickListener = new AdapterView.OnItemLongClickListener() { + RecyclerExListView.OnItemLongClickListener onItemLongClickListener = new RecyclerExListView.OnItemLongClickListener() { @Override - public boolean onItemLongClick(AdapterView adapter, View view, int position, long id) { + public void onItemClick(View view, int position) { if (!actionBar.isActionModeShowed()) { createMenu(view, false); } - return true; } }; - AdapterView.OnItemClickListener onItemClickListener = new AdapterView.OnItemClickListener() { + RecyclerExListView.OnItemClickListener onItemClickListener = new RecyclerExListView.OnItemClickListener() { @Override - public void onItemClick(AdapterView adapterView, View view, int i, long l) { + public void onItemClick(View view, int position) { if (actionBar.isActionModeShowed()) { processRowSelect(view); return; @@ -407,7 +417,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not NotificationCenter.getInstance().addObserver(this, NotificationCenter.chatInfoDidLoaded); NotificationCenter.getInstance().addObserver(this, NotificationCenter.contactsDidLoaded); NotificationCenter.getInstance().addObserver(this, NotificationCenter.encryptedChatUpdated); - NotificationCenter.getInstance().addObserver(this, NotificationCenter.messagesReadedEncrypted); + NotificationCenter.getInstance().addObserver(this, NotificationCenter.messagesReadEncrypted); NotificationCenter.getInstance().addObserver(this, NotificationCenter.removeAllMessagesFromDialog); NotificationCenter.getInstance().addObserver(this, NotificationCenter.audioProgressDidChanged); NotificationCenter.getInstance().addObserver(this, NotificationCenter.audioDidReset); @@ -422,6 +432,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not NotificationCenter.getInstance().addObserver(this, NotificationCenter.didLoadedReplyMessages); NotificationCenter.getInstance().addObserver(this, NotificationCenter.didReceivedWebpages); NotificationCenter.getInstance().addObserver(this, NotificationCenter.didReceivedWebpagesInUpdates); + NotificationCenter.getInstance().addObserver(this, NotificationCenter.messagesReadContent); super.onFragmentCreate(); @@ -444,6 +455,10 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not typingDotsDrawable = new TypingDotsDrawable(); typingDotsDrawable.setIsChat(currentChat != null); + recordStatusDrawable = new RecordStatusDrawable(); + recordStatusDrawable.setIsChat(currentChat != null); + sendingFileDrawable = new SendingFileExDrawable(); + sendingFileDrawable.setIsChat(currentChat != null); if (currentEncryptedChat != null && AndroidUtilities.getMyLayerVersion(currentEncryptedChat.layer) != SecretChatHelper.CURRENT_SECRET_CHAT_LAYER) { SecretChatHelper.getInstance().sendNotifyLayerMessage(currentEncryptedChat, null); @@ -470,7 +485,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not NotificationCenter.getInstance().removeObserver(this, NotificationCenter.messageSendError); NotificationCenter.getInstance().removeObserver(this, NotificationCenter.chatInfoDidLoaded); NotificationCenter.getInstance().removeObserver(this, NotificationCenter.encryptedChatUpdated); - NotificationCenter.getInstance().removeObserver(this, NotificationCenter.messagesReadedEncrypted); + NotificationCenter.getInstance().removeObserver(this, NotificationCenter.messagesReadEncrypted); NotificationCenter.getInstance().removeObserver(this, NotificationCenter.removeAllMessagesFromDialog); NotificationCenter.getInstance().removeObserver(this, NotificationCenter.contactsDidLoaded); NotificationCenter.getInstance().removeObserver(this, NotificationCenter.audioProgressDidChanged); @@ -486,6 +501,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not NotificationCenter.getInstance().removeObserver(this, NotificationCenter.didLoadedReplyMessages); NotificationCenter.getInstance().removeObserver(this, NotificationCenter.didReceivedWebpages); NotificationCenter.getInstance().removeObserver(this, NotificationCenter.didReceivedWebpagesInUpdates); + NotificationCenter.getInstance().removeObserver(this, NotificationCenter.messagesReadContent); if (AndroidUtilities.isTablet()) { NotificationCenter.getInstance().postNotificationName(NotificationCenter.openedChatChanged, dialog_id, true); @@ -509,10 +525,19 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not @Override public View createView(Context context, LayoutInflater inflater) { + for (int a = 0; a < 8; a++) { + chatMessageCellsCache.add(new ChatMessageCell(context)); + } + for (int a = 0; a < 4; a++) { + chatMediaCellsCache.add(new ChatMediaCell(context)); + } + lastPrintString = null; lastStatus = null; hasOwnBackground = true; + ResourceLoader.loadRecources(context); + actionBar.setBackButtonImage(R.drawable.ic_ab_back); actionBar.setActionBarMenuOnItemClick(new ActionBar.ActionBarMenuOnItemClick() { @Override @@ -545,6 +570,9 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not } } if (id == -1) { + if (chatActivityEnterView != null) { + chatActivityEnterView.hideEmojiPopup(); + } finishFragment(); } else if (id == -2) { selectedMessagesIds.clear(); @@ -564,11 +592,11 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not FileLog.e("tmessages", e); } } else if (id == attach_gallery) { - PhotoAlbumPickerActivity fragment = new PhotoAlbumPickerActivity(false); + PhotoAlbumPickerActivity fragment = new PhotoAlbumPickerActivity(false, ChatActivity.this); fragment.setDelegate(new PhotoAlbumPickerActivity.PhotoAlbumPickerActivityDelegate() { @Override - public void didSelectPhotos(ArrayList photos, ArrayList webPhotos) { - SendMessagesHelper.prepareSendingPhotos(photos, null, dialog_id, replyingMessageObject); + public void didSelectPhotos(ArrayList photos, ArrayList captions, ArrayList webPhotos) { + SendMessagesHelper.prepareSendingPhotos(photos, null, dialog_id, replyingMessageObject, captions); SendMessagesHelper.prepareSendingPhotosSearch(webPhotos, dialog_id, replyingMessageObject); showReplyPanel(false, null, null, null, false, true); } @@ -576,21 +604,36 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not @Override public void startPhotoSelectActivity() { try { + Intent videoPickerIntent = new Intent(); + videoPickerIntent.setType("video/*"); + videoPickerIntent.setAction(Intent.ACTION_GET_CONTENT); + videoPickerIntent.putExtra(MediaStore.EXTRA_SIZE_LIMIT, (long) (1024 * 1024 * 1536)); + Intent photoPickerIntent = new Intent(Intent.ACTION_PICK); photoPickerIntent.setType("image/*"); - startActivityForResult(photoPickerIntent, 1); + Intent chooserIntent = Intent.createChooser(photoPickerIntent, null); + chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, new Intent[]{videoPickerIntent}); + + startActivityForResult(chooserIntent, 1); } catch (Exception e) { FileLog.e("tmessages", e); } } + + @Override + public boolean didSelectVideo(String path) { + if (Build.VERSION.SDK_INT >= 16) { + return !openVideoEditor(path, true, true); + } else { + SendMessagesHelper.prepareSendingVideo(path, 0, 0, 0, 0, null, dialog_id, replyingMessageObject); + showReplyPanel(false, null, null, null, false, true); + return true; + } + } }); presentFragment(fragment); } else if (id == attach_video) { try { - Intent pickIntent = new Intent(); - pickIntent.setType("video/*"); - pickIntent.setAction(Intent.ACTION_GET_CONTENT); - pickIntent.putExtra(MediaStore.EXTRA_SIZE_LIMIT, (long) (1024 * 1024 * 1536)); Intent takeVideoIntent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE); File video = Utilities.generateVideoPath(); if (video != null) { @@ -600,10 +643,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not takeVideoIntent.putExtra(MediaStore.EXTRA_SIZE_LIMIT, (long) (1024 * 1024 * 1536)); currentPicturePath = video.getAbsolutePath(); } - Intent chooserIntent = Intent.createChooser(pickIntent, null); - chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, new Intent[]{takeVideoIntent}); - - startActivityForResult(chooserIntent, 2); + startActivityForResult(takeVideoIntent, 2); } catch (Exception e) { FileLog.e("tmessages", e); } @@ -614,8 +654,8 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not LocationActivity fragment = new LocationActivity(); fragment.setDelegate(new LocationActivity.LocationActivityDelegate() { @Override - public void didSelectLocation(double latitude, double longitude) { - SendMessagesHelper.getInstance().sendMessage(latitude, longitude, dialog_id, replyingMessageObject); + public void didSelectLocation(TLRPC.MessageMedia location) { + SendMessagesHelper.getInstance().sendMessage(location, dialog_id, replyingMessageObject); moveScrollToLastMessage(); showReplyPanel(false, null, null, null, false, true); if (paused) { @@ -855,14 +895,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not avatarContainer = new FrameLayoutFixed(context); avatarContainer.setBackgroundResource(R.drawable.bar_selector); avatarContainer.setPadding(AndroidUtilities.dp(8), 0, AndroidUtilities.dp(8), 0); - actionBar.addView(avatarContainer); - FrameLayout.LayoutParams layoutParams2 = (FrameLayout.LayoutParams) avatarContainer.getLayoutParams(); - layoutParams2.height = FrameLayout.LayoutParams.MATCH_PARENT; - layoutParams2.width = FrameLayout.LayoutParams.WRAP_CONTENT; - layoutParams2.rightMargin = AndroidUtilities.dp(40); - layoutParams2.leftMargin = AndroidUtilities.dp(56); - layoutParams2.gravity = Gravity.TOP | Gravity.LEFT; - avatarContainer.setLayoutParams(layoutParams2); + actionBar.addView(avatarContainer, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.MATCH_PARENT, Gravity.TOP | Gravity.LEFT, 56, 0, 40, 0)); avatarContainer.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { @@ -895,30 +928,14 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not avatarImageView = new BackupImageView(context); avatarImageView.setRoundRadius(AndroidUtilities.dp(21)); - avatarContainer.addView(avatarImageView); - layoutParams2 = (FrameLayout.LayoutParams) avatarImageView.getLayoutParams(); - layoutParams2.width = AndroidUtilities.dp(42); - layoutParams2.height = AndroidUtilities.dp(42); - layoutParams2.topMargin = AndroidUtilities.dp(3); - layoutParams2.gravity = Gravity.TOP | Gravity.LEFT; - avatarImageView.setLayoutParams(layoutParams2); + avatarContainer.addView(avatarImageView, LayoutHelper.createFrame(42, 42, Gravity.TOP | Gravity.LEFT, 0, 3, 0, 0)); if (currentEncryptedChat != null) { timeItem = new ImageView(context); timeItem.setPadding(AndroidUtilities.dp(10), AndroidUtilities.dp(10), AndroidUtilities.dp(5), AndroidUtilities.dp(5)); timeItem.setScaleType(ImageView.ScaleType.CENTER); - avatarContainer.addView(timeItem); - timerDrawable = new TimerDrawable(context); - - layoutParams2 = (FrameLayout.LayoutParams) timeItem.getLayoutParams(); - layoutParams2.width = AndroidUtilities.dp(34); - layoutParams2.height = AndroidUtilities.dp(34); - layoutParams2.topMargin = AndroidUtilities.dp(18); - layoutParams2.leftMargin = AndroidUtilities.dp(16); - layoutParams2.gravity = Gravity.TOP | Gravity.LEFT; - timeItem.setLayoutParams(layoutParams2); - timeItem.setImageDrawable(timerDrawable); - + timeItem.setImageDrawable(timerDrawable = new TimerDrawable(context)); + avatarContainer.addView(timeItem, LayoutHelper.createFrame(34, 34, Gravity.TOP | Gravity.LEFT, 16, 18, 0, 0)); timeItem.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { @@ -940,14 +957,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not nameTextView.setGravity(Gravity.LEFT); nameTextView.setCompoundDrawablePadding(AndroidUtilities.dp(4)); nameTextView.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); - avatarContainer.addView(nameTextView); - layoutParams2 = (FrameLayout.LayoutParams) nameTextView.getLayoutParams(); - layoutParams2.width = FrameLayout.LayoutParams.WRAP_CONTENT; - layoutParams2.height = FrameLayout.LayoutParams.WRAP_CONTENT; - layoutParams2.leftMargin = AndroidUtilities.dp(54); - layoutParams2.bottomMargin = AndroidUtilities.dp(22); - layoutParams2.gravity = Gravity.BOTTOM; - nameTextView.setLayoutParams(layoutParams2); + avatarContainer.addView(nameTextView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.LEFT | Gravity.BOTTOM, 54, 0, 0, 22)); onlineTextView = new TextView(context); onlineTextView.setTextColor(0xffd7e8f7); @@ -957,14 +967,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not onlineTextView.setSingleLine(true); onlineTextView.setEllipsize(TextUtils.TruncateAt.END); onlineTextView.setGravity(Gravity.LEFT); - avatarContainer.addView(onlineTextView); - layoutParams2 = (FrameLayout.LayoutParams) onlineTextView.getLayoutParams(); - layoutParams2.width = FrameLayout.LayoutParams.WRAP_CONTENT; - layoutParams2.height = FrameLayout.LayoutParams.WRAP_CONTENT; - layoutParams2.leftMargin = AndroidUtilities.dp(54); - layoutParams2.bottomMargin = AndroidUtilities.dp(4); - layoutParams2.gravity = Gravity.BOTTOM; - onlineTextView.setLayoutParams(layoutParams2); + avatarContainer.addView(onlineTextView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.LEFT | Gravity.BOTTOM, 54, 0, 0, 4)); ActionBarMenu menu = actionBar.createMenu(); @@ -982,10 +985,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not headerItem.addSubItem(delete_chat, LocaleController.getString("DeleteChatUser", R.string.DeleteChatUser), 0); } muteItem = headerItem.addSubItem(mute, null, 0); - - LinearLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams) headerItem.getLayoutParams(); - layoutParams.rightMargin = AndroidUtilities.dp(-48); - headerItem.setLayoutParams(layoutParams); + ((LinearLayout.LayoutParams) headerItem.getLayoutParams()).setMargins(0, 0, AndroidUtilities.dp(-48), 0); updateTitle(); updateSubtitle(); @@ -1022,18 +1022,13 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not selectedMessagesCountTextView.setEllipsize(TextUtils.TruncateAt.END); selectedMessagesCountTextView.setPadding(AndroidUtilities.dp(11), 0, 0, AndroidUtilities.dp(2)); selectedMessagesCountTextView.setGravity(Gravity.CENTER_VERTICAL); + actionMode.addView(selectedMessagesCountTextView, LayoutHelper.createLinear(0, LayoutHelper.MATCH_PARENT, 1.0f)); selectedMessagesCountTextView.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { return true; } }); - actionMode.addView(selectedMessagesCountTextView); - layoutParams = (LinearLayout.LayoutParams) selectedMessagesCountTextView.getLayoutParams(); - layoutParams.weight = 1; - layoutParams.width = 0; - layoutParams.height = LinearLayout.LayoutParams.MATCH_PARENT; - selectedMessagesCountTextView.setLayoutParams(layoutParams); if (currentEncryptedChat == null) { actionModeViews.add(actionMode.addItem(copy, R.drawable.ic_ab_fwd_copy, R.drawable.bar_selector_mode, null, AndroidUtilities.dp(54))); @@ -1053,6 +1048,20 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not checkActionBarMenu(); fragmentView = new SizeNotifierRelativeLayout(context); + + /* + { + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + if (chatActivityEnterView chatActivityEnterView.isEmojiPopupShowing()) { + int height = MeasureSpec.getSize(heightMeasureSpec); + heightMeasureSpec = MeasureSpec.makeMeasureSpec(height - chatActivityEnterView.getEmojiHeight(), MeasureSpec.getMode(heightMeasureSpec)); + } + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + } + }; + */ + SizeNotifierRelativeLayout contentView = (SizeNotifierRelativeLayout) fragmentView; contentView.setBackgroundImage(ApplicationLoader.getCachedWallpaper()); @@ -1060,11 +1069,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not emptyViewContainer = new FrameLayout(context); emptyViewContainer.setPadding(0, 0, 0, AndroidUtilities.dp(48)); emptyViewContainer.setVisibility(View.INVISIBLE); - contentView.addView(emptyViewContainer); - RelativeLayout.LayoutParams layoutParams3 = (RelativeLayout.LayoutParams) emptyViewContainer.getLayoutParams(); - layoutParams3.width = RelativeLayout.LayoutParams.MATCH_PARENT; - layoutParams3.height = RelativeLayout.LayoutParams.MATCH_PARENT; - emptyViewContainer.setLayoutParams(layoutParams3); + contentView.addView(emptyViewContainer, new RelativeLayout.LayoutParams(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT)); emptyViewContainer.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { @@ -1084,23 +1089,13 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not emptyView.setTextColor(0xffffffff); emptyView.setBackgroundResource(ApplicationLoader.isCustomTheme() ? R.drawable.system_black : R.drawable.system_blue); emptyView.setPadding(AndroidUtilities.dp(7), AndroidUtilities.dp(1), AndroidUtilities.dp(7), AndroidUtilities.dp(1)); - emptyViewContainer.addView(emptyView); - layoutParams2 = (FrameLayout.LayoutParams) emptyView.getLayoutParams(); - layoutParams2.width = FrameLayout.LayoutParams.WRAP_CONTENT; - layoutParams2.height = FrameLayout.LayoutParams.WRAP_CONTENT; - layoutParams2.gravity = Gravity.CENTER; - emptyView.setLayoutParams(layoutParams2); + emptyViewContainer.addView(emptyView, new FrameLayout.LayoutParams(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER)); } else { LinearLayout secretChatPlaceholder = new LinearLayout(context); secretChatPlaceholder.setBackgroundResource(ApplicationLoader.isCustomTheme() ? R.drawable.system_black : R.drawable.system_blue); secretChatPlaceholder.setPadding(AndroidUtilities.dp(16), AndroidUtilities.dp(12), AndroidUtilities.dp(16), AndroidUtilities.dp(12)); secretChatPlaceholder.setOrientation(LinearLayout.VERTICAL); - emptyViewContainer.addView(secretChatPlaceholder); - layoutParams2 = (FrameLayout.LayoutParams) secretChatPlaceholder.getLayoutParams(); - layoutParams2.width = FrameLayout.LayoutParams.WRAP_CONTENT; - layoutParams2.height = FrameLayout.LayoutParams.WRAP_CONTENT; - layoutParams2.gravity = Gravity.CENTER; - secretChatPlaceholder.setLayoutParams(layoutParams2); + emptyViewContainer.addView(secretChatPlaceholder, new FrameLayout.LayoutParams(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER)); secretViewStatusTextView = new TextView(context); secretViewStatusTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 15); @@ -1120,12 +1115,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not secretViewStatusTextView.setText(LocaleController.formatString("EncryptedPlaceholderTitleIncoming", R.string.EncryptedPlaceholderTitleIncoming, currentUser.last_name)); } } - secretChatPlaceholder.addView(secretViewStatusTextView); - layoutParams = (LinearLayout.LayoutParams) secretViewStatusTextView.getLayoutParams(); - layoutParams.width = LinearLayout.LayoutParams.WRAP_CONTENT; - layoutParams.height = LinearLayout.LayoutParams.WRAP_CONTENT; - layoutParams.gravity = Gravity.CENTER_HORIZONTAL; - secretViewStatusTextView.setLayoutParams(layoutParams); + secretChatPlaceholder.addView(secretViewStatusTextView, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER_HORIZONTAL | Gravity.TOP)); TextView textView = new TextView(context); textView.setText(LocaleController.getString("EncryptedDescriptionTitle", R.string.EncryptedDescriptionTitle)); @@ -1133,24 +1123,12 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not textView.setTextColor(0xffffffff); textView.setGravity(Gravity.CENTER_HORIZONTAL); textView.setMaxWidth(AndroidUtilities.dp(260)); - secretChatPlaceholder.addView(textView); - layoutParams = (LinearLayout.LayoutParams) textView.getLayoutParams(); - layoutParams.width = LinearLayout.LayoutParams.WRAP_CONTENT; - layoutParams.height = LinearLayout.LayoutParams.WRAP_CONTENT; - layoutParams.topMargin = AndroidUtilities.dp(8); - layoutParams.gravity = LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT; - textView.setLayoutParams(layoutParams); + secretChatPlaceholder.addView(textView, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP, 0, 8, 0, 0)); for (int a = 0; a < 4; a++) { LinearLayout linearLayout = new LinearLayout(context); linearLayout.setOrientation(LinearLayout.HORIZONTAL); - secretChatPlaceholder.addView(linearLayout); - layoutParams = (LinearLayout.LayoutParams) linearLayout.getLayoutParams(); - layoutParams.width = LinearLayout.LayoutParams.WRAP_CONTENT; - layoutParams.height = LinearLayout.LayoutParams.WRAP_CONTENT; - layoutParams.topMargin = AndroidUtilities.dp(8); - layoutParams.gravity = LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT; - linearLayout.setLayoutParams(layoutParams); + secretChatPlaceholder.addView(linearLayout, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT, 0, 8, 0, 0)); ImageView imageView = new ImageView(context); imageView.setImageResource(R.drawable.ic_lock_white); @@ -1177,45 +1155,123 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not } if (LocaleController.isRTL) { - linearLayout.addView(textView); - linearLayout.addView(imageView); + linearLayout.addView(textView, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT)); + linearLayout.addView(imageView, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, 8, 3, 0, 0)); } else { - linearLayout.addView(imageView); - linearLayout.addView(textView); + linearLayout.addView(imageView, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, 0, 4, 8, 0)); + linearLayout.addView(textView, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT)); } - layoutParams = (LinearLayout.LayoutParams) imageView.getLayoutParams(); - layoutParams.width = LinearLayout.LayoutParams.WRAP_CONTENT; - layoutParams.height = LinearLayout.LayoutParams.WRAP_CONTENT; - layoutParams.rightMargin = LocaleController.isRTL ? 0 : AndroidUtilities.dp(8); - layoutParams.leftMargin = LocaleController.isRTL ? AndroidUtilities.dp(8) : 0; - layoutParams.topMargin = AndroidUtilities.dp(LocaleController.isRTL ? 3 : 4); - imageView.setLayoutParams(layoutParams); - - layoutParams = (LinearLayout.LayoutParams) textView.getLayoutParams(); - layoutParams.width = LinearLayout.LayoutParams.WRAP_CONTENT; - layoutParams.height = LinearLayout.LayoutParams.WRAP_CONTENT; - textView.setLayoutParams(layoutParams); } } - chatListView = new LayoutListView(context); - chatListView.setAdapter(chatAdapter = new ChatAdapter(context)); - chatListView.setCacheColorHint(ApplicationLoader.getSelectedColor()); + chatListView = (RecyclerExListView) inflater.inflate(R.layout.recycler_view, null); + chatListView.setAdapter(chatAdapter = new ChatActivityAdapter(context)); chatListView.setClipToPadding(false); - chatListView.setStackFromBottom(true); chatListView.setPadding(0, AndroidUtilities.dp(4), 0, AndroidUtilities.dp(3)); - chatListView.setDivider(null); - chatListView.setSelector(R.drawable.transparent); + chatListView.setItemAnimator(null); + chatListView.setLayoutAnimation(null); + chatLayoutManager = new LinearLayoutManager(context) { + @Override + public boolean supportsPredictiveItemAnimations() { + return false; + } + }; + chatLayoutManager.setOrientation(LinearLayoutManager.VERTICAL); + chatLayoutManager.setStackFromEnd(true); + chatListView.setLayoutManager(chatLayoutManager); + contentView.addView(chatListView, LayoutHelper.createRelative(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, 0, 0, 0, -3, RelativeLayout.ABOVE, id_chat_compose_panel)); chatListView.setOnItemLongClickListener(onItemLongClickListener); chatListView.setOnItemClickListener(onItemClickListener); - contentView.addView(chatListView); - layoutParams3 = (RelativeLayout.LayoutParams) chatListView.getLayoutParams(); - layoutParams3.width = RelativeLayout.LayoutParams.MATCH_PARENT; - layoutParams3.height = RelativeLayout.LayoutParams.MATCH_PARENT; - layoutParams3.bottomMargin = -AndroidUtilities.dp(3); - layoutParams3.addRule(RelativeLayout.ABOVE, id_chat_compose_panel); - chatListView.setLayoutParams(layoutParams3); - chatListView.setOnInterceptTouchEventListener(new LayoutListView.OnInterceptTouchEventListener() { + chatListView.setOnScrollListener(new RecyclerView.OnScrollListener() { + + @Override + public void onScrollStateChanged(RecyclerView recyclerView, int newState) { + if (newState != RecyclerView.SCROLL_STATE_DRAGGING && highlightMessageId != Integer.MAX_VALUE) { + highlightMessageId = Integer.MAX_VALUE; + updateVisibleRows(); + } + } + + @Override + public void onScrolled(RecyclerView recyclerView, int dx, int dy) { + int firstVisibleItem = chatLayoutManager.findFirstVisibleItemPosition(); + int visibleItemCount = Math.abs(chatLayoutManager.findLastVisibleItemPosition() - firstVisibleItem) + 1; + if (visibleItemCount > 0) { + int totalItemCount = chatAdapter.getItemCount(); + if (firstVisibleItem <= 10) { + if (!endReached && !loading) { + if (messagesByDays.size() != 0) { + MessagesController.getInstance().loadMessages(dialog_id, 20, maxMessageId, !cacheEndReaced && startLoadFromMessageId == 0, minDate, classGuid, 0, 0, 0, startLoadFromMessageId == 0); + } else { + MessagesController.getInstance().loadMessages(dialog_id, 20, 0, !cacheEndReaced && startLoadFromMessageId == 0, minDate, classGuid, 0, 0, 0, startLoadFromMessageId == 0); + } + loading = true; + } + } + if (firstVisibleItem + visibleItemCount >= totalItemCount - 6) { + if (!forward_end_reached && !loadingForward) { + MessagesController.getInstance().loadMessages(dialog_id, 20, minMessageId, startLoadFromMessageId == 0, maxDate, classGuid, 1, 0, 0, startLoadFromMessageId == 0); + loadingForward = true; + } + } + if (firstVisibleItem + visibleItemCount == totalItemCount && forward_end_reached) { + showPagedownButton(false, true); + } + } + updateMessagesVisisblePart(); + } + }); + chatListView.setOnTouchListener(new View.OnTouchListener() { + @Override + public boolean onTouch(View v, MotionEvent event) { + if (openSecretPhotoRunnable != null || SecretPhotoViewer.getInstance().isVisible()) { + if (event.getAction() == MotionEvent.ACTION_UP || event.getAction() == MotionEvent.ACTION_CANCEL || event.getAction() == MotionEvent.ACTION_POINTER_UP) { + AndroidUtilities.runOnUIThread(new Runnable() { + @Override + public void run() { + chatListView.setOnItemClickListener(onItemClickListener); + } + }, 150); + if (openSecretPhotoRunnable != null) { + AndroidUtilities.cancelRunOnUIThread(openSecretPhotoRunnable); + openSecretPhotoRunnable = null; + try { + Toast.makeText(v.getContext(), LocaleController.getString("PhotoTip", R.string.PhotoTip), Toast.LENGTH_SHORT).show(); + } catch (Exception e) { + FileLog.e("tmessages", e); + } + } else { + if (SecretPhotoViewer.getInstance().isVisible()) { + AndroidUtilities.runOnUIThread(new Runnable() { + @Override + public void run() { + chatListView.setOnItemLongClickListener(onItemLongClickListener); + chatListView.setLongClickable(true); + } + }); + SecretPhotoViewer.getInstance().closePhoto(); + } + } + } else if (event.getAction() != MotionEvent.ACTION_DOWN) { + if (SecretPhotoViewer.getInstance().isVisible()) { + return true; + } else if (openSecretPhotoRunnable != null) { + if (event.getAction() == MotionEvent.ACTION_MOVE) { + if (Math.hypot(startX - event.getX(), startY - event.getY()) > AndroidUtilities.dp(5)) { + AndroidUtilities.cancelRunOnUIThread(openSecretPhotoRunnable); + openSecretPhotoRunnable = null; + } + } else { + AndroidUtilities.cancelRunOnUIThread(openSecretPhotoRunnable); + openSecretPhotoRunnable = null; + } + } + } + } + return false; + } + }); + chatListView.setOnInterceptTouchListener(new RecyclerExListView.OnInterceptTouchListener() { @Override public boolean onInterceptTouchEvent(MotionEvent event) { if (actionBar.isActionModeShowed()) { @@ -1273,111 +1329,14 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not return false; } }); - chatListView.setOnTouchListener(new View.OnTouchListener() { - @Override - public boolean onTouch(View v, MotionEvent event) { - if (openSecretPhotoRunnable != null || SecretPhotoViewer.getInstance().isVisible()) { - if (event.getAction() == MotionEvent.ACTION_UP || event.getAction() == MotionEvent.ACTION_CANCEL || event.getAction() == MotionEvent.ACTION_POINTER_UP) { - AndroidUtilities.runOnUIThread(new Runnable() { - @Override - public void run() { - chatListView.setOnItemClickListener(onItemClickListener); - } - }, 150); - if (openSecretPhotoRunnable != null) { - AndroidUtilities.cancelRunOnUIThread(openSecretPhotoRunnable); - openSecretPhotoRunnable = null; - try { - Toast.makeText(v.getContext(), LocaleController.getString("PhotoTip", R.string.PhotoTip), Toast.LENGTH_SHORT).show(); - } catch (Exception e) { - FileLog.e("tmessages", e); - } - } else { - if (SecretPhotoViewer.getInstance().isVisible()) { - AndroidUtilities.runOnUIThread(new Runnable() { - @Override - public void run() { - chatListView.setOnItemLongClickListener(onItemLongClickListener); - chatListView.setLongClickable(true); - } - }); - SecretPhotoViewer.getInstance().closePhoto(); - } - } - } else if (event.getAction() != MotionEvent.ACTION_DOWN) { - if (SecretPhotoViewer.getInstance().isVisible()) { - return true; - } else if (openSecretPhotoRunnable != null) { - if (event.getAction() == MotionEvent.ACTION_MOVE) { - if (Math.hypot(startX - event.getX(), startY - event.getY()) > AndroidUtilities.dp(5)) { - AndroidUtilities.cancelRunOnUIThread(openSecretPhotoRunnable); - openSecretPhotoRunnable = null; - } - } else { - AndroidUtilities.cancelRunOnUIThread(openSecretPhotoRunnable); - openSecretPhotoRunnable = null; - } - } - } - } - return false; - } - }); - chatListView.setOnScrollListener(new AbsListView.OnScrollListener() { - Rect scrollRect = new Rect(); - - @Override - public void onScrollStateChanged(AbsListView absListView, int i) { - if (i == AbsListView.OnScrollListener.SCROLL_STATE_TOUCH_SCROLL || i == AbsListView.OnScrollListener.SCROLL_STATE_FLING && highlightMessageId != Integer.MAX_VALUE) { - highlightMessageId = Integer.MAX_VALUE; - updateVisibleRows(); - } - } - - @Override - public void onScroll(AbsListView absListView, int firstVisibleItem, int visibleItemCount, int totalItemCount) { - if (visibleItemCount > 0) { - if (firstVisibleItem <= 10) { - if (!endReached && !loading) { - if (messagesByDays.size() != 0) { - MessagesController.getInstance().loadMessages(dialog_id, 20, maxMessageId, !cacheEndReaced && startLoadFromMessageId == 0, minDate, classGuid, 0, 0, 0, startLoadFromMessageId == 0); - } else { - MessagesController.getInstance().loadMessages(dialog_id, 20, 0, !cacheEndReaced && startLoadFromMessageId == 0, minDate, classGuid, 0, 0, 0, startLoadFromMessageId == 0); - } - loading = true; - } - } - if (firstVisibleItem + visibleItemCount >= totalItemCount - 6) { - if (!forward_end_reached && !loadingForward) { - MessagesController.getInstance().loadMessages(dialog_id, 20, minMessageId, startLoadFromMessageId == 0, maxDate, classGuid, 1, 0, 0, startLoadFromMessageId == 0); - loadingForward = true; - } - } - if (firstVisibleItem + visibleItemCount == totalItemCount && forward_end_reached) { - showPagedownButton(false, true); - } - } - for (int a = 0; a < visibleItemCount; a++) { - View view = absListView.getChildAt(a); - if (view instanceof ChatMessageCell) { - ChatMessageCell messageCell = (ChatMessageCell) view; - messageCell.getLocalVisibleRect(scrollRect); - messageCell.setVisiblePart(scrollRect.top, scrollRect.bottom - scrollRect.top); - } - } - } - }); progressView = new FrameLayout(context); progressView.setVisibility(View.INVISIBLE); - progressView.setBackgroundResource(ApplicationLoader.isCustomTheme() ? R.drawable.system_loader2 : R.drawable.system_loader1); - contentView.addView(progressView); - layoutParams3 = (RelativeLayout.LayoutParams) progressView.getLayoutParams(); - layoutParams3.width = AndroidUtilities.dp(36); - layoutParams3.height = AndroidUtilities.dp(36); - layoutParams3.bottomMargin = AndroidUtilities.dp(48); - layoutParams3.addRule(RelativeLayout.CENTER_IN_PARENT); - progressView.setLayoutParams(layoutParams3); + contentView.addView(progressView, LayoutHelper.createRelative(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, 0, 0, 0, 48)); + + View view = new View(context); + view.setBackgroundResource(ApplicationLoader.isCustomTheme() ? R.drawable.system_loader2 : R.drawable.system_loader1); + progressView.addView(view, LayoutHelper.createFrame(36, 36, Gravity.CENTER)); ProgressBar progressBar = new ProgressBar(context); try { @@ -1387,12 +1346,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not } progressBar.setIndeterminate(true); AndroidUtilities.setProgressBarAnimationDuration(progressBar, 1500); - progressView.addView(progressBar); - layoutParams2 = (FrameLayout.LayoutParams) progressBar.getLayoutParams(); - layoutParams2.width = AndroidUtilities.dp(32); - layoutParams2.height = AndroidUtilities.dp(32); - layoutParams2.gravity = Gravity.CENTER; - progressBar.setLayoutParams(layoutParams2); + progressView.addView(progressBar, LayoutHelper.createFrame(32, 32, Gravity.CENTER)); if (currentEncryptedChat == null && !isBroadcast) { mentionListView = new ListView(context); @@ -1405,15 +1359,9 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not if (Build.VERSION.SDK_INT > 8) { mentionListView.setOverScrollMode(ListView.OVER_SCROLL_NEVER); } - contentView.addView(mentionListView); - layoutParams3 = (RelativeLayout.LayoutParams) mentionListView.getLayoutParams(); - layoutParams3.width = RelativeLayout.LayoutParams.MATCH_PARENT; - layoutParams3.height = AndroidUtilities.dp(110); - layoutParams3.topMargin = -AndroidUtilities.dp(108); - layoutParams3.addRule(RelativeLayout.ALIGN_TOP, id_chat_compose_panel); - mentionListView.setLayoutParams(layoutParams3); + contentView.addView(mentionListView, LayoutHelper.createRelative(LayoutHelper.MATCH_PARENT, 110, 0, -108, 0, 0, RelativeLayout.ALIGN_TOP, id_chat_compose_panel)); - mentionListView.setAdapter(mentionsAdapter = new MentionsAdapter(context, new MentionsAdapter.MentionsAdapterDelegate() { + mentionListView.setAdapter(mentionsAdapter = new MentionsAdapter(context, false, new MentionsAdapter.MentionsAdapterDelegate() { @Override public void needChangePanelVisibility(boolean show) { if (show) { @@ -1537,12 +1485,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not chatActivityEnterView.setDialogId(dialog_id); chatActivityEnterView.addToAttachLayout(menuItem); chatActivityEnterView.setId(id_chat_compose_panel); - contentView.addView(chatActivityEnterView); - layoutParams3 = (RelativeLayout.LayoutParams) chatActivityEnterView.getLayoutParams(); - layoutParams3.width = RelativeLayout.LayoutParams.MATCH_PARENT; - layoutParams3.height = RelativeLayout.LayoutParams.WRAP_CONTENT; - layoutParams3.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM); - chatActivityEnterView.setLayoutParams(layoutParams3); + contentView.addView(chatActivityEnterView, LayoutHelper.createRelative(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, RelativeLayout.ALIGN_PARENT_BOTTOM)); chatActivityEnterView.setDelegate(new ChatActivityEnterView.ChatActivityEnterViewDelegate() { @Override public void onMessageSend(String message) { @@ -1568,25 +1511,27 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not AndroidUtilities.cancelRunOnUIThread(waitingForCharaterEnterRunnable); waitingForCharaterEnterRunnable = null; } - if (bigChange) { - searchLinks(text, true); - } else { - waitingForCharaterEnterRunnable = new Runnable() { - @Override - public void run() { - if (this == waitingForCharaterEnterRunnable) { - searchLinks(text, false); - waitingForCharaterEnterRunnable = null; + if (chatActivityEnterView.isMessageWebPageSearchEnabled()) { + if (bigChange) { + searchLinks(text, true); + } else { + waitingForCharaterEnterRunnable = new Runnable() { + @Override + public void run() { + if (this == waitingForCharaterEnterRunnable) { + searchLinks(text, false); + waitingForCharaterEnterRunnable = null; + } } - } - }; - AndroidUtilities.runOnUIThread(waitingForCharaterEnterRunnable, 1000); + }; + AndroidUtilities.runOnUIThread(waitingForCharaterEnterRunnable, 3000); + } } } @Override public void needSendTyping() { - MessagesController.getInstance().sendTyping(dialog_id, classGuid); + MessagesController.getInstance().sendTyping(dialog_id, 0, classGuid); } @Override @@ -1632,6 +1577,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not mentionListView.setVisibility(View.VISIBLE); } } + updateMessagesVisisblePart(); } }); @@ -1641,32 +1587,16 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not View lineView = new View(context); lineView.setBackgroundColor(0xffe8e8e8); - replyLayout.addView(lineView); - layoutParams2 = (FrameLayout.LayoutParams) lineView.getLayoutParams(); - layoutParams2.gravity = Gravity.BOTTOM; - layoutParams2.width = FrameLayout.LayoutParams.MATCH_PARENT; - layoutParams2.height = AndroidUtilities.dp(1); - lineView.setLayoutParams(layoutParams2); + replyLayout.addView(lineView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 1, Gravity.BOTTOM | Gravity.LEFT)); replyIconImageView = new ImageView(context); replyIconImageView.setScaleType(ImageView.ScaleType.CENTER); - replyLayout.addView(replyIconImageView); - layoutParams2 = (FrameLayout.LayoutParams) replyIconImageView.getLayoutParams(); - layoutParams2.gravity = Gravity.TOP | Gravity.LEFT; - layoutParams2.width = AndroidUtilities.dp(52); - layoutParams2.height = AndroidUtilities.dp(46); - replyIconImageView.setLayoutParams(layoutParams2); + replyLayout.addView(replyIconImageView, LayoutHelper.createFrame(52, 46, Gravity.TOP | Gravity.LEFT)); ImageView imageView = new ImageView(context); imageView.setImageResource(R.drawable.delete_reply); imageView.setScaleType(ImageView.ScaleType.CENTER); - replyLayout.addView(imageView); - layoutParams2 = (FrameLayout.LayoutParams) imageView.getLayoutParams(); - layoutParams2.topMargin = AndroidUtilities.dp(0.5f); - layoutParams2.width = AndroidUtilities.dp(52); - layoutParams2.height = AndroidUtilities.dp(46); - layoutParams2.gravity = Gravity.RIGHT | Gravity.TOP; - imageView.setLayoutParams(layoutParams2); + replyLayout.addView(imageView, LayoutHelper.createFrame(52, 46, Gravity.RIGHT | Gravity.TOP, 0, 0.5f, 0, 0)); imageView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { @@ -1684,15 +1614,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not replyNameTextView.setSingleLine(true); replyNameTextView.setEllipsize(TextUtils.TruncateAt.END); replyNameTextView.setMaxLines(1); - replyLayout.addView(replyNameTextView); - layoutParams2 = (FrameLayout.LayoutParams) replyNameTextView.getLayoutParams(); - layoutParams2.leftMargin = AndroidUtilities.dp(52); - layoutParams2.rightMargin = AndroidUtilities.dp(52); - layoutParams2.topMargin = AndroidUtilities.dp(4); - layoutParams2.width = FrameLayout.LayoutParams.WRAP_CONTENT; - layoutParams2.height = FrameLayout.LayoutParams.WRAP_CONTENT; - layoutParams2.gravity = Gravity.TOP | Gravity.LEFT; - replyNameTextView.setLayoutParams(layoutParams2); + replyLayout.addView(replyNameTextView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.TOP | Gravity.LEFT, 52, 4, 52, 0)); replyObjectTextView = new TextView(context); replyObjectTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); @@ -1700,35 +1622,14 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not replyObjectTextView.setSingleLine(true); replyObjectTextView.setEllipsize(TextUtils.TruncateAt.END); replyObjectTextView.setMaxLines(1); - replyLayout.addView(replyObjectTextView); - layoutParams2 = (FrameLayout.LayoutParams) replyObjectTextView.getLayoutParams(); - layoutParams2.leftMargin = AndroidUtilities.dp(52); - layoutParams2.rightMargin = AndroidUtilities.dp(52); - layoutParams2.topMargin = AndroidUtilities.dp(22); - layoutParams2.width = FrameLayout.LayoutParams.WRAP_CONTENT; - layoutParams2.height = FrameLayout.LayoutParams.WRAP_CONTENT; - layoutParams2.gravity = Gravity.TOP | Gravity.LEFT; - replyObjectTextView.setLayoutParams(layoutParams2); + replyLayout.addView(replyObjectTextView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.TOP | Gravity.LEFT, 52, 22, 52, 0)); replyImageView = new BackupImageView(context); - replyLayout.addView(replyImageView); - layoutParams2 = (FrameLayout.LayoutParams) replyImageView.getLayoutParams(); - layoutParams2.leftMargin = AndroidUtilities.dp(52); - layoutParams2.topMargin = AndroidUtilities.dp(6); - layoutParams2.width = AndroidUtilities.dp(34); - layoutParams2.height = AndroidUtilities.dp(34); - layoutParams2.gravity = Gravity.TOP | Gravity.LEFT; - replyImageView.setLayoutParams(layoutParams2); + replyLayout.addView(replyImageView, LayoutHelper.createFrame(34, 34, Gravity.TOP | Gravity.LEFT, 52, 6, 0, 0)); stickersPanel = new FrameLayout(context); stickersPanel.setVisibility(View.GONE); - contentView.addView(stickersPanel); - layoutParams3 = (RelativeLayout.LayoutParams) stickersPanel.getLayoutParams(); - layoutParams3.width = RelativeLayout.LayoutParams.WRAP_CONTENT; - layoutParams3.height = AndroidUtilities.dp(81.5f); - layoutParams3.bottomMargin = AndroidUtilities.dp(38); - layoutParams3.addRule(RelativeLayout.ALIGN_BOTTOM, id_chat_compose_panel); - stickersPanel.setLayoutParams(layoutParams3); + contentView.addView(stickersPanel, LayoutHelper.createRelative(LayoutHelper.WRAP_CONTENT, 81.5f, 0, 0, 0, 38, RelativeLayout.ALIGN_BOTTOM, id_chat_compose_panel)); stickersListView = new RecyclerListView(context); LinearLayoutManager layoutManager = new LinearLayoutManager(context); @@ -1738,12 +1639,9 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not if (Build.VERSION.SDK_INT >= 9) { stickersListView.setOverScrollMode(RecyclerListView.OVER_SCROLL_NEVER); } - stickersPanel.addView(stickersListView); - layoutParams2 = (FrameLayout.LayoutParams) stickersListView.getLayoutParams(); - layoutParams2.width = FrameLayout.LayoutParams.MATCH_PARENT; - layoutParams2.height = AndroidUtilities.dp(78); - stickersListView.setLayoutParams(layoutParams2); + stickersPanel.addView(stickersListView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 78)); if (currentEncryptedChat == null || currentEncryptedChat != null && AndroidUtilities.getPeerLayerVersion(currentEncryptedChat.layer) >= 23) { + chatActivityEnterView.setAllowStickers(true); if (stickersAdapter != null) { stickersAdapter.destroy(); } @@ -1793,45 +1691,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not public void onItemClick(View view, int position) { TLRPC.Document document = stickersAdapter.getItem(position); if (document instanceof TLRPC.TL_document) { - if (currentEncryptedChat != null && document.thumb instanceof TLRPC.TL_photoSize) { - File file = FileLoader.getPathToAttach(document.thumb, true); - if (file.exists()) { - try { - int len = (int) file.length(); - byte[] arr = new byte[(int) file.length()]; - RandomAccessFile reader = new RandomAccessFile(file, "r"); - reader.readFully(arr); - TLRPC.TL_document newDocument = new TLRPC.TL_document(); - newDocument.thumb = new TLRPC.TL_photoCachedSize(); - newDocument.thumb.location = document.thumb.location; - newDocument.thumb.size = document.thumb.size; - newDocument.thumb.w = document.thumb.w; - newDocument.thumb.h = document.thumb.h; - newDocument.thumb.type = document.thumb.type; - newDocument.thumb.bytes = arr; - - newDocument.id = document.id; - newDocument.access_hash = document.access_hash; - newDocument.date = document.date; - newDocument.mime_type = document.mime_type; - newDocument.size = document.size; - newDocument.dc_id = document.dc_id; - newDocument.attributes = document.attributes; - document = newDocument; - } catch (Exception e) { - FileLog.e("tmessages", e); - } - } - } - for (int a = 0; a < document.attributes.size(); a++) { - TLRPC.DocumentAttribute attribute = document.attributes.get(a); - if (attribute instanceof TLRPC.TL_documentAttributeSticker) { - document.attributes.remove(a); - document.attributes.add(new TLRPC.TL_documentAttributeSticker_old()); - break; - } - } - SendMessagesHelper.getInstance().sendMessage((TLRPC.TL_document) document, null, null, dialog_id, replyingMessageObject); + SendMessagesHelper.getInstance().sendSticker(document, dialog_id, replyingMessageObject); showReplyPanel(false, null, null, null, false, true); } chatActivityEnterView.setFieldText(""); @@ -1841,13 +1701,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not imageView = new ImageView(context); imageView.setImageResource(R.drawable.stickers_back_arrow); - stickersPanel.addView(imageView); - layoutParams2 = (FrameLayout.LayoutParams) imageView.getLayoutParams(); - layoutParams2.width = FrameLayout.LayoutParams.WRAP_CONTENT; - layoutParams2.height = FrameLayout.LayoutParams.WRAP_CONTENT; - layoutParams2.gravity = Gravity.BOTTOM; - layoutParams2.leftMargin = AndroidUtilities.dp(53); - imageView.setLayoutParams(layoutParams2); + stickersPanel.addView(imageView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.BOTTOM | Gravity.LEFT, 53, 0, 0, 0)); bottomOverlay = new FrameLayout(context); bottomOverlay.setBackgroundColor(0xffffffff); @@ -1855,32 +1709,17 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not bottomOverlay.setFocusable(true); bottomOverlay.setFocusableInTouchMode(true); bottomOverlay.setClickable(true); - contentView.addView(bottomOverlay); - layoutParams3 = (RelativeLayout.LayoutParams) bottomOverlay.getLayoutParams(); - layoutParams3.width = RelativeLayout.LayoutParams.MATCH_PARENT; - layoutParams3.height = AndroidUtilities.dp(48); - layoutParams3.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM); - bottomOverlay.setLayoutParams(layoutParams3); + contentView.addView(bottomOverlay, LayoutHelper.createRelative(LayoutHelper.MATCH_PARENT, 48, RelativeLayout.ALIGN_PARENT_BOTTOM)); bottomOverlayText = new TextView(context); bottomOverlayText.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16); bottomOverlayText.setTextColor(0xff7f7f7f); - bottomOverlay.addView(bottomOverlayText); - layoutParams2 = (FrameLayout.LayoutParams) bottomOverlayText.getLayoutParams(); - layoutParams2.width = FrameLayout.LayoutParams.WRAP_CONTENT; - layoutParams2.height = FrameLayout.LayoutParams.WRAP_CONTENT; - layoutParams2.gravity = Gravity.CENTER; - bottomOverlayText.setLayoutParams(layoutParams2); + bottomOverlay.addView(bottomOverlayText, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER)); bottomOverlayChat = new FrameLayout(context); bottomOverlayChat.setBackgroundColor(0xfffbfcfd); bottomOverlayChat.setVisibility(View.INVISIBLE); - contentView.addView(bottomOverlayChat); - layoutParams3 = (RelativeLayout.LayoutParams) bottomOverlayChat.getLayoutParams(); - layoutParams3.width = RelativeLayout.LayoutParams.MATCH_PARENT; - layoutParams3.height = AndroidUtilities.dp(48); - layoutParams3.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM); - bottomOverlayChat.setLayoutParams(layoutParams3); + contentView.addView(bottomOverlayChat, LayoutHelper.createRelative(LayoutHelper.MATCH_PARENT, 48, RelativeLayout.ALIGN_PARENT_BOTTOM)); bottomOverlayChat.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { @@ -1915,25 +1754,12 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not bottomOverlayChatText = new TextView(context); bottomOverlayChatText.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 18); bottomOverlayChatText.setTextColor(0xff3e6fa1); - bottomOverlayChat.addView(bottomOverlayChatText); - layoutParams2 = (FrameLayout.LayoutParams) bottomOverlayChatText.getLayoutParams(); - layoutParams2.width = FrameLayout.LayoutParams.WRAP_CONTENT; - layoutParams2.height = FrameLayout.LayoutParams.WRAP_CONTENT; - layoutParams2.gravity = Gravity.CENTER; - bottomOverlayChatText.setLayoutParams(layoutParams2); + bottomOverlayChat.addView(bottomOverlayChatText, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER)); pagedownButton = new ImageView(context); pagedownButton.setVisibility(View.INVISIBLE); pagedownButton.setImageResource(R.drawable.pagedown); - contentView.addView(pagedownButton); - layoutParams3 = (RelativeLayout.LayoutParams) pagedownButton.getLayoutParams(); - layoutParams3.width = RelativeLayout.LayoutParams.WRAP_CONTENT; - layoutParams3.height = RelativeLayout.LayoutParams.WRAP_CONTENT; - layoutParams3.rightMargin = AndroidUtilities.dp(6); - layoutParams3.bottomMargin = AndroidUtilities.dp(4); - layoutParams3.addRule(RelativeLayout.ABOVE, id_chat_compose_panel); - layoutParams3.addRule(RelativeLayout.ALIGN_PARENT_RIGHT); - pagedownButton.setLayoutParams(layoutParams3); + contentView.addView(pagedownButton, LayoutHelper.createRelative(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, 0, 0, 6, 4, RelativeLayout.ALIGN_PARENT_RIGHT, RelativeLayout.ABOVE, id_chat_compose_panel)); pagedownButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { @@ -1988,7 +1814,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not pendingLinkSearchString = null; showReplyPanel(false, null, null, foundWebPage, false, true); } - if (charSequence.length() == 0 || TextUtils.indexOf(charSequence, "http") == -1 && TextUtils.indexOf(charSequence, ".com/") == -1) { + if (charSequence.length() < 13 || TextUtils.indexOf(charSequence, "http://") == -1 && TextUtils.indexOf(charSequence, "https://") == -1) { return; } final TLRPC.TL_messages_getWebPagePreview req = new TLRPC.TL_messages_getWebPagePreview(); @@ -2234,15 +2060,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not private void moveScrollToLastMessage() { if (chatListView != null) { - chatListView.setSelectionFromTop(messages.size() - 1, -100000 - chatListView.getPaddingTop()); - chatListView.setForceTop(-100000 - chatListView.getPaddingTop()); - chatListView.post(new Runnable() { - @Override - public void run() { - chatListView.setForceTop(-100000 - chatListView.getPaddingTop()); - chatListView.setSelectionFromTop(messages.size() - 1, -100000 - chatListView.getPaddingTop()); - } - }); + chatLayoutManager.scrollToPositionWithOffset(messages.size() - 1, -100000 - chatListView.getPaddingTop()); } } @@ -2257,7 +2075,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not private void scrollToLastMessage() { if (forward_end_reached && first_unread_id == 0 && startLoadFromMessageId == 0) { - chatListView.setSelectionFromTop(messages.size() - 1, -100000 - chatListView.getPaddingTop()); + chatLayoutManager.scrollToPositionWithOffset(messages.size() - 1, -100000 - chatListView.getPaddingTop()); } else { messages.clear(); messagesByDays.clear(); @@ -2282,6 +2100,21 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not } } + private void updateMessagesVisisblePart() { + if (chatListView == null) { + return; + } + int count = chatListView.getChildCount(); + for (int a = 0; a < count; a++) { + View view = chatListView.getChildAt(a); + if (view instanceof ChatMessageCell) { + ChatMessageCell messageCell = (ChatMessageCell) view; + messageCell.getLocalVisibleRect(scrollRect); + messageCell.setVisiblePart(scrollRect.top, scrollRect.bottom - scrollRect.top); + } + } + } + private void scrollToMessageId(int id, int fromMessageId, boolean select) { returnToMessageId = fromMessageId; needSelectFromMessageId = select; @@ -2298,9 +2131,9 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not } final int yOffset = Math.max(0, (chatListView.getHeight() - object.getApproximateHeight()) / 2); if (messages.get(messages.size() - 1) == object) { - chatListView.setSelectionFromTop(0, AndroidUtilities.dp(-11) + yOffset); + chatLayoutManager.scrollToPositionWithOffset(0, AndroidUtilities.dp(-11) + yOffset); } else { - chatListView.setSelectionFromTop(messages.size() - messages.indexOf(object), AndroidUtilities.dp(-11) + yOffset); + chatLayoutManager.scrollToPositionWithOffset(messages.size() - messages.indexOf(object), AndroidUtilities.dp(-11) + yOffset); } updateVisibleRows(); showPagedownButton(true, true); @@ -2540,7 +2373,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not return 7; } } else if (messageObject.type == 10 || messageObject.type == 11) { - if (messageObject.isSending()) { + if (messageObject.getId() == 0 || messageObject.isSending()) { return -1; } else { return 1; @@ -2722,23 +2555,62 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not } if (start) { try { - if (onlineTextView != null) { - onlineTextView.setCompoundDrawablesWithIntrinsicBounds(typingDotsDrawable, null, null, null); - onlineTextView.setCompoundDrawablePadding(AndroidUtilities.dp(4)); - } - if (typingDotsDrawable != null) { - typingDotsDrawable.start(); + Integer type = MessagesController.getInstance().printingStringsTypes.get(dialog_id); + if (type == 0) { + if (lastStatusDrawable == 1) { + return; + } + lastStatusDrawable = 1; + if (onlineTextView != null) { + onlineTextView.setCompoundDrawablesWithIntrinsicBounds(typingDotsDrawable, null, null, null); + onlineTextView.setCompoundDrawablePadding(AndroidUtilities.dp(4)); + + typingDotsDrawable.start(); + recordStatusDrawable.stop(); + sendingFileDrawable.stop(); + } + } else if (type == 1) { + if (lastStatusDrawable == 2) { + return; + } + lastStatusDrawable = 2; + if (onlineTextView != null) { + onlineTextView.setCompoundDrawablesWithIntrinsicBounds(recordStatusDrawable, null, null, null); + onlineTextView.setCompoundDrawablePadding(AndroidUtilities.dp(4)); + + recordStatusDrawable.start(); + typingDotsDrawable.stop(); + sendingFileDrawable.stop(); + } + } else if (type == 2) { + if (lastStatusDrawable == 3) { + return; + } + lastStatusDrawable = 3; + if (onlineTextView != null) { + onlineTextView.setCompoundDrawablesWithIntrinsicBounds(sendingFileDrawable, null, null, null); + onlineTextView.setCompoundDrawablePadding(AndroidUtilities.dp(4)); + + sendingFileDrawable.start(); + typingDotsDrawable.stop(); + recordStatusDrawable.stop(); + } } } catch (Exception e) { FileLog.e("tmessages", e); } } else { + if (lastStatusDrawable == 0) { + return; + } + lastStatusDrawable = 0; if (onlineTextView != null) { onlineTextView.setCompoundDrawablesWithIntrinsicBounds(null, null, null, null); onlineTextView.setCompoundDrawablePadding(0); - } - if (typingDotsDrawable != null) { + typingDotsDrawable.stop(); + recordStatusDrawable.stop(); + sendingFileDrawable.stop(); } } } @@ -2772,7 +2644,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not } } - public boolean openVideoEditor(String videoPath, boolean removeLast) { + public boolean openVideoEditor(String videoPath, boolean removeLast, boolean animated) { Bundle args = new Bundle(); args.putString("videoPath", videoPath); VideoEditorActivity fragment = new VideoEditorActivity(args); @@ -2799,7 +2671,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not showReplyPanel(false, null, null, null, false, true); return false; } - parentLayout.presentFragment(fragment, removeLast, true, true); + parentLayout.presentFragment(fragment, removeLast, !animated, true); return true; } @@ -2835,21 +2707,21 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not } catch (Exception e) { FileLog.e("tmessages", e); } - arrayList.add(new MediaController.PhotoEntry(0, 0, 0, currentPicturePath, orientation)); + arrayList.add(new MediaController.PhotoEntry(0, 0, 0, currentPicturePath, orientation, false)); PhotoViewer.getInstance().openPhotoForSelect(arrayList, 0, 2, new PhotoViewer.EmptyPhotoViewerProvider() { @Override public void sendButtonPressed(int index) { MediaController.PhotoEntry photoEntry = (MediaController.PhotoEntry) arrayList.get(0); if (photoEntry.imagePath != null) { - SendMessagesHelper.prepareSendingPhoto(photoEntry.imagePath, null, dialog_id, replyingMessageObject); + SendMessagesHelper.prepareSendingPhoto(photoEntry.imagePath, null, dialog_id, replyingMessageObject, photoEntry.caption); showReplyPanel(false, null, null, null, false, true); } else if (photoEntry.path != null) { - SendMessagesHelper.prepareSendingPhoto(photoEntry.path, null, dialog_id, replyingMessageObject); + SendMessagesHelper.prepareSendingPhoto(photoEntry.path, null, dialog_id, replyingMessageObject, photoEntry.caption); showReplyPanel(false, null, null, null, false, true); } } - }); + }, this); Utilities.addMediaToGallery(currentPicturePath); currentPicturePath = null; } else if (requestCode == 1) { @@ -2857,33 +2729,42 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not showAttachmentError(); return; } - SendMessagesHelper.prepareSendingPhoto(null, data.getData(), dialog_id, replyingMessageObject); + Uri uri = data.getData(); + if (uri.toString().contains("video")) { + String videoPath = null; + try { + videoPath = Utilities.getPath(uri); + } catch (Exception e) { + FileLog.e("tmessages", e); + } + if (videoPath == null) { + showAttachmentError(); + } + if (Build.VERSION.SDK_INT >= 16) { + if (paused) { + startVideoEdit = videoPath; + } else { + openVideoEditor(videoPath, false, false); + } + } else { + SendMessagesHelper.prepareSendingVideo(videoPath, 0, 0, 0, 0, null, dialog_id, replyingMessageObject); + showReplyPanel(false, null, null, null, false, true); + } + } else { + SendMessagesHelper.prepareSendingPhoto(null, uri, dialog_id, replyingMessageObject, null); + } showReplyPanel(false, null, null, null, false, true); } else if (requestCode == 2) { String videoPath = null; if (data != null) { Uri uri = data.getData(); - boolean fromCamera = false; - if (uri != null && uri.getScheme() != null) { - fromCamera = uri.getScheme().contains("file"); - } else if (uri == null) { - fromCamera = true; - } - if (fromCamera) { - if (uri != null) { - videoPath = uri.getPath(); - } else { - videoPath = currentPicturePath; - } - Utilities.addMediaToGallery(currentPicturePath); - currentPicturePath = null; + if (uri != null) { + videoPath = uri.getPath(); } else { - try { - videoPath = Utilities.getPath(uri); - } catch (Exception e) { - FileLog.e("tmessages", e); - } + videoPath = currentPicturePath; } + Utilities.addMediaToGallery(currentPicturePath); + currentPicturePath = null; } if (videoPath == null && currentPicturePath != null) { File f = new File(currentPicturePath); @@ -2896,7 +2777,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not if (paused) { startVideoEdit = videoPath; } else { - openVideoEditor(videoPath, false); + openVideoEditor(videoPath, false, false); } } else { SendMessagesHelper.prepareSendingVideo(videoPath, 0, 0, 0, 0, null, dialog_id, replyingMessageObject); @@ -2935,17 +2816,18 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not currentPicturePath = args.getString("path"); } - private void removeUnreadPlane(boolean reload) { + private void removeUnreadPlane() { if (unreadMessageObject != null) { - messages.remove(unreadMessageObject); forward_end_reached = true; first_unread_id = 0; last_message_id = 0; unread_to_load = 0; - unreadMessageObject = null; - if (reload) { - chatAdapter.notifyDataSetChanged(); + if (chatAdapter != null) { + chatAdapter.removeMessageObject(unreadMessageObject); + } else { + messages.remove(unreadMessageObject); } + unreadMessageObject = null; } } @@ -2957,7 +2839,6 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not @Override public void didReceivedNotification(int id, final Object... args) { if (id == NotificationCenter.messagesDidLoaded) { - long did = (Long) args[0]; if (did == dialog_id) { loadsCount++; @@ -3113,9 +2994,12 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not last_message_id = 0; first_message_id = 0; startLoadFromMessageId = 0; + chatAdapter.notifyItemChanged(chatAdapter.getItemCount() - 1); + newRowsCount--; + } + if (newRowsCount != 0) { + chatAdapter.notifyItemRangeInserted(chatAdapter.getItemCount() - 2, newRowsCount); } - - chatAdapter.notifyDataSetChanged(); loadingForward = false; } else { if (messArr.size() != count) { @@ -3136,37 +3020,44 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not chatAdapter.notifyDataSetChanged(); if (scrollToMessage != null) { final int yOffset = scrollToMessageMiddleScreen ? Math.max(0, (chatListView.getHeight() - scrollToMessage.getApproximateHeight()) / 2) : 0; - if (messages.get(messages.size() - 1) == scrollToMessage) { - chatListView.setSelectionFromTop(0, AndroidUtilities.dp(-11) + yOffset); - } else { - chatListView.setSelectionFromTop(messages.size() - messages.indexOf(scrollToMessage), AndroidUtilities.dp(-11) + yOffset); - } - ViewTreeObserver obs = chatListView.getViewTreeObserver(); - obs.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() { - @Override - public boolean onPreDraw() { - if (!messages.isEmpty()) { - if (messages.get(messages.size() - 1) == scrollToMessage) { - chatListView.setSelectionFromTop(0, AndroidUtilities.dp(-11) + yOffset); - } else { - chatListView.setSelectionFromTop(messages.size() - messages.indexOf(scrollToMessage), AndroidUtilities.dp(-11) + yOffset); - } - } - chatListView.getViewTreeObserver().removeOnPreDrawListener(this); - return true; + if (!messages.isEmpty()) { + if (messages.get(messages.size() - 1) == scrollToMessage) { + chatLayoutManager.scrollToPositionWithOffset(0, AndroidUtilities.dp(-11) + yOffset); + } else { + chatLayoutManager.scrollToPositionWithOffset(messages.size() - messages.indexOf(scrollToMessage), AndroidUtilities.dp(-11) + yOffset); } - }); + ViewTreeObserver obs = chatListView.getViewTreeObserver(); + obs.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() { + @Override + public boolean onPreDraw() { //TODO remove it? + if (!messages.isEmpty()) { + if (messages.get(messages.size() - 1) == scrollToMessage) { + chatLayoutManager.scrollToPositionWithOffset(0, AndroidUtilities.dp(-11) + yOffset); + } else { + chatLayoutManager.scrollToPositionWithOffset(messages.size() - messages.indexOf(scrollToMessage), AndroidUtilities.dp(-11) + yOffset); + } + } + chatListView.getViewTreeObserver().removeOnPreDrawListener(this); + return true; + } + }); + } chatListView.invalidate(); showPagedownButton(true, true); } else { moveScrollToLastMessage(); } } else { - int firstVisPos = chatListView.getLastVisiblePosition(); - View firstVisView = chatListView.getChildAt(chatListView.getChildCount() - 1); - int top = ((firstVisView == null) ? 0 : firstVisView.getTop()) - chatListView.getPaddingTop(); - chatAdapter.notifyDataSetChanged(); - chatListView.setSelectionFromTop(firstVisPos + newRowsCount - (endReached ? 1 : 0), top); + if (endReached) { + chatAdapter.notifyItemRemoved(0); + } + if (newRowsCount != 0) { + int firstVisPos = chatLayoutManager.findLastVisibleItemPosition(); + View firstVisView = chatListView.getChildAt(chatListView.getChildCount() - 1); + int top = ((firstVisView == null) ? 0 : firstVisView.getTop()) - chatListView.getPaddingTop(); + chatAdapter.notifyItemRangeInserted(1, newRowsCount); + chatLayoutManager.scrollToPositionWithOffset(firstVisPos + newRowsCount - (endReached ? 1 : 0), top); + } } if (paused) { @@ -3177,9 +3068,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not } if (first) { - if (chatListView.getEmptyView() == null) { - chatListView.setEmptyView(emptyViewContainer); - } + chatListView.setEmptyView(emptyViewContainer); } } else { scrollToTopOnResume = true; @@ -3327,6 +3216,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not } else { ReplyMessageQuery.loadReplyMessagesForMessages(arr, dialog_id); boolean markAsRead = false; + boolean unreadUpdated = true; int oldCount = messages.size(); for (MessageObject obj : arr) { if (currentEncryptedChat != null && obj.messageOwner.action != null && obj.messageOwner.action instanceof TLRPC.TL_messageEncryptedAction && @@ -3342,14 +3232,10 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not } if (obj.isOut()) { - removeUnreadPlane(false); + removeUnreadPlane(); hasFromMe = true; } - if (!obj.isOut() && unreadMessageObject != null) { - unread_to_load++; - } - if (obj.getId() > 0) { maxMessageId = Math.min(obj.getId(), maxMessageId); minMessageId = Math.max(obj.getId(), minMessageId); @@ -3372,11 +3258,41 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not dateObj.contentType = 4; messages.add(0, dateObj); } - if (!obj.isOut() && obj.isUnread()) { - if (!paused) { - obj.setIsRead(); + if (!obj.isOut()) { + if (paused) { + if (!scrollToTopUnReadOnResume && unreadMessageObject != null) { + if (chatAdapter != null) { + chatAdapter.removeMessageObject(unreadMessageObject); + } else { + messages.remove(unreadMessageObject); + } + unreadMessageObject = null; + } + if (unreadMessageObject == null) { + TLRPC.Message dateMsg = new TLRPC.Message(); + dateMsg.message = ""; + dateMsg.id = 0; + MessageObject dateObj = new MessageObject(dateMsg, null, false); + dateObj.contentType = dateObj.type = 6; + messages.add(0, dateObj); + unreadMessageObject = dateObj; + scrollToMessage = unreadMessageObject; + scrollToMessageMiddleScreen = false; + unreadUpdated = false; + unread_to_load = 0; + scrollToTopUnReadOnResume = true; + } + } + if (unreadMessageObject != null) { + unread_to_load++; + unreadUpdated = true; + } + if (obj.isUnread()) { + if (!paused) { + obj.setIsRead(); + } + markAsRead = true; } - markAsRead = true; } dayArray.add(0, obj); messages.add(0, obj); @@ -3388,13 +3304,18 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not progressView.setVisibility(View.INVISIBLE); } if (chatAdapter != null) { - chatAdapter.notifyDataSetChanged(); + if (unreadUpdated) { + chatAdapter.updateRowWithMessageObject(unreadMessageObject); + } + if (messages.size() - oldCount != 0) { + chatAdapter.notifyItemRangeInserted(chatAdapter.getItemCount(), messages.size() - oldCount); + } } else { scrollToTopOnResume = true; } if (chatListView != null && chatAdapter != null) { - int lastVisible = chatListView.getLastVisiblePosition(); + int lastVisible = chatLayoutManager.findLastVisibleItemPosition(); if (endReached) { lastVisible++; } @@ -3516,7 +3437,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not } } if (updated && chatAdapter != null) { - removeUnreadPlane(false); + removeUnreadPlane(); chatAdapter.notifyDataSetChanged(); } } else if (id == NotificationCenter.messageReceivedByServer) { @@ -3535,7 +3456,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not obj.messageOwner.id = newMsgId; obj.messageOwner.send_state = MessageObject.MESSAGE_SEND_STATE_SENT; updateVisibleRows(); - if (mediaUpdated && chatListView.getLastVisiblePosition() >= messages.size() - 1) { + if (mediaUpdated && chatLayoutManager.findLastVisibleItemPosition() >= messages.size() - 1) { moveScrollToLastMessage(); } } @@ -3576,7 +3497,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not updateContactStatus(); updateSecretStatus(); } - } else if (id == NotificationCenter.messagesReadedEncrypted) { + } else if (id == NotificationCenter.messagesReadEncrypted) { int encId = (Integer) args[0]; if (currentEncryptedChat != null && currentEncryptedChat.id == encId) { int date = (Integer) args[1]; @@ -3709,13 +3630,13 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not int idx = messages.indexOf(old); if (idx >= 0) { messages.set(idx, messageObject); + chatAdapter.notifyItemChanged(messages.size() - (!endReached ? 0 : 1) - idx); changed = true; } } } if (changed) { - chatAdapter.notifyDataSetChanged(); - if (mediaUpdated && chatListView.getLastVisiblePosition() >= messages.size() - 1) { + if (mediaUpdated && chatLayoutManager.findLastVisibleItemPosition() >= messages.size() - 1) { moveScrollToLastMessage(); } } @@ -3740,7 +3661,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not } if (updated) { updateVisibleRows(); - if (chatListView.getLastVisiblePosition() >= messages.size() - 1) { + if (chatLayoutManager.findLastVisibleItemPosition() >= messages.size() - 1) { moveScrollToLastMessage(); } } @@ -3754,6 +3675,19 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not } } } + } else if (id == NotificationCenter.messagesReadContent) { + ArrayList arrayList = (ArrayList) args[0]; + boolean updated = false; + for (Integer mid : arrayList) { + MessageObject currentMessage = messagesDict.get(mid); + if (currentMessage != null) { + currentMessage.setContentIsRead(); + updated = true; + } + } + if (updated) { + updateVisibleRows(); + } } } @@ -3831,10 +3765,6 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not getParentActivity().getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE); } - if (chatAdapter != null) { - chatAdapter.notifyDataSetChanged(); - } - checkActionBarMenu(); if (replyImageLocation != null && replyImageView != null) { replyImageView.setImage(replyImageLocation, "50_50", (Drawable) null); @@ -3845,7 +3775,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not if (scrollToTopUnReadOnResume && scrollToMessage != null) { if (chatListView != null) { final int yOffset = scrollToMessageMiddleScreen ? Math.max(0, (chatListView.getHeight() - scrollToMessage.getApproximateHeight()) / 2) : 0; - chatListView.setSelectionFromTop(messages.size() - messages.indexOf(scrollToMessage), -chatListView.getPaddingTop() - AndroidUtilities.dp(7) + yOffset); + chatLayoutManager.scrollToPositionWithOffset(messages.size() - messages.indexOf(scrollToMessage), -chatListView.getPaddingTop() - AndroidUtilities.dp(7) + yOffset); } } else { moveScrollToLastMessage(); @@ -3867,6 +3797,12 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not readWhenResume = false; MessagesController.getInstance().markDialogAsRead(dialog_id, messages.get(0).getId(), readWithMid, 0, readWithDate, true, false); } + if (wasPaused) { + wasPaused = false; + if (chatAdapter != null) { + chatAdapter.notifyDataSetChanged(); + } + } fixLayout(true); SharedPreferences preferences = ApplicationLoader.applicationContext.getSharedPreferences("mainconfig", Activity.MODE_PRIVATE); @@ -3882,7 +3818,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not byte[] bytes = Base64.decode(lastReplyMessage, Base64.DEFAULT); if (bytes != null) { SerializedData data = new SerializedData(bytes); - TLRPC.Message message = (TLRPC.Message) TLClassStore.Instance().TLdeserialize(data, data.readInt32()); + TLRPC.Message message = TLRPC.Message.TLdeserialize(data, data.readInt32(false), false); if (message != null) { replyingMessageObject = new MessageObject(message, MessagesController.getInstance().getUsers(), false); showReplyPanel(true, replyingMessageObject, null, null, false, false); @@ -3904,7 +3840,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not AndroidUtilities.runOnUIThread(new Runnable() { @Override public void run() { - openVideoEditor(startVideoEdit, false); + openVideoEditor(startVideoEdit, false, false); startVideoEdit = null; } }); @@ -3928,6 +3864,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not menuItem.closeSubMenu(); } paused = true; + wasPaused = true; NotificationsController.getInstance().setOpennedDialogId(0); if (chatActivityEnterView != null) { chatActivityEnterView.hideEmojiPopup(); @@ -3957,7 +3894,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not editor.commit(); } - MessagesController.getInstance().cancelTyping(dialog_id); + MessagesController.getInstance().cancelTyping(0, dialog_id); if (currentEncryptedChat != null) { chatLeaveTime = System.currentTimeMillis(); @@ -4019,22 +3956,6 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not } }); } - if (!resume && chatListView != null) { - final int lastPos = chatListView.getLastVisiblePosition(); - chatListView.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() { - @Override - public boolean onPreDraw() { - if (chatListView == null) { - return false; - } - chatListView.getViewTreeObserver().removeOnPreDrawListener(this); - if (lastPos >= messages.size() - 1) { - moveScrollToLastMessage(); - } - return false; - } - }); - } } @Override @@ -4151,9 +4072,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not } else if (type == 5) { items = new CharSequence[]{LocaleController.getString("ApplyLocalizationFile", R.string.ApplyLocalizationFile), LocaleController.getString("Delete", R.string.Delete)}; options = new int[]{5, 1}; - }/* else if (type == 6) { - options = new int[]{7, 6, 2, 1}; - }*/ + } } } @@ -4215,7 +4134,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not public void onClick(DialogInterface dialogInterface, int i) { ArrayList ids = new ArrayList<>(); ids.add(finalSelectedObject.getId()); - removeUnreadPlane(true); + removeUnreadPlane(); ArrayList random_ids = null; if (currentEncryptedChat != null && finalSelectedObject.messageOwner.random_id != 0 && finalSelectedObject.type != 10) { random_ids = new ArrayList<>(); @@ -4442,6 +4361,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not if (AndroidUtilities.isTablet()) { actionBar.hideActionMode(); } + updateVisibleRows(); } } } @@ -4618,26 +4538,23 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not return 0; } - private class ChatAdapter extends BaseFragmentAdapter { + public class ChatActivityAdapter extends RecyclerView.Adapter { private Context mContext; - public ChatAdapter(Context context) { + public ChatActivityAdapter(Context context) { mContext = context; } - @Override - public boolean areAllItemsEnabled() { - return true; + private class Holder extends RecyclerView.ViewHolder { + + public Holder(View itemView) { + super(itemView); + } } @Override - public boolean isEnabled(int i) { - return true; - } - - @Override - public int getCount() { + public int getItemCount() { int count = messages.size(); if (count != 0) { if (!endReached) { @@ -4650,290 +4567,282 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not return count; } - @Override - public Object getItem(int i) { - return null; - } - @Override public long getItemId(int i) { - return i; + return RecyclerExListView.NO_ID; } @Override - public boolean hasStableIds() { - return true; - } - - @Override - public View getView(int i, View view, ViewGroup viewGroup) { - int offset = 1; - if ((!endReached || !forward_end_reached) && messages.size() != 0) { - if (!endReached) { - offset = 0; - } - if (i == 0 && !endReached || !forward_end_reached && i == (messages.size() + 1 - offset)) { - View progressBar = null; - if (view == null) { - LayoutInflater li = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE); - view = li.inflate(R.layout.chat_loading_layout, viewGroup, false); - progressBar = view.findViewById(R.id.progressLayout); - if (ApplicationLoader.isCustomTheme()) { - progressBar.setBackgroundResource(R.drawable.system_loader2); - } else { - progressBar.setBackgroundResource(R.drawable.system_loader1); - } - } else { - progressBar = view.findViewById(R.id.progressLayout); - } - progressBar.setVisibility(loadsCount > 1 ? View.VISIBLE : View.INVISIBLE); - - return view; - } - } - final MessageObject message = messages.get(messages.size() - i - offset); - int type = message.contentType; - if (view == null) { - if (type == 0) { + public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { + View view = null; + if (viewType == 0) { + if (!chatMessageCellsCache.isEmpty()) { + view = chatMessageCellsCache.get(0); + chatMessageCellsCache.remove(0); + } else { view = new ChatMessageCell(mContext); } - if (type == 1) { + } else if (viewType == 1) { + if (!chatMediaCellsCache.isEmpty()) { + view = chatMediaCellsCache.get(0); + chatMediaCellsCache.remove(0); + } else { view = new ChatMediaCell(mContext); - } else if (type == 2) { - view = new ChatAudioCell(mContext); - } else if (type == 3) { - view = new ChatContactCell(mContext); - } else if (type == 6) { - LayoutInflater li = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE); - view = li.inflate(R.layout.chat_unread_layout, viewGroup, false); - } else if (type == 4) { - view = new ChatActionCell(mContext); } + } else if (viewType == 2) { + view = new ChatAudioCell(mContext); + } else if (viewType == 3) { + view = new ChatContactCell(mContext); + } else if (viewType == 4) { + view = new ChatActionCell(mContext); + } else if (viewType == 5) { + LayoutInflater li = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE); + view = li.inflate(R.layout.chat_loading_layout, parent, false); + view.findViewById(R.id.progressLayout).setBackgroundResource(ApplicationLoader.isCustomTheme() ? R.drawable.system_loader2 : R.drawable.system_loader1); + } else if (viewType == 6) { + LayoutInflater li = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE); + view = li.inflate(R.layout.chat_unread_layout, parent, false); + } - if (view instanceof ChatBaseCell) { - ((ChatBaseCell) view).setDelegate(new ChatBaseCell.ChatBaseCellDelegate() { + if (view instanceof ChatBaseCell) { + ((ChatBaseCell) view).setDelegate(new ChatBaseCell.ChatBaseCellDelegate() { + @Override + public void didPressedUserAvatar(ChatBaseCell cell, TLRPC.User user) { + if (actionBar.isActionModeShowed()) { + processRowSelect(cell); + return; + } + if (user != null && user.id != UserConfig.getClientUserId()) { + Bundle args = new Bundle(); + args.putInt("user_id", user.id); + presentFragment(new ProfileActivity(args)); + } + } + + @Override + public void didPressedCancelSendButton(ChatBaseCell cell) { + MessageObject message = cell.getMessageObject(); + if (message.messageOwner.send_state != 0) { + SendMessagesHelper.getInstance().cancelSendingMessage(message); + } + } + + @Override + public void didLongPressed(ChatBaseCell cell) { + createMenu(cell, false); + } + + @Override + public boolean canPerformActions() { + return actionBar != null && !actionBar.isActionModeShowed(); + } + + @Override + public void didPressUrl(String url) { + if (url.startsWith("@")) { + openProfileWithUsername(url.substring(1)); + } else if (url.startsWith("#")) { + MessagesActivity fragment = new MessagesActivity(null); + fragment.setSearchString(url); + presentFragment(fragment); + } + } + + @Override + public void didPressReplyMessage(ChatBaseCell cell, int id) { + scrollToMessageId(id, cell.getMessageObject().getId(), true); + } + }); + if (view instanceof ChatMediaCell) { + ((ChatMediaCell) view).setAllowedToSetPhoto(openAnimationEnded); + ((ChatMediaCell) view).setMediaDelegate(new ChatMediaCell.ChatMediaCellDelegate() { @Override - public void didPressedUserAvatar(ChatBaseCell cell, TLRPC.User user) { - if (actionBar.isActionModeShowed()) { - processRowSelect(cell); + public void didClickedImage(ChatMediaCell cell) { + MessageObject message = cell.getMessageObject(); + if (message.isSendError()) { + createMenu(cell, false); + return; + } else if (message.isSending()) { return; } - if (user != null && user.id != UserConfig.getClientUserId()) { - Bundle args = new Bundle(); - args.putInt("user_id", user.id); - presentFragment(new ProfileActivity(args)); - } - } - - @Override - public void didPressedCancelSendButton(ChatBaseCell cell) { - MessageObject message = cell.getMessageObject(); - if (message.messageOwner.send_state != 0) { - SendMessagesHelper.getInstance().cancelSendingMessage(message); - } - } - - @Override - public void didLongPressed(ChatBaseCell cell) { - createMenu(cell, false); - } - - @Override - public boolean canPerformActions() { - return actionBar != null && !actionBar.isActionModeShowed(); - } - - @Override - public void didPressUrl(String url) { - if (url.startsWith("@")) { - openProfileWithUsername(url.substring(1)); - } else if (url.startsWith("#")) { - MessagesActivity fragment = new MessagesActivity(null); - fragment.setSearchString(url); - presentFragment(fragment); - } - } - - @Override - public void didPressReplyMessage(ChatBaseCell cell, int id) { - scrollToMessageId(id, cell.getMessageObject().getId(), true); - } - }); - if (view instanceof ChatMediaCell) { - ((ChatMediaCell) view).setAllowedToSetPhoto(openAnimationEnded); - ((ChatMediaCell) view).setMediaDelegate(new ChatMediaCell.ChatMediaCellDelegate() { - @Override - public void didClickedImage(ChatMediaCell cell) { - MessageObject message = cell.getMessageObject(); - if (message.isSendError()) { - createMenu(cell, false); - return; - } else if (message.isSending()) { - return; - } - if (message.type == 1) { - PhotoViewer.getInstance().setParentActivity(getParentActivity()); - PhotoViewer.getInstance().openPhoto(message, ChatActivity.this); - } else if (message.type == 3) { - sendSecretMessageRead(message); - try { - File f = null; - if (message.messageOwner.attachPath != null && message.messageOwner.attachPath.length() != 0) { - f = new File(message.messageOwner.attachPath); - } - if (f == null || f != null && !f.exists()) { - f = FileLoader.getPathToMessage(message.messageOwner); - } - Intent intent = new Intent(Intent.ACTION_VIEW); - intent.setDataAndType(Uri.fromFile(f), "video/mp4"); - getParentActivity().startActivityForResult(intent, 500); - } catch (Exception e) { - alertUserOpenError(message); - } - } else if (message.type == 4) { - if (!isGoogleMapsInstalled()) { - return; - } - LocationActivity fragment = new LocationActivity(); - fragment.setMessageObject(message); - presentFragment(fragment); - } else if (message.type == 9) { + if (message.type == 1) { + PhotoViewer.getInstance().setParentActivity(getParentActivity()); + PhotoViewer.getInstance().openPhoto(message, ChatActivity.this); + } else if (message.type == 3) { + sendSecretMessageRead(message); + try { File f = null; - String fileName = message.getFileName(); if (message.messageOwner.attachPath != null && message.messageOwner.attachPath.length() != 0) { f = new File(message.messageOwner.attachPath); } if (f == null || f != null && !f.exists()) { f = FileLoader.getPathToMessage(message.messageOwner); } - if (f != null && f.exists()) { - String realMimeType = null; - try { - Intent intent = new Intent(Intent.ACTION_VIEW); - if (message.type == 8 || message.type == 9) { - MimeTypeMap myMime = MimeTypeMap.getSingleton(); - int idx = fileName.lastIndexOf("."); - if (idx != -1) { - String ext = fileName.substring(idx + 1); - realMimeType = myMime.getMimeTypeFromExtension(ext.toLowerCase()); - if (realMimeType == null) { - realMimeType = message.messageOwner.media.document.mime_type; - if (realMimeType == null || realMimeType.length() == 0) { - realMimeType = null; - } - } - if (realMimeType != null) { - intent.setDataAndType(Uri.fromFile(f), realMimeType); - } else { - intent.setDataAndType(Uri.fromFile(f), "text/plain"); + Intent intent = new Intent(Intent.ACTION_VIEW); + intent.setDataAndType(Uri.fromFile(f), "video/mp4"); + getParentActivity().startActivityForResult(intent, 500); + } catch (Exception e) { + alertUserOpenError(message); + } + } else if (message.type == 4) { + if (!isGoogleMapsInstalled()) { + return; + } + LocationActivity fragment = new LocationActivity(); + fragment.setMessageObject(message); + presentFragment(fragment); + } else if (message.type == 9) { + File f = null; + String fileName = message.getFileName(); + if (message.messageOwner.attachPath != null && message.messageOwner.attachPath.length() != 0) { + f = new File(message.messageOwner.attachPath); + } + if (f == null || f != null && !f.exists()) { + f = FileLoader.getPathToMessage(message.messageOwner); + } + if (f != null && f.exists()) { + String realMimeType = null; + try { + Intent intent = new Intent(Intent.ACTION_VIEW); + if (message.type == 8 || message.type == 9) { + MimeTypeMap myMime = MimeTypeMap.getSingleton(); + int idx = fileName.lastIndexOf("."); + if (idx != -1) { + String ext = fileName.substring(idx + 1); + realMimeType = myMime.getMimeTypeFromExtension(ext.toLowerCase()); + if (realMimeType == null) { + realMimeType = message.messageOwner.media.document.mime_type; + if (realMimeType == null || realMimeType.length() == 0) { + realMimeType = null; } + } + if (realMimeType != null) { + intent.setDataAndType(Uri.fromFile(f), realMimeType); } else { intent.setDataAndType(Uri.fromFile(f), "text/plain"); } - } - if (realMimeType != null) { - try { - getParentActivity().startActivityForResult(intent, 500); - } catch (Exception e) { - intent.setDataAndType(Uri.fromFile(f), "text/plain"); - getParentActivity().startActivityForResult(intent, 500); - } } else { + intent.setDataAndType(Uri.fromFile(f), "text/plain"); + } + } + if (realMimeType != null) { + try { + getParentActivity().startActivityForResult(intent, 500); + } catch (Exception e) { + intent.setDataAndType(Uri.fromFile(f), "text/plain"); getParentActivity().startActivityForResult(intent, 500); } - } catch (Exception e) { - alertUserOpenError(message); + } else { + getParentActivity().startActivityForResult(intent, 500); } + } catch (Exception e) { + alertUserOpenError(message); } } } + } - @Override - public void didPressedOther(ChatMediaCell cell) { - createMenu(cell, true); - } - }); - } else if (view instanceof ChatContactCell) { - ((ChatContactCell) view).setContactDelegate(new ChatContactCell.ChatContactCellDelegate() { - @Override - public void didClickAddButton(ChatContactCell cell, TLRPC.User user) { - if (actionBar.isActionModeShowed()) { - processRowSelect(cell); - return; - } - MessageObject messageObject = cell.getMessageObject(); - Bundle args = new Bundle(); - args.putInt("user_id", messageObject.messageOwner.media.user_id); - args.putString("phone", messageObject.messageOwner.media.phone_number); - args.putBoolean("addContact", true); - presentFragment(new ContactAddActivity(args)); + @Override + public void didPressedOther(ChatMediaCell cell) { + createMenu(cell, true); + } + }); + } else if (view instanceof ChatContactCell) { + ((ChatContactCell) view).setContactDelegate(new ChatContactCell.ChatContactCellDelegate() { + @Override + public void didClickAddButton(ChatContactCell cell, TLRPC.User user) { + if (actionBar.isActionModeShowed()) { + processRowSelect(cell); + return; } + MessageObject messageObject = cell.getMessageObject(); + Bundle args = new Bundle(); + args.putInt("user_id", messageObject.messageOwner.media.user_id); + args.putString("phone", messageObject.messageOwner.media.phone_number); + args.putBoolean("addContact", true); + presentFragment(new ContactAddActivity(args)); + } - @Override - public void didClickPhone(ChatContactCell cell) { - if (actionBar.isActionModeShowed()) { - processRowSelect(cell); - return; - } - final MessageObject messageObject = cell.getMessageObject(); - if (getParentActivity() == null || messageObject.messageOwner.media.phone_number == null || messageObject.messageOwner.media.phone_number.length() == 0) { - return; - } - AlertDialog.Builder builder = new AlertDialog.Builder(getParentActivity()); - builder.setItems(new CharSequence[]{LocaleController.getString("Copy", R.string.Copy), LocaleController.getString("Call", R.string.Call)}, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialogInterface, int i) { - if (i == 1) { - try { - Intent intent = new Intent(Intent.ACTION_DIAL, Uri.parse("tel:" + messageObject.messageOwner.media.phone_number)); - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - getParentActivity().startActivityForResult(intent, 500); - } catch (Exception e) { - FileLog.e("tmessages", e); - } - } else if (i == 0) { - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) { - android.text.ClipboardManager clipboard = (android.text.ClipboardManager) ApplicationLoader.applicationContext.getSystemService(Context.CLIPBOARD_SERVICE); - clipboard.setText(messageObject.messageOwner.media.phone_number); - } else { - android.content.ClipboardManager clipboard = (android.content.ClipboardManager) ApplicationLoader.applicationContext.getSystemService(Context.CLIPBOARD_SERVICE); - android.content.ClipData clip = android.content.ClipData.newPlainText("label", messageObject.messageOwner.media.phone_number); - clipboard.setPrimaryClip(clip); - } + @Override + public void didClickPhone(ChatContactCell cell) { + if (actionBar.isActionModeShowed()) { + processRowSelect(cell); + return; + } + final MessageObject messageObject = cell.getMessageObject(); + if (getParentActivity() == null || messageObject.messageOwner.media.phone_number == null || messageObject.messageOwner.media.phone_number.length() == 0) { + return; + } + AlertDialog.Builder builder = new AlertDialog.Builder(getParentActivity()); + builder.setItems(new CharSequence[]{LocaleController.getString("Copy", R.string.Copy), LocaleController.getString("Call", R.string.Call)}, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialogInterface, int i) { + if (i == 1) { + try { + Intent intent = new Intent(Intent.ACTION_DIAL, Uri.parse("tel:" + messageObject.messageOwner.media.phone_number)); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + getParentActivity().startActivityForResult(intent, 500); + } catch (Exception e) { + FileLog.e("tmessages", e); + } + } else if (i == 0) { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) { + android.text.ClipboardManager clipboard = (android.text.ClipboardManager) ApplicationLoader.applicationContext.getSystemService(Context.CLIPBOARD_SERVICE); + clipboard.setText(messageObject.messageOwner.media.phone_number); + } else { + android.content.ClipboardManager clipboard = (android.content.ClipboardManager) ApplicationLoader.applicationContext.getSystemService(Context.CLIPBOARD_SERVICE); + android.content.ClipData clip = android.content.ClipData.newPlainText("label", messageObject.messageOwner.media.phone_number); + clipboard.setPrimaryClip(clip); } } } - ); - showAlertDialog(builder); - } - }); - } - } else if (view instanceof ChatActionCell) { - ((ChatActionCell) view).setDelegate(new ChatActionCell.ChatActionCellDelegate() { - @Override - public void didClickedImage(ChatActionCell cell) { - MessageObject message = cell.getMessageObject(); - PhotoViewer.getInstance().setParentActivity(getParentActivity()); - PhotoViewer.getInstance().openPhoto(message, ChatActivity.this); - } - - @Override - public void didLongPressed(ChatActionCell cell) { - createMenu(cell, false); - } - - @Override - public void needOpenUserProfile(int uid) { - if (uid != UserConfig.getClientUserId()) { - Bundle args = new Bundle(); - args.putInt("user_id", uid); - presentFragment(new ProfileActivity(args)); - } + } + ); + showAlertDialog(builder); } }); } + } else if (view instanceof ChatActionCell) { + ((ChatActionCell) view).setDelegate(new ChatActionCell.ChatActionCellDelegate() { + @Override + public void didClickedImage(ChatActionCell cell) { + MessageObject message = cell.getMessageObject(); + PhotoViewer.getInstance().setParentActivity(getParentActivity()); + PhotoViewer.getInstance().openPhoto(message, ChatActivity.this); + } + + @Override + public void didLongPressed(ChatActionCell cell) { + createMenu(cell, false); + } + + @Override + public void needOpenUserProfile(int uid) { + if (uid != UserConfig.getClientUserId()) { + Bundle args = new Bundle(); + args.putInt("user_id", uid); + presentFragment(new ProfileActivity(args)); + } + } + }); } + return new Holder(view); + } + + @Override + public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { + int viewType = holder.getItemViewType(); + if (viewType == 5) { + holder.itemView.findViewById(R.id.progressLayout).setVisibility(loadsCount > 1 ? View.VISIBLE : View.INVISIBLE); + return; + } + + MessageObject message = messages.get(messages.size() - position - (!endReached ? 0 : 1)); + View view = holder.itemView; + + int type = message.contentType; + boolean selected = false; boolean disableSelection = false; if (actionBar.isActionModeShowed()) { @@ -4960,48 +4869,63 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not } else if (view instanceof ChatActionCell) { ChatActionCell actionCell = (ChatActionCell) view; actionCell.setMessageObject(message); - } - if (type == 6) { + } else if (type == 6) { TextView messageTextView = (TextView) view.findViewById(R.id.chat_message_text); messageTextView.setText(LocaleController.formatPluralString("NewMessages", unread_to_load)); } - - return view; } @Override - public int getItemViewType(int i) { + public int getItemViewType(int position) { int offset = 1; if (!endReached && messages.size() != 0) { offset = 0; - if (i == 0) { + if (position == 0) { return 5; } } - if (!forward_end_reached && i == (messages.size() + 1 - offset)) { + if (!forward_end_reached && position == (messages.size() + 1 - offset)) { return 5; } - MessageObject message = messages.get(messages.size() - i - offset); + MessageObject message = messages.get(messages.size() - position - offset); return message.contentType; } @Override - public int getViewTypeCount() { - return 7; + public void onViewAttachedToWindow(RecyclerView.ViewHolder holder) { + if (holder.itemView instanceof ChatBaseCell) { + ChatBaseCell baseCell = (ChatBaseCell) holder.itemView; + baseCell.setHighlighted(highlightMessageId != Integer.MAX_VALUE && baseCell.getMessageObject().getId() == highlightMessageId); + } + if (holder.itemView instanceof ChatMessageCell) { + final ChatMessageCell messageCell = (ChatMessageCell) holder.itemView; + messageCell.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() { + @Override + public boolean onPreDraw() { + messageCell.getViewTreeObserver().removeOnPreDrawListener(this); + messageCell.getLocalVisibleRect(scrollRect); + messageCell.setVisiblePart(scrollRect.top, scrollRect.bottom - scrollRect.top); + return true; + } + }); + } } - @Override - public boolean isEmpty() { - int count = messages.size(); - if (count != 0) { - if (!endReached) { - count++; - } - if (!forward_end_reached) { - count++; - } + public void updateRowWithMessageObject(MessageObject messageObject) { + int index = messages.indexOf(messageObject); + if (index == -1) { + return; } - return count == 0; + notifyItemChanged(messages.size() - (!endReached ? 0 : 1) - index); + } + + public void removeMessageObject(MessageObject messageObject) { + int index = messages.indexOf(messageObject); + if (index == -1) { + return; + } + messages.remove(index); + notifyItemRemoved(messages.size() - (!endReached ? 0 : 1) - index); } } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/AvatarDrawable.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/AvatarDrawable.java index e2a850391..e35f5f3ce 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/AvatarDrawable.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/AvatarDrawable.java @@ -52,6 +52,7 @@ public class AvatarDrawable extends Drawable { private boolean drawBrodcast; private boolean drawPhoto; private boolean smallStyle; + private StringBuilder stringBuilder = new StringBuilder(5); public AvatarDrawable() { super(); @@ -178,9 +179,9 @@ public class AvatarDrawable extends Drawable { lastName = null; } - String text = ""; + stringBuilder.setLength(0); if (firstName != null && firstName.length() > 0) { - text += firstName.substring(0, 1); + stringBuilder.append(firstName.substring(0, 1)); } if (lastName != null && lastName.length() > 0) { String lastch = null; @@ -191,27 +192,25 @@ public class AvatarDrawable extends Drawable { lastch = lastName.substring(a, a + 1); } if (Build.VERSION.SDK_INT >= 16) { - text += "\u200C" + lastch; - } else { - text += lastch; + stringBuilder.append("\u200C"); } + stringBuilder.append(lastch); } else if (firstName != null && firstName.length() > 0) { for (int a = firstName.length() - 1; a >= 0; a--) { if (firstName.charAt(a) == ' ') { if (a != firstName.length() - 1 && firstName.charAt(a + 1) != ' ') { if (Build.VERSION.SDK_INT >= 16) { - text += "\u200C" + firstName.substring(a + 1, a + 2); - } else { - text += firstName.substring(a + 1, a + 2); + stringBuilder.append("\u200C"); } + stringBuilder.append(firstName.substring(a + 1, a + 2)); break; } } } } - if (text.length() > 0) { - text = text.toUpperCase(); + if (stringBuilder.length() > 0) { + String text = stringBuilder.toString().toUpperCase(); try { textLayout = new StaticLayout(text, (smallStyle ? namePaintSmall : namePaint), AndroidUtilities.dp(100), Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false); if (textLayout.getLineCount() > 0) { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/AvatarUpdater.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/AvatarUpdater.java index 1fa82ddd5..b8001641c 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/AvatarUpdater.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/AvatarUpdater.java @@ -74,10 +74,10 @@ public class AvatarUpdater implements NotificationCenter.NotificationCenterDeleg } public void openGallery() { - PhotoAlbumPickerActivity fragment = new PhotoAlbumPickerActivity(true); + PhotoAlbumPickerActivity fragment = new PhotoAlbumPickerActivity(true, null); fragment.setDelegate(new PhotoAlbumPickerActivity.PhotoAlbumPickerActivityDelegate() { @Override - public void didSelectPhotos(ArrayList photos, ArrayList webPhotos) { + public void didSelectPhotos(ArrayList photos, ArrayList captions, ArrayList webPhotos) { if (!photos.isEmpty()) { Bitmap bitmap = ImageLoader.loadBitmap(photos.get(0), null, 800, 800, true); processBitmap(bitmap); @@ -94,6 +94,11 @@ public class AvatarUpdater implements NotificationCenter.NotificationCenterDeleg FileLog.e("tmessages", e); } } + + @Override + public boolean didSelectVideo(String path) { + return true; + } }); parentFragment.presentFragment(fragment); } @@ -143,7 +148,7 @@ public class AvatarUpdater implements NotificationCenter.NotificationCenterDeleg FileLog.e("tmessages", e); } final ArrayList arrayList = new ArrayList<>(); - arrayList.add(new MediaController.PhotoEntry(0, 0, 0, currentPicturePath, orientation)); + arrayList.add(new MediaController.PhotoEntry(0, 0, 0, currentPicturePath, orientation, false)); PhotoViewer.getInstance().openPhotoForSelect(arrayList, 0, 1, new PhotoViewer.EmptyPhotoViewerProvider() { @Override public void sendButtonPressed(int index) { @@ -157,7 +162,7 @@ public class AvatarUpdater implements NotificationCenter.NotificationCenterDeleg Bitmap bitmap = ImageLoader.loadBitmap(path, null, 800, 800, true); processBitmap(bitmap); } - }); + }, null); Utilities.addMediaToGallery(currentPicturePath); currentPicturePath = null; } else if (requestCode == 14) { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/BackupImageView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/BackupImageView.java index e625402ef..3d991dde3 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/BackupImageView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/BackupImageView.java @@ -23,6 +23,8 @@ import org.telegram.messenger.TLRPC; public class BackupImageView extends View { private ImageReceiver imageReceiver; + private int width = -1; + private int height = -1; public BackupImageView(Context context) { super(context); @@ -102,15 +104,30 @@ public class BackupImageView extends View { return imageReceiver; } + public void setSize(int w, int h) { + width = w; + height = h; + } + @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); - imageReceiver.clearImage(); + imageReceiver.onDetachedFromWindow(); + } + + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + imageReceiver.onAttachedToWindow(); } @Override protected void onDraw(Canvas canvas) { - imageReceiver.setImageCoords(0, 0, getWidth(), getHeight()); + if (width != -1 && height != -1) { + imageReceiver.setImageCoords((getWidth() - width) / 2, (getHeight() - height) / 2, width, height); + } else { + imageReceiver.setImageCoords(0, 0, getWidth(), getHeight()); + } imageReceiver.draw(canvas); } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatActivityEnterView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatActivityEnterView.java index de1b773b6..233374b32 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatActivityEnterView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatActivityEnterView.java @@ -12,7 +12,6 @@ import android.app.Activity; import android.content.Context; import android.content.SharedPreferences; import android.media.AudioManager; -import android.os.Build; import android.os.PowerManager; import android.text.Editable; import android.text.TextWatcher; @@ -23,6 +22,7 @@ import android.view.KeyEvent; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; +import android.view.ViewTreeObserver; import android.view.WindowManager; import android.view.animation.AccelerateDecelerateInterpolator; import android.view.inputmethod.EditorInfo; @@ -31,7 +31,6 @@ import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.PopupWindow; -import android.widget.RelativeLayout; import android.widget.TextView; import org.telegram.android.AndroidUtilities; @@ -48,14 +47,12 @@ import org.telegram.messenger.R; import org.telegram.messenger.TLRPC; import org.telegram.messenger.UserConfig; import org.telegram.ui.ActionBar.BaseFragment; -import org.telegram.ui.AnimationCompat.AnimatorListenerAdapterProxy; -import org.telegram.ui.AnimationCompat.AnimatorSetProxy; -import org.telegram.ui.AnimationCompat.ObjectAnimatorProxy; -import org.telegram.ui.AnimationCompat.ViewProxy; +import org.telegram.android.AnimationCompat.AnimatorListenerAdapterProxy; +import org.telegram.android.AnimationCompat.AnimatorSetProxy; +import org.telegram.android.AnimationCompat.ObjectAnimatorProxy; +import org.telegram.android.AnimationCompat.ViewProxy; import org.telegram.messenger.ApplicationLoader; -import java.lang.reflect.Field; - public class ChatActivityEnterView extends FrameLayoutFixed implements NotificationCenter.NotificationCenterDelegate, SizeNotifierRelativeLayout.SizeNotifierRelativeLayoutDelegate { public interface ChatActivityEnterViewDelegate { @@ -81,6 +78,12 @@ public class ChatActivityEnterView extends FrameLayoutFixed implements Notificat private LinearLayout textFieldContainer; private View topView; + private int framesDroped; + + private int keyboardTransitionState; + private boolean showKeyboardOnEmojiButton; + private ViewTreeObserver.OnPreDrawListener onPreDrawListener; + private PowerManager.WakeLock mWakeLock; private AnimatorSetProxy runningAnimation; private AnimatorSetProxy runningAnimation2; @@ -98,6 +101,7 @@ public class ChatActivityEnterView extends FrameLayoutFixed implements Notificat private float distCanMove = AndroidUtilities.dp(80); private boolean recordingAudio; private boolean forceShowSendButton; + private boolean allowStickers; private Activity parentActivity; private BaseFragment parentFragment; @@ -105,7 +109,7 @@ public class ChatActivityEnterView extends FrameLayoutFixed implements Notificat private boolean ignoreTextChange; private MessageObject replyingMessageObject; private TLRPC.WebPage messageWebPage; - private boolean messageWebPageSearch; + private boolean messageWebPageSearch = true; private ChatActivityEnterViewDelegate delegate; private float topViewAnimation; @@ -136,14 +140,39 @@ public class ChatActivityEnterView extends FrameLayoutFixed implements Notificat SharedPreferences preferences = ApplicationLoader.applicationContext.getSharedPreferences("mainconfig", Activity.MODE_PRIVATE); sendByEnter = preferences.getBoolean("send_by_enter", false); + parent.getViewTreeObserver().addOnPreDrawListener(onPreDrawListener = new ViewTreeObserver.OnPreDrawListener() { + @Override + public boolean onPreDraw() { + if (keyboardTransitionState == 1) { + if (keyboardVisible || framesDroped >= 60) { + showEmojiPopup(false, false); + keyboardTransitionState = 0; + } else { + openKeyboard(); + } + framesDroped++; + return false; + } else if (keyboardTransitionState == 2) { + if (!keyboardVisible || framesDroped >= 60) { + int currentHeight = AndroidUtilities.displaySize.x > AndroidUtilities.displaySize.y ? keyboardHeightLand : keyboardHeight; + sizeNotifierRelativeLayout.setPadding(0, 0, 0, currentHeight); + keyboardTransitionState = 0; + } + framesDroped++; + return false; + } + return true; + } + }); + textFieldContainer = new LinearLayout(context); textFieldContainer.setBackgroundColor(0xffffffff); textFieldContainer.setOrientation(LinearLayout.HORIZONTAL); addView(textFieldContainer); LayoutParams layoutParams2 = (LayoutParams) textFieldContainer.getLayoutParams(); layoutParams2.gravity = Gravity.LEFT | Gravity.TOP; - layoutParams2.width = LayoutParams.MATCH_PARENT; - layoutParams2.height = LayoutParams.WRAP_CONTENT; + layoutParams2.width = LayoutHelper.MATCH_PARENT; + layoutParams2.height = LayoutHelper.WRAP_CONTENT; layoutParams2.topMargin = AndroidUtilities.dp(2); textFieldContainer.setLayoutParams(layoutParams2); @@ -151,7 +180,7 @@ public class ChatActivityEnterView extends FrameLayoutFixed implements Notificat textFieldContainer.addView(frameLayout); LinearLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams) frameLayout.getLayoutParams(); layoutParams.width = 0; - layoutParams.height = LayoutParams.WRAP_CONTENT; + layoutParams.height = LayoutHelper.WRAP_CONTENT; layoutParams.weight = 1; frameLayout.setLayoutParams(layoutParams); @@ -168,7 +197,16 @@ public class ChatActivityEnterView extends FrameLayoutFixed implements Notificat emojiButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { - showEmojiPopup(emojiPopup == null || !emojiPopup.isShowing()); + if (showKeyboardOnEmojiButton) { + setKeyboardTransitionState(1); + int selection = messageEditText.getSelectionStart(); + MotionEvent event = MotionEvent.obtain(0, 0, MotionEvent.ACTION_UP, 0, 0, 0); + messageEditText.onTouchEvent(event); + event.recycle(); + messageEditText.setSelection(selection); + } else { + showEmojiPopup(emojiPopup == null || !emojiPopup.isShowing(), true); + } } }); @@ -187,8 +225,8 @@ public class ChatActivityEnterView extends FrameLayoutFixed implements Notificat messageEditText.setHintTextColor(0xffb2b2b2); frameLayout.addView(messageEditText); layoutParams1 = (FrameLayout.LayoutParams) messageEditText.getLayoutParams(); - layoutParams1.width = FrameLayout.LayoutParams.MATCH_PARENT; - layoutParams1.height = FrameLayout.LayoutParams.WRAP_CONTENT; + layoutParams1.width = LayoutHelper.MATCH_PARENT; + layoutParams1.height = LayoutHelper.WRAP_CONTENT; layoutParams1.gravity = Gravity.BOTTOM; layoutParams1.leftMargin = AndroidUtilities.dp(52); layoutParams1.rightMargin = AndroidUtilities.dp(isChat ? 50 : 2); @@ -198,7 +236,7 @@ public class ChatActivityEnterView extends FrameLayoutFixed implements Notificat public boolean onKey(View view, int i, KeyEvent keyEvent) { if (i == 4 && !keyboardVisible && emojiPopup != null && emojiPopup.isShowing()) { if (keyEvent.getAction() == 1) { - showEmojiPopup(false); + showEmojiPopup(false, true); } return true; } else if (i == KeyEvent.KEYCODE_ENTER && sendByEnter && keyEvent.getAction() == KeyEvent.ACTION_DOWN) { @@ -212,7 +250,7 @@ public class ChatActivityEnterView extends FrameLayoutFixed implements Notificat @Override public void onClick(View view) { if (emojiPopup != null && emojiPopup.isShowing()) { - showEmojiPopup(false); + setKeyboardTransitionState(1); } } }); @@ -243,10 +281,10 @@ public class ChatActivityEnterView extends FrameLayoutFixed implements Notificat checkSendButton(true); if (delegate != null) { - if (before > count || count > 1) { + if (count > 2 || charSequence == null || charSequence.length() == 0) { messageWebPageSearch = true; } - delegate.onTextChanged(charSequence, before > count || count > 1); + delegate.onTextChanged(charSequence, before > count + 1 || (count - before) > 2); } if (message.length() != 0 && lastTypingTimeSend < System.currentTimeMillis() - 5000 && !ignoreTextChange) { @@ -301,7 +339,7 @@ public class ChatActivityEnterView extends FrameLayoutFixed implements Notificat recordPanel.setBackgroundColor(0xffffffff); frameLayout.addView(recordPanel); layoutParams1 = (FrameLayout.LayoutParams) recordPanel.getLayoutParams(); - layoutParams1.width = FrameLayout.LayoutParams.MATCH_PARENT; + layoutParams1.width = LayoutHelper.MATCH_PARENT; layoutParams1.height = AndroidUtilities.dp(48); layoutParams1.gravity = Gravity.BOTTOM; recordPanel.setLayoutParams(layoutParams1); @@ -310,8 +348,8 @@ public class ChatActivityEnterView extends FrameLayoutFixed implements Notificat slideText.setOrientation(LinearLayout.HORIZONTAL); recordPanel.addView(slideText); layoutParams1 = (FrameLayout.LayoutParams) slideText.getLayoutParams(); - layoutParams1.width = FrameLayout.LayoutParams.WRAP_CONTENT; - layoutParams1.height = FrameLayout.LayoutParams.WRAP_CONTENT; + layoutParams1.width = LayoutHelper.WRAP_CONTENT; + layoutParams1.height = LayoutHelper.WRAP_CONTENT; layoutParams1.gravity = Gravity.CENTER; layoutParams1.leftMargin = AndroidUtilities.dp(30); slideText.setLayoutParams(layoutParams1); @@ -320,8 +358,8 @@ public class ChatActivityEnterView extends FrameLayoutFixed implements Notificat imageView.setImageResource(R.drawable.slidearrow); slideText.addView(imageView); layoutParams = (LinearLayout.LayoutParams) imageView.getLayoutParams(); - layoutParams.width = LayoutParams.WRAP_CONTENT; - layoutParams.height = LayoutParams.WRAP_CONTENT; + layoutParams.width = LayoutHelper.WRAP_CONTENT; + layoutParams.height = LayoutHelper.WRAP_CONTENT; layoutParams.gravity = Gravity.CENTER_VERTICAL; layoutParams.topMargin = AndroidUtilities.dp(1); imageView.setLayoutParams(layoutParams); @@ -332,8 +370,8 @@ public class ChatActivityEnterView extends FrameLayoutFixed implements Notificat textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 12); slideText.addView(textView); layoutParams = (LinearLayout.LayoutParams) textView.getLayoutParams(); - layoutParams.width = LayoutParams.WRAP_CONTENT; - layoutParams.height = LayoutParams.WRAP_CONTENT; + layoutParams.width = LayoutHelper.WRAP_CONTENT; + layoutParams.height = LayoutHelper.WRAP_CONTENT; layoutParams.gravity = Gravity.CENTER_VERTICAL; layoutParams.leftMargin = AndroidUtilities.dp(6); textView.setLayoutParams(layoutParams); @@ -344,8 +382,8 @@ public class ChatActivityEnterView extends FrameLayoutFixed implements Notificat linearLayout.setBackgroundColor(0xffffffff); recordPanel.addView(linearLayout); layoutParams1 = (FrameLayout.LayoutParams) linearLayout.getLayoutParams(); - layoutParams1.width = FrameLayout.LayoutParams.WRAP_CONTENT; - layoutParams1.height = FrameLayout.LayoutParams.WRAP_CONTENT; + layoutParams1.width = LayoutHelper.WRAP_CONTENT; + layoutParams1.height = LayoutHelper.WRAP_CONTENT; layoutParams1.gravity = Gravity.CENTER_VERTICAL; linearLayout.setLayoutParams(layoutParams1); @@ -353,8 +391,8 @@ public class ChatActivityEnterView extends FrameLayoutFixed implements Notificat imageView.setImageResource(R.drawable.rec); linearLayout.addView(imageView); layoutParams = (LinearLayout.LayoutParams) imageView.getLayoutParams(); - layoutParams.width = LayoutParams.WRAP_CONTENT; - layoutParams.height = LayoutParams.WRAP_CONTENT; + layoutParams.width = LayoutHelper.WRAP_CONTENT; + layoutParams.height = LayoutHelper.WRAP_CONTENT; layoutParams.gravity = Gravity.CENTER_VERTICAL; layoutParams.topMargin = AndroidUtilities.dp(1); imageView.setLayoutParams(layoutParams); @@ -365,8 +403,8 @@ public class ChatActivityEnterView extends FrameLayoutFixed implements Notificat recordTimeText.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16); linearLayout.addView(recordTimeText); layoutParams = (LinearLayout.LayoutParams) recordTimeText.getLayoutParams(); - layoutParams.width = LayoutParams.WRAP_CONTENT; - layoutParams.height = LayoutParams.WRAP_CONTENT; + layoutParams.width = LayoutHelper.WRAP_CONTENT; + layoutParams.height = LayoutHelper.WRAP_CONTENT; layoutParams.gravity = Gravity.CENTER_VERTICAL; layoutParams.leftMargin = AndroidUtilities.dp(6); recordTimeText.setLayoutParams(layoutParams); @@ -489,6 +527,26 @@ public class ChatActivityEnterView extends FrameLayoutFixed implements Notificat checkSendButton(false); } + private void setKeyboardTransitionState(int state) { + if (AndroidUtilities.usingHardwareInput) { + if (state == 1) { + showEmojiPopup(false, false); + keyboardTransitionState = 0; + + } else if (state == 2) { + int currentHeight = AndroidUtilities.displaySize.x > AndroidUtilities.displaySize.y ? keyboardHeightLand : keyboardHeight; + sizeNotifierRelativeLayout.setPadding(0, 0, 0, currentHeight); + keyboardTransitionState = 0; + } + } else { + framesDroped = 0; + keyboardTransitionState = state; + if (state == 1) { + sizeNotifierRelativeLayout.setPadding(0, 0, 0, 0); + } + } + } + public void addTopView(View view, int height) { if (view == null) { return; @@ -498,7 +556,7 @@ public class ChatActivityEnterView extends FrameLayoutFixed implements Notificat topView.setVisibility(GONE); needShowTopView = false; LayoutParams layoutParams = (LayoutParams) topView.getLayoutParams(); - layoutParams.width = RelativeLayout.LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; layoutParams.height = height; layoutParams.topMargin = AndroidUtilities.dp(2); layoutParams.gravity = Gravity.TOP | Gravity.LEFT; @@ -521,6 +579,10 @@ public class ChatActivityEnterView extends FrameLayoutFixed implements Notificat checkSendButton(animated); } + public void setAllowStickers(boolean value) { + allowStickers = value; + } + public void showTopView(boolean animated) { if (topView == null || topViewShowed) { return; @@ -644,6 +706,7 @@ public class ChatActivityEnterView extends FrameLayoutFixed implements Notificat NotificationCenter.getInstance().removeObserver(this, NotificationCenter.emojiDidLoaded); NotificationCenter.getInstance().removeObserver(this, NotificationCenter.hideEmojiKeyboard); NotificationCenter.getInstance().removeObserver(this, NotificationCenter.audioRouteChanged); + sizeNotifierRelativeLayout.getViewTreeObserver().removeOnPreDrawListener(onPreDrawListener); if (mWakeLock != null) { try { mWakeLock.release(); @@ -670,6 +733,10 @@ public class ChatActivityEnterView extends FrameLayoutFixed implements Notificat messageWebPageSearch = searchWebPages; } + public boolean isMessageWebPageSearchEnabled() { + return messageWebPageSearch; + } + private void sendMessage() { if (parentFragment != null) { String action = null; @@ -978,16 +1045,20 @@ public class ChatActivityEnterView extends FrameLayoutFixed implements Notificat } } - private void showEmojiPopup(boolean show) { + private void showEmojiPopup(boolean show, boolean post) { if (show) { if (emojiPopup == null) { if (parentActivity == null) { return; } - emojiView = new EmojiView(parentActivity); + emojiView = new EmojiView(allowStickers, parentActivity); emojiView.setListener(new EmojiView.Listener() { - public void onBackspace() { - messageEditText.dispatchKeyEvent(new KeyEvent(0, 67)); + public boolean onBackspace() { + if (messageEditText.length() == 0) { + return false; + } + messageEditText.dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DEL)); + return true; } public void onEmojiSelected(String symbol) { @@ -1004,24 +1075,16 @@ public class ChatActivityEnterView extends FrameLayoutFixed implements Notificat FileLog.e("tmessages", e); } } + + public void onStickerSelected(TLRPC.Document sticker) { + SendMessagesHelper.getInstance().sendSticker(sticker, dialog_id, replyingMessageObject); + if (delegate != null) { + delegate.onMessageSend(null); + } + } }); emojiPopup = new PopupWindow(emojiView); - - if (Build.VERSION.SDK_INT >= 21) { - /*emojiPopup.setAnimationStyle(0); - emojiPopup.setClippingEnabled(true); - emojiPopup.setInputMethodMode(PopupWindow.INPUT_METHOD_NOT_NEEDED); - emojiPopup.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED);*/ - try { - Field field = PopupWindow.class.getDeclaredField("mWindowLayoutType"); - field.setAccessible(true); - field.set(emojiPopup, WindowManager.LayoutParams.TYPE_SYSTEM_ERROR); - } catch (Exception e) { - //ignored - } - } } - int currentHeight; if (keyboardHeight <= 0) { keyboardHeight = ApplicationLoader.applicationContext.getSharedPreferences("emoji", 0).getInt("kbd_height", AndroidUtilities.dp(200)); @@ -1029,11 +1092,7 @@ public class ChatActivityEnterView extends FrameLayoutFixed implements Notificat if (keyboardHeightLand <= 0) { keyboardHeightLand = ApplicationLoader.applicationContext.getSharedPreferences("emoji", 0).getInt("kbd_height_land3", AndroidUtilities.dp(200)); } - if (AndroidUtilities.displaySize.x > AndroidUtilities.displaySize.y) { - currentHeight = keyboardHeightLand; - } else { - currentHeight = keyboardHeight; - } + int currentHeight = AndroidUtilities.displaySize.x > AndroidUtilities.displaySize.y ? keyboardHeightLand : keyboardHeight; FileLog.e("tmessages", "show emoji with height = " + currentHeight); emojiPopup.setHeight(View.MeasureSpec.makeMeasureSpec(currentHeight, View.MeasureSpec.EXACTLY)); if (sizeNotifierRelativeLayout != null) { @@ -1042,46 +1101,24 @@ public class ChatActivityEnterView extends FrameLayoutFixed implements Notificat emojiPopup.showAtLocation(parentActivity.getWindow().getDecorView(), Gravity.BOTTOM | Gravity.LEFT, 0, 0); - /*if (Build.VERSION.SDK_INT < 21) { - try { - - } catch (Exception e) { - FileLog.e("tmessages", e); - return; - } - }*/ - if (!keyboardVisible) { - /*if (Build.VERSION.SDK_INT >= 21) { - try { - emojiPopup.showAsDropDown(this, 0, 0); - } catch (Exception e) { - FileLog.e("tmessages", e); - return; - } - }*/ if (sizeNotifierRelativeLayout != null) { sizeNotifierRelativeLayout.setPadding(0, 0, 0, currentHeight); emojiButton.setImageResource(R.drawable.ic_msg_panel_hide); + showKeyboardOnEmojiButton = false; onWindowSizeChanged(sizeNotifierRelativeLayout.getHeight() - sizeNotifierRelativeLayout.getPaddingBottom()); } return; } else { - /*if (Build.VERSION.SDK_INT >= 21) { - try { - emojiPopup.showAsDropDown(this, 0, -currentHeight - getHeight()); - emojiPopup.update(this, 0, -currentHeight - getHeight(), -1, -1); - AndroidUtilities.hideKeyboard(messageEditText); - } catch (Exception e) { - FileLog.e("tmessages", e); - return; - } - }*/ + setKeyboardTransitionState(2); + AndroidUtilities.hideKeyboard(messageEditText); } emojiButton.setImageResource(R.drawable.ic_msg_panel_kb); + showKeyboardOnEmojiButton = true; return; } if (emojiButton != null) { + showKeyboardOnEmojiButton = false; emojiButton.setImageResource(R.drawable.ic_msg_panel_smiles); } if (emojiPopup != null) { @@ -1091,21 +1128,28 @@ public class ChatActivityEnterView extends FrameLayoutFixed implements Notificat //don't promt } } - if (sizeNotifierRelativeLayout != null) { - sizeNotifierRelativeLayout.post(new Runnable() { - public void run() { - if (sizeNotifierRelativeLayout != null) { - sizeNotifierRelativeLayout.setPadding(0, 0, 0, 0); - onWindowSizeChanged(sizeNotifierRelativeLayout.getHeight() - sizeNotifierRelativeLayout.getPaddingBottom()); - } + if (keyboardTransitionState == 0) { + if (sizeNotifierRelativeLayout != null) { + if (post) { + sizeNotifierRelativeLayout.post(new Runnable() { + public void run() { + if (sizeNotifierRelativeLayout != null) { + sizeNotifierRelativeLayout.setPadding(0, 0, 0, 0); + onWindowSizeChanged(sizeNotifierRelativeLayout.getHeight()); + } + } + }); + } else { + sizeNotifierRelativeLayout.setPadding(0, 0, 0, 0); + onWindowSizeChanged(sizeNotifierRelativeLayout.getHeight()); } - }); + } } } public void hideEmojiPopup() { if (emojiPopup != null && emojiPopup.isShowing()) { - showEmojiPopup(false); + showEmojiPopup(false, true); } } @@ -1201,7 +1245,7 @@ public class ChatActivityEnterView extends FrameLayoutFixed implements Notificat FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) view.getLayoutParams(); layoutParams.gravity = Gravity.CENTER; layoutParams.width = AndroidUtilities.dp(48); - layoutParams.height = FrameLayout.LayoutParams.MATCH_PARENT; + layoutParams.height = LayoutHelper.MATCH_PARENT; view.setLayoutParams(layoutParams); } @@ -1227,13 +1271,6 @@ public class ChatActivityEnterView extends FrameLayoutFixed implements Notificat final WindowManager.LayoutParams layoutParams = (WindowManager.LayoutParams) emojiPopup.getContentView().getLayoutParams(); FileLog.e("tmessages", "update emoji height to = " + newHeight); if (layoutParams.width != AndroidUtilities.displaySize.x || layoutParams.height != newHeight) { - /*if (Build.VERSION.SDK_INT >= 21) { - if (!keyboardVisible) { - emojiPopup.update(this, 0, 0, -1, -1); - } else { - emojiPopup.update(this, 0, -newHeight - getHeight(), -1, -1); - } - }*/ layoutParams.width = AndroidUtilities.displaySize.x; layoutParams.height = newHeight; WindowManager wm = (WindowManager) ApplicationLoader.applicationContext.getSystemService(Activity.WINDOW_SERVICE); @@ -1252,12 +1289,22 @@ public class ChatActivityEnterView extends FrameLayoutFixed implements Notificat boolean oldValue = keyboardVisible; keyboardVisible = height > 0; - if (keyboardVisible && sizeNotifierRelativeLayout.getPaddingBottom() > 0) { - showEmojiPopup(false); - } else if (!keyboardVisible && keyboardVisible != oldValue && emojiPopup != null && emojiPopup.isShowing()) { - showEmojiPopup(false); + if (keyboardVisible && (sizeNotifierRelativeLayout.getPaddingBottom() > 0 || keyboardTransitionState == 1)) { + setKeyboardTransitionState(1); + } else if (keyboardTransitionState != 2 && !keyboardVisible && keyboardVisible != oldValue && emojiPopup != null && emojiPopup.isShowing()) { + showEmojiPopup(false, true); + } + if (keyboardTransitionState == 0) { + onWindowSizeChanged(sizeNotifierRelativeLayout.getHeight() - sizeNotifierRelativeLayout.getPaddingBottom()); + } + } + + public int getEmojiHeight() { + if (AndroidUtilities.displaySize.x > AndroidUtilities.displaySize.y) { + return keyboardHeightLand; + } else { + return keyboardHeight; } - onWindowSizeChanged(sizeNotifierRelativeLayout.getHeight() - sizeNotifierRelativeLayout.getPaddingBottom()); } @Override @@ -1270,6 +1317,9 @@ public class ChatActivityEnterView extends FrameLayoutFixed implements Notificat Long time = (Long) args[0] / 1000; String str = String.format("%02d:%02d", time / 60, time % 60); if (lastTimeString == null || !lastTimeString.equals(str)) { + if (time % 5 == 0) { + MessagesController.getInstance().sendTyping(dialog_id, 1, 0); + } if (recordTimeText != null) { recordTimeText.setText(str); } @@ -1280,6 +1330,7 @@ public class ChatActivityEnterView extends FrameLayoutFixed implements Notificat } } else if (id == NotificationCenter.recordStartError || id == NotificationCenter.recordStopped) { if (recordingAudio) { + MessagesController.getInstance().sendTyping(dialog_id, 2, 0); recordingAudio = false; updateAudioRecordIntefrace(); } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/CheckBox.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/CheckBox.java index a34b7a989..95707a7f4 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/CheckBox.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/CheckBox.java @@ -18,7 +18,7 @@ import android.graphics.drawable.Drawable; import android.view.View; import org.telegram.android.AndroidUtilities; -import org.telegram.ui.AnimationCompat.ObjectAnimatorProxy; +import org.telegram.android.AnimationCompat.ObjectAnimatorProxy; public class CheckBox extends View { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/ClippingImageView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/ClippingImageView.java index 3c17e47ee..71ec5be0f 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/ClippingImageView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/ClippingImageView.java @@ -19,7 +19,7 @@ import android.graphics.Shader; import android.view.View; import org.telegram.messenger.FileLog; -import org.telegram.ui.AnimationCompat.ViewProxy; +import org.telegram.android.AnimationCompat.ViewProxy; public class ClippingImageView extends View { @@ -32,7 +32,6 @@ public class ClippingImageView extends View { private Paint paint; private Bitmap bmp; private Matrix matrix; - private onDrawListener drawListener; private boolean needRadius; private int radius; @@ -42,9 +41,8 @@ public class ClippingImageView extends View { private RectF bitmapRect; private Matrix shaderMatrix; - public interface onDrawListener { - void onDraw(); - } + private float animationProgress; + private float animationValues[][]; public ClippingImageView(Context context) { super(context); @@ -55,6 +53,29 @@ public class ClippingImageView extends View { bitmapRect = new RectF(); } + public void setAnimationValues(float[][] values) { + animationValues = values; + } + + public float getAnimationProgress() { + return animationProgress; + } + + public void setAnimationProgress(float progress) { + animationProgress = progress; + + ViewProxy.setScaleX(this, animationValues[0][0] + (animationValues[1][0] - animationValues[0][0]) * animationProgress); + ViewProxy.setScaleY(this, animationValues[0][1] + (animationValues[1][1] - animationValues[0][1]) * animationProgress); + ViewProxy.setTranslationX(this, animationValues[0][2] + (animationValues[1][2] - animationValues[0][2]) * animationProgress); + ViewProxy.setTranslationY(this, animationValues[0][3] + (animationValues[1][3] - animationValues[0][3]) * animationProgress); + setClipHorizontal((int) (animationValues[0][4] + (animationValues[1][4] - animationValues[0][4]) * animationProgress)); + setClipTop((int) (animationValues[0][5] + (animationValues[1][5] - animationValues[0][5]) * animationProgress)); + setClipBottom((int) (animationValues[0][6] + (animationValues[1][6] - animationValues[0][6]) * animationProgress)); + setRadius((int) (animationValues[0][7] + (animationValues[1][7] - animationValues[0][7]) * animationProgress)); + + invalidate(); + } + public int getClipBottom() { return clipBottom; } @@ -85,9 +106,6 @@ public class ClippingImageView extends View { } if (bmp != null) { float scaleY = ViewProxy.getScaleY(this); - if (drawListener != null && scaleY != 1) { - drawListener.onDraw(); - } canvas.save(); if (needRadius) { @@ -174,10 +192,6 @@ public class ClippingImageView extends View { invalidate(); } - public void setOnDrawListener(onDrawListener listener) { - drawListener = listener; - } - public void setNeedRadius(boolean value) { needRadius = value; } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/EmojiView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/EmojiView.java index 2f2706d4d..2800d68a9 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/EmojiView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/EmojiView.java @@ -13,7 +13,9 @@ import android.database.DataSetObserver; import android.support.v4.view.PagerAdapter; import android.support.v4.view.ViewPager; import android.text.TextUtils; -import android.util.AttributeSet; +import android.view.Gravity; +import android.view.HapticFeedbackConstants; +import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import android.widget.BaseAdapter; @@ -26,61 +28,181 @@ import android.widget.TextView; import org.telegram.android.AndroidUtilities; import org.telegram.android.Emoji; import org.telegram.android.LocaleController; +import org.telegram.android.NotificationCenter; +import org.telegram.android.query.StickersQuery; import org.telegram.messenger.R; +import org.telegram.messenger.TLRPC; +import org.telegram.ui.Cells.StickerEmojiCell; import java.util.ArrayList; -public class EmojiView extends LinearLayout { +public class EmojiView extends LinearLayout implements NotificationCenter.NotificationCenterDelegate { + + public interface Listener { + boolean onBackspace(); + void onEmojiSelected(String emoji); + void onStickerSelected(TLRPC.Document sticker); + } private ArrayList adapters = new ArrayList<>(); + private StickersGridAdapter stickersGridAdapter; private int[] icons = { R.drawable.ic_emoji_recent, R.drawable.ic_emoji_smile, R.drawable.ic_emoji_flower, R.drawable.ic_emoji_bell, R.drawable.ic_emoji_car, - R.drawable.ic_emoji_symbol }; + R.drawable.ic_emoji_symbol, + R.drawable.ic_emoji_sticker}; + private Listener listener; private ViewPager pager; private FrameLayout recentsWrap; private ArrayList views = new ArrayList<>(); + private ImageView backspaceButton; - public EmojiView(Context paramContext) { - super(paramContext); - init(); + private boolean backspacePressed; + private boolean backspaceOnce; + + public EmojiView(boolean needStickers, Context context) { + super(context); + + setOrientation(LinearLayout.VERTICAL); + for (int i = 0; i < Emoji.data.length; i++) { + GridView gridView = new GridView(context); + if (AndroidUtilities.isTablet()) { + gridView.setColumnWidth(AndroidUtilities.dp(60)); + } else { + gridView.setColumnWidth(AndroidUtilities.dp(45)); + } + gridView.setNumColumns(-1); + views.add(gridView); + + EmojiGridAdapter emojiGridAdapter = new EmojiGridAdapter(Emoji.data[i]); + gridView.setAdapter(emojiGridAdapter); + AndroidUtilities.setListViewEdgeEffectColor(gridView, 0xfff5f6f7); + adapters.add(emojiGridAdapter); + } + + if (needStickers) { + GridView gridView = new GridView(context); + gridView.setColumnWidth(AndroidUtilities.dp(72)); + gridView.setNumColumns(-1); + gridView.setPadding(0, AndroidUtilities.dp(4), 0, 0); + gridView.setClipToPadding(false); + views.add(gridView); + stickersGridAdapter = new StickersGridAdapter(context); + gridView.setAdapter(stickersGridAdapter); + AndroidUtilities.setListViewEdgeEffectColor(gridView, 0xfff5f6f7); + } + + setBackgroundColor(0xfff5f6f7); + + pager = new ViewPager(context); + pager.setAdapter(new EmojiPagesAdapter()); + + LinearLayout linearLayout = new LinearLayout(context); + linearLayout.setOrientation(LinearLayout.HORIZONTAL); + linearLayout.setBackgroundColor(0xfff5f6f7); + addView(linearLayout, new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, AndroidUtilities.dp(48))); + + PagerSlidingTabStrip tabs = new PagerSlidingTabStrip(context); + tabs.setViewPager(pager); + tabs.setShouldExpand(true); + tabs.setIndicatorHeight(AndroidUtilities.dp(2)); + tabs.setUnderlineHeight(AndroidUtilities.dp(1)); + tabs.setIndicatorColor(0xff2b96e2); + tabs.setUnderlineColor(0xffe2e5e7); + linearLayout.addView(tabs, new LinearLayout.LayoutParams(0, AndroidUtilities.dp(48), 1.0f)); + + FrameLayout frameLayout = new FrameLayout(context); + linearLayout.addView(frameLayout, new LinearLayout.LayoutParams(AndroidUtilities.dp(52), AndroidUtilities.dp(48))); + + backspaceButton = new ImageView(context) { + @Override + public boolean onTouchEvent(MotionEvent event) { + if (event.getAction() == MotionEvent.ACTION_DOWN) { + backspacePressed = true; + backspaceOnce = false; + postBackspaceRunnable(350); + } else if (event.getAction() == MotionEvent.ACTION_CANCEL || event.getAction() == MotionEvent.ACTION_UP) { + backspacePressed = false; + if (!backspaceOnce) { + if (EmojiView.this.listener != null && EmojiView.this.listener.onBackspace()) { + backspaceButton.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP); + } + } + } + super.onTouchEvent(event); + return true; + } + }; + backspaceButton.setImageResource(R.drawable.ic_smiles_backspace); + backspaceButton.setBackgroundResource(R.drawable.ic_emoji_backspace); + backspaceButton.setScaleType(ImageView.ScaleType.CENTER); + frameLayout.addView(backspaceButton, new FrameLayout.LayoutParams(AndroidUtilities.dp(52), AndroidUtilities.dp(48))); + + View view = new View(context); + view.setBackgroundColor(0xffe2e5e7); + frameLayout.addView(view, new FrameLayout.LayoutParams(AndroidUtilities.dp(52), AndroidUtilities.dp(1), Gravity.LEFT | Gravity.BOTTOM)); + + recentsWrap = new FrameLayout(context); + recentsWrap.addView(views.get(0)); + + TextView textView = new TextView(context); + textView.setText(LocaleController.getString("NoRecent", R.string.NoRecent)); + textView.setTextSize(18); + textView.setTextColor(0xff888888); + textView.setGravity(Gravity.CENTER); + recentsWrap.addView(textView); + views.get(0).setEmptyView(textView); + + addView(pager); + + loadRecents(); + + if (Emoji.data[0] == null || Emoji.data[0].length == 0) { + pager.setCurrentItem(1); + } } - public EmojiView(Context paramContext, AttributeSet paramAttributeSet) { - super(paramContext, paramAttributeSet); - init(); + private void postBackspaceRunnable(final int time) { + AndroidUtilities.runOnUIThread(new Runnable() { + @Override + public void run() { + if (!backspacePressed) { + return; + } + if (EmojiView.this.listener != null && EmojiView.this.listener.onBackspace()) { + backspaceButton.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP); + } + backspaceOnce = true; + postBackspaceRunnable(Math.max(50, time - 100)); + } + }, time); } - public EmojiView(Context paramContext, AttributeSet paramAttributeSet, int paramInt) { - super(paramContext, paramAttributeSet, paramInt); - init(); - } - - private void addToRecent(long paramLong) { - if (this.pager.getCurrentItem() == 0) { + private void addToRecent(long code) { + if (pager.getCurrentItem() == 0) { return; } - ArrayList localArrayList = new ArrayList<>(); + ArrayList recent = new ArrayList<>(); long[] currentRecent = Emoji.data[0]; boolean was = false; for (long aCurrentRecent : currentRecent) { - if (paramLong == aCurrentRecent) { - localArrayList.add(0, paramLong); + if (code == aCurrentRecent) { + recent.add(0, code); was = true; } else { - localArrayList.add(aCurrentRecent); + recent.add(aCurrentRecent); } } if (!was) { - localArrayList.add(0, paramLong); + recent.add(0, code); } - Emoji.data[0] = new long[Math.min(localArrayList.size(), 50)]; + Emoji.data[0] = new long[Math.min(recent.size(), 50)]; for (int q = 0; q < Emoji.data[0].length; q++) { - Emoji.data[0][q] = localArrayList.get(q); + Emoji.data[0][q] = recent.get(q); } adapters.get(0).data = Emoji.data[0]; adapters.get(0).notifyDataSetChanged(); @@ -93,74 +215,13 @@ public class EmojiView extends LinearLayout { if (i >= 4) { return str; } - int j = (int)(0xFFFF & paramLong >> 16 * (3 - i)); + int j = (int) (0xFFFF & paramLong >> 16 * (3 - i)); if (j != 0) { - str = str + (char)j; + str = str + (char) j; } } } - private void init() { - setOrientation(LinearLayout.VERTICAL); - for (int i = 0; i < Emoji.data.length; i++) { - GridView gridView = new GridView(getContext()); - if (AndroidUtilities.isTablet()) { - gridView.setColumnWidth(AndroidUtilities.dp(60)); - } else { - gridView.setColumnWidth(AndroidUtilities.dp(45)); - } - gridView.setNumColumns(-1); - views.add(gridView); - - EmojiGridAdapter localEmojiGridAdapter = new EmojiGridAdapter(Emoji.data[i]); - gridView.setAdapter(localEmojiGridAdapter); - AndroidUtilities.setListViewEdgeEffectColor(gridView, 0xff999999); - adapters.add(localEmojiGridAdapter); - } - - setBackgroundColor(0xff222222); - pager = new ViewPager(getContext()); - pager.setAdapter(new EmojiPagesAdapter()); - PagerSlidingTabStrip tabs = new PagerSlidingTabStrip(getContext()); - tabs.setViewPager(pager); - tabs.setShouldExpand(true); - tabs.setIndicatorColor(0xff33b5e5); - tabs.setIndicatorHeight(AndroidUtilities.dp(2.0f)); - tabs.setUnderlineHeight(AndroidUtilities.dp(2.0f)); - tabs.setUnderlineColor(0x66000000); - tabs.setTabBackground(0); - LinearLayout localLinearLayout = new LinearLayout(getContext()); - localLinearLayout.setOrientation(LinearLayout.HORIZONTAL); - localLinearLayout.addView(tabs, new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT, 1.0f)); - ImageView localImageView = new ImageView(getContext()); - localImageView.setImageResource(R.drawable.ic_emoji_backspace); - localImageView.setScaleType(ImageView.ScaleType.CENTER); - localImageView.setBackgroundResource(R.drawable.bg_emoji_bs); - localImageView.setOnClickListener(new View.OnClickListener() { - public void onClick(View view) { - if (EmojiView.this.listener != null) { - EmojiView.this.listener.onBackspace(); - } - } - }); - localLinearLayout.addView(localImageView, new LinearLayout.LayoutParams(AndroidUtilities.dp(61), LayoutParams.MATCH_PARENT)); - recentsWrap = new FrameLayout(getContext()); - recentsWrap.addView(views.get(0)); - TextView localTextView = new TextView(getContext()); - localTextView.setText(LocaleController.getString("NoRecent", R.string.NoRecent)); - localTextView.setTextSize(18.0f); - localTextView.setTextColor(-7829368); - localTextView.setGravity(17); - recentsWrap.addView(localTextView); - views.get(0).setEmptyView(localTextView); - addView(localLinearLayout, new LinearLayout.LayoutParams(-1, AndroidUtilities.dp(48.0f))); - addView(pager); - loadRecents(); - if (Emoji.data[0] == null || Emoji.data[0].length == 0) { - pager.setCurrentItem(1); - } - } - private void saveRecents() { ArrayList localArrayList = new ArrayList<>(); long[] arrayOfLong = Emoji.data[0]; @@ -177,25 +238,30 @@ public class EmojiView extends LinearLayout { public void loadRecents() { String str = getContext().getSharedPreferences("emoji", 0).getString("recents", ""); String[] arrayOfString = null; - if ((str != null) && (str.length() > 0)) { + if (str != null && str.length() > 0) { arrayOfString = str.split(","); Emoji.data[0] = new long[arrayOfString.length]; - } - if (arrayOfString != null) { for (int i = 0; i < arrayOfString.length; i++) { Emoji.data[0][i] = Long.parseLong(arrayOfString[i]); } - adapters.get(0).data = Emoji.data[0]; - adapters.get(0).notifyDataSetChanged(); + } else { + Emoji.data[0] = new long[]{0x00000000D83DDE02L, 0x00000000D83DDE18L, 0x0000000000002764L, 0x00000000D83DDE0DL, 0x00000000D83DDE0AL, 0x00000000D83DDE01L, + 0x00000000D83DDC4DL, 0x000000000000263AL, 0x00000000D83DDE14L, 0x00000000D83DDE04L, 0x00000000D83DDE2DL, 0x00000000D83DDC8BL, + 0x00000000D83DDE12L, 0x00000000D83DDE33L, 0x00000000D83DDE1CL, 0x00000000D83DDE48L, 0x00000000D83DDE09L, 0x00000000D83DDE03L, + 0x00000000D83DDE22L, 0x00000000D83DDE1DL, 0x00000000D83DDE31L, 0x00000000D83DDE21L, 0x00000000D83DDE0FL, 0x00000000D83DDE1EL, + 0x00000000D83DDE05L, 0x00000000D83DDE1AL, 0x00000000D83DDE4AL, 0x00000000D83DDE0CL, 0x00000000D83DDE00L, 0x00000000D83DDE0BL, + 0x00000000D83DDE06L, 0x00000000D83DDC4CL, 0x00000000D83DDE10L, 0x00000000D83DDE15L}; } + adapters.get(0).data = Emoji.data[0]; + adapters.get(0).notifyDataSetChanged(); } - public void onMeasure(int paramInt1, int paramInt2) { - super.onMeasure(View.MeasureSpec.makeMeasureSpec(View.MeasureSpec.getSize(paramInt1), MeasureSpec.EXACTLY), View.MeasureSpec.makeMeasureSpec(View.MeasureSpec.getSize(paramInt2), MeasureSpec.EXACTLY)); + public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(View.MeasureSpec.makeMeasureSpec(View.MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.EXACTLY), View.MeasureSpec.makeMeasureSpec(View.MeasureSpec.getSize(heightMeasureSpec), MeasureSpec.EXACTLY)); } - public void setListener(Listener paramListener) { - this.listener = paramListener; + public void setListener(Listener value) { + listener = value; } public void invalidateViews() { @@ -206,6 +272,78 @@ public class EmojiView extends LinearLayout { } } + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + if (stickersGridAdapter != null) { + NotificationCenter.getInstance().addObserver(this, NotificationCenter.stickersDidLoaded); + } + } + + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + if (stickersGridAdapter != null) { + NotificationCenter.getInstance().removeObserver(this, NotificationCenter.stickersDidLoaded); + } + } + + @Override + public void didReceivedNotification(int id, Object... args) { + if (id == NotificationCenter.stickersDidLoaded) { + stickersGridAdapter.notifyDataSetChanged(); + } + } + + private class StickersGridAdapter extends BaseAdapter { + + Context context; + + public StickersGridAdapter(Context context) { + this.context = context; + StickersQuery.checkStickers(); + } + + public int getCount() { + return StickersQuery.getStickers().size(); + } + + public Object getItem(int i) { + return StickersQuery.getStickers().get(i); + } + + public long getItemId(int i) { + return StickersQuery.getStickers().get(i).id; + } + + public View getView(int i, View view, ViewGroup viewGroup) { + if (view == null) { + view = new StickerEmojiCell(context) { + public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(82), MeasureSpec.EXACTLY)); + } + }; + view.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + if (listener != null) { + listener.onStickerSelected(((StickerEmojiCell) v).getSticker()); + } + } + }); + } + ((StickerEmojiCell) view).setSticker(StickersQuery.getStickers().get(i)); + return view; + } + + @Override + public void unregisterDataSetObserver(DataSetObserver observer) { + if (observer != null) { + super.unregisterDataSetObserver(observer); + } + } + } + private class EmojiGridAdapter extends BaseAdapter { long[] data; @@ -299,9 +437,4 @@ public class EmojiView extends LinearLayout { } } } - - public interface Listener { - void onBackspace(); - void onEmojiSelected(String paramString); - } } \ No newline at end of file diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/FrameLayoutFixed.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/FrameLayoutFixed.java index 542623e9d..cd169b3f9 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/FrameLayoutFixed.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/FrameLayoutFixed.java @@ -87,8 +87,8 @@ public class FrameLayoutFixed extends FrameLayout { child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin); childState |= getMeasuredStateFixed(child); if (measureMatchParentChildren) { - if (lp.width == LayoutParams.MATCH_PARENT || - lp.height == LayoutParams.MATCH_PARENT) { + if (lp.width == LayoutHelper.MATCH_PARENT || + lp.height == LayoutHelper.MATCH_PARENT) { mMatchParentChildren.add(child); } } @@ -122,7 +122,7 @@ public class FrameLayoutFixed extends FrameLayout { int childWidthMeasureSpec; int childHeightMeasureSpec; - if (lp.width == LayoutParams.MATCH_PARENT) { + if (lp.width == LayoutHelper.MATCH_PARENT) { childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(getMeasuredWidth() - getPaddingLeft() - getPaddingRight() - lp.leftMargin - lp.rightMargin, @@ -134,7 +134,7 @@ public class FrameLayoutFixed extends FrameLayout { lp.width); } - if (lp.height == LayoutParams.MATCH_PARENT) { + if (lp.height == LayoutHelper.MATCH_PARENT) { childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(getMeasuredHeight() - getPaddingTop() - getPaddingBottom() - lp.topMargin - lp.bottomMargin, diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/HorizontalListView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/HorizontalListView.java index 38728e89b..973a775a5 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/HorizontalListView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/HorizontalListView.java @@ -126,7 +126,7 @@ public class HorizontalListView extends AdapterView { private void addAndMeasureChild(final View child, int viewPos) { LayoutParams params = child.getLayoutParams(); if (params == null) { - params = new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT); + params = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutHelper.MATCH_PARENT); } addViewInLayout(child, viewPos, params, true); child.measure(MeasureSpec.makeMeasureSpec(getWidth(), MeasureSpec.AT_MOST), MeasureSpec.makeMeasureSpec(getHeight(), MeasureSpec.AT_MOST)); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/LayoutHelper.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/LayoutHelper.java new file mode 100644 index 000000000..f449daf27 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/LayoutHelper.java @@ -0,0 +1,122 @@ +/* + * This is the source code of Telegram for Android v. 2.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-2015. + */ + +package org.telegram.ui.Components; + +import android.widget.FrameLayout; +import android.widget.LinearLayout; +import android.widget.RelativeLayout; + +import org.telegram.android.AndroidUtilities; + +public class LayoutHelper { + + public static final int MATCH_PARENT = -1; + public static final int WRAP_CONTENT = -2; + + private static int getSize(float size) { + return (int) (size < 0 ? size : AndroidUtilities.dp(size)); + } + + public static FrameLayout.LayoutParams createFrame(int width, int height, int gravity, float leftMargin, float topMargin, float rightMargin, float bottomMargin) { + FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(getSize(width), getSize(height), gravity); + layoutParams.setMargins(AndroidUtilities.dp(leftMargin), AndroidUtilities.dp(topMargin), AndroidUtilities.dp(rightMargin), AndroidUtilities.dp(bottomMargin)); + return layoutParams; + } + + public static FrameLayout.LayoutParams createFrame(int width, int height, int gravity) { + return new FrameLayout.LayoutParams(getSize(width), getSize(height), gravity); + } + + public static FrameLayout.LayoutParams createFrame(int width, int height) { + return new FrameLayout.LayoutParams(getSize(width), getSize(height)); + } + + public static RelativeLayout.LayoutParams createRelative(float width, float height, int leftMargin, int topMargin, int rightMargin, int bottomMargin, int alignParent, int alignRelative, int anchorRelative) { + RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(getSize(width), getSize(height)); + if (alignParent >= 0) { + layoutParams.addRule(alignParent); + } + if (alignRelative >= 0 && anchorRelative >= 0) { + layoutParams.addRule(alignRelative, anchorRelative); + } + layoutParams.leftMargin = AndroidUtilities.dp(leftMargin); + layoutParams.topMargin = AndroidUtilities.dp(topMargin); + layoutParams.rightMargin = AndroidUtilities.dp(rightMargin); + layoutParams.bottomMargin = AndroidUtilities.dp(bottomMargin); + return layoutParams; + } + + public static RelativeLayout.LayoutParams createRelative(int width, int height, int leftMargin, int topMargin, int rightMargin, int bottomMargin) { + return createRelative(width, height, leftMargin, topMargin, rightMargin, bottomMargin, -1, -1, -1); + } + + public static RelativeLayout.LayoutParams createRelative(int width, int height, int leftMargin, int topMargin, int rightMargin, int bottomMargin, int alignParent) { + return createRelative(width, height, leftMargin, topMargin, rightMargin, bottomMargin, alignParent, -1, -1); + } + + public static RelativeLayout.LayoutParams createRelative(float width, float height, int leftMargin, int topMargin, int rightMargin, int bottomMargin, int alignRelative, int anchorRelative) { + return createRelative(width, height, leftMargin, topMargin, rightMargin, bottomMargin, -1, alignRelative, anchorRelative); + } + + public static RelativeLayout.LayoutParams createRelative(int width, int height, int alignParent, int alignRelative, int anchorRelative) { + return createRelative(width, height, 0, 0, 0, 0, alignParent, alignRelative, anchorRelative); + } + + public static RelativeLayout.LayoutParams createRelative(int width, int height) { + return createRelative(width, height, 0, 0, 0, 0, -1, -1, -1); + } + + public static RelativeLayout.LayoutParams createRelative(int width, int height, int alignParent) { + return createRelative(width, height, 0, 0, 0, 0, alignParent, -1, -1); + } + + public static RelativeLayout.LayoutParams createRelative(int width, int height, int alignRelative, int anchorRelative) { + return createRelative(width, height, 0, 0, 0, 0, -1, alignRelative, anchorRelative); + } + + public static LinearLayout.LayoutParams createLinear(int width, int height, float weight, int gravity, int leftMargin, int topMargin, int rightMargin, int bottomMargin) { + LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(getSize(width), getSize(height), weight); + layoutParams.setMargins(AndroidUtilities.dp(leftMargin), AndroidUtilities.dp(topMargin), AndroidUtilities.dp(rightMargin), AndroidUtilities.dp(bottomMargin)); + layoutParams.gravity = gravity; + return layoutParams; + } + + public static LinearLayout.LayoutParams createLinear(int width, int height, int gravity, int leftMargin, int topMargin, int rightMargin, int bottomMargin) { + LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(getSize(width), getSize(height)); + layoutParams.setMargins(AndroidUtilities.dp(leftMargin), AndroidUtilities.dp(topMargin), AndroidUtilities.dp(rightMargin), AndroidUtilities.dp(bottomMargin)); + layoutParams.gravity = gravity; + return layoutParams; + } + + public static LinearLayout.LayoutParams createLinear(int width, int height, int leftMargin, int topMargin, int rightMargin, int bottomMargin) { + LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(getSize(width), getSize(height)); + layoutParams.setMargins(AndroidUtilities.dp(leftMargin), AndroidUtilities.dp(topMargin), AndroidUtilities.dp(rightMargin), AndroidUtilities.dp(bottomMargin)); + return layoutParams; + } + + public static LinearLayout.LayoutParams createLinear(int width, int height, float weight, int gravity) { + LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(getSize(width), getSize(height), weight); + layoutParams.gravity = gravity; + return layoutParams; + } + + public static LinearLayout.LayoutParams createLinear(int width, int height, int gravity) { + LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(getSize(width), getSize(height)); + layoutParams.gravity = gravity; + return layoutParams; + } + + public static LinearLayout.LayoutParams createLinear(int width, int height, float weight) { + return new LinearLayout.LayoutParams(getSize(width), getSize(height), weight); + } + + public static LinearLayout.LayoutParams createLinear(int width, int height) { + return new LinearLayout.LayoutParams(getSize(width), getSize(height)); + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/LayoutListView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/LayoutListView.java deleted file mode 100644 index 1ed6f83b6..000000000 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/LayoutListView.java +++ /dev/null @@ -1,90 +0,0 @@ -/* - * This is the source code of Telegram for Android v. 1.3.2. - * 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. - */ - -package org.telegram.ui.Components; - -import android.annotation.SuppressLint; -import android.content.Context; -import android.util.AttributeSet; -import android.view.MotionEvent; -import android.view.View; -import android.widget.ListView; - -public class LayoutListView extends ListView { - - public interface OnInterceptTouchEventListener { - boolean onInterceptTouchEvent(MotionEvent event); - } - - private OnInterceptTouchEventListener onInterceptTouchEventListener; - private int height = -1; - private int forceTop = Integer.MIN_VALUE; - - public LayoutListView(Context context) { - super(context); - } - - public LayoutListView(Context context, AttributeSet attrs) { - super(context, attrs); - } - - public LayoutListView(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - } - - public void setOnInterceptTouchEventListener(OnInterceptTouchEventListener listener) { - onInterceptTouchEventListener = listener; - } - - public void setForceTop(int value) { - forceTop = value; - } - - @Override - public boolean onInterceptTouchEvent(MotionEvent ev) { - if (onInterceptTouchEventListener != null) { - return onInterceptTouchEventListener.onInterceptTouchEvent(ev) || super.onInterceptTouchEvent(ev); - } - return super.onInterceptTouchEvent(ev); - } - - @SuppressLint("DrawAllocation") - @Override - protected void onLayout(boolean changed, int left, int top, int right, int bottom) { - View v = getChildAt(getChildCount() - 1); - int scrollTo = getLastVisiblePosition(); - if (v != null && height > 0 && changed && ((bottom - top) < height)) { - int lastTop = forceTop == Integer.MIN_VALUE ? (bottom - top) - (height - v.getTop()) - getPaddingTop() : forceTop; - forceTop = Integer.MIN_VALUE; - setSelectionFromTop(scrollTo, lastTop); - super.onLayout(changed, left, top, right, bottom); - -// post(new Runnable() { -// @Override -// public void run() { -// try { -// setSelectionFromTop(scrollTo, lastTop); -// } catch (Exception e) { -// e.printStackTrace(); -// } -// } -// }); - } else { - if (forceTop != Integer.MIN_VALUE) { - setSelectionFromTop(scrollTo, forceTop); - forceTop = Integer.MIN_VALUE; - } - try { - super.onLayout(changed, left, top, right, bottom); - } catch (Exception e) { - e.printStackTrace(); - } - } - height = (bottom - top); - } -} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/MapPlaceholderDrawable.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/MapPlaceholderDrawable.java new file mode 100644 index 000000000..7611268df --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/MapPlaceholderDrawable.java @@ -0,0 +1,72 @@ +/* + * This is the source code of Telegram for Android v. 2.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-2015. + */ + +package org.telegram.ui.Components; + +import android.graphics.Canvas; +import android.graphics.ColorFilter; +import android.graphics.Paint; +import android.graphics.drawable.Drawable; + +import org.telegram.android.AndroidUtilities; + +public class MapPlaceholderDrawable extends Drawable { + + private Paint paint; + private Paint linePaint; + + public MapPlaceholderDrawable() { + super(); + paint = new Paint(); + paint.setColor(0xffded7d6); + linePaint = new Paint(); + linePaint.setColor(0xffc6bfbe); + linePaint.setStrokeWidth(AndroidUtilities.dp(1)); + } + + @Override + public void draw(Canvas canvas) { + canvas.drawRect(getBounds(), paint); + int gap = AndroidUtilities.dp(9); + int xcount = getBounds().width() / gap; + int ycount = getBounds().height() / gap; + int x = getBounds().left; + int y = getBounds().top; + for (int a = 0; a < xcount; a++) { + canvas.drawLine(x + gap * (a + 1), y, x + gap * (a + 1), y + getBounds().height(), linePaint); + } + for (int a = 0; a < ycount; a++) { + canvas.drawLine(x, y + gap * (a + 1), x + getBounds().width(), y + gap * (a + 1), linePaint); + } + } + + @Override + public void setAlpha(int alpha) { + + } + + @Override + public void setColorFilter(ColorFilter cf) { + + } + + @Override + public int getOpacity() { + return 0; + } + + @Override + public int getIntrinsicWidth() { + return 0; + } + + @Override + public int getIntrinsicHeight() { + return 0; + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/PagerSlidingTabStrip.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/PagerSlidingTabStrip.java index 33b34963e..6321efc63 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/PagerSlidingTabStrip.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/PagerSlidingTabStrip.java @@ -8,32 +8,22 @@ package org.telegram.ui.Components; -import java.util.Locale; - -import android.annotation.SuppressLint; import android.content.Context; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Paint.Style; -import android.graphics.Typeface; import android.os.Build; -import android.os.Parcel; -import android.os.Parcelable; import android.support.v4.view.ViewPager; import android.support.v4.view.ViewPager.OnPageChangeListener; -import android.util.AttributeSet; import android.util.DisplayMetrics; -import android.util.TypedValue; -import android.view.Gravity; import android.view.View; import android.view.ViewTreeObserver.OnGlobalLayoutListener; import android.widget.FrameLayout; import android.widget.HorizontalScrollView; -import android.widget.ImageButton; +import android.widget.ImageView; import android.widget.LinearLayout; -import android.widget.TextView; -import org.telegram.messenger.R; +import org.telegram.android.AndroidUtilities; public class PagerSlidingTabStrip extends HorizontalScrollView { @@ -56,77 +46,45 @@ public class PagerSlidingTabStrip extends HorizontalScrollView { private Paint rectPaint; - private int indicatorColor = 0xFF666666; - private int underlineColor = 0x1A000000; + private int indicatorColor = 0xff666666; + private int underlineColor = 0x1a000000; private boolean shouldExpand = false; - private boolean textAllCaps = true; - private int scrollOffset = 52; - private int indicatorHeight = 8; - private int underlineHeight = 2; - private int dividerPadding = 12; - private int tabPadding = 24; - - private int tabTextSize = 12; - private int tabTextColor = 0xFF666666; - private Typeface tabTypeface = null; - private int tabTypefaceStyle = Typeface.BOLD; + private int scrollOffset = AndroidUtilities.dp(52); + private int indicatorHeight = AndroidUtilities.dp(8); + private int underlineHeight = AndroidUtilities.dp(2); + private int dividerPadding = AndroidUtilities.dp(12); + private int tabPadding = AndroidUtilities.dp(24); private int lastScrollX = 0; - private int tabBackgroundResId = R.drawable.background_tab; - - private Locale locale; - public PagerSlidingTabStrip(Context context) { - this(context, null); - } - - public PagerSlidingTabStrip(Context context, AttributeSet attrs) { - this(context, attrs, 0); - } - - public PagerSlidingTabStrip(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); + super(context); setFillViewport(true); setWillNotDraw(false); tabsContainer = new LinearLayout(context); tabsContainer.setOrientation(LinearLayout.HORIZONTAL); - tabsContainer.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)); + tabsContainer.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutHelper.MATCH_PARENT)); addView(tabsContainer); DisplayMetrics dm = getResources().getDisplayMetrics(); - scrollOffset = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, scrollOffset, dm); - indicatorHeight = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, indicatorHeight, dm); - underlineHeight = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, underlineHeight, dm); - dividerPadding = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dividerPadding, dm); - tabPadding = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, tabPadding, dm); - tabTextSize = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, tabTextSize, dm); - rectPaint = new Paint(); rectPaint.setAntiAlias(true); rectPaint.setStyle(Style.FILL); - defaultTabLayoutParams = new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT); - - if (locale == null) { - locale = getResources().getConfiguration().locale; - } + defaultTabLayoutParams = new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutHelper.MATCH_PARENT); } public void setViewPager(ViewPager pager) { this.pager = pager; - if (pager.getAdapter() == null) { throw new IllegalStateException("ViewPager does not have adapter instance."); } - pager.setOnPageChangeListener(pageListener); - notifyDataSetChanged(); } @@ -135,128 +93,62 @@ public class PagerSlidingTabStrip extends HorizontalScrollView { } public void notifyDataSetChanged() { - tabsContainer.removeAllViews(); - tabCount = pager.getAdapter().getCount(); - for (int i = 0; i < tabCount; i++) { - if (pager.getAdapter() instanceof IconTabProvider) { addIconTab(i, ((IconTabProvider) pager.getAdapter()).getPageIconResId(i)); - } else { - addTextTab(i, pager.getAdapter().getPageTitle(i).toString()); } - } - updateTabStyles(); - getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() { - - @SuppressWarnings("deprecation") - @SuppressLint("NewApi") @Override public void onGlobalLayout() { - if (Build.VERSION.SDK_INT < 16) { getViewTreeObserver().removeGlobalOnLayoutListener(this); } else { getViewTreeObserver().removeOnGlobalLayoutListener(this); } - currentPosition = pager.getCurrentItem(); scrollToChild(currentPosition, 0); } }); - - updateExpanded(); - - } - - private void addTextTab(final int position, String title) { - - TextView tab = new TextView(getContext()); - tab.setText(title); - tab.setFocusable(true); - tab.setGravity(Gravity.CENTER); - tab.setSingleLine(); - - tab.setOnClickListener(new OnClickListener() { - @Override - public void onClick(View v) { - pager.setCurrentItem(position); - } - }); - - tabsContainer.addView(tab); - } private void addIconTab(final int position, int resId) { - - ImageButton tab = new ImageButton(getContext()); + ImageView tab = new ImageView(getContext()); tab.setFocusable(true); tab.setImageResource(resId); - + tab.setScaleType(ImageView.ScaleType.CENTER); tab.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { pager.setCurrentItem(position); } }); - tabsContainer.addView(tab); tab.setSelected(position == currentPosition); } - private void updateExpanded() { - - } - private void updateTabStyles() { - for (int i = 0; i < tabCount; i++) { - View v = tabsContainer.getChildAt(i); - v.setLayoutParams(defaultTabLayoutParams); - v.setBackgroundResource(tabBackgroundResId); if (shouldExpand) { v.setPadding(0, 0, 0, 0); v.setLayoutParams(new LinearLayout.LayoutParams(-1, -1, 1.0F)); } else { v.setPadding(tabPadding, 0, tabPadding, 0); } - - if (v instanceof TextView) { - - TextView tab = (TextView) v; - tab.setTextSize(TypedValue.COMPLEX_UNIT_PX, tabTextSize); - tab.setTypeface(tabTypeface, tabTypefaceStyle); - tab.setTextColor(tabTextColor); - - // setAllCaps() is only available from API 14, so the upper case is made manually if we are on a - // pre-ICS-build - if (textAllCaps) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) { - tab.setAllCaps(true); - } else { - tab.setText(tab.getText().toString().toUpperCase(locale)); - } - } - } } } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); - if (!shouldExpand || MeasureSpec.getMode(widthMeasureSpec) == MeasureSpec.UNSPECIFIED) { return; } - int myWidth = getMeasuredWidth(); tabsContainer.measure(MeasureSpec.EXACTLY | myWidth, heightMeasureSpec); } @@ -265,13 +157,10 @@ public class PagerSlidingTabStrip extends HorizontalScrollView { if (tabCount == 0) { return; } - int newScrollX = tabsContainer.getChildAt(position).getLeft() + offset; - if (position > 0 || offset > 0) { newScrollX -= scrollOffset; } - if (newScrollX != lastScrollX) { lastScrollX = newScrollX; scrollTo(newScrollX, 0); @@ -288,9 +177,9 @@ public class PagerSlidingTabStrip extends HorizontalScrollView { final int height = getHeight(); - // draw indicator line - - rectPaint.setColor(indicatorColor); + // draw underline + rectPaint.setColor(underlineColor); + canvas.drawRect(0, height - underlineHeight, tabsContainer.getWidth(), height, rectPaint); // default: line below current tab View currentTab = tabsContainer.getChildAt(currentPosition); @@ -308,26 +197,19 @@ public class PagerSlidingTabStrip extends HorizontalScrollView { lineRight = (currentPositionOffset * nextTabRight + (1f - currentPositionOffset) * lineRight); } + // draw indicator line + rectPaint.setColor(indicatorColor); canvas.drawRect(lineLeft, height - indicatorHeight, lineRight, height, rectPaint); - - // draw underline - - rectPaint.setColor(underlineColor); - canvas.drawRect(0, height - underlineHeight, tabsContainer.getWidth(), height, rectPaint); } private class PageListener implements OnPageChangeListener { @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { - currentPosition = position; currentPositionOffset = positionOffset; - scrollToChild(position, (int) (positionOffset * tabsContainer.getChildAt(position).getWidth())); - invalidate(); - if (delegatePageListener != null) { delegatePageListener.onPageScrolled(position, positionOffset, positionOffsetPixels); } @@ -338,7 +220,6 @@ public class PagerSlidingTabStrip extends HorizontalScrollView { if (state == ViewPager.SCROLL_STATE_IDLE) { scrollToChild(pager.getCurrentItem(), 0); } - if (delegatePageListener != null) { delegatePageListener.onPageScrollStateChanged(state); } @@ -353,7 +234,6 @@ public class PagerSlidingTabStrip extends HorizontalScrollView { tabsContainer.getChildAt(a).setSelected(a == position); } } - } public void onSizeChanged(int paramInt1, int paramInt2, int paramInt3, int paramInt4) { @@ -441,51 +321,6 @@ public class PagerSlidingTabStrip extends HorizontalScrollView { return shouldExpand; } - public boolean isTextAllCaps() { - return textAllCaps; - } - - public void setAllCaps(boolean textAllCaps) { - this.textAllCaps = textAllCaps; - } - - public void setTextSize(int textSizePx) { - this.tabTextSize = textSizePx; - updateTabStyles(); - } - - public int getTextSize() { - return tabTextSize; - } - - public void setTextColor(int textColor) { - this.tabTextColor = textColor; - updateTabStyles(); - } - - public void setTextColorResource(int resId) { - this.tabTextColor = getResources().getColor(resId); - updateTabStyles(); - } - - public int getTextColor() { - return tabTextColor; - } - - public void setTypeface(Typeface typeface, int style) { - this.tabTypeface = typeface; - this.tabTypefaceStyle = style; - updateTabStyles(); - } - - public void setTabBackground(int resId) { - this.tabBackgroundResId = resId; - } - - public int getTabBackground() { - return tabBackgroundResId; - } - public void setTabPaddingLeftRight(int paddingPx) { this.tabPadding = paddingPx; updateTabStyles(); @@ -494,51 +329,4 @@ public class PagerSlidingTabStrip extends HorizontalScrollView { public int getTabPaddingLeftRight() { return tabPadding; } - - @Override - public void onRestoreInstanceState(Parcelable state) { - SavedState savedState = (SavedState) state; - super.onRestoreInstanceState(savedState.getSuperState()); - currentPosition = savedState.currentPosition; - requestLayout(); - } - - @Override - public Parcelable onSaveInstanceState() { - Parcelable superState = super.onSaveInstanceState(); - SavedState savedState = new SavedState(superState); - savedState.currentPosition = currentPosition; - return savedState; - } - - static class SavedState extends BaseSavedState { - int currentPosition; - - public SavedState(Parcelable superState) { - super(superState); - } - - private SavedState(Parcel in) { - super(in); - currentPosition = in.readInt(); - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - super.writeToParcel(dest, flags); - dest.writeInt(currentPosition); - } - - public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { - @Override - public SavedState createFromParcel(Parcel in) { - return new SavedState(in); - } - - @Override - public SavedState[] newArray(int size) { - return new SavedState[size]; - } - }; - } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/PasscodeView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/PasscodeView.java index e314602c3..b5898e18c 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/PasscodeView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/PasscodeView.java @@ -38,7 +38,6 @@ import android.view.inputmethod.EditorInfo; import android.widget.EditText; import android.widget.FrameLayout; import android.widget.ImageView; -import android.widget.LinearLayout; import android.widget.TextView; import org.telegram.android.AndroidUtilities; @@ -49,10 +48,10 @@ import org.telegram.messenger.FileLog; import org.telegram.messenger.R; import org.telegram.messenger.UserConfig; import org.telegram.messenger.Utilities; -import org.telegram.ui.AnimationCompat.AnimatorListenerAdapterProxy; -import org.telegram.ui.AnimationCompat.AnimatorSetProxy; -import org.telegram.ui.AnimationCompat.ObjectAnimatorProxy; -import org.telegram.ui.AnimationCompat.ViewProxy; +import org.telegram.android.AnimationCompat.AnimatorListenerAdapterProxy; +import org.telegram.android.AnimationCompat.AnimatorSetProxy; +import org.telegram.android.AnimationCompat.ObjectAnimatorProxy; +import org.telegram.android.AnimationCompat.ViewProxy; import java.util.ArrayList; import java.util.Locale; @@ -424,15 +423,15 @@ public class PasscodeView extends FrameLayout { backgroundFrameLayout = new FrameLayout(context); addView(backgroundFrameLayout); LayoutParams layoutParams = (LayoutParams) backgroundFrameLayout.getLayoutParams(); - layoutParams.width = LayoutParams.MATCH_PARENT; - layoutParams.height = LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; + layoutParams.height = LayoutHelper.MATCH_PARENT; backgroundFrameLayout.setLayoutParams(layoutParams); passwordFrameLayout = new FrameLayout(context); addView(passwordFrameLayout); layoutParams = (LayoutParams) passwordFrameLayout.getLayoutParams(); - layoutParams.width = LayoutParams.MATCH_PARENT; - layoutParams.height = LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; + layoutParams.height = LayoutHelper.MATCH_PARENT; layoutParams.gravity = Gravity.TOP | Gravity.LEFT; passwordFrameLayout.setLayoutParams(layoutParams); @@ -458,8 +457,8 @@ public class PasscodeView extends FrameLayout { passcodeTextView.setGravity(Gravity.CENTER_HORIZONTAL); passwordFrameLayout.addView(passcodeTextView); layoutParams = (LayoutParams) passcodeTextView.getLayoutParams(); - layoutParams.width = LayoutParams.WRAP_CONTENT; - layoutParams.height = LayoutParams.WRAP_CONTENT; + layoutParams.width = LayoutHelper.WRAP_CONTENT; + layoutParams.height = LayoutHelper.WRAP_CONTENT; layoutParams.bottomMargin = AndroidUtilities.dp(62); layoutParams.gravity = Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL; passcodeTextView.setLayoutParams(layoutParams); @@ -467,8 +466,8 @@ public class PasscodeView extends FrameLayout { passwordEditText2 = new AnimatingTextView(context); passwordFrameLayout.addView(passwordEditText2); layoutParams = (FrameLayout.LayoutParams) passwordEditText2.getLayoutParams(); - layoutParams.height = LinearLayout.LayoutParams.WRAP_CONTENT; - layoutParams.width = LinearLayout.LayoutParams.MATCH_PARENT; + layoutParams.height = LayoutHelper.WRAP_CONTENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; layoutParams.leftMargin = AndroidUtilities.dp(70); layoutParams.rightMargin = AndroidUtilities.dp(70); layoutParams.bottomMargin = AndroidUtilities.dp(6); @@ -488,8 +487,8 @@ public class PasscodeView extends FrameLayout { AndroidUtilities.clearCursorDrawable(passwordEditText); passwordFrameLayout.addView(passwordEditText); layoutParams = (FrameLayout.LayoutParams) passwordEditText.getLayoutParams(); - layoutParams.height = LinearLayout.LayoutParams.WRAP_CONTENT; - layoutParams.width = LinearLayout.LayoutParams.MATCH_PARENT; + layoutParams.height = LayoutHelper.WRAP_CONTENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; layoutParams.leftMargin = AndroidUtilities.dp(70); layoutParams.rightMargin = AndroidUtilities.dp(70); layoutParams.bottomMargin = AndroidUtilities.dp(6); @@ -571,7 +570,7 @@ public class PasscodeView extends FrameLayout { lineFrameLayout.setBackgroundColor(0x26ffffff); passwordFrameLayout.addView(lineFrameLayout); layoutParams = (LayoutParams) lineFrameLayout.getLayoutParams(); - layoutParams.width = LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; layoutParams.height = AndroidUtilities.dp(1); layoutParams.gravity = Gravity.BOTTOM | Gravity.LEFT; layoutParams.leftMargin = AndroidUtilities.dp(20); @@ -581,8 +580,8 @@ public class PasscodeView extends FrameLayout { numbersFrameLayout = new FrameLayout(context); addView(numbersFrameLayout); layoutParams = (LayoutParams) numbersFrameLayout.getLayoutParams(); - layoutParams.width = LayoutParams.MATCH_PARENT; - layoutParams.height = LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; + layoutParams.height = LayoutHelper.MATCH_PARENT; layoutParams.gravity = Gravity.TOP | Gravity.LEFT; numbersFrameLayout.setLayoutParams(layoutParams); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/PhotoFilterView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/PhotoFilterView.java index da9ac0662..319286ca5 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/PhotoFilterView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/PhotoFilterView.java @@ -18,8 +18,6 @@ import android.opengl.GLES20; import android.opengl.GLUtils; import android.os.Build; import android.os.Looper; -import android.support.v7.widget.LinearLayoutManager; -import android.support.v7.widget.RecyclerView; import android.util.TypedValue; import android.view.Gravity; import android.view.MotionEvent; @@ -32,14 +30,16 @@ import android.widget.TextView; import org.telegram.android.AndroidUtilities; import org.telegram.android.LocaleController; +import org.telegram.android.support.widget.LinearLayoutManager; +import org.telegram.android.support.widget.RecyclerView; import org.telegram.messenger.DispatchQueue; import org.telegram.messenger.FileLog; import org.telegram.messenger.R; import org.telegram.messenger.Utilities; -import org.telegram.ui.AnimationCompat.AnimatorListenerAdapterProxy; -import org.telegram.ui.AnimationCompat.AnimatorSetProxy; -import org.telegram.ui.AnimationCompat.ObjectAnimatorProxy; -import org.telegram.ui.AnimationCompat.ViewProxy; +import org.telegram.android.AnimationCompat.AnimatorListenerAdapterProxy; +import org.telegram.android.AnimationCompat.AnimatorSetProxy; +import org.telegram.android.AnimationCompat.ObjectAnimatorProxy; +import org.telegram.android.AnimationCompat.ViewProxy; import org.telegram.ui.Cells.PhotoEditToolCell; import java.nio.ByteBuffer; @@ -1333,8 +1333,8 @@ public class PhotoFilterView extends FrameLayout { addView(textureView); textureView.setVisibility(INVISIBLE); LayoutParams layoutParams = (LayoutParams) textureView.getLayoutParams(); - layoutParams.width = FrameLayout.LayoutParams.MATCH_PARENT; - layoutParams.height = FrameLayout.LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; + layoutParams.height = LayoutHelper.MATCH_PARENT; layoutParams.gravity = Gravity.TOP | Gravity.LEFT; textureView.setLayoutParams(layoutParams); textureView.setSurfaceTextureListener(new TextureView.SurfaceTextureListener() { @@ -1380,8 +1380,8 @@ public class PhotoFilterView extends FrameLayout { blurControl.setVisibility(INVISIBLE); addView(blurControl); layoutParams = (LayoutParams) blurControl.getLayoutParams(); - layoutParams.width = LayoutParams.MATCH_PARENT; - layoutParams.height = LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; + layoutParams.height = LayoutHelper.MATCH_PARENT; layoutParams.gravity = Gravity.LEFT | Gravity.TOP; blurControl.setLayoutParams(layoutParams); blurControl.setDelegate(new PhotoFilterBlurControl.PhotoFilterLinearBlurControlDelegate() { @@ -1400,7 +1400,7 @@ public class PhotoFilterView extends FrameLayout { toolsView = new FrameLayout(context); addView(toolsView); layoutParams = (LayoutParams) toolsView.getLayoutParams(); - layoutParams.width = LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; layoutParams.height = AndroidUtilities.dp(126); layoutParams.gravity = Gravity.LEFT | Gravity.BOTTOM; toolsView.setLayoutParams(layoutParams); @@ -1409,7 +1409,7 @@ public class PhotoFilterView extends FrameLayout { frameLayout.setBackgroundColor(0xff1a1a1a); toolsView.addView(frameLayout); layoutParams = (LayoutParams) frameLayout.getLayoutParams(); - layoutParams.width = LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; layoutParams.height = AndroidUtilities.dp(48); layoutParams.gravity = Gravity.BOTTOM | Gravity.LEFT; frameLayout.setLayoutParams(layoutParams); @@ -1424,8 +1424,8 @@ public class PhotoFilterView extends FrameLayout { cancelTextView.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); frameLayout.addView(cancelTextView); layoutParams = (LayoutParams) cancelTextView.getLayoutParams(); - layoutParams.width = LayoutParams.WRAP_CONTENT; - layoutParams.height = LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.WRAP_CONTENT; + layoutParams.height = LayoutHelper.MATCH_PARENT; layoutParams.gravity = Gravity.TOP | Gravity.LEFT; cancelTextView.setLayoutParams(layoutParams); @@ -1439,8 +1439,8 @@ public class PhotoFilterView extends FrameLayout { doneTextView.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); frameLayout.addView(doneTextView); layoutParams = (LayoutParams) doneTextView.getLayoutParams(); - layoutParams.width = LayoutParams.WRAP_CONTENT; - layoutParams.height = LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.WRAP_CONTENT; + layoutParams.height = LayoutHelper.MATCH_PARENT; layoutParams.gravity = Gravity.TOP | Gravity.RIGHT; doneTextView.setLayoutParams(layoutParams); @@ -1455,7 +1455,7 @@ public class PhotoFilterView extends FrameLayout { recyclerListView.setAdapter(toolsAdapter = new ToolsAdapter(context)); toolsView.addView(recyclerListView); layoutParams = (FrameLayout.LayoutParams) recyclerListView.getLayoutParams(); - layoutParams.width = FrameLayout.LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; layoutParams.height = AndroidUtilities.dp(60); layoutParams.gravity = Gravity.LEFT | Gravity.TOP; recyclerListView.setLayoutParams(layoutParams); @@ -1516,7 +1516,7 @@ public class PhotoFilterView extends FrameLayout { editView.setVisibility(GONE); addView(editView); layoutParams = (LayoutParams) editView.getLayoutParams(); - layoutParams.width = LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; layoutParams.height = AndroidUtilities.dp(126); layoutParams.gravity = Gravity.LEFT | Gravity.BOTTOM; editView.setLayoutParams(layoutParams); @@ -1525,7 +1525,7 @@ public class PhotoFilterView extends FrameLayout { frameLayout.setBackgroundColor(0xff1a1a1a); editView.addView(frameLayout); layoutParams = (LayoutParams) frameLayout.getLayoutParams(); - layoutParams.width = LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; layoutParams.height = AndroidUtilities.dp(48); layoutParams.gravity = Gravity.BOTTOM | Gravity.LEFT; frameLayout.setLayoutParams(layoutParams); @@ -1536,8 +1536,8 @@ public class PhotoFilterView extends FrameLayout { imageView.setPadding(AndroidUtilities.dp(22), 0, AndroidUtilities.dp(22), 0); frameLayout.addView(imageView); layoutParams = (LayoutParams) imageView.getLayoutParams(); - layoutParams.width = LayoutParams.WRAP_CONTENT; - layoutParams.height = LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.WRAP_CONTENT; + layoutParams.height = LayoutHelper.MATCH_PARENT; layoutParams.gravity = Gravity.TOP | Gravity.LEFT; imageView.setLayoutParams(layoutParams); imageView.setOnClickListener(new OnClickListener() { @@ -1579,8 +1579,8 @@ public class PhotoFilterView extends FrameLayout { imageView.setPadding(AndroidUtilities.dp(22), AndroidUtilities.dp(1), AndroidUtilities.dp(22), 0); frameLayout.addView(imageView); layoutParams = (LayoutParams) imageView.getLayoutParams(); - layoutParams.width = LayoutParams.WRAP_CONTENT; - layoutParams.height = LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.WRAP_CONTENT; + layoutParams.height = LayoutHelper.MATCH_PARENT; layoutParams.gravity = Gravity.TOP | Gravity.RIGHT; imageView.setLayoutParams(layoutParams); imageView.setOnClickListener(new OnClickListener() { @@ -1597,8 +1597,8 @@ public class PhotoFilterView extends FrameLayout { blurTextView.setText(LocaleController.getString("Blur", R.string.Blur)); frameLayout.addView(blurTextView); layoutParams = (LayoutParams) blurTextView.getLayoutParams(); - layoutParams.width = LayoutParams.WRAP_CONTENT; - layoutParams.height = LayoutParams.WRAP_CONTENT; + layoutParams.width = LayoutHelper.WRAP_CONTENT; + layoutParams.height = LayoutHelper.WRAP_CONTENT; layoutParams.gravity = Gravity.CENTER_HORIZONTAL; layoutParams.topMargin = AndroidUtilities.dp(9); blurTextView.setLayoutParams(layoutParams); @@ -1608,8 +1608,8 @@ public class PhotoFilterView extends FrameLayout { paramTextView.setTextColor(0xff808080); frameLayout.addView(paramTextView); layoutParams = (LayoutParams) paramTextView.getLayoutParams(); - layoutParams.width = LayoutParams.WRAP_CONTENT; - layoutParams.height = LayoutParams.WRAP_CONTENT; + layoutParams.width = LayoutHelper.WRAP_CONTENT; + layoutParams.height = LayoutHelper.WRAP_CONTENT; layoutParams.gravity = Gravity.CENTER_HORIZONTAL; layoutParams.topMargin = AndroidUtilities.dp(26); paramTextView.setLayoutParams(layoutParams); @@ -1619,8 +1619,8 @@ public class PhotoFilterView extends FrameLayout { valueTextView.setTextColor(0xffffffff); frameLayout.addView(valueTextView); layoutParams = (LayoutParams) valueTextView.getLayoutParams(); - layoutParams.width = LayoutParams.WRAP_CONTENT; - layoutParams.height = LayoutParams.WRAP_CONTENT; + layoutParams.width = LayoutHelper.WRAP_CONTENT; + layoutParams.height = LayoutHelper.WRAP_CONTENT; layoutParams.gravity = Gravity.CENTER_HORIZONTAL; layoutParams.topMargin = AndroidUtilities.dp(3); valueTextView.setLayoutParams(layoutParams); @@ -1667,7 +1667,7 @@ public class PhotoFilterView extends FrameLayout { layoutParams.width = AndroidUtilities.dp(498); layoutParams.gravity = Gravity.CENTER_HORIZONTAL | Gravity.TOP; } else { - layoutParams.width = FrameLayout.LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; layoutParams.gravity = Gravity.LEFT | Gravity.TOP; } valueSeekBar.setLayoutParams(layoutParams); @@ -1966,7 +1966,7 @@ public class PhotoFilterView extends FrameLayout { layoutParams.width = total; layoutParams.leftMargin = (viewWidth - total) / 2; } else { - layoutParams.width = FrameLayout.LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; layoutParams.leftMargin = 0; } recyclerListView.setLayoutParams(layoutParams); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/PhotoPickerBottomLayout.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/PhotoPickerBottomLayout.java index fb6db0a09..67c72c127 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/PhotoPickerBottomLayout.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/PhotoPickerBottomLayout.java @@ -41,8 +41,8 @@ public class PhotoPickerBottomLayout extends FrameLayout { cancelButton.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); addView(cancelButton); LayoutParams layoutParams = (LayoutParams) cancelButton.getLayoutParams(); - layoutParams.width = LayoutParams.WRAP_CONTENT; - layoutParams.height = LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.WRAP_CONTENT; + layoutParams.height = LayoutHelper.MATCH_PARENT; layoutParams.gravity = Gravity.TOP | Gravity.LEFT; cancelButton.setLayoutParams(layoutParams); @@ -52,8 +52,8 @@ public class PhotoPickerBottomLayout extends FrameLayout { doneButton.setPadding(AndroidUtilities.dp(29), 0, AndroidUtilities.dp(29), 0); addView(doneButton); layoutParams = (LayoutParams) doneButton.getLayoutParams(); - layoutParams.width = LayoutParams.WRAP_CONTENT; - layoutParams.height = LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.WRAP_CONTENT; + layoutParams.height = LayoutHelper.MATCH_PARENT; layoutParams.gravity = Gravity.TOP | Gravity.RIGHT; doneButton.setLayoutParams(layoutParams); @@ -67,7 +67,7 @@ public class PhotoPickerBottomLayout extends FrameLayout { doneButtonBadgeTextView.setPadding(AndroidUtilities.dp(8), 0, AndroidUtilities.dp(8), AndroidUtilities.dp(1)); doneButton.addView(doneButtonBadgeTextView); LinearLayout.LayoutParams layoutParams1 = (LinearLayout.LayoutParams) doneButtonBadgeTextView.getLayoutParams(); - layoutParams1.width = LayoutParams.WRAP_CONTENT; + layoutParams1.width = LayoutHelper.WRAP_CONTENT; layoutParams1.height = AndroidUtilities.dp(23); layoutParams1.rightMargin = AndroidUtilities.dp(10); layoutParams1.gravity = Gravity.CENTER_VERTICAL; @@ -82,9 +82,9 @@ public class PhotoPickerBottomLayout extends FrameLayout { doneButtonTextView.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); doneButton.addView(doneButtonTextView); layoutParams1 = (LinearLayout.LayoutParams) doneButtonTextView.getLayoutParams(); - layoutParams1.width = LayoutParams.WRAP_CONTENT; + layoutParams1.width = LayoutHelper.WRAP_CONTENT; layoutParams1.gravity = Gravity.CENTER_VERTICAL; - layoutParams1.height = LayoutParams.WRAP_CONTENT; + layoutParams1.height = LayoutHelper.WRAP_CONTENT; doneButtonTextView.setLayoutParams(layoutParams1); } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/PhotoViewerCaptionEnterView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/PhotoViewerCaptionEnterView.java new file mode 100644 index 000000000..0bf595a75 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/PhotoViewerCaptionEnterView.java @@ -0,0 +1,610 @@ +/* + * This is the source code of Telegram for Android v. 2.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-2015. + */ + +package org.telegram.ui.Components; + +import android.app.Activity; +import android.content.Context; +import android.text.Editable; +import android.text.InputFilter; +import android.text.InputType; +import android.text.TextWatcher; +import android.text.style.ImageSpan; +import android.util.TypedValue; +import android.view.Gravity; +import android.view.KeyEvent; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewTreeObserver; +import android.view.WindowManager; +import android.view.inputmethod.EditorInfo; +import android.widget.EditText; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.PopupWindow; +import android.widget.RelativeLayout; +import android.widget.TextView; + +import org.telegram.android.AndroidUtilities; +import org.telegram.android.Emoji; +import org.telegram.android.LocaleController; +import org.telegram.android.NotificationCenter; +import org.telegram.messenger.ApplicationLoader; +import org.telegram.messenger.FileLog; +import org.telegram.messenger.R; +import org.telegram.messenger.TLRPC; +import org.telegram.android.AnimationCompat.AnimatorSetProxy; +import org.telegram.android.AnimationCompat.ObjectAnimatorProxy; + +public class PhotoViewerCaptionEnterView extends FrameLayoutFixed implements NotificationCenter.NotificationCenterDelegate, SizeNotifierRelativeLayoutPhoto.SizeNotifierRelativeLayoutPhotoDelegate { + + public interface PhotoViewerCaptionEnterViewDelegate { + void onCaptionEnter(); + void onTextChanged(CharSequence text, boolean bigChange); + void onWindowSizeChanged(int size); + } + + private EditText messageEditText; + private PopupWindow emojiPopup; + private ImageView emojiButton; + private EmojiView emojiView; + private SizeNotifierRelativeLayoutPhoto sizeNotifierFrameLayout; + + private int framesDroped; + + private int keyboardTransitionState; + private boolean showKeyboardOnEmojiButton; + private ViewTreeObserver.OnPreDrawListener onPreDrawListener; + + private AnimatorSetProxy runningAnimation; + private AnimatorSetProxy runningAnimation2; + private ObjectAnimatorProxy runningAnimationAudio; + private int runningAnimationType; + private int audioInterfaceState; + + private int keyboardHeight; + private int keyboardHeightLand; + private boolean keyboardVisible; + + private View window; + private PhotoViewerCaptionEnterViewDelegate delegate; + private boolean wasFocus; + + public PhotoViewerCaptionEnterView(Context context, View windowView, SizeNotifierRelativeLayoutPhoto parent) { + super(context); + setBackgroundColor(0x7f000000); + setFocusable(true); + setFocusableInTouchMode(true); + + window = windowView; + sizeNotifierFrameLayout = parent; + + LinearLayout textFieldContainer = new LinearLayout(context); + textFieldContainer.setOrientation(LinearLayout.HORIZONTAL); + addView(textFieldContainer, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.LEFT | Gravity.TOP, 2, 0, 0, 0)); + + FrameLayoutFixed frameLayout = new FrameLayoutFixed(context); + textFieldContainer.addView(frameLayout, LayoutHelper.createLinear(0, LayoutHelper.WRAP_CONTENT, 1.0f)); + + emojiButton = new ImageView(context); + emojiButton.setImageResource(R.drawable.ic_smile_w); + 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 | Gravity.LEFT)); + emojiButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + if (showKeyboardOnEmojiButton) { + setKeyboardTransitionState(1); + showEmojiPopup(false, false); + int selection = messageEditText.getSelectionStart(); + MotionEvent event = MotionEvent.obtain(0, 0, MotionEvent.ACTION_UP, 0, 0, 0); + messageEditText.onTouchEvent(event); + event.recycle(); + messageEditText.setSelection(selection); + } else { + boolean show = emojiPopup == null || !emojiPopup.isShowing(); + if (show) { + setKeyboardTransitionState(5); + showEmojiPopup(show, true); + } else { + showEmojiPopup(show, true); + setKeyboardTransitionState(1); + } + } + } + }); + + messageEditText = new EditText(context); + messageEditText.setHint(LocaleController.getString("AddCaption", R.string.AddCaption)); + messageEditText.setImeOptions(EditorInfo.IME_FLAG_NO_EXTRACT_UI | EditorInfo.IME_ACTION_DONE); + messageEditText.setInputType(EditorInfo.TYPE_TEXT_FLAG_CAP_SENTENCES | EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE | InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS); + messageEditText.setSingleLine(false); + messageEditText.setMaxLines(4); + messageEditText.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 18); + messageEditText.setGravity(Gravity.BOTTOM); + messageEditText.setPadding(0, AndroidUtilities.dp(11), 0, AndroidUtilities.dp(12)); + messageEditText.setBackgroundDrawable(null); + AndroidUtilities.clearCursorDrawable(messageEditText); + messageEditText.setTextColor(0xffffffff); + messageEditText.setHintTextColor(0xb2ffffff); + InputFilter[] inputFilters = new InputFilter[1]; + inputFilters[0] = new InputFilter.LengthFilter(140); + messageEditText.setFilters(inputFilters); + frameLayout.addView(messageEditText, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.LEFT | Gravity.BOTTOM, 52, 0, 6, 0)); + messageEditText.setOnKeyListener(new OnKeyListener() { + @Override + public boolean onKey(View view, int i, KeyEvent keyEvent) { + if (i == 4 && !keyboardVisible && emojiPopup != null && emojiPopup.isShowing()) { + if (keyEvent.getAction() == 1) { + showEmojiPopup(false, true); + } + return true; + } else if (i == KeyEvent.KEYCODE_ENTER && keyEvent.getAction() == KeyEvent.ACTION_DOWN) { + delegate.onCaptionEnter(); + return true; + } + return false; + } + }); + messageEditText.setOnFocusChangeListener(new OnFocusChangeListener() { + @Override + public void onFocusChange(View v, boolean hasFocus) { + if (!wasFocus) { + setKeyboardTransitionState(3); + } + wasFocus = hasFocus; + } + }); + messageEditText.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View view) { + if (emojiPopup != null && emojiPopup.isShowing()) { + setKeyboardTransitionState(1); + showEmojiPopup(false, false); + } else { + setKeyboardTransitionState(3); + } + } + }); + messageEditText.setOnEditorActionListener(new TextView.OnEditorActionListener() { + @Override + public boolean onEditorAction(TextView textView, int i, KeyEvent keyEvent) { + if (i == EditorInfo.IME_ACTION_DONE) { + delegate.onCaptionEnter(); + return true; + } else if (keyEvent != null && i == EditorInfo.IME_NULL && keyEvent.getAction() == KeyEvent.ACTION_DOWN) { + delegate.onCaptionEnter(); + return true; + } + return false; + } + }); + messageEditText.addTextChangedListener(new TextWatcher() { + @Override + public void beforeTextChanged(CharSequence charSequence, int i, int i2, int i3) { + + } + + @Override + public void onTextChanged(CharSequence charSequence, int start, int before, int count) { + String message = getTrimmedString(charSequence.toString()); + + if (delegate != null) { + delegate.onTextChanged(charSequence, before > count || count > 2); + } + } + + @Override + public void afterTextChanged(Editable editable) { + int i = 0; + ImageSpan[] arrayOfImageSpan = editable.getSpans(0, editable.length(), ImageSpan.class); + int j = arrayOfImageSpan.length; + while (true) { + if (i >= j) { + Emoji.replaceEmoji(editable, messageEditText.getPaint().getFontMetricsInt(), AndroidUtilities.dp(20)); + return; + } + editable.removeSpan(arrayOfImageSpan[i]); + i++; + } + } + }); + } + + private void setKeyboardTransitionState(int state) { + if (AndroidUtilities.usingHardwareInput) { + if (state == 1) { + showEmojiPopup(false, false); + RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) getLayoutParams(); + layoutParams.bottomMargin = 0;//AndroidUtilities.dp(48); + setLayoutParams(layoutParams); + keyboardTransitionState = 0; + } else if (state == 2) { + int currentHeight = AndroidUtilities.displaySize.x > AndroidUtilities.displaySize.y ? keyboardHeightLand : keyboardHeight; + sizeNotifierFrameLayout.setPadding(0, 0, 0, currentHeight); + keyboardTransitionState = 0; + } else if (state == 3) { + RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) getLayoutParams(); + layoutParams.bottomMargin = 0;//AndroidUtilities.dp(48); + setLayoutParams(layoutParams); + keyboardTransitionState = 0; + } else if (state == 4) { + RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) getLayoutParams(); + layoutParams.bottomMargin = -AndroidUtilities.dp(400); + setLayoutParams(layoutParams); + keyboardTransitionState = 0; + } else if (state == 5) { + RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) getLayoutParams(); + layoutParams.bottomMargin = 0; + setLayoutParams(layoutParams); + keyboardTransitionState = 0; + } + } else { + framesDroped = 0; + keyboardTransitionState = state; + if (state == 1) { + sizeNotifierFrameLayout.setPadding(0, 0, 0, 0); + } + } + } + + public int getKeyboardTransitionState() { + return keyboardTransitionState; + } + + private void onWindowSizeChanged(int size) { + if (delegate != null) { + delegate.onWindowSizeChanged(size); + } + } + + public void onCreate() { + NotificationCenter.getInstance().addObserver(this, NotificationCenter.emojiDidLoaded); + NotificationCenter.getInstance().addObserver(this, NotificationCenter.hideEmojiKeyboard); + sizeNotifierFrameLayout.getViewTreeObserver().addOnPreDrawListener(onPreDrawListener = new ViewTreeObserver.OnPreDrawListener() { + @Override + public boolean onPreDraw() { + if (keyboardTransitionState == 1) { + if (keyboardVisible || framesDroped >= 60) { + showEmojiPopup(false, false); + keyboardTransitionState = 0; + } else { + if (messageEditText != null) { + messageEditText.requestFocus(); + AndroidUtilities.showKeyboard(messageEditText); + } + } + framesDroped++; + return false; + } else if (keyboardTransitionState == 2) { + if (!keyboardVisible || framesDroped >= 60) { + int currentHeight = AndroidUtilities.displaySize.x > AndroidUtilities.displaySize.y ? keyboardHeightLand : keyboardHeight; + sizeNotifierFrameLayout.setPadding(0, 0, 0, currentHeight); + keyboardTransitionState = 0; + } + framesDroped++; + return false; + } else if (keyboardTransitionState == 3) { + if (keyboardVisible) { + RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) getLayoutParams(); + layoutParams.bottomMargin = 0;//AndroidUtilities.usingHardwareInput ? AndroidUtilities.dp(48) : 0; + setLayoutParams(layoutParams); + keyboardTransitionState = 0; + } + } else if (keyboardTransitionState == 4) { + if (!keyboardVisible && (emojiPopup == null || !emojiPopup.isShowing())) { + RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) getLayoutParams(); + layoutParams.bottomMargin = -AndroidUtilities.dp(400); + setLayoutParams(layoutParams); + keyboardTransitionState = 0; + } + } else if (keyboardTransitionState == 5) { + if (emojiPopup != null && emojiPopup.isShowing()) { + RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) getLayoutParams(); + layoutParams.bottomMargin = 0; + setLayoutParams(layoutParams); + keyboardTransitionState = 0; + } + } + return true; + } + }); + sizeNotifierFrameLayout.setDelegate(this); + } + + public void onDestroy() { + if (isEmojiPopupShowing()) { + hideEmojiPopup(); + } + if (isKeyboardVisible()) { + closeKeyboard(); + } + keyboardVisible = false; + NotificationCenter.getInstance().removeObserver(this, NotificationCenter.emojiDidLoaded); + NotificationCenter.getInstance().removeObserver(this, NotificationCenter.hideEmojiKeyboard); + if (sizeNotifierFrameLayout != null) { + sizeNotifierFrameLayout.getViewTreeObserver().removeOnPreDrawListener(onPreDrawListener); + sizeNotifierFrameLayout.setDelegate(null); + } + } + + private String getTrimmedString(String src) { + String result = src.trim(); + if (result.length() == 0) { + return result; + } + while (src.startsWith("\n")) { + src = src.substring(1); + } + while (src.endsWith("\n")) { + src = src.substring(0, src.length() - 1); + } + return src; + } + + private void showEmojiPopup(boolean show, boolean post) { + if (show) { + if (emojiPopup == null) { + emojiView = new EmojiView(false, getContext()); + emojiView.setListener(new EmojiView.Listener() { + public boolean onBackspace() { + if (messageEditText.length() == 0) { + return false; + } + messageEditText.dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DEL)); + return true; + } + + public void onEmojiSelected(String symbol) { + int i = messageEditText.getSelectionEnd(); + if (i < 0) { + i = 0; + } + try { + CharSequence localCharSequence = Emoji.replaceEmoji(symbol, messageEditText.getPaint().getFontMetricsInt(), AndroidUtilities.dp(20)); + messageEditText.setText(messageEditText.getText().insert(i, localCharSequence)); + int j = i + localCharSequence.length(); + messageEditText.setSelection(j, j); + } catch (Exception e) { + FileLog.e("tmessages", e); + } + } + + public void onStickerSelected(TLRPC.Document sticker) { + + } + }); + emojiPopup = new PopupWindow(emojiView); + } + + if (keyboardHeight <= 0) { + keyboardHeight = ApplicationLoader.applicationContext.getSharedPreferences("emoji", 0).getInt("kbd_height", AndroidUtilities.dp(200)); + } + if (keyboardHeightLand <= 0) { + keyboardHeightLand = ApplicationLoader.applicationContext.getSharedPreferences("emoji", 0).getInt("kbd_height_land3", AndroidUtilities.dp(200)); + } + int currentHeight = AndroidUtilities.displaySize.x > AndroidUtilities.displaySize.y ? keyboardHeightLand : keyboardHeight; + FileLog.e("tmessages", "show emoji with height = " + currentHeight); + emojiPopup.setHeight(View.MeasureSpec.makeMeasureSpec(currentHeight, View.MeasureSpec.EXACTLY)); + if (sizeNotifierFrameLayout != null) { + emojiPopup.setWidth(View.MeasureSpec.makeMeasureSpec(AndroidUtilities.displaySize.x, View.MeasureSpec.EXACTLY)); + } + + emojiPopup.showAtLocation(window, Gravity.BOTTOM | Gravity.LEFT, 0, 0); + + if (!keyboardVisible) { + if (sizeNotifierFrameLayout != null) { + sizeNotifierFrameLayout.setPadding(0, 0, 0, currentHeight); + emojiButton.setImageResource(R.drawable.arrow_down_w); + showKeyboardOnEmojiButton = false; + onWindowSizeChanged(sizeNotifierFrameLayout.getHeight() - sizeNotifierFrameLayout.getPaddingBottom()); + } + return; + } else { + setKeyboardTransitionState(2); + AndroidUtilities.hideKeyboard(messageEditText); + } + emojiButton.setImageResource(R.drawable.ic_keyboard_w); + showKeyboardOnEmojiButton = true; + return; + } + if (emojiButton != null) { + showKeyboardOnEmojiButton = false; + emojiButton.setImageResource(R.drawable.ic_smile_w); + } + if (emojiPopup != null) { + try { + emojiPopup.dismiss(); + } catch (Exception e) { + //don't promt + } + } + if (keyboardTransitionState == 0) { + if (sizeNotifierFrameLayout != null) { + if (post) { + sizeNotifierFrameLayout.post(new Runnable() { + public void run() { + if (sizeNotifierFrameLayout != null) { + sizeNotifierFrameLayout.setPadding(0, 0, 0, 0); + onWindowSizeChanged(sizeNotifierFrameLayout.getHeight()); + } + } + }); + } else { + sizeNotifierFrameLayout.setPadding(0, 0, 0, 0); + onWindowSizeChanged(sizeNotifierFrameLayout.getHeight()); + } + } + } + } + + public void hideEmojiPopup() { + if (emojiPopup != null && emojiPopup.isShowing()) { + showEmojiPopup(false, true); + } + setKeyboardTransitionState(4); + } + + public void openKeyboard() { + setKeyboardTransitionState(3); + messageEditText.requestFocus(); + AndroidUtilities.showKeyboard(messageEditText); + } + + public void closeKeyboard() { + setKeyboardTransitionState(4); + AndroidUtilities.hideKeyboard(messageEditText); + } + + public void setDelegate(PhotoViewerCaptionEnterViewDelegate delegate) { + this.delegate = delegate; + } + + public void setFieldText(CharSequence text) { + if (messageEditText == null) { + return; + } + messageEditText.setText(text); + messageEditText.setSelection(messageEditText.getText().length()); + if (delegate != null) { + delegate.onTextChanged(messageEditText.getText(), true); + } + } + + public int getCursorPosition() { + if (messageEditText == null) { + return 0; + } + return messageEditText.getSelectionStart(); + } + + public void replaceWithText(int start, int len, String text) { + try { + StringBuilder builder = new StringBuilder(messageEditText.getText()); + builder.replace(start, start + len, text); + messageEditText.setText(builder); + if (start + text.length() <= messageEditText.length()) { + messageEditText.setSelection(start + text.length()); + } else { + messageEditText.setSelection(messageEditText.length()); + } + } catch (Exception e) { + FileLog.e("tmessages", e); + } + } + + public void setFieldFocused(boolean focus) { + if (messageEditText == null) { + return; + } + if (focus) { + if (!messageEditText.isFocused()) { + messageEditText.postDelayed(new Runnable() { + @Override + public void run() { + if (messageEditText != null) { + try { + messageEditText.requestFocus(); + } catch (Exception e) { + FileLog.e("tmessages", e); + } + } + } + }, 600); + } + } else { + if (messageEditText.isFocused() && !keyboardVisible) { + messageEditText.clearFocus(); + } + } + } + + public boolean hasText() { + return messageEditText != null && messageEditText.length() > 0; + } + + public String getFieldText() { + if (messageEditText != null && messageEditText.length() > 0) { + return getTrimmedString(messageEditText.getText().toString()); + } + return null; + } + + public CharSequence getFieldCharSequence() { + return messageEditText.getText(); + } + + public boolean isEmojiPopupShowing() { + return emojiPopup != null && emojiPopup.isShowing(); + } + + public boolean isKeyboardVisible() { + return AndroidUtilities.usingHardwareInput && getLayoutParams() != null && ((RelativeLayout.LayoutParams) getLayoutParams()).bottomMargin == 0 || keyboardVisible; + } + + @Override + public void onSizeChanged(int height, boolean isWidthGreater) { + if (height > AndroidUtilities.dp(50) && keyboardVisible) { + if (isWidthGreater) { + keyboardHeightLand = height; + ApplicationLoader.applicationContext.getSharedPreferences("emoji", 0).edit().putInt("kbd_height_land3", keyboardHeightLand).commit(); + } else { + keyboardHeight = height; + ApplicationLoader.applicationContext.getSharedPreferences("emoji", 0).edit().putInt("kbd_height", keyboardHeight).commit(); + } + } + + if (emojiPopup != null && emojiPopup.isShowing()) { + int newHeight = 0; + if (isWidthGreater) { + newHeight = keyboardHeightLand; + } else { + newHeight = keyboardHeight; + } + WindowManager.LayoutParams layoutParams = (WindowManager.LayoutParams) emojiPopup.getContentView().getLayoutParams(); + if (layoutParams.width != AndroidUtilities.displaySize.x || layoutParams.height != newHeight) { + layoutParams.width = AndroidUtilities.displaySize.x; + layoutParams.height = newHeight; + WindowManager wm = (WindowManager) ApplicationLoader.applicationContext.getSystemService(Activity.WINDOW_SERVICE); + if (wm != null) { + wm.updateViewLayout(emojiPopup.getContentView(), layoutParams); + if (!keyboardVisible) { + if (sizeNotifierFrameLayout != null) { + sizeNotifierFrameLayout.setPadding(0, 0, 0, layoutParams.height); + sizeNotifierFrameLayout.requestLayout(); + onWindowSizeChanged(sizeNotifierFrameLayout.getHeight() - sizeNotifierFrameLayout.getPaddingBottom()); + } + } + } + } + } + + boolean oldValue = keyboardVisible; + keyboardVisible = height > 0; + if (keyboardVisible && (sizeNotifierFrameLayout.getPaddingBottom() > 0 || keyboardTransitionState == 1)) { + setKeyboardTransitionState(1); + } else if (keyboardTransitionState != 2 && !keyboardVisible && keyboardVisible != oldValue && emojiPopup != null && emojiPopup.isShowing()) { + showEmojiPopup(false, true); + } + if (keyboardTransitionState == 0) { + onWindowSizeChanged(sizeNotifierFrameLayout.getHeight() - sizeNotifierFrameLayout.getPaddingBottom()); + } + } + + @Override + public void didReceivedNotification(int id, Object... args) { + if (id == NotificationCenter.emojiDidLoaded) { + if (emojiView != null) { + emojiView.invalidateViews(); + } + } else if (id == NotificationCenter.hideEmojiKeyboard) { + hideEmojiPopup(); + } + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/PopupAudioView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/PopupAudioView.java index 3d32ec5ef..c6f7bb038 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/PopupAudioView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/PopupAudioView.java @@ -20,6 +20,7 @@ import android.view.SoundEffectConstants; import org.telegram.android.AndroidUtilities; import org.telegram.android.ImageLoader; import org.telegram.android.MediaController; +import org.telegram.android.MessagesController; import org.telegram.messenger.FileLoader; import org.telegram.messenger.R; import org.telegram.android.MessageObject; @@ -58,23 +59,23 @@ public class PopupAudioView extends BaseCell implements SeekBar.SeekBarDelegate, super(context); if (backgroundMediaDrawableIn == null) { backgroundMediaDrawableIn = getResources().getDrawable(R.drawable.msg_in_photo); - statesDrawable[0][0] = getResources().getDrawable(R.drawable.play1); - statesDrawable[0][1] = getResources().getDrawable(R.drawable.play1_pressed); - statesDrawable[1][0] = getResources().getDrawable(R.drawable.pause1); - statesDrawable[1][1] = getResources().getDrawable(R.drawable.pause1_pressed); - statesDrawable[2][0] = getResources().getDrawable(R.drawable.audioload1); - statesDrawable[2][1] = getResources().getDrawable(R.drawable.audioload1_pressed); - statesDrawable[3][0] = getResources().getDrawable(R.drawable.audiocancel1); - statesDrawable[3][1] = getResources().getDrawable(R.drawable.audiocancel1_pressed); + statesDrawable[0][0] = getResources().getDrawable(R.drawable.play_w2); + statesDrawable[0][1] = getResources().getDrawable(R.drawable.play_w2_pressed); + statesDrawable[1][0] = getResources().getDrawable(R.drawable.pause_w2); + statesDrawable[1][1] = getResources().getDrawable(R.drawable.pause_w2_pressed); + statesDrawable[2][0] = getResources().getDrawable(R.drawable.download_g); + statesDrawable[2][1] = getResources().getDrawable(R.drawable.download_g_pressed); + statesDrawable[3][0] = getResources().getDrawable(R.drawable.pause_g); + statesDrawable[3][1] = getResources().getDrawable(R.drawable.pause_g_pressed); - statesDrawable[4][0] = getResources().getDrawable(R.drawable.play2); - statesDrawable[4][1] = getResources().getDrawable(R.drawable.play2_pressed); - statesDrawable[5][0] = getResources().getDrawable(R.drawable.pause2); - statesDrawable[5][1] = getResources().getDrawable(R.drawable.pause2_pressed); - statesDrawable[6][0] = getResources().getDrawable(R.drawable.audioload2); - statesDrawable[6][1] = getResources().getDrawable(R.drawable.audioload2_pressed); - statesDrawable[7][0] = getResources().getDrawable(R.drawable.audiocancel2); - statesDrawable[7][1] = getResources().getDrawable(R.drawable.audiocancel2_pressed); + statesDrawable[4][0] = getResources().getDrawable(R.drawable.play_w); + statesDrawable[4][1] = getResources().getDrawable(R.drawable.play_w_pressed); + statesDrawable[5][0] = getResources().getDrawable(R.drawable.pause_w); + statesDrawable[5][1] = getResources().getDrawable(R.drawable.pause_w_pressed); + statesDrawable[6][0] = getResources().getDrawable(R.drawable.download_b); + statesDrawable[6][1] = getResources().getDrawable(R.drawable.download_b_pressed); + statesDrawable[7][0] = getResources().getDrawable(R.drawable.pause_b); + statesDrawable[7][1] = getResources().getDrawable(R.drawable.pause_b_pressed); timePaint = new TextPaint(TextPaint.ANTI_ALIAS_FLAG); timePaint.setTextSize(AndroidUtilities.dp(16)); @@ -122,13 +123,13 @@ public class PopupAudioView extends BaseCell implements SeekBar.SeekBarDelegate, return; } - seekBarX = AndroidUtilities.dp(40); + seekBarX = AndroidUtilities.dp(54); buttonX = AndroidUtilities.dp(10); timeX = getMeasuredWidth() - timeWidth - AndroidUtilities.dp(16); - seekBar.width = getMeasuredWidth() - AndroidUtilities.dp(56) - timeWidth; + seekBar.width = getMeasuredWidth() - AndroidUtilities.dp(70) - timeWidth; seekBar.height = AndroidUtilities.dp(30); - progressView.width = getMeasuredWidth() - AndroidUtilities.dp(80) - timeWidth; + progressView.width = getMeasuredWidth() - AndroidUtilities.dp(94) - timeWidth; progressView.height = AndroidUtilities.dp(30); seekBarY = AndroidUtilities.dp(13); buttonY = AndroidUtilities.dp(10); @@ -234,6 +235,10 @@ public class PopupAudioView extends BaseCell implements SeekBar.SeekBarDelegate, private void didPressedButton() { if (buttonState == 0) { boolean result = MediaController.getInstance().playAudio(currentMessageObject); + if (!currentMessageObject.isOut() && currentMessageObject.isContentUnread()) { + MessagesController.getInstance().markMessageContentAsRead(currentMessageObject.getId()); + currentMessageObject.setContentIsRead(); + } if (result) { buttonState = 1; invalidate(); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/RecordStatusDrawable.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/RecordStatusDrawable.java new file mode 100644 index 000000000..b224eb3e5 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/RecordStatusDrawable.java @@ -0,0 +1,110 @@ +/* + * This is the source code of Telegram for Android v. 2.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-2015. + */ + +package org.telegram.ui.Components; + +import android.graphics.Canvas; +import android.graphics.ColorFilter; +import android.graphics.Paint; +import android.graphics.RectF; +import android.graphics.drawable.Drawable; + +import org.telegram.android.AndroidUtilities; + +public class RecordStatusDrawable extends Drawable { + + private boolean isChat = false; + private Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); + private long lastUpdateTime = 0; + private boolean started = false; + private RectF rect = new RectF(); + private float progress; + + public RecordStatusDrawable() { + super(); + paint.setColor(0xffd7e8f7); + paint.setStyle(Paint.Style.STROKE); + paint.setStrokeWidth(AndroidUtilities.dp(2)); + paint.setStrokeCap(Paint.Cap.ROUND); + } + + public void setIsChat(boolean value) { + isChat = value; + } + + private void update() { + long newTime = System.currentTimeMillis(); + long dt = newTime - lastUpdateTime; + lastUpdateTime = newTime; + if (dt > 50) { + dt = 50; + } + progress += dt / 300.0f; + while (progress > 1.0f) { + progress -= 1.0f; + } + invalidateSelf(); + } + + public void start() { + lastUpdateTime = System.currentTimeMillis(); + started = true; + invalidateSelf(); + } + + public void stop() { + started = false; + } + + @Override + public void draw(Canvas canvas) { + canvas.save(); + canvas.translate(0, getIntrinsicHeight() / 2 + AndroidUtilities.dp(isChat ? 1 : 2)); + for (int a = 0; a < 4; a++) { + if (a == 0) { + paint.setAlpha((int) (255 * progress)); + } else if (a == 3) { + paint.setAlpha((int) (255 * (1.0f - progress))); + } else { + paint.setAlpha(255); + } + float side = AndroidUtilities.dp(4) * a + AndroidUtilities.dp(4) * progress; + rect.set(-side, -side, side, side); + canvas.drawArc(rect, -15, 30, false, paint); + } + canvas.restore(); + if (started) { + update(); + } + } + + @Override + public void setAlpha(int alpha) { + + } + + @Override + public void setColorFilter(ColorFilter cf) { + + } + + @Override + public int getOpacity() { + return 0; + } + + @Override + public int getIntrinsicWidth() { + return AndroidUtilities.dp(18); + } + + @Override + public int getIntrinsicHeight() { + return AndroidUtilities.dp(14); + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/RecyclerExListView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/RecyclerExListView.java new file mode 100644 index 000000000..09e818588 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/RecyclerExListView.java @@ -0,0 +1,292 @@ +/* + * This is the source code of Telegram for Android v. 2.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-2015. + */ + +package org.telegram.ui.Components; + +import android.content.Context; +import android.util.AttributeSet; +import android.view.GestureDetector; +import android.view.HapticFeedbackConstants; +import android.view.MotionEvent; +import android.view.SoundEffectConstants; +import android.view.View; +import android.view.ViewConfiguration; + +import org.telegram.android.AndroidUtilities; +import org.telegram.android.support.widget.RecyclerView; + +public class RecyclerExListView extends RecyclerView { + + private OnItemClickListener onItemClickListener; + private OnItemLongClickListener onItemLongClickListener; + private RecyclerView.OnScrollListener onScrollListener; + private OnInterceptTouchListener onInterceptTouchListener; + private View emptyView; + private Runnable selectChildRunnable; + + private GestureDetector mGestureDetector; + private View currentChildView; + private int currentChildPosition; + private boolean interceptedByChild; + private boolean wasPressed; + + public interface OnItemClickListener { + void onItemClick(View view, int position); + } + + public interface OnItemLongClickListener { + void onItemClick(View view, int position); + } + + public interface OnInterceptTouchListener { + boolean onInterceptTouchEvent(MotionEvent event); + } + + private class RecyclerListViewItemClickListener implements RecyclerView.OnItemTouchListener { + + public RecyclerListViewItemClickListener(Context context) { + mGestureDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() { + @Override + public boolean onSingleTapUp(MotionEvent e) { + if (currentChildView != null && onItemClickListener != null) { + currentChildView.playSoundEffect(SoundEffectConstants.CLICK); + onItemClickListener.onItemClick(currentChildView, currentChildPosition); + if (selectChildRunnable != null) { + currentChildView.setPressed(true); + final View view = currentChildView; + AndroidUtilities.runOnUIThread(new Runnable() { + @Override + public void run() { + if (view != null) { + view.setPressed(false); + } + } + }, ViewConfiguration.getPressedStateDuration()); + AndroidUtilities.cancelRunOnUIThread(selectChildRunnable); + selectChildRunnable = null; + currentChildView = null; + interceptedByChild = false; + } + } + return true; + } + + @Override + public void onLongPress(MotionEvent e) { + if (currentChildView != null && onItemLongClickListener != null) { + currentChildView.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS); + onItemLongClickListener.onItemClick(currentChildView, currentChildPosition); + } + } + }); + } + + @Override + public boolean onInterceptTouchEvent(RecyclerView view, MotionEvent e) { + int action = e.getActionMasked(); + boolean isScrollIdle = RecyclerExListView.this.getScrollState() == RecyclerExListView.SCROLL_STATE_IDLE; + + if ((action == MotionEvent.ACTION_DOWN || action == MotionEvent.ACTION_POINTER_DOWN) && currentChildView == null && isScrollIdle) { + currentChildView = view.findChildViewUnder(e.getX(), e.getY()); + currentChildPosition = -1; + if (currentChildView != null) { + currentChildPosition = view.getChildPosition(currentChildView); + MotionEvent childEvent = MotionEvent.obtain(0, 0, e.getActionMasked(), e.getX() - currentChildView.getLeft(), e.getY() - currentChildView.getTop(), 0); + if (currentChildView.onTouchEvent(childEvent)) { + interceptedByChild = true; + } + childEvent.recycle(); + } + } + + if (currentChildView != null && !interceptedByChild) { + mGestureDetector.onTouchEvent(e); + } + + if (action == MotionEvent.ACTION_DOWN || action == MotionEvent.ACTION_POINTER_DOWN) { + if (!interceptedByChild && currentChildView != null) { + selectChildRunnable = new Runnable() { + @Override + public void run() { + if (selectChildRunnable != null && currentChildView != null) { + currentChildView.setPressed(true); + selectChildRunnable = null; + } + } + }; + AndroidUtilities.runOnUIThread(selectChildRunnable, ViewConfiguration.getTapTimeout()); + } + } else if (currentChildView != null && (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_POINTER_UP || action == MotionEvent.ACTION_CANCEL || !isScrollIdle)) { + if (selectChildRunnable != null) { + AndroidUtilities.cancelRunOnUIThread(selectChildRunnable); + selectChildRunnable = null; + } + currentChildView.setPressed(false); + currentChildView = null; + interceptedByChild = false; + } + return false; + } + + @Override + public void onTouchEvent(RecyclerView view, MotionEvent e) { + + } + } + + private AdapterDataObserver observer = new AdapterDataObserver() { + @Override + public void onChanged() { + checkIfEmpty(); + } + + @Override + public void onItemRangeInserted(int positionStart, int itemCount) { + checkIfEmpty(); + } + + @Override + public void onItemRangeRemoved(int positionStart, int itemCount) { + checkIfEmpty(); + } + }; + + public void init(Context context) { + super.setOnScrollListener(new OnScrollListener() { + @Override + public void onScrollStateChanged(RecyclerView recyclerView, int newState) { + if (newState != SCROLL_STATE_IDLE && currentChildView != null) { + if (selectChildRunnable != null) { + AndroidUtilities.cancelRunOnUIThread(selectChildRunnable); + selectChildRunnable = null; + } + MotionEvent event = MotionEvent.obtain(0, 0, MotionEvent.ACTION_CANCEL, 0, 0, 0); + mGestureDetector.onTouchEvent(event); + currentChildView.onTouchEvent(event); + event.recycle(); + currentChildView.setPressed(false); + currentChildView = null; + interceptedByChild = false; + } + if (onScrollListener != null) { + onScrollListener.onScrollStateChanged(recyclerView, newState); + } + } + + @Override + public void onScrolled(RecyclerView recyclerView, int dx, int dy) { + if (onScrollListener != null) { + onScrollListener.onScrolled(recyclerView, dx, dy); + } + } + }); + addOnItemTouchListener(new RecyclerListViewItemClickListener(context)); + } + + public RecyclerExListView(Context context) { + super(context); + + /*setVerticalScrollBarEnabled(true); + try { + TypedArray a = context.getTheme().obtainStyledAttributes(new int[0]); + Method initializeScrollbars = android.view.View.class.getDeclaredMethod("initializeScrollbars", TypedArray.class); + initializeScrollbars.invoke(this, a); + a.recycle(); + } catch (Throwable e) { + FileLog.e("tmessages", e); + }*/ + + init(context); + } + + public RecyclerExListView(Context context, AttributeSet attrs) { + super(context, attrs); + init(context); + } + + public RecyclerExListView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + init(context); + } + + public void setOnItemClickListener(OnItemClickListener listener) { + onItemClickListener = listener; + } + + public void setOnItemLongClickListener(OnItemLongClickListener listener) { + onItemLongClickListener = listener; + } + + public void setEmptyView(View view) { + if (emptyView == view) { + return; + } + emptyView = view; + checkIfEmpty(); + } + + public View getEmptyView() { + return emptyView; + } + + public void invalidateViews() { + int count = getChildCount(); + for (int a = 0; a < count; a++) { + getChildAt(a).invalidate(); + } + } + + @Override + public boolean onInterceptTouchEvent(MotionEvent e) { + return onInterceptTouchListener != null && onInterceptTouchListener.onInterceptTouchEvent(e) || super.onInterceptTouchEvent(e); + } + + private void checkIfEmpty() { + if (emptyView == null || getAdapter() == null) { + return; + } + boolean emptyViewVisible = getAdapter().getItemCount() == 0; + emptyView.setVisibility(emptyViewVisible ? VISIBLE : INVISIBLE); + setVisibility(emptyViewVisible ? INVISIBLE : VISIBLE); + } + + @Override + public void setOnScrollListener(OnScrollListener listener) { + onScrollListener = listener; + } + + public void setOnInterceptTouchListener(OnInterceptTouchListener listener) { + onInterceptTouchListener = listener; + } + + @Override + public void setAdapter(Adapter adapter) { + final Adapter oldAdapter = getAdapter(); + if (oldAdapter != null) { + oldAdapter.unregisterAdapterDataObserver(observer); + } + super.setAdapter(adapter); + if (adapter != null) { + adapter.registerAdapterDataObserver(observer); + } + checkIfEmpty(); + } + + @Override + public void stopScroll() { + try { + super.stopScroll(); + } catch (NullPointerException exception) { + /** + * The mLayout has been disposed of before the + * RecyclerView and this stops the application + * from crashing. + */ + } + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/RecyclerListView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/RecyclerListView.java index 08eea19d4..3f1e8f9ba 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/RecyclerListView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/RecyclerListView.java @@ -9,12 +9,15 @@ package org.telegram.ui.Components; import android.content.Context; -import android.support.v7.widget.RecyclerView; +import org.telegram.android.support.widget.RecyclerView; + import android.util.AttributeSet; import android.view.GestureDetector; import android.view.MotionEvent; import android.view.View; +import org.telegram.messenger.FileLog; + public class RecyclerListView extends RecyclerView { public interface OnItemClickListener { @@ -82,6 +85,15 @@ public class RecyclerListView extends RecyclerView { return super.onInterceptTouchEvent(e); } + @Override + protected void onLayout(boolean changed, int l, int t, int r, int b) { + try { + super.onLayout(changed, l, t, r, b); + } catch (Exception e) { + FileLog.e("tmessages", e); + } + } + @Override public void stopScroll() { try { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/ResourceLoader.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/ResourceLoader.java new file mode 100644 index 000000000..a72c52739 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/ResourceLoader.java @@ -0,0 +1,120 @@ +/* + * This is the source code of Telegram for Android v. 2.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-2015. + */ + +package org.telegram.ui.Components; + +import android.content.Context; +import android.graphics.drawable.Drawable; + +import org.telegram.messenger.R; + +public class ResourceLoader { + + public static Drawable backgroundDrawableIn; + public static Drawable backgroundDrawableInSelected; + public static Drawable backgroundDrawableOut; + public static Drawable backgroundDrawableOutSelected; + public static Drawable backgroundMediaDrawableIn; + public static Drawable backgroundMediaDrawableInSelected; + public static Drawable backgroundMediaDrawableOut; + public static Drawable backgroundMediaDrawableOutSelected; + public static Drawable checkDrawable; + public static Drawable halfCheckDrawable; + public static Drawable clockDrawable; + public static Drawable broadcastDrawable; + public static Drawable checkMediaDrawable; + public static Drawable halfCheckMediaDrawable; + public static Drawable clockMediaDrawable; + public static Drawable broadcastMediaDrawable; + public static Drawable errorDrawable; + public static Drawable backgroundBlack; + public static Drawable backgroundBlue; + public static Drawable mediaBackgroundDrawable; + + public static Drawable geoInDrawable; + public static Drawable geoOutDrawable; + + public static Drawable[][] audioStatesDrawable = new Drawable[10][2]; + + public static Drawable placeholderDocInDrawable; + public static Drawable placeholderDocOutDrawable; + public static Drawable videoIconDrawable; + public static Drawable docMenuInDrawable; + public static Drawable docMenuOutDrawable; + public static Drawable[] buttonStatesDrawables = new Drawable[8]; + public static Drawable[][] buttonStatesDrawablesDoc = new Drawable[3][2]; + + public static void loadRecources(Context context) { + if (backgroundDrawableIn == null) { + backgroundDrawableIn = context.getResources().getDrawable(R.drawable.msg_in); + backgroundDrawableInSelected = context.getResources().getDrawable(R.drawable.msg_in_selected); + backgroundDrawableOut = context.getResources().getDrawable(R.drawable.msg_out); + backgroundDrawableOutSelected = context.getResources().getDrawable(R.drawable.msg_out_selected); + backgroundMediaDrawableIn = context.getResources().getDrawable(R.drawable.msg_in_photo); + backgroundMediaDrawableInSelected = context.getResources().getDrawable(R.drawable.msg_in_photo_selected); + backgroundMediaDrawableOut = context.getResources().getDrawable(R.drawable.msg_out_photo); + backgroundMediaDrawableOutSelected = context.getResources().getDrawable(R.drawable.msg_out_photo_selected); + checkDrawable = context.getResources().getDrawable(R.drawable.msg_check); + halfCheckDrawable = context.getResources().getDrawable(R.drawable.msg_halfcheck); + clockDrawable = context.getResources().getDrawable(R.drawable.msg_clock); + checkMediaDrawable = context.getResources().getDrawable(R.drawable.msg_check_w); + halfCheckMediaDrawable = context.getResources().getDrawable(R.drawable.msg_halfcheck_w); + clockMediaDrawable = context.getResources().getDrawable(R.drawable.msg_clock_photo); + errorDrawable = context.getResources().getDrawable(R.drawable.msg_warning); + mediaBackgroundDrawable = context.getResources().getDrawable(R.drawable.phototime); + broadcastDrawable = context.getResources().getDrawable(R.drawable.broadcast3); + broadcastMediaDrawable = context.getResources().getDrawable(R.drawable.broadcast4); + backgroundBlack = context.getResources().getDrawable(R.drawable.system_black); + backgroundBlue = context.getResources().getDrawable(R.drawable.system_blue); + + audioStatesDrawable[0][0] = context.getResources().getDrawable(R.drawable.play_w2); + audioStatesDrawable[0][1] = context.getResources().getDrawable(R.drawable.play_w2_pressed); + audioStatesDrawable[1][0] = context.getResources().getDrawable(R.drawable.pause_w2); + audioStatesDrawable[1][1] = context.getResources().getDrawable(R.drawable.pause_w2_pressed); + audioStatesDrawable[2][0] = context.getResources().getDrawable(R.drawable.download_g); + audioStatesDrawable[2][1] = context.getResources().getDrawable(R.drawable.download_g_pressed); + audioStatesDrawable[3][0] = context.getResources().getDrawable(R.drawable.pause_g); + audioStatesDrawable[3][1] = context.getResources().getDrawable(R.drawable.pause_g_pressed); + audioStatesDrawable[4][0] = context.getResources().getDrawable(R.drawable.cancel_g); + audioStatesDrawable[4][1] = context.getResources().getDrawable(R.drawable.cancel_g_pressed); + audioStatesDrawable[5][0] = context.getResources().getDrawable(R.drawable.play_w); + audioStatesDrawable[5][1] = context.getResources().getDrawable(R.drawable.play_w_pressed); + audioStatesDrawable[6][0] = context.getResources().getDrawable(R.drawable.pause_w); + audioStatesDrawable[6][1] = context.getResources().getDrawable(R.drawable.pause_w_pressed); + audioStatesDrawable[7][0] = context.getResources().getDrawable(R.drawable.download_b); + audioStatesDrawable[7][1] = context.getResources().getDrawable(R.drawable.download_b_pressed); + audioStatesDrawable[8][0] = context.getResources().getDrawable(R.drawable.pause_b); + audioStatesDrawable[8][1] = context.getResources().getDrawable(R.drawable.pause_b_pressed); + audioStatesDrawable[9][0] = context.getResources().getDrawable(R.drawable.cancel_b); + audioStatesDrawable[9][1] = context.getResources().getDrawable(R.drawable.cancel_b_pressed); + + placeholderDocInDrawable = context.getResources().getDrawable(R.drawable.doc_blue); + placeholderDocOutDrawable = context.getResources().getDrawable(R.drawable.doc_green); + buttonStatesDrawables[0] = context.getResources().getDrawable(R.drawable.photoload); + buttonStatesDrawables[1] = context.getResources().getDrawable(R.drawable.photocancel); + buttonStatesDrawables[2] = context.getResources().getDrawable(R.drawable.photogif); + buttonStatesDrawables[3] = context.getResources().getDrawable(R.drawable.playvideo); + buttonStatesDrawables[4] = context.getResources().getDrawable(R.drawable.photopause); + buttonStatesDrawables[5] = context.getResources().getDrawable(R.drawable.burn); + buttonStatesDrawables[6] = context.getResources().getDrawable(R.drawable.circle); + buttonStatesDrawables[7] = context.getResources().getDrawable(R.drawable.photocheck); + buttonStatesDrawablesDoc[0][0] = context.getResources().getDrawable(R.drawable.docload_b); + buttonStatesDrawablesDoc[1][0] = context.getResources().getDrawable(R.drawable.doccancel_b); + buttonStatesDrawablesDoc[2][0] = context.getResources().getDrawable(R.drawable.docpause_b); + buttonStatesDrawablesDoc[0][1] = context.getResources().getDrawable(R.drawable.docload_g); + buttonStatesDrawablesDoc[1][1] = context.getResources().getDrawable(R.drawable.doccancel_g); + buttonStatesDrawablesDoc[2][1] = context.getResources().getDrawable(R.drawable.docpause_g); + videoIconDrawable = context.getResources().getDrawable(R.drawable.ic_video); + docMenuInDrawable = context.getResources().getDrawable(R.drawable.doc_actions_b); + docMenuOutDrawable = context.getResources().getDrawable(R.drawable.doc_actions_g); + + geoInDrawable = context.getResources().getDrawable(R.drawable.location_b); + geoOutDrawable = context.getResources().getDrawable(R.drawable.location_g); + } + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/SendingFileDrawable.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/SendingFileDrawable.java new file mode 100644 index 000000000..103a440af --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/SendingFileDrawable.java @@ -0,0 +1,126 @@ +/* + * This is the source code of Telegram for Android v. 2.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-2015. + */ + +package org.telegram.ui.Components; + +import android.graphics.Canvas; +import android.graphics.ColorFilter; +import android.graphics.Paint; +import android.graphics.RectF; +import android.graphics.drawable.Drawable; +import android.view.animation.DecelerateInterpolator; + +import org.telegram.android.AndroidUtilities; + +public class SendingFileDrawable extends Drawable { + + private float radOffset = 0; + private float currentProgress = 0; + private float animationProgressStart = 0; + private long currentProgressTime = 0; + private float animatedProgressValue = 0; + private RectF cicleRect = new RectF(); + private boolean isChat = false; + private Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); + private long lastUpdateTime = 0; + private boolean started = false; + private static DecelerateInterpolator decelerateInterpolator = null; + + public SendingFileDrawable() { + super(); + paint.setColor(0xffd7e8f7); + paint.setStyle(Paint.Style.STROKE); + paint.setStrokeWidth(AndroidUtilities.dp(2)); + paint.setStrokeCap(Paint.Cap.ROUND); + decelerateInterpolator = new DecelerateInterpolator(); + } + + public void setIsChat(boolean value) { + isChat = value; + } + + public void setProgress(float value, boolean animated) { + if (!animated) { + animatedProgressValue = value; + animationProgressStart = value; + } else { + animationProgressStart = animatedProgressValue; + } + currentProgress = value; + currentProgressTime = 0; + + invalidateSelf(); + } + + private void update() { + long newTime = System.currentTimeMillis(); + long dt = newTime - lastUpdateTime; + lastUpdateTime = newTime; + + if (animatedProgressValue != 1) { + radOffset += 360 * dt / 1000.0f; + float progressDiff = currentProgress - animationProgressStart; + if (progressDiff > 0) { + currentProgressTime += dt; + if (currentProgressTime >= 300) { + animatedProgressValue = currentProgress; + animationProgressStart = currentProgress; + currentProgressTime = 0; + } else { + animatedProgressValue = animationProgressStart + progressDiff * decelerateInterpolator.getInterpolation(currentProgressTime / 300.0f); + } + } + invalidateSelf(); + } + } + + public void start() { + lastUpdateTime = System.currentTimeMillis(); + started = true; + invalidateSelf(); + } + + public void stop() { + started = false; + } + + @Override + public void draw(Canvas canvas) { + cicleRect.set(AndroidUtilities.dp(1), AndroidUtilities.dp(isChat ? 3 : 4), AndroidUtilities.dp(10), AndroidUtilities.dp(isChat ? 11 : 12)); + canvas.drawArc(cicleRect, -90 + radOffset, Math.max(60, 360 * animatedProgressValue), false, paint); + + if (started) { + update(); + } + } + + @Override + public void setAlpha(int alpha) { + + } + + @Override + public void setColorFilter(ColorFilter cf) { + + } + + @Override + public int getOpacity() { + return 0; + } + + @Override + public int getIntrinsicWidth() { + return AndroidUtilities.dp(14); + } + + @Override + public int getIntrinsicHeight() { + return AndroidUtilities.dp(14); + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/SendingFileEx2Drawable.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/SendingFileEx2Drawable.java new file mode 100644 index 000000000..48aae49e3 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/SendingFileEx2Drawable.java @@ -0,0 +1,97 @@ +/* + * This is the source code of Telegram for Android v. 2.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-2015. + */ + +package org.telegram.ui.Components; + +import android.graphics.Canvas; +import android.graphics.ColorFilter; +import android.graphics.Paint; +import android.graphics.drawable.Drawable; + +import org.telegram.android.AndroidUtilities; + +public class SendingFileEx2Drawable extends Drawable { + + private boolean isChat = false; + private Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); + private long lastUpdateTime = 0; + private boolean started = false; + private float progress; + + public SendingFileEx2Drawable() { + super(); + paint.setColor(0xffd7e8f7); + paint.setStyle(Paint.Style.STROKE); + paint.setStrokeWidth(AndroidUtilities.dp(3)); + paint.setStrokeCap(Paint.Cap.ROUND); + } + + public void setIsChat(boolean value) { + isChat = value; + } + + private void update() { + long newTime = System.currentTimeMillis(); + long dt = newTime - lastUpdateTime; + lastUpdateTime = newTime; + if (dt > 50) { + dt = 50; + } + progress += dt / 1000.0f; + while (progress > 1.0f) { + progress -= 1.0f; + } + invalidateSelf(); + } + + public void start() { + lastUpdateTime = System.currentTimeMillis(); + started = true; + invalidateSelf(); + } + + public void stop() { + started = false; + } + + @Override + public void draw(Canvas canvas) { + int start = (int) (progress <= 0.5f ? AndroidUtilities.dp(1) : AndroidUtilities.dp(11) * (progress - 0.5f) * 2); + int end = (int) (progress >= 0.5f ? AndroidUtilities.dp(11) : AndroidUtilities.dp(11) * progress * 2); + canvas.drawLine(start, AndroidUtilities.dp(isChat ? 11 : 12), end, AndroidUtilities.dp(isChat ? 11 : 12), paint); + + if (started) { + update(); + } + } + + @Override + public void setAlpha(int alpha) { + + } + + @Override + public void setColorFilter(ColorFilter cf) { + + } + + @Override + public int getOpacity() { + return 0; + } + + @Override + public int getIntrinsicWidth() { + return AndroidUtilities.dp(18); + } + + @Override + public int getIntrinsicHeight() { + return AndroidUtilities.dp(14); + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/SendingFileExDrawable.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/SendingFileExDrawable.java new file mode 100644 index 000000000..9b5d5069b --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/SendingFileExDrawable.java @@ -0,0 +1,107 @@ +/* + * This is the source code of Telegram for Android v. 2.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-2015. + */ + +package org.telegram.ui.Components; + +import android.graphics.Canvas; +import android.graphics.ColorFilter; +import android.graphics.Paint; +import android.graphics.drawable.Drawable; + +import org.telegram.android.AndroidUtilities; + +public class SendingFileExDrawable extends Drawable { + + private boolean isChat = false; + private Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); + private long lastUpdateTime = 0; + private boolean started = false; + private float progress; + + public SendingFileExDrawable() { + super(); + paint.setColor(0xffd7e8f7); + paint.setStyle(Paint.Style.STROKE); + paint.setStrokeWidth(AndroidUtilities.dp(2)); + paint.setStrokeCap(Paint.Cap.ROUND); + } + + public void setIsChat(boolean value) { + isChat = value; + } + + private void update() { + long newTime = System.currentTimeMillis(); + long dt = newTime - lastUpdateTime; + lastUpdateTime = newTime; + if (dt > 50) { + dt = 50; + } + progress += dt / 500.0f; + while (progress > 1.0f) { + progress -= 1.0f; + } + invalidateSelf(); + } + + public void start() { + lastUpdateTime = System.currentTimeMillis(); + started = true; + invalidateSelf(); + } + + public void stop() { + started = false; + } + + @Override + public void draw(Canvas canvas) { + + for (int a = 0; a < 3; a++) { + if (a == 0) { + paint.setAlpha((int) (255 * progress)); + } else if (a == 2) { + paint.setAlpha((int) (255 * (1.0f - progress))); + } else { + paint.setAlpha(255); + } + float side = AndroidUtilities.dp(5) * a + AndroidUtilities.dp(5) * progress; + canvas.drawLine(side, AndroidUtilities.dp(isChat ? 3 : 4), side + AndroidUtilities.dp(4), AndroidUtilities.dp(isChat ? 7 : 8), paint); + canvas.drawLine(side, AndroidUtilities.dp(isChat ? 11 : 12), side + AndroidUtilities.dp(4), AndroidUtilities.dp(isChat ? 7 : 8), paint); + } + + if (started) { + update(); + } + } + + @Override + public void setAlpha(int alpha) { + + } + + @Override + public void setColorFilter(ColorFilter cf) { + + } + + @Override + public int getOpacity() { + return 0; + } + + @Override + public int getIntrinsicWidth() { + return AndroidUtilities.dp(18); + } + + @Override + public int getIntrinsicHeight() { + return AndroidUtilities.dp(14); + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/SimpleTextView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/SimpleTextView.java new file mode 100644 index 000000000..2bbe59040 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/SimpleTextView.java @@ -0,0 +1,126 @@ +/* + * This is the source code of Telegram for Android v. 2.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-2015. + */ + +package org.telegram.ui.Components; + +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.Typeface; +import android.text.Layout; +import android.text.SpannableStringBuilder; +import android.text.StaticLayout; +import android.text.TextPaint; +import android.text.TextUtils; +import android.view.Gravity; +import android.view.View; + +import org.telegram.android.AndroidUtilities; + +public class SimpleTextView extends View { + + private Layout layout; + private TextPaint textPaint; + private int gravity; + private CharSequence text; + private SpannableStringBuilder spannableStringBuilder; + + private int offsetX; + private boolean wasLayout = false; + + public SimpleTextView(Context context) { + super(context); + textPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG); + } + + public void setTextColor(int color) { + textPaint.setColor(color); + } + + public void setTextSize(int size) { + textPaint.setTextSize(AndroidUtilities.dp(size)); + } + + public void setGravity(int value) { + gravity = value; + } + + public void setTypeface(Typeface typeface) { + textPaint.setTypeface(typeface); + } + + private void createLayout(int width) { + if (text != null) { + try { + CharSequence string = TextUtils.ellipsize(text, textPaint, width, TextUtils.TruncateAt.END); + layout = new StaticLayout(string, 0, string.length(), textPaint, width, Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false); + + /*if (metrics == null) { + metrics = BoringLayout.isBoring(text, textPaint); + } + if (layout == null) { + layout = BoringLayout.make(text, textPaint, width, Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, metrics, false, TextUtils.TruncateAt.END, width); + } else { + layout = ((BoringLayout) layout).replaceOrMake(text, textPaint, width, Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, metrics, false, TextUtils.TruncateAt.END, width); + }*/ + + /*if (spannableStringBuilder == null) { + spannableStringBuilder = new SpannableStringBuilder(text); + layout = new DynamicLayout(text, text, textPaint, width, Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false, TextUtils.TruncateAt.END, width); + } else { + spannableStringBuilder.replace(0, text.length(), text); + }*/ + + if (layout.getLineCount() > 0) { + if ((gravity & Gravity.HORIZONTAL_GRAVITY_MASK) == Gravity.LEFT) { + offsetX = -(int) layout.getLineLeft(0); + } else if (layout.getLineLeft(0) == 0) { + offsetX = (int) (width - layout.getLineWidth(0)); + } else { + offsetX = 0; + } + } + } catch (Exception e) { + //ignore + } + } + } + + @Override + protected void onLayout(boolean changed, int left, int top, int right, int bottom) { + if (changed) { + createLayout(right - left); + invalidate(); + wasLayout = true; + } + } + + public void setText(CharSequence value) { + text = value; + if (wasLayout) { + createLayout(getMeasuredWidth()); + invalidate(); + } else { + requestLayout(); + } + } + + @Override + protected void onDraw(Canvas canvas) { + if (layout != null) { + if (offsetX != 0) { + canvas.save(); + canvas.translate(offsetX, 0); + } + layout.draw(canvas); + if (offsetX != 0) { + canvas.restore(); + } + } + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/SizeNotifierRelativeLayout.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/SizeNotifierRelativeLayout.java index 54ec83b9a..8802e8505 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/SizeNotifierRelativeLayout.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/SizeNotifierRelativeLayout.java @@ -77,7 +77,6 @@ public class SizeNotifierRelativeLayout extends RelativeLayout { this.getWindowVisibleDisplayFrame(rect); keyboardHeight = usableViewHeight - (rect.bottom - rect.top); final boolean isWidthGreater = AndroidUtilities.displaySize.x > AndroidUtilities.displaySize.y; - FileLog.e("tmessages", "isWidthGreater = " + isWidthGreater + " height = " + keyboardHeight); post(new Runnable() { @Override public void run() { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/SizeNotifierRelativeLayoutPhoto.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/SizeNotifierRelativeLayoutPhoto.java new file mode 100644 index 000000000..ef2d6055e --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/SizeNotifierRelativeLayoutPhoto.java @@ -0,0 +1,58 @@ +/* + * This is the source code of Telegram for Android v. 2.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-2015. + */ + +package org.telegram.ui.Components; + +import android.annotation.SuppressLint; +import android.content.Context; +import android.graphics.Rect; +import android.view.View; +import android.widget.RelativeLayout; + +import org.telegram.android.AndroidUtilities; + +public class SizeNotifierRelativeLayoutPhoto extends RelativeLayout { + + public interface SizeNotifierRelativeLayoutPhotoDelegate { + void onSizeChanged(int keyboardHeight, boolean isWidthGreater); + } + + private Rect rect = new Rect(); + private int keyboardHeight; + private SizeNotifierRelativeLayoutPhotoDelegate delegate; + + public SizeNotifierRelativeLayoutPhoto(Context context) { + super(context); + } + + public void setDelegate(SizeNotifierRelativeLayoutPhotoDelegate delegate) { + this.delegate = delegate; + } + + @SuppressLint("DrawAllocation") + @Override + protected void onLayout(boolean changed, int l, int t, int r, int b) { + super.onLayout(changed, l, t, r, b); + + if (delegate != null) { + View rootView = this.getRootView(); + int usableViewHeight = rootView.getHeight() - AndroidUtilities.getViewInset(rootView); + this.getWindowVisibleDisplayFrame(rect); + keyboardHeight = (rect.bottom - rect.top) - usableViewHeight; + final boolean isWidthGreater = AndroidUtilities.displaySize.x > AndroidUtilities.displaySize.y; + post(new Runnable() { + @Override + public void run() { + if (delegate != null) { + delegate.onSizeChanged(keyboardHeight, isWidthGreater); + } + } + }); + } + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/SlidingTabView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/SlidingTabView.java index 4c01ff275..1ba3e974c 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/SlidingTabView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/SlidingTabView.java @@ -12,7 +12,6 @@ import android.content.Context; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Typeface; -import android.util.AttributeSet; import android.util.TypedValue; import android.view.Gravity; import android.view.View; @@ -41,33 +40,15 @@ public class SlidingTabView extends LinearLayout { private float startAnimationX = 0; private DecelerateInterpolator interpolator; - private void init() { + public SlidingTabView(Context context) { + super(context); setOrientation(HORIZONTAL); setWeightSum(100); paint.setColor(0xffffffff); + setWillNotDraw(false); interpolator = new DecelerateInterpolator(); } - public SlidingTabView(Context context) { - super(context); - init(); - } - - public SlidingTabView(Context context, AttributeSet attrs) { - super(context, attrs); - init(); - } - - public SlidingTabView(Context context, AttributeSet attrs, int defStyleAttr) { - super(context, attrs, defStyleAttr); - init(); - } - - public SlidingTabView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { - super(context, attrs, defStyleAttr, defStyleRes); - init(); - } - public void addTextTab(final int position, String title) { TextView tab = new TextView(getContext()); tab.setText(title); @@ -87,7 +68,7 @@ public class SlidingTabView extends LinearLayout { }); addView(tab); LayoutParams layoutParams = (LayoutParams)tab.getLayoutParams(); - layoutParams.height = LayoutParams.MATCH_PARENT; + layoutParams.height = LayoutHelper.MATCH_PARENT; layoutParams.width = 0; layoutParams.weight = 50; tab.setLayoutParams(layoutParams); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/StaticLayoutEx.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/StaticLayoutEx.java index 4700a15e6..d904f7c00 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/StaticLayoutEx.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/StaticLayoutEx.java @@ -10,6 +10,7 @@ package org.telegram.ui.Components; import android.os.Build; import android.text.Layout; +import android.text.SpannableStringBuilder; import android.text.StaticLayout; import android.text.TextDirectionHeuristic; import android.text.TextDirectionHeuristics; @@ -78,7 +79,7 @@ public class StaticLayoutEx { } public static StaticLayout createStaticLayout(CharSequence source, int bufstart, int bufend, TextPaint paint, int outerWidth, Layout.Alignment align, float spacingMult, float spacingAdd, boolean includePad, TextUtils.TruncateAt ellipsize, int ellipsisWidth, int maxLines) { - if (Build.VERSION.SDK_INT >= 14) { + /*if (Build.VERSION.SDK_INT >= 14) { init(); try { sConstructorArgs[0] = source; @@ -98,17 +99,20 @@ public class StaticLayoutEx { } catch (Exception e) { FileLog.e("tmessages", e); } - } + }*/ try { if (maxLines == 1) { - return new StaticLayout(source, bufstart, bufend, paint, outerWidth, align, spacingMult, spacingAdd, includePad, ellipsize, ellipsisWidth); + CharSequence text = TextUtils.ellipsize(source, paint, ellipsisWidth, TextUtils.TruncateAt.END); + return new StaticLayout(text, 0, text.length(), paint, outerWidth, align, spacingMult, spacingAdd, includePad); } else { StaticLayout layout = new StaticLayout(source, paint, outerWidth, align, spacingMult, spacingAdd, includePad); if (layout.getLineCount() <= maxLines) { return layout; } else { int off = layout.getOffsetForHorizontal(maxLines - 1, layout.getLineWidth(maxLines - 1)); - return new StaticLayout(source.subSequence(0, off), paint, outerWidth, align, spacingMult, spacingAdd, includePad); + SpannableStringBuilder stringBuilder = new SpannableStringBuilder(source.subSequence(0, Math.max(0, off - 1))); + stringBuilder.append("\u2026"); + return new StaticLayout(stringBuilder, paint, outerWidth, align, spacingMult, spacingAdd, includePad); } } } catch (Exception e) { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/Switch.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/Switch.java index f1a940f40..fa6740a9d 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/Switch.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/Switch.java @@ -33,7 +33,7 @@ import android.widget.CompoundButton; import org.telegram.android.AndroidUtilities; import org.telegram.android.LocaleController; import org.telegram.messenger.R; -import org.telegram.ui.AnimationCompat.ObjectAnimatorProxy; +import org.telegram.android.AnimationCompat.ObjectAnimatorProxy; public class Switch extends CompoundButton { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/TypingDotsDrawable.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/TypingDotsDrawable.java index 393482f89..d644d7d6d 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/TypingDotsDrawable.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/TypingDotsDrawable.java @@ -17,6 +17,7 @@ import android.view.animation.DecelerateInterpolator; import org.telegram.android.AndroidUtilities; public class TypingDotsDrawable extends Drawable { + private boolean isChat = false; private Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); private float[] scales = new float[3]; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ContactAddActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/ContactAddActivity.java index cf56dd75b..c09173f9d 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ContactAddActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ContactAddActivity.java @@ -41,6 +41,7 @@ import org.telegram.ui.ActionBar.ActionBarMenu; import org.telegram.ui.Components.AvatarDrawable; import org.telegram.ui.Components.BackupImageView; import org.telegram.ui.ActionBar.BaseFragment; +import org.telegram.ui.Components.LayoutHelper; public class ContactAddActivity extends BaseFragment implements NotificationCenter.NotificationCenterDelegate { @@ -129,8 +130,8 @@ public class ContactAddActivity extends BaseFragment implements NotificationCent layoutParams.topMargin = AndroidUtilities.dp(24); layoutParams.leftMargin = AndroidUtilities.dp(24); layoutParams.rightMargin = AndroidUtilities.dp(24); - layoutParams.width = LinearLayout.LayoutParams.MATCH_PARENT; - layoutParams.height = LinearLayout.LayoutParams.WRAP_CONTENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; + layoutParams.height = LayoutHelper.WRAP_CONTENT; frameLayout.setLayoutParams(layoutParams); avatarImage = new BackupImageView(context); @@ -153,8 +154,8 @@ public class ContactAddActivity extends BaseFragment implements NotificationCent nameTextView.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); frameLayout.addView(nameTextView); layoutParams3 = (FrameLayout.LayoutParams) nameTextView.getLayoutParams(); - layoutParams3.width = FrameLayout.LayoutParams.WRAP_CONTENT; - layoutParams3.height = FrameLayout.LayoutParams.WRAP_CONTENT; + layoutParams3.width = LayoutHelper.WRAP_CONTENT; + layoutParams3.height = LayoutHelper.WRAP_CONTENT; layoutParams3.leftMargin = AndroidUtilities.dp(LocaleController.isRTL ? 0 : 80); layoutParams3.rightMargin = AndroidUtilities.dp(LocaleController.isRTL ? 80 : 0); layoutParams3.topMargin = AndroidUtilities.dp(3); @@ -171,8 +172,8 @@ public class ContactAddActivity extends BaseFragment implements NotificationCent onlineTextView.setGravity((LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT)); frameLayout.addView(onlineTextView); layoutParams3 = (FrameLayout.LayoutParams) onlineTextView.getLayoutParams(); - layoutParams3.width = FrameLayout.LayoutParams.WRAP_CONTENT; - layoutParams3.height = FrameLayout.LayoutParams.WRAP_CONTENT; + layoutParams3.width = LayoutHelper.WRAP_CONTENT; + layoutParams3.height = LayoutHelper.WRAP_CONTENT; layoutParams3.leftMargin = AndroidUtilities.dp(LocaleController.isRTL ? 0 : 80); layoutParams3.rightMargin = AndroidUtilities.dp(LocaleController.isRTL ? 80 : 0); layoutParams3.topMargin = AndroidUtilities.dp(32); @@ -197,7 +198,7 @@ public class ContactAddActivity extends BaseFragment implements NotificationCent layoutParams.height = AndroidUtilities.dp(36); layoutParams.leftMargin = AndroidUtilities.dp(24); layoutParams.rightMargin = AndroidUtilities.dp(24); - layoutParams.width = LinearLayout.LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; firstNameField.setLayoutParams(layoutParams); firstNameField.setOnEditorActionListener(new TextView.OnEditorActionListener() { @Override @@ -229,7 +230,7 @@ public class ContactAddActivity extends BaseFragment implements NotificationCent layoutParams.height = AndroidUtilities.dp(36); layoutParams.leftMargin = AndroidUtilities.dp(24); layoutParams.rightMargin = AndroidUtilities.dp(24); - layoutParams.width = LinearLayout.LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; lastNameField.setLayoutParams(layoutParams); lastNameField.setOnEditorActionListener(new TextView.OnEditorActionListener() { @Override diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ContactsActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/ContactsActivity.java index 39b4310bc..793629602 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ContactsActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ContactsActivity.java @@ -49,6 +49,7 @@ import org.telegram.ui.ActionBar.ActionBar; import org.telegram.ui.ActionBar.ActionBarMenu; import org.telegram.ui.ActionBar.ActionBarMenuItem; import org.telegram.ui.ActionBar.BaseFragment; +import org.telegram.ui.Components.LayoutHelper; import org.telegram.ui.Components.LetterSectionsListView; import java.util.ArrayList; @@ -69,6 +70,7 @@ public class ContactsActivity extends BaseFragment implements NotificationCenter private boolean returnAsResult; private boolean createSecretChat; private boolean creatingChat = false; + private int chat_id; private String selectAlertString = null; private HashMap ignoreUsers; private boolean allowUsernameSearch = true; @@ -97,6 +99,7 @@ public class ContactsActivity extends BaseFragment implements NotificationCenter createSecretChat = arguments.getBoolean("createSecretChat", false); selectAlertString = arguments.getString("selectAlertString"); allowUsernameSearch = arguments.getBoolean("allowUsernameSearch", true); + chat_id = arguments.getInt("chat_id", 0); } else { needPhonebook = true; } @@ -197,7 +200,7 @@ public class ContactsActivity extends BaseFragment implements NotificationCenter item.getSearchField().setHint(LocaleController.getString("Search", R.string.Search)); searchListViewAdapter = new SearchAdapter(context, ignoreUsers, allowUsernameSearch); - listViewAdapter = new ContactsAdapter(context, onlyUsers, needPhonebook, ignoreUsers); + listViewAdapter = new ContactsAdapter(context, onlyUsers, needPhonebook, ignoreUsers, chat_id != 0); fragmentView = new FrameLayout(context); @@ -206,8 +209,8 @@ public class ContactsActivity extends BaseFragment implements NotificationCenter emptyTextLayout.setOrientation(LinearLayout.VERTICAL); ((FrameLayout) fragmentView).addView(emptyTextLayout); FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) emptyTextLayout.getLayoutParams(); - layoutParams.width = FrameLayout.LayoutParams.MATCH_PARENT; - layoutParams.height = FrameLayout.LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; + layoutParams.height = LayoutHelper.MATCH_PARENT; layoutParams.gravity = Gravity.TOP; emptyTextLayout.setLayoutParams(layoutParams); emptyTextLayout.setOnTouchListener(new View.OnTouchListener() { @@ -224,16 +227,16 @@ public class ContactsActivity extends BaseFragment implements NotificationCenter emptyTextView.setText(LocaleController.getString("NoContacts", R.string.NoContacts)); emptyTextLayout.addView(emptyTextView); LinearLayout.LayoutParams layoutParams1 = (LinearLayout.LayoutParams) emptyTextView.getLayoutParams(); - layoutParams1.width = LinearLayout.LayoutParams.MATCH_PARENT; - layoutParams1.height = LinearLayout.LayoutParams.MATCH_PARENT; + layoutParams1.width = LayoutHelper.MATCH_PARENT; + layoutParams1.height = LayoutHelper.MATCH_PARENT; layoutParams1.weight = 0.5f; emptyTextView.setLayoutParams(layoutParams1); FrameLayout frameLayout = new FrameLayout(context); emptyTextLayout.addView(frameLayout); layoutParams1 = (LinearLayout.LayoutParams) frameLayout.getLayoutParams(); - layoutParams1.width = LinearLayout.LayoutParams.MATCH_PARENT; - layoutParams1.height = LinearLayout.LayoutParams.MATCH_PARENT; + layoutParams1.width = LayoutHelper.MATCH_PARENT; + layoutParams1.height = LayoutHelper.MATCH_PARENT; layoutParams1.weight = 0.5f; frameLayout.setLayoutParams(layoutParams1); @@ -251,8 +254,8 @@ public class ContactsActivity extends BaseFragment implements NotificationCenter } ((FrameLayout) fragmentView).addView(listView); layoutParams = (FrameLayout.LayoutParams) listView.getLayoutParams(); - layoutParams.width = FrameLayout.LayoutParams.MATCH_PARENT; - layoutParams.height = FrameLayout.LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; + layoutParams.height = LayoutHelper.MATCH_PARENT; listView.setLayoutParams(layoutParams); listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { @@ -290,7 +293,7 @@ public class ContactsActivity extends BaseFragment implements NotificationCenter if (row < 0 || section < 0) { return; } - if (!onlyUsers && section == 0) { + if ((!onlyUsers || chat_id != 0) && section == 0) { if (needPhonebook) { if (row == 0) { try { @@ -302,6 +305,10 @@ public class ContactsActivity extends BaseFragment implements NotificationCenter FileLog.e("tmessages", e); } } + } else if (chat_id != 0) { + if (row == 0) { + presentFragment(new GroupInviteActivity(chat_id)); + } } else { if (row == 0) { if (!MessagesController.isFeatureEnabled("chat_create", ContactsActivity.this)) { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/CountrySelectActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/CountrySelectActivity.java index 2eb56da69..4bc604486 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/CountrySelectActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/CountrySelectActivity.java @@ -32,6 +32,7 @@ import org.telegram.ui.ActionBar.ActionBar; import org.telegram.ui.ActionBar.ActionBarMenu; import org.telegram.ui.ActionBar.ActionBarMenuItem; import org.telegram.ui.ActionBar.BaseFragment; +import org.telegram.ui.Components.LayoutHelper; import org.telegram.ui.Components.LetterSectionsListView; public class CountrySelectActivity extends BaseFragment { @@ -134,8 +135,8 @@ public class CountrySelectActivity extends BaseFragment { emptyTextLayout.setOrientation(LinearLayout.VERTICAL); ((FrameLayout) fragmentView).addView(emptyTextLayout); FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) emptyTextLayout.getLayoutParams(); - layoutParams.width = FrameLayout.LayoutParams.MATCH_PARENT; - layoutParams.height = FrameLayout.LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; + layoutParams.height = LayoutHelper.MATCH_PARENT; layoutParams.gravity = Gravity.TOP; emptyTextLayout.setLayoutParams(layoutParams); emptyTextLayout.setOnTouchListener(new View.OnTouchListener() { @@ -152,16 +153,16 @@ public class CountrySelectActivity extends BaseFragment { emptyTextView.setText(LocaleController.getString("NoResult", R.string.NoResult)); emptyTextLayout.addView(emptyTextView); LinearLayout.LayoutParams layoutParams1 = (LinearLayout.LayoutParams) emptyTextView.getLayoutParams(); - layoutParams1.width = LinearLayout.LayoutParams.MATCH_PARENT; - layoutParams1.height = LinearLayout.LayoutParams.MATCH_PARENT; + layoutParams1.width = LayoutHelper.MATCH_PARENT; + layoutParams1.height = LayoutHelper.MATCH_PARENT; layoutParams1.weight = 0.5f; emptyTextView.setLayoutParams(layoutParams1); FrameLayout frameLayout = new FrameLayout(context); emptyTextLayout.addView(frameLayout); layoutParams1 = (LinearLayout.LayoutParams) frameLayout.getLayoutParams(); - layoutParams1.width = LinearLayout.LayoutParams.MATCH_PARENT; - layoutParams1.height = LinearLayout.LayoutParams.MATCH_PARENT; + layoutParams1.width = LayoutHelper.MATCH_PARENT; + layoutParams1.height = LayoutHelper.MATCH_PARENT; layoutParams1.weight = 0.5f; frameLayout.setLayoutParams(layoutParams1); @@ -179,8 +180,8 @@ public class CountrySelectActivity extends BaseFragment { } ((FrameLayout) fragmentView).addView(listView); layoutParams = (FrameLayout.LayoutParams) listView.getLayoutParams(); - layoutParams.width = FrameLayout.LayoutParams.MATCH_PARENT; - layoutParams.height = FrameLayout.LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; + layoutParams.height = LayoutHelper.MATCH_PARENT; listView.setLayoutParams(layoutParams); listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/DocumentSelectActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/DocumentSelectActivity.java index 1daa1499b..cf02ca29f 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/DocumentSelectActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/DocumentSelectActivity.java @@ -36,10 +36,11 @@ import org.telegram.messenger.Utilities; import org.telegram.ui.Adapters.BaseFragmentAdapter; import org.telegram.ui.ActionBar.ActionBar; import org.telegram.ui.ActionBar.ActionBarMenu; -import org.telegram.ui.AnimationCompat.AnimatorSetProxy; -import org.telegram.ui.AnimationCompat.ObjectAnimatorProxy; +import org.telegram.android.AnimationCompat.AnimatorSetProxy; +import org.telegram.android.AnimationCompat.ObjectAnimatorProxy; import org.telegram.ui.ActionBar.BaseFragment; import org.telegram.ui.Cells.SharedDocumentCell; +import org.telegram.ui.Components.LayoutHelper; import java.io.BufferedReader; import java.io.File; @@ -188,7 +189,7 @@ public class DocumentSelectActivity extends BaseFragment { LinearLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams) selectedMessagesCountTextView.getLayoutParams(); layoutParams.weight = 1; layoutParams.width = 0; - layoutParams.height = LinearLayout.LayoutParams.MATCH_PARENT; + layoutParams.height = LayoutHelper.MATCH_PARENT; selectedMessagesCountTextView.setLayoutParams(layoutParams); actionModeViews.add(actionMode.addItem(done, R.drawable.ic_ab_done_gray, R.drawable.bar_selector_mode, null, AndroidUtilities.dp(54))); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/GroupCreateActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/GroupCreateActivity.java index 5f18cf414..ba5470f4b 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/GroupCreateActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/GroupCreateActivity.java @@ -54,6 +54,7 @@ import org.telegram.ui.ActionBar.ActionBar; import org.telegram.ui.ActionBar.ActionBarMenu; import org.telegram.ui.ActionBar.BaseFragment; import org.telegram.ui.Cells.UserCell; +import org.telegram.ui.Components.LayoutHelper; import org.telegram.ui.Components.LetterSectionsListView; import java.util.ArrayList; @@ -187,7 +188,7 @@ public class GroupCreateActivity extends BaseFragment implements NotificationCen searchListViewAdapter = new SearchAdapter(context, null, false); searchListViewAdapter.setCheckedMap(selectedContacts); searchListViewAdapter.setUseUserCell(true); - listViewAdapter = new ContactsAdapter(context, true, false, null); + listViewAdapter = new ContactsAdapter(context, true, false, null, false); listViewAdapter.setCheckedMap(selectedContacts); fragmentView = new LinearLayout(context); @@ -197,8 +198,8 @@ public class GroupCreateActivity extends BaseFragment implements NotificationCen FrameLayout frameLayout = new FrameLayout(context); linearLayout.addView(frameLayout); LinearLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams) frameLayout.getLayoutParams(); - layoutParams.width = LinearLayout.LayoutParams.MATCH_PARENT; - layoutParams.height = LinearLayout.LayoutParams.WRAP_CONTENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; + layoutParams.height = LayoutHelper.WRAP_CONTENT; layoutParams.gravity = Gravity.TOP; frameLayout.setLayoutParams(layoutParams); @@ -219,8 +220,8 @@ public class GroupCreateActivity extends BaseFragment implements NotificationCen AndroidUtilities.clearCursorDrawable(userSelectEditText); frameLayout.addView(userSelectEditText); FrameLayout.LayoutParams layoutParams1 = (FrameLayout.LayoutParams) userSelectEditText.getLayoutParams(); - layoutParams1.width = FrameLayout.LayoutParams.MATCH_PARENT; - layoutParams1.height = FrameLayout.LayoutParams.WRAP_CONTENT; + layoutParams1.width = LayoutHelper.MATCH_PARENT; + layoutParams1.height = LayoutHelper.WRAP_CONTENT; layoutParams1.leftMargin = AndroidUtilities.dp(10); layoutParams1.rightMargin = AndroidUtilities.dp(10); layoutParams1.gravity = Gravity.TOP; @@ -325,8 +326,8 @@ public class GroupCreateActivity extends BaseFragment implements NotificationCen emptyTextLayout.setOrientation(LinearLayout.VERTICAL); linearLayout.addView(emptyTextLayout); layoutParams = (LinearLayout.LayoutParams) emptyTextLayout.getLayoutParams(); - layoutParams.width = FrameLayout.LayoutParams.MATCH_PARENT; - layoutParams.height = FrameLayout.LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; + layoutParams.height = LayoutHelper.MATCH_PARENT; emptyTextLayout.setLayoutParams(layoutParams); emptyTextLayout.setOnTouchListener(new View.OnTouchListener() { @Override @@ -342,16 +343,16 @@ public class GroupCreateActivity extends BaseFragment implements NotificationCen emptyTextView.setText(LocaleController.getString("NoContacts", R.string.NoContacts)); emptyTextLayout.addView(emptyTextView); layoutParams = (LinearLayout.LayoutParams) emptyTextView.getLayoutParams(); - layoutParams.width = LinearLayout.LayoutParams.MATCH_PARENT; - layoutParams.height = LinearLayout.LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; + layoutParams.height = LayoutHelper.MATCH_PARENT; layoutParams.weight = 0.5f; emptyTextView.setLayoutParams(layoutParams); FrameLayout frameLayout2 = new FrameLayout(context); emptyTextLayout.addView(frameLayout2); layoutParams = (LinearLayout.LayoutParams) frameLayout2.getLayoutParams(); - layoutParams.width = LinearLayout.LayoutParams.MATCH_PARENT; - layoutParams.height = LinearLayout.LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; + layoutParams.height = LayoutHelper.MATCH_PARENT; layoutParams.weight = 0.5f; frameLayout2.setLayoutParams(layoutParams); @@ -369,8 +370,8 @@ public class GroupCreateActivity extends BaseFragment implements NotificationCen } linearLayout.addView(listView); layoutParams = (LinearLayout.LayoutParams) listView.getLayoutParams(); - layoutParams.width = LinearLayout.LayoutParams.MATCH_PARENT; - layoutParams.height = LinearLayout.LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; + layoutParams.height = LayoutHelper.MATCH_PARENT; listView.setLayoutParams(layoutParams); listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override diff --git a/TMessagesProj/src/main/java/org/telegram/ui/GroupCreateFinalActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/GroupCreateFinalActivity.java index 6436ef0ce..a0aecf567 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/GroupCreateFinalActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/GroupCreateFinalActivity.java @@ -47,6 +47,7 @@ import org.telegram.ui.Components.AvatarUpdater; import org.telegram.ui.Components.BackupImageView; import org.telegram.ui.ActionBar.BaseFragment; import org.telegram.ui.Components.FrameLayoutFixed; +import org.telegram.ui.Components.LayoutHelper; import java.util.ArrayList; import java.util.concurrent.Semaphore; @@ -203,8 +204,8 @@ public class GroupCreateFinalActivity extends BaseFragment implements Notificati FrameLayout frameLayout = new FrameLayoutFixed(context); linearLayout.addView(frameLayout); LinearLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams) frameLayout.getLayoutParams(); - layoutParams.width = LinearLayout.LayoutParams.MATCH_PARENT; - layoutParams.height = LinearLayout.LayoutParams.WRAP_CONTENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; + layoutParams.height = LayoutHelper.WRAP_CONTENT; layoutParams.gravity = Gravity.TOP | Gravity.LEFT; frameLayout.setLayoutParams(layoutParams); @@ -276,8 +277,8 @@ public class GroupCreateFinalActivity extends BaseFragment implements Notificati nameTextView.setTextColor(0xff212121); frameLayout.addView(nameTextView); layoutParams1 = (FrameLayout.LayoutParams) nameTextView.getLayoutParams(); - layoutParams1.width = FrameLayout.LayoutParams.MATCH_PARENT; - layoutParams1.height = FrameLayout.LayoutParams.WRAP_CONTENT; + layoutParams1.width = LayoutHelper.MATCH_PARENT; + layoutParams1.height = LayoutHelper.WRAP_CONTENT; layoutParams1.leftMargin = LocaleController.isRTL ? AndroidUtilities.dp(16) : AndroidUtilities.dp(96); layoutParams1.rightMargin = LocaleController.isRTL ? AndroidUtilities.dp(96) : AndroidUtilities.dp(16); layoutParams1.gravity = Gravity.CENTER_VERTICAL; @@ -313,8 +314,8 @@ public class GroupCreateFinalActivity extends BaseFragment implements Notificati listView.setAdapter(listAdapter = new ListAdapter(context)); linearLayout.addView(listView); layoutParams = (LinearLayout.LayoutParams) listView.getLayoutParams(); - layoutParams.width = LinearLayout.LayoutParams.MATCH_PARENT; - layoutParams.height = LinearLayout.LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; + layoutParams.height = LayoutHelper.MATCH_PARENT; listView.setLayoutParams(layoutParams); return fragmentView; @@ -369,6 +370,12 @@ public class GroupCreateFinalActivity extends BaseFragment implements Notificati } } + @Override + public void onOpenAnimationEnd() { + nameTextView.requestFocus(); + AndroidUtilities.showKeyboard(nameTextView); + } + @Override public void didReceivedNotification(int id, final Object... args) { if (id == NotificationCenter.updateInterfaces) { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/GroupInviteActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/GroupInviteActivity.java new file mode 100644 index 000000000..7af61e6a9 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/GroupInviteActivity.java @@ -0,0 +1,339 @@ +/* + * This is the source code of Telegram for Android v. 2.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-2015. + */ + +package org.telegram.ui; + +import android.app.AlertDialog; +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.os.Build; +import android.view.Gravity; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.AdapterView; +import android.widget.FrameLayout; +import android.widget.ListView; +import android.widget.ProgressBar; +import android.widget.Toast; + +import org.telegram.android.AndroidUtilities; +import org.telegram.android.LocaleController; +import org.telegram.android.MessagesController; +import org.telegram.android.NotificationCenter; +import org.telegram.messenger.ApplicationLoader; +import org.telegram.messenger.ConnectionsManager; +import org.telegram.messenger.FileLog; +import org.telegram.messenger.R; +import org.telegram.messenger.RPCRequest; +import org.telegram.messenger.TLObject; +import org.telegram.messenger.TLRPC; +import org.telegram.ui.ActionBar.ActionBar; +import org.telegram.ui.ActionBar.BaseFragment; +import org.telegram.ui.Adapters.BaseFragmentAdapter; +import org.telegram.ui.Cells.TextBlockCell; +import org.telegram.ui.Cells.TextInfoPrivacyCell; +import org.telegram.ui.Cells.TextSettingsCell; +import org.telegram.ui.Components.LayoutHelper; + +public class GroupInviteActivity extends BaseFragment implements NotificationCenter.NotificationCenterDelegate { + + private ListAdapter listAdapter; + + private int chat_id; + private boolean loading; + private TLRPC.ExportedChatInvite invite; + + private int linkRow; + private int linkInfoRow; + private int copyLinkRow; + private int revokeLinkRow; + private int shareLinkRow; + private int shadowRow; + private int rowCount; + + public GroupInviteActivity(int cid) { + super(); + chat_id = cid; + } + + @Override + public boolean onFragmentCreate() { + super.onFragmentCreate(); + + NotificationCenter.getInstance().addObserver(this, NotificationCenter.chatInfoDidLoaded); + MessagesController.getInstance().loadFullChat(chat_id, classGuid, true); + loading = true; + + rowCount = 0; + linkRow = rowCount++; + linkInfoRow = rowCount++; + copyLinkRow = rowCount++; + revokeLinkRow = rowCount++; + shareLinkRow = rowCount++; + shadowRow = rowCount++; + + return true; + } + + @Override + public void onFragmentDestroy() { + NotificationCenter.getInstance().removeObserver(this, NotificationCenter.chatInfoDidLoaded); + } + + @Override + public View createView(Context context, LayoutInflater inflater) { + actionBar.setBackButtonImage(R.drawable.ic_ab_back); + actionBar.setAllowOverlayTitle(true); + actionBar.setTitle(LocaleController.getString("InviteLink", R.string.InviteLink)); + 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); + + FrameLayout progressView = new FrameLayout(context); + frameLayout.addView(progressView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT)); + + ProgressBar progressBar = new ProgressBar(context); + progressView.addView(progressBar, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER)); + + ListView listView = new ListView(context); + listView.setDivider(null); + listView.setDividerHeight(0); + listView.setEmptyView(progressView); + listView.setVerticalScrollBarEnabled(false); + listView.setDrawSelectorOnTop(true); + frameLayout.addView(listView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.TOP | Gravity.LEFT)); + listView.setAdapter(listAdapter); + listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { + @Override + public void onItemClick(AdapterView adapterView, View view, final int i, long l) { + if (getParentActivity() == null) { + return; + } + if (i == copyLinkRow || i == linkRow) { + 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); + } + } else if (i == shareLinkRow) { + if (invite == null) { + return; + } + try { + Intent intent = new Intent(Intent.ACTION_SEND); + intent.setType("text/plain"); + intent.putExtra(Intent.EXTRA_TEXT, invite.link); + getParentActivity().startActivityForResult(Intent.createChooser(intent, LocaleController.getString("InviteToGroupByLink", R.string.InviteToGroupByLink)), 500); + } catch (Exception e) { + FileLog.e("tmessages", e); + } + } else if (i == revokeLinkRow) { + AlertDialog.Builder builder = new AlertDialog.Builder(getParentActivity()); + builder.setMessage(LocaleController.getString("RevokeAlert", R.string.RevokeAlert)); + builder.setTitle(LocaleController.getString("RevokeLink", R.string.RevokeLink)); + builder.setPositiveButton(LocaleController.getString("RevokeButton", R.string.RevokeButton), new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialogInterface, int i) { + generateLink(true); + } + }); + builder.setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), null); + showAlertDialog(builder); + } + } + }); + + return fragmentView; + } + + @Override + public void didReceivedNotification(int id, Object... args) { + if (id == NotificationCenter.chatInfoDidLoaded) { + if (args.length != 3) { + return; + } + int cid = (int) args[0]; + int guid = (int) args[2]; + if (cid == chat_id && guid == classGuid) { + invite = MessagesController.getInstance().getExportedInvite(chat_id); + if (!(invite instanceof TLRPC.TL_chatInviteExported)) { + generateLink(false); + } else { + loading = false; + if (listAdapter != null) { + listAdapter.notifyDataSetChanged(); + } + } + } + } + } + + @Override + public void onResume() { + super.onResume(); + if (listAdapter != null) { + listAdapter.notifyDataSetChanged(); + } + } + + private void generateLink(final boolean request) { + loading = true; + TLRPC.TL_messages_exportChatInvite req = new TLRPC.TL_messages_exportChatInvite(); + req.chat_id = chat_id; + final long reqId = ConnectionsManager.getInstance().performRpc(req, new RPCRequest.RPCRequestDelegate() { + @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; + if (request) { + if (getParentActivity() == null) { + return; + } + AlertDialog.Builder builder = new AlertDialog.Builder(getParentActivity()); + builder.setMessage(LocaleController.getString("RevokeAlertNewLink", R.string.RevokeAlertNewLink)); + builder.setTitle(LocaleController.getString("RevokeLink", R.string.RevokeLink)); + builder.setNegativeButton(LocaleController.getString("OK", R.string.OK), null); + showAlertDialog(builder); + } + } + loading = false; + listAdapter.notifyDataSetChanged(); + } + }); + } + }); + ConnectionsManager.getInstance().bindRequestToGuid(reqId, classGuid); + if (listAdapter != null) { + listAdapter.notifyDataSetChanged(); + } + } + + 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 == revokeLinkRow || i == copyLinkRow || i == shareLinkRow || i == linkRow; + } + + @Override + public int getCount() { + return loading ? 0 : 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 == copyLinkRow) { + textCell.setText(LocaleController.getString("CopyLink", R.string.CopyLink), true); + } else if (i == shareLinkRow) { + textCell.setText(LocaleController.getString("ShareLink", R.string.ShareLink), false); + } else if (i == revokeLinkRow) { + textCell.setText(LocaleController.getString("RevokeLink", R.string.RevokeLink), true); + } + } else if (type == 1) { + if (view == null) { + view = new TextInfoPrivacyCell(mContext); + } + if (i == shadowRow) { + ((TextInfoPrivacyCell) view).setText(""); + view.setBackgroundResource(R.drawable.greydivider_bottom); + } else if (i == linkInfoRow) { + ((TextInfoPrivacyCell) view).setText(LocaleController.getString("LinkInfo", R.string.LinkInfo)); + view.setBackgroundResource(R.drawable.greydivider); + } + } else if (type == 2) { + if (view == null) { + view = new TextBlockCell(mContext); + view.setBackgroundColor(0xffffffff); + } + ((TextBlockCell) view).setText(invite != null ? invite.link : "error", false); + } + return view; + } + + @Override + public int getItemViewType(int i) { + if (i == copyLinkRow || i == shareLinkRow || i == revokeLinkRow) { + return 0; + } else if (i == shadowRow || i == linkInfoRow) { + return 1; + } else if (i == linkRow) { + return 2; + } + return 0; + } + + @Override + public int getViewTypeCount() { + return 3; + } + + @Override + public boolean isEmpty() { + return loading; + } + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/LanguageSelectActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/LanguageSelectActivity.java index a6efe56be..0e17c33d2 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/LanguageSelectActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/LanguageSelectActivity.java @@ -35,6 +35,7 @@ import org.telegram.ui.ActionBar.ActionBar; import org.telegram.ui.ActionBar.ActionBarMenu; import org.telegram.ui.ActionBar.ActionBarMenuItem; import org.telegram.ui.ActionBar.BaseFragment; +import org.telegram.ui.Components.LayoutHelper; import java.util.ArrayList; import java.util.Timer; @@ -113,8 +114,8 @@ public class LanguageSelectActivity extends BaseFragment { emptyTextLayout.setOrientation(LinearLayout.VERTICAL); ((FrameLayout) fragmentView).addView(emptyTextLayout); FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) emptyTextLayout.getLayoutParams(); - layoutParams.width = FrameLayout.LayoutParams.MATCH_PARENT; - layoutParams.height = FrameLayout.LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; + layoutParams.height = LayoutHelper.MATCH_PARENT; layoutParams.gravity = Gravity.TOP; emptyTextLayout.setLayoutParams(layoutParams); emptyTextLayout.setOnTouchListener(new View.OnTouchListener() { @@ -131,16 +132,16 @@ public class LanguageSelectActivity extends BaseFragment { emptyTextView.setText(LocaleController.getString("NoResult", R.string.NoResult)); emptyTextLayout.addView(emptyTextView); LinearLayout.LayoutParams layoutParams1 = (LinearLayout.LayoutParams) emptyTextView.getLayoutParams(); - layoutParams1.width = LinearLayout.LayoutParams.MATCH_PARENT; - layoutParams1.height = LinearLayout.LayoutParams.MATCH_PARENT; + layoutParams1.width = LayoutHelper.MATCH_PARENT; + layoutParams1.height = LayoutHelper.MATCH_PARENT; layoutParams1.weight = 0.5f; emptyTextView.setLayoutParams(layoutParams1); FrameLayout frameLayout = new FrameLayout(context); emptyTextLayout.addView(frameLayout); layoutParams1 = (LinearLayout.LayoutParams) frameLayout.getLayoutParams(); - layoutParams1.width = LinearLayout.LayoutParams.MATCH_PARENT; - layoutParams1.height = LinearLayout.LayoutParams.MATCH_PARENT; + layoutParams1.width = LayoutHelper.MATCH_PARENT; + layoutParams1.height = LayoutHelper.MATCH_PARENT; layoutParams1.weight = 0.5f; frameLayout.setLayoutParams(layoutParams1); @@ -152,8 +153,8 @@ public class LanguageSelectActivity extends BaseFragment { listView.setAdapter(listAdapter); ((FrameLayout) fragmentView).addView(listView); layoutParams = (FrameLayout.LayoutParams) listView.getLayoutParams(); - layoutParams.width = FrameLayout.LayoutParams.MATCH_PARENT; - layoutParams.height = FrameLayout.LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; + layoutParams.height = LayoutHelper.MATCH_PARENT; listView.setLayoutParams(layoutParams); listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/LastSeenActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/LastSeenActivity.java index b2c6096be..fcc8ace2a 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/LastSeenActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/LastSeenActivity.java @@ -46,6 +46,7 @@ import org.telegram.ui.Adapters.BaseFragmentAdapter; import org.telegram.ui.Cells.HeaderCell; import org.telegram.ui.Cells.TextInfoPrivacyCell; import org.telegram.ui.Cells.TextSettingsCell; +import org.telegram.ui.Components.LayoutHelper; import java.util.ArrayList; @@ -154,8 +155,8 @@ public class LastSeenActivity extends BaseFragment implements NotificationCenter listView.setDrawSelectorOnTop(true); frameLayout.addView(listView); FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) listView.getLayoutParams(); - layoutParams.width = FrameLayout.LayoutParams.MATCH_PARENT; - layoutParams.height = FrameLayout.LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; + layoutParams.height = LayoutHelper.MATCH_PARENT; layoutParams.gravity = Gravity.TOP; listView.setLayoutParams(layoutParams); listView.setAdapter(listAdapter); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/LastSeenUsersActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/LastSeenUsersActivity.java index a0ce4a77e..b4d4c93d0 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/LastSeenUsersActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/LastSeenUsersActivity.java @@ -35,6 +35,7 @@ import org.telegram.ui.ActionBar.BaseFragment; import org.telegram.ui.Adapters.BaseFragmentAdapter; import org.telegram.ui.Cells.TextInfoCell; import org.telegram.ui.Cells.UserCell; +import org.telegram.ui.Components.LayoutHelper; import java.util.ArrayList; @@ -126,8 +127,8 @@ public class LastSeenUsersActivity extends BaseFragment implements NotificationC emptyTextView.setText(LocaleController.getString("NoContacts", R.string.NoContacts)); frameLayout.addView(emptyTextView); FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) emptyTextView.getLayoutParams(); - layoutParams.width = FrameLayout.LayoutParams.MATCH_PARENT; - layoutParams.height = FrameLayout.LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; + layoutParams.height = LayoutHelper.MATCH_PARENT; layoutParams.gravity = Gravity.TOP; emptyTextView.setLayoutParams(layoutParams); emptyTextView.setOnTouchListener(new View.OnTouchListener() { @@ -148,8 +149,8 @@ public class LastSeenUsersActivity extends BaseFragment implements NotificationC } frameLayout.addView(listView); layoutParams = (FrameLayout.LayoutParams) listView.getLayoutParams(); - layoutParams.width = FrameLayout.LayoutParams.MATCH_PARENT; - layoutParams.height = FrameLayout.LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; + layoutParams.height = LayoutHelper.MATCH_PARENT; listView.setLayoutParams(layoutParams); listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/LaunchActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/LaunchActivity.java index e75d44830..6d517a28a 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/LaunchActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/LaunchActivity.java @@ -9,6 +9,7 @@ package org.telegram.ui; import android.app.Activity; +import android.app.AlertDialog; import android.app.ProgressDialog; import android.content.ContentResolver; import android.content.DialogInterface; @@ -59,6 +60,7 @@ import org.telegram.ui.Adapters.DrawerLayoutAdapter; import org.telegram.ui.ActionBar.ActionBarLayout; import org.telegram.ui.ActionBar.BaseFragment; import org.telegram.ui.ActionBar.DrawerLayoutContainer; +import org.telegram.ui.Components.LayoutHelper; import org.telegram.ui.Components.PasscodeView; import java.io.BufferedReader; @@ -88,9 +90,10 @@ public class LaunchActivity extends Activity implements ActionBarLayout.ActionBa private FrameLayout shadowTablet; private FrameLayout shadowTabletSide; private ImageView backgroundTablet; - private DrawerLayoutContainer drawerLayoutContainer; + protected DrawerLayoutContainer drawerLayoutContainer; private DrawerLayoutAdapter drawerLayoutAdapter; private PasscodeView passcodeView; + private AlertDialog visibleDialog; private Intent passcodeSaveIntent; private boolean passcodeSaveIntentIsNew; @@ -150,8 +153,8 @@ public class LaunchActivity extends Activity implements ActionBarLayout.ActionBa RelativeLayout launchLayout = new RelativeLayout(this); drawerLayoutContainer.addView(launchLayout); FrameLayout.LayoutParams layoutParams1 = (FrameLayout.LayoutParams) launchLayout.getLayoutParams(); - layoutParams1.width = FrameLayout.LayoutParams.MATCH_PARENT; - layoutParams1.height = FrameLayout.LayoutParams.MATCH_PARENT; + layoutParams1.width = LayoutHelper.MATCH_PARENT; + layoutParams1.height = LayoutHelper.MATCH_PARENT; launchLayout.setLayoutParams(layoutParams1); backgroundTablet = new ImageView(this); @@ -159,21 +162,21 @@ public class LaunchActivity extends Activity implements ActionBarLayout.ActionBa backgroundTablet.setImageResource(R.drawable.cats); launchLayout.addView(backgroundTablet); RelativeLayout.LayoutParams relativeLayoutParams = (RelativeLayout.LayoutParams) backgroundTablet.getLayoutParams(); - relativeLayoutParams.width = RelativeLayout.LayoutParams.MATCH_PARENT; - relativeLayoutParams.height = RelativeLayout.LayoutParams.MATCH_PARENT; + relativeLayoutParams.width = LayoutHelper.MATCH_PARENT; + relativeLayoutParams.height = LayoutHelper.MATCH_PARENT; backgroundTablet.setLayoutParams(relativeLayoutParams); launchLayout.addView(actionBarLayout); relativeLayoutParams = (RelativeLayout.LayoutParams) actionBarLayout.getLayoutParams(); - relativeLayoutParams.width = RelativeLayout.LayoutParams.MATCH_PARENT; - relativeLayoutParams.height = RelativeLayout.LayoutParams.MATCH_PARENT; + relativeLayoutParams.width = LayoutHelper.MATCH_PARENT; + relativeLayoutParams.height = LayoutHelper.MATCH_PARENT; actionBarLayout.setLayoutParams(relativeLayoutParams); rightActionBarLayout = new ActionBarLayout(this); launchLayout.addView(rightActionBarLayout); relativeLayoutParams = (RelativeLayout.LayoutParams)rightActionBarLayout.getLayoutParams(); relativeLayoutParams.width = AndroidUtilities.dp(320); - relativeLayoutParams.height = RelativeLayout.LayoutParams.MATCH_PARENT; + relativeLayoutParams.height = LayoutHelper.MATCH_PARENT; rightActionBarLayout.setLayoutParams(relativeLayoutParams); rightActionBarLayout.init(rightFragmentsStack); rightActionBarLayout.setDelegate(this); @@ -183,7 +186,7 @@ public class LaunchActivity extends Activity implements ActionBarLayout.ActionBa launchLayout.addView(shadowTabletSide); relativeLayoutParams = (RelativeLayout.LayoutParams) shadowTabletSide.getLayoutParams(); relativeLayoutParams.width = AndroidUtilities.dp(1); - relativeLayoutParams.height = RelativeLayout.LayoutParams.MATCH_PARENT; + relativeLayoutParams.height = LayoutHelper.MATCH_PARENT; shadowTabletSide.setLayoutParams(relativeLayoutParams); shadowTablet = new FrameLayout(this); @@ -191,8 +194,8 @@ public class LaunchActivity extends Activity implements ActionBarLayout.ActionBa shadowTablet.setBackgroundColor(0x7F000000); launchLayout.addView(shadowTablet); relativeLayoutParams = (RelativeLayout.LayoutParams) shadowTablet.getLayoutParams(); - relativeLayoutParams.width = RelativeLayout.LayoutParams.MATCH_PARENT; - relativeLayoutParams.height = RelativeLayout.LayoutParams.MATCH_PARENT; + relativeLayoutParams.width = LayoutHelper.MATCH_PARENT; + relativeLayoutParams.height = LayoutHelper.MATCH_PARENT; shadowTablet.setLayoutParams(relativeLayoutParams); shadowTablet.setOnTouchListener(new View.OnTouchListener() { @Override @@ -254,7 +257,7 @@ public class LaunchActivity extends Activity implements ActionBarLayout.ActionBa FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams)listView.getLayoutParams(); Point screenSize = AndroidUtilities.getRealScreenSize(); layoutParams.width = AndroidUtilities.isTablet() ? AndroidUtilities.dp(320) : Math.min(screenSize.x, screenSize.y) - AndroidUtilities.dp(56); - layoutParams.height = FrameLayout.LayoutParams.MATCH_PARENT; + layoutParams.height = LayoutHelper.MATCH_PARENT; listView.setPadding(0, 0, 0, 0); listView.setChoiceMode(AbsListView.CHOICE_MODE_SINGLE); listView.setDivider(null); @@ -324,8 +327,8 @@ public class LaunchActivity extends Activity implements ActionBarLayout.ActionBa passcodeView = new PasscodeView(this); drawerLayoutContainer.addView(passcodeView); FrameLayout.LayoutParams layoutParams1 = (FrameLayout.LayoutParams) passcodeView.getLayoutParams(); - layoutParams1.width = FrameLayout.LayoutParams.MATCH_PARENT; - layoutParams1.height = FrameLayout.LayoutParams.MATCH_PARENT; + layoutParams1.width = LayoutHelper.MATCH_PARENT; + layoutParams1.height = LayoutHelper.MATCH_PARENT; passcodeView.setLayoutParams(layoutParams1); NotificationCenter.getInstance().postNotificationName(NotificationCenter.closeOtherAppActivities, this); @@ -669,6 +672,7 @@ public class LaunchActivity extends Activity implements ActionBarLayout.ActionBa Uri data = intent.getData(); if (data != null) { String username = null; + String group = null; String scheme = data.getScheme(); if (scheme != null) { if ((scheme.equals("http") || scheme.equals("https"))) { @@ -676,68 +680,29 @@ public class LaunchActivity extends Activity implements ActionBarLayout.ActionBa if (host.equals("telegram.me")) { String path = data.getPath(); if (path != null && path.length() >= 6) { - username = path.substring(1); + path = path.substring(1); + if (path.startsWith("joinchat/")) { + group = path.replace("joinchat/", ""); + } else { + username = path; + } } } } else if (scheme.equals("tg")) { - String url = data.toString().toLowerCase(); + String url = data.toString(); if (url.startsWith("tg:resolve") || url.startsWith("tg://resolve")) { url = url.replace("tg:resolve", "tg://telegram.org").replace("tg://resolve", "tg://telegram.org"); data = Uri.parse(url); username = data.getQueryParameter("domain"); + } else if (url.startsWith("tg:join") || url.startsWith("tg://join")) { + url = url.replace("tg:join", "tg://telegram.org").replace("tg://join", "tg://telegram.org"); + data = Uri.parse(url); + group = data.getQueryParameter("invite"); } } } - if (username != null) { - final ProgressDialog progressDialog = new ProgressDialog(this); - progressDialog.setMessage(LocaleController.getString("Loading", R.string.Loading)); - progressDialog.setCanceledOnTouchOutside(false); - progressDialog.setCancelable(false); - - TLRPC.TL_contacts_resolveUsername req = new TLRPC.TL_contacts_resolveUsername(); - req.username = username; - final long reqId = ConnectionsManager.getInstance().performRpc(req, new RPCRequest.RPCRequestDelegate() { - @Override - public void run(final TLObject response, final TLRPC.TL_error error) { - AndroidUtilities.runOnUIThread(new Runnable() { - @Override - public void run() { - if (!LaunchActivity.this.isFinishing()) { - try { - progressDialog.dismiss(); - } catch (Exception e) { - FileLog.e("tmessages", e); - } - if (error == null && actionBarLayout != null) { - TLRPC.User user = (TLRPC.User) response; - MessagesController.getInstance().putUser(user, false); - ArrayList users = new ArrayList<>(); - users.add(user); - MessagesStorage.getInstance().putUsersAndChats(users, null, false, true); - Bundle args = new Bundle(); - args.putInt("user_id", user.id); - ChatActivity fragment = new ChatActivity(args); - NotificationCenter.getInstance().postNotificationName(NotificationCenter.closeChats); - actionBarLayout.presentFragment(fragment, false, true, true); - } - } - } - }); - } - }); - - progressDialog.setButton(DialogInterface.BUTTON_NEGATIVE, LocaleController.getString("Cancel", R.string.Cancel), new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - ConnectionsManager.getInstance().cancelRpc(reqId, true); - try { - dialog.dismiss(); - } catch (Exception e) { - FileLog.e("tmessages", e); - } - } - }); - progressDialog.show(); + if (username != null || group != null) { + runLinkRequest(username, group, 0); } else { try { Cursor cursor = getContentResolver().query(intent.getData(), null, null, null, null); @@ -893,6 +858,189 @@ public class LaunchActivity extends Activity implements ActionBarLayout.ActionBa return false; } + private void runLinkRequest(final String username, final String group, final int state) { + final ProgressDialog progressDialog = new ProgressDialog(this); + progressDialog.setMessage(LocaleController.getString("Loading", R.string.Loading)); + progressDialog.setCanceledOnTouchOutside(false); + progressDialog.setCancelable(false); + long requestId = 0; + + if (username != null) { + TLRPC.TL_contacts_resolveUsername req = new TLRPC.TL_contacts_resolveUsername(); + req.username = username; + requestId = ConnectionsManager.getInstance().performRpc(req, new RPCRequest.RPCRequestDelegate() { + @Override + public void run(final TLObject response, final TLRPC.TL_error error) { + AndroidUtilities.runOnUIThread(new Runnable() { + @Override + public void run() { + if (!LaunchActivity.this.isFinishing()) { + try { + progressDialog.dismiss(); + } catch (Exception e) { + FileLog.e("tmessages", e); + } + if (error == null && actionBarLayout != null) { + TLRPC.User user = (TLRPC.User) response; + MessagesController.getInstance().putUser(user, false); + ArrayList users = new ArrayList<>(); + users.add(user); + MessagesStorage.getInstance().putUsersAndChats(users, null, false, true); + Bundle args = new Bundle(); + args.putInt("user_id", user.id); + ChatActivity fragment = new ChatActivity(args); + NotificationCenter.getInstance().postNotificationName(NotificationCenter.closeChats); + actionBarLayout.presentFragment(fragment, false, true, true); + } + } + } + }); + } + }); + } else if (group != null) { + if (state == 0) { + final TLRPC.TL_messages_checkChatInvite req = new TLRPC.TL_messages_checkChatInvite(); + req.hash = group; + requestId = ConnectionsManager.getInstance().performRpc(req, new RPCRequest.RPCRequestDelegate() { + @Override + public void run(final TLObject response, final TLRPC.TL_error error) { + AndroidUtilities.runOnUIThread(new Runnable() { + @Override + public void run() { + if (!LaunchActivity.this.isFinishing()) { + try { + progressDialog.dismiss(); + } catch (Exception e) { + FileLog.e("tmessages", e); + } + if (error == null && actionBarLayout != null) { + TLRPC.ChatInvite invite = (TLRPC.ChatInvite) response; + if (invite.chat != null && !invite.chat.left) { + MessagesController.getInstance().putChat(invite.chat, false); + ArrayList chats = new ArrayList<>(); + chats.add(invite.chat); + 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); + } else { + AlertDialog.Builder builder = new AlertDialog.Builder(LaunchActivity.this); + builder.setTitle(LocaleController.getString("AppName", R.string.AppName)); + builder.setMessage(LocaleController.formatString("JoinToGroup", R.string.JoinToGroup, invite.chat != null ? invite.chat.title : invite.title)); + builder.setPositiveButton(LocaleController.getString("OK", R.string.OK), new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialogInterface, int i) { + runLinkRequest(username, group, 1); + } + }); + builder.setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), null); + showAlertDialog(builder); + } + } else { + AlertDialog.Builder builder = new AlertDialog.Builder(LaunchActivity.this); + builder.setTitle(LocaleController.getString("AppName", R.string.AppName)); + builder.setMessage(LocaleController.getString("JoinToGroupErrorNotExist", R.string.JoinToGroupErrorNotExist)); + builder.setPositiveButton(LocaleController.getString("OK", R.string.OK), null); + showAlertDialog(builder); + } + } + } + }); + } + }); + } else if (state == 1) { + TLRPC.TL_messages_importChatInvite req = new TLRPC.TL_messages_importChatInvite(); + req.hash = group; + ConnectionsManager.getInstance().performRpc(req, new RPCRequest.RPCRequestDelegate() { + @Override + public void run(final TLObject response, final TLRPC.TL_error error) { + if (error == null) { + TLRPC.Updates updates = (TLRPC.Updates) response; + MessagesController.getInstance().processUpdates(updates, false); + } + AndroidUtilities.runOnUIThread(new Runnable() { + @Override + public void run() { + if (!LaunchActivity.this.isFinishing()) { + try { + progressDialog.dismiss(); + } catch (Exception e) { + FileLog.e("tmessages", e); + } + if (error == null) { + if (actionBarLayout != null) { + TLRPC.Updates updates = (TLRPC.Updates) response; + if (!updates.chats.isEmpty()) { + MessagesController.getInstance().putUsers(updates.users, false); + MessagesController.getInstance().putChats(updates.chats, false); + Bundle args = new Bundle(); + args.putInt("chat_id", updates.chats.get(0).id); + 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)); + if (error.text.equals("USERS_TOO_MUCH")) { + builder.setMessage(LocaleController.getString("JoinToGroupErrorFull", R.string.JoinToGroupErrorFull)); + } else { + builder.setMessage(LocaleController.getString("JoinToGroupErrorNotExist", R.string.JoinToGroupErrorNotExist)); + } + builder.setPositiveButton(LocaleController.getString("OK", R.string.OK), null); + showAlertDialog(builder); + } + } + } + }); + } + }); + } + } + + final long reqId = requestId; + progressDialog.setButton(DialogInterface.BUTTON_NEGATIVE, LocaleController.getString("Cancel", R.string.Cancel), new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + ConnectionsManager.getInstance().cancelRpc(reqId, true); + try { + dialog.dismiss(); + } catch (Exception e) { + FileLog.e("tmessages", e); + } + } + }); + progressDialog.show(); + } + + public AlertDialog showAlertDialog(AlertDialog.Builder builder) { + try { + if (visibleDialog != null) { + visibleDialog.dismiss(); + visibleDialog = null; + } + } catch (Exception e) { + FileLog.e("tmessages", e); + } + try { + visibleDialog = builder.show(); + visibleDialog.setCanceledOnTouchOutside(true); + visibleDialog.setOnDismissListener(new DialogInterface.OnDismissListener() { + @Override + public void onDismiss(DialogInterface dialog) { + visibleDialog = null; + } + }); + return visibleDialog; + } catch (Exception e) { + FileLog.e("tmessages", e); + } + return null; + } + @Override protected void onNewIntent(Intent intent) { super.onNewIntent(intent); @@ -935,7 +1083,7 @@ public class LaunchActivity extends Activity implements ActionBarLayout.ActionBa actionBarLayout.addFragmentToStack(fragment, actionBarLayout.fragmentsStack.size() - 1); } - if (!fragment.openVideoEditor(videoPath, true)) { + if (!fragment.openVideoEditor(videoPath, true, false)) { if (!AndroidUtilities.isTablet()) { messageFragment.finishFragment(true); } @@ -945,13 +1093,14 @@ public class LaunchActivity extends Activity implements ActionBarLayout.ActionBa SendMessagesHelper.prepareSendingVideo(videoPath, 0, 0, 0, 0, null, dialog_id, null); } } else { + if (sendingText != null) { + SendMessagesHelper.prepareSendingText(sendingText, dialog_id); + } + actionBarLayout.presentFragment(fragment, true); - if (sendingText != null) { - fragment.processSendingText(sendingText); - } if (photoPathsArray != null) { - SendMessagesHelper.prepareSendingPhotos(null, photoPathsArray, dialog_id, null); + SendMessagesHelper.prepareSendingPhotos(null, photoPathsArray, dialog_id, null, null); } if (documentsPathsArray != null || documentsUrisArray != null) { SendMessagesHelper.prepareSendingDocuments(documentsPathsArray, documentsOriginalPathsArray, documentsUrisArray, documentsMimeType, dialog_id, null); @@ -1018,7 +1167,7 @@ public class LaunchActivity extends Activity implements ActionBarLayout.ActionBa relativeLayoutParams = (RelativeLayout.LayoutParams) actionBarLayout.getLayoutParams(); relativeLayoutParams.width = leftWidth; - relativeLayoutParams.height = RelativeLayout.LayoutParams.MATCH_PARENT; + relativeLayoutParams.height = LayoutHelper.MATCH_PARENT; actionBarLayout.setLayoutParams(relativeLayoutParams); relativeLayoutParams = (RelativeLayout.LayoutParams) shadowTabletSide.getLayoutParams(); @@ -1027,7 +1176,7 @@ public class LaunchActivity extends Activity implements ActionBarLayout.ActionBa relativeLayoutParams = (RelativeLayout.LayoutParams) rightActionBarLayout.getLayoutParams(); relativeLayoutParams.width = AndroidUtilities.displaySize.x - leftWidth; - relativeLayoutParams.height = RelativeLayout.LayoutParams.MATCH_PARENT; + relativeLayoutParams.height = LayoutHelper.MATCH_PARENT; relativeLayoutParams.leftMargin = leftWidth; rightActionBarLayout.setLayoutParams(relativeLayoutParams); @@ -1049,8 +1198,8 @@ public class LaunchActivity extends Activity implements ActionBarLayout.ActionBa tabletFullSize = true; relativeLayoutParams = (RelativeLayout.LayoutParams) actionBarLayout.getLayoutParams(); - relativeLayoutParams.width = RelativeLayout.LayoutParams.MATCH_PARENT; - relativeLayoutParams.height = RelativeLayout.LayoutParams.MATCH_PARENT; + relativeLayoutParams.width = LayoutHelper.MATCH_PARENT; + relativeLayoutParams.height = LayoutHelper.MATCH_PARENT; actionBarLayout.setLayoutParams(relativeLayoutParams); shadowTabletSide.setVisibility(View.GONE); @@ -1131,6 +1280,14 @@ public class LaunchActivity extends Activity implements ActionBarLayout.ActionBa protected void onDestroy() { PhotoViewer.getInstance().destroyPhotoViewer(); SecretPhotoViewer.getInstance().destroyPhotoViewer(); + try { + if (visibleDialog != null) { + visibleDialog.dismiss(); + visibleDialog = null; + } + } catch (Exception e) { + FileLog.e("tmessages", e); + } super.onDestroy(); onFinish(); } @@ -1153,6 +1310,9 @@ public class LaunchActivity extends Activity implements ActionBarLayout.ActionBa ApplicationLoader.mainInterfacePaused = false; ConnectionsManager.getInstance().setAppPaused(false, false); updateCurrentConnectionState(); + if (PhotoViewer.getInstance().isVisible()) { + PhotoViewer.getInstance().onResume(); + } } @Override @@ -1413,7 +1573,7 @@ public class LaunchActivity extends Activity implements ActionBarLayout.ActionBa @Override public boolean needPresentFragment(BaseFragment fragment, boolean removeLast, boolean forceWithoutAnimation, ActionBarLayout layout) { if (AndroidUtilities.isTablet()) { - drawerLayoutContainer.setAllowOpenDrawer(!(fragment instanceof LoginActivity) && layersActionBarLayout.getVisibility() != View.VISIBLE, true); + drawerLayoutContainer.setAllowOpenDrawer(!(fragment instanceof LoginActivity || fragment instanceof CountrySelectActivity) && layersActionBarLayout.getVisibility() != View.VISIBLE, true); if (fragment instanceof MessagesActivity) { MessagesActivity messagesActivity = (MessagesActivity)fragment; if (messagesActivity.isMainDialogList() && layout != actionBarLayout) { @@ -1494,7 +1654,7 @@ public class LaunchActivity extends Activity implements ActionBarLayout.ActionBa } return true; } else { - drawerLayoutContainer.setAllowOpenDrawer(!(fragment instanceof LoginActivity), false); + drawerLayoutContainer.setAllowOpenDrawer(!(fragment instanceof LoginActivity || fragment instanceof CountrySelectActivity), false); return true; } } @@ -1502,7 +1662,7 @@ public class LaunchActivity extends Activity implements ActionBarLayout.ActionBa @Override public boolean needAddFragmentToStack(BaseFragment fragment, ActionBarLayout layout) { if (AndroidUtilities.isTablet()) { - drawerLayoutContainer.setAllowOpenDrawer(!(fragment instanceof LoginActivity) && layersActionBarLayout.getVisibility() != View.VISIBLE, true); + drawerLayoutContainer.setAllowOpenDrawer(!(fragment instanceof LoginActivity || fragment instanceof CountrySelectActivity) && layersActionBarLayout.getVisibility() != View.VISIBLE, true); if (fragment instanceof MessagesActivity) { MessagesActivity messagesActivity = (MessagesActivity)fragment; if (messagesActivity.isMainDialogList() && layout != actionBarLayout) { @@ -1559,7 +1719,7 @@ public class LaunchActivity extends Activity implements ActionBarLayout.ActionBa } return true; } else { - drawerLayoutContainer.setAllowOpenDrawer(!(fragment instanceof LoginActivity), false); + drawerLayoutContainer.setAllowOpenDrawer(!(fragment instanceof LoginActivity || fragment instanceof CountrySelectActivity), false); return true; } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/LocationActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/LocationActivity.java index 86e4b1cab..b275de811 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/LocationActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/LocationActivity.java @@ -8,11 +8,31 @@ package org.telegram.ui; +import android.animation.AnimatorSet; +import android.animation.ObjectAnimator; +import android.animation.StateListAnimator; import android.content.Context; +import android.content.Intent; +import android.graphics.Outline; import android.location.Location; import android.location.LocationManager; +import android.net.Uri; +import android.os.Build; +import android.text.TextUtils; +import android.util.TypedValue; +import android.view.Gravity; import android.view.LayoutInflater; +import android.view.MotionEvent; import android.view.View; +import android.view.ViewOutlineProvider; +import android.view.WindowManager; +import android.widget.AbsListView; +import android.widget.AdapterView; +import android.widget.EditText; +import android.widget.FrameLayout; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.ListView; import android.widget.TextView; import com.google.android.gms.maps.CameraUpdate; @@ -22,8 +42,8 @@ import com.google.android.gms.maps.MapView; import com.google.android.gms.maps.MapsInitializer; import com.google.android.gms.maps.model.BitmapDescriptorFactory; +import com.google.android.gms.maps.model.CircleOptions; import com.google.android.gms.maps.model.LatLng; -import com.google.android.gms.maps.model.Marker; import com.google.android.gms.maps.model.MarkerOptions; import org.telegram.android.AndroidUtilities; @@ -39,34 +59,68 @@ import org.telegram.messenger.R; import org.telegram.ui.ActionBar.ActionBar; import org.telegram.ui.ActionBar.ActionBarMenu; import org.telegram.ui.ActionBar.ActionBarMenuItem; +import org.telegram.ui.Adapters.BaseLocationAdapter; +import org.telegram.ui.Adapters.LocationActivityAdapter; +import org.telegram.ui.Adapters.LocationActivitySearchAdapter; import org.telegram.ui.Components.AvatarDrawable; import org.telegram.ui.Components.BackupImageView; import org.telegram.ui.ActionBar.BaseFragment; +import org.telegram.ui.Components.LayoutHelper; +import org.telegram.ui.Components.MapPlaceholderDrawable; +import java.util.ArrayList; import java.util.List; +import java.util.Locale; public class LocationActivity extends BaseFragment implements NotificationCenter.NotificationCenterDelegate { private GoogleMap googleMap; private TextView distanceTextView; - private Marker userMarker; - private Location myLocation; - private Location userLocation; - private MessageObject messageObject; private BackupImageView avatarImageView; private TextView nameTextView; + private MapView mapView; + private FrameLayout mapViewClip; + private LocationActivityAdapter adapter; + private ListView listView; + private ListView searchListView; + private LocationActivitySearchAdapter searchAdapter; + private LinearLayout emptyTextLayout; + private ImageView markerImageView; + private ImageView markerXImageView; + private ImageView locationButton; + + private AnimatorSet animatorSet; + + private boolean searching; + private boolean searchWas; + + private boolean wasResults; + + private Location myLocation; + private Location userLocation; + private int markerTop; + + private MessageObject messageObject; private boolean userLocationMoved = false; private boolean firstWas = false; - private MapView mapView; + private CircleOptions circleOptions; private LocationActivityDelegate delegate; - private final static int map_to_my_location = 1; + private int overScrollHeight = AndroidUtilities.displaySize.x - AndroidUtilities.getCurrentActionBarHeight() - AndroidUtilities.dp(66); + private int halfHeight; + + private final static int share = 1; private final static int map_list_menu_map = 2; private final static int map_list_menu_satellite = 3; private final static int map_list_menu_hybrid = 4; public interface LocationActivityDelegate { - void didSelectLocation(double latitude, double longitude); + void didSelectLocation(TLRPC.MessageMedia location); + } + + @Override + public boolean needAddActionBar() { + return messageObject != null; } @Override @@ -88,16 +142,20 @@ public class LocationActivity extends BaseFragment implements NotificationCenter if (mapView != null) { mapView.onDestroy(); } + if (adapter != null) { + adapter.destroy(); + } + if (searchAdapter != null) { + searchAdapter.destroy(); + } } @Override public View createView(Context context, LayoutInflater inflater) { actionBar.setBackButtonImage(R.drawable.ic_ab_back); actionBar.setAllowOverlayTitle(true); - if (messageObject != null) { - actionBar.setTitle(LocaleController.getString("ChatLocation", R.string.ChatLocation)); - } else { - actionBar.setTitle(LocaleController.getString("ShareLocation", R.string.ShareLocation)); + if (AndroidUtilities.isTablet()) { + actionBar.setOccupyStatusBar(false); } actionBar.setActionBarMenuOnItemClick(new ActionBar.ActionBarMenuOnItemClick() { @@ -117,52 +175,428 @@ public class LocationActivity extends BaseFragment implements NotificationCenter if (googleMap != null) { googleMap.setMapType(GoogleMap.MAP_TYPE_HYBRID); } - } else if (id == map_to_my_location) { - if (myLocation != null) { - LatLng latLng = new LatLng(myLocation.getLatitude(), myLocation.getLongitude()); - if (googleMap != null) { - CameraUpdate position = CameraUpdateFactory.newLatLngZoom(latLng, googleMap.getMaxZoomLevel() - 8); - googleMap.animateCamera(position); - } + } else if (id == share) { + try { + double lat = messageObject.messageOwner.media.geo.lat; + double lon = messageObject.messageOwner.media.geo._long; + getParentActivity().startActivity(new Intent(android.content.Intent.ACTION_VIEW, Uri.parse("geo:" + lat + "," + lon + "?q=" + lat + "," + lon))); + } catch (Exception e) { + FileLog.e("tmessages", e); } } } }); ActionBarMenu menu = actionBar.createMenu(); - menu.addItem(map_to_my_location, R.drawable.ic_ab_location); + if (messageObject != null) { + if (messageObject.messageOwner.media.title != null && messageObject.messageOwner.media.title.length() > 0) { + actionBar.setTitle(messageObject.messageOwner.media.title); + if (messageObject.messageOwner.media.address != null && messageObject.messageOwner.media.address.length() > 0) { + actionBar.setSubtitle(messageObject.messageOwner.media.address); + } + } else { + actionBar.setTitle(LocaleController.getString("ChatLocation", R.string.ChatLocation)); + } + menu.addItem(share, R.drawable.share); + } else { + actionBar.setTitle(LocaleController.getString("ShareLocation", R.string.ShareLocation)); + + ActionBarMenuItem item = menu.addItem(0, R.drawable.ic_ab_search).setIsSearchField(true).setActionBarMenuItemSearchListener(new ActionBarMenuItem.ActionBarMenuItemSearchListener() { + @Override + public void onSearchExpand() { + searching = true; + listView.setVisibility(View.GONE); + mapViewClip.setVisibility(View.GONE); + searchListView.setVisibility(View.VISIBLE); + searchListView.setEmptyView(emptyTextLayout); + } + + @Override + public boolean onSearchCollapse() { + searching = false; + searchWas = false; + searchListView.setEmptyView(null); + listView.setVisibility(View.VISIBLE); + mapViewClip.setVisibility(View.VISIBLE); + searchListView.setVisibility(View.GONE); + emptyTextLayout.setVisibility(View.GONE); + searchAdapter.searchDelayed(null, null); + return true; + } + + @Override + public void onTextChanged(EditText editText) { + if (searchAdapter == null) { + return; + } + String text = editText.getText().toString(); + if (text.length() != 0) { + searchWas = true; + } + searchAdapter.searchDelayed(text, userLocation); + } + }); + item.getSearchField().setHint(LocaleController.getString("Search", R.string.Search)); + } ActionBarMenuItem item = menu.addItem(0, R.drawable.ic_ab_other); item.addSubItem(map_list_menu_map, LocaleController.getString("Map", R.string.Map), 0); item.addSubItem(map_list_menu_satellite, LocaleController.getString("Satellite", R.string.Satellite), 0); item.addSubItem(map_list_menu_hybrid, LocaleController.getString("Hybrid", R.string.Hybrid), 0); + fragmentView = new FrameLayout(context) { + private boolean first = true; + + @Override + protected void onLayout(boolean changed, int left, int top, int right, int bottom) { + super.onLayout(changed, left, top, right, bottom); + + if (changed) { + fixLayoutInternal(first); + first = false; + } + } + }; + FrameLayout frameLayout = (FrameLayout) fragmentView; + + locationButton = new ImageView(context); + locationButton.setBackgroundResource(R.drawable.floating_user_states); + locationButton.setImageResource(R.drawable.myloc_on); + locationButton.setScaleType(ImageView.ScaleType.CENTER); + if (Build.VERSION.SDK_INT >= 21) { + StateListAnimator animator = new StateListAnimator(); + animator.addState(new int[]{android.R.attr.state_pressed}, ObjectAnimator.ofFloat(locationButton, "translationZ", AndroidUtilities.dp(2), AndroidUtilities.dp(4)).setDuration(200)); + animator.addState(new int[]{}, ObjectAnimator.ofFloat(locationButton, "translationZ", AndroidUtilities.dp(4), AndroidUtilities.dp(2)).setDuration(200)); + locationButton.setStateListAnimator(animator); + locationButton.setOutlineProvider(new ViewOutlineProvider() { + @Override + public void getOutline(View view, Outline outline) { + outline.setOval(0, 0, AndroidUtilities.dp(56), AndroidUtilities.dp(56)); + } + }); + } if (messageObject != null) { - fragmentView = inflater.inflate(R.layout.location_view_layout, null, false); + mapView = new MapView(context); + frameLayout.setBackgroundDrawable(new MapPlaceholderDrawable()); + mapView.onCreate(null); + try { + MapsInitializer.initialize(context); + googleMap = mapView.getMap(); + } catch (Exception e) { + FileLog.e("tmessages", e); + } + + FrameLayout bottomView = new FrameLayout(context); + bottomView.setBackgroundResource(R.drawable.location_panel); + frameLayout.addView(bottomView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 60, Gravity.LEFT | Gravity.BOTTOM)); + bottomView.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + if (userLocation != null) { + LatLng latLng = new LatLng(userLocation.getLatitude(), userLocation.getLongitude()); + if (googleMap != null) { + CameraUpdate position = CameraUpdateFactory.newLatLngZoom(latLng, googleMap.getMaxZoomLevel() - 8); + googleMap.animateCamera(position); + } + } + } + }); + + avatarImageView = new BackupImageView(context); + avatarImageView.setRoundRadius(AndroidUtilities.dp(20)); + bottomView.addView(avatarImageView, LayoutHelper.createFrame(40, 40, Gravity.TOP | (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT), LocaleController.isRTL ? 0 : 12, 12, LocaleController.isRTL ? 12 : 0, 0)); + + nameTextView = new TextView(context); + nameTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16); + nameTextView.setTextColor(0xff212121); + nameTextView.setMaxLines(1); + nameTextView.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); + nameTextView.setEllipsize(TextUtils.TruncateAt.END); + nameTextView.setSingleLine(true); + nameTextView.setGravity(LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT); + bottomView.addView(nameTextView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.TOP | (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT), LocaleController.isRTL ? 12 : 72, 10, LocaleController.isRTL ? 72 : 12, 0)); + + distanceTextView = new TextView(context); + distanceTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); + distanceTextView.setTextColor(0xff2f8cc9); + distanceTextView.setMaxLines(1); + distanceTextView.setEllipsize(TextUtils.TruncateAt.END); + distanceTextView.setSingleLine(true); + distanceTextView.setGravity(LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT); + bottomView.addView(distanceTextView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.TOP | (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT), LocaleController.isRTL ? 12 : 72, 33, LocaleController.isRTL ? 72 : 12, 0)); + + userLocation = new Location("network"); + userLocation.setLatitude(messageObject.messageOwner.media.geo.lat); + userLocation.setLongitude(messageObject.messageOwner.media.geo._long); + if (googleMap != null) { + LatLng latLng = new LatLng(userLocation.getLatitude(), userLocation.getLongitude()); + try { + googleMap.addMarker(new MarkerOptions().position(latLng).icon(BitmapDescriptorFactory.fromResource(R.drawable.map_pin))); + } catch (Exception e) { + FileLog.e("tmessages", e); + } + CameraUpdate position = CameraUpdateFactory.newLatLngZoom(latLng, googleMap.getMaxZoomLevel() - 8); + googleMap.moveCamera(position); + } + + ImageView routeButton = new ImageView(context); + routeButton.setBackgroundResource(R.drawable.floating_states); + routeButton.setImageResource(R.drawable.navigate); + routeButton.setScaleType(ImageView.ScaleType.CENTER); + if (Build.VERSION.SDK_INT >= 21) { + StateListAnimator animator = new StateListAnimator(); + animator.addState(new int[]{android.R.attr.state_pressed}, ObjectAnimator.ofFloat(routeButton, "translationZ", AndroidUtilities.dp(2), AndroidUtilities.dp(4)).setDuration(200)); + animator.addState(new int[]{}, ObjectAnimator.ofFloat(routeButton, "translationZ", AndroidUtilities.dp(4), AndroidUtilities.dp(2)).setDuration(200)); + routeButton.setStateListAnimator(animator); + routeButton.setOutlineProvider(new ViewOutlineProvider() { + @Override + public void getOutline(View view, Outline outline) { + outline.setOval(0, 0, AndroidUtilities.dp(56), AndroidUtilities.dp(56)); + } + }); + } + frameLayout.addView(routeButton, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, (LocaleController.isRTL ? Gravity.LEFT : Gravity.RIGHT) | Gravity.BOTTOM, LocaleController.isRTL ? 14 : 0, 0, LocaleController.isRTL ? 0 : 14, 28)); + routeButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (myLocation != null) { + try { + Intent intent = new Intent(android.content.Intent.ACTION_VIEW, Uri.parse(String.format(Locale.US, "http://maps.google.com/maps?saddr=%f,%f&daddr=%f,%f", myLocation.getLatitude(), myLocation.getLongitude(), messageObject.messageOwner.media.geo.lat, messageObject.messageOwner.media.geo._long))); + getParentActivity().startActivity(intent); + } catch (Exception e) { + FileLog.e("tmessages", e); + } + } + } + }); + + frameLayout.addView(locationButton, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, (LocaleController.isRTL ? Gravity.LEFT : Gravity.RIGHT) | Gravity.BOTTOM, LocaleController.isRTL ? 14 : 0, 0, LocaleController.isRTL ? 0 : 14, 100)); + locationButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (myLocation != null && googleMap != null) { + googleMap.animateCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(myLocation.getLatitude(), myLocation.getLongitude()), googleMap.getMaxZoomLevel() - 8)); + } + } + }); } else { - fragmentView = inflater.inflate(R.layout.location_attach_layout, null, false); - } + searchWas = false; + searching = false; + mapViewClip = new FrameLayout(context); + mapViewClip.setBackgroundDrawable(new MapPlaceholderDrawable()); + if (adapter != null) { + adapter.destroy(); + } + if (searchAdapter != null) { + searchAdapter.destroy(); + } - avatarImageView = (BackupImageView) fragmentView.findViewById(R.id.location_avatar_view); - if (avatarImageView != null) { - avatarImageView.setRoundRadius(AndroidUtilities.dp(32)); - } - nameTextView = (TextView) fragmentView.findViewById(R.id.location_name_label); - distanceTextView = (TextView) fragmentView.findViewById(R.id.location_distance_label); - View bottomView = fragmentView.findViewById(R.id.location_bottom_view); - TextView sendButton = (TextView) fragmentView.findViewById(R.id.location_send_button); - if (sendButton != null) { - sendButton.setText(LocaleController.getString("SendLocation", R.string.SendLocation).toUpperCase()); - sendButton.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); - } + listView = new ListView(context); + listView.setAdapter(adapter = new LocationActivityAdapter(context)); + listView.setVerticalScrollBarEnabled(false); + listView.setDividerHeight(0); + listView.setDivider(null); + frameLayout.addView(listView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.LEFT | Gravity.TOP)); + listView.setOnScrollListener(new AbsListView.OnScrollListener() { + @Override + public void onScrollStateChanged(AbsListView view, int scrollState) { - mapView = (MapView) fragmentView.findViewById(R.id.map_view); - mapView.onCreate(null); - try { - MapsInitializer.initialize(context); - googleMap = mapView.getMap(); - } catch (Exception e) { - FileLog.e("tmessages", e); + } + + @Override + public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { + if (totalItemCount == 0) { + return; + } + updateClipView(firstVisibleItem); + } + }); + listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { + @Override + public void onItemClick(AdapterView parent, View view, int position, long id) { + if (position == 1) { + if (delegate != null && userLocation != null) { + TLRPC.TL_messageMediaGeo location = new TLRPC.TL_messageMediaGeo(); + location.geo = new TLRPC.TL_geoPoint(); + location.geo.lat = userLocation.getLatitude(); + location.geo._long = userLocation.getLongitude(); + delegate.didSelectLocation(location); + } + finishFragment(); + } else { + TLRPC.TL_messageMediaVenue object = adapter.getItem(position); + if (object != null && delegate != null) { + delegate.didSelectLocation(object); + } + finishFragment(); + } + } + }); + adapter.setDelegate(new BaseLocationAdapter.BaseLocationAdapterDelegate() { + @Override + public void didLoadedSearchResult(ArrayList places) { + if (!wasResults && !places.isEmpty()) { + wasResults = true; + } + } + }); + adapter.setOverScrollHeight(overScrollHeight); + + frameLayout.addView(mapViewClip, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.LEFT | Gravity.TOP)); + + mapView = new MapView(context) { + @Override + public boolean onInterceptTouchEvent(MotionEvent ev) { + if (Build.VERSION.SDK_INT >= 11) { + if (ev.getAction() == MotionEvent.ACTION_DOWN) { + if (animatorSet != null) { + animatorSet.cancel(); + } + animatorSet = new AnimatorSet(); + animatorSet.setDuration(200); + animatorSet.playTogether( + ObjectAnimator.ofFloat(markerImageView, "translationY", markerTop + -AndroidUtilities.dp(10)), + ObjectAnimator.ofFloat(markerXImageView, "alpha", 1.0f)); + animatorSet.start(); + } else if (ev.getAction() == MotionEvent.ACTION_UP) { + if (animatorSet != null) { + animatorSet.cancel(); + } + animatorSet = new AnimatorSet(); + animatorSet.setDuration(200); + animatorSet.playTogether( + ObjectAnimator.ofFloat(markerImageView, "translationY", markerTop), + ObjectAnimator.ofFloat(markerXImageView, "alpha", 0.0f)); + animatorSet.start(); + } + } + if (ev.getAction() == MotionEvent.ACTION_MOVE) { + if (!userLocationMoved) { + if (Build.VERSION.SDK_INT >= 11) { + AnimatorSet animatorSet = new AnimatorSet(); + animatorSet.setDuration(200); + animatorSet.play(ObjectAnimator.ofFloat(locationButton, "alpha", 1.0f)); + animatorSet.start(); + } else { + locationButton.setVisibility(VISIBLE); + } + userLocationMoved = true; + } + if (googleMap != null && userLocation != null) { + userLocation.setLatitude(googleMap.getCameraPosition().target.latitude); + userLocation.setLongitude(googleMap.getCameraPosition().target.longitude); + } + adapter.setCustomLocation(userLocation); + } + return super.onInterceptTouchEvent(ev); + } + }; + mapView.onCreate(null); + try { + MapsInitializer.initialize(context); + googleMap = mapView.getMap(); + } catch (Exception e) { + FileLog.e("tmessages", e); + } + + View shadow = new View(context); + shadow.setBackgroundResource(R.drawable.header_shadow_reverse); + mapViewClip.addView(shadow, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, AndroidUtilities.dp(3), Gravity.LEFT | Gravity.BOTTOM)); + + markerImageView = new ImageView(context); + markerImageView.setImageResource(R.drawable.map_pin); + mapViewClip.addView(markerImageView, LayoutHelper.createFrame(24, 42, Gravity.TOP | Gravity.CENTER_HORIZONTAL)); + + if (Build.VERSION.SDK_INT >= 11) { + markerXImageView = new ImageView(context); + markerXImageView.setAlpha(0.0f); + markerXImageView.setImageResource(R.drawable.place_x); + mapViewClip.addView(markerXImageView, LayoutHelper.createFrame(14, 14, Gravity.TOP | Gravity.CENTER_HORIZONTAL)); + } + + mapViewClip.addView(locationButton, LayoutHelper.createFrame(Build.VERSION.SDK_INT >= 21 ? 56 : 60, Build.VERSION.SDK_INT >= 21 ? 56 : 60, (LocaleController.isRTL ? Gravity.LEFT : Gravity.RIGHT) | Gravity.BOTTOM, LocaleController.isRTL ? 14 : 0, 0, LocaleController.isRTL ? 0 : 14, 14)); + locationButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (myLocation != null && googleMap != null) { + if (Build.VERSION.SDK_INT >= 11) { + AnimatorSet animatorSet = new AnimatorSet(); + animatorSet.setDuration(200); + animatorSet.play(ObjectAnimator.ofFloat(locationButton, "alpha", 0.0f)); + animatorSet.start(); + } else { + locationButton.setVisibility(View.INVISIBLE); + } + adapter.setCustomLocation(null); + userLocationMoved = false; + googleMap.animateCamera(CameraUpdateFactory.newLatLng(new LatLng(myLocation.getLatitude(), myLocation.getLongitude()))); + } + } + }); + if (Build.VERSION.SDK_INT >= 11) { + locationButton.setAlpha(0.0f); + } else { + locationButton.setVisibility(View.INVISIBLE); + } + + emptyTextLayout = new LinearLayout(context); + emptyTextLayout.setVisibility(View.GONE); + emptyTextLayout.setOrientation(LinearLayout.VERTICAL); + frameLayout.addView(emptyTextLayout, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.LEFT | Gravity.TOP)); + emptyTextLayout.setOnTouchListener(new View.OnTouchListener() { + @Override + public boolean onTouch(View v, MotionEvent event) { + return true; + } + }); + + TextView emptyTextView = new TextView(context); + emptyTextView.setTextColor(0xff808080); + emptyTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 20); + emptyTextView.setGravity(Gravity.CENTER); + emptyTextView.setText(LocaleController.getString("NoResult", R.string.NoResult)); + emptyTextLayout.addView(emptyTextView, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, 0.5f)); + + FrameLayout frameLayoutEmpty = new FrameLayout(context); + emptyTextLayout.addView(frameLayoutEmpty, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, 0.5f)); + + searchListView = new ListView(context); + searchListView.setVisibility(View.GONE); + searchListView.setDividerHeight(0); + searchListView.setDivider(null); + searchListView.setAdapter(searchAdapter = new LocationActivitySearchAdapter(context)); + frameLayout.addView(searchListView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.LEFT | Gravity.TOP)); + searchListView.setOnScrollListener(new AbsListView.OnScrollListener() { + @Override + public void onScrollStateChanged(AbsListView view, int scrollState) { + if (scrollState == SCROLL_STATE_TOUCH_SCROLL && searching && searchWas) { + AndroidUtilities.hideKeyboard(getParentActivity().getCurrentFocus()); + } + } + + @Override + public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { + + } + }); + searchListView.setOnItemClickListener(new AdapterView.OnItemClickListener() { + @Override + public void onItemClick(AdapterView parent, View view, int position, long id) { + TLRPC.TL_messageMediaVenue object = searchAdapter.getItem(position); + if (object != null && delegate != null) { + delegate.didSelectLocation(object); + } + finishFragment(); + } + }); + + if (googleMap != null) { + userLocation = new Location("network"); + userLocation.setLatitude(20.659322); + userLocation.setLongitude(-11.406250); + } + + frameLayout.addView(actionBar); } if (googleMap != null) { @@ -176,74 +610,129 @@ public class LocationActivity extends BaseFragment implements NotificationCenter positionMarker(location); } }); - myLocation = getLastLocation(); - - if (sendButton != null) { - userLocation = new Location("network"); - userLocation.setLatitude(20.659322); - userLocation.setLongitude(-11.406250); - LatLng latLng = new LatLng(20.659322, -11.406250); - userMarker = googleMap.addMarker(new MarkerOptions().position(latLng).icon(BitmapDescriptorFactory.fromResource(R.drawable.map_pin)).draggable(true)); - - sendButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - if (delegate != null) { - delegate.didSelectLocation(userLocation.getLatitude(), userLocation.getLongitude()); - } - finishFragment(); - } - }); - - googleMap.setOnMarkerDragListener(new GoogleMap.OnMarkerDragListener() { - @Override - public void onMarkerDragStart(Marker marker) { - } - - @Override - public void onMarkerDrag(Marker marker) { - userLocationMoved = true; - } - - @Override - public void onMarkerDragEnd(Marker marker) { - LatLng latLng = marker.getPosition(); - userLocation.setLatitude(latLng.latitude); - userLocation.setLongitude(latLng.longitude); - } - }); - } - - if (bottomView != null) { - bottomView.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - if (userLocation != null) { - LatLng latLng = new LatLng(userLocation.getLatitude(), userLocation.getLongitude()); - CameraUpdate position = CameraUpdateFactory.newLatLngZoom(latLng, googleMap.getMaxZoomLevel() - 8); - googleMap.animateCamera(position); - } - } - }); - } - - if (messageObject != null) { - userLocation = new Location("network"); - userLocation.setLatitude(messageObject.messageOwner.media.geo.lat); - userLocation.setLongitude(messageObject.messageOwner.media.geo._long); - LatLng latLng = new LatLng(userLocation.getLatitude(), userLocation.getLongitude()); - userMarker = googleMap.addMarker(new MarkerOptions().position(latLng). - icon(BitmapDescriptorFactory.fromResource(R.drawable.map_pin))); - CameraUpdate position = CameraUpdateFactory.newLatLngZoom(latLng, googleMap.getMaxZoomLevel() - 8); - googleMap.moveCamera(position); - } - - positionMarker(myLocation); + positionMarker(myLocation = getLastLocation()); } return fragmentView; } + @Override + public void onOpenAnimationEnd() { + if (mapViewClip != null) { + mapViewClip.addView(mapView, 0, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, overScrollHeight + AndroidUtilities.dp(10), Gravity.TOP | Gravity.LEFT)); + updateClipView(listView.getFirstVisiblePosition()); + } else { + ((FrameLayout) fragmentView).addView(mapView, 0, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.TOP | Gravity.LEFT)); + } + } + + private void updateClipView(int firstVisibleItem) { + int height = 0; + int top = 0; + View child = listView.getChildAt(0); + if (child != null) { + if (firstVisibleItem == 0) { + top = child.getTop(); + height = overScrollHeight + (top < 0 ? top : 0); + halfHeight = (top < 0 ? top : 0) / 2; + } + FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) mapViewClip.getLayoutParams(); + if (layoutParams != null) { + if (height <= 0) { + if (mapView.getVisibility() == View.VISIBLE) { + mapView.setVisibility(View.INVISIBLE); + mapViewClip.setVisibility(View.INVISIBLE); + } + } else { + if (mapView.getVisibility() == View.INVISIBLE) { + mapView.setVisibility(View.VISIBLE); + mapViewClip.setVisibility(View.VISIBLE); + } + } + if (Build.VERSION.SDK_INT >= 11) { + mapViewClip.setTranslationY(Math.min(0, top)); + mapView.setTranslationY(Math.max(0, -top / 2)); + markerImageView.setTranslationY(markerTop = -top - AndroidUtilities.dp(42) + height / 2); + markerXImageView.setTranslationY(-top - AndroidUtilities.dp(7) + height / 2); + + if (googleMap != null) { + layoutParams = (FrameLayout.LayoutParams) mapView.getLayoutParams(); + if (layoutParams != null && layoutParams.height != overScrollHeight + AndroidUtilities.dp(10)) { + layoutParams.height = overScrollHeight + AndroidUtilities.dp(10); + googleMap.setPadding(0, 0, 0, AndroidUtilities.dp(10)); + mapView.setLayoutParams(layoutParams); + } + } + } else { + markerTop = 0; + layoutParams.height = height; + mapViewClip.setLayoutParams(layoutParams); + + layoutParams = (FrameLayout.LayoutParams) markerImageView.getLayoutParams(); + layoutParams.topMargin = height / 2 - AndroidUtilities.dp(42); + markerImageView.setLayoutParams(layoutParams); + + if (googleMap != null) { + layoutParams = (FrameLayout.LayoutParams) mapView.getLayoutParams(); + if (layoutParams != null) { + layoutParams.topMargin = halfHeight; + layoutParams.height = overScrollHeight + AndroidUtilities.dp(10); + googleMap.setPadding(0, 0, 0, AndroidUtilities.dp(10)); + mapView.setLayoutParams(layoutParams); + } + } + } + } + } + } + + private void fixLayoutInternal(final boolean resume) { + if (listView != null) { + int height = (actionBar.getOccupyStatusBar() ? AndroidUtilities.statusBarHeight : 0) + AndroidUtilities.getCurrentActionBarHeight(); + int viewHeight = fragmentView.getMeasuredHeight(); + if (viewHeight == 0) { + return; + } + overScrollHeight = viewHeight - AndroidUtilities.dp(66) - height; + + FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) listView.getLayoutParams(); + layoutParams.topMargin = height; + listView.setLayoutParams(layoutParams); + layoutParams = (FrameLayout.LayoutParams) mapViewClip.getLayoutParams(); + layoutParams.topMargin = height; + layoutParams.height = overScrollHeight; + mapViewClip.setLayoutParams(layoutParams); + layoutParams = (FrameLayout.LayoutParams) searchListView.getLayoutParams(); + layoutParams.topMargin = height; + searchListView.setLayoutParams(layoutParams); + + adapter.setOverScrollHeight(overScrollHeight); + layoutParams = (FrameLayout.LayoutParams) mapView.getLayoutParams(); + if (layoutParams != null) { + layoutParams.height = overScrollHeight + AndroidUtilities.dp(10); + if (googleMap != null) { + googleMap.setPadding(0, 0, 0, AndroidUtilities.dp(10)); + } + mapView.setLayoutParams(layoutParams); + } + adapter.notifyDataSetChanged(); + + if (resume) { + listView.setSelectionFromTop(0, -(int) (AndroidUtilities.dp(56) * 2.5f + AndroidUtilities.dp(36 + 66))); + updateClipView(listView.getFirstVisiblePosition()); + listView.post(new Runnable() { + @Override + public void run() { + listView.setSelectionFromTop(0, -(int) (AndroidUtilities.dp(56) * 2.5f + AndroidUtilities.dp(36 + 66))); + updateClipView(listView.getFirstVisiblePosition()); + } + }); + } else { + updateClipView(listView.getFirstVisiblePosition()); + } + } + } + private Location getLastLocation() { LocationManager lm = (LocationManager) ApplicationLoader.applicationContext.getSystemService(Context.LOCATION_SERVICE); List providers = lm.getProviders(true); @@ -281,7 +770,7 @@ public class LocationActivity extends BaseFragment implements NotificationCenter if (location == null) { return; } - myLocation = location; + myLocation = new Location(location); if (messageObject != null) { if (userLocation != null && distanceTextView != null) { float distance = location.distanceTo(userLocation); @@ -291,11 +780,14 @@ public class LocationActivity extends BaseFragment implements NotificationCenter distanceTextView.setText(String.format("%.2f %s", distance / 1000.0f, LocaleController.getString("KMetersAway", R.string.KMetersAway))); } } - } else { - if (!userLocationMoved && googleMap != null) { - userLocation = location; - LatLng latLng = new LatLng(location.getLatitude(), location.getLongitude()); - userMarker.setPosition(latLng); + } else if (googleMap != null) { + LatLng latLng = new LatLng(location.getLatitude(), location.getLongitude()); + if (adapter != null) { + adapter.searchGooglePlacesWithQuery(null, myLocation); + adapter.setGpsLocation(myLocation); + } + if (!userLocationMoved) { + userLocation = new Location(location); if (firstWas) { CameraUpdate position = CameraUpdateFactory.newLatLng(latLng); googleMap.animateCamera(position); @@ -339,10 +831,14 @@ public class LocationActivity extends BaseFragment implements NotificationCenter @Override public void onResume() { super.onResume(); + if (!AndroidUtilities.isTablet()) { + getParentActivity().getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN); + } if (mapView != null) { mapView.onResume(); } updateUserData(); + fixLayoutInternal(true); } @Override @@ -356,4 +852,10 @@ public class LocationActivity extends BaseFragment implements NotificationCenter public void setDelegate(LocationActivityDelegate delegate) { this.delegate = delegate; } + + private void updateSearchInterface() { + if (adapter != null) { + adapter.notifyDataSetChanged(); + } + } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/LoginActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/LoginActivity.java index b7b6fcf7c..7dd309e84 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/LoginActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/LoginActivity.java @@ -64,6 +64,7 @@ import org.telegram.messenger.Utilities; import org.telegram.ui.ActionBar.ActionBar; import org.telegram.ui.ActionBar.ActionBarMenu; import org.telegram.ui.ActionBar.BaseFragment; +import org.telegram.ui.Components.LayoutHelper; import org.telegram.ui.Components.SlideView; import org.telegram.ui.Components.TypefaceSpan; @@ -145,8 +146,8 @@ public class LoginActivity extends BaseFragment { views[a].setVisibility(a == 0 ? View.VISIBLE : View.GONE); frameLayout.addView(views[a]); FrameLayout.LayoutParams layoutParams1 = (FrameLayout.LayoutParams) views[a].getLayoutParams(); - layoutParams1.width = FrameLayout.LayoutParams.MATCH_PARENT; - layoutParams1.height = a == 0 ? FrameLayout.LayoutParams.WRAP_CONTENT : FrameLayout.LayoutParams.MATCH_PARENT; + layoutParams1.width = LayoutHelper.MATCH_PARENT; + layoutParams1.height = a == 0 ? LayoutHelper.WRAP_CONTENT : LayoutHelper.MATCH_PARENT; layoutParams1.leftMargin = AndroidUtilities.dp(AndroidUtilities.isTablet() ? 26 : 18); layoutParams1.rightMargin = AndroidUtilities.dp(AndroidUtilities.isTablet() ? 26 : 18); layoutParams1.topMargin = AndroidUtilities.dp(30); @@ -424,11 +425,11 @@ public class LoginActivity 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); LayoutParams layoutParams = (LayoutParams) countryButton.getLayoutParams(); - layoutParams.width = LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; layoutParams.height = AndroidUtilities.dp(36); layoutParams.bottomMargin = AndroidUtilities.dp(14); countryButton.setLayoutParams(layoutParams); @@ -452,7 +453,7 @@ public class LoginActivity extends BaseFragment { view.setBackgroundColor(0xffdbdbdb); addView(view); layoutParams = (LayoutParams) view.getLayoutParams(); - layoutParams.width = LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; layoutParams.height = 1; layoutParams.topMargin = AndroidUtilities.dp(-17.5f); layoutParams.leftMargin = AndroidUtilities.dp(4); @@ -463,8 +464,8 @@ public class LoginActivity extends BaseFragment { linearLayout.setOrientation(HORIZONTAL); addView(linearLayout); layoutParams = (LayoutParams) linearLayout.getLayoutParams(); - layoutParams.width = LayoutParams.MATCH_PARENT; - layoutParams.height = LayoutParams.WRAP_CONTENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; + layoutParams.height = LayoutHelper.WRAP_CONTENT; layoutParams.topMargin = AndroidUtilities.dp(20); linearLayout.setLayoutParams(layoutParams); @@ -474,8 +475,8 @@ public class LoginActivity extends BaseFragment { textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 18); linearLayout.addView(textView); layoutParams = (LayoutParams) textView.getLayoutParams(); - layoutParams.width = LayoutParams.WRAP_CONTENT; - layoutParams.height = LayoutParams.WRAP_CONTENT; + layoutParams.width = LayoutHelper.WRAP_CONTENT; + layoutParams.height = LayoutHelper.WRAP_CONTENT; textView.setLayoutParams(layoutParams); codeField = new EditText(context); @@ -565,7 +566,7 @@ public class LoginActivity extends BaseFragment { phoneField.setImeOptions(EditorInfo.IME_ACTION_NEXT | EditorInfo.IME_FLAG_NO_EXTRACT_UI); linearLayout.addView(phoneField); layoutParams = (LayoutParams) phoneField.getLayoutParams(); - layoutParams.width = LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; layoutParams.height = AndroidUtilities.dp(36); phoneField.setLayoutParams(layoutParams); phoneField.addTextChangedListener(new TextWatcher() { @@ -630,15 +631,15 @@ public class LoginActivity 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); layoutParams = (LayoutParams) textView.getLayoutParams(); - layoutParams.width = LayoutParams.WRAP_CONTENT; - layoutParams.height = LayoutParams.WRAP_CONTENT; + layoutParams.width = LayoutHelper.WRAP_CONTENT; + layoutParams.height = LayoutHelper.WRAP_CONTENT; layoutParams.topMargin = AndroidUtilities.dp(28); layoutParams.bottomMargin = AndroidUtilities.dp(10); - layoutParams.gravity = Gravity.LEFT; + layoutParams.gravity = LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT; textView.setLayoutParams(layoutParams); HashMap languageMap = new HashMap<>(); @@ -776,7 +777,7 @@ public class LoginActivity extends BaseFragment { req.api_id = BuildVars.APP_ID; req.sms_type = 0; req.phone_number = phone; - req.lang_code = LocaleController.getLocaleString(Locale.getDefault()); + req.lang_code = LocaleController.getLocaleString(LocaleController.getInstance().getSystemDefaultLocale()); if (req.lang_code == null || req.lang_code.length() == 0) { req.lang_code = "en"; } @@ -895,13 +896,13 @@ public class LoginActivity extends BaseFragment { 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 = LayoutParams.WRAP_CONTENT; - layoutParams.height = LayoutParams.WRAP_CONTENT; - layoutParams.gravity = Gravity.LEFT; + layoutParams.width = LayoutHelper.WRAP_CONTENT; + layoutParams.height = LayoutHelper.WRAP_CONTENT; + layoutParams.gravity = LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT; confirmTextView.setLayoutParams(layoutParams); codeField = new EditText(context); @@ -916,7 +917,7 @@ public class LoginActivity extends BaseFragment { codeField.setPadding(0, 0, 0, 0); addView(codeField); layoutParams = (LayoutParams) codeField.getLayoutParams(); - layoutParams.width = LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; layoutParams.height = AndroidUtilities.dp(36); layoutParams.gravity = Gravity.CENTER_HORIZONTAL; layoutParams.topMargin = AndroidUtilities.dp(20); @@ -936,28 +937,28 @@ public class LoginActivity extends BaseFragment { timeText.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); timeText.setTextColor(0xff757575); timeText.setLineSpacing(AndroidUtilities.dp(2), 1.0f); - timeText.setGravity(Gravity.LEFT); + timeText.setGravity(LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT); addView(timeText); layoutParams = (LayoutParams) timeText.getLayoutParams(); - layoutParams.width = LayoutParams.WRAP_CONTENT; - layoutParams.height = LayoutParams.WRAP_CONTENT; - layoutParams.gravity = Gravity.LEFT; + layoutParams.width = LayoutHelper.WRAP_CONTENT; + layoutParams.height = LayoutHelper.WRAP_CONTENT; + layoutParams.gravity = LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT; layoutParams.topMargin = AndroidUtilities.dp(30); timeText.setLayoutParams(layoutParams); problemText = new TextView(context); problemText.setText(LocaleController.getString("DidNotGetTheCode", R.string.DidNotGetTheCode)); problemText.setVisibility(time < 1000 ? VISIBLE : GONE); - problemText.setGravity(Gravity.LEFT); + 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); layoutParams = (LayoutParams) problemText.getLayoutParams(); - layoutParams.width = LayoutParams.WRAP_CONTENT; - layoutParams.height = LayoutParams.WRAP_CONTENT; - layoutParams.gravity = Gravity.LEFT; + layoutParams.width = LayoutHelper.WRAP_CONTENT; + layoutParams.height = LayoutHelper.WRAP_CONTENT; + layoutParams.gravity = LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT; layoutParams.topMargin = AndroidUtilities.dp(20); problemText.setLayoutParams(layoutParams); problemText.setOnClickListener(new OnClickListener() { @@ -980,24 +981,25 @@ public class LoginActivity extends BaseFragment { }); LinearLayout linearLayout = new LinearLayout(context); - linearLayout.setGravity(Gravity.BOTTOM | Gravity.CENTER_VERTICAL); + linearLayout.setGravity((LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.CENTER_VERTICAL); addView(linearLayout); layoutParams = (LayoutParams) linearLayout.getLayoutParams(); - layoutParams.width = LayoutParams.MATCH_PARENT; - layoutParams.height = LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; + layoutParams.height = LayoutHelper.MATCH_PARENT; + layoutParams.gravity = (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT); linearLayout.setLayoutParams(layoutParams); 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 = LayoutParams.WRAP_CONTENT; - layoutParams.height = LayoutParams.WRAP_CONTENT; - layoutParams.gravity = Gravity.BOTTOM | Gravity.LEFT; + layoutParams.width = LayoutHelper.WRAP_CONTENT; + layoutParams.height = LayoutHelper.WRAP_CONTENT; + layoutParams.gravity = Gravity.BOTTOM | (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT); layoutParams.bottomMargin = AndroidUtilities.dp(10); wrongNumber.setLayoutParams(layoutParams); wrongNumber.setText(LocaleController.getString("WrongNumber", R.string.WrongNumber)); @@ -1036,7 +1038,7 @@ public class LoginActivity extends BaseFragment { } String number = PhoneFormat.getInstance().format(phone); - String str = String.format(Locale.US, LocaleController.getString("SentSmsCode", R.string.SentSmsCode) + " %s", number); + String str = String.format(LocaleController.getString("SentSmsCode", R.string.SentSmsCode) + " %s", number); try { SpannableStringBuilder stringBuilder = new SpannableStringBuilder(str); TypefaceSpan span = new TypefaceSpan(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); @@ -1371,14 +1373,14 @@ public class LoginActivity extends BaseFragment { 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); confirmTextView.setText(LocaleController.getString("LoginPasswordText", R.string.LoginPasswordText)); addView(confirmTextView); LayoutParams layoutParams = (LayoutParams) confirmTextView.getLayoutParams(); - layoutParams.width = LayoutParams.WRAP_CONTENT; - layoutParams.height = LayoutParams.WRAP_CONTENT; - layoutParams.gravity = Gravity.LEFT; + layoutParams.width = LayoutHelper.WRAP_CONTENT; + layoutParams.height = LayoutHelper.WRAP_CONTENT; + layoutParams.gravity = LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT; confirmTextView.setLayoutParams(layoutParams); codeField = new EditText(context); @@ -1393,9 +1395,10 @@ public class LoginActivity extends BaseFragment { codeField.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD); codeField.setTransformationMethod(PasswordTransformationMethod.getInstance()); codeField.setTypeface(Typeface.DEFAULT); + codeField.setGravity(LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT); addView(codeField); layoutParams = (LayoutParams) codeField.getLayoutParams(); - layoutParams.width = LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; layoutParams.height = AndroidUtilities.dp(36); layoutParams.gravity = Gravity.CENTER_HORIZONTAL; layoutParams.topMargin = AndroidUtilities.dp(20); @@ -1412,7 +1415,7 @@ public class LoginActivity extends BaseFragment { }); TextView cancelButton = new TextView(context); - cancelButton.setGravity(Gravity.LEFT | Gravity.TOP); + cancelButton.setGravity((LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP); cancelButton.setTextColor(0xff4d83b3); cancelButton.setText(LocaleController.getString("ForgotPassword", R.string.ForgotPassword)); cancelButton.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); @@ -1420,9 +1423,9 @@ public class LoginActivity extends BaseFragment { cancelButton.setPadding(0, AndroidUtilities.dp(14), 0, 0); addView(cancelButton); layoutParams = (LayoutParams) cancelButton.getLayoutParams(); - layoutParams.width = LayoutParams.WRAP_CONTENT; - layoutParams.height = LayoutParams.WRAP_CONTENT; - layoutParams.gravity = Gravity.TOP | Gravity.LEFT; + layoutParams.width = LayoutHelper.WRAP_CONTENT; + layoutParams.height = LayoutHelper.WRAP_CONTENT; + layoutParams.gravity = Gravity.TOP | (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT); cancelButton.setLayoutParams(layoutParams); cancelButton.setOnClickListener(new OnClickListener() { @Override @@ -1483,7 +1486,7 @@ public class LoginActivity extends BaseFragment { }); resetAccountButton = new TextView(context); - resetAccountButton.setGravity(Gravity.LEFT | Gravity.TOP); + resetAccountButton.setGravity((LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP); resetAccountButton.setTextColor(0xffff6666); resetAccountButton.setVisibility(GONE); resetAccountButton.setText(LocaleController.getString("ResetMyAccount", R.string.ResetMyAccount)); @@ -1493,9 +1496,9 @@ public class LoginActivity extends BaseFragment { resetAccountButton.setPadding(0, AndroidUtilities.dp(14), 0, 0); addView(resetAccountButton); layoutParams = (LayoutParams) resetAccountButton.getLayoutParams(); - layoutParams.width = LayoutParams.WRAP_CONTENT; - layoutParams.height = LayoutParams.WRAP_CONTENT; - layoutParams.gravity = Gravity.TOP | Gravity.LEFT; + layoutParams.width = LayoutHelper.WRAP_CONTENT; + layoutParams.height = LayoutHelper.WRAP_CONTENT; + layoutParams.gravity = Gravity.TOP | (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT); layoutParams.topMargin = AndroidUtilities.dp(34); resetAccountButton.setLayoutParams(layoutParams); resetAccountButton.setOnClickListener(new OnClickListener() { @@ -1538,7 +1541,7 @@ public class LoginActivity extends BaseFragment { }); resetAccountText = new TextView(context); - resetAccountText.setGravity(Gravity.LEFT | Gravity.TOP); + resetAccountText.setGravity((LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP); resetAccountText.setVisibility(GONE); resetAccountText.setTextColor(0xff757575); resetAccountText.setText(LocaleController.getString("ResetMyAccountText", R.string.ResetMyAccountText)); @@ -1546,9 +1549,9 @@ public class LoginActivity extends BaseFragment { resetAccountText.setLineSpacing(AndroidUtilities.dp(2), 1.0f); addView(resetAccountText); layoutParams = (LayoutParams) resetAccountText.getLayoutParams(); - layoutParams.width = LayoutParams.WRAP_CONTENT; - layoutParams.height = LayoutParams.WRAP_CONTENT; - layoutParams.gravity = Gravity.TOP | Gravity.LEFT; + layoutParams.width = LayoutHelper.WRAP_CONTENT; + layoutParams.height = LayoutHelper.WRAP_CONTENT; + layoutParams.gravity = Gravity.TOP | (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT); layoutParams.bottomMargin = AndroidUtilities.dp(14); layoutParams.topMargin = AndroidUtilities.dp(7); resetAccountText.setLayoutParams(layoutParams); @@ -1746,14 +1749,14 @@ public class LoginActivity extends BaseFragment { 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); confirmTextView.setText(LocaleController.getString("RestoreEmailSentInfo", R.string.RestoreEmailSentInfo)); addView(confirmTextView); LayoutParams layoutParams = (LayoutParams) confirmTextView.getLayoutParams(); - layoutParams.width = LayoutParams.WRAP_CONTENT; - layoutParams.height = LayoutParams.WRAP_CONTENT; - layoutParams.gravity = Gravity.LEFT; + layoutParams.width = LayoutHelper.WRAP_CONTENT; + layoutParams.height = LayoutHelper.WRAP_CONTENT; + layoutParams.gravity = (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT); confirmTextView.setLayoutParams(layoutParams); codeField = new EditText(context); @@ -1768,9 +1771,10 @@ public class LoginActivity extends BaseFragment { codeField.setInputType(InputType.TYPE_CLASS_PHONE); codeField.setTransformationMethod(PasswordTransformationMethod.getInstance()); codeField.setTypeface(Typeface.DEFAULT); + codeField.setGravity(LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT); addView(codeField); layoutParams = (LayoutParams) codeField.getLayoutParams(); - layoutParams.width = LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; layoutParams.height = AndroidUtilities.dp(36); layoutParams.gravity = Gravity.CENTER_HORIZONTAL; layoutParams.topMargin = AndroidUtilities.dp(20); @@ -1787,16 +1791,16 @@ public class LoginActivity extends BaseFragment { }); cancelButton = new TextView(context); - cancelButton.setGravity(Gravity.LEFT | Gravity.BOTTOM); + cancelButton.setGravity((LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.BOTTOM); cancelButton.setTextColor(0xff4d83b3); cancelButton.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); cancelButton.setLineSpacing(AndroidUtilities.dp(2), 1.0f); cancelButton.setPadding(0, AndroidUtilities.dp(14), 0, 0); addView(cancelButton); layoutParams = (LayoutParams) cancelButton.getLayoutParams(); - layoutParams.width = LayoutParams.WRAP_CONTENT; - layoutParams.height = LayoutParams.WRAP_CONTENT; - layoutParams.gravity = Gravity.BOTTOM | Gravity.LEFT; + layoutParams.width = LayoutHelper.WRAP_CONTENT; + layoutParams.height = LayoutHelper.WRAP_CONTENT; + layoutParams.gravity = Gravity.BOTTOM | (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT); layoutParams.bottomMargin = AndroidUtilities.dp(14); cancelButton.setLayoutParams(layoutParams); cancelButton.setOnClickListener(new OnClickListener() { @@ -1992,14 +1996,14 @@ public class LoginActivity extends BaseFragment { TextView textView = new TextView(context); textView.setText(LocaleController.getString("RegisterText", R.string.RegisterText)); textView.setTextColor(0xff757575); - textView.setGravity(Gravity.LEFT); + textView.setGravity(LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT); textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); addView(textView); LayoutParams layoutParams = (LayoutParams) textView.getLayoutParams(); - layoutParams.width = LayoutParams.WRAP_CONTENT; - layoutParams.height = LayoutParams.WRAP_CONTENT; + layoutParams.width = LayoutHelper.WRAP_CONTENT; + layoutParams.height = LayoutHelper.WRAP_CONTENT; layoutParams.topMargin = AndroidUtilities.dp(8); - layoutParams.gravity = Gravity.LEFT; + layoutParams.gravity = LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT; textView.setLayoutParams(layoutParams); firstNameField = new EditText(context); @@ -2013,7 +2017,7 @@ public class LoginActivity extends BaseFragment { firstNameField.setInputType(InputType.TYPE_TEXT_FLAG_CAP_WORDS); addView(firstNameField); layoutParams = (LayoutParams) firstNameField.getLayoutParams(); - layoutParams.width = LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; layoutParams.height = AndroidUtilities.dp(36); layoutParams.topMargin = AndroidUtilities.dp(26); firstNameField.setLayoutParams(layoutParams); @@ -2039,7 +2043,7 @@ public class LoginActivity extends BaseFragment { lastNameField.setInputType(InputType.TYPE_TEXT_FLAG_CAP_WORDS); addView(lastNameField); layoutParams = (LayoutParams) lastNameField.getLayoutParams(); - layoutParams.width = LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; layoutParams.height = AndroidUtilities.dp(36); layoutParams.topMargin = AndroidUtilities.dp(10); lastNameField.setLayoutParams(layoutParams); @@ -2048,22 +2052,22 @@ public class LoginActivity extends BaseFragment { linearLayout.setGravity(Gravity.BOTTOM | Gravity.CENTER_VERTICAL); addView(linearLayout); layoutParams = (LayoutParams) linearLayout.getLayoutParams(); - layoutParams.width = LayoutParams.MATCH_PARENT; - layoutParams.height = LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; + layoutParams.height = LayoutHelper.MATCH_PARENT; linearLayout.setLayoutParams(layoutParams); TextView wrongNumber = new TextView(context); wrongNumber.setText(LocaleController.getString("CancelRegistration", R.string.CancelRegistration)); - 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 = LayoutParams.WRAP_CONTENT; - layoutParams.height = LayoutParams.WRAP_CONTENT; - layoutParams.gravity = Gravity.BOTTOM | Gravity.LEFT; + layoutParams.width = LayoutHelper.WRAP_CONTENT; + layoutParams.height = LayoutHelper.WRAP_CONTENT; + layoutParams.gravity = Gravity.BOTTOM | (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT); layoutParams.bottomMargin = AndroidUtilities.dp(10); wrongNumber.setLayoutParams(layoutParams); wrongNumber.setOnClickListener(new OnClickListener() { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/MediaActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/MediaActivity.java index 90d6f84fb..3f92e3936 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/MediaActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/MediaActivity.java @@ -59,8 +59,8 @@ import org.telegram.ui.ActionBar.ActionBarPopupWindow; import org.telegram.ui.ActionBar.ActionBar; import org.telegram.ui.Adapters.BaseFragmentAdapter; import org.telegram.ui.Adapters.BaseSectionsAdapter; -import org.telegram.ui.AnimationCompat.AnimatorSetProxy; -import org.telegram.ui.AnimationCompat.ObjectAnimatorProxy; +import org.telegram.android.AnimationCompat.AnimatorSetProxy; +import org.telegram.android.AnimationCompat.ObjectAnimatorProxy; import org.telegram.ui.Cells.GreySectionCell; import org.telegram.ui.Cells.LoadingCell; import org.telegram.ui.Cells.SharedDocumentCell; @@ -68,6 +68,7 @@ import org.telegram.ui.Cells.SharedMediaSectionCell; import org.telegram.ui.Cells.SharedPhotoVideoCell; import org.telegram.ui.Components.BackupImageView; import org.telegram.ui.ActionBar.BaseFragment; +import org.telegram.ui.Components.LayoutHelper; import org.telegram.ui.Components.SectionsListView; import java.io.File; @@ -377,8 +378,8 @@ public class MediaActivity extends BaseFragment implements NotificationCenter.No dropDownContainer.addSubItem(files_item, LocaleController.getString("DocumentsTitle", R.string.DocumentsTitle), 0); actionBar.addView(dropDownContainer); FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) dropDownContainer.getLayoutParams(); - layoutParams.height = FrameLayout.LayoutParams.MATCH_PARENT; - layoutParams.width = FrameLayout.LayoutParams.WRAP_CONTENT; + layoutParams.height = LayoutHelper.MATCH_PARENT; + layoutParams.width = LayoutHelper.WRAP_CONTENT; layoutParams.rightMargin = AndroidUtilities.dp(40); layoutParams.leftMargin = AndroidUtilities.isTablet() ? AndroidUtilities.dp(64) : AndroidUtilities.dp(56); layoutParams.gravity = Gravity.TOP | Gravity.LEFT; @@ -403,8 +404,8 @@ public class MediaActivity extends BaseFragment implements NotificationCenter.No dropDown.setPadding(0, 0, AndroidUtilities.dp(10), 0); dropDownContainer.addView(dropDown); layoutParams = (FrameLayout.LayoutParams) dropDown.getLayoutParams(); - layoutParams.width = FrameLayout.LayoutParams.WRAP_CONTENT; - layoutParams.height = FrameLayout.LayoutParams.WRAP_CONTENT; + layoutParams.width = LayoutHelper.WRAP_CONTENT; + layoutParams.height = LayoutHelper.WRAP_CONTENT; layoutParams.leftMargin = AndroidUtilities.dp(16); layoutParams.gravity = Gravity.CENTER_VERTICAL; dropDown.setLayoutParams(layoutParams); @@ -431,7 +432,7 @@ public class MediaActivity extends BaseFragment implements NotificationCenter.No LinearLayout.LayoutParams layoutParams1 = (LinearLayout.LayoutParams) selectedMessagesCountTextView.getLayoutParams(); layoutParams1.weight = 1; layoutParams1.width = 0; - layoutParams1.height = LinearLayout.LayoutParams.MATCH_PARENT; + layoutParams1.height = LayoutHelper.MATCH_PARENT; selectedMessagesCountTextView.setLayoutParams(layoutParams1); if ((int) dialog_id != 0) { @@ -453,8 +454,8 @@ public class MediaActivity extends BaseFragment implements NotificationCenter.No listView.setClipToPadding(false); frameLayout.addView(listView); layoutParams = (FrameLayout.LayoutParams) listView.getLayoutParams(); - layoutParams.width = FrameLayout.LayoutParams.MATCH_PARENT; - layoutParams.height = FrameLayout.LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; + layoutParams.height = LayoutHelper.MATCH_PARENT; layoutParams.gravity = Gravity.TOP; listView.setLayoutParams(layoutParams); listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { @@ -518,8 +519,8 @@ public class MediaActivity extends BaseFragment implements NotificationCenter.No emptyView.setBackgroundColor(0xfff0f0f0); frameLayout.addView(emptyView); layoutParams = (FrameLayout.LayoutParams) emptyView.getLayoutParams(); - layoutParams.width = FrameLayout.LayoutParams.MATCH_PARENT; - layoutParams.height = FrameLayout.LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; + layoutParams.height = LayoutHelper.MATCH_PARENT; emptyView.setLayoutParams(layoutParams); emptyView.setOnTouchListener(new View.OnTouchListener() { @Override @@ -531,8 +532,8 @@ public class MediaActivity extends BaseFragment implements NotificationCenter.No emptyImageView = new ImageView(context); emptyView.addView(emptyImageView); layoutParams1 = (LinearLayout.LayoutParams) emptyImageView.getLayoutParams(); - layoutParams1.width = LinearLayout.LayoutParams.WRAP_CONTENT; - layoutParams1.height = LinearLayout.LayoutParams.WRAP_CONTENT; + layoutParams1.width = LayoutHelper.WRAP_CONTENT; + layoutParams1.height = LayoutHelper.WRAP_CONTENT; emptyImageView.setLayoutParams(layoutParams1); emptyTextView = new TextView(context); @@ -543,8 +544,8 @@ public class MediaActivity extends BaseFragment implements NotificationCenter.No emptyView.addView(emptyTextView); layoutParams1 = (LinearLayout.LayoutParams) emptyTextView.getLayoutParams(); layoutParams1.topMargin = AndroidUtilities.dp(24); - layoutParams1.width = FrameLayout.LayoutParams.WRAP_CONTENT; - layoutParams1.height = FrameLayout.LayoutParams.WRAP_CONTENT; + layoutParams1.width = LayoutHelper.WRAP_CONTENT; + layoutParams1.height = LayoutHelper.WRAP_CONTENT; layoutParams1.gravity = Gravity.CENTER; emptyTextView.setLayoutParams(layoutParams1); @@ -555,15 +556,15 @@ public class MediaActivity extends BaseFragment implements NotificationCenter.No progressView.setBackgroundColor(0xfff0f0f0); frameLayout.addView(progressView); layoutParams = (FrameLayout.LayoutParams) progressView.getLayoutParams(); - layoutParams.width = FrameLayout.LayoutParams.MATCH_PARENT; - layoutParams.height = FrameLayout.LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; + layoutParams.height = LayoutHelper.MATCH_PARENT; progressView.setLayoutParams(layoutParams); ProgressBar progressBar = new ProgressBar(context); progressView.addView(progressBar); layoutParams1 = (LinearLayout.LayoutParams) progressBar.getLayoutParams(); - layoutParams1.width = LinearLayout.LayoutParams.WRAP_CONTENT; - layoutParams1.height = LinearLayout.LayoutParams.WRAP_CONTENT; + layoutParams1.width = LayoutHelper.WRAP_CONTENT; + layoutParams1.height = LayoutHelper.WRAP_CONTENT; progressBar.setLayoutParams(layoutParams1); switchToCurrentSelectedMode(); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/MessagesActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/MessagesActivity.java index 4d56274f6..28bc4ca84 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/MessagesActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/MessagesActivity.java @@ -48,8 +48,8 @@ import org.telegram.messenger.UserConfig; import org.telegram.ui.Adapters.BaseFragmentAdapter; import org.telegram.ui.Adapters.DialogsAdapter; import org.telegram.ui.Adapters.DialogsSearchAdapter; -import org.telegram.ui.AnimationCompat.ObjectAnimatorProxy; -import org.telegram.ui.AnimationCompat.ViewProxy; +import org.telegram.android.AnimationCompat.ObjectAnimatorProxy; +import org.telegram.android.AnimationCompat.ViewProxy; import org.telegram.ui.Cells.UserCell; import org.telegram.ui.Cells.DialogCell; import org.telegram.ui.ActionBar.ActionBar; @@ -57,6 +57,7 @@ import org.telegram.ui.ActionBar.ActionBarMenu; import org.telegram.ui.ActionBar.ActionBarMenuItem; import org.telegram.ui.ActionBar.BaseFragment; import org.telegram.ui.ActionBar.MenuDrawable; +import org.telegram.ui.Components.ResourceLoader; import java.util.ArrayList; @@ -160,6 +161,8 @@ public class MessagesActivity extends BaseFragment implements NotificationCenter searching = false; searchWas = false; + ResourceLoader.loadRecources(context); + ActionBarMenu menu = actionBar.createMenu(); if (!onlySelect && searchString == null) { passcodeItem = menu.addItem(passcode_menu_item, R.drawable.lock_close); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/NotificationsSettingsActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/NotificationsSettingsActivity.java index a8f3bfd52..14868a899 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/NotificationsSettingsActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/NotificationsSettingsActivity.java @@ -48,6 +48,7 @@ import org.telegram.ui.Cells.TextDetailSettingsCell; import org.telegram.ui.ActionBar.ActionBar; import org.telegram.ui.ActionBar.BaseFragment; import org.telegram.ui.Components.ColorPickerView; +import org.telegram.ui.Components.LayoutHelper; public class NotificationsSettingsActivity extends BaseFragment implements NotificationCenter.NotificationCenterDelegate { private ListView listView; @@ -178,8 +179,8 @@ public class NotificationsSettingsActivity extends BaseFragment implements Notif listView.setVerticalScrollBarEnabled(false); frameLayout.addView(listView); FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) listView.getLayoutParams(); - layoutParams.width = FrameLayout.LayoutParams.MATCH_PARENT; - layoutParams.height = FrameLayout.LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; + layoutParams.height = LayoutHelper.MATCH_PARENT; listView.setLayoutParams(layoutParams); listView.setAdapter(new ListAdapter(context)); listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/PasscodeActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/PasscodeActivity.java index f5cad11a8..5cbf1d5ed 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/PasscodeActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/PasscodeActivity.java @@ -58,6 +58,7 @@ import org.telegram.ui.Adapters.BaseFragmentAdapter; import org.telegram.ui.Cells.TextCheckCell; import org.telegram.ui.Cells.TextInfoPrivacyCell; import org.telegram.ui.Cells.TextSettingsCell; +import org.telegram.ui.Components.LayoutHelper; import org.telegram.ui.Components.NumberPicker; public class PasscodeActivity extends BaseFragment implements NotificationCenter.NotificationCenterDelegate { @@ -156,8 +157,8 @@ public class PasscodeActivity extends BaseFragment implements NotificationCenter titleTextView.setGravity(Gravity.CENTER_HORIZONTAL); frameLayout.addView(titleTextView); FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) titleTextView.getLayoutParams(); - layoutParams.width = FrameLayout.LayoutParams.WRAP_CONTENT; - layoutParams.height = FrameLayout.LayoutParams.WRAP_CONTENT; + layoutParams.width = LayoutHelper.WRAP_CONTENT; + layoutParams.height = LayoutHelper.WRAP_CONTENT; layoutParams.gravity = Gravity.CENTER_HORIZONTAL; layoutParams.topMargin = AndroidUtilities.dp(38); titleTextView.setLayoutParams(layoutParams); @@ -184,7 +185,7 @@ public class PasscodeActivity extends BaseFragment implements NotificationCenter layoutParams.leftMargin = AndroidUtilities.dp(40); layoutParams.gravity = Gravity.TOP | Gravity.LEFT; layoutParams.rightMargin = AndroidUtilities.dp(40); - layoutParams.width = LinearLayout.LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; passwordEditText.setLayoutParams(layoutParams); passwordEditText.setOnEditorActionListener(new TextView.OnEditorActionListener() { @Override @@ -257,8 +258,8 @@ public class PasscodeActivity extends BaseFragment implements NotificationCenter dropDownContainer.addSubItem(password_item, LocaleController.getString("PasscodePassword", R.string.PasscodePassword), 0); actionBar.addView(dropDownContainer); layoutParams = (FrameLayout.LayoutParams) dropDownContainer.getLayoutParams(); - layoutParams.height = FrameLayout.LayoutParams.MATCH_PARENT; - layoutParams.width = FrameLayout.LayoutParams.WRAP_CONTENT; + layoutParams.height = LayoutHelper.MATCH_PARENT; + layoutParams.width = LayoutHelper.WRAP_CONTENT; layoutParams.rightMargin = AndroidUtilities.dp(40); layoutParams.leftMargin = AndroidUtilities.isTablet() ? AndroidUtilities.dp(64) : AndroidUtilities.dp(56); layoutParams.gravity = Gravity.TOP | Gravity.LEFT; @@ -283,8 +284,8 @@ public class PasscodeActivity extends BaseFragment implements NotificationCenter dropDown.setPadding(0, 0, AndroidUtilities.dp(10), 0); dropDownContainer.addView(dropDown); layoutParams = (FrameLayout.LayoutParams) dropDown.getLayoutParams(); - layoutParams.width = FrameLayout.LayoutParams.WRAP_CONTENT; - layoutParams.height = FrameLayout.LayoutParams.WRAP_CONTENT; + layoutParams.width = LayoutHelper.WRAP_CONTENT; + layoutParams.height = LayoutHelper.WRAP_CONTENT; layoutParams.leftMargin = AndroidUtilities.dp(16); layoutParams.gravity = Gravity.CENTER_VERTICAL; layoutParams.bottomMargin = AndroidUtilities.dp(1); @@ -304,8 +305,8 @@ public class PasscodeActivity extends BaseFragment implements NotificationCenter listView.setDrawSelectorOnTop(true); frameLayout.addView(listView); FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) listView.getLayoutParams(); - layoutParams.width = FrameLayout.LayoutParams.MATCH_PARENT; - layoutParams.height = FrameLayout.LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; + layoutParams.height = LayoutHelper.MATCH_PARENT; layoutParams.gravity = Gravity.TOP; listView.setLayoutParams(layoutParams); listView.setAdapter(listAdapter = new ListAdapter(context)); @@ -528,7 +529,6 @@ public class PasscodeActivity extends BaseFragment implements NotificationCenter UserConfig.passcodeHash = Utilities.MD5(firstPassword); UserConfig.passcodeType = currentPasswordType; UserConfig.saveConfig(false); - //TODO show alert finishFragment(); NotificationCenter.getInstance().postNotificationName(NotificationCenter.didSetPasscode); passwordEditText.clearFocus(); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/PhotoAlbumPickerActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/PhotoAlbumPickerActivity.java index fa330b1db..82c3db435 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/PhotoAlbumPickerActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/PhotoAlbumPickerActivity.java @@ -10,8 +10,10 @@ package org.telegram.ui; import android.app.Activity; import android.content.Context; +import android.content.res.Configuration; import android.graphics.drawable.ColorDrawable; import android.os.Build; +import android.text.TextUtils; import android.view.Gravity; import android.view.LayoutInflater; import android.view.MotionEvent; @@ -34,10 +36,12 @@ import org.telegram.messenger.ApplicationLoader; import org.telegram.messenger.R; import org.telegram.ui.ActionBar.ActionBar; import org.telegram.ui.ActionBar.ActionBarMenu; +import org.telegram.ui.ActionBar.ActionBarMenuItem; import org.telegram.ui.ActionBar.BaseFragment; import org.telegram.ui.Adapters.BaseFragmentAdapter; import org.telegram.ui.Cells.PhotoPickerAlbumsCell; import org.telegram.ui.Cells.PhotoPickerSearchCell; +import org.telegram.ui.Components.LayoutHelper; import org.telegram.ui.Components.PhotoPickerBottomLayout; import java.util.ArrayList; @@ -46,11 +50,13 @@ import java.util.HashMap; public class PhotoAlbumPickerActivity extends BaseFragment implements NotificationCenter.NotificationCenterDelegate { public interface PhotoAlbumPickerActivityDelegate { - void didSelectPhotos(ArrayList photos, ArrayList webPhotos); + void didSelectPhotos(ArrayList photos, ArrayList captions, ArrayList webPhotos); + boolean didSelectVideo(String path); void startPhotoSelectActivity(); } private ArrayList albumsSorted = null; + private ArrayList videoAlbumsSorted = null; private HashMap selectedPhotos = new HashMap<>(); private HashMap selectedWebPhotos = new HashMap<>(); private HashMap recentImagesWebKeys = new HashMap<>(); @@ -64,15 +70,23 @@ public class PhotoAlbumPickerActivity extends BaseFragment implements Notificati private ListAdapter listAdapter; private FrameLayout progressView; private TextView emptyView; + private TextView dropDown; + private ActionBarMenuItem dropDownContainer; private PhotoPickerBottomLayout photoPickerBottomLayout; private boolean sendPressed = false; private boolean singlePhoto = false; + private int selectedMode; + private ChatActivity chatActivity; private PhotoAlbumPickerActivityDelegate delegate; - public PhotoAlbumPickerActivity(boolean onlyOnePhoto) { + private final static int item_photos = 2; + private final static int item_video = 3; + + public PhotoAlbumPickerActivity(boolean singlePhoto, ChatActivity chatActivity) { super(); - singlePhoto = onlyOnePhoto; + this.chatActivity = chatActivity; + this.singlePhoto = singlePhoto; } @Override @@ -99,7 +113,6 @@ public class PhotoAlbumPickerActivity extends BaseFragment implements Notificati actionBar.setBackgroundColor(0xff333333); actionBar.setItemsBackground(R.drawable.bar_selector_picker); actionBar.setBackButtonImage(R.drawable.ic_ab_back); - actionBar.setTitle(LocaleController.getString("Gallery", R.string.Gallery)); actionBar.setActionBarMenuOnItemClick(new ActionBar.ActionBarMenuOnItemClick() { @Override public void onItemClick(int id) { @@ -115,6 +128,22 @@ public class PhotoAlbumPickerActivity extends BaseFragment implements Notificati finishFragment(false); delegate.startPhotoSelectActivity(); } + } else if (id == item_photos) { + if (selectedMode == 0) { + return; + } + selectedMode = 0; + dropDown.setText(LocaleController.getString("PickerPhotos", R.string.PickerPhotos)); + emptyView.setText(LocaleController.getString("NoPhotos", R.string.NoPhotos)); + listAdapter.notifyDataSetChanged(); + } else if (id == item_video) { + if (selectedMode == 1) { + return; + } + selectedMode = 1; + dropDown.setText(LocaleController.getString("PickerVideo", R.string.PickerVideo)); + emptyView.setText(LocaleController.getString("NoVideo", R.string.NoVideo)); + listAdapter.notifyDataSetChanged(); } } }); @@ -127,6 +156,51 @@ public class PhotoAlbumPickerActivity extends BaseFragment implements Notificati FrameLayout frameLayout = (FrameLayout) fragmentView; frameLayout.setBackgroundColor(0xff000000); + if (!singlePhoto) { + selectedMode = 0; + + dropDownContainer = new ActionBarMenuItem(context, menu, R.drawable.bar_selector_picker); + dropDownContainer.setSubMenuOpenSide(1); + dropDownContainer.addSubItem(item_photos, LocaleController.getString("PickerPhotos", R.string.PickerPhotos), 0); + dropDownContainer.addSubItem(item_video, LocaleController.getString("PickerVideo", R.string.PickerVideo), 0); + actionBar.addView(dropDownContainer); + FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) dropDownContainer.getLayoutParams(); + layoutParams.height = LayoutHelper.MATCH_PARENT; + layoutParams.width = LayoutHelper.WRAP_CONTENT; + layoutParams.rightMargin = AndroidUtilities.dp(40); + layoutParams.leftMargin = AndroidUtilities.isTablet() ? AndroidUtilities.dp(64) : AndroidUtilities.dp(56); + layoutParams.gravity = Gravity.TOP | Gravity.LEFT; + dropDownContainer.setLayoutParams(layoutParams); + dropDownContainer.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + dropDownContainer.toggleSubMenu(); + } + }); + + dropDown = new TextView(context); + dropDown.setGravity(Gravity.LEFT); + dropDown.setSingleLine(true); + dropDown.setLines(1); + dropDown.setMaxLines(1); + dropDown.setEllipsize(TextUtils.TruncateAt.END); + dropDown.setTextColor(0xffffffff); + dropDown.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); + dropDown.setCompoundDrawablesWithIntrinsicBounds(0, 0, R.drawable.ic_arrow_drop_down, 0); + dropDown.setCompoundDrawablePadding(AndroidUtilities.dp(4)); + dropDown.setPadding(0, 0, AndroidUtilities.dp(10), 0); + dropDown.setText(LocaleController.getString("PickerPhotos", R.string.PickerPhotos)); + dropDownContainer.addView(dropDown); + layoutParams = (FrameLayout.LayoutParams) dropDown.getLayoutParams(); + layoutParams.width = LayoutHelper.WRAP_CONTENT; + layoutParams.height = LayoutHelper.WRAP_CONTENT; + layoutParams.leftMargin = AndroidUtilities.dp(16); + layoutParams.gravity = Gravity.CENTER_VERTICAL; + dropDown.setLayoutParams(layoutParams); + } else { + actionBar.setTitle(LocaleController.getString("Gallery", R.string.Gallery)); + } + listView = new ListView(context); listView.setPadding(AndroidUtilities.dp(4), 0, AndroidUtilities.dp(4), AndroidUtilities.dp(4)); listView.setClipToPadding(false); @@ -139,8 +213,8 @@ public class PhotoAlbumPickerActivity extends BaseFragment implements Notificati listView.setScrollingCacheEnabled(false); frameLayout.addView(listView); FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) listView.getLayoutParams(); - layoutParams.width = FrameLayout.LayoutParams.MATCH_PARENT; - layoutParams.height = FrameLayout.LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; + layoutParams.height = LayoutHelper.MATCH_PARENT; layoutParams.bottomMargin = AndroidUtilities.dp(48); listView.setLayoutParams(layoutParams); listView.setAdapter(listAdapter = new ListAdapter(context)); @@ -154,8 +228,8 @@ public class PhotoAlbumPickerActivity extends BaseFragment implements Notificati emptyView.setText(LocaleController.getString("NoPhotos", R.string.NoPhotos)); frameLayout.addView(emptyView); layoutParams = (FrameLayout.LayoutParams) emptyView.getLayoutParams(); - layoutParams.width = FrameLayout.LayoutParams.MATCH_PARENT; - layoutParams.height = FrameLayout.LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; + layoutParams.height = LayoutHelper.MATCH_PARENT; layoutParams.bottomMargin = AndroidUtilities.dp(48); emptyView.setLayoutParams(layoutParams); emptyView.setOnTouchListener(new View.OnTouchListener() { @@ -169,23 +243,23 @@ public class PhotoAlbumPickerActivity extends BaseFragment implements Notificati progressView.setVisibility(View.GONE); frameLayout.addView(progressView); layoutParams = (FrameLayout.LayoutParams) progressView.getLayoutParams(); - layoutParams.width = FrameLayout.LayoutParams.MATCH_PARENT; - layoutParams.height = FrameLayout.LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; + layoutParams.height = LayoutHelper.MATCH_PARENT; layoutParams.bottomMargin = AndroidUtilities.dp(48); progressView.setLayoutParams(layoutParams); ProgressBar progressBar = new ProgressBar(context); progressView.addView(progressBar); layoutParams = (FrameLayout.LayoutParams) progressView.getLayoutParams(); - layoutParams.width = FrameLayout.LayoutParams.WRAP_CONTENT; - layoutParams.height = FrameLayout.LayoutParams.WRAP_CONTENT; + layoutParams.width = LayoutHelper.WRAP_CONTENT; + layoutParams.height = LayoutHelper.WRAP_CONTENT; layoutParams.gravity = Gravity.CENTER; progressView.setLayoutParams(layoutParams); photoPickerBottomLayout = new PhotoPickerBottomLayout(context); frameLayout.addView(photoPickerBottomLayout); layoutParams = (FrameLayout.LayoutParams) photoPickerBottomLayout.getLayoutParams(); - layoutParams.width = FrameLayout.LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; layoutParams.height = AndroidUtilities.dp(48); layoutParams.gravity = Gravity.BOTTOM; photoPickerBottomLayout.setLayoutParams(layoutParams); @@ -237,6 +311,7 @@ public class PhotoAlbumPickerActivity extends BaseFragment implements Notificati int guid = (Integer) args[0]; if (classGuid == guid) { albumsSorted = (ArrayList) args[1]; + videoAlbumsSorted = (ArrayList) args[3]; if (progressView != null) { progressView.setVisibility(View.GONE); } @@ -278,12 +353,15 @@ public class PhotoAlbumPickerActivity extends BaseFragment implements Notificati } sendPressed = true; ArrayList photos = new ArrayList<>(); + ArrayList captions = new ArrayList<>(); for (HashMap.Entry entry : selectedPhotos.entrySet()) { MediaController.PhotoEntry photoEntry = entry.getValue(); if (photoEntry.imagePath != null) { photos.add(photoEntry.imagePath); + captions.add(photoEntry.caption != null ? photoEntry.caption.toString() : null); } else if (photoEntry.path != null) { photos.add(photoEntry.path); + captions.add(photoEntry.caption != null ? photoEntry.caption.toString() : null); } } ArrayList webPhotos = new ArrayList<>(); @@ -293,6 +371,7 @@ public class PhotoAlbumPickerActivity extends BaseFragment implements Notificati MediaController.SearchImage searchImage = entry.getValue(); if (searchImage.imagePath != null) { photos.add(searchImage.imagePath); + captions.add(searchImage.caption != null ? searchImage.caption.toString() : null); } else { webPhotos.add(searchImage); } @@ -325,7 +404,7 @@ public class PhotoAlbumPickerActivity extends BaseFragment implements Notificati MessagesStorage.getInstance().putWebRecent(recentGifImages); } - delegate.didSelectPhotos(photos, webPhotos); + delegate.didSelectPhotos(photos, captions, webPhotos); } private void fixLayout() { @@ -348,6 +427,7 @@ public class PhotoAlbumPickerActivity extends BaseFragment implements Notificati if (getParentActivity() == null) { return; } + WindowManager manager = (WindowManager) ApplicationLoader.applicationContext.getSystemService(Activity.WINDOW_SERVICE); int rotation = manager.getDefaultDisplay().getRotation(); columnsCount = 2; @@ -355,6 +435,20 @@ public class PhotoAlbumPickerActivity extends BaseFragment implements Notificati columnsCount = 4; } listAdapter.notifyDataSetChanged(); + + if (dropDownContainer != null) { + if (!AndroidUtilities.isTablet()) { + FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) dropDownContainer.getLayoutParams(); + layoutParams.topMargin = (Build.VERSION.SDK_INT >= 21 ? AndroidUtilities.statusBarHeight : 0); + dropDownContainer.setLayoutParams(layoutParams); + } + + if (!AndroidUtilities.isTablet() && ApplicationLoader.applicationContext.getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) { + dropDown.setTextSize(18); + } else { + dropDown.setTextSize(20); + } + } } private void openPhotoPicker(MediaController.AlbumEntry albumEntry, int type) { @@ -366,7 +460,7 @@ public class PhotoAlbumPickerActivity extends BaseFragment implements Notificati recentImages = recentGifImages; } } - PhotoPickerActivity fragment = new PhotoPickerActivity(type, albumEntry, selectedPhotos, selectedWebPhotos, recentImages, singlePhoto); + PhotoPickerActivity fragment = new PhotoPickerActivity(type, albumEntry, selectedPhotos, selectedWebPhotos, recentImages, singlePhoto, chatActivity); fragment.setDelegate(new PhotoPickerActivity.PhotoPickerActivityDelegate() { @Override public void selectedPhotosChanged() { @@ -382,6 +476,12 @@ public class PhotoAlbumPickerActivity extends BaseFragment implements Notificati sendSelectedPhotos(); } } + + @Override + public boolean didSelectVideo(String path) { + removeSelfFromStack(); + return delegate.didSelectVideo(path); + } }); presentFragment(fragment); } @@ -405,10 +505,14 @@ public class PhotoAlbumPickerActivity extends BaseFragment implements Notificati @Override public int getCount() { - if (singlePhoto) { - return albumsSorted != null ? (int) Math.ceil(albumsSorted.size() / (float) columnsCount) : 0; + if (singlePhoto || selectedMode == 0) { + if (singlePhoto) { + return albumsSorted != null ? (int) Math.ceil(albumsSorted.size() / (float) columnsCount) : 0; + } + return 1 + (albumsSorted != null ? (int) Math.ceil(albumsSorted.size() / (float) columnsCount) : 0); + } else { + return (videoAlbumsSorted != null ? (int) Math.ceil(videoAlbumsSorted.size() / (float) columnsCount) : 0); } - return 1 + (albumsSorted != null ? (int) Math.ceil(albumsSorted.size() / (float) columnsCount) : 0); } @Override @@ -446,16 +550,25 @@ public class PhotoAlbumPickerActivity extends BaseFragment implements Notificati photoPickerAlbumsCell.setAlbumsCount(columnsCount); for (int a = 0; a < columnsCount; a++) { int index; - if (singlePhoto) { + if (singlePhoto || selectedMode == 1) { index = i * columnsCount + a; } else { index = (i - 1) * columnsCount + a; } - if (index < albumsSorted.size()) { - MediaController.AlbumEntry albumEntry = albumsSorted.get(index); - photoPickerAlbumsCell.setAlbum(a, albumEntry); + if (singlePhoto || selectedMode == 0) { + if (index < albumsSorted.size()) { + MediaController.AlbumEntry albumEntry = albumsSorted.get(index); + photoPickerAlbumsCell.setAlbum(a, albumEntry); + } else { + photoPickerAlbumsCell.setAlbum(a, null); + } } else { - photoPickerAlbumsCell.setAlbum(a, null); + if (index < videoAlbumsSorted.size()) { + MediaController.AlbumEntry albumEntry = videoAlbumsSorted.get(index); + photoPickerAlbumsCell.setAlbum(a, albumEntry); + } else { + photoPickerAlbumsCell.setAlbum(a, null); + } } } photoPickerAlbumsCell.requestLayout(); @@ -475,7 +588,7 @@ public class PhotoAlbumPickerActivity extends BaseFragment implements Notificati @Override public int getItemViewType(int i) { - if (singlePhoto) { + if (singlePhoto || selectedMode == 1) { return 0; } if (i == 0) { @@ -486,7 +599,7 @@ public class PhotoAlbumPickerActivity extends BaseFragment implements Notificati @Override public int getViewTypeCount() { - if (singlePhoto) { + if (singlePhoto || selectedMode == 1) { return 1; } return 2; @@ -494,7 +607,7 @@ public class PhotoAlbumPickerActivity extends BaseFragment implements Notificati @Override public boolean isEmpty() { - return false; + return getCount() == 0; } } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/PhotoCropActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/PhotoCropActivity.java index af94958c1..fd947b253 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/PhotoCropActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/PhotoCropActivity.java @@ -29,6 +29,7 @@ import org.telegram.messenger.R; import org.telegram.ui.ActionBar.ActionBar; import org.telegram.ui.ActionBar.ActionBarMenu; import org.telegram.ui.ActionBar.BaseFragment; +import org.telegram.ui.Components.LayoutHelper; import java.io.File; @@ -461,7 +462,7 @@ public class PhotoCropActivity extends BaseFragment { fragmentView = view = new PhotoCropView(context); ((PhotoCropView) fragmentView).freeform = getArguments().getBoolean("freeform", false); - fragmentView.setLayoutParams(new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT)); + fragmentView.setLayoutParams(new FrameLayout.LayoutParams(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT)); return fragmentView; } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/PhotoPickerActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/PhotoPickerActivity.java index d58873cad..ee45902c1 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/PhotoPickerActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/PhotoPickerActivity.java @@ -51,6 +51,7 @@ import org.telegram.messenger.FileLog; import org.telegram.messenger.R; import org.telegram.messenger.TLRPC; import org.telegram.android.MessageObject; +import org.telegram.messenger.UserConfig; import org.telegram.messenger.Utilities; import org.telegram.ui.ActionBar.ActionBarMenu; import org.telegram.ui.ActionBar.ActionBarMenuItem; @@ -59,6 +60,7 @@ import org.telegram.ui.ActionBar.ActionBar; import org.telegram.ui.ActionBar.BaseFragment; import org.telegram.ui.Cells.PhotoPickerPhotoCell; import org.telegram.ui.Components.BackupImageView; +import org.telegram.ui.Components.LayoutHelper; import org.telegram.ui.Components.PhotoPickerBottomLayout; import java.net.URLEncoder; @@ -72,6 +74,7 @@ public class PhotoPickerActivity extends BaseFragment implements NotificationCen public interface PhotoPickerActivityDelegate { void selectedPhotosChanged(); void actionButtonPressed(boolean canceled); + boolean didSelectVideo(String path); } private RequestQueue requestQueue; @@ -102,10 +105,11 @@ public class PhotoPickerActivity extends BaseFragment implements NotificationCen private int itemWidth = 100; private boolean sendPressed; private boolean singlePhoto; + private ChatActivity chatActivity; private PhotoPickerActivityDelegate delegate; - public PhotoPickerActivity(int type, MediaController.AlbumEntry selectedAlbum, HashMap selectedPhotos, HashMap selectedWebPhotos, ArrayList recentImages, boolean onlyOnePhoto) { + public PhotoPickerActivity(int type, MediaController.AlbumEntry selectedAlbum, HashMap selectedPhotos, HashMap selectedWebPhotos, ArrayList recentImages, boolean onlyOnePhoto, ChatActivity chatActivity) { super(); this.selectedAlbum = selectedAlbum; this.selectedPhotos = selectedPhotos; @@ -113,6 +117,10 @@ public class PhotoPickerActivity extends BaseFragment implements NotificationCen this.type = type; this.recentImages = recentImages; this.singlePhoto = onlyOnePhoto; + this.chatActivity = chatActivity; + if (selectedAlbum != null && selectedAlbum.isVideo) { + singlePhoto = true; + } } @Override @@ -256,8 +264,8 @@ public class PhotoPickerActivity extends BaseFragment implements NotificationCen listView.setSelector(R.drawable.list_selector); frameLayout.addView(listView); FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) listView.getLayoutParams(); - layoutParams.width = FrameLayout.LayoutParams.MATCH_PARENT; - layoutParams.height = FrameLayout.LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; + layoutParams.height = LayoutHelper.MATCH_PARENT; layoutParams.bottomMargin = singlePhoto ? 0 : AndroidUtilities.dp(48); listView.setLayoutParams(layoutParams); listView.setAdapter(listAdapter = new ListAdapter(context)); @@ -265,21 +273,30 @@ public class PhotoPickerActivity extends BaseFragment implements NotificationCen listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView adapterView, View view, int i, long l) { - ArrayList arrayList = null; - if (selectedAlbum != null) { - arrayList = (ArrayList) selectedAlbum.photos; - } else { - if (searchResult.isEmpty() && lastSearchString == null) { - arrayList = (ArrayList) recentImages; - } else { - arrayList = (ArrayList) searchResult; + if (selectedAlbum != null && selectedAlbum.isVideo) { + if (i < 0 || i >= selectedAlbum.photos.size()) { + return; } + if (delegate.didSelectVideo(selectedAlbum.photos.get(i).path)) { + finishFragment(); + } + } else { + ArrayList arrayList = null; + if (selectedAlbum != null) { + arrayList = (ArrayList) selectedAlbum.photos; + } else { + if (searchResult.isEmpty() && lastSearchString == null) { + arrayList = (ArrayList) recentImages; + } else { + arrayList = (ArrayList) searchResult; + } + } + if (i < 0 || i >= arrayList.size()) { + return; + } + PhotoViewer.getInstance().setParentActivity(getParentActivity()); + PhotoViewer.getInstance().openPhotoForSelect(arrayList, i, singlePhoto ? 1 : 0, PhotoPickerActivity.this, chatActivity); } - if (i < 0 || i >= arrayList.size()) { - return; - } - PhotoViewer.getInstance().setParentActivity(getParentActivity()); - PhotoViewer.getInstance().openPhotoForSelect(arrayList, i, singlePhoto ? 1 : 0, PhotoPickerActivity.this); } }); @@ -326,8 +343,8 @@ public class PhotoPickerActivity extends BaseFragment implements NotificationCen } frameLayout.addView(emptyView); layoutParams = (FrameLayout.LayoutParams) emptyView.getLayoutParams(); - layoutParams.width = FrameLayout.LayoutParams.MATCH_PARENT; - layoutParams.height = FrameLayout.LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; + layoutParams.height = LayoutHelper.MATCH_PARENT; layoutParams.bottomMargin = singlePhoto ? 0 : AndroidUtilities.dp(48); emptyView.setLayoutParams(layoutParams); emptyView.setOnTouchListener(new View.OnTouchListener() { @@ -362,16 +379,16 @@ public class PhotoPickerActivity extends BaseFragment implements NotificationCen progressView.setVisibility(View.GONE); frameLayout.addView(progressView); layoutParams = (FrameLayout.LayoutParams) progressView.getLayoutParams(); - layoutParams.width = FrameLayout.LayoutParams.MATCH_PARENT; - layoutParams.height = FrameLayout.LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; + layoutParams.height = LayoutHelper.MATCH_PARENT; layoutParams.bottomMargin = singlePhoto ? 0 : AndroidUtilities.dp(48); progressView.setLayoutParams(layoutParams); ProgressBar progressBar = new ProgressBar(context); progressView.addView(progressBar); layoutParams = (FrameLayout.LayoutParams) progressBar.getLayoutParams(); - layoutParams.width = FrameLayout.LayoutParams.WRAP_CONTENT; - layoutParams.height = FrameLayout.LayoutParams.WRAP_CONTENT; + layoutParams.width = LayoutHelper.WRAP_CONTENT; + layoutParams.height = LayoutHelper.WRAP_CONTENT; layoutParams.gravity = Gravity.CENTER; progressBar.setLayoutParams(layoutParams); @@ -381,7 +398,7 @@ public class PhotoPickerActivity extends BaseFragment implements NotificationCen photoPickerBottomLayout = new PhotoPickerBottomLayout(context); frameLayout.addView(photoPickerBottomLayout); layoutParams = (FrameLayout.LayoutParams) photoPickerBottomLayout.getLayoutParams(); - layoutParams.width = FrameLayout.LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; layoutParams.height = AndroidUtilities.dp(48); layoutParams.gravity = Gravity.BOTTOM; photoPickerBottomLayout.setLayoutParams(layoutParams); @@ -501,7 +518,11 @@ public class PhotoPickerActivity extends BaseFragment implements NotificationCen cell.photoImage.setImage(photoEntry.thumbPath, null, cell.getContext().getResources().getDrawable(R.drawable.nophotos)); } else if (photoEntry.path != null) { cell.photoImage.setOrientation(photoEntry.orientation, true); - cell.photoImage.setImage("thumb://" + photoEntry.imageId + ":" + photoEntry.path, null, cell.getContext().getResources().getDrawable(R.drawable.nophotos)); + if (photoEntry.isVideo) { + cell.photoImage.setImage("vthumb://" + photoEntry.imageId + ":" + photoEntry.path, null, cell.getContext().getResources().getDrawable(R.drawable.nophotos)); + } else { + cell.photoImage.setImage("thumb://" + photoEntry.imageId + ":" + photoEntry.path, null, cell.getContext().getResources().getDrawable(R.drawable.nophotos)); + } } else { cell.photoImage.setImageResource(R.drawable.nophotos); } @@ -777,7 +798,10 @@ public class PhotoPickerActivity extends BaseFragment implements NotificationCen if (nextSearchBingString != null) { url = nextSearchBingString; } else { - url = String.format(Locale.US, "https://api.datamarket.azure.com/Bing/Search/v1/Image?Query='%s'&$skip=%d&$top=%d&$format=json&Adult='Off'", URLEncoder.encode(query, "UTF-8"), offset, count); + boolean adult = false; + String phone = UserConfig.getCurrentUser().phone; + adult = phone.startsWith("44") || phone.startsWith("49") || phone.startsWith("43") || phone.startsWith("31") || phone.startsWith("1"); + url = String.format(Locale.US, "https://api.datamarket.azure.com/Bing/Search/v1/Image?Query='%s'&$skip=%d&$top=%d&$format=json%s", URLEncoder.encode(query, "UTF-8"), offset, count, adult ? "" : "&Adult='Off'"); } JsonObjectRequest jsonObjReq = new JsonObjectRequest(Request.Method.GET, url, null, new Response.Listener() { @@ -1028,7 +1052,11 @@ public class PhotoPickerActivity extends BaseFragment implements NotificationCen imageView.setImage(photoEntry.thumbPath, null, mContext.getResources().getDrawable(R.drawable.nophotos)); } else if (photoEntry.path != null) { imageView.setOrientation(photoEntry.orientation, true); - imageView.setImage("thumb://" + photoEntry.imageId + ":" + photoEntry.path, null, mContext.getResources().getDrawable(R.drawable.nophotos)); + if (photoEntry.isVideo) { + imageView.setImage("vthumb://" + photoEntry.imageId + ":" + photoEntry.path, null, mContext.getResources().getDrawable(R.drawable.nophotos)); + } else { + imageView.setImage("thumb://" + photoEntry.imageId + ":" + photoEntry.path, null, mContext.getResources().getDrawable(R.drawable.nophotos)); + } } else { imageView.setImageResource(R.drawable.nophotos); } @@ -1051,7 +1079,7 @@ public class PhotoPickerActivity extends BaseFragment implements NotificationCen cell.checkBox.setChecked(selectedWebPhotos.containsKey(photoEntry.id), false); showing = PhotoViewer.getInstance().isShowingImage(photoEntry.thumbUrl); } - imageView.getImageReceiver().setVisible(!showing, false); + imageView.getImageReceiver().setVisible(!showing, true); cell.checkBox.setVisibility(singlePhoto || showing ? View.GONE : View.VISIBLE); } else if (viewType == 1) { if (view == null) { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/PhotoViewer.java b/TMessagesProj/src/main/java/org/telegram/ui/PhotoViewer.java index 79bd4d4cb..699433b21 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/PhotoViewer.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/PhotoViewer.java @@ -30,16 +30,19 @@ import android.text.TextUtils; import android.util.TypedValue; import android.view.GestureDetector; import android.view.Gravity; +import android.view.KeyEvent; import android.view.MotionEvent; import android.view.Surface; import android.view.VelocityTracker; import android.view.View; import android.view.ViewGroup; -import android.view.ViewTreeObserver; import android.view.WindowManager; import android.view.animation.DecelerateInterpolator; +import android.widget.AdapterView; import android.widget.FrameLayout; import android.widget.ImageView; +import android.widget.ListView; +import android.widget.RelativeLayout; import android.widget.Scroller; import android.widget.TextView; @@ -61,10 +64,11 @@ import org.telegram.messenger.TLRPC; import org.telegram.messenger.UserConfig; import org.telegram.android.MessageObject; import org.telegram.messenger.Utilities; -import org.telegram.ui.AnimationCompat.AnimatorListenerAdapterProxy; -import org.telegram.ui.AnimationCompat.AnimatorSetProxy; -import org.telegram.ui.AnimationCompat.ObjectAnimatorProxy; -import org.telegram.ui.AnimationCompat.ViewProxy; +import org.telegram.ui.Adapters.MentionsAdapter; +import org.telegram.android.AnimationCompat.AnimatorListenerAdapterProxy; +import org.telegram.android.AnimationCompat.AnimatorSetProxy; +import org.telegram.android.AnimationCompat.ObjectAnimatorProxy; +import org.telegram.android.AnimationCompat.ViewProxy; import org.telegram.ui.ActionBar.ActionBar; import org.telegram.ui.ActionBar.ActionBarMenu; import org.telegram.ui.ActionBar.ActionBarMenuItem; @@ -72,9 +76,12 @@ import org.telegram.ui.Components.CheckBox; import org.telegram.ui.Components.ClippingImageView; import org.telegram.android.ImageReceiver; import org.telegram.ui.Components.GifDrawable; +import org.telegram.ui.Components.LayoutHelper; import org.telegram.ui.Components.PhotoCropView; import org.telegram.ui.Components.PhotoFilterView; import org.telegram.ui.Components.PhotoPickerBottomLayout; +import org.telegram.ui.Components.PhotoViewerCaptionEnterView; +import org.telegram.ui.Components.SizeNotifierRelativeLayoutPhoto; import java.io.File; import java.lang.ref.WeakReference; @@ -88,14 +95,14 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat private int classGuid; private PhotoViewerProvider placeProvider; - private boolean isVisible = false; + private boolean isVisible; private Activity parentActivity; private ActionBar actionBar; private boolean isActionBarVisible = true; - private static Drawable[] progressDrawables = null; + private static Drawable[] progressDrawables; private WindowManager.LayoutParams windowLayoutParams; private FrameLayoutDrawer containerView; @@ -106,7 +113,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat private TextView dateTextView; private ActionBarMenuItem menuItem; private ImageView shareButton; - private ColorDrawable backgroundDrawable = new ColorDrawable(0xff000000); + private BackgroundDrawable backgroundDrawable = new BackgroundDrawable(0xff000000); private CheckBox checkImageView; private PhotoPickerBottomLayout pickerView; private PhotoPickerBottomLayout editorDoneLayout; @@ -114,12 +121,27 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat private GifDrawable gifDrawable; private ActionBarMenuItem cropItem; private ActionBarMenuItem tuneItem; + private ActionBarMenuItem captionItem; + private ActionBarMenuItem captionDoneItem; private AnimatorSetProxy currentActionBarAnimation; private PhotoCropView photoCropView; private PhotoFilterView photoFilterView; - private AlertDialog visibleDialog = null; + private AlertDialog visibleDialog; + private TextView captionTextView; + private TextView captionTextViewOld; + private TextView captionTextViewNew; + private PhotoViewerCaptionEnterView captionEditText; private boolean canShowBottom = true; private int sendPhotoType = 0; + private boolean needCaptionLayout; + + private float animationValues[][] = new float[2][8]; + + private ChatActivity parentChatActivity; + private MentionsAdapter mentionsAdapter; + private ListView mentionListView; + private AnimatorSetProxy mentionListAnimation; + private boolean allowMentions; private int animationInProgress = 0; private long transitionAnimationStartTime = 0; @@ -128,6 +150,8 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat private PlaceProviderObject hideAfterAnimation; private boolean disableShowCheck = false; + private String lastTitle; + private int currentEditMode; private ImageReceiver leftImage = new ImageReceiver(); @@ -161,6 +185,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat private float animationValue; private long animationStartTime; private AnimatorSetProxy imageMoveAnimation; + private AnimatorSetProxy changeModeAnimation; private GestureDetector gestureDetector; private DecelerateInterpolator interpolator = new DecelerateInterpolator(1.5f); private float pinchStartDistance = 0; @@ -204,10 +229,43 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat private final static int gallery_menu_crop = 4; private final static int gallery_menu_delete = 6; private final static int gallery_menu_tune = 7; + private final static int gallery_menu_caption = 8; + private final static int gallery_menu_caption_done = 9; private final static int PAGE_SPACING = AndroidUtilities.dp(30); - private static class RadialProgressView { + private static DecelerateInterpolator decelerateInterpolator = null; + private static Paint progressPaint = null; + + private class BackgroundDrawable extends ColorDrawable { + + private Runnable drawRunnable; + + public BackgroundDrawable(int color) { + super(color); + } + + @Override + public void setAlpha(int alpha) { + if (parentActivity instanceof LaunchActivity) { + ((LaunchActivity) parentActivity).drawerLayoutContainer.setAllowDrawContent(!isVisible || alpha != 255); + } + super.setAlpha(alpha); + } + + @Override + public void draw(Canvas canvas) { + super.draw(canvas); + if (getAlpha() != 0) { + if (drawRunnable != null) { + drawRunnable.run(); + drawRunnable = null; + } + } + } + } + + private class RadialProgressView { private long lastUpdateTime = 0; private float radOffset = 0; @@ -224,9 +282,6 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat private float alpha = 1.0f; private float scale = 1.0f; - private static DecelerateInterpolator decelerateInterpolator = null; - private static Paint progressPaint = null; - public RadialProgressView(Context context, View parentView) { if (decelerateInterpolator == null) { decelerateInterpolator = new DecelerateInterpolator(1.5f); @@ -302,8 +357,8 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat public void onDraw(Canvas canvas) { int sizeScaled = (int) (size * scale); - int x = (canvas.getWidth() - sizeScaled) / 2; - int y = (canvas.getHeight() - sizeScaled) / 2; + int x = (getContainerViewWidth() - sizeScaled) / 2; + int y = (getContainerViewHeight() - sizeScaled) / 2; if (previousBackgroundState >= 0 && previousBackgroundState < 4) { Drawable drawable = progressDrawables[previousBackgroundState]; @@ -428,6 +483,10 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat } private class FrameLayoutTouchListener extends FrameLayout { + + private boolean attachedToWindow; + private Runnable attachRunnable; + public FrameLayoutTouchListener(Context context) { super(context); } @@ -442,9 +501,21 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat super.onLayout(changed, left, top, right, bottom); getInstance().onLayout(changed, left, top, right, bottom); } + + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + attachedToWindow = true; + } + + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + attachedToWindow = false; + } } - private class FrameLayoutDrawer extends FrameLayout { + private class FrameLayoutDrawer extends SizeNotifierRelativeLayoutPhoto { public FrameLayoutDrawer(Context context) { super(context); setWillNotDraw(false); @@ -454,6 +525,27 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat protected void onDraw(Canvas canvas) { getInstance().onDraw(canvas); } + + @Override + protected boolean drawChild(Canvas canvas, View child, long drawingTime) { + if ((child == captionEditText || child == pickerView || child == captionTextView || child == mentionListView)) { + int state = captionEditText.getKeyboardTransitionState(); + if (!(state == 0 || state == 1 || state == 2)) { + if (child == captionTextView) { + AndroidUtilities.runOnUIThread(new Runnable() { + @Override + public void run() { + if (captionTextView != null) { + captionTextView.invalidate(); + } + } + }, 50); + } + return false; + } + } + return super.drawChild(canvas, child, drawingTime); + } } private static volatile PhotoViewer Instance = null; @@ -668,10 +760,14 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat } } } + } else if (id == NotificationCenter.emojiDidLoaded) { + if (captionTextView != null) { + captionTextView.invalidate(); + } } } - public void setParentActivity(Activity activity) { + public void setParentActivity(final Activity activity) { if (parentActivity == activity) { return; } @@ -687,21 +783,30 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat scroller = new Scroller(activity); - windowView = new FrameLayoutTouchListener(activity); + windowView = new FrameLayoutTouchListener(activity) { + @Override + public boolean dispatchKeyEventPreIme(KeyEvent event) { + if (event != null && event.getKeyCode() == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_UP) { + if (captionEditText.isEmojiPopupShowing() || captionEditText.isKeyboardVisible()) { + closeCaptionEnter(false); + return false; + } + PhotoViewer.getInstance().closePhoto(true, false); + return true; + } + return super.dispatchKeyEventPreIme(event); + } + }; windowView.setBackgroundDrawable(backgroundDrawable); windowView.setFocusable(false); - animatingImageView = new ClippingImageView(windowView.getContext()); - windowView.addView(animatingImageView); + animatingImageView = new ClippingImageView(activity); + animatingImageView.setAnimationValues(animationValues); + windowView.addView(animatingImageView, LayoutHelper.createFrame(40, 40)); containerView = new FrameLayoutDrawer(activity); containerView.setFocusable(false); - windowView.addView(containerView); - FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) containerView.getLayoutParams(); - layoutParams.width = FrameLayout.LayoutParams.MATCH_PARENT; - layoutParams.height = FrameLayout.LayoutParams.MATCH_PARENT; - layoutParams.gravity = Gravity.TOP | Gravity.LEFT; - containerView.setLayoutParams(layoutParams); + windowView.addView(containerView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.TOP | Gravity.LEFT)); windowLayoutParams = new WindowManager.LayoutParams(); windowLayoutParams.height = WindowManager.LayoutParams.MATCH_PARENT; @@ -717,15 +822,16 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat actionBar.setItemsBackground(R.drawable.bar_selector_white); actionBar.setBackButtonImage(R.drawable.ic_ab_back); actionBar.setTitle(LocaleController.formatString("Of", R.string.Of, 1, 1)); - containerView.addView(actionBar); - layoutParams = (FrameLayout.LayoutParams) actionBar.getLayoutParams(); - layoutParams.width = FrameLayout.LayoutParams.MATCH_PARENT; - actionBar.setLayoutParams(layoutParams); + containerView.addView(actionBar, LayoutHelper.createRelative(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); actionBar.setActionBarMenuOnItemClick(new ActionBar.ActionBarMenuOnItemClick() { @Override public void onItemClick(int id) { if (id == -1) { + if (needCaptionLayout && (captionEditText.isEmojiPopupShowing() || captionEditText.isKeyboardVisible())) { + closeCaptionEnter(false); + return; + } closePhoto(true, false); } else if (id == gallery_menu_save) { File f = null; @@ -875,6 +981,22 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat }); builder.setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), null); showAlertDialog(builder); + } else if (id == gallery_menu_caption) { + if (imageMoveAnimation != null || changeModeAnimation != null) { + return; + } + cropItem.setVisibility(View.GONE); + tuneItem.setVisibility(View.GONE); + captionItem.setVisibility(View.GONE); + checkImageView.setVisibility(View.GONE); + captionDoneItem.setVisibility(View.VISIBLE); + pickerView.setVisibility(View.GONE); + captionTextView.setVisibility(View.INVISIBLE); + captionEditText.openKeyboard(); + lastTitle = actionBar.getTitle(); + actionBar.setTitle(LocaleController.getString("PhotoCaption", R.string.PhotoCaption)); + } else if (id == gallery_menu_caption_done) { + closeCaptionEnter(true); } } @@ -903,17 +1025,36 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat menuItem.addSubItem(gallery_menu_save, LocaleController.getString("SaveToGallery", R.string.SaveToGallery), 0); menuItem.addSubItem(gallery_menu_delete, LocaleController.getString("Delete", R.string.Delete), 0); + captionDoneItem = menu.addItemWithWidth(gallery_menu_caption_done, R.drawable.ic_done, AndroidUtilities.dp(56)); + captionItem = menu.addItemWithWidth(gallery_menu_caption, R.drawable.photo_text, AndroidUtilities.dp(56)); cropItem = menu.addItemWithWidth(gallery_menu_crop, R.drawable.photo_crop, AndroidUtilities.dp(56)); - tuneItem = menu.addItemWithWidth(gallery_menu_tune, R.drawable.tune, AndroidUtilities.dp(56)); + tuneItem = menu.addItemWithWidth(gallery_menu_tune, R.drawable.photo_tools, AndroidUtilities.dp(56)); - bottomLayout = new FrameLayout(containerView.getContext()); - containerView.addView(bottomLayout); - layoutParams = (FrameLayout.LayoutParams) bottomLayout.getLayoutParams(); - layoutParams.width = FrameLayout.LayoutParams.MATCH_PARENT; - layoutParams.height = AndroidUtilities.dp(48); - layoutParams.gravity = Gravity.BOTTOM | Gravity.LEFT; - bottomLayout.setLayoutParams(layoutParams); + bottomLayout = new FrameLayout(parentActivity); bottomLayout.setBackgroundColor(0x7f000000); + containerView.addView(bottomLayout, LayoutHelper.createRelative(LayoutHelper.MATCH_PARENT, 48, RelativeLayout.ALIGN_PARENT_BOTTOM)); + + captionTextViewOld = new TextView(parentActivity); + captionTextViewOld.setMaxLines(10); + captionTextViewOld.setBackgroundColor(0x7f000000); + captionTextViewOld.setPadding(AndroidUtilities.dp(16), AndroidUtilities.dp(8), AndroidUtilities.dp(16), AndroidUtilities.dp(8)); + captionTextViewOld.setLinkTextColor(0xffffffff); + captionTextViewOld.setTextColor(0xffffffff); + captionTextViewOld.setGravity(Gravity.CENTER_VERTICAL | Gravity.LEFT); + captionTextViewOld.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16); + captionTextViewOld.setVisibility(View.INVISIBLE); + containerView.addView(captionTextViewOld, LayoutHelper.createRelative(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, 0, 0, 0, 48, RelativeLayout.ALIGN_PARENT_BOTTOM)); + + captionTextView = captionTextViewNew = new TextView(parentActivity); + captionTextViewNew.setMaxLines(10); + captionTextViewNew.setBackgroundColor(0x7f000000); + captionTextViewNew.setPadding(AndroidUtilities.dp(16), AndroidUtilities.dp(8), AndroidUtilities.dp(16), AndroidUtilities.dp(8)); + captionTextViewNew.setLinkTextColor(0xffffffff); + captionTextViewNew.setTextColor(0xffffffff); + captionTextViewNew.setGravity(Gravity.CENTER_VERTICAL | Gravity.LEFT); + captionTextViewNew.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16); + captionTextViewNew.setVisibility(View.INVISIBLE); + containerView.addView(captionTextViewNew, LayoutHelper.createRelative(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, 0, 0, 0, 48, RelativeLayout.ALIGN_PARENT_BOTTOM)); radialProgressViews[0] = new RadialProgressView(containerView.getContext(), containerView); radialProgressViews[0].setBackgroundState(0, false); @@ -926,12 +1067,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat shareButton.setImageResource(R.drawable.share); shareButton.setScaleType(ImageView.ScaleType.CENTER); shareButton.setBackgroundResource(R.drawable.bar_selector_white); - bottomLayout.addView(shareButton); - layoutParams = (FrameLayout.LayoutParams) shareButton.getLayoutParams(); - layoutParams.width = AndroidUtilities.dp(50); - layoutParams.height = FrameLayout.LayoutParams.MATCH_PARENT; - layoutParams.gravity = Gravity.TOP | Gravity.RIGHT; - shareButton.setLayoutParams(layoutParams); + bottomLayout.addView(shareButton, LayoutHelper.createFrame(50, LayoutHelper.MATCH_PARENT, Gravity.TOP | Gravity.RIGHT)); shareButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { @@ -978,15 +1114,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat nameTextView.setEllipsize(TextUtils.TruncateAt.END); nameTextView.setTextColor(0xffffffff); nameTextView.setGravity(Gravity.LEFT); - bottomLayout.addView(nameTextView); - layoutParams = (FrameLayout.LayoutParams) nameTextView.getLayoutParams(); - layoutParams.width = FrameLayout.LayoutParams.MATCH_PARENT; - layoutParams.height = FrameLayout.LayoutParams.WRAP_CONTENT; - layoutParams.gravity = Gravity.TOP | Gravity.LEFT; - layoutParams.leftMargin = AndroidUtilities.dp(16); - layoutParams.rightMargin = AndroidUtilities.dp(50); - layoutParams.topMargin = AndroidUtilities.dp(5); - nameTextView.setLayoutParams(layoutParams); + bottomLayout.addView(nameTextView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.TOP | Gravity.LEFT, 16, 5, 60, 0)); dateTextView = new TextView(containerView.getContext()); dateTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 13); @@ -996,24 +1124,11 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat dateTextView.setTextColor(0xffffffff); dateTextView.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); dateTextView.setGravity(Gravity.LEFT); - bottomLayout.addView(dateTextView); - layoutParams = (FrameLayout.LayoutParams) dateTextView.getLayoutParams(); - layoutParams.width = FrameLayout.LayoutParams.MATCH_PARENT; - layoutParams.height = FrameLayout.LayoutParams.WRAP_CONTENT; - layoutParams.gravity = Gravity.TOP | Gravity.LEFT; - layoutParams.leftMargin = AndroidUtilities.dp(16); - layoutParams.rightMargin = AndroidUtilities.dp(50); - layoutParams.topMargin = AndroidUtilities.dp(25); - dateTextView.setLayoutParams(layoutParams); + bottomLayout.addView(dateTextView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.TOP | Gravity.LEFT, 16, 25, 50, 0)); pickerView = new PhotoPickerBottomLayout(parentActivity); pickerView.setBackgroundColor(0x7f000000); - containerView.addView(pickerView); - layoutParams = (FrameLayout.LayoutParams) pickerView.getLayoutParams(); - layoutParams.width = FrameLayout.LayoutParams.MATCH_PARENT; - layoutParams.height = AndroidUtilities.dp(48); - layoutParams.gravity = Gravity.BOTTOM; - pickerView.setLayoutParams(layoutParams); + containerView.addView(pickerView, LayoutHelper.createRelative(LayoutHelper.MATCH_PARENT, 48, RelativeLayout.ALIGN_PARENT_BOTTOM)); pickerView.cancelButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { @@ -1037,12 +1152,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat editorDoneLayout.setBackgroundColor(0x7f000000); editorDoneLayout.updateSelectedCount(0, false); editorDoneLayout.setVisibility(View.GONE); - containerView.addView(editorDoneLayout); - layoutParams = (FrameLayout.LayoutParams) editorDoneLayout.getLayoutParams(); - layoutParams.width = FrameLayout.LayoutParams.MATCH_PARENT; - layoutParams.height = AndroidUtilities.dp(48); - layoutParams.gravity = Gravity.BOTTOM; - editorDoneLayout.setLayoutParams(layoutParams); + containerView.addView(editorDoneLayout, LayoutHelper.createRelative(LayoutHelper.MATCH_PARENT, 48, RelativeLayout.ALIGN_PARENT_BOTTOM)); editorDoneLayout.cancelButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { @@ -1057,6 +1167,9 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat public void onClick(View view) { if (currentEditMode == 1) { photoCropView.cancelAnimationRunnable(); + if (imageMoveAnimation != null) { + return; + } } applyCurrentEditMode(); switchToEditMode(0); @@ -1067,29 +1180,22 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat gestureDetector.setOnDoubleTapListener(this); centerImage.setParentView(containerView); + centerImage.setCrossfadeAlpha((byte) 2); leftImage.setParentView(containerView); + leftImage.setCrossfadeAlpha((byte) 2); rightImage.setParentView(containerView); + rightImage.setCrossfadeAlpha((byte) 2); + + WindowManager manager = (WindowManager) ApplicationLoader.applicationContext.getSystemService(Activity.WINDOW_SERVICE); + int rotation = manager.getDefaultDisplay().getRotation(); checkImageView = new CheckBox(containerView.getContext(), R.drawable.selectphoto_large); checkImageView.setDrawBackground(true); checkImageView.setSize(45); checkImageView.setCheckOffset(AndroidUtilities.dp(1)); checkImageView.setColor(0xff3ccaef); - containerView.addView(checkImageView); checkImageView.setVisibility(View.GONE); - layoutParams = (FrameLayout.LayoutParams) checkImageView.getLayoutParams(); - layoutParams.width = AndroidUtilities.dp(45); - layoutParams.height = AndroidUtilities.dp(45); - layoutParams.gravity = Gravity.RIGHT; - layoutParams.rightMargin = AndroidUtilities.dp(10); - WindowManager manager = (WindowManager) ApplicationLoader.applicationContext.getSystemService(Activity.WINDOW_SERVICE); - int rotation = manager.getDefaultDisplay().getRotation(); - if (rotation == Surface.ROTATION_270 || rotation == Surface.ROTATION_90) { - layoutParams.topMargin = AndroidUtilities.dp(58); - } else { - layoutParams.topMargin = AndroidUtilities.dp(68); - } - checkImageView.setLayoutParams(layoutParams); + containerView.addView(checkImageView, LayoutHelper.createRelative(45, 45, 0, rotation == Surface.ROTATION_270 || rotation == Surface.ROTATION_90 ? 58 : 68, 10, 0, RelativeLayout.ALIGN_PARENT_RIGHT)); checkImageView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { @@ -1100,6 +1206,220 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat } } }); + + captionEditText = new PhotoViewerCaptionEnterView(parentActivity, windowView, containerView); + captionEditText.setId(1000); + captionEditText.setDelegate(new PhotoViewerCaptionEnterView.PhotoViewerCaptionEnterViewDelegate() { + @Override + public void onCaptionEnter() { + closeCaptionEnter(true); + } + + @Override + public void onTextChanged(CharSequence text, boolean bigChange) { + if (mentionsAdapter != null && captionEditText != null && parentChatActivity != null && text != null) { + mentionsAdapter.searchUsernameOrHashtag(text.toString(), captionEditText.getCursorPosition(), parentChatActivity.messages); + } + } + + @Override + public void onWindowSizeChanged(int size) { + int height = AndroidUtilities.dp(36 * Math.min(3, mentionsAdapter.getCount()) + (mentionsAdapter.getCount() > 3 ? 18 : 0)); + if (size - AndroidUtilities.getCurrentActionBarHeight() * 2 < height) { + allowMentions = false; + if (mentionListView != null && mentionListView.getVisibility() == View.VISIBLE) { + mentionListView.clearAnimation(); + mentionListView.setVisibility(View.INVISIBLE); + } + } else { + allowMentions = true; + if (mentionListView != null && mentionListView.getVisibility() == View.INVISIBLE) { + mentionListView.clearAnimation(); + mentionListView.setVisibility(View.VISIBLE); + } + } + } + }); + containerView.addView(captionEditText, LayoutHelper.createRelative(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, 0, 0, 0, -400, RelativeLayout.ALIGN_PARENT_BOTTOM)); + + mentionListView = new ListView(parentActivity); + mentionListView.setBackgroundColor(0x7f000000); + mentionListView.setVisibility(View.GONE); + mentionListView.setClipToPadding(true); + mentionListView.setDividerHeight(0); + mentionListView.setDivider(null); + if (Build.VERSION.SDK_INT > 8) { + mentionListView.setOverScrollMode(ListView.OVER_SCROLL_NEVER); + } + containerView.addView(mentionListView, LayoutHelper.createRelative(LayoutHelper.MATCH_PARENT, 110, 0, -110, 0, 0, RelativeLayout.ALIGN_TOP, 1000)); + + mentionListView.setAdapter(mentionsAdapter = new MentionsAdapter(parentActivity, true, new MentionsAdapter.MentionsAdapterDelegate() { + @Override + public void needChangePanelVisibility(boolean show) { + if (show) { + RelativeLayout.LayoutParams layoutParams3 = (RelativeLayout.LayoutParams) mentionListView.getLayoutParams(); + int height = 36 * Math.min(3, mentionsAdapter.getCount()) + (mentionsAdapter.getCount() > 3 ? 18 : 0); + layoutParams3.height = AndroidUtilities.dp(height); + layoutParams3.topMargin = -AndroidUtilities.dp(height); + mentionListView.setLayoutParams(layoutParams3); + + if (mentionListAnimation != null) { + mentionListAnimation.cancel(); + mentionListAnimation = null; + } + + if (mentionListView.getVisibility() == View.VISIBLE) { + ViewProxy.setAlpha(mentionListView, 1.0f); + return; + } + if (allowMentions) { + mentionListView.setVisibility(View.VISIBLE); + mentionListAnimation = new AnimatorSetProxy(); + mentionListAnimation.playTogether( + ObjectAnimatorProxy.ofFloat(mentionListView, "alpha", 0.0f, 1.0f) + ); + mentionListAnimation.addListener(new AnimatorListenerAdapterProxy() { + @Override + public void onAnimationEnd(Object animation) { + if (mentionListAnimation != null && mentionListAnimation.equals(animation)) { + mentionListView.clearAnimation(); + mentionListAnimation = null; + } + } + }); + mentionListAnimation.setDuration(200); + mentionListAnimation.start(); + } else { + ViewProxy.setAlpha(mentionListView, 1.0f); + mentionListView.clearAnimation(); + mentionListView.setVisibility(View.INVISIBLE); + } + } else { + if (mentionListAnimation != null) { + mentionListAnimation.cancel(); + mentionListAnimation = null; + } + + if (mentionListView.getVisibility() == View.GONE) { + return; + } + if (allowMentions) { + mentionListAnimation = new AnimatorSetProxy(); + mentionListAnimation.playTogether( + ObjectAnimatorProxy.ofFloat(mentionListView, "alpha", 0.0f) + ); + mentionListAnimation.addListener(new AnimatorListenerAdapterProxy() { + @Override + public void onAnimationEnd(Object animation) { + if (mentionListAnimation != null && mentionListAnimation.equals(animation)) { + mentionListView.clearAnimation(); + mentionListView.setVisibility(View.GONE); + mentionListAnimation = null; + } + } + }); + mentionListAnimation.setDuration(200); + mentionListAnimation.start(); + } else { + mentionListView.clearAnimation(); + mentionListView.setVisibility(View.GONE); + } + } + } + })); + + mentionListView.setOnItemClickListener(new AdapterView.OnItemClickListener() { + @Override + public void onItemClick(AdapterView parent, View view, int position, long id) { + Object object = mentionsAdapter.getItem(position); + int start = mentionsAdapter.getResultStartPosition(); + int len = mentionsAdapter.getResultLength(); + if (object instanceof TLRPC.User) { + TLRPC.User user = (TLRPC.User) object; + if (user != null) { + captionEditText.replaceWithText(start, len, "@" + user.username + " "); + } + } else if (object instanceof String) { + captionEditText.replaceWithText(start, len, object + " "); + } + } + }); + + mentionListView.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() { + @Override + public boolean onItemLongClick(AdapterView parent, View view, int position, long id) { + Object object = mentionsAdapter.getItem(position); + if (object instanceof String) { + AlertDialog.Builder builder = new AlertDialog.Builder(parentActivity); + builder.setTitle(LocaleController.getString("AppName", R.string.AppName)); + builder.setMessage(LocaleController.getString("ClearSearch", R.string.ClearSearch)); + builder.setPositiveButton(LocaleController.getString("ClearButton", R.string.ClearButton).toUpperCase(), new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialogInterface, int i) { + mentionsAdapter.clearRecentHashtags(); + } + }); + builder.setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), null); + showAlertDialog(builder); + return true; + } + return false; + } + }); + } + + private void updateCaptionTextForCurrentPhoto(Object object) { + CharSequence caption = null; + if (object instanceof MediaController.PhotoEntry) { + caption = ((MediaController.PhotoEntry) object).caption; + } else if (object instanceof MediaController.SearchImage) { + caption = ((MediaController.SearchImage) object).caption; + } + if (caption == null || caption.length() == 0) { + captionEditText.setFieldText(""); + } else { + captionEditText.setFieldText(caption); + } + } + + private void closeCaptionEnter(boolean apply) { + Object object = imagesArrLocals.get(currentIndex); + if (apply) { + if (object instanceof MediaController.PhotoEntry) { + ((MediaController.PhotoEntry) object).caption = captionEditText.getFieldCharSequence(); + } else if (object instanceof MediaController.SearchImage) { + ((MediaController.SearchImage) object).caption = captionEditText.getFieldCharSequence(); + } + + if (captionEditText.getFieldCharSequence().length() != 0 && !placeProvider.isPhotoChecked(currentIndex)) { + placeProvider.setPhotoChecked(currentIndex); + checkImageView.setChecked(placeProvider.isPhotoChecked(currentIndex), true); + updateSelectedCount(); + } + } + cropItem.setVisibility(View.VISIBLE); + captionItem.setVisibility(View.VISIBLE); + if (Build.VERSION.SDK_INT >= 16) { + tuneItem.setVisibility(View.VISIBLE); + } + if (sendPhotoType == 0) { + checkImageView.setVisibility(View.VISIBLE); + } + captionDoneItem.setVisibility(View.GONE); + pickerView.setVisibility(View.VISIBLE); + + if (lastTitle != null) { + actionBar.setTitle(lastTitle); + lastTitle = null; + } + + updateCaptionTextForCurrentPhoto(object); + setCurrentCaption(captionEditText.getFieldCharSequence()); + if (captionEditText.isEmojiPopupShowing()) { + captionEditText.hideEmojiPopup(); + } else { + captionEditText.closeKeyboard(); + } } private void showAlertDialog(AlertDialog.Builder builder) { @@ -1179,7 +1499,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat } private void switchToEditMode(final int mode) { - if (currentEditMode == mode || centerImage.getBitmap() == null || imageMoveAnimation != null || radialProgressViews[0].backgroundState != -1) { + if (currentEditMode == mode || centerImage.getBitmap() == null || changeModeAnimation != null || imageMoveAnimation != null || radialProgressViews[0].backgroundState != -1) { return; } if (mode == 0) { @@ -1255,6 +1575,9 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat ArrayList arrayList = new ArrayList<>(); arrayList.add(ObjectAnimatorProxy.ofFloat(pickerView, "translationY", 0)); arrayList.add(ObjectAnimatorProxy.ofFloat(actionBar, "translationY", 0)); + if (needCaptionLayout) { + arrayList.add(ObjectAnimatorProxy.ofFloat(captionTextView, "translationY", 0)); + } if (sendPhotoType == 0) { arrayList.add(ObjectAnimatorProxy.ofFloat(checkImageView, "alpha", 1)); } @@ -1265,6 +1588,9 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat public void onAnimationStart(Object animation) { pickerView.setVisibility(View.VISIBLE); actionBar.setVisibility(View.VISIBLE); + if (needCaptionLayout) { + captionTextView.setVisibility(captionTextView.getTag() != null ? View.VISIBLE : View.INVISIBLE); + } if (sendPhotoType == 0) { checkImageView.setVisibility(View.VISIBLE); } @@ -1274,6 +1600,9 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat public void onAnimationEnd(Object animation) { pickerView.clearAnimation(); actionBar.clearAnimation(); + if (needCaptionLayout) { + captionTextView.clearAnimation(); + } if (sendPhotoType == 0) { checkImageView.clearAnimation(); } @@ -1287,12 +1616,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat if (photoCropView == null) { photoCropView = new PhotoCropView(parentActivity); photoCropView.setVisibility(View.GONE); - containerView.addView(photoCropView); - FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) photoCropView.getLayoutParams(); - layoutParams.width = FrameLayout.LayoutParams.MATCH_PARENT; - layoutParams.height = FrameLayout.LayoutParams.MATCH_PARENT; - layoutParams.bottomMargin = AndroidUtilities.dp(48); - photoCropView.setLayoutParams(layoutParams); + containerView.addView(photoCropView, LayoutHelper.createRelative(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, 0, 0, 0, 48)); photoCropView.setDelegate(new PhotoCropView.PhotoCropViewDelegate() { @Override public void needMoveImageTo(float x, float y, float s, boolean animated) { @@ -1309,22 +1633,30 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat } editorDoneLayout.doneButtonTextView.setText(LocaleController.getString("Crop", R.string.Crop)); - AnimatorSetProxy animatorSet = new AnimatorSetProxy(); + changeModeAnimation = new AnimatorSetProxy(); ArrayList arrayList = new ArrayList<>(); - arrayList.add(ObjectAnimatorProxy.ofFloat(pickerView, "translationY", 0, AndroidUtilities.dp(48))); + arrayList.add(ObjectAnimatorProxy.ofFloat(pickerView, "translationY", 0, AndroidUtilities.dp(96))); arrayList.add(ObjectAnimatorProxy.ofFloat(actionBar, "translationY", 0, -actionBar.getHeight())); + if (needCaptionLayout) { + arrayList.add(ObjectAnimatorProxy.ofFloat(captionTextView, "translationY", 0, AndroidUtilities.dp(96))); + } if (sendPhotoType == 0) { arrayList.add(ObjectAnimatorProxy.ofFloat(checkImageView, "alpha", 1, 0)); } - animatorSet.playTogether(arrayList); - animatorSet.setDuration(200); - animatorSet.addListener(new AnimatorListenerAdapterProxy() { + changeModeAnimation.playTogether(arrayList); + changeModeAnimation.setDuration(200); + changeModeAnimation.addListener(new AnimatorListenerAdapterProxy() { @Override public void onAnimationEnd(Object animation) { + changeModeAnimation = null; pickerView.clearAnimation(); actionBar.clearAnimation(); pickerView.setVisibility(View.GONE); actionBar.setVisibility(View.GONE); + if (needCaptionLayout) { + captionTextView.clearAnimation(); + captionTextView.setVisibility(View.INVISIBLE); + } if (sendPhotoType == 0) { checkImageView.clearAnimation(); checkImageView.setVisibility(View.GONE); @@ -1383,15 +1715,11 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat imageMoveAnimation.start(); } }); - animatorSet.start(); + changeModeAnimation.start(); } else if (mode == 2) { if (photoFilterView == null) { photoFilterView = new PhotoFilterView(parentActivity, centerImage.getBitmap(), centerImage.getOrientation()); - containerView.addView(photoFilterView); - FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) photoFilterView.getLayoutParams(); - layoutParams.width = FrameLayout.LayoutParams.MATCH_PARENT; - layoutParams.height = FrameLayout.LayoutParams.MATCH_PARENT; - photoFilterView.setLayoutParams(layoutParams); + containerView.addView(photoFilterView, LayoutHelper.createRelative(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT)); photoFilterView.getDoneTextView().setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { @@ -1426,22 +1754,30 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat ViewProxy.setTranslationY(photoFilterView.getToolsView(), AndroidUtilities.dp(126)); } - AnimatorSetProxy animatorSet = new AnimatorSetProxy(); + changeModeAnimation = new AnimatorSetProxy(); ArrayList arrayList = new ArrayList<>(); - arrayList.add(ObjectAnimatorProxy.ofFloat(pickerView, "translationY", 0, AndroidUtilities.dp(48))); + arrayList.add(ObjectAnimatorProxy.ofFloat(pickerView, "translationY", 0, AndroidUtilities.dp(96))); arrayList.add(ObjectAnimatorProxy.ofFloat(actionBar, "translationY", 0, -actionBar.getHeight())); + if (needCaptionLayout) { + arrayList.add(ObjectAnimatorProxy.ofFloat(captionTextView, "translationY", 0, AndroidUtilities.dp(96))); + } if (sendPhotoType == 0) { arrayList.add(ObjectAnimatorProxy.ofFloat(checkImageView, "alpha", 1, 0)); } - animatorSet.playTogether(arrayList); - animatorSet.setDuration(200); - animatorSet.addListener(new AnimatorListenerAdapterProxy() { + changeModeAnimation.playTogether(arrayList); + changeModeAnimation.setDuration(200); + changeModeAnimation.addListener(new AnimatorListenerAdapterProxy() { @Override public void onAnimationEnd(Object animation) { + changeModeAnimation = null; pickerView.clearAnimation(); actionBar.clearAnimation(); pickerView.setVisibility(View.GONE); actionBar.setVisibility(View.GONE); + if (needCaptionLayout) { + captionTextView.clearAnimation(); + captionTextView.setVisibility(View.INVISIBLE); + } if (sendPhotoType == 0) { checkImageView.clearAnimation(); checkImageView.setVisibility(View.GONE); @@ -1497,28 +1833,17 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat imageMoveAnimation.start(); } }); - animatorSet.start(); + changeModeAnimation.start(); } - /*Bundle args = new Bundle(); - Bitmap bitmap = centerImage.getBitmap(); - String key = centerImage.getKey(); - if (bitmap == null) { - args.putString("photoPath", currentPathObject); - } - MediaController.PhotoEntry object = - args.putInt("id", object.imageId); - args.putBoolean("freeformCrop", true); - args.putBoolean("onlyCrop", true); - PhotoEditorActivity fragment = new PhotoEditorActivity(args, bitmap, key); - fragment.setDelegate((PhotoCropActivity.PhotoEditActivityDelegate) placeProvider); - ((LaunchActivity) parentActivity).presentFragment(fragment, false, true); - closePhoto(false);*/ } private void toggleCheckImageView(boolean show) { AnimatorSetProxy animatorSet = new AnimatorSetProxy(); ArrayList arrayList = new ArrayList<>(); arrayList.add(ObjectAnimatorProxy.ofFloat(pickerView, "alpha", show ? 1.0f : 0.0f)); + if (needCaptionLayout) { + arrayList.add(ObjectAnimatorProxy.ofFloat(captionTextView, "alpha", show ? 1.0f : 0.0f)); + } if (sendPhotoType == 0) { arrayList.add(ObjectAnimatorProxy.ofFloat(checkImageView, "alpha", show ? 1.0f : 0.0f)); } @@ -1532,6 +1857,9 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat actionBar.setVisibility(View.VISIBLE); if (canShowBottom) { bottomLayout.setVisibility(View.VISIBLE); + if (captionTextView.getTag() != null) { + captionTextView.setVisibility(View.VISIBLE); + } } } isActionBarVisible = show; @@ -1539,11 +1867,14 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat bottomLayout.setEnabled(show); if (animated) { + ArrayList arrayList = new ArrayList<>(); + arrayList.add(ObjectAnimatorProxy.ofFloat(actionBar, "alpha", show ? 1.0f : 0.0f)); + arrayList.add(ObjectAnimatorProxy.ofFloat(bottomLayout, "alpha", show ? 1.0f : 0.0f)); + if (captionTextView.getTag() != null) { + arrayList.add(ObjectAnimatorProxy.ofFloat(captionTextView, "alpha", show ? 1.0f : 0.0f)); + } currentActionBarAnimation = new AnimatorSetProxy(); - currentActionBarAnimation.playTogether( - ObjectAnimatorProxy.ofFloat(actionBar, "alpha", show ? 1.0f : 0.0f), - ObjectAnimatorProxy.ofFloat(bottomLayout, "alpha", show ? 1.0f : 0.0f) - ); + currentActionBarAnimation.playTogether(arrayList); if (!show) { currentActionBarAnimation.addListener(new AnimatorListenerAdapterProxy() { @Override @@ -1553,6 +1884,10 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat if (canShowBottom) { bottomLayout.clearAnimation(); bottomLayout.setVisibility(View.GONE); + if (captionTextView.getTag() != null) { + captionTextView.clearAnimation(); + captionTextView.setVisibility(View.INVISIBLE); + } } currentActionBarAnimation = null; } @@ -1565,11 +1900,18 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat } else { ViewProxy.setAlpha(actionBar, show ? 1.0f : 0.0f); ViewProxy.setAlpha(bottomLayout, show ? 1.0f : 0.0f); + if (captionTextView.getTag() != null) { + ViewProxy.setAlpha(captionTextView, show ? 1.0f : 0.0f); + } if (!show) { actionBar.setVisibility(View.GONE); if (canShowBottom) { bottomLayout.clearAnimation(); bottomLayout.setVisibility(View.GONE); + if (captionTextView.getTag() != null) { + captionTextView.clearAnimation(); + captionTextView.setVisibility(View.INVISIBLE); + } } } } @@ -1756,6 +2098,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat loadingMoreImages = false; cacheEndReached = false; opennedFromMedia = false; + needCaptionLayout = false; canShowBottom = true; imagesArr.clear(); imagesArrLocations.clear(); @@ -1766,6 +2109,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat imagesArrTemp.clear(); imagesByIdsTemp.clear(); currentUserAvatarLocation = null; + containerView.setPadding(0, 0, 0, 0); currentThumb = object != null ? object.thumb : null; menuItem.setVisibility(View.VISIBLE); bottomLayout.setVisibility(View.VISIBLE); @@ -1782,7 +2126,14 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat pickerView.setVisibility(View.GONE); cropItem.setVisibility(View.GONE); tuneItem.setVisibility(View.GONE); + captionItem.setVisibility(View.GONE); + captionDoneItem.setVisibility(View.GONE); + captionEditText.clearAnimation(); + captionEditText.setVisibility(View.GONE); + mentionListView.setVisibility(View.GONE); editorDoneLayout.setVisibility(View.GONE); + captionTextView.setTag(null); + captionTextView.setVisibility(View.INVISIBLE); if (photoCropView != null) { photoCropView.clearAnimation(); photoCropView.setVisibility(View.GONE); @@ -1874,6 +2225,19 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat canShowBottom = false; Object obj = imagesArrLocals.get(index); cropItem.setVisibility(obj instanceof MediaController.PhotoEntry || obj instanceof MediaController.SearchImage && ((MediaController.SearchImage) obj).type == 0 ? View.VISIBLE : View.GONE); + if (parentChatActivity != null && parentChatActivity.currentEncryptedChat == null) { + mentionsAdapter.setChatInfo(parentChatActivity.info); + mentionsAdapter.setNeedUsernames(parentChatActivity.currentChat != null); + captionItem.setVisibility(cropItem.getVisibility()); + captionEditText.setVisibility(cropItem.getVisibility()); + needCaptionLayout = captionItem.getVisibility() == View.VISIBLE; + if (needCaptionLayout) { + RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) captionEditText.getLayoutParams(); + layoutParams.bottomMargin = -AndroidUtilities.dp(400); + captionEditText.setLayoutParams(layoutParams); + captionEditText.onCreate(); + } + } if (Build.VERSION.SDK_INT >= 16) { tuneItem.setVisibility(cropItem.getVisibility()); } @@ -1931,6 +2295,8 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat } else { dateTextView.setText(dateString); } + CharSequence caption = currentMessageObject.caption; + setCurrentCaption(caption); if (totalImagesCount != 0 && !needSearchImageInArr) { if (opennedFromMedia) { @@ -1983,11 +2349,14 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat return; } boolean fromCamera = false; + CharSequence caption = null; if (object instanceof MediaController.PhotoEntry) { currentPathObject = ((MediaController.PhotoEntry) object).path; fromCamera = ((MediaController.PhotoEntry) object).bucketId == 0 && ((MediaController.PhotoEntry) object).dateTaken == 0 && imagesArrLocals.size() == 1; + caption = ((MediaController.PhotoEntry) object).caption; } else if (object instanceof MediaController.SearchImage) { currentPathObject = ((MediaController.SearchImage) object).imageUrl; + caption = ((MediaController.SearchImage) object).caption; } if (fromCamera) { actionBar.setTitle(LocaleController.getString("AttachPhoto", R.string.AttachPhoto)); @@ -1997,6 +2366,9 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat if (sendPhotoType == 0) { checkImageView.setChecked(placeProvider.isPhotoChecked(currentIndex), false); } + + setCurrentCaption(caption); + updateCaptionTextForCurrentPhoto(object); } @@ -2026,6 +2398,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat animateToScale = 1; animationStartTime = 0; imageMoveAnimation = null; + changeModeAnimation = null; pinchStartDistance = 0; pinchStartScale = 1; @@ -2086,6 +2459,33 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat createGifForCurrentImage(); } + private void setCurrentCaption(final CharSequence caption) { + if (caption != null && caption.length() > 0) { + captionTextView = captionTextViewOld; + captionTextViewOld = captionTextViewNew; + captionTextViewNew = captionTextView; + + captionItem.setIcon(R.drawable.photo_text2); + CharSequence oldText = captionTextView.getText(); + captionTextView.setTag(caption); + captionTextView.setText(caption); + ViewProxy.setAlpha(captionTextView, bottomLayout.getVisibility() == View.VISIBLE || pickerView.getVisibility() == View.VISIBLE ? 1.0f : 0.0f); + AndroidUtilities.runOnUIThread(new Runnable() { + @Override + public void run() { + captionTextViewOld.setTag(null); + captionTextViewOld.setVisibility(View.INVISIBLE); + captionTextViewNew.setVisibility(bottomLayout.getVisibility() == View.VISIBLE || pickerView.getVisibility() == View.VISIBLE ? View.VISIBLE : View.INVISIBLE); + } + }); + } else { + captionItem.setIcon(R.drawable.photo_text); + captionTextView.setTag(null); + captionTextView.clearAnimation(); + captionTextView.setVisibility(View.INVISIBLE); + } + } + private void createGifForCurrentImage() { if (gifDrawable != null) { gifDrawable.recycle(); @@ -2269,23 +2669,23 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat } public void openPhoto(final MessageObject messageObject, final PhotoViewerProvider provider) { - openPhoto(messageObject, null, null, null, 0, provider); + openPhoto(messageObject, null, null, null, 0, provider, null); } public void openPhoto(final TLRPC.FileLocation fileLocation, final PhotoViewerProvider provider) { - openPhoto(null, fileLocation, null, null, 0, provider); + openPhoto(null, fileLocation, null, null, 0, provider, null); } public void openPhoto(final ArrayList messages, final int index, final PhotoViewerProvider provider) { - openPhoto(messages.get(index), null, messages, null, index, provider); + openPhoto(messages.get(index), null, messages, null, index, provider, null); } - public void openPhotoForSelect(final ArrayList photos, final int index, int type, final PhotoViewerProvider provider) { + public void openPhotoForSelect(final ArrayList photos, final int index, int type, final PhotoViewerProvider provider, ChatActivity chatActivity) { sendPhotoType = type; if (pickerView != null) { pickerView.doneButtonTextView.setText(sendPhotoType == 1 ? LocaleController.getString("Set", R.string.Set).toUpperCase() : LocaleController.getString("Send", R.string.Send).toUpperCase()); } - openPhoto(null, null, null, photos, index, provider); + openPhoto(null, null, null, photos, index, provider, chatActivity); } private boolean checkAnimation() { @@ -2301,7 +2701,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat return animationInProgress != 0; } - public void openPhoto(final MessageObject messageObject, final TLRPC.FileLocation fileLocation, final ArrayList messages, final ArrayList photos, final int index, final PhotoViewerProvider provider) { + public void openPhoto(final MessageObject messageObject, final TLRPC.FileLocation fileLocation, final ArrayList messages, final ArrayList photos, final int index, final PhotoViewerProvider provider, ChatActivity chatActivity) { if (parentActivity == null || isVisible || provider == null && checkAnimation() || messageObject == null && fileLocation == null && messages == null && photos == null) { return; } @@ -2311,21 +2711,37 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat return; } - try { - WindowManager wm = (WindowManager) parentActivity.getSystemService(Context.WINDOW_SERVICE); - wm.removeView(windowView); - } catch (Exception e) { - //don't promt + WindowManager wm = (WindowManager) parentActivity.getSystemService(Context.WINDOW_SERVICE); + if (windowView.attachedToWindow) { + try { + wm.removeView(windowView); + } catch (Exception e) { + //don't promt + } } - WindowManager wm = (WindowManager) parentActivity.getSystemService(Context.WINDOW_SERVICE); try { + if (photos != null) { + windowLayoutParams.type = WindowManager.LayoutParams.LAST_APPLICATION_WINDOW; + windowLayoutParams.flags = 0; + windowLayoutParams.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN; + windowView.setFocusable(true); + containerView.setFocusable(true); + } else { + windowLayoutParams.type = WindowManager.LayoutParams.LAST_APPLICATION_WINDOW; + windowLayoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; + windowLayoutParams.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_UNSPECIFIED; + windowView.setFocusable(false); + containerView.setFocusable(false); + } wm.addView(windowView, windowLayoutParams); } catch (Exception e) { FileLog.e("tmessages", e); return; } + parentChatActivity = chatActivity; + actionBar.setTitle(LocaleController.formatString("Of", R.string.Of, 1, 1)); NotificationCenter.getInstance().addObserver(this, NotificationCenter.FileDidFailedLoad); NotificationCenter.getInstance().addObserver(this, NotificationCenter.FileDidLoaded); @@ -2333,6 +2749,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat NotificationCenter.getInstance().addObserver(this, NotificationCenter.mediaCountDidLoaded); NotificationCenter.getInstance().addObserver(this, NotificationCenter.mediaDidLoaded); NotificationCenter.getInstance().addObserver(this, NotificationCenter.userPhotosLoaded); + NotificationCenter.getInstance().addObserver(this, NotificationCenter.emojiDidLoaded); placeProvider = provider; @@ -2341,7 +2758,6 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat } isVisible = true; - backgroundDrawable.setAlpha(255); toggleActionBar(true, false); if (object != null) { @@ -2349,8 +2765,6 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat animationInProgress = 1; onPhotoShow(messageObject, fileLocation, messages, photos, index, object); - AndroidUtilities.lockOrientation(parentActivity); - final Rect drawRegion = object.imageReceiver.getDrawRegion(); int orientation = object.imageReceiver.getOrientation(); @@ -2372,96 +2786,115 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat layoutParams.height = drawRegion.bottom - drawRegion.top; animatingImageView.setLayoutParams(layoutParams); - containerView.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() { + float scaleX = (float) AndroidUtilities.displaySize.x / layoutParams.width; + float scaleY = (float) (AndroidUtilities.displaySize.y - AndroidUtilities.statusBarHeight) / layoutParams.height; + float scale = scaleX > scaleY ? scaleY : scaleX; + float width = layoutParams.width * scale; + float height = layoutParams.height * scale; + float xPos = (AndroidUtilities.displaySize.x - width) / 2.0f; + float yPos = (AndroidUtilities.displaySize.y - AndroidUtilities.statusBarHeight - height) / 2.0f; + int clipHorizontal = Math.abs(drawRegion.left - object.imageReceiver.getImageX()); + int clipVertical = Math.abs(drawRegion.top - object.imageReceiver.getImageY()); + + int coords2[] = new int[2]; + object.parentView.getLocationInWindow(coords2); + int clipTop = coords2[1] - AndroidUtilities.statusBarHeight - (object.viewY + drawRegion.top); + if (clipTop < 0) { + clipTop = 0; + } + int clipBottom = (object.viewY + drawRegion.top + layoutParams.height) - (coords2[1] + object.parentView.getHeight() - AndroidUtilities.statusBarHeight); + if (clipBottom < 0) { + clipBottom = 0; + } + clipTop = Math.max(clipTop, clipVertical); + clipBottom = Math.max(clipBottom, clipVertical); + + animationValues[0][0] = ViewProxy.getScaleX(animatingImageView); + animationValues[0][1] = ViewProxy.getScaleY(animatingImageView); + animationValues[0][2] = ViewProxy.getTranslationX(animatingImageView); + animationValues[0][3] = ViewProxy.getTranslationY(animatingImageView); + animationValues[0][4] = clipHorizontal; + animationValues[0][5] = clipTop; + animationValues[0][6] = clipBottom; + animationValues[0][7] = animatingImageView.getRadius(); + + animationValues[1][0] = scale; + animationValues[1][1] = scale; + animationValues[1][2] = xPos; + animationValues[1][3] = yPos; + animationValues[1][4] = 0; + animationValues[1][5] = 0; + animationValues[1][6] = 0; + animationValues[1][7] = 0; + + animatingImageView.setAnimationProgress(0); + backgroundDrawable.setAlpha(0); + ViewProxy.setAlpha(containerView, 0); + + final AnimatorSetProxy animatorSet = new AnimatorSetProxy(); + animatorSet.playTogether( + ObjectAnimatorProxy.ofFloat(animatingImageView, "animationProgress", 0.0f, 1.0f), + ObjectAnimatorProxy.ofInt(backgroundDrawable, "alpha", 0, 255), + ObjectAnimatorProxy.ofFloat(containerView, "alpha", 0.0f, 1.0f) + ); + + animationEndRunnable = new Runnable() { @Override - public boolean onPreDraw() { - containerView.getViewTreeObserver().removeOnPreDrawListener(this); - - float scaleX = (float) AndroidUtilities.displaySize.x / layoutParams.width; - float scaleY = (float) (AndroidUtilities.displaySize.y - AndroidUtilities.statusBarHeight) / layoutParams.height; - float scale = scaleX > scaleY ? scaleY : scaleX; - float width = layoutParams.width * scale; - float height = layoutParams.height * scale; - float xPos = (AndroidUtilities.displaySize.x - width) / 2.0f; - float yPos = (AndroidUtilities.displaySize.y - AndroidUtilities.statusBarHeight - height) / 2.0f; - int clipHorizontal = Math.abs(drawRegion.left - object.imageReceiver.getImageX()); - int clipVertical = Math.abs(drawRegion.top - object.imageReceiver.getImageY()); - - int coords2[] = new int[2]; - object.parentView.getLocationInWindow(coords2); - int clipTop = coords2[1] - AndroidUtilities.statusBarHeight - (object.viewY + drawRegion.top); - if (clipTop < 0) { - clipTop = 0; + public void run() { + if (containerView == null) { + return; } - int clipBottom = (object.viewY + drawRegion.top + layoutParams.height) - (coords2[1] + object.parentView.getHeight() - AndroidUtilities.statusBarHeight); - if (clipBottom < 0) { - clipBottom = 0; + animationInProgress = 0; + transitionAnimationStartTime = 0; + setImages(); + containerView.invalidate(); + animatingImageView.setVisibility(View.GONE); + if (showAfterAnimation != null) { + showAfterAnimation.imageReceiver.setVisible(true, true); } - clipTop = Math.max(clipTop, clipVertical); - clipBottom = Math.max(clipBottom, clipVertical); + if (hideAfterAnimation != null) { + hideAfterAnimation.imageReceiver.setVisible(false, true); + } + } + }; - AnimatorSetProxy animatorSet = new AnimatorSetProxy(); - animatorSet.playTogether( - ObjectAnimatorProxy.ofFloat(animatingImageView, "scaleX", scale), - ObjectAnimatorProxy.ofFloat(animatingImageView, "scaleY", scale), - ObjectAnimatorProxy.ofFloat(animatingImageView, "translationX", xPos), - ObjectAnimatorProxy.ofFloat(animatingImageView, "translationY", yPos), - ObjectAnimatorProxy.ofInt(backgroundDrawable, "alpha", 0, 255), - ObjectAnimatorProxy.ofInt(animatingImageView, "clipHorizontal", clipHorizontal, 0), - ObjectAnimatorProxy.ofInt(animatingImageView, "clipTop", clipTop, 0), - ObjectAnimatorProxy.ofInt(animatingImageView, "clipBottom", clipBottom, 0), - ObjectAnimatorProxy.ofInt(animatingImageView, "radius", 0), - ObjectAnimatorProxy.ofFloat(containerView, "alpha", 0.0f, 1.0f) - ); - - animationEndRunnable = new Runnable() { + animatorSet.setDuration(200); + animatorSet.addListener(new AnimatorListenerAdapterProxy() { + @Override + public void onAnimationEnd(Object animation) { + AndroidUtilities.runOnUIThread(new Runnable() { @Override public void run() { - animationInProgress = 0; - setImages(); - transitionAnimationStartTime = 0; - containerView.invalidate(); - animatingImageView.setVisibility(View.GONE); - AndroidUtilities.unlockOrientation(parentActivity); - if (showAfterAnimation != null) { - showAfterAnimation.imageReceiver.setVisible(true, true); - } - if (hideAfterAnimation != null) { - hideAfterAnimation.imageReceiver.setVisible(false, true); - } - } - }; - - animatorSet.setDuration(200); - animatorSet.addListener(new AnimatorListenerAdapterProxy() { - @Override - public void onAnimationEnd(Object animation) { if (animationEndRunnable != null) { animationEndRunnable.run(); animationEndRunnable = null; } } - - @Override - public void onAnimationCancel(Object animation) { - onAnimationEnd(animation); - } }); - transitionAnimationStartTime = System.currentTimeMillis(); - animatorSet.start(); + } - animatingImageView.setOnDrawListener(new ClippingImageView.onDrawListener() { - @Override - public void onDraw() { - disableShowCheck = false; - animatingImageView.setOnDrawListener(null); - object.imageReceiver.setVisible(false, true); - } - }); - return true; + @Override + public void onAnimationCancel(Object animation) { + onAnimationEnd(animation); } }); + transitionAnimationStartTime = System.currentTimeMillis(); + AndroidUtilities.runOnUIThread(new Runnable() { + @Override + public void run() { + animatorSet.start(); + } + }); + + backgroundDrawable.drawRunnable = new Runnable() { + @Override + public void run() { + disableShowCheck = false; + object.imageReceiver.setVisible(false, true); + } + }; } else { + backgroundDrawable.setAlpha(255); ViewProxy.setAlpha(containerView, 1.0f); onPhotoShow(messageObject, fileLocation, messages, photos, index, object); } @@ -2500,12 +2933,15 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat return; } + captionEditText.onDestroy(); + parentChatActivity = null; NotificationCenter.getInstance().removeObserver(this, NotificationCenter.FileDidFailedLoad); NotificationCenter.getInstance().removeObserver(this, NotificationCenter.FileDidLoaded); NotificationCenter.getInstance().removeObserver(this, NotificationCenter.FileLoadProgressChanged); NotificationCenter.getInstance().removeObserver(this, NotificationCenter.mediaCountDidLoaded); NotificationCenter.getInstance().removeObserver(this, NotificationCenter.mediaDidLoaded); NotificationCenter.getInstance().removeObserver(this, NotificationCenter.userPhotosLoaded); + NotificationCenter.getInstance().removeObserver(this, NotificationCenter.emojiDidLoaded); ConnectionsManager.getInstance().cancelRpcsForClassGuid(classGuid); isActionBarVisible = false; @@ -2519,8 +2955,6 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat final PlaceProviderObject object = placeProvider.getPlaceForPhoto(currentMessageObject, currentFileLocation, currentIndex); if (animated) { - AndroidUtilities.lockOrientation(parentActivity); - animationInProgress = 1; int visibility = animatingImageView.getVisibility(); animatingImageView.setVisibility(View.VISIBLE); @@ -2576,16 +3010,27 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat clipTop = Math.max(clipTop, clipVertical); clipBottom = Math.max(clipBottom, clipVertical); + animationValues[0][0] = ViewProxy.getScaleX(animatingImageView); + animationValues[0][1] = ViewProxy.getScaleY(animatingImageView); + animationValues[0][2] = ViewProxy.getTranslationX(animatingImageView); + animationValues[0][3] = ViewProxy.getTranslationY(animatingImageView); + animationValues[0][4] = 0; + animationValues[0][5] = 0; + animationValues[0][6] = 0; + animationValues[0][7] = 0; + + animationValues[1][0] = 1; + animationValues[1][1] = 1; + animationValues[1][2] = object.viewX + drawRegion.left; + animationValues[1][3] = object.viewY + drawRegion.top; + animationValues[1][4] = clipHorizontal; + animationValues[1][5] = clipTop; + animationValues[1][6] = clipBottom; + animationValues[1][7] = object.radius; + animatorSet.playTogether( - ObjectAnimatorProxy.ofFloat(animatingImageView, "scaleX", 1), - ObjectAnimatorProxy.ofFloat(animatingImageView, "scaleY", 1), - ObjectAnimatorProxy.ofFloat(animatingImageView, "translationX", object.viewX + drawRegion.left), - ObjectAnimatorProxy.ofFloat(animatingImageView, "translationY", object.viewY + drawRegion.top), + ObjectAnimatorProxy.ofFloat(animatingImageView, "animationProgress", 0.0f, 1.0f), ObjectAnimatorProxy.ofInt(backgroundDrawable, "alpha", 0), - ObjectAnimatorProxy.ofInt(animatingImageView, "clipHorizontal", clipHorizontal), - ObjectAnimatorProxy.ofInt(animatingImageView, "clipTop", clipTop), - ObjectAnimatorProxy.ofInt(animatingImageView, "clipBottom", clipBottom), - ObjectAnimatorProxy.ofInt(animatingImageView, "radius", object.radius), ObjectAnimatorProxy.ofFloat(containerView, "alpha", 0.0f) ); } else { @@ -2600,7 +3045,6 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat animationEndRunnable = new Runnable() { @Override public void run() { - AndroidUtilities.unlockOrientation(parentActivity); animationInProgress = 0; onPhotoClosed(object); } @@ -2610,10 +3054,15 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat animatorSet.addListener(new AnimatorListenerAdapterProxy() { @Override public void onAnimationEnd(Object animation) { - if (animationEndRunnable != null) { - animationEndRunnable.run(); - animationEndRunnable = null; - } + AndroidUtilities.runOnUIThread(new Runnable() { + @Override + public void run() { + if (animationEndRunnable != null) { + animationEndRunnable.run(); + animationEndRunnable = null; + } + } + }); } @Override @@ -2635,6 +3084,9 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat animationEndRunnable = new Runnable() { @Override public void run() { + if (containerView == null) { + return; + } animationInProgress = 0; onPhotoClosed(object); ViewProxy.setScaleX(containerView, 1.0f); @@ -2670,6 +3122,9 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat } catch (Exception e) { FileLog.e("tmessages", e); } + if (captionEditText != null) { + captionEditText.onDestroy(); + } Instance = null; } @@ -2716,6 +3171,24 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat } } + private void redraw(final int count) { + if (count < 6) { + if (containerView != null) { + containerView.invalidate(); + AndroidUtilities.runOnUIThread(new Runnable() { + @Override + public void run() { + redraw(count + 1); + } + }, 100); + } + } + } + + public void onResume() { + redraw(0); //workaround for camera bug + } + public boolean isVisible() { return isVisible && placeProvider != null; } @@ -2774,7 +3247,8 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat } private int getContainerViewHeight(int mode) { - int height = containerView.getHeight(); + //int height = containerView.getHeight(); + int height = AndroidUtilities.displaySize.y - AndroidUtilities.statusBarHeight; if (mode == 1) { height -= AndroidUtilities.dp(76); } else if (mode == 2) { @@ -2785,9 +3259,6 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat private boolean onTouchEvent(MotionEvent ev) { if (animationInProgress != 0 || animationStartTime != 0) { - if (animationStartTime == 0) { - AndroidUtilities.unlockOrientation(parentActivity); - } return false; } @@ -2807,6 +3278,10 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat } } + if (captionEditText.isEmojiPopupShowing() || captionEditText.isKeyboardVisible()) { + return true; + } + if (currentEditMode == 0 && ev.getPointerCount() == 1 && gestureDetector.onTouchEvent(ev)) { if (doubleTap) { doubleTap = false; @@ -2843,7 +3318,6 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat dragY = moveStartY = ev.getY(); draggingDown = false; canDragDown = true; - AndroidUtilities.lockOrientation(parentActivity); if (velocityTracker != null) { velocityTracker.clear(); } @@ -3000,8 +3474,6 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat moveToY = maxY; } animateTo(scale, moveToX, moveToY, false); - } else { - AndroidUtilities.unlockOrientation(parentActivity); } } return false; @@ -3048,7 +3520,6 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat private void animateTo(float newScale, float newTx, float newTy, boolean isZoom, int duration) { if (scale == newScale && translationX == newTx && translationY == newTy) { - AndroidUtilities.unlockOrientation(parentActivity); return; } zoomAnimation = isZoom; @@ -3066,12 +3537,10 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat @Override public void onAnimationEnd(Object animation) { imageMoveAnimation = null; - AndroidUtilities.unlockOrientation(parentActivity); containerView.invalidate(); } }); imageMoveAnimation.start(); - AndroidUtilities.lockOrientation(parentActivity); } public void setAnimationValue(float value) { @@ -3122,7 +3591,6 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat photoCropView.setAnimationProgress(1); } updateMinMax(scale); - AndroidUtilities.unlockOrientation(parentActivity); zoomAnimation = false; } if (!scroller.isFinished()) { @@ -3298,20 +3766,14 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat updateMinMax(scale); if (checkImageView != null) { - checkImageView.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() { + checkImageView.post(new Runnable() { @Override - public boolean onPreDraw() { - checkImageView.getViewTreeObserver().removeOnPreDrawListener(this); - FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) checkImageView.getLayoutParams(); + public void run() { + RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) checkImageView.getLayoutParams(); WindowManager manager = (WindowManager) ApplicationLoader.applicationContext.getSystemService(Activity.WINDOW_SERVICE); int rotation = manager.getDefaultDisplay().getRotation(); - if (rotation == Surface.ROTATION_270 || rotation == Surface.ROTATION_90) { - layoutParams.topMargin = AndroidUtilities.dp(58); - } else { - layoutParams.topMargin = AndroidUtilities.dp(68); - } + layoutParams.topMargin = AndroidUtilities.dp(rotation == Surface.ROTATION_270 || rotation == Surface.ROTATION_90 ? 58 : 68); checkImageView.setLayoutParams(layoutParams); - return false; } }); } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/PopupNotificationActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/PopupNotificationActivity.java index b8a8832d8..12515c1a5 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/PopupNotificationActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/PopupNotificationActivity.java @@ -52,7 +52,9 @@ import org.telegram.ui.Components.AvatarDrawable; import org.telegram.ui.Components.BackupImageView; import org.telegram.ui.Components.ChatActivityEnterView; import org.telegram.ui.Components.FrameLayoutFixed; +import org.telegram.ui.Components.LayoutHelper; import org.telegram.ui.Components.PopupAudioView; +import org.telegram.ui.Components.RecordStatusDrawable; import org.telegram.ui.Components.SizeNotifierRelativeLayout; import org.telegram.ui.Components.TypingDotsDrawable; @@ -78,6 +80,7 @@ public class PopupNotificationActivity extends Activity implements NotificationC private ArrayList audioViews = new ArrayList<>(); private VelocityTracker velocityTracker = null; private TypingDotsDrawable typingDotsDrawable; + private RecordStatusDrawable recordStatusDrawable; private int classGuid; private TLRPC.User currentUser; @@ -159,6 +162,7 @@ public class PopupNotificationActivity extends Activity implements NotificationC NotificationCenter.getInstance().addObserver(this, NotificationCenter.emojiDidLoaded); typingDotsDrawable = new TypingDotsDrawable(); + recordStatusDrawable = new RecordStatusDrawable(); SizeNotifierRelativeLayout contentView = new SizeNotifierRelativeLayout(this); setContentView(contentView); @@ -167,15 +171,15 @@ public class PopupNotificationActivity extends Activity implements NotificationC RelativeLayout relativeLayout = new RelativeLayout(this); contentView.addView(relativeLayout); RelativeLayout.LayoutParams layoutParams3 = (RelativeLayout.LayoutParams) relativeLayout.getLayoutParams(); - layoutParams3.width = RelativeLayout.LayoutParams.MATCH_PARENT; - layoutParams3.height = RelativeLayout.LayoutParams.MATCH_PARENT; + layoutParams3.width = LayoutHelper.MATCH_PARENT; + layoutParams3.height = LayoutHelper.MATCH_PARENT; relativeLayout.setLayoutParams(layoutParams3); RelativeLayout popupContainer = new RelativeLayout(this); popupContainer.setBackgroundColor(0xffffffff); relativeLayout.addView(popupContainer); layoutParams3 = (RelativeLayout.LayoutParams) popupContainer.getLayoutParams(); - layoutParams3.width = RelativeLayout.LayoutParams.MATCH_PARENT; + layoutParams3.width = LayoutHelper.MATCH_PARENT; layoutParams3.height = AndroidUtilities.dp(240); layoutParams3.leftMargin = AndroidUtilities.dp(12); layoutParams3.rightMargin = AndroidUtilities.dp(12); @@ -188,8 +192,8 @@ public class PopupNotificationActivity extends Activity implements NotificationC chatActivityEnterView = new ChatActivityEnterView(this, contentView, null, false); popupContainer.addView(chatActivityEnterView); layoutParams3 = (RelativeLayout.LayoutParams) chatActivityEnterView.getLayoutParams(); - layoutParams3.width = RelativeLayout.LayoutParams.MATCH_PARENT; - layoutParams3.height = RelativeLayout.LayoutParams.WRAP_CONTENT; + layoutParams3.width = LayoutHelper.MATCH_PARENT; + layoutParams3.height = LayoutHelper.WRAP_CONTENT; layoutParams3.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM); chatActivityEnterView.setLayoutParams(layoutParams3); chatActivityEnterView.setDelegate(new ChatActivityEnterView.ChatActivityEnterViewDelegate() { @@ -214,7 +218,7 @@ public class PopupNotificationActivity extends Activity implements NotificationC @Override public void needSendTyping() { if (currentMessageObject != null) { - MessagesController.getInstance().sendTyping(currentMessageObject.getDialogId(), classGuid); + MessagesController.getInstance().sendTyping(currentMessageObject.getDialogId(), 0, classGuid); } } @@ -256,8 +260,8 @@ public class PopupNotificationActivity extends Activity implements NotificationC avatarContainer.setPadding(AndroidUtilities.dp(4), 0, AndroidUtilities.dp(4), 0); actionBar.addView(avatarContainer); FrameLayout.LayoutParams layoutParams2 = (FrameLayout.LayoutParams) avatarContainer.getLayoutParams(); - layoutParams2.height = FrameLayout.LayoutParams.MATCH_PARENT; - layoutParams2.width = FrameLayout.LayoutParams.WRAP_CONTENT; + layoutParams2.height = LayoutHelper.MATCH_PARENT; + layoutParams2.width = LayoutHelper.WRAP_CONTENT; layoutParams2.rightMargin = AndroidUtilities.dp(48); layoutParams2.leftMargin = AndroidUtilities.dp(60); layoutParams2.gravity = Gravity.TOP | Gravity.LEFT; @@ -283,8 +287,8 @@ public class PopupNotificationActivity extends Activity implements NotificationC nameTextView.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); avatarContainer.addView(nameTextView); layoutParams2 = (FrameLayout.LayoutParams) nameTextView.getLayoutParams(); - layoutParams2.width = FrameLayout.LayoutParams.WRAP_CONTENT; - layoutParams2.height = FrameLayout.LayoutParams.WRAP_CONTENT; + layoutParams2.width = LayoutHelper.WRAP_CONTENT; + layoutParams2.height = LayoutHelper.WRAP_CONTENT; layoutParams2.leftMargin = AndroidUtilities.dp(54); layoutParams2.bottomMargin = AndroidUtilities.dp(22); layoutParams2.gravity = Gravity.BOTTOM; @@ -300,8 +304,8 @@ public class PopupNotificationActivity extends Activity implements NotificationC onlineTextView.setGravity(Gravity.LEFT); avatarContainer.addView(onlineTextView); layoutParams2 = (FrameLayout.LayoutParams) onlineTextView.getLayoutParams(); - layoutParams2.width = FrameLayout.LayoutParams.WRAP_CONTENT; - layoutParams2.height = FrameLayout.LayoutParams.WRAP_CONTENT; + layoutParams2.width = LayoutHelper.WRAP_CONTENT; + layoutParams2.height = LayoutHelper.WRAP_CONTENT; layoutParams2.leftMargin = AndroidUtilities.dp(54); layoutParams2.bottomMargin = AndroidUtilities.dp(4); layoutParams2.gravity = Gravity.BOTTOM; @@ -955,9 +959,18 @@ public class PopupNotificationActivity extends Activity implements NotificationC } if (start) { try { - onlineTextView.setCompoundDrawablesWithIntrinsicBounds(typingDotsDrawable, null, null, null); - onlineTextView.setCompoundDrawablePadding(AndroidUtilities.dp(4)); - typingDotsDrawable.start(); + Integer type = MessagesController.getInstance().printingStringsTypes.get(currentMessageObject.getDialogId()); + if (type == 0) { + onlineTextView.setCompoundDrawablesWithIntrinsicBounds(typingDotsDrawable, null, null, null); + onlineTextView.setCompoundDrawablePadding(AndroidUtilities.dp(4)); + typingDotsDrawable.start(); + recordStatusDrawable.stop(); + } else if (type == 1) { + onlineTextView.setCompoundDrawablesWithIntrinsicBounds(recordStatusDrawable, null, null, null); + onlineTextView.setCompoundDrawablePadding(AndroidUtilities.dp(4)); + recordStatusDrawable.start(); + typingDotsDrawable.stop(); + } } catch (Exception e) { FileLog.e("tmessages", e); } @@ -965,6 +978,7 @@ public class PopupNotificationActivity extends Activity implements NotificationC onlineTextView.setCompoundDrawablesWithIntrinsicBounds(null, null, null, null); onlineTextView.setCompoundDrawablePadding(0); typingDotsDrawable.stop(); + recordStatusDrawable.stop(); } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/PrivacySettingsActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/PrivacySettingsActivity.java index 6f5d0e901..6fb67435a 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/PrivacySettingsActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/PrivacySettingsActivity.java @@ -37,6 +37,7 @@ import org.telegram.ui.Adapters.BaseFragmentAdapter; import org.telegram.ui.Cells.HeaderCell; import org.telegram.ui.Cells.TextInfoPrivacyCell; import org.telegram.ui.Cells.TextSettingsCell; +import org.telegram.ui.Components.LayoutHelper; import java.util.ArrayList; @@ -116,8 +117,8 @@ public class PrivacySettingsActivity extends BaseFragment implements Notificatio listView.setDrawSelectorOnTop(true); frameLayout.addView(listView); FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) listView.getLayoutParams(); - layoutParams.width = FrameLayout.LayoutParams.MATCH_PARENT; - layoutParams.height = FrameLayout.LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; + layoutParams.height = LayoutHelper.MATCH_PARENT; layoutParams.gravity = Gravity.TOP; listView.setLayoutParams(layoutParams); listView.setAdapter(listAdapter); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ProfileActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/ProfileActivity.java index 99fae5d7e..84c5df504 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ProfileActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ProfileActivity.java @@ -54,7 +54,8 @@ import org.telegram.android.MessageObject; import org.telegram.messenger.UserConfig; import org.telegram.messenger.Utilities; import org.telegram.ui.Adapters.BaseFragmentAdapter; -import org.telegram.ui.AnimationCompat.ViewProxy; +import org.telegram.android.AnimationCompat.ViewProxy; +import org.telegram.ui.Cells.AddMemberCell; import org.telegram.ui.Cells.DividerCell; import org.telegram.ui.Cells.EmptyCell; import org.telegram.ui.Cells.ShadowSectionCell; @@ -69,6 +70,7 @@ import org.telegram.ui.Components.AvatarUpdater; import org.telegram.ui.Components.BackupImageView; import org.telegram.ui.ActionBar.BaseFragment; import org.telegram.ui.Components.IdenticonDrawable; +import org.telegram.ui.Components.LayoutHelper; import java.util.ArrayList; import java.util.Collections; @@ -125,6 +127,7 @@ public class ProfileActivity extends BaseFragment implements NotificationCenter. private int sectionRow; private int membersSectionRow; private int membersEndRow; + private int addMemberRow; private int rowCount = 0; public ProfileActivity(Bundle args) { @@ -294,29 +297,7 @@ public class ProfileActivity extends BaseFragment implements NotificationCenter. builder.setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), null); showAlertDialog(builder); } else if (id == add_member) { - Bundle args = new Bundle(); - args.putBoolean("onlyUsers", true); - args.putBoolean("destroyAfterSelect", true); - args.putBoolean("returnAsResult", true); - //args.putBoolean("allowUsernameSearch", false); - if (chat_id > 0) { - args.putString("selectAlertString", LocaleController.getString("AddToTheGroup", R.string.AddToTheGroup)); - } - ContactsActivity fragment = new ContactsActivity(args); - fragment.setDelegate(new ContactsActivity.ContactsActivityDelegate() { - @Override - public void didSelectContact(TLRPC.User user, String param) { - MessagesController.getInstance().addUserToChat(chat_id, user, info, param != null ? Utilities.parseInt(param) : 0); - } - }); - if (info != null) { - HashMap users = new HashMap<>(); - for (TLRPC.TL_chatParticipant p : info.participants) { - users.put(p.user_id, null); - } - fragment.setIgnoreUsers(users); - } - presentFragment(fragment); + openAddMember(); } else if (id == leave_group) { AlertDialog.Builder builder = new AlertDialog.Builder(getParentActivity()); builder.setMessage(LocaleController.getString("AreYouSureDeleteAndExit", R.string.AreYouSureDeleteAndExit)); @@ -385,8 +366,8 @@ public class ProfileActivity extends BaseFragment implements NotificationCenter. nameTextView.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); actionBar.addView(nameTextView); layoutParams = (FrameLayout.LayoutParams) nameTextView.getLayoutParams(); - layoutParams.width = FrameLayout.LayoutParams.WRAP_CONTENT; - layoutParams.height = FrameLayout.LayoutParams.WRAP_CONTENT; + layoutParams.width = LayoutHelper.WRAP_CONTENT; + layoutParams.height = LayoutHelper.WRAP_CONTENT; layoutParams.leftMargin = AndroidUtilities.dp(LocaleController.isRTL ? 16 : 97); layoutParams.rightMargin = AndroidUtilities.dp(LocaleController.isRTL ? 97 : 16); layoutParams.bottomMargin = AndroidUtilities.dp(51); @@ -403,8 +384,8 @@ public class ProfileActivity extends BaseFragment implements NotificationCenter. onlineTextView.setGravity((LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT)); actionBar.addView(onlineTextView); layoutParams = (FrameLayout.LayoutParams) onlineTextView.getLayoutParams(); - layoutParams.width = FrameLayout.LayoutParams.WRAP_CONTENT; - layoutParams.height = FrameLayout.LayoutParams.WRAP_CONTENT; + layoutParams.width = LayoutHelper.WRAP_CONTENT; + layoutParams.height = LayoutHelper.WRAP_CONTENT; layoutParams.leftMargin = AndroidUtilities.dp(LocaleController.isRTL ? 16 : 97); layoutParams.rightMargin = AndroidUtilities.dp(LocaleController.isRTL ? 97 : 16); layoutParams.bottomMargin = AndroidUtilities.dp(30); @@ -418,8 +399,8 @@ public class ProfileActivity extends BaseFragment implements NotificationCenter. AndroidUtilities.setListViewEdgeEffectColor(listView, AvatarDrawable.getProfileBackColorForId(user_id != 0 ? 5 : chat_id)); frameLayout.addView(listView); layoutParams = (FrameLayout.LayoutParams) listView.getLayoutParams(); - layoutParams.width = FrameLayout.LayoutParams.MATCH_PARENT; - layoutParams.height = FrameLayout.LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; + layoutParams.height = LayoutHelper.MATCH_PARENT; layoutParams.gravity = Gravity.TOP; listView.setLayoutParams(layoutParams); @@ -507,6 +488,8 @@ public class ProfileActivity extends BaseFragment implements NotificationCenter. Bundle args = new Bundle(); args.putInt("user_id", user_id); presentFragment(new ProfileActivity(args)); + } else if (i == addMemberRow) { + openAddMember(); } } }); @@ -579,8 +562,8 @@ public class ProfileActivity extends BaseFragment implements NotificationCenter. }); } layoutParams = (FrameLayout.LayoutParams) writeButton.getLayoutParams(); - layoutParams.width = FrameLayout.LayoutParams.WRAP_CONTENT; - layoutParams.height = FrameLayout.LayoutParams.WRAP_CONTENT; + layoutParams.width = LayoutHelper.WRAP_CONTENT; + layoutParams.height = LayoutHelper.WRAP_CONTENT; layoutParams.leftMargin = AndroidUtilities.dp(LocaleController.isRTL ? 16 : 0); layoutParams.rightMargin = AndroidUtilities.dp(LocaleController.isRTL ? 0 : 16); layoutParams.gravity = (LocaleController.isRTL ? Gravity.LEFT : Gravity.RIGHT); @@ -671,6 +654,35 @@ public class ProfileActivity extends BaseFragment implements NotificationCenter. } } + private void openAddMember() { + Bundle args = new Bundle(); + args.putBoolean("onlyUsers", true); + args.putBoolean("destroyAfterSelect", true); + args.putBoolean("returnAsResult", true); + if (info != null && info.admin_id == UserConfig.getClientUserId()) { + args.putInt("chat_id", currentChat.id); + } + //args.putBoolean("allowUsernameSearch", false); + if (chat_id > 0) { + args.putString("selectAlertString", LocaleController.getString("AddToTheGroup", R.string.AddToTheGroup)); + } + ContactsActivity fragment = new ContactsActivity(args); + fragment.setDelegate(new ContactsActivity.ContactsActivityDelegate() { + @Override + public void didSelectContact(TLRPC.User user, String param) { + MessagesController.getInstance().addUserToChat(chat_id, user, info, param != null ? Utilities.parseInt(param) : 0); + } + }); + if (info != null) { + HashMap users = new HashMap<>(); + for (TLRPC.TL_chatParticipant p : info.participants) { + users.put(p.user_id, null); + } + fragment.setIgnoreUsers(users); + } + presentFragment(fragment); + } + private void checkListViewScroll() { if (listView.getChildCount() == 0) { return; @@ -1080,16 +1092,18 @@ public class ProfileActivity extends BaseFragment implements NotificationCenter. rowCount += info.participants.size(); membersEndRow = rowCount; int maxCount = chat_id > 0 ? MessagesController.getInstance().maxGroupCount : MessagesController.getInstance().maxBroadcastCount; + addMemberRow = rowCount++; } else { membersEndRow = -1; membersSectionRow = -1; emptyRowChat2 = -1; + addMemberRow = -1; } } } private void updateProfileData() { - if (avatarImage == null) { + if (avatarImage == null || nameTextView == null) { return; } if (user_id != 0) { @@ -1115,6 +1129,8 @@ public class ProfileActivity extends BaseFragment implements NotificationCenter. TLRPC.Chat chat = MessagesController.getInstance().getChat(chat_id); if (chat != null) { currentChat = chat; + } else { + chat = currentChat; } nameTextView.setText(chat.title); @@ -1227,7 +1243,7 @@ public class ProfileActivity extends BaseFragment implements NotificationCenter. if (user_id != 0) { return i == phoneRow || i == settingsTimerRow || i == settingsKeyRow || i == settingsNotificationsRow || i == sharedMediaRow || i == startSecretChatRow; } else if (chat_id != 0) { - return i == settingsNotificationsRow || i == sharedMediaRow || i > emptyRowChat2 && i < membersEndRow; + return i == settingsNotificationsRow || i == sharedMediaRow || i > emptyRowChat2 && i < membersEndRow || i == addMemberRow; } return false; } @@ -1260,11 +1276,11 @@ public class ProfileActivity extends BaseFragment implements NotificationCenter. view = new EmptyCell(mContext); } if (i == overscrollRow) { - ((EmptyCell) view).setHeight(88); + ((EmptyCell) view).setHeight(AndroidUtilities.dp(88)); } else if (i == emptyRowChat || i == emptyRowChat2) { - ((EmptyCell) view).setHeight(8); + ((EmptyCell) view).setHeight(AndroidUtilities.dp(8)); } else { - ((EmptyCell) view).setHeight(36); + ((EmptyCell) view).setHeight(AndroidUtilities.dp(36)); } } else if (type == 1) { if (view == null) { @@ -1341,6 +1357,10 @@ public class ProfileActivity extends BaseFragment implements NotificationCenter. if (view == null) { view = new ShadowSectionCell(mContext); } + } else if (type == 6) { + if (view == null) { + view = new AddMemberCell(mContext); + } } return view; } @@ -1359,13 +1379,15 @@ public class ProfileActivity extends BaseFragment implements NotificationCenter. return 4; } else if (i == membersSectionRow) { return 5; + } else if (i == addMemberRow) { + return 6; } return 0; } @Override public int getViewTypeCount() { - return 6; + return 7; } @Override diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ProfileNotificationsActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/ProfileNotificationsActivity.java index e845fd4de..9ed9efbf6 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ProfileNotificationsActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ProfileNotificationsActivity.java @@ -20,12 +20,16 @@ import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.provider.Settings; +import android.util.TypedValue; +import android.view.Gravity; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.AdapterView; import android.widget.FrameLayout; +import android.widget.LinearLayout; import android.widget.ListView; +import android.widget.TextView; import org.telegram.android.AndroidUtilities; import org.telegram.android.MessagesController; @@ -45,6 +49,8 @@ import org.telegram.ui.ActionBar.ActionBar; import org.telegram.ui.ActionBar.BaseFragment; import org.telegram.ui.Components.AvatarDrawable; import org.telegram.ui.Components.ColorPickerView; +import org.telegram.ui.Components.LayoutHelper; +import org.telegram.ui.Components.NumberPicker; public class ProfileNotificationsActivity extends BaseFragment implements NotificationCenter.NotificationCenterDelegate { @@ -56,6 +62,7 @@ public class ProfileNotificationsActivity extends BaseFragment implements Notifi private int settingsSoundRow; private int settingsPriorityRow; private int settingsLedRow; + private int smartRow; private int rowCount = 0; public ProfileNotificationsActivity(Bundle args) { @@ -73,6 +80,12 @@ public class ProfileNotificationsActivity extends BaseFragment implements Notifi } else { settingsPriorityRow = -1; } + int lower_id = (int) dialog_id; + if (lower_id < 0) { + smartRow = rowCount++; + } else { + smartRow = 1; + } settingsLedRow = rowCount++; NotificationCenter.getInstance().addObserver(this, NotificationCenter.notificationsSettingsUpdated); return super.onFragmentCreate(); @@ -107,9 +120,9 @@ public class ProfileNotificationsActivity extends BaseFragment implements Notifi listView.setVerticalScrollBarEnabled(false); AndroidUtilities.setListViewEdgeEffectColor(listView, AvatarDrawable.getProfileBackColorForId(5)); frameLayout.addView(listView); - FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) listView.getLayoutParams(); - layoutParams.width = FrameLayout.LayoutParams.MATCH_PARENT; - layoutParams.height = FrameLayout.LayoutParams.MATCH_PARENT; + final FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) listView.getLayoutParams(); + layoutParams.width = LayoutHelper.MATCH_PARENT; + layoutParams.height = LayoutHelper.MATCH_PARENT; listView.setLayoutParams(layoutParams); listView.setAdapter(new ListAdapter(context)); listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { @@ -289,6 +302,121 @@ public class ProfileNotificationsActivity extends BaseFragment implements Notifi }); builder.setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), null); showAlertDialog(builder); + } else if (i == smartRow) { + if (getParentActivity() == null) { + return; + } + SharedPreferences preferences = ApplicationLoader.applicationContext.getSharedPreferences("Notifications", Activity.MODE_PRIVATE); + int notifyMaxCount = preferences.getInt("smart_max_count_" + dialog_id, 2); + int notifyDelay = preferences.getInt("smart_delay_" + dialog_id, 3 * 60); + if (notifyMaxCount == 0) { + notifyMaxCount = 2; + } + + LinearLayout linearLayout = new LinearLayout(getParentActivity()); + linearLayout.setOrientation(LinearLayout.VERTICAL); + + LinearLayout linearLayout2 = new LinearLayout(getParentActivity()); + linearLayout2.setOrientation(LinearLayout.HORIZONTAL); + linearLayout.addView(linearLayout2); + LinearLayout.LayoutParams layoutParams1 = (LinearLayout.LayoutParams) linearLayout2.getLayoutParams(); + layoutParams1.width = LayoutHelper.WRAP_CONTENT; + layoutParams1.height = LayoutHelper.WRAP_CONTENT; + layoutParams1.gravity = Gravity.CENTER_HORIZONTAL | Gravity.TOP; + linearLayout2.setLayoutParams(layoutParams1); + + TextView textView = new TextView(getParentActivity()); + textView.setText(LocaleController.getString("SmartNotificationsSoundAtMost", R.string.SmartNotificationsSoundAtMost)); + textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 18); + linearLayout2.addView(textView); + layoutParams1 = (LinearLayout.LayoutParams) textView.getLayoutParams(); + layoutParams1.width = LayoutHelper.WRAP_CONTENT; + layoutParams1.height = LayoutHelper.WRAP_CONTENT; + layoutParams1.gravity = Gravity.CENTER_VERTICAL | Gravity.LEFT; + textView.setLayoutParams(layoutParams1); + + final NumberPicker numberPickerTimes = new NumberPicker(getParentActivity()); + numberPickerTimes.setMinValue(1); + numberPickerTimes.setMaxValue(10); + numberPickerTimes.setValue(notifyMaxCount); + linearLayout2.addView(numberPickerTimes); + layoutParams1 = (LinearLayout.LayoutParams) numberPickerTimes.getLayoutParams(); + layoutParams1.width = AndroidUtilities.dp(50); + numberPickerTimes.setLayoutParams(layoutParams1); + + textView = new TextView(getParentActivity()); + textView.setText(LocaleController.getString("SmartNotificationsTimes", R.string.SmartNotificationsTimes)); + textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 18); + linearLayout2.addView(textView); + layoutParams1 = (LinearLayout.LayoutParams) textView.getLayoutParams(); + layoutParams1.width = LayoutHelper.WRAP_CONTENT; + layoutParams1.height = LayoutHelper.WRAP_CONTENT; + layoutParams1.gravity = Gravity.CENTER_VERTICAL | Gravity.LEFT; + textView.setLayoutParams(layoutParams1); + + linearLayout2 = new LinearLayout(getParentActivity()); + linearLayout2.setOrientation(LinearLayout.HORIZONTAL); + linearLayout.addView(linearLayout2); + layoutParams1 = (LinearLayout.LayoutParams) linearLayout2.getLayoutParams(); + layoutParams1.width = LayoutHelper.WRAP_CONTENT; + layoutParams1.height = LayoutHelper.WRAP_CONTENT; + layoutParams1.gravity = Gravity.CENTER_HORIZONTAL | Gravity.TOP; + linearLayout2.setLayoutParams(layoutParams1); + + textView = new TextView(getParentActivity()); + textView.setText(LocaleController.getString("SmartNotificationsWithin", R.string.SmartNotificationsWithin)); + textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 18); + linearLayout2.addView(textView); + layoutParams1 = (LinearLayout.LayoutParams) textView.getLayoutParams(); + layoutParams1.width = LayoutHelper.WRAP_CONTENT; + layoutParams1.height = LayoutHelper.WRAP_CONTENT; + layoutParams1.gravity = Gravity.CENTER_VERTICAL | Gravity.LEFT; + textView.setLayoutParams(layoutParams1); + + final NumberPicker numberPickerMinutes = new NumberPicker(getParentActivity()); + numberPickerMinutes.setMinValue(1); + numberPickerMinutes.setMaxValue(10); + numberPickerMinutes.setValue(notifyDelay / 60); + linearLayout2.addView(numberPickerMinutes); + layoutParams1 = (LinearLayout.LayoutParams) numberPickerMinutes.getLayoutParams(); + layoutParams1.width = AndroidUtilities.dp(50); + numberPickerMinutes.setLayoutParams(layoutParams1); + + textView = new TextView(getParentActivity()); + textView.setText(LocaleController.getString("SmartNotificationsMinutes", R.string.SmartNotificationsMinutes)); + textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 18); + linearLayout2.addView(textView); + layoutParams1 = (LinearLayout.LayoutParams) textView.getLayoutParams(); + layoutParams1.width = LayoutHelper.WRAP_CONTENT; + layoutParams1.height = LayoutHelper.WRAP_CONTENT; + layoutParams1.gravity = Gravity.CENTER_VERTICAL | Gravity.LEFT; + textView.setLayoutParams(layoutParams1); + + AlertDialog.Builder builder = new AlertDialog.Builder(getParentActivity()); + builder.setTitle(LocaleController.getString("SmartNotifications", R.string.SmartNotifications)); + builder.setView(linearLayout); + builder.setPositiveButton(LocaleController.getString("OK", R.string.OK), new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + SharedPreferences preferences = ApplicationLoader.applicationContext.getSharedPreferences("Notifications", Activity.MODE_PRIVATE); + preferences.edit().putInt("smart_max_count_" + dialog_id, numberPickerTimes.getValue()).commit(); + preferences.edit().putInt("smart_delay_" + dialog_id, numberPickerMinutes.getValue() * 60).commit(); + if (listView != null) { + listView.invalidateViews(); + } + } + }); + builder.setNegativeButton(LocaleController.getString("SmartNotificationsDisabled", R.string.SmartNotificationsDisabled), new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + SharedPreferences preferences = ApplicationLoader.applicationContext.getSharedPreferences("Notifications", Activity.MODE_PRIVATE); + preferences.edit().putInt("smart_max_count_" + dialog_id, 0).commit(); + if (listView != null) { + listView.invalidateViews(); + } + } + }); + showAlertDialog(builder); } } }); @@ -447,6 +575,16 @@ public class ProfileNotificationsActivity extends BaseFragment implements Notifi } else if (value == 3) { textCell.setTextAndValue(LocaleController.getString("NotificationsPriority", R.string.NotificationsPriority), LocaleController.getString("SettingsDefault", R.string.SettingsDefault), true); } + } else if (i == smartRow) { + int notifyMaxCount = preferences.getInt("smart_max_count_" + dialog_id, 2); + int notifyDelay = preferences.getInt("smart_delay_" + dialog_id, 3 * 60); + if (notifyMaxCount == 0) { + textCell.setTextAndValue(LocaleController.getString("SmartNotifications", R.string.SmartNotifications), LocaleController.getString("SmartNotificationsDisabled", R.string.SmartNotificationsDisabled), true); + } else { + String times = LocaleController.formatPluralString("Times", notifyMaxCount); + String minutes = LocaleController.formatPluralString("Minutes", notifyDelay / 60); + textCell.setTextAndValue(LocaleController.getString("SmartNotifications", R.string.SmartNotifications), LocaleController.formatString("SmartNotificationsInfo", R.string.SmartNotificationsInfo, times, minutes), true); + } } } else if (type == 1) { if (view == null) { @@ -472,7 +610,7 @@ public class ProfileNotificationsActivity extends BaseFragment implements Notifi @Override public int getItemViewType(int i) { - if (i == settingsNotificationsRow || i == settingsVibrateRow || i == settingsSoundRow || i == settingsPriorityRow) { + if (i == settingsNotificationsRow || i == settingsVibrateRow || i == settingsSoundRow || i == settingsPriorityRow || i == smartRow) { return 0; } else if (i == settingsLedRow) { return 1; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/SecretPhotoViewer.java b/TMessagesProj/src/main/java/org/telegram/ui/SecretPhotoViewer.java index ee5928a7e..682d366bb 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/SecretPhotoViewer.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/SecretPhotoViewer.java @@ -39,6 +39,7 @@ import org.telegram.messenger.FileLoader; import org.telegram.messenger.FileLog; import org.telegram.messenger.R; import org.telegram.messenger.TLRPC; +import org.telegram.ui.Components.LayoutHelper; import java.io.File; import java.util.ArrayList; @@ -213,8 +214,8 @@ public class SecretPhotoViewer implements NotificationCenter.NotificationCenterD containerView.setFocusable(false); windowView.addView(containerView); FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams)containerView.getLayoutParams(); - layoutParams.width = FrameLayout.LayoutParams.MATCH_PARENT; - layoutParams.height = FrameLayout.LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; + layoutParams.height = LayoutHelper.MATCH_PARENT; layoutParams.gravity = Gravity.TOP | Gravity.LEFT; containerView.setLayoutParams(layoutParams); containerView.setOnTouchListener(new View.OnTouchListener() { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/SessionsActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/SessionsActivity.java index fb90c3100..c27a0a4c4 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/SessionsActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/SessionsActivity.java @@ -47,6 +47,7 @@ import org.telegram.ui.Cells.HeaderCell; import org.telegram.ui.Cells.SessionCell; import org.telegram.ui.Cells.TextInfoPrivacyCell; import org.telegram.ui.Cells.TextSettingsCell; +import org.telegram.ui.Components.LayoutHelper; import java.util.ArrayList; @@ -114,8 +115,8 @@ public class SessionsActivity extends BaseFragment implements NotificationCenter imageView.setImageResource(R.drawable.devices); emptyLayout.addView(imageView); LinearLayout.LayoutParams layoutParams2 = (LinearLayout.LayoutParams) imageView.getLayoutParams(); - layoutParams2.width = LinearLayout.LayoutParams.WRAP_CONTENT; - layoutParams2.height = LinearLayout.LayoutParams.WRAP_CONTENT; + layoutParams2.width = LayoutHelper.WRAP_CONTENT; + layoutParams2.height = LayoutHelper.WRAP_CONTENT; imageView.setLayoutParams(layoutParams2); TextView textView = new TextView(context); @@ -127,8 +128,8 @@ public class SessionsActivity extends BaseFragment implements NotificationCenter emptyLayout.addView(textView); layoutParams2 = (LinearLayout.LayoutParams) textView.getLayoutParams(); layoutParams2.topMargin = AndroidUtilities.dp(16); - layoutParams2.width = FrameLayout.LayoutParams.WRAP_CONTENT; - layoutParams2.height = FrameLayout.LayoutParams.WRAP_CONTENT; + layoutParams2.width = LayoutHelper.WRAP_CONTENT; + layoutParams2.height = LayoutHelper.WRAP_CONTENT; layoutParams2.gravity = Gravity.CENTER; textView.setLayoutParams(layoutParams2); @@ -141,16 +142,16 @@ public class SessionsActivity extends BaseFragment implements NotificationCenter emptyLayout.addView(textView); layoutParams2 = (LinearLayout.LayoutParams) textView.getLayoutParams(); layoutParams2.topMargin = AndroidUtilities.dp(14); - layoutParams2.width = FrameLayout.LayoutParams.WRAP_CONTENT; - layoutParams2.height = FrameLayout.LayoutParams.WRAP_CONTENT; + layoutParams2.width = LayoutHelper.WRAP_CONTENT; + layoutParams2.height = LayoutHelper.WRAP_CONTENT; layoutParams2.gravity = Gravity.CENTER; textView.setLayoutParams(layoutParams2); FrameLayout progressView = new FrameLayout(context); frameLayout.addView(progressView); FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) progressView.getLayoutParams(); - layoutParams.width = FrameLayout.LayoutParams.MATCH_PARENT; - layoutParams.height = FrameLayout.LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; + layoutParams.height = LayoutHelper.MATCH_PARENT; progressView.setLayoutParams(layoutParams); progressView.setOnTouchListener(new View.OnTouchListener() { @Override @@ -162,8 +163,8 @@ public class SessionsActivity extends BaseFragment implements NotificationCenter ProgressBar progressBar = new ProgressBar(context); progressView.addView(progressBar); layoutParams = (FrameLayout.LayoutParams) progressView.getLayoutParams(); - layoutParams.width = FrameLayout.LayoutParams.WRAP_CONTENT; - layoutParams.height = FrameLayout.LayoutParams.WRAP_CONTENT; + layoutParams.width = LayoutHelper.WRAP_CONTENT; + layoutParams.height = LayoutHelper.WRAP_CONTENT; layoutParams.gravity = Gravity.CENTER; progressView.setLayoutParams(layoutParams); @@ -175,8 +176,8 @@ public class SessionsActivity extends BaseFragment implements NotificationCenter listView.setEmptyView(progressView); frameLayout.addView(listView); layoutParams = (FrameLayout.LayoutParams) listView.getLayoutParams(); - layoutParams.width = FrameLayout.LayoutParams.MATCH_PARENT; - layoutParams.height = FrameLayout.LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; + layoutParams.height = LayoutHelper.MATCH_PARENT; layoutParams.gravity = Gravity.TOP; listView.setLayoutParams(layoutParams); listView.setAdapter(listAdapter); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/SettingsActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/SettingsActivity.java index f6ac527a5..99b5f9e5d 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/SettingsActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/SettingsActivity.java @@ -53,7 +53,6 @@ import org.telegram.messenger.BuildVars; import org.telegram.android.LocaleController; import org.telegram.messenger.FileLoader; import org.telegram.messenger.SerializedData; -import org.telegram.messenger.TLClassStore; import org.telegram.messenger.TLObject; import org.telegram.messenger.TLRPC; import org.telegram.messenger.ConnectionsManager; @@ -66,7 +65,7 @@ import org.telegram.messenger.RPCRequest; import org.telegram.messenger.UserConfig; import org.telegram.android.MessageObject; import org.telegram.ui.Adapters.BaseFragmentAdapter; -import org.telegram.ui.AnimationCompat.ViewProxy; +import org.telegram.android.AnimationCompat.ViewProxy; import org.telegram.ui.Cells.TextInfoCell; import org.telegram.ui.Cells.EmptyCell; import org.telegram.ui.Cells.HeaderCell; @@ -81,6 +80,7 @@ import org.telegram.ui.Components.AvatarDrawable; import org.telegram.ui.Components.AvatarUpdater; import org.telegram.ui.Components.BackupImageView; import org.telegram.ui.ActionBar.BaseFragment; +import org.telegram.ui.Components.LayoutHelper; import org.telegram.ui.Components.NumberPicker; import java.io.File; @@ -353,8 +353,8 @@ public class SettingsActivity extends BaseFragment implements NotificationCenter nameTextView.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); actionBar.addView(nameTextView); layoutParams = (FrameLayout.LayoutParams) nameTextView.getLayoutParams(); - layoutParams.width = FrameLayout.LayoutParams.WRAP_CONTENT; - layoutParams.height = FrameLayout.LayoutParams.WRAP_CONTENT; + layoutParams.width = LayoutHelper.WRAP_CONTENT; + layoutParams.height = LayoutHelper.WRAP_CONTENT; layoutParams.leftMargin = AndroidUtilities.dp(LocaleController.isRTL ? 16 : 97); layoutParams.rightMargin = AndroidUtilities.dp(LocaleController.isRTL ? 97 : 16); layoutParams.bottomMargin = AndroidUtilities.dp(51); @@ -371,8 +371,8 @@ public class SettingsActivity extends BaseFragment implements NotificationCenter onlineTextView.setGravity((LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT)); actionBar.addView(onlineTextView); layoutParams = (FrameLayout.LayoutParams) onlineTextView.getLayoutParams(); - layoutParams.width = FrameLayout.LayoutParams.WRAP_CONTENT; - layoutParams.height = FrameLayout.LayoutParams.WRAP_CONTENT; + layoutParams.width = LayoutHelper.WRAP_CONTENT; + layoutParams.height = LayoutHelper.WRAP_CONTENT; layoutParams.leftMargin = AndroidUtilities.dp(LocaleController.isRTL ? 16 : 97); layoutParams.rightMargin = AndroidUtilities.dp(LocaleController.isRTL ? 97 : 16); layoutParams.bottomMargin = AndroidUtilities.dp(30); @@ -386,8 +386,8 @@ public class SettingsActivity extends BaseFragment implements NotificationCenter AndroidUtilities.setListViewEdgeEffectColor(listView, AvatarDrawable.getProfileBackColorForId(5)); frameLayout.addView(listView); layoutParams = (FrameLayout.LayoutParams) listView.getLayoutParams(); - layoutParams.width = FrameLayout.LayoutParams.MATCH_PARENT; - layoutParams.height = FrameLayout.LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; + layoutParams.height = LayoutHelper.MATCH_PARENT; layoutParams.gravity = Gravity.TOP; listView.setLayoutParams(layoutParams); listView.setAdapter(listAdapter); @@ -619,8 +619,8 @@ public class SettingsActivity extends BaseFragment implements NotificationCenter } frameLayout.addView(writeButton); layoutParams = (FrameLayout.LayoutParams) writeButton.getLayoutParams(); - layoutParams.width = FrameLayout.LayoutParams.WRAP_CONTENT; - layoutParams.height = FrameLayout.LayoutParams.WRAP_CONTENT; + layoutParams.width = LayoutHelper.WRAP_CONTENT; + layoutParams.height = LayoutHelper.WRAP_CONTENT; layoutParams.leftMargin = AndroidUtilities.dp(LocaleController.isRTL ? 16 : 0); layoutParams.rightMargin = AndroidUtilities.dp(LocaleController.isRTL ? 0 : 16); layoutParams.gravity = (LocaleController.isRTL ? Gravity.LEFT : Gravity.RIGHT); @@ -772,7 +772,7 @@ public class SettingsActivity extends BaseFragment implements NotificationCenter byte[] datacentersBytes = Base64.decode(userString, Base64.DEFAULT); if (datacentersBytes != null) { SerializedData data = new SerializedData(datacentersBytes); - supportUser = (TLRPC.User)TLClassStore.Instance().TLdeserialize(data, data.readInt32()); + supportUser = TLRPC.User.TLdeserialize(data, data.readInt32(false), false); if (supportUser != null && supportUser.id == 333000) { supportUser = null; } @@ -1055,9 +1055,9 @@ public class SettingsActivity extends BaseFragment implements NotificationCenter view = new EmptyCell(mContext); } if (i == overscrollRow) { - ((EmptyCell) view).setHeight(88); + ((EmptyCell) view).setHeight(AndroidUtilities.dp(88)); } else { - ((EmptyCell) view).setHeight(16); + ((EmptyCell) view).setHeight(AndroidUtilities.dp(16)); } } else if (type == 1) { if (view == null) { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/TwoStepVerificationActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/TwoStepVerificationActivity.java index 74f7e9de3..b9c8aeb5f 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/TwoStepVerificationActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/TwoStepVerificationActivity.java @@ -56,6 +56,7 @@ 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 TwoStepVerificationActivity extends BaseFragment implements NotificationCenter.NotificationCenterDelegate { @@ -167,8 +168,8 @@ public class TwoStepVerificationActivity extends BaseFragment implements Notific scrollView.setFillViewport(true); frameLayout.addView(scrollView); FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) scrollView.getLayoutParams(); - layoutParams.width = FrameLayout.LayoutParams.MATCH_PARENT; - layoutParams.height = FrameLayout.LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; + layoutParams.height = LayoutHelper.MATCH_PARENT; scrollView.setLayoutParams(layoutParams); LinearLayout linearLayout = new LinearLayout(context); @@ -185,8 +186,8 @@ public class TwoStepVerificationActivity extends BaseFragment implements Notific titleTextView.setGravity(Gravity.CENTER_HORIZONTAL); linearLayout.addView(titleTextView); LinearLayout.LayoutParams layoutParams3 = (LinearLayout.LayoutParams) titleTextView.getLayoutParams(); - layoutParams3.width = FrameLayout.LayoutParams.WRAP_CONTENT; - layoutParams3.height = FrameLayout.LayoutParams.WRAP_CONTENT; + layoutParams3.width = LayoutHelper.WRAP_CONTENT; + layoutParams3.height = LayoutHelper.WRAP_CONTENT; layoutParams3.gravity = Gravity.CENTER_HORIZONTAL; layoutParams3.topMargin = AndroidUtilities.dp(38); titleTextView.setLayoutParams(layoutParams3); @@ -209,7 +210,7 @@ public class TwoStepVerificationActivity extends BaseFragment implements Notific layoutParams3.leftMargin = AndroidUtilities.dp(40); layoutParams3.rightMargin = AndroidUtilities.dp(40); layoutParams3.gravity = Gravity.TOP | Gravity.LEFT; - layoutParams3.width = LinearLayout.LayoutParams.MATCH_PARENT; + layoutParams3.width = LayoutHelper.MATCH_PARENT; passwordEditText.setLayoutParams(layoutParams3); passwordEditText.setOnEditorActionListener(new TextView.OnEditorActionListener() { @Override @@ -253,8 +254,8 @@ public class TwoStepVerificationActivity extends BaseFragment implements Notific bottomTextView.setText(LocaleController.getString("YourEmailInfo", R.string.YourEmailInfo)); linearLayout.addView(bottomTextView); layoutParams3 = (LinearLayout.LayoutParams) bottomTextView.getLayoutParams(); - layoutParams3.width = FrameLayout.LayoutParams.WRAP_CONTENT; - layoutParams3.height = FrameLayout.LayoutParams.WRAP_CONTENT; + layoutParams3.width = LayoutHelper.WRAP_CONTENT; + layoutParams3.height = LayoutHelper.WRAP_CONTENT; layoutParams3.gravity = (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP; layoutParams3.topMargin = AndroidUtilities.dp(30); layoutParams3.leftMargin = AndroidUtilities.dp(40); @@ -265,8 +266,8 @@ public class TwoStepVerificationActivity extends BaseFragment implements Notific linearLayout2.setGravity(Gravity.BOTTOM | Gravity.CENTER_VERTICAL); linearLayout.addView(linearLayout2); layoutParams3 = (LinearLayout.LayoutParams) linearLayout2.getLayoutParams(); - layoutParams3.width = LinearLayout.LayoutParams.MATCH_PARENT; - layoutParams3.height = LinearLayout.LayoutParams.MATCH_PARENT; + layoutParams3.width = LayoutHelper.MATCH_PARENT; + layoutParams3.height = LayoutHelper.MATCH_PARENT; linearLayout2.setLayoutParams(layoutParams3); bottomButton = new TextView(context); @@ -277,8 +278,8 @@ public class TwoStepVerificationActivity extends BaseFragment implements Notific bottomButton.setPadding(0, AndroidUtilities.dp(10), 0, 0); linearLayout2.addView(bottomButton); layoutParams3 = (LinearLayout.LayoutParams) bottomButton.getLayoutParams(); - layoutParams3.width = FrameLayout.LayoutParams.WRAP_CONTENT; - layoutParams3.height = FrameLayout.LayoutParams.WRAP_CONTENT; + layoutParams3.width = LayoutHelper.WRAP_CONTENT; + layoutParams3.height = LayoutHelper.WRAP_CONTENT; layoutParams3.gravity = (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.BOTTOM; layoutParams3.bottomMargin = AndroidUtilities.dp(14); layoutParams3.leftMargin = AndroidUtilities.dp(40); @@ -364,8 +365,8 @@ public class TwoStepVerificationActivity extends BaseFragment implements Notific progressView = new FrameLayout(context); frameLayout.addView(progressView); layoutParams = (FrameLayout.LayoutParams) progressView.getLayoutParams(); - layoutParams.width = FrameLayout.LayoutParams.MATCH_PARENT; - layoutParams.height = FrameLayout.LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; + layoutParams.height = LayoutHelper.MATCH_PARENT; progressView.setLayoutParams(layoutParams); progressView.setOnTouchListener(new View.OnTouchListener() { @Override @@ -377,8 +378,8 @@ public class TwoStepVerificationActivity extends BaseFragment implements Notific ProgressBar progressBar = new ProgressBar(context); progressView.addView(progressBar); layoutParams = (FrameLayout.LayoutParams) progressView.getLayoutParams(); - layoutParams.width = FrameLayout.LayoutParams.WRAP_CONTENT; - layoutParams.height = FrameLayout.LayoutParams.WRAP_CONTENT; + layoutParams.width = LayoutHelper.WRAP_CONTENT; + layoutParams.height = LayoutHelper.WRAP_CONTENT; layoutParams.gravity = Gravity.CENTER; progressView.setLayoutParams(layoutParams); @@ -390,8 +391,8 @@ public class TwoStepVerificationActivity extends BaseFragment implements Notific listView.setDrawSelectorOnTop(true); frameLayout.addView(listView); layoutParams = (FrameLayout.LayoutParams) listView.getLayoutParams(); - layoutParams.width = FrameLayout.LayoutParams.MATCH_PARENT; - layoutParams.height = FrameLayout.LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; + layoutParams.height = LayoutHelper.MATCH_PARENT; layoutParams.gravity = Gravity.TOP; listView.setLayoutParams(layoutParams); listView.setAdapter(listAdapter = new ListAdapter(context)); @@ -440,7 +441,7 @@ public class TwoStepVerificationActivity extends BaseFragment implements Notific @Override public void didReceivedNotification(int id, Object... args) { if (id == NotificationCenter.didSetTwoStepPassword) { - if (args != null && args.length > 0) { + if (args != null && args.length > 0 && args[0] != null) { currentPasswordHash = (byte[]) args[0]; } loadPasswordInfo(false); @@ -993,7 +994,7 @@ public class TwoStepVerificationActivity extends BaseFragment implements Notific @Override public boolean isEnabled(int i) { - return i != setPasswordDetailRow && i != shadowRow && i != passwordSetupDetailRow && i != passwordEmailVerifyDetailRow; + return i != setPasswordDetailRow && i != shadowRow && i != passwordSetupDetailRow && i != passwordEmailVerifyDetailRow && i != passwordEnabledDetailRow; } @Override diff --git a/TMessagesProj/src/main/java/org/telegram/ui/VideoEditorActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/VideoEditorActivity.java index 7121d0a42..1c794ab99 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/VideoEditorActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/VideoEditorActivity.java @@ -51,6 +51,7 @@ import org.telegram.messenger.Utilities; import org.telegram.ui.ActionBar.ActionBar; import org.telegram.ui.ActionBar.ActionBarMenu; import org.telegram.ui.ActionBar.BaseFragment; +import org.telegram.ui.Components.LayoutHelper; import org.telegram.ui.Components.VideoSeekBarView; import org.telegram.ui.Components.VideoTimelineView; @@ -611,7 +612,7 @@ public class VideoEditorActivity extends BaseFragment implements TextureView.Sur FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) videoContainerView.getLayoutParams(); layoutParams.topMargin = AndroidUtilities.dp(16); layoutParams.bottomMargin = AndroidUtilities.dp(260 + (compressVideo.getVisibility() == View.VISIBLE ? 20 : 0)); - layoutParams.width = FrameLayout.LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; layoutParams.leftMargin = 0; videoContainerView.setLayoutParams(layoutParams); @@ -619,12 +620,12 @@ public class VideoEditorActivity extends BaseFragment implements TextureView.Sur layoutParams.topMargin = 0; layoutParams.leftMargin = 0; layoutParams.bottomMargin = AndroidUtilities.dp(150 + (compressVideo.getVisibility() == View.VISIBLE ? 20 : 0)); - layoutParams.width = FrameLayout.LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; layoutParams.gravity = Gravity.BOTTOM; controlView.setLayoutParams(layoutParams); layoutParams = (FrameLayout.LayoutParams) textContainerView.getLayoutParams(); - layoutParams.width = FrameLayout.LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; layoutParams.leftMargin = AndroidUtilities.dp(16); layoutParams.rightMargin = AndroidUtilities.dp(16); layoutParams.bottomMargin = AndroidUtilities.dp(16); diff --git a/TMessagesProj/src/main/res/drawable-hdpi/addmember.png b/TMessagesProj/src/main/res/drawable-hdpi/addmember.png new file mode 100755 index 000000000..166473d7b Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/addmember.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/arrow_down_w.png b/TMessagesProj/src/main/res/drawable-hdpi/arrow_down_w.png new file mode 100755 index 000000000..deca1e6e9 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/arrow_down_w.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/audiocancel1.png b/TMessagesProj/src/main/res/drawable-hdpi/audiocancel1.png deleted file mode 100755 index 7f5b68969..000000000 Binary files a/TMessagesProj/src/main/res/drawable-hdpi/audiocancel1.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/audiocancel1_pressed.png b/TMessagesProj/src/main/res/drawable-hdpi/audiocancel1_pressed.png deleted file mode 100755 index 8963b7120..000000000 Binary files a/TMessagesProj/src/main/res/drawable-hdpi/audiocancel1_pressed.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/audiocancel2.png b/TMessagesProj/src/main/res/drawable-hdpi/audiocancel2.png deleted file mode 100755 index aad25e8ed..000000000 Binary files a/TMessagesProj/src/main/res/drawable-hdpi/audiocancel2.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/audiocancel2_pressed.png b/TMessagesProj/src/main/res/drawable-hdpi/audiocancel2_pressed.png deleted file mode 100755 index 9bdc31ef1..000000000 Binary files a/TMessagesProj/src/main/res/drawable-hdpi/audiocancel2_pressed.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/audioload1.png b/TMessagesProj/src/main/res/drawable-hdpi/audioload1.png deleted file mode 100755 index 1eecb03db..000000000 Binary files a/TMessagesProj/src/main/res/drawable-hdpi/audioload1.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/audioload1_pressed.png b/TMessagesProj/src/main/res/drawable-hdpi/audioload1_pressed.png deleted file mode 100755 index 854fe27a3..000000000 Binary files a/TMessagesProj/src/main/res/drawable-hdpi/audioload1_pressed.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/audioload2.png b/TMessagesProj/src/main/res/drawable-hdpi/audioload2.png deleted file mode 100755 index 9dc1787a7..000000000 Binary files a/TMessagesProj/src/main/res/drawable-hdpi/audioload2.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/audioload2_pressed.png b/TMessagesProj/src/main/res/drawable-hdpi/audioload2_pressed.png deleted file mode 100755 index feab040a6..000000000 Binary files a/TMessagesProj/src/main/res/drawable-hdpi/audioload2_pressed.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/bg_emoji_bs.9.png b/TMessagesProj/src/main/res/drawable-hdpi/bg_emoji_bs.9.png deleted file mode 100644 index a90f085cb..000000000 Binary files a/TMessagesProj/src/main/res/drawable-hdpi/bg_emoji_bs.9.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/cancel_b.png b/TMessagesProj/src/main/res/drawable-hdpi/cancel_b.png new file mode 100755 index 000000000..c942d603c Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/cancel_b.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/cancel_b_pressed.png b/TMessagesProj/src/main/res/drawable-hdpi/cancel_b_pressed.png new file mode 100755 index 000000000..58add0c14 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/cancel_b_pressed.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/cancel_g.png b/TMessagesProj/src/main/res/drawable-hdpi/cancel_g.png new file mode 100755 index 000000000..7c9c3decb Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/cancel_g.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/cancel_g_pressed.png b/TMessagesProj/src/main/res/drawable-hdpi/cancel_g_pressed.png new file mode 100755 index 000000000..2efb6df04 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/cancel_g_pressed.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/download_b.png b/TMessagesProj/src/main/res/drawable-hdpi/download_b.png new file mode 100755 index 000000000..a9f428125 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/download_b.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/download_b_pressed.png b/TMessagesProj/src/main/res/drawable-hdpi/download_b_pressed.png new file mode 100755 index 000000000..d3a6eb918 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/download_b_pressed.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/download_g.png b/TMessagesProj/src/main/res/drawable-hdpi/download_g.png new file mode 100755 index 000000000..1c5762132 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/download_g.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/download_g_pressed.png b/TMessagesProj/src/main/res/drawable-hdpi/download_g_pressed.png new file mode 100755 index 000000000..c4bba83d5 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/download_g_pressed.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/foursquare.png b/TMessagesProj/src/main/res/drawable-hdpi/foursquare.png new file mode 100644 index 000000000..64783f7ec Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/foursquare.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/header_shadow_reverse.png b/TMessagesProj/src/main/res/drawable-hdpi/header_shadow_reverse.png new file mode 100644 index 000000000..57ccb3eb2 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/header_shadow_reverse.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/ic_ab_location.png b/TMessagesProj/src/main/res/drawable-hdpi/ic_ab_location.png deleted file mode 100755 index 9f11eace7..000000000 Binary files a/TMessagesProj/src/main/res/drawable-hdpi/ic_ab_location.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/ic_keyboard_w.png b/TMessagesProj/src/main/res/drawable-hdpi/ic_keyboard_w.png new file mode 100755 index 000000000..f0b6a5da5 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/ic_keyboard_w.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/ic_smile_w.png b/TMessagesProj/src/main/res/drawable-hdpi/ic_smile_w.png new file mode 100755 index 000000000..2f8711cd9 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/ic_smile_w.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/ic_smiles_backspace.png b/TMessagesProj/src/main/res/drawable-hdpi/ic_smiles_backspace.png old mode 100644 new mode 100755 index 917303959..50c5312f2 Binary files a/TMessagesProj/src/main/res/drawable-hdpi/ic_smiles_backspace.png and b/TMessagesProj/src/main/res/drawable-hdpi/ic_smiles_backspace.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/ic_smiles_backspace_active.png b/TMessagesProj/src/main/res/drawable-hdpi/ic_smiles_backspace_active.png old mode 100644 new mode 100755 index 2fa818795..b02ed7c29 Binary files a/TMessagesProj/src/main/res/drawable-hdpi/ic_smiles_backspace_active.png and b/TMessagesProj/src/main/res/drawable-hdpi/ic_smiles_backspace_active.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/ic_smiles_bell.png b/TMessagesProj/src/main/res/drawable-hdpi/ic_smiles_bell.png old mode 100644 new mode 100755 index d018c1739..a7a2cffb3 Binary files a/TMessagesProj/src/main/res/drawable-hdpi/ic_smiles_bell.png and b/TMessagesProj/src/main/res/drawable-hdpi/ic_smiles_bell.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/ic_smiles_bell_active.png b/TMessagesProj/src/main/res/drawable-hdpi/ic_smiles_bell_active.png old mode 100644 new mode 100755 index 98c7f66ca..14cc9aedc Binary files a/TMessagesProj/src/main/res/drawable-hdpi/ic_smiles_bell_active.png and b/TMessagesProj/src/main/res/drawable-hdpi/ic_smiles_bell_active.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/ic_smiles_car.png b/TMessagesProj/src/main/res/drawable-hdpi/ic_smiles_car.png old mode 100644 new mode 100755 index 449b32b47..b8fa468aa Binary files a/TMessagesProj/src/main/res/drawable-hdpi/ic_smiles_car.png and b/TMessagesProj/src/main/res/drawable-hdpi/ic_smiles_car.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/ic_smiles_car_active.png b/TMessagesProj/src/main/res/drawable-hdpi/ic_smiles_car_active.png old mode 100644 new mode 100755 index 3f03ef735..7436b5603 Binary files a/TMessagesProj/src/main/res/drawable-hdpi/ic_smiles_car_active.png and b/TMessagesProj/src/main/res/drawable-hdpi/ic_smiles_car_active.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/ic_smiles_flower.png b/TMessagesProj/src/main/res/drawable-hdpi/ic_smiles_flower.png old mode 100644 new mode 100755 index 8f190cb4d..b70bcd452 Binary files a/TMessagesProj/src/main/res/drawable-hdpi/ic_smiles_flower.png and b/TMessagesProj/src/main/res/drawable-hdpi/ic_smiles_flower.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/ic_smiles_flower_active.png b/TMessagesProj/src/main/res/drawable-hdpi/ic_smiles_flower_active.png old mode 100644 new mode 100755 index ca9dd9748..bd783f405 Binary files a/TMessagesProj/src/main/res/drawable-hdpi/ic_smiles_flower_active.png and b/TMessagesProj/src/main/res/drawable-hdpi/ic_smiles_flower_active.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/ic_smiles_grid.png b/TMessagesProj/src/main/res/drawable-hdpi/ic_smiles_grid.png old mode 100644 new mode 100755 index ed02b9a1e..f311de53e Binary files a/TMessagesProj/src/main/res/drawable-hdpi/ic_smiles_grid.png and b/TMessagesProj/src/main/res/drawable-hdpi/ic_smiles_grid.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/ic_smiles_grid_active.png b/TMessagesProj/src/main/res/drawable-hdpi/ic_smiles_grid_active.png old mode 100644 new mode 100755 index 4b1f86867..2d020da1f Binary files a/TMessagesProj/src/main/res/drawable-hdpi/ic_smiles_grid_active.png and b/TMessagesProj/src/main/res/drawable-hdpi/ic_smiles_grid_active.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/ic_smiles_recent.png b/TMessagesProj/src/main/res/drawable-hdpi/ic_smiles_recent.png old mode 100644 new mode 100755 index 6d032d62b..3208bcecc Binary files a/TMessagesProj/src/main/res/drawable-hdpi/ic_smiles_recent.png and b/TMessagesProj/src/main/res/drawable-hdpi/ic_smiles_recent.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/ic_smiles_recent_active.png b/TMessagesProj/src/main/res/drawable-hdpi/ic_smiles_recent_active.png old mode 100644 new mode 100755 index 756df9196..bbf721b80 Binary files a/TMessagesProj/src/main/res/drawable-hdpi/ic_smiles_recent_active.png and b/TMessagesProj/src/main/res/drawable-hdpi/ic_smiles_recent_active.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/ic_smiles_smile.png b/TMessagesProj/src/main/res/drawable-hdpi/ic_smiles_smile.png old mode 100644 new mode 100755 index 70ee89c2b..2d98101f8 Binary files a/TMessagesProj/src/main/res/drawable-hdpi/ic_smiles_smile.png and b/TMessagesProj/src/main/res/drawable-hdpi/ic_smiles_smile.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/ic_smiles_smile_active.png b/TMessagesProj/src/main/res/drawable-hdpi/ic_smiles_smile_active.png old mode 100644 new mode 100755 index 078f863b4..d37ab0982 Binary files a/TMessagesProj/src/main/res/drawable-hdpi/ic_smiles_smile_active.png and b/TMessagesProj/src/main/res/drawable-hdpi/ic_smiles_smile_active.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/ic_smiles_sticker.png b/TMessagesProj/src/main/res/drawable-hdpi/ic_smiles_sticker.png new file mode 100755 index 000000000..b6f0f7fa0 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/ic_smiles_sticker.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/ic_smiles_sticker_active.png b/TMessagesProj/src/main/res/drawable-hdpi/ic_smiles_sticker_active.png new file mode 100755 index 000000000..6fa2547d7 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/ic_smiles_sticker_active.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/location_b.9.png b/TMessagesProj/src/main/res/drawable-hdpi/location_b.9.png new file mode 100644 index 000000000..84e92b27b Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/location_b.9.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/location_g.9.png b/TMessagesProj/src/main/res/drawable-hdpi/location_g.9.png new file mode 100644 index 000000000..2c9c70da2 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/location_g.9.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/myloc_on.png b/TMessagesProj/src/main/res/drawable-hdpi/myloc_on.png new file mode 100644 index 000000000..0e93b8072 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/myloc_on.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/navigate.png b/TMessagesProj/src/main/res/drawable-hdpi/navigate.png new file mode 100644 index 000000000..5bf5640aa Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/navigate.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/pause1.png b/TMessagesProj/src/main/res/drawable-hdpi/pause1.png deleted file mode 100755 index 30e945f31..000000000 Binary files a/TMessagesProj/src/main/res/drawable-hdpi/pause1.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/pause1_pressed.png b/TMessagesProj/src/main/res/drawable-hdpi/pause1_pressed.png deleted file mode 100755 index 0053b7b03..000000000 Binary files a/TMessagesProj/src/main/res/drawable-hdpi/pause1_pressed.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/pause2.png b/TMessagesProj/src/main/res/drawable-hdpi/pause2.png deleted file mode 100755 index f96f878e9..000000000 Binary files a/TMessagesProj/src/main/res/drawable-hdpi/pause2.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/pause2_pressed.png b/TMessagesProj/src/main/res/drawable-hdpi/pause2_pressed.png deleted file mode 100755 index c3bca6315..000000000 Binary files a/TMessagesProj/src/main/res/drawable-hdpi/pause2_pressed.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/pause_b.png b/TMessagesProj/src/main/res/drawable-hdpi/pause_b.png new file mode 100755 index 000000000..a9ac7a794 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/pause_b.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/pause_b_pressed.png b/TMessagesProj/src/main/res/drawable-hdpi/pause_b_pressed.png new file mode 100755 index 000000000..9f4b6e1db Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/pause_b_pressed.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/pause_g.png b/TMessagesProj/src/main/res/drawable-hdpi/pause_g.png new file mode 100755 index 000000000..b8bd88ef3 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/pause_g.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/pause_g_pressed.png b/TMessagesProj/src/main/res/drawable-hdpi/pause_g_pressed.png new file mode 100755 index 000000000..19c76b441 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/pause_g_pressed.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/pause_w.png b/TMessagesProj/src/main/res/drawable-hdpi/pause_w.png new file mode 100755 index 000000000..b3ba2dce6 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/pause_w.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/pause_w2.png b/TMessagesProj/src/main/res/drawable-hdpi/pause_w2.png new file mode 100755 index 000000000..c673dbc14 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/pause_w2.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/pause_w2_pressed.png b/TMessagesProj/src/main/res/drawable-hdpi/pause_w2_pressed.png new file mode 100755 index 000000000..7fdae4f8a Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/pause_w2_pressed.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/pause_w_pressed.png b/TMessagesProj/src/main/res/drawable-hdpi/pause_w_pressed.png new file mode 100755 index 000000000..ca28d541d Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/pause_w_pressed.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/photo_crop.png b/TMessagesProj/src/main/res/drawable-hdpi/photo_crop.png old mode 100644 new mode 100755 index facaf39e7..aa78fee8b Binary files a/TMessagesProj/src/main/res/drawable-hdpi/photo_crop.png and b/TMessagesProj/src/main/res/drawable-hdpi/photo_crop.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/photo_edit.png b/TMessagesProj/src/main/res/drawable-hdpi/photo_edit.png deleted file mode 100644 index 8768155c2..000000000 Binary files a/TMessagesProj/src/main/res/drawable-hdpi/photo_edit.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/photo_text.png b/TMessagesProj/src/main/res/drawable-hdpi/photo_text.png new file mode 100755 index 000000000..1009bbd79 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/photo_text.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/photo_text2.png b/TMessagesProj/src/main/res/drawable-hdpi/photo_text2.png new file mode 100755 index 000000000..9e0fc848b Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/photo_text2.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/photo_tools.png b/TMessagesProj/src/main/res/drawable-hdpi/photo_tools.png new file mode 100755 index 000000000..195c6626f Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/photo_tools.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/pin.png b/TMessagesProj/src/main/res/drawable-hdpi/pin.png new file mode 100644 index 000000000..386acb228 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/pin.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/place_x.png b/TMessagesProj/src/main/res/drawable-hdpi/place_x.png new file mode 100644 index 000000000..c4ee065d3 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/place_x.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/play1.png b/TMessagesProj/src/main/res/drawable-hdpi/play1.png deleted file mode 100755 index 8bb953bcf..000000000 Binary files a/TMessagesProj/src/main/res/drawable-hdpi/play1.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/play1_pressed.png b/TMessagesProj/src/main/res/drawable-hdpi/play1_pressed.png deleted file mode 100755 index d5d220250..000000000 Binary files a/TMessagesProj/src/main/res/drawable-hdpi/play1_pressed.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/play2.png b/TMessagesProj/src/main/res/drawable-hdpi/play2.png deleted file mode 100755 index 775a87a6a..000000000 Binary files a/TMessagesProj/src/main/res/drawable-hdpi/play2.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/play2_pressed.png b/TMessagesProj/src/main/res/drawable-hdpi/play2_pressed.png deleted file mode 100755 index 08e691986..000000000 Binary files a/TMessagesProj/src/main/res/drawable-hdpi/play2_pressed.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/play_w.png b/TMessagesProj/src/main/res/drawable-hdpi/play_w.png new file mode 100755 index 000000000..3b9bac668 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/play_w.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/play_w2.png b/TMessagesProj/src/main/res/drawable-hdpi/play_w2.png new file mode 100755 index 000000000..330da7e1e Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/play_w2.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/play_w2_pressed.png b/TMessagesProj/src/main/res/drawable-hdpi/play_w2_pressed.png new file mode 100755 index 000000000..7b7018e2a Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/play_w2_pressed.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/play_w_pressed.png b/TMessagesProj/src/main/res/drawable-hdpi/play_w_pressed.png new file mode 100755 index 000000000..7261585f5 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/play_w_pressed.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/round_grey.png b/TMessagesProj/src/main/res/drawable-hdpi/round_grey.png new file mode 100755 index 000000000..6f9f3f215 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/round_grey.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/tune.png b/TMessagesProj/src/main/res/drawable-hdpi/tune.png deleted file mode 100755 index ab404e6a5..000000000 Binary files a/TMessagesProj/src/main/res/drawable-hdpi/tune.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/addmember.png b/TMessagesProj/src/main/res/drawable-mdpi/addmember.png new file mode 100755 index 000000000..439b82d41 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/addmember.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/arrow_down_w.png b/TMessagesProj/src/main/res/drawable-mdpi/arrow_down_w.png new file mode 100755 index 000000000..02ad9766d Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/arrow_down_w.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/audiocancel1.png b/TMessagesProj/src/main/res/drawable-mdpi/audiocancel1.png deleted file mode 100755 index 3ec699799..000000000 Binary files a/TMessagesProj/src/main/res/drawable-mdpi/audiocancel1.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/audiocancel1_pressed.png b/TMessagesProj/src/main/res/drawable-mdpi/audiocancel1_pressed.png deleted file mode 100755 index 81b12fca1..000000000 Binary files a/TMessagesProj/src/main/res/drawable-mdpi/audiocancel1_pressed.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/audiocancel2.png b/TMessagesProj/src/main/res/drawable-mdpi/audiocancel2.png deleted file mode 100755 index f626dd957..000000000 Binary files a/TMessagesProj/src/main/res/drawable-mdpi/audiocancel2.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/audiocancel2_pressed.png b/TMessagesProj/src/main/res/drawable-mdpi/audiocancel2_pressed.png deleted file mode 100755 index 2129a26cf..000000000 Binary files a/TMessagesProj/src/main/res/drawable-mdpi/audiocancel2_pressed.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/audioload1.png b/TMessagesProj/src/main/res/drawable-mdpi/audioload1.png deleted file mode 100755 index e06d062f2..000000000 Binary files a/TMessagesProj/src/main/res/drawable-mdpi/audioload1.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/audioload1_pressed.png b/TMessagesProj/src/main/res/drawable-mdpi/audioload1_pressed.png deleted file mode 100755 index 96774c41a..000000000 Binary files a/TMessagesProj/src/main/res/drawable-mdpi/audioload1_pressed.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/audioload2.png b/TMessagesProj/src/main/res/drawable-mdpi/audioload2.png deleted file mode 100755 index cad0562f4..000000000 Binary files a/TMessagesProj/src/main/res/drawable-mdpi/audioload2.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/audioload2_pressed.png b/TMessagesProj/src/main/res/drawable-mdpi/audioload2_pressed.png deleted file mode 100755 index b1c3c6c0c..000000000 Binary files a/TMessagesProj/src/main/res/drawable-mdpi/audioload2_pressed.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/bg_emoji_bs.9.png b/TMessagesProj/src/main/res/drawable-mdpi/bg_emoji_bs.9.png deleted file mode 100644 index 1d6f546b1..000000000 Binary files a/TMessagesProj/src/main/res/drawable-mdpi/bg_emoji_bs.9.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/cancel_b.png b/TMessagesProj/src/main/res/drawable-mdpi/cancel_b.png new file mode 100755 index 000000000..b0dd70ea0 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/cancel_b.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/cancel_b_pressed.png b/TMessagesProj/src/main/res/drawable-mdpi/cancel_b_pressed.png new file mode 100755 index 000000000..412153274 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/cancel_b_pressed.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/cancel_g.png b/TMessagesProj/src/main/res/drawable-mdpi/cancel_g.png new file mode 100755 index 000000000..43b0a71e4 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/cancel_g.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/cancel_g_pressed.png b/TMessagesProj/src/main/res/drawable-mdpi/cancel_g_pressed.png new file mode 100755 index 000000000..4942c02cb Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/cancel_g_pressed.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/download_b.png b/TMessagesProj/src/main/res/drawable-mdpi/download_b.png new file mode 100755 index 000000000..7c3fb80ce Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/download_b.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/download_b_pressed.png b/TMessagesProj/src/main/res/drawable-mdpi/download_b_pressed.png new file mode 100755 index 000000000..7e5385dd7 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/download_b_pressed.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/download_g.png b/TMessagesProj/src/main/res/drawable-mdpi/download_g.png new file mode 100755 index 000000000..2b6c9b925 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/download_g.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/download_g_pressed.png b/TMessagesProj/src/main/res/drawable-mdpi/download_g_pressed.png new file mode 100755 index 000000000..a1270b164 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/download_g_pressed.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/foursquare.png b/TMessagesProj/src/main/res/drawable-mdpi/foursquare.png new file mode 100644 index 000000000..e13d17641 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/foursquare.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/header_shadow_reverse.png b/TMessagesProj/src/main/res/drawable-mdpi/header_shadow_reverse.png new file mode 100644 index 000000000..cab866e17 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/header_shadow_reverse.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/ic_ab_location.png b/TMessagesProj/src/main/res/drawable-mdpi/ic_ab_location.png deleted file mode 100755 index 19fc6d1c3..000000000 Binary files a/TMessagesProj/src/main/res/drawable-mdpi/ic_ab_location.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/ic_keyboard_w.png b/TMessagesProj/src/main/res/drawable-mdpi/ic_keyboard_w.png new file mode 100755 index 000000000..1f4614aab Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/ic_keyboard_w.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/ic_smile_w.png b/TMessagesProj/src/main/res/drawable-mdpi/ic_smile_w.png new file mode 100755 index 000000000..a110abc10 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/ic_smile_w.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/ic_smiles_backspace.png b/TMessagesProj/src/main/res/drawable-mdpi/ic_smiles_backspace.png old mode 100644 new mode 100755 index 76076a60a..d63ba596a Binary files a/TMessagesProj/src/main/res/drawable-mdpi/ic_smiles_backspace.png and b/TMessagesProj/src/main/res/drawable-mdpi/ic_smiles_backspace.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/ic_smiles_backspace_active.png b/TMessagesProj/src/main/res/drawable-mdpi/ic_smiles_backspace_active.png old mode 100644 new mode 100755 index 23bbc4b88..45140c69f Binary files a/TMessagesProj/src/main/res/drawable-mdpi/ic_smiles_backspace_active.png and b/TMessagesProj/src/main/res/drawable-mdpi/ic_smiles_backspace_active.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/ic_smiles_bell.png b/TMessagesProj/src/main/res/drawable-mdpi/ic_smiles_bell.png old mode 100644 new mode 100755 index 1242d7dcd..8807b989f Binary files a/TMessagesProj/src/main/res/drawable-mdpi/ic_smiles_bell.png and b/TMessagesProj/src/main/res/drawable-mdpi/ic_smiles_bell.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/ic_smiles_bell_active.png b/TMessagesProj/src/main/res/drawable-mdpi/ic_smiles_bell_active.png old mode 100644 new mode 100755 index 647f84536..6536bc7b3 Binary files a/TMessagesProj/src/main/res/drawable-mdpi/ic_smiles_bell_active.png and b/TMessagesProj/src/main/res/drawable-mdpi/ic_smiles_bell_active.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/ic_smiles_car.png b/TMessagesProj/src/main/res/drawable-mdpi/ic_smiles_car.png old mode 100644 new mode 100755 index 4c9d24fcd..e52411af8 Binary files a/TMessagesProj/src/main/res/drawable-mdpi/ic_smiles_car.png and b/TMessagesProj/src/main/res/drawable-mdpi/ic_smiles_car.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/ic_smiles_car_active.png b/TMessagesProj/src/main/res/drawable-mdpi/ic_smiles_car_active.png old mode 100644 new mode 100755 index f52038ef7..360b8e85d Binary files a/TMessagesProj/src/main/res/drawable-mdpi/ic_smiles_car_active.png and b/TMessagesProj/src/main/res/drawable-mdpi/ic_smiles_car_active.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/ic_smiles_flower.png b/TMessagesProj/src/main/res/drawable-mdpi/ic_smiles_flower.png old mode 100644 new mode 100755 index aece41763..867f5b3f0 Binary files a/TMessagesProj/src/main/res/drawable-mdpi/ic_smiles_flower.png and b/TMessagesProj/src/main/res/drawable-mdpi/ic_smiles_flower.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/ic_smiles_flower_active.png b/TMessagesProj/src/main/res/drawable-mdpi/ic_smiles_flower_active.png old mode 100644 new mode 100755 index 26026ef5d..53c890bc8 Binary files a/TMessagesProj/src/main/res/drawable-mdpi/ic_smiles_flower_active.png and b/TMessagesProj/src/main/res/drawable-mdpi/ic_smiles_flower_active.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/ic_smiles_grid.png b/TMessagesProj/src/main/res/drawable-mdpi/ic_smiles_grid.png old mode 100644 new mode 100755 index 3d8934c0e..844206619 Binary files a/TMessagesProj/src/main/res/drawable-mdpi/ic_smiles_grid.png and b/TMessagesProj/src/main/res/drawable-mdpi/ic_smiles_grid.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/ic_smiles_grid_active.png b/TMessagesProj/src/main/res/drawable-mdpi/ic_smiles_grid_active.png old mode 100644 new mode 100755 index 6bea9c24d..926576661 Binary files a/TMessagesProj/src/main/res/drawable-mdpi/ic_smiles_grid_active.png and b/TMessagesProj/src/main/res/drawable-mdpi/ic_smiles_grid_active.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/ic_smiles_recent.png b/TMessagesProj/src/main/res/drawable-mdpi/ic_smiles_recent.png old mode 100644 new mode 100755 index 41d6f8a29..d306da4f3 Binary files a/TMessagesProj/src/main/res/drawable-mdpi/ic_smiles_recent.png and b/TMessagesProj/src/main/res/drawable-mdpi/ic_smiles_recent.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/ic_smiles_recent_active.png b/TMessagesProj/src/main/res/drawable-mdpi/ic_smiles_recent_active.png old mode 100644 new mode 100755 index 0f8caad5c..c208b675e Binary files a/TMessagesProj/src/main/res/drawable-mdpi/ic_smiles_recent_active.png and b/TMessagesProj/src/main/res/drawable-mdpi/ic_smiles_recent_active.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/ic_smiles_smile.png b/TMessagesProj/src/main/res/drawable-mdpi/ic_smiles_smile.png old mode 100644 new mode 100755 index 9b7c59f8f..eb7c383dc Binary files a/TMessagesProj/src/main/res/drawable-mdpi/ic_smiles_smile.png and b/TMessagesProj/src/main/res/drawable-mdpi/ic_smiles_smile.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/ic_smiles_smile_active.png b/TMessagesProj/src/main/res/drawable-mdpi/ic_smiles_smile_active.png old mode 100644 new mode 100755 index f62176957..1de2e16a7 Binary files a/TMessagesProj/src/main/res/drawable-mdpi/ic_smiles_smile_active.png and b/TMessagesProj/src/main/res/drawable-mdpi/ic_smiles_smile_active.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/ic_smiles_sticker.png b/TMessagesProj/src/main/res/drawable-mdpi/ic_smiles_sticker.png new file mode 100755 index 000000000..b76d05515 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/ic_smiles_sticker.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/ic_smiles_sticker_active.png b/TMessagesProj/src/main/res/drawable-mdpi/ic_smiles_sticker_active.png new file mode 100755 index 000000000..056a2d0d5 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/ic_smiles_sticker_active.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/location_b.9.png b/TMessagesProj/src/main/res/drawable-mdpi/location_b.9.png new file mode 100644 index 000000000..3f0abf8b4 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/location_b.9.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/location_g.9.png b/TMessagesProj/src/main/res/drawable-mdpi/location_g.9.png new file mode 100644 index 000000000..d77aa911d Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/location_g.9.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/myloc_on.png b/TMessagesProj/src/main/res/drawable-mdpi/myloc_on.png new file mode 100644 index 000000000..a86dd1570 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/myloc_on.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/navigate.png b/TMessagesProj/src/main/res/drawable-mdpi/navigate.png new file mode 100644 index 000000000..872d18fa7 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/navigate.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/pause1.png b/TMessagesProj/src/main/res/drawable-mdpi/pause1.png deleted file mode 100755 index 35570d84a..000000000 Binary files a/TMessagesProj/src/main/res/drawable-mdpi/pause1.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/pause1_pressed.png b/TMessagesProj/src/main/res/drawable-mdpi/pause1_pressed.png deleted file mode 100755 index 557cac120..000000000 Binary files a/TMessagesProj/src/main/res/drawable-mdpi/pause1_pressed.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/pause2.png b/TMessagesProj/src/main/res/drawable-mdpi/pause2.png deleted file mode 100755 index 50f4366db..000000000 Binary files a/TMessagesProj/src/main/res/drawable-mdpi/pause2.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/pause2_pressed.png b/TMessagesProj/src/main/res/drawable-mdpi/pause2_pressed.png deleted file mode 100755 index 50305fead..000000000 Binary files a/TMessagesProj/src/main/res/drawable-mdpi/pause2_pressed.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/pause_b.png b/TMessagesProj/src/main/res/drawable-mdpi/pause_b.png new file mode 100755 index 000000000..4d74e901d Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/pause_b.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/pause_b_pressed.png b/TMessagesProj/src/main/res/drawable-mdpi/pause_b_pressed.png new file mode 100755 index 000000000..e300fbbca Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/pause_b_pressed.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/pause_g.png b/TMessagesProj/src/main/res/drawable-mdpi/pause_g.png new file mode 100755 index 000000000..a95855259 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/pause_g.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/pause_g_pressed.png b/TMessagesProj/src/main/res/drawable-mdpi/pause_g_pressed.png new file mode 100755 index 000000000..82e15e54d Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/pause_g_pressed.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/pause_w.png b/TMessagesProj/src/main/res/drawable-mdpi/pause_w.png new file mode 100755 index 000000000..a6416a69e Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/pause_w.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/pause_w2.png b/TMessagesProj/src/main/res/drawable-mdpi/pause_w2.png new file mode 100755 index 000000000..a5d419e71 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/pause_w2.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/pause_w2_pressed.png b/TMessagesProj/src/main/res/drawable-mdpi/pause_w2_pressed.png new file mode 100755 index 000000000..03dad7468 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/pause_w2_pressed.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/pause_w_pressed.png b/TMessagesProj/src/main/res/drawable-mdpi/pause_w_pressed.png new file mode 100755 index 000000000..b12e36df9 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/pause_w_pressed.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/photo_crop.png b/TMessagesProj/src/main/res/drawable-mdpi/photo_crop.png old mode 100644 new mode 100755 index 103f1c262..9133959b4 Binary files a/TMessagesProj/src/main/res/drawable-mdpi/photo_crop.png and b/TMessagesProj/src/main/res/drawable-mdpi/photo_crop.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/photo_edit.png b/TMessagesProj/src/main/res/drawable-mdpi/photo_edit.png deleted file mode 100644 index 437baede1..000000000 Binary files a/TMessagesProj/src/main/res/drawable-mdpi/photo_edit.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/photo_text.png b/TMessagesProj/src/main/res/drawable-mdpi/photo_text.png new file mode 100755 index 000000000..b1301d532 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/photo_text.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/photo_text2.png b/TMessagesProj/src/main/res/drawable-mdpi/photo_text2.png new file mode 100755 index 000000000..bbc1835bd Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/photo_text2.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/photo_tools.png b/TMessagesProj/src/main/res/drawable-mdpi/photo_tools.png new file mode 100755 index 000000000..86296a075 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/photo_tools.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/pin.png b/TMessagesProj/src/main/res/drawable-mdpi/pin.png new file mode 100644 index 000000000..ce126ca8c Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/pin.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/place_x.png b/TMessagesProj/src/main/res/drawable-mdpi/place_x.png new file mode 100644 index 000000000..3e4869bf6 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/place_x.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/play1.png b/TMessagesProj/src/main/res/drawable-mdpi/play1.png deleted file mode 100755 index 25cee312d..000000000 Binary files a/TMessagesProj/src/main/res/drawable-mdpi/play1.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/play1_pressed.png b/TMessagesProj/src/main/res/drawable-mdpi/play1_pressed.png deleted file mode 100755 index 124eb2479..000000000 Binary files a/TMessagesProj/src/main/res/drawable-mdpi/play1_pressed.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/play2.png b/TMessagesProj/src/main/res/drawable-mdpi/play2.png deleted file mode 100755 index 9d5a7c907..000000000 Binary files a/TMessagesProj/src/main/res/drawable-mdpi/play2.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/play2_pressed.png b/TMessagesProj/src/main/res/drawable-mdpi/play2_pressed.png deleted file mode 100755 index 52ce63376..000000000 Binary files a/TMessagesProj/src/main/res/drawable-mdpi/play2_pressed.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/play_w.png b/TMessagesProj/src/main/res/drawable-mdpi/play_w.png new file mode 100755 index 000000000..ca0df62f6 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/play_w.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/play_w2.png b/TMessagesProj/src/main/res/drawable-mdpi/play_w2.png new file mode 100755 index 000000000..5fba3f1b9 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/play_w2.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/play_w2_pressed.png b/TMessagesProj/src/main/res/drawable-mdpi/play_w2_pressed.png new file mode 100755 index 000000000..7b9fc05b3 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/play_w2_pressed.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/play_w_pressed.png b/TMessagesProj/src/main/res/drawable-mdpi/play_w_pressed.png new file mode 100755 index 000000000..b8b601b8a Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/play_w_pressed.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/round_grey.png b/TMessagesProj/src/main/res/drawable-mdpi/round_grey.png new file mode 100755 index 000000000..7753080de Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/round_grey.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/tune.png b/TMessagesProj/src/main/res/drawable-mdpi/tune.png deleted file mode 100755 index 633d4b02d..000000000 Binary files a/TMessagesProj/src/main/res/drawable-mdpi/tune.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/addmember.png b/TMessagesProj/src/main/res/drawable-xhdpi/addmember.png new file mode 100755 index 000000000..8440ca18f Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/addmember.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/arrow_down_w.png b/TMessagesProj/src/main/res/drawable-xhdpi/arrow_down_w.png new file mode 100755 index 000000000..a94e1d57b Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/arrow_down_w.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/audiocancel1.png b/TMessagesProj/src/main/res/drawable-xhdpi/audiocancel1.png deleted file mode 100755 index 9a589b40c..000000000 Binary files a/TMessagesProj/src/main/res/drawable-xhdpi/audiocancel1.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/audiocancel1_pressed.png b/TMessagesProj/src/main/res/drawable-xhdpi/audiocancel1_pressed.png deleted file mode 100755 index 405882168..000000000 Binary files a/TMessagesProj/src/main/res/drawable-xhdpi/audiocancel1_pressed.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/audiocancel2.png b/TMessagesProj/src/main/res/drawable-xhdpi/audiocancel2.png deleted file mode 100755 index 8af795283..000000000 Binary files a/TMessagesProj/src/main/res/drawable-xhdpi/audiocancel2.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/audiocancel2_pressed.png b/TMessagesProj/src/main/res/drawable-xhdpi/audiocancel2_pressed.png deleted file mode 100755 index b5ca6b6e4..000000000 Binary files a/TMessagesProj/src/main/res/drawable-xhdpi/audiocancel2_pressed.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/audioload1.png b/TMessagesProj/src/main/res/drawable-xhdpi/audioload1.png deleted file mode 100755 index 48db9f061..000000000 Binary files a/TMessagesProj/src/main/res/drawable-xhdpi/audioload1.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/audioload1_pressed.png b/TMessagesProj/src/main/res/drawable-xhdpi/audioload1_pressed.png deleted file mode 100755 index 48db9f061..000000000 Binary files a/TMessagesProj/src/main/res/drawable-xhdpi/audioload1_pressed.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/audioload2.png b/TMessagesProj/src/main/res/drawable-xhdpi/audioload2.png deleted file mode 100755 index 02dbe77b2..000000000 Binary files a/TMessagesProj/src/main/res/drawable-xhdpi/audioload2.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/audioload2_pressed.png b/TMessagesProj/src/main/res/drawable-xhdpi/audioload2_pressed.png deleted file mode 100755 index 434cc9d22..000000000 Binary files a/TMessagesProj/src/main/res/drawable-xhdpi/audioload2_pressed.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/bg_emoji_bs.9.png b/TMessagesProj/src/main/res/drawable-xhdpi/bg_emoji_bs.9.png deleted file mode 100644 index 54c8f9ac8..000000000 Binary files a/TMessagesProj/src/main/res/drawable-xhdpi/bg_emoji_bs.9.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/cancel_b.png b/TMessagesProj/src/main/res/drawable-xhdpi/cancel_b.png new file mode 100755 index 000000000..36c6390ed Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/cancel_b.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/cancel_b_pressed.png b/TMessagesProj/src/main/res/drawable-xhdpi/cancel_b_pressed.png new file mode 100755 index 000000000..79f951727 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/cancel_b_pressed.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/cancel_g.png b/TMessagesProj/src/main/res/drawable-xhdpi/cancel_g.png new file mode 100755 index 000000000..a77e504f2 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/cancel_g.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/cancel_g_pressed.png b/TMessagesProj/src/main/res/drawable-xhdpi/cancel_g_pressed.png new file mode 100755 index 000000000..dea2364da Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/cancel_g_pressed.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/download_b.png b/TMessagesProj/src/main/res/drawable-xhdpi/download_b.png new file mode 100755 index 000000000..5d114b083 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/download_b.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/download_b_pressed.png b/TMessagesProj/src/main/res/drawable-xhdpi/download_b_pressed.png new file mode 100755 index 000000000..671f591b2 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/download_b_pressed.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/download_g.png b/TMessagesProj/src/main/res/drawable-xhdpi/download_g.png new file mode 100755 index 000000000..1eaf6cfcd Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/download_g.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/download_g_pressed.png b/TMessagesProj/src/main/res/drawable-xhdpi/download_g_pressed.png new file mode 100755 index 000000000..4d785fbbe Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/download_g_pressed.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/foursquare.png b/TMessagesProj/src/main/res/drawable-xhdpi/foursquare.png new file mode 100644 index 000000000..2b5bb36dc Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/foursquare.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/header_shadow_reverse.png b/TMessagesProj/src/main/res/drawable-xhdpi/header_shadow_reverse.png new file mode 100644 index 000000000..b8ab34806 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/header_shadow_reverse.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/ic_ab_location.png b/TMessagesProj/src/main/res/drawable-xhdpi/ic_ab_location.png deleted file mode 100755 index ebc61a535..000000000 Binary files a/TMessagesProj/src/main/res/drawable-xhdpi/ic_ab_location.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/ic_keyboard_w.png b/TMessagesProj/src/main/res/drawable-xhdpi/ic_keyboard_w.png new file mode 100755 index 000000000..4866b74e4 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/ic_keyboard_w.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/ic_smile_w.png b/TMessagesProj/src/main/res/drawable-xhdpi/ic_smile_w.png new file mode 100755 index 000000000..f03eb4053 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/ic_smile_w.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/ic_smiles_backspace.png b/TMessagesProj/src/main/res/drawable-xhdpi/ic_smiles_backspace.png old mode 100644 new mode 100755 index 561f125e3..3d4d64c15 Binary files a/TMessagesProj/src/main/res/drawable-xhdpi/ic_smiles_backspace.png and b/TMessagesProj/src/main/res/drawable-xhdpi/ic_smiles_backspace.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/ic_smiles_backspace_active.png b/TMessagesProj/src/main/res/drawable-xhdpi/ic_smiles_backspace_active.png old mode 100644 new mode 100755 index 204a9ee4c..6f76811cb Binary files a/TMessagesProj/src/main/res/drawable-xhdpi/ic_smiles_backspace_active.png and b/TMessagesProj/src/main/res/drawable-xhdpi/ic_smiles_backspace_active.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/ic_smiles_bell.png b/TMessagesProj/src/main/res/drawable-xhdpi/ic_smiles_bell.png old mode 100644 new mode 100755 index d299db86b..faad89895 Binary files a/TMessagesProj/src/main/res/drawable-xhdpi/ic_smiles_bell.png and b/TMessagesProj/src/main/res/drawable-xhdpi/ic_smiles_bell.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/ic_smiles_bell_active.png b/TMessagesProj/src/main/res/drawable-xhdpi/ic_smiles_bell_active.png old mode 100644 new mode 100755 index 075fc5fa9..ebdb0c9e2 Binary files a/TMessagesProj/src/main/res/drawable-xhdpi/ic_smiles_bell_active.png and b/TMessagesProj/src/main/res/drawable-xhdpi/ic_smiles_bell_active.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/ic_smiles_car.png b/TMessagesProj/src/main/res/drawable-xhdpi/ic_smiles_car.png old mode 100644 new mode 100755 index 0c8519f11..5d312c39c Binary files a/TMessagesProj/src/main/res/drawable-xhdpi/ic_smiles_car.png and b/TMessagesProj/src/main/res/drawable-xhdpi/ic_smiles_car.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/ic_smiles_car_active.png b/TMessagesProj/src/main/res/drawable-xhdpi/ic_smiles_car_active.png old mode 100644 new mode 100755 index 3bae05c06..152f0c1bd Binary files a/TMessagesProj/src/main/res/drawable-xhdpi/ic_smiles_car_active.png and b/TMessagesProj/src/main/res/drawable-xhdpi/ic_smiles_car_active.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/ic_smiles_flower.png b/TMessagesProj/src/main/res/drawable-xhdpi/ic_smiles_flower.png old mode 100644 new mode 100755 index c5643d4fd..c9e2a36a2 Binary files a/TMessagesProj/src/main/res/drawable-xhdpi/ic_smiles_flower.png and b/TMessagesProj/src/main/res/drawable-xhdpi/ic_smiles_flower.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/ic_smiles_flower_active.png b/TMessagesProj/src/main/res/drawable-xhdpi/ic_smiles_flower_active.png old mode 100644 new mode 100755 index fc5082c78..d95654808 Binary files a/TMessagesProj/src/main/res/drawable-xhdpi/ic_smiles_flower_active.png and b/TMessagesProj/src/main/res/drawable-xhdpi/ic_smiles_flower_active.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/ic_smiles_grid.png b/TMessagesProj/src/main/res/drawable-xhdpi/ic_smiles_grid.png old mode 100644 new mode 100755 index 550d0016c..aae876d0b Binary files a/TMessagesProj/src/main/res/drawable-xhdpi/ic_smiles_grid.png and b/TMessagesProj/src/main/res/drawable-xhdpi/ic_smiles_grid.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/ic_smiles_grid_active.png b/TMessagesProj/src/main/res/drawable-xhdpi/ic_smiles_grid_active.png old mode 100644 new mode 100755 index 4d3758fc6..ea9338477 Binary files a/TMessagesProj/src/main/res/drawable-xhdpi/ic_smiles_grid_active.png and b/TMessagesProj/src/main/res/drawable-xhdpi/ic_smiles_grid_active.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/ic_smiles_recent.png b/TMessagesProj/src/main/res/drawable-xhdpi/ic_smiles_recent.png old mode 100644 new mode 100755 index 69f4c19ac..aa1b12a7e Binary files a/TMessagesProj/src/main/res/drawable-xhdpi/ic_smiles_recent.png and b/TMessagesProj/src/main/res/drawable-xhdpi/ic_smiles_recent.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/ic_smiles_recent_active.png b/TMessagesProj/src/main/res/drawable-xhdpi/ic_smiles_recent_active.png old mode 100644 new mode 100755 index 9b270fc31..950176fdf Binary files a/TMessagesProj/src/main/res/drawable-xhdpi/ic_smiles_recent_active.png and b/TMessagesProj/src/main/res/drawable-xhdpi/ic_smiles_recent_active.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/ic_smiles_smile.png b/TMessagesProj/src/main/res/drawable-xhdpi/ic_smiles_smile.png old mode 100644 new mode 100755 index e367ee699..0f94a602e Binary files a/TMessagesProj/src/main/res/drawable-xhdpi/ic_smiles_smile.png and b/TMessagesProj/src/main/res/drawable-xhdpi/ic_smiles_smile.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/ic_smiles_smile_active.png b/TMessagesProj/src/main/res/drawable-xhdpi/ic_smiles_smile_active.png old mode 100644 new mode 100755 index cc7b338e3..6b883ef6f Binary files a/TMessagesProj/src/main/res/drawable-xhdpi/ic_smiles_smile_active.png and b/TMessagesProj/src/main/res/drawable-xhdpi/ic_smiles_smile_active.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/ic_smiles_sticker.png b/TMessagesProj/src/main/res/drawable-xhdpi/ic_smiles_sticker.png new file mode 100755 index 000000000..8f423142f Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/ic_smiles_sticker.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/ic_smiles_sticker_active.png b/TMessagesProj/src/main/res/drawable-xhdpi/ic_smiles_sticker_active.png new file mode 100755 index 000000000..b2141d6cb Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/ic_smiles_sticker_active.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/location_b.9.png b/TMessagesProj/src/main/res/drawable-xhdpi/location_b.9.png new file mode 100644 index 000000000..ade7b9ef9 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/location_b.9.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/location_g.9.png b/TMessagesProj/src/main/res/drawable-xhdpi/location_g.9.png new file mode 100644 index 000000000..bbaeb93c4 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/location_g.9.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/myloc_on.png b/TMessagesProj/src/main/res/drawable-xhdpi/myloc_on.png new file mode 100644 index 000000000..0ec99ef2a Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/myloc_on.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/navigate.png b/TMessagesProj/src/main/res/drawable-xhdpi/navigate.png new file mode 100644 index 000000000..6c7b7e134 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/navigate.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/pause1.png b/TMessagesProj/src/main/res/drawable-xhdpi/pause1.png deleted file mode 100755 index 5a0abe48a..000000000 Binary files a/TMessagesProj/src/main/res/drawable-xhdpi/pause1.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/pause1_pressed.png b/TMessagesProj/src/main/res/drawable-xhdpi/pause1_pressed.png deleted file mode 100755 index 799a2314b..000000000 Binary files a/TMessagesProj/src/main/res/drawable-xhdpi/pause1_pressed.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/pause2.png b/TMessagesProj/src/main/res/drawable-xhdpi/pause2.png deleted file mode 100755 index 973b1675b..000000000 Binary files a/TMessagesProj/src/main/res/drawable-xhdpi/pause2.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/pause2_pressed.png b/TMessagesProj/src/main/res/drawable-xhdpi/pause2_pressed.png deleted file mode 100755 index 09e75bb8c..000000000 Binary files a/TMessagesProj/src/main/res/drawable-xhdpi/pause2_pressed.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/pause_b.png b/TMessagesProj/src/main/res/drawable-xhdpi/pause_b.png new file mode 100755 index 000000000..851500ecd Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/pause_b.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/pause_b_pressed.png b/TMessagesProj/src/main/res/drawable-xhdpi/pause_b_pressed.png new file mode 100755 index 000000000..ab048657c Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/pause_b_pressed.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/pause_g.png b/TMessagesProj/src/main/res/drawable-xhdpi/pause_g.png new file mode 100755 index 000000000..0708a5ede Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/pause_g.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/pause_g_pressed.png b/TMessagesProj/src/main/res/drawable-xhdpi/pause_g_pressed.png new file mode 100755 index 000000000..c6d72f73e Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/pause_g_pressed.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/pause_w.png b/TMessagesProj/src/main/res/drawable-xhdpi/pause_w.png new file mode 100755 index 000000000..9228d6e52 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/pause_w.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/pause_w2.png b/TMessagesProj/src/main/res/drawable-xhdpi/pause_w2.png new file mode 100755 index 000000000..bcb65abfa Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/pause_w2.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/pause_w2_pressed.png b/TMessagesProj/src/main/res/drawable-xhdpi/pause_w2_pressed.png new file mode 100755 index 000000000..7b7e39079 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/pause_w2_pressed.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/pause_w_pressed.png b/TMessagesProj/src/main/res/drawable-xhdpi/pause_w_pressed.png new file mode 100755 index 000000000..214572b3e Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/pause_w_pressed.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/photo_crop.png b/TMessagesProj/src/main/res/drawable-xhdpi/photo_crop.png old mode 100644 new mode 100755 index d69c3c12a..312b56546 Binary files a/TMessagesProj/src/main/res/drawable-xhdpi/photo_crop.png and b/TMessagesProj/src/main/res/drawable-xhdpi/photo_crop.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/photo_edit.png b/TMessagesProj/src/main/res/drawable-xhdpi/photo_edit.png deleted file mode 100644 index 33176600b..000000000 Binary files a/TMessagesProj/src/main/res/drawable-xhdpi/photo_edit.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/photo_text.png b/TMessagesProj/src/main/res/drawable-xhdpi/photo_text.png new file mode 100755 index 000000000..4faf7715a Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/photo_text.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/photo_text2.png b/TMessagesProj/src/main/res/drawable-xhdpi/photo_text2.png new file mode 100755 index 000000000..244a4b355 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/photo_text2.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/photo_tools.png b/TMessagesProj/src/main/res/drawable-xhdpi/photo_tools.png new file mode 100755 index 000000000..c4ff608ae Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/photo_tools.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/pin.png b/TMessagesProj/src/main/res/drawable-xhdpi/pin.png new file mode 100644 index 000000000..1fa36f892 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/pin.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/place_x.png b/TMessagesProj/src/main/res/drawable-xhdpi/place_x.png new file mode 100644 index 000000000..3d95f25f9 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/place_x.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/play1.png b/TMessagesProj/src/main/res/drawable-xhdpi/play1.png deleted file mode 100755 index 442474dac..000000000 Binary files a/TMessagesProj/src/main/res/drawable-xhdpi/play1.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/play1_pressed.png b/TMessagesProj/src/main/res/drawable-xhdpi/play1_pressed.png deleted file mode 100755 index 9391b98f7..000000000 Binary files a/TMessagesProj/src/main/res/drawable-xhdpi/play1_pressed.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/play2.png b/TMessagesProj/src/main/res/drawable-xhdpi/play2.png deleted file mode 100755 index a2bf525bd..000000000 Binary files a/TMessagesProj/src/main/res/drawable-xhdpi/play2.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/play2_pressed.png b/TMessagesProj/src/main/res/drawable-xhdpi/play2_pressed.png deleted file mode 100755 index 164ff9e10..000000000 Binary files a/TMessagesProj/src/main/res/drawable-xhdpi/play2_pressed.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/play_w.png b/TMessagesProj/src/main/res/drawable-xhdpi/play_w.png new file mode 100755 index 000000000..77d1a292d Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/play_w.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/play_w2.png b/TMessagesProj/src/main/res/drawable-xhdpi/play_w2.png new file mode 100755 index 000000000..580da8336 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/play_w2.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/play_w2_pressed.png b/TMessagesProj/src/main/res/drawable-xhdpi/play_w2_pressed.png new file mode 100755 index 000000000..9bdc7f202 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/play_w2_pressed.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/play_w_pressed.png b/TMessagesProj/src/main/res/drawable-xhdpi/play_w_pressed.png new file mode 100755 index 000000000..11b12a54c Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/play_w_pressed.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/round_grey.png b/TMessagesProj/src/main/res/drawable-xhdpi/round_grey.png new file mode 100755 index 000000000..de6e1f5cc Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/round_grey.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/tune.png b/TMessagesProj/src/main/res/drawable-xhdpi/tune.png deleted file mode 100755 index c6d6b9c7f..000000000 Binary files a/TMessagesProj/src/main/res/drawable-xhdpi/tune.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/addmember.png b/TMessagesProj/src/main/res/drawable-xxhdpi/addmember.png new file mode 100755 index 000000000..b4a262083 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/addmember.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/arrow_down_w.png b/TMessagesProj/src/main/res/drawable-xxhdpi/arrow_down_w.png new file mode 100755 index 000000000..e7710ef9a Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/arrow_down_w.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/audiocancel1.png b/TMessagesProj/src/main/res/drawable-xxhdpi/audiocancel1.png deleted file mode 100755 index c7f705a60..000000000 Binary files a/TMessagesProj/src/main/res/drawable-xxhdpi/audiocancel1.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/audiocancel1_pressed.png b/TMessagesProj/src/main/res/drawable-xxhdpi/audiocancel1_pressed.png deleted file mode 100755 index ba16d325d..000000000 Binary files a/TMessagesProj/src/main/res/drawable-xxhdpi/audiocancel1_pressed.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/audiocancel2.png b/TMessagesProj/src/main/res/drawable-xxhdpi/audiocancel2.png deleted file mode 100755 index ff6cb6814..000000000 Binary files a/TMessagesProj/src/main/res/drawable-xxhdpi/audiocancel2.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/audiocancel2_pressed.png b/TMessagesProj/src/main/res/drawable-xxhdpi/audiocancel2_pressed.png deleted file mode 100755 index cc9f56b11..000000000 Binary files a/TMessagesProj/src/main/res/drawable-xxhdpi/audiocancel2_pressed.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/audioload1.png b/TMessagesProj/src/main/res/drawable-xxhdpi/audioload1.png deleted file mode 100755 index 15b9c0217..000000000 Binary files a/TMessagesProj/src/main/res/drawable-xxhdpi/audioload1.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/audioload1_pressed.png b/TMessagesProj/src/main/res/drawable-xxhdpi/audioload1_pressed.png deleted file mode 100755 index 3465e6e61..000000000 Binary files a/TMessagesProj/src/main/res/drawable-xxhdpi/audioload1_pressed.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/audioload2.png b/TMessagesProj/src/main/res/drawable-xxhdpi/audioload2.png deleted file mode 100755 index 4699e159d..000000000 Binary files a/TMessagesProj/src/main/res/drawable-xxhdpi/audioload2.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/audioload2_pressed.png b/TMessagesProj/src/main/res/drawable-xxhdpi/audioload2_pressed.png deleted file mode 100755 index c206870fd..000000000 Binary files a/TMessagesProj/src/main/res/drawable-xxhdpi/audioload2_pressed.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/cancel_b.png b/TMessagesProj/src/main/res/drawable-xxhdpi/cancel_b.png new file mode 100755 index 000000000..2fa1d0c12 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/cancel_b.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/cancel_b_pressed.png b/TMessagesProj/src/main/res/drawable-xxhdpi/cancel_b_pressed.png new file mode 100755 index 000000000..8f3b4c08d Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/cancel_b_pressed.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/cancel_g.png b/TMessagesProj/src/main/res/drawable-xxhdpi/cancel_g.png new file mode 100755 index 000000000..34c23a830 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/cancel_g.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/cancel_g_pressed.png b/TMessagesProj/src/main/res/drawable-xxhdpi/cancel_g_pressed.png new file mode 100755 index 000000000..a8585f32d Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/cancel_g_pressed.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/download_b.png b/TMessagesProj/src/main/res/drawable-xxhdpi/download_b.png new file mode 100755 index 000000000..0199ccc17 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/download_b.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/download_b_pressed.png b/TMessagesProj/src/main/res/drawable-xxhdpi/download_b_pressed.png new file mode 100755 index 000000000..ba3e4dab4 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/download_b_pressed.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/download_g.png b/TMessagesProj/src/main/res/drawable-xxhdpi/download_g.png new file mode 100755 index 000000000..edc1b88e9 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/download_g.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/download_g_pressed.png b/TMessagesProj/src/main/res/drawable-xxhdpi/download_g_pressed.png new file mode 100755 index 000000000..4a258462a Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/download_g_pressed.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/foursquare.png b/TMessagesProj/src/main/res/drawable-xxhdpi/foursquare.png new file mode 100644 index 000000000..0104f7dc9 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/foursquare.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/header_shadow_reverse.png b/TMessagesProj/src/main/res/drawable-xxhdpi/header_shadow_reverse.png new file mode 100644 index 000000000..3d39b80a1 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/header_shadow_reverse.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/ic_ab_location.png b/TMessagesProj/src/main/res/drawable-xxhdpi/ic_ab_location.png deleted file mode 100755 index 814bc8bd7..000000000 Binary files a/TMessagesProj/src/main/res/drawable-xxhdpi/ic_ab_location.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/ic_keyboard_w.png b/TMessagesProj/src/main/res/drawable-xxhdpi/ic_keyboard_w.png new file mode 100755 index 000000000..f6bb99dc9 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/ic_keyboard_w.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/ic_smile_w.png b/TMessagesProj/src/main/res/drawable-xxhdpi/ic_smile_w.png new file mode 100755 index 000000000..fd3453ebd Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/ic_smile_w.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/ic_smiles_backspace.png b/TMessagesProj/src/main/res/drawable-xxhdpi/ic_smiles_backspace.png old mode 100644 new mode 100755 index 83d822615..8580a8b67 Binary files a/TMessagesProj/src/main/res/drawable-xxhdpi/ic_smiles_backspace.png and b/TMessagesProj/src/main/res/drawable-xxhdpi/ic_smiles_backspace.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/ic_smiles_backspace_active.png b/TMessagesProj/src/main/res/drawable-xxhdpi/ic_smiles_backspace_active.png old mode 100644 new mode 100755 index 6a1900234..eb046dea7 Binary files a/TMessagesProj/src/main/res/drawable-xxhdpi/ic_smiles_backspace_active.png and b/TMessagesProj/src/main/res/drawable-xxhdpi/ic_smiles_backspace_active.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/ic_smiles_bell.png b/TMessagesProj/src/main/res/drawable-xxhdpi/ic_smiles_bell.png old mode 100644 new mode 100755 index e155c18ef..bcd04f991 Binary files a/TMessagesProj/src/main/res/drawable-xxhdpi/ic_smiles_bell.png and b/TMessagesProj/src/main/res/drawable-xxhdpi/ic_smiles_bell.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/ic_smiles_bell_active.png b/TMessagesProj/src/main/res/drawable-xxhdpi/ic_smiles_bell_active.png old mode 100644 new mode 100755 index 34be89d5c..701047dcc Binary files a/TMessagesProj/src/main/res/drawable-xxhdpi/ic_smiles_bell_active.png and b/TMessagesProj/src/main/res/drawable-xxhdpi/ic_smiles_bell_active.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/ic_smiles_car.png b/TMessagesProj/src/main/res/drawable-xxhdpi/ic_smiles_car.png old mode 100644 new mode 100755 index 9b5299f1e..430ef9348 Binary files a/TMessagesProj/src/main/res/drawable-xxhdpi/ic_smiles_car.png and b/TMessagesProj/src/main/res/drawable-xxhdpi/ic_smiles_car.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/ic_smiles_car_active.png b/TMessagesProj/src/main/res/drawable-xxhdpi/ic_smiles_car_active.png old mode 100644 new mode 100755 index 03b0b7658..8568ffb25 Binary files a/TMessagesProj/src/main/res/drawable-xxhdpi/ic_smiles_car_active.png and b/TMessagesProj/src/main/res/drawable-xxhdpi/ic_smiles_car_active.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/ic_smiles_flower.png b/TMessagesProj/src/main/res/drawable-xxhdpi/ic_smiles_flower.png old mode 100644 new mode 100755 index 0843eb941..63ba2bcee Binary files a/TMessagesProj/src/main/res/drawable-xxhdpi/ic_smiles_flower.png and b/TMessagesProj/src/main/res/drawable-xxhdpi/ic_smiles_flower.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/ic_smiles_flower_active.png b/TMessagesProj/src/main/res/drawable-xxhdpi/ic_smiles_flower_active.png old mode 100644 new mode 100755 index ed10b1369..852144ab4 Binary files a/TMessagesProj/src/main/res/drawable-xxhdpi/ic_smiles_flower_active.png and b/TMessagesProj/src/main/res/drawable-xxhdpi/ic_smiles_flower_active.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/ic_smiles_grid.png b/TMessagesProj/src/main/res/drawable-xxhdpi/ic_smiles_grid.png old mode 100644 new mode 100755 index 73e324c41..c701d3124 Binary files a/TMessagesProj/src/main/res/drawable-xxhdpi/ic_smiles_grid.png and b/TMessagesProj/src/main/res/drawable-xxhdpi/ic_smiles_grid.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/ic_smiles_grid_active.png b/TMessagesProj/src/main/res/drawable-xxhdpi/ic_smiles_grid_active.png old mode 100644 new mode 100755 index de877ff80..dc60847bb Binary files a/TMessagesProj/src/main/res/drawable-xxhdpi/ic_smiles_grid_active.png and b/TMessagesProj/src/main/res/drawable-xxhdpi/ic_smiles_grid_active.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/ic_smiles_recent.png b/TMessagesProj/src/main/res/drawable-xxhdpi/ic_smiles_recent.png old mode 100644 new mode 100755 index 9f3778260..d08b38cd7 Binary files a/TMessagesProj/src/main/res/drawable-xxhdpi/ic_smiles_recent.png and b/TMessagesProj/src/main/res/drawable-xxhdpi/ic_smiles_recent.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/ic_smiles_recent_active.png b/TMessagesProj/src/main/res/drawable-xxhdpi/ic_smiles_recent_active.png old mode 100644 new mode 100755 index 232d52544..34fe4ff4d Binary files a/TMessagesProj/src/main/res/drawable-xxhdpi/ic_smiles_recent_active.png and b/TMessagesProj/src/main/res/drawable-xxhdpi/ic_smiles_recent_active.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/ic_smiles_smile.png b/TMessagesProj/src/main/res/drawable-xxhdpi/ic_smiles_smile.png old mode 100644 new mode 100755 index c473c122d..0e8515a12 Binary files a/TMessagesProj/src/main/res/drawable-xxhdpi/ic_smiles_smile.png and b/TMessagesProj/src/main/res/drawable-xxhdpi/ic_smiles_smile.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/ic_smiles_smile_active.png b/TMessagesProj/src/main/res/drawable-xxhdpi/ic_smiles_smile_active.png old mode 100644 new mode 100755 index 1981d0c77..36dff7616 Binary files a/TMessagesProj/src/main/res/drawable-xxhdpi/ic_smiles_smile_active.png and b/TMessagesProj/src/main/res/drawable-xxhdpi/ic_smiles_smile_active.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/ic_smiles_sticker.png b/TMessagesProj/src/main/res/drawable-xxhdpi/ic_smiles_sticker.png new file mode 100755 index 000000000..91e9a0746 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/ic_smiles_sticker.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/ic_smiles_sticker_active.png b/TMessagesProj/src/main/res/drawable-xxhdpi/ic_smiles_sticker_active.png new file mode 100755 index 000000000..c0b2af66c Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/ic_smiles_sticker_active.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/location_b.9.png b/TMessagesProj/src/main/res/drawable-xxhdpi/location_b.9.png new file mode 100644 index 000000000..01ea9f4dd Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/location_b.9.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/location_g.9.png b/TMessagesProj/src/main/res/drawable-xxhdpi/location_g.9.png new file mode 100644 index 000000000..7343d9636 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/location_g.9.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/myloc_on.png b/TMessagesProj/src/main/res/drawable-xxhdpi/myloc_on.png new file mode 100644 index 000000000..1b40c0f1b Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/myloc_on.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/navigate.png b/TMessagesProj/src/main/res/drawable-xxhdpi/navigate.png new file mode 100644 index 000000000..ffe55f7fe Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/navigate.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/pause1.png b/TMessagesProj/src/main/res/drawable-xxhdpi/pause1.png deleted file mode 100755 index 9293c448b..000000000 Binary files a/TMessagesProj/src/main/res/drawable-xxhdpi/pause1.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/pause1_pressed.png b/TMessagesProj/src/main/res/drawable-xxhdpi/pause1_pressed.png deleted file mode 100755 index e86f91f93..000000000 Binary files a/TMessagesProj/src/main/res/drawable-xxhdpi/pause1_pressed.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/pause2.png b/TMessagesProj/src/main/res/drawable-xxhdpi/pause2.png deleted file mode 100755 index d18cf420d..000000000 Binary files a/TMessagesProj/src/main/res/drawable-xxhdpi/pause2.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/pause2_pressed.png b/TMessagesProj/src/main/res/drawable-xxhdpi/pause2_pressed.png deleted file mode 100755 index f7259a314..000000000 Binary files a/TMessagesProj/src/main/res/drawable-xxhdpi/pause2_pressed.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/pause_b.png b/TMessagesProj/src/main/res/drawable-xxhdpi/pause_b.png new file mode 100755 index 000000000..fa34156e3 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/pause_b.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/pause_b_pressed.png b/TMessagesProj/src/main/res/drawable-xxhdpi/pause_b_pressed.png new file mode 100755 index 000000000..14417454f Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/pause_b_pressed.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/pause_g.png b/TMessagesProj/src/main/res/drawable-xxhdpi/pause_g.png new file mode 100755 index 000000000..58230127c Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/pause_g.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/pause_g_pressed.png b/TMessagesProj/src/main/res/drawable-xxhdpi/pause_g_pressed.png new file mode 100755 index 000000000..6afeea45e Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/pause_g_pressed.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/pause_w.png b/TMessagesProj/src/main/res/drawable-xxhdpi/pause_w.png new file mode 100755 index 000000000..31bdd7380 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/pause_w.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/pause_w2.png b/TMessagesProj/src/main/res/drawable-xxhdpi/pause_w2.png new file mode 100755 index 000000000..d4be9ec78 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/pause_w2.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/pause_w2_pressed.png b/TMessagesProj/src/main/res/drawable-xxhdpi/pause_w2_pressed.png new file mode 100755 index 000000000..94ce677c2 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/pause_w2_pressed.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/pause_w_pressed.png b/TMessagesProj/src/main/res/drawable-xxhdpi/pause_w_pressed.png new file mode 100755 index 000000000..0f81862fe Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/pause_w_pressed.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/photo_crop.png b/TMessagesProj/src/main/res/drawable-xxhdpi/photo_crop.png old mode 100644 new mode 100755 index 460fdb38d..1276ad189 Binary files a/TMessagesProj/src/main/res/drawable-xxhdpi/photo_crop.png and b/TMessagesProj/src/main/res/drawable-xxhdpi/photo_crop.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/photo_edit.png b/TMessagesProj/src/main/res/drawable-xxhdpi/photo_edit.png deleted file mode 100644 index 6df74a5b3..000000000 Binary files a/TMessagesProj/src/main/res/drawable-xxhdpi/photo_edit.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/photo_text.png b/TMessagesProj/src/main/res/drawable-xxhdpi/photo_text.png new file mode 100755 index 000000000..41c6315d7 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/photo_text.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/photo_text2.png b/TMessagesProj/src/main/res/drawable-xxhdpi/photo_text2.png new file mode 100755 index 000000000..461b2a3e2 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/photo_text2.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/photo_tools.png b/TMessagesProj/src/main/res/drawable-xxhdpi/photo_tools.png new file mode 100755 index 000000000..ab1bb0e95 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/photo_tools.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/pin.png b/TMessagesProj/src/main/res/drawable-xxhdpi/pin.png new file mode 100644 index 000000000..8580d9a2f Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/pin.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/place_x.png b/TMessagesProj/src/main/res/drawable-xxhdpi/place_x.png new file mode 100644 index 000000000..aa7aebba0 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/place_x.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/play1.png b/TMessagesProj/src/main/res/drawable-xxhdpi/play1.png deleted file mode 100755 index 1c0924fdf..000000000 Binary files a/TMessagesProj/src/main/res/drawable-xxhdpi/play1.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/play1_pressed.png b/TMessagesProj/src/main/res/drawable-xxhdpi/play1_pressed.png deleted file mode 100755 index 51642f673..000000000 Binary files a/TMessagesProj/src/main/res/drawable-xxhdpi/play1_pressed.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/play2.png b/TMessagesProj/src/main/res/drawable-xxhdpi/play2.png deleted file mode 100755 index d34c30371..000000000 Binary files a/TMessagesProj/src/main/res/drawable-xxhdpi/play2.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/play2_pressed.png b/TMessagesProj/src/main/res/drawable-xxhdpi/play2_pressed.png deleted file mode 100755 index cff0cf086..000000000 Binary files a/TMessagesProj/src/main/res/drawable-xxhdpi/play2_pressed.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/play_w.png b/TMessagesProj/src/main/res/drawable-xxhdpi/play_w.png new file mode 100755 index 000000000..be2cb867f Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/play_w.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/play_w2.png b/TMessagesProj/src/main/res/drawable-xxhdpi/play_w2.png new file mode 100755 index 000000000..263f27db6 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/play_w2.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/play_w2_pressed.png b/TMessagesProj/src/main/res/drawable-xxhdpi/play_w2_pressed.png new file mode 100755 index 000000000..96e0e1276 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/play_w2_pressed.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/play_w_pressed.png b/TMessagesProj/src/main/res/drawable-xxhdpi/play_w_pressed.png new file mode 100755 index 000000000..48aa6b658 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/play_w_pressed.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/round_grey.png b/TMessagesProj/src/main/res/drawable-xxhdpi/round_grey.png new file mode 100755 index 000000000..7b037c623 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/round_grey.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/tune.png b/TMessagesProj/src/main/res/drawable-xxhdpi/tune.png deleted file mode 100755 index eda3e5a32..000000000 Binary files a/TMessagesProj/src/main/res/drawable-xxhdpi/tune.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable/background_tab.xml b/TMessagesProj/src/main/res/drawable/background_tab.xml deleted file mode 100755 index 885cf036a..000000000 --- a/TMessagesProj/src/main/res/drawable/background_tab.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/TMessagesProj/src/main/res/drawable/ic_emoji_backspace.xml b/TMessagesProj/src/main/res/drawable/ic_emoji_backspace.xml index 0346dc27c..41ea5b772 100644 --- a/TMessagesProj/src/main/res/drawable/ic_emoji_backspace.xml +++ b/TMessagesProj/src/main/res/drawable/ic_emoji_backspace.xml @@ -1,10 +1,11 @@ - - - - + + + + + + diff --git a/TMessagesProj/src/main/res/drawable/ic_emoji_bell.xml b/TMessagesProj/src/main/res/drawable/ic_emoji_bell.xml index 5c3d57160..17b59fd60 100644 --- a/TMessagesProj/src/main/res/drawable/ic_emoji_bell.xml +++ b/TMessagesProj/src/main/res/drawable/ic_emoji_bell.xml @@ -2,15 +2,12 @@ + android:exitFadeDuration="300"> + android:drawable="@drawable/ic_smiles_bell_active"> + android:drawable="@drawable/ic_smiles_bell"> diff --git a/TMessagesProj/src/main/res/drawable/ic_emoji_sticker.xml b/TMessagesProj/src/main/res/drawable/ic_emoji_sticker.xml new file mode 100644 index 000000000..2cb1ecbef --- /dev/null +++ b/TMessagesProj/src/main/res/drawable/ic_emoji_sticker.xml @@ -0,0 +1,9 @@ + + + + + + diff --git a/TMessagesProj/src/main/res/layout-ar/location_view_layout.xml b/TMessagesProj/src/main/res/layout-ar/location_view_layout.xml deleted file mode 100644 index a39c6a5e5..000000000 --- a/TMessagesProj/src/main/res/layout-ar/location_view_layout.xml +++ /dev/null @@ -1,58 +0,0 @@ - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/TMessagesProj/src/main/res/layout/document_select_layout.xml b/TMessagesProj/src/main/res/layout/document_select_layout.xml index 9576e5914..9c9329f61 100644 --- a/TMessagesProj/src/main/res/layout/document_select_layout.xml +++ b/TMessagesProj/src/main/res/layout/document_select_layout.xml @@ -4,8 +4,8 @@ android:layout_height="match_parent"> - + android:layout_width="match_parent" + android:layout_height="match_parent"> - - - - - - - - \ No newline at end of file diff --git a/TMessagesProj/src/main/res/layout/location_view_layout.xml b/TMessagesProj/src/main/res/layout/location_view_layout.xml deleted file mode 100644 index d89e26ebd..000000000 --- a/TMessagesProj/src/main/res/layout/location_view_layout.xml +++ /dev/null @@ -1,56 +0,0 @@ - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/TMessagesProj/src/main/res/layout/popup_count_layout.xml b/TMessagesProj/src/main/res/layout/popup_count_layout.xml index 1593006af..6011dc4cb 100644 --- a/TMessagesProj/src/main/res/layout/popup_count_layout.xml +++ b/TMessagesProj/src/main/res/layout/popup_count_layout.xml @@ -5,7 +5,7 @@ \ No newline at end of file diff --git a/TMessagesProj/src/main/res/layout/settings_wallpapers_layout.xml b/TMessagesProj/src/main/res/layout/settings_wallpapers_layout.xml index a49b123c4..4f83f96d2 100644 --- a/TMessagesProj/src/main/res/layout/settings_wallpapers_layout.xml +++ b/TMessagesProj/src/main/res/layout/settings_wallpapers_layout.xml @@ -1,11 +1,11 @@ + android:layout_width="match_parent" + android:layout_height="match_parent"> @@ -19,7 +19,7 @@ android:layout_marginBottom="52dp"/> رمز التفعيل تم إرسال رسالة قصيرة تحتوي على رمز التفعيل الخاص بك - %1$d:%2$02d سنتصل بك خلال + سنتصل بك خلال %1$d:%2$02d جاري الاتصال بك ... رمز التفعيل الرقم خاطئ؟ @@ -89,6 +89,10 @@ %1$s يقوم بإرسال صورة... %1$s يقوم بإرسال مقطع مرئي... %1$s يقوم بإرسال ملف... + جاري تسجيل المقطع الصوتي... + جاري إرسال الصورة... + جاري إرسال المقطع المرئي... + جاري إرسال الملف... هل يوجد لديك سؤال\nحول تيليجرام؟ التقط صورة صورة @@ -157,6 +161,7 @@ %1$s قام بالتسجيل في تيليجرام! %1$s,\nتم تسجيل الدخول لحسابك من جهاز جديد يوم %2$s\n\nالجهاز: %3$s\nالموقع: %4$s\n\nإذا لم يكن أنت من سجل الدخول، يمكنك الذهاب للإعدادات ثم تسجيل الخروج من كافة الأجهزة الأخرى. كما يمكنك تفعيل التحقق بخطوتين إذا رغبت بذلك عن طريق إعدادات الخصوصية.\n\nشكرًا,\nفريق عمل تيليجرام %1$s قام بتغيير صورته الشخصية + %1$s قام بالدخول للمجموعة %2$s باستخدام رابط الدعوة الرد الرد على %1$s الرد على %1$s @@ -182,6 +187,19 @@ أدخل اسم للمجموعة اسم المجموعة %1$d/%2$d عضو + هل ترغب في الدخول للمجموعة \'%1$s\'؟ + المعذرة، هذه المجموعة ممتلئة. + المعذرة، هذه المجموعة غير موجودة. + تم نسخ الرابط إلى الحافظة + قم بدعوة للمجموعة باستخدام رابط + رابط دعوة + هل أنت متأكد من رغبتك في إلغاء رابط الدعوة؟ إذا قمت بذلك، لن يستطيع أحد من استخدامه للدخول للمجموعة. + رابط الدعوة السابق لا يعمل الآن. سيتم إنشاء رابط جديد. + إلغاء + إلغاء الرابط + نسخ الرابط + شارك الرابط + أي شخص يمتلك تيليجرام على جهازه سيسطيع الدخول لمجموعتك باستخدام الرابط التالي. عدد الوسائط المشتركة الإعدادات @@ -311,6 +329,13 @@ إيقاف الأصوات داخل المحادثات افتراضي + إشعارات ذكية + تعطيل + أعلى صوت %1$s خلال %2$s + أعلى صوت + الأوقات + خلال + دقائق الأجهزة المسجّل دخول منها الجهاز الحالي @@ -350,15 +375,20 @@ هجين متر يبعد كيلومتر يبعد - أرسل موقعك - مشاركة الموقع + أرسل مكانك الحالي + أرسل المكان المختار + المكان + دقيق لدرجة %1$s + أو اختر مكان عرض كافة الوسائط حفظ في الجهاز %1$d من %2$d الألبوم جميع الصور + كافة المقاطع المرئية لا توجد صور حتى الآن + لا يوجد مقاطع مرئية بعد فضلًا، قم بتنزيل الوسائط أولًا لا توجد صور حديثة لا يوجد صور متحركة حديثة @@ -388,6 +418,11 @@ تجاهل التغييرات؟ هل ترغب في مسح سجل البحث؟ مسح + صور + مقطع مرئي + أضف تعليق... + تعليق الصورة + تعليق المقطع المرئي التحقق بخطوتين تعيين كلمة مرور إضافية @@ -497,6 +532,8 @@ موافق قطع + لقد قمت بالدخول للمجموعة باستخدام رابط الدعوة + un1 قام بالدخول للمجموعة باستخدام رابط الدعوة un1 أزال un2 غادر المجموعة العضو un1 un1 قام بإضافة un2 @@ -664,6 +701,18 @@ %1$d مستخدمون %1$d مستخدم %1$d مستخدم + %1$d مرات + %1$d مرة + %1$d مرة + %1$d مرة + %1$d مرة + %1$d مرة + %1$d أمتار + %1$d متر + %1$d متر + %1$d متر + %1$d متر + %1$d متر %1$d رسالة معاد توجيهها الرسالة المعاد توجيهها @@ -730,4 +779,7 @@ HH:mm h:mm a %1$s الساعة %2$s + + تم تحديث تيليجرام نسخة الأندرويد. الجديد في النسخة ٢.٨:\n\n- روابط الدعوة للمجموعات\n- الإشعارات الذكية\n- خيار الملصقات في قائمة الإموجي\n- التعليقات للصور\n- الأماكن في خدمة المواقع\n- وضعية \"مسموع\" في الرسائل الصوتية\n- شرح متطور لحالة الطرف الآخر: يرسل صورة، يسجل مقطع صوتي، إلخ\n- تطوير التحريك والحركات داخل التطبيق ليكون أسهل وأسرع + 516 \ No newline at end of file diff --git a/TMessagesProj/src/main/res/values-de/strings.xml b/TMessagesProj/src/main/res/values-de/strings.xml index fd4c0035a..37451fdb4 100644 --- a/TMessagesProj/src/main/res/values-de/strings.xml +++ b/TMessagesProj/src/main/res/values-de/strings.xml @@ -40,7 +40,7 @@ Aktualisiere… Neuer Geheimer Chat Warte, bis %s online geht… - Geheimen Chat beendet + Geheimer Chat beendet Tausche Schlüssel aus… %s ist deinem geheimen Chat beigetreten. Du bist dem geheimen Chat beigetreten. @@ -89,6 +89,10 @@ %1$s schickt Bild... %1$s schickt Video... %1$s schickt Datei... + nimmt etwas auf... + schickt Bild... + schickt Video... + schickt Datei... Hast du eine Frage\nzu Telegram? Foto aufnehmen Galerie @@ -101,7 +105,7 @@ Keine aktuellen Nachricht Nachricht - Meine Telefonnummer teilen + Meine Nummer teilen Zu Kontakten hinzufügen %s hat dich zu einem\nEnde-zu-Ende verschlüsselten\nGeheimen Chat eingeladen. Du hast %s zu einem\nEnde-zu-Ende verschlüsselten\nGeheimen Chat eingeladen. @@ -155,8 +159,9 @@ %1$s hat dich aus der Gruppe %2$s entfernt %1$s hat die Gruppe %2$s verlassen %1$s benutzt jetzt Telegram! - %1$s,\nWir haben eine Anmeldung von einem neuen Gerät am %2$s festgestellt.\n\nGerät: %3$s\nStandort: %4$s\n\nWenn du das nicht selbst gewesen bist, melde alle anderen Sitzungen in den Telegram Einstellungen unverzüglich ab.\n\nBeachte unsere zweistufige Bestätigung, welche du in den Telegram Einstellungen unter Privatsphäre und Sicherheit optional aktivieren kannst.\n\nDein Telegram Team + %1$s,\nWir haben eine Anmeldung von einem neuen Gerät am %2$s festgestellt.\n\nGerät: %3$s\nStandort: %4$s\n\nWenn du das nicht selbst gewesen bist, melde die entsprechende Sitzung in den Telegram Einstellungen unter Privatsphäre und Sicherheit - Sitzungen unverzüglich ab.\n\nKennst du schon unsere zweistufige Bestätigung? Diese kannst du in den Telegram Einstellungen unter Privatsphäre und Sicherheit optional aktivieren.\n\nDein Telegram Team %1$s hat das Profilbild geändert + %1$s ist per Einladungslink der Gruppe %2$s beigetreten Antworten %1$s antworten %1$s antworten @@ -182,6 +187,19 @@ Gruppenname Gruppenname %1$d/%2$d Mitglieder + Möchtest du der Gruppe \"%1$s\" beitreten? + Leider ist diese Gruppe schon voll. + Leider gibt es diese Gruppe nicht. + Link in die Zwischenablage kopiert + Per Link zur Gruppe einladen + Einladungslink + Bist du sicher, dass du diesen Link widerrufen willst? Dadurch kann ihn niemand mehr nutzen. + Der vorige Link ist nun inaktiv. Ein neuer Einladungslink wurde gerade erstellt. + Widerrufen + Link widerrufen + Link kopieren + Link teilen + Jeder, der Telegram installiert hat, kann anhand dieses Links in deine Gruppe. Geteilte Medien Einstellungen @@ -218,7 +236,7 @@ Ein Benutzername benötigt mindestens 5 Zeichen. Ein Benutzername darf maximal 32 Zeichen haben. Benutzernamen dürfen leider nicht mit einer Zahl anfangen. - Wähle einen für jeden sichtbaren Benutzernamen, wenn du von anderen bei ]]>Telegram]]> gefunden werden willst — ohne, dass sie deine Nummer kennen müssen.
]]>Erlaubt sind ]]>a-z]]>, ]]>0-9]]> und Unterstriche. Die Mindestlänge beträgt ]]>5]]> Zeichen.
+ Wähle einen öffentlichen Benutzernamen, wenn du von anderen bei ]]>Telegram]]> gefunden werden willst — ohne, dass sie deine Nummer kennen müssen.
]]>Erlaubt sind ]]>a-z]]>, ]]>0-9]]> und Unterstriche. Die Mindestlänge beträgt ]]>5]]> Zeichen.
Prüfe Benutzername... %1$s ist verfügbar. Keiner @@ -311,9 +329,16 @@ Aus In-Chat Töne Standard + Intelligente Benachrichtigungen + Deaktiviert + Höchstens %1$s innerhalb %2$s + Höchstens + Mal + innerhalb von + Minuten Sitzungen - Aktuelle Sitzung + Aktuelles Gerät Keine anderen Geräte Du kannst dich von jedem Handy, Tablet und Computer bei Telegram mit derselben Telefonnummer anmelden. Alles wird immer sofort synchronisiert. Andere Geräte @@ -350,15 +375,20 @@ Hybrid m entfernt km entfernt - Standort senden - Teile Standort + Meinen Standort senden + Diesen Standort senden + Standort + Auf %1$s genau + Oder wähle einen Ort Zeige alle Medien In der Galerie speichern %1$d von %2$d Galerie Alle Fotos + Alle Videos Noch keine Fotos + Noch keine Videos Medien bitte zuerst herunterladen Suchverlauf Suchverlauf @@ -388,6 +418,11 @@ Änderungen verwerfen? Suchverlauf löschen? Löschen + Bilder + Video + Beschriftung... + Bildbeschriftung + Videobeschriftung Zweistufige Bestätigung Zusätzliches Kennwort festlegen @@ -497,6 +532,8 @@ OK SCHNEIDEN + Du bist der Gruppe per Link beigetreten + un1 ist der Gruppe per Link beigetreten un1 hat un2 aus der Gruppe entfernt un1 hat die Gruppe verlassen un1 hat un2 hinzugefügt @@ -664,6 +701,18 @@ %1$d Nutzer %1$d Nutzer %1$d Nutzer + %1$d Mal + %1$d Mal + %1$d Mal + %1$d Mal + %1$d Mal + %1$d Mal + %1$d Meter + %1$d Meter + %1$d Meter + %1$d Meter + %1$d Meter + %1$d Meter %1$d angehängten Nachrichten Angehängte Nachricht @@ -730,4 +779,7 @@ HH:mm h:mm a %1$s um %2$s + + Telegram für Android wurde aktualisiert. Neu in 2.8:\n\n- Einladungslinks für Gruppenchats\n- Intelligente Benachrichtigungen\n- Sticker Tab im Emojimenü\n- Beschriftungen für Bilder\n- Orte in der Umgebung im Standortmenü\n- \'Angehört\'-Status Sprachnachrichten\n- Erweiterterte \'schreibt gerade\' Status: schickt Bild, nimmt Audio auf, etc.\n- Verbesserter Bildlauf und flüssige Animationen + 516 \ No newline at end of file diff --git a/TMessagesProj/src/main/res/values-es/strings.xml b/TMessagesProj/src/main/res/values-es/strings.xml index b2b2f7b37..cc33630f4 100644 --- a/TMessagesProj/src/main/res/values-es/strings.xml +++ b/TMessagesProj/src/main/res/values-es/strings.xml @@ -89,6 +89,10 @@ %1$s está enviando una foto... %1$s está enviando un vídeo... %1$s está enviando un archivo... + grabando audio... + enviando foto... + enviando vídeo... + enviando archivo... ¿Tienes preguntas\nsobre Telegram? Hacer foto Galería @@ -157,6 +161,7 @@ ¡%1$s se unió a Telegram! %1$s,\nDetectamos un inicio de sesión en tu cuenta desde un nuevo dispositivo, el %2$s\n\nDispositivo: %3$s\nUbicación: %4$s\n\nSi no eras tú, puedes ir a Ajustes - Privacidad y seguridad - Cerrar todas las otras sesiones.\n\nSi crees que alguien ha iniciado la sesión sin tu consentimiento, puedes activar la verificación en dos pasos, en los ajustes de privacidad y seguridad.\n\nAtentamente,\nEl equipo de Telegram %1$s actualizó su foto de perfil + %1$s se unió al grupo %2$s con un enlace de invitación Responder Responder a %1$s Responder a %1$s @@ -182,8 +187,21 @@ Nombre del grupo Nombre del grupo %1$d/%2$d miembros + ¿Quieres unirte al grupo \'%1$s\'? + Lo sentimos. Este grupo está lleno. + Lo sentimos. Este grupo no existe. + Enlace copiado al portapapeles + Invitar al grupo con un enlace + Enlace de invitación + ¿Quieres anular este enlace? Una vez hecho, nadie podrá usarlo para unirse al grupo. + El enlace de invitación anterior está inactivo. Ha sido creado uno nuevo. + Anular + Anular enlace + Copiar enlace + Compartir enlace + Cualquiera que tenga Telegram instalado podrá unirse a tu grupo siguiendo este enlace. - Multimedia compartida + Todos los archivos Ajustes Añadir miembro Eliminar y dejar el grupo @@ -211,15 +229,15 @@ Información Teléfono - Apodo - Tu apodo - Lo siento, este apodo ya está ocupado. - Lo siento, este apodo es inválido. - Un apodo debe tener al menos 5 caracteres. - El apodo no debe exceder los 32 caracteres. - Lo siento, un apodo no puede comenzar con un número. - Puedes elegir un apodo en ]]>Telegram]]>. Si lo haces, otras personas te podrán encontrar por ese apodo y contactarte sin saber tu número de teléfono.
]]>Puedes usar ]]>a–z]]>, ]]>0–9]]> y guiones bajos. La longitud mínima es de ]]>5]]> caracteres.
- Verificando apodo... + Alias + Tu alias + Lo siento, este alias ya está ocupado. + Lo siento, este alias es inválido. + Un alias debe tener al menos 5 caracteres. + El alias no debe exceder los 32 caracteres. + Lo siento, un alias no puede comenzar con un número. + Puedes elegir un alias en ]]>Telegram]]>. Si lo haces, otras personas te podrán encontrar por ese alias y contactarte sin saber tu número de teléfono.
]]>Puedes usar ]]>a–z]]>, ]]>0–9]]> y guiones bajos. La longitud mínima es de ]]>5]]> caracteres.
+ Verificando alias... %1$s está disponible. Ninguno Ocurrió un error. @@ -311,6 +329,13 @@ Apagado Sonidos en el chat Por defecto + Notificaciones inteligentes + Desactivadas + Sonar como máximo %1$s en %2$s + Sonar como máximo + veces + en + minutos Sesiones activas Sesión actual @@ -350,15 +375,20 @@ Híbrido m de distancia km de distancia - Enviar ubicación + Enviar tu ubicación actual + Enviar la ubicación seleccionada Ubicación + Exacto a %1$s + O ELIGE UN LUGAR Ir a Multimedia Guardar en galería %1$d de %2$d Galería Todas las fotos + Todos los vídeos Aún sin fotos + Sin vídeos aún Por favor, primero descarga la multimedia No hay fotos recientes No hay GIF recientes @@ -388,6 +418,11 @@ ¿Descartar cambios? ¿Quieres borrar el historial de búsqueda? Borrar + Fotos + Vídeo + Añadir un comentario... + Comentario de foto + Comentario de vídeo Verificación en dos pasos Poner contraseña adicional @@ -497,6 +532,8 @@ OK RECORTAR + Te uniste al grupo con un enlace de invitación + un1 se unió al grupo con un enlace de invitación un1 expulsó a un2 un1 dejó el grupo un1 añadió a un2 @@ -664,6 +701,18 @@ %1$d usuarios %1$d usuarios %1$d usuarios + %1$d veces + %1$d vez + %1$d veces + %1$d veces + %1$d veces + %1$d veces + %1$d metros + %1$d metro + %1$d metros + %1$d metros + %1$d metros + %1$d metros %1$d mensajes adjuntos Mensaje adjunto @@ -730,4 +779,7 @@ HH:mm h:mm a %1$s a las %2$s + + Telegram para Android ha sido actualizada. Novedades en la versión 2.8:\n\n- Enlaces de invitación para chats grupales\n- Notificaciones inteligentes\n- Pestaña de stickers en el menú de emojis\n- Comentarios en las fotos\n- Lugares y direcciones en las ubicaciones\n- Estado \'escuchado\' para los mensajes de voz\n- Estado \'escribiendo\' avanzado: enviando foto, grabando audio, etc.\n- Deslizamiento y animaciones mejoradas para una experiencia fluiiiiiiiiiiiiiiiiiida + 515 \ No newline at end of file diff --git a/TMessagesProj/src/main/res/values-it/strings.xml b/TMessagesProj/src/main/res/values-it/strings.xml index 8d827737c..0b0cd0f1f 100644 --- a/TMessagesProj/src/main/res/values-it/strings.xml +++ b/TMessagesProj/src/main/res/values-it/strings.xml @@ -89,6 +89,10 @@ %1$s sta inviando una foto... %1$s sta inviando un video... %1$s sta inviando un file... + registrando un audio... + inviando una foto... + inviando un video... + inviando un file... Hai una domanda\nsu Telegram? Scatta una foto Galleria @@ -157,6 +161,7 @@ %1$s ha iniziato a usare Telegram! %1$s,\nAbbiamo rilevato un accesso al tuo account da un nuovo dispositivo il %2$s\n\nDispositivo: %3$s\nPosizione: %4$s\n\nSe non sei stato tu, puoi andare su Impostazioni - Privacy e Sicurezza - Sessioni - Termina tutte le sessioni.\n\nSe pensi che qualcuno si sia collegato al tuo account contro il tuo volere, ti raccomandiamo di attivare la verifica in due passaggi nelle impostazioni di Privacy e Sicurezza.\n\nGrazie,\nil team di Telegram %1$s ha aggiornato la foto del profilo + %1$s si è unito al gruppo %2$s tramite link di invito Rispondi Rispondi a %1$s Rispondi a %1$s @@ -182,6 +187,19 @@ Immetti il nome del gruppo Nome gruppo %1$d/%2$d membri + Vuoi unirti al gruppo \'%1$s\'? + Ci spiace, questo gruppo è già pieno. + Ci spiace, sembra che questo gruppo non esista. + Link copiato negli appunti + Invita nel gruppo tramite link + Link di invito + Sei sicuro di voler revocare questo link? Una volta fatto, nessuno potrà unirsi al gruppo utilizzandolo. + Il precedente link di invito è inattivo. Ne è appena stato creato uno nuovo. + Revoca + Revoca link + Copia link + Condividi link + Chiunque abbia Telegram installato, sarà in grado di aggiungersi al tuo gruppo aprendo il link. Media condivisi Impostazioni @@ -279,7 +297,7 @@ Solo con schermo acceso Solo con schermo spento Mostra sempre i popup - Contatore Badge + Contatore badge Breve Lunga Predefinito di sistema @@ -297,7 +315,7 @@ Alta Massima Mai - Ripeti Notifiche + Ripeti notifiche Puoi cambiare il tuo numero di telefono qui. Il tuo account e tutti i tuoi dati cloud — messaggi, file, contatti, etc. saranno trasferiti sul nuovo numero.\n\nImportante:]]> a tutti i tuoi contatti di Telegram verrà aggiunto il tuo nuovo numero]]> ai contatti, purché abbiano il tuo vecchio numero e tu non li abbia bloccati su Telegram. Tutti i tuoi contatti Telegram avranno il tuo nuovo numero tra i loro contatti, purché abbiano il tuo vecchio numero e tu non li abbia bloccati su Telegram. CAMBIA NUMERO @@ -311,6 +329,13 @@ No Suoni in-chat Predefinito + Notifiche intelligenti + Disabilitate + Suona al massimo %1$s in %2$s + Suona al massimo + volte + in + minuti Sessioni attive Sessione corrente @@ -350,15 +375,20 @@ Ibrido m di distanza km di distanza - Invia posizione - Condividi posizione + Invia la tua posizione attuale + Invia la posizione selezionata + Posizione + Precisione di %1$s + O SELEZIONA UN LUOGO Mostra tutti i file media Salva nella galleria %1$d di %2$d Galleria Tutte le foto + Tutti i video Ancora nessuna foto + Nessun video Scarica prima il file Nessuna foto recente Nessuna GIF recente @@ -388,6 +418,11 @@ Annullare le modifiche? Cancellare la cronologia di ricerca? Pulisci + Foto + Video + Aggiungi una didascalia... + Didascalia foto + Didascalia video Verifica in due passaggi Imposta password aggiuntiva @@ -439,16 +474,16 @@ Hai attivato la verifica in due passaggi.\nAvrai bisogno della password che hai impostato per accedere al tuo account Telegram. La tua e-mail di recupero %1$s non è ancora attiva e attende la conferma. - Privacy e Sicurezza + Privacy e sicurezza Privacy - Ultimo Accesso + Ultimo accesso Tutti - I miei Contatti + I miei contatti Nessuno Tutti (-%1$d) - I miei Contatti (+%1$d) - I miei Contatti (-%1$d) - I miei Contatti (-%1$d, +%2$d) + I miei contatti (+%1$d) + I miei contatti (-%1$d) + I miei contatti (-%1$d, +%2$d) Nessuno (+%1$d) Sicurezza Elimina il mio account @@ -497,6 +532,8 @@ OK RITAGLIA + Ti sei unito al gruppo tramite link di invito + un1 si è unito al gruppo tramite link di invito un1 ha espulso un2 un1 ha lasciato il gruppo un1 ha aggiunto un2 @@ -664,6 +701,18 @@ %1$d utenti %1$d utenti %1$d utenti + %1$d volte + %1$d volta + %1$d volte + %1$d volte + %1$d volte + %1$d volte + %1$d metri + %1$d metro + %1$d metri + %1$d metri + %1$d metri + %1$d metri %1$d messaggi inoltrati Messaggio inoltrato @@ -730,4 +779,7 @@ HH:mm h:mm a %1$s alle %2$s + + Telegram per Android si è aggiornato. Nuovo nella versione 2.8:\n\n- Invita nei gruppi tramite link\n- Notifiche intelligenti\n- Pannello degli sticker nella lista emoji\n- Luoghi e direzioni nelle posizioni\n- Stato \'ascoltato\' per i messaggi vocali\n- Stato di scrittura \'avanzato\': inviando una foto, registrando un audio, etc\n- Animazioni e scorrimento migliorati per un\'esperienza rapidissimaaaa + 516 \ No newline at end of file diff --git a/TMessagesProj/src/main/res/values-ko/strings.xml b/TMessagesProj/src/main/res/values-ko/strings.xml index e4c80afb8..8429a7221 100644 --- a/TMessagesProj/src/main/res/values-ko/strings.xml +++ b/TMessagesProj/src/main/res/values-ko/strings.xml @@ -89,6 +89,10 @@ %1$s님이 사진 보내는 중... %1$s님이 동영상 보내는 중... %1$s님이 파일 보내는 중... + 음성메시지 녹음 중... + 사진 전송 중.. + 동영상 전송 중.. + 파일 전송 중... 텔레그램에 관해\n궁금한 사항이 있나요? 사진 촬영 앨범 @@ -157,6 +161,7 @@ %1$s님이 텔레그램에 가입했습니다! %1$s님,\n%2$s에 새 기기에서 회원님의 계정 로그인이 감지되었습니다. \n\n기기: %3$s\n위치: %4$s\n\n본인의 접속이 아니라면 \'설정\' 창에서 \'모든 세션 종료\' 기능을 실행하세요.\n\n만약 강제접속 의심이 되신다면 2단계 인증을 설정 - 개인정보 및 보안에서 설정할 수 있습니다.\n\n감사합니다.\n텔레그램 팀 %1$s님이 프로필 사진을 변경했습니다 + 초대링크를 타고 %1$s님께서 %2$s 그룹에 참여하셨습니다. 답장 %1$s 그룹에 답장하기 %1$s님에게 답장하기 @@ -182,6 +187,19 @@ 그룹 이름 입력 그룹 이름 대화상대 %1$d/%2$d + \'%1$s\' 그룹에 참여하시겠습니까? + 죄송합니다, 그룹방의 인원이 최대치입니다. + 죄송합니다, 그룹방이 더이상 존재하지 않습니다. + 클립보드로 링크가 복사되었습니다. + 링크를 통하여 그룹방에 초대하기 + 초대링크 + 초대링크를 폐지하시겠습니까? 진행하실 경우 해당 링크로 그룹방에 참여할 수 없게 됩니다. + 기존 초대링크는 비활성화 되었습니다. 새로운 링크가 생성되었습니다. + 폐지하기 + 링크 폐지 + 링크 복사 + 링크 공유 + 텔레그램이 설치된 분들은 링크를 타고 그룹방에 참여가 가능합니다. 공유한 미디어 설정 @@ -311,6 +329,13 @@ 채팅중 소리 설정 기본값 + 스마트 알림 + 비활성화됨 + 최대 %1$s번, %2$s번 이내 알림 + 알림 최대치 + + 이내 + 활성화된 세션 현재 세션 @@ -350,15 +375,20 @@ 혼합 m 떨어짐 km 떨어짐 - 위치 보내기 - 위치 공유 + 현재 위치 전송 + 선택한 위치 전송 + 위치 + %1$s 반경 내 정확함 + 혹은 위치를 선택 모든 미디어 보기 앨범에 저장 %1$d / %2$d 앨범 모든 사진 + 모든 동영상 사진이 없습니다. + 동영상이 아직 없음 사진/동영상을 먼저 다운로드하세요 최근 사진 없음 최근에 검색한 GIF @@ -388,6 +418,11 @@ 변경을 취소하시겠습니까? 검색기록을 지우시겠습니까? 지우기 + 사진 + 동영상 + 설명 추가... + 사진 설명 + 동영상 설명 2단계 인증 개별 비밀번호 설정 @@ -497,6 +532,8 @@ 확인 자르기 + 초대링크를 타고 그룹에 참여하였습니다. + 초대링크를 타고 그룹에 un1님이 참여하였습니다. un1님이 un2님을 추방했습니다 un1님이 퇴장했습니다 un1님이 un2님을 초대했습니다 @@ -664,6 +701,18 @@ %1$d명의 대화상대 %1$d명의 대화상대 %1$d명의 대화상대 + %1$d 번 + %1$d 번 + %1$d 번 + %1$d 번 + %1$d 번 + %1$d 번 + %1$d 미터 + %1$d 미터 + %1$d 미터 + %1$d 미터 + %1$d 미터 + %1$d 미터 %1$d 개의 전달된 메시지 전달된 메시지 @@ -730,4 +779,7 @@ HH:mm a h:mm %1$s %2$s + + 텔레그램 안드로이드가 업데이트 되었습니다. 새로운 버전 2.8:\n\n-그룹방 초대링크 기능\n-스마트 알림\n-이모티콘 스티커탭\n-사진 설명 기능\n-위치에 장소 및 안내 기능\n-음성 메시지 \'청취확인\' 기능\n-\'입력 중\' 확장 : 사진 보내는중, 녹음 중등\n-부드러운 스크롤링 및 애니메이션 향상 + 516 \ No newline at end of file diff --git a/TMessagesProj/src/main/res/values-nl/strings.xml b/TMessagesProj/src/main/res/values-nl/strings.xml index d652346b1..54ae2cf52 100644 --- a/TMessagesProj/src/main/res/values-nl/strings.xml +++ b/TMessagesProj/src/main/res/values-nl/strings.xml @@ -89,6 +89,10 @@ %1$s verstuurt een foto %1$s verstuurt een video %1$s verstuurt een bestand + geluid opnemen + foto versturen + video versturen + bestand versturen Heb je een vraag\nover Telegram? Foto maken Galerij @@ -157,6 +161,7 @@ %1$s heeft nu Telegram! %1$s,\nEr is op je account ingelogd vanaf een nieuw apparaat op %2$s\n\nApparaat: %3$s\nLocatie: %4$s\n\nAls jij dit niet was, kun je die sessie beëindigen via Instellingen - Privacy en veiligheid - Sessies.\n\nAls je dat denkt dat iemand anders zonder jouw toestemming is ingelogd kun je twee-staps-verificatie activeren via instellingen - privacy en veiligheid .\n\nBedankt,\nHet Telegram-team %1$s heeft zijn/haar profielfoto gewijzigd + %1$s neemt deel aan de groep %2$s via uitnodigingslink Antwoord Antwoord op %1$s Antwoord op %1$s @@ -182,6 +187,19 @@ Groepsnaam Groepsnaam %1$d/%2$d deelnemers + Wil je deelnemen aan de groep \'%1$s\'? + Sorry, deze groep is al vol. + Sorry, deze groep bestaat niet. + Link gekopieerd naar klembord. + Uitnodigingslink sturen + Uitnodigingslink + Deze link echt intrekken? Na intrekken kan niemand meer lid worden met de oude link. + De oude link is nu inactief. Een nieuwe link is aangemaakt. + Intrekken + Link intrekken + Link kopiëren + Link delen + Andere Telegram-gebruikers kunnen aan je groep deelnemen door deze link te openen. Gedeelde media Instellingen @@ -311,6 +329,13 @@ Uit Chatgeluiden Standaard + Slimme meldingen + Uitgeschakeld + Geluid maximaal %1$s per %2$s + Geluid maximaal + keer + binnen + minuten Actieve sessies Huidige sessie @@ -350,15 +375,20 @@ Hybride m hiervandaan km hiervandaan - Locatie versturen - Locatie delen + Huidige locatie sturen + Geselecteerde locatie sturen + Locatie + Nauwkeurig tot op %1$s + OF KIES EEN PLEK Alle media weergeven Opslaan in galerij %1$d van %2$d Galerij Alle foto\'s + Alle video\'s Nog geen foto\'s + Nog geen video\'s Download media eerst Niets recents Niets recents @@ -388,6 +418,11 @@ Wijzigingen negeren? Zoekgeschiedenis wissen? Wissen + Foto\'s + Video + Onderschrift toevoegen + Foto-onderschrift + Video-onderschrift Twee-staps-verificatie Extra wachtwoord instellen @@ -497,6 +532,8 @@ OK BIJSNIJDEN + Je neemt deel aan de groep via uitnodigingslink + un1 neemt deel aan de groep via uitnodigingslink un1 heeft un2 verwijderd un1 heeft de groep verlaten un1 heeft un2 toegevoegd @@ -664,6 +701,18 @@ %1$d gebruikers %1$d gebruikers %1$d gebruikers + %1$d keer + %1$d keer + %1$d keer + %1$d keer + %1$d keer + %1$d keer + %1$d meter + %1$d meter + %1$d meter + %1$d meter + %1$d meter + %1$d meter Bijlage: %1$d berichten Bijlage: 1 bericht @@ -730,4 +779,7 @@ HH:mm h:mm a %1$s om %2$s + + Telegram voor Android is geüpdatet. Nieuw in versie 2.8:\n\n- Uitnodigingslinks voor groepchats\n- Slimme meldingen\n- Stickerstab in emojimenu\n- Onderschriften voor foto\'s\n- Plekken en routebeschrijvingen op locaties\n- \'Beluistered\'-status voor spraakberichten\n- Geadvanceerde \'aan het typen\'-status: verstuurt een foto, audio opnemen, etc.\n- Scrollen en animaties verbeterd voor een soepele ervaring + 516 \ No newline at end of file diff --git a/TMessagesProj/src/main/res/values-pt-rBR/strings.xml b/TMessagesProj/src/main/res/values-pt-rBR/strings.xml index b0d1b594a..b7b5d2758 100644 --- a/TMessagesProj/src/main/res/values-pt-rBR/strings.xml +++ b/TMessagesProj/src/main/res/values-pt-rBR/strings.xml @@ -85,10 +85,14 @@ escrevendo... está escrevendo... estão escrevendo... - %1$s está gravando uma mensagem... + %1$s está gravando um áudio... %1$s está enviando uma foto... %1$s está enviando um vídeo... %1$s está enviando um arquivo... + gravando áudio... + enviando foto... + enviando vídeo... + enviando arquivo... Tem alguma dúvida\nsobre o Telegram? Tirar foto Galeria @@ -157,6 +161,7 @@ %1$s entrou para o Telegram! %1$s,\nNós detectamos que alguém acessou a sua conta a partir de um novo aparelho em %2$s\n\nAparelho: %3$s\nLocalização: %4$s\n\nSe não foi você, você pode ir em Configurações - Provacidade e Segurança - Sessões, e terminar aquela sessão.\n\nSe você acha que alguém acessou a sua conta contra a sua vontade, você pode habilitar a verificação em duas etapas nas configurações de Privacidade e Segurança.\n\nAtenciosamente,\nEquipe Telegram %1$s atualizou a foto do perfil + %1$s entrou para o grupo %2$s via link de convite Responder Responder para %1$s Responder para %1$s @@ -182,6 +187,19 @@ Digite o nome do grupo Nome do grupo %1$d/%2$d membros + Você deseja entrar no grupo \'%1$s\'? + Desculpe, este grupo já está lotado. + Desculpe, este grupo não existe. + Link copiado para área de transferência + Convidar para o Grupo via Link + Link de Convite + Você tem certeza que deseja desativar o link? Uma vez feito, ninguém conseguirá entrar no grupo usando-o. + Este link de convite está inativo. Um novo link foi gerado. + Desativar + Desativar Link + Copiar Link + Compartilhar Link + Qualquer um com Telegram instalado poderá entrar no seu grupo abrindo este link. Mídia Compartilhada Configurações @@ -311,6 +329,13 @@ Desativado Sons no Chat Padrão + Notificações Inteligentes + Desativado + Tocar no máximo %1$s a cada %2$s + Tocar no máximo + vezes + a cada + minutos Sessões Ativas Sessão atual @@ -350,15 +375,20 @@ Híbrido m de distância km de distância - Enviar Localização - Compartilhar Localização + Enviar sua localização atual + Enviar localização selecionada + Localização + Precisão de %1$s + OU ESCOLHA UM LUGAR Mostrar todas as mídias Salvar na galeria %1$d de %2$d Galeria Todas as Fotos + Todos os Vídeos Ainda não há fotos + Nenhum vídeo ainda Baixar o vídeo primeiro Nenhuma foto recente Nenhum GIF recente @@ -388,6 +418,11 @@ Descartar mudanças? Limpar histórico de busca? Limpar + Fotos + Vídeo + Adicionar legenda... + Legenda da Foto + Legenda do Vídeo Verificação em duas etapas Configurar senha adicional @@ -424,7 +459,7 @@ O código de recuperação foi enviado para o e-mail fornecido: \n\n%1$s Por favor, verifique o seu e-mail e digite aqui o código de 6 dígitos recebido. Está tendo problemas para acessar seu e-mail %1$s? - Se você não puder acessar o seu e-mail, as suas únicas opções são são lembrar a senha ou apagar a sua conta. + Se você não puder acessar o seu e-mail, as suas únicas opções são lembrar a senha ou apagar a sua conta. APAGAR MINHA CONTA Se você prosseguir e apagar a sua conta, você perderá todos os seus chats e mensagens, assim como todas as suas mídias e arquivos compartilhados. Aviso @@ -497,6 +532,8 @@ OK CORTAR + Você entrou para o grupo via link de convite + un1 entrou para o grupo via link de convite un1 removeu un2 un1 saiu do grupo un1 adicionou un2 @@ -664,6 +701,18 @@ %1$d usuários %1$d usuários %1$d usuários + %1$d vezes + %1$d vez + %1$d vezes + %1$d vezes + %1$d vezes + %1$d vezes + %1$d metros + %1$d metro + %1$d metros + %1$d metros + %1$d metros + %1$d metros %1$d mensagens encaminhadas Mensagem encaminhada @@ -730,4 +779,7 @@ HH:mm h:mm a %1$s às %2$s + + Telegram para Android atualizado: Novidades na versão 2.8:\n\n- Link de convite para chats em grupo\n- Notificações inteligentes\n- Botão para stickers no menu de emojis\n- Legendas para fotos\n- Lugares e direções nas localizações\n- Status \'ouvido\' para mensagens de voz\n- Status \'escrevendo\' avançado: enviando foto, gravando áudio, etc.\n- Melhorias nas animações de rolagem para uma leeeeeeeve experiência + 516 \ No newline at end of file diff --git a/TMessagesProj/src/main/res/values-pt-rPT/strings.xml b/TMessagesProj/src/main/res/values-pt-rPT/strings.xml index dd6aca2f4..2facbb7ac 100644 --- a/TMessagesProj/src/main/res/values-pt-rPT/strings.xml +++ b/TMessagesProj/src/main/res/values-pt-rPT/strings.xml @@ -4,9 +4,9 @@ Telegram - Português (Portugal) - Portuguese (Portugal) - pt_PT + Português (Brasil) + Português (Brasil) + pt_BR Seu número Confirme o código de seu país e preencha seu número de telefone. @@ -85,10 +85,14 @@ escrevendo... está escrevendo... estão escrevendo... - %1$s está gravando uma mensagem... + %1$s está gravando um áudio... %1$s está enviando uma foto... %1$s está enviando um vídeo... %1$s está enviando um arquivo... + gravando áudio... + enviando foto... + enviando vídeo... + enviando arquivo... Tem alguma dúvida\nsobre o Telegram? Tirar foto Galeria @@ -157,6 +161,7 @@ %1$s entrou para o Telegram! %1$s,\nNós detectamos que alguém acessou a sua conta a partir de um novo aparelho em %2$s\n\nAparelho: %3$s\nLocalização: %4$s\n\nSe não foi você, você pode ir em Configurações - Provacidade e Segurança - Sessões, e terminar aquela sessão.\n\nSe você acha que alguém acessou a sua conta contra a sua vontade, você pode habilitar a verificação em duas etapas nas configurações de Privacidade e Segurança.\n\nAtenciosamente,\nEquipe Telegram %1$s atualizou a foto do perfil + %1$s entrou para o grupo %2$s via link de convite Responder Responder para %1$s Responder para %1$s @@ -182,6 +187,19 @@ Digite o nome do grupo Nome do grupo %1$d/%2$d membros + Você deseja entrar no grupo \'%1$s\'? + Desculpe, este grupo já está lotado. + Desculpe, este grupo não existe. + Link copiado para área de transferência + Convidar para o Grupo via Link + Link de Convite + Você tem certeza que deseja desativar o link? Uma vez feito, ninguém conseguirá entrar no grupo usando-o. + Este link de convite está inativo. Um novo link foi gerado. + Desativar + Desativar Link + Copiar Link + Compartilhar Link + Qualquer um com Telegram instalado poderá entrar no seu grupo abrindo este link. Mídia Compartilhada Configurações @@ -311,6 +329,13 @@ Desativado Sons no Chat Padrão + Notificações Inteligentes + Desativado + Tocar no máximo %1$s a cada %2$s + Tocar no máximo + vezes + a cada + minutos Sessões Ativas Sessão atual @@ -350,15 +375,20 @@ Híbrido m de distância km de distância - Enviar Localização - Compartilhar Localização + Enviar sua localização atual + Enviar localização selecionada + Localização + Precisão de %1$s + OU ESCOLHA UM LUGAR Mostrar todas as mídias Salvar na galeria %1$d de %2$d Galeria Todas as Fotos + Todos os Vídeos Ainda não há fotos + Nenhum vídeo ainda Baixar o vídeo primeiro Nenhuma foto recente Nenhum GIF recente @@ -388,6 +418,11 @@ Descartar mudanças? Limpar histórico de busca? Limpar + Fotos + Vídeo + Adicionar legenda... + Legenda da Foto + Legenda do Vídeo Verificação em duas etapas Configurar senha adicional @@ -424,7 +459,7 @@ O código de recuperação foi enviado para o e-mail fornecido: \n\n%1$s Por favor, verifique o seu e-mail e digite aqui o código de 6 dígitos recebido. Está tendo problemas para acessar seu e-mail %1$s? - Se você não puder acessar o seu e-mail, as suas únicas opções são são lembrar a senha ou apagar a sua conta. + Se você não puder acessar o seu e-mail, as suas únicas opções são lembrar a senha ou apagar a sua conta. APAGAR MINHA CONTA Se você prosseguir e apagar a sua conta, você perderá todos os seus chats e mensagens, assim como todas as suas mídias e arquivos compartilhados. Aviso @@ -497,6 +532,8 @@ OK CORTAR + Você entrou para o grupo via link de convite + un1 entrou para o grupo via link de convite un1 removeu un2 un1 saiu do grupo un1 adicionou un2 @@ -664,6 +701,18 @@ %1$d usuários %1$d usuários %1$d usuários + %1$d vezes + %1$d vez + %1$d vezes + %1$d vezes + %1$d vezes + %1$d vezes + %1$d metros + %1$d metro + %1$d metros + %1$d metros + %1$d metros + %1$d metros %1$d mensagens encaminhadas Mensagem encaminhada @@ -730,4 +779,7 @@ HH:mm h:mm a %1$s às %2$s + + Telegram para Android atualizado: Novidades na versão 2.8:\n\n- Link de convite para chats em grupo\n- Notificações inteligentes\n- Botão para stickers no menu de emojis\n- Legendas para fotos\n- Lugares e direções nas localizações\n- Status \'ouvido\' para mensagens de voz\n- Status \'escrevendo\' avançado: enviando foto, gravando áudio, etc.\n- Melhorias nas animações de rolagem para uma leeeeeeeve experiência + 516 \ No newline at end of file diff --git a/TMessagesProj/src/main/res/values/colors.xml b/TMessagesProj/src/main/res/values/colors.xml index 31a03a7c9..b4eac3ecb 100755 --- a/TMessagesProj/src/main/res/values/colors.xml +++ b/TMessagesProj/src/main/res/values/colors.xml @@ -1,5 +1,4 @@ - #6633B5E5 #dcdcdc \ No newline at end of file diff --git a/TMessagesProj/src/main/res/values/strings.xml b/TMessagesProj/src/main/res/values/strings.xml index 847bff777..33423ab8c 100644 --- a/TMessagesProj/src/main/res/values/strings.xml +++ b/TMessagesProj/src/main/res/values/strings.xml @@ -89,6 +89,10 @@ %1$s is sending photo... %1$s is sending video... %1$s is sending file... + recording audio... + sending photo... + sending video... + sending file... Got a question\nabout Telegram? Take photo Gallery @@ -157,6 +161,7 @@ %1$s joined Telegram! %1$s,\nWe detected a login into your account from a new device on %2$s\n\nDevice: %3$s\nLocation: %4$s\n\nIf this wasn\'t you, you can go to Settings - Privacy and Security - Sessions and terminate that session.\n\nIf you think that somebody logged in to your account against your will, you can enable two-step verification in Privacy and Security settings.\n\nSincerely,\nThe Telegram Team %1$s updated profile photo + %1$s joined to the group %2$s via invite link Reply Reply to %1$s Reply to %1$s @@ -182,6 +187,19 @@ Enter group name Group name %1$d/%2$d members + Do you want to join the group \'%1$s\'? + Sorry, this group is already full. + Sorry, this group does not seem to exist. + Link copied to clipboard + Invite to Group via Link + Invite Link + Are you sure you want to revoke this link? Once you do, no one will be able to join the group using it. + The previous invite link is now inactive. A new link has been generated. + Revoke + Revoke Link + Copy Link + Share Link + Anyone who has Telegram installed will be able to join your group by following this link. Shared Media Settings @@ -311,6 +329,13 @@ Off In-Chat Sounds Default + Smart Notifications + Disabled + Sound at most %1$s within %2$s + Sound at most + times + within + minutes Active Sessions Current session @@ -350,15 +375,20 @@ Hybrid m away km away - Send Location - Share Location + Send your current location + Send selected location + Location + Accurate to %1$s + OR CHOOSE A PLACE Show all media Save to gallery %1$d of %2$d Gallery All Photos + All Videos No photos yet + No videos yet Please download media first No recent photos No recent GIFs @@ -388,6 +418,11 @@ Discard changes? Clear search history? Clear + Photos + Video + Add a caption... + Photo Caption + Video Caption Two-Step Verification Set Additional Password @@ -497,6 +532,8 @@ OK CROP + You joined the group via invite link + un1 joined the group via invite link un1 removed un2 un1 left group un1 added un2 @@ -664,6 +701,18 @@ %1$d users %1$d users %1$d users + %1$d times + %1$d time + %1$d times + %1$d times + %1$d times + %1$d times + %1$d meters + %1$d meter + %1$d meters + %1$d meters + %1$d meters + %1$d meters %1$d forwarded messages Forwarded message @@ -730,4 +779,7 @@ HH:mm h:mm a %1$s at %2$s + + Telegram for Android has been updated. New in version 2.8:\n\n- Places and directions in locations\n- Smart notifications\n- Invite links for group chats\n- Stickers tab in emoji menu\n- Captions for photos\n- \'Listened\' status for voice messages\n- Advanced \'typing\' status: sending photo, recording audio, etc.\n- Improved scrolling and animations for a smooooooooth experience + 516 \ No newline at end of file diff --git a/TMessagesProj/src/main/res/xml/auth.xml b/TMessagesProj/src/main/res/xml/auth.xml index 7856d0c83..cc285409e 100644 --- a/TMessagesProj/src/main/res/xml/auth.xml +++ b/TMessagesProj/src/main/res/xml/auth.xml @@ -1,7 +1,7 @@ \ No newline at end of file + android:accountType="org.telegram.messenger"/> \ No newline at end of file