blob: 56795ae8d2100b5c41c4a950156edc2f113c779e [file] [log] [blame]
The Android Open Source Projectafc4ab22009-03-03 19:32:34 -08001/**
2 * Copyright (C) 2007 Google Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5 * use this file except in compliance with the License. You may obtain a copy
6 * of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 * License for the specific language governing permissions and limitations
14 * under the License.
15 */
16
17package com.android.settings;
18
Benjamin Franz194300d2016-01-13 12:16:25 +000019import android.annotation.NonNull;
Alexandra Gherghina7d748c02014-06-27 12:33:42 +010020import android.annotation.Nullable;
Amith Yamasaniae697552011-09-27 11:33:17 -070021import android.app.ActivityManager;
Alexandra Gherghina7d748c02014-06-27 12:33:42 +010022import android.app.ActivityManagerNative;
Amith Yamasani9627a8e2012-09-23 12:54:14 -070023import android.app.AlertDialog;
Sudheer Shankabc956302015-04-09 12:19:53 +010024import android.app.AppGlobals;
Amith Yamasani9627a8e2012-09-23 12:54:14 -070025import android.app.Dialog;
Fabrice Di Meglio769630c2014-04-24 14:48:48 -070026import android.app.Fragment;
Alexandra Gherghina1eb3f312014-06-10 14:01:10 +010027import android.app.IActivityManager;
Sudheer Shankabc956302015-04-09 12:19:53 +010028import android.app.admin.DevicePolicyManager;
Fabrice Di Meglio8b2ea392015-01-23 19:03:22 -080029import android.content.ComponentName;
Amith Yamasani8d40fac2012-10-23 15:36:16 -070030import android.content.ContentResolver;
The Android Open Source Projectafc4ab22009-03-03 19:32:34 -080031import android.content.Context;
Amith Yamasani9627a8e2012-09-23 12:54:14 -070032import android.content.DialogInterface;
The Android Open Source Projectafc4ab22009-03-03 19:32:34 -080033import android.content.Intent;
Fabrice Di Meglio8b2ea392015-01-23 19:03:22 -080034import android.content.IntentFilter;
The Android Open Source Projectafc4ab22009-03-03 19:32:34 -080035import android.content.pm.ApplicationInfo;
Sudheer Shankabc956302015-04-09 12:19:53 +010036import android.content.pm.IPackageManager;
Christopher Tatea08a2252015-07-01 16:52:43 -070037import android.content.pm.IntentFilterVerificationInfo;
The Android Open Source Projectafc4ab22009-03-03 19:32:34 -080038import android.content.pm.PackageManager;
Jeff Sharkeyb654cbb2011-08-18 11:59:19 -070039import android.content.pm.ResolveInfo;
Amith Yamasaniae47ef42012-09-16 17:53:35 -070040import android.content.pm.UserInfo;
Anders Hammar1b2dd9032010-04-08 10:03:50 +020041import android.content.res.Resources;
Jason Monkb5aa73f2015-03-31 12:59:33 -040042import android.content.res.TypedArray;
Amith Yamasaniae47ef42012-09-16 17:53:35 -070043import android.database.Cursor;
Amith Yamasanif34a85d2012-09-17 18:31:45 -070044import android.graphics.Bitmap;
45import android.graphics.BitmapFactory;
Amith Yamasanic06d4c42011-02-25 14:35:20 -080046import android.net.ConnectivityManager;
47import android.net.LinkProperties;
Amith Yamasaniae47ef42012-09-16 17:53:35 -070048import android.net.Uri;
Amith Yamasania4379d62011-07-22 10:34:58 -070049import android.os.BatteryManager;
Anders Hammar1b2dd9032010-04-08 10:03:50 +020050import android.os.Bundle;
Alexandra Gherghina1eb3f312014-06-10 14:01:10 +010051import android.os.IBinder;
Jason Monkb45e27b2015-05-20 13:35:43 -040052import android.os.INetworkManagementService;
Alexandra Gherghina1eb3f312014-06-10 14:01:10 +010053import android.os.RemoteException;
Jason Monkb45e27b2015-05-20 13:35:43 -040054import android.os.ServiceManager;
Amith Yamasaniae47ef42012-09-16 17:53:35 -070055import android.os.UserHandle;
56import android.os.UserManager;
Fabrice Di Megliodff3faa2015-02-27 11:14:11 -080057import android.os.storage.StorageManager;
Jeff Sharkeyb654cbb2011-08-18 11:59:19 -070058import android.preference.PreferenceFrameLayout;
Amith Yamasani8d40fac2012-10-23 15:36:16 -070059import android.provider.ContactsContract.CommonDataKinds;
Amith Yamasaniae47ef42012-09-16 17:53:35 -070060import android.provider.ContactsContract.Contacts;
Amith Yamasani8d40fac2012-10-23 15:36:16 -070061import android.provider.ContactsContract.Data;
Amith Yamasaniae47ef42012-09-16 17:53:35 -070062import android.provider.ContactsContract.Profile;
Amith Yamasani8d40fac2012-10-23 15:36:16 -070063import android.provider.ContactsContract.RawContacts;
Andres Moralesce249fe2014-07-07 16:58:16 -070064import android.service.persistentdata.PersistentDataBlockManager;
Jason Monk39b46742015-09-10 15:52:51 -040065import android.support.v7.preference.Preference;
66import android.support.v7.preference.PreferenceGroup;
Amith Yamasani60133dd2010-09-11 14:17:31 -070067import android.telephony.TelephonyManager;
Julia Reynoldsce25af42015-07-08 16:56:31 -040068import android.text.Spannable;
69import android.text.SpannableString;
Anders Hammar1b2dd9032010-04-08 10:03:50 +020070import android.text.TextUtils;
Julia Reynoldsce25af42015-07-08 16:56:31 -040071import android.text.style.TtsSpan;
Christopher Tatea08a2252015-07-01 16:52:43 -070072import android.util.ArraySet;
Alexandra Gherghina1eb3f312014-06-10 14:01:10 +010073import android.util.Log;
Zoltan Szatmary-Bane5814ff2014-12-19 16:27:45 +000074import android.util.SparseArray;
Jason Monkdb4ed192015-12-11 16:48:31 -050075import android.util.TypedValue;
Zoltan Szatmary-Ban3af2e4c2014-12-19 17:17:23 +000076import android.view.LayoutInflater;
Jeff Sharkeyb654cbb2011-08-18 11:59:19 -070077import android.view.View;
78import android.view.ViewGroup;
Jason Monkb5aa73f2015-03-31 12:59:33 -040079import android.view.animation.Animation;
80import android.view.animation.Animation.AnimationListener;
81import android.view.animation.AnimationUtils;
Jeff Sharkeyb654cbb2011-08-18 11:59:19 -070082import android.widget.ListView;
83import android.widget.TabWidget;
Alexandra Gherghinabc6e78f2014-09-03 10:22:09 +010084import com.android.internal.util.UserIcons;
Daisuke Miyakawaa2633d02010-09-15 20:09:12 -070085
Amith Yamasaniae47ef42012-09-16 17:53:35 -070086import java.io.IOException;
87import java.io.InputStream;
Amith Yamasanic06d4c42011-02-25 14:35:20 -080088import java.net.InetAddress;
Alexandra Gherghina80e1f1b2014-07-31 14:56:33 +010089import java.util.ArrayList;
Amith Yamasanic06d4c42011-02-25 14:35:20 -080090import java.util.Iterator;
Daisuke Miyakawaa2633d02010-09-15 20:09:12 -070091import java.util.List;
Jean Chalard71ad1f42011-05-12 15:06:16 +090092import java.util.Locale;
The Android Open Source Projectafc4ab22009-03-03 19:32:34 -080093
Tony Mantler67cd6ab2015-06-08 14:41:02 -070094import static android.content.Intent.EXTRA_USER;
95
Jason Monk27985e12016-01-08 14:13:05 -050096public final class Utils extends com.android.settingslib.Utils {
97
Alexandra Gherghina1eb3f312014-06-10 14:01:10 +010098 private static final String TAG = "Settings";
Alexandra Gherghina7d748c02014-06-27 12:33:42 +010099
The Android Open Source Projectafc4ab22009-03-03 19:32:34 -0800100 /**
101 * Set the preference's title to the matching activity's label.
102 */
103 public static final int UPDATE_PREFERENCE_FLAG_SET_TITLE_TO_MATCHING_ACTIVITY = 1;
104
105 /**
Shuhrat Dehkanov96577682012-10-03 12:24:07 +0900106 * The opacity level of a disabled icon.
107 */
108 public static final float DISABLED_ALPHA = 0.4f;
109
110 /**
Dianne Hackborn68f005f2014-06-18 18:29:12 -0700111 * Color spectrum to use to indicate badness. 0 is completely transparent (no data),
112 * 1 is most bad (red), the last value is least bad (green).
113 */
114 public static final int[] BADNESS_COLORS = new int[] {
115 0x00000000, 0xffc43828, 0xffe54918, 0xfff47b00,
116 0xfffabf2c, 0xff679e37, 0xff0a7f42
117 };
118
Alexandra Gherghina7d748c02014-06-27 12:33:42 +0100119 private static final String SETTINGS_PACKAGE_NAME = "com.android.settings";
120
Elliott Hughes7253df32014-09-02 17:10:14 -0700121 private static final int SECONDS_PER_MINUTE = 60;
122 private static final int SECONDS_PER_HOUR = 60 * 60;
123 private static final int SECONDS_PER_DAY = 24 * 60 * 60;
124
Jason Monkbeb171d2015-05-21 15:24:37 -0400125 public static final String OS_PKG = "os";
126
Zoltan Szatmary-Bane5814ff2014-12-19 16:27:45 +0000127 private static SparseArray<Bitmap> sDarkDefaultUserBitmapCache = new SparseArray<Bitmap>();
128
Anders Hammar1b2dd9032010-04-08 10:03:50 +0200129 /**
The Android Open Source Projectafc4ab22009-03-03 19:32:34 -0800130 * Finds a matching activity for a preference's intent. If a matching
131 * activity is not found, it will remove the preference.
Ying Wanga7188322010-01-04 18:45:10 -0800132 *
The Android Open Source Projectafc4ab22009-03-03 19:32:34 -0800133 * @param context The context.
134 * @param parentPreferenceGroup The preference group that contains the
135 * preference whose intent is being resolved.
136 * @param preferenceKey The key of the preference whose intent is being
137 * resolved.
138 * @param flags 0 or one or more of
139 * {@link #UPDATE_PREFERENCE_FLAG_SET_TITLE_TO_MATCHING_ACTIVITY}
140 * .
141 * @return Whether an activity was found. If false, the preference was
142 * removed.
143 */
144 public static boolean updatePreferenceToSpecificActivityOrRemove(Context context,
145 PreferenceGroup parentPreferenceGroup, String preferenceKey, int flags) {
Ying Wanga7188322010-01-04 18:45:10 -0800146
The Android Open Source Projectafc4ab22009-03-03 19:32:34 -0800147 Preference preference = parentPreferenceGroup.findPreference(preferenceKey);
148 if (preference == null) {
149 return false;
150 }
Ying Wanga7188322010-01-04 18:45:10 -0800151
The Android Open Source Projectafc4ab22009-03-03 19:32:34 -0800152 Intent intent = preference.getIntent();
153 if (intent != null) {
154 // Find the activity that is in the system image
155 PackageManager pm = context.getPackageManager();
156 List<ResolveInfo> list = pm.queryIntentActivities(intent, 0);
157 int listSize = list.size();
158 for (int i = 0; i < listSize; i++) {
159 ResolveInfo resolveInfo = list.get(i);
160 if ((resolveInfo.activityInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM)
161 != 0) {
Ying Wanga7188322010-01-04 18:45:10 -0800162
The Android Open Source Projectafc4ab22009-03-03 19:32:34 -0800163 // Replace the intent with this specific activity
164 preference.setIntent(new Intent().setClassName(
165 resolveInfo.activityInfo.packageName,
166 resolveInfo.activityInfo.name));
167
168 if ((flags & UPDATE_PREFERENCE_FLAG_SET_TITLE_TO_MATCHING_ACTIVITY) != 0) {
169 // Set the preference title to the activity's label
170 preference.setTitle(resolveInfo.loadLabel(pm));
171 }
Ying Wanga7188322010-01-04 18:45:10 -0800172
The Android Open Source Projectafc4ab22009-03-03 19:32:34 -0800173 return true;
174 }
175 }
176 }
177
178 // Did not find a matching activity, so remove the preference
179 parentPreferenceGroup.removePreference(preference);
Ying Wanga7188322010-01-04 18:45:10 -0800180
Shuhrat Dehkanov7dc567a2012-04-23 01:59:56 +0900181 return false;
The Android Open Source Projectafc4ab22009-03-03 19:32:34 -0800182 }
Ying Wanga7188322010-01-04 18:45:10 -0800183
Anders Hammar1b2dd9032010-04-08 10:03:50 +0200184 /**
Benjamin Franz194300d2016-01-13 12:16:25 +0000185 * Returns the UserManager for a given context
186 *
187 * @throws IllegalStateException if no UserManager could be retrieved.
188 */
189 public static UserManager getUserManager(Context context) {
190 UserManager um = UserManager.get(context);
191 if (um == null) {
192 throw new IllegalStateException("Unable to load UserManager");
193 }
194 return um;
195 }
196
197 /**
Ying Wanga7188322010-01-04 18:45:10 -0800198 * Returns true if Monkey is running.
199 */
200 public static boolean isMonkeyRunning() {
Amith Yamasaniae697552011-09-27 11:33:17 -0700201 return ActivityManager.isUserAMonkey();
Ying Wanga7188322010-01-04 18:45:10 -0800202 }
Amith Yamasani60133dd2010-09-11 14:17:31 -0700203
204 /**
205 * Returns whether the device is voice-capable (meaning, it is also a phone).
206 */
207 public static boolean isVoiceCapable(Context context) {
208 TelephonyManager telephony =
209 (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
210 return telephony != null && telephony.isVoiceCapable();
211 }
Amith Yamasani0f85c482011-02-23 17:19:11 -0800212
Robert Greenwalt8af88fb2011-08-31 11:17:47 -0700213 public static boolean isWifiOnly(Context context) {
214 ConnectivityManager cm = (ConnectivityManager)context.getSystemService(
215 Context.CONNECTIVITY_SERVICE);
216 return (cm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE) == false);
Amith Yamasani0f85c482011-02-23 17:19:11 -0800217 }
Amith Yamasanic06d4c42011-02-25 14:35:20 -0800218
219 /**
220 * Returns the WIFI IP Addresses, if any, taking into account IPv4 and IPv6 style addresses.
221 * @param context the application context
Lorenzo Colitti769f0692013-08-01 17:30:07 +0900222 * @return the formatted and newline-separated IP addresses, or null if none.
Amith Yamasanic06d4c42011-02-25 14:35:20 -0800223 */
224 public static String getWifiIpAddresses(Context context) {
225 ConnectivityManager cm = (ConnectivityManager)
226 context.getSystemService(Context.CONNECTIVITY_SERVICE);
227 LinkProperties prop = cm.getLinkProperties(ConnectivityManager.TYPE_WIFI);
Amith Yamasani6822b742011-10-17 16:41:00 -0700228 return formatIpAddresses(prop);
229 }
230
231 /**
232 * Returns the default link's IP addresses, if any, taking into account IPv4 and IPv6 style
233 * addresses.
234 * @param context the application context
Lorenzo Colitti769f0692013-08-01 17:30:07 +0900235 * @return the formatted and newline-separated IP addresses, or null if none.
Amith Yamasani6822b742011-10-17 16:41:00 -0700236 */
Lorenzo Colitti6eb6a902013-11-08 03:53:29 +0900237 public static String getDefaultIpAddresses(ConnectivityManager cm) {
Amith Yamasani6822b742011-10-17 16:41:00 -0700238 LinkProperties prop = cm.getActiveLinkProperties();
239 return formatIpAddresses(prop);
240 }
241
242 private static String formatIpAddresses(LinkProperties prop) {
Amith Yamasanic06d4c42011-02-25 14:35:20 -0800243 if (prop == null) return null;
Lorenzo Colitti769f0692013-08-01 17:30:07 +0900244 Iterator<InetAddress> iter = prop.getAllAddresses().iterator();
Amith Yamasanic06d4c42011-02-25 14:35:20 -0800245 // If there are no entries, return null
246 if (!iter.hasNext()) return null;
247 // Concatenate all available addresses, comma separated
248 String addresses = "";
249 while (iter.hasNext()) {
250 addresses += iter.next().getHostAddress();
Lorenzo Colitti769f0692013-08-01 17:30:07 +0900251 if (iter.hasNext()) addresses += "\n";
Amith Yamasanic06d4c42011-02-25 14:35:20 -0800252 }
253 return addresses;
254 }
Jean Chalard71ad1f42011-05-12 15:06:16 +0900255
256 public static Locale createLocaleFromString(String localeStr) {
257 // TODO: is there a better way to actually construct a locale that will match?
258 // The main problem is, on top of Java specs, locale.toString() and
259 // new Locale(locale.toString()).toString() do not return equal() strings in
260 // many cases, because the constructor takes the only string as the language
261 // code. So : new Locale("en", "US").toString() => "en_US"
262 // And : new Locale("en_US").toString() => "en_us"
263 if (null == localeStr)
264 return Locale.getDefault();
265 String[] brokenDownLocale = localeStr.split("_", 3);
266 // split may not return a 0-length array.
267 if (1 == brokenDownLocale.length) {
268 return new Locale(brokenDownLocale[0]);
269 } else if (2 == brokenDownLocale.length) {
270 return new Locale(brokenDownLocale[0], brokenDownLocale[1]);
271 } else {
272 return new Locale(brokenDownLocale[0], brokenDownLocale[1], brokenDownLocale[2]);
273 }
274 }
Amith Yamasania4379d62011-07-22 10:34:58 -0700275
Jaewan Kima3fe77b2013-06-04 21:17:40 +0900276 public static boolean isBatteryPresent(Intent batteryChangedIntent) {
277 return batteryChangedIntent.getBooleanExtra(BatteryManager.EXTRA_PRESENT, true);
278 }
279
Amith Yamasania4379d62011-07-22 10:34:58 -0700280 public static String getBatteryPercentage(Intent batteryChangedIntent) {
Elliott Hughes7253df32014-09-02 17:10:14 -0700281 return formatPercentage(getBatteryLevel(batteryChangedIntent));
Dianne Hackborn525f2bd2014-04-29 11:24:06 -0700282 }
283
Jeff Sharkey97d07fa2012-11-30 12:36:53 -0800284 public static void forcePrepareCustomPreferencesList(
285 ViewGroup parent, View child, ListView list, boolean ignoreSidePadding) {
286 list.setScrollBarStyle(View.SCROLLBARS_OUTSIDE_OVERLAY);
287 list.setClipToPadding(false);
288 prepareCustomPreferencesList(parent, child, list, ignoreSidePadding);
289 }
290
Jeff Sharkeyb654cbb2011-08-18 11:59:19 -0700291 /**
292 * Prepare a custom preferences layout, moving padding to {@link ListView}
293 * when outside scrollbars are requested. Usually used to display
294 * {@link ListView} and {@link TabWidget} with correct padding.
295 */
Jeff Sharkey5d706792011-09-08 18:57:17 -0700296 public static void prepareCustomPreferencesList(
Jeff Sharkey97d07fa2012-11-30 12:36:53 -0800297 ViewGroup parent, View child, View list, boolean ignoreSidePadding) {
Jeff Sharkeyb654cbb2011-08-18 11:59:19 -0700298 final boolean movePadding = list.getScrollBarStyle() == View.SCROLLBARS_OUTSIDE_OVERLAY;
Fabrice Di Meglio97a18c82014-07-18 19:12:36 -0700299 if (movePadding) {
Jeff Sharkeyb654cbb2011-08-18 11:59:19 -0700300 final Resources res = list.getResources();
Amith Yamasani56f51a82013-08-05 10:07:23 -0700301 final int paddingSide = res.getDimensionPixelSize(R.dimen.settings_side_margin);
Jeff Sharkeyb654cbb2011-08-18 11:59:19 -0700302 final int paddingBottom = res.getDimensionPixelSize(
303 com.android.internal.R.dimen.preference_fragment_padding_bottom);
Jeff Sharkey5d706792011-09-08 18:57:17 -0700304
Fabrice Di Meglio97a18c82014-07-18 19:12:36 -0700305 if (parent instanceof PreferenceFrameLayout) {
306 ((PreferenceFrameLayout.LayoutParams) child.getLayoutParams()).removeBorders = true;
307
308 final int effectivePaddingSide = ignoreSidePadding ? 0 : paddingSide;
309 list.setPaddingRelative(effectivePaddingSide, 0, effectivePaddingSide, paddingBottom);
310 } else {
311 list.setPaddingRelative(paddingSide, 0, paddingSide, paddingBottom);
312 }
Jeff Sharkeyb654cbb2011-08-18 11:59:19 -0700313 }
314 }
Jeff Sharkeya83a24f2011-09-16 01:52:39 -0700315
Fabrice Di Meglio0f4a7792014-07-28 18:25:14 -0700316 public static void forceCustomPadding(View view, boolean additive) {
Fabrice Di Meglio38ba9a22014-07-18 19:58:50 -0700317 final Resources res = view.getResources();
318 final int paddingSide = res.getDimensionPixelSize(R.dimen.settings_side_margin);
Fabrice Di Meglio0f4a7792014-07-28 18:25:14 -0700319
320 final int paddingStart = paddingSide + (additive ? view.getPaddingStart() : 0);
321 final int paddingEnd = paddingSide + (additive ? view.getPaddingEnd() : 0);
Fabrice Di Meglio38ba9a22014-07-18 19:58:50 -0700322 final int paddingBottom = res.getDimensionPixelSize(
323 com.android.internal.R.dimen.preference_fragment_padding_bottom);
324
Fabrice Di Meglio0f4a7792014-07-28 18:25:14 -0700325 view.setPaddingRelative(paddingStart, 0, paddingEnd, paddingBottom);
Fabrice Di Meglio38ba9a22014-07-18 19:58:50 -0700326 }
327
Amith Yamasaniae47ef42012-09-16 17:53:35 -0700328 /* Used by UserSettings as well. Call this on a non-ui thread. */
329 public static boolean copyMeProfilePhoto(Context context, UserInfo user) {
330 Uri contactUri = Profile.CONTENT_URI;
331
332 InputStream avatarDataStream = Contacts.openContactPhotoInputStream(
333 context.getContentResolver(),
334 contactUri, true);
335 // If there's no profile photo, assign a default avatar
336 if (avatarDataStream == null) {
337 return false;
338 }
339 int userId = user != null ? user.id : UserHandle.myUserId();
340 UserManager um = (UserManager) context.getSystemService(Context.USER_SERVICE);
Amith Yamasanif34a85d2012-09-17 18:31:45 -0700341 Bitmap icon = BitmapFactory.decodeStream(avatarDataStream);
342 um.setUserIcon(userId, icon);
Amith Yamasaniae47ef42012-09-16 17:53:35 -0700343 try {
Amith Yamasanif34a85d2012-09-17 18:31:45 -0700344 avatarDataStream.close();
345 } catch (IOException ioe) { }
346 return true;
Amith Yamasaniae47ef42012-09-16 17:53:35 -0700347 }
348
Amith Yamasani8d40fac2012-10-23 15:36:16 -0700349 public static String getMeProfileName(Context context, boolean full) {
350 if (full) {
351 return getProfileDisplayName(context);
352 } else {
353 return getShorterNameIfPossible(context);
Amith Yamasaniae47ef42012-09-16 17:53:35 -0700354 }
Amith Yamasani8d40fac2012-10-23 15:36:16 -0700355 }
356
357 private static String getShorterNameIfPossible(Context context) {
358 final String given = getLocalProfileGivenName(context);
359 return !TextUtils.isEmpty(given) ? given : getProfileDisplayName(context);
360 }
361
362 private static String getLocalProfileGivenName(Context context) {
363 final ContentResolver cr = context.getContentResolver();
364
365 // Find the raw contact ID for the local ME profile raw contact.
366 final long localRowProfileId;
367 final Cursor localRawProfile = cr.query(
368 Profile.CONTENT_RAW_CONTACTS_URI,
369 new String[] {RawContacts._ID},
370 RawContacts.ACCOUNT_TYPE + " IS NULL AND " +
371 RawContacts.ACCOUNT_NAME + " IS NULL",
372 null, null);
373 if (localRawProfile == null) return null;
Amith Yamasaniae47ef42012-09-16 17:53:35 -0700374
375 try {
Amith Yamasani8d40fac2012-10-23 15:36:16 -0700376 if (!localRawProfile.moveToFirst()) {
377 return null;
Amith Yamasaniae47ef42012-09-16 17:53:35 -0700378 }
Amith Yamasani8d40fac2012-10-23 15:36:16 -0700379 localRowProfileId = localRawProfile.getLong(0);
Amith Yamasaniae47ef42012-09-16 17:53:35 -0700380 } finally {
Amith Yamasani8d40fac2012-10-23 15:36:16 -0700381 localRawProfile.close();
Amith Yamasaniae47ef42012-09-16 17:53:35 -0700382 }
Amith Yamasani8d40fac2012-10-23 15:36:16 -0700383
384 // Find the structured name for the raw contact.
385 final Cursor structuredName = cr.query(
386 Profile.CONTENT_URI.buildUpon().appendPath(Contacts.Data.CONTENT_DIRECTORY).build(),
387 new String[] {CommonDataKinds.StructuredName.GIVEN_NAME,
388 CommonDataKinds.StructuredName.FAMILY_NAME},
389 Data.RAW_CONTACT_ID + "=" + localRowProfileId,
390 null, null);
391 if (structuredName == null) return null;
392
393 try {
394 if (!structuredName.moveToFirst()) {
395 return null;
396 }
397 String partialName = structuredName.getString(0);
398 if (TextUtils.isEmpty(partialName)) {
399 partialName = structuredName.getString(1);
400 }
401 return partialName;
402 } finally {
403 structuredName.close();
404 }
405 }
406
407 private static final String getProfileDisplayName(Context context) {
408 final ContentResolver cr = context.getContentResolver();
409 final Cursor profile = cr.query(Profile.CONTENT_URI,
410 new String[] {Profile.DISPLAY_NAME}, null, null, null);
411 if (profile == null) return null;
412
413 try {
414 if (!profile.moveToFirst()) {
415 return null;
416 }
417 return profile.getString(0);
418 } finally {
419 profile.close();
420 }
Amith Yamasaniae47ef42012-09-16 17:53:35 -0700421 }
Amith Yamasani9627a8e2012-09-23 12:54:14 -0700422
423 /** Not global warming, it's global change warning. */
424 public static Dialog buildGlobalChangeWarningDialog(final Context context, int titleResId,
425 final Runnable positiveAction) {
426 final AlertDialog.Builder builder = new AlertDialog.Builder(context);
427 builder.setTitle(titleResId);
428 builder.setMessage(R.string.global_change_warning);
429 builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
430 @Override
431 public void onClick(DialogInterface dialog, int which) {
432 positiveAction.run();
433 }
434 });
435 builder.setNegativeButton(android.R.string.cancel, null);
436
437 return builder.create();
438 }
439
440 public static boolean hasMultipleUsers(Context context) {
441 return ((UserManager) context.getSystemService(Context.USER_SERVICE))
442 .getUsers().size() > 1;
443 }
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700444
445 /**
446 * Start a new instance of the activity, showing only the given fragment.
447 * When launched in this mode, the given preference fragment will be instantiated and fill the
448 * entire activity.
449 *
450 * @param context The context.
Fabrice Di Meglio93b77b72014-05-17 00:01:07 +0000451 * @param fragmentName The name of the fragment to display.
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700452 * @param args Optional arguments to supply to the fragment.
Fabrice Di Meglioa9e77992014-06-09 12:52:24 -0700453 * @param resultTo Option fragment that should receive the result of the activity launch.
454 * @param resultRequestCode If resultTo is non-null, this is the request code in which
455 * to report the result.
456 * @param titleResId resource id for the String to display for the title of this set
457 * of preferences.
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700458 * @param title String to display for the title of this set of preferences.
459 */
Fabrice Di Meglio93b77b72014-05-17 00:01:07 +0000460 public static void startWithFragment(Context context, String fragmentName, Bundle args,
Alexandra Gherghina62464b82014-08-11 12:40:13 +0100461 Fragment resultTo, int resultRequestCode, int titleResId,
462 CharSequence title) {
Fabrice Di Meglio0d643fd2014-06-16 20:11:27 -0700463 startWithFragment(context, fragmentName, args, resultTo, resultRequestCode,
Alexandra Gherghina62464b82014-08-11 12:40:13 +0100464 null /* titleResPackageName */, titleResId, title, false /* not a shortcut */);
465 }
466
467 /**
468 * Start a new instance of the activity, showing only the given fragment.
469 * When launched in this mode, the given preference fragment will be instantiated and fill the
470 * entire activity.
471 *
472 * @param context The context.
473 * @param fragmentName The name of the fragment to display.
474 * @param args Optional arguments to supply to the fragment.
475 * @param resultTo Option fragment that should receive the result of the activity launch.
476 * @param resultRequestCode If resultTo is non-null, this is the request code in which
477 * to report the result.
478 * @param titleResPackageName Optional package name for the resource id of the title.
479 * @param titleResId resource id for the String to display for the title of this set
480 * of preferences.
481 * @param title String to display for the title of this set of preferences.
482 */
483 public static void startWithFragment(Context context, String fragmentName, Bundle args,
484 Fragment resultTo, int resultRequestCode, String titleResPackageName, int titleResId,
485 CharSequence title) {
486 startWithFragment(context, fragmentName, args, resultTo, resultRequestCode,
487 titleResPackageName, titleResId, title, false /* not a shortcut */);
Fabrice Di Meglio0d643fd2014-06-16 20:11:27 -0700488 }
489
490 public static void startWithFragment(Context context, String fragmentName, Bundle args,
Alexandra Gherghina62464b82014-08-11 12:40:13 +0100491 Fragment resultTo, int resultRequestCode, int titleResId,
492 CharSequence title, boolean isShortcut) {
493 Intent intent = onBuildStartFragmentIntent(context, fragmentName, args,
494 null /* titleResPackageName */, titleResId, title, isShortcut);
495 if (resultTo == null) {
496 context.startActivity(intent);
497 } else {
498 resultTo.startActivityForResult(intent, resultRequestCode);
499 }
500 }
501
502 public static void startWithFragment(Context context, String fragmentName, Bundle args,
503 Fragment resultTo, int resultRequestCode, String titleResPackageName, int titleResId,
504 CharSequence title, boolean isShortcut) {
505 Intent intent = onBuildStartFragmentIntent(context, fragmentName, args, titleResPackageName,
506 titleResId, title, isShortcut);
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700507 if (resultTo == null) {
508 context.startActivity(intent);
509 } else {
510 resultTo.startActivityForResult(intent, resultRequestCode);
511 }
512 }
513
Zoltan Szatmary-Ban7a2ccf22014-09-18 10:26:11 +0100514 public static void startWithFragmentAsUser(Context context, String fragmentName, Bundle args,
Alexandra Gherghina62464b82014-08-11 12:40:13 +0100515 int titleResId, CharSequence title, boolean isShortcut,
516 UserHandle userHandle) {
517 Intent intent = onBuildStartFragmentIntent(context, fragmentName, args,
518 null /* titleResPackageName */, titleResId, title, isShortcut);
519 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
520 intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
521 context.startActivityAsUser(intent, userHandle);
522 }
523
524 public static void startWithFragmentAsUser(Context context, String fragmentName, Bundle args,
525 String titleResPackageName, int titleResId, CharSequence title, boolean isShortcut,
526 UserHandle userHandle) {
527 Intent intent = onBuildStartFragmentIntent(context, fragmentName, args, titleResPackageName,
528 titleResId, title, isShortcut);
Zoltan Szatmary-Band50c7a82014-09-22 17:14:08 +0100529 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
530 intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
Zoltan Szatmary-Ban7a2ccf22014-09-18 10:26:11 +0100531 context.startActivityAsUser(intent, userHandle);
532 }
533
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700534 /**
535 * Build an Intent to launch a new activity showing the selected fragment.
536 * The implementation constructs an Intent that re-launches the current activity with the
537 * appropriate arguments to display the fragment.
538 *
Fabrice Di Meglio0d643fd2014-06-16 20:11:27 -0700539 *
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700540 * @param context The Context.
Fabrice Di Meglio93b77b72014-05-17 00:01:07 +0000541 * @param fragmentName The name of the fragment to display.
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700542 * @param args Optional arguments to supply to the fragment.
Alexandra Gherghina62464b82014-08-11 12:40:13 +0100543 * @param titleResPackageName Optional package name for the resource id of the title.
Fabrice Di Meglioa9e77992014-06-09 12:52:24 -0700544 * @param titleResId Optional title resource id to show for this item.
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700545 * @param title Optional title to show for this item.
Fabrice Di Meglio0d643fd2014-06-16 20:11:27 -0700546 * @param isShortcut tell if this is a Launcher Shortcut or not
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700547 * @return Returns an Intent that can be launched to display the given
548 * fragment.
549 */
Fabrice Di Meglio93b77b72014-05-17 00:01:07 +0000550 public static Intent onBuildStartFragmentIntent(Context context, String fragmentName,
Alexandra Gherghina62464b82014-08-11 12:40:13 +0100551 Bundle args, String titleResPackageName, int titleResId, CharSequence title,
552 boolean isShortcut) {
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700553 Intent intent = new Intent(Intent.ACTION_MAIN);
554 intent.setClass(context, SubSettings.class);
Fabrice Di Meglio93b77b72014-05-17 00:01:07 +0000555 intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT, fragmentName);
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700556 intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_ARGUMENTS, args);
Alexandra Gherghina62464b82014-08-11 12:40:13 +0100557 intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_TITLE_RES_PACKAGE_NAME,
558 titleResPackageName);
Fabrice Di Meglioa9e77992014-06-09 12:52:24 -0700559 intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_TITLE_RESID, titleResId);
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700560 intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_TITLE, title);
Fabrice Di Meglio0d643fd2014-06-16 20:11:27 -0700561 intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_AS_SHORTCUT, isShortcut);
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700562 return intent;
563 }
Alexandra Gherghina3939cd72014-06-04 10:02:55 +0100564
565 /**
566 * Returns the managed profile of the current user or null if none found.
567 */
568 public static UserHandle getManagedProfile(UserManager userManager) {
569 List<UserHandle> userProfiles = userManager.getUserProfiles();
570 final int count = userProfiles.size();
571 for (int i = 0; i < count; i++) {
572 final UserHandle profile = userProfiles.get(i);
573 if (profile.getIdentifier() == userManager.getUserHandle()) {
574 continue;
575 }
576 final UserInfo userInfo = userManager.getUserInfo(profile.getIdentifier());
577 if (userInfo.isManagedProfile()) {
578 return profile;
579 }
580 }
581 return null;
582 }
583
584 /**
585 * Returns true if the current profile is a managed one.
Benjamin Franz194300d2016-01-13 12:16:25 +0000586 *
587 * @throws IllegalArgumentException if userManager is null.
Alexandra Gherghina3939cd72014-06-04 10:02:55 +0100588 */
Benjamin Franz194300d2016-01-13 12:16:25 +0000589 public static boolean isManagedProfile(@NonNull UserManager userManager) {
590 return isManagedProfile(userManager, UserHandle.myUserId());
591 }
592
593 /**
594 * Returns true if the userId passed in is a managed profile.
595 *
596 * @throws IllegalArgumentException if userManager is null.
597 */
598 public static boolean isManagedProfile(@NonNull UserManager userManager, int userId) {
599 if (userManager == null) {
600 throw new IllegalArgumentException("userManager must not be null");
601 }
602 UserInfo userInfo = userManager.getUserInfo(userId);
603 return (userInfo != null) ? userInfo.isManagedProfile() : false;
Alexandra Gherghina3939cd72014-06-04 10:02:55 +0100604 }
Alexandra Gherghina1eb3f312014-06-10 14:01:10 +0100605
606 /**
Alexandra Gherghina7d748c02014-06-27 12:33:42 +0100607 * Returns the target user for a Settings activity.
Alexandra Gherghina1eb3f312014-06-10 14:01:10 +0100608 *
Alexandra Gherghina7d748c02014-06-27 12:33:42 +0100609 * The target user can be either the current user, the user that launched this activity or
610 * the user contained as an extra in the arguments or intent extras.
611 *
612 * Note: This is secure in the sense that it only returns a target user different to the current
613 * one if the app launching this activity is the Settings app itself, running in the same user
614 * or in one that is in the same profile group, or if the user id is provided by the system.
Alexandra Gherghina1eb3f312014-06-10 14:01:10 +0100615 */
Alexandra Gherghina7d748c02014-06-27 12:33:42 +0100616 public static UserHandle getSecureTargetUser(IBinder activityToken,
617 UserManager um, @Nullable Bundle arguments, @Nullable Bundle intentExtras) {
618 UserHandle currentUser = new UserHandle(UserHandle.myUserId());
619 IActivityManager am = ActivityManagerNative.getDefault();
620 try {
621 String launchedFromPackage = am.getLaunchedFromPackage(activityToken);
622 boolean launchedFromSettingsApp = SETTINGS_PACKAGE_NAME.equals(launchedFromPackage);
623
624 UserHandle launchedFromUser = new UserHandle(UserHandle.getUserId(
625 am.getLaunchedFromUid(activityToken)));
626 if (launchedFromUser != null && !launchedFromUser.equals(currentUser)) {
627 // Check it's secure
628 if (isProfileOf(um, launchedFromUser)) {
629 return launchedFromUser;
630 }
631 }
632 UserHandle extrasUser = intentExtras != null
633 ? (UserHandle) intentExtras.getParcelable(EXTRA_USER) : null;
634 if (extrasUser != null && !extrasUser.equals(currentUser)) {
635 // Check it's secure
636 if (launchedFromSettingsApp && isProfileOf(um, extrasUser)) {
637 return extrasUser;
638 }
639 }
640 UserHandle argumentsUser = arguments != null
641 ? (UserHandle) arguments.getParcelable(EXTRA_USER) : null;
642 if (argumentsUser != null && !argumentsUser.equals(currentUser)) {
643 // Check it's secure
644 if (launchedFromSettingsApp && isProfileOf(um, argumentsUser)) {
645 return argumentsUser;
646 }
647 }
648 } catch (RemoteException e) {
649 // Should not happen
650 Log.v(TAG, "Could not talk to activity manager.", e);
651 }
652 return currentUser;
653 }
654
655 /**
656 * Returns the target user for a Settings activity.
657 *
658 * The target user can be either the current user, the user that launched this activity or
659 * the user contained as an extra in the arguments or intent extras.
660 *
661 * You should use {@link #getSecureTargetUser(IBinder, UserManager, Bundle, Bundle)} if
662 * possible.
663 *
664 * @see #getInsecureTargetUser(IBinder, Bundle, Bundle)
665 */
666 public static UserHandle getInsecureTargetUser(IBinder activityToken, @Nullable Bundle arguments,
667 @Nullable Bundle intentExtras) {
668 UserHandle currentUser = new UserHandle(UserHandle.myUserId());
669 IActivityManager am = ActivityManagerNative.getDefault();
Alexandra Gherghina1eb3f312014-06-10 14:01:10 +0100670 try {
Alexandra Gherghina7d748c02014-06-27 12:33:42 +0100671 UserHandle launchedFromUser = new UserHandle(UserHandle.getUserId(
672 am.getLaunchedFromUid(activityToken)));
673 if (launchedFromUser != null && !launchedFromUser.equals(currentUser)) {
674 return launchedFromUser;
675 }
676 UserHandle extrasUser = intentExtras != null
677 ? (UserHandle) intentExtras.getParcelable(EXTRA_USER) : null;
678 if (extrasUser != null && !extrasUser.equals(currentUser)) {
679 return extrasUser;
680 }
681 UserHandle argumentsUser = arguments != null
682 ? (UserHandle) arguments.getParcelable(EXTRA_USER) : null;
683 if (argumentsUser != null && !argumentsUser.equals(currentUser)) {
684 return argumentsUser;
Alexandra Gherghina1eb3f312014-06-10 14:01:10 +0100685 }
686 } catch (RemoteException e) {
687 // Should not happen
Alexandra Gherghina7d748c02014-06-27 12:33:42 +0100688 Log.v(TAG, "Could not talk to activity manager.", e);
689 return null;
Alexandra Gherghina1eb3f312014-06-10 14:01:10 +0100690 }
Alexandra Gherghina7d748c02014-06-27 12:33:42 +0100691 return currentUser;
692 }
Alexandra Gherghina1eb3f312014-06-10 14:01:10 +0100693
Alexandra Gherghina7d748c02014-06-27 12:33:42 +0100694 /**
695 * Returns true if the user provided is in the same profiles group as the current user.
696 */
697 private static boolean isProfileOf(UserManager um, UserHandle otherUser) {
698 if (um == null || otherUser == null) return false;
699 return (UserHandle.myUserId() == otherUser.getIdentifier())
700 || um.getUserProfiles().contains(otherUser);
Alexandra Gherghina1eb3f312014-06-10 14:01:10 +0100701 }
Amith Yamasani51c6dac2014-07-02 00:06:37 +0530702
Andres Moralesce249fe2014-07-07 16:58:16 -0700703
704 /**
705 * Returns whether or not this device is able to be OEM unlocked.
706 */
707 static boolean isOemUnlockEnabled(Context context) {
708 PersistentDataBlockManager manager =(PersistentDataBlockManager)
709 context.getSystemService(Context.PERSISTENT_DATA_BLOCK_SERVICE);
710 return manager.getOemUnlockEnabled();
711 }
712
713 /**
714 * Allows enabling or disabling OEM unlock on this device. OEM unlocked
715 * devices allow users to flash other OSes to them.
716 */
717 static void setOemUnlockEnabled(Context context, boolean enabled) {
718 PersistentDataBlockManager manager =(PersistentDataBlockManager)
719 context.getSystemService(Context.PERSISTENT_DATA_BLOCK_SERVICE);
720 manager.setOemUnlockEnabled(enabled);
721 }
Alexandra Gherghina95b86a52014-07-24 19:13:25 +0100722
723 /**
Fabrice Di Meglio22a2a492014-08-08 12:27:57 -0700724 * Return whether or not the user should have a SIM Cards option in Settings.
725 * TODO: Change back to returning true if count is greater than one after testing.
726 * TODO: See bug 16533525.
727 */
728 public static boolean showSimCardTile(Context context) {
729 final TelephonyManager tm =
730 (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
731
PauloftheWestdbd03822014-12-04 11:56:35 -0800732 return tm.getSimCount() > 1;
Fabrice Di Meglio22a2a492014-08-08 12:27:57 -0700733 }
Dan Sandlerb58b5122014-09-02 18:31:49 +0200734
735 /**
Elliott Hughes7253df32014-09-02 17:10:14 -0700736 * Returns elapsed time for the given millis, in the following format:
737 * 2d 5h 40m 29s
738 * @param context the application context
739 * @param millis the elapsed time in milli seconds
740 * @param withSeconds include seconds?
741 * @return the formatted elapsed time
742 */
743 public static String formatElapsedTime(Context context, double millis, boolean withSeconds) {
744 StringBuilder sb = new StringBuilder();
745 int seconds = (int) Math.floor(millis / 1000);
746 if (!withSeconds) {
747 // Round up.
748 seconds += 30;
749 }
750
751 int days = 0, hours = 0, minutes = 0;
752 if (seconds >= SECONDS_PER_DAY) {
753 days = seconds / SECONDS_PER_DAY;
754 seconds -= days * SECONDS_PER_DAY;
755 }
756 if (seconds >= SECONDS_PER_HOUR) {
757 hours = seconds / SECONDS_PER_HOUR;
758 seconds -= hours * SECONDS_PER_HOUR;
759 }
760 if (seconds >= SECONDS_PER_MINUTE) {
761 minutes = seconds / SECONDS_PER_MINUTE;
762 seconds -= minutes * SECONDS_PER_MINUTE;
763 }
764 if (withSeconds) {
765 if (days > 0) {
766 sb.append(context.getString(R.string.battery_history_days,
767 days, hours, minutes, seconds));
768 } else if (hours > 0) {
769 sb.append(context.getString(R.string.battery_history_hours,
770 hours, minutes, seconds));
771 } else if (minutes > 0) {
772 sb.append(context.getString(R.string.battery_history_minutes, minutes, seconds));
773 } else {
774 sb.append(context.getString(R.string.battery_history_seconds, seconds));
775 }
776 } else {
777 if (days > 0) {
778 sb.append(context.getString(R.string.battery_history_days_no_seconds,
779 days, hours, minutes));
780 } else if (hours > 0) {
781 sb.append(context.getString(R.string.battery_history_hours_no_seconds,
782 hours, minutes));
783 } else {
784 sb.append(context.getString(R.string.battery_history_minutes_no_seconds, minutes));
785 }
786 }
787 return sb.toString();
788 }
PauloftheWest0a0daca2014-11-06 15:02:58 -0800789
790 /**
Amith Yamasani45f86232014-11-19 17:12:46 -0800791 * Queries for the UserInfo of a user. Returns null if the user doesn't exist (was removed).
792 * @param userManager Instance of UserManager
793 * @param checkUser The user to check the existence of.
794 * @return UserInfo of the user or null for non-existent user.
795 */
796 public static UserInfo getExistingUser(UserManager userManager, UserHandle checkUser) {
797 final List<UserInfo> users = userManager.getUsers(true /* excludeDying */);
798 final int checkUserId = checkUser.getIdentifier();
799 for (UserInfo user : users) {
800 if (user.id == checkUserId) {
801 return user;
802 }
803 }
804 return null;
805 }
806
Zoltan Szatmary-Ban3af2e4c2014-12-19 17:17:23 +0000807 public static View inflateCategoryHeader(LayoutInflater inflater, ViewGroup parent) {
808 final TypedArray a = inflater.getContext().obtainStyledAttributes(null,
809 com.android.internal.R.styleable.Preference,
810 com.android.internal.R.attr.preferenceCategoryStyle, 0);
811 final int resId = a.getResourceId(com.android.internal.R.styleable.Preference_layout,
812 0);
813 a.recycle();
814 return inflater.inflate(resId, parent, false);
815 }
816
Fabrice Di Megliodff3faa2015-02-27 11:14:11 -0800817 /**
818 * Return if we are running low on storage space or not.
819 *
820 * @param context The context
821 * @return true if we are running low on storage space
822 */
823 public static boolean isLowStorage(Context context) {
824 final StorageManager sm = StorageManager.from(context);
825 return (sm.getStorageBytesUntilLow(context.getFilesDir()) < 0);
826 }
827
Zoltan Szatmary-Bane5814ff2014-12-19 16:27:45 +0000828 /**
829 * Returns a default user icon (as a {@link Bitmap}) for the given user.
830 *
831 * Note that for guest users, you should pass in {@code UserHandle.USER_NULL}.
832 * @param userId the user id or {@code UserHandle.USER_NULL} for a non-user specific icon
833 */
834 public static Bitmap getDefaultUserIconAsBitmap(int userId) {
835 Bitmap bitmap = null;
836 // Try finding the corresponding bitmap in the dark bitmap cache
837 bitmap = sDarkDefaultUserBitmapCache.get(userId);
838 if (bitmap == null) {
839 bitmap = UserIcons.convertToBitmap(UserIcons.getDefaultUserIcon(userId, false));
840 // Save it to cache
841 sDarkDefaultUserBitmapCache.put(userId, bitmap);
842 }
843 return bitmap;
844 }
Fabrice Di Meglio8b2ea392015-01-23 19:03:22 -0800845
Fabrice Di Meglio8b2ea392015-01-23 19:03:22 -0800846 public static boolean hasPreferredActivities(PackageManager pm, String packageName) {
847 // Get list of preferred activities
848 List<ComponentName> prefActList = new ArrayList<>();
849 // Intent list cannot be null. so pass empty list
850 List<IntentFilter> intentList = new ArrayList<>();
851 pm.getPreferredActivities(intentList, prefActList, packageName);
852 Log.d(TAG, "Have " + prefActList.size() + " number of activities in preferred list");
853 return prefActList.size() > 0;
854 }
855
Christopher Tatea08a2252015-07-01 16:52:43 -0700856 public static ArraySet<String> getHandledDomains(PackageManager pm, String packageName) {
857 List<IntentFilterVerificationInfo> iviList = pm.getIntentFilterVerifications(packageName);
858 List<IntentFilter> filters = pm.getAllIntentFilters(packageName);
859
860 ArraySet<String> result = new ArraySet<>();
861 if (iviList.size() > 0) {
862 for (IntentFilterVerificationInfo ivi : iviList) {
863 for (String host : ivi.getDomains()) {
864 result.add(host);
865 }
866 }
867 }
868 if (filters != null && filters.size() > 0) {
869 for (IntentFilter filter : filters) {
Christopher Tateddaa1422015-07-16 16:00:49 -0700870 if (filter.hasCategory(Intent.CATEGORY_BROWSABLE)
871 && (filter.hasDataScheme(IntentFilter.SCHEME_HTTP) ||
872 filter.hasDataScheme(IntentFilter.SCHEME_HTTPS))) {
Christopher Tatea08a2252015-07-01 16:52:43 -0700873 result.addAll(filter.getHostsList());
874 }
875 }
876 }
877 return result;
878 }
879
Jason Monkb5aa73f2015-03-31 12:59:33 -0400880 public static void handleLoadingContainer(View loading, View doneLoading, boolean done,
881 boolean animate) {
882 setViewShown(loading, !done, animate);
883 setViewShown(doneLoading, done, animate);
884 }
885
886 private static void setViewShown(final View view, boolean shown, boolean animate) {
887 if (animate) {
888 Animation animation = AnimationUtils.loadAnimation(view.getContext(),
889 shown ? android.R.anim.fade_in : android.R.anim.fade_out);
890 if (shown) {
891 view.setVisibility(View.VISIBLE);
892 } else {
893 animation.setAnimationListener(new AnimationListener() {
894 @Override
895 public void onAnimationStart(Animation animation) {
896 }
897
898 @Override
899 public void onAnimationRepeat(Animation animation) {
900 }
901
902 @Override
903 public void onAnimationEnd(Animation animation) {
904 view.setVisibility(View.INVISIBLE);
905 }
906 });
907 }
908 view.startAnimation(animation);
909 } else {
910 view.clearAnimation();
911 view.setVisibility(shown ? View.VISIBLE : View.INVISIBLE);
912 }
913 }
Sudheer Shankabc956302015-04-09 12:19:53 +0100914
915 /**
916 * Returns the application info of the currently installed MDM package.
917 */
918 public static ApplicationInfo getAdminApplicationInfo(Context context, int profileId) {
919 DevicePolicyManager dpm =
920 (DevicePolicyManager) context.getSystemService(Context.DEVICE_POLICY_SERVICE);
921 ComponentName mdmPackage = dpm.getProfileOwnerAsUser(profileId);
922 if (mdmPackage == null) {
923 return null;
924 }
925 String mdmPackageName = mdmPackage.getPackageName();
926 try {
927 IPackageManager ipm = AppGlobals.getPackageManager();
928 ApplicationInfo mdmApplicationInfo =
929 ipm.getApplicationInfo(mdmPackageName, 0, profileId);
930 return mdmApplicationInfo;
931 } catch (RemoteException e) {
932 Log.e(TAG, "Error while retrieving application info for package " + mdmPackageName
933 + ", userId " + profileId, e);
934 return null;
935 }
936 }
Jason Monkb45e27b2015-05-20 13:35:43 -0400937
938 public static boolean isBandwidthControlEnabled() {
939 final INetworkManagementService netManager = INetworkManagementService.Stub
940 .asInterface(ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE));
941 try {
942 return netManager.isBandwidthControlEnabled();
943 } catch (RemoteException e) {
944 return false;
945 }
946 }
Julia Reynoldsce25af42015-07-08 16:56:31 -0400947
948 /**
949 * Returns an accessible SpannableString.
950 * @param displayText the text to display
951 * @param accessibileText the text text-to-speech engines should read
952 */
953 public static SpannableString createAccessibleSequence(CharSequence displayText,
954 String accessibileText) {
955 SpannableString str = new SpannableString(displayText);
956 str.setSpan(new TtsSpan.TextBuilder(accessibileText).build(), 0,
957 displayText.length(),
958 Spannable.SPAN_INCLUSIVE_INCLUSIVE);
959 return str;
960 }
Andres Morales7bdffd82015-08-04 16:55:00 -0700961
Clara Bayarrife432e82015-10-12 12:07:02 +0100962 /**
Benjamin Franz194300d2016-01-13 12:16:25 +0000963 * Returns the user id present in the bundle with {@link Intent#EXTRA_USER_ID} if it
Clara Bayarrife432e82015-10-12 12:07:02 +0100964 * belongs to the current user.
965 *
966 * @throws SecurityException if the given userId does not belong to the current user group.
967 */
Benjamin Franz194300d2016-01-13 12:16:25 +0000968 public static int getUserIdFromBundle(Context context, Bundle bundle) {
Clara Bayarrife432e82015-10-12 12:07:02 +0100969 if (bundle == null) {
Benjamin Franz194300d2016-01-13 12:16:25 +0000970 return getCredentialOwnerUserId(context);
Clara Bayarrife432e82015-10-12 12:07:02 +0100971 }
Clara Bayarri6934a042015-10-14 11:07:35 +0100972 int userId = bundle.getInt(Intent.EXTRA_USER_ID, UserHandle.myUserId());
Benjamin Franz194300d2016-01-13 12:16:25 +0000973 return enforceSameOwner(context, userId);
Clara Bayarrife432e82015-10-12 12:07:02 +0100974 }
975
976 /**
977 * Returns the given user id if it belongs to the current user.
978 *
979 * @throws SecurityException if the given userId does not belong to the current user group.
980 */
Benjamin Franz194300d2016-01-13 12:16:25 +0000981 public static int enforceSameOwner(Context context, int userId) {
982 UserManager um = getUserManager(context);
983 if (!um.getUserProfiles().contains(new UserHandle(userId))) {
984 throw new SecurityException("Given user id " + userId + " does not belong to user "
985 + UserHandle.myUserId());
Clara Bayarrife432e82015-10-12 12:07:02 +0100986 }
Benjamin Franz194300d2016-01-13 12:16:25 +0000987 return userId;
Clara Bayarrife432e82015-10-12 12:07:02 +0100988 }
989
Benjamin Franz194300d2016-01-13 12:16:25 +0000990 /**
991 * Returns the effective credential owner of the calling user.
992 */
993 public static int getCredentialOwnerUserId(Context context) {
994 return getCredentialOwnerUserId(context, UserHandle.myUserId());
995 }
996
997 /**
998 * Returns the user id of the credential owner of the given user id.
999 */
1000 public static int getCredentialOwnerUserId(Context context, int userId) {
1001 UserManager um = getUserManager(context);
1002 return um.getCredentialOwnerProfile(userId);
Andres Morales7bdffd82015-08-04 16:55:00 -07001003 }
Jason Monkdb4ed192015-12-11 16:48:31 -05001004
1005 public static int resolveResource(Context context, int attr) {
1006 TypedValue value = new TypedValue();
1007 context.getTheme().resolveAttribute(attr, value, true);
1008 return value.resourceId;
1009 }
Andres Morales7bdffd82015-08-04 16:55:00 -07001010}
1011