blob: 8d6e55c359a18eaa3412a9093f63cb84c31635cd [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
Alexandra Gherghina7d748c02014-06-27 12:33:42 +010019import static android.content.Intent.EXTRA_USER;
20
21import android.annotation.Nullable;
Amith Yamasaniae697552011-09-27 11:33:17 -070022import android.app.ActivityManager;
Alexandra Gherghina7d748c02014-06-27 12:33:42 +010023import android.app.ActivityManagerNative;
Amith Yamasani9627a8e2012-09-23 12:54:14 -070024import android.app.AlertDialog;
25import 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;
Amith Yamasani8d40fac2012-10-23 15:36:16 -070028import android.content.ContentResolver;
The Android Open Source Projectafc4ab22009-03-03 19:32:34 -080029import android.content.Context;
Amith Yamasani9627a8e2012-09-23 12:54:14 -070030import android.content.DialogInterface;
The Android Open Source Projectafc4ab22009-03-03 19:32:34 -080031import android.content.Intent;
32import android.content.pm.ApplicationInfo;
Dan Sandlerb58b5122014-09-02 18:31:49 +020033import android.content.pm.PackageInfo;
The Android Open Source Projectafc4ab22009-03-03 19:32:34 -080034import android.content.pm.PackageManager;
Amith Yamasani02cf71a2010-09-21 15:48:52 -070035import android.content.pm.PackageManager.NameNotFoundException;
Jeff Sharkeyb654cbb2011-08-18 11:59:19 -070036import android.content.pm.ResolveInfo;
Dan Sandlerb58b5122014-09-02 18:31:49 +020037import android.content.pm.Signature;
Amith Yamasaniae47ef42012-09-16 17:53:35 -070038import android.content.pm.UserInfo;
Anders Hammar1b2dd9032010-04-08 10:03:50 +020039import android.content.res.Resources;
40import android.content.res.Resources.NotFoundException;
Amith Yamasaniae47ef42012-09-16 17:53:35 -070041import android.database.Cursor;
Amith Yamasanif34a85d2012-09-17 18:31:45 -070042import android.graphics.Bitmap;
43import android.graphics.BitmapFactory;
Anders Hammar1b2dd9032010-04-08 10:03:50 +020044import android.graphics.drawable.Drawable;
Amith Yamasanic06d4c42011-02-25 14:35:20 -080045import android.net.ConnectivityManager;
46import android.net.LinkProperties;
Amith Yamasaniae47ef42012-09-16 17:53:35 -070047import android.net.Uri;
Amith Yamasania4379d62011-07-22 10:34:58 -070048import android.os.BatteryManager;
Anders Hammar1b2dd9032010-04-08 10:03:50 +020049import android.os.Bundle;
Alexandra Gherghina1eb3f312014-06-10 14:01:10 +010050import android.os.IBinder;
51import android.os.RemoteException;
Amith Yamasaniae47ef42012-09-16 17:53:35 -070052import android.os.UserHandle;
53import android.os.UserManager;
The Android Open Source Projectafc4ab22009-03-03 19:32:34 -080054import android.preference.Preference;
Jeff Sharkeyb654cbb2011-08-18 11:59:19 -070055import android.preference.PreferenceFrameLayout;
56import android.preference.PreferenceGroup;
Amith Yamasani8d40fac2012-10-23 15:36:16 -070057import android.provider.ContactsContract.CommonDataKinds;
Amith Yamasaniae47ef42012-09-16 17:53:35 -070058import android.provider.ContactsContract.Contacts;
Amith Yamasani8d40fac2012-10-23 15:36:16 -070059import android.provider.ContactsContract.Data;
Amith Yamasaniae47ef42012-09-16 17:53:35 -070060import android.provider.ContactsContract.Profile;
Amith Yamasani8d40fac2012-10-23 15:36:16 -070061import android.provider.ContactsContract.RawContacts;
Andres Moralesce249fe2014-07-07 16:58:16 -070062import android.service.persistentdata.PersistentDataBlockManager;
Amith Yamasani60133dd2010-09-11 14:17:31 -070063import android.telephony.TelephonyManager;
Anders Hammar1b2dd9032010-04-08 10:03:50 +020064import android.text.TextUtils;
Alexandra Gherghina1eb3f312014-06-10 14:01:10 +010065import android.util.Log;
Jeff Sharkeyb654cbb2011-08-18 11:59:19 -070066import android.view.View;
67import android.view.ViewGroup;
68import android.widget.ListView;
69import android.widget.TabWidget;
Alexandra Gherghina7d748c02014-06-27 12:33:42 +010070
Alexandra Gherghinabc6e78f2014-09-03 10:22:09 +010071import com.android.internal.util.UserIcons;
Alexandra Gherghina80e1f1b2014-07-31 14:56:33 +010072import com.android.settings.UserSpinnerAdapter.UserDetails;
Fabrice Di Meglio63bbb8e2014-04-23 16:44:30 -070073import com.android.settings.dashboard.DashboardTile;
Alexandra Gherghinafe47a8d2014-07-18 17:23:37 +010074import com.android.settings.drawable.CircleFramedDrawable;
Daisuke Miyakawaa2633d02010-09-15 20:09:12 -070075
Amith Yamasaniae47ef42012-09-16 17:53:35 -070076import java.io.IOException;
77import java.io.InputStream;
Amith Yamasanic06d4c42011-02-25 14:35:20 -080078import java.net.InetAddress;
Elliott Hughes7253df32014-09-02 17:10:14 -070079import java.text.NumberFormat;
Alexandra Gherghina80e1f1b2014-07-31 14:56:33 +010080import java.util.ArrayList;
Amith Yamasanic06d4c42011-02-25 14:35:20 -080081import java.util.Iterator;
Daisuke Miyakawaa2633d02010-09-15 20:09:12 -070082import java.util.List;
Jean Chalard71ad1f42011-05-12 15:06:16 +090083import java.util.Locale;
The Android Open Source Projectafc4ab22009-03-03 19:32:34 -080084
Fabrice Di Meglioc9711be2014-06-17 12:30:18 -070085public final class Utils {
Alexandra Gherghina1eb3f312014-06-10 14:01:10 +010086 private static final String TAG = "Settings";
Alexandra Gherghina7d748c02014-06-27 12:33:42 +010087
The Android Open Source Projectafc4ab22009-03-03 19:32:34 -080088 /**
89 * Set the preference's title to the matching activity's label.
90 */
91 public static final int UPDATE_PREFERENCE_FLAG_SET_TITLE_TO_MATCHING_ACTIVITY = 1;
92
93 /**
Shuhrat Dehkanov96577682012-10-03 12:24:07 +090094 * The opacity level of a disabled icon.
95 */
96 public static final float DISABLED_ALPHA = 0.4f;
97
98 /**
Dianne Hackborn68f005f2014-06-18 18:29:12 -070099 * Color spectrum to use to indicate badness. 0 is completely transparent (no data),
100 * 1 is most bad (red), the last value is least bad (green).
101 */
102 public static final int[] BADNESS_COLORS = new int[] {
103 0x00000000, 0xffc43828, 0xffe54918, 0xfff47b00,
104 0xfffabf2c, 0xff679e37, 0xff0a7f42
105 };
106
107 /**
Anders Hammar1b2dd9032010-04-08 10:03:50 +0200108 * Name of the meta-data item that should be set in the AndroidManifest.xml
109 * to specify the icon that should be displayed for the preference.
110 */
111 private static final String META_DATA_PREFERENCE_ICON = "com.android.settings.icon";
112
113 /**
114 * Name of the meta-data item that should be set in the AndroidManifest.xml
115 * to specify the title that should be displayed for the preference.
116 */
117 private static final String META_DATA_PREFERENCE_TITLE = "com.android.settings.title";
118
119 /**
120 * Name of the meta-data item that should be set in the AndroidManifest.xml
121 * to specify the summary text that should be displayed for the preference.
122 */
123 private static final String META_DATA_PREFERENCE_SUMMARY = "com.android.settings.summary";
124
Alexandra Gherghina7d748c02014-06-27 12:33:42 +0100125 private static final String SETTINGS_PACKAGE_NAME = "com.android.settings";
126
Elliott Hughes7253df32014-09-02 17:10:14 -0700127 private static final int SECONDS_PER_MINUTE = 60;
128 private static final int SECONDS_PER_HOUR = 60 * 60;
129 private static final int SECONDS_PER_DAY = 24 * 60 * 60;
130
Anders Hammar1b2dd9032010-04-08 10:03:50 +0200131 /**
The Android Open Source Projectafc4ab22009-03-03 19:32:34 -0800132 * Finds a matching activity for a preference's intent. If a matching
133 * activity is not found, it will remove the preference.
Ying Wanga7188322010-01-04 18:45:10 -0800134 *
The Android Open Source Projectafc4ab22009-03-03 19:32:34 -0800135 * @param context The context.
136 * @param parentPreferenceGroup The preference group that contains the
137 * preference whose intent is being resolved.
138 * @param preferenceKey The key of the preference whose intent is being
139 * resolved.
140 * @param flags 0 or one or more of
141 * {@link #UPDATE_PREFERENCE_FLAG_SET_TITLE_TO_MATCHING_ACTIVITY}
142 * .
143 * @return Whether an activity was found. If false, the preference was
144 * removed.
145 */
146 public static boolean updatePreferenceToSpecificActivityOrRemove(Context context,
147 PreferenceGroup parentPreferenceGroup, String preferenceKey, int flags) {
Ying Wanga7188322010-01-04 18:45:10 -0800148
The Android Open Source Projectafc4ab22009-03-03 19:32:34 -0800149 Preference preference = parentPreferenceGroup.findPreference(preferenceKey);
150 if (preference == null) {
151 return false;
152 }
Ying Wanga7188322010-01-04 18:45:10 -0800153
The Android Open Source Projectafc4ab22009-03-03 19:32:34 -0800154 Intent intent = preference.getIntent();
155 if (intent != null) {
156 // Find the activity that is in the system image
157 PackageManager pm = context.getPackageManager();
158 List<ResolveInfo> list = pm.queryIntentActivities(intent, 0);
159 int listSize = list.size();
160 for (int i = 0; i < listSize; i++) {
161 ResolveInfo resolveInfo = list.get(i);
162 if ((resolveInfo.activityInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM)
163 != 0) {
Ying Wanga7188322010-01-04 18:45:10 -0800164
The Android Open Source Projectafc4ab22009-03-03 19:32:34 -0800165 // Replace the intent with this specific activity
166 preference.setIntent(new Intent().setClassName(
167 resolveInfo.activityInfo.packageName,
168 resolveInfo.activityInfo.name));
169
170 if ((flags & UPDATE_PREFERENCE_FLAG_SET_TITLE_TO_MATCHING_ACTIVITY) != 0) {
171 // Set the preference title to the activity's label
172 preference.setTitle(resolveInfo.loadLabel(pm));
173 }
Ying Wanga7188322010-01-04 18:45:10 -0800174
The Android Open Source Projectafc4ab22009-03-03 19:32:34 -0800175 return true;
176 }
177 }
178 }
179
180 // Did not find a matching activity, so remove the preference
181 parentPreferenceGroup.removePreference(preference);
Ying Wanga7188322010-01-04 18:45:10 -0800182
Shuhrat Dehkanov7dc567a2012-04-23 01:59:56 +0900183 return false;
The Android Open Source Projectafc4ab22009-03-03 19:32:34 -0800184 }
Ying Wanga7188322010-01-04 18:45:10 -0800185
Fabrice Di Meglio63bbb8e2014-04-23 16:44:30 -0700186 public static boolean updateTileToSpecificActivityFromMetaDataOrRemove(Context context,
Amith Yamasani57fd5fd2014-08-06 15:48:10 -0700187 DashboardTile tile) {
Fabrice Di Meglio63bbb8e2014-04-23 16:44:30 -0700188
189 Intent intent = tile.intent;
190 if (intent != null) {
191 // Find the activity that is in the system image
192 PackageManager pm = context.getPackageManager();
193 List<ResolveInfo> list = pm.queryIntentActivities(intent, PackageManager.GET_META_DATA);
194 int listSize = list.size();
195 for (int i = 0; i < listSize; i++) {
196 ResolveInfo resolveInfo = list.get(i);
197 if ((resolveInfo.activityInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM)
198 != 0) {
199 Drawable icon = null;
200 String title = null;
201 String summary = null;
202
203 // Get the activity's meta-data
204 try {
205 Resources res = pm.getResourcesForApplication(
206 resolveInfo.activityInfo.packageName);
207 Bundle metaData = resolveInfo.activityInfo.metaData;
208
209 if (res != null && metaData != null) {
Alan Viverette0ba89bd2014-10-10 10:58:58 -0700210 icon = res.getDrawable(
211 metaData.getInt(META_DATA_PREFERENCE_ICON), null);
Fabrice Di Meglio63bbb8e2014-04-23 16:44:30 -0700212 title = res.getString(metaData.getInt(META_DATA_PREFERENCE_TITLE));
213 summary = res.getString(metaData.getInt(META_DATA_PREFERENCE_SUMMARY));
214 }
215 } catch (NameNotFoundException e) {
216 // Ignore
217 } catch (NotFoundException e) {
218 // Ignore
219 }
220
221 // Set the preference title to the activity's label if no
222 // meta-data is found
223 if (TextUtils.isEmpty(title)) {
224 title = resolveInfo.loadLabel(pm).toString();
225 }
226
227 // Set icon, title and summary for the preference
228 // TODO:
229 //tile.icon = icon;
230 tile.title = title;
231 tile.summary = summary;
232 // Replace the intent with this specific activity
233 tile.intent = new Intent().setClassName(resolveInfo.activityInfo.packageName,
234 resolveInfo.activityInfo.name);
235
236 return true;
237 }
238 }
239 }
240
Fabrice Di Meglio63bbb8e2014-04-23 16:44:30 -0700241 return false;
242 }
243
Anders Hammar1b2dd9032010-04-08 10:03:50 +0200244 /**
Ying Wanga7188322010-01-04 18:45:10 -0800245 * Returns true if Monkey is running.
246 */
247 public static boolean isMonkeyRunning() {
Amith Yamasaniae697552011-09-27 11:33:17 -0700248 return ActivityManager.isUserAMonkey();
Ying Wanga7188322010-01-04 18:45:10 -0800249 }
Amith Yamasani60133dd2010-09-11 14:17:31 -0700250
251 /**
252 * Returns whether the device is voice-capable (meaning, it is also a phone).
253 */
254 public static boolean isVoiceCapable(Context context) {
255 TelephonyManager telephony =
256 (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
257 return telephony != null && telephony.isVoiceCapable();
258 }
Amith Yamasani0f85c482011-02-23 17:19:11 -0800259
Robert Greenwalt8af88fb2011-08-31 11:17:47 -0700260 public static boolean isWifiOnly(Context context) {
261 ConnectivityManager cm = (ConnectivityManager)context.getSystemService(
262 Context.CONNECTIVITY_SERVICE);
263 return (cm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE) == false);
Amith Yamasani0f85c482011-02-23 17:19:11 -0800264 }
Amith Yamasanic06d4c42011-02-25 14:35:20 -0800265
266 /**
267 * Returns the WIFI IP Addresses, if any, taking into account IPv4 and IPv6 style addresses.
268 * @param context the application context
Lorenzo Colitti769f0692013-08-01 17:30:07 +0900269 * @return the formatted and newline-separated IP addresses, or null if none.
Amith Yamasanic06d4c42011-02-25 14:35:20 -0800270 */
271 public static String getWifiIpAddresses(Context context) {
272 ConnectivityManager cm = (ConnectivityManager)
273 context.getSystemService(Context.CONNECTIVITY_SERVICE);
274 LinkProperties prop = cm.getLinkProperties(ConnectivityManager.TYPE_WIFI);
Amith Yamasani6822b742011-10-17 16:41:00 -0700275 return formatIpAddresses(prop);
276 }
277
278 /**
279 * Returns the default link's IP addresses, if any, taking into account IPv4 and IPv6 style
280 * addresses.
281 * @param context the application context
Lorenzo Colitti769f0692013-08-01 17:30:07 +0900282 * @return the formatted and newline-separated IP addresses, or null if none.
Amith Yamasani6822b742011-10-17 16:41:00 -0700283 */
Lorenzo Colitti6eb6a902013-11-08 03:53:29 +0900284 public static String getDefaultIpAddresses(ConnectivityManager cm) {
Amith Yamasani6822b742011-10-17 16:41:00 -0700285 LinkProperties prop = cm.getActiveLinkProperties();
286 return formatIpAddresses(prop);
287 }
288
289 private static String formatIpAddresses(LinkProperties prop) {
Amith Yamasanic06d4c42011-02-25 14:35:20 -0800290 if (prop == null) return null;
Lorenzo Colitti769f0692013-08-01 17:30:07 +0900291 Iterator<InetAddress> iter = prop.getAllAddresses().iterator();
Amith Yamasanic06d4c42011-02-25 14:35:20 -0800292 // If there are no entries, return null
293 if (!iter.hasNext()) return null;
294 // Concatenate all available addresses, comma separated
295 String addresses = "";
296 while (iter.hasNext()) {
297 addresses += iter.next().getHostAddress();
Lorenzo Colitti769f0692013-08-01 17:30:07 +0900298 if (iter.hasNext()) addresses += "\n";
Amith Yamasanic06d4c42011-02-25 14:35:20 -0800299 }
300 return addresses;
301 }
Jean Chalard71ad1f42011-05-12 15:06:16 +0900302
303 public static Locale createLocaleFromString(String localeStr) {
304 // TODO: is there a better way to actually construct a locale that will match?
305 // The main problem is, on top of Java specs, locale.toString() and
306 // new Locale(locale.toString()).toString() do not return equal() strings in
307 // many cases, because the constructor takes the only string as the language
308 // code. So : new Locale("en", "US").toString() => "en_US"
309 // And : new Locale("en_US").toString() => "en_us"
310 if (null == localeStr)
311 return Locale.getDefault();
312 String[] brokenDownLocale = localeStr.split("_", 3);
313 // split may not return a 0-length array.
314 if (1 == brokenDownLocale.length) {
315 return new Locale(brokenDownLocale[0]);
316 } else if (2 == brokenDownLocale.length) {
317 return new Locale(brokenDownLocale[0], brokenDownLocale[1]);
318 } else {
319 return new Locale(brokenDownLocale[0], brokenDownLocale[1], brokenDownLocale[2]);
320 }
321 }
Amith Yamasania4379d62011-07-22 10:34:58 -0700322
Elliott Hughes7253df32014-09-02 17:10:14 -0700323 /** Formats the ratio of amount/total as a percentage. */
324 public static String formatPercentage(long amount, long total) {
325 return formatPercentage(((double) amount) / total);
326 }
327
328 /** Formats an integer from 0..100 as a percentage. */
329 public static String formatPercentage(int percentage) {
330 return formatPercentage(((double) percentage) / 100.0);
331 }
332
333 /** Formats a double from 0.0..1.0 as a percentage. */
334 private static String formatPercentage(double percentage) {
Elliott Hughes83f013c2014-10-02 16:36:32 -0700335 return NumberFormat.getPercentInstance().format(percentage);
Elliott Hughes7253df32014-09-02 17:10:14 -0700336 }
337
Jaewan Kima3fe77b2013-06-04 21:17:40 +0900338 public static boolean isBatteryPresent(Intent batteryChangedIntent) {
339 return batteryChangedIntent.getBooleanExtra(BatteryManager.EXTRA_PRESENT, true);
340 }
341
Amith Yamasania4379d62011-07-22 10:34:58 -0700342 public static String getBatteryPercentage(Intent batteryChangedIntent) {
Elliott Hughes7253df32014-09-02 17:10:14 -0700343 return formatPercentage(getBatteryLevel(batteryChangedIntent));
Dianne Hackborn525f2bd2014-04-29 11:24:06 -0700344 }
345
346 public static int getBatteryLevel(Intent batteryChangedIntent) {
Jaewan Kima3fe77b2013-06-04 21:17:40 +0900347 int level = batteryChangedIntent.getIntExtra(BatteryManager.EXTRA_LEVEL, 0);
348 int scale = batteryChangedIntent.getIntExtra(BatteryManager.EXTRA_SCALE, 100);
Dianne Hackborn525f2bd2014-04-29 11:24:06 -0700349 return (level * 100) / scale;
Amith Yamasania4379d62011-07-22 10:34:58 -0700350 }
351
352 public static String getBatteryStatus(Resources res, Intent batteryChangedIntent) {
353 final Intent intent = batteryChangedIntent;
354
Jaewan Kima3fe77b2013-06-04 21:17:40 +0900355 int plugType = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0);
356 int status = intent.getIntExtra(BatteryManager.EXTRA_STATUS,
357 BatteryManager.BATTERY_STATUS_UNKNOWN);
Amith Yamasania4379d62011-07-22 10:34:58 -0700358 String statusString;
359 if (status == BatteryManager.BATTERY_STATUS_CHARGING) {
Dianne Hackborn5a9ace32014-05-15 17:04:42 -0700360 int resId;
361 if (plugType == BatteryManager.BATTERY_PLUGGED_AC) {
362 resId = R.string.battery_info_status_charging_ac;
363 } else if (plugType == BatteryManager.BATTERY_PLUGGED_USB) {
364 resId = R.string.battery_info_status_charging_usb;
365 } else if (plugType == BatteryManager.BATTERY_PLUGGED_WIRELESS) {
366 resId = R.string.battery_info_status_charging_wireless;
367 } else {
368 resId = R.string.battery_info_status_charging;
Amith Yamasania4379d62011-07-22 10:34:58 -0700369 }
Dianne Hackborn5a9ace32014-05-15 17:04:42 -0700370 statusString = res.getString(resId);
Amith Yamasania4379d62011-07-22 10:34:58 -0700371 } else if (status == BatteryManager.BATTERY_STATUS_DISCHARGING) {
372 statusString = res.getString(R.string.battery_info_status_discharging);
373 } else if (status == BatteryManager.BATTERY_STATUS_NOT_CHARGING) {
374 statusString = res.getString(R.string.battery_info_status_not_charging);
375 } else if (status == BatteryManager.BATTERY_STATUS_FULL) {
376 statusString = res.getString(R.string.battery_info_status_full);
377 } else {
378 statusString = res.getString(R.string.battery_info_status_unknown);
379 }
380
381 return statusString;
382 }
Jeff Sharkeyb654cbb2011-08-18 11:59:19 -0700383
Jeff Sharkey97d07fa2012-11-30 12:36:53 -0800384 public static void forcePrepareCustomPreferencesList(
385 ViewGroup parent, View child, ListView list, boolean ignoreSidePadding) {
386 list.setScrollBarStyle(View.SCROLLBARS_OUTSIDE_OVERLAY);
387 list.setClipToPadding(false);
388 prepareCustomPreferencesList(parent, child, list, ignoreSidePadding);
389 }
390
Jeff Sharkeyb654cbb2011-08-18 11:59:19 -0700391 /**
392 * Prepare a custom preferences layout, moving padding to {@link ListView}
393 * when outside scrollbars are requested. Usually used to display
394 * {@link ListView} and {@link TabWidget} with correct padding.
395 */
Jeff Sharkey5d706792011-09-08 18:57:17 -0700396 public static void prepareCustomPreferencesList(
Jeff Sharkey97d07fa2012-11-30 12:36:53 -0800397 ViewGroup parent, View child, View list, boolean ignoreSidePadding) {
Jeff Sharkeyb654cbb2011-08-18 11:59:19 -0700398 final boolean movePadding = list.getScrollBarStyle() == View.SCROLLBARS_OUTSIDE_OVERLAY;
Fabrice Di Meglio97a18c82014-07-18 19:12:36 -0700399 if (movePadding) {
Jeff Sharkeyb654cbb2011-08-18 11:59:19 -0700400 final Resources res = list.getResources();
Amith Yamasani56f51a82013-08-05 10:07:23 -0700401 final int paddingSide = res.getDimensionPixelSize(R.dimen.settings_side_margin);
Jeff Sharkeyb654cbb2011-08-18 11:59:19 -0700402 final int paddingBottom = res.getDimensionPixelSize(
403 com.android.internal.R.dimen.preference_fragment_padding_bottom);
Jeff Sharkey5d706792011-09-08 18:57:17 -0700404
Fabrice Di Meglio97a18c82014-07-18 19:12:36 -0700405 if (parent instanceof PreferenceFrameLayout) {
406 ((PreferenceFrameLayout.LayoutParams) child.getLayoutParams()).removeBorders = true;
407
408 final int effectivePaddingSide = ignoreSidePadding ? 0 : paddingSide;
409 list.setPaddingRelative(effectivePaddingSide, 0, effectivePaddingSide, paddingBottom);
410 } else {
411 list.setPaddingRelative(paddingSide, 0, paddingSide, paddingBottom);
412 }
Jeff Sharkeyb654cbb2011-08-18 11:59:19 -0700413 }
414 }
Jeff Sharkeya83a24f2011-09-16 01:52:39 -0700415
Fabrice Di Meglio0f4a7792014-07-28 18:25:14 -0700416 public static void forceCustomPadding(View view, boolean additive) {
Fabrice Di Meglio38ba9a22014-07-18 19:58:50 -0700417 final Resources res = view.getResources();
418 final int paddingSide = res.getDimensionPixelSize(R.dimen.settings_side_margin);
Fabrice Di Meglio0f4a7792014-07-28 18:25:14 -0700419
420 final int paddingStart = paddingSide + (additive ? view.getPaddingStart() : 0);
421 final int paddingEnd = paddingSide + (additive ? view.getPaddingEnd() : 0);
Fabrice Di Meglio38ba9a22014-07-18 19:58:50 -0700422 final int paddingBottom = res.getDimensionPixelSize(
423 com.android.internal.R.dimen.preference_fragment_padding_bottom);
424
Fabrice Di Meglio0f4a7792014-07-28 18:25:14 -0700425 view.setPaddingRelative(paddingStart, 0, paddingEnd, paddingBottom);
Fabrice Di Meglio38ba9a22014-07-18 19:58:50 -0700426 }
427
Jeff Sharkeya83a24f2011-09-16 01:52:39 -0700428 /**
429 * Return string resource that best describes combination of tethering
430 * options available on this device.
431 */
432 public static int getTetheringLabel(ConnectivityManager cm) {
433 String[] usbRegexs = cm.getTetherableUsbRegexs();
434 String[] wifiRegexs = cm.getTetherableWifiRegexs();
435 String[] bluetoothRegexs = cm.getTetherableBluetoothRegexs();
436
437 boolean usbAvailable = usbRegexs.length != 0;
438 boolean wifiAvailable = wifiRegexs.length != 0;
439 boolean bluetoothAvailable = bluetoothRegexs.length != 0;
440
441 if (wifiAvailable && usbAvailable && bluetoothAvailable) {
442 return R.string.tether_settings_title_all;
443 } else if (wifiAvailable && usbAvailable) {
444 return R.string.tether_settings_title_all;
445 } else if (wifiAvailable && bluetoothAvailable) {
446 return R.string.tether_settings_title_all;
447 } else if (wifiAvailable) {
448 return R.string.tether_settings_title_wifi;
449 } else if (usbAvailable && bluetoothAvailable) {
450 return R.string.tether_settings_title_usb_bluetooth;
451 } else if (usbAvailable) {
452 return R.string.tether_settings_title_usb;
453 } else {
454 return R.string.tether_settings_title_bluetooth;
455 }
456 }
Amith Yamasaniae47ef42012-09-16 17:53:35 -0700457
458 /* Used by UserSettings as well. Call this on a non-ui thread. */
459 public static boolean copyMeProfilePhoto(Context context, UserInfo user) {
460 Uri contactUri = Profile.CONTENT_URI;
461
462 InputStream avatarDataStream = Contacts.openContactPhotoInputStream(
463 context.getContentResolver(),
464 contactUri, true);
465 // If there's no profile photo, assign a default avatar
466 if (avatarDataStream == null) {
467 return false;
468 }
469 int userId = user != null ? user.id : UserHandle.myUserId();
470 UserManager um = (UserManager) context.getSystemService(Context.USER_SERVICE);
Amith Yamasanif34a85d2012-09-17 18:31:45 -0700471 Bitmap icon = BitmapFactory.decodeStream(avatarDataStream);
472 um.setUserIcon(userId, icon);
Amith Yamasaniae47ef42012-09-16 17:53:35 -0700473 try {
Amith Yamasanif34a85d2012-09-17 18:31:45 -0700474 avatarDataStream.close();
475 } catch (IOException ioe) { }
476 return true;
Amith Yamasaniae47ef42012-09-16 17:53:35 -0700477 }
478
Amith Yamasani8d40fac2012-10-23 15:36:16 -0700479 public static String getMeProfileName(Context context, boolean full) {
480 if (full) {
481 return getProfileDisplayName(context);
482 } else {
483 return getShorterNameIfPossible(context);
Amith Yamasaniae47ef42012-09-16 17:53:35 -0700484 }
Amith Yamasani8d40fac2012-10-23 15:36:16 -0700485 }
486
487 private static String getShorterNameIfPossible(Context context) {
488 final String given = getLocalProfileGivenName(context);
489 return !TextUtils.isEmpty(given) ? given : getProfileDisplayName(context);
490 }
491
492 private static String getLocalProfileGivenName(Context context) {
493 final ContentResolver cr = context.getContentResolver();
494
495 // Find the raw contact ID for the local ME profile raw contact.
496 final long localRowProfileId;
497 final Cursor localRawProfile = cr.query(
498 Profile.CONTENT_RAW_CONTACTS_URI,
499 new String[] {RawContacts._ID},
500 RawContacts.ACCOUNT_TYPE + " IS NULL AND " +
501 RawContacts.ACCOUNT_NAME + " IS NULL",
502 null, null);
503 if (localRawProfile == null) return null;
Amith Yamasaniae47ef42012-09-16 17:53:35 -0700504
505 try {
Amith Yamasani8d40fac2012-10-23 15:36:16 -0700506 if (!localRawProfile.moveToFirst()) {
507 return null;
Amith Yamasaniae47ef42012-09-16 17:53:35 -0700508 }
Amith Yamasani8d40fac2012-10-23 15:36:16 -0700509 localRowProfileId = localRawProfile.getLong(0);
Amith Yamasaniae47ef42012-09-16 17:53:35 -0700510 } finally {
Amith Yamasani8d40fac2012-10-23 15:36:16 -0700511 localRawProfile.close();
Amith Yamasaniae47ef42012-09-16 17:53:35 -0700512 }
Amith Yamasani8d40fac2012-10-23 15:36:16 -0700513
514 // Find the structured name for the raw contact.
515 final Cursor structuredName = cr.query(
516 Profile.CONTENT_URI.buildUpon().appendPath(Contacts.Data.CONTENT_DIRECTORY).build(),
517 new String[] {CommonDataKinds.StructuredName.GIVEN_NAME,
518 CommonDataKinds.StructuredName.FAMILY_NAME},
519 Data.RAW_CONTACT_ID + "=" + localRowProfileId,
520 null, null);
521 if (structuredName == null) return null;
522
523 try {
524 if (!structuredName.moveToFirst()) {
525 return null;
526 }
527 String partialName = structuredName.getString(0);
528 if (TextUtils.isEmpty(partialName)) {
529 partialName = structuredName.getString(1);
530 }
531 return partialName;
532 } finally {
533 structuredName.close();
534 }
535 }
536
537 private static final String getProfileDisplayName(Context context) {
538 final ContentResolver cr = context.getContentResolver();
539 final Cursor profile = cr.query(Profile.CONTENT_URI,
540 new String[] {Profile.DISPLAY_NAME}, null, null, null);
541 if (profile == null) return null;
542
543 try {
544 if (!profile.moveToFirst()) {
545 return null;
546 }
547 return profile.getString(0);
548 } finally {
549 profile.close();
550 }
Amith Yamasaniae47ef42012-09-16 17:53:35 -0700551 }
Amith Yamasani9627a8e2012-09-23 12:54:14 -0700552
553 /** Not global warming, it's global change warning. */
554 public static Dialog buildGlobalChangeWarningDialog(final Context context, int titleResId,
555 final Runnable positiveAction) {
556 final AlertDialog.Builder builder = new AlertDialog.Builder(context);
557 builder.setTitle(titleResId);
558 builder.setMessage(R.string.global_change_warning);
559 builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
560 @Override
561 public void onClick(DialogInterface dialog, int which) {
562 positiveAction.run();
563 }
564 });
565 builder.setNegativeButton(android.R.string.cancel, null);
566
567 return builder.create();
568 }
569
570 public static boolean hasMultipleUsers(Context context) {
571 return ((UserManager) context.getSystemService(Context.USER_SERVICE))
572 .getUsers().size() > 1;
573 }
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700574
575 /**
576 * Start a new instance of the activity, showing only the given fragment.
577 * When launched in this mode, the given preference fragment will be instantiated and fill the
578 * entire activity.
579 *
580 * @param context The context.
Fabrice Di Meglio93b77b72014-05-17 00:01:07 +0000581 * @param fragmentName The name of the fragment to display.
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700582 * @param args Optional arguments to supply to the fragment.
Fabrice Di Meglioa9e77992014-06-09 12:52:24 -0700583 * @param resultTo Option fragment that should receive the result of the activity launch.
584 * @param resultRequestCode If resultTo is non-null, this is the request code in which
585 * to report the result.
586 * @param titleResId resource id for the String to display for the title of this set
587 * of preferences.
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700588 * @param title String to display for the title of this set of preferences.
589 */
Fabrice Di Meglio93b77b72014-05-17 00:01:07 +0000590 public static void startWithFragment(Context context, String fragmentName, Bundle args,
Fabrice Di Meglioa9e77992014-06-09 12:52:24 -0700591 Fragment resultTo, int resultRequestCode, int titleResId, CharSequence title) {
Fabrice Di Meglio0d643fd2014-06-16 20:11:27 -0700592 startWithFragment(context, fragmentName, args, resultTo, resultRequestCode,
593 titleResId, title, false /* not a shortcut */);
594 }
595
596 public static void startWithFragment(Context context, String fragmentName, Bundle args,
597 Fragment resultTo, int resultRequestCode, int titleResId, CharSequence title,
598 boolean isShortcut) {
599 Intent intent = onBuildStartFragmentIntent(context, fragmentName, args, titleResId,
600 title, isShortcut);
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700601 if (resultTo == null) {
602 context.startActivity(intent);
603 } else {
604 resultTo.startActivityForResult(intent, resultRequestCode);
605 }
606 }
607
Zoltan Szatmary-Ban7a2ccf22014-09-18 10:26:11 +0100608 public static void startWithFragmentAsUser(Context context, String fragmentName, Bundle args,
609 int titleResId, CharSequence title, boolean isShortcut, UserHandle userHandle) {
610 Intent intent = onBuildStartFragmentIntent(context, fragmentName, args, titleResId,
611 title, isShortcut);
Zoltan Szatmary-Band50c7a82014-09-22 17:14:08 +0100612 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
613 intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
Zoltan Szatmary-Ban7a2ccf22014-09-18 10:26:11 +0100614 context.startActivityAsUser(intent, userHandle);
615 }
616
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700617 /**
618 * Build an Intent to launch a new activity showing the selected fragment.
619 * The implementation constructs an Intent that re-launches the current activity with the
620 * appropriate arguments to display the fragment.
621 *
Fabrice Di Meglio0d643fd2014-06-16 20:11:27 -0700622 *
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700623 * @param context The Context.
Fabrice Di Meglio93b77b72014-05-17 00:01:07 +0000624 * @param fragmentName The name of the fragment to display.
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700625 * @param args Optional arguments to supply to the fragment.
Fabrice Di Meglioa9e77992014-06-09 12:52:24 -0700626 * @param titleResId Optional title resource id to show for this item.
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700627 * @param title Optional title to show for this item.
Fabrice Di Meglio0d643fd2014-06-16 20:11:27 -0700628 * @param isShortcut tell if this is a Launcher Shortcut or not
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700629 * @return Returns an Intent that can be launched to display the given
630 * fragment.
631 */
Fabrice Di Meglio93b77b72014-05-17 00:01:07 +0000632 public static Intent onBuildStartFragmentIntent(Context context, String fragmentName,
Fabrice Di Meglio0d643fd2014-06-16 20:11:27 -0700633 Bundle args, int titleResId, CharSequence title, boolean isShortcut) {
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700634 Intent intent = new Intent(Intent.ACTION_MAIN);
635 intent.setClass(context, SubSettings.class);
Fabrice Di Meglio93b77b72014-05-17 00:01:07 +0000636 intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT, fragmentName);
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700637 intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_ARGUMENTS, args);
Fabrice Di Meglioa9e77992014-06-09 12:52:24 -0700638 intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_TITLE_RESID, titleResId);
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700639 intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_TITLE, title);
Fabrice Di Meglio0d643fd2014-06-16 20:11:27 -0700640 intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_AS_SHORTCUT, isShortcut);
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700641 return intent;
642 }
Alexandra Gherghina3939cd72014-06-04 10:02:55 +0100643
644 /**
645 * Returns the managed profile of the current user or null if none found.
646 */
647 public static UserHandle getManagedProfile(UserManager userManager) {
648 List<UserHandle> userProfiles = userManager.getUserProfiles();
649 final int count = userProfiles.size();
650 for (int i = 0; i < count; i++) {
651 final UserHandle profile = userProfiles.get(i);
652 if (profile.getIdentifier() == userManager.getUserHandle()) {
653 continue;
654 }
655 final UserInfo userInfo = userManager.getUserInfo(profile.getIdentifier());
656 if (userInfo.isManagedProfile()) {
657 return profile;
658 }
659 }
660 return null;
661 }
662
663 /**
664 * Returns true if the current profile is a managed one.
665 */
666 public static boolean isManagedProfile(UserManager userManager) {
667 UserInfo currentUser = userManager.getUserInfo(userManager.getUserHandle());
668 return currentUser.isManagedProfile();
669 }
Alexandra Gherghina1eb3f312014-06-10 14:01:10 +0100670
671 /**
Alexandra Gherghina80e1f1b2014-07-31 14:56:33 +0100672 * Creates a {@link UserSpinnerAdapter} if there is more than one profile on the device.
673 *
674 * <p> The adapter can be used to populate a spinner that switches between the Settings
675 * app on the different profiles.
676 *
677 * @return a {@link UserSpinnerAdapter} or null if there is only one profile.
678 */
679 public static UserSpinnerAdapter createUserSpinnerAdapter(UserManager userManager,
680 Context context) {
681 List<UserHandle> userProfiles = userManager.getUserProfiles();
682 if (userProfiles.size() < 2) {
683 return null;
684 }
685
686 UserHandle myUserHandle = new UserHandle(UserHandle.myUserId());
687 // The first option should be the current profile
688 userProfiles.remove(myUserHandle);
689 userProfiles.add(0, myUserHandle);
690
691 ArrayList<UserDetails> userDetails = new ArrayList<UserDetails>(userProfiles.size());
692 final int count = userProfiles.size();
693 for (int i = 0; i < count; i++) {
694 userDetails.add(new UserDetails(userProfiles.get(i), userManager, context));
695 }
696 return new UserSpinnerAdapter(context, userDetails);
697 }
698
699 /**
Alexandra Gherghina7d748c02014-06-27 12:33:42 +0100700 * Returns the target user for a Settings activity.
Alexandra Gherghina1eb3f312014-06-10 14:01:10 +0100701 *
Alexandra Gherghina7d748c02014-06-27 12:33:42 +0100702 * The target user can be either the current user, the user that launched this activity or
703 * the user contained as an extra in the arguments or intent extras.
704 *
705 * Note: This is secure in the sense that it only returns a target user different to the current
706 * one if the app launching this activity is the Settings app itself, running in the same user
707 * 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 +0100708 */
Alexandra Gherghina7d748c02014-06-27 12:33:42 +0100709 public static UserHandle getSecureTargetUser(IBinder activityToken,
710 UserManager um, @Nullable Bundle arguments, @Nullable Bundle intentExtras) {
711 UserHandle currentUser = new UserHandle(UserHandle.myUserId());
712 IActivityManager am = ActivityManagerNative.getDefault();
713 try {
714 String launchedFromPackage = am.getLaunchedFromPackage(activityToken);
715 boolean launchedFromSettingsApp = SETTINGS_PACKAGE_NAME.equals(launchedFromPackage);
716
717 UserHandle launchedFromUser = new UserHandle(UserHandle.getUserId(
718 am.getLaunchedFromUid(activityToken)));
719 if (launchedFromUser != null && !launchedFromUser.equals(currentUser)) {
720 // Check it's secure
721 if (isProfileOf(um, launchedFromUser)) {
722 return launchedFromUser;
723 }
724 }
725 UserHandle extrasUser = intentExtras != null
726 ? (UserHandle) intentExtras.getParcelable(EXTRA_USER) : null;
727 if (extrasUser != null && !extrasUser.equals(currentUser)) {
728 // Check it's secure
729 if (launchedFromSettingsApp && isProfileOf(um, extrasUser)) {
730 return extrasUser;
731 }
732 }
733 UserHandle argumentsUser = arguments != null
734 ? (UserHandle) arguments.getParcelable(EXTRA_USER) : null;
735 if (argumentsUser != null && !argumentsUser.equals(currentUser)) {
736 // Check it's secure
737 if (launchedFromSettingsApp && isProfileOf(um, argumentsUser)) {
738 return argumentsUser;
739 }
740 }
741 } catch (RemoteException e) {
742 // Should not happen
743 Log.v(TAG, "Could not talk to activity manager.", e);
744 }
745 return currentUser;
746 }
747
748 /**
749 * Returns the target user for a Settings activity.
750 *
751 * The target user can be either the current user, the user that launched this activity or
752 * the user contained as an extra in the arguments or intent extras.
753 *
754 * You should use {@link #getSecureTargetUser(IBinder, UserManager, Bundle, Bundle)} if
755 * possible.
756 *
757 * @see #getInsecureTargetUser(IBinder, Bundle, Bundle)
758 */
759 public static UserHandle getInsecureTargetUser(IBinder activityToken, @Nullable Bundle arguments,
760 @Nullable Bundle intentExtras) {
761 UserHandle currentUser = new UserHandle(UserHandle.myUserId());
762 IActivityManager am = ActivityManagerNative.getDefault();
Alexandra Gherghina1eb3f312014-06-10 14:01:10 +0100763 try {
Alexandra Gherghina7d748c02014-06-27 12:33:42 +0100764 UserHandle launchedFromUser = new UserHandle(UserHandle.getUserId(
765 am.getLaunchedFromUid(activityToken)));
766 if (launchedFromUser != null && !launchedFromUser.equals(currentUser)) {
767 return launchedFromUser;
768 }
769 UserHandle extrasUser = intentExtras != null
770 ? (UserHandle) intentExtras.getParcelable(EXTRA_USER) : null;
771 if (extrasUser != null && !extrasUser.equals(currentUser)) {
772 return extrasUser;
773 }
774 UserHandle argumentsUser = arguments != null
775 ? (UserHandle) arguments.getParcelable(EXTRA_USER) : null;
776 if (argumentsUser != null && !argumentsUser.equals(currentUser)) {
777 return argumentsUser;
Alexandra Gherghina1eb3f312014-06-10 14:01:10 +0100778 }
779 } catch (RemoteException e) {
780 // Should not happen
Alexandra Gherghina7d748c02014-06-27 12:33:42 +0100781 Log.v(TAG, "Could not talk to activity manager.", e);
782 return null;
Alexandra Gherghina1eb3f312014-06-10 14:01:10 +0100783 }
Alexandra Gherghina7d748c02014-06-27 12:33:42 +0100784 return currentUser;
785 }
Alexandra Gherghina1eb3f312014-06-10 14:01:10 +0100786
Alexandra Gherghina7d748c02014-06-27 12:33:42 +0100787 /**
788 * Returns true if the user provided is in the same profiles group as the current user.
789 */
790 private static boolean isProfileOf(UserManager um, UserHandle otherUser) {
791 if (um == null || otherUser == null) return false;
792 return (UserHandle.myUserId() == otherUser.getIdentifier())
793 || um.getUserProfiles().contains(otherUser);
Alexandra Gherghina1eb3f312014-06-10 14:01:10 +0100794 }
Amith Yamasani51c6dac2014-07-02 00:06:37 +0530795
796 /**
797 * Creates a dialog to confirm with the user if it's ok to remove the user
798 * and delete all the data.
799 *
800 * @param context a Context object
801 * @param removingUserId The userId of the user to remove
802 * @param onConfirmListener Callback object for positive action
803 * @return the created Dialog
804 */
805 public static Dialog createRemoveConfirmationDialog(Context context, int removingUserId,
806 DialogInterface.OnClickListener onConfirmListener) {
807 UserManager um = (UserManager) context.getSystemService(Context.USER_SERVICE);
808 UserInfo userInfo = um.getUserInfo(removingUserId);
Alexandra Gherghina4c962672014-08-22 11:17:29 +0100809 int titleResId;
810 int messageResId;
811 if (UserHandle.myUserId() == removingUserId) {
812 titleResId = R.string.user_confirm_remove_self_title;
813 messageResId = R.string.user_confirm_remove_self_message;
814 } else if (userInfo.isRestricted()) {
815 titleResId = R.string.user_profile_confirm_remove_title;
816 messageResId = R.string.user_profile_confirm_remove_message;
817 } else if (userInfo.isManagedProfile()) {
818 titleResId = R.string.work_profile_confirm_remove_title;
819 messageResId = R.string.work_profile_confirm_remove_message;
820 } else {
821 titleResId = R.string.user_confirm_remove_title;
822 messageResId = R.string.user_confirm_remove_message;
823 }
Amith Yamasani51c6dac2014-07-02 00:06:37 +0530824 Dialog dlg = new AlertDialog.Builder(context)
Alexandra Gherghina4c962672014-08-22 11:17:29 +0100825 .setTitle(titleResId)
826 .setMessage(messageResId)
Amith Yamasani51c6dac2014-07-02 00:06:37 +0530827 .setPositiveButton(R.string.user_delete_button,
828 onConfirmListener)
829 .setNegativeButton(android.R.string.cancel, null)
830 .create();
831 return dlg;
832 }
Andres Moralesce249fe2014-07-07 16:58:16 -0700833
834 /**
835 * Returns whether or not this device is able to be OEM unlocked.
836 */
837 static boolean isOemUnlockEnabled(Context context) {
838 PersistentDataBlockManager manager =(PersistentDataBlockManager)
839 context.getSystemService(Context.PERSISTENT_DATA_BLOCK_SERVICE);
840 return manager.getOemUnlockEnabled();
841 }
842
843 /**
844 * Allows enabling or disabling OEM unlock on this device. OEM unlocked
845 * devices allow users to flash other OSes to them.
846 */
847 static void setOemUnlockEnabled(Context context, boolean enabled) {
848 PersistentDataBlockManager manager =(PersistentDataBlockManager)
849 context.getSystemService(Context.PERSISTENT_DATA_BLOCK_SERVICE);
850 manager.setOemUnlockEnabled(enabled);
851 }
Alexandra Gherghina95b86a52014-07-24 19:13:25 +0100852
853 /**
854 * Returns a circular icon for a user.
855 */
856 public static Drawable getUserIcon(Context context, UserManager um, UserInfo user) {
Alexandra Gherghina0f5440f2014-10-30 11:56:18 +0000857 if (user.isManagedProfile()) {
858 // We use predefined values for managed profiles
859 Bitmap b = BitmapFactory.decodeResource(context.getResources(),
860 com.android.internal.R.drawable.ic_corp_icon);
861 return CircleFramedDrawable.getInstance(context, b);
862 }
Alexandra Gherghinabc6e78f2014-09-03 10:22:09 +0100863 if (user.iconPath != null) {
864 Bitmap icon = um.getUserIcon(user.id);
865 if (icon != null) {
866 return CircleFramedDrawable.getInstance(context, icon);
867 }
868 }
869 return UserIcons.getDefaultUserIcon(user.id, /* light= */ false);
Alexandra Gherghina95b86a52014-07-24 19:13:25 +0100870 }
Fabrice Di Meglio22a2a492014-08-08 12:27:57 -0700871
872 /**
Alexandra Gherghina0f5440f2014-10-30 11:56:18 +0000873 * Returns a label for the user, in the form of "User: user name" or "Work profile".
874 */
875 public static String getUserLabel(Context context, UserInfo info) {
876 if (info.isManagedProfile()) {
877 // We use predefined values for managed profiles
878 return context.getString(R.string.managed_user_title);
879 }
880 String name = info != null ? info.name : null;
881 if (name == null && info != null) {
882 name = Integer.toString(info.id);
883 } else if (info == null) {
884 name = context.getString(R.string.unknown);
885 }
886 return context.getResources().getString(R.string.running_process_item_user_label, name);
887 }
888
889 /**
Fabrice Di Meglio22a2a492014-08-08 12:27:57 -0700890 * Return whether or not the user should have a SIM Cards option in Settings.
891 * TODO: Change back to returning true if count is greater than one after testing.
892 * TODO: See bug 16533525.
893 */
894 public static boolean showSimCardTile(Context context) {
895 final TelephonyManager tm =
896 (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
897
Wink Savilledbd052e2014-10-02 15:54:21 -0700898 return tm.getSimCount() > 0;
Fabrice Di Meglio22a2a492014-08-08 12:27:57 -0700899 }
Dan Sandlerb58b5122014-09-02 18:31:49 +0200900
901 /**
902 * Determine whether a package is a "system package", in which case certain things (like
903 * disabling notifications or disabling the package altogether) should be disallowed.
904 */
905 public static boolean isSystemPackage(PackageManager pm, PackageInfo pkg) {
906 if (sSystemSignature == null) {
907 sSystemSignature = new Signature[]{ getSystemSignature(pm) };
908 }
909 return sSystemSignature[0] != null && sSystemSignature[0].equals(getFirstSignature(pkg));
910 }
911
912 private static Signature[] sSystemSignature;
913
914 private static Signature getFirstSignature(PackageInfo pkg) {
915 if (pkg != null && pkg.signatures != null && pkg.signatures.length > 0) {
916 return pkg.signatures[0];
917 }
918 return null;
919 }
920
921 private static Signature getSystemSignature(PackageManager pm) {
922 try {
923 final PackageInfo sys = pm.getPackageInfo("android", PackageManager.GET_SIGNATURES);
924 return getFirstSignature(sys);
925 } catch (NameNotFoundException e) {
926 }
927 return null;
928 }
929
Elliott Hughes7253df32014-09-02 17:10:14 -0700930 /**
931 * Returns elapsed time for the given millis, in the following format:
932 * 2d 5h 40m 29s
933 * @param context the application context
934 * @param millis the elapsed time in milli seconds
935 * @param withSeconds include seconds?
936 * @return the formatted elapsed time
937 */
938 public static String formatElapsedTime(Context context, double millis, boolean withSeconds) {
939 StringBuilder sb = new StringBuilder();
940 int seconds = (int) Math.floor(millis / 1000);
941 if (!withSeconds) {
942 // Round up.
943 seconds += 30;
944 }
945
946 int days = 0, hours = 0, minutes = 0;
947 if (seconds >= SECONDS_PER_DAY) {
948 days = seconds / SECONDS_PER_DAY;
949 seconds -= days * SECONDS_PER_DAY;
950 }
951 if (seconds >= SECONDS_PER_HOUR) {
952 hours = seconds / SECONDS_PER_HOUR;
953 seconds -= hours * SECONDS_PER_HOUR;
954 }
955 if (seconds >= SECONDS_PER_MINUTE) {
956 minutes = seconds / SECONDS_PER_MINUTE;
957 seconds -= minutes * SECONDS_PER_MINUTE;
958 }
959 if (withSeconds) {
960 if (days > 0) {
961 sb.append(context.getString(R.string.battery_history_days,
962 days, hours, minutes, seconds));
963 } else if (hours > 0) {
964 sb.append(context.getString(R.string.battery_history_hours,
965 hours, minutes, seconds));
966 } else if (minutes > 0) {
967 sb.append(context.getString(R.string.battery_history_minutes, minutes, seconds));
968 } else {
969 sb.append(context.getString(R.string.battery_history_seconds, seconds));
970 }
971 } else {
972 if (days > 0) {
973 sb.append(context.getString(R.string.battery_history_days_no_seconds,
974 days, hours, minutes));
975 } else if (hours > 0) {
976 sb.append(context.getString(R.string.battery_history_hours_no_seconds,
977 hours, minutes));
978 } else {
979 sb.append(context.getString(R.string.battery_history_minutes_no_seconds, minutes));
980 }
981 }
982 return sb.toString();
983 }
The Android Open Source Projectafc4ab22009-03-03 19:32:34 -0800984}