diff options
Diffstat (limited to 'src/com/android/launcher3/Utilities.java')
-rw-r--r-- | src/com/android/launcher3/Utilities.java | 288 |
1 files changed, 228 insertions, 60 deletions
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java index 215d63d2e..adedd33b2 100644 --- a/src/com/android/launcher3/Utilities.java +++ b/src/com/android/launcher3/Utilities.java @@ -25,13 +25,16 @@ import android.content.ActivityNotFoundException; import android.content.ComponentName; import android.content.Context; import android.content.Intent; +import android.content.SharedPreferences; import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.ResolveInfo; import android.content.res.Resources; +import android.database.Cursor; import android.graphics.Bitmap; +import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Matrix; @@ -42,26 +45,38 @@ import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.graphics.drawable.PaintDrawable; import android.os.Build; +import android.os.Bundle; +import android.os.Process; +import android.text.TextUtils; +import android.util.DisplayMetrics; import android.util.Log; import android.util.Pair; import android.util.SparseArray; +import android.util.TypedValue; import android.view.View; import android.widget.Toast; +import java.io.ByteArrayOutputStream; +import java.io.IOException; import java.util.ArrayList; +import java.util.Locale; +import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; /** * Various utilities shared amongst the Launcher's classes. */ public final class Utilities { - private static final String TAG = "Launcher.Utilities"; - private static int sIconWidth = -1; - private static int sIconHeight = -1; + private static final String TAG = "Launcher.Utilities"; private static final Rect sOldBounds = new Rect(); private static final Canvas sCanvas = new Canvas(); + private static final Pattern sTrimPattern = + Pattern.compile("^[\\s|\\p{javaSpaceChar}]*(.*)[\\s|\\p{javaSpaceChar}]*$"); + static { sCanvas.setDrawFilter(new PaintFlagsDrawFilter(Paint.DITHER_FLAG, Paint.FILTER_BITMAP_FLAG)); @@ -69,53 +84,64 @@ public final class Utilities { static int sColors[] = { 0xffff0000, 0xff00ff00, 0xff0000ff }; static int sColorIndex = 0; - static int[] sLoc0 = new int[2]; - static int[] sLoc1 = new int[2]; + private static final int[] sLoc0 = new int[2]; + private static final int[] sLoc1 = new int[2]; + + // TODO: use Build.VERSION_CODES when available + public static final boolean ATLEAST_MARSHMALLOW = Build.VERSION.SDK_INT >= 23; + + public static final boolean ATLEAST_LOLLIPOP_MR1 = + Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1; + + public static final boolean ATLEAST_LOLLIPOP = + Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP; + + public static final boolean ATLEAST_KITKAT = + Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT; + + public static final boolean ATLEAST_JB_MR1 = + Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1; + + public static final boolean ATLEAST_JB_MR2 = + Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2; // To turn on these properties, type // adb shell setprop log.tag.PROPERTY_NAME [VERBOSE | SUPPRESS] - static final String FORCE_ENABLE_ROTATION_PROPERTY = "launcher_force_rotate"; - public static boolean sForceEnableRotation = isPropertyEnabled(FORCE_ENABLE_ROTATION_PROPERTY); - - /** - * Returns a FastBitmapDrawable with the icon, accurately sized. - */ - public static FastBitmapDrawable createIconDrawable(Bitmap icon) { - FastBitmapDrawable d = new FastBitmapDrawable(icon); - d.setFilterBitmap(true); - resizeIconDrawable(d); - return d; - } + private static final String FORCE_ENABLE_ROTATION_PROPERTY = "launcher_force_rotate"; + private static boolean sForceEnableRotation = isPropertyEnabled(FORCE_ENABLE_ROTATION_PROPERTY); - /** - * Resizes an icon drawable to the correct icon size. - */ - static void resizeIconDrawable(Drawable icon) { - icon.setBounds(0, 0, sIconWidth, sIconHeight); - } + public static final String ALLOW_ROTATION_PREFERENCE_KEY = "pref_allowRotation"; public static boolean isPropertyEnabled(String propertyName) { return Log.isLoggable(propertyName, Log.VERBOSE); } - public static boolean isRotationEnabled(Context c) { - boolean enableRotation = sForceEnableRotation || - c.getResources().getBoolean(R.bool.allow_rotation); - return enableRotation; + public static boolean isAllowRotationPrefEnabled(Context context, boolean multiProcess) { + SharedPreferences sharedPrefs = context.getSharedPreferences( + LauncherAppState.getSharedPreferencesKey(), Context.MODE_PRIVATE | (multiProcess ? + Context.MODE_MULTI_PROCESS : 0)); + boolean allowRotationPref = sharedPrefs.getBoolean(ALLOW_ROTATION_PREFERENCE_KEY, false); + return sForceEnableRotation || allowRotationPref; } - /** - * Indicates if the device is running LMP or higher. - */ - public static boolean isLmpOrAbove() { - return Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP; + public static boolean isRotationAllowedForDevice(Context context) { + return sForceEnableRotation || context.getResources().getBoolean(R.bool.allow_rotation); + } + + public static Bitmap createIconBitmap(Cursor c, int iconIndex, Context context) { + byte[] data = c.getBlob(iconIndex); + try { + return createIconBitmap(BitmapFactory.decodeByteArray(data, 0, data.length), context); + } catch (Exception e) { + return null; + } } /** * Returns a bitmap suitable for the all apps view. If the package or the resource do not * exist, it returns null. */ - static Bitmap createIconBitmap(String packageName, String resourceName, IconCache cache, + public static Bitmap createIconBitmap(String packageName, String resourceName, Context context) { PackageManager packageManager = context.getPackageManager(); // the resource @@ -124,7 +150,8 @@ public final class Utilities { if (resources != null) { final int id = resources.getIdentifier(resourceName, null, null); return createIconBitmap( - resources.getDrawableForDensity(id, cache.getFullResIconDpi()), context); + resources.getDrawableForDensity(id, LauncherAppState.getInstance() + .getInvariantDeviceProfile().fillResIconDpi), context); } } catch (Exception e) { // Icon not found. @@ -132,16 +159,16 @@ public final class Utilities { return null; } + private static int getIconBitmapSize() { + return LauncherAppState.getInstance().getInvariantDeviceProfile().iconBitmapSize; + } + /** * Returns a bitmap which is of the appropriate size to be displayed as an icon */ - static Bitmap createIconBitmap(Bitmap icon, Context context) { - synchronized (sCanvas) { // we share the statics :-( - if (sIconWidth == -1) { - initStatics(context); - } - } - if (sIconWidth == icon.getWidth() && sIconHeight == icon.getHeight()) { + public static Bitmap createIconBitmap(Bitmap icon, Context context) { + final int iconBitmapSize = getIconBitmapSize(); + if (iconBitmapSize == icon.getWidth() && iconBitmapSize == icon.getHeight()) { return icon; } return createIconBitmap(new BitmapDrawable(context.getResources(), icon), context); @@ -151,13 +178,11 @@ public final class Utilities { * Returns a bitmap suitable for the all apps view. */ public static Bitmap createIconBitmap(Drawable icon, Context context) { - synchronized (sCanvas) { // we share the statics :-( - if (sIconWidth == -1) { - initStatics(context); - } + synchronized (sCanvas) { + final int iconBitmapSize = getIconBitmapSize(); - int width = sIconWidth; - int height = sIconHeight; + int width = iconBitmapSize; + int height = iconBitmapSize; if (icon instanceof PaintDrawable) { PaintDrawable painter = (PaintDrawable) icon; @@ -184,8 +209,8 @@ public final class Utilities { } // no intrinsic size --> use default size - int textureWidth = sIconWidth; - int textureHeight = sIconHeight; + int textureWidth = iconBitmapSize; + int textureHeight = iconBitmapSize; final Bitmap bitmap = Bitmap.createBitmap(textureWidth, textureHeight, Bitmap.Config.ARGB_8888); @@ -315,15 +340,6 @@ public final class Utilities { localY < (v.getHeight() + slop); } - private static void initStatics(Context context) { - final Resources resources = context.getResources(); - sIconWidth = sIconHeight = (int) resources.getDimension(R.dimen.app_icon_size); - } - - public static void setIconSize(int widthPx) { - sIconWidth = sIconHeight = widthPx; - } - public static void scaleRect(Rect r, float scale) { if (scale != 1.0f) { r.left = (int) (r.left * scale + 0.5f); @@ -502,7 +518,7 @@ public final class Utilities { @TargetApi(Build.VERSION_CODES.KITKAT) public static boolean isViewAttachedToWindow(View v) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { + if (ATLEAST_KITKAT) { return v.isAttachedToWindow(); } else { // A proxy call which returns null, if the view is not attached to the window. @@ -529,7 +545,7 @@ public final class Utilities { AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context); for (AppWidgetProviderInfo info : appWidgetManager.getInstalledProviders()) { if (info.provider.getPackageName().equals(providerPkg)) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { + if (ATLEAST_JB_MR1) { if ((info.widgetCategory & AppWidgetProviderInfo.WIDGET_CATEGORY_SEARCHBOX) != 0) { return info; } else if (defaultWidgetForSearchPackage == null) { @@ -542,4 +558,156 @@ public final class Utilities { } return defaultWidgetForSearchPackage; } + + /** + * Compresses the bitmap to a byte array for serialization. + */ + public static byte[] flattenBitmap(Bitmap bitmap) { + // Try go guesstimate how much space the icon will take when serialized + // to avoid unnecessary allocations/copies during the write. + int size = bitmap.getWidth() * bitmap.getHeight() * 4; + ByteArrayOutputStream out = new ByteArrayOutputStream(size); + try { + bitmap.compress(Bitmap.CompressFormat.PNG, 100, out); + out.flush(); + out.close(); + return out.toByteArray(); + } catch (IOException e) { + Log.w(TAG, "Could not write bitmap"); + return null; + } + } + + /** + * Find the first vacant cell, if there is one. + * + * @param vacant Holds the x and y coordinate of the vacant cell + * @param spanX Horizontal cell span. + * @param spanY Vertical cell span. + * + * @return true if a vacant cell was found + */ + public static boolean findVacantCell(int[] vacant, int spanX, int spanY, + int xCount, int yCount, boolean[][] occupied) { + + for (int y = 0; (y + spanY) <= yCount; y++) { + for (int x = 0; (x + spanX) <= xCount; x++) { + boolean available = !occupied[x][y]; + out: for (int i = x; i < x + spanX; i++) { + for (int j = y; j < y + spanY; j++) { + available = available && !occupied[i][j]; + if (!available) break out; + } + } + + if (available) { + vacant[0] = x; + vacant[1] = y; + return true; + } + } + } + + return false; + } + + /** + * Trims the string, removing all whitespace at the beginning and end of the string. + * Non-breaking whitespaces are also removed. + */ + public static String trim(CharSequence s) { + if (s == null) { + return null; + } + + // Just strip any sequence of whitespace or java space characters from the beginning and end + Matcher m = sTrimPattern.matcher(s); + return m.replaceAll("$1"); + } + + /** + * Calculates the height of a given string at a specific text size. + */ + public static float calculateTextHeight(float textSizePx) { + Paint p = new Paint(); + p.setTextSize(textSizePx); + Paint.FontMetrics fm = p.getFontMetrics(); + return -fm.top + fm.bottom; + } + + /** + * Convenience println with multiple args. + */ + public static void println(String key, Object... args) { + StringBuilder b = new StringBuilder(); + b.append(key); + b.append(": "); + boolean isFirstArgument = true; + for (Object arg : args) { + if (isFirstArgument) { + isFirstArgument = false; + } else { + b.append(", "); + } + b.append(arg); + } + System.out.println(b.toString()); + } + + @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1) + public static boolean isRtl(Resources res) { + return ATLEAST_JB_MR1 && + (res.getConfiguration().getLayoutDirection() == View.LAYOUT_DIRECTION_RTL); + } + + public static void assertWorkerThread() { + if (LauncherAppState.isDogfoodBuild() && + (LauncherModel.sWorkerThread.getThreadId() != Process.myTid())) { + throw new IllegalStateException(); + } + } + + /** + * Returns true if the intent is a valid launch intent for a launcher activity of an app. + * This is used to identify shortcuts which are different from the ones exposed by the + * applications' manifest file. + * + * @param launchIntent The intent that will be launched when the shortcut is clicked. + */ + public static boolean isLauncherAppTarget(Intent launchIntent) { + if (launchIntent != null + && Intent.ACTION_MAIN.equals(launchIntent.getAction()) + && launchIntent.getComponent() != null + && launchIntent.getCategories() != null + && launchIntent.getCategories().size() == 1 + && launchIntent.hasCategory(Intent.CATEGORY_LAUNCHER) + && TextUtils.isEmpty(launchIntent.getDataString())) { + // An app target can either have no extra or have ItemInfo.EXTRA_PROFILE. + Bundle extras = launchIntent.getExtras(); + if (extras == null) { + return true; + } else { + Set<String> keys = extras.keySet(); + return keys.size() == 1 && keys.contains(ItemInfo.EXTRA_PROFILE); + } + }; + return false; + } + + public static float dpiFromPx(int size, DisplayMetrics metrics){ + float densityRatio = (float) metrics.densityDpi / DisplayMetrics.DENSITY_DEFAULT; + return (size / densityRatio); + } + public static int pxFromDp(float size, DisplayMetrics metrics) { + return (int) Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, + size, metrics)); + } + public static int pxFromSp(float size, DisplayMetrics metrics) { + return (int) Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, + size, metrics)); + } + + public static String createDbSelectionQuery(String columnName, Iterable<?> values) { + return String.format(Locale.ENGLISH, "%s IN (%s)", columnName, TextUtils.join(", ", values)); + } } |