summaryrefslogtreecommitdiff
path: root/packages/SettingsLib/src
diff options
context:
space:
mode:
Diffstat (limited to 'packages/SettingsLib/src')
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/CustomEditTextPreference.java18
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/DeviceInfoUtils.java61
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/HelpUtils.java2
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtils.java4
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/TwoTargetPreference.java25
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/Utils.java80
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java34
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/applications/PackageManagerWrapper.java131
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/applications/PackageManagerWrapperImpl.java121
-rwxr-xr-xpackages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java9
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothA2dpWrapper.java58
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/OWNERS8
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/Utils.java82
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/core/AbstractPreferenceController.java118
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/core/ConfirmationDialogController.java46
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/core/lifecycle/Lifecycle.java87
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/core/lifecycle/LifecycleObserver.java5
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/core/lifecycle/ObservableActivity.java28
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/core/lifecycle/ObservableDialogFragment.java38
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/core/lifecycle/ObservableFragment.java37
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/core/lifecycle/ObservablePreferenceFragment.java38
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/core/lifecycle/events/OnAttach.java4
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/core/lifecycle/events/OnCreate.java6
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/core/lifecycle/events/OnDestroy.java7
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/core/lifecycle/events/OnPause.java7
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/core/lifecycle/events/OnResume.java7
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/core/lifecycle/events/OnStart.java8
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/core/lifecycle/events/OnStop.java8
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/datetime/ZoneGetter.java17
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/development/AbstractEnableAdbPreferenceController.java33
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/development/AbstractLogdSizePreferenceController.java184
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/development/AbstractLogpersistPreferenceController.java258
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/development/DeveloperOptionsPreferenceController.java76
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/development/DevelopmentSettingsEnabler.java44
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/development/SystemPropPoker.java98
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractBluetoothAddressPreferenceController.java85
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractConnectivityPreferenceController.java114
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractImsStatusPreferenceController.java95
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractIpAddressPreferenceController.java112
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractSerialNumberPreferenceController.java66
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractSimStatusImeiInfoPreferenceController.java36
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractUptimePreferenceController.java119
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractWifiMacAddressPreferenceController.java88
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/drawer/CategoryManager.java41
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/drawer/DashboardCategory.java105
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/drawer/TileUtils.java44
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/drawer/UserAdapter.java5
-rwxr-xr-xpackages/SettingsLib/src/com/android/settingslib/graph/BatteryMeterDrawableBase.java18
-rw-r--r--[-rwxr-xr-x]packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodPreference.java43
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodSubtypePreference.java40
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/inputmethod/OWNERS5
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlGeneratorFromXml.java292
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlLoader.java110
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/suggestions/SuggestionList.java10
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/suggestions/SuggestionParser.java2
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/utils/AsyncLoader.java110
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/utils/ThreadUtils.java35
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java24
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java100
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/wifi/OWNERS7
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java143
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/wifi/WifiTrackerFactory.java11
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/wrapper/BluetoothA2dpWrapper.java (renamed from packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothA2dpWrapperImpl.java)38
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/wrapper/PackageManagerWrapper.java246
64 files changed, 3093 insertions, 838 deletions
diff --git a/packages/SettingsLib/src/com/android/settingslib/CustomEditTextPreference.java b/packages/SettingsLib/src/com/android/settingslib/CustomEditTextPreference.java
index 253ca11bc44e..8e29e50fc848 100644
--- a/packages/SettingsLib/src/com/android/settingslib/CustomEditTextPreference.java
+++ b/packages/SettingsLib/src/com/android/settingslib/CustomEditTextPreference.java
@@ -20,6 +20,7 @@ import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.os.Bundle;
+import android.support.annotation.CallSuper;
import android.support.v14.preference.EditTextPreferenceDialogFragment;
import android.support.v7.preference.EditTextPreference;
import android.util.AttributeSet;
@@ -30,7 +31,8 @@ public class CustomEditTextPreference extends EditTextPreference {
private CustomPreferenceDialogFragment mFragment;
- public CustomEditTextPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+ public CustomEditTextPreference(Context context, AttributeSet attrs, int defStyleAttr,
+ int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
@@ -47,8 +49,13 @@ public class CustomEditTextPreference extends EditTextPreference {
}
public EditText getEditText() {
- return mFragment != null ? (EditText) mFragment.getDialog().findViewById(android.R.id.edit)
- : null;
+ if (mFragment != null) {
+ final Dialog dialog = mFragment.getDialog();
+ if (dialog != null) {
+ return (EditText) dialog.findViewById(android.R.id.edit);
+ }
+ }
+ return null;
}
public boolean isDialogOpen() {
@@ -69,7 +76,12 @@ public class CustomEditTextPreference extends EditTextPreference {
protected void onClick(DialogInterface dialog, int which) {
}
+ @CallSuper
protected void onBindDialogView(View view) {
+ final EditText editText = view.findViewById(android.R.id.edit);
+ if (editText != null) {
+ editText.requestFocus();
+ }
}
private void setFragment(CustomPreferenceDialogFragment fragment) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/DeviceInfoUtils.java b/packages/SettingsLib/src/com/android/settingslib/DeviceInfoUtils.java
index 78a9064f1400..f2cd1033cd38 100644
--- a/packages/SettingsLib/src/com/android/settingslib/DeviceInfoUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/DeviceInfoUtils.java
@@ -22,12 +22,15 @@ import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.os.Build;
+import android.system.Os;
+import android.system.StructUtsname;
import android.telephony.PhoneNumberUtils;
import android.telephony.SubscriptionInfo;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.text.format.DateFormat;
import android.util.Log;
+import android.support.annotation.VisibleForTesting;
import java.io.BufferedReader;
import java.io.FileReader;
@@ -45,7 +48,6 @@ import static android.content.Context.TELEPHONY_SERVICE;
public class DeviceInfoUtils {
private static final String TAG = "DeviceInfoUtils";
- private static final String FILENAME_PROC_VERSION = "/proc/version";
private static final String FILENAME_MSV = "/sys/board_properties/soc/msv";
/**
@@ -63,43 +65,36 @@ public class DeviceInfoUtils {
}
}
- public static String getFormattedKernelVersion() {
- try {
- return formatKernelVersion(readLine(FILENAME_PROC_VERSION));
- } catch (IOException e) {
- Log.e(TAG, "IO Exception when getting kernel version for Device Info screen",
- e);
-
- return "Unavailable";
- }
+ public static String getFormattedKernelVersion(Context context) {
+ return formatKernelVersion(context, Os.uname());
}
- public static String formatKernelVersion(String rawKernelVersion) {
- // Example (see tests for more):
- // Linux version 4.9.29-g958411d (android-build@xyz) (Android clang version 3.8.256229 \
- // (based on LLVM 3.8.256229)) #1 SMP PREEMPT Wed Jun 7 00:06:03 CST 2017
- // Linux version 4.9.29-geb63318482a7 (android-build@xyz) (gcc version 4.9.x 20150123 \
- // (prerelease) (GCC) ) #1 SMP PREEMPT Thu Jun 1 03:41:57 UTC 2017
- final String PROC_VERSION_REGEX =
- "Linux version (\\S+) " + /* group 1: "3.0.31-g6fb96c9" */
- "\\((\\S+?)\\) " + /* group 2: "(x@y.com) " */
- "\\((.+?)\\) " + /* group 3: kernel toolchain version information */
- "(#\\d+) " + /* group 4: "#1" */
+ @VisibleForTesting
+ static String formatKernelVersion(Context context, StructUtsname uname) {
+ if (uname == null) {
+ return context.getString(R.string.status_unavailable);
+ }
+ // Example:
+ // 4.9.29-g958411d
+ // #1 SMP PREEMPT Wed Jun 7 00:06:03 CST 2017
+ final String VERSION_REGEX =
+ "(#\\d+) " + /* group 1: "#1" */
"(?:.*?)?" + /* ignore: optional SMP, PREEMPT, and any CONFIG_FLAGS */
- "((Sun|Mon|Tue|Wed|Thu|Fri|Sat).+)"; /* group 5: "Thu Jun 28 11:02:39 PDT 2012" */
-
- Matcher m = Pattern.compile(PROC_VERSION_REGEX).matcher(rawKernelVersion);
+ "((Sun|Mon|Tue|Wed|Thu|Fri|Sat).+)"; /* group 2: "Thu Jun 28 11:02:39 PDT 2012" */
+ Matcher m = Pattern.compile(VERSION_REGEX).matcher(uname.version);
if (!m.matches()) {
- Log.e(TAG, "Regex did not match on /proc/version: " + rawKernelVersion);
- return "Unavailable";
- } else if (m.groupCount() < 4) {
- Log.e(TAG, "Regex match on /proc/version only returned " + m.groupCount()
- + " groups");
- return "Unavailable";
+ Log.e(TAG, "Regex did not match on uname version " + uname.version);
+ return context.getString(R.string.status_unavailable);
}
- return m.group(1) + " ("+ m.group(3) + ")\n" + // 3.0.31-g6fb96c9 (toolchain version)
- m.group(2) + " " + m.group(4) + "\n" + // x@y.com #1
- m.group(5); // Thu Jun 28 11:02:39 PDT 2012
+
+ // Example output:
+ // 4.9.29-g958411d
+ // #1 Wed Jun 7 00:06:03 CST 2017
+ return new StringBuilder().append(uname.release)
+ .append("\n")
+ .append(m.group(1))
+ .append(" ")
+ .append(m.group(2)).toString();
}
/**
diff --git a/packages/SettingsLib/src/com/android/settingslib/HelpUtils.java b/packages/SettingsLib/src/com/android/settingslib/HelpUtils.java
index 2c2641079953..8055caaad536 100644
--- a/packages/SettingsLib/src/com/android/settingslib/HelpUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/HelpUtils.java
@@ -227,7 +227,7 @@ public class HelpUtils {
// cache the version code
PackageInfo info = context.getPackageManager().getPackageInfo(
context.getPackageName(), 0);
- sCachedVersionCode = Integer.toString(info.versionCode);
+ sCachedVersionCode = Long.toString(info.getLongVersionCode());
// append the version code to the uri
builder.appendQueryParameter(PARAM_VERSION, sCachedVersionCode);
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtils.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtils.java
index 32e6389dc34f..c3a36e97be3a 100644
--- a/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtils.java
@@ -28,6 +28,7 @@ import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.IPackageManager;
+import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
import android.graphics.drawable.Drawable;
import android.os.RemoteException;
@@ -343,7 +344,8 @@ public class RestrictedLockUtils {
}
DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService(
Context.DEVICE_POLICY_SERVICE);
- if (dpm == null) {
+ PackageManager pm = context.getPackageManager();
+ if (!pm.hasSystemFeature(PackageManager.FEATURE_DEVICE_ADMIN) || dpm == null) {
return null;
}
boolean isAccountTypeDisabled = false;
diff --git a/packages/SettingsLib/src/com/android/settingslib/TwoTargetPreference.java b/packages/SettingsLib/src/com/android/settingslib/TwoTargetPreference.java
index 1c161dffced9..8b39f60aa9ca 100644
--- a/packages/SettingsLib/src/com/android/settingslib/TwoTargetPreference.java
+++ b/packages/SettingsLib/src/com/android/settingslib/TwoTargetPreference.java
@@ -21,41 +21,56 @@ import android.support.v7.preference.Preference;
import android.support.v7.preference.PreferenceViewHolder;
import android.util.AttributeSet;
import android.view.View;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
public class TwoTargetPreference extends Preference {
+ private boolean mUseSmallIcon;
+ private int mSmallIconSize;
+
public TwoTargetPreference(Context context, AttributeSet attrs,
int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
- init();
+ init(context);
}
public TwoTargetPreference(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
- init();
+ init(context);
}
public TwoTargetPreference(Context context, AttributeSet attrs) {
super(context, attrs);
- init();
+ init(context);
}
public TwoTargetPreference(Context context) {
super(context);
- init();
+ init(context);
}
- private void init() {
+ private void init(Context context) {
setLayoutResource(R.layout.preference_two_target);
+ mSmallIconSize = context.getResources().getDimensionPixelSize(
+ R.dimen.two_target_pref_small_icon_size);
final int secondTargetResId = getSecondTargetResId();
if (secondTargetResId != 0) {
setWidgetLayoutResource(secondTargetResId);
}
}
+ public void setUseSmallIcon(boolean useSmallIcon) {
+ mUseSmallIcon = useSmallIcon;
+ }
+
@Override
public void onBindViewHolder(PreferenceViewHolder holder) {
super.onBindViewHolder(holder);
+ if (mUseSmallIcon) {
+ ImageView icon = holder.itemView.findViewById(android.R.id.icon);
+ icon.setLayoutParams(new LinearLayout.LayoutParams(mSmallIconSize, mSmallIconSize));
+ }
final View divider = holder.findViewById(R.id.two_target_divider);
final View widgetFrame = holder.findViewById(android.R.id.widget_frame);
final boolean shouldHideSecondTarget = shouldHideSecondTarget();
diff --git a/packages/SettingsLib/src/com/android/settingslib/Utils.java b/packages/SettingsLib/src/com/android/settingslib/Utils.java
index 64ec16a23b46..3c46d99906c7 100644
--- a/packages/SettingsLib/src/com/android/settingslib/Utils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/Utils.java
@@ -12,18 +12,15 @@ import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
import android.graphics.Color;
-import android.graphics.PorterDuff;
import android.graphics.drawable.Drawable;
-import android.graphics.drawable.LayerDrawable;
+import android.location.LocationManager;
import android.net.ConnectivityManager;
-import android.net.NetworkBadging;
import android.os.BatteryManager;
+import android.os.UserHandle;
import android.os.UserManager;
import android.print.PrintManager;
import android.provider.Settings;
-import android.view.View;
import com.android.internal.util.UserIcons;
import com.android.settingslib.drawable.UserIconDrawable;
@@ -31,19 +28,33 @@ import com.android.settingslib.drawable.UserIconDrawable;
import java.text.NumberFormat;
public class Utils {
+
+ private static final String CURRENT_MODE_KEY = "CURRENT_MODE";
+ private static final String NEW_MODE_KEY = "NEW_MODE";
+
private static Signature[] sSystemSignature;
private static String sPermissionControllerPackageName;
private static String sServicesSystemSharedLibPackageName;
private static String sSharedSystemSharedLibPackageName;
- static final int[] WIFI_PIE_FOR_BADGING = {
- com.android.internal.R.drawable.ic_signal_wifi_badged_0_bars,
- com.android.internal.R.drawable.ic_signal_wifi_badged_1_bar,
- com.android.internal.R.drawable.ic_signal_wifi_badged_2_bars,
- com.android.internal.R.drawable.ic_signal_wifi_badged_3_bars,
- com.android.internal.R.drawable.ic_signal_wifi_badged_4_bars
+ static final int[] WIFI_PIE = {
+ com.android.internal.R.drawable.ic_wifi_signal_0,
+ com.android.internal.R.drawable.ic_wifi_signal_1,
+ com.android.internal.R.drawable.ic_wifi_signal_2,
+ com.android.internal.R.drawable.ic_wifi_signal_3,
+ com.android.internal.R.drawable.ic_wifi_signal_4
};
+ public static boolean updateLocationMode(Context context, int oldMode, int newMode, int userId) {
+ Intent intent = new Intent(LocationManager.MODE_CHANGING_ACTION);
+ intent.putExtra(CURRENT_MODE_KEY, oldMode);
+ intent.putExtra(NEW_MODE_KEY, newMode);
+ context.sendBroadcastAsUser(
+ intent, UserHandle.of(userId), android.Manifest.permission.WRITE_SECURE_SETTINGS);
+ return Settings.Secure.putIntForUser(
+ context.getContentResolver(), Settings.Secure.LOCATION_MODE, newMode, userId);
+ }
+
/**
* Return string resource that best describes combination of tethering
* options available on this device.
@@ -99,7 +110,7 @@ public class Utils {
public static Drawable getUserIcon(Context context, UserManager um, UserInfo user) {
final int iconSize = UserIconDrawable.getSizeForList(context);
if (user.isManagedProfile()) {
- Drawable drawable = context.getDrawable(com.android.internal.R.drawable.ic_corp_icon);
+ Drawable drawable = context.getDrawable(com.android.internal.R.drawable.ic_corp_badge);
drawable.setBounds(0, 0, iconSize, iconSize);
return drawable;
}
@@ -110,7 +121,8 @@ public class Utils {
}
}
return new UserIconDrawable(iconSize).setIconDrawable(
- UserIcons.getDefaultUserIcon(user.id, /* light= */ false)).bake();
+ UserIcons.getDefaultUserIcon(context.getResources(), user.id, /* light= */ false))
+ .bake();
}
/** Formats a double from 0.0..100.0 with an option to round **/
@@ -272,42 +284,17 @@ public class Utils {
}
/**
- * Returns a badged Wifi icon drawable.
- *
- * <p>The first layer contains the Wifi pie and the second layer contains the badge. Callers
- * should set the drawable to the appropriate size and tint color.
+ * Returns the Wifi icon resource for a given RSSI level.
*
- * @param context The caller's context (must have access to internal resources)
* @param level The number of bars to show (0-4)
- * @param badge The badge enum {@see android.net.ScoredNetwork}
*
- * @throws IllegalArgumentException if an invalid badge enum is given
- *
- * @deprecated TODO(sghuman): Finalize the form of this method and then move it to a new
- * location.
+ * @throws IllegalArgumentException if an invalid RSSI level is given.
*/
- public static LayerDrawable getBadgedWifiIcon(Context context, int level, int badge) {
- return new LayerDrawable(
- new Drawable[] {
- context.getDrawable(WIFI_PIE_FOR_BADGING[level]),
- context.getDrawable(getWifiBadgeResource(badge))
- });
- }
-
- private static int getWifiBadgeResource(int badge) {
- switch (badge) {
- case NetworkBadging.BADGING_NONE:
- return View.NO_ID;
- case NetworkBadging.BADGING_SD:
- return com.android.internal.R.drawable.ic_signal_wifi_badged_sd;
- case NetworkBadging.BADGING_HD:
- return com.android.internal.R.drawable.ic_signal_wifi_badged_hd;
- case NetworkBadging.BADGING_4K:
- return com.android.internal.R.drawable.ic_signal_wifi_badged_4k;
- default:
- throw new IllegalArgumentException(
- "No badge resource found for badge value: " + badge);
+ public static int getWifiIconResource(int level) {
+ if (level < 0 || level >= WIFI_PIE.length) {
+ throw new IllegalArgumentException("No Wifi icon found for level: " + level);
}
+ return WIFI_PIE[level];
}
public static int getDefaultStorageManagerDaysToRetain(Resources resources) {
@@ -325,4 +312,9 @@ public class Utils {
}
return defaultDays;
}
+
+ public static boolean isWifiOnly(Context context) {
+ return !context.getSystemService(ConnectivityManager.class)
+ .isNetworkSupported(ConnectivityManager.TYPE_MOBILE);
+ }
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java b/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
index 40c2b1f3b771..fa2499f6c144 100644
--- a/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
+++ b/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
@@ -52,6 +52,11 @@ import android.util.SparseArray;
import com.android.internal.R;
import com.android.internal.util.ArrayUtils;
+import com.android.settingslib.core.lifecycle.Lifecycle;
+import com.android.settingslib.core.lifecycle.LifecycleObserver;
+import com.android.settingslib.core.lifecycle.events.OnDestroy;
+import com.android.settingslib.core.lifecycle.events.OnPause;
+import com.android.settingslib.core.lifecycle.events.OnResume;
import java.io.File;
import java.io.IOException;
@@ -180,7 +185,11 @@ public class ApplicationsState {
}
public Session newSession(Callbacks callbacks) {
- Session s = new Session(callbacks);
+ return newSession(callbacks, null);
+ }
+
+ public Session newSession(Callbacks callbacks, Lifecycle lifecycle) {
+ Session s = new Session(callbacks, lifecycle);
synchronized (mEntriesMap) {
mSessions.add(s);
}
@@ -586,7 +595,7 @@ public class ApplicationsState {
.replaceAll("").toLowerCase();
}
- public class Session {
+ public class Session implements LifecycleObserver, OnPause, OnResume, OnDestroy {
final Callbacks mCallbacks;
boolean mResumed;
@@ -600,11 +609,19 @@ public class ApplicationsState {
ArrayList<AppEntry> mLastAppList;
boolean mRebuildForeground;
- Session(Callbacks callbacks) {
+ private final boolean mHasLifecycle;
+
+ Session(Callbacks callbacks, Lifecycle lifecycle) {
mCallbacks = callbacks;
+ if (lifecycle != null) {
+ lifecycle.addObserver(this);
+ mHasLifecycle = true;
+ } else {
+ mHasLifecycle = false;
+ }
}
- public void resume() {
+ public void onResume() {
if (DEBUG_LOCKING) Log.v(TAG, "resume about to acquire lock...");
synchronized (mEntriesMap) {
if (!mResumed) {
@@ -616,7 +633,7 @@ public class ApplicationsState {
if (DEBUG_LOCKING) Log.v(TAG, "...resume releasing lock");
}
- public void pause() {
+ public void onPause() {
if (DEBUG_LOCKING) Log.v(TAG, "pause about to acquire lock...");
synchronized (mEntriesMap) {
if (mResumed) {
@@ -735,8 +752,11 @@ public class ApplicationsState {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
}
- public void release() {
- pause();
+ public void onDestroy() {
+ if (!mHasLifecycle) {
+ // TODO: Legacy, remove this later once all usages are switched to Lifecycle
+ onPause();
+ }
synchronized (mEntriesMap) {
mSessions.remove(this);
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/applications/PackageManagerWrapper.java b/packages/SettingsLib/src/com/android/settingslib/applications/PackageManagerWrapper.java
deleted file mode 100644
index 6c79a6124ca2..000000000000
--- a/packages/SettingsLib/src/com/android/settingslib/applications/PackageManagerWrapper.java
+++ /dev/null
@@ -1,131 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.settingslib.applications;
-
-import android.content.ComponentName;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.content.pm.ResolveInfo;
-import android.graphics.drawable.Drawable;
-import android.os.UserHandle;
-
-import java.util.List;
-
-/**
- * This interface replicates a subset of the android.content.pm.PackageManager (PM). The interface
- * exists so that we can use a thin wrapper around the PM in production code and a mock in tests.
- * We cannot directly mock or shadow the PM, because some of the methods we rely on are newer than
- * the API version supported by Robolectric.
- */
-public interface PackageManagerWrapper {
-
- /**
- * Returns the real {@code PackageManager} object.
- */
- PackageManager getPackageManager();
-
- /**
- * Calls {@code PackageManager.getInstalledApplicationsAsUser()}.
- *
- * @see android.content.pm.PackageManager#getInstalledApplicationsAsUser
- */
- List<ApplicationInfo> getInstalledApplicationsAsUser(int flags, int userId);
-
- /**
- * Calls {@code PackageManager.hasSystemFeature()}.
- *
- * @see android.content.pm.PackageManager#hasSystemFeature
- */
- boolean hasSystemFeature(String name);
-
- /**
- * Calls {@code PackageManager.queryIntentActivitiesAsUser()}.
- *
- * @see android.content.pm.PackageManager#queryIntentActivitiesAsUser
- */
- List<ResolveInfo> queryIntentActivitiesAsUser(Intent intent, int flags, int userId);
-
- /**
- * Calls {@code PackageManager.getInstallReason()}.
- *
- * @see android.content.pm.PackageManager#getInstallReason
- */
- int getInstallReason(String packageName, UserHandle user);
-
- /**
- * Calls {@code PackageManager.getApplicationInfoAsUser}
- */
- ApplicationInfo getApplicationInfoAsUser(String packageName, int i, int userId)
- throws PackageManager.NameNotFoundException;
-
- /**
- * Calls {@code PackageManager.setDefaultBrowserPackageNameAsUser}
- */
- boolean setDefaultBrowserPackageNameAsUser(String packageName, int userId);
-
- /**
- * Calls {@code PackageManager.getDefaultBrowserPackageNameAsUser}
- */
- String getDefaultBrowserPackageNameAsUser(int userId);
-
- /**
- * Calls {@code PackageManager.getHomeActivities}
- */
- ComponentName getHomeActivities(List<ResolveInfo> homeActivities);
-
- /**
- * Calls {@code PackageManager.queryIntentServicesAsUser}
- */
- List<ResolveInfo> queryIntentServicesAsUser(Intent intent, int i, int user);
-
- /**
- * Calls {@code PackageManager.replacePreferredActivity}
- */
- void replacePreferredActivity(IntentFilter homeFilter, int matchCategoryEmpty,
- ComponentName[] componentNames, ComponentName component);
-
- /**
- * Gets information about a particular package from the package manager.
- * @param packageName The name of the package we would like information about.
- * @param i additional options flags. see javadoc for {@link PackageManager#getPackageInfo(String, int)}
- * @return The PackageInfo for the requested package
- * @throws NameNotFoundException
- */
- PackageInfo getPackageInfo(String packageName, int i) throws NameNotFoundException;
-
- /**
- * Retrieves the icon associated with this particular set of ApplicationInfo
- * @param info The ApplicationInfo to retrieve the icon for
- * @return The icon as a drawable.
- */
- Drawable getUserBadgedIcon(ApplicationInfo info);
-
- /**
- * Retrieves the label associated with the particular set of ApplicationInfo
- * @param app The ApplicationInfo to retrieve the label for
- * @return the label as a CharSequence
- */
- CharSequence loadLabel(ApplicationInfo app);
-
- /**
- * Retrieve all activities that can be performed for the given intent.
- */
- List<ResolveInfo> queryIntentActivities(Intent intent, int flags);
-}
diff --git a/packages/SettingsLib/src/com/android/settingslib/applications/PackageManagerWrapperImpl.java b/packages/SettingsLib/src/com/android/settingslib/applications/PackageManagerWrapperImpl.java
deleted file mode 100644
index dcb40b20365e..000000000000
--- a/packages/SettingsLib/src/com/android/settingslib/applications/PackageManagerWrapperImpl.java
+++ /dev/null
@@ -1,121 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.settingslib.applications;
-
-import android.content.ComponentName;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.content.pm.ResolveInfo;
-import android.graphics.drawable.Drawable;
-import android.os.UserHandle;
-
-import java.util.List;
-
-/**
- * A thin wrapper class that simplifies testing by putting a mockable layer between the application
- * and the PackageManager. This class only provides access to the minimum number of functions from
- * the PackageManager needed for DeletionHelper to work.
- */
-public class PackageManagerWrapperImpl implements PackageManagerWrapper {
-
- private final PackageManager mPm;
-
- public PackageManagerWrapperImpl(PackageManager pm) {
- mPm = pm;
- }
-
- @Override
- public PackageManager getPackageManager() {
- return mPm;
- }
-
- @Override
- public List<ApplicationInfo> getInstalledApplicationsAsUser(int flags, int userId) {
- return mPm.getInstalledApplicationsAsUser(flags, userId);
- }
-
- @Override
- public boolean hasSystemFeature(String name) {
- return mPm.hasSystemFeature(name);
- }
-
- @Override
- public List<ResolveInfo> queryIntentActivitiesAsUser(Intent intent, int flags, int userId) {
- return mPm.queryIntentActivitiesAsUser(intent, flags, userId);
- }
-
- @Override
- public int getInstallReason(String packageName, UserHandle user) {
- return mPm.getInstallReason(packageName, user);
- }
-
- @Override
- public ApplicationInfo getApplicationInfoAsUser(String packageName, int i, int userId)
- throws PackageManager.NameNotFoundException {
- return mPm.getApplicationInfoAsUser(packageName, i, userId);
- }
-
- @Override
- public boolean setDefaultBrowserPackageNameAsUser(String packageName, int userId) {
- return mPm.setDefaultBrowserPackageNameAsUser(packageName, userId);
- }
-
- @Override
- public String getDefaultBrowserPackageNameAsUser(int userId) {
- return mPm.getDefaultBrowserPackageNameAsUser(userId);
- }
-
- @Override
- public ComponentName getHomeActivities(List<ResolveInfo> homeActivities) {
- return mPm.getHomeActivities(homeActivities);
- }
-
- @Override
- public List<ResolveInfo> queryIntentServicesAsUser(Intent intent, int i, int user) {
- return mPm.queryIntentServicesAsUser(intent, i, user);
- }
-
- @Override
- public void replacePreferredActivity(IntentFilter homeFilter, int matchCategoryEmpty,
- ComponentName[] componentNames, ComponentName component) {
- mPm.replacePreferredActivity(homeFilter, matchCategoryEmpty, componentNames, component);
- }
-
- @Override
- public PackageInfo getPackageInfo(String packageName, int i) throws NameNotFoundException {
- return mPm.getPackageInfo(packageName, i);
- }
-
- @Override
- public Drawable getUserBadgedIcon(ApplicationInfo info) {
- return mPm.getUserBadgedIcon(mPm.loadUnbadgedItemIcon(info, info),
- new UserHandle(UserHandle.getUserId(info.uid)));
- }
-
- @Override
- public CharSequence loadLabel(ApplicationInfo app) {
- return app.loadLabel(mPm);
- }
-
- @Override
- public List<ResolveInfo> queryIntentActivities(Intent intent, int flags) {
- return mPm.queryIntentActivities(intent, flags);
- }
-}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java
index 0946181ab710..764c5922cc64 100755
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java
@@ -30,6 +30,7 @@ import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
import com.android.settingslib.R;
+import com.android.settingslib.wrapper.BluetoothA2dpWrapper;
import java.util.ArrayList;
import java.util.Arrays;
@@ -42,7 +43,6 @@ public class A2dpProfile implements LocalBluetoothProfile {
private Context mContext;
private BluetoothA2dp mService;
- BluetoothA2dpWrapper.Factory mWrapperFactory;
private BluetoothA2dpWrapper mServiceWrapper;
private boolean mIsProfileReady;
@@ -67,7 +67,7 @@ public class A2dpProfile implements LocalBluetoothProfile {
public void onServiceConnected(int profile, BluetoothProfile proxy) {
if (V) Log.d(TAG,"Bluetooth service connected");
mService = (BluetoothA2dp) proxy;
- mServiceWrapper = mWrapperFactory.getInstance(mService);
+ mServiceWrapper = new BluetoothA2dpWrapper(mService);
// We just bound to the service, so refresh the UI for any connected A2DP devices.
List<BluetoothDevice> deviceList = mService.getConnectedDevices();
while (!deviceList.isEmpty()) {
@@ -101,14 +101,13 @@ public class A2dpProfile implements LocalBluetoothProfile {
mLocalAdapter = adapter;
mDeviceManager = deviceManager;
mProfileManager = profileManager;
- mWrapperFactory = new BluetoothA2dpWrapperImpl.Factory();
mLocalAdapter.getProfileProxy(context, new A2dpServiceListener(),
BluetoothProfile.A2DP);
}
@VisibleForTesting
- void setWrapperFactory(BluetoothA2dpWrapper.Factory factory) {
- mWrapperFactory = factory;
+ void setBluetoothA2dpWrapper(BluetoothA2dpWrapper wrapper) {
+ mServiceWrapper = wrapper;
}
public boolean isConnectable() {
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothA2dpWrapper.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothA2dpWrapper.java
deleted file mode 100644
index aa3e8356739a..000000000000
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothA2dpWrapper.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settingslib.bluetooth;
-
-import android.bluetooth.BluetoothA2dp;
-import android.bluetooth.BluetoothCodecStatus;
-import android.bluetooth.BluetoothDevice;
-
-/**
- * This interface replicates some methods of android.bluetooth.BluetoothA2dp that are new and not
- * yet available in our current version of Robolectric. It provides a thin wrapper to call the real
- * methods in production and a mock in tests.
- */
-public interface BluetoothA2dpWrapper {
-
- static interface Factory {
- BluetoothA2dpWrapper getInstance(BluetoothA2dp service);
- }
-
- /**
- * @return the real {@code BluetoothA2dp} object
- */
- BluetoothA2dp getService();
-
- /**
- * Wraps {@code BluetoothA2dp.getCodecStatus}
- */
- public BluetoothCodecStatus getCodecStatus();
-
- /**
- * Wraps {@code BluetoothA2dp.supportsOptionalCodecs}
- */
- int supportsOptionalCodecs(BluetoothDevice device);
-
- /**
- * Wraps {@code BluetoothA2dp.getOptionalCodecsEnabled}
- */
- int getOptionalCodecsEnabled(BluetoothDevice device);
-
- /**
- * Wraps {@code BluetoothA2dp.setOptionalCodecsEnabled}
- */
- void setOptionalCodecsEnabled(BluetoothDevice device, int value);
-}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/OWNERS b/packages/SettingsLib/src/com/android/settingslib/bluetooth/OWNERS
new file mode 100644
index 000000000000..7162121330ef
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/OWNERS
@@ -0,0 +1,8 @@
+# Default reviewers for this and subdirectories.
+asapperstein@google.com
+asargent@google.com
+eisenbach@google.com
+jackqdyulei@google.com
+siyuanh@google.com
+
+# Emergency approvers in case the above are not available \ No newline at end of file
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/Utils.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/Utils.java
index c9194268543a..0ee1dad9d744 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/Utils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/Utils.java
@@ -1,9 +1,17 @@
package com.android.settingslib.bluetooth;
+import android.bluetooth.BluetoothClass;
+import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothProfile;
import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.support.annotation.DrawableRes;
+import android.util.Pair;
import com.android.settingslib.R;
+import com.android.settingslib.graph.BluetoothDeviceLayerDrawable;
+
+import java.util.List;
public class Utils {
public static final boolean V = false; // verbose logging
@@ -40,4 +48,78 @@ public class Utils {
void onShowError(Context context, String name, int messageResId);
}
+ public static Pair<Drawable, String> getBtClassDrawableWithDescription(Context context,
+ CachedBluetoothDevice cachedDevice) {
+ return getBtClassDrawableWithDescription(context, cachedDevice, 1 /* iconScale */);
+ }
+
+ public static Pair<Drawable, String> getBtClassDrawableWithDescription(Context context,
+ CachedBluetoothDevice cachedDevice, float iconScale) {
+ BluetoothClass btClass = cachedDevice.getBtClass();
+ final int level = cachedDevice.getBatteryLevel();
+ if (btClass != null) {
+ switch (btClass.getMajorDeviceClass()) {
+ case BluetoothClass.Device.Major.COMPUTER:
+ return new Pair<>(getBluetoothDrawable(context, R.drawable.ic_bt_laptop, level,
+ iconScale),
+ context.getString(R.string.bluetooth_talkback_computer));
+
+ case BluetoothClass.Device.Major.PHONE:
+ return new Pair<>(
+ getBluetoothDrawable(context, R.drawable.ic_bt_cellphone, level,
+ iconScale),
+ context.getString(R.string.bluetooth_talkback_phone));
+
+ case BluetoothClass.Device.Major.PERIPHERAL:
+ return new Pair<>(
+ getBluetoothDrawable(context, HidProfile.getHidClassDrawable(btClass),
+ level, iconScale),
+ context.getString(R.string.bluetooth_talkback_input_peripheral));
+
+ case BluetoothClass.Device.Major.IMAGING:
+ return new Pair<>(
+ getBluetoothDrawable(context, R.drawable.ic_settings_print, level,
+ iconScale),
+ context.getString(R.string.bluetooth_talkback_imaging));
+
+ default:
+ // unrecognized device class; continue
+ }
+ }
+
+ List<LocalBluetoothProfile> profiles = cachedDevice.getProfiles();
+ for (LocalBluetoothProfile profile : profiles) {
+ int resId = profile.getDrawableResource(btClass);
+ if (resId != 0) {
+ return new Pair<>(getBluetoothDrawable(context, resId, level, iconScale), null);
+ }
+ }
+ if (btClass != null) {
+ if (btClass.doesClassMatch(BluetoothClass.PROFILE_HEADSET)) {
+ return new Pair<>(
+ getBluetoothDrawable(context, R.drawable.ic_bt_headset_hfp, level,
+ iconScale),
+ context.getString(R.string.bluetooth_talkback_headset));
+ }
+ if (btClass.doesClassMatch(BluetoothClass.PROFILE_A2DP)) {
+ return new Pair<>(
+ getBluetoothDrawable(context, R.drawable.ic_bt_headphones_a2dp, level,
+ iconScale),
+ context.getString(R.string.bluetooth_talkback_headphone));
+ }
+ }
+ return new Pair<>(
+ getBluetoothDrawable(context, R.drawable.ic_settings_bluetooth, level, iconScale),
+ context.getString(R.string.bluetooth_talkback_bluetooth));
+ }
+
+ public static Drawable getBluetoothDrawable(Context context, @DrawableRes int resId,
+ int batteryLevel, float iconScale) {
+ if (batteryLevel != BluetoothDevice.BATTERY_LEVEL_UNKNOWN) {
+ return BluetoothDeviceLayerDrawable.createLayerDrawable(context, resId, batteryLevel,
+ iconScale);
+ } else {
+ return context.getDrawable(resId);
+ }
+ }
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/core/AbstractPreferenceController.java b/packages/SettingsLib/src/com/android/settingslib/core/AbstractPreferenceController.java
index 38fe8790e4d0..88f0d2be311f 100644
--- a/packages/SettingsLib/src/com/android/settingslib/core/AbstractPreferenceController.java
+++ b/packages/SettingsLib/src/com/android/settingslib/core/AbstractPreferenceController.java
@@ -10,79 +10,63 @@ import android.support.v7.preference.PreferenceScreen;
*/
public abstract class AbstractPreferenceController {
- protected final Context mContext;
+ protected final Context mContext;
- public AbstractPreferenceController(Context context) {
- mContext = context;
- }
+ public AbstractPreferenceController(Context context) {
+ mContext = context;
+ }
- /**
- * Displays preference in this controller.
- */
- public void displayPreference(PreferenceScreen screen) {
- if (isAvailable()) {
- if (this instanceof Preference.OnPreferenceChangeListener) {
- final Preference preference = screen.findPreference(getPreferenceKey());
- preference.setOnPreferenceChangeListener(
- (Preference.OnPreferenceChangeListener) this);
- }
- } else {
- removePreference(screen, getPreferenceKey());
- }
- }
+ /**
+ * Displays preference in this controller.
+ */
+ public void displayPreference(PreferenceScreen screen) {
+ final String prefKey = getPreferenceKey();
+ if (isAvailable()) {
+ setVisible(screen, prefKey, true /* visible */);
+ if (this instanceof Preference.OnPreferenceChangeListener) {
+ final Preference preference = screen.findPreference(prefKey);
+ preference.setOnPreferenceChangeListener(
+ (Preference.OnPreferenceChangeListener) this);
+ }
+ } else {
+ setVisible(screen, prefKey, false /* visible */);
+ }
+ }
- /**
- * Updates the current status of preference (summary, switch state, etc)
- */
- public void updateState(Preference preference) {
+ /**
+ * Updates the current status of preference (summary, switch state, etc)
+ */
+ public void updateState(Preference preference) {
- }
+ }
- /**
- * Returns true if preference is available (should be displayed)
- */
- public abstract boolean isAvailable();
+ /**
+ * Returns true if preference is available (should be displayed)
+ */
+ public abstract boolean isAvailable();
- /**
- * Handles preference tree click
- *
- * @param preference the preference being clicked
- * @return true if click is handled
- */
- public boolean handlePreferenceTreeClick(Preference preference) {
- return false;
- }
+ /**
+ * Handles preference tree click
+ *
+ * @param preference the preference being clicked
+ * @return true if click is handled
+ */
+ public boolean handlePreferenceTreeClick(Preference preference) {
+ return false;
+ }
- /**
- * Returns the key for this preference.
- */
- public abstract String getPreferenceKey();
-
- /**
- * Removes preference from screen.
- */
- protected final void removePreference(PreferenceScreen screen, String key) {
- findAndRemovePreference(screen, key);
- }
-
- // finds the preference recursively and removes it from its parent
- private boolean findAndRemovePreference(PreferenceGroup prefGroup, String key) {
- final int preferenceCount = prefGroup.getPreferenceCount();
- for (int i = 0; i < preferenceCount; i++) {
- final Preference preference = prefGroup.getPreference(i);
- final String curKey = preference.getKey();
-
- if (curKey != null && curKey.equals(key)) {
- return prefGroup.removePreference(preference);
- }
-
- if (preference instanceof PreferenceGroup) {
- if (findAndRemovePreference((PreferenceGroup) preference, key)) {
- return true;
- }
- }
- }
- return false;
- }
+ /**
+ * Returns the key for this preference.
+ */
+ public abstract String getPreferenceKey();
+ /**
+ * Show/hide a preference.
+ */
+ protected final void setVisible(PreferenceGroup group, String key, boolean isVisible) {
+ final Preference pref = group.findPreference(key);
+ if (pref != null) {
+ pref.setVisible(isVisible);
+ }
+ }
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/core/ConfirmationDialogController.java b/packages/SettingsLib/src/com/android/settingslib/core/ConfirmationDialogController.java
new file mode 100644
index 000000000000..72ab8c3848c5
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/core/ConfirmationDialogController.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.core;
+
+import android.support.annotation.Nullable;
+import android.support.v7.preference.Preference;
+
+/**
+ * Interface for {@link AbstractPreferenceController} objects which manage confirmation dialogs
+ */
+public interface ConfirmationDialogController {
+ /**
+ * Returns the key for this preference.
+ */
+ String getPreferenceKey();
+
+ /**
+ * Shows the dialog
+ * @param preference Preference object relevant to the dialog being shown
+ */
+ void showConfirmationDialog(@Nullable Preference preference);
+
+ /**
+ * Dismiss the dialog managed by this object
+ */
+ void dismissConfirmationDialog();
+
+ /**
+ * @return {@code true} if the dialog is showing
+ */
+ boolean isConfirmationDialogShowing();
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/core/lifecycle/Lifecycle.java b/packages/SettingsLib/src/com/android/settingslib/core/lifecycle/Lifecycle.java
index b2351a9ee0e4..451e5611979a 100644
--- a/packages/SettingsLib/src/com/android/settingslib/core/lifecycle/Lifecycle.java
+++ b/packages/SettingsLib/src/com/android/settingslib/core/lifecycle/Lifecycle.java
@@ -15,11 +15,18 @@
*/
package com.android.settingslib.core.lifecycle;
+import static android.arch.lifecycle.Lifecycle.Event.ON_ANY;
+
import android.annotation.UiThread;
+import android.arch.lifecycle.LifecycleOwner;
+import android.arch.lifecycle.LifecycleRegistry;
+import android.arch.lifecycle.OnLifecycleEvent;
import android.content.Context;
import android.os.Bundle;
+import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v7.preference.PreferenceScreen;
+import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
@@ -44,18 +51,46 @@ import java.util.List;
/**
* Dispatcher for lifecycle events.
*/
-public class Lifecycle {
+public class Lifecycle extends LifecycleRegistry {
+ private static final String TAG = "LifecycleObserver";
+
+ private final List<LifecycleObserver> mObservers = new ArrayList<>();
+ private final LifecycleProxy mProxy = new LifecycleProxy();
- protected final List<LifecycleObserver> mObservers = new ArrayList<>();
+ /**
+ * Creates a new LifecycleRegistry for the given provider.
+ * <p>
+ * You should usually create this inside your LifecycleOwner class's constructor and hold
+ * onto the same instance.
+ *
+ * @param provider The owner LifecycleOwner
+ */
+ public Lifecycle(@NonNull LifecycleOwner provider) {
+ super(provider);
+ addObserver(mProxy);
+ }
/**
* Registers a new observer of lifecycle events.
*/
@UiThread
- public <T extends LifecycleObserver> T addObserver(T observer) {
+ @Override
+ public void addObserver(android.arch.lifecycle.LifecycleObserver observer) {
ThreadUtils.ensureMainThread();
- mObservers.add(observer);
- return observer;
+ super.addObserver(observer);
+ if (observer instanceof LifecycleObserver) {
+ mObservers.add((LifecycleObserver) observer);
+ }
+ }
+
+ @UiThread
+ @Override
+ public void removeObserver(android.arch.lifecycle.LifecycleObserver observer) {
+ ThreadUtils.ensureMainThread();
+ super.removeObserver(observer);
+ if (observer instanceof LifecycleObserver) {
+ mObservers.remove(observer);
+ }
}
public void onAttach(Context context) {
@@ -67,6 +102,8 @@ public class Lifecycle {
}
}
+ // This method is not called from the proxy because it does not have access to the
+ // savedInstanceState
public void onCreate(Bundle savedInstanceState) {
for (int i = 0, size = mObservers.size(); i < size; i++) {
final LifecycleObserver observer = mObservers.get(i);
@@ -76,7 +113,7 @@ public class Lifecycle {
}
}
- public void onStart() {
+ private void onStart() {
for (int i = 0, size = mObservers.size(); i < size; i++) {
final LifecycleObserver observer = mObservers.get(i);
if (observer instanceof OnStart) {
@@ -94,7 +131,7 @@ public class Lifecycle {
}
}
- public void onResume() {
+ private void onResume() {
for (int i = 0, size = mObservers.size(); i < size; i++) {
final LifecycleObserver observer = mObservers.get(i);
if (observer instanceof OnResume) {
@@ -103,7 +140,7 @@ public class Lifecycle {
}
}
- public void onPause() {
+ private void onPause() {
for (int i = 0, size = mObservers.size(); i < size; i++) {
final LifecycleObserver observer = mObservers.get(i);
if (observer instanceof OnPause) {
@@ -121,7 +158,7 @@ public class Lifecycle {
}
}
- public void onStop() {
+ private void onStop() {
for (int i = 0, size = mObservers.size(); i < size; i++) {
final LifecycleObserver observer = mObservers.get(i);
if (observer instanceof OnStop) {
@@ -130,7 +167,7 @@ public class Lifecycle {
}
}
- public void onDestroy() {
+ private void onDestroy() {
for (int i = 0, size = mObservers.size(); i < size; i++) {
final LifecycleObserver observer = mObservers.get(i);
if (observer instanceof OnDestroy) {
@@ -168,4 +205,34 @@ public class Lifecycle {
}
return false;
}
+
+ private class LifecycleProxy
+ implements android.arch.lifecycle.LifecycleObserver {
+ @OnLifecycleEvent(ON_ANY)
+ public void onLifecycleEvent(LifecycleOwner owner, Event event) {
+ switch (event) {
+ case ON_CREATE:
+ // onCreate is called directly since we don't have savedInstanceState here
+ break;
+ case ON_START:
+ onStart();
+ break;
+ case ON_RESUME:
+ onResume();
+ break;
+ case ON_PAUSE:
+ onPause();
+ break;
+ case ON_STOP:
+ onStop();
+ break;
+ case ON_DESTROY:
+ onDestroy();
+ break;
+ case ON_ANY:
+ Log.wtf(TAG, "Should not receive an 'ANY' event!");
+ break;
+ }
+ }
+ }
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/core/lifecycle/LifecycleObserver.java b/packages/SettingsLib/src/com/android/settingslib/core/lifecycle/LifecycleObserver.java
index 6c4107290274..ec8a8b537f48 100644
--- a/packages/SettingsLib/src/com/android/settingslib/core/lifecycle/LifecycleObserver.java
+++ b/packages/SettingsLib/src/com/android/settingslib/core/lifecycle/LifecycleObserver.java
@@ -17,6 +17,9 @@ package com.android.settingslib.core.lifecycle;
/**
* Observer of lifecycle events.
+ * @deprecated use {@link android.arch.lifecycle.LifecycleObserver} instead
*/
-public interface LifecycleObserver {
+@Deprecated
+public interface LifecycleObserver extends
+ android.arch.lifecycle.LifecycleObserver {
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/core/lifecycle/ObservableActivity.java b/packages/SettingsLib/src/com/android/settingslib/core/lifecycle/ObservableActivity.java
index 727bec75c204..8b062f8447b0 100644
--- a/packages/SettingsLib/src/com/android/settingslib/core/lifecycle/ObservableActivity.java
+++ b/packages/SettingsLib/src/com/android/settingslib/core/lifecycle/ObservableActivity.java
@@ -15,8 +15,16 @@
*/
package com.android.settingslib.core.lifecycle;
+import static android.arch.lifecycle.Lifecycle.Event.ON_CREATE;
+import static android.arch.lifecycle.Lifecycle.Event.ON_DESTROY;
+import static android.arch.lifecycle.Lifecycle.Event.ON_PAUSE;
+import static android.arch.lifecycle.Lifecycle.Event.ON_RESUME;
+import static android.arch.lifecycle.Lifecycle.Event.ON_START;
+import static android.arch.lifecycle.Lifecycle.Event.ON_STOP;
+
import android.annotation.Nullable;
import android.app.Activity;
+import android.arch.lifecycle.LifecycleOwner;
import android.os.Bundle;
import android.os.PersistableBundle;
import android.view.Menu;
@@ -25,17 +33,19 @@ import android.view.MenuItem;
/**
* {@link Activity} that has hooks to observe activity lifecycle events.
*/
-public class ObservableActivity extends Activity {
+public class ObservableActivity extends Activity implements LifecycleOwner {
- private final Lifecycle mLifecycle = new Lifecycle();
+ private final Lifecycle mLifecycle = new Lifecycle(this);
- protected Lifecycle getLifecycle() {
+ public Lifecycle getLifecycle() {
return mLifecycle;
}
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
mLifecycle.onAttach(this);
+ mLifecycle.onCreate(savedInstanceState);
+ mLifecycle.handleLifecycleEvent(ON_CREATE);
super.onCreate(savedInstanceState);
}
@@ -43,36 +53,38 @@ public class ObservableActivity extends Activity {
public void onCreate(@Nullable Bundle savedInstanceState,
@Nullable PersistableBundle persistentState) {
mLifecycle.onAttach(this);
+ mLifecycle.onCreate(savedInstanceState);
+ mLifecycle.handleLifecycleEvent(ON_CREATE);
super.onCreate(savedInstanceState, persistentState);
}
@Override
protected void onStart() {
- mLifecycle.onStart();
+ mLifecycle.handleLifecycleEvent(ON_START);
super.onStart();
}
@Override
protected void onResume() {
- mLifecycle.onResume();
+ mLifecycle.handleLifecycleEvent(ON_RESUME);
super.onResume();
}
@Override
protected void onPause() {
- mLifecycle.onPause();
+ mLifecycle.handleLifecycleEvent(ON_PAUSE);
super.onPause();
}
@Override
protected void onStop() {
- mLifecycle.onStop();
+ mLifecycle.handleLifecycleEvent(ON_STOP);
super.onStop();
}
@Override
protected void onDestroy() {
- mLifecycle.onDestroy();
+ mLifecycle.handleLifecycleEvent(ON_DESTROY);
super.onDestroy();
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/core/lifecycle/ObservableDialogFragment.java b/packages/SettingsLib/src/com/android/settingslib/core/lifecycle/ObservableDialogFragment.java
index 315bedc168bb..dc95384b26a5 100644
--- a/packages/SettingsLib/src/com/android/settingslib/core/lifecycle/ObservableDialogFragment.java
+++ b/packages/SettingsLib/src/com/android/settingslib/core/lifecycle/ObservableDialogFragment.java
@@ -15,9 +15,17 @@
*/
package com.android.settingslib.core.lifecycle;
+import static android.arch.lifecycle.Lifecycle.Event.ON_CREATE;
+import static android.arch.lifecycle.Lifecycle.Event.ON_DESTROY;
+import static android.arch.lifecycle.Lifecycle.Event.ON_PAUSE;
+import static android.arch.lifecycle.Lifecycle.Event.ON_RESUME;
+import static android.arch.lifecycle.Lifecycle.Event.ON_START;
+import static android.arch.lifecycle.Lifecycle.Event.ON_STOP;
+
import android.app.DialogFragment;
+import android.arch.lifecycle.LifecycleOwner;
import android.content.Context;
-import android.support.annotation.VisibleForTesting;
+import android.os.Bundle;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
@@ -25,9 +33,9 @@ import android.view.MenuItem;
/**
* {@link DialogFragment} that has hooks to observe fragment lifecycle events.
*/
-public class ObservableDialogFragment extends DialogFragment {
+public class ObservableDialogFragment extends DialogFragment implements LifecycleOwner {
- protected final Lifecycle mLifecycle = createLifecycle();
+ protected final Lifecycle mLifecycle = new Lifecycle(this);
@Override
public void onAttach(Context context) {
@@ -36,32 +44,39 @@ public class ObservableDialogFragment extends DialogFragment {
}
@Override
+ public void onCreate(Bundle savedInstanceState) {
+ mLifecycle.onCreate(savedInstanceState);
+ mLifecycle.handleLifecycleEvent(ON_CREATE);
+ super.onCreate(savedInstanceState);
+ }
+
+ @Override
public void onStart() {
- mLifecycle.onStart();
+ mLifecycle.handleLifecycleEvent(ON_START);
super.onStart();
}
@Override
public void onResume() {
- mLifecycle.onResume();
+ mLifecycle.handleLifecycleEvent(ON_RESUME);
super.onResume();
}
@Override
public void onPause() {
- mLifecycle.onPause();
+ mLifecycle.handleLifecycleEvent(ON_PAUSE);
super.onPause();
}
@Override
public void onStop() {
- mLifecycle.onStop();
+ mLifecycle.handleLifecycleEvent(ON_STOP);
super.onStop();
}
@Override
public void onDestroy() {
- mLifecycle.onDestroy();
+ mLifecycle.handleLifecycleEvent(ON_DESTROY);
super.onDestroy();
}
@@ -86,9 +101,8 @@ public class ObservableDialogFragment extends DialogFragment {
return lifecycleHandled;
}
- @VisibleForTesting(otherwise = VisibleForTesting.NONE)
- /** @return a new lifecycle. */
- public static Lifecycle createLifecycle() {
- return new Lifecycle();
+ @Override
+ public Lifecycle getLifecycle() {
+ return mLifecycle;
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/core/lifecycle/ObservableFragment.java b/packages/SettingsLib/src/com/android/settingslib/core/lifecycle/ObservableFragment.java
index 3a00eba664c4..925eda6ef9d5 100644
--- a/packages/SettingsLib/src/com/android/settingslib/core/lifecycle/ObservableFragment.java
+++ b/packages/SettingsLib/src/com/android/settingslib/core/lifecycle/ObservableFragment.java
@@ -16,19 +16,27 @@
package com.android.settingslib.core.lifecycle;
+import static android.arch.lifecycle.Lifecycle.Event.ON_CREATE;
+import static android.arch.lifecycle.Lifecycle.Event.ON_DESTROY;
+import static android.arch.lifecycle.Lifecycle.Event.ON_PAUSE;
+import static android.arch.lifecycle.Lifecycle.Event.ON_RESUME;
+import static android.arch.lifecycle.Lifecycle.Event.ON_START;
+import static android.arch.lifecycle.Lifecycle.Event.ON_STOP;
+
import android.annotation.CallSuper;
import android.app.Fragment;
+import android.arch.lifecycle.LifecycleOwner;
import android.content.Context;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
-public class ObservableFragment extends Fragment {
+public class ObservableFragment extends Fragment implements LifecycleOwner {
- private final Lifecycle mLifecycle = new Lifecycle();
+ private final Lifecycle mLifecycle = new Lifecycle(this);
- protected Lifecycle getLifecycle() {
+ public Lifecycle getLifecycle() {
return mLifecycle;
}
@@ -43,6 +51,7 @@ public class ObservableFragment extends Fragment {
@Override
public void onCreate(Bundle savedInstanceState) {
mLifecycle.onCreate(savedInstanceState);
+ mLifecycle.handleLifecycleEvent(ON_CREATE);
super.onCreate(savedInstanceState);
}
@@ -56,35 +65,35 @@ public class ObservableFragment extends Fragment {
@CallSuper
@Override
public void onStart() {
- mLifecycle.onStart();
+ mLifecycle.handleLifecycleEvent(ON_START);
super.onStart();
}
@CallSuper
@Override
- public void onStop() {
- mLifecycle.onStop();
- super.onStop();
- }
-
- @CallSuper
- @Override
public void onResume() {
- mLifecycle.onResume();
+ mLifecycle.handleLifecycleEvent(ON_RESUME);
super.onResume();
}
@CallSuper
@Override
public void onPause() {
- mLifecycle.onPause();
+ mLifecycle.handleLifecycleEvent(ON_PAUSE);
super.onPause();
}
@CallSuper
@Override
+ public void onStop() {
+ mLifecycle.handleLifecycleEvent(ON_STOP);
+ super.onStop();
+ }
+
+ @CallSuper
+ @Override
public void onDestroy() {
- mLifecycle.onDestroy();
+ mLifecycle.handleLifecycleEvent(ON_DESTROY);
super.onDestroy();
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/core/lifecycle/ObservablePreferenceFragment.java b/packages/SettingsLib/src/com/android/settingslib/core/lifecycle/ObservablePreferenceFragment.java
index 76e5c8579f05..abd77559612e 100644
--- a/packages/SettingsLib/src/com/android/settingslib/core/lifecycle/ObservablePreferenceFragment.java
+++ b/packages/SettingsLib/src/com/android/settingslib/core/lifecycle/ObservablePreferenceFragment.java
@@ -16,7 +16,15 @@
package com.android.settingslib.core.lifecycle;
+import static android.arch.lifecycle.Lifecycle.Event.ON_CREATE;
+import static android.arch.lifecycle.Lifecycle.Event.ON_DESTROY;
+import static android.arch.lifecycle.Lifecycle.Event.ON_PAUSE;
+import static android.arch.lifecycle.Lifecycle.Event.ON_RESUME;
+import static android.arch.lifecycle.Lifecycle.Event.ON_START;
+import static android.arch.lifecycle.Lifecycle.Event.ON_STOP;
+
import android.annotation.CallSuper;
+import android.arch.lifecycle.LifecycleOwner;
import android.content.Context;
import android.os.Bundle;
import android.support.v14.preference.PreferenceFragment;
@@ -28,11 +36,12 @@ import android.view.MenuItem;
/**
* {@link PreferenceFragment} that has hooks to observe fragment lifecycle events.
*/
-public abstract class ObservablePreferenceFragment extends PreferenceFragment {
+public abstract class ObservablePreferenceFragment extends PreferenceFragment
+ implements LifecycleOwner {
- private final Lifecycle mLifecycle = new Lifecycle();
+ private final Lifecycle mLifecycle = new Lifecycle(this);
- protected Lifecycle getLifecycle() {
+ public Lifecycle getLifecycle() {
return mLifecycle;
}
@@ -47,6 +56,7 @@ public abstract class ObservablePreferenceFragment extends PreferenceFragment {
@Override
public void onCreate(Bundle savedInstanceState) {
mLifecycle.onCreate(savedInstanceState);
+ mLifecycle.handleLifecycleEvent(ON_CREATE);
super.onCreate(savedInstanceState);
}
@@ -66,35 +76,35 @@ public abstract class ObservablePreferenceFragment extends PreferenceFragment {
@CallSuper
@Override
public void onStart() {
- mLifecycle.onStart();
+ mLifecycle.handleLifecycleEvent(ON_START);
super.onStart();
}
@CallSuper
@Override
- public void onStop() {
- mLifecycle.onStop();
- super.onStop();
- }
-
- @CallSuper
- @Override
public void onResume() {
- mLifecycle.onResume();
+ mLifecycle.handleLifecycleEvent(ON_RESUME);
super.onResume();
}
@CallSuper
@Override
public void onPause() {
- mLifecycle.onPause();
+ mLifecycle.handleLifecycleEvent(ON_PAUSE);
super.onPause();
}
@CallSuper
@Override
+ public void onStop() {
+ mLifecycle.handleLifecycleEvent(ON_STOP);
+ super.onStop();
+ }
+
+ @CallSuper
+ @Override
public void onDestroy() {
- mLifecycle.onDestroy();
+ mLifecycle.handleLifecycleEvent(ON_DESTROY);
super.onDestroy();
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/core/lifecycle/events/OnAttach.java b/packages/SettingsLib/src/com/android/settingslib/core/lifecycle/events/OnAttach.java
index 152cbac3ad3d..e28c38736639 100644
--- a/packages/SettingsLib/src/com/android/settingslib/core/lifecycle/events/OnAttach.java
+++ b/packages/SettingsLib/src/com/android/settingslib/core/lifecycle/events/OnAttach.java
@@ -17,6 +17,10 @@ package com.android.settingslib.core.lifecycle.events;
import android.content.Context;
+/**
+ * @deprecated pass {@link Context} in constructor instead
+ */
+@Deprecated
public interface OnAttach {
void onAttach(Context context);
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/core/lifecycle/events/OnCreate.java b/packages/SettingsLib/src/com/android/settingslib/core/lifecycle/events/OnCreate.java
index 44cbf8d26757..ad7068e2f9fb 100644
--- a/packages/SettingsLib/src/com/android/settingslib/core/lifecycle/events/OnCreate.java
+++ b/packages/SettingsLib/src/com/android/settingslib/core/lifecycle/events/OnCreate.java
@@ -16,8 +16,14 @@
package com.android.settingslib.core.lifecycle.events;
+import android.arch.lifecycle.Lifecycle;
+import android.arch.lifecycle.OnLifecycleEvent;
import android.os.Bundle;
+/**
+ * @deprecated use {@link OnLifecycleEvent(Lifecycle.Event) }
+ */
+@Deprecated
public interface OnCreate {
void onCreate(Bundle savedInstanceState);
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/core/lifecycle/events/OnDestroy.java b/packages/SettingsLib/src/com/android/settingslib/core/lifecycle/events/OnDestroy.java
index ffa3d168b904..c37286e1efaa 100644
--- a/packages/SettingsLib/src/com/android/settingslib/core/lifecycle/events/OnDestroy.java
+++ b/packages/SettingsLib/src/com/android/settingslib/core/lifecycle/events/OnDestroy.java
@@ -15,6 +15,13 @@
*/
package com.android.settingslib.core.lifecycle.events;
+import android.arch.lifecycle.Lifecycle;
+import android.arch.lifecycle.OnLifecycleEvent;
+
+/**
+ * @deprecated use {@link OnLifecycleEvent(Lifecycle.Event) }
+ */
+@Deprecated
public interface OnDestroy {
void onDestroy();
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/core/lifecycle/events/OnPause.java b/packages/SettingsLib/src/com/android/settingslib/core/lifecycle/events/OnPause.java
index 4a711058df21..a5ab39c46ab1 100644
--- a/packages/SettingsLib/src/com/android/settingslib/core/lifecycle/events/OnPause.java
+++ b/packages/SettingsLib/src/com/android/settingslib/core/lifecycle/events/OnPause.java
@@ -15,6 +15,13 @@
*/
package com.android.settingslib.core.lifecycle.events;
+import android.arch.lifecycle.Lifecycle;
+import android.arch.lifecycle.OnLifecycleEvent;
+
+/**
+ * @deprecated use {@link OnLifecycleEvent(Lifecycle.Event) }
+ */
+@Deprecated
public interface OnPause {
void onPause();
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/core/lifecycle/events/OnResume.java b/packages/SettingsLib/src/com/android/settingslib/core/lifecycle/events/OnResume.java
index 8dd24e98401d..1effba4a750d 100644
--- a/packages/SettingsLib/src/com/android/settingslib/core/lifecycle/events/OnResume.java
+++ b/packages/SettingsLib/src/com/android/settingslib/core/lifecycle/events/OnResume.java
@@ -15,6 +15,13 @@
*/
package com.android.settingslib.core.lifecycle.events;
+import android.arch.lifecycle.Lifecycle;
+import android.arch.lifecycle.OnLifecycleEvent;
+
+/**
+ * @deprecated use {@link OnLifecycleEvent(Lifecycle.Event)}
+ */
+@Deprecated
public interface OnResume {
void onResume();
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/core/lifecycle/events/OnStart.java b/packages/SettingsLib/src/com/android/settingslib/core/lifecycle/events/OnStart.java
index c88ddaa1d8de..07b8460367c1 100644
--- a/packages/SettingsLib/src/com/android/settingslib/core/lifecycle/events/OnStart.java
+++ b/packages/SettingsLib/src/com/android/settingslib/core/lifecycle/events/OnStart.java
@@ -15,7 +15,13 @@
*/
package com.android.settingslib.core.lifecycle.events;
-public interface OnStart {
+import android.arch.lifecycle.Lifecycle;
+import android.arch.lifecycle.OnLifecycleEvent;
+/**
+ * @deprecated use {@link OnLifecycleEvent(Lifecycle.Event) }
+ */
+@Deprecated
+public interface OnStart {
void onStart();
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/core/lifecycle/events/OnStop.java b/packages/SettingsLib/src/com/android/settingslib/core/lifecycle/events/OnStop.java
index 32f61d98e4ad..d6a5967116be 100644
--- a/packages/SettingsLib/src/com/android/settingslib/core/lifecycle/events/OnStop.java
+++ b/packages/SettingsLib/src/com/android/settingslib/core/lifecycle/events/OnStop.java
@@ -15,7 +15,13 @@
*/
package com.android.settingslib.core.lifecycle.events;
-public interface OnStop {
+import android.arch.lifecycle.Lifecycle;
+import android.arch.lifecycle.OnLifecycleEvent;
+/**
+ * @deprecated use {@link OnLifecycleEvent(Lifecycle.Event) }
+ */
+@Deprecated
+public interface OnStop {
void onStop();
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/datetime/ZoneGetter.java b/packages/SettingsLib/src/com/android/settingslib/datetime/ZoneGetter.java
index 1771208398fd..a8262c8cc4c8 100644
--- a/packages/SettingsLib/src/com/android/settingslib/datetime/ZoneGetter.java
+++ b/packages/SettingsLib/src/com/android/settingslib/datetime/ZoneGetter.java
@@ -20,6 +20,7 @@ import android.content.Context;
import android.content.res.XmlResourceParser;
import android.icu.text.TimeZoneFormat;
import android.icu.text.TimeZoneNames;
+import android.support.annotation.VisibleForTesting;
import android.support.v4.text.BidiFormatter;
import android.support.v4.text.TextDirectionHeuristicsCompat;
import android.text.SpannableString;
@@ -32,6 +33,8 @@ import android.view.View;
import com.android.settingslib.R;
+import libcore.util.TimeZoneFinder;
+
import org.xmlpull.v1.XmlPullParserException;
import java.util.ArrayList;
@@ -349,7 +352,8 @@ public class ZoneGetter {
return gmtText;
}
- private static final class ZoneGetterData {
+ @VisibleForTesting
+ public static final class ZoneGetterData {
public final String[] olsonIdsToDisplay;
public final CharSequence[] gmtOffsetTexts;
public final TimeZone[] timeZones;
@@ -376,10 +380,13 @@ public class ZoneGetter {
}
// Create a lookup of local zone IDs.
- localZoneIds = new HashSet<String>();
- for (String olsonId : libcore.icu.TimeZoneNames.forLocale(locale)) {
- localZoneIds.add(olsonId);
- }
+ final List<String> zoneIds = lookupTimeZoneIdsByCountry(locale.getCountry());
+ localZoneIds = new HashSet<>(zoneIds);
+ }
+
+ @VisibleForTesting
+ public List<String> lookupTimeZoneIdsByCountry(String country) {
+ return TimeZoneFinder.getInstance().lookupTimeZoneIdsByCountry(country);
}
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/development/AbstractEnableAdbPreferenceController.java b/packages/SettingsLib/src/com/android/settingslib/development/AbstractEnableAdbPreferenceController.java
index 75b6696d923c..3c02f6a234f4 100644
--- a/packages/SettingsLib/src/com/android/settingslib/development/AbstractEnableAdbPreferenceController.java
+++ b/packages/SettingsLib/src/com/android/settingslib/development/AbstractEnableAdbPreferenceController.java
@@ -16,11 +16,13 @@
package com.android.settingslib.development;
+import android.app.ActivityManager;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.os.UserManager;
import android.provider.Settings;
+import android.support.annotation.VisibleForTesting;
import android.support.v14.preference.SwitchPreference;
import android.support.v4.content.LocalBroadcastManager;
import android.support.v7.preference.Preference;
@@ -28,15 +30,20 @@ import android.support.v7.preference.PreferenceScreen;
import android.support.v7.preference.TwoStatePreference;
import android.text.TextUtils;
-import com.android.settingslib.core.AbstractPreferenceController;
+import com.android.settingslib.core.ConfirmationDialogController;
-public abstract class AbstractEnableAdbPreferenceController extends AbstractPreferenceController {
+public abstract class AbstractEnableAdbPreferenceController extends
+ DeveloperOptionsPreferenceController implements ConfirmationDialogController {
private static final String KEY_ENABLE_ADB = "enable_adb";
public static final String ACTION_ENABLE_ADB_STATE_CHANGED =
"com.android.settingslib.development.AbstractEnableAdbController."
+ "ENABLE_ADB_STATE_CHANGED";
- private SwitchPreference mPreference;
+ public static final int ADB_SETTING_ON = 1;
+ public static final int ADB_SETTING_OFF = 0;
+
+
+ protected SwitchPreference mPreference;
public AbstractEnableAdbPreferenceController(Context context) {
super(context);
@@ -62,12 +69,13 @@ public abstract class AbstractEnableAdbPreferenceController extends AbstractPref
private boolean isAdbEnabled() {
final ContentResolver cr = mContext.getContentResolver();
- return Settings.Global.getInt(cr, Settings.Global.ADB_ENABLED, 0) != 0;
+ return Settings.Global.getInt(cr, Settings.Global.ADB_ENABLED, ADB_SETTING_OFF)
+ != ADB_SETTING_OFF;
}
@Override
public void updateState(Preference preference) {
- ((TwoStatePreference)preference).setChecked(isAdbEnabled());
+ ((TwoStatePreference) preference).setChecked(isAdbEnabled());
}
public void enablePreference(boolean enabled) {
@@ -89,9 +97,13 @@ public abstract class AbstractEnableAdbPreferenceController extends AbstractPref
@Override
public boolean handlePreferenceTreeClick(Preference preference) {
+ if (isUserAMonkey()) {
+ return false;
+ }
+
if (TextUtils.equals(KEY_ENABLE_ADB, preference.getKey())) {
if (!isAdbEnabled()) {
- showConfirmationDialog((SwitchPreference) preference);
+ showConfirmationDialog(preference);
} else {
writeAdbSetting(false);
}
@@ -103,14 +115,17 @@ public abstract class AbstractEnableAdbPreferenceController extends AbstractPref
protected void writeAdbSetting(boolean enabled) {
Settings.Global.putInt(mContext.getContentResolver(),
- Settings.Global.ADB_ENABLED, enabled ? 1 : 0);
+ Settings.Global.ADB_ENABLED, enabled ? ADB_SETTING_ON : ADB_SETTING_OFF);
notifyStateChanged();
}
- protected void notifyStateChanged() {
+ private void notifyStateChanged() {
LocalBroadcastManager.getInstance(mContext)
.sendBroadcast(new Intent(ACTION_ENABLE_ADB_STATE_CHANGED));
}
- public abstract void showConfirmationDialog(SwitchPreference preference);
+ @VisibleForTesting
+ boolean isUserAMonkey() {
+ return ActivityManager.isUserAMonkey();
+ }
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/development/AbstractLogdSizePreferenceController.java b/packages/SettingsLib/src/com/android/settingslib/development/AbstractLogdSizePreferenceController.java
new file mode 100644
index 000000000000..f79be7eaddb1
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/development/AbstractLogdSizePreferenceController.java
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.development;
+
+import android.content.Context;
+import android.content.Intent;
+import android.os.SystemProperties;
+import android.support.annotation.VisibleForTesting;
+import android.support.v4.content.LocalBroadcastManager;
+import android.support.v7.preference.ListPreference;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceScreen;
+
+import com.android.settingslib.R;
+
+public abstract class AbstractLogdSizePreferenceController extends
+ DeveloperOptionsPreferenceController implements Preference.OnPreferenceChangeListener {
+ public static final String ACTION_LOGD_SIZE_UPDATED = "com.android.settingslib.development."
+ + "AbstractLogdSizePreferenceController.LOGD_SIZE_UPDATED";
+ public static final String EXTRA_CURRENT_LOGD_VALUE = "CURRENT_LOGD_VALUE";
+
+ @VisibleForTesting
+ static final String LOW_RAM_CONFIG_PROPERTY_KEY = "ro.config.low_ram";
+ private static final String SELECT_LOGD_SIZE_KEY = "select_logd_size";
+ @VisibleForTesting
+ static final String SELECT_LOGD_SIZE_PROPERTY = "persist.logd.size";
+ static final String SELECT_LOGD_TAG_PROPERTY = "persist.log.tag";
+ // Tricky, isLoggable only checks for first character, assumes silence
+ static final String SELECT_LOGD_TAG_SILENCE = "Settings";
+ @VisibleForTesting
+ static final String SELECT_LOGD_SNET_TAG_PROPERTY = "persist.log.tag.snet_event_log";
+ private static final String SELECT_LOGD_RUNTIME_SNET_TAG_PROPERTY = "log.tag.snet_event_log";
+ private static final String SELECT_LOGD_DEFAULT_SIZE_PROPERTY = "ro.logd.size";
+ @VisibleForTesting
+ static final String SELECT_LOGD_DEFAULT_SIZE_VALUE = "262144";
+ private static final String SELECT_LOGD_SVELTE_DEFAULT_SIZE_VALUE = "65536";
+ // 32768 is merely a menu marker, 64K is our lowest log buffer size we replace it with.
+ @VisibleForTesting
+ static final String SELECT_LOGD_MINIMUM_SIZE_VALUE = "65536";
+ static final String SELECT_LOGD_OFF_SIZE_MARKER_VALUE = "32768";
+ @VisibleForTesting
+ static final String DEFAULT_SNET_TAG = "I";
+
+ private ListPreference mLogdSize;
+
+ public AbstractLogdSizePreferenceController(Context context) {
+ super(context);
+ }
+
+ @Override
+ public String getPreferenceKey() {
+ return SELECT_LOGD_SIZE_KEY;
+ }
+
+ @Override
+ public void displayPreference(PreferenceScreen screen) {
+ super.displayPreference(screen);
+ if (isAvailable()) {
+ mLogdSize = (ListPreference) screen.findPreference(SELECT_LOGD_SIZE_KEY);
+ }
+ }
+
+ @Override
+ public boolean onPreferenceChange(Preference preference, Object newValue) {
+ if (preference == mLogdSize) {
+ writeLogdSizeOption(newValue);
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ public void enablePreference(boolean enabled) {
+ if (isAvailable()) {
+ mLogdSize.setEnabled(enabled);
+ }
+ }
+
+ private String defaultLogdSizeValue() {
+ String defaultValue = SystemProperties.get(SELECT_LOGD_DEFAULT_SIZE_PROPERTY);
+ if ((defaultValue == null) || (defaultValue.length() == 0)) {
+ if (SystemProperties.get("ro.config.low_ram").equals("true")) {
+ defaultValue = SELECT_LOGD_SVELTE_DEFAULT_SIZE_VALUE;
+ } else {
+ defaultValue = SELECT_LOGD_DEFAULT_SIZE_VALUE;
+ }
+ }
+ return defaultValue;
+ }
+
+ public void updateLogdSizeValues() {
+ if (mLogdSize != null) {
+ String currentTag = SystemProperties.get(SELECT_LOGD_TAG_PROPERTY);
+ String currentValue = SystemProperties.get(SELECT_LOGD_SIZE_PROPERTY);
+ if ((currentTag != null) && currentTag.startsWith(SELECT_LOGD_TAG_SILENCE)) {
+ currentValue = SELECT_LOGD_OFF_SIZE_MARKER_VALUE;
+ }
+ LocalBroadcastManager.getInstance(mContext).sendBroadcastSync(
+ new Intent(ACTION_LOGD_SIZE_UPDATED)
+ .putExtra(EXTRA_CURRENT_LOGD_VALUE, currentValue));
+ if ((currentValue == null) || (currentValue.length() == 0)) {
+ currentValue = defaultLogdSizeValue();
+ }
+ String[] values = mContext.getResources()
+ .getStringArray(R.array.select_logd_size_values);
+ String[] titles = mContext.getResources()
+ .getStringArray(R.array.select_logd_size_titles);
+ int index = 2; // punt to second entry if not found
+ if (SystemProperties.get("ro.config.low_ram").equals("true")) {
+ mLogdSize.setEntries(R.array.select_logd_size_lowram_titles);
+ titles = mContext.getResources()
+ .getStringArray(R.array.select_logd_size_lowram_titles);
+ index = 1;
+ }
+ String[] summaries = mContext.getResources()
+ .getStringArray(R.array.select_logd_size_summaries);
+ for (int i = 0; i < titles.length; i++) {
+ if (currentValue.equals(values[i])
+ || currentValue.equals(titles[i])) {
+ index = i;
+ break;
+ }
+ }
+ mLogdSize.setValue(values[index]);
+ mLogdSize.setSummary(summaries[index]);
+ }
+ }
+
+ public void writeLogdSizeOption(Object newValue) {
+ boolean disable = (newValue != null) &&
+ (newValue.toString().equals(SELECT_LOGD_OFF_SIZE_MARKER_VALUE));
+ String currentTag = SystemProperties.get(SELECT_LOGD_TAG_PROPERTY);
+ if (currentTag == null) {
+ currentTag = "";
+ }
+ // filter clean and unstack all references to our setting
+ String newTag = currentTag.replaceAll(
+ ",+" + SELECT_LOGD_TAG_SILENCE, "").replaceFirst(
+ "^" + SELECT_LOGD_TAG_SILENCE + ",*", "").replaceAll(
+ ",+", ",").replaceFirst(
+ ",+$", "");
+ if (disable) {
+ newValue = SELECT_LOGD_MINIMUM_SIZE_VALUE;
+ // Make sure snet_event_log get through first, but do not override
+ String snetValue = SystemProperties.get(SELECT_LOGD_SNET_TAG_PROPERTY);
+ if ((snetValue == null) || (snetValue.length() == 0)) {
+ snetValue = SystemProperties.get(SELECT_LOGD_RUNTIME_SNET_TAG_PROPERTY);
+ if ((snetValue == null) || (snetValue.length() == 0)) {
+ SystemProperties.set(SELECT_LOGD_SNET_TAG_PROPERTY, DEFAULT_SNET_TAG);
+ }
+ }
+ // Silence all log sources, security logs notwithstanding
+ if (newTag.length() != 0) {
+ newTag = "," + newTag;
+ }
+ // Stack settings, stack to help preserve original value
+ newTag = SELECT_LOGD_TAG_SILENCE + newTag;
+ }
+ if (!newTag.equals(currentTag)) {
+ SystemProperties.set(SELECT_LOGD_TAG_PROPERTY, newTag);
+ }
+ String defaultValue = defaultLogdSizeValue();
+ final String size = ((newValue != null) && (newValue.toString().length() != 0)) ?
+ newValue.toString() : defaultValue;
+ SystemProperties.set(SELECT_LOGD_SIZE_PROPERTY, defaultValue.equals(size) ? "" : size);
+ SystemProperties.set("ctl.start", "logd-reinit");
+ SystemPropPoker.getInstance().poke();
+ updateLogdSizeValues();
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/development/AbstractLogpersistPreferenceController.java b/packages/SettingsLib/src/com/android/settingslib/development/AbstractLogpersistPreferenceController.java
new file mode 100644
index 000000000000..77b2d86c445f
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/development/AbstractLogpersistPreferenceController.java
@@ -0,0 +1,258 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.development;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Bundle;
+import android.os.SystemProperties;
+import android.support.annotation.VisibleForTesting;
+import android.support.v4.content.LocalBroadcastManager;
+import android.support.v7.preference.ListPreference;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceScreen;
+import android.text.TextUtils;
+
+import com.android.settingslib.R;
+import com.android.settingslib.core.ConfirmationDialogController;
+import com.android.settingslib.core.lifecycle.Lifecycle;
+import com.android.settingslib.core.lifecycle.LifecycleObserver;
+import com.android.settingslib.core.lifecycle.events.OnCreate;
+import com.android.settingslib.core.lifecycle.events.OnDestroy;
+
+public abstract class AbstractLogpersistPreferenceController extends
+ DeveloperOptionsPreferenceController implements Preference.OnPreferenceChangeListener,
+ LifecycleObserver, OnCreate, OnDestroy, ConfirmationDialogController {
+
+ private static final String SELECT_LOGPERSIST_KEY = "select_logpersist";
+ private static final String SELECT_LOGPERSIST_PROPERTY = "persist.logd.logpersistd";
+ @VisibleForTesting
+ static final String ACTUAL_LOGPERSIST_PROPERTY = "logd.logpersistd";
+ @VisibleForTesting
+ static final String SELECT_LOGPERSIST_PROPERTY_SERVICE = "logcatd";
+ private static final String SELECT_LOGPERSIST_PROPERTY_CLEAR = "clear";
+ private static final String SELECT_LOGPERSIST_PROPERTY_STOP = "stop";
+ private static final String SELECT_LOGPERSIST_PROPERTY_BUFFER =
+ "persist.logd.logpersistd.buffer";
+ @VisibleForTesting
+ static final String ACTUAL_LOGPERSIST_PROPERTY_BUFFER = "logd.logpersistd.buffer";
+ private static final String ACTUAL_LOGPERSIST_PROPERTY_ENABLE = "logd.logpersistd.enable";
+
+ private ListPreference mLogpersist;
+ private boolean mLogpersistCleared;
+
+ private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final String currentValue = intent.getStringExtra(
+ AbstractLogdSizePreferenceController.EXTRA_CURRENT_LOGD_VALUE);
+ onLogdSizeSettingUpdate(currentValue);
+ }
+ };
+
+ public AbstractLogpersistPreferenceController(Context context, Lifecycle lifecycle) {
+ super(context);
+ if (isAvailable() && lifecycle != null) {
+ lifecycle.addObserver(this);
+ }
+ }
+
+ @Override
+ public boolean isAvailable() {
+ return TextUtils.equals(SystemProperties.get("ro.debuggable", "0"), "1");
+ }
+
+ @Override
+ public String getPreferenceKey() {
+ return SELECT_LOGPERSIST_KEY;
+ }
+
+ @Override
+ public void displayPreference(PreferenceScreen screen) {
+ super.displayPreference(screen);
+ if (isAvailable()) {
+ mLogpersist = (ListPreference) screen.findPreference(SELECT_LOGPERSIST_KEY);
+ }
+ }
+
+ @Override
+ public boolean onPreferenceChange(Preference preference, Object newValue) {
+ if (preference == mLogpersist) {
+ writeLogpersistOption(newValue, false);
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ LocalBroadcastManager.getInstance(mContext).registerReceiver(mReceiver,
+ new IntentFilter(AbstractLogdSizePreferenceController.ACTION_LOGD_SIZE_UPDATED));
+ }
+
+ @Override
+ public void onDestroy() {
+ LocalBroadcastManager.getInstance(mContext).unregisterReceiver(mReceiver);
+ }
+
+ public void enablePreference(boolean enabled) {
+ if (isAvailable()) {
+ mLogpersist.setEnabled(enabled);
+ }
+ }
+
+ private void onLogdSizeSettingUpdate(String currentValue) {
+ if (mLogpersist != null) {
+ String currentLogpersistEnable
+ = SystemProperties.get(ACTUAL_LOGPERSIST_PROPERTY_ENABLE);
+ if ((currentLogpersistEnable == null)
+ || !currentLogpersistEnable.equals("true")
+ || currentValue.equals(
+ AbstractLogdSizePreferenceController.SELECT_LOGD_OFF_SIZE_MARKER_VALUE)) {
+ writeLogpersistOption(null, true);
+ mLogpersist.setEnabled(false);
+ } else if (DevelopmentSettingsEnabler.isDevelopmentSettingsEnabled(mContext)) {
+ mLogpersist.setEnabled(true);
+ }
+ }
+ }
+
+ public void updateLogpersistValues() {
+ if (mLogpersist == null) {
+ return;
+ }
+ String currentValue = SystemProperties.get(ACTUAL_LOGPERSIST_PROPERTY);
+ if (currentValue == null) {
+ currentValue = "";
+ }
+ String currentBuffers = SystemProperties.get(ACTUAL_LOGPERSIST_PROPERTY_BUFFER);
+ if ((currentBuffers == null) || (currentBuffers.length() == 0)) {
+ currentBuffers = "all";
+ }
+ int index = 0;
+ if (currentValue.equals(SELECT_LOGPERSIST_PROPERTY_SERVICE)) {
+ index = 1;
+ if (currentBuffers.equals("kernel")) {
+ index = 3;
+ } else if (!currentBuffers.equals("all") &&
+ !currentBuffers.contains("radio") &&
+ currentBuffers.contains("security") &&
+ currentBuffers.contains("kernel")) {
+ index = 2;
+ if (!currentBuffers.contains("default")) {
+ String[] contains = {"main", "events", "system", "crash"};
+ for (String type : contains) {
+ if (!currentBuffers.contains(type)) {
+ index = 1;
+ break;
+ }
+ }
+ }
+ }
+ }
+ mLogpersist.setValue(
+ mContext.getResources().getStringArray(R.array.select_logpersist_values)[index]);
+ mLogpersist.setSummary(
+ mContext.getResources().getStringArray(R.array.select_logpersist_summaries)[index]);
+ if (index != 0) {
+ mLogpersistCleared = false;
+ } else if (!mLogpersistCleared) {
+ // would File.delete() directly but need to switch uid/gid to access
+ SystemProperties.set(ACTUAL_LOGPERSIST_PROPERTY, SELECT_LOGPERSIST_PROPERTY_CLEAR);
+ SystemPropPoker.getInstance().poke();
+ mLogpersistCleared = true;
+ }
+ }
+
+ protected void setLogpersistOff(boolean update) {
+ SystemProperties.set(SELECT_LOGPERSIST_PROPERTY_BUFFER, "");
+ // deal with trampoline of empty properties
+ SystemProperties.set(ACTUAL_LOGPERSIST_PROPERTY_BUFFER, "");
+ SystemProperties.set(SELECT_LOGPERSIST_PROPERTY, "");
+ SystemProperties.set(ACTUAL_LOGPERSIST_PROPERTY,
+ update ? "" : SELECT_LOGPERSIST_PROPERTY_STOP);
+ SystemPropPoker.getInstance().poke();
+ if (update) {
+ updateLogpersistValues();
+ } else {
+ for (int i = 0; i < 3; i++) {
+ String currentValue = SystemProperties.get(ACTUAL_LOGPERSIST_PROPERTY);
+ if ((currentValue == null) || currentValue.equals("")) {
+ break;
+ }
+ try {
+ Thread.sleep(100);
+ } catch (InterruptedException e) {
+ // Ignore
+ }
+ }
+ }
+ }
+
+ public void writeLogpersistOption(Object newValue, boolean skipWarning) {
+ if (mLogpersist == null) {
+ return;
+ }
+ String currentTag = SystemProperties.get(
+ AbstractLogdSizePreferenceController.SELECT_LOGD_TAG_PROPERTY);
+ if ((currentTag != null) && currentTag.startsWith(
+ AbstractLogdSizePreferenceController.SELECT_LOGD_TAG_SILENCE)) {
+ newValue = null;
+ skipWarning = true;
+ }
+
+ if ((newValue == null) || newValue.toString().equals("")) {
+ if (skipWarning) {
+ mLogpersistCleared = false;
+ } else if (!mLogpersistCleared) {
+ // if transitioning from on to off, pop up an are you sure?
+ String currentValue = SystemProperties.get(ACTUAL_LOGPERSIST_PROPERTY);
+ if ((currentValue != null) &&
+ currentValue.equals(SELECT_LOGPERSIST_PROPERTY_SERVICE)) {
+ showConfirmationDialog(mLogpersist);
+ return;
+ }
+ }
+ setLogpersistOff(true);
+ return;
+ }
+
+ String currentBuffer = SystemProperties.get(ACTUAL_LOGPERSIST_PROPERTY_BUFFER);
+ if ((currentBuffer != null) && !currentBuffer.equals(newValue.toString())) {
+ setLogpersistOff(false);
+ }
+ SystemProperties.set(SELECT_LOGPERSIST_PROPERTY_BUFFER, newValue.toString());
+ SystemProperties.set(SELECT_LOGPERSIST_PROPERTY, SELECT_LOGPERSIST_PROPERTY_SERVICE);
+ SystemPropPoker.getInstance().poke();
+ for (int i = 0; i < 3; i++) {
+ String currentValue = SystemProperties.get(ACTUAL_LOGPERSIST_PROPERTY);
+ if ((currentValue != null)
+ && currentValue.equals(SELECT_LOGPERSIST_PROPERTY_SERVICE)) {
+ break;
+ }
+ try {
+ Thread.sleep(100);
+ } catch (InterruptedException e) {
+ // Ignore
+ }
+ }
+ updateLogpersistValues();
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/development/DeveloperOptionsPreferenceController.java b/packages/SettingsLib/src/com/android/settingslib/development/DeveloperOptionsPreferenceController.java
new file mode 100644
index 000000000000..f68c04f91dbf
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/development/DeveloperOptionsPreferenceController.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.development;
+
+import android.content.Context;
+
+import com.android.settingslib.core.AbstractPreferenceController;
+
+/**
+ * This controller is used handle changes for the master switch in the developer options page.
+ *
+ * All Preference Controllers that are a part of the developer options page should inherit this
+ * class.
+ */
+public abstract class DeveloperOptionsPreferenceController extends
+ AbstractPreferenceController {
+
+ public DeveloperOptionsPreferenceController(Context context) {
+ super(context);
+ }
+
+ /**
+ * Child classes should override this method to create custom logic for hiding preferences.
+ *
+ * @return true if the preference is to be displayed.
+ */
+ @Override
+ public boolean isAvailable() {
+ return true;
+ }
+
+ /**
+ * Called when developer options is enabled
+ */
+ public void onDeveloperOptionsEnabled() {
+ if (isAvailable()) {
+ onDeveloperOptionsSwitchEnabled();
+ }
+ }
+
+ /**
+ * Called when developer options is disabled
+ */
+ public void onDeveloperOptionsDisabled() {
+ if (isAvailable()) {
+ onDeveloperOptionsSwitchDisabled();
+ }
+ }
+
+ /**
+ * Called when developer options is enabled and the preference is available
+ */
+ protected void onDeveloperOptionsSwitchEnabled() {
+ }
+
+ /**
+ * Called when developer options is disabled and the preference is available
+ */
+ protected void onDeveloperOptionsSwitchDisabled() {
+ }
+
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/development/DevelopmentSettingsEnabler.java b/packages/SettingsLib/src/com/android/settingslib/development/DevelopmentSettingsEnabler.java
new file mode 100644
index 000000000000..4e78d9b3d33c
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/development/DevelopmentSettingsEnabler.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.development;
+
+import android.content.Context;
+import android.content.Intent;
+import android.os.Build;
+import android.provider.Settings;
+import android.support.v4.content.LocalBroadcastManager;
+
+public class DevelopmentSettingsEnabler {
+
+ public static final String DEVELOPMENT_SETTINGS_CHANGED_ACTION =
+ "com.android.settingslib.development.DevelopmentSettingsEnabler.SETTINGS_CHANGED";
+
+ private DevelopmentSettingsEnabler() {}
+
+ public static void setDevelopmentSettingsEnabled(Context context, boolean enable) {
+ Settings.Global.putInt(context.getContentResolver(),
+ Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, enable ? 1 : 0);
+ LocalBroadcastManager.getInstance(context)
+ .sendBroadcast(new Intent(DEVELOPMENT_SETTINGS_CHANGED_ACTION));
+ }
+
+ public static boolean isDevelopmentSettingsEnabled(Context context) {
+ return Settings.Global.getInt(context.getContentResolver(),
+ Settings.Global.DEVELOPMENT_SETTINGS_ENABLED,
+ Build.TYPE.equals("eng") ? 1 : 0) != 0;
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/development/SystemPropPoker.java b/packages/SettingsLib/src/com/android/settingslib/development/SystemPropPoker.java
new file mode 100644
index 000000000000..628d0d08b526
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/development/SystemPropPoker.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.development;
+
+import android.os.AsyncTask;
+import android.os.IBinder;
+import android.os.Parcel;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.support.annotation.NonNull;
+import android.support.annotation.VisibleForTesting;
+import android.util.Log;
+
+public class SystemPropPoker {
+ private static final String TAG = "SystemPropPoker";
+
+ private static final SystemPropPoker sInstance = new SystemPropPoker();
+
+ private boolean mBlockPokes = false;
+
+ private SystemPropPoker() {}
+
+ @NonNull
+ public static SystemPropPoker getInstance() {
+ return sInstance;
+ }
+
+ public void blockPokes() {
+ mBlockPokes = true;
+ }
+
+ public void unblockPokes() {
+ mBlockPokes = false;
+ }
+
+ public void poke() {
+ if (!mBlockPokes) {
+ createPokerTask().execute();
+ }
+ }
+
+ @VisibleForTesting
+ PokerTask createPokerTask() {
+ return new PokerTask();
+ }
+
+ public static class PokerTask extends AsyncTask<Void, Void, Void> {
+
+ @VisibleForTesting
+ String[] listServices() {
+ return ServiceManager.listServices();
+ }
+
+ @VisibleForTesting
+ IBinder checkService(String service) {
+ return ServiceManager.checkService(service);
+ }
+
+ @Override
+ protected Void doInBackground(Void... params) {
+ String[] services = listServices();
+ if (services == null) {
+ Log.e(TAG, "There are no services, how odd");
+ return null;
+ }
+ for (String service : services) {
+ IBinder obj = checkService(service);
+ if (obj != null) {
+ Parcel data = Parcel.obtain();
+ try {
+ obj.transact(IBinder.SYSPROPS_TRANSACTION, data, null, 0);
+ } catch (RemoteException e) {
+ // Ignore
+ } catch (Exception e) {
+ Log.i(TAG, "Someone wrote a bad service '" + service
+ + "' that doesn't like to be poked", e);
+ }
+ data.recycle();
+ }
+ }
+ return null;
+ }
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractBluetoothAddressPreferenceController.java b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractBluetoothAddressPreferenceController.java
new file mode 100644
index 000000000000..ba358f83c9d5
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractBluetoothAddressPreferenceController.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.deviceinfo;
+
+import android.annotation.SuppressLint;
+import android.bluetooth.BluetoothAdapter;
+import android.content.Context;
+import android.support.annotation.VisibleForTesting;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceScreen;
+import android.text.TextUtils;
+
+import com.android.settingslib.R;
+import com.android.settingslib.core.lifecycle.Lifecycle;
+
+/**
+ * Preference controller for bluetooth address
+ */
+public abstract class AbstractBluetoothAddressPreferenceController
+ extends AbstractConnectivityPreferenceController {
+
+ @VisibleForTesting
+ static final String KEY_BT_ADDRESS = "bt_address";
+
+ private static final String[] CONNECTIVITY_INTENTS = {
+ BluetoothAdapter.ACTION_STATE_CHANGED
+ };
+
+ private Preference mBtAddress;
+
+ public AbstractBluetoothAddressPreferenceController(Context context, Lifecycle lifecycle) {
+ super(context, lifecycle);
+ }
+
+ @Override
+ public boolean isAvailable() {
+ return BluetoothAdapter.getDefaultAdapter() != null;
+ }
+
+ @Override
+ public String getPreferenceKey() {
+ return KEY_BT_ADDRESS;
+ }
+
+ @Override
+ public void displayPreference(PreferenceScreen screen) {
+ super.displayPreference(screen);
+ mBtAddress = screen.findPreference(KEY_BT_ADDRESS);
+ updateConnectivity();
+ }
+
+ @Override
+ protected String[] getConnectivityIntents() {
+ return CONNECTIVITY_INTENTS;
+ }
+
+ @SuppressLint("HardwareIds")
+ @Override
+ protected void updateConnectivity() {
+ BluetoothAdapter bluetooth = BluetoothAdapter.getDefaultAdapter();
+ if (bluetooth != null && mBtAddress != null) {
+ String address = bluetooth.isEnabled() ? bluetooth.getAddress() : null;
+ if (!TextUtils.isEmpty(address)) {
+ // Convert the address to lowercase for consistency with the wifi MAC address.
+ mBtAddress.setSummary(address.toLowerCase());
+ } else {
+ mBtAddress.setSummary(R.string.status_unavailable);
+ }
+ }
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractConnectivityPreferenceController.java b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractConnectivityPreferenceController.java
new file mode 100644
index 000000000000..c6552f77a2b2
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractConnectivityPreferenceController.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.deviceinfo;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Handler;
+import android.os.Message;
+
+import com.android.internal.util.ArrayUtils;
+import com.android.settingslib.core.AbstractPreferenceController;
+import com.android.settingslib.core.lifecycle.Lifecycle;
+import com.android.settingslib.core.lifecycle.LifecycleObserver;
+import com.android.settingslib.core.lifecycle.events.OnStart;
+import com.android.settingslib.core.lifecycle.events.OnStop;
+
+import java.lang.ref.WeakReference;
+
+/**
+ * Base class for preference controllers which listen to connectivity broadcasts
+ */
+public abstract class AbstractConnectivityPreferenceController
+ extends AbstractPreferenceController implements LifecycleObserver, OnStart, OnStop {
+
+ private final BroadcastReceiver mConnectivityReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ if (ArrayUtils.contains(getConnectivityIntents(), action)) {
+ getHandler().sendEmptyMessage(EVENT_UPDATE_CONNECTIVITY);
+ }
+ }
+ };
+
+ private static final int EVENT_UPDATE_CONNECTIVITY = 600;
+
+ private Handler mHandler;
+
+ public AbstractConnectivityPreferenceController(Context context, Lifecycle lifecycle) {
+ super(context);
+ if (lifecycle != null) {
+ lifecycle.addObserver(this);
+ }
+ }
+
+ @Override
+ public void onStop() {
+ mContext.unregisterReceiver(mConnectivityReceiver);
+ }
+
+ @Override
+ public void onStart() {
+ final IntentFilter connectivityIntentFilter = new IntentFilter();
+ final String[] intents = getConnectivityIntents();
+ for (String intent : intents) {
+ connectivityIntentFilter.addAction(intent);
+ }
+
+ mContext.registerReceiver(mConnectivityReceiver, connectivityIntentFilter,
+ android.Manifest.permission.CHANGE_NETWORK_STATE, null);
+ }
+
+ protected abstract String[] getConnectivityIntents();
+
+ protected abstract void updateConnectivity();
+
+ private Handler getHandler() {
+ if (mHandler == null) {
+ mHandler = new ConnectivityEventHandler(this);
+ }
+ return mHandler;
+ }
+
+ private static class ConnectivityEventHandler extends Handler {
+ private WeakReference<AbstractConnectivityPreferenceController> mPreferenceController;
+
+ public ConnectivityEventHandler(AbstractConnectivityPreferenceController activity) {
+ mPreferenceController = new WeakReference<>(activity);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ AbstractConnectivityPreferenceController preferenceController
+ = mPreferenceController.get();
+ if (preferenceController == null) {
+ return;
+ }
+
+ switch (msg.what) {
+ case EVENT_UPDATE_CONNECTIVITY:
+ preferenceController.updateConnectivity();
+ break;
+ default:
+ throw new IllegalStateException("Unknown message " + msg.what);
+ }
+ }
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractImsStatusPreferenceController.java b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractImsStatusPreferenceController.java
new file mode 100644
index 000000000000..bb8404b0abd5
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractImsStatusPreferenceController.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.deviceinfo;
+
+import android.bluetooth.BluetoothAdapter;
+import android.content.Context;
+import android.net.ConnectivityManager;
+import android.net.wifi.WifiManager;
+import android.os.PersistableBundle;
+import android.support.annotation.VisibleForTesting;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceScreen;
+import android.telephony.CarrierConfigManager;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
+
+import com.android.settingslib.R;
+import com.android.settingslib.core.lifecycle.Lifecycle;
+
+/**
+ * Preference controller for IMS status
+ */
+public abstract class AbstractImsStatusPreferenceController
+ extends AbstractConnectivityPreferenceController {
+
+ @VisibleForTesting
+ static final String KEY_IMS_REGISTRATION_STATE = "ims_reg_state";
+
+ private static final String[] CONNECTIVITY_INTENTS = {
+ BluetoothAdapter.ACTION_STATE_CHANGED,
+ ConnectivityManager.CONNECTIVITY_ACTION,
+ WifiManager.LINK_CONFIGURATION_CHANGED_ACTION,
+ WifiManager.NETWORK_STATE_CHANGED_ACTION,
+ };
+
+ private Preference mImsStatus;
+
+ public AbstractImsStatusPreferenceController(Context context,
+ Lifecycle lifecycle) {
+ super(context, lifecycle);
+ }
+
+ @Override
+ public boolean isAvailable() {
+ CarrierConfigManager configManager = mContext.getSystemService(CarrierConfigManager.class);
+ int subId = SubscriptionManager.getDefaultDataSubscriptionId();
+ PersistableBundle config = null;
+ if (configManager != null) {
+ config = configManager.getConfigForSubId(subId);
+ }
+ return config != null && config.getBoolean(
+ CarrierConfigManager.KEY_SHOW_IMS_REGISTRATION_STATUS_BOOL);
+ }
+
+ @Override
+ public String getPreferenceKey() {
+ return KEY_IMS_REGISTRATION_STATE;
+ }
+
+ @Override
+ public void displayPreference(PreferenceScreen screen) {
+ super.displayPreference(screen);
+ mImsStatus = screen.findPreference(KEY_IMS_REGISTRATION_STATE);
+ updateConnectivity();
+ }
+
+ @Override
+ protected String[] getConnectivityIntents() {
+ return CONNECTIVITY_INTENTS;
+ }
+
+ @Override
+ protected void updateConnectivity() {
+ int subId = SubscriptionManager.getDefaultDataSubscriptionId();
+ if (mImsStatus != null) {
+ TelephonyManager tm = mContext.getSystemService(TelephonyManager.class);
+ mImsStatus.setSummary((tm != null && tm.isImsRegistered(subId)) ?
+ R.string.ims_reg_status_registered : R.string.ims_reg_status_not_registered);
+ }
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractIpAddressPreferenceController.java b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractIpAddressPreferenceController.java
new file mode 100644
index 000000000000..ded30226e2ae
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractIpAddressPreferenceController.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.deviceinfo;
+
+import android.content.Context;
+import android.net.ConnectivityManager;
+import android.net.LinkProperties;
+import android.net.wifi.WifiManager;
+import android.support.annotation.VisibleForTesting;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceScreen;
+
+import com.android.settingslib.R;
+import com.android.settingslib.core.lifecycle.Lifecycle;
+
+import java.net.InetAddress;
+import java.util.Iterator;
+
+/**
+ * Preference controller for IP address
+ */
+public abstract class AbstractIpAddressPreferenceController
+ extends AbstractConnectivityPreferenceController {
+
+ @VisibleForTesting
+ static final String KEY_IP_ADDRESS = "wifi_ip_address";
+
+ private static final String[] CONNECTIVITY_INTENTS = {
+ ConnectivityManager.CONNECTIVITY_ACTION,
+ WifiManager.LINK_CONFIGURATION_CHANGED_ACTION,
+ WifiManager.NETWORK_STATE_CHANGED_ACTION,
+ };
+
+ private Preference mIpAddress;
+ private final ConnectivityManager mCM;
+
+ public AbstractIpAddressPreferenceController(Context context, Lifecycle lifecycle) {
+ super(context, lifecycle);
+ mCM = context.getSystemService(ConnectivityManager.class);
+ }
+
+ @Override
+ public boolean isAvailable() {
+ return true;
+ }
+
+ @Override
+ public String getPreferenceKey() {
+ return KEY_IP_ADDRESS;
+ }
+
+ @Override
+ public void displayPreference(PreferenceScreen screen) {
+ super.displayPreference(screen);
+ mIpAddress = screen.findPreference(KEY_IP_ADDRESS);
+ updateConnectivity();
+ }
+
+ @Override
+ protected String[] getConnectivityIntents() {
+ return CONNECTIVITY_INTENTS;
+ }
+
+ @Override
+ protected void updateConnectivity() {
+ String ipAddress = getDefaultIpAddresses(mCM);
+ if (ipAddress != null) {
+ mIpAddress.setSummary(ipAddress);
+ } else {
+ mIpAddress.setSummary(R.string.status_unavailable);
+ }
+ }
+
+ /**
+ * Returns the default link's IP addresses, if any, taking into account IPv4 and IPv6 style
+ * addresses.
+ * @param cm ConnectivityManager
+ * @return the formatted and newline-separated IP addresses, or null if none.
+ */
+ private static String getDefaultIpAddresses(ConnectivityManager cm) {
+ LinkProperties prop = cm.getActiveLinkProperties();
+ return formatIpAddresses(prop);
+ }
+
+ private static String formatIpAddresses(LinkProperties prop) {
+ if (prop == null) return null;
+ Iterator<InetAddress> iter = prop.getAllAddresses().iterator();
+ // If there are no entries, return null
+ if (!iter.hasNext()) return null;
+ // Concatenate all available addresses, newline separated
+ StringBuilder addresses = new StringBuilder();
+ while (iter.hasNext()) {
+ addresses.append(iter.next().getHostAddress());
+ if (iter.hasNext()) addresses.append("\n");
+ }
+ return addresses.toString();
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractSerialNumberPreferenceController.java b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractSerialNumberPreferenceController.java
new file mode 100644
index 000000000000..90f14ef4c32c
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractSerialNumberPreferenceController.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.deviceinfo;
+
+import android.content.Context;
+import android.os.Build;
+import android.support.annotation.VisibleForTesting;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceScreen;
+import android.text.TextUtils;
+
+import com.android.settingslib.core.AbstractPreferenceController;
+
+/**
+ * Preference controller for displaying device serial number. Wraps {@link Build#getSerial()}.
+ */
+public class AbstractSerialNumberPreferenceController extends AbstractPreferenceController {
+
+ @VisibleForTesting
+ static final String KEY_SERIAL_NUMBER = "serial_number";
+
+ private final String mSerialNumber;
+
+ public AbstractSerialNumberPreferenceController(Context context) {
+ this(context, Build.getSerial());
+ }
+
+ @VisibleForTesting
+ AbstractSerialNumberPreferenceController(Context context, String serialNumber) {
+ super(context);
+ mSerialNumber = serialNumber;
+ }
+
+ @Override
+ public boolean isAvailable() {
+ return !TextUtils.isEmpty(mSerialNumber);
+ }
+
+ @Override
+ public void displayPreference(PreferenceScreen screen) {
+ super.displayPreference(screen);
+ final Preference pref = screen.findPreference(KEY_SERIAL_NUMBER);
+ if (pref != null) {
+ pref.setSummary(mSerialNumber);
+ }
+ }
+
+ @Override
+ public String getPreferenceKey() {
+ return KEY_SERIAL_NUMBER;
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractSimStatusImeiInfoPreferenceController.java b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractSimStatusImeiInfoPreferenceController.java
new file mode 100644
index 000000000000..a78440c271c9
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractSimStatusImeiInfoPreferenceController.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.deviceinfo;
+
+import android.content.Context;
+import android.os.UserManager;
+
+import com.android.settingslib.Utils;
+import com.android.settingslib.core.AbstractPreferenceController;
+
+public abstract class AbstractSimStatusImeiInfoPreferenceController
+ extends AbstractPreferenceController {
+ public AbstractSimStatusImeiInfoPreferenceController(Context context) {
+ super(context);
+ }
+
+ @Override
+ public boolean isAvailable() {
+ return mContext.getSystemService(UserManager.class).isAdminUser()
+ && !Utils.isWifiOnly(mContext);
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractUptimePreferenceController.java b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractUptimePreferenceController.java
new file mode 100644
index 000000000000..ac61ade19222
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractUptimePreferenceController.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.deviceinfo;
+
+import android.content.Context;
+import android.os.Handler;
+import android.os.Message;
+import android.os.SystemClock;
+import android.support.annotation.VisibleForTesting;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceScreen;
+import android.text.format.DateUtils;
+
+import com.android.settingslib.core.AbstractPreferenceController;
+import com.android.settingslib.core.lifecycle.Lifecycle;
+import com.android.settingslib.core.lifecycle.LifecycleObserver;
+import com.android.settingslib.core.lifecycle.events.OnStart;
+import com.android.settingslib.core.lifecycle.events.OnStop;
+
+import java.lang.ref.WeakReference;
+
+/**
+ * Preference controller for uptime
+ */
+public abstract class AbstractUptimePreferenceController extends AbstractPreferenceController
+ implements LifecycleObserver, OnStart, OnStop {
+
+ @VisibleForTesting
+ static final String KEY_UPTIME = "up_time";
+ private static final int EVENT_UPDATE_STATS = 500;
+
+ private Preference mUptime;
+ private Handler mHandler;
+
+ public AbstractUptimePreferenceController(Context context, Lifecycle lifecycle) {
+ super(context);
+ if (lifecycle != null) {
+ lifecycle.addObserver(this);
+ }
+ }
+
+ @Override
+ public void onStart() {
+ getHandler().sendEmptyMessage(EVENT_UPDATE_STATS);
+ }
+
+ @Override
+ public void onStop() {
+ getHandler().removeMessages(EVENT_UPDATE_STATS);
+ }
+
+ @Override
+ public boolean isAvailable() {
+ return true;
+ }
+
+ @Override
+ public String getPreferenceKey() {
+ return KEY_UPTIME;
+ }
+
+ @Override
+ public void displayPreference(PreferenceScreen screen) {
+ super.displayPreference(screen);
+ mUptime = screen.findPreference(KEY_UPTIME);
+ updateTimes();
+ }
+
+ private Handler getHandler() {
+ if (mHandler == null) {
+ mHandler = new MyHandler(this);
+ }
+ return mHandler;
+ }
+
+ private void updateTimes() {
+ mUptime.setSummary(DateUtils.formatDuration(SystemClock.elapsedRealtime()));
+ }
+
+ private static class MyHandler extends Handler {
+ private WeakReference<AbstractUptimePreferenceController> mStatus;
+
+ public MyHandler(AbstractUptimePreferenceController activity) {
+ mStatus = new WeakReference<>(activity);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ AbstractUptimePreferenceController status = mStatus.get();
+ if (status == null) {
+ return;
+ }
+
+ switch (msg.what) {
+ case EVENT_UPDATE_STATS:
+ status.updateTimes();
+ sendEmptyMessageDelayed(EVENT_UPDATE_STATS, 1000);
+ break;
+
+ default:
+ throw new IllegalStateException("Unknown message " + msg.what);
+ }
+ }
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractWifiMacAddressPreferenceController.java b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractWifiMacAddressPreferenceController.java
new file mode 100644
index 000000000000..d57b64f0c0cb
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractWifiMacAddressPreferenceController.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.deviceinfo;
+
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.net.ConnectivityManager;
+import android.net.wifi.WifiInfo;
+import android.net.wifi.WifiManager;
+import android.support.annotation.VisibleForTesting;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceScreen;
+import android.text.TextUtils;
+
+import com.android.settingslib.R;
+import com.android.settingslib.core.lifecycle.Lifecycle;
+
+/**
+ * Preference controller for WIFI MAC address
+ */
+public abstract class AbstractWifiMacAddressPreferenceController
+ extends AbstractConnectivityPreferenceController {
+
+ @VisibleForTesting
+ static final String KEY_WIFI_MAC_ADDRESS = "wifi_mac_address";
+
+ private static final String[] CONNECTIVITY_INTENTS = {
+ ConnectivityManager.CONNECTIVITY_ACTION,
+ WifiManager.LINK_CONFIGURATION_CHANGED_ACTION,
+ WifiManager.NETWORK_STATE_CHANGED_ACTION,
+ };
+
+ private Preference mWifiMacAddress;
+ private final WifiManager mWifiManager;
+
+ public AbstractWifiMacAddressPreferenceController(Context context, Lifecycle lifecycle) {
+ super(context, lifecycle);
+ mWifiManager = context.getSystemService(WifiManager.class);
+ }
+
+ @Override
+ public boolean isAvailable() {
+ return true;
+ }
+
+ @Override
+ public String getPreferenceKey() {
+ return KEY_WIFI_MAC_ADDRESS;
+ }
+
+ @Override
+ public void displayPreference(PreferenceScreen screen) {
+ super.displayPreference(screen);
+ mWifiMacAddress = screen.findPreference(KEY_WIFI_MAC_ADDRESS);
+ updateConnectivity();
+ }
+
+ @Override
+ protected String[] getConnectivityIntents() {
+ return CONNECTIVITY_INTENTS;
+ }
+
+ @SuppressLint("HardwareIds")
+ @Override
+ protected void updateConnectivity() {
+ WifiInfo wifiInfo = mWifiManager.getConnectionInfo();
+ String macAddress = wifiInfo == null ? null : wifiInfo.getMacAddress();
+ if (!TextUtils.isEmpty(macAddress)) {
+ mWifiMacAddress.setSummary(macAddress);
+ } else {
+ mWifiMacAddress.setSummary(R.string.status_unavailable);
+ }
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/CategoryManager.java b/packages/SettingsLib/src/com/android/settingslib/drawer/CategoryManager.java
index ee7885d2a077..07033304653c 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawer/CategoryManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/drawer/CategoryManager.java
@@ -18,7 +18,6 @@ package com.android.settingslib.drawer;
import android.content.ComponentName;
import android.content.Context;
import android.support.annotation.VisibleForTesting;
-import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Log;
@@ -27,7 +26,6 @@ import android.util.Pair;
import com.android.settingslib.applications.InterestingConfigChanges;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -104,10 +102,10 @@ public class CategoryManager {
}
for (int i = 0; i < mCategories.size(); i++) {
DashboardCategory category = mCategories.get(i);
- for (int j = 0; j < category.tiles.size(); j++) {
- Tile tile = category.tiles.get(j);
+ for (int j = 0; j < category.getTilesCount(); j++) {
+ Tile tile = category.getTile(j);
if (tileBlacklist.contains(tile.intent.getComponent())) {
- category.tiles.remove(j--);
+ category.removeTile(j--);
}
}
}
@@ -181,7 +179,7 @@ public class CategoryManager {
newCategory = new DashboardCategory();
categoryByKeyMap.put(newCategoryKey, newCategory);
}
- newCategory.tiles.add(tile);
+ newCategory.addTile(tile);
}
}
}
@@ -198,7 +196,7 @@ public class CategoryManager {
synchronized void sortCategories(Context context,
Map<String, DashboardCategory> categoryByKeyMap) {
for (Entry<String, DashboardCategory> categoryEntry : categoryByKeyMap.entrySet()) {
- sortCategoriesForExternalTiles(context, categoryEntry.getValue());
+ categoryEntry.getValue().sortTiles(context.getPackageName());
}
}
@@ -210,16 +208,16 @@ public class CategoryManager {
synchronized void filterDuplicateTiles(Map<String, DashboardCategory> categoryByKeyMap) {
for (Entry<String, DashboardCategory> categoryEntry : categoryByKeyMap.entrySet()) {
final DashboardCategory category = categoryEntry.getValue();
- final int count = category.tiles.size();
+ final int count = category.getTilesCount();
final Set<ComponentName> components = new ArraySet<>();
for (int i = count - 1; i >= 0; i--) {
- final Tile tile = category.tiles.get(i);
+ final Tile tile = category.getTile(i);
if (tile.intent == null) {
continue;
}
final ComponentName tileComponent = tile.intent.getComponent();
if (components.contains(tileComponent)) {
- category.tiles.remove(i);
+ category.removeTile(i);
} else {
components.add(tileComponent);
}
@@ -234,28 +232,7 @@ public class CategoryManager {
*/
private synchronized void sortCategoriesForExternalTiles(Context context,
DashboardCategory dashboardCategory) {
- final String skipPackageName = context.getPackageName();
+ dashboardCategory.sortTiles(context.getPackageName());
- // Sort tiles based on [priority, package within priority]
- Collections.sort(dashboardCategory.tiles, (tile1, tile2) -> {
- final String package1 = tile1.intent.getComponent().getPackageName();
- final String package2 = tile2.intent.getComponent().getPackageName();
- final int packageCompare = CASE_INSENSITIVE_ORDER.compare(package1, package2);
- // First sort by priority
- final int priorityCompare = tile2.priority - tile1.priority;
- if (priorityCompare != 0) {
- return priorityCompare;
- }
- // Then sort by package name, skip package take precedence
- if (packageCompare != 0) {
- if (TextUtils.equals(package1, skipPackageName)) {
- return -1;
- }
- if (TextUtils.equals(package2, skipPackageName)) {
- return 1;
- }
- }
- return packageCompare;
- });
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/DashboardCategory.java b/packages/SettingsLib/src/com/android/settingslib/drawer/DashboardCategory.java
index f6f81682ad6f..3a03644b6226 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawer/DashboardCategory.java
+++ b/packages/SettingsLib/src/com/android/settingslib/drawer/DashboardCategory.java
@@ -16,6 +16,8 @@
package com.android.settingslib.drawer;
+import static java.lang.String.CASE_INSENSITIVE_ORDER;
+
import android.content.ComponentName;
import android.os.Parcel;
import android.os.Parcelable;
@@ -23,6 +25,8 @@ import android.text.TextUtils;
import android.util.Log;
import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
import java.util.List;
public class DashboardCategory implements Parcelable {
@@ -48,39 +52,63 @@ public class DashboardCategory implements Parcelable {
/**
* List of the category's children
*/
- public List<Tile> tiles = new ArrayList<>();
-
+ private List<Tile> mTiles = new ArrayList<>();
+
+ DashboardCategory(DashboardCategory in) {
+ if (in != null) {
+ title = in.title;
+ key = in.key;
+ priority = in.priority;
+ for (Tile tile : in.mTiles) {
+ mTiles.add(tile);
+ }
+ }
+ }
public DashboardCategory() {
// Empty
}
- public void addTile(Tile tile) {
- tiles.add(tile);
+ /**
+ * Get a copy of the list of the category's children.
+ *
+ * Note: the returned list serves as a read-only list. If tiles needs to be added or removed
+ * from the actual tiles list, it should be done through {@link #addTile}, {@link #removeTile}.
+ */
+ public synchronized List<Tile> getTiles() {
+ final List<Tile> result = new ArrayList<>(mTiles.size());
+ for (Tile tile : mTiles) {
+ result.add(tile);
+ }
+ return result;
+ }
+
+ public synchronized void addTile(Tile tile) {
+ mTiles.add(tile);
}
- public void addTile(int n, Tile tile) {
- tiles.add(n, tile);
+ public synchronized void addTile(int n, Tile tile) {
+ mTiles.add(n, tile);
}
- public void removeTile(Tile tile) {
- tiles.remove(tile);
+ public synchronized void removeTile(Tile tile) {
+ mTiles.remove(tile);
}
- public void removeTile(int n) {
- tiles.remove(n);
+ public synchronized void removeTile(int n) {
+ mTiles.remove(n);
}
public int getTilesCount() {
- return tiles.size();
+ return mTiles.size();
}
public Tile getTile(int n) {
- return tiles.get(n);
+ return mTiles.get(n);
}
- public boolean containsComponent(ComponentName component) {
- for (Tile tile : tiles) {
+ public synchronized boolean containsComponent(ComponentName component) {
+ for (Tile tile : mTiles) {
if (TextUtils.equals(tile.intent.getComponent().getClassName(),
component.getClassName())) {
if (DEBUG) {
@@ -95,6 +123,40 @@ public class DashboardCategory implements Parcelable {
return false;
}
+ /**
+ * Sort priority value for tiles in this category.
+ */
+ public void sortTiles() {
+ Collections.sort(mTiles, TILE_COMPARATOR);
+ }
+
+ /**
+ * Sort priority value and package name for tiles in this category.
+ */
+ public synchronized void sortTiles(String skipPackageName) {
+ // Sort mTiles based on [priority, package within priority]
+ Collections.sort(mTiles, (tile1, tile2) -> {
+ final String package1 = tile1.intent.getComponent().getPackageName();
+ final String package2 = tile2.intent.getComponent().getPackageName();
+ final int packageCompare = CASE_INSENSITIVE_ORDER.compare(package1, package2);
+ // First sort by priority
+ final int priorityCompare = tile2.priority - tile1.priority;
+ if (priorityCompare != 0) {
+ return priorityCompare;
+ }
+ // Then sort by package name, skip package take precedence
+ if (packageCompare != 0) {
+ if (TextUtils.equals(package1, skipPackageName)) {
+ return -1;
+ }
+ if (TextUtils.equals(package2, skipPackageName)) {
+ return 1;
+ }
+ }
+ return packageCompare;
+ });
+ }
+
@Override
public int describeContents() {
return 0;
@@ -106,11 +168,11 @@ public class DashboardCategory implements Parcelable {
dest.writeString(key);
dest.writeInt(priority);
- final int count = tiles.size();
+ final int count = mTiles.size();
dest.writeInt(count);
for (int n = 0; n < count; n++) {
- Tile tile = tiles.get(n);
+ Tile tile = mTiles.get(n);
tile.writeToParcel(dest, flags);
}
}
@@ -124,7 +186,7 @@ public class DashboardCategory implements Parcelable {
for (int n = 0; n < count; n++) {
Tile tile = Tile.CREATOR.createFromParcel(in);
- tiles.add(tile);
+ mTiles.add(tile);
}
}
@@ -141,4 +203,13 @@ public class DashboardCategory implements Parcelable {
return new DashboardCategory[size];
}
};
+
+ public static final Comparator<Tile> TILE_COMPARATOR =
+ new Comparator<Tile>() {
+ @Override
+ public int compare(Tile lhs, Tile rhs) {
+ return rhs.priority - lhs.priority;
+ }
+ };
+
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/TileUtils.java b/packages/SettingsLib/src/com/android/settingslib/drawer/TileUtils.java
index 6e676bc4f460..e986e0f78de4 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawer/TileUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/drawer/TileUtils.java
@@ -65,7 +65,7 @@ public class TileUtils {
*
* <p>A summary my be defined by meta-data named {@link #META_DATA_PREFERENCE_SUMMARY}
*/
- private static final String EXTRA_SETTINGS_ACTION =
+ public static final String EXTRA_SETTINGS_ACTION =
"com.android.settings.action.EXTRA_SETTINGS";
/**
@@ -149,13 +149,6 @@ public class TileUtils {
public static final String META_DATA_PREFERENCE_TITLE = "com.android.settings.title";
/**
- * @deprecated Use {@link #META_DATA_PREFERENCE_TITLE} with {@code android:resource}
- */
- @Deprecated
- public static final String META_DATA_PREFERENCE_TITLE_RES_ID =
- "com.android.settings.title.resid";
-
- /**
* Name of the meta-data item that should be set in the AndroidManifest.xml
* to specify the summary text that should be displayed for the preference.
*/
@@ -176,7 +169,7 @@ public class TileUtils {
* custom view which should be displayed for the preference. The custom view will be inflated
* as a remote view.
*
- * This also can be used with {@link META_DATA_PREFERENCE_SUMMARY_URI} above, by setting the id
+ * This also can be used with {@link #META_DATA_PREFERENCE_SUMMARY_URI}, by setting the id
* of the summary TextView to '@android:id/summary'.
*/
public static final String META_DATA_PREFERENCE_CUSTOM_VIEW =
@@ -260,7 +253,7 @@ public class TileUtils {
}
ArrayList<DashboardCategory> categories = new ArrayList<>(categoryMap.values());
for (DashboardCategory category : categories) {
- Collections.sort(category.tiles, TILE_COMPARATOR);
+ category.sortTiles();
}
Collections.sort(categories, CATEGORY_COMPARATOR);
if (DEBUG_TIMING) Log.d(LOG_TAG, "getCategories took "
@@ -421,14 +414,7 @@ public class TileUtils {
metaData.getBoolean(META_DATA_PREFERENCE_ICON_TINTABLE);
}
}
- int resId = 0;
- if (metaData.containsKey(META_DATA_PREFERENCE_TITLE_RES_ID)) {
- resId = metaData.getInt(META_DATA_PREFERENCE_TITLE_RES_ID);
- if (resId != 0) {
- title = res.getString(resId);
- }
- }
- if ((resId == 0) && metaData.containsKey(META_DATA_PREFERENCE_TITLE)) {
+ if (metaData.containsKey(META_DATA_PREFERENCE_TITLE)) {
if (metaData.get(META_DATA_PREFERENCE_TITLE) instanceof Integer) {
title = res.getString(metaData.getInt(META_DATA_PREFERENCE_TITLE));
} else {
@@ -466,12 +452,14 @@ public class TileUtils {
}
// Set the icon
- if (iconFromUri != null) {
- tile.icon = Icon.createWithResource(iconFromUri.first, iconFromUri.second);
- } else {
- if (icon == 0) {
+ if (icon == 0) {
+ // Only fallback to activityinfo.icon if metadata does not contain ICON_URI.
+ // ICON_URI should be loaded in app UI when need the icon object.
+ if (!tile.metaData.containsKey(META_DATA_PREFERENCE_ICON_URI)) {
icon = activityInfo.icon;
}
+ }
+ if (icon != 0) {
tile.icon = Icon.createWithResource(activityInfo.packageName, icon);
}
@@ -513,7 +501,7 @@ public class TileUtils {
/**
* Gets the icon package name and resource id from content provider.
- * @param Context context
+ * @param context context
* @param packageName package name of the target activity
* @param uriString URI for the content provider
* @param providerMap Maps URI authorities to providers
@@ -543,7 +531,7 @@ public class TileUtils {
/**
* Gets text associated with the input key from the content provider.
- * @param Context context
+ * @param context context
* @param uriString URI for the content provider
* @param providerMap Maps URI authorities to providers
* @param key Key mapping to the text in bundle returned by the content provider
@@ -607,14 +595,6 @@ public class TileUtils {
return pathSegments.get(0);
}
- public static final Comparator<Tile> TILE_COMPARATOR =
- new Comparator<Tile>() {
- @Override
- public int compare(Tile lhs, Tile rhs) {
- return rhs.priority - lhs.priority;
- }
- };
-
private static final Comparator<DashboardCategory> CATEGORY_COMPARATOR =
new Comparator<DashboardCategory>() {
@Override
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/UserAdapter.java b/packages/SettingsLib/src/com/android/settingslib/drawer/UserAdapter.java
index 750601d82ce5..8a09df2849c9 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawer/UserAdapter.java
+++ b/packages/SettingsLib/src/com/android/settingslib/drawer/UserAdapter.java
@@ -57,14 +57,15 @@ public class UserAdapter implements SpinnerAdapter, ListAdapter {
if (userInfo.isManagedProfile()) {
mName = context.getString(R.string.managed_user_title);
icon = context.getDrawable(
- com.android.internal.R.drawable.ic_corp_icon);
+ com.android.internal.R.drawable.ic_corp_badge);
} else {
mName = userInfo.name;
final int userId = userInfo.id;
if (um.getUserIcon(userId) != null) {
icon = new BitmapDrawable(context.getResources(), um.getUserIcon(userId));
} else {
- icon = UserIcons.getDefaultUserIcon(userId, /* light= */ false);
+ icon = UserIcons.getDefaultUserIcon(
+ context.getResources(), userId, /* light= */ false);
}
}
this.mIcon = encircle(context, icon);
diff --git a/packages/SettingsLib/src/com/android/settingslib/graph/BatteryMeterDrawableBase.java b/packages/SettingsLib/src/com/android/settingslib/graph/BatteryMeterDrawableBase.java
index ec45b7e91700..4fe9d56a4179 100755
--- a/packages/SettingsLib/src/com/android/settingslib/graph/BatteryMeterDrawableBase.java
+++ b/packages/SettingsLib/src/com/android/settingslib/graph/BatteryMeterDrawableBase.java
@@ -16,7 +16,6 @@
package com.android.settingslib.graph;
-import android.animation.ArgbEvaluator;
import android.annotation.Nullable;
import android.content.Context;
import android.content.res.Resources;
@@ -90,7 +89,6 @@ public class BatteryMeterDrawableBase extends Drawable {
private final RectF mPlusFrame = new RectF();
private final Path mShapePath = new Path();
- private final Path mClipPath = new Path();
private final Path mTextPath = new Path();
public BatteryMeterDrawableBase(Context context, int frameColor) {
@@ -101,7 +99,7 @@ public class BatteryMeterDrawableBase extends Drawable {
final int N = levels.length();
mColors = new int[2 * N];
- for (int i=0; i < N; i++) {
+ for (int i = 0; i < N; i++) {
mColors[2 * i] = levels.getInt(i, 0);
if (colors.getType(i) == TypedValue.TYPE_ATTRIBUTE) {
mColors[2 * i + 1] = Utils.getColorAttr(context, colors.getThemeAttributeId(i, 0));
@@ -416,8 +414,8 @@ public class BatteryMeterDrawableBase extends Drawable {
: (mLevel == 100 ? 0.38f : 0.5f)));
mTextHeight = -mTextPaint.getFontMetrics().ascent;
pctText = String.valueOf(SINGLE_DIGIT_PERCENT ? (level / 10) : level);
- pctX = mWidth * 0.5f;
- pctY = (mHeight + mTextHeight) * 0.47f;
+ pctX = mWidth * 0.5f + left;
+ pctY = (mHeight + mTextHeight) * 0.47f + top;
pctOpaque = levelTop > pctY;
if (!pctOpaque) {
mTextPath.reset();
@@ -432,16 +430,16 @@ public class BatteryMeterDrawableBase extends Drawable {
// draw the battery shape, clipped to charging level
mFrame.top = levelTop;
- mClipPath.reset();
- mClipPath.addRect(mFrame, Path.Direction.CCW);
- mShapePath.op(mClipPath, Path.Op.INTERSECT);
+ c.save();
+ c.clipRect(mFrame);
c.drawPath(mShapePath, mBatteryPaint);
+ c.restore();
if (!mCharging && !mPowerSaveEnabled) {
if (level <= mCriticalLevel) {
// draw the warning text
- final float x = mWidth * 0.5f;
- final float y = (mHeight + mWarningTextHeight) * 0.48f;
+ final float x = mWidth * 0.5f + left;
+ final float y = (mHeight + mWarningTextHeight) * 0.48f + top;
c.drawText(mWarningString, x, y, mWarningTextPaint);
} else if (pctOpaque) {
// draw the percentage text
diff --git a/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodPreference.java b/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodPreference.java
index 1bbc878b56c9..5e25f519a130 100755..100644
--- a/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodPreference.java
+++ b/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodPreference.java
@@ -34,6 +34,7 @@ import android.view.inputmethod.InputMethodManager;
import android.view.inputmethod.InputMethodSubtype;
import android.widget.Toast;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.inputmethod.InputMethodUtils;
import com.android.settingslib.R;
import com.android.settingslib.RestrictedLockUtils;
@@ -91,20 +92,28 @@ public class InputMethodPreference extends RestrictedSwitchPreference implements
public InputMethodPreference(final Context context, final InputMethodInfo imi,
final boolean isImeEnabler, final boolean isAllowedByOrganization,
final OnSavePreferenceListener onSaveListener) {
+ this(context, imi, imi.loadLabel(context.getPackageManager()), isAllowedByOrganization,
+ onSaveListener);
+ if (!isImeEnabler) {
+ // Remove switch widget.
+ setWidgetLayoutResource(NO_WIDGET);
+ }
+ }
+
+ @VisibleForTesting
+ InputMethodPreference(final Context context, final InputMethodInfo imi,
+ final CharSequence title, final boolean isAllowedByOrganization,
+ final OnSavePreferenceListener onSaveListener) {
super(context);
setPersistent(false);
mImi = imi;
mIsAllowedByOrganization = isAllowedByOrganization;
mOnSaveListener = onSaveListener;
- if (!isImeEnabler) {
- // Remove switch widget.
- setWidgetLayoutResource(NO_WIDGET);
- }
// Disable on/off switch texts.
setSwitchTextOn(EMPTY_TEXT);
setSwitchTextOff(EMPTY_TEXT);
setKey(imi.getId());
- setTitle(imi.loadLabel(context.getPackageManager()));
+ setTitle(title);
final String settingsActivity = imi.getSettingsActivity();
if (TextUtils.isEmpty(settingsActivity)) {
setIntent(null);
@@ -283,18 +292,18 @@ public class InputMethodPreference extends RestrictedSwitchPreference implements
if (this == rhs) {
return 0;
}
- if (mHasPriorityInSorting == rhs.mHasPriorityInSorting) {
- final CharSequence t0 = getTitle();
- final CharSequence t1 = rhs.getTitle();
- if (TextUtils.isEmpty(t0)) {
- return 1;
- }
- if (TextUtils.isEmpty(t1)) {
- return -1;
- }
- return collator.compare(t0.toString(), t1.toString());
+ if (mHasPriorityInSorting != rhs.mHasPriorityInSorting) {
+ // Prefer always checked system IMEs
+ return mHasPriorityInSorting ? -1 : 1;
+ }
+ final CharSequence title = getTitle();
+ final CharSequence rhsTitle = rhs.getTitle();
+ final boolean emptyTitle = TextUtils.isEmpty(title);
+ final boolean rhsEmptyTitle = TextUtils.isEmpty(rhsTitle);
+ if (!emptyTitle && !rhsEmptyTitle) {
+ return collator.compare(title.toString(), rhsTitle.toString());
}
- // Prefer always checked system IMEs
- return mHasPriorityInSorting ? -1 : 1;
+ // For historical reasons, an empty text needs to be put at the first.
+ return (emptyTitle ? -1 : 0) - (rhsEmptyTitle ? -1 : 0);
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodSubtypePreference.java b/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodSubtypePreference.java
index 5fdab2967d1e..f824ec75968b 100644
--- a/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodSubtypePreference.java
+++ b/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodSubtypePreference.java
@@ -17,12 +17,12 @@
package com.android.settingslib.inputmethod;
import android.content.Context;
-import android.support.v14.preference.SwitchPreference;
import android.support.v7.preference.Preference;
import android.text.TextUtils;
import android.view.inputmethod.InputMethodInfo;
import android.view.inputmethod.InputMethodSubtype;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.inputmethod.InputMethodUtils;
import java.text.Collator;
@@ -39,18 +39,28 @@ public class InputMethodSubtypePreference extends SwitchWithNoTextPreference {
public InputMethodSubtypePreference(final Context context, final InputMethodSubtype subtype,
final InputMethodInfo imi) {
+ this(context,
+ imi.getId() + subtype.hashCode(),
+ InputMethodAndSubtypeUtil.getSubtypeLocaleNameAsSentence(subtype, context, imi),
+ subtype.getLocale(),
+ context.getResources().getConfiguration().locale);
+ }
+
+ @VisibleForTesting
+ InputMethodSubtypePreference(
+ final Context context,
+ final String prefKey,
+ final CharSequence title,
+ final String subtypeLocaleString,
+ final Locale systemLocale) {
super(context);
setPersistent(false);
- setKey(imi.getId() + subtype.hashCode());
- final CharSequence subtypeLabel =
- InputMethodAndSubtypeUtil.getSubtypeLocaleNameAsSentence(subtype, context, imi);
- setTitle(subtypeLabel);
- final String subtypeLocaleString = subtype.getLocale();
+ setKey(prefKey);
+ setTitle(title);
if (TextUtils.isEmpty(subtypeLocaleString)) {
mIsSystemLocale = false;
mIsSystemLanguage = false;
} else {
- final Locale systemLocale = context.getResources().getConfiguration().locale;
mIsSystemLocale = subtypeLocaleString.equals(systemLocale.toString());
mIsSystemLanguage = mIsSystemLocale
|| InputMethodUtils.getLanguageFromLocaleString(subtypeLocaleString)
@@ -76,15 +86,15 @@ public class InputMethodSubtypePreference extends SwitchWithNoTextPreference {
if (!mIsSystemLanguage && rhsPref.mIsSystemLanguage) {
return 1;
}
- final CharSequence t0 = getTitle();
- final CharSequence t1 = rhs.getTitle();
- if (t0 == null && t1 == null) {
- return Integer.compare(hashCode(), rhs.hashCode());
- }
- if (t0 != null && t1 != null) {
- return collator.compare(t0.toString(), t1.toString());
+ final CharSequence title = getTitle();
+ final CharSequence rhsTitle = rhs.getTitle();
+ final boolean emptyTitle = TextUtils.isEmpty(title);
+ final boolean rhsEmptyTitle = TextUtils.isEmpty(rhsTitle);
+ if (!emptyTitle && !rhsEmptyTitle) {
+ return collator.compare(title.toString(), rhsTitle.toString());
}
- return t0 == null ? -1 : 1;
+ // For historical reasons, an empty text needs to be put at the first.
+ return (emptyTitle ? -1 : 0) - (rhsEmptyTitle ? -1 : 0);
}
return super.compareTo(rhs);
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/inputmethod/OWNERS b/packages/SettingsLib/src/com/android/settingslib/inputmethod/OWNERS
new file mode 100644
index 000000000000..a0e28baee3f6
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/inputmethod/OWNERS
@@ -0,0 +1,5 @@
+# Default reviewers for this and subdirectories.
+takaoka@google.com
+yukawa@google.com
+
+# Emergency approvers in case the above are not available \ No newline at end of file
diff --git a/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlGeneratorFromXml.java b/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlGeneratorFromXml.java
new file mode 100644
index 000000000000..e3e27ce17c9a
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlGeneratorFromXml.java
@@ -0,0 +1,292 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.license;
+
+import android.support.annotation.VisibleForTesting;
+import android.text.TextUtils;
+import android.util.Log;
+import android.util.Xml;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.zip.GZIPInputStream;
+
+/**
+ * The utility class that generate a license html file from xml files.
+ * All the HTML snippets and logic are copied from build/make/tools/generate-notice-files.py.
+ *
+ * TODO: Remove duplicate codes once backward support ends.
+ */
+class LicenseHtmlGeneratorFromXml {
+ private static final String TAG = "LicenseHtmlGeneratorFromXml";
+
+ private static final String TAG_ROOT = "licenses";
+ private static final String TAG_FILE_NAME = "file-name";
+ private static final String TAG_FILE_CONTENT = "file-content";
+ private static final String ATTR_CONTENT_ID = "contentId";
+
+ private static final String HTML_HEAD_STRING =
+ "<html><head>\n"
+ + "<style type=\"text/css\">\n"
+ + "body { padding: 0; font-family: sans-serif; }\n"
+ + ".same-license { background-color: #eeeeee;\n"
+ + " border-top: 20px solid white;\n"
+ + " padding: 10px; }\n"
+ + ".label { font-weight: bold; }\n"
+ + ".file-list { margin-left: 1em; color: blue; }\n"
+ + "</style>\n"
+ + "</head>"
+ + "<body topmargin=\"0\" leftmargin=\"0\" rightmargin=\"0\" bottommargin=\"0\">\n"
+ + "<div class=\"toc\">\n"
+ + "<ul>";
+
+ private static final String HTML_MIDDLE_STRING =
+ "</ul>\n"
+ + "</div><!-- table of contents -->\n"
+ + "<table cellpadding=\"0\" cellspacing=\"0\" border=\"0\">";
+
+ private static final String HTML_REAR_STRING =
+ "</table></body></html>";
+
+ private final List<File> mXmlFiles;
+
+ /*
+ * A map from a file name to a content id (MD5 sum of file content) for its license.
+ * For example, "/system/priv-app/TeleService/TeleService.apk" maps to
+ * "9645f39e9db895a4aa6e02cb57294595". Here "9645f39e9db895a4aa6e02cb57294595" is a MD5 sum
+ * of the content of packages/services/Telephony/MODULE_LICENSE_APACHE2.
+ */
+ private final Map<String, String> mFileNameToContentIdMap = new HashMap();
+
+ /*
+ * A map from a content id (MD5 sum of file content) to a license file content.
+ * For example, "9645f39e9db895a4aa6e02cb57294595" maps to the content string of
+ * packages/services/Telephony/MODULE_LICENSE_APACHE2. Here "9645f39e9db895a4aa6e02cb57294595"
+ * is a MD5 sum of the file content.
+ */
+ private final Map<String, String> mContentIdToFileContentMap = new HashMap();
+
+ static class ContentIdAndFileNames {
+ final String mContentId;
+ final List<String> mFileNameList = new ArrayList();
+
+ ContentIdAndFileNames(String contentId) {
+ mContentId = contentId;
+ }
+ }
+
+ private LicenseHtmlGeneratorFromXml(List<File> xmlFiles) {
+ mXmlFiles = xmlFiles;
+ }
+
+ public static boolean generateHtml(List<File> xmlFiles, File outputFile) {
+ LicenseHtmlGeneratorFromXml genertor = new LicenseHtmlGeneratorFromXml(xmlFiles);
+ return genertor.generateHtml(outputFile);
+ }
+
+ private boolean generateHtml(File outputFile) {
+ for (File xmlFile : mXmlFiles) {
+ parse(xmlFile);
+ }
+
+ if (mFileNameToContentIdMap.isEmpty() || mContentIdToFileContentMap.isEmpty()) {
+ return false;
+ }
+
+ PrintWriter writer = null;
+ try {
+ writer = new PrintWriter(outputFile);
+
+ generateHtml(mFileNameToContentIdMap, mContentIdToFileContentMap, writer);
+
+ writer.flush();
+ writer.close();
+ return true;
+ } catch (FileNotFoundException | SecurityException e) {
+ Log.e(TAG, "Failed to generate " + outputFile, e);
+
+ if (writer != null) {
+ writer.close();
+ }
+ return false;
+ }
+ }
+
+ private void parse(File xmlFile) {
+ if (xmlFile == null || !xmlFile.exists() || xmlFile.length() == 0) {
+ return;
+ }
+
+ InputStreamReader in = null;
+ try {
+ if (xmlFile.getName().endsWith(".gz")) {
+ in = new InputStreamReader(new GZIPInputStream(new FileInputStream(xmlFile)));
+ } else {
+ in = new FileReader(xmlFile);
+ }
+
+ parse(in, mFileNameToContentIdMap, mContentIdToFileContentMap);
+
+ in.close();
+ } catch (XmlPullParserException | IOException e) {
+ Log.e(TAG, "Failed to parse " + xmlFile, e);
+ if (in != null) {
+ try {
+ in.close();
+ } catch (IOException ie) {
+ Log.w(TAG, "Failed to close " + xmlFile);
+ }
+ }
+ }
+ }
+
+ /*
+ * Parses an input stream and fills a map from a file name to a content id for its license
+ * and a map from a content id to a license file content.
+ *
+ * Following xml format is expected from the input stream.
+ *
+ * <licenses>
+ * <file-name contentId="content_id_of_license1">file1</file-name>
+ * <file-name contentId="content_id_of_license2">file2</file-name>
+ * ...
+ * <file-content contentId="content_id_of_license1">license1 file contents</file-content>
+ * <file-content contentId="content_id_of_license2">license2 file contents</file-content>
+ * ...
+ * </licenses>
+ */
+ @VisibleForTesting
+ static void parse(InputStreamReader in, Map<String, String> outFileNameToContentIdMap,
+ Map<String, String> outContentIdToFileContentMap)
+ throws XmlPullParserException, IOException {
+ Map<String, String> fileNameToContentIdMap = new HashMap<String, String>();
+ Map<String, String> contentIdToFileContentMap = new HashMap<String, String>();
+
+ XmlPullParser parser = Xml.newPullParser();
+ parser.setInput(in);
+ parser.nextTag();
+
+ parser.require(XmlPullParser.START_TAG, "", TAG_ROOT);
+
+ int state = parser.getEventType();
+ while (state != XmlPullParser.END_DOCUMENT) {
+ if (state == XmlPullParser.START_TAG) {
+ if (TAG_FILE_NAME.equals(parser.getName())) {
+ String contentId = parser.getAttributeValue("", ATTR_CONTENT_ID);
+ if (!TextUtils.isEmpty(contentId)) {
+ String fileName = readText(parser).trim();
+ if (!TextUtils.isEmpty(fileName)) {
+ fileNameToContentIdMap.put(fileName, contentId);
+ }
+ }
+ } else if (TAG_FILE_CONTENT.equals(parser.getName())) {
+ String contentId = parser.getAttributeValue("", ATTR_CONTENT_ID);
+ if (!TextUtils.isEmpty(contentId)
+ && !outContentIdToFileContentMap.containsKey(contentId)
+ && !contentIdToFileContentMap.containsKey(contentId)) {
+ String fileContent = readText(parser);
+ if (!TextUtils.isEmpty(fileContent)) {
+ contentIdToFileContentMap.put(contentId, fileContent);
+ }
+ }
+ }
+ }
+
+ state = parser.next();
+ }
+ outFileNameToContentIdMap.putAll(fileNameToContentIdMap);
+ outContentIdToFileContentMap.putAll(contentIdToFileContentMap);
+ }
+
+ private static String readText(XmlPullParser parser)
+ throws IOException, XmlPullParserException {
+ StringBuffer result = new StringBuffer();
+ int state = parser.next();
+ while (state == XmlPullParser.TEXT) {
+ result.append(parser.getText());
+ state = parser.next();
+ }
+ return result.toString();
+ }
+
+ @VisibleForTesting
+ static void generateHtml(Map<String, String> fileNameToContentIdMap,
+ Map<String, String> contentIdToFileContentMap, PrintWriter writer) {
+ List<String> fileNameList = new ArrayList();
+ fileNameList.addAll(fileNameToContentIdMap.keySet());
+ Collections.sort(fileNameList);
+
+ writer.println(HTML_HEAD_STRING);
+
+ int count = 0;
+ Map<String, Integer> contentIdToOrderMap = new HashMap();
+ List<ContentIdAndFileNames> contentIdAndFileNamesList = new ArrayList();
+
+ // Prints all the file list with a link to its license file content.
+ for (String fileName : fileNameList) {
+ String contentId = fileNameToContentIdMap.get(fileName);
+ // Assigns an id to a newly referred license file content.
+ if (!contentIdToOrderMap.containsKey(contentId)) {
+ contentIdToOrderMap.put(contentId, count);
+
+ // An index in contentIdAndFileNamesList is the order of each element.
+ contentIdAndFileNamesList.add(new ContentIdAndFileNames(contentId));
+ count++;
+ }
+
+ int id = contentIdToOrderMap.get(contentId);
+ contentIdAndFileNamesList.get(id).mFileNameList.add(fileName);
+ writer.format("<li><a href=\"#id%d\">%s</a></li>\n", id, fileName);
+ }
+
+ writer.println(HTML_MIDDLE_STRING);
+
+ count = 0;
+ // Prints all contents of the license files in order of id.
+ for (ContentIdAndFileNames contentIdAndFileNames : contentIdAndFileNamesList) {
+ writer.format("<tr id=\"id%d\"><td class=\"same-license\">\n", count);
+ writer.println("<div class=\"label\">Notices for file(s):</div>");
+ writer.println("<div class=\"file-list\">");
+ for (String fileName : contentIdAndFileNames.mFileNameList) {
+ writer.format("%s <br/>\n", fileName);
+ }
+ writer.println("</div><!-- file-list -->");
+ writer.println("<pre class=\"license-text\">");
+ writer.println(contentIdToFileContentMap.get(
+ contentIdAndFileNames.mContentId));
+ writer.println("</pre><!-- license-text -->");
+ writer.println("</td></tr><!-- same-license -->");
+
+ count++;
+ }
+
+ writer.println(HTML_REAR_STRING);
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlLoader.java b/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlLoader.java
new file mode 100644
index 000000000000..a9fb20ca9e17
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlLoader.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.license;
+
+import android.content.Context;
+import android.support.annotation.VisibleForTesting;
+import android.util.Log;
+
+import com.android.settingslib.utils.AsyncLoader;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * LicenseHtmlLoader is a loader which loads a license html file from default license xml files.
+ */
+public class LicenseHtmlLoader extends AsyncLoader<File> {
+ private static final String TAG = "LicenseHtmlLoader";
+
+ private static final String[] DEFAULT_LICENSE_XML_PATHS = {
+ "/system/etc/NOTICE.xml.gz",
+ "/vendor/etc/NOTICE.xml.gz",
+ "/odm/etc/NOTICE.xml.gz",
+ "/oem/etc/NOTICE.xml.gz"};
+ private static final String NOTICE_HTML_FILE_NAME = "NOTICE.html";
+
+ private Context mContext;
+
+ public LicenseHtmlLoader(Context context) {
+ super(context);
+ mContext = context;
+ }
+
+ @Override
+ public File loadInBackground() {
+ return generateHtmlFromDefaultXmlFiles();
+ }
+
+ @Override
+ protected void onDiscardResult(File f) {
+ }
+
+ private File generateHtmlFromDefaultXmlFiles() {
+ final List<File> xmlFiles = getVaildXmlFiles();
+ if (xmlFiles.isEmpty()) {
+ Log.e(TAG, "No notice file exists.");
+ return null;
+ }
+
+ File cachedHtmlFile = getCachedHtmlFile();
+ if (!isCachedHtmlFileOutdated(xmlFiles, cachedHtmlFile)
+ || generateHtmlFile(xmlFiles, cachedHtmlFile)) {
+ return cachedHtmlFile;
+ }
+
+ return null;
+ }
+
+ @VisibleForTesting
+ List<File> getVaildXmlFiles() {
+ final List<File> xmlFiles = new ArrayList();
+ for (final String xmlPath : DEFAULT_LICENSE_XML_PATHS) {
+ File file = new File(xmlPath);
+ if (file.exists() && file.length() != 0) {
+ xmlFiles.add(file);
+ }
+ }
+ return xmlFiles;
+ }
+
+ @VisibleForTesting
+ File getCachedHtmlFile() {
+ return new File(mContext.getCacheDir(), NOTICE_HTML_FILE_NAME);
+ }
+
+ @VisibleForTesting
+ boolean isCachedHtmlFileOutdated(List<File> xmlFiles, File cachedHtmlFile) {
+ boolean outdated = true;
+ if (cachedHtmlFile.exists() && cachedHtmlFile.length() != 0) {
+ outdated = false;
+ for (File file : xmlFiles) {
+ if (cachedHtmlFile.lastModified() < file.lastModified()) {
+ outdated = true;
+ break;
+ }
+ }
+ }
+ return outdated;
+ }
+
+ @VisibleForTesting
+ boolean generateHtmlFile(List<File> xmlFiles, File htmlFile) {
+ return LicenseHtmlGeneratorFromXml.generateHtml(xmlFiles, htmlFile);
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/suggestions/SuggestionList.java b/packages/SettingsLib/src/com/android/settingslib/suggestions/SuggestionList.java
index 2151ba096c62..a89092040e71 100644
--- a/packages/SettingsLib/src/com/android/settingslib/suggestions/SuggestionList.java
+++ b/packages/SettingsLib/src/com/android/settingslib/suggestions/SuggestionList.java
@@ -17,7 +17,6 @@
package com.android.settingslib.suggestions;
import android.content.Intent;
-import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
@@ -68,15 +67,6 @@ public class SuggestionList {
return false;
}
- public List<Tile> getSuggestionForCategory(String category) {
- for (Map.Entry<SuggestionCategory, List<Tile>> entry : mSuggestions.entrySet()) {
- if (TextUtils.equals(entry.getKey().category, category)) {
- return entry.getValue();
- }
- }
- return null;
- }
-
/**
* Filter suggestions list so they are all unique.
*/
diff --git a/packages/SettingsLib/src/com/android/settingslib/suggestions/SuggestionParser.java b/packages/SettingsLib/src/com/android/settingslib/suggestions/SuggestionParser.java
index 56b84415e907..9c347631d817 100644
--- a/packages/SettingsLib/src/com/android/settingslib/suggestions/SuggestionParser.java
+++ b/packages/SettingsLib/src/com/android/settingslib/suggestions/SuggestionParser.java
@@ -261,7 +261,7 @@ public class SuggestionParser {
if (requiredAccountType == null) {
return true;
}
- AccountManager accountManager = AccountManager.get(mContext);
+ AccountManager accountManager = mContext.getSystemService(AccountManager.class);
Account[] accounts = accountManager.getAccountsByType(requiredAccountType);
boolean satisfiesRequiredAccount = accounts.length > 0;
if (!satisfiesRequiredAccount) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/utils/AsyncLoader.java b/packages/SettingsLib/src/com/android/settingslib/utils/AsyncLoader.java
new file mode 100644
index 000000000000..06770ac09558
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/utils/AsyncLoader.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.utils;
+
+import android.content.AsyncTaskLoader;
+import android.content.Context;
+
+/**
+ * This class fills in some boilerplate for AsyncTaskLoader to actually load things.
+ *
+ * Subclasses need to implement {@link AsyncLoader#loadInBackground()} to perform the actual
+ * background task, and {@link AsyncLoader#onDiscardResult(T)} to clean up previously loaded
+ * results.
+ *
+ * This loader is based on the MailAsyncTaskLoader from the AOSP EmailUnified repo.
+ *
+ * @param <T> the data type to be loaded.
+ */
+public abstract class AsyncLoader<T> extends AsyncTaskLoader<T> {
+ private T mResult;
+
+ public AsyncLoader(final Context context) {
+ super(context);
+ }
+
+ @Override
+ protected void onStartLoading() {
+ if (mResult != null) {
+ deliverResult(mResult);
+ }
+
+ if (takeContentChanged() || mResult == null) {
+ forceLoad();
+ }
+ }
+
+ @Override
+ protected void onStopLoading() {
+ cancelLoad();
+ }
+
+ @Override
+ public void deliverResult(final T data) {
+ if (isReset()) {
+ if (data != null) {
+ onDiscardResult(data);
+ }
+ return;
+ }
+
+ final T oldResult = mResult;
+ mResult = data;
+
+ if (isStarted()) {
+ super.deliverResult(data);
+ }
+
+ if (oldResult != null && oldResult != mResult) {
+ onDiscardResult(oldResult);
+ }
+ }
+
+ @Override
+ protected void onReset() {
+ super.onReset();
+
+ onStopLoading();
+
+ if (mResult != null) {
+ onDiscardResult(mResult);
+ }
+ mResult = null;
+ }
+
+ @Override
+ public void onCanceled(final T data) {
+ super.onCanceled(data);
+
+ if (data != null) {
+ onDiscardResult(data);
+ }
+ }
+
+ /**
+ * Called when discarding the load results so subclasses can take care of clean-up or
+ * recycling tasks. This is not called if the same result (by way of pointer equality) is
+ * returned again by a subsequent call to loadInBackground, or if result is null.
+ *
+ * Note that this may be called concurrently with loadInBackground(), and in some circumstances
+ * may be called more than once for a given object.
+ *
+ * @param result The value returned from {@link AsyncLoader#loadInBackground()} which
+ * is to be discarded.
+ */
+ protected abstract void onDiscardResult(T result);
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/utils/ThreadUtils.java b/packages/SettingsLib/src/com/android/settingslib/utils/ThreadUtils.java
index 2024991b3d58..88adcdb16edc 100644
--- a/packages/SettingsLib/src/com/android/settingslib/utils/ThreadUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/utils/ThreadUtils.java
@@ -15,10 +15,17 @@
*/
package com.android.settingslib.utils;
+import android.os.Handler;
import android.os.Looper;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
public class ThreadUtils {
+
private static volatile Thread sMainThread;
+ private static volatile Handler sMainThreadHandler;
+ private static volatile ExecutorService sSingleThreadExecutor;
/**
* Returns true if the current thread is the UI thread.
@@ -31,6 +38,17 @@ public class ThreadUtils {
}
/**
+ * Returns a shared UI thread handler.
+ */
+ public static Handler getUiThreadHandler() {
+ if (sMainThreadHandler == null) {
+ sMainThreadHandler = new Handler(Looper.getMainLooper());
+ }
+
+ return sMainThreadHandler;
+ }
+
+ /**
* Checks that the current thread is the UI thread. Otherwise throws an exception.
*/
public static void ensureMainThread() {
@@ -39,4 +57,21 @@ public class ThreadUtils {
}
}
+ /**
+ * Posts runnable in background using shared background thread pool.
+ */
+ public static void postOnBackgroundThread(Runnable runnable) {
+ if (sSingleThreadExecutor == null) {
+ sSingleThreadExecutor = Executors.newSingleThreadExecutor();
+ }
+ sSingleThreadExecutor.execute(runnable);
+ }
+
+ /**
+ * Posts the runnable on the main thread.
+ */
+ public static void postOnMainThread(Runnable runnable) {
+ getUiThreadHandler().post(runnable);
+ }
+
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
index 682b85e46165..58f122619cd6 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
@@ -63,6 +63,7 @@ import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
+import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
@@ -186,7 +187,6 @@ public class AccessPoint implements Comparable<AccessPoint> {
private WifiConfiguration mConfig;
private int mRssi = UNREACHABLE_RSSI;
- private long mSeen = 0;
private WifiInfo mInfo;
private NetworkInfo mNetworkInfo;
@@ -270,7 +270,6 @@ public class AccessPoint implements Comparable<AccessPoint> {
// Do not evict old scan results on initial creation
updateRssi();
- updateSeen();
mId = sLastId.incrementAndGet();
}
@@ -316,7 +315,6 @@ public class AccessPoint implements Comparable<AccessPoint> {
this.pskType = that.pskType;
this.mConfig = that.mConfig; //TODO: Watch out, this object is mutated.
this.mRssi = that.mRssi;
- this.mSeen = that.mSeen;
this.mInfo = that.mInfo;
this.mNetworkInfo = that.mNetworkInfo;
this.mScanResultCache.clear();
@@ -551,8 +549,7 @@ public class AccessPoint implements Comparable<AccessPoint> {
mIsScoredNetworkMetered = false;
if (isActive() && mInfo != null) {
- NetworkKey key = new NetworkKey(new WifiKey(
- AccessPoint.convertToQuotedString(ssid), mInfo.getBSSID()));
+ NetworkKey key = NetworkKey.createFromWifiInfo(mInfo);
ScoredNetwork score = scoreCache.getScoredNetwork(key);
if (score != null) {
mIsScoredNetworkMetered |= score.meteredHint;
@@ -653,21 +650,6 @@ public class AccessPoint implements Comparable<AccessPoint> {
}
}
- /** Updates {@link #mSeen} based on the scan result cache. */
- private void updateSeen() {
- long seen = 0;
- for (ScanResult result : mScanResultCache.values()) {
- if (result.timestamp > seen) {
- seen = result.timestamp;
- }
- }
-
- // Only replace the previous value if we have a recent scan result to use
- if (seen != 0) {
- mSeen = seen;
- }
- }
-
/**
* Returns if the network should be considered metered.
*/
@@ -1131,7 +1113,6 @@ public class AccessPoint implements Comparable<AccessPoint> {
mScanResultCache.put(result.BSSID, result);
updateRssi();
- mSeen = result.timestamp; // even if the timestamp is old it is still valid
mIsCarrierAp = result.isCarrierAp;
mCarrierApEapType = result.carrierApEapType;
mCarrierName = result.carrierName;
@@ -1183,7 +1164,6 @@ public class AccessPoint implements Comparable<AccessPoint> {
/* Add or update the scan result for the BSSID */
mScanResultCache.put(result.BSSID, result);
if (evictOldScanResults) evictOldScanResults();
- updateSeen();
updateRssi();
int newLevel = getLevel();
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java
index 9ed8de059996..dd55188e390f 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java
@@ -15,13 +15,13 @@
*/
package com.android.settingslib.wifi;
+import android.annotation.Nullable;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.StateListDrawable;
-import android.net.NetworkBadging;
import android.net.wifi.WifiConfiguration;
import android.os.Looper;
import android.os.UserHandle;
@@ -31,14 +31,17 @@ import android.support.v7.preference.PreferenceViewHolder;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.SparseArray;
+import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
import com.android.settingslib.R;
import com.android.settingslib.TronUtils;
+import com.android.settingslib.TwoTargetPreference;
import com.android.settingslib.Utils;
+import com.android.settingslib.wifi.AccessPoint.Speed;
-public class AccessPointPreference extends Preference {
+public class AccessPointPreference extends TwoTargetPreference {
private static final int[] STATE_SECURED = {
R.attr.state_encrypted
@@ -60,9 +63,10 @@ public class AccessPointPreference extends Preference {
R.string.accessibility_wifi_signal_full
};
- private final StateListDrawable mFrictionSld;
+ @Nullable private final StateListDrawable mFrictionSld;
private final int mBadgePadding;
private final UserBadgeCache mBadgeCache;
+ private final IconInjector mIconInjector;
private TextView mTitleView;
private boolean mForSavedNetworks = false;
@@ -71,7 +75,7 @@ public class AccessPointPreference extends Preference {
private int mLevel;
private CharSequence mContentDescription;
private int mDefaultIconResId;
- private int mWifiSpeed = NetworkBadging.BADGING_NONE;
+ private int mWifiSpeed = Speed.NONE;
public static String generatePreferenceKey(AccessPoint accessPoint) {
StringBuilder builder = new StringBuilder();
@@ -86,60 +90,52 @@ public class AccessPointPreference extends Preference {
return builder.toString();
}
+ @Nullable
+ private static StateListDrawable getFrictionStateListDrawable(Context context) {
+ TypedArray frictionSld;
+ try {
+ frictionSld = context.getTheme().obtainStyledAttributes(FRICTION_ATTRS);
+ } catch (Resources.NotFoundException e) {
+ // Fallback for platforms that do not need friction icon resources.
+ frictionSld = null;
+ }
+ return frictionSld != null ? (StateListDrawable) frictionSld.getDrawable(0) : null;
+ }
+
// Used for dummy pref.
public AccessPointPreference(Context context, AttributeSet attrs) {
super(context, attrs);
mFrictionSld = null;
mBadgePadding = 0;
mBadgeCache = null;
+ mIconInjector = new IconInjector(context);
}
public AccessPointPreference(AccessPoint accessPoint, Context context, UserBadgeCache cache,
boolean forSavedNetworks) {
- super(context);
- setWidgetLayoutResource(R.layout.access_point_friction_widget);
- mBadgeCache = cache;
- mAccessPoint = accessPoint;
- mForSavedNetworks = forSavedNetworks;
- mAccessPoint.setTag(this);
- mLevel = -1;
-
- TypedArray frictionSld;
- try {
- frictionSld = context.getTheme().obtainStyledAttributes(FRICTION_ATTRS);
- } catch (Resources.NotFoundException e) {
- // Fallback for platforms that do not need friction icon resources.
- frictionSld = null;
- }
- mFrictionSld = frictionSld != null ? (StateListDrawable) frictionSld.getDrawable(0) : null;
-
- // Distance from the end of the title at which this AP's user badge should sit.
- mBadgePadding = context.getResources()
- .getDimensionPixelSize(R.dimen.wifi_preference_badge_padding);
+ this(accessPoint, context, cache, 0 /* iconResId */, forSavedNetworks);
refresh();
}
public AccessPointPreference(AccessPoint accessPoint, Context context, UserBadgeCache cache,
int iconResId, boolean forSavedNetworks) {
+ this(accessPoint, context, cache, iconResId, forSavedNetworks,
+ getFrictionStateListDrawable(context), -1 /* level */, new IconInjector(context));
+ }
+
+ @VisibleForTesting
+ AccessPointPreference(AccessPoint accessPoint, Context context, UserBadgeCache cache,
+ int iconResId, boolean forSavedNetworks, StateListDrawable frictionSld,
+ int level, IconInjector iconInjector) {
super(context);
- setWidgetLayoutResource(R.layout.access_point_friction_widget);
mBadgeCache = cache;
mAccessPoint = accessPoint;
mForSavedNetworks = forSavedNetworks;
mAccessPoint.setTag(this);
- mLevel = -1;
+ mLevel = level;
mDefaultIconResId = iconResId;
-
- TypedArray frictionSld;
- try {
- frictionSld = context.getTheme().obtainStyledAttributes(FRICTION_ATTRS);
- } catch (Resources.NotFoundException e) {
- // Fallback for platforms that do not need friction icon resources.
- frictionSld = null;
- }
- mFrictionSld = frictionSld != null ? (StateListDrawable) frictionSld.getDrawable(0) : null;
-
- // Distance from the end of the title at which this AP's user badge should sit.
+ mFrictionSld = frictionSld;
+ mIconInjector = iconInjector;
mBadgePadding = context.getResources()
.getDimensionPixelSize(R.dimen.wifi_preference_badge_padding);
}
@@ -170,6 +166,20 @@ public class AccessPointPreference extends Preference {
ImageView frictionImageView = (ImageView) view.findViewById(R.id.friction_icon);
bindFrictionImage(frictionImageView);
+ setDividerVisibility(view, View.GONE);
+ }
+
+ protected void setDividerVisibility(final PreferenceViewHolder view,
+ @View.Visibility int visibility) {
+ final View divider = view.findViewById(R.id.two_target_divider);
+ if (divider != null) {
+ divider.setVisibility(visibility);
+ }
+ }
+
+ @Override
+ protected int getSecondTargetResId() {
+ return R.layout.access_point_friction_widget;
}
protected void updateIcon(int level, Context context) {
@@ -179,9 +189,7 @@ public class AccessPointPreference extends Preference {
}
TronUtils.logWifiSettingsSpeed(context, mWifiSpeed);
- // TODO(b/62355275): Revert this to N code after deleting NetworkBadging API
- Drawable drawable = NetworkBadging.getWifiIcon(
- level, NetworkBadging.BADGING_NONE, getContext().getTheme());
+ Drawable drawable = mIconInjector.getIcon(level);
if (!mForSavedNetworks && drawable != null) {
drawable.setTint(Utils.getColorAttr(context, android.R.attr.colorControlNormal));
setIcon(drawable);
@@ -325,4 +333,16 @@ public class AccessPointPreference extends Preference {
return mBadges.valueAt(index);
}
}
+
+ static class IconInjector {
+ private final Context mContext;
+
+ public IconInjector(Context context) {
+ mContext = context;
+ }
+
+ public Drawable getIcon(int level) {
+ return mContext.getDrawable(Utils.getWifiIconResource(level));
+ }
+ }
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/OWNERS b/packages/SettingsLib/src/com/android/settingslib/wifi/OWNERS
new file mode 100644
index 000000000000..d5d2e9e8c146
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/OWNERS
@@ -0,0 +1,7 @@
+# Default reviewers for this and subdirectories.
+asapperstein@google.com
+asargent@google.com
+dling@google.com
+zhfan@google.com
+
+# Emergency approvers in case the above are not available \ No newline at end of file
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
index 3c664b0b01a9..c56e1da14921 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
@@ -24,7 +24,6 @@ import android.net.ConnectivityManager;
import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkInfo;
-import android.net.NetworkInfo.DetailedState;
import android.net.NetworkKey;
import android.net.NetworkRequest;
import android.net.NetworkScoreManager;
@@ -36,10 +35,14 @@ import android.net.wifi.WifiManager;
import android.net.wifi.WifiNetworkScoreCache;
import android.net.wifi.WifiNetworkScoreCache.CacheListener;
import android.os.Handler;
+import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
+import android.os.Process;
import android.provider.Settings;
import android.support.annotation.GuardedBy;
+import android.support.annotation.NonNull;
+import android.support.annotation.VisibleForTesting;
import android.text.format.DateUtils;
import android.util.ArraySet;
import android.util.Log;
@@ -47,8 +50,12 @@ import android.util.SparseArray;
import android.util.SparseIntArray;
import android.widget.Toast;
-import com.android.internal.annotations.VisibleForTesting;
import com.android.settingslib.R;
+import com.android.settingslib.core.lifecycle.Lifecycle;
+import com.android.settingslib.core.lifecycle.LifecycleObserver;
+import com.android.settingslib.core.lifecycle.events.OnDestroy;
+import com.android.settingslib.core.lifecycle.events.OnStart;
+import com.android.settingslib.core.lifecycle.events.OnStop;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -64,7 +71,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
/**
* Tracks saved or available wifi networks and their state.
*/
-public class WifiTracker {
+public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestroy {
/**
* Default maximum age in millis of cached scored networks in
* {@link AccessPoint#mScoredNetworkCache} to be used for speed label generation.
@@ -80,7 +87,7 @@ public class WifiTracker {
* and used so as to assist with in-the-field WiFi connectivity debugging */
public static boolean sVerboseLogging;
- // TODO(b/36733768): Remove flag includeSaved and includePasspoints.
+ // TODO(b/36733768): Remove flag includeSaved
// TODO: Allow control of this?
// Combo scans can take 5-6s to complete - set to 10s.
@@ -96,9 +103,9 @@ public class WifiTracker {
private final WifiListener mListener;
private final boolean mIncludeSaved;
private final boolean mIncludeScans;
- private final boolean mIncludePasspoints;
- @VisibleForTesting final MainHandler mMainHandler;
- @VisibleForTesting final WorkHandler mWorkHandler;
+ @VisibleForTesting MainHandler mMainHandler;
+ @VisibleForTesting WorkHandler mWorkHandler;
+ private HandlerThread mWorkThread;
private WifiTrackerNetworkCallback mNetworkCallback;
@@ -142,7 +149,7 @@ public class WifiTracker {
private WifiInfo mLastInfo;
private final NetworkScoreManager mNetworkScoreManager;
- private final WifiNetworkScoreCache mScoreCache;
+ private WifiNetworkScoreCache mScoreCache;
private boolean mNetworkScoringUiEnabled;
private long mMaxSpeedLabelScoreCacheAge;
@@ -155,65 +162,64 @@ public class WifiTracker {
@GuardedBy("mLock")
private boolean mStaleScanResults = true;
- public WifiTracker(Context context, WifiListener wifiListener,
- boolean includeSaved, boolean includeScans) {
- this(context, wifiListener, null, includeSaved, includeScans);
+ private static IntentFilter newIntentFilter() {
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
+ filter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
+ filter.addAction(WifiManager.NETWORK_IDS_CHANGED_ACTION);
+ filter.addAction(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION);
+ filter.addAction(WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION);
+ filter.addAction(WifiManager.LINK_CONFIGURATION_CHANGED_ACTION);
+ filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
+ filter.addAction(WifiManager.RSSI_CHANGED_ACTION);
+
+ return filter;
}
- public WifiTracker(Context context, WifiListener wifiListener, Looper workerLooper,
+ /**
+ * Use the lifecycle constructor below whenever possible
+ */
+ @Deprecated
+ public WifiTracker(Context context, WifiListener wifiListener,
boolean includeSaved, boolean includeScans) {
- this(context, wifiListener, workerLooper, includeSaved, includeScans, false);
+ this(context, wifiListener, includeSaved, includeScans,
+ context.getSystemService(WifiManager.class),
+ context.getSystemService(ConnectivityManager.class),
+ context.getSystemService(NetworkScoreManager.class),
+ newIntentFilter());
}
public WifiTracker(Context context, WifiListener wifiListener,
- boolean includeSaved, boolean includeScans, boolean includePasspoints) {
- this(context, wifiListener, null, includeSaved, includeScans, includePasspoints);
- }
-
- public WifiTracker(Context context, WifiListener wifiListener, Looper workerLooper,
- boolean includeSaved, boolean includeScans, boolean includePasspoints) {
- this(context, wifiListener, workerLooper, includeSaved, includeScans, includePasspoints,
+ @NonNull Lifecycle lifecycle, boolean includeSaved, boolean includeScans) {
+ this(context, wifiListener, includeSaved, includeScans,
context.getSystemService(WifiManager.class),
context.getSystemService(ConnectivityManager.class),
- context.getSystemService(NetworkScoreManager.class), Looper.myLooper()
- );
+ context.getSystemService(NetworkScoreManager.class),
+ newIntentFilter());
+ lifecycle.addObserver(this);
}
@VisibleForTesting
- WifiTracker(Context context, WifiListener wifiListener, Looper workerLooper,
- boolean includeSaved, boolean includeScans, boolean includePasspoints,
+ WifiTracker(Context context, WifiListener wifiListener,
+ boolean includeSaved, boolean includeScans,
WifiManager wifiManager, ConnectivityManager connectivityManager,
- NetworkScoreManager networkScoreManager, Looper currentLooper) {
+ NetworkScoreManager networkScoreManager,
+ IntentFilter filter) {
if (!includeSaved && !includeScans) {
throw new IllegalArgumentException("Must include either saved or scans");
}
mContext = context;
- if (currentLooper == null) {
- // When we aren't on a looper thread, default to the main.
- currentLooper = Looper.getMainLooper();
- }
- mMainHandler = new MainHandler(currentLooper);
- mWorkHandler = new WorkHandler(
- workerLooper != null ? workerLooper : currentLooper);
+ mMainHandler = new MainHandler(Looper.getMainLooper());
mWifiManager = wifiManager;
mIncludeSaved = includeSaved;
mIncludeScans = includeScans;
- mIncludePasspoints = includePasspoints;
mListener = wifiListener;
mConnectivityManager = connectivityManager;
// check if verbose logging has been turned on or off
sVerboseLogging = (mWifiManager.getVerboseLoggingLevel() > 0);
- mFilter = new IntentFilter();
- mFilter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
- mFilter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
- mFilter.addAction(WifiManager.NETWORK_IDS_CHANGED_ACTION);
- mFilter.addAction(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION);
- mFilter.addAction(WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION);
- mFilter.addAction(WifiManager.LINK_CONFIGURATION_CHANGED_ACTION);
- mFilter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
- mFilter.addAction(WifiManager.RSSI_CHANGED_ACTION);
+ mFilter = filter;
mNetworkRequest = new NetworkRequest.Builder()
.clearCapabilities()
@@ -222,7 +228,22 @@ public class WifiTracker {
mNetworkScoreManager = networkScoreManager;
- mScoreCache = new WifiNetworkScoreCache(context, new CacheListener(mWorkHandler) {
+ final HandlerThread workThread = new HandlerThread(TAG
+ + "{" + Integer.toHexString(System.identityHashCode(this)) + "}",
+ Process.THREAD_PRIORITY_BACKGROUND);
+ workThread.start();
+ setWorkThread(workThread);
+ }
+
+ /**
+ * Sanity warning: this wipes out mScoreCache, so use with extreme caution
+ * @param workThread substitute Handler thread, for testing purposes only
+ */
+ @VisibleForTesting
+ void setWorkThread(HandlerThread workThread) {
+ mWorkThread = workThread;
+ mWorkHandler = new WorkHandler(workThread.getLooper());
+ mScoreCache = new WifiNetworkScoreCache(mContext, new CacheListener(mWorkHandler) {
@Override
public void networkCacheUpdated(List<ScoredNetwork> networks) {
synchronized (mLock) {
@@ -237,6 +258,11 @@ public class WifiTracker {
});
}
+ @Override
+ public void onDestroy() {
+ mWorkThread.quit();
+ }
+
/** Synchronously update the list of access points with the latest information. */
@MainThread
public void forceUpdate() {
@@ -265,15 +291,6 @@ public class WifiTracker {
}
/**
- * Force a scan for wifi networks to happen now.
- */
- public void forceScan() {
- if (mWifiManager.isWifiEnabled() && mScanner != null) {
- mScanner.forceScan();
- }
- }
-
- /**
* Temporarily stop scanning for wifi networks.
*/
public void pauseScanning() {
@@ -305,8 +322,9 @@ public class WifiTracker {
* <p>Registers listeners and starts scanning for wifi networks. If this is not called
* then forceUpdate() must be called to populate getAccessPoints().
*/
+ @Override
@MainThread
- public void startTracking() {
+ public void onStart() {
synchronized (mLock) {
registerScoreCache();
@@ -354,15 +372,16 @@ public class WifiTracker {
/**
* Stop tracking wifi networks and scores.
*
- * <p>This should always be called when done with a WifiTracker (if startTracking was called) to
+ * <p>This should always be called when done with a WifiTracker (if onStart was called) to
* ensure proper cleanup and prevent any further callbacks from occurring.
*
* <p>Calling this method will set the {@link #mStaleScanResults} bit, which prevents
* {@link WifiListener#onAccessPointsChanged()} callbacks from being invoked (until the bit
* is unset on the next SCAN_RESULTS_AVAILABLE_ACTION).
*/
+ @Override
@MainThread
- public void stopTracking() {
+ public void onStop() {
synchronized (mLock) {
if (mRegistered) {
mContext.unregisterReceiver(mReceiver);
@@ -761,15 +780,6 @@ public class WifiTracker {
}
}
- public static List<AccessPoint> getCurrentAccessPoints(Context context, boolean includeSaved,
- boolean includeScans, boolean includePasspoints) {
- WifiTracker tracker = new WifiTracker(context,
- null, null, includeSaved, includeScans, includePasspoints);
- tracker.forceUpdate();
- tracker.copyAndNotifyListeners(false /*notifyListeners*/);
- return tracker.getAccessPoints();
- }
-
@VisibleForTesting
final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
@@ -959,11 +969,6 @@ public class WifiTracker {
}
}
- void forceScan() {
- removeMessages(MSG_SCAN);
- sendEmptyMessage(MSG_SCAN);
- }
-
void pause() {
mRetry = 0;
removeMessages(MSG_SCAN);
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTrackerFactory.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTrackerFactory.java
index 79cee046140d..8b5863aee91f 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTrackerFactory.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTrackerFactory.java
@@ -16,8 +16,10 @@
package com.android.settingslib.wifi;
import android.content.Context;
-import android.os.Looper;
import android.support.annotation.Keep;
+import android.support.annotation.NonNull;
+
+import com.android.settingslib.core.lifecycle.Lifecycle;
/**
* Factory method used to inject WifiTracker instances.
@@ -31,12 +33,11 @@ public class WifiTrackerFactory {
}
public static WifiTracker create(
- Context context, WifiTracker.WifiListener wifiListener, Looper workerLooper,
- boolean includeSaved, boolean includeScans, boolean includePasspoints) {
+ Context context, WifiTracker.WifiListener wifiListener, @NonNull Lifecycle lifecycle,
+ boolean includeSaved, boolean includeScans) {
if(sTestingWifiTracker != null) {
return sTestingWifiTracker;
}
- return new WifiTracker(
- context, wifiListener, workerLooper, includeSaved, includeScans, includePasspoints);
+ return new WifiTracker(context, wifiListener, lifecycle, includeSaved, includeScans);
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothA2dpWrapperImpl.java b/packages/SettingsLib/src/com/android/settingslib/wrapper/BluetoothA2dpWrapper.java
index 14fa7966eab1..4c52a9f8d3ce 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothA2dpWrapperImpl.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wrapper/BluetoothA2dpWrapper.java
@@ -14,48 +14,56 @@
* limitations under the License.
*/
-package com.android.settingslib.bluetooth;
+package com.android.settingslib.wrapper;
import android.bluetooth.BluetoothA2dp;
import android.bluetooth.BluetoothCodecStatus;
import android.bluetooth.BluetoothDevice;
-public class BluetoothA2dpWrapperImpl implements BluetoothA2dpWrapper {
-
- public static class Factory implements BluetoothA2dpWrapper.Factory {
- @Override
- public BluetoothA2dpWrapper getInstance(BluetoothA2dp service) {
- return new BluetoothA2dpWrapperImpl(service);
- }
- }
+/**
+ * This class replicates some methods of android.bluetooth.BluetoothA2dp that are new and not
+ * yet available in our current version of Robolectric. It provides a thin wrapper to call the real
+ * methods in production and a mock in tests.
+ */
+public class BluetoothA2dpWrapper {
private BluetoothA2dp mService;
- public BluetoothA2dpWrapperImpl(BluetoothA2dp service) {
+ public BluetoothA2dpWrapper(BluetoothA2dp service) {
mService = service;
}
- @Override
+ /**
+ * @return the real {@code BluetoothA2dp} object
+ */
public BluetoothA2dp getService() {
return mService;
}
- @Override
+ /**
+ * Wraps {@code BluetoothA2dp.getCodecStatus}
+ */
public BluetoothCodecStatus getCodecStatus() {
return mService.getCodecStatus();
}
- @Override
+ /**
+ * Wraps {@code BluetoothA2dp.supportsOptionalCodecs}
+ */
public int supportsOptionalCodecs(BluetoothDevice device) {
return mService.supportsOptionalCodecs(device);
}
- @Override
+ /**
+ * Wraps {@code BluetoothA2dp.getOptionalCodecsEnabled}
+ */
public int getOptionalCodecsEnabled(BluetoothDevice device) {
return mService.getOptionalCodecsEnabled(device);
}
- @Override
+ /**
+ * Wraps {@code BluetoothA2dp.setOptionalCodecsEnabled}
+ */
public void setOptionalCodecsEnabled(BluetoothDevice device, int value) {
mService.setOptionalCodecsEnabled(device, value);
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/wrapper/PackageManagerWrapper.java b/packages/SettingsLib/src/com/android/settingslib/wrapper/PackageManagerWrapper.java
new file mode 100644
index 000000000000..b1f3f3ce4300
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/wrapper/PackageManagerWrapper.java
@@ -0,0 +1,246 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.settingslib.wrapper;
+
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.IPackageDeleteObserver;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.ResolveInfo;
+import android.graphics.drawable.Drawable;
+import android.os.UserHandle;
+import android.os.storage.VolumeInfo;
+
+import java.util.List;
+
+/**
+ * A thin wrapper class that simplifies testing by putting a mockable layer between the application
+ * and the PackageManager. This class only provides access to the minimum number of functions from
+ * the PackageManager needed for DeletionHelper to work.
+ */
+public class PackageManagerWrapper {
+
+ private final PackageManager mPm;
+
+ public PackageManagerWrapper(PackageManager pm) {
+ mPm = pm;
+ }
+
+ /**
+ * Returns the real {@code PackageManager} object.
+ */
+ public PackageManager getPackageManager() {
+ return mPm;
+ }
+
+ /**
+ * Calls {@code PackageManager.getInstalledApplicationsAsUser()}.
+ *
+ * @see android.content.pm.PackageManager#getInstalledApplicationsAsUser
+ */
+ public List<ApplicationInfo> getInstalledApplicationsAsUser(int flags, int userId) {
+ return mPm.getInstalledApplicationsAsUser(flags, userId);
+ }
+
+ /**
+ * Calls {@code PackageManager.getInstalledPackagesAsUser}
+ */
+ public List<PackageInfo> getInstalledPackagesAsUser(int flags, int userId) {
+ return mPm.getInstalledPackagesAsUser(flags, userId);
+ }
+
+ /**
+ * Calls {@code PackageManager.hasSystemFeature()}.
+ *
+ * @see android.content.pm.PackageManager#hasSystemFeature
+ */
+ public boolean hasSystemFeature(String name) {
+ return mPm.hasSystemFeature(name);
+ }
+
+ /**
+ * Calls {@code PackageManager.queryIntentActivitiesAsUser()}.
+ *
+ * @see android.content.pm.PackageManager#queryIntentActivitiesAsUser
+ */
+ public List<ResolveInfo> queryIntentActivitiesAsUser(Intent intent, int flags, int userId) {
+ return mPm.queryIntentActivitiesAsUser(intent, flags, userId);
+ }
+
+ /**
+ * Calls {@code PackageManager.getInstallReason()}.
+ *
+ * @see android.content.pm.PackageManager#getInstallReason
+ */
+ public int getInstallReason(String packageName, UserHandle user) {
+ return mPm.getInstallReason(packageName, user);
+ }
+
+ /**
+ * Calls {@code PackageManager.getApplicationInfoAsUser}
+ */
+ public ApplicationInfo getApplicationInfoAsUser(String packageName, int i, int userId)
+ throws PackageManager.NameNotFoundException {
+ return mPm.getApplicationInfoAsUser(packageName, i, userId);
+ }
+
+ /**
+ * Calls {@code PackageManager.setDefaultBrowserPackageNameAsUser}
+ */
+ public boolean setDefaultBrowserPackageNameAsUser(String packageName, int userId) {
+ return mPm.setDefaultBrowserPackageNameAsUser(packageName, userId);
+ }
+
+ /**
+ * Calls {@code PackageManager.getDefaultBrowserPackageNameAsUser}
+ */
+ public String getDefaultBrowserPackageNameAsUser(int userId) {
+ return mPm.getDefaultBrowserPackageNameAsUser(userId);
+ }
+
+ /**
+ * Calls {@code PackageManager.getHomeActivities}
+ */
+ public ComponentName getHomeActivities(List<ResolveInfo> homeActivities) {
+ return mPm.getHomeActivities(homeActivities);
+ }
+
+ /**
+ * Calls {@code PackageManager.queryIntentServicesAsUser}
+ */
+ public List<ResolveInfo> queryIntentServicesAsUser(Intent intent, int i, int user) {
+ return mPm.queryIntentServicesAsUser(intent, i, user);
+ }
+
+ /**
+ * Calls {@code PackageManager.replacePreferredActivity}
+ */
+ public void replacePreferredActivity(IntentFilter homeFilter, int matchCategoryEmpty,
+ ComponentName[] componentNames, ComponentName component) {
+ mPm.replacePreferredActivity(homeFilter, matchCategoryEmpty, componentNames, component);
+ }
+
+ /**
+ * Gets information about a particular package from the package manager.
+ *
+ * @param packageName The name of the package we would like information about.
+ * @param i additional options flags. see javadoc for
+ * {@link PackageManager#getPackageInfo(String, int)}
+ * @return The PackageInfo for the requested package
+ */
+ public PackageInfo getPackageInfo(String packageName, int i) throws NameNotFoundException {
+ return mPm.getPackageInfo(packageName, i);
+ }
+
+ /**
+ * Retrieves the icon associated with this particular set of ApplicationInfo
+ *
+ * @param info The ApplicationInfo to retrieve the icon for
+ * @return The icon as a drawable.
+ */
+ public Drawable getUserBadgedIcon(ApplicationInfo info) {
+ return mPm.getUserBadgedIcon(mPm.loadUnbadgedItemIcon(info, info),
+ new UserHandle(UserHandle.getUserId(info.uid)));
+ }
+
+ /**
+ * Retrieves the label associated with the particular set of ApplicationInfo
+ *
+ * @param app The ApplicationInfo to retrieve the label for
+ * @return the label as a CharSequence
+ */
+ public CharSequence loadLabel(ApplicationInfo app) {
+ return app.loadLabel(mPm);
+ }
+
+ /**
+ * Retrieve all activities that can be performed for the given intent.
+ */
+ public List<ResolveInfo> queryIntentActivities(Intent intent, int flags) {
+ return mPm.queryIntentActivities(intent, flags);
+ }
+
+ /**
+ * Calls {@code PackageManager.getPrimaryStorageCurrentVolume}
+ */
+ public VolumeInfo getPrimaryStorageCurrentVolume() {
+ return mPm.getPrimaryStorageCurrentVolume();
+ }
+
+ /**
+ * Calls {@code PackageManager.deletePackageAsUser}
+ */
+ public void deletePackageAsUser(String packageName, IPackageDeleteObserver observer, int flags,
+ int userId) {
+ mPm.deletePackageAsUser(packageName, observer, flags, userId);
+ }
+
+ /**
+ * Calls {@code PackageManager.getPackageUidAsUser}
+ */
+ public int getPackageUidAsUser(String pkg, int userId)
+ throws PackageManager.NameNotFoundException {
+ return mPm.getPackageUidAsUser(pkg, userId);
+ }
+
+ /**
+ * Calls {@code PackageManager.setApplicationEnabledSetting}
+ */
+ public void setApplicationEnabledSetting(String packageName, int newState, int flags) {
+ mPm.setApplicationEnabledSetting(packageName, newState, flags);
+ }
+
+ /**
+ * Calls {@code PackageManager.getApplicationEnabledSetting}
+ */
+ public int getApplicationEnabledSetting(String packageName) {
+ return mPm.getApplicationEnabledSetting(packageName);
+ }
+
+ /**
+ * Calls {@code PackageManager.setComponentEnabledSetting}
+ */
+ public void setComponentEnabledSetting(ComponentName componentName, int newState, int flags) {
+ mPm.setComponentEnabledSetting(componentName, newState, flags);
+ }
+
+ /**
+ * Calls {@code PackageManager.getApplicationInfo}
+ */
+ public ApplicationInfo getApplicationInfo(String packageName, int flags)
+ throws NameNotFoundException {
+ return mPm.getApplicationInfo(packageName, flags);
+ }
+
+ /**
+ * Calls {@code PackageManager.getApplicationLabel}
+ */
+ public CharSequence getApplicationLabel(ApplicationInfo info) {
+ return mPm.getApplicationLabel(info);
+ }
+
+ /**
+ * Calls {@code PackageManager.queryBroadcastReceivers}
+ */
+ public List<ResolveInfo> queryBroadcastReceivers(Intent intent, int flags) {
+ return mPm.queryBroadcastReceivers(intent, flags);
+ }
+}
+