131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project/*
231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project * Copyright (C) 2008 The Android Open Source Project
331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project *
431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project * Licensed under the Apache License, Version 2.0 (the "License");
531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project * you may not use this file except in compliance with the License.
631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project * You may obtain a copy of the License at
731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project *
831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project *      http://www.apache.org/licenses/LICENSE-2.0
931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project *
1031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project * Unless required by applicable law or agreed to in writing, software
1131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project * distributed under the License is distributed on an "AS IS" BASIS,
1231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project * See the License for the specific language governing permissions and
1431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project * limitations under the License.
1531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project */
1631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
17325dc23624160689e59fbac708cf6f222b20d025Daniel Sandlerpackage com.android.launcher3;
1831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
191ed6c4adde9c2ce597deb3e57600c664684eb639Sunny Goyalimport android.app.WallpaperManager;
20c5c60ad3592f53549c9ffaa58e9a87b0480080e8Sunny Goyalimport android.content.ComponentName;
21aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chungimport android.content.Context;
22a805e1a297c0d1fa84d9fed51e0167aa32bd42bbMichael Jurkaimport android.content.Intent;
237fc77cad3d06bd3647e550f7419e89116471240aRahul Chaturvediimport android.content.SharedPreferences;
24c5c60ad3592f53549c9ffaa58e9a87b0480080e8Sunny Goyalimport android.content.pm.ApplicationInfo;
25c5c60ad3592f53549c9ffaa58e9a87b0480080e8Sunny Goyalimport android.content.pm.PackageInfo;
26c5c60ad3592f53549c9ffaa58e9a87b0480080e8Sunny Goyalimport android.content.pm.PackageManager;
27c5c60ad3592f53549c9ffaa58e9a87b0480080e8Sunny Goyalimport android.content.pm.PackageManager.NameNotFoundException;
28c5c60ad3592f53549c9ffaa58e9a87b0480080e8Sunny Goyalimport android.content.pm.ResolveInfo;
29aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chungimport android.content.res.Resources;
3031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Projectimport android.graphics.Bitmap;
31c763c4e4d28c256d1368be3fc1c4526c8b9bd232Winson Chungimport android.graphics.Matrix;
3231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Projectimport android.graphics.Paint;
3331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Projectimport android.graphics.Rect;
34b1e2e2956e78b4845bdb2abbe3654de6445785f2Sunny Goyalimport android.graphics.RectF;
352efc7d928a3df57838f8abd8ed6fdb9eeb21b481Sandeep Siddharthaimport android.os.Build;
364e5cc64eaf1f63d866d51ce0a6bbafb3d4085c21Sunny Goyalimport android.os.Bundle;
37712ee53c67bbfb5ad4735217f3a9b063d9c60ad9Sunny Goyalimport android.os.DeadObjectException;
383483c52e6bd81b7c37eb0089940e90a6f3e6d80cSunny Goyalimport android.os.Handler;
393483c52e6bd81b7c37eb0089940e90a6f3e6d80cSunny Goyalimport android.os.Message;
40112ac95571c9b81921a912d67b9c369fb523a235Tony Wickhamimport android.os.PowerManager;
41712ee53c67bbfb5ad4735217f3a9b063d9c60ad9Sunny Goyalimport android.os.TransactionTooLargeException;
42e78e3d734b577c1ab6dc0738a83600374908ea52Sunny Goyalimport android.text.Spannable;
43e78e3d734b577c1ab6dc0738a83600374908ea52Sunny Goyalimport android.text.SpannableString;
445c97f51fd9443e461a360ff7852aa223c05d667fSunny Goyalimport android.text.TextUtils;
45e78e3d734b577c1ab6dc0738a83600374908ea52Sunny Goyalimport android.text.style.TtsSpan;
462e6da1539bc7286336b3c24d96ab76434939ce4dAdam Cohenimport android.util.DisplayMetrics;
47a805e1a297c0d1fa84d9fed51e0167aa32bd42bbMichael Jurkaimport android.util.Log;
480fe505bf82a265e51c556d7204976651cde7f55cSunny Goyalimport android.util.Pair;
492e6da1539bc7286336b3c24d96ab76434939ce4dAdam Cohenimport android.util.TypedValue;
50c763c4e4d28c256d1368be3fc1c4526c8b9bd232Winson Chungimport android.view.View;
51b1622cc30f2fd9b579cb918083e063685950df92Sunny Goyal
523d706ad70365052e3224fc4f4b0e7d1f5e8abf22Sunny Goyalimport com.android.launcher3.config.FeatureFlags;
536c56c68555dcb3d99b05d5faecd582a1f683cc92Sunny Goyal
545b0e669169ea2c951bf2f6f71faf793b24db3c23Sunny Goyalimport java.io.ByteArrayOutputStream;
55713edfce264db7edc409216d5c083f8dd6a7083fSunny Goyalimport java.io.Closeable;
565b0e669169ea2c951bf2f6f71faf793b24db3c23Sunny Goyalimport java.io.IOException;
57ab121c17159a7f655b772fa78c04a6d2479e2a8eSunny Goyalimport java.lang.reflect.InvocationTargetException;
5825aba0aea58b2ecbc0fd350c378fa9e45edb81c3Sunny Goyalimport java.lang.reflect.Method;
591bce7fd342875be8f7c1f82c8cf21d0199c8d544Tony Wickhamimport java.util.Collection;
60580edcf529b0755f2bba442e97dd02d8cdad3b82Tony Wickhamimport java.util.HashSet;
61b1622cc30f2fd9b579cb918083e063685950df92Sunny Goyalimport java.util.Locale;
628ac727b2c6627028960a1efe18ab9c72442eef48Sunny Goyalimport java.util.concurrent.Executor;
638ac727b2c6627028960a1efe18ab9c72442eef48Sunny Goyalimport java.util.concurrent.LinkedBlockingQueue;
648ac727b2c6627028960a1efe18ab9c72442eef48Sunny Goyalimport java.util.concurrent.ThreadPoolExecutor;
658ac727b2c6627028960a1efe18ab9c72442eef48Sunny Goyalimport java.util.concurrent.TimeUnit;
6682b016cb56540fe26213e817dd0dd668099c8e20Winson Chungimport java.util.regex.Matcher;
6782b016cb56540fe26213e817dd0dd668099c8e20Winson Chungimport java.util.regex.Pattern;
6831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
6931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project/**
7031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project * Various utilities shared amongst the Launcher's classes.
7131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project */
7272fbec17e09a1120971621587d5005f683baafd1Mathew Inwoodpublic final class Utilities {
737fc77cad3d06bd3647e550f7419e89116471240aRahul Chaturvedi
741291a8c236c84451321438cb68855f6f2eb24959Joe Onorato    private static final String TAG = "Launcher.Utilities";
751291a8c236c84451321438cb68855f6f2eb24959Joe Onorato
7682b016cb56540fe26213e817dd0dd668099c8e20Winson Chung    private static final Pattern sTrimPattern =
7782b016cb56540fe26213e817dd0dd668099c8e20Winson Chung            Pattern.compile("^[\\s|\\p{javaSpaceChar}]*(.*)[\\s|\\p{javaSpaceChar}]*$");
7882b016cb56540fe26213e817dd0dd668099c8e20Winson Chung
7953d7ee4285842732517edcccbdcaa92dccb1e107Sunny Goyal    private static final int[] sLoc0 = new int[2];
8053d7ee4285842732517edcccbdcaa92dccb1e107Sunny Goyal    private static final int[] sLoc1 = new int[2];
815e0e94f0b2ae79770dd07b66441fda6e063e64eaSunny Goyal    private static final float[] sPoint = new float[2];
825e0e94f0b2ae79770dd07b66441fda6e063e64eaSunny Goyal    private static final Matrix sMatrix = new Matrix();
835e0e94f0b2ae79770dd07b66441fda6e063e64eaSunny Goyal    private static final Matrix sInverseMatrix = new Matrix();
847ad868b86e45d6f58c186d2731ab2beb84643757Michael Jurka
858155fa2aa50a4c2ae3d4d1c599d249b6e20ec47dTony Wickham    public static final boolean ATLEAST_P =
868155fa2aa50a4c2ae3d4d1c599d249b6e20ec47dTony Wickham            Build.VERSION.SDK_INT >= Build.VERSION_CODES.P;
876eeb040d86ba209bffbbb9a1c59d5ca0b659f343Tony Mak
88e24cb63b811e2885a69e7df794e9972cdf5a57b9Hyunyoung Song    public static final boolean ATLEAST_OREO_MR1 =
89e24cb63b811e2885a69e7df794e9972cdf5a57b9Hyunyoung Song            Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1;
90e24cb63b811e2885a69e7df794e9972cdf5a57b9Hyunyoung Song
91e24cb63b811e2885a69e7df794e9972cdf5a57b9Hyunyoung Song    public static final boolean ATLEAST_OREO =
92e24cb63b811e2885a69e7df794e9972cdf5a57b9Hyunyoung Song            Build.VERSION.SDK_INT >= Build.VERSION_CODES.O;
93dcc0ba29a5222c9b0002604299501b716cda0b56Sunny Goyal
94f5e3744637db1598c389e62450627b2548f8f517Sunny Goyal    public static final boolean ATLEAST_NOUGAT_MR1 =
95e24cb63b811e2885a69e7df794e9972cdf5a57b9Hyunyoung Song            Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1;
969994b2b171ab78d1d93e8a25086d6853f766e80fSunny Goyal
97f5e3744637db1598c389e62450627b2548f8f517Sunny Goyal    public static final boolean ATLEAST_NOUGAT =
98e24cb63b811e2885a69e7df794e9972cdf5a57b9Hyunyoung Song            Build.VERSION.SDK_INT >= Build.VERSION_CODES.N;
99e2ec8a3cd52f2322016fcadbae8963a446e2b0b3Sunny Goyal
100be9798b6a2ef7c2c827a612203d81c67b3ec81acWinson    public static final boolean ATLEAST_MARSHMALLOW =
101be9798b6a2ef7c2c827a612203d81c67b3ec81acWinson            Build.VERSION.SDK_INT >= Build.VERSION_CODES.M;
1029fc953b94dbc6b99e6de08c9dcc80a0cb8e3e319Sunny Goyal
1039fc953b94dbc6b99e6de08c9dcc80a0cb8e3e319Sunny Goyal    public static final boolean ATLEAST_LOLLIPOP_MR1 =
1049fc953b94dbc6b99e6de08c9dcc80a0cb8e3e319Sunny Goyal            Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1;
1059fc953b94dbc6b99e6de08c9dcc80a0cb8e3e319Sunny Goyal
10683337f9e8e6ff91fe3bf64eddab3d72cc2d8369cJon Miranda    public static final int SINGLE_FRAME_MS = 16;
10783337f9e8e6ff91fe3bf64eddab3d72cc2d8369cJon Miranda
10849f19f0af96ccf9da78b62a4ac9f51e40b40adc5Sunny Goyal    /**
10949f19f0af96ccf9da78b62a4ac9f51e40b40adc5Sunny Goyal     * Indicates if the device has a debug build. Should only be used to store additional info or
11049f19f0af96ccf9da78b62a4ac9f51e40b40adc5Sunny Goyal     * add extra logging and not for changing the app behavior.
11149f19f0af96ccf9da78b62a4ac9f51e40b40adc5Sunny Goyal     */
112f0f00b30b8821b00e889dad3926da7a47a82b9a8Matthew Ng    public static final boolean IS_DEBUG_DEVICE = Build.TYPE.toLowerCase().contains("debug")
113f0f00b30b8821b00e889dad3926da7a47a82b9a8Matthew Ng            || Build.TYPE.toLowerCase().equals("eng");
11449f19f0af96ccf9da78b62a4ac9f51e40b40adc5Sunny Goyal
1156f866095c0177a794050e578f148b640f4e0a0abSunny Goyal    // An intent extra to indicate the horizontal scroll of the wallpaper.
1166f866095c0177a794050e578f148b640f4e0a0abSunny Goyal    public static final String EXTRA_WALLPAPER_OFFSET = "com.android.launcher3.WALLPAPER_OFFSET";
1176f866095c0177a794050e578f148b640f4e0a0abSunny Goyal
118629a67c5ac33409ebccf13284505a4cadc65656aTony    public static final int COLOR_EXTRACTION_JOB_ID = 1;
1198231596619aa1e67be46f22f87da09b8407722e6Sunny Goyal    public static final int WALLPAPER_COMPAT_JOB_ID = 2;
120629a67c5ac33409ebccf13284505a4cadc65656aTony
1218ac727b2c6627028960a1efe18ab9c72442eef48Sunny Goyal    // These values are same as that in {@link AsyncTask}.
1228ac727b2c6627028960a1efe18ab9c72442eef48Sunny Goyal    private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
1238ac727b2c6627028960a1efe18ab9c72442eef48Sunny Goyal    private static final int CORE_POOL_SIZE = CPU_COUNT + 1;
1248ac727b2c6627028960a1efe18ab9c72442eef48Sunny Goyal    private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
1258ac727b2c6627028960a1efe18ab9c72442eef48Sunny Goyal    private static final int KEEP_ALIVE = 1;
1268ac727b2c6627028960a1efe18ab9c72442eef48Sunny Goyal    /**
1278ac727b2c6627028960a1efe18ab9c72442eef48Sunny Goyal     * An {@link Executor} to be used with async task with no limit on the queue size.
1288ac727b2c6627028960a1efe18ab9c72442eef48Sunny Goyal     */
1298ac727b2c6627028960a1efe18ab9c72442eef48Sunny Goyal    public static final Executor THREAD_POOL_EXECUTOR = new ThreadPoolExecutor(
1308ac727b2c6627028960a1efe18ab9c72442eef48Sunny Goyal            CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
1318ac727b2c6627028960a1efe18ab9c72442eef48Sunny Goyal            TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());
1328ac727b2c6627028960a1efe18ab9c72442eef48Sunny Goyal
1334bbf419bf98c00decc74b429320258c2a16faeecSunny Goyal    public static boolean isPropertyEnabled(String propertyName) {
1347ad868b86e45d6f58c186d2731ab2beb84643757Michael Jurka        return Log.isLoggable(propertyName, Log.VERBOSE);
1357ad868b86e45d6f58c186d2731ab2beb84643757Michael Jurka    }
1367ad868b86e45d6f58c186d2731ab2beb84643757Michael Jurka
13731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    /**
138c763c4e4d28c256d1368be3fc1c4526c8b9bd232Winson Chung     * Given a coordinate relative to the descendant, find the coordinate in a parent view's
139c763c4e4d28c256d1368be3fc1c4526c8b9bd232Winson Chung     * coordinates.
140c763c4e4d28c256d1368be3fc1c4526c8b9bd232Winson Chung     *
141c763c4e4d28c256d1368be3fc1c4526c8b9bd232Winson Chung     * @param descendant The descendant to which the passed coordinate is relative.
142e8b5d20d94cb03878137a7fdf65d7a88270b73f8Sunny Goyal     * @param ancestor The root view to make the coordinates relative to.
143c763c4e4d28c256d1368be3fc1c4526c8b9bd232Winson Chung     * @param coord The coordinate that we want mapped.
144c763c4e4d28c256d1368be3fc1c4526c8b9bd232Winson Chung     * @param includeRootScroll Whether or not to account for the scroll of the descendant:
145c763c4e4d28c256d1368be3fc1c4526c8b9bd232Winson Chung     *          sometimes this is relevant as in a child's coordinates within the descendant.
146c763c4e4d28c256d1368be3fc1c4526c8b9bd232Winson Chung     * @return The factor by which this descendant is scaled relative to this DragLayer. Caution
147c763c4e4d28c256d1368be3fc1c4526c8b9bd232Winson Chung     *         this scale factor is assumed to be equal in X and Y, and so if at any point this
148c763c4e4d28c256d1368be3fc1c4526c8b9bd232Winson Chung     *         assumption fails, we will need to return a pair of scale factors.
149c763c4e4d28c256d1368be3fc1c4526c8b9bd232Winson Chung     */
150e8b5d20d94cb03878137a7fdf65d7a88270b73f8Sunny Goyal    public static float getDescendantCoordRelativeToAncestor(
151e8b5d20d94cb03878137a7fdf65d7a88270b73f8Sunny Goyal            View descendant, View ancestor, int[] coord, boolean includeRootScroll) {
1525e0e94f0b2ae79770dd07b66441fda6e063e64eaSunny Goyal        sPoint[0] = coord[0];
1535e0e94f0b2ae79770dd07b66441fda6e063e64eaSunny Goyal        sPoint[1] = coord[1];
1545e0e94f0b2ae79770dd07b66441fda6e063e64eaSunny Goyal
1558cf47a5121205486e20694bf48eafc171175027aSunny Goyal        float scale = 1.0f;
156c763c4e4d28c256d1368be3fc1c4526c8b9bd232Winson Chung        View v = descendant;
157e8b5d20d94cb03878137a7fdf65d7a88270b73f8Sunny Goyal        while(v != ancestor && v != null) {
158c763c4e4d28c256d1368be3fc1c4526c8b9bd232Winson Chung            // For TextViews, scroll has a meaning which relates to the text position
159c763c4e4d28c256d1368be3fc1c4526c8b9bd232Winson Chung            // which is very strange... ignore the scroll.
1608cf47a5121205486e20694bf48eafc171175027aSunny Goyal            if (v != descendant || includeRootScroll) {
1615e0e94f0b2ae79770dd07b66441fda6e063e64eaSunny Goyal                sPoint[0] -= v.getScrollX();
1625e0e94f0b2ae79770dd07b66441fda6e063e64eaSunny Goyal                sPoint[1] -= v.getScrollY();
163c763c4e4d28c256d1368be3fc1c4526c8b9bd232Winson Chung            }
164c763c4e4d28c256d1368be3fc1c4526c8b9bd232Winson Chung
1655e0e94f0b2ae79770dd07b66441fda6e063e64eaSunny Goyal            v.getMatrix().mapPoints(sPoint);
1665e0e94f0b2ae79770dd07b66441fda6e063e64eaSunny Goyal            sPoint[0] += v.getLeft();
1675e0e94f0b2ae79770dd07b66441fda6e063e64eaSunny Goyal            sPoint[1] += v.getTop();
1688cf47a5121205486e20694bf48eafc171175027aSunny Goyal            scale *= v.getScaleX();
1698cf47a5121205486e20694bf48eafc171175027aSunny Goyal
1708cf47a5121205486e20694bf48eafc171175027aSunny Goyal            v = (View) v.getParent();
171c763c4e4d28c256d1368be3fc1c4526c8b9bd232Winson Chung        }
172c763c4e4d28c256d1368be3fc1c4526c8b9bd232Winson Chung
1735e0e94f0b2ae79770dd07b66441fda6e063e64eaSunny Goyal        coord[0] = Math.round(sPoint[0]);
1745e0e94f0b2ae79770dd07b66441fda6e063e64eaSunny Goyal        coord[1] = Math.round(sPoint[1]);
175c763c4e4d28c256d1368be3fc1c4526c8b9bd232Winson Chung        return scale;
176c763c4e4d28c256d1368be3fc1c4526c8b9bd232Winson Chung    }
177c763c4e4d28c256d1368be3fc1c4526c8b9bd232Winson Chung
178c763c4e4d28c256d1368be3fc1c4526c8b9bd232Winson Chung    /**
179e8b5d20d94cb03878137a7fdf65d7a88270b73f8Sunny Goyal     * Inverse of {@link #getDescendantCoordRelativeToAncestor(View, View, int[], boolean)}.
180c763c4e4d28c256d1368be3fc1c4526c8b9bd232Winson Chung     */
1815e0e94f0b2ae79770dd07b66441fda6e063e64eaSunny Goyal    public static void mapCoordInSelfToDescendant(View descendant, View root, int[] coord) {
1825e0e94f0b2ae79770dd07b66441fda6e063e64eaSunny Goyal        sMatrix.reset();
183c763c4e4d28c256d1368be3fc1c4526c8b9bd232Winson Chung        View v = descendant;
184c763c4e4d28c256d1368be3fc1c4526c8b9bd232Winson Chung        while(v != root) {
1855e0e94f0b2ae79770dd07b66441fda6e063e64eaSunny Goyal            sMatrix.postTranslate(-v.getScrollX(), -v.getScrollY());
1865e0e94f0b2ae79770dd07b66441fda6e063e64eaSunny Goyal            sMatrix.postConcat(v.getMatrix());
1875e0e94f0b2ae79770dd07b66441fda6e063e64eaSunny Goyal            sMatrix.postTranslate(v.getLeft(), v.getTop());
188c763c4e4d28c256d1368be3fc1c4526c8b9bd232Winson Chung            v = (View) v.getParent();
189c763c4e4d28c256d1368be3fc1c4526c8b9bd232Winson Chung        }
1905e0e94f0b2ae79770dd07b66441fda6e063e64eaSunny Goyal        sMatrix.postTranslate(-v.getScrollX(), -v.getScrollY());
1915e0e94f0b2ae79770dd07b66441fda6e063e64eaSunny Goyal        sMatrix.invert(sInverseMatrix);
1925e0e94f0b2ae79770dd07b66441fda6e063e64eaSunny Goyal
1935e0e94f0b2ae79770dd07b66441fda6e063e64eaSunny Goyal        sPoint[0] = coord[0];
1945e0e94f0b2ae79770dd07b66441fda6e063e64eaSunny Goyal        sPoint[1] = coord[1];
1955e0e94f0b2ae79770dd07b66441fda6e063e64eaSunny Goyal        sInverseMatrix.mapPoints(sPoint);
1965e0e94f0b2ae79770dd07b66441fda6e063e64eaSunny Goyal        coord[0] = Math.round(sPoint[0]);
1975e0e94f0b2ae79770dd07b66441fda6e063e64eaSunny Goyal        coord[1] = Math.round(sPoint[1]);
198c763c4e4d28c256d1368be3fc1c4526c8b9bd232Winson Chung    }
199c763c4e4d28c256d1368be3fc1c4526c8b9bd232Winson Chung
20002dd7aea3d8ac94126a8f8069e6762592ea60326Jason Monk    /**
20102dd7aea3d8ac94126a8f8069e6762592ea60326Jason Monk     * Utility method to determine whether the given point, in local coordinates,
20202dd7aea3d8ac94126a8f8069e6762592ea60326Jason Monk     * is inside the view, where the area of the view is expanded by the slop factor.
20302dd7aea3d8ac94126a8f8069e6762592ea60326Jason Monk     * This method is called while processing touch-move events to determine if the event
20402dd7aea3d8ac94126a8f8069e6762592ea60326Jason Monk     * is still within the view.
20502dd7aea3d8ac94126a8f8069e6762592ea60326Jason Monk     */
20602dd7aea3d8ac94126a8f8069e6762592ea60326Jason Monk    public static boolean pointInView(View v, float localX, float localY, float slop) {
20702dd7aea3d8ac94126a8f8069e6762592ea60326Jason Monk        return localX >= -slop && localY >= -slop && localX < (v.getWidth() + slop) &&
20802dd7aea3d8ac94126a8f8069e6762592ea60326Jason Monk                localY < (v.getHeight() + slop);
20902dd7aea3d8ac94126a8f8069e6762592ea60326Jason Monk    }
21002dd7aea3d8ac94126a8f8069e6762592ea60326Jason Monk
211a52ecb0390c85afb385371bb844bb496c59ddf87Sunny Goyal    public static int[] getCenterDeltaInScreenSpace(View v0, View v1) {
21263f1ec00fbbd109130d1e476371dbc9a87bb544dAdam Cohen        v0.getLocationInWindow(sLoc0);
21363f1ec00fbbd109130d1e476371dbc9a87bb544dAdam Cohen        v1.getLocationInWindow(sLoc1);
21463f1ec00fbbd109130d1e476371dbc9a87bb544dAdam Cohen
21563f1ec00fbbd109130d1e476371dbc9a87bb544dAdam Cohen        sLoc0[0] += (v0.getMeasuredWidth() * v0.getScaleX()) / 2;
21663f1ec00fbbd109130d1e476371dbc9a87bb544dAdam Cohen        sLoc0[1] += (v0.getMeasuredHeight() * v0.getScaleY()) / 2;
21763f1ec00fbbd109130d1e476371dbc9a87bb544dAdam Cohen        sLoc1[0] += (v1.getMeasuredWidth() * v1.getScaleX()) / 2;
21863f1ec00fbbd109130d1e476371dbc9a87bb544dAdam Cohen        sLoc1[1] += (v1.getMeasuredHeight() * v1.getScaleY()) / 2;
219a52ecb0390c85afb385371bb844bb496c59ddf87Sunny Goyal        return new int[] {sLoc1[0] - sLoc0[0], sLoc1[1] - sLoc0[1]};
22063f1ec00fbbd109130d1e476371dbc9a87bb544dAdam Cohen    }
22163f1ec00fbbd109130d1e476371dbc9a87bb544dAdam Cohen
222b1e2e2956e78b4845bdb2abbe3654de6445785f2Sunny Goyal    public static void scaleRectFAboutCenter(RectF r, float scale) {
223b1e2e2956e78b4845bdb2abbe3654de6445785f2Sunny Goyal        if (scale != 1.0f) {
224b1e2e2956e78b4845bdb2abbe3654de6445785f2Sunny Goyal            float cx = r.centerX();
225b1e2e2956e78b4845bdb2abbe3654de6445785f2Sunny Goyal            float cy = r.centerY();
226b1e2e2956e78b4845bdb2abbe3654de6445785f2Sunny Goyal            r.offset(-cx, -cy);
227b1e2e2956e78b4845bdb2abbe3654de6445785f2Sunny Goyal            r.left = r.left * scale;
228b1e2e2956e78b4845bdb2abbe3654de6445785f2Sunny Goyal            r.top = r.top * scale ;
229b1e2e2956e78b4845bdb2abbe3654de6445785f2Sunny Goyal            r.right = r.right * scale;
230b1e2e2956e78b4845bdb2abbe3654de6445785f2Sunny Goyal            r.bottom = r.bottom * scale;
231b1e2e2956e78b4845bdb2abbe3654de6445785f2Sunny Goyal            r.offset(cx, cy);
232b1e2e2956e78b4845bdb2abbe3654de6445785f2Sunny Goyal        }
233b1e2e2956e78b4845bdb2abbe3654de6445785f2Sunny Goyal    }
234b1e2e2956e78b4845bdb2abbe3654de6445785f2Sunny Goyal
2356f6a06ac470df4f58a6eea902b79a8813c44c8f7Jon Miranda    public static void scaleRectAboutCenter(Rect r, float scale) {
2366f6a06ac470df4f58a6eea902b79a8813c44c8f7Jon Miranda        if (scale != 1.0f) {
2376f6a06ac470df4f58a6eea902b79a8813c44c8f7Jon Miranda            int cx = r.centerX();
2386f6a06ac470df4f58a6eea902b79a8813c44c8f7Jon Miranda            int cy = r.centerY();
2396f6a06ac470df4f58a6eea902b79a8813c44c8f7Jon Miranda            r.offset(-cx, -cy);
2403e803c7a4bec4d14006057d918fe8663c7d36769Winson Chung            scaleRect(r, scale);
2413e803c7a4bec4d14006057d918fe8663c7d36769Winson Chung            r.offset(cx, cy);
2423e803c7a4bec4d14006057d918fe8663c7d36769Winson Chung        }
2433e803c7a4bec4d14006057d918fe8663c7d36769Winson Chung    }
2446f6a06ac470df4f58a6eea902b79a8813c44c8f7Jon Miranda
2453e803c7a4bec4d14006057d918fe8663c7d36769Winson Chung    public static void scaleRect(Rect r, float scale) {
2463e803c7a4bec4d14006057d918fe8663c7d36769Winson Chung        if (scale != 1.0f) {
2476f6a06ac470df4f58a6eea902b79a8813c44c8f7Jon Miranda            r.left = (int) (r.left * scale + 0.5f);
2486f6a06ac470df4f58a6eea902b79a8813c44c8f7Jon Miranda            r.top = (int) (r.top * scale + 0.5f);
2496f6a06ac470df4f58a6eea902b79a8813c44c8f7Jon Miranda            r.right = (int) (r.right * scale + 0.5f);
2506f6a06ac470df4f58a6eea902b79a8813c44c8f7Jon Miranda            r.bottom = (int) (r.bottom * scale + 0.5f);
2516f6a06ac470df4f58a6eea902b79a8813c44c8f7Jon Miranda        }
2526f6a06ac470df4f58a6eea902b79a8813c44c8f7Jon Miranda    }
2536f6a06ac470df4f58a6eea902b79a8813c44c8f7Jon Miranda
2543e803c7a4bec4d14006057d918fe8663c7d36769Winson Chung    public static void insetRect(Rect r, Rect insets) {
2553e803c7a4bec4d14006057d918fe8663c7d36769Winson Chung        r.left = Math.min(r.right, r.left + insets.left);
2563e803c7a4bec4d14006057d918fe8663c7d36769Winson Chung        r.top = Math.min(r.bottom, r.top + insets.top);
2573e803c7a4bec4d14006057d918fe8663c7d36769Winson Chung        r.right = Math.max(r.left, r.right - insets.right);
2583e803c7a4bec4d14006057d918fe8663c7d36769Winson Chung        r.bottom = Math.max(r.top, r.bottom - insets.bottom);
2593e803c7a4bec4d14006057d918fe8663c7d36769Winson Chung    }
2603e803c7a4bec4d14006057d918fe8663c7d36769Winson Chung
2616f6a06ac470df4f58a6eea902b79a8813c44c8f7Jon Miranda    public static float shrinkRect(Rect r, float scaleX, float scaleY) {
262e96798e885e3ac97b6de0a1c598eb09cf62349afJon Miranda        float scale = Math.min(Math.min(scaleX, scaleY), 1.0f);
263e96798e885e3ac97b6de0a1c598eb09cf62349afJon Miranda        if (scale < 1.0f) {
264e96798e885e3ac97b6de0a1c598eb09cf62349afJon Miranda            int deltaX = (int) (r.width() * (scaleX - scale) * 0.5f);
265e96798e885e3ac97b6de0a1c598eb09cf62349afJon Miranda            r.left += deltaX;
266e96798e885e3ac97b6de0a1c598eb09cf62349afJon Miranda            r.right -= deltaX;
267e96798e885e3ac97b6de0a1c598eb09cf62349afJon Miranda
268e96798e885e3ac97b6de0a1c598eb09cf62349afJon Miranda            int deltaY = (int) (r.height() * (scaleY - scale) * 0.5f);
269e96798e885e3ac97b6de0a1c598eb09cf62349afJon Miranda            r.top += deltaY;
270e96798e885e3ac97b6de0a1c598eb09cf62349afJon Miranda            r.bottom -= deltaY;
271106bf64f4a5b4b7772c971f62b6207b5a73529a2Sunny Goyal        }
272e96798e885e3ac97b6de0a1c598eb09cf62349afJon Miranda        return scale;
2733a6e7f330e680ef718ca7c0921d842efb4d8bbaeWinson Chung    }
2743a6e7f330e680ef718ca7c0921d842efb4d8bbaeWinson Chung
2753e803c7a4bec4d14006057d918fe8663c7d36769Winson Chung    public static float mapRange(float value, float min, float max) {
2763e803c7a4bec4d14006057d918fe8663c7d36769Winson Chung        return min + (value * (max - min));
2773e803c7a4bec4d14006057d918fe8663c7d36769Winson Chung    }
2783e803c7a4bec4d14006057d918fe8663c7d36769Winson Chung
2790e947fbdbb49d2d58b4d1719224ded4c3f18707aSunny Goyal    public static boolean isSystemApp(Context context, Intent intent) {
280c5c60ad3592f53549c9ffaa58e9a87b0480080e8Sunny Goyal        PackageManager pm = context.getPackageManager();
281c5c60ad3592f53549c9ffaa58e9a87b0480080e8Sunny Goyal        ComponentName cn = intent.getComponent();
282c5c60ad3592f53549c9ffaa58e9a87b0480080e8Sunny Goyal        String packageName = null;
283c5c60ad3592f53549c9ffaa58e9a87b0480080e8Sunny Goyal        if (cn == null) {
284c5c60ad3592f53549c9ffaa58e9a87b0480080e8Sunny Goyal            ResolveInfo info = pm.resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY);
285c5c60ad3592f53549c9ffaa58e9a87b0480080e8Sunny Goyal            if ((info != null) && (info.activityInfo != null)) {
286c5c60ad3592f53549c9ffaa58e9a87b0480080e8Sunny Goyal                packageName = info.activityInfo.packageName;
287c5c60ad3592f53549c9ffaa58e9a87b0480080e8Sunny Goyal            }
288c5c60ad3592f53549c9ffaa58e9a87b0480080e8Sunny Goyal        } else {
289c5c60ad3592f53549c9ffaa58e9a87b0480080e8Sunny Goyal            packageName = cn.getPackageName();
290c5c60ad3592f53549c9ffaa58e9a87b0480080e8Sunny Goyal        }
291c5c60ad3592f53549c9ffaa58e9a87b0480080e8Sunny Goyal        if (packageName != null) {
292c5c60ad3592f53549c9ffaa58e9a87b0480080e8Sunny Goyal            try {
293c5c60ad3592f53549c9ffaa58e9a87b0480080e8Sunny Goyal                PackageInfo info = pm.getPackageInfo(packageName, 0);
294c5c60ad3592f53549c9ffaa58e9a87b0480080e8Sunny Goyal                return (info != null) && (info.applicationInfo != null) &&
295c5c60ad3592f53549c9ffaa58e9a87b0480080e8Sunny Goyal                        ((info.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0);
296c5c60ad3592f53549c9ffaa58e9a87b0480080e8Sunny Goyal            } catch (NameNotFoundException e) {
297c5c60ad3592f53549c9ffaa58e9a87b0480080e8Sunny Goyal                return false;
298c5c60ad3592f53549c9ffaa58e9a87b0480080e8Sunny Goyal            }
299c5c60ad3592f53549c9ffaa58e9a87b0480080e8Sunny Goyal        } else {
300c5c60ad3592f53549c9ffaa58e9a87b0480080e8Sunny Goyal            return false;
301c5c60ad3592f53549c9ffaa58e9a87b0480080e8Sunny Goyal        }
302c5c60ad3592f53549c9ffaa58e9a87b0480080e8Sunny Goyal    }
30395abbb330ce9bbaf23594245f0f8a795c8118038Sunny Goyal
3040fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal    /*
3050fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal     * Finds a system apk which had a broadcast receiver listening to a particular action.
3060fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal     * @param action intent action used to find the apk
3070fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal     * @return a pair of apk package name and the resources.
3080fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal     */
3090fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal    static Pair<String, Resources> findSystemApk(String action, PackageManager pm) {
3100fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal        final Intent intent = new Intent(action);
3110fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal        for (ResolveInfo info : pm.queryBroadcastReceivers(intent, 0)) {
3120fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal            if (info.activityInfo != null &&
3130fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal                    (info.activityInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
3140fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal                final String packageName = info.activityInfo.packageName;
3150fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal                try {
3160fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal                    final Resources res = pm.getResourcesForApplication(packageName);
3170fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal                    return Pair.create(packageName, res);
3180fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal                } catch (NameNotFoundException e) {
3190fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal                    Log.w(TAG, "Failed to find resources for " + packageName);
3200fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal                }
3210fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal            }
3220fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal        }
3230fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal        return null;
3240fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal    }
325fafca5299e13e0f44d1d2332d7669d55ad38fdb5Sunny Goyal
326594d76dc66cb5666830b62594909fed187987d42Sunny Goyal    /**
3275b0e669169ea2c951bf2f6f71faf793b24db3c23Sunny Goyal     * Compresses the bitmap to a byte array for serialization.
3285b0e669169ea2c951bf2f6f71faf793b24db3c23Sunny Goyal     */
3295b0e669169ea2c951bf2f6f71faf793b24db3c23Sunny Goyal    public static byte[] flattenBitmap(Bitmap bitmap) {
3305b0e669169ea2c951bf2f6f71faf793b24db3c23Sunny Goyal        // Try go guesstimate how much space the icon will take when serialized
3315b0e669169ea2c951bf2f6f71faf793b24db3c23Sunny Goyal        // to avoid unnecessary allocations/copies during the write.
3325b0e669169ea2c951bf2f6f71faf793b24db3c23Sunny Goyal        int size = bitmap.getWidth() * bitmap.getHeight() * 4;
3335b0e669169ea2c951bf2f6f71faf793b24db3c23Sunny Goyal        ByteArrayOutputStream out = new ByteArrayOutputStream(size);
3345b0e669169ea2c951bf2f6f71faf793b24db3c23Sunny Goyal        try {
3355b0e669169ea2c951bf2f6f71faf793b24db3c23Sunny Goyal            bitmap.compress(Bitmap.CompressFormat.PNG, 100, out);
3365b0e669169ea2c951bf2f6f71faf793b24db3c23Sunny Goyal            out.flush();
3375b0e669169ea2c951bf2f6f71faf793b24db3c23Sunny Goyal            out.close();
3385b0e669169ea2c951bf2f6f71faf793b24db3c23Sunny Goyal            return out.toByteArray();
3395b0e669169ea2c951bf2f6f71faf793b24db3c23Sunny Goyal        } catch (IOException e) {
3405b0e669169ea2c951bf2f6f71faf793b24db3c23Sunny Goyal            Log.w(TAG, "Could not write bitmap");
3415b0e669169ea2c951bf2f6f71faf793b24db3c23Sunny Goyal            return null;
3425b0e669169ea2c951bf2f6f71faf793b24db3c23Sunny Goyal        }
3435b0e669169ea2c951bf2f6f71faf793b24db3c23Sunny Goyal    }
3445b0e669169ea2c951bf2f6f71faf793b24db3c23Sunny Goyal
345f7a29e83f06909b378dba39c83a522375682710aSunny Goyal    /**
34682b016cb56540fe26213e817dd0dd668099c8e20Winson Chung     * Trims the string, removing all whitespace at the beginning and end of the string.
34782b016cb56540fe26213e817dd0dd668099c8e20Winson Chung     * Non-breaking whitespaces are also removed.
34882b016cb56540fe26213e817dd0dd668099c8e20Winson Chung     */
34982b016cb56540fe26213e817dd0dd668099c8e20Winson Chung    public static String trim(CharSequence s) {
350afa77e947d7b286c29cd6fc590b88d6cb425334fWinson Chung        if (s == null) {
351afa77e947d7b286c29cd6fc590b88d6cb425334fWinson Chung            return null;
352afa77e947d7b286c29cd6fc590b88d6cb425334fWinson Chung        }
353afa77e947d7b286c29cd6fc590b88d6cb425334fWinson Chung
35482b016cb56540fe26213e817dd0dd668099c8e20Winson Chung        // Just strip any sequence of whitespace or java space characters from the beginning and end
35582b016cb56540fe26213e817dd0dd668099c8e20Winson Chung        Matcher m = sTrimPattern.matcher(s);
35682b016cb56540fe26213e817dd0dd668099c8e20Winson Chung        return m.replaceAll("$1");
35782b016cb56540fe26213e817dd0dd668099c8e20Winson Chung    }
3587066003b2032a49ae5e59dab9b706259bdeb7e6eSunny Goyal
3592c6e5ccbcd7c3f87e7e85029782440600dc0a3d8Winson Chung    /**
3602c6e5ccbcd7c3f87e7e85029782440600dc0a3d8Winson Chung     * Calculates the height of a given string at a specific text size.
3612c6e5ccbcd7c3f87e7e85029782440600dc0a3d8Winson Chung     */
362baec6ffae2c260180dbdf2c6b2ca2f38e1df8f1aSunny Goyal    public static int calculateTextHeight(float textSizePx) {
3632c6e5ccbcd7c3f87e7e85029782440600dc0a3d8Winson Chung        Paint p = new Paint();
3642c6e5ccbcd7c3f87e7e85029782440600dc0a3d8Winson Chung        p.setTextSize(textSizePx);
3652c6e5ccbcd7c3f87e7e85029782440600dc0a3d8Winson Chung        Paint.FontMetrics fm = p.getFontMetrics();
366baec6ffae2c260180dbdf2c6b2ca2f38e1df8f1aSunny Goyal        return (int) Math.ceil(fm.bottom - fm.top);
3672c6e5ccbcd7c3f87e7e85029782440600dc0a3d8Winson Chung    }
3682c6e5ccbcd7c3f87e7e85029782440600dc0a3d8Winson Chung
369b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung    /**
370b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung     * Convenience println with multiple args.
371b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung     */
372b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung    public static void println(String key, Object... args) {
373b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung        StringBuilder b = new StringBuilder();
374b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung        b.append(key);
375b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung        b.append(": ");
376b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung        boolean isFirstArgument = true;
377b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung        for (Object arg : args) {
378b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung            if (isFirstArgument) {
379b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung                isFirstArgument = false;
380b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung            } else {
381b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung                b.append(", ");
382b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung            }
383b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung            b.append(arg);
384b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung        }
385b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung        System.out.println(b.toString());
386b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung    }
387b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung
3887066003b2032a49ae5e59dab9b706259bdeb7e6eSunny Goyal    public static boolean isRtl(Resources res) {
389a52ecb0390c85afb385371bb844bb496c59ddf87Sunny Goyal        return res.getConfiguration().getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
3907066003b2032a49ae5e59dab9b706259bdeb7e6eSunny Goyal    }
3912bd3d7d1cb5e4d8d826982d11b456739fed6b817Hyunyoung Song
3925c97f51fd9443e461a360ff7852aa223c05d667fSunny Goyal    /**
3935c97f51fd9443e461a360ff7852aa223c05d667fSunny Goyal     * Returns true if the intent is a valid launch intent for a launcher activity of an app.
3945c97f51fd9443e461a360ff7852aa223c05d667fSunny Goyal     * This is used to identify shortcuts which are different from the ones exposed by the
3955c97f51fd9443e461a360ff7852aa223c05d667fSunny Goyal     * applications' manifest file.
3965c97f51fd9443e461a360ff7852aa223c05d667fSunny Goyal     *
3975c97f51fd9443e461a360ff7852aa223c05d667fSunny Goyal     * @param launchIntent The intent that will be launched when the shortcut is clicked.
3985c97f51fd9443e461a360ff7852aa223c05d667fSunny Goyal     */
3995c97f51fd9443e461a360ff7852aa223c05d667fSunny Goyal    public static boolean isLauncherAppTarget(Intent launchIntent) {
4004e5cc64eaf1f63d866d51ce0a6bbafb3d4085c21Sunny Goyal        if (launchIntent != null
4015c97f51fd9443e461a360ff7852aa223c05d667fSunny Goyal                && Intent.ACTION_MAIN.equals(launchIntent.getAction())
4025c97f51fd9443e461a360ff7852aa223c05d667fSunny Goyal                && launchIntent.getComponent() != null
4035c97f51fd9443e461a360ff7852aa223c05d667fSunny Goyal                && launchIntent.getCategories() != null
4045c97f51fd9443e461a360ff7852aa223c05d667fSunny Goyal                && launchIntent.getCategories().size() == 1
4055c97f51fd9443e461a360ff7852aa223c05d667fSunny Goyal                && launchIntent.hasCategory(Intent.CATEGORY_LAUNCHER)
4064e5cc64eaf1f63d866d51ce0a6bbafb3d4085c21Sunny Goyal                && TextUtils.isEmpty(launchIntent.getDataString())) {
4074e5cc64eaf1f63d866d51ce0a6bbafb3d4085c21Sunny Goyal            // An app target can either have no extra or have ItemInfo.EXTRA_PROFILE.
4084e5cc64eaf1f63d866d51ce0a6bbafb3d4085c21Sunny Goyal            Bundle extras = launchIntent.getExtras();
40924bb66a1c5a435ab0c55b4a97e788bdc2595bf84Sunny Goyal            return extras == null || extras.keySet().isEmpty();
41024bb66a1c5a435ab0c55b4a97e788bdc2595bf84Sunny Goyal        }
4114e5cc64eaf1f63d866d51ce0a6bbafb3d4085c21Sunny Goyal        return false;
4125c97f51fd9443e461a360ff7852aa223c05d667fSunny Goyal    }
4132e6da1539bc7286336b3c24d96ab76434939ce4dAdam Cohen
4142e6da1539bc7286336b3c24d96ab76434939ce4dAdam Cohen    public static float dpiFromPx(int size, DisplayMetrics metrics){
4152e6da1539bc7286336b3c24d96ab76434939ce4dAdam Cohen        float densityRatio = (float) metrics.densityDpi / DisplayMetrics.DENSITY_DEFAULT;
4162e6da1539bc7286336b3c24d96ab76434939ce4dAdam Cohen        return (size / densityRatio);
4172e6da1539bc7286336b3c24d96ab76434939ce4dAdam Cohen    }
4182e6da1539bc7286336b3c24d96ab76434939ce4dAdam Cohen    public static int pxFromDp(float size, DisplayMetrics metrics) {
4192e6da1539bc7286336b3c24d96ab76434939ce4dAdam Cohen        return (int) Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
4202e6da1539bc7286336b3c24d96ab76434939ce4dAdam Cohen                size, metrics));
4212e6da1539bc7286336b3c24d96ab76434939ce4dAdam Cohen    }
4222e6da1539bc7286336b3c24d96ab76434939ce4dAdam Cohen    public static int pxFromSp(float size, DisplayMetrics metrics) {
4232e6da1539bc7286336b3c24d96ab76434939ce4dAdam Cohen        return (int) Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP,
4242e6da1539bc7286336b3c24d96ab76434939ce4dAdam Cohen                size, metrics));
4252e6da1539bc7286336b3c24d96ab76434939ce4dAdam Cohen    }
426b1622cc30f2fd9b579cb918083e063685950df92Sunny Goyal
427b1622cc30f2fd9b579cb918083e063685950df92Sunny Goyal    public static String createDbSelectionQuery(String columnName, Iterable<?> values) {
428b1622cc30f2fd9b579cb918083e063685950df92Sunny Goyal        return String.format(Locale.ENGLISH, "%s IN (%s)", columnName, TextUtils.join(", ", values));
429b1622cc30f2fd9b579cb918083e063685950df92Sunny Goyal    }
43025aba0aea58b2ecbc0fd350c378fa9e45edb81c3Sunny Goyal
43125aba0aea58b2ecbc0fd350c378fa9e45edb81c3Sunny Goyal    public static boolean isBootCompleted() {
432dfc8b6685bf330b203cb16b67c39bed936030306Sunny Goyal        return "1".equals(getSystemProperty("sys.boot_completed", "1"));
433dfc8b6685bf330b203cb16b67c39bed936030306Sunny Goyal    }
434dfc8b6685bf330b203cb16b67c39bed936030306Sunny Goyal
435dfc8b6685bf330b203cb16b67c39bed936030306Sunny Goyal    public static String getSystemProperty(String property, String defaultValue) {
43625aba0aea58b2ecbc0fd350c378fa9e45edb81c3Sunny Goyal        try {
43725aba0aea58b2ecbc0fd350c378fa9e45edb81c3Sunny Goyal            Class clazz = Class.forName("android.os.SystemProperties");
43825aba0aea58b2ecbc0fd350c378fa9e45edb81c3Sunny Goyal            Method getter = clazz.getDeclaredMethod("get", String.class);
439dfc8b6685bf330b203cb16b67c39bed936030306Sunny Goyal            String value = (String) getter.invoke(null, property);
440dfc8b6685bf330b203cb16b67c39bed936030306Sunny Goyal            if (!TextUtils.isEmpty(value)) {
441dfc8b6685bf330b203cb16b67c39bed936030306Sunny Goyal                return value;
442dfc8b6685bf330b203cb16b67c39bed936030306Sunny Goyal            }
44325aba0aea58b2ecbc0fd350c378fa9e45edb81c3Sunny Goyal        } catch (Exception e) {
44425aba0aea58b2ecbc0fd350c378fa9e45edb81c3Sunny Goyal            Log.d(TAG, "Unable to read system properties");
44525aba0aea58b2ecbc0fd350c378fa9e45edb81c3Sunny Goyal        }
446dfc8b6685bf330b203cb16b67c39bed936030306Sunny Goyal        return defaultValue;
44725aba0aea58b2ecbc0fd350c378fa9e45edb81c3Sunny Goyal    }
44829d853c2ddf6432685a54e3966820f1a81cee183Tony Wickham
44929d853c2ddf6432685a54e3966820f1a81cee183Tony Wickham    /**
45029d853c2ddf6432685a54e3966820f1a81cee183Tony Wickham     * Ensures that a value is within given bounds. Specifically:
45129d853c2ddf6432685a54e3966820f1a81cee183Tony Wickham     * If value is less than lowerBound, return lowerBound; else if value is greater than upperBound,
45229d853c2ddf6432685a54e3966820f1a81cee183Tony Wickham     * return upperBound; else return value unchanged.
45329d853c2ddf6432685a54e3966820f1a81cee183Tony Wickham     */
454f549dab31d0fa3e4d0cf8d6025b20e49aafb2d59Tony Wickham    public static int boundToRange(int value, int lowerBound, int upperBound) {
455f549dab31d0fa3e4d0cf8d6025b20e49aafb2d59Tony Wickham        return Math.max(lowerBound, Math.min(value, upperBound));
456f549dab31d0fa3e4d0cf8d6025b20e49aafb2d59Tony Wickham    }
457f549dab31d0fa3e4d0cf8d6025b20e49aafb2d59Tony Wickham
458f549dab31d0fa3e4d0cf8d6025b20e49aafb2d59Tony Wickham    /**
459f549dab31d0fa3e4d0cf8d6025b20e49aafb2d59Tony Wickham     * @see #boundToRange(int, int, int).
460f549dab31d0fa3e4d0cf8d6025b20e49aafb2d59Tony Wickham     */
461f549dab31d0fa3e4d0cf8d6025b20e49aafb2d59Tony Wickham    public static float boundToRange(float value, float lowerBound, float upperBound) {
46229d853c2ddf6432685a54e3966820f1a81cee183Tony Wickham        return Math.max(lowerBound, Math.min(value, upperBound));
46329d853c2ddf6432685a54e3966820f1a81cee183Tony Wickham    }
4649d4380856ff41ecb26c0d5aee1747b6060d2ef0eSunny Goyal
465e78e3d734b577c1ab6dc0738a83600374908ea52Sunny Goyal    /**
466e78e3d734b577c1ab6dc0738a83600374908ea52Sunny Goyal     * Wraps a message with a TTS span, so that a different message is spoken than
467e78e3d734b577c1ab6dc0738a83600374908ea52Sunny Goyal     * what is getting displayed.
468e78e3d734b577c1ab6dc0738a83600374908ea52Sunny Goyal     * @param msg original message
469e78e3d734b577c1ab6dc0738a83600374908ea52Sunny Goyal     * @param ttsMsg message to be spoken
470e78e3d734b577c1ab6dc0738a83600374908ea52Sunny Goyal     */
471e78e3d734b577c1ab6dc0738a83600374908ea52Sunny Goyal    public static CharSequence wrapForTts(CharSequence msg, String ttsMsg) {
472a52ecb0390c85afb385371bb844bb496c59ddf87Sunny Goyal        SpannableString spanned = new SpannableString(msg);
473a52ecb0390c85afb385371bb844bb496c59ddf87Sunny Goyal        spanned.setSpan(new TtsSpan.TextBuilder(ttsMsg).build(),
474a52ecb0390c85afb385371bb844bb496c59ddf87Sunny Goyal                0, spanned.length(), Spannable.SPAN_INCLUSIVE_INCLUSIVE);
475a52ecb0390c85afb385371bb844bb496c59ddf87Sunny Goyal        return spanned;
476e78e3d734b577c1ab6dc0738a83600374908ea52Sunny Goyal    }
47712fb9fc3c3133c3a748deef7a0265e6a11a24cd6Winson
47812fb9fc3c3133c3a748deef7a0265e6a11a24cd6Winson    /**
47912fb9fc3c3133c3a748deef7a0265e6a11a24cd6Winson     * Replacement for Long.compare() which was added in API level 19.
48012fb9fc3c3133c3a748deef7a0265e6a11a24cd6Winson     */
48112fb9fc3c3133c3a748deef7a0265e6a11a24cd6Winson    public static int longCompare(long lhs, long rhs) {
48212fb9fc3c3133c3a748deef7a0265e6a11a24cd6Winson        return lhs < rhs ? -1 : (lhs == rhs ? 0 : 1);
48312fb9fc3c3133c3a748deef7a0265e6a11a24cd6Winson    }
484f725824fa2091cab44cf1bcbfe5b5b8d06475560Sunny Goyal
485f725824fa2091cab44cf1bcbfe5b5b8d06475560Sunny Goyal    public static SharedPreferences getPrefs(Context context) {
486f725824fa2091cab44cf1bcbfe5b5b8d06475560Sunny Goyal        return context.getSharedPreferences(
487f725824fa2091cab44cf1bcbfe5b5b8d06475560Sunny Goyal                LauncherFiles.SHARED_PREFERENCES_KEY, Context.MODE_PRIVATE);
488f725824fa2091cab44cf1bcbfe5b5b8d06475560Sunny Goyal    }
489112ac95571c9b81921a912d67b9c369fb523a235Tony Wickham
4909d887a2c452f652624f0d28950c1b69cb37a8e23Sunny Goyal    public static SharedPreferences getDevicePrefs(Context context) {
4919d887a2c452f652624f0d28950c1b69cb37a8e23Sunny Goyal        return context.getSharedPreferences(
4929d887a2c452f652624f0d28950c1b69cb37a8e23Sunny Goyal                LauncherFiles.DEVICE_PREFERENCES_KEY, Context.MODE_PRIVATE);
4939d887a2c452f652624f0d28950c1b69cb37a8e23Sunny Goyal    }
4949d887a2c452f652624f0d28950c1b69cb37a8e23Sunny Goyal
4958155fa2aa50a4c2ae3d4d1c599d249b6e20ec47dTony Wickham    public static boolean isPowerSaverPreventingAnimation(Context context) {
4968155fa2aa50a4c2ae3d4d1c599d249b6e20ec47dTony Wickham        if (ATLEAST_P) {
4978155fa2aa50a4c2ae3d4d1c599d249b6e20ec47dTony Wickham            // Battery saver mode no longer prevents animations.
4988155fa2aa50a4c2ae3d4d1c599d249b6e20ec47dTony Wickham            return false;
4998155fa2aa50a4c2ae3d4d1c599d249b6e20ec47dTony Wickham        }
500112ac95571c9b81921a912d67b9c369fb523a235Tony Wickham        PowerManager powerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
501a52ecb0390c85afb385371bb844bb496c59ddf87Sunny Goyal        return powerManager.isPowerSaveMode();
502112ac95571c9b81921a912d67b9c369fb523a235Tony Wickham    }
503e1483fb1cdabb33e7031bdebba96212d920c7416Sunny Goyal
5040de011705b132eacdd10f7bfb5cdc4bd79a147e8Hyunyoung Song    public static boolean isWallpaperAllowed(Context context) {
505f5e3744637db1598c389e62450627b2548f8f517Sunny Goyal        if (ATLEAST_NOUGAT) {
5061ed6c4adde9c2ce597deb3e57600c664684eb639Sunny Goyal            try {
5071ed6c4adde9c2ce597deb3e57600c664684eb639Sunny Goyal                WallpaperManager wm = context.getSystemService(WallpaperManager.class);
508367da9f422e7fad45da351e79b1ade29c366ef7eKenny Guy                return (Boolean) wm.getClass().getDeclaredMethod("isSetWallpaperAllowed")
5091ed6c4adde9c2ce597deb3e57600c664684eb639Sunny Goyal                        .invoke(wm);
5101ed6c4adde9c2ce597deb3e57600c664684eb639Sunny Goyal            } catch (Exception e) { }
5111ed6c4adde9c2ce597deb3e57600c664684eb639Sunny Goyal        }
5121ed6c4adde9c2ce597deb3e57600c664684eb639Sunny Goyal        return true;
5131ed6c4adde9c2ce597deb3e57600c664684eb639Sunny Goyal    }
5141ed6c4adde9c2ce597deb3e57600c664684eb639Sunny Goyal
515713edfce264db7edc409216d5c083f8dd6a7083fSunny Goyal    public static void closeSilently(Closeable c) {
516713edfce264db7edc409216d5c083f8dd6a7083fSunny Goyal        if (c != null) {
517713edfce264db7edc409216d5c083f8dd6a7083fSunny Goyal            try {
518713edfce264db7edc409216d5c083f8dd6a7083fSunny Goyal                c.close();
519713edfce264db7edc409216d5c083f8dd6a7083fSunny Goyal            } catch (IOException e) {
5203d706ad70365052e3224fc4f4b0e7d1f5e8abf22Sunny Goyal                if (FeatureFlags.IS_DOGFOOD_BUILD) {
521713edfce264db7edc409216d5c083f8dd6a7083fSunny Goyal                    Log.d(TAG, "Error closing", e);
522713edfce264db7edc409216d5c083f8dd6a7083fSunny Goyal                }
523713edfce264db7edc409216d5c083f8dd6a7083fSunny Goyal            }
524713edfce264db7edc409216d5c083f8dd6a7083fSunny Goyal        }
525713edfce264db7edc409216d5c083f8dd6a7083fSunny Goyal    }
526713edfce264db7edc409216d5c083f8dd6a7083fSunny Goyal
527e1483fb1cdabb33e7031bdebba96212d920c7416Sunny Goyal    /**
5283f732190cc6c8feef53383d369b66dcf38f06e44Sunny Goyal     * Returns true if {@param original} contains all entries defined in {@param updates} and
5293f732190cc6c8feef53383d369b66dcf38f06e44Sunny Goyal     * have the same value.
5303f732190cc6c8feef53383d369b66dcf38f06e44Sunny Goyal     * The comparison uses {@link Object#equals(Object)} to compare the values.
5313f732190cc6c8feef53383d369b66dcf38f06e44Sunny Goyal     */
5323f732190cc6c8feef53383d369b66dcf38f06e44Sunny Goyal    public static boolean containsAll(Bundle original, Bundle updates) {
5333f732190cc6c8feef53383d369b66dcf38f06e44Sunny Goyal        for (String key : updates.keySet()) {
5343f732190cc6c8feef53383d369b66dcf38f06e44Sunny Goyal            Object value1 = updates.get(key);
5353f732190cc6c8feef53383d369b66dcf38f06e44Sunny Goyal            Object value2 = original.get(key);
5363f732190cc6c8feef53383d369b66dcf38f06e44Sunny Goyal            if (value1 == null) {
5373f732190cc6c8feef53383d369b66dcf38f06e44Sunny Goyal                if (value2 != null) {
5383f732190cc6c8feef53383d369b66dcf38f06e44Sunny Goyal                    return false;
5393f732190cc6c8feef53383d369b66dcf38f06e44Sunny Goyal                }
5403f732190cc6c8feef53383d369b66dcf38f06e44Sunny Goyal            } else if (!value1.equals(value2)) {
5413f732190cc6c8feef53383d369b66dcf38f06e44Sunny Goyal                return false;
5423f732190cc6c8feef53383d369b66dcf38f06e44Sunny Goyal            }
5433f732190cc6c8feef53383d369b66dcf38f06e44Sunny Goyal        }
5443f732190cc6c8feef53383d369b66dcf38f06e44Sunny Goyal        return true;
5453f732190cc6c8feef53383d369b66dcf38f06e44Sunny Goyal    }
5463f732190cc6c8feef53383d369b66dcf38f06e44Sunny Goyal
5471bce7fd342875be8f7c1f82c8cf21d0199c8d544Tony Wickham    /** Returns whether the collection is null or empty. */
5481bce7fd342875be8f7c1f82c8cf21d0199c8d544Tony Wickham    public static boolean isEmpty(Collection c) {
5491bce7fd342875be8f7c1f82c8cf21d0199c8d544Tony Wickham        return c == null || c.isEmpty();
5501bce7fd342875be8f7c1f82c8cf21d0199c8d544Tony Wickham    }
5511bce7fd342875be8f7c1f82c8cf21d0199c8d544Tony Wickham
552712ee53c67bbfb5ad4735217f3a9b063d9c60ad9Sunny Goyal    public static boolean isBinderSizeError(Exception e) {
553712ee53c67bbfb5ad4735217f3a9b063d9c60ad9Sunny Goyal        return e.getCause() instanceof TransactionTooLargeException
554712ee53c67bbfb5ad4735217f3a9b063d9c60ad9Sunny Goyal                || e.getCause() instanceof DeadObjectException;
555712ee53c67bbfb5ad4735217f3a9b063d9c60ad9Sunny Goyal    }
556ab121c17159a7f655b772fa78c04a6d2479e2a8eSunny Goyal
557ab121c17159a7f655b772fa78c04a6d2479e2a8eSunny Goyal    public static <T> T getOverrideObject(Class<T> clazz, Context context, int resId) {
558ab121c17159a7f655b772fa78c04a6d2479e2a8eSunny Goyal        String className = context.getString(resId);
559ab121c17159a7f655b772fa78c04a6d2479e2a8eSunny Goyal        if (!TextUtils.isEmpty(className)) {
560ab121c17159a7f655b772fa78c04a6d2479e2a8eSunny Goyal            try {
561ab121c17159a7f655b772fa78c04a6d2479e2a8eSunny Goyal                Class<?> cls = Class.forName(className);
562ab121c17159a7f655b772fa78c04a6d2479e2a8eSunny Goyal                return (T) cls.getDeclaredConstructor(Context.class).newInstance(context);
563ab121c17159a7f655b772fa78c04a6d2479e2a8eSunny Goyal            } catch (ClassNotFoundException | InstantiationException | IllegalAccessException
564ab121c17159a7f655b772fa78c04a6d2479e2a8eSunny Goyal                    | ClassCastException | NoSuchMethodException | InvocationTargetException e) {
565ab121c17159a7f655b772fa78c04a6d2479e2a8eSunny Goyal                Log.e(TAG, "Bad overriden class", e);
566ab121c17159a7f655b772fa78c04a6d2479e2a8eSunny Goyal            }
567ab121c17159a7f655b772fa78c04a6d2479e2a8eSunny Goyal        }
568ab121c17159a7f655b772fa78c04a6d2479e2a8eSunny Goyal
569ab121c17159a7f655b772fa78c04a6d2479e2a8eSunny Goyal        try {
570ab121c17159a7f655b772fa78c04a6d2479e2a8eSunny Goyal            return clazz.newInstance();
571ab121c17159a7f655b772fa78c04a6d2479e2a8eSunny Goyal        } catch (InstantiationException|IllegalAccessException e) {
572ab121c17159a7f655b772fa78c04a6d2479e2a8eSunny Goyal            throw new RuntimeException(e);
573ab121c17159a7f655b772fa78c04a6d2479e2a8eSunny Goyal        }
574ab121c17159a7f655b772fa78c04a6d2479e2a8eSunny Goyal    }
575580edcf529b0755f2bba442e97dd02d8cdad3b82Tony Wickham
576580edcf529b0755f2bba442e97dd02d8cdad3b82Tony Wickham    /**
577580edcf529b0755f2bba442e97dd02d8cdad3b82Tony Wickham     * Returns a HashSet with a single element. We use this instead of Collections.singleton()
578580edcf529b0755f2bba442e97dd02d8cdad3b82Tony Wickham     * because HashSet ensures all operations, such as remove, are supported.
579580edcf529b0755f2bba442e97dd02d8cdad3b82Tony Wickham     */
580580edcf529b0755f2bba442e97dd02d8cdad3b82Tony Wickham    public static <T> HashSet<T> singletonHashSet(T elem) {
581580edcf529b0755f2bba442e97dd02d8cdad3b82Tony Wickham        HashSet<T> hashSet = new HashSet<>(1);
582580edcf529b0755f2bba442e97dd02d8cdad3b82Tony Wickham        hashSet.add(elem);
583580edcf529b0755f2bba442e97dd02d8cdad3b82Tony Wickham        return hashSet;
584580edcf529b0755f2bba442e97dd02d8cdad3b82Tony Wickham    }
58548198d004af64d0d7c8b86bc243087f68dc374c9Mario Bertschler
5863483c52e6bd81b7c37eb0089940e90a6f3e6d80cSunny Goyal    /**
5873483c52e6bd81b7c37eb0089940e90a6f3e6d80cSunny Goyal     * Utility method to post a runnable on the handler, skipping the synchronization barriers.
5883483c52e6bd81b7c37eb0089940e90a6f3e6d80cSunny Goyal     */
5893483c52e6bd81b7c37eb0089940e90a6f3e6d80cSunny Goyal    public static void postAsyncCallback(Handler handler, Runnable callback) {
5903483c52e6bd81b7c37eb0089940e90a6f3e6d80cSunny Goyal        Message msg = Message.obtain(handler, callback);
5913483c52e6bd81b7c37eb0089940e90a6f3e6d80cSunny Goyal        msg.setAsynchronous(true);
5923483c52e6bd81b7c37eb0089940e90a6f3e6d80cSunny Goyal        handler.sendMessage(msg);
5933483c52e6bd81b7c37eb0089940e90a6f3e6d80cSunny Goyal    }
59431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project}
595