/* * This is the source code of Telegram for Android v. 3.x.x. * It is licensed under GNU GPL v. 2 or later. * You should have received a copy of the license in this archive (see LICENSE). * * Copyright Nikolai Kudashov, 2013-2016. */ package org.telegram.messenger; import android.animation.Animator; import android.animation.AnimatorSet; import android.animation.ObjectAnimator; 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.pm.PackageManager; import android.content.res.Configuration; import android.database.Cursor; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.BitmapShader; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Point; import android.graphics.Rect; import android.graphics.RectF; import android.graphics.Shader; import android.graphics.Typeface; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.ColorDrawable; 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.SpannableStringBuilder; import android.text.Spanned; import android.text.style.ForegroundColorSpan; import android.util.DisplayMetrics; import android.util.StateSet; import android.view.Display; import android.view.Surface; import android.view.View; import android.view.WindowManager; import android.view.inputmethod.InputMethodManager; import android.webkit.MimeTypeMap; import android.widget.AbsListView; import android.widget.EdgeEffect; import android.widget.EditText; 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.tgnet.ConnectionsManager; import org.telegram.tgnet.TLRPC; import org.telegram.ui.ActionBar.BaseFragment; 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; import java.util.regex.Pattern; public class AndroidUtilities { private static final Hashtable typefaceCache = new Hashtable<>(); private static int prevOrientation = -10; private static boolean waitingForSms = false; private static boolean waitingForCall = false; private static final Object smsLock = new Object(); private static final Object callLock = new Object(); public static int statusBarHeight = 0; public static float density = 1; public static Point displaySize = new Point(); public static Integer photoSize = null; public static DisplayMetrics displayMetrics = new DisplayMetrics(); public static int leftBaseline; public static boolean usingHardwareInput; private static Boolean isTablet = null; private static int adjustOwnerClassGuid = 0; private static Paint roundPaint; private static RectF bitmapRect; public static Pattern WEB_URL = null; static { try { final String GOOD_IRI_CHAR = "a-zA-Z0-9\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF"; final Pattern IP_ADDRESS = Pattern.compile( "((25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}|[1-9][0-9]|[1-9])\\.(25[0-5]|2[0-4]" + "[0-9]|[0-1][0-9]{2}|[1-9][0-9]|[1-9]|0)\\.(25[0-5]|2[0-4][0-9]|[0-1]" + "[0-9]{2}|[1-9][0-9]|[1-9]|0)\\.(25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}" + "|[1-9][0-9]|[0-9]))"); final String IRI = "[" + GOOD_IRI_CHAR + "]([" + GOOD_IRI_CHAR + "\\-]{0,61}[" + GOOD_IRI_CHAR + "]){0,1}"; final String GOOD_GTLD_CHAR = "a-zA-Z\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF"; final String GTLD = "[" + GOOD_GTLD_CHAR + "]{2,63}"; final String HOST_NAME = "(" + IRI + "\\.)+" + GTLD; final Pattern DOMAIN_NAME = Pattern.compile("(" + HOST_NAME + "|" + IP_ADDRESS + ")"); WEB_URL = Pattern.compile( "((?:(http|https|Http|Https):\\/\\/(?:(?:[a-zA-Z0-9\\$\\-\\_\\.\\+\\!\\*\\'\\(\\)" + "\\,\\;\\?\\&\\=]|(?:\\%[a-fA-F0-9]{2})){1,64}(?:\\:(?:[a-zA-Z0-9\\$\\-\\_" + "\\.\\+\\!\\*\\'\\(\\)\\,\\;\\?\\&\\=]|(?:\\%[a-fA-F0-9]{2})){1,25})?\\@)?)?" + "(?:" + DOMAIN_NAME + ")" + "(?:\\:\\d{1,5})?)" // plus option port number + "(\\/(?:(?:[" + GOOD_IRI_CHAR + "\\;\\/\\?\\:\\@\\&\\=\\#\\~" // plus option query params + "\\-\\.\\+\\!\\*\\'\\(\\)\\,\\_])|(?:\\%[a-fA-F0-9]{2}))*)?" + "(?:\\b|$)"); } catch (Exception e) { FileLog.e("tmessages", e); } } static { density = ApplicationLoader.applicationContext.getResources().getDisplayMetrics().density; leftBaseline = isTablet() ? 80 : 72; checkDisplaySize(); } public static int[] calcDrawableColor(Drawable drawable) { int bitmapColor = 0xff000000; int result[] = new int[2]; try { if (drawable instanceof BitmapDrawable) { Bitmap bitmap = ((BitmapDrawable) drawable).getBitmap(); if (bitmap != null) { Bitmap b = Bitmaps.createScaledBitmap(bitmap, 1, 1, true); if (b != null) { bitmapColor = b.getPixel(0, 0); b.recycle(); } } } else if (drawable instanceof ColorDrawable) { bitmapColor = ((ColorDrawable) drawable).getColor(); } } catch (Exception e) { FileLog.e("tmessages", e); } double[] hsv = rgbToHsv((bitmapColor >> 16) & 0xff, (bitmapColor >> 8) & 0xff, bitmapColor & 0xff); hsv[1] = Math.min(1.0, hsv[1] + 0.05 + 0.1 * (1.0 - hsv[1])); hsv[2] = Math.max(0, hsv[2] * 0.65); int rgb[] = hsvToRgb(hsv[0], hsv[1], hsv[2]); result[0] = Color.argb(0x66, rgb[0], rgb[1], rgb[2]); result[1] = Color.argb(0x88, rgb[0], rgb[1], rgb[2]); return result; } private static double[] rgbToHsv(int r, int g, int b) { double rf = r / 255.0; double gf = g / 255.0; double bf = b / 255.0; double max = (rf > gf && rf > bf) ? rf : (gf > bf) ? gf : bf; double min = (rf < gf && rf < bf) ? rf : (gf < bf) ? gf : bf; double h, s; double d = max - min; s = max == 0 ? 0 : d / max; if (max == min) { h = 0; } else { if (rf > gf && rf > bf) { h = (gf - bf) / d + (gf < bf ? 6 : 0); } else if (gf > bf) { h = (bf - rf) / d + 2; } else { h = (rf - gf) / d + 4; } h /= 6; } return new double[]{h, s, max}; } private static int[] hsvToRgb(double h, double s, double v) { double r = 0, g = 0, b = 0; double i = (int) Math.floor(h * 6); double f = h * 6 - i; double p = v * (1 - s); double q = v * (1 - f * s); double t = v * (1 - (1 - f) * s); switch ((int) i % 6) { case 0: r = v; g = t; b = p; break; case 1: r = q; g = v; b = p; break; case 2: r = p; g = v; b = t; break; case 3: r = p; g = q; b = v; break; case 4: r = t; g = p; b = v; break; case 5: r = v; g = p; b = q; break; } return new int[]{(int) (r * 255), (int) (g * 255), (int) (b * 255)}; } public static void requestAdjustResize(Activity activity, int classGuid) { if (activity == null || isTablet()) { return; } activity.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE); adjustOwnerClassGuid = classGuid; } public static void removeAdjustResize(Activity activity, int classGuid) { if (activity == null || isTablet()) { return; } if (adjustOwnerClassGuid == classGuid) { activity.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN); } } public static boolean isGoogleMapsInstalled(final BaseFragment fragment) { try { ApplicationLoader.applicationContext.getPackageManager().getApplicationInfo("com.google.android.apps.maps", 0); return true; } catch (PackageManager.NameNotFoundException e) { if (fragment.getParentActivity() == null) { return false; } AlertDialog.Builder builder = new AlertDialog.Builder(fragment.getParentActivity()); builder.setMessage("Install Google Maps?"); builder.setCancelable(true); builder.setPositiveButton(LocaleController.getString("OK", R.string.OK), new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialogInterface, int i) { try { Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse("market://details?id=com.google.android.apps.maps")); fragment.getParentActivity().startActivityForResult(intent, 500); } catch (Exception e) { FileLog.e("tmessages", e); } } }); builder.setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), null); fragment.showDialog(builder.create()); return false; } } public static boolean isInternalUri(Uri uri) { String pathString = uri.getPath(); if (pathString == null) { return false; } while (true) { String newPath = Utilities.readlink(pathString); if (newPath == null || newPath.equals(pathString)) { break; } pathString = newPath; } return pathString != null && pathString.toLowerCase().contains("/data/data/" + ApplicationLoader.applicationContext.getPackageName() + "/files"); } public static void lockOrientation(Activity activity) { if (activity == null || prevOrientation != -10) { return; } try { prevOrientation = activity.getRequestedOrientation(); WindowManager manager = (WindowManager)activity.getSystemService(Activity.WINDOW_SERVICE); if (manager != null && manager.getDefaultDisplay() != null) { int rotation = manager.getDefaultDisplay().getRotation(); int orientation = activity.getResources().getConfiguration().orientation; if (rotation == Surface.ROTATION_270) { if (orientation == Configuration.ORIENTATION_PORTRAIT) { activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); } else { activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE); } } else if (rotation == Surface.ROTATION_90) { if (orientation == Configuration.ORIENTATION_PORTRAIT) { activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT); } else { activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); } } else if (rotation == Surface.ROTATION_0) { if (orientation == Configuration.ORIENTATION_LANDSCAPE) { activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); } else { activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); } } else { if (orientation == Configuration.ORIENTATION_LANDSCAPE) { activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE); } else { activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT); } } } } catch (Exception e) { FileLog.e("tmessages", e); } } public static void unlockOrientation(Activity activity) { if (activity == null) { return; } try { if (prevOrientation != -10) { activity.setRequestedOrientation(prevOrientation); prevOrientation = -10; } } catch (Exception e) { FileLog.e("tmessages", e); } } public static Typeface getTypeface(String assetPath) { synchronized (typefaceCache) { if (!typefaceCache.containsKey(assetPath)) { try { Typeface t = Typeface.createFromAsset(ApplicationLoader.applicationContext.getAssets(), assetPath); typefaceCache.put(assetPath, t); } catch (Exception e) { FileLog.e("Typefaces", "Could not get typeface '" + assetPath + "' because " + e.getMessage()); return null; } } return typefaceCache.get(assetPath); } } public static boolean isWaitingForSms() { boolean value; synchronized (smsLock) { value = waitingForSms; } return value; } public static void setWaitingForSms(boolean value) { synchronized (smsLock) { waitingForSms = value; } } public static boolean isWaitingForCall() { boolean value; synchronized (callLock) { value = waitingForCall; } return value; } public static void setWaitingForCall(boolean value) { synchronized (callLock) { waitingForCall = value; } } public static void showKeyboard(View view) { if (view == null) { return; } try { InputMethodManager inputManager = (InputMethodManager)view.getContext().getSystemService(Context.INPUT_METHOD_SERVICE); inputManager.showSoftInput(view, InputMethodManager.SHOW_IMPLICIT); } catch (Exception e) { FileLog.e("tmessages", e); } } public static boolean isKeyboardShowed(View view) { if (view == null) { return false; } try { InputMethodManager inputManager = (InputMethodManager) view.getContext().getSystemService(Context.INPUT_METHOD_SERVICE); return inputManager.isActive(view); } catch (Exception e) { FileLog.e("tmessages", e); } return false; } public static void hideKeyboard(View view) { if (view == null) { return; } try { InputMethodManager imm = (InputMethodManager) view.getContext().getSystemService(Context.INPUT_METHOD_SERVICE); if (!imm.isActive()) { return; } imm.hideSoftInputFromWindow(view.getWindowToken(), 0); } catch (Exception e) { FileLog.e("tmessages", e); } } public static File getCacheDir() { String state = null; try { state = Environment.getExternalStorageState(); } catch (Exception e) { FileLog.e("tmessages", e); } if (state == null || state.startsWith(Environment.MEDIA_MOUNTED)) { try { File file = ApplicationLoader.applicationContext.getExternalCacheDir(); if (file != null) { return file; } } catch (Exception e) { FileLog.e("tmessages", e); } } try { File file = ApplicationLoader.applicationContext.getCacheDir(); if (file != null) { return file; } } catch (Exception e) { FileLog.e("tmessages", e); } return new File(""); } 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 { 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); display.getSize(displaySize); FileLog.e("tmessages", "display size = " + displaySize.x + " " + displaySize.y + " " + displayMetrics.xdpi + "x" + displayMetrics.ydpi); } } } catch (Exception e) { FileLog.e("tmessages", e); } } public static float getPixelsInCM(float cm, boolean isX) { return (cm / 2.54f) * (isX ? displayMetrics.xdpi : displayMetrics.ydpi); } public static long makeBroadcastId(int id) { return 0x0000000100000000L | ((long)id & 0x00000000FFFFFFFFL); } public static int getMyLayerVersion(int layer) { return layer & 0xffff; } public static int getPeerLayerVersion(int layer) { return (layer >> 16) & 0xffff; } public static int setMyLayerVersion(int layer, int version) { return layer & 0xffff0000 | version; } public static int setPeerLayerVersion(int layer, int version) { return layer & 0x0000ffff | (version << 16); } public static void runOnUIThread(Runnable runnable) { runOnUIThread(runnable, 0); } public static void runOnUIThread(Runnable runnable, long delay) { if (delay == 0) { ApplicationLoader.applicationHandler.post(runnable); } else { ApplicationLoader.applicationHandler.postDelayed(runnable, delay); } } public static void cancelRunOnUIThread(Runnable runnable) { ApplicationLoader.applicationHandler.removeCallbacks(runnable); } public static boolean isTablet() { if (isTablet == null) { isTablet = ApplicationLoader.applicationContext.getResources().getBoolean(R.bool.isTablet); } return isTablet; } public static boolean isSmallTablet() { float minSide = Math.min(displaySize.x, displaySize.y) / density; return minSide <= 700; } public static int getMinTabletSide() { if (!isSmallTablet()) { int smallSide = Math.min(displaySize.x, displaySize.y); int leftSide = smallSide * 35 / 100; if (leftSide < dp(320)) { leftSide = dp(320); } return smallSide - leftSide; } else { int smallSide = Math.min(displaySize.x, displaySize.y); int maxSide = Math.max(displaySize.x, displaySize.y); int leftSide = maxSide * 35 / 100; if (leftSide < dp(320)) { leftSide = dp(320); } return Math.min(smallSide, maxSide - leftSide); } } public static int getPhotoSize() { if (photoSize == null) { if (Build.VERSION.SDK_INT >= 16) { photoSize = 1280; } else { photoSize = 800; } } return photoSize; } public static String formatTTLString(int ttl) { if (ttl < 60) { return LocaleController.formatPluralString("Seconds", ttl); } else if (ttl < 60 * 60) { return LocaleController.formatPluralString("Minutes", ttl / 60); } else if (ttl < 60 * 60 * 24) { return LocaleController.formatPluralString("Hours", ttl / 60 / 60); } else if (ttl < 60 * 60 * 24 * 7) { return LocaleController.formatPluralString("Days", ttl / 60 / 60 / 24); } else { int days = ttl / 60 / 60 / 24; if (ttl % 7 == 0) { return LocaleController.formatPluralString("Weeks", days / 7); } else { return String.format("%s %s", LocaleController.formatPluralString("Weeks", days / 7), LocaleController.formatPluralString("Days", days % 7)); } } } public static AlertDialog.Builder buildTTLAlert(final Context context, final TLRPC.EncryptedChat encryptedChat) { AlertDialog.Builder builder = new AlertDialog.Builder(context); builder.setTitle(LocaleController.getString("MessageLifetime", R.string.MessageLifetime)); final NumberPicker numberPicker = new NumberPicker(context); numberPicker.setMinValue(0); numberPicker.setMaxValue(20); if (encryptedChat.ttl > 0 && encryptedChat.ttl < 16) { numberPicker.setValue(encryptedChat.ttl); } else if (encryptedChat.ttl == 30) { numberPicker.setValue(16); } else if (encryptedChat.ttl == 60) { numberPicker.setValue(17); } else if (encryptedChat.ttl == 60 * 60) { numberPicker.setValue(18); } else if (encryptedChat.ttl == 60 * 60 * 24) { numberPicker.setValue(19); } else if (encryptedChat.ttl == 60 * 60 * 24 * 7) { numberPicker.setValue(20); } else if (encryptedChat.ttl == 0) { numberPicker.setValue(0); } numberPicker.setFormatter(new NumberPicker.Formatter() { @Override public String format(int value) { if (value == 0) { return LocaleController.getString("ShortMessageLifetimeForever", R.string.ShortMessageLifetimeForever); } else if (value >= 1 && value < 16) { return AndroidUtilities.formatTTLString(value); } else if (value == 16) { return AndroidUtilities.formatTTLString(30); } else if (value == 17) { return AndroidUtilities.formatTTLString(60); } else if (value == 18) { return AndroidUtilities.formatTTLString(60 * 60); } else if (value == 19) { return AndroidUtilities.formatTTLString(60 * 60 * 24); } else if (value == 20) { return AndroidUtilities.formatTTLString(60 * 60 * 24 * 7); } return ""; } }); builder.setView(numberPicker); builder.setNegativeButton(LocaleController.getString("Done", R.string.Done), new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { int oldValue = encryptedChat.ttl; which = numberPicker.getValue(); if (which >= 0 && which < 16) { encryptedChat.ttl = which; } else if (which == 16) { encryptedChat.ttl = 30; } else if (which == 17) { encryptedChat.ttl = 60; } else if (which == 18) { encryptedChat.ttl = 60 * 60; } else if (which == 19) { encryptedChat.ttl = 60 * 60 * 24; } else if (which == 20) { encryptedChat.ttl = 60 * 60 * 24 * 7; } if (oldValue != encryptedChat.ttl) { SecretChatHelper.getInstance().sendTTLMessage(encryptedChat, null); MessagesStorage.getInstance().updateEncryptedChatTTL(encryptedChat); } } }); return builder; } public static void clearCursorDrawable(EditText editText) { if (editText == null) { return; } try { Field mCursorDrawableRes = TextView.class.getDeclaredField("mCursorDrawableRes"); mCursorDrawableRes.setAccessible(true); mCursorDrawableRes.setInt(editText, 0); } catch (Exception e) { FileLog.e("tmessages", e); } } public static void setProgressBarAnimationDuration(ProgressBar progressBar, int duration) { if (progressBar == null) { return; } try { Field mCursorDrawableRes = ProgressBar.class.getDeclaredField("mDuration"); mCursorDrawableRes.setAccessible(true); mCursorDrawableRes.setInt(progressBar, duration); } catch (Exception e) { FileLog.e("tmessages", e); } } private static Intent createShortcutIntent(long did, boolean forDelete) { Intent shortcutIntent = new Intent(ApplicationLoader.applicationContext, OpenChatReceiver.class); int lower_id = (int) did; int high_id = (int) (did >> 32); TLRPC.User user = null; TLRPC.Chat chat = null; if (lower_id == 0) { shortcutIntent.putExtra("encId", high_id); TLRPC.EncryptedChat encryptedChat = MessagesController.getInstance().getEncryptedChat(high_id); if (encryptedChat == null) { return null; } user = MessagesController.getInstance().getUser(encryptedChat.user_id); } else if (lower_id > 0) { shortcutIntent.putExtra("userId", lower_id); user = MessagesController.getInstance().getUser(lower_id); } else if (lower_id < 0) { chat = MessagesController.getInstance().getChat(-lower_id); shortcutIntent.putExtra("chatId", -lower_id); } else { return null; } if (user == null && chat == null) { return null; } String name; TLRPC.FileLocation photo = null; if (user != null) { name = ContactsController.formatName(user.first_name, user.last_name); if (user.photo != null) { photo = user.photo.photo_small; } } else { name = chat.title; if (chat.photo != null) { photo = chat.photo.photo_small; } } shortcutIntent.setAction("com.tmessages.openchat" + did); shortcutIntent.addFlags(0x4000000); Intent addIntent = new Intent(); addIntent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, shortcutIntent); addIntent.putExtra(Intent.EXTRA_SHORTCUT_NAME, name); addIntent.putExtra("duplicate", false); if (!forDelete) { Bitmap bitmap = null; if (photo != null) { try { File path = FileLoader.getPathToAttach(photo, true); bitmap = BitmapFactory.decodeFile(path.toString()); if (bitmap != null) { int size = AndroidUtilities.dp(58); Bitmap result = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888); result.eraseColor(Color.TRANSPARENT); Canvas canvas = new Canvas(result); BitmapShader shader = new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP); if (roundPaint == null) { roundPaint = new Paint(Paint.ANTI_ALIAS_FLAG); bitmapRect = new RectF(); } float scale = size / (float) bitmap.getWidth(); canvas.save(); canvas.scale(scale, scale); roundPaint.setShader(shader); bitmapRect.set(0, 0, bitmap.getWidth(), bitmap.getHeight()); canvas.drawRoundRect(bitmapRect, bitmap.getWidth(), bitmap.getHeight(), roundPaint); canvas.restore(); Drawable drawable = ApplicationLoader.applicationContext.getResources().getDrawable(R.drawable.book_logo); int w = AndroidUtilities.dp(15); int left = size - w - AndroidUtilities.dp(2); int top = size - w - AndroidUtilities.dp(2); drawable.setBounds(left, top, left + w, top + w); drawable.draw(canvas); try { canvas.setBitmap(null); } catch (Exception e) { //don't promt, this will crash on 2.x } bitmap = result; } } catch (Throwable e) { FileLog.e("tmessages", e); } } if (bitmap != null) { addIntent.putExtra(Intent.EXTRA_SHORTCUT_ICON, bitmap); } else { if (user != null) { if (user.bot) { addIntent.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE, Intent.ShortcutIconResource.fromContext(ApplicationLoader.applicationContext, R.drawable.book_bot)); } else { addIntent.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE, Intent.ShortcutIconResource.fromContext(ApplicationLoader.applicationContext, R.drawable.book_user)); } } else if (chat != null) { if (ChatObject.isChannel(chat) && !chat.megagroup) { addIntent.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE, Intent.ShortcutIconResource.fromContext(ApplicationLoader.applicationContext, R.drawable.book_channel)); } else { addIntent.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE, Intent.ShortcutIconResource.fromContext(ApplicationLoader.applicationContext, R.drawable.book_group)); } } } } return addIntent; } public static void installShortcut(long did) { try { Intent addIntent = createShortcutIntent(did, false); addIntent.setAction("com.android.launcher.action.INSTALL_SHORTCUT"); ApplicationLoader.applicationContext.sendBroadcast(addIntent); } catch (Exception e) { FileLog.e("tmessages", e); } } public static void uninstallShortcut(long did) { try { Intent addIntent = createShortcutIntent(did, true); addIntent.setAction("com.android.launcher.action.UNINSTALL_SHORTCUT"); ApplicationLoader.applicationContext.sendBroadcast(addIntent); } catch (Exception e) { FileLog.e("tmessages", e); } } public static int getViewInset(View view) { if (view == null || Build.VERSION.SDK_INT < 21 || view.getHeight() == AndroidUtilities.displaySize.y || view.getHeight() == AndroidUtilities.displaySize.y - statusBarHeight) { return 0; } try { Field mAttachInfoField = View.class.getDeclaredField("mAttachInfo"); mAttachInfoField.setAccessible(true); Object mAttachInfo = mAttachInfoField.get(view); if (mAttachInfo != null) { Field mStableInsetsField = mAttachInfo.getClass().getDeclaredField("mStableInsets"); mStableInsetsField.setAccessible(true); Rect insets = (Rect)mStableInsetsField.get(mAttachInfo); return insets.bottom; } } catch (Exception e) { FileLog.e("tmessages", e); } return 0; } public static Point getRealScreenSize() { Point size = new Point(); try { WindowManager windowManager = (WindowManager) ApplicationLoader.applicationContext.getSystemService(Context.WINDOW_SERVICE); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { windowManager.getDefaultDisplay().getRealSize(size); } else { try { Method mGetRawW = Display.class.getMethod("getRawWidth"); Method mGetRawH = Display.class.getMethod("getRawHeight"); size.set((Integer) mGetRawW.invoke(windowManager.getDefaultDisplay()), (Integer) mGetRawH.invoke(windowManager.getDefaultDisplay())); } catch (Exception e) { size.set(windowManager.getDefaultDisplay().getWidth(), windowManager.getDefaultDisplay().getHeight()); FileLog.e("tmessages", e); } } } catch (Exception e) { FileLog.e("tmessages", e); } return size; } public static CharSequence getTrimmedString(CharSequence src) { if (src == null || src.length() == 0) { return src; } while (src.length() > 0 && (src.charAt(0) == '\n' || src.charAt(0) == ' ')) { src = src.subSequence(1, src.length()); } while (src.length() > 0 && (src.charAt(src.length() - 1) == '\n' || src.charAt(src.length() - 1) == ' ')) { src = src.subSequence(0, src.length() - 1); } return src; } public static void setListViewEdgeEffectColor(AbsListView listView, int color) { if (Build.VERSION.SDK_INT >= 21) { try { Field field = AbsListView.class.getDeclaredField("mEdgeGlowTop"); field.setAccessible(true); EdgeEffect mEdgeGlowTop = (EdgeEffect) field.get(listView); if (mEdgeGlowTop != null) { mEdgeGlowTop.setColor(color); } field = AbsListView.class.getDeclaredField("mEdgeGlowBottom"); field.setAccessible(true); EdgeEffect mEdgeGlowBottom = (EdgeEffect) field.get(listView); if (mEdgeGlowBottom != null) { mEdgeGlowBottom.setColor(color); } } catch (Exception e) { FileLog.e("tmessages", e); } } } @SuppressLint("NewApi") public static void clearDrawableAnimation(View view) { if (Build.VERSION.SDK_INT < 21 || view == null) { return; } Drawable drawable; if (view instanceof ListView) { drawable = ((ListView) view).getSelector(); if (drawable != null) { drawable.setState(StateSet.NOTHING); } } else { drawable = view.getBackground(); if (drawable != null) { drawable.setState(StateSet.NOTHING); drawable.jumpToCurrentState(); } } } public static final int FLAG_TAG_BR = 1; public static final int FLAG_TAG_BOLD = 2; public static final int FLAG_TAG_COLOR = 4; public static final int FLAG_TAG_ALL = FLAG_TAG_BR | FLAG_TAG_BOLD | FLAG_TAG_COLOR; public static SpannableStringBuilder replaceTags(String str) { return replaceTags(str, FLAG_TAG_ALL); } public static SpannableStringBuilder replaceTags(String str, int flag) { try { int start; int end; StringBuilder stringBuilder = new StringBuilder(str); if ((flag & FLAG_TAG_BR) != 0) { while ((start = stringBuilder.indexOf("
")) != -1) { stringBuilder.replace(start, start + 4, "\n"); } while ((start = stringBuilder.indexOf("
")) != -1) { stringBuilder.replace(start, start + 5, "\n"); } } ArrayList bolds = new ArrayList<>(); if ((flag & FLAG_TAG_BOLD) != 0) { while ((start = stringBuilder.indexOf("")) != -1) { stringBuilder.replace(start, start + 3, ""); end = stringBuilder.indexOf(""); if (end == -1) { end = stringBuilder.indexOf(""); } stringBuilder.replace(end, end + 4, ""); bolds.add(start); bolds.add(end); } } ArrayList colors = new ArrayList<>(); if ((flag & FLAG_TAG_COLOR) != 0) { while ((start = stringBuilder.indexOf("", start); int color = Color.parseColor(stringBuilder.substring(start, end)); stringBuilder.replace(start, end + 1, ""); end = stringBuilder.indexOf(""); stringBuilder.replace(end, end + 4, ""); colors.add(start); colors.add(end); colors.add(color); } } SpannableStringBuilder spannableStringBuilder = new SpannableStringBuilder(stringBuilder); for (int a = 0; a < bolds.size() / 2; a++) { spannableStringBuilder.setSpan(new TypefaceSpan(AndroidUtilities.getTypeface("fonts/rmedium.ttf")), bolds.get(a * 2), bolds.get(a * 2 + 1), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); } for (int a = 0; a < colors.size() / 3; a++) { spannableStringBuilder.setSpan(new ForegroundColorSpan(colors.get(a * 3 + 2)), colors.get(a * 3), colors.get(a * 3 + 1), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); } return spannableStringBuilder; } catch (Exception e) { FileLog.e("tmessages", e); } return new SpannableStringBuilder(str); } public static boolean needShowPasscode(boolean reset) { boolean wasInBackground = ForegroundDetector.getInstance().isWasInBackground(reset); if (reset) { ForegroundDetector.getInstance().resetBackgroundVar(); } return UserConfig.passcodeHash.length() > 0 && wasInBackground && (UserConfig.appLocked || UserConfig.autoLockIn != 0 && UserConfig.lastPauseTime != 0 && !UserConfig.appLocked && (UserConfig.lastPauseTime + UserConfig.autoLockIn) <= ConnectionsManager.getInstance().getCurrentTime()); } public static void shakeView(final View view, final float x, final int num) { if (num == 6) { view.setTranslationX(0); return; } AnimatorSet animatorSet = new AnimatorSet(); animatorSet.playTogether(ObjectAnimator.ofFloat(view, "translationX", AndroidUtilities.dp(x))); animatorSet.setDuration(50); animatorSet.addListener(new AnimatorListenerAdapterProxy() { @Override public void onAnimationEnd(Animator animation) { shakeView(view, num == 5 ? 0 : -x, num + 1); } }); animatorSet.start(); } /*public static String ellipsize(String text, int maxLines, int maxWidth, TextPaint paint) { if (text == null || paint == null) { return null; } int count; int offset = 0; StringBuilder result = null; TextView for (int a = 0; a < maxLines; a++) { count = paint.breakText(text, true, maxWidth, null); if (a != maxLines - 1) { if (result == null) { result = new StringBuilder(count * maxLines + 1); } boolean foundSpace = false; for (int c = count - 1; c >= offset; c--) { if (text.charAt(c) == ' ') { foundSpace = true; result.append(text.substring(offset, c - 1)); offset = c - 1; } } if (!foundSpace) { offset = count; } text = text.substring(0, offset); } else if (maxLines == 1) { return text.substring(0, count); } else { result.append(text.substring(0, count)); } } return result.toString(); }*/ /*public static void turnOffHardwareAcceleration(Window window) { if (window == null || Build.MODEL == null) { return; } if (Build.MODEL.contains("GT-S5301") || Build.MODEL.contains("GT-S5303") || Build.MODEL.contains("GT-B5330") || Build.MODEL.contains("GT-S5302") || Build.MODEL.contains("GT-S6012B") || Build.MODEL.contains("MegaFon_SP-AI")) { window.clearFlags(WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED); } }*/ public static void checkForCrashes(Activity context) { CrashManager.register(context, BuildVars.DEBUG_VERSION ? BuildVars.HOCKEY_APP_HASH_DEBUG : 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.DEBUG_VERSION ? BuildVars.HOCKEY_APP_HASH_DEBUG : BuildVars.HOCKEY_APP_HASH); } } public static void unregisterUpdates() { if (BuildVars.DEBUG_VERSION) { UpdateManager.unregister(); } } public static void addToClipboard(CharSequence str) { try { android.content.ClipboardManager clipboard = (android.content.ClipboardManager) ApplicationLoader.applicationContext.getSystemService(Context.CLIPBOARD_SERVICE); android.content.ClipData clip = android.content.ClipData.newPlainText("label", str); clipboard.setPrimaryClip(clip); } catch (Exception e) { FileLog.e("tmessages", e); } } 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; } try { Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE); mediaScanIntent.setData(uri); ApplicationLoader.applicationContext.sendBroadcast(mediaScanIntent); } catch (Exception e) { FileLog.e("tmessages", e); } } private static File getAlbumDir() { if (Build.VERSION.SDK_INT >= 23 && ApplicationLoader.applicationContext.checkSelfPermission(android.Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { return FileLoader.getInstance().getDirectory(FileLoader.MEDIA_DIR_CACHE); } 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); String value = cursor.getString(column_index); if (value.startsWith("content://") || !value.startsWith("/") && !value.startsWith("file://")) { return null; } return value; } } catch (Exception e) { FileLog.e("tmessages", e); } 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("" + query + "")); 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; } public static byte[] calcAuthKeyHash(byte[] auth_key) { byte[] sha1 = Utilities.computeSHA1(auth_key); byte[] key_hash = new byte[16]; System.arraycopy(sha1, 0, key_hash, 0, 16); return key_hash; } public static void openForView(MessageObject message, Activity activity) throws Exception { File f = null; String fileName = message.getFileName(); if (message.messageOwner.attachPath != null && message.messageOwner.attachPath.length() != 0) { f = new File(message.messageOwner.attachPath); } if (f == null || !f.exists()) { f = FileLoader.getPathToMessage(message.messageOwner); } if (f != null && f.exists()) { String realMimeType = null; Intent intent = new Intent(Intent.ACTION_VIEW); MimeTypeMap myMime = MimeTypeMap.getSingleton(); int idx = fileName.lastIndexOf('.'); if (idx != -1) { String ext = fileName.substring(idx + 1); realMimeType = myMime.getMimeTypeFromExtension(ext.toLowerCase()); if (realMimeType == null) { if (message.type == 9 || message.type == 0) { realMimeType = message.getDocument().mime_type; } if (realMimeType == null || realMimeType.length() == 0) { realMimeType = null; } } if (realMimeType != null) { intent.setDataAndType(Uri.fromFile(f), realMimeType); } else { intent.setDataAndType(Uri.fromFile(f), "text/plain"); } } else { intent.setDataAndType(Uri.fromFile(f), "text/plain"); } if (realMimeType != null) { try { activity.startActivityForResult(intent, 500); } catch (Exception e) { intent.setDataAndType(Uri.fromFile(f), "text/plain"); activity.startActivityForResult(intent, 500); } } else { activity.startActivityForResult(intent, 500); } } } }