1
0
mirror of https://github.com/MGislv/NekoX.git synced 2024-06-30 22:22:57 +00:00

Merge branch 'dev'

This commit is contained in:
DrKLO 2015-06-25 02:17:35 +03:00
commit 163461882d
607 changed files with 53847 additions and 19720 deletions

View File

@ -3,7 +3,7 @@ buildscript {
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:1.1.3'
classpath 'com.android.tools.build:gradle:1.2.3'
}
}
apply plugin: 'com.android.application'
@ -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 542
versionName "2.9.1"
}
}

View File

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

View File

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

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -123,6 +123,18 @@
android:windowSoftInputMode="adjustResize|stateHidden">
</activity>
<receiver android:name="org.telegram.android.AutoMessageHeardReceiver">
<intent-filter>
<action android:name="org.telegram.messenger.ACTION_MESSAGE_HEARD"/>
</intent-filter>
</receiver>
<receiver android:name="org.telegram.android.AutoMessageReplyReceiver">
<intent-filter>
<action android:name="org.telegram.messenger.ACTION_MESSAGE_REPLY"/>
</intent-filter>
</receiver>
<receiver android:name="org.telegram.android.SmsListener">
<intent-filter>
<action android:name="android.provider.Telephony.SMS_RECEIVED" />
@ -168,6 +180,8 @@
<meta-data android:name="com.sec.android.multiwindow.MINIMUM_SIZE_W" android:value="632dp" />
<meta-data android:name="com.sec.android.multiwindow.MINIMUM_SIZE_H" android:value="598dp" />
<meta-data android:name="com.google.android.gms.car.application" android:resource="@xml/automotive_app_desc" />
</application>
</manifest>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 135 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 151 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 80 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 136 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 73 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 237 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 132 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 270 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 144 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 249 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 97 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 122 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 102 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 152 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 167 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 86 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 154 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 112 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 136 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 103 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 260 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 146 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 297 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 160 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 280 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 180 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 126 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 222 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 85 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 182 KiB

View File

@ -27,11 +27,11 @@ package org.telegram.PhoneFormat;
import java.util.ArrayList;
public class CallingCodeInfo {
public ArrayList<String> countries = new ArrayList<String>();
public ArrayList<String> countries = new ArrayList<>();
public String callingCode = "";
public ArrayList<String> trunkPrefixes = new ArrayList<String>();
public ArrayList<String> intlPrefixes = new ArrayList<String>();
public ArrayList<RuleSet> ruleSets = new ArrayList<RuleSet>();
public ArrayList<String> trunkPrefixes = new ArrayList<>();
public ArrayList<String> intlPrefixes = new ArrayList<>();
public ArrayList<RuleSet> ruleSets = new ArrayList<>();
//public ArrayList formatStrings;
String matchingAccessCode(String str) {
@ -107,14 +107,14 @@ public class CallingCodeInfo {
for (RuleSet set : ruleSets) {
boolean valid = set.isValid(str, intlPrefix, trunkPrefix, true);
if (valid) {
return valid;
return true;
}
}
for (RuleSet set : ruleSets) {
boolean valid = set.isValid(str, intlPrefix, trunkPrefix, false);
if (valid) {
return valid;
return true;
}
}

View File

@ -8,19 +8,26 @@
package org.telegram.android;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.ContentUris;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
import android.database.Cursor;
import android.graphics.Color;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.Typeface;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import android.provider.DocumentsContract;
import android.provider.MediaStore;
import android.text.Spannable;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
@ -39,25 +46,39 @@ import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.TextView;
import net.hockeyapp.android.CrashManager;
import net.hockeyapp.android.CrashManagerListener;
import net.hockeyapp.android.UpdateManager;
import org.telegram.messenger.BuildVars;
import org.telegram.messenger.ConnectionsManager;
import org.telegram.messenger.FileLog;
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;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.Hashtable;
import java.util.Locale;
public class AndroidUtilities {
@ -72,6 +93,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 {
@ -158,7 +180,7 @@ public class AndroidUtilities {
}
public static boolean isWaitingForSms() {
boolean value = false;
boolean value;
synchronized (smsLock) {
value = waitingForSms;
}
@ -227,21 +249,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 +291,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) {
@ -525,11 +596,12 @@ public class AndroidUtilities {
}
}
@SuppressLint("NewApi")
public static void clearDrawableAnimation(View view) {
if (Build.VERSION.SDK_INT < 21 || view == null) {
return;
}
Drawable drawable = null;
Drawable drawable;
if (view instanceof ListView) {
drawable = ((ListView) view).getSelector();
if (drawable != null) {
@ -546,9 +618,9 @@ public class AndroidUtilities {
public static Spannable replaceTags(String str) {
try {
int start = -1;
int start;
int startColor = -1;
int end = -1;
int end;
StringBuilder stringBuilder = new StringBuilder(str);
while ((start = stringBuilder.indexOf("<br>")) != -1) {
stringBuilder.replace(start, start + 4, "\n");
@ -558,10 +630,13 @@ public class AndroidUtilities {
}
ArrayList<Integer> bolds = new ArrayList<>();
ArrayList<Integer> colors = new ArrayList<>();
while ((start = stringBuilder.indexOf("<b>")) != -1 || (startColor = stringBuilder.indexOf("<c")) != -1) {
while ((start = stringBuilder.indexOf("<b>")) != -1 || (startColor = stringBuilder.indexOf("<c#")) != -1) {
if (start != -1) {
stringBuilder.replace(start, start + 3, "");
end = stringBuilder.indexOf("</b>");
if (end == -1) {
end = stringBuilder.indexOf("<b>");
}
stringBuilder.replace(end, end + 4, "");
bolds.add(start);
bolds.add(end);
@ -575,6 +650,7 @@ public class AndroidUtilities {
colors.add(startColor);
colors.add(end);
colors.add(color);
startColor = -1;
}
}
SpannableStringBuilder spannableStringBuilder = new SpannableStringBuilder(stringBuilder);
@ -673,4 +749,290 @@ public class AndroidUtilities {
window.clearFlags(WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED);
}
}*/
public static void checkForCrashes(Activity context) {
CrashManager.register(context, BuildVars.HOCKEY_APP_HASH, new CrashManagerListener() {
@Override
public boolean includeDeviceData() {
return true;
}
});
}
public static void checkForUpdates(Activity context) {
if (BuildVars.DEBUG_VERSION) {
UpdateManager.register(context, BuildVars.HOCKEY_APP_HASH);
}
}
public static void unregisterUpdates() {
if (BuildVars.DEBUG_VERSION) {
UpdateManager.unregister();
}
}
public static void addMediaToGallery(String fromPath) {
if (fromPath == null) {
return;
}
File f = new File(fromPath);
Uri contentUri = Uri.fromFile(f);
addMediaToGallery(contentUri);
}
public static void addMediaToGallery(Uri uri) {
if (uri == null) {
return;
}
Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
mediaScanIntent.setData(uri);
ApplicationLoader.applicationContext.sendBroadcast(mediaScanIntent);
}
private static File getAlbumDir() {
File storageDir = null;
if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) {
storageDir = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), "Telegram");
if (!storageDir.mkdirs()) {
if (!storageDir.exists()){
FileLog.d("tmessages", "failed to create directory");
return null;
}
}
} else {
FileLog.d("tmessages", "External storage is not mounted READ/WRITE.");
}
return storageDir;
}
@SuppressLint("NewApi")
public static String getPath(final Uri uri) {
try {
final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;
if (isKitKat && DocumentsContract.isDocumentUri(ApplicationLoader.applicationContext, uri)) {
if (isExternalStorageDocument(uri)) {
final String docId = DocumentsContract.getDocumentId(uri);
final String[] split = docId.split(":");
final String type = split[0];
if ("primary".equalsIgnoreCase(type)) {
return Environment.getExternalStorageDirectory() + "/" + split[1];
}
} else if (isDownloadsDocument(uri)) {
final String id = DocumentsContract.getDocumentId(uri);
final Uri contentUri = ContentUris.withAppendedId(Uri.parse("content://downloads/public_downloads"), Long.valueOf(id));
return getDataColumn(ApplicationLoader.applicationContext, contentUri, null, null);
} else if (isMediaDocument(uri)) {
final String docId = DocumentsContract.getDocumentId(uri);
final String[] split = docId.split(":");
final String type = split[0];
Uri contentUri = null;
switch (type) {
case "image":
contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
break;
case "video":
contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
break;
case "audio":
contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
break;
}
final String selection = "_id=?";
final String[] selectionArgs = new String[] {
split[1]
};
return getDataColumn(ApplicationLoader.applicationContext, contentUri, selection, selectionArgs);
}
} else if ("content".equalsIgnoreCase(uri.getScheme())) {
return getDataColumn(ApplicationLoader.applicationContext, uri, null, null);
} else if ("file".equalsIgnoreCase(uri.getScheme())) {
return uri.getPath();
}
} catch (Exception e) {
FileLog.e("tmessages", e);
}
return null;
}
public static String getDataColumn(Context context, Uri uri, String selection, String[] selectionArgs) {
Cursor cursor = null;
final String column = "_data";
final String[] projection = {
column
};
try {
cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs, null);
if (cursor != null && cursor.moveToFirst()) {
final int column_index = cursor.getColumnIndexOrThrow(column);
return cursor.getString(column_index);
}
} catch (Exception e) {
FileLog.e("tmessages", e);
} finally {
if (cursor != null) {
cursor.close();
}
}
return null;
}
public static boolean isExternalStorageDocument(Uri uri) {
return "com.android.externalstorage.documents".equals(uri.getAuthority());
}
public static boolean isDownloadsDocument(Uri uri) {
return "com.android.providers.downloads.documents".equals(uri.getAuthority());
}
public static boolean isMediaDocument(Uri uri) {
return "com.android.providers.media.documents".equals(uri.getAuthority());
}
public static File generatePicturePath() {
try {
File storageDir = getAlbumDir();
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.US).format(new Date());
return new File(storageDir, "IMG_" + timeStamp + ".jpg");
} catch (Exception e) {
FileLog.e("tmessages", e);
}
return null;
}
public static CharSequence generateSearchName(String name, String name2, String q) {
if (name == null && name2 == null) {
return "";
}
SpannableStringBuilder builder = new SpannableStringBuilder();
String wholeString = name;
if (wholeString == null || wholeString.length() == 0) {
wholeString = name2;
} else if (name2 != null && name2.length() != 0) {
wholeString += " " + name2;
}
wholeString = wholeString.trim();
String lower = " " + wholeString.toLowerCase();
int index;
int lastIndex = 0;
while ((index = lower.indexOf(" " + q, lastIndex)) != -1) {
int idx = index - (index == 0 ? 0 : 1);
int end = q.length() + (index == 0 ? 0 : 1) + idx;
if (lastIndex != 0 && lastIndex != idx + 1) {
builder.append(wholeString.substring(lastIndex, idx));
} else if (lastIndex == 0 && idx != 0) {
builder.append(wholeString.substring(0, idx));
}
String query = wholeString.substring(idx, end);
if (query.startsWith(" ")) {
builder.append(" ");
}
query = query.trim();
builder.append(AndroidUtilities.replaceTags("<c#ff4d83b3>" + query + "</c>"));
lastIndex = end;
}
if (lastIndex != -1 && lastIndex != wholeString.length()) {
builder.append(wholeString.substring(lastIndex, wholeString.length()));
}
return builder;
}
public static File generateVideoPath() {
try {
File storageDir = getAlbumDir();
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.US).format(new Date());
return new File(storageDir, "VID_" + timeStamp + ".mp4");
} catch (Exception e) {
FileLog.e("tmessages", e);
}
return null;
}
public static String formatFileSize(long size) {
if (size < 1024) {
return String.format("%d B", size);
} else if (size < 1024 * 1024) {
return String.format("%.1f KB", size / 1024.0f);
} else if (size < 1024 * 1024 * 1024) {
return String.format("%.1f MB", size / 1024.0f / 1024.0f);
} else {
return String.format("%.1f GB", size / 1024.0f / 1024.0f / 1024.0f);
}
}
public static byte[] decodeQuotedPrintable(final byte[] bytes) {
if (bytes == null) {
return null;
}
final ByteArrayOutputStream buffer = new ByteArrayOutputStream();
for (int i = 0; i < bytes.length; i++) {
final int b = bytes[i];
if (b == '=') {
try {
final int u = Character.digit((char) bytes[++i], 16);
final int l = Character.digit((char) bytes[++i], 16);
buffer.write((char) ((u << 4) + l));
} catch (Exception e) {
FileLog.e("tmessages", e);
return null;
}
} else {
buffer.write(b);
}
}
byte[] array = buffer.toByteArray();
try {
buffer.close();
} catch (Exception e) {
FileLog.e("tmessages", e);
}
return array;
}
public static boolean copyFile(InputStream sourceFile, File destFile) throws IOException {
OutputStream out = new FileOutputStream(destFile);
byte[] buf = new byte[4096];
int len;
while ((len = sourceFile.read(buf)) > 0) {
Thread.yield();
out.write(buf, 0, len);
}
out.close();
return true;
}
public static boolean copyFile(File sourceFile, File destFile) throws IOException {
if (!destFile.exists()) {
destFile.createNewFile();
}
FileInputStream source = null;
FileOutputStream destination = null;
try {
source = new FileInputStream(sourceFile);
destination = new FileOutputStream(destFile);
destination.getChannel().transferFrom(source.getChannel(), 0, source.getChannel().size());
} catch (Exception e) {
FileLog.e("tmessages", e);
return false;
} finally {
if (source != null) {
source.close();
}
if (destination != null) {
destination.close();
}
}
return true;
}
}

View File

@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.telegram.ui.Animation;
package org.telegram.android.Animation;
import android.view.animation.Interpolator;

View File

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

View File

@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.telegram.ui.Animation;
package org.telegram.android.Animation;
import android.view.animation.Interpolator;

View File

@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.telegram.ui.Animation;
package org.telegram.android.Animation;
public class FloatEvaluator implements TypeEvaluator<Number> {
public Float evaluate(float fraction, Number startValue, Number endValue) {

View File

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

View File

@ -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<T> extends Property<T, Float> {

View File

@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.telegram.ui.Animation;
package org.telegram.android.Animation;
public class IntEvaluator implements TypeEvaluator<Integer> {
public Integer evaluate(float fraction, Integer startValue, Integer endValue) {

View File

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

View File

@ -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<T> extends Property<T, Integer> {

View File

@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.telegram.ui.Animation;
package org.telegram.android.Animation;
import android.view.animation.Interpolator;

View File

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

View File

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

View File

@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.telegram.ui.Animation;
package org.telegram.android.Animation;
import android.view.View;

View File

@ -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<T, V> {

View File

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

View File

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

View File

@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.telegram.ui.Animation;
package org.telegram.android.Animation;
public interface TypeEvaluator<T> {
T evaluate(float fraction, T startValue, T endValue);

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -0,0 +1,26 @@
/*
* This is the source code of Telegram for Android v. 2.x.x.
* It is licensed under GNU GPL v. 2 or later.
* You should have received a copy of the license in this archive (see LICENSE).
*
* Copyright Nikolai Kudashov, 2013-2015.
*/
package org.telegram.android;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
public class AutoMessageHeardReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
long dialog_id = intent.getLongExtra("dialog_id", 0);
int max_id = intent.getIntExtra("max_id", 0);
if (dialog_id == 0 || max_id == 0) {
return;
}
MessagesController.getInstance().markDialogAsRead(dialog_id, max_id, max_id, 0, 0, true, false);
}
}

View File

@ -0,0 +1,37 @@
/*
* This is the source code of Telegram for Android v. 2.x.x.
* It is licensed under GNU GPL v. 2 or later.
* You should have received a copy of the license in this archive (see LICENSE).
*
* Copyright Nikolai Kudashov, 2013-2014.
*/
package org.telegram.android;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.support.v4.app.RemoteInput;
public class AutoMessageReplyReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Bundle remoteInput = RemoteInput.getResultsFromIntent(intent);
if (remoteInput == null) {
return;
}
CharSequence text = remoteInput.getCharSequence(NotificationsController.EXTRA_VOICE_REPLY);
if (text == null || text.length() == 0) {
return;
}
long dialog_id = intent.getLongExtra("dialog_id", 0);
int max_id = intent.getIntExtra("max_id", 0);
if (dialog_id == 0 || max_id == 0) {
return;
}
SendMessagesHelper.getInstance().sendMessage(text.toString(), dialog_id, null, null, true);
MessagesController.getInstance().markDialogAsRead(dialog_id, max_id, max_id, 0, 0, true, false);
}
}

View File

@ -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<TLRPC.TL_contact> 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();
}
}

View File

@ -33,7 +33,8 @@ import org.telegram.messenger.ApplicationLoader;
public class Emoji {
private static HashMap<Long, DrawableInfo> 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++) {
@ -220,7 +221,7 @@ public class Emoji {
private static void loadEmoji(final int page) {
try {
float scale = 1.0f;
float scale;
int imageResize = 1;
if (AndroidUtilities.density <= 1.0f) {
scale = 2.0f;
@ -234,11 +235,29 @@ 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);
AndroidUtilities.copyFile(is, imageFile);
is.close();
}
@ -253,11 +272,11 @@ 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);
Utilities.copyFile(is, imageFile);
AndroidUtilities.copyFile(is, imageFile);
is.close();
}

View File

@ -67,9 +67,12 @@ public class ImageLoader {
private DispatchQueue cacheThumbOutQueue = new DispatchQueue("cacheThumbOutQueue");
private DispatchQueue thumbGeneratingQueue = new DispatchQueue("thumbGeneratingQueue");
private DispatchQueue imageLoadQueue = new DispatchQueue("imageLoadQueue");
private DispatchQueue recycleQueue = new DispatchQueue("recycleQueue");
private ConcurrentHashMap<String, Float> fileProgresses = new ConcurrentHashMap<>();
private HashMap<String, ThumbGenerateTask> thumbGenerateTasks = new HashMap<>();
private static byte[] bytes;
private static byte[] bytesThumb;
private static byte[] header = new byte[12];
private static byte[] headerThumb = new byte[12];
private int currentHttpTasksCount = 0;
private LinkedList<HttpFileTask> httpFileLoadTasks = new LinkedList<>();
@ -114,8 +117,24 @@ public class ImageLoader {
try {
URL downloadUrl = new URL(url);
httpConnection = downloadUrl.openConnection();
httpConnection.addRequestProperty("User-Agent", "Mozilla/5.0 (Linux; Android 4.4; Nexus 5 Build/_BuildID_) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/30.0.0.0 Mobile Safari/537.36");
httpConnection.addRequestProperty("Referer", "google.com");
httpConnection.setConnectTimeout(5000);
httpConnection.setReadTimeout(5000);
if (httpConnection instanceof HttpURLConnection) {
HttpURLConnection httpURLConnection = (HttpURLConnection) httpConnection;
httpURLConnection.setInstanceFollowRedirects(true);
int status = httpURLConnection.getResponseCode();
if (status == HttpURLConnection.HTTP_MOVED_TEMP || status == HttpURLConnection.HTTP_MOVED_PERM || status == HttpURLConnection.HTTP_SEE_OTHER) {
String newUrl = httpURLConnection.getHeaderField("Location");
String cookies = httpURLConnection.getHeaderField("Set-Cookie");
downloadUrl = new URL(newUrl);
httpConnection = downloadUrl.openConnection();
httpConnection.setRequestProperty("Cookie", cookies);
httpConnection.addRequestProperty("User-Agent", "Mozilla/5.0 (Linux; Android 4.4; Nexus 5 Build/_BuildID_) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/30.0.0.0 Mobile Safari/537.36");
httpConnection.addRequestProperty("Referer", "google.com");
}
}
httpConnection.connect();
httpConnectionStream = httpConnection.getInputStream();
@ -135,29 +154,31 @@ public class ImageLoader {
FileLog.e("tmessages", e);
}
try {
byte[] data = new byte[1024 * 4];
while (true) {
if (isCancelled()) {
break;
}
try {
int readed = httpConnectionStream.read(data);
if (readed > 0) {
fileOutputStream.write(data, 0, readed);
} else if (readed == -1) {
done = true;
break;
} else {
if (httpConnectionStream != null) {
try {
byte[] data = new byte[1024 * 4];
while (true) {
if (isCancelled()) {
break;
}
try {
int read = httpConnectionStream.read(data);
if (read > 0) {
fileOutputStream.write(data, 0, read);
} else if (read == -1) {
done = true;
break;
} else {
break;
}
} catch (Exception e) {
FileLog.e("tmessages", e);
break;
}
} catch (Exception e) {
FileLog.e("tmessages", e);
break;
}
} catch (Throwable e) {
FileLog.e("tmessages", e);
}
} catch (Throwable e) {
FileLog.e("tmessages", e);
}
try {
@ -173,7 +194,6 @@ public class ImageLoader {
if (httpConnectionStream != null) {
httpConnectionStream.close();
}
httpConnectionStream = null;
} catch (Throwable e) {
FileLog.e("tmessages", e);
}
@ -233,12 +253,16 @@ public class ImageLoader {
try {
URL downloadUrl = new URL(cacheImage.httpUrl);
httpConnection = downloadUrl.openConnection();
httpConnection.addRequestProperty("User-Agent", "Mozilla/5.0 (Linux; Android 4.4; Nexus 5 Build/_BuildID_) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/30.0.0.0 Mobile Safari/537.36");
httpConnection.addRequestProperty("Referer", "google.com");
httpConnection.setConnectTimeout(5000);
httpConnection.setReadTimeout(5000);
if (httpConnection instanceof HttpURLConnection) {
((HttpURLConnection) httpConnection).setInstanceFollowRedirects(true);
}
if (!isCancelled()) {
httpConnection.connect();
httpConnectionStream = httpConnection.getInputStream();
fileOutputStream = new RandomAccessFile(cacheImage.tempFilePath, "rws");
}
} catch (Throwable e) {
@ -258,37 +282,39 @@ public class ImageLoader {
FileLog.e("tmessages", e);
}
try {
byte[] data = new byte[1024 * 2];
int totalLoaded = 0;
while (true) {
if (isCancelled()) {
break;
}
try {
int readed = httpConnectionStream.read(data);
if (readed > 0) {
totalLoaded += readed;
fileOutputStream.write(data, 0, readed);
if (imageSize != 0) {
reportProgress(totalLoaded / (float) imageSize);
}
} else if (readed == -1) {
done = true;
if (imageSize != 0) {
reportProgress(1.0f);
}
break;
} else {
if (httpConnectionStream != null) {
try {
byte[] data = new byte[1024 * 2];
int totalLoaded = 0;
while (true) {
if (isCancelled()) {
break;
}
try {
int read = httpConnectionStream.read(data);
if (read > 0) {
totalLoaded += read;
fileOutputStream.write(data, 0, read);
if (imageSize != 0) {
reportProgress(totalLoaded / (float) imageSize);
}
} else if (read == -1) {
done = true;
if (imageSize != 0) {
reportProgress(1.0f);
}
break;
} else {
break;
}
} catch (Exception e) {
FileLog.e("tmessages", e);
break;
}
} catch (Exception e) {
FileLog.e("tmessages", e);
break;
}
} catch (Throwable e) {
FileLog.e("tmessages", e);
}
} catch (Throwable e) {
FileLog.e("tmessages", e);
}
}
@ -305,7 +331,6 @@ public class ImageLoader {
if (httpConnectionStream != null) {
httpConnectionStream.close();
}
httpConnectionStream = null;
} catch (Throwable e) {
FileLog.e("tmessages", e);
}
@ -444,7 +469,6 @@ public class ImageLoader {
Bitmap scaledBitmap = Bitmap.createScaledBitmap(originalBitmap, (int) (w / scaleFactor), (int) (h / scaleFactor), true);
if (scaledBitmap != originalBitmap) {
originalBitmap.recycle();
callGC();
}
originalBitmap = scaledBitmap;
FileOutputStream stream = new FileOutputStream(thumbFile);
@ -507,17 +531,45 @@ public class ImageLoader {
}
Long mediaId = null;
boolean mediaIsVideo = false;
Bitmap image = null;
File cacheFileFinal = cacheImage.finalFilePath;
boolean canDeleteFile = true;
boolean isWebp = false;
boolean useNativeWebpLoaded = false;
if (cacheFileFinal.toString().endsWith("webp")) {
isWebp = true;
if (Build.VERSION.SDK_INT < 18) {
RandomAccessFile randomAccessFile = null;
try {
randomAccessFile = new RandomAccessFile(cacheFileFinal, "r");
byte[] bytes;
if (cacheImage.thumb) {
bytes = headerThumb;
} else {
bytes = header;
}
randomAccessFile.readFully(bytes, 0, bytes.length);
String str = new String(bytes);
if (str != null) {
str = str.toLowerCase();
if (str.startsWith("riff") && str.endsWith("webp")) {
useNativeWebpLoaded = true;
}
}
randomAccessFile.close();
} catch (Exception e) {
FileLog.e("tmessages", e);
} finally {
if (randomAccessFile != null) {
try {
randomAccessFile.close();
} catch (Exception e) {
FileLog.e("tmessages", e);
}
}
}
}
if (cacheImage.thumb) {
int blurType = 0;
if (cacheImage.filter != null) {
if (cacheImage.filter.contains("b2")) {
@ -537,33 +589,51 @@ 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 (!useNativeWebpLoaded && Build.VERSION.SDK_INT > 10 && Build.VERSION.SDK_INT < 21) {
opts.inPurgeable = true;
}
if (useNativeWebpLoaded) {
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)) {
if (cacheFileFinal.length() == 0 || cacheImage.filter == null) {
cacheFileFinal.delete();
}
} else {
if (image != null) {
if (blurType == 1) {
Utilities.blurBitmap(image, 3);
} else if (blurType == 2) {
Utilities.blurBitmap(image, 1);
} else if (blurType == 3) {
Utilities.blurBitmap(image, 7);
Utilities.blurBitmap(image, 7);
Utilities.blurBitmap(image, 7);
}
if (blurType == 1) {
Utilities.blurBitmap(image, 3, opts.inPurgeable ? 0 : 1);
} else if (blurType == 2) {
Utilities.blurBitmap(image, 1, opts.inPurgeable ? 0 : 1);
} else if (blurType == 3) {
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 +649,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 +682,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,24 +732,39 @@ public class ImageLoader {
} else {
opts.inPreferredConfig = Bitmap.Config.RGB_565;
}
//if (Build.VERSION.SDK_INT < 21) {
// opts.inPurgeable = true;
//}
if (!useNativeWebpLoaded && 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) {
if (useNativeWebpLoaded) {
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 {
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,23 +772,27 @@ 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();
image = scaledBitmap;
}
}
if (image != null && blur && bitmapH < 100 && bitmapW < 100) {
Utilities.blurBitmap(image, 3);
Utilities.blurBitmap(image, 3, opts.inPurgeable ? 0 : 1);
blured = true;
}
}
if (runtimeHack != null) {
if (!blured && opts.inPurgeable) {
Utilities.pinBitmap(image);
}
if (runtimeHack != null && image != null) {
runtimeHack.trackFree(image.getRowBytes() * image.getHeight());
}
}
@ -714,7 +820,6 @@ public class ImageLoader {
runtimeHack.trackAlloc(image.getRowBytes() * image.getHeight());
}
image.recycle();
callGC();
}
}
final BitmapDrawable toSetFinal = toSet;
@ -753,7 +858,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 +870,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 +883,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;
@ -793,6 +898,7 @@ public class ImageLoader {
protected String key;
protected String url;
protected String filter;
protected String ext;
protected TLObject location;
protected File finalFilePath;
@ -837,7 +943,7 @@ public class ImageLoader {
imageReceiverArray.clear();
if (location != null) {
if (location instanceof TLRPC.FileLocation) {
FileLoader.getInstance().cancelLoadFile((TLRPC.FileLocation) location);
FileLoader.getInstance().cancelLoadFile((TLRPC.FileLocation) location, ext);
} else if (location instanceof TLRPC.Document) {
FileLoader.getInstance().cancelLoadFile((TLRPC.Document) location);
}
@ -872,7 +978,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 +997,7 @@ public class ImageLoader {
}
private static volatile ImageLoader Instance = null;
public static ImageLoader getInstance() {
ImageLoader localInstance = Instance;
if (localInstance == null) {
@ -915,12 +1022,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)) {
@ -957,11 +1065,16 @@ public class ImageLoader {
}
@Override
public void fileDidUploaded(final String location, final TLRPC.InputFile inputFile, final TLRPC.InputEncryptedFile inputEncryptedFile) {
public void fileDidUploaded(final String location, final TLRPC.InputFile inputFile, final TLRPC.InputEncryptedFile inputEncryptedFile, final byte[] key, final byte[] iv) {
Utilities.stageQueue.postRunnable(new Runnable() {
@Override
public void run() {
NotificationCenter.getInstance().postNotificationName(NotificationCenter.FileDidUpload, location, inputFile, inputEncryptedFile);
AndroidUtilities.runOnUIThread(new Runnable() {
@Override
public void run() {
NotificationCenter.getInstance().postNotificationName(NotificationCenter.FileDidUpload, location, inputFile, inputEncryptedFile, key, iv);
}
});
fileProgresses.remove(location);
}
});
@ -972,7 +1085,12 @@ public class ImageLoader {
Utilities.stageQueue.postRunnable(new Runnable() {
@Override
public void run() {
NotificationCenter.getInstance().postNotificationName(NotificationCenter.FileDidFailUpload, location, isEncrypted);
AndroidUtilities.runOnUIThread(new Runnable() {
@Override
public void run() {
NotificationCenter.getInstance().postNotificationName(NotificationCenter.FileDidFailUpload, location, isEncrypted);
}
});
fileProgresses.remove(location);
}
});
@ -987,7 +1105,7 @@ public class ImageLoader {
if (location != null) {
if (MediaController.getInstance().canSaveToGallery() && telegramPath != null && finalFile != null && finalFile.exists() && (location.endsWith(".mp4") || location.endsWith(".jpg"))) {
if (finalFile.toString().startsWith(telegramPath.toString())) {
Utilities.addMediaToGallery(finalFile.toString());
AndroidUtilities.addMediaToGallery(finalFile.toString());
}
}
}
@ -1188,17 +1306,6 @@ public class ImageLoader {
}
}
public void callGC() {
if (Build.VERSION.SDK_INT > 13) {
recycleQueue.postRunnable(new Runnable() {
@Override
public void run() {
System.gc();
}
});
}
}
public boolean decrementUseCount(String key) {
Integer count = bitmapUseCounts.get(key);
if (count == null) {
@ -1296,17 +1403,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<String> 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);
}
}
});
@ -1328,7 +1439,7 @@ public class ImageLoader {
}
}
private void createLoadOperationForImageReceiver(final ImageReceiver imageReceiver, final String key, final String url, final TLObject imageLocation, final String httpLocation, final String filter, final int size, final boolean cacheOnly, final int thumb) {
private void createLoadOperationForImageReceiver(final ImageReceiver imageReceiver, final String key, final String url, final String ext, final TLObject imageLocation, final String httpLocation, final String filter, final int size, final boolean cacheOnly, final int thumb) {
if (imageReceiver == null || url == null || key == null) {
return;
}
@ -1384,6 +1495,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);
}
@ -1439,6 +1555,7 @@ public class ImageLoader {
img.key = key;
img.filter = filter;
img.httpUrl = httpLocation;
img.ext = ext;
img.addImageReceiver(imageReceiver);
if (onlyCache || cacheFile.exists()) {
img.finalFilePath = cacheFile;
@ -1456,7 +1573,7 @@ public class ImageLoader {
if (httpLocation == null) {
if (imageLocation instanceof TLRPC.FileLocation) {
TLRPC.FileLocation location = (TLRPC.FileLocation) imageLocation;
FileLoader.getInstance().loadFile(location, size, size == 0 || location.key != null || cacheOnly);
FileLoader.getInstance().loadFile(location, ext, size, size == 0 || location.key != null || cacheOnly);
} else if (imageLocation instanceof TLRPC.Document) {
FileLoader.getInstance().loadFile((TLRPC.Document) imageLocation, true, true);
}
@ -1487,7 +1604,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 +1614,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;
}
@ -1513,7 +1630,10 @@ public class ImageLoader {
String thumbUrl = null;
key = null;
thumbKey = null;
String ext = null;
String ext = imageReceiver.getExt();
if (ext == null) {
ext = "jpg";
}
if (httpLocation != null) {
key = Utilities.MD5(httpLocation);
url = key + "." + getHttpUrlExtension(httpLocation);
@ -1521,9 +1641,8 @@ public class ImageLoader {
if (imageLocation instanceof TLRPC.FileLocation) {
TLRPC.FileLocation location = (TLRPC.FileLocation) imageLocation;
key = location.volume_id + "_" + location.local_id;
ext = "." + (location.ext != null ? location.ext : "jpg");
url = key + ext;
if (location.ext != null || location.key != null || location.volume_id == Integer.MIN_VALUE && location.local_id < 0) {
url = key + "." + ext;
if (imageReceiver.getExt() != null || location.key != null || location.volume_id == Integer.MIN_VALUE && location.local_id < 0) {
saveImageToCache = true;
}
} else if (imageLocation instanceof TLRPC.Document) {
@ -1532,10 +1651,19 @@ public class ImageLoader {
return;
}
key = location.dc_id + "_" + location.id;
ext = ".webp";
url = key + ext;
String docExt = FileLoader.getDocumentFileName(location);
int idx;
if (docExt == null || (idx = docExt.lastIndexOf(".")) == -1) {
docExt = "";
} else {
docExt = docExt.substring(idx);
if (docExt.length() <= 1) {
docExt = "";
}
}
url = key + docExt;
if (thumbKey != null) {
thumbUrl = thumbKey + ext;
thumbUrl = thumbKey + "." + ext;
}
saveImageToCache = true;
}
@ -1548,11 +1676,7 @@ public class ImageLoader {
if (thumbLocation != null) {
thumbKey = thumbLocation.volume_id + "_" + thumbLocation.local_id;
if (ext != null) {
thumbUrl = thumbKey + ext;
} else {
thumbUrl = thumbKey + "." + (thumbLocation.ext != null ? thumbLocation.ext : "jpg");
}
thumbUrl = thumbKey + "." + ext;
}
String filter = imageReceiver.getFilter();
@ -1565,10 +1689,10 @@ public class ImageLoader {
}
if (httpLocation != null) {
createLoadOperationForImageReceiver(imageReceiver, key, url, null, httpLocation, filter, 0, true, 0);
createLoadOperationForImageReceiver(imageReceiver, key, url, ext, null, httpLocation, filter, 0, true, 0);
} else {
createLoadOperationForImageReceiver(imageReceiver, thumbKey, thumbUrl, thumbLocation, null, thumbFilter, 0, true, thumbSet ? 2 : 1);
createLoadOperationForImageReceiver(imageReceiver, key, url, imageLocation, null, filter, imageReceiver.getSize(), saveImageToCache || imageReceiver.getCacheOnly(), 0);
createLoadOperationForImageReceiver(imageReceiver, thumbKey, thumbUrl, ext, thumbLocation, null, thumbFilter, 0, true, thumbSet ? 2 : 1);
createLoadOperationForImageReceiver(imageReceiver, key, url, ext, imageLocation, null, filter, imageReceiver.getSize(), saveImageToCache || imageReceiver.getCacheOnly(), 0);
}
}
@ -1611,6 +1735,7 @@ public class ImageLoader {
cacheImage.key = img.key;
cacheImage.httpUrl = img.httpUrl;
cacheImage.thumb = img.thumb;
cacheImage.ext = img.ext;
cacheImage.cacheTask = task = new CacheOutTask(cacheImage);
cacheImage.filter = img.filter;
imageLoadingByKeys.put(cacheImage.key, cacheImage);
@ -1750,7 +1875,7 @@ public class ImageLoader {
path = uri.getPath();
} else {
try {
path = Utilities.getPath(uri);
path = AndroidUtilities.getPath(uri);
} catch (Throwable e) {
FileLog.e("tmessages", e);
}
@ -1784,13 +1909,13 @@ public class ImageLoader {
scaleFactor = 1;
}
bmOptions.inJustDecodeBounds = false;
bmOptions.inSampleSize = (int)scaleFactor;
bmOptions.inSampleSize = (int) scaleFactor;
String exifPath = null;
if (path != null) {
exifPath = path;
} else if (uri != null) {
exifPath = Utilities.getPath(uri);
exifPath = AndroidUtilities.getPath(uri);
}
Matrix matrix = null;
@ -1848,9 +1973,7 @@ public class ImageLoader {
FileLog.e("tmessages", e);
} finally {
try {
if (parcelFD != null) {
parcelFD.close();
}
parcelFD.close();
} catch (Throwable e) {
FileLog.e("tmessages", e);
}
@ -1860,8 +1983,25 @@ public class ImageLoader {
return b;
}
public static void fillPhotoSizeWithBytes(TLRPC.PhotoSize photoSize) {
if (photoSize == null || photoSize.bytes != null) {
return;
}
File file = FileLoader.getPathToAttach(photoSize, true);
try {
RandomAccessFile f = new RandomAccessFile(file, "r");
int len = (int) f.length();
if (len < 20000) {
photoSize.bytes = new byte[(int) f.length()];
f.readFully(photoSize.bytes, 0, photoSize.bytes.length);
}
} catch (Throwable e) {
FileLog.e("tmessages", e);
}
}
private static TLRPC.PhotoSize scaleAndSaveImageInternal(Bitmap bitmap, int w, int h, float photoW, float photoH, float scaleFactor, int quality, boolean cache, boolean scaleAnyway) throws Exception {
Bitmap scaledBitmap = null;
Bitmap scaledBitmap;
if (scaleFactor > 1 || scaleAnyway) {
scaledBitmap = Bitmap.createScaledBitmap(bitmap, w, h, true);
} else {
@ -1900,7 +2040,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 +2066,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;
}
@ -1978,12 +2124,6 @@ public class ImageLoader {
} else if (message.media instanceof TLRPC.TL_messageMediaDocument) {
if (message.media.document.thumb instanceof TLRPC.TL_photoCachedSize) {
photoSize = message.media.document.thumb;
for (TLRPC.DocumentAttribute attribute : message.media.document.attributes) {
if (attribute instanceof TLRPC.TL_documentAttributeSticker) {
photoSize.location.ext = "webp";
break;
}
}
}
} else if (message.media instanceof TLRPC.TL_messageMediaWebPage) {
if (message.media.webpage.photo != null) {

View File

@ -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,18 +34,33 @@ 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;
public String ext;
}
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;
private String currentHttpUrl;
private String currentFilter;
private String currentThumbFilter;
private String currentExt;
private TLRPC.FileLocation currentThumbLocation;
private int currentSize;
private boolean currentCacheOnly;
@ -54,6 +70,7 @@ public class ImageReceiver implements NotificationCenter.NotificationCenterDeleg
private boolean needsQualityThumb;
private boolean shouldGenerateQualityThumb;
private boolean invalidateAll;
private int imageX, imageY, imageW, imageH;
private Rect drawRegion = new Rect();
@ -66,11 +83,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() {
@ -85,27 +107,34 @@ public class ImageReceiver implements NotificationCenter.NotificationCenterDeleg
canceledLoading = true;
}
public void setImage(TLObject path, String filter, Drawable thumb, boolean cacheOnly) {
setImage(path, null, filter, thumb, null, null, 0, cacheOnly);
public void setImage(TLObject path, String filter, Drawable thumb, String ext, boolean cacheOnly) {
setImage(path, null, filter, thumb, null, null, 0, ext, cacheOnly);
}
public void setImage(TLObject path, String filter, Drawable thumb, int size, boolean cacheOnly) {
setImage(path, null, filter, thumb, null, null, size, cacheOnly);
public void setImage(TLObject path, String filter, Drawable thumb, int size, String ext, boolean cacheOnly) {
setImage(path, null, filter, thumb, null, null, size, ext, cacheOnly);
}
public void setImage(String httpUrl, String filter, Drawable thumb, int size) {
setImage(null, httpUrl, filter, thumb, null, null, size, true);
public void setImage(String httpUrl, String filter, Drawable thumb, String ext, int size) {
setImage(null, httpUrl, filter, thumb, null, null, size, ext, true);
}
public void setImage(TLObject fileLocation, String filter, TLRPC.FileLocation thumbLocation, String thumbFilter, boolean cacheOnly) {
setImage(fileLocation, null, filter, null, thumbLocation, thumbFilter, 0, cacheOnly);
public void setImage(TLObject fileLocation, String filter, TLRPC.FileLocation thumbLocation, String thumbFilter, String ext, boolean cacheOnly) {
setImage(fileLocation, null, filter, null, thumbLocation, thumbFilter, 0, ext, cacheOnly);
}
public void setImage(TLObject fileLocation, String filter, TLRPC.FileLocation thumbLocation, String thumbFilter, int size, boolean cacheOnly) {
setImage(fileLocation, null, filter, null, thumbLocation, thumbFilter, size, cacheOnly);
public void setImage(TLObject fileLocation, String filter, TLRPC.FileLocation thumbLocation, String thumbFilter, int size, String ext, boolean cacheOnly) {
setImage(fileLocation, null, filter, null, thumbLocation, thumbFilter, size, ext, cacheOnly);
}
public void setImage(TLObject fileLocation, String httpUrl, String filter, Drawable thumb, TLRPC.FileLocation thumbLocation, String thumbFilter, int size, boolean cacheOnly) {
public void setImage(TLObject fileLocation, String httpUrl, String filter, Drawable thumb, TLRPC.FileLocation thumbLocation, String thumbFilter, int size, String ext, 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)
@ -113,6 +142,7 @@ public class ImageReceiver implements NotificationCenter.NotificationCenterDeleg
recycleBitmap(null, false);
recycleBitmap(null, true);
currentKey = null;
currentExt = ext;
currentThumbKey = null;
currentThumbFilter = null;
currentImageLocation = null;
@ -120,13 +150,18 @@ 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();
if (invalidateAll) {
parentView.invalidate();
} else {
parentView.invalidate(imageX, imageY, imageX + imageW, imageY + imageH);
}
}
if (delegate != null) {
delegate.didSetImage(this, currentImage != null || currentThumb != null || staticThumb != null, currentImage == null);
@ -134,6 +169,8 @@ public class ImageReceiver implements NotificationCenter.NotificationCenterDeleg
return;
}
if (!(thumbLocation instanceof TLRPC.TL_fileLocation)) {
thumbLocation = null;
}
@ -178,6 +215,7 @@ public class ImageReceiver implements NotificationCenter.NotificationCenterDeleg
currentThumbKey = thumbKey;
currentKey = key;
currentExt = ext;
currentImageLocation = fileLocation;
currentHttpUrl = httpUrl;
currentFilter = filter;
@ -187,6 +225,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 +233,18 @@ public class ImageReceiver implements NotificationCenter.NotificationCenterDeleg
ImageLoader.getInstance().loadImageForImageReceiver(this);
if (parentView != null) {
parentView.invalidate();
if (invalidateAll) {
parentView.invalidate();
} else {
parentView.invalidate(imageX, imageY, imageX + imageW, imageY + imageH);
}
}
}
public void setColorFilter(ColorFilter filter) {
colorFilter = filter;
}
public void setDelegate(ImageReceiverDelegate delegate) {
this.delegate = delegate;
}
@ -215,6 +262,10 @@ public class ImageReceiver implements NotificationCenter.NotificationCenterDeleg
centerRotation = center;
}
public void setInvalidateAll(boolean value) {
invalidateAll = value;
}
public int getOrientation() {
return orientation;
}
@ -230,6 +281,7 @@ public class ImageReceiver implements NotificationCenter.NotificationCenterDeleg
staticThumb = bitmap;
currentThumbLocation = null;
currentKey = null;
currentExt = null;
currentThumbKey = null;
currentImage = null;
currentThumbFilter = null;
@ -239,11 +291,22 @@ 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();
if (invalidateAll) {
parentView.invalidate();
} else {
parentView.invalidate(imageX, imageY, imageX + imageW, imageY + imageH);
}
}
}
@ -256,6 +319,211 @@ 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.ext = currentExt;
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.ext, 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);
} else if (!hasFilter && isPressed) {
bitmapDrawable.setColorFilter(new PorterDuffColorFilter(0xffdddddd, PorterDuff.Mode.MULTIPLY));
}
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;
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, currentExt, 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;
drawRegion.set(imageX - (bitmapW - imageW) / 2, imageY, imageX + (bitmapW + imageW) / 2, imageY + imageH);
} else {
bitmapH /= 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, currentExt, 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, currentExt, 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) {
if (invalidateAll) {
parentView.invalidate();
} else {
parentView.invalidate(imageX, imageY, imageX + imageW, imageY + imageH);
}
}
}
}
public boolean draw(Canvas canvas) {
try {
BitmapDrawable bitmapDrawable = null;
@ -267,161 +535,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 +598,11 @@ public class ImageReceiver implements NotificationCenter.NotificationCenterDeleg
}
isVisible = value;
if (invalidate && parentView != null) {
parentView.invalidate();
if (invalidateAll) {
parentView.invalidate();
} else {
parentView.invalidate(imageX, imageY, imageX + imageW, imageY + imageH);
}
}
}
@ -466,7 +611,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() {
@ -512,6 +661,10 @@ public class ImageReceiver implements NotificationCenter.NotificationCenterDeleg
return imageH;
}
public String getExt() {
return currentExt;
}
public boolean isInsideImage(float x, float y) {
return x >= imageX && x <= imageX + imageW && y >= imageY && y <= imageY + imageH;
}
@ -630,7 +783,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 +799,46 @@ 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();
if (invalidateAll) {
parentView.invalidate();
} else {
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();
if (invalidateAll) {
parentView.invalidate();
} else {
parentView.invalidate(imageX, imageY, imageX + imageW, imageY + imageH);
}
}
}
@ -696,7 +878,6 @@ public class ImageReceiver implements NotificationCenter.NotificationCenterDeleg
}
if (canDelete) {
bitmap.recycle();
ImageLoader.getInstance().callGC();
}
}
if (thumb) {
@ -721,7 +902,31 @@ public class ImageReceiver implements NotificationCenter.NotificationCenterDeleg
staticThumb = null;
}
if (parentView != null) {
parentView.invalidate();
if (invalidateAll) {
parentView.invalidate();
} else {
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];
}
}
}

View File

@ -23,7 +23,6 @@ import org.telegram.messenger.ConnectionsManager;
import org.telegram.messenger.FileLog;
import org.telegram.messenger.R;
import org.telegram.messenger.TLRPC;
import org.telegram.messenger.Utilities;
import org.telegram.messenger.ApplicationLoader;
import org.xmlpull.v1.XmlPullParser;
@ -309,6 +308,10 @@ public class LocaleController {
}
}
public Locale getSystemDefaultLocale() {
return systemDefaultLocale;
}
public static String getLocaleString(Locale locale) {
if (locale == null) {
return "en";
@ -355,7 +358,7 @@ public class LocaleController {
}
File finalFile = new File(ApplicationLoader.applicationContext.getFilesDir(), languageCode + ".xml");
if (!Utilities.copyFile(file, finalFile)) {
if (!AndroidUtilities.copyFile(file, finalFile)) {
return false;
}
@ -490,7 +493,6 @@ public class LocaleController {
try {
if (stream != null) {
stream.close();
stream = null;
}
} catch (Exception e) {
FileLog.e("tmessages", e);
@ -508,7 +510,7 @@ public class LocaleController {
return;
}
try {
Locale newLocale = null;
Locale newLocale;
if (localeInfo.shortName != null) {
String[] args = localeInfo.shortName.split("_");
if (args.length == 1) {
@ -607,11 +609,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 {
@ -677,50 +680,60 @@ public class LocaleController {
}
public static String formatDate(long date) {
Calendar rightNow = Calendar.getInstance();
int day = rightNow.get(Calendar.DAY_OF_YEAR);
int year = rightNow.get(Calendar.YEAR);
rightNow.setTimeInMillis(date * 1000);
int dateDay = rightNow.get(Calendar.DAY_OF_YEAR);
int dateYear = rightNow.get(Calendar.YEAR);
try {
Calendar rightNow = Calendar.getInstance();
int day = rightNow.get(Calendar.DAY_OF_YEAR);
int year = rightNow.get(Calendar.YEAR);
rightNow.setTimeInMillis(date * 1000);
int dateDay = rightNow.get(Calendar.DAY_OF_YEAR);
int dateYear = rightNow.get(Calendar.YEAR);
if (dateDay == day && year == dateYear) {
return formatterDay.format(new Date(date * 1000));
} else if (dateDay + 1 == day && year == dateYear) {
return getString("Yesterday", R.string.Yesterday);
} else if (year == dateYear) {
return formatterMonth.format(new Date(date * 1000));
} else {
return formatterYear.format(new Date(date * 1000));
if (dateDay == day && year == dateYear) {
return formatterDay.format(new Date(date * 1000));
} else if (dateDay + 1 == day && year == dateYear) {
return getString("Yesterday", R.string.Yesterday);
} else if (year == dateYear) {
return formatterMonth.format(new Date(date * 1000));
} else {
return formatterYear.format(new Date(date * 1000));
}
} catch (Exception e) {
FileLog.e("tmessages", e);
}
return "LOC_ERR";
}
public static String formatDateOnline(long date) {
Calendar rightNow = Calendar.getInstance();
int day = rightNow.get(Calendar.DAY_OF_YEAR);
int year = rightNow.get(Calendar.YEAR);
rightNow.setTimeInMillis(date * 1000);
int dateDay = rightNow.get(Calendar.DAY_OF_YEAR);
int dateYear = rightNow.get(Calendar.YEAR);
try {
Calendar rightNow = Calendar.getInstance();
int day = rightNow.get(Calendar.DAY_OF_YEAR);
int year = rightNow.get(Calendar.YEAR);
rightNow.setTimeInMillis(date * 1000);
int dateDay = rightNow.get(Calendar.DAY_OF_YEAR);
int dateYear = rightNow.get(Calendar.YEAR);
if (dateDay == day && year == dateYear) {
return String.format("%s %s %s", LocaleController.getString("LastSeen", R.string.LastSeen), LocaleController.getString("TodayAt", R.string.TodayAt), formatterDay.format(new Date(date * 1000)));
} else if (dateDay + 1 == day && year == dateYear) {
return String.format("%s %s %s", LocaleController.getString("LastSeen", R.string.LastSeen), LocaleController.getString("YesterdayAt", R.string.YesterdayAt), formatterDay.format(new Date(date * 1000)));
} else if (year == dateYear) {
String format = LocaleController.formatString("formatDateAtTime", R.string.formatDateAtTime, formatterMonth.format(new Date(date * 1000)), formatterDay.format(new Date(date * 1000)));
return String.format("%s %s", LocaleController.getString("LastSeenDate", R.string.LastSeenDate), format);
} else {
String format = LocaleController.formatString("formatDateAtTime", R.string.formatDateAtTime, formatterYear.format(new Date(date * 1000)), formatterDay.format(new Date(date * 1000)));
return String.format("%s %s", LocaleController.getString("LastSeenDate", R.string.LastSeenDate), format);
if (dateDay == day && year == dateYear) {
return String.format("%s %s %s", LocaleController.getString("LastSeen", R.string.LastSeen), LocaleController.getString("TodayAt", R.string.TodayAt), formatterDay.format(new Date(date * 1000)));
} else if (dateDay + 1 == day && year == dateYear) {
return String.format("%s %s %s", LocaleController.getString("LastSeen", R.string.LastSeen), LocaleController.getString("YesterdayAt", R.string.YesterdayAt), formatterDay.format(new Date(date * 1000)));
} else if (year == dateYear) {
String format = LocaleController.formatString("formatDateAtTime", R.string.formatDateAtTime, formatterMonth.format(new Date(date * 1000)), formatterDay.format(new Date(date * 1000)));
return String.format("%s %s", LocaleController.getString("LastSeenDate", R.string.LastSeenDate), format);
} else {
String format = LocaleController.formatString("formatDateAtTime", R.string.formatDateAtTime, formatterYear.format(new Date(date * 1000)), formatterDay.format(new Date(date * 1000)));
return String.format("%s %s", LocaleController.getString("LastSeenDate", R.string.LastSeenDate), format);
}
} catch (Exception e) {
FileLog.e("tmessages", e);
}
return "LOC_ERR";
}
private FastDateFormat createFormatter(Locale locale, String format, String defaultFormat) {
if (format == null || format.length() == 0) {
format = defaultFormat;
}
FastDateFormat formatter = null;
FastDateFormat formatter;
try {
formatter = FastDateFormat.getInstance(format, locale);
} catch (Exception e) {
@ -753,25 +766,30 @@ public class LocaleController {
}
public static String stringForMessageListDate(long date) {
Calendar rightNow = Calendar.getInstance();
int day = rightNow.get(Calendar.DAY_OF_YEAR);
int year = rightNow.get(Calendar.YEAR);
rightNow.setTimeInMillis(date * 1000);
int dateDay = rightNow.get(Calendar.DAY_OF_YEAR);
int dateYear = rightNow.get(Calendar.YEAR);
try {
Calendar rightNow = Calendar.getInstance();
int day = rightNow.get(Calendar.DAY_OF_YEAR);
int year = rightNow.get(Calendar.YEAR);
rightNow.setTimeInMillis(date * 1000);
int dateDay = rightNow.get(Calendar.DAY_OF_YEAR);
int dateYear = rightNow.get(Calendar.YEAR);
if (year != dateYear) {
return formatterYear.format(new Date(date * 1000));
} else {
int dayDiff = dateDay - day;
if(dayDiff == 0 || dayDiff == -1 && (int)(System.currentTimeMillis() / 1000) - date < 60 * 60 * 8) {
return formatterDay.format(new Date(date * 1000));
} else if(dayDiff > -7 && dayDiff <= -1) {
return formatterWeek.format(new Date(date * 1000));
if (year != dateYear) {
return formatterYear.format(new Date(date * 1000));
} else {
return formatterMonth.format(new Date(date * 1000));
int dayDiff = dateDay - day;
if(dayDiff == 0 || dayDiff == -1 && (int)(System.currentTimeMillis() / 1000) - date < 60 * 60 * 8) {
return formatterDay.format(new Date(date * 1000));
} else if(dayDiff > -7 && dayDiff <= -1) {
return formatterWeek.format(new Date(date * 1000));
} else {
return formatterMonth.format(new Date(date * 1000));
}
}
} catch (Exception e) {
FileLog.e("tmessages", e);
}
return "LOC_ERR";
}
public static String formatUserStatus(TLRPC.User user) {

View File

@ -107,7 +107,6 @@ public class LruCache {
if (previous != null) {
entryRemoved(false, key, previous, value);
ImageLoader.getInstance().callGC();
}
trimToSize(maxSize, key);
@ -148,7 +147,6 @@ public class LruCache {
entryRemoved(true, key, value, null);
}
ImageLoader.getInstance().callGC();
}
}
@ -183,7 +181,6 @@ public class LruCache {
}
entryRemoved(false, key, previous, null);
ImageLoader.getInstance().callGC();
}
return previous;

View File

@ -8,6 +8,7 @@
package org.telegram.android;
import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import android.app.Activity;
import android.app.ProgressDialog;
@ -117,17 +118,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<PhotoEntry> photos = new ArrayList<>();
public HashMap<Integer, PhotoEntry> 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 +155,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 +181,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 +193,8 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
private final static int PROCESSOR_TYPE_TI = 5;
private final Object videoConvertSync = new Object();
private HashMap<Long, Long> typingTimes = new HashMap<>();
private SensorManager sensorManager;
private Sensor proximitySensor;
private boolean ignoreProximity;
@ -227,6 +244,7 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
private Timer progressTimer = null;
private final Object progressTimerSync = new Object();
private boolean useFrontSpeaker;
private int buffersWrited;
private AudioRecord audioRecorder = null;
private TLRPC.TL_audio recordingAudio = null;
@ -255,7 +273,7 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
@Override
public void run() {
if (audioRecorder != null) {
ByteBuffer buffer = null;
ByteBuffer buffer;
if (!recordBuffers.isEmpty()) {
buffer = recordBuffers.get(0);
recordBuffers.remove(0);
@ -550,6 +568,7 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
videoDownloadQueue.clear();
downloadQueueKeys.clear();
videoConvertQueue.clear();
typingTimes.clear();
cancelVideoConvert(null);
}
@ -674,7 +693,7 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
if (downloadObject.object instanceof TLRPC.Audio) {
FileLoader.getInstance().loadFile((TLRPC.Audio)downloadObject.object, false);
} else if (downloadObject.object instanceof TLRPC.PhotoSize) {
FileLoader.getInstance().loadFile((TLRPC.PhotoSize)downloadObject.object, false);
FileLoader.getInstance().loadFile((TLRPC.PhotoSize)downloadObject.object, null, false);
} else if (downloadObject.object instanceof TLRPC.Video) {
FileLoader.getInstance().loadFile((TLRPC.Video)downloadObject.object, false);
} else if (downloadObject.object instanceof TLRPC.Document) {
@ -710,7 +729,7 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
if (downloadObject != null) {
downloadQueueKeys.remove(fileName);
if (state == 0 || state == 2) {
MessagesStorage.getInstance().removeFromDownloadQueue(downloadObject.id, downloadObject.type, state != 0);
MessagesStorage.getInstance().removeFromDownloadQueue(downloadObject.id, downloadObject.type, false /*state != 0*/);
}
if (downloadObject.type == AUTODOWNLOAD_MASK_PHOTO) {
photoDownloadQueue.remove(downloadObject);
@ -975,6 +994,29 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
}
listenerInProgress = false;
processLaterArrays();
try {
ArrayList<SendMessagesHelper.DelayedMessage> 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<Integer> markAsDeletedMessages = (ArrayList<Integer>)args[0];
@ -1066,10 +1108,12 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
} catch (Exception e) {
FileLog.e("tmessages", e);
}
buffersWrited++;
if (count > 0) {
final long pcm = buffer.pcmOffset;
final int marker = buffer.finished == 1 ? buffer.size : -1;
final int marker = buffer.finished == 1 ? count : -1;
final int finalBuffersWrited = buffersWrited;
AndroidUtilities.runOnUIThread(new Runnable() {
@Override
public void run() {
@ -1078,6 +1122,9 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
if (audioTrackPlayer != null) {
audioTrackPlayer.setNotificationMarkerPosition(1);
}
if (finalBuffersWrited == 1) {
clenupPlayer(true);
}
}
}
});
@ -1100,12 +1147,17 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
});
}
private boolean isNearToSensor(float value) {
return value < 5.0f && value != proximitySensor.getMaximumRange();
}
@Override
public void onSensorChanged(SensorEvent event) {
if (proximitySensor != null && audioTrackPlayer == null && audioPlayer == null || isPaused || (useFrontSpeaker == (event.values[0] < proximitySensor.getMaximumRange() / 10))) {
FileLog.e("tmessages", "proximity changed to " + event.values[0]);
if (proximitySensor != null && audioTrackPlayer == null && audioPlayer == null || isPaused || (useFrontSpeaker == isNearToSensor(event.values[0]))) {
return;
}
boolean newValue = event.values[0] < proximitySensor.getMaximumRange() / 10;
boolean newValue = isNearToSensor(event.values[0]);
try {
if (newValue && NotificationsController.getInstance().audioManager.isWiredHeadsetOn()) {
return;
@ -1190,6 +1242,7 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
}
stopProgressTimer();
lastProgress = 0;
buffersWrited = 0;
isPaused = false;
MessageObject lastFile = playingMessageObject;
playingMessageObject.audioProgress = 0.0f;
@ -1424,6 +1477,7 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
if (audioTrackPlayer == null && audioPlayer == null || messageObject == null || playingMessageObject == null || playingMessageObject != null && playingMessageObject.getId() != messageObject.getId()) {
return false;
}
stopProgressTimer();
try {
if (audioPlayer != null) {
audioPlayer.pause();
@ -1440,11 +1494,12 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
}
public boolean resumeAudio(MessageObject messageObject) {
startProximitySensor();
if (audioTrackPlayer == null && audioPlayer == null || messageObject == null || playingMessageObject == null || playingMessageObject != null && playingMessageObject.getId() != messageObject.getId()) {
return false;
}
startProximitySensor();
try {
startProgressTimer();
if (audioPlayer != null) {
audioPlayer.start();
} else if (audioTrackPlayer != null) {
@ -1666,9 +1721,9 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
try {
File destFile = null;
if (type == 0) {
destFile = Utilities.generatePicturePath();
destFile = AndroidUtilities.generatePicturePath();
} else if (type == 1) {
destFile = Utilities.generateVideoPath();
destFile = AndroidUtilities.generateVideoPath();
} else if (type == 2) {
File f = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);
destFile = new File(f, name);
@ -1685,8 +1740,8 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
source = new FileInputStream(sourceFile).getChannel();
destination = new FileOutputStream(destFile).getChannel();
long size = source.size();
for (long a = 0; a < size; a += 1024) {
destination.transferFrom(source, a, Math.min(1024, size - a));
for (long a = 0; a < size; a += 4096) {
destination.transferFrom(source, a, Math.min(4096, size - a));
if (finalProgress != null) {
if (lastProgress <= System.currentTimeMillis() - 500) {
lastProgress = System.currentTimeMillis();
@ -1717,7 +1772,7 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
}
if (result && (type == 0 || type == 1)) {
Utilities.addMediaToGallery(Uri.fromFile(destFile));
AndroidUtilities.addMediaToGallery(Uri.fromFile(destFile));
}
} catch (Exception e) {
FileLog.e("tmessages", e);
@ -1970,10 +2025,12 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
@Override
public void run() {
final ArrayList<AlbumEntry> albumsSorted = new ArrayList<>();
final ArrayList<AlbumEntry> videoAlbumsSorted = new ArrayList<>();
HashMap<Integer, AlbumEntry> 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 +2055,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 +2067,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 +2091,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);
}
});
}
@ -2082,6 +2200,7 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
}
}
@SuppressLint("NewApi")
public static MediaCodecInfo selectCodec(String mimeType) {
int numCodecs = MediaCodecList.getCodecCount();
MediaCodecInfo lastCodecInfo = null;
@ -2118,6 +2237,7 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
}
}
@SuppressLint("NewApi")
public static int selectColorFormat(MediaCodecInfo codecInfo, String mimeType) {
MediaCodecInfo.CodecCapabilities capabilities = codecInfo.getCapabilitiesForType(mimeType);
int lastColorFormat = 0;
@ -2270,7 +2390,7 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
}
private void checkConversionCanceled() throws Exception {
boolean cancelConversion = false;
boolean cancelConversion;
synchronized (videoConvertSync) {
cancelConversion = cancelCurrentVideoConversion;
}
@ -2281,15 +2401,15 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
@TargetApi(16)
private boolean convertVideo(final MessageObject messageObject) {
String videoPath = messageObject.messageOwner.videoEditedInfo.originalPath;
long startTime = messageObject.messageOwner.videoEditedInfo.startTime;
long endTime = messageObject.messageOwner.videoEditedInfo.endTime;
int resultWidth = messageObject.messageOwner.videoEditedInfo.resultWidth;
int resultHeight = messageObject.messageOwner.videoEditedInfo.resultHeight;
int rotationValue = messageObject.messageOwner.videoEditedInfo.rotationValue;
int originalWidth = messageObject.messageOwner.videoEditedInfo.originalWidth;
int originalHeight = messageObject.messageOwner.videoEditedInfo.originalHeight;
int bitrate = messageObject.messageOwner.videoEditedInfo.bitrate;
String videoPath = messageObject.videoEditedInfo.originalPath;
long startTime = messageObject.videoEditedInfo.startTime;
long endTime = messageObject.videoEditedInfo.endTime;
int resultWidth = messageObject.videoEditedInfo.resultWidth;
int resultHeight = messageObject.videoEditedInfo.resultHeight;
int rotationValue = messageObject.videoEditedInfo.rotationValue;
int originalWidth = messageObject.videoEditedInfo.originalWidth;
int originalHeight = messageObject.videoEditedInfo.originalHeight;
int bitrate = messageObject.videoEditedInfo.bitrate;
int rotateRender = 0;
File cacheFile = new File(messageObject.messageOwner.attachPath);
@ -2352,7 +2472,7 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
checkConversionCanceled();
if (resultWidth != originalWidth || resultHeight != originalHeight) {
int videoIndex = -5;
int videoIndex;
videoIndex = selectTrack(extractor, false);
if (videoIndex >= 0) {
MediaCodec decoder = null;
@ -2368,7 +2488,7 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
int swapUV = 0;
int videoTrackIndex = -5;
int colorFormat = 0;
int colorFormat;
int processorType = PROCESSOR_TYPE_OTHER;
String manufacturer = Build.MANUFACTURER.toLowerCase();
if (Build.VERSION.SDK_INT < 18) {
@ -2487,7 +2607,7 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
if (index == videoIndex) {
int inputBufIndex = decoder.dequeueInputBuffer(TIMEOUT_USEC);
if (inputBufIndex >= 0) {
ByteBuffer inputBuf = null;
ByteBuffer inputBuf;
if (Build.VERSION.SDK_INT < 21) {
inputBuf = decoderInputBuffers[inputBufIndex];
} else {
@ -2533,7 +2653,7 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
} else if (encoderStatus < 0) {
throw new RuntimeException("unexpected result from encoder.dequeueOutputBuffer: " + encoderStatus);
} else {
ByteBuffer encodedData = null;
ByteBuffer encodedData;
if (Build.VERSION.SDK_INT < 21) {
encodedData = encoderOutputBuffers[encoderStatus];
} else {
@ -2595,7 +2715,7 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
} else if (decoderStatus < 0) {
throw new RuntimeException("unexpected result from decoder.dequeueOutputBuffer: " + decoderStatus);
} else {
boolean doRender = false;
boolean doRender;
if (Build.VERSION.SDK_INT >= 18) {
doRender = info.size != 0;
} else {
@ -2672,21 +2792,17 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
if (outputSurface != null) {
outputSurface.release();
outputSurface = null;
}
if (inputSurface != null) {
inputSurface.release();
inputSurface = null;
}
if (decoder != null) {
decoder.stop();
decoder.release();
decoder = null;
}
if (encoder != null) {
encoder.stop();
encoder.release();
encoder = null;
}
checkConversionCanceled();
@ -2706,7 +2822,6 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
} finally {
if (extractor != null) {
extractor.release();
extractor = null;
}
if (mediaMuxer != null) {
try {
@ -2714,7 +2829,6 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
} catch (Exception e) {
FileLog.e("tmessages", e);
}
mediaMuxer = null;
}
FileLog.e("tmessages", "time = " + (System.currentTimeMillis() - time));
}

View File

@ -25,6 +25,7 @@ import org.telegram.messenger.TLRPC;
import org.telegram.messenger.R;
import org.telegram.messenger.UserConfig;
import org.telegram.ui.Components.URLSpanNoUnderline;
import org.telegram.ui.Components.URLSpanNoUnderlineBold;
import java.util.AbstractMap;
import java.util.ArrayList;
@ -42,17 +43,19 @@ 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;
public String dateKey;
public String monthKey;
public boolean deleted = false;
public boolean deleted;
public float audioProgress;
public int audioProgressSec;
public ArrayList<TLRPC.PhotoSize> photoThumbs;
public VideoEditedInfo videoEditedInfo;
private static TextPaint textPaint;
public static TextPaint textPaint;
public int lastLineWidth;
public int textWidth;
public int textHeight;
@ -144,17 +147,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("ActionAddUserSelfYou", R.string.ActionAddUserSelfYou);
} 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 +300,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);
@ -312,9 +333,12 @@ public class MessageObject {
if (message instanceof TLRPC.TL_message || message instanceof TLRPC.TL_messageForwarded_old2) {
if (isMediaEmpty()) {
contentType = type = 0;
if (messageText.length() == 0) {
messageText = "Empty message";
}
} 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) {
@ -332,9 +356,6 @@ public class MessageObject {
type = 8;
} else if (message.media.document.mime_type.equals("image/webp") && isSticker()) {
type = 13;
if (messageOwner.media.document.thumb != null && messageOwner.media.document.thumb.location != null) {
messageOwner.media.document.thumb.location.ext = "webp";
}
} else {
type = 9;
}
@ -374,6 +395,12 @@ public class MessageObject {
monthKey = String.format("%d_%02d", dateYear, dateMonth);
}
if (messageOwner.message != null && messageOwner.id < 0 && messageOwner.message.length() > 6 && messageOwner.media instanceof TLRPC.TL_messageMediaVideo) {
videoEditedInfo = new VideoEditedInfo();
videoEditedInfo.parseString(messageOwner.message);
}
generateCaption();
if (generateLayout) {
generateLayout();
}
@ -438,7 +465,7 @@ public class MessageObject {
if (messageOwner.media.webpage.photo != null) {
if (!update || photoThumbs == null) {
photoThumbs = new ArrayList<>(messageOwner.media.webpage.photo.sizes);
} else if (photoThumbs != null && !photoThumbs.isEmpty()) {
} else if (!photoThumbs.isEmpty()) {
for (TLRPC.PhotoSize photoObject : photoThumbs) {
for (TLRPC.PhotoSize size : messageOwner.media.webpage.photo.sizes) {
if (size instanceof TLRPC.TL_photoSizeEmpty) {
@ -459,7 +486,7 @@ public class MessageObject {
public CharSequence replaceWithLink(CharSequence source, String param, TLRPC.User user) {
String name = ContactsController.formatName(user.first_name, user.last_name);
int start = TextUtils.indexOf(source, param);
URLSpanNoUnderline span = new URLSpanNoUnderline("" + user.id);
URLSpanNoUnderlineBold span = new URLSpanNoUnderlineBold("" + user.id);
SpannableStringBuilder builder = new SpannableStringBuilder(TextUtils.replace(source, new String[]{param}, new String[]{name}));
builder.setSpan(span, start, start + name.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
return builder;
@ -569,6 +596,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 +641,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;
@ -616,7 +671,7 @@ public class MessageObject {
}
}
StaticLayout textLayout = null;
StaticLayout textLayout;
try {
textLayout = new StaticLayout(messageText, textPaint, maxWidth, Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false);
@ -735,7 +790,6 @@ public class MessageObject {
if (a == blocksCount - 1) {
lastLineWidth = lastLineWidthWithLeft;
}
linesMaxWidth = linesMaxWidthWithLeft;
} else if (a == blocksCount - 1) {
lastLineWidth = linesMaxWidth;
}
@ -763,10 +817,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 +859,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;
}
@ -859,6 +945,20 @@ public class MessageObject {
return false;
}
public static TLRPC.InputStickerSet getInputStickerSet(TLRPC.Message message) {
if (message.media != null && message.media.document != null) {
for (TLRPC.DocumentAttribute attribute : message.media.document.attributes) {
if (attribute instanceof TLRPC.TL_documentAttributeSticker) {
if (attribute.stickerset instanceof TLRPC.TL_inputStickerSetEmpty) {
return null;
}
return attribute.stickerset;
}
}
}
return null;
}
public String getStrickerChar() {
if (messageOwner.media != null && messageOwner.media.document != null) {
for (TLRPC.DocumentAttribute attribute : messageOwner.media.document.attributes) {
@ -911,8 +1011,8 @@ public class MessageObject {
}
return photoHeight + AndroidUtilities.dp(14);
} else {
int photoHeight = 0;
int photoWidth = 0;
int photoHeight;
int photoWidth;
if (AndroidUtilities.isTablet()) {
photoWidth = (int) (AndroidUtilities.getMinTabletSide() * 0.7f);
@ -930,35 +1030,22 @@ public class MessageObject {
if (currentPhotoObject != null) {
float scale = (float) currentPhotoObject.w / (float) photoWidth;
int w = (int) (currentPhotoObject.w / scale);
int h = (int) (currentPhotoObject.h / scale);
if (w == 0) {
w = AndroidUtilities.dp(100);
}
if (h == 0) {
h = AndroidUtilities.dp(100);
}
if (h > photoHeight) {
float scale2 = h;
h = photoHeight;
scale2 /= h;
w = (int) (w / scale2);
} else if (h < AndroidUtilities.dp(120)) {
h = AndroidUtilities.dp(120);
float hScale = (float) currentPhotoObject.h / h;
if (currentPhotoObject.w / hScale < photoWidth) {
w = (int) (currentPhotoObject.w / hScale);
}
}
if (isSecretPhoto()) {
if (AndroidUtilities.isTablet()) {
w = h = (int) (AndroidUtilities.getMinTabletSide() * 0.5f);
h = (int) (AndroidUtilities.getMinTabletSide() * 0.5f);
} else {
w = h = (int) (Math.min(AndroidUtilities.displaySize.x, AndroidUtilities.displaySize.y) * 0.5f);
h = (int) (Math.min(AndroidUtilities.displaySize.x, AndroidUtilities.displaySize.y) * 0.5f);
}
}
photoWidth = w;
photoHeight = h;
}
return photoHeight + AndroidUtilities.dp(14);
@ -969,6 +1056,10 @@ public class MessageObject {
return isStickerMessage(messageOwner);
}
public TLRPC.InputStickerSet getInputStickerSet() {
return getInputStickerSet(messageOwner);
}
public boolean isForwarded() {
return (messageOwner.flags & TLRPC.MESSAGE_FLAG_FWD) != 0;
}

View File

@ -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);
}
}
@ -282,7 +282,7 @@ public class MessagesStorage {
database.executeFast("PRAGMA user_version = 4").stepThis().dispose();
version = 4;
}
if (version == 4 && version < 6) {
if (version == 4) {
database.executeFast("CREATE TABLE IF NOT EXISTS enc_tasks_v2(mid INTEGER PRIMARY KEY, date INTEGER)").stepThis().dispose();
database.executeFast("CREATE INDEX IF NOT EXISTS date_idx_enc_tasks_v2 ON enc_tasks_v2(date);").stepThis().dispose();
database.beginTransaction();
@ -290,12 +290,12 @@ public class MessagesStorage {
SQLitePreparedStatement state = database.executeFast("REPLACE INTO enc_tasks_v2 VALUES(?, ?)");
if (cursor.next()) {
int date = cursor.intValue(0);
int length = 0;
int length;
ByteBufferDesc data = buffersStorage.getFreeBuffer(cursor.byteArrayLength(1));
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();
}
@ -313,7 +313,7 @@ public class MessagesStorage {
database.executeFast("PRAGMA user_version = 6").stepThis().dispose();
version = 6;
}
if (version == 6 && version < 7) {
if (version == 6) {
database.executeFast("CREATE TABLE IF NOT EXISTS messages_seq(mid INTEGER PRIMARY KEY, seq_in INTEGER, seq_out INTEGER);").stepThis().dispose();
database.executeFast("CREATE INDEX IF NOT EXISTS seq_idx_messages_seq ON messages_seq(seq_in, seq_out);").stepThis().dispose();
database.executeFast("ALTER TABLE enc_chats ADD COLUMN layer INTEGER default 0").stepThis().dispose();
@ -333,7 +333,7 @@ public class MessagesStorage {
database.executeFast("PRAGMA user_version = 9").stepThis().dispose();
version = 9;
}*/
if ((version == 7 || version == 8 || version == 9) && version < 10) {
if (version == 7 || version == 8 || version == 9) {
database.executeFast("ALTER TABLE enc_chats ADD COLUMN use_count INTEGER default 0").stepThis().dispose();
database.executeFast("ALTER TABLE enc_chats ADD COLUMN exchange_id INTEGER default 0").stepThis().dispose();
database.executeFast("ALTER TABLE enc_chats ADD COLUMN key_date INTEGER default 0").stepThis().dispose();
@ -343,17 +343,17 @@ public class MessagesStorage {
database.executeFast("PRAGMA user_version = 10").stepThis().dispose();
version = 10;
}
if (version == 10 && version < 11) {
if (version == 10) {
database.executeFast("CREATE TABLE IF NOT EXISTS web_recent_v3(id TEXT, type INTEGER, image_url TEXT, thumb_url TEXT, local_url TEXT, width INTEGER, height INTEGER, size INTEGER, date INTEGER, PRIMARY KEY (id, type));").stepThis().dispose();
database.executeFast("PRAGMA user_version = 11").stepThis().dispose();
version = 11;
}
if (version == 11 && version < 12) {
if (version == 11) {
database.executeFast("CREATE TABLE IF NOT EXISTS stickers(id INTEGER PRIMARY KEY, data BLOB, date INTEGER);").stepThis().dispose();
database.executeFast("PRAGMA user_version = 12").stepThis().dispose();
version = 12;
}
if (version == 12 && version < 13) {
if (version == 12) {
database.executeFast("DROP INDEX IF EXISTS uid_mid_idx_media;").stepThis().dispose();
database.executeFast("DROP INDEX IF EXISTS mid_idx_media;").stepThis().dispose();
database.executeFast("DROP INDEX IF EXISTS uid_date_mid_idx_media;").stepThis().dispose();
@ -370,21 +370,27 @@ public class MessagesStorage {
database.executeFast("PRAGMA user_version = 13").stepThis().dispose();
version = 13;
}
if (version == 13 && version < 14) {
if (version == 13) {
database.executeFast("ALTER TABLE messages ADD COLUMN replydata BLOB default NULL").stepThis().dispose();
database.executeFast("PRAGMA user_version = 14").stepThis().dispose();
version = 14;
}
if (version == 14 && version < 15) {
if (version == 14) {
database.executeFast("CREATE TABLE IF NOT EXISTS hashtag_recent_v2(id TEXT PRIMARY KEY, date INTEGER);").stepThis().dispose();
database.executeFast("PRAGMA user_version = 15").stepThis().dispose();
version = 15;
}
if (version == 15 && version < 16) {
if (version == 15) {
database.executeFast("CREATE TABLE IF NOT EXISTS webpage_pending(id INTEGER, mid INTEGER, PRIMARY KEY (id, mid));").stepThis().dispose();
database.executeFast("PRAGMA user_version = 16").stepThis().dispose();
version = 16;
}
if (version == 16) {
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<Integer> chatIds = new ArrayList<>();
ArrayList<Integer> 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);
@ -758,17 +764,22 @@ public class MessagesStorage {
public void run() {
try {
SQLiteCursor cursor = database.queryFinalized("SELECT data FROM wallpapers WHERE 1");
ArrayList<TLRPC.WallPaper> wallPapers = new ArrayList<>();
final ArrayList<TLRPC.WallPaper> wallPapers = new ArrayList<>();
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);
}
cursor.dispose();
NotificationCenter.getInstance().postNotificationName(NotificationCenter.wallpapersDidLoaded, wallPapers);
AndroidUtilities.runOnUIThread(new Runnable() {
@Override
public void run() {
NotificationCenter.getInstance().postNotificationName(NotificationCenter.wallpapersDidLoaded, wallPapers);
}
});
} catch (Exception e) {
FileLog.e("tmessages", e);
}
@ -876,7 +887,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 +961,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);
@ -1063,9 +1074,9 @@ public class MessagesStorage {
int minDate = Integer.MAX_VALUE;
SparseArray<ArrayList<Integer>> messages = new SparseArray<>();
StringBuilder mids = new StringBuilder();
SQLiteCursor cursor = null;
SQLiteCursor cursor;
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 +1158,7 @@ public class MessagesStorage {
cursor.dispose();
} else if (inbox != null && !inbox.isEmpty()) {
for (HashMap.Entry<Integer, Integer> 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 +1266,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 +1329,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 +1394,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 +1624,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)) {
@ -1757,7 +1768,7 @@ public class MessagesStorage {
ArrayList<Integer> replyMessages = new ArrayList<>();
HashMap<Integer, ArrayList<TLRPC.Message>> replyMessageOwners = new HashMap<>();
SQLiteCursor cursor = null;
SQLiteCursor cursor;
int lower_id = (int)dialog_id;
if (lower_id != 0) {
@ -1798,14 +1809,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 +1854,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 +1887,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 +1911,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 +1998,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 +2102,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 +2385,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 +2482,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 +2507,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 +2524,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 +2646,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 +2703,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 +2877,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 +2966,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<Long, TLRPC.Message> pair : messagesMap.entrySet()) {
Long key = pair.getKey();
@ -2987,6 +2999,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();
@ -3232,7 +3246,6 @@ public class MessagesStorage {
} finally {
if (state != null) {
state.dispose();
state = null;
}
}
@ -3294,8 +3307,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 +3352,22 @@ public class MessagesStorage {
}
private void markMessagesAsReadInternal(HashMap<Integer, Integer> inbox, HashMap<Integer, Integer> outbox, HashMap<Integer, Integer> encryptedMessages) {
if (Thread.currentThread().getId() != storageQueue.getId()) {
throw new RuntimeException("wrong db thread");
}
try {
if (inbox != null) {
for (HashMap.Entry<Integer, Integer> 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 > 0 AND mid <= %d AND read_state IN(0,2) AND out = 0", entry.getKey(), entry.getValue())).stepThis().dispose();
}
}
if (outbox != null) {
for (HashMap.Entry<Integer, Integer> 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 > 0 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<Integer, Integer> 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 +3380,22 @@ public class MessagesStorage {
}
}
public void markMessagesContentAsRead(final ArrayList<Integer> 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<Integer, Integer> inbox, final HashMap<Integer, Integer> outbox, final HashMap<Integer, Integer> encryptedMessages, boolean useQueue) {
if (useQueue) {
storageQueue.postRunnable(new Runnable() {
@ -3433,7 +3461,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 +3555,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 +3695,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 +3760,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 +3859,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 +3878,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 +3891,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);

View File

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

View File

@ -8,6 +8,10 @@
package org.telegram.android;
import org.telegram.messenger.ApplicationLoader;
import org.telegram.messenger.BuildVars;
import org.telegram.messenger.FileLog;
import java.util.ArrayList;
import java.util.HashMap;
@ -32,7 +36,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++;
@ -51,11 +55,13 @@ public class NotificationCenter {
public static final int didSetPasscode = totalEvents++;
public static final int didSetTwoStepPassword = totalEvents++;
public static final int screenStateChanged = totalEvents++;
public static final int appSwitchedToForeground = totalEvents++;
public static final int didLoadedReplyMessages = totalEvents++;
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++;
@ -91,14 +97,31 @@ public class NotificationCenter {
public static final int audioDidStarted = totalEvents++;
public static final int audioRouteChanged = totalEvents++;
final private HashMap<Integer, ArrayList<Object>> observers = new HashMap<>();
final private HashMap<Integer, Object> removeAfterBroadcast = new HashMap<>();
final private HashMap<Integer, Object> addAfterBroadcast = new HashMap<>();
private HashMap<Integer, ArrayList<Object>> observers = new HashMap<>();
private HashMap<Integer, Object> removeAfterBroadcast = new HashMap<>();
private HashMap<Integer, Object> addAfterBroadcast = new HashMap<>();
private ArrayList<DelayedPost> delayedPosts = new ArrayList<>(10);
private int broadcasting = 0;
private boolean animationInProgress;
public interface NotificationCenterDelegate {
void didReceivedNotification(int id, Object... args);
}
private class DelayedPost {
private DelayedPost(int id, Object[] args) {
this.id = id;
this.args = args;
}
private int id;
private Object[] args;
}
private static volatile NotificationCenter Instance = null;
public static NotificationCenter getInstance() {
NotificationCenter localInstance = Instance;
if (localInstance == null) {
@ -112,66 +135,97 @@ public class NotificationCenter {
return localInstance;
}
public interface NotificationCenterDelegate {
void didReceivedNotification(int id, Object... args);
public void setAnimationInProgress(boolean value) {
animationInProgress = value;
if (!animationInProgress && !delayedPosts.isEmpty()) {
for (DelayedPost delayedPost : delayedPosts) {
postNotificationNameInternal(delayedPost.id, true, delayedPost.args);
}
delayedPosts.clear();
}
}
public void postNotificationName(int id, Object... args) {
synchronized (observers) {
broadcasting++;
ArrayList<Object> objects = observers.get(id);
if (objects != null) {
for (Object obj : objects) {
((NotificationCenterDelegate)obj).didReceivedNotification(id, args);
}
boolean allowDuringAnimation = false;
if (id == dialogsNeedReload || id == closeChats || id == messagesDidLoaded || id == mediaCountDidLoaded || id == mediaDidLoaded) {
allowDuringAnimation = true;
}
postNotificationNameInternal(id, allowDuringAnimation, args);
}
public void postNotificationNameInternal(int id, boolean allowDuringAnimation, Object... args) {
if (BuildVars.DEBUG_VERSION) {
if (Thread.currentThread() != ApplicationLoader.applicationHandler.getLooper().getThread()) {
throw new RuntimeException("postNotificationName allowed only from MAIN thread");
}
broadcasting--;
if (broadcasting == 0) {
if (!removeAfterBroadcast.isEmpty()) {
for (HashMap.Entry<Integer, Object> entry : removeAfterBroadcast.entrySet()) {
removeObserver(entry.getValue(), entry.getKey());
}
removeAfterBroadcast.clear();
}
if (!allowDuringAnimation && animationInProgress) {
DelayedPost delayedPost = new DelayedPost(id, args);
delayedPosts.add(delayedPost);
if (BuildVars.DEBUG_VERSION) {
FileLog.e("tmessages", "delay post notification " + id + " with args count = " + args.length);
}
return;
}
broadcasting++;
ArrayList<Object> objects = observers.get(id);
if (objects != null) {
for (Object obj : objects) {
((NotificationCenterDelegate) obj).didReceivedNotification(id, args);
}
}
broadcasting--;
if (broadcasting == 0) {
if (!removeAfterBroadcast.isEmpty()) {
for (HashMap.Entry<Integer, Object> entry : removeAfterBroadcast.entrySet()) {
removeObserver(entry.getValue(), entry.getKey());
}
if (!addAfterBroadcast.isEmpty()) {
for (HashMap.Entry<Integer, Object> entry : addAfterBroadcast.entrySet()) {
addObserver(entry.getValue(), entry.getKey());
}
addAfterBroadcast.clear();
removeAfterBroadcast.clear();
}
if (!addAfterBroadcast.isEmpty()) {
for (HashMap.Entry<Integer, Object> entry : addAfterBroadcast.entrySet()) {
addObserver(entry.getValue(), entry.getKey());
}
addAfterBroadcast.clear();
}
}
}
public void addObserver(Object observer, int id) {
synchronized (observers) {
if (broadcasting != 0) {
addAfterBroadcast.put(id, observer);
return;
if (BuildVars.DEBUG_VERSION) {
if (Thread.currentThread() != ApplicationLoader.applicationHandler.getLooper().getThread()) {
throw new RuntimeException("addObserver allowed only from MAIN thread");
}
ArrayList<Object> objects = observers.get(id);
if (objects == null) {
observers.put(id, (objects = new ArrayList<>()));
}
if (objects.contains(observer)) {
return;
}
objects.add(observer);
}
if (broadcasting != 0) {
addAfterBroadcast.put(id, observer);
return;
}
ArrayList<Object> objects = observers.get(id);
if (objects == null) {
observers.put(id, (objects = new ArrayList<>()));
}
if (objects.contains(observer)) {
return;
}
objects.add(observer);
}
public void removeObserver(Object observer, int id) {
synchronized (observers) {
if (broadcasting != 0) {
removeAfterBroadcast.put(id, observer);
return;
if (BuildVars.DEBUG_VERSION) {
if (Thread.currentThread() != ApplicationLoader.applicationHandler.getLooper().getThread()) {
throw new RuntimeException("removeObserver allowed only from MAIN thread");
}
ArrayList<Object> objects = observers.get(id);
if (objects != null) {
objects.remove(observer);
if (objects.size() == 0) {
observers.remove(id);
}
}
if (broadcasting != 0) {
removeAfterBroadcast.put(id, observer);
return;
}
ArrayList<Object> objects = observers.get(id);
if (objects != null) {
objects.remove(observer);
if (objects.size() == 0) {
observers.remove(id);
}
}
}

View File

@ -8,6 +8,7 @@
package org.telegram.android;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.AlarmManager;
import android.app.PendingIntent;
@ -17,10 +18,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,12 +55,16 @@ public class NotificationsController {
private DispatchQueue notificationsQueue = new DispatchQueue("notificationsQueue");
private ArrayList<MessageObject> pushMessages = new ArrayList<>();
private ArrayList<MessageObject> delayedPushMessages = new ArrayList<>();
private HashMap<Integer, MessageObject> pushMessagesDict = new HashMap<>();
private HashMap<Long, Point> smartNotificationsDialogs = new HashMap<>();
private NotificationManagerCompat notificationManager = null;
private HashMap<Long, Integer> pushDialogs = new HashMap<>();
private HashMap<Long, Integer> wearNoticationsIds = new HashMap<>();
private HashMap<Long, Integer> wearNotificationsIds = new HashMap<>();
private HashMap<Long, Integer> autoNotificationsIds = new HashMap<>();
private HashMap<Long, Integer> pushDialogsOverrideMention = new HashMap<>();
private int wearNotificationId = 10000;
private int autoNotificationId = 20000;
public ArrayList<MessageObject> popupMessages = new ArrayList<>();
private long openned_dialog_id = 0;
private int total_unread_count = 0;
@ -67,10 +72,14 @@ public class NotificationsController {
private boolean notifyCheck = false;
private int lastOnlineFromOtherDevice = 0;
private boolean inChatSoundEnabled = true;
private int lastBadgeCount;
private String launcherClassName;
private long lastSoundPlay;
private MediaPlayer mediaPlayerIn;
private MediaPlayer mediaPlayerOut;
private long lastSoundOutPlay;
private SoundPool soundPool;
private int soundIn;
private int soundOut;
protected AudioManager audioManager;
private static volatile NotificationsController Instance = null;
@ -94,7 +103,6 @@ public class NotificationsController {
try {
audioManager = (AudioManager) ApplicationLoader.applicationContext.getSystemService(Context.AUDIO_SERVICE);
//mediaPlayer = new MediaPlayer();
} catch (Exception e) {
FileLog.e("tmessages", e);
}
@ -108,8 +116,10 @@ public class NotificationsController {
pushMessagesDict.clear();
pushDialogs.clear();
popupMessages.clear();
wearNoticationsIds.clear();
wearNotificationsIds.clear();
autoNotificationsIds.clear();
notifyCheck = false;
lastBadgeCount = 0;
SharedPreferences preferences = ApplicationLoader.applicationContext.getSharedPreferences("Notifications", Context.MODE_PRIVATE);
SharedPreferences.Editor editor = preferences.edit();
editor.clear();
@ -187,7 +197,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 +224,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 +264,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 +302,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 +318,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 +338,7 @@ public class NotificationsController {
AndroidUtilities.runOnUIThread(new Runnable() {
@Override
public void run() {
FileLog.e("tmessages", "set last online from other device = " + time);
lastOnlineFromOtherDevice = time;
}
});
@ -359,34 +379,52 @@ public class NotificationsController {
int needVibrate = 0;
String choosenSoundPath = null;
int ledColor = 0xff00ff00;
boolean inAppSounds = false;
boolean inAppVibrate = false;
boolean inAppSounds;
boolean inAppVibrate;
boolean inAppPreview = false;
boolean inAppPriority = false;
boolean inAppPriority;
int priority = 0;
int priority_override = 0;
int vibrate_override = 0;
int priorityOverride;
int vibrateOverride;
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 +451,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) {
@ -482,7 +520,7 @@ public class NotificationsController {
}
PendingIntent contentIntent = PendingIntent.getActivity(ApplicationLoader.applicationContext, 0, intent, PendingIntent.FLAG_ONE_SHOT);
String name = null;
String name;
boolean replace = true;
if ((int)dialog_id == 0 || pushDialogs.size() > 1 || AndroidUtilities.needShowPasscode(false) || UserConfig.isWaitingForPasscodeEnter) {
name = LocaleController.getString("AppName", R.string.AppName);
@ -495,7 +533,7 @@ public class NotificationsController {
}
}
String detailText = null;
String detailText;
if (pushDialogs.size() == 1) {
detailText = LocaleController.formatPluralString("NewMessages", total_unread_count);
} else {
@ -512,21 +550,22 @@ public class NotificationsController {
.setGroupSummary(true)
.setColor(0xff2ca5e0);
if (priority == 0) {
mBuilder.setPriority(NotificationCompat.PRIORITY_DEFAULT);
} else if (priority == 1) {
mBuilder.setPriority(NotificationCompat.PRIORITY_HIGH);
} else if (priority == 2) {
mBuilder.setPriority(NotificationCompat.PRIORITY_MAX);
if (!notifyAboutLast) {
mBuilder.setPriority(NotificationCompat.PRIORITY_LOW);
} else {
if (priority == 0) {
mBuilder.setPriority(NotificationCompat.PRIORITY_DEFAULT);
} else if (priority == 1) {
mBuilder.setPriority(NotificationCompat.PRIORITY_HIGH);
} else if (priority == 2) {
mBuilder.setPriority(NotificationCompat.PRIORITY_MAX);
}
}
mBuilder.setCategory(NotificationCompat.CATEGORY_MESSAGE);
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 +597,6 @@ public class NotificationsController {
}
if (i == 0) {
lastMessageFull = message;
//lastMessage = getStringForMessage(pushMessages.get(i), true);
lastMessage = lastMessageFull;
}
if (pushDialogs.size() == 1) {
@ -618,18 +656,20 @@ public class NotificationsController {
mBuilder.setVibrate(new long[]{0, 0});
}
showExtraNotifications(mBuilder, notifyAboutLast);
notificationManager.notify(1, mBuilder.build());
if (preferences.getBoolean("EnablePebbleNotifications", false)) {
sendAlertToPebble(lastMessageFull);
}
showWearNotifications(notifyAboutLast);
scheduleNotificationRepeat();
} catch (Exception e) {
FileLog.e("tmessages", e);
}
}
public void showWearNotifications(boolean notifyAboutLast) {
@SuppressLint("InlinedApi")
public void showExtraNotifications(NotificationCompat.Builder notificationBuilder, boolean notifyAboutLast) {
if (Build.VERSION.SDK_INT < 19) {
return;
}
@ -650,16 +690,21 @@ public class NotificationsController {
arrayList.add(messageObject);
}
HashMap<Long, Integer> oldIds = new HashMap<>();
oldIds.putAll(wearNoticationsIds);
wearNoticationsIds.clear();
HashMap<Long, Integer> oldIdsWear = new HashMap<>();
oldIdsWear.putAll(wearNotificationsIds);
wearNotificationsIds.clear();
HashMap<Long, Integer> oldIdsAuto = new HashMap<>();
oldIdsAuto.putAll(autoNotificationsIds);
autoNotificationsIds.clear();
for (long dialog_id : sortedDialogs) {
ArrayList<MessageObject> messageObjects = messagesByDialogs.get(dialog_id);
int max_id = messageObjects.get(0).getId();
int max_date = messageObjects.get(0).messageOwner.date;
TLRPC.Chat chat = null;
TLRPC.User user = null;
String name = null;
String name;
if (dialog_id > 0) {
user = MessagesController.getInstance().getUser((int)dialog_id);
if (user == null) {
@ -677,28 +722,56 @@ public class NotificationsController {
name = ContactsController.formatName(user.first_name, user.last_name);
}
Integer notificationId = oldIds.get(dialog_id);
if (notificationId == null) {
notificationId = wearNotificationId++;
Integer notificationIdWear = oldIdsWear.get(dialog_id);
if (notificationIdWear == null) {
notificationIdWear = wearNotificationId++;
} else {
oldIds.remove(dialog_id);
oldIdsWear.remove(dialog_id);
}
Integer notificationIdAuto = oldIdsAuto.get(dialog_id);
if (notificationIdAuto == null) {
notificationIdAuto = autoNotificationId++;
} else {
oldIdsAuto.remove(dialog_id);
}
Intent replyIntent = new Intent(ApplicationLoader.applicationContext, WearReplyReceiver.class);
replyIntent.putExtra("dialog_id", dialog_id);
replyIntent.putExtra("max_id", max_id);
PendingIntent replyPendingIntent = PendingIntent.getBroadcast(ApplicationLoader.applicationContext, notificationId, replyIntent, PendingIntent.FLAG_UPDATE_CURRENT);
RemoteInput remoteInput = new RemoteInput.Builder(EXTRA_VOICE_REPLY).setLabel(LocaleController.getString("Reply", R.string.Reply)).build();
PendingIntent replyPendingIntent = PendingIntent.getBroadcast(ApplicationLoader.applicationContext, notificationIdWear, replyIntent, PendingIntent.FLAG_UPDATE_CURRENT);
RemoteInput remoteInputWear = new RemoteInput.Builder(EXTRA_VOICE_REPLY).setLabel(LocaleController.getString("Reply", R.string.Reply)).build();
String replyToString;
if (chat != null) {
replyToString = LocaleController.formatString("ReplyToGroup", R.string.ReplyToGroup, name);
} else {
replyToString = LocaleController.formatString("ReplyToUser", R.string.ReplyToUser, name);
}
NotificationCompat.Action action = new NotificationCompat.Action.Builder(R.drawable.ic_reply_icon, replyToString, replyPendingIntent).addRemoteInput(remoteInput).build();
NotificationCompat.Action action = new NotificationCompat.Action.Builder(R.drawable.ic_reply_icon, replyToString, replyPendingIntent).addRemoteInput(remoteInputWear).build();
Intent msgHeardIntent = new Intent();
msgHeardIntent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);
msgHeardIntent.setAction("org.telegram.messenger.ACTION_MESSAGE_HEARD");
msgHeardIntent.putExtra("dialog_id", dialog_id);
msgHeardIntent.putExtra("max_id", max_id);
PendingIntent msgHeardPendingIntent = PendingIntent.getBroadcast(ApplicationLoader.applicationContext, notificationIdAuto, msgHeardIntent, PendingIntent.FLAG_UPDATE_CURRENT);
Intent msgReplyIntent = new Intent();
msgReplyIntent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);
msgReplyIntent.setAction("org.telegram.messenger.ACTION_MESSAGE_REPLY");
msgReplyIntent.putExtra("dialog_id", dialog_id);
msgReplyIntent.putExtra("max_id", max_id);
PendingIntent msgReplyPendingIntent = PendingIntent.getBroadcast(ApplicationLoader.applicationContext, notificationIdAuto, msgReplyIntent, PendingIntent.FLAG_UPDATE_CURRENT);
RemoteInput remoteInputAuto = new RemoteInput.Builder(NotificationsController.EXTRA_VOICE_REPLY).setLabel(LocaleController.getString("Reply", R.string.Reply)).build();
NotificationCompat.CarExtender.UnreadConversation.Builder unreadConvBuilder = new NotificationCompat.CarExtender.UnreadConversation.Builder(name)
.setReadPendingIntent(msgHeardPendingIntent)
.setReplyAction(msgReplyPendingIntent, remoteInputAuto)
.setLatestTimestamp((long) max_date * 1000);
String text = "";
for (MessageObject messageObject : messageObjects) {
for (int a = messageObjects.size() - 1; a >= 0; a--) {
MessageObject messageObject = messageObjects.get(a);
String message = getStringForMessage(messageObject, false);
if (message == null) {
continue;
@ -712,8 +785,40 @@ public class NotificationsController {
text += "\n\n";
}
text += message;
unreadConvBuilder.addMessage(message);
}
TLRPC.FileLocation photoPath = null;
if (chat != null) {
if (chat.photo != null && chat.photo.photo_small != null && chat.photo.photo_small.volume_id != 0 && chat.photo.photo_small.local_id != 0) {
photoPath = chat.photo.photo_small;
}
} else {
if (user.photo != null && user.photo.photo_small != null && user.photo.photo_small.volume_id != 0 && user.photo.photo_small.local_id != 0) {
photoPath = user.photo.photo_small;
}
}
//notificationBuilder.extend(new NotificationCompat.CarExtender().setUnreadConversation(unreadConvBuilder.build()));
NotificationCompat.Builder builderAuto = new NotificationCompat.Builder(ApplicationLoader.applicationContext)
.setSmallIcon(R.drawable.notification)
.setColor(0xff2ca5e0)
.setGroup("messages")
.setLocalOnly(true)
//.setGroupSummary(false)
//.setCategory(NotificationCompat.CATEGORY_MESSAGE)
.extend(new NotificationCompat.CarExtender().setUnreadConversation(unreadConvBuilder.build()));
if (photoPath != null) {
BitmapDrawable img = ImageLoader.getInstance().getImageFromMemory(photoPath, null, "50_50");
if (img != null) {
builderAuto.setLargeIcon(img.getBitmap());
}
}
notificationManager.notify("android_auto", notificationIdAuto, builderAuto.build());
autoNotificationsIds.put(dialog_id, notificationIdAuto);
Intent intent = new Intent(ApplicationLoader.applicationContext, LaunchActivity.class);
intent.setAction("com.tmessages.openchat" + Math.random() + Integer.MAX_VALUE);
intent.setFlags(32768);
@ -729,20 +834,30 @@ public class NotificationsController {
.setSmallIcon(R.drawable.notification)
.setGroup("messages")
.setContentText(text)
.setColor(0xff2ca5e0)
.setGroupSummary(false)
.setContentIntent(contentIntent)
.extend(new NotificationCompat.WearableExtender().addAction(action))
.setCategory(NotificationCompat.CATEGORY_MESSAGE);
if (photoPath != null) {
BitmapDrawable img = ImageLoader.getInstance().getImageFromMemory(photoPath, null, "50_50");
if (img != null) {
builder.setLargeIcon(img.getBitmap());
}
}
if (chat == null && user != null && user.phone != null && user.phone.length() > 0) {
builder.addPerson("tel:+" + user.phone);
}
notificationManager.notify(notificationId, builder.build());
wearNoticationsIds.put(dialog_id, notificationId);
notificationManager.notify(notificationIdWear, builder.build());
wearNotificationsIds.put(dialog_id, notificationIdWear);
}
for (HashMap.Entry<Long, Integer> entry : oldIds.entrySet()) {
for (HashMap.Entry<Long, Integer> entry : oldIdsAuto.entrySet()) {
notificationManager.cancel(entry.getValue());
}
for (HashMap.Entry<Long, Integer> entry : oldIdsWear.entrySet()) {
notificationManager.cancel(entry.getValue());
}
}
@ -752,6 +867,14 @@ public class NotificationsController {
notificationManager.cancel(1);
pushMessages.clear();
pushMessagesDict.clear();
for (HashMap.Entry<Long, Integer> entry : autoNotificationsIds.entrySet()) {
notificationManager.cancel(entry.getValue());
}
autoNotificationsIds.clear();
for (HashMap.Entry<Long, Integer> entry : wearNotificationsIds.entrySet()) {
notificationManager.cancel(entry.getValue());
}
wearNotificationsIds.clear();
NotificationCenter.getInstance().postNotificationName(NotificationCenter.pushMessagesUpdated);
} catch (Exception e) {
FileLog.e("tmessages", e);
@ -790,6 +913,7 @@ public class NotificationsController {
}
popupMessages.remove(messageObject);
pushMessagesDict.remove(messageObject.getId());
delayedPushMessages.remove(messageObject);
pushMessages.remove(a);
a--;
}
@ -821,6 +945,7 @@ public class NotificationsController {
personal_count--;
}
pushMessages.remove(a);
delayedPushMessages.remove(messageObject);
popupMessages.remove(messageObject);
pushMessagesDict.remove(messageObject.getId());
a--;
@ -847,14 +972,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,56 +983,26 @@ public class NotificationsController {
return;
}
try {
if (mediaPlayerIn == null) {
AssetFileDescriptor assetFileDescriptor = ApplicationLoader.applicationContext.getResources().openRawResourceFd(R.raw.sound_in);
if (assetFileDescriptor != null) {
mediaPlayerIn = new MediaPlayer();
mediaPlayerIn.setAudioStreamType(AudioManager.STREAM_SYSTEM);
mediaPlayerIn.setDataSource(assetFileDescriptor.getFileDescriptor(), assetFileDescriptor.getStartOffset(), assetFileDescriptor.getLength());
mediaPlayerIn.setLooping(false);
assetFileDescriptor.close();
mediaPlayerIn.prepare();
}
if (soundPool == null) {
soundPool = new SoundPool(4, AudioManager.STREAM_SYSTEM, 0);
soundPool.setOnLoadCompleteListener(new SoundPool.OnLoadCompleteListener() {
@Override
public void onLoadComplete(SoundPool soundPool, int sampleId, int status) {
if (status == 0) {
soundPool.play(sampleId, 1.0f, 1.0f, 1, 0, 1.0f);
}
}
});
}
mediaPlayerIn.start();
if (soundIn == 0) {
soundIn = soundPool.load(ApplicationLoader.applicationContext, R.raw.sound_in, 1);
}
soundPool.play(soundIn, 1.0f, 1.0f, 1, 0, 1.0f);
} 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,18 +1023,25 @@ public class NotificationsController {
@Override
public void run() {
try {
if (mediaPlayerOut == null) {
AssetFileDescriptor assetFileDescriptor = ApplicationLoader.applicationContext.getResources().openRawResourceFd(R.raw.sound_out);
if (assetFileDescriptor != null) {
mediaPlayerOut = new MediaPlayer();
mediaPlayerOut.setAudioStreamType(AudioManager.STREAM_SYSTEM);
mediaPlayerOut.setDataSource(assetFileDescriptor.getFileDescriptor(), assetFileDescriptor.getStartOffset(), assetFileDescriptor.getLength());
mediaPlayerOut.setLooping(false);
assetFileDescriptor.close();
mediaPlayerOut.prepare();
}
if (lastSoundOutPlay > System.currentTimeMillis() - 100) {
return;
}
mediaPlayerOut.start();
lastSoundOutPlay = System.currentTimeMillis();
if (soundPool == null) {
soundPool = new SoundPool(4, AudioManager.STREAM_SYSTEM, 0);
soundPool.setOnLoadCompleteListener(new SoundPool.OnLoadCompleteListener() {
@Override
public void onLoadComplete(SoundPool soundPool, int sampleId, int status) {
if (status == 0) {
soundPool.play(sampleId, 1.0f, 1.0f, 1, 0, 1.0f);
}
}
});
}
if (soundOut == 0) {
soundOut = soundPool.load(ApplicationLoader.applicationContext, R.raw.sound_out, 1);
}
soundPool.play(soundOut, 1.0f, 1.0f, 1, 0, 1.0f);
} catch (Exception e) {
FileLog.e("tmessages", e);
}
@ -953,6 +1049,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<MessageObject> messageObjects, boolean isLast) {
if (messageObjects.isEmpty()) {
return;
@ -986,20 +1093,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 +1132,22 @@ public class NotificationsController {
for (HashMap.Entry<Long, Integer> 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 +1170,7 @@ public class NotificationsController {
}
pushMessages.remove(a);
a--;
delayedPushMessages.remove(messageObject);
pushMessagesDict.remove(messageObject.getId());
popupMessages.remove(messageObject);
}
@ -1079,16 +1180,13 @@ 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 {
scheduleNotificationDelay(lastOnlineFromOtherDevice > ConnectionsManager.getInstance().getCurrentTime());
}
}
notifyCheck = false;
if (preferences.getBoolean("badgeNumber", true)) {
@ -1125,14 +1223,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 +1241,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 +1276,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");
@ -1199,15 +1289,24 @@ public class NotificationsController {
//ignore
}
try {
String launcherClassName = getLauncherClassName(context);
launcherClassName = getLauncherClassName(context);
if (launcherClassName == null) {
return;
}
Intent intent = new Intent("android.intent.action.BADGE_COUNT_UPDATE");
intent.putExtra("badge_count", count);
intent.putExtra("badge_count_package_name", context.getPackageName());
intent.putExtra("badge_count_class_name", launcherClassName);
context.sendBroadcast(intent);
AndroidUtilities.runOnUIThread(new Runnable() {
@Override
public void run() {
try {
Intent intent = new Intent("android.intent.action.BADGE_COUNT_UPDATE");
intent.putExtra("badge_count", count);
intent.putExtra("badge_count_package_name", context.getPackageName());
intent.putExtra("badge_count_class_name", launcherClassName);
context.sendBroadcast(intent);
} catch (Exception e) {
FileLog.e("tmessages", e);
}
}
});
} catch (Throwable e) {
FileLog.e("tmessages", e);
}

View File

@ -17,6 +17,7 @@ import org.telegram.messenger.FileLog;
import org.telegram.messenger.ApplicationLoader;
public class ScreenReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) {

View File

@ -48,6 +48,7 @@ public class SecretChatHelper {
private boolean startingSecretChat = false;
private static volatile SecretChatHelper Instance = null;
public static SecretChatHelper getInstance() {
SecretChatHelper localInstance = Instance;
if (localInstance == null) {
@ -87,7 +88,7 @@ public class SecretChatHelper {
newMsg.local_id = newMsg.id = UserConfig.getNewMessageId();
newMsg.from_id = UserConfig.getClientUserId();
newMsg.flags = TLRPC.MESSAGE_FLAG_UNREAD | TLRPC.MESSAGE_FLAG_OUT;
newMsg.dialog_id = ((long)encryptedChat.id) << 32;
newMsg.dialog_id = ((long) encryptedChat.id) << 32;
newMsg.to_id = new TLRPC.TL_peerUser();
newMsg.send_state = MessageObject.MESSAGE_SEND_STATE_SENDING;
if (encryptedChat.participant_id == UserConfig.getClientUserId()) {
@ -114,7 +115,7 @@ public class SecretChatHelper {
if (!(encryptedChat instanceof TLRPC.TL_encryptedChat)) {
return;
}
TLRPC.TL_decryptedMessageService reqSend = null;
TLRPC.TL_decryptedMessageService reqSend;
if (AndroidUtilities.getPeerLayerVersion(encryptedChat.layer) >= 17) {
reqSend = new TLRPC.TL_decryptedMessageService();
} else {
@ -123,7 +124,7 @@ public class SecretChatHelper {
Utilities.random.nextBytes(reqSend.random_bytes);
}
TLRPC.Message message = null;
TLRPC.Message message;
if (resendMessage != null) {
message = resendMessage;
@ -140,7 +141,7 @@ public class SecretChatHelper {
protected void processUpdateEncryption(TLRPC.TL_updateEncryption update, ConcurrentHashMap<Integer, TLRPC.User> usersDict) {
final TLRPC.EncryptedChat newChat = update.chat;
long dialog_id = ((long)newChat.id) << 32;
long dialog_id = ((long) newChat.id) << 32;
TLRPC.EncryptedChat existingChat = MessagesController.getInstance().getEncryptedChatDB(newChat.id);
if (newChat instanceof TLRPC.TL_encryptedChatRequested && existingChat == null) {
@ -219,7 +220,7 @@ public class SecretChatHelper {
if (!(encryptedChat instanceof TLRPC.TL_encryptedChat)) {
return;
}
TLRPC.TL_decryptedMessageService reqSend = null;
TLRPC.TL_decryptedMessageService reqSend;
if (AndroidUtilities.getPeerLayerVersion(encryptedChat.layer) >= 17) {
reqSend = new TLRPC.TL_decryptedMessageService();
} else {
@ -228,7 +229,7 @@ public class SecretChatHelper {
Utilities.random.nextBytes(reqSend.random_bytes);
}
TLRPC.Message message = null;
TLRPC.Message message;
if (resendMessage != null) {
message = resendMessage;
@ -247,7 +248,7 @@ public class SecretChatHelper {
if (!(encryptedChat instanceof TLRPC.TL_encryptedChat)) {
return;
}
TLRPC.TL_decryptedMessageService reqSend = null;
TLRPC.TL_decryptedMessageService reqSend;
if (AndroidUtilities.getPeerLayerVersion(encryptedChat.layer) >= 17) {
reqSend = new TLRPC.TL_decryptedMessageService();
} else {
@ -256,7 +257,7 @@ public class SecretChatHelper {
Utilities.random.nextBytes(reqSend.random_bytes);
}
TLRPC.Message message = null;
TLRPC.Message message;
if (resendMessage != null) {
message = resendMessage;
@ -278,7 +279,7 @@ public class SecretChatHelper {
return;
}
sendingNotifyLayer.add(encryptedChat.id);
TLRPC.TL_decryptedMessageService reqSend = null;
TLRPC.TL_decryptedMessageService reqSend;
if (AndroidUtilities.getPeerLayerVersion(encryptedChat.layer) >= 17) {
reqSend = new TLRPC.TL_decryptedMessageService();
} else {
@ -287,7 +288,7 @@ public class SecretChatHelper {
Utilities.random.nextBytes(reqSend.random_bytes);
}
TLRPC.Message message = null;
TLRPC.Message message;
if (resendMessage != null) {
message = resendMessage;
@ -307,7 +308,7 @@ public class SecretChatHelper {
return;
}
TLRPC.TL_decryptedMessageService reqSend = null;
TLRPC.TL_decryptedMessageService reqSend;
if (AndroidUtilities.getPeerLayerVersion(encryptedChat.layer) >= 17) {
reqSend = new TLRPC.TL_decryptedMessageService();
} else {
@ -316,7 +317,7 @@ public class SecretChatHelper {
Utilities.random.nextBytes(reqSend.random_bytes);
}
TLRPC.Message message = null;
TLRPC.Message message;
if (resendMessage != null) {
message = resendMessage;
@ -338,7 +339,7 @@ public class SecretChatHelper {
return;
}
TLRPC.TL_decryptedMessageService reqSend = null;
TLRPC.TL_decryptedMessageService reqSend;
if (AndroidUtilities.getPeerLayerVersion(encryptedChat.layer) >= 17) {
reqSend = new TLRPC.TL_decryptedMessageService();
} else {
@ -347,7 +348,7 @@ public class SecretChatHelper {
Utilities.random.nextBytes(reqSend.random_bytes);
}
TLRPC.Message message = null;
TLRPC.Message message;
if (resendMessage != null) {
message = resendMessage;
@ -370,7 +371,7 @@ public class SecretChatHelper {
return;
}
TLRPC.TL_decryptedMessageService reqSend = null;
TLRPC.TL_decryptedMessageService reqSend;
if (AndroidUtilities.getPeerLayerVersion(encryptedChat.layer) >= 17) {
reqSend = new TLRPC.TL_decryptedMessageService();
} else {
@ -379,7 +380,7 @@ public class SecretChatHelper {
Utilities.random.nextBytes(reqSend.random_bytes);
}
TLRPC.Message message = null;
TLRPC.Message message;
if (resendMessage != null) {
message = resendMessage;
@ -401,7 +402,7 @@ public class SecretChatHelper {
return;
}
TLRPC.TL_decryptedMessageService reqSend = null;
TLRPC.TL_decryptedMessageService reqSend;
if (AndroidUtilities.getPeerLayerVersion(encryptedChat.layer) >= 17) {
reqSend = new TLRPC.TL_decryptedMessageService();
} else {
@ -410,7 +411,7 @@ public class SecretChatHelper {
Utilities.random.nextBytes(reqSend.random_bytes);
}
TLRPC.Message message = null;
TLRPC.Message message;
if (resendMessage != null) {
message = resendMessage;
@ -431,7 +432,7 @@ public class SecretChatHelper {
return;
}
TLRPC.TL_decryptedMessageService reqSend = null;
TLRPC.TL_decryptedMessageService reqSend;
if (AndroidUtilities.getPeerLayerVersion(encryptedChat.layer) >= 17) {
reqSend = new TLRPC.TL_decryptedMessageService();
} else {
@ -440,7 +441,7 @@ public class SecretChatHelper {
Utilities.random.nextBytes(reqSend.random_bytes);
}
TLRPC.Message message = null;
TLRPC.Message message;
if (resendMessage != null) {
message = resendMessage;
@ -459,7 +460,7 @@ public class SecretChatHelper {
return;
}
TLRPC.TL_decryptedMessageService reqSend = null;
TLRPC.TL_decryptedMessageService reqSend;
if (AndroidUtilities.getPeerLayerVersion(encryptedChat.layer) >= 17) {
reqSend = new TLRPC.TL_decryptedMessageService();
} else {
@ -468,7 +469,7 @@ public class SecretChatHelper {
Utilities.random.nextBytes(reqSend.random_bytes);
}
TLRPC.Message message = null;
TLRPC.Message message;
if (resendMessage != null) {
message = resendMessage;
@ -495,7 +496,7 @@ public class SecretChatHelper {
return;
}
TLRPC.TL_decryptedMessageService reqSend = null;
TLRPC.TL_decryptedMessageService reqSend;
if (AndroidUtilities.getPeerLayerVersion(encryptedChat.layer) >= 17) {
reqSend = new TLRPC.TL_decryptedMessageService();
} else {
@ -504,7 +505,7 @@ public class SecretChatHelper {
Utilities.random.nextBytes(reqSend.random_bytes);
}
TLRPC.Message message = null;
TLRPC.Message message;
if (resendMessage != null) {
message = resendMessage;
@ -542,7 +543,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<TLRPC.Message> arr = new ArrayList<>();
arr.add(newMsg);
MessagesStorage.getInstance().putMessages(arr, false, true, false, 0);
@ -557,7 +558,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 +566,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);
@ -653,7 +655,7 @@ public class SecretChatHelper {
Utilities.stageQueue.postRunnable(new Runnable() {
@Override
public void run() {
TLObject toEncryptObject = null;
TLObject toEncryptObject;
if (AndroidUtilities.getPeerLayerVersion(chat.layer) >= 17) {
TLRPC.TL_decryptedMessageLayer layer = new TLRPC.TL_decryptedMessageLayer();
int myLayer = Math.max(17, AndroidUtilities.getMyLayerVersion(chat.layer));
@ -709,7 +711,7 @@ public class SecretChatHelper {
byte[] messageKey = new byte[16];
System.arraycopy(messageKeyFull, messageKeyFull.length - 16, messageKey, 0, 16);
MessageKeyData keyData = Utilities.generateMessageKeyData(chat.auth_key, messageKey, false);
MessageKeyData keyData = MessageKeyData.generateMessageKeyData(chat.auth_key, messageKey, false);
len = toEncrypt.length();
int extraLen = len % 16 != 0 ? 16 - len % 16 : 0;
@ -733,7 +735,7 @@ public class SecretChatHelper {
BuffersStorage.getInstance().reuseFreeBuffer(dataForEncryption);
data.position(0);
TLObject reqToSend = null;
TLObject reqToSend;
if (encryptedFile == null) {
if (req instanceof TLRPC.TL_decryptedMessageService) {
@ -769,7 +771,7 @@ public class SecretChatHelper {
if (error == null) {
if (req.action instanceof TLRPC.TL_decryptedMessageActionNotifyLayer) {
TLRPC.EncryptedChat currentChat = MessagesController.getInstance().getEncryptedChat(chat.id);
sendingNotifyLayer.remove((Integer)currentChat.id);
sendingNotifyLayer.remove((Integer) currentChat.id);
currentChat.layer = AndroidUtilities.setMyLayerVersion(currentChat.layer, CURRENT_SECRET_CHAT_LAYER);
MessagesStorage.getInstance().updateEncryptedChatLayer(currentChat);
}
@ -856,8 +858,8 @@ public class SecretChatHelper {
}
if (object instanceof TLRPC.TL_decryptedMessage) {
TLRPC.TL_decryptedMessage decryptedMessage = (TLRPC.TL_decryptedMessage)object;
TLRPC.TL_message newMessage = null;
TLRPC.TL_decryptedMessage decryptedMessage = (TLRPC.TL_decryptedMessage) object;
TLRPC.TL_message newMessage;
if (AndroidUtilities.getPeerLayerVersion(chat.layer) >= 17) {
newMessage = new TLRPC.TL_message_secret();
newMessage.ttl = decryptedMessage.ttl;
@ -874,7 +876,7 @@ public class SecretChatHelper {
newMessage.random_id = random_id;
newMessage.to_id.user_id = UserConfig.getClientUserId();
newMessage.flags = TLRPC.MESSAGE_FLAG_UNREAD;
newMessage.dialog_id = ((long)chat.id) << 32;
newMessage.dialog_id = ((long) chat.id) << 32;
if (decryptedMessage.media instanceof TLRPC.TL_decryptedMessageMediaEmpty) {
newMessage.media = new TLRPC.TL_messageMediaEmpty();
} else if (decryptedMessage.media instanceof TLRPC.TL_decryptedMessageMediaContact) {
@ -893,16 +895,17 @@ 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) {
byte[] thumb = ((TLRPC.TL_decryptedMessageMediaPhoto) decryptedMessage.media).thumb;
if (thumb != null && thumb.length != 0 && thumb.length <= 6000 && decryptedMessage.media.thumb_w <= 100 && decryptedMessage.media.thumb_h <= 100) {
TLRPC.TL_photoCachedSize small = new TLRPC.TL_photoCachedSize();
small.w = decryptedMessage.media.thumb_w;
small.h = decryptedMessage.media.thumb_h;
small.bytes = decryptedMessage.media.thumb;
small.bytes = thumb;
small.type = "s";
small.location = new TLRPC.TL_fileLocationUnavailable();
newMessage.media.photo.sizes.add(small);
@ -926,10 +929,12 @@ 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) {
byte[] thumb = ((TLRPC.TL_decryptedMessageMediaVideo) decryptedMessage.media).thumb;
if (thumb != null && thumb.length != 0 && thumb.length <= 6000 && decryptedMessage.media.thumb_w <= 100 && decryptedMessage.media.thumb_h <= 100) {
newMessage.media.video.thumb = new TLRPC.TL_photoCachedSize();
newMessage.media.video.thumb.bytes = decryptedMessage.media.thumb;
newMessage.media.video.thumb.bytes = thumb;
newMessage.media.video.thumb.w = decryptedMessage.media.thumb_w;
newMessage.media.video.thumb.h = decryptedMessage.media.thumb_h;
newMessage.media.video.thumb.type = "s";
@ -943,7 +948,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 +955,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);
}
@ -973,9 +978,10 @@ public class SecretChatHelper {
newMessage.media.document.size = file.size;
newMessage.media.document.key = decryptedMessage.media.key;
newMessage.media.document.iv = decryptedMessage.media.iv;
if (decryptedMessage.media.thumb.length != 0 && decryptedMessage.media.thumb.length <= 6000 && decryptedMessage.media.thumb_w <= 100 && decryptedMessage.media.thumb_h <= 100) {
byte[] thumb = ((TLRPC.TL_decryptedMessageMediaDocument) decryptedMessage.media).thumb;
if (thumb != null && thumb.length != 0 && thumb.length <= 6000 && decryptedMessage.media.thumb_w <= 100 && decryptedMessage.media.thumb_h <= 100) {
newMessage.media.document.thumb = new TLRPC.TL_photoCachedSize();
newMessage.media.document.thumb.bytes = decryptedMessage.media.thumb;
newMessage.media.document.thumb.bytes = thumb;
newMessage.media.document.thumb.w = decryptedMessage.media.thumb_w;
newMessage.media.document.thumb.h = decryptedMessage.media.thumb_h;
newMessage.media.document.thumb.type = "s";
@ -995,7 +1001,7 @@ public class SecretChatHelper {
newMessage.media.document.mime_type = decryptedMessage.media.mime_type;
newMessage.media.document.dc_id = decryptedMessage.media.dc_id;
newMessage.media.document.size = decryptedMessage.media.size;
newMessage.media.document.thumb = decryptedMessage.media.thumbImage;
newMessage.media.document.thumb = ((TLRPC.TL_decryptedMessageMediaExternalDocument) decryptedMessage.media).thumb;
} else if (decryptedMessage.media instanceof TLRPC.TL_decryptedMessageMediaAudio) {
if (decryptedMessage.media.key == null || decryptedMessage.media.key.length != 32 || decryptedMessage.media.iv == null || decryptedMessage.media.iv.length != 32) {
return null;
@ -1023,7 +1029,7 @@ public class SecretChatHelper {
}
return newMessage;
} else if (object instanceof TLRPC.TL_decryptedMessageService) {
final TLRPC.TL_decryptedMessageService serviceMessage = (TLRPC.TL_decryptedMessageService)object;
final TLRPC.TL_decryptedMessageService serviceMessage = (TLRPC.TL_decryptedMessageService) object;
if (serviceMessage.action instanceof TLRPC.TL_decryptedMessageActionSetMessageTTL || serviceMessage.action instanceof TLRPC.TL_decryptedMessageActionScreenshotMessages) {
TLRPC.TL_messageService newMessage = new TLRPC.TL_messageService();
if (serviceMessage.action instanceof TLRPC.TL_decryptedMessageActionSetMessageTTL) {
@ -1045,10 +1051,10 @@ public class SecretChatHelper {
newMessage.from_id = from_id;
newMessage.to_id = new TLRPC.TL_peerUser();
newMessage.to_id.user_id = UserConfig.getClientUserId();
newMessage.dialog_id = ((long)chat.id) << 32;
newMessage.dialog_id = ((long) chat.id) << 32;
return newMessage;
} else if (serviceMessage.action instanceof TLRPC.TL_decryptedMessageActionFlushHistory) {
final long did = ((long)chat.id) << 32;
final long did = ((long) chat.id) << 32;
AndroidUtilities.runOnUIThread(new Runnable() {
@Override
public void run() {
@ -1300,7 +1306,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 +1317,12 @@ public class SecretChatHelper {
}
if (keyToDecrypt != null) {
byte[] messageKey = is.readData(16);
MessageKeyData keyData = Utilities.generateMessageKeyData(keyToDecrypt, messageKey, false);
byte[] messageKey = is.readData(16, false);
MessageKeyData keyData = MessageKeyData.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,13 +1331,14 @@ public class SecretChatHelper {
return null;
}
TLObject object = TLClassStore.Instance().TLdeserialize(is, is.readInt32());
TLObject object = TLClassStore.Instance().TLdeserialize(is, is.readInt32(false), false);
BuffersStorage.getInstance().reuseFreeBuffer(is);
if (!new_key_used && AndroidUtilities.getPeerLayerVersion(chat.layer) >= 20) {
chat.key_use_count_in++;
}
if (object instanceof TLRPC.TL_decryptedMessageLayer) {
final TLRPC.TL_decryptedMessageLayer layer = (TLRPC.TL_decryptedMessageLayer)object;
final TLRPC.TL_decryptedMessageLayer layer = (TLRPC.TL_decryptedMessageLayer) object;
if (chat.seq_in == 0 && chat.seq_out == 0) {
if (chat.admin_id == UserConfig.getClientUserId()) {
chat.seq_out = 1;

View File

@ -44,7 +44,7 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter
private HashMap<Integer, MessageObject> unsentMessages = new HashMap<>();
private HashMap<Integer, TLRPC.Message> sendingMessages = new HashMap<>();
private class DelayedMessage {
protected class DelayedMessage {
public TLObject sendRequest;
public TLRPC.TL_decryptedMessage sendEncryptedRequest;
public int type;
@ -56,9 +56,11 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter
public String httpLocation;
public MessageObject obj;
public TLRPC.EncryptedChat encryptedChat;
public VideoEditedInfo videoEditedInfo;
}
private static volatile SendMessagesHelper Instance = null;
public static SendMessagesHelper getInstance() {
SendMessagesHelper localInstance = Instance;
if (localInstance == null) {
@ -94,113 +96,100 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter
}
@Override
public void didReceivedNotification(int id, Object... args) {
public void didReceivedNotification(int id, final Object... args) {
if (id == NotificationCenter.FileDidUpload) {
final String location = (String)args[0];
final TLRPC.InputFile file = (TLRPC.InputFile)args[1];
final TLRPC.InputEncryptedFile encryptedFile = (TLRPC.InputEncryptedFile)args[2];
final String location = (String) args[0];
final TLRPC.InputFile file = (TLRPC.InputFile) args[1];
final TLRPC.InputEncryptedFile encryptedFile = (TLRPC.InputEncryptedFile) args[2];
ArrayList<DelayedMessage> arr = delayedMessages.get(location);
if (arr != null) {
for (int a = 0; a < arr.size(); a++) {
DelayedMessage message = arr.get(a);
TLRPC.InputMedia media = null;
if (message.sendRequest instanceof TLRPC.TL_messages_sendMedia) {
media = ((TLRPC.TL_messages_sendMedia) message.sendRequest).media;
} else if (message.sendRequest instanceof TLRPC.TL_messages_sendBroadcast) {
media = ((TLRPC.TL_messages_sendBroadcast) message.sendRequest).media;
}
AndroidUtilities.runOnUIThread(new Runnable() {
@Override
public void run() {
ArrayList<DelayedMessage> arr = delayedMessages.get(location);
if (arr != null) {
for (int a = 0; a < arr.size(); a++) {
DelayedMessage message = arr.get(a);
TLRPC.InputMedia media = null;
if (message.sendRequest instanceof TLRPC.TL_messages_sendMedia) {
media = ((TLRPC.TL_messages_sendMedia)message.sendRequest).media;
} else if (message.sendRequest instanceof TLRPC.TL_messages_sendBroadcast) {
media = ((TLRPC.TL_messages_sendBroadcast)message.sendRequest).media;
}
if (file != null && media != null) {
if (message.type == 0) {
media.file = file;
performSendMessageRequest(message.sendRequest, message.obj.messageOwner, message.originalPath);
} else if (message.type == 1) {
if (media.file == null) {
media.file = file;
if (media.thumb == null && message.location != null) {
performSendDelayedMessage(message);
} else {
performSendMessageRequest(message.sendRequest, message.obj.messageOwner, message.originalPath);
}
} else {
media.thumb = file;
performSendMessageRequest(message.sendRequest, message.obj.messageOwner, message.originalPath);
}
} else if (message.type == 2) {
if (media.file == null) {
media.file = file;
if (media.thumb == null && message.location != null) {
performSendDelayedMessage(message);
} else {
performSendMessageRequest(message.sendRequest, message.obj.messageOwner, message.originalPath);
}
} else {
media.thumb = file;
performSendMessageRequest(message.sendRequest, message.obj.messageOwner, message.originalPath);
}
} else if (message.type == 3) {
media.file = file;
if (file != null && media != null) {
if (message.type == 0) {
media.file = file;
performSendMessageRequest(message.sendRequest, message.obj.messageOwner, message.originalPath);
} else if (message.type == 1) {
if (media.file == null) {
media.file = file;
if (media.thumb == null && message.location != null) {
performSendDelayedMessage(message);
} else {
performSendMessageRequest(message.sendRequest, message.obj.messageOwner, message.originalPath);
}
arr.remove(a);
a--;
} else if (encryptedFile != null && message.sendEncryptedRequest != null) {
message.sendEncryptedRequest.media.key = encryptedFile.key;
message.sendEncryptedRequest.media.iv = encryptedFile.iv;
SecretChatHelper.getInstance().performSendEncryptedRequest(message.sendEncryptedRequest, message.obj.messageOwner, message.encryptedChat, encryptedFile, message.originalPath);
arr.remove(a);
a--;
} else {
media.thumb = file;
performSendMessageRequest(message.sendRequest, message.obj.messageOwner, message.originalPath);
}
} else if (message.type == 2) {
if (media.file == null) {
media.file = file;
if (media.thumb == null && message.location != null) {
performSendDelayedMessage(message);
} else {
performSendMessageRequest(message.sendRequest, message.obj.messageOwner, message.originalPath);
}
} else {
media.thumb = file;
performSendMessageRequest(message.sendRequest, message.obj.messageOwner, message.originalPath);
}
} else if (message.type == 3) {
media.file = file;
performSendMessageRequest(message.sendRequest, message.obj.messageOwner, message.originalPath);
}
if (arr.isEmpty()) {
delayedMessages.remove(location);
}
arr.remove(a);
a--;
} else if (encryptedFile != null && message.sendEncryptedRequest != null) {
message.sendEncryptedRequest.media.key = (byte[]) args[3];
message.sendEncryptedRequest.media.iv = (byte[]) args[4];
SecretChatHelper.getInstance().performSendEncryptedRequest(message.sendEncryptedRequest, message.obj.messageOwner, message.encryptedChat, encryptedFile, message.originalPath);
arr.remove(a);
a--;
}
}
});
if (arr.isEmpty()) {
delayedMessages.remove(location);
}
}
} else if (id == NotificationCenter.FileDidFailUpload) {
final String location = (String) args[0];
final boolean enc = (Boolean) args[1];
AndroidUtilities.runOnUIThread(new Runnable() {
@Override
public void run() {
ArrayList<DelayedMessage> arr = delayedMessages.get(location);
if (arr != null) {
for (int a = 0; a < arr.size(); a++) {
DelayedMessage obj = arr.get(a);
if (enc && obj.sendEncryptedRequest != null || !enc && obj.sendRequest != null) {
MessagesStorage.getInstance().markMessageAsSendError(obj.obj.getId());
obj.obj.messageOwner.send_state = MessageObject.MESSAGE_SEND_STATE_SEND_ERROR;
arr.remove(a);
a--;
NotificationCenter.getInstance().postNotificationName(NotificationCenter.messageSendError, obj.obj.getId());
processSentMessage(obj.obj.getId());
}
}
if (arr.isEmpty()) {
delayedMessages.remove(location);
}
ArrayList<DelayedMessage> arr = delayedMessages.get(location);
if (arr != null) {
for (int a = 0; a < arr.size(); a++) {
DelayedMessage obj = arr.get(a);
if (enc && obj.sendEncryptedRequest != null || !enc && obj.sendRequest != null) {
MessagesStorage.getInstance().markMessageAsSendError(obj.obj.getId());
obj.obj.messageOwner.send_state = MessageObject.MESSAGE_SEND_STATE_SEND_ERROR;
arr.remove(a);
a--;
NotificationCenter.getInstance().postNotificationName(NotificationCenter.messageSendError, obj.obj.getId());
processSentMessage(obj.obj.getId());
}
}
});
if (arr.isEmpty()) {
delayedMessages.remove(location);
}
}
} else if (id == NotificationCenter.FilePreparingStarted) {
MessageObject messageObject = (MessageObject)args[0];
String finalPath = (String)args[1];
MessageObject messageObject = (MessageObject) args[0];
String finalPath = (String) args[1];
ArrayList<DelayedMessage> arr = delayedMessages.get(messageObject.messageOwner.attachPath);
if (arr != null) {
for (int a = 0; a < arr.size(); a++) {
DelayedMessage message = arr.get(a);
if (message.obj == messageObject) {
message.videoLocation.videoEditedInfo = null;
message.videoEditedInfo = null;
performSendDelayedMessage(message);
arr.remove(a);
a--;
break;
}
}
@ -209,19 +198,19 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter
}
}
} else if (id == NotificationCenter.FileNewChunkAvailable) {
MessageObject messageObject = (MessageObject)args[0];
String finalPath = (String)args[1];
long finalSize = (Long)args[2];
boolean isEncrypted = ((int)messageObject.getDialogId()) == 0;
MessageObject messageObject = (MessageObject) args[0];
String finalPath = (String) args[1];
long finalSize = (Long) args[2];
boolean isEncrypted = ((int) messageObject.getDialogId()) == 0;
FileLoader.getInstance().checkUploadNewDataAvailable(finalPath, isEncrypted, finalSize);
if (finalSize != 0) {
ArrayList<DelayedMessage> arr = delayedMessages.get(messageObject.messageOwner.attachPath);
if (arr != null) {
for (DelayedMessage message : arr) {
if (message.obj == messageObject) {
message.obj.messageOwner.videoEditedInfo = null;
message.obj.videoEditedInfo = null;
message.obj.messageOwner.message = "-1";
message.obj.messageOwner.media.video.size = (int)finalSize;
message.obj.messageOwner.media.video.size = (int) finalSize;
ArrayList<TLRPC.Message> messages = new ArrayList<>();
messages.add(message.obj.messageOwner);
@ -235,8 +224,8 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter
}
}
} else if (id == NotificationCenter.FilePreparingFailed) {
MessageObject messageObject = (MessageObject)args[0];
String finalPath = (String)args[1];
MessageObject messageObject = (MessageObject) args[0];
String finalPath = (String) args[1];
stopVideoService(messageObject.messageOwner.attachPath);
ArrayList<DelayedMessage> arr = delayedMessages.get(finalPath);
@ -257,8 +246,8 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter
}
}
} else if (id == NotificationCenter.httpFileDidLoaded) {
String path = (String)args[0];
String file = (String)args[1];
String path = (String) args[0];
String file = (String) args[1];
ArrayList<DelayedMessage> arr = delayedMessages.get(path);
if (arr != null) {
for (final DelayedMessage message : arr) {
@ -334,7 +323,7 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter
delayedMessages.remove(path);
}
} else if (id == NotificationCenter.httpFileDidFailedLoad) {
String path = (String)args[0];
String path = (String) args[0];
ArrayList<DelayedMessage> arr = delayedMessages.get(path);
if (arr != null) {
@ -454,12 +443,11 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter
sendMessage((TLRPC.TL_audio) messageObject.messageOwner.media.audio, messageObject.messageOwner.attachPath, did, messageObject.replyMessageObject);
} else if (messageObject.messageOwner.media.video instanceof TLRPC.TL_video) {
TLRPC.TL_video video = (TLRPC.TL_video) messageObject.messageOwner.media.video;
video.videoEditedInfo = messageObject.messageOwner.videoEditedInfo;
sendMessage(video, null, messageObject.messageOwner.attachPath, did, messageObject.replyMessageObject);
sendMessage(video, messageObject.videoEditedInfo, 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,6 +469,51 @@ 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);
}
@ -550,6 +583,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 +596,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);
@ -670,23 +706,23 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter
sendMessage(message, null, 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, 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, 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);
public void sendMessage(TLRPC.TL_video video, VideoEditedInfo videoEditedInfo, String originalPath, String path, long peer, MessageObject reply_to_msg) {
sendMessage(null, null, null, video, videoEditedInfo, 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);
}
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, VideoEditedInfo videoEditedInfo, 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 +749,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()) {
@ -729,7 +764,6 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter
} else {
type = 3;
video = (TLRPC.TL_video) newMsg.media.video;
video.videoEditedInfo = newMsg.videoEditedInfo;
}
} else if (msgObj.type == 12) {
user = new TLRPC.TL_userRequest();
@ -760,16 +794,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 +810,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,13 +827,13 @@ 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;
if (video.videoEditedInfo == null) {
if (videoEditedInfo == null) {
newMsg.message = "-1";
} else {
newMsg.message = video.videoEditedInfo.getString();
newMsg.message = videoEditedInfo.getString();
}
newMsg.attachPath = path;
} else if (msgObj != null) {
@ -868,6 +900,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 +1028,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 +1056,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 +1068,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;
@ -1033,9 +1079,11 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter
delayedMessage.obj = newMsgObj;
delayedMessage.location = video.thumb.location;
delayedMessage.videoLocation = video;
delayedMessage.videoEditedInfo = videoEditedInfo;
} 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;
@ -1088,7 +1136,7 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter
}
}
TLObject reqSend = null;
TLObject reqSend;
if (sendToPeers != null) {
TLRPC.TL_messages_sendBroadcast request = new TLRPC.TL_messages_sendBroadcast();
@ -1161,14 +1209,19 @@ 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);
TLRPC.PhotoSize big = photo.sizes.get(photo.sizes.size() - 1);
reqSend.media = new TLRPC.TL_decryptedMessageMediaPhoto();
reqSend.media.thumb = small.bytes;
ImageLoader.fillPhotoSizeWithBytes(small);
if (small.bytes != null) {
((TLRPC.TL_decryptedMessageMediaPhoto) reqSend.media).thumb = small.bytes;
} else {
((TLRPC.TL_decryptedMessageMediaPhoto) reqSend.media).thumb = new byte[0];
}
reqSend.media.thumb_h = small.h;
reqSend.media.thumb_w = small.w;
reqSend.media.w = big.w;
@ -1196,16 +1249,26 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter
SecretChatHelper.getInstance().performSendEncryptedRequest(reqSend, newMsgObj.messageOwner, encryptedChat, encryptedFile, null);
}
} else if (type == 3) {
ImageLoader.fillPhotoSizeWithBytes(video.thumb);
if (AndroidUtilities.getPeerLayerVersion(encryptedChat.layer) >= 17) {
reqSend.media = new TLRPC.TL_decryptedMessageMediaVideo();
if (video.thumb != null && video.thumb.bytes != null) {
((TLRPC.TL_decryptedMessageMediaVideo) reqSend.media).thumb = video.thumb.bytes;
} else {
((TLRPC.TL_decryptedMessageMediaVideo) reqSend.media).thumb = new byte[0];
}
} else {
reqSend.media = new TLRPC.TL_decryptedMessageMediaVideo_old();
if (video.thumb != null && video.thumb.bytes != null) {
((TLRPC.TL_decryptedMessageMediaVideo_old) reqSend.media).thumb = video.thumb.bytes;
} else {
((TLRPC.TL_decryptedMessageMediaVideo_old) reqSend.media).thumb = new byte[0];
}
}
reqSend.media.duration = video.duration;
reqSend.media.size = video.size;
reqSend.media.w = video.w;
reqSend.media.h = video.h;
reqSend.media.thumb = video.thumb.bytes;
reqSend.media.thumb_h = video.thumb.h;
reqSend.media.thumb_w = video.thumb.w;
reqSend.media.mime_type = "video/mp4";
@ -1217,6 +1280,7 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter
delayedMessage.obj = newMsgObj;
delayedMessage.encryptedChat = encryptedChat;
delayedMessage.videoLocation = video;
delayedMessage.videoEditedInfo = videoEditedInfo;
performSendDelayedMessage(delayedMessage);
} else {
TLRPC.TL_inputEncryptedFile encryptedFile = new TLRPC.TL_inputEncryptedFile();
@ -1244,21 +1308,27 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter
reqSend.media = new TLRPC.TL_decryptedMessageMediaExternalDocument();
reqSend.media.id = document.id;
reqSend.media.date = document.date;
reqSend.media.access_hash = document.access_hash;
reqSend.media.mime_type = document.mime_type;
reqSend.media.size = document.size;
((TLRPC.TL_decryptedMessageMediaExternalDocument) reqSend.media).thumbImage = document.thumb;
reqSend.media.dc_id = document.dc_id;
reqSend.media.attributes = document.attributes;
if (document.thumb == null) {
((TLRPC.TL_decryptedMessageMediaExternalDocument) reqSend.media).thumb = new TLRPC.TL_photoSizeEmpty();
} else {
((TLRPC.TL_decryptedMessageMediaExternalDocument) reqSend.media).thumb = document.thumb;
}
SecretChatHelper.getInstance().performSendEncryptedRequest(reqSend, newMsgObj.messageOwner, encryptedChat, null, null);
} else {
ImageLoader.fillPhotoSizeWithBytes(document.thumb);
reqSend.media = new TLRPC.TL_decryptedMessageMediaDocument();
reqSend.media.size = document.size;
if (!(document.thumb instanceof TLRPC.TL_photoSizeEmpty)) {
reqSend.media.thumb = document.thumb.bytes;
if (document.thumb != null && document.thumb.bytes != null) {
((TLRPC.TL_decryptedMessageMediaDocument) reqSend.media).thumb = document.thumb.bytes;
reqSend.media.thumb_h = document.thumb.h;
reqSend.media.thumb_w = document.thumb.w;
} else {
reqSend.media.thumb = new byte[0];
((TLRPC.TL_decryptedMessageMediaDocument) reqSend.media).thumb = new byte[0];
reqSend.media.thumb_h = 0;
reqSend.media.thumb_w = 0;
}
@ -1340,7 +1410,7 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter
}
}
} else if (message.type == 1) {
if (message.videoLocation.videoEditedInfo != null) {
if (message.videoEditedInfo != null) {
String location = message.obj.messageOwner.attachPath;
if (location == null) {
location = FileLoader.getInstance().getDirectory(FileLoader.MEDIA_DIR_CACHE) + "/" + message.videoLocation.id + ".mp4";
@ -1349,10 +1419,10 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter
MediaController.getInstance().scheduleVideoConvert(message.obj);
} else {
if (message.sendRequest != null) {
TLRPC.InputMedia media = null;
TLRPC.InputMedia media;
if (message.sendRequest instanceof TLRPC.TL_messages_sendMedia) {
media = ((TLRPC.TL_messages_sendMedia) message.sendRequest).media;
} else if (message.sendRequest instanceof TLRPC.TL_messages_sendBroadcast) {
} else {
media = ((TLRPC.TL_messages_sendBroadcast) message.sendRequest).media;
}
if (media.file == null) {
@ -1361,7 +1431,7 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter
location = FileLoader.getInstance().getDirectory(FileLoader.MEDIA_DIR_CACHE) + "/" + message.videoLocation.id + ".mp4";
}
putToDelayedMessages(location, message);
if (message.obj.messageOwner.videoEditedInfo != null) {
if (message.obj.videoEditedInfo != null) {
FileLoader.getInstance().uploadFile(location, false, false, message.videoLocation.size);
} else {
FileLoader.getInstance().uploadFile(location, false, false);
@ -1377,7 +1447,7 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter
location = FileLoader.getInstance().getDirectory(FileLoader.MEDIA_DIR_CACHE) + "/" + message.videoLocation.id + ".mp4";
}
putToDelayedMessages(location, message);
if (message.obj.messageOwner.videoEditedInfo != null) {
if (message.obj.videoEditedInfo != null) {
FileLoader.getInstance().uploadFile(location, true, false, message.videoLocation.size);
} else {
FileLoader.getInstance().uploadFile(location, true, false);
@ -1390,10 +1460,10 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter
ImageLoader.getInstance().loadHttpFile(message.httpLocation, "gif");
} else {
if (message.sendRequest != null) {
TLRPC.InputMedia media = null;
TLRPC.InputMedia media;
if (message.sendRequest instanceof TLRPC.TL_messages_sendMedia) {
media = ((TLRPC.TL_messages_sendMedia) message.sendRequest).media;
} else if (message.sendRequest instanceof TLRPC.TL_messages_sendBroadcast) {
} else {
media = ((TLRPC.TL_messages_sendBroadcast) message.sendRequest).media;
}
if (media.file == null) {
@ -1514,7 +1584,7 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter
ArrayList<MessageObject> arr = new ArrayList<>();
MessageObject messageObject = new MessageObject(message, null, false);
arr.add(messageObject);
MessagesController.getInstance().updateInterfaceWithMessages(messageObject.getDialogId(), arr, isBroadcast);
MessagesController.getInstance().updateInterfaceWithMessages(messageObject.getDialogId(), arr, true);
}
NotificationCenter.getInstance().postNotificationName(NotificationCenter.dialogsNeedReload);
}
@ -1571,25 +1641,28 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter
MessagesStorage.getInstance().putSentFile(originalPath, sentMessage.media.photo, 0);
for (TLRPC.PhotoSize size : sentMessage.media.photo.sizes) {
if (size instanceof TLRPC.TL_photoSizeEmpty) {
if (size == null || size instanceof TLRPC.TL_photoSizeEmpty || size.type == null) {
continue;
}
for (TLRPC.PhotoSize size2 : newMsg.media.photo.sizes) {
if (size2.location != null && size2.location.volume_id == Integer.MIN_VALUE && size.type.equals(size2.type) || size.w == size2.w && size.h == size2.h) {
if (size2 == null || size2.location == null || size2.type == null) {
continue;
}
if (size2.location.volume_id == Integer.MIN_VALUE && size.type.equals(size2.type) || size.w == size2.w && size.h == size2.h) {
String fileName = size2.location.volume_id + "_" + size2.location.local_id;
String fileName2 = size.location.volume_id + "_" + size.location.local_id;
if (fileName.equals(fileName2)) {
break;
}
File cacheFile = new File(FileLoader.getInstance().getDirectory(FileLoader.MEDIA_DIR_CACHE), fileName + ".jpg");
File cacheFile2 = null;
File cacheFile2;
if (sentMessage.media.photo.sizes.size() == 1 || size.w > 90 || size.h > 90) {
cacheFile2 = FileLoader.getPathToAttach(size);
} else {
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 +1684,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 +1715,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 +1770,10 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter
arrayList.add(message);
}
protected ArrayList<DelayedMessage> getDelayedMessages(String location) {
return delayedMessages.get(location);
}
protected long getNextRandomId() {
long val = 0;
while (val == 0) {
@ -1749,7 +1826,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;
}
@ -1778,13 +1854,10 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter
return false;
}
boolean isEncrypted = (int)dialog_id == 0;
boolean isEncrypted = (int) dialog_id == 0;
boolean allowSticker = !isEncrypted;
String name = f.getName();
if (name == null) {
name = "noname";
}
String ext = "";
int idx = path.lastIndexOf(".");
if (idx != -1) {
@ -1808,14 +1881,18 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter
TLRPC.TL_documentAttributeFilename fileName = new TLRPC.TL_documentAttributeFilename();
fileName.file_name = name;
document.attributes.add(fileName);
document.size = (int)f.length();
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";
@ -1843,12 +1920,13 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter
FileLog.e("tmessages", e);
}
if (bmOptions.outWidth != 0 && bmOptions.outHeight != 0 && bmOptions.outWidth <= 800 && bmOptions.outHeight <= 800) {
TLRPC.TL_documentAttributeSticker attributeSticker = null;
TLRPC.TL_documentAttributeSticker attributeSticker;
if (isEncrypted) {
attributeSticker = new TLRPC.TL_documentAttributeSticker_old();
} else {
attributeSticker = new TLRPC.TL_documentAttributeSticker();
attributeSticker.alt = "";
attributeSticker.stickerset = new TLRPC.TL_inputStickerSetEmpty();
}
document.attributes.add(attributeSticker);
TLRPC.TL_documentAttributeImageSize attributeImageSize = new TLRPC.TL_documentAttributeImageSize();
@ -1929,9 +2007,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<String> paths = null;
ArrayList<Uri> uris = null;
ArrayList<String> captions = null;
if (imageFilePath != null && imageFilePath.length() != 0) {
paths = new ArrayList<>();
paths.add(imageFilePath);
@ -1940,7 +2019,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<MediaController.SearchImage> photos, final long dialog_id, final MessageObject reply_to_msg) {
@ -1950,8 +2033,9 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter
new Thread(new Runnable() {
@Override
public void run() {
boolean isEncrypted = (int)dialog_id == 0;
for (final MediaController.SearchImage searchImage : photos) {
boolean isEncrypted = (int) dialog_id == 0;
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) {
@ -1960,7 +2044,7 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter
String md5 = Utilities.MD5(searchImage.imageUrl) + "." + ImageLoader.getHttpUrlExtension(searchImage.imageUrl);
File cacheFile = new File(FileLoader.getInstance().getDirectory(FileLoader.MEDIA_DIR_CACHE), md5);
if (document == null) {
File thumbFile = null;
File thumbFile;
document = new TLRPC.TL_document();
document.id = 0;
document.date = ConnectionsManager.getInstance().getCurrentTime();
@ -2017,19 +2101,22 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter
if (photo == null) {
String md5 = Utilities.MD5(searchImage.imageUrl) + "." + ImageLoader.getHttpUrlExtension(searchImage.imageUrl);
File cacheFile = new File(FileLoader.getInstance().getDirectory(FileLoader.MEDIA_DIR_CACHE), md5);
if (cacheFile.exists()) {
if (cacheFile.exists() && cacheFile.length() != 0) {
photo = SendMessagesHelper.getInstance().generatePhotoSizes(cacheFile.toString(), null);
needDownloadHttp = false;
} else {
if (photo != null) {
needDownloadHttp = false;
}
}
if (photo == null) {
md5 = Utilities.MD5(searchImage.thumbUrl) + "." + ImageLoader.getHttpUrlExtension(searchImage.thumbUrl);
cacheFile = new File(FileLoader.getInstance().getDirectory(FileLoader.MEDIA_DIR_CACHE), md5);
if (cacheFile.exists()) {
photo = SendMessagesHelper.getInstance().generatePhotoSizes(cacheFile.toString(), null);
} else {
}
if (photo == null) {
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 +2129,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 +2148,32 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter
}).start();
}
public static void prepareSendingPhotos(ArrayList<String> paths, ArrayList<Uri> 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<String> paths, ArrayList<Uri> uris, final long dialog_id, final MessageObject reply_to_msg, final ArrayList<String> captions) {
if (paths == null && uris == null || paths != null && paths.isEmpty() || uris != null && uris.isEmpty()) {
return;
}
@ -2073,13 +2188,14 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter
new Thread(new Runnable() {
@Override
public void run() {
boolean isEncrypted = (int)dialog_id == 0;
boolean isEncrypted = (int) dialog_id == 0;
ArrayList<String> sendAsDocuments = null;
ArrayList<String> sendAsDocumentsOriginal = null;
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);
@ -2090,22 +2206,29 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter
String originalPath = path;
String tempPath = path;
if (tempPath == null && uri != null) {
tempPath = Utilities.getPath(uri);
tempPath = AndroidUtilities.getPath(uri);
originalPath = uri.toString();
}
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";
}
}
@ -2127,13 +2250,16 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter
if (!isEncrypted) {
photo = (TLRPC.TL_photo) MessagesStorage.getInstance().getSentFile(originalPath, !isEncrypted ? 0 : 3);
if (photo == null && uri != null) {
photo = (TLRPC.TL_photo) MessagesStorage.getInstance().getSentFile(Utilities.getPath(uri), !isEncrypted ? 0 : 3);
photo = (TLRPC.TL_photo) MessagesStorage.getInstance().getSentFile(AndroidUtilities.getPath(uri), !isEncrypted ? 0 : 3);
}
}
if (photo == null) {
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,14 +2273,14 @@ 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);
}
}
}
}).start();
}
public static void prepareSendingVideo(final String videoPath, final long estimatedSize, final long duration, final int width, final int height, final TLRPC.VideoEditedInfo videoEditedInfo, final long dialog_id, final MessageObject reply_to_msg) {
public static void prepareSendingVideo(final String videoPath, final long estimatedSize, final long duration, final int width, final int height, final VideoEditedInfo videoEditedInfo, final long dialog_id, final MessageObject reply_to_msg) {
if (videoPath == null || videoPath.length() == 0) {
return;
}
@ -2162,7 +2288,7 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter
@Override
public void run() {
boolean isEncrypted = (int)dialog_id == 0;
boolean isEncrypted = (int) dialog_id == 0;
if (videoEditedInfo != null || videoPath.endsWith("mp4")) {
String path = videoPath;
@ -2190,7 +2316,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);
@ -2205,14 +2330,13 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter
video.h = height;
}
video.size = (int) estimatedSize;
video.videoEditedInfo = videoEditedInfo;
String fileName = Integer.MIN_VALUE + "_" + UserConfig.lastLocalId + ".mp4";
UserConfig.lastLocalId--;
File cacheFile = new File(FileLoader.getInstance().getDirectory(FileLoader.MEDIA_DIR_CACHE), fileName);
UserConfig.saveConfig(false);
path = cacheFile.getAbsolutePath();
} else {
if (temp != null && temp.exists()) {
if (temp.exists()) {
video.size = (int) temp.length();
}
boolean infoObtained = false;
@ -2240,7 +2364,6 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter
try {
if (mediaMetadataRetriever != null) {
mediaMetadataRetriever.release();
mediaMetadataRetriever = null;
}
} catch (Exception e) {
FileLog.e("tmessages", e);
@ -2268,7 +2391,7 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter
AndroidUtilities.runOnUIThread(new Runnable() {
@Override
public void run() {
SendMessagesHelper.getInstance().sendMessage(videoFinal, originalPathFinal, finalPath, dialog_id, reply_to_msg);
SendMessagesHelper.getInstance().sendMessage(videoFinal, videoEditedInfo, originalPathFinal, finalPath, dialog_id, reply_to_msg);
}
});
} else {

View File

@ -44,11 +44,16 @@ public class SmsListener extends BroadcastReceiver {
try {
Pattern pattern = Pattern.compile("[0-9]+");
Matcher matcher = pattern.matcher(wholeString);
final Matcher matcher = pattern.matcher(wholeString);
if (matcher.find()) {
String str = matcher.group(0);
if (str.length() >= 3) {
NotificationCenter.getInstance().postNotificationName(NotificationCenter.didReceiveSmsCode, matcher.group(0));
AndroidUtilities.runOnUIThread(new Runnable() {
@Override
public void run() {
NotificationCenter.getInstance().postNotificationName(NotificationCenter.didReceiveSmsCode, matcher.group(0));
}
});
}
}
} catch (Throwable e) {

View File

@ -0,0 +1,51 @@
/*
* This is the source code of Telegram for Android v. 2.x.x.
* It is licensed under GNU GPL v. 2 or later.
* You should have received a copy of the license in this archive (see LICENSE).
*
* Copyright Nikolai Kudashov, 2013-2014.
*/
package org.telegram.android;
import java.util.Locale;
public class VideoEditedInfo {
public long startTime;
public long endTime;
public int rotationValue;
public int originalWidth;
public int originalHeight;
public int resultWidth;
public int resultHeight;
public int bitrate;
public String originalPath;
public String getString() {
return String.format(Locale.US, "-1_%d_%d_%d_%d_%d_%d_%d_%d_%s", startTime, endTime, rotationValue, originalWidth, originalHeight, bitrate, resultWidth, resultHeight, originalPath);
}
public void parseString(String string) {
if (string.length() < 6) {
return;
}
String args[] = string.split("_");
if (args.length >= 10) {
startTime = Long.parseLong(args[1]);
endTime = Long.parseLong(args[2]);
rotationValue = Integer.parseInt(args[3]);
originalWidth = Integer.parseInt(args[4]);
originalHeight = Integer.parseInt(args[5]);
bitrate = Integer.parseInt(args[6]);
resultWidth = Integer.parseInt(args[7]);
resultHeight = Integer.parseInt(args[8]);
for (int a = 9; a < args.length; a++) {
if (originalPath == null) {
originalPath = args[a];
} else {
originalPath += "_" + args[a];
}
}
}
}
}

View File

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

View File

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

View File

@ -0,0 +1,437 @@
/*
* 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 android.app.AlertDialog;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.os.Message;
import android.widget.Toast;
import org.telegram.SQLite.SQLiteCursor;
import org.telegram.SQLite.SQLitePreparedStatement;
import org.telegram.android.AndroidUtilities;
import org.telegram.android.LocaleController;
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.R;
import org.telegram.messenger.RPCRequest;
import org.telegram.messenger.TLObject;
import org.telegram.messenger.TLRPC;
import org.telegram.messenger.Utilities;
import org.telegram.ui.ActionBar.BaseFragment;
import org.telegram.ui.Components.StickersAlert;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
public class StickersQuery {
private static String hash;
private static int loadDate;
private static ArrayList<TLRPC.Document> stickers = new ArrayList<>();
private static HashMap<String, ArrayList<TLRPC.Document>> allStickers = new HashMap<>();
private static ArrayList<TLRPC.TL_stickerPack> stickerPacks = new ArrayList<>();
private static ArrayList<TLRPC.TL_stickerSet> stickerSets = new ArrayList<>();
private static HashMap<Long, ArrayList<TLRPC.Document>> stickersBySets = new HashMap<>();
private static HashMap<Long, String> stickersByEmoji = new HashMap<>();
private static boolean loadingStickers;
private static boolean stickersLoaded;
private static boolean hideMainStickersPack;
public static void checkStickers() {
if (!loadingStickers && (!stickersLoaded || loadDate < (System.currentTimeMillis() / 1000 - 60 * 60))) {
loadStickers(true, false);
}
}
public static boolean isLoadingStickers() {
return loadingStickers;
}
public static HashMap<String, ArrayList<TLRPC.Document>> getAllStickers() {
return allStickers;
}
public static ArrayList<TLRPC.Document> getStickersForSet(long id) {
return stickersBySets.get(id);
}
public static ArrayList<TLRPC.TL_stickerPack> getStickerPacks() {
return stickerPacks;
}
public static ArrayList<TLRPC.Document> getStickers() {
return stickers;
}
public static ArrayList<TLRPC.TL_stickerSet> getStickerSets() {
return stickerSets;
}
public static boolean isStickerPackInstalled(long id) {
return stickersBySets.containsKey(id);
}
public static String getEmojiForSticker(long id) {
String value = stickersByEmoji.get(id);
return value != null ? value : "";
}
public static void loadStickers(boolean cache, boolean force) {
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 value FROM keyvalue WHERE id = 'hide_stickers'");
if (cursor.next()) {
int value = Utilities.parseInt(cursor.stringValue(0));
hideMainStickersPack = value == 1;
}
cursor.dispose();
cursor = MessagesStorage.getInstance().getDatabase().queryFinalized("SELECT data, date FROM stickers WHERE 1");
ArrayList<TLRPC.User> 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 == null || force ? "" : 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 long getStickerSetId(TLRPC.Document document) {
for (TLRPC.DocumentAttribute attribute : document.attributes) {
if (attribute instanceof TLRPC.TL_documentAttributeSticker) {
if (attribute.stickerset instanceof TLRPC.TL_inputStickerSetID) {
return attribute.stickerset.id;
}
break;
}
}
return -1;
}
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;
stickersLoaded = true;
}
});
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, false);
}
});
if (res == null) {
return;
}
}
if (res instanceof TLRPC.TL_messages_allStickers) {
HashMap<Long, TLRPC.Document> documents = new HashMap<>();
final HashMap<Long, ArrayList<TLRPC.Document>> sets = new HashMap<>();
final ArrayList<TLRPC.Document> allDocuments = new ArrayList<>();
final HashMap<Long, String> stickersEmoji = new HashMap<>();
for (TLRPC.Document document : res.documents) {
if (document == null) {
continue;
}
documents.put(document.id, document);
long setId = getStickerSetId(document);
if (setId != -1 || setId == -1 && !hideMainStickersPack) {
allDocuments.add(document);
}
ArrayList<TLRPC.Document> docs = sets.get(setId);
if (docs == null) {
docs = new ArrayList<>();
sets.put(setId, docs);
if (setId == -1) {
boolean contain = false;
for (TLRPC.TL_stickerSet set : res.sets) {
if (set.id == setId) {
contain = true;
break;
}
}
if (!contain) {
TLRPC.TL_stickerSet set = new TLRPC.TL_stickerSet();
set.title = set.short_name = "";
set.id = -1;
res.sets.add(0, set);
}
}
}
docs.add(document);
}
final HashMap<String, ArrayList<TLRPC.Document>> result = new HashMap<>();
for (TLRPC.TL_stickerPack stickerPack : res.packs) {
if (stickerPack != null && stickerPack.emoticon != null) {
stickerPack.emoticon = stickerPack.emoticon.replace("\uFE0F", "");
ArrayList<TLRPC.Document> arrayList = result.get(stickerPack.emoticon);
for (Long id : stickerPack.documents) {
if (!stickersEmoji.containsKey(id)) {
stickersEmoji.put(id, stickerPack.emoticon);
}
TLRPC.Document document = documents.get(id);
if (document != null) {
long setId = getStickerSetId(document);
if (setId == -1 && hideMainStickersPack) {
continue;
}
if (arrayList == null) {
arrayList = new ArrayList<>();
result.put(stickerPack.emoticon, arrayList);
}
arrayList.add(document);
}
}
}
}
Collections.sort(allDocuments, new Comparator<TLRPC.Document>() {
@Override
public int compare(TLRPC.Document lhs, TLRPC.Document rhs) {
long lid = getStickerSetId(lhs);
long rid = getStickerSetId(rhs);
if (lid < rid) {
return -1;
} else if (lid > rid) {
return 1;
}
return 0;
}
});
if (!cache) {
putStickersToCache((TLRPC.TL_messages_allStickers) res);
}
AndroidUtilities.runOnUIThread(new Runnable() {
@Override
public void run() {
stickerSets = res.sets;
allStickers = result;
stickers = allDocuments;
stickersBySets = sets;
stickersByEmoji = stickersEmoji;
hash = res.hash;
loadDate = date;
NotificationCenter.getInstance().postNotificationName(NotificationCenter.stickersDidLoaded);
}
});
}
}
});
}
public static void loadStickers(final BaseFragment fragment, final TLRPC.InputStickerSet stickerSet) {
if (fragment == null || stickerSet == null) {
return;
}
final ProgressDialog progressDialog = new ProgressDialog(fragment.getParentActivity());
progressDialog.setMessage(LocaleController.getString("Loading", R.string.Loading));
progressDialog.setCanceledOnTouchOutside(false);
progressDialog.setCancelable(false);
TLRPC.TL_messages_getStickerSet req = new TLRPC.TL_messages_getStickerSet();
req.stickerset = stickerSet;
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() {
try {
progressDialog.dismiss();
} catch (Exception e) {
FileLog.e("tmessages", e);
}
if (fragment != null && fragment.getParentActivity() != null && !fragment.getParentActivity().isFinishing()) {
if (error == null) {
final TLRPC.TL_messages_stickerSet res = (TLRPC.TL_messages_stickerSet) response;
StickersAlert alert = new StickersAlert(fragment.getParentActivity(), res.set, res.documents);
if (res.set == null || !StickersQuery.isStickerPackInstalled(res.set.id)) {
alert.setButton(AlertDialog.BUTTON_POSITIVE, LocaleController.getString("AddStickers", R.string.AddStickers), new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
TLRPC.TL_messages_installStickerSet req = new TLRPC.TL_messages_installStickerSet();
req.stickerset = stickerSet;
ConnectionsManager.getInstance().performRpc(req, new RPCRequest.RPCRequestDelegate() {
@Override
public void run(TLObject response, final TLRPC.TL_error error) {
AndroidUtilities.runOnUIThread(new Runnable() {
@Override
public void run() {
if (fragment != null && fragment.getParentActivity() != null) {
if (error == null) {
Toast.makeText(fragment.getParentActivity(), LocaleController.getString("AddStickersInstalled", R.string.AddStickersInstalled), Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(fragment.getParentActivity(), LocaleController.getString("ErrorOccurred", R.string.ErrorOccurred), Toast.LENGTH_SHORT).show();
}
}
loadStickers(false, true);
}
});
}
});
}
});
} else {
alert.setButton(AlertDialog.BUTTON_NEUTRAL, LocaleController.getString("StickersRemove", R.string.StickersRemove), new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
removeStickersSet(fragment.getParentActivity(), res.set);
}
});
}
alert.setButton(AlertDialog.BUTTON_NEGATIVE, LocaleController.getString("Close", R.string.Close), (Message) null);
fragment.setVisibleDialog(alert);
alert.show();
} else {
Toast.makeText(fragment.getParentActivity(), LocaleController.getString("AddStickersNotFound", R.string.AddStickersNotFound), Toast.LENGTH_SHORT).show();
}
}
}
});
}
});
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);
}
}
});
fragment.setVisibleDialog(progressDialog);
progressDialog.show();
}
public static void setHideMainStickersPack(final boolean value) {
hideMainStickersPack = value;
MessagesStorage.getInstance().getStorageQueue().postRunnable(new Runnable() {
@Override
public void run() {
try {
SQLitePreparedStatement state = MessagesStorage.getInstance().getDatabase().executeFast("REPLACE INTO keyvalue VALUES(?, ?)");
state.requery();
state.bindString(1, "hide_stickers");
state.bindString(2, value ? "1" : "0");
state.step();
state.dispose();
} catch (Exception e) {
FileLog.e("tmessages", e);
}
}
});
}
public static void removeStickersSet(final Context context, TLRPC.TL_stickerSet stickerSet) {
TLRPC.TL_messages_uninstallStickerSet req = new TLRPC.TL_messages_uninstallStickerSet();
req.stickerset = new TLRPC.TL_inputStickerSetID();
req.stickerset.access_hash = stickerSet.access_hash;
req.stickerset.id = stickerSet.id;
ConnectionsManager.getInstance().performRpc(req, new RPCRequest.RPCRequestDelegate() {
@Override
public void run(TLObject response, final TLRPC.TL_error error) {
AndroidUtilities.runOnUIThread(new Runnable() {
@Override
public void run() {
try {
if (error == null) {
Toast.makeText(context, LocaleController.getString("StickersRemoved", R.string.StickersRemoved), Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(context, LocaleController.getString("ErrorOccurred", R.string.ErrorOccurred), Toast.LENGTH_SHORT).show();
}
} catch (Exception e) {
FileLog.e("tmessages", e);
}
loadStickers(false, true);
}
});
}
});
}
public static boolean getHideMainStickersPack() {
return hideMainStickersPack;
}
}

View File

@ -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}.
* <p>
* 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.
* <p>
* You can control the order of items and change notifications via the {@link Callback} parameter.
*/
@SuppressWarnings("unchecked")
public class SortedList<T> {
/**
* 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<T> 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<T> klass, Callback<T> 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<T> klass, Callback<T> 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)}.
* <p>
* 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.
* <p>
* 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.
* <p>
* After running your data updates, you <b>must</b> call {@link #endBatchedUpdates()}
* which will dispatch any deferred data change event to the current callback.
* <p>
* A sample implementation may look like this:
* <pre>
* mSortedList.beginBatchedUpdates();
* try {
* mSortedList.add(item1)
* mSortedList.add(item2)
* mSortedList.remove(item3)
* ...
* } finally {
* mSortedList.endBatchedUpdates();
* }
* </pre>
* <p>
* 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.
* <p>
* 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.
* <p>
* You can use this method if you need to change an existing Item such that its position in the
* list may change.
* <p>
* If the new object is a different object (<code>get(index) != item</code>) and
* {@link Callback#areContentsTheSame(Object, Object)} returns <code>true</code>, SortedList
* avoids calling {@link Callback#onChanged(int, int)} otherwise it calls
* {@link Callback#onChanged(int, int)}.
* <p>
* If the new position of the item is different than the provided <code>index</code>,
* 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.
* <p>
* 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)}.
* <p>
* A sample usage may look like:
*
* <pre>
* final int position = mSortedList.indexOf(item);
* item.incrementPriority(); // assume items are sorted by priority
* mSortedList.recalculatePositionOfItemAt(position);
* </pre>
* 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}.
* <p>
* It defines how items should be sorted and how duplicates should be handled.
* <p>
* SortedList calls the callback methods on this class to notify changes about the underlying
* data.
*/
public static abstract class Callback<T2> {
/**
* 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.
* <p>
* SortedList uses this method to check equality instead of {@link Object#equals(Object)}
* so
* that you can change its behavior depending on your UI.
* <p>
* 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.
* <p>
* 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.
* <p>
* 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.
* <p>
* For example, if you are going to add multiple items to a SortedList, BatchedCallback call
* convert individual <code>onInserted(index, 1)</code> calls into one
* <code>onInserted(index, N)</code> if items are added into consecutive indices. This change
* can help RecyclerView resolve changes much more easily.
* <p>
* 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 <b>must</b> always call {@link BatchedCallback#dispatchLastEvent()} to flush
* all changes to the Callback.
*/
public static class BatchedCallback<T2> extends Callback<T2> {
private final Callback<T2> 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<T2> 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 <b>must</b> 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;
}
}
}

View File

@ -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.
* <p>
* 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.
* <p>
* 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.
* <p>
* 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<UpdateOp> mUpdateOpPool = new Pools.SimplePool<UpdateOp>(UpdateOp.POOL_SIZE);
final ArrayList<UpdateOp> mPendingUpdates = new ArrayList<UpdateOp>();
final ArrayList<UpdateOp> mPostponedList = new ArrayList<UpdateOp>();
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<UpdateOp> 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);
}
}

View File

@ -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.
* <p>
* It wraps a RecyclerView and adds ability to hide some children. There are two sets of methods
* provided by this class. <b>Regular</b> methods are the ones that replicate ViewGroup methods
* like getChildAt, getChildCount etc. These methods ignore hidden children.
* <p>
* 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<View> mHiddenViews;
ChildHelper(Callback callback) {
mCallback = callback;
mBucket = new Bucket();
mHiddenViews = new ArrayList<View>();
}
/**
* 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);
}
}

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