diff --git a/res/drawable/ic_settings_sim.xml b/res/drawable/ic_settings_sim.xml
index ca548cfefad..d083c9de6cf 100644
--- a/res/drawable/ic_settings_sim.xml
+++ b/res/drawable/ic_settings_sim.xml
@@ -15,7 +15,14 @@
limitations under the License.
-->
-
-
+
+
+
+
diff --git a/res/layout/choose_lock_pattern_common.xml b/res/layout/choose_lock_pattern_common.xml
index 65135ddc90c..949d1303293 100644
--- a/res/layout/choose_lock_pattern_common.xml
+++ b/res/layout/choose_lock_pattern_common.xml
@@ -24,7 +24,7 @@
android:icon="@drawable/ic_lock"
android:layout="@layout/suw_glif_blank_template"
settings:suwFooter="@layout/choose_lock_pattern_common_footer"
- settings:suwHeaderText="@string/lockpassword_choose_your_pattern_header">
+ settings:suwHeaderText="@string/lockpassword_choose_your_screen_lock_header">
fragment, int titleRes,
diff --git a/src/com/android/settings/applications/PictureInPictureSettings.java b/src/com/android/settings/applications/PictureInPictureSettings.java
index d8e0b2bf8fc..5569a4ef785 100644
--- a/src/com/android/settings/applications/PictureInPictureSettings.java
+++ b/src/com/android/settings/applications/PictureInPictureSettings.java
@@ -22,14 +22,16 @@ import android.content.Context;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
-import android.content.pm.PackageItemInfo;
import android.content.pm.PackageManager;
+import android.content.pm.UserInfo;
import android.os.Bundle;
import android.os.UserHandle;
+import android.os.UserManager;
import android.support.v7.preference.Preference;
import android.support.v7.preference.Preference.OnPreferenceClickListener;
import android.support.v7.preference.PreferenceScreen;
-import android.util.ArrayMap;
+import android.util.IconDrawableFactory;
+import android.util.Pair;
import android.view.View;
import com.android.internal.annotations.VisibleForTesting;
@@ -37,9 +39,13 @@ import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.settings.R;
import com.android.settings.notification.EmptyTextSettings;
import com.android.settings.wrapper.ActivityInfoWrapper;
+import com.android.settings.wrapper.UserManagerWrapper;
+import com.android.settingslib.wrapper.PackageManagerWrapper;
+import java.text.Collator;
import java.util.ArrayList;
import java.util.Collections;
+import java.util.Comparator;
import java.util.List;
public class PictureInPictureSettings extends EmptyTextSettings {
@@ -51,8 +57,38 @@ public class PictureInPictureSettings extends EmptyTextSettings {
IGNORE_PACKAGE_LIST.add("com.android.systemui");
}
+ /**
+ * Comparator by name, then user id.
+ * {@see PackageItemInfo#DisplayNameComparator}
+ */
+ static class AppComparator implements Comparator> {
+
+ private final Collator mCollator = Collator.getInstance();
+ private final PackageManager mPm;
+
+ public AppComparator(PackageManager pm) {
+ mPm = pm;
+ }
+
+ public final int compare(Pair a,
+ Pair b) {
+ CharSequence sa = a.first.loadLabel(mPm);
+ if (sa == null) sa = a.first.name;
+ CharSequence sb = b.first.loadLabel(mPm);
+ if (sb == null) sb = b.first.name;
+ int nameCmp = mCollator.compare(sa.toString(), sb.toString());
+ if (nameCmp != 0) {
+ return nameCmp;
+ } else {
+ return a.second - b.second;
+ }
+ }
+ }
+
private Context mContext;
- private PackageManager mPackageManager;
+ private PackageManagerWrapper mPackageManager;
+ private UserManagerWrapper mUserManager;
+ private IconDrawableFactory mIconDrawableFactory;
/**
* @return true if the package has any activities that declare that they support
@@ -94,12 +130,23 @@ public class PictureInPictureSettings extends EmptyTextSettings {
return false;
}
+ public PictureInPictureSettings() {
+ // Do nothing
+ }
+
+ public PictureInPictureSettings(PackageManagerWrapper pm, UserManagerWrapper um) {
+ mPackageManager = pm;
+ mUserManager = um;
+ }
+
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
mContext = getActivity();
- mPackageManager = mContext.getPackageManager();
+ mPackageManager = new PackageManagerWrapper(mContext.getPackageManager());
+ mUserManager = new UserManagerWrapper(mContext.getSystemService(UserManager.class));
+ mIconDrawableFactory = IconDrawableFactory.newInstance(mContext);
setPreferenceScreen(getPreferenceManager().createPreferenceScreen(mContext));
}
@@ -111,33 +158,25 @@ public class PictureInPictureSettings extends EmptyTextSettings {
final PreferenceScreen screen = getPreferenceScreen();
screen.removeAll();
- // Fetch the set of applications which have at least one activity that declare that they
- // support picture-in-picture
- final ArrayMap packageToState = new ArrayMap<>();
- final ArrayList pipApps = new ArrayList<>();
- final List installedPackages = mPackageManager.getInstalledPackagesAsUser(
- GET_ACTIVITIES, UserHandle.myUserId());
- for (PackageInfo packageInfo : installedPackages) {
- if (checkPackageHasPictureInPictureActivities(packageInfo.packageName,
- packageInfo.activities)) {
- final String packageName = packageInfo.applicationInfo.packageName;
- final boolean state = PictureInPictureDetails.getEnterPipStateForPackage(
- mContext, packageInfo.applicationInfo.uid, packageName);
- pipApps.add(packageInfo.applicationInfo);
- packageToState.put(packageName, state);
- }
- }
- Collections.sort(pipApps, new PackageItemInfo.DisplayNameComparator(mPackageManager));
+ // Fetch the set of applications for each profile which have at least one activity that
+ // declare that they support picture-in-picture
+ final PackageManager pm = mPackageManager.getPackageManager();
+ final ArrayList> pipApps =
+ collectPipApps(UserHandle.myUserId());
+ Collections.sort(pipApps, new AppComparator(pm));
// Rebuild the list of prefs
final Context prefContext = getPrefContext();
- for (final ApplicationInfo appInfo : pipApps) {
+ for (final Pair appData : pipApps) {
+ final ApplicationInfo appInfo = appData.first;
+ final int userId = appData.second;
+ final UserHandle user = UserHandle.of(userId);
final String packageName = appInfo.packageName;
- final CharSequence label = appInfo.loadLabel(mPackageManager);
+ final CharSequence label = appInfo.loadLabel(pm);
final Preference pref = new Preference(prefContext);
- pref.setIcon(appInfo.loadIcon(mPackageManager));
- pref.setTitle(label);
+ pref.setIcon(mIconDrawableFactory.getBadgedIcon(appInfo, userId));
+ pref.setTitle(pm.getUserBadgedLabel(label, user));
pref.setSummary(PictureInPictureDetails.getPreferenceSummary(prefContext,
appInfo.uid, packageName));
pref.setOnPreferenceClickListener(new OnPreferenceClickListener() {
@@ -163,4 +202,28 @@ public class PictureInPictureSettings extends EmptyTextSettings {
public int getMetricsCategory() {
return MetricsEvent.SETTINGS_MANAGE_PICTURE_IN_PICTURE;
}
+
+ /**
+ * @return the list of applications for the given user and all their profiles that have
+ * activities which support PiP.
+ */
+ ArrayList> collectPipApps(int userId) {
+ final ArrayList> pipApps = new ArrayList<>();
+ final ArrayList userIds = new ArrayList<>();
+ for (UserInfo user : mUserManager.getProfiles(userId)) {
+ userIds.add(user.id);
+ }
+
+ for (int id : userIds) {
+ final List installedPackages = mPackageManager.getInstalledPackagesAsUser(
+ GET_ACTIVITIES, id);
+ for (PackageInfo packageInfo : installedPackages) {
+ if (checkPackageHasPictureInPictureActivities(packageInfo.packageName,
+ packageInfo.activities)) {
+ pipApps.add(new Pair<>(packageInfo.applicationInfo, id));
+ }
+ }
+ }
+ return pipApps;
+ }
}
diff --git a/src/com/android/settings/applications/UsageAccessDetails.java b/src/com/android/settings/applications/UsageAccessDetails.java
index e40ae37c23f..253ddfdbbba 100644
--- a/src/com/android/settings/applications/UsageAccessDetails.java
+++ b/src/com/android/settings/applications/UsageAccessDetails.java
@@ -137,6 +137,9 @@ public class UsageAccessDetails extends AppInfoWithHeader implements OnPreferenc
@Override
protected boolean refreshUi() {
+ if (mPackageInfo == null) {
+ return false;
+ }
mUsageState = mUsageBridge.getUsageInfo(mPackageName,
mPackageInfo.applicationInfo.uid);
diff --git a/src/com/android/settings/bluetooth/BluetoothSummaryUpdater.java b/src/com/android/settings/bluetooth/BluetoothSummaryUpdater.java
index 7d2cc181897..662cd701de2 100644
--- a/src/com/android/settings/bluetooth/BluetoothSummaryUpdater.java
+++ b/src/com/android/settings/bluetooth/BluetoothSummaryUpdater.java
@@ -42,31 +42,21 @@ public final class BluetoothSummaryUpdater extends SummaryUpdater implements Blu
private final LocalBluetoothManager mBluetoothManager;
private final LocalBluetoothAdapter mBluetoothAdapter;
- private boolean mEnabled;
- private int mConnectionState;
-
public BluetoothSummaryUpdater(Context context, OnSummaryChangeListener listener,
LocalBluetoothManager bluetoothManager) {
super(context, listener);
mBluetoothManager = bluetoothManager;
mBluetoothAdapter = mBluetoothManager != null
- ? mBluetoothManager.getBluetoothAdapter() : null;
+ ? mBluetoothManager.getBluetoothAdapter() : null;
}
@Override
public void onBluetoothStateChanged(int bluetoothState) {
- mEnabled = bluetoothState == BluetoothAdapter.STATE_ON
- || bluetoothState == BluetoothAdapter.STATE_TURNING_ON;
- if (!mEnabled) {
- mConnectionState = BluetoothAdapter.STATE_DISCONNECTED;
- }
notifyChangeIfNeeded();
}
@Override
public void onConnectionStateChanged(CachedBluetoothDevice cachedDevice, int state) {
- mConnectionState = state;
- updateConnected();
notifyChangeIfNeeded();
}
@@ -92,8 +82,6 @@ public final class BluetoothSummaryUpdater extends SummaryUpdater implements Blu
return;
}
if (listening) {
- mEnabled = mBluetoothAdapter.isEnabled();
- mConnectionState = mBluetoothAdapter.getConnectionState();
notifyChangeIfNeeded();
mBluetoothManager.getEventManager().registerCallback(this);
} else {
@@ -103,10 +91,10 @@ public final class BluetoothSummaryUpdater extends SummaryUpdater implements Blu
@Override
public String getSummary() {
- if (!mEnabled) {
+ if (mBluetoothAdapter == null || !mBluetoothAdapter.isEnabled()) {
return mContext.getString(R.string.bluetooth_disabled);
}
- switch (mConnectionState) {
+ switch (mBluetoothAdapter.getConnectionState()) {
case BluetoothAdapter.STATE_CONNECTED:
return getConnectedDeviceSummary();
case BluetoothAdapter.STATE_CONNECTING:
@@ -118,50 +106,17 @@ public final class BluetoothSummaryUpdater extends SummaryUpdater implements Blu
}
}
- private void updateConnected() {
- if (mBluetoothAdapter == null) {
- return;
- }
- // Make sure our connection state is up to date.
- int state = mBluetoothAdapter.getConnectionState();
- if (state != mConnectionState) {
- mConnectionState = state;
- return;
- }
- final Collection devices = getDevices();
- if (devices == null) {
- mConnectionState = BluetoothAdapter.STATE_DISCONNECTED;
- return;
- }
- if (mConnectionState == BluetoothAdapter.STATE_CONNECTED) {
- CachedBluetoothDevice connectedDevice = null;
- for (CachedBluetoothDevice device : devices) {
- if (device.isConnected()) {
- connectedDevice = device;
- break;
- }
- }
- if (connectedDevice == null) {
- // If somehow we think we are connected, but have no connected devices, we
- // aren't connected.
- mConnectionState = BluetoothAdapter.STATE_DISCONNECTED;
- }
- }
- }
-
- private Collection getDevices() {
- return mBluetoothManager != null
- ? mBluetoothManager.getCachedDeviceManager().getCachedDevicesCopy()
- : null;
- }
-
@VisibleForTesting
String getConnectedDeviceSummary() {
String deviceName = null;
int count = 0;
final Set devices = mBluetoothAdapter.getBondedDevices();
- if (devices == null || devices.isEmpty()) {
- return null;
+ if (devices == null) {
+ Log.e(TAG, "getConnectedDeviceSummary, bonded devices are null");
+ return mContext.getString(R.string.bluetooth_disabled);
+ } else if (devices.isEmpty()) {
+ Log.e(TAG, "getConnectedDeviceSummary, no bonded devices");
+ return mContext.getString(R.string.disconnected);
}
for (BluetoothDevice device : devices) {
if (device.isConnected()) {
@@ -173,12 +128,13 @@ public final class BluetoothSummaryUpdater extends SummaryUpdater implements Blu
}
}
if (deviceName == null) {
- Log.w(TAG, "getConnectedDeviceSummary, deviceName is null, numBondedDevices="
+ Log.e(TAG, "getConnectedDeviceSummary, deviceName is null, numBondedDevices="
+ devices.size());
for (BluetoothDevice device : devices) {
- Log.w(TAG, "getConnectedDeviceSummary, device=" + device.getName() + "["
+ Log.e(TAG, "getConnectedDeviceSummary, device=" + device.getName() + "["
+ device.getAddress() + "]" + ", isConnected=" + device.isConnected());
}
+ return mContext.getString(R.string.disconnected);
}
return count > 1 ? mContext.getString(R.string.bluetooth_connected_multiple_devices_summary)
: mContext.getString(R.string.bluetooth_connected_summary, deviceName);
diff --git a/src/com/android/settings/core/InstrumentedPreferenceFragment.java b/src/com/android/settings/core/InstrumentedPreferenceFragment.java
index bfb69e78a3b..a5d07156190 100644
--- a/src/com/android/settings/core/InstrumentedPreferenceFragment.java
+++ b/src/com/android/settings/core/InstrumentedPreferenceFragment.java
@@ -65,4 +65,8 @@ public abstract class InstrumentedPreferenceFragment extends ObservablePreferenc
protected final Context getPrefContext() {
return getPreferenceManager().getContext();
}
+
+ protected final VisibilityLoggerMixin getVisibilityLogger() {
+ return mVisibilityLoggerMixin;
+ }
}
diff --git a/src/com/android/settings/core/instrumentation/EventLogWriter.java b/src/com/android/settings/core/instrumentation/EventLogWriter.java
index e7628e8b6b7..3196f76b323 100644
--- a/src/com/android/settings/core/instrumentation/EventLogWriter.java
+++ b/src/com/android/settings/core/instrumentation/EventLogWriter.java
@@ -18,6 +18,7 @@ package com.android.settings.core.instrumentation;
import android.content.Context;
import android.metrics.LogMaker;
+import android.util.Log;
import android.util.Pair;
import com.android.internal.logging.MetricsLogger;
@@ -28,6 +29,8 @@ import com.android.internal.logging.nano.MetricsProto;
*/
public class EventLogWriter implements LogWriter {
+ private final MetricsLogger mMetricsLogger = new MetricsLogger();
+
public void visible(Context context, int source, int category) {
final LogMaker logMaker = new LogMaker(category)
.setType(MetricsProto.MetricsEvent.TYPE_OPEN)
@@ -39,6 +42,24 @@ public class EventLogWriter implements LogWriter {
MetricsLogger.hidden(context, category);
}
+ public void action(int category, int value, Pair... taggedData) {
+ if (taggedData == null || taggedData.length == 0) {
+ mMetricsLogger.action(category, value);
+ } else {
+ final LogMaker logMaker = new LogMaker(category)
+ .setType(MetricsProto.MetricsEvent.TYPE_ACTION)
+ .setSubtype(value);
+ for (Pair pair : taggedData) {
+ logMaker.addTaggedData(pair.first, pair.second);
+ }
+ mMetricsLogger.write(logMaker);
+ }
+ }
+
+ public void action(int category, boolean value, Pair... taggedData) {
+ action(category, value ? 1 : 0, taggedData);
+ }
+
public void action(Context context, int category, Pair... taggedData) {
action(context, category, "", taggedData);
}
@@ -52,12 +73,16 @@ public class EventLogWriter implements LogWriter {
MetricsLogger.action(logMaker);
}
+ /** @deprecated use {@link #action(int, int, Pair[])} */
+ @Deprecated
public void action(Context context, int category, int value) {
- MetricsLogger.action(context, category, Integer.toString(value));
+ MetricsLogger.action(context, category, value);
}
+ /** @deprecated use {@link #action(int, boolean, Pair[])} */
+ @Deprecated
public void action(Context context, int category, boolean value) {
- MetricsLogger.action(context, category, Boolean.toString(value));
+ MetricsLogger.action(context, category, value);
}
public void action(Context context, int category, String pkg,
diff --git a/src/com/android/settings/core/instrumentation/LogWriter.java b/src/com/android/settings/core/instrumentation/LogWriter.java
index 584217d85cb..062d46f759f 100644
--- a/src/com/android/settings/core/instrumentation/LogWriter.java
+++ b/src/com/android/settings/core/instrumentation/LogWriter.java
@@ -33,6 +33,16 @@ public interface LogWriter {
*/
void hidden(Context context, int category);
+ /**
+ * Logs a user action.
+ */
+ void action(int category, int value, Pair... taggedData);
+
+ /**
+ * Logs a user action.
+ */
+ void action(int category, boolean value, Pair... taggedData);
+
/**
* Logs an user action.
*/
@@ -45,12 +55,16 @@ public interface LogWriter {
/**
* Logs an user action.
+ * @deprecated use {@link #action(int, int, Pair[])}
*/
+ @Deprecated
void action(Context context, int category, int value);
/**
* Logs an user action.
+ * @deprecated use {@link #action(int, boolean, Pair[])}
*/
+ @Deprecated
void action(Context context, int category, boolean value);
/**
diff --git a/src/com/android/settings/core/instrumentation/MetricsFeatureProvider.java b/src/com/android/settings/core/instrumentation/MetricsFeatureProvider.java
index afdec558556..532ec66316f 100644
--- a/src/com/android/settings/core/instrumentation/MetricsFeatureProvider.java
+++ b/src/com/android/settings/core/instrumentation/MetricsFeatureProvider.java
@@ -21,7 +21,7 @@ import android.content.Intent;
import android.text.TextUtils;
import android.util.Pair;
-import com.android.internal.logging.nano.MetricsProto;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import java.util.ArrayList;
import java.util.List;
@@ -60,18 +60,44 @@ public class MetricsFeatureProvider {
}
}
+ /**
+ * Logs a user action. Includes the elapsed time since the containing
+ * fragment has been visible.
+ */
+ public void action(VisibilityLoggerMixin visibilityLogger, int category, int value) {
+ for (LogWriter writer : mLoggerWriters) {
+ writer.action(category, value,
+ sinceVisibleTaggedData(visibilityLogger.elapsedTimeSinceVisible()));
+ }
+ }
+
+ /**
+ * Logs a user action. Includes the elapsed time since the containing
+ * fragment has been visible.
+ */
+ public void action(VisibilityLoggerMixin visibilityLogger, int category, boolean value) {
+ for (LogWriter writer : mLoggerWriters) {
+ writer.action(category, value,
+ sinceVisibleTaggedData(visibilityLogger.elapsedTimeSinceVisible()));
+ }
+ }
+
public void action(Context context, int category, Pair... taggedData) {
for (LogWriter writer : mLoggerWriters) {
writer.action(context, category, taggedData);
}
}
+ /** @deprecated use {@link #action(VisibilityLoggerMixin, int, int)} */
+ @Deprecated
public void action(Context context, int category, int value) {
for (LogWriter writer : mLoggerWriters) {
writer.action(context, category, value);
}
}
+ /** @deprecated use {@link #action(VisibilityLoggerMixin, int, boolean)} */
+ @Deprecated
public void action(Context context, int category, boolean value) {
for (LogWriter writer : mLoggerWriters) {
writer.action(context, category, value);
@@ -99,7 +125,7 @@ public class MetricsFeatureProvider {
public int getMetricsCategory(Object object) {
if (object == null || !(object instanceof Instrumentable)) {
- return MetricsProto.MetricsEvent.VIEW_UNKNOWN;
+ return MetricsEvent.VIEW_UNKNOWN;
}
return ((Instrumentable) object).getMetricsCategory();
}
@@ -116,15 +142,19 @@ public class MetricsFeatureProvider {
// Not loggable
return;
}
- action(context, MetricsProto.MetricsEvent.ACTION_SETTINGS_TILE_CLICK, action,
- Pair.create(MetricsProto.MetricsEvent.FIELD_CONTEXT, sourceMetricsCategory));
+ action(context, MetricsEvent.ACTION_SETTINGS_TILE_CLICK, action,
+ Pair.create(MetricsEvent.FIELD_CONTEXT, sourceMetricsCategory));
return;
} else if (TextUtils.equals(cn.getPackageName(), context.getPackageName())) {
// Going to a Setting internal page, skip click logging in favor of page's own
// visibility logging.
return;
}
- action(context, MetricsProto.MetricsEvent.ACTION_SETTINGS_TILE_CLICK, cn.flattenToString(),
- Pair.create(MetricsProto.MetricsEvent.FIELD_CONTEXT, sourceMetricsCategory));
+ action(context, MetricsEvent.ACTION_SETTINGS_TILE_CLICK, cn.flattenToString(),
+ Pair.create(MetricsEvent.FIELD_CONTEXT, sourceMetricsCategory));
+ }
+
+ private Pair sinceVisibleTaggedData(long timestamp) {
+ return Pair.create(MetricsEvent.NOTIFICATION_SINCE_VISIBLE_MILLIS, timestamp);
}
}
diff --git a/src/com/android/settings/core/instrumentation/SettingSuggestionsLogWriter.java b/src/com/android/settings/core/instrumentation/SettingSuggestionsLogWriter.java
index bbdf8c94983..697f0a3c902 100644
--- a/src/com/android/settings/core/instrumentation/SettingSuggestionsLogWriter.java
+++ b/src/com/android/settings/core/instrumentation/SettingSuggestionsLogWriter.java
@@ -45,6 +45,14 @@ public class SettingSuggestionsLogWriter implements LogWriter {
public void actionWithSource(Context context, int source, int category) {
}
+ @Override
+ public void action(int category, int value, Pair... taggedData) {
+ }
+
+ @Override
+ public void action(int category, boolean value, Pair... taggedData) {
+ }
+
@Override
public void action(Context context, int category, int value) {
}
diff --git a/src/com/android/settings/core/instrumentation/VisibilityLoggerMixin.java b/src/com/android/settings/core/instrumentation/VisibilityLoggerMixin.java
index 8de35adfd79..2fe2a3beb1d 100644
--- a/src/com/android/settings/core/instrumentation/VisibilityLoggerMixin.java
+++ b/src/com/android/settings/core/instrumentation/VisibilityLoggerMixin.java
@@ -20,6 +20,7 @@ import android.app.Activity;
import android.content.Context;
import android.content.Intent;
+import android.os.SystemClock;
import com.android.internal.logging.nano.MetricsProto;
import com.android.settings.SettingsActivity;
import com.android.settings.overlay.FeatureFactory;
@@ -41,6 +42,7 @@ public class VisibilityLoggerMixin implements LifecycleObserver, OnResume, OnPau
private MetricsFeatureProvider mMetricsFeature;
private int mSourceMetricsCategory = MetricsProto.MetricsEvent.VIEW_UNKNOWN;
+ private long mVisibleTimestamp;
public VisibilityLoggerMixin(int metricsCategory) {
// MetricsFeature will be set during onAttach.
@@ -59,6 +61,7 @@ public class VisibilityLoggerMixin implements LifecycleObserver, OnResume, OnPau
@Override
public void onResume() {
+ mVisibleTimestamp = SystemClock.elapsedRealtime();
if (mMetricsFeature != null && mMetricsCategory != METRICS_CATEGORY_UNKNOWN) {
mMetricsFeature.visible(null /* context */, mSourceMetricsCategory, mMetricsCategory);
}
@@ -66,6 +69,7 @@ public class VisibilityLoggerMixin implements LifecycleObserver, OnResume, OnPau
@Override
public void onPause() {
+ mVisibleTimestamp = 0;
if (mMetricsFeature != null && mMetricsCategory != METRICS_CATEGORY_UNKNOWN) {
mMetricsFeature.hidden(null /* context */, mMetricsCategory);
}
@@ -85,4 +89,12 @@ public class VisibilityLoggerMixin implements LifecycleObserver, OnResume, OnPau
mSourceMetricsCategory = intent.getIntExtra(SettingsActivity.EXTRA_SOURCE_METRICS_CATEGORY,
MetricsProto.MetricsEvent.VIEW_UNKNOWN);
}
+
+ /** Returns elapsed time since onResume() */
+ public long elapsedTimeSinceVisible() {
+ if (mVisibleTimestamp == 0) {
+ return 0;
+ }
+ return SystemClock.elapsedRealtime() - mVisibleTimestamp;
+ }
}
diff --git a/src/com/android/settings/dashboard/suggestions/SuggestionAdapter.java b/src/com/android/settings/dashboard/suggestions/SuggestionAdapter.java
index 94435bda2c6..38b9d287528 100644
--- a/src/com/android/settings/dashboard/suggestions/SuggestionAdapter.java
+++ b/src/com/android/settings/dashboard/suggestions/SuggestionAdapter.java
@@ -220,6 +220,9 @@ public class SuggestionAdapter extends RecyclerView.Adapter
public Tile getSuggestion(int position) {
final long itemId = getItemId(position);
+ if (mSuggestions == null) {
+ return null;
+ }
for (Tile tile : mSuggestions) {
if (Objects.hash(tile.title) == itemId) {
return tile;
@@ -230,6 +233,9 @@ public class SuggestionAdapter extends RecyclerView.Adapter
public Suggestion getSuggestionsV2(int position) {
final long itemId = getItemId(position);
+ if (mSuggestionsV2 == null) {
+ return null;
+ }
for (Suggestion suggestion : mSuggestionsV2) {
if (Objects.hash(suggestion.getId()) == itemId) {
return suggestion;
diff --git a/src/com/android/settings/dashboard/suggestions/SuggestionFeatureProviderImpl.java b/src/com/android/settings/dashboard/suggestions/SuggestionFeatureProviderImpl.java
index f85d95a57af..1c61d8eae63 100644
--- a/src/com/android/settings/dashboard/suggestions/SuggestionFeatureProviderImpl.java
+++ b/src/com/android/settings/dashboard/suggestions/SuggestionFeatureProviderImpl.java
@@ -194,8 +194,7 @@ public class SuggestionFeatureProviderImpl implements SuggestionFeatureProvider
@VisibleForTesting
boolean hasUsedNightDisplay(Context context) {
final ContentResolver cr = context.getContentResolver();
- final long lastActivatedTimeMillis = Secure.getLong(cr,
- Secure.NIGHT_DISPLAY_LAST_ACTIVATED_TIME, -1);
- return lastActivatedTimeMillis > 0;
+ return Secure.getInt(cr, Secure.NIGHT_DISPLAY_AUTO_MODE, 0) != 0
+ || Secure.getString(cr, Secure.NIGHT_DISPLAY_LAST_ACTIVATED_TIME) != null;
}
}
diff --git a/src/com/android/settings/development/BluetoothSnoopLogPreferenceController.java b/src/com/android/settings/development/BluetoothSnoopLogPreferenceController.java
index 7734e938b29..44a2ecd19e4 100644
--- a/src/com/android/settings/development/BluetoothSnoopLogPreferenceController.java
+++ b/src/com/android/settings/development/BluetoothSnoopLogPreferenceController.java
@@ -69,12 +69,12 @@ public class BluetoothSnoopLogPreferenceController extends
}
@Override
- public void onDeveloperOptionsEnabled() {
+ protected void onDeveloperOptionsSwitchEnabled() {
mPreference.setEnabled(true);
}
@Override
- public void onDeveloperOptionsDisabled() {
+ protected void onDeveloperOptionsSwitchDisabled() {
SystemProperties.set(BLUETOOTH_BTSNOOP_ENABLE_PROPERTY, Boolean.toString(false));
mPreference.setChecked(false);
mPreference.setEnabled(false);
diff --git a/src/com/android/settings/development/ColorModePreference.java b/src/com/android/settings/development/ColorModePreference.java
index e0b0837d800..20af2c62ac8 100644
--- a/src/com/android/settings/development/ColorModePreference.java
+++ b/src/com/android/settings/development/ColorModePreference.java
@@ -28,6 +28,7 @@ import android.view.Display;
import com.android.settings.R;
import java.util.ArrayList;
+import java.util.List;
public class ColorModePreference extends SwitchPreference implements DisplayListener {
@@ -35,7 +36,28 @@ public class ColorModePreference extends SwitchPreference implements DisplayList
private Display mDisplay;
private int mCurrentIndex;
- private ArrayList mDescriptions;
+ private List mDescriptions;
+
+ public static List getColorModeDescriptions(Context context) {
+
+ List colorModeDescriptions = new ArrayList<>();
+ Resources resources = context.getResources();
+ int[] colorModes = resources.getIntArray(R.array.color_mode_ids);
+ String[] titles = resources.getStringArray(R.array.color_mode_names);
+ String[] descriptions = resources.getStringArray(R.array.color_mode_descriptions);
+ // Map the resource information describing color modes.
+ for (int i = 0; i < colorModes.length; i++) {
+ if (colorModes[i] != -1 && i != 1 /* Skip Natural for now. */) {
+ ColorModeDescription desc = new ColorModeDescription();
+ desc.colorMode = colorModes[i];
+ desc.title = titles[i];
+ desc.summary = descriptions[i];
+ colorModeDescriptions.add(desc);
+ }
+ }
+
+ return colorModeDescriptions;
+ }
public ColorModePreference(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -75,22 +97,7 @@ public class ColorModePreference extends SwitchPreference implements DisplayList
public void updateCurrentAndSupported() {
mDisplay = mDisplayManager.getDisplay(Display.DEFAULT_DISPLAY);
- mDescriptions = new ArrayList<>();
-
- Resources resources = getContext().getResources();
- int[] colorModes = resources.getIntArray(R.array.color_mode_ids);
- String[] titles = resources.getStringArray(R.array.color_mode_names);
- String[] descriptions = resources.getStringArray(R.array.color_mode_descriptions);
- // Map the resource information describing color modes.
- for (int i = 0; i < colorModes.length; i++) {
- if (colorModes[i] != -1 && i != 1 /* Skip Natural for now. */) {
- ColorModeDescription desc = new ColorModeDescription();
- desc.colorMode = colorModes[i];
- desc.title = titles[i];
- desc.summary = descriptions[i];
- mDescriptions.add(desc);
- }
- }
+ mDescriptions = getColorModeDescriptions(getContext());
int currentColorMode = mDisplay.getColorMode();
mCurrentIndex = -1;
diff --git a/src/com/android/settings/development/DeveloperOptionsPreferenceController.java b/src/com/android/settings/development/DeveloperOptionsPreferenceController.java
index a7d45ebba8e..2f1f2540ed0 100644
--- a/src/com/android/settings/development/DeveloperOptionsPreferenceController.java
+++ b/src/com/android/settings/development/DeveloperOptionsPreferenceController.java
@@ -17,6 +17,7 @@
package com.android.settings.development;
import android.content.Context;
+import android.content.Intent;
import com.android.settings.core.PreferenceControllerMixin;
import com.android.settingslib.core.AbstractPreferenceController;
@@ -35,12 +36,47 @@ public abstract class DeveloperOptionsPreferenceController extends
}
/**
- * Called when developer options is enabled
+ * Called when an activity returns to the DeveloperSettingsDashboardFragment.
+ *
+ * @param requestCode The integer request code originally supplied to
+ * startActivityForResult(), allowing you to identify who this
+ * result came from.
+ * @param resultCode The integer result code returned by the child activity
+ * through its setResult().
+ * @param data An Intent, which can return result data to the caller
+ * (various data can be attached to Intent "extras").
+ * @return true if the controller handled the activity result
*/
- public abstract void onDeveloperOptionsEnabled();
+ public boolean onActivityResult(int requestCode, int resultCode, Intent data) {
+ return false;
+ }
/**
- *Called when developer options is disabled
+ * Called when developer options is enabled
*/
- public abstract void onDeveloperOptionsDisabled();
+ 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 abstract void onDeveloperOptionsSwitchEnabled();
+
+ /**
+ * Called when developer options is disabled and the preference is available
+ */
+ protected abstract void onDeveloperOptionsSwitchDisabled();
+
}
diff --git a/src/com/android/settings/development/DevelopmentOptionsActivityRequestCodes.java b/src/com/android/settings/development/DevelopmentOptionsActivityRequestCodes.java
new file mode 100644
index 00000000000..54d1fa39d23
--- /dev/null
+++ b/src/com/android/settings/development/DevelopmentOptionsActivityRequestCodes.java
@@ -0,0 +1,24 @@
+/*
+ * 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.settings.development;
+
+/**
+ * Interface for storing Activity request codes in development options
+ */
+public interface DevelopmentOptionsActivityRequestCodes {
+ int REQUEST_CODE_ENABLE_OEM_UNLOCK = 0;
+}
diff --git a/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java b/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java
index 8df25c227c3..d334bd05185 100644
--- a/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java
+++ b/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java
@@ -16,10 +16,13 @@
package com.android.settings.development;
+import android.app.Activity;
import android.content.Context;
+import android.content.Intent;
import android.os.Bundle;
import android.os.UserManager;
import android.provider.SearchIndexableResource;
+import android.support.annotation.VisibleForTesting;
import android.util.Log;
import android.widget.Switch;
@@ -40,7 +43,7 @@ import java.util.Arrays;
import java.util.List;
public class DevelopmentSettingsDashboardFragment extends RestrictedDashboardFragment
- implements SwitchBar.OnSwitchChangeListener {
+ implements SwitchBar.OnSwitchChangeListener, OemUnlockDialogHost {
private static final String TAG = "DevSettingsDashboard";
@@ -103,6 +106,33 @@ public class DevelopmentSettingsDashboardFragment extends RestrictedDashboardFra
}
}
+ @Override
+ public void onOemUnlockDialogConfirmed() {
+ final OemUnlockPreferenceController controller = getDevelopmentOptionsController(
+ OemUnlockPreferenceController.class);
+ controller.onOemUnlockConfirmed();
+ }
+
+ @Override
+ public void onOemUnlockDialogDismissed() {
+ final OemUnlockPreferenceController controller = getDevelopmentOptionsController(
+ OemUnlockPreferenceController.class);
+ controller.onOemUnlockDismissed();
+ }
+
+ @Override
+ public void onActivityResult(int requestCode, int resultCode, Intent data) {
+ for (AbstractPreferenceController controller : mPreferenceControllers) {
+ if (controller instanceof DeveloperOptionsPreferenceController) {
+ if (((DeveloperOptionsPreferenceController) controller).onActivityResult(
+ requestCode, resultCode, data)) {
+ return;
+ }
+ }
+ }
+ super.onActivityResult(requestCode, resultCode, data);
+ }
+
@Override
protected String getLogTag() {
return TAG;
@@ -121,7 +151,8 @@ public class DevelopmentSettingsDashboardFragment extends RestrictedDashboardFra
@Override
protected List getPreferenceControllers(Context context) {
- mPreferenceControllers = buildPreferenceControllers(context, getLifecycle());
+ mPreferenceControllers = buildPreferenceControllers(context, getActivity(), getLifecycle(),
+ this /* devOptionsDashboardFragment */);
return mPreferenceControllers;
}
@@ -140,14 +171,92 @@ public class DevelopmentSettingsDashboardFragment extends RestrictedDashboardFra
}
private static List buildPreferenceControllers(Context context,
- Lifecycle lifecycle) {
+ Activity activity, Lifecycle lifecycle, DevelopmentSettingsDashboardFragment fragment) {
final List controllers = new ArrayList<>();
+ // take bug report
+ // desktop backup password
controllers.add(new StayAwakePreferenceController(context, lifecycle));
+ // hdcp checking
controllers.add(new BluetoothSnoopLogPreferenceController(context));
-
+ controllers.add(new OemUnlockPreferenceController(context, activity, fragment));
+ // running services
+ // convert to file encryption
+ controllers.add(new PictureColorModePreferenceController(context, lifecycle));
+ // webview implementation
+ // cool color temperature
+ // automatic system updates
+ // system ui demo mode
+ // quick settings developer tiles
+ // usb debugging
+ // revoke usb debugging authorizations
+ // local terminal
+ // bug report shortcut
+ // select mock location app
+ // enable view attribute inspection
+ // select debug app
+ // wait for debugger
+ // verify apps over usb
+ // logger buffer sizes
+ // store logger data persistently on device
+ // telephony monitor
+ // camera laser sensor
+ // camera HAL HDR+
+ // feature flags
+ // wireless display certification
+ // enable wi-fi verbose logging
+ // aggressive wifi to mobile handover
+ // always allow wifi roam scans
+ // mobile always active
+ // tethering hardware acceleration
+ // select usb configuration
+ // show bluetooth devices without names
+ // disable absolute volume
+ // enable in-band ringing
+ // bluetooth avrcp version
+ // bluetooth audio codec
+ // bluetooth audio sample rate
+ // bluetooth audio bits per sample
+ // bluetooth audio channel mode
+ // bluetooth audio ldac codec: playback quality
+ // show taps
+ // pointer location
+ // show surface updates
+ // show layout bounds
+ // force rtl layout direction
+ // window animation scale
+ // transition animation scale
+ // animator duration scale
+ // simulate secondary displays
+ // smallest width
+ // force gpu rendering
+ // show gpu view updates
+ // show hardware layers updates
+ // debug gpu overdraw
+ // debug non-rectangular clip operations
+ // force 4x msaa
+ // disable hw overlays
+ // simulate color space
+ // set gpu renderer
+ // disable usb audio routing
+ // strict mode enabled
+ // profile gpu rendering
+ // don't keep activities
+ // background process limit
+ // background check
+ // show all anrs
+ // show notification channel warnings
+ // inactive apps
+ // force allow apps on external
+ // force activities to be resizable
+ // reset shortcutmanager rate-limiting
return controllers;
}
+ @VisibleForTesting
+ T getDevelopmentOptionsController(Class clazz) {
+ return getPreferenceController(clazz);
+ }
+
/**
* For Search.
*/
@@ -171,7 +280,8 @@ public class DevelopmentSettingsDashboardFragment extends RestrictedDashboardFra
@Override
public List getPreferenceControllers(Context
context) {
- return buildPreferenceControllers(context, null /* lifecycle */);
+ return buildPreferenceControllers(context, null /* activity */,
+ null /* lifecycle */, null /* devOptionsDashboardFragment */);
}
};
}
diff --git a/src/com/android/settings/development/EnableOemUnlockSettingWarningDialog.java b/src/com/android/settings/development/EnableOemUnlockSettingWarningDialog.java
new file mode 100644
index 00000000000..2486ef53589
--- /dev/null
+++ b/src/com/android/settings/development/EnableOemUnlockSettingWarningDialog.java
@@ -0,0 +1,82 @@
+/*
+ * 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.settings.development;
+
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.Fragment;
+import android.app.FragmentManager;
+import android.content.DialogInterface;
+import android.os.Bundle;
+
+import com.android.internal.logging.nano.MetricsProto;
+import com.android.settings.R;
+import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
+
+public class EnableOemUnlockSettingWarningDialog extends InstrumentedDialogFragment implements
+ DialogInterface.OnClickListener, DialogInterface.OnDismissListener {
+
+ public static final String TAG = "EnableOemUnlockDlg";
+
+ public static void show(Fragment host) {
+ final FragmentManager manager = host.getActivity().getFragmentManager();
+ if (manager.findFragmentByTag(TAG) == null) {
+ final EnableOemUnlockSettingWarningDialog dialog =
+ new EnableOemUnlockSettingWarningDialog();
+ dialog.setTargetFragment(host, 0 /* requestCode */);
+ dialog.show(manager, TAG);
+ }
+ }
+
+ @Override
+ public int getMetricsCategory() {
+ return MetricsProto.MetricsEvent.DIALOG_ENABLE_OEM_UNLOCKING;
+ }
+
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ return new AlertDialog.Builder(getActivity())
+ .setTitle(R.string.confirm_enable_oem_unlock_title)
+ .setMessage(R.string.confirm_enable_oem_unlock_text)
+ .setPositiveButton(R.string.enable_text, this /* onClickListener */)
+ .setNegativeButton(android.R.string.cancel, this /* onClickListener */)
+ .create();
+ }
+
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ final OemUnlockDialogHost host = (OemUnlockDialogHost) getTargetFragment();
+ if (host == null) {
+ return;
+ }
+ if (which == DialogInterface.BUTTON_POSITIVE) {
+ host.onOemUnlockDialogConfirmed();
+ } else {
+ host.onOemUnlockDialogDismissed();
+ }
+ }
+
+ @Override
+ public void onDismiss(DialogInterface dialog) {
+ super.onDismiss(dialog);
+ final OemUnlockDialogHost host = (OemUnlockDialogHost) getTargetFragment();
+ if (host == null) {
+ return;
+ }
+ host.onOemUnlockDialogDismissed();
+ }
+}
diff --git a/src/com/android/settings/development/OemUnlockDialogHost.java b/src/com/android/settings/development/OemUnlockDialogHost.java
new file mode 100644
index 00000000000..c134e9c2ea2
--- /dev/null
+++ b/src/com/android/settings/development/OemUnlockDialogHost.java
@@ -0,0 +1,33 @@
+/*
+ * 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.settings.development;
+
+/**
+ * Interface for OemUnlockDialogFragment callbacks.
+ */
+public interface OemUnlockDialogHost {
+
+ /**
+ * Called when the user presses enable on the warning dialog.
+ */
+ void onOemUnlockDialogConfirmed();
+
+ /**
+ * Called when the user dismisses or cancels the warning dialog.
+ */
+ void onOemUnlockDialogDismissed();
+}
diff --git a/src/com/android/settings/development/OemUnlockPreferenceController.java b/src/com/android/settings/development/OemUnlockPreferenceController.java
new file mode 100644
index 00000000000..cb391a8e893
--- /dev/null
+++ b/src/com/android/settings/development/OemUnlockPreferenceController.java
@@ -0,0 +1,216 @@
+/*
+ * 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.settings.development;
+
+import static com.android.settings.development.DevelopmentOptionsActivityRequestCodes
+ .REQUEST_CODE_ENABLE_OEM_UNLOCK;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.content.res.Resources;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.service.oemlock.OemLockManager;
+import android.support.annotation.VisibleForTesting;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceScreen;
+import android.telephony.TelephonyManager;
+
+import com.android.settings.R;
+import com.android.settings.password.ChooseLockSettingsHelper;
+import com.android.settingslib.RestrictedSwitchPreference;
+
+public class OemUnlockPreferenceController extends DeveloperOptionsPreferenceController implements
+ Preference.OnPreferenceChangeListener {
+
+ private static final String PREFERENCE_KEY = "oem_unlock_enable";
+
+ private final OemLockManager mOemLockManager;
+ private final UserManager mUserManager;
+ private final TelephonyManager mTelephonyManager;
+ private final DevelopmentSettingsDashboardFragment mFragment;
+ private final ChooseLockSettingsHelper mChooseLockSettingsHelper;
+ private RestrictedSwitchPreference mPreference;
+
+ public OemUnlockPreferenceController(Context context, Activity activity,
+ DevelopmentSettingsDashboardFragment fragment) {
+ super(context);
+ mOemLockManager = (OemLockManager) context.getSystemService(Context.OEM_LOCK_SERVICE);
+ mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
+ mTelephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
+ mFragment = fragment;
+ if (activity != null || mFragment != null) {
+ mChooseLockSettingsHelper = new ChooseLockSettingsHelper(activity, mFragment);
+ } else {
+ mChooseLockSettingsHelper = null;
+ }
+ }
+
+ @Override
+ public boolean isAvailable() {
+ return mOemLockManager != null;
+ }
+
+ @Override
+ public String getPreferenceKey() {
+ return PREFERENCE_KEY;
+ }
+
+ @Override
+ public void displayPreference(PreferenceScreen screen) {
+ super.displayPreference(screen);
+
+ mPreference = (RestrictedSwitchPreference) screen.findPreference(getPreferenceKey());
+ }
+
+ @Override
+ public boolean onPreferenceChange(Preference preference, Object newValue) {
+ boolean isUnlocked = (Boolean) newValue;
+ if (isUnlocked) {
+ if (!showKeyguardConfirmation(mContext.getResources(),
+ REQUEST_CODE_ENABLE_OEM_UNLOCK)) {
+ confirmEnableOemUnlock();
+ }
+ } else {
+ mOemLockManager.setOemUnlockAllowedByUser(false);
+ }
+ return true;
+ }
+
+ @Override
+ public void updateState(Preference preference) {
+ super.updateState(preference);
+ mPreference.setChecked(mOemLockManager.isOemUnlockAllowed());
+ updateOemUnlockSettingDescription();
+ // Showing mEnableOemUnlock preference as device has persistent data block.
+ mPreference.setDisabledByAdmin(null);
+ mPreference.setEnabled(enableOemUnlockPreference());
+ if (mPreference.isEnabled()) {
+ // Check restriction, disable mEnableOemUnlock and apply policy transparency.
+ mPreference.checkRestrictionAndSetDisabled(UserManager.DISALLOW_FACTORY_RESET);
+ }
+ }
+
+ @Override
+ public boolean onActivityResult(int requestCode, int resultCode, Intent data) {
+ if (requestCode == REQUEST_CODE_ENABLE_OEM_UNLOCK) {
+ if (resultCode == Activity.RESULT_OK) {
+ if (mPreference.isChecked()) {
+ confirmEnableOemUnlock();
+ } else {
+ mOemLockManager.setOemUnlockAllowedByUser(false);
+ }
+ }
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ protected void onDeveloperOptionsSwitchEnabled() {
+ handleDeveloperOptionsToggled();
+ }
+
+ @Override
+ protected void onDeveloperOptionsSwitchDisabled() {
+ handleDeveloperOptionsToggled();
+ }
+
+ public void onOemUnlockConfirmed() {
+ mOemLockManager.setOemUnlockAllowedByUser(true);
+ }
+
+ public void onOemUnlockDismissed() {
+ if (mPreference == null) {
+ return;
+ }
+ updateState(mPreference);
+ }
+
+ private void handleDeveloperOptionsToggled() {
+ mPreference.setEnabled(enableOemUnlockPreference());
+ if (mPreference.isEnabled()) {
+ // Check restriction, disable mEnableOemUnlock and apply policy transparency.
+ mPreference.checkRestrictionAndSetDisabled(UserManager.DISALLOW_FACTORY_RESET);
+ }
+ }
+
+ private void updateOemUnlockSettingDescription() {
+ int oemUnlockSummary = R.string.oem_unlock_enable_summary;
+ if (isBootloaderUnlocked()) {
+ oemUnlockSummary = R.string.oem_unlock_enable_disabled_summary_bootloader_unlocked;
+ } else if (isSimLockedDevice()) {
+ oemUnlockSummary = R.string.oem_unlock_enable_disabled_summary_sim_locked_device;
+ } else if (!isOemUnlockAllowedByUserAndCarrier()) {
+ // If the device isn't SIM-locked but OEM unlock is disallowed by some party, this
+ // means either some other carrier restriction is in place or the device hasn't been
+ // able to confirm which restrictions (SIM-lock or otherwise) apply.
+ oemUnlockSummary =
+ R.string.oem_unlock_enable_disabled_summary_connectivity_or_locked;
+ }
+ mPreference.setSummary(mContext.getResources().getString(oemUnlockSummary));
+ }
+
+ /** Returns {@code true} if the device is SIM-locked. Otherwise, returns {@code false}. */
+ private boolean isSimLockedDevice() {
+ int phoneCount = mTelephonyManager.getPhoneCount();
+ for (int i = 0; i < phoneCount; i++) {
+ if (mTelephonyManager.getAllowedCarriers(i).size() > 0) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Returns {@code true} if the bootloader has been unlocked. Otherwise, returns {code false}.
+ */
+ private boolean isBootloaderUnlocked() {
+ return mOemLockManager.isDeviceOemUnlocked();
+ }
+
+ private boolean enableOemUnlockPreference() {
+ return !isBootloaderUnlocked() && isOemUnlockAllowedByUserAndCarrier();
+ }
+
+
+ @VisibleForTesting
+ boolean showKeyguardConfirmation(Resources resources, int requestCode) {
+ return mChooseLockSettingsHelper.launchConfirmationActivity(
+ requestCode, resources.getString(R.string.oem_unlock_enable));
+ }
+
+ @VisibleForTesting
+ void confirmEnableOemUnlock() {
+ EnableOemUnlockSettingWarningDialog.show(mFragment);
+ }
+
+ /**
+ * Returns whether OEM unlock is allowed by the user and carrier.
+ *
+ * This does not take into account any restrictions imposed by the device policy.
+ */
+ @VisibleForTesting
+ boolean isOemUnlockAllowedByUserAndCarrier() {
+ final UserHandle userHandle = UserHandle.of(UserHandle.myUserId());
+ return mOemLockManager.isOemUnlockAllowedByCarrier()
+ && !mUserManager.hasBaseUserRestriction(UserManager.DISALLOW_FACTORY_RESET,
+ userHandle);
+ }
+
+}
diff --git a/src/com/android/settings/development/PictureColorModePreferenceController.java b/src/com/android/settings/development/PictureColorModePreferenceController.java
new file mode 100644
index 00000000000..fe4755ff409
--- /dev/null
+++ b/src/com/android/settings/development/PictureColorModePreferenceController.java
@@ -0,0 +1,99 @@
+/*
+ * 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.settings.development;
+
+import android.content.Context;
+import android.support.annotation.VisibleForTesting;
+import android.support.v7.preference.PreferenceScreen;
+
+import com.android.settingslib.core.lifecycle.Lifecycle;
+import com.android.settingslib.core.lifecycle.LifecycleObserver;
+import com.android.settingslib.core.lifecycle.events.OnPause;
+import com.android.settingslib.core.lifecycle.events.OnResume;
+
+public class PictureColorModePreferenceController extends
+ DeveloperOptionsPreferenceController implements
+ LifecycleObserver, OnResume, OnPause {
+
+ private static final String KEY_COLOR_MODE = "picture_color_mode";
+
+ private ColorModePreference mPreference;
+
+ public PictureColorModePreferenceController(Context context, Lifecycle lifecycle) {
+ super(context);
+
+ if (lifecycle != null) {
+ lifecycle.addObserver(this);
+ }
+ }
+
+ @Override
+ public boolean isAvailable() {
+ return getColorModeDescriptionsSize() > 1 && !isWideColorGamut();
+ }
+
+ @Override
+ public String getPreferenceKey() {
+ return KEY_COLOR_MODE;
+ }
+
+ @Override
+ public void displayPreference(PreferenceScreen screen) {
+ super.displayPreference(screen);
+ mPreference = (ColorModePreference) screen.findPreference(getPreferenceKey());
+ if (mPreference != null) {
+ mPreference.updateCurrentAndSupported();
+ }
+ }
+
+ @Override
+ public void onResume() {
+ if (mPreference == null) {
+ return;
+ }
+ mPreference.startListening();
+ mPreference.updateCurrentAndSupported();
+ }
+
+ @Override
+ public void onPause() {
+ if (mPreference == null) {
+ return;
+ }
+ mPreference.stopListening();
+ }
+
+ @Override
+ protected void onDeveloperOptionsSwitchEnabled() {
+ mPreference.setEnabled(true);
+ }
+
+ @Override
+ protected void onDeveloperOptionsSwitchDisabled() {
+ mPreference.setEnabled(false);
+ }
+
+ @VisibleForTesting
+ boolean isWideColorGamut() {
+ return mContext.getDisplay().isWideColorGamut();
+ }
+
+ @VisibleForTesting
+ int getColorModeDescriptionsSize() {
+ return ColorModePreference.getColorModeDescriptions(mContext).size();
+ }
+}
diff --git a/src/com/android/settings/development/StayAwakePreferenceController.java b/src/com/android/settings/development/StayAwakePreferenceController.java
index a590d7d66af..ecbb9d0b576 100644
--- a/src/com/android/settings/development/StayAwakePreferenceController.java
+++ b/src/com/android/settings/development/StayAwakePreferenceController.java
@@ -100,19 +100,6 @@ public class StayAwakePreferenceController extends DeveloperOptionsPreferenceCon
mPreference.setChecked(stayAwakeMode != SETTING_VALUE_OFF);
}
- @Override
- public void onDeveloperOptionsEnabled() {
- mPreference.setEnabled(true);
- }
-
- @Override
- public void onDeveloperOptionsDisabled() {
- Settings.Global.putInt(mContext.getContentResolver(),
- Settings.Global.STAY_ON_WHILE_PLUGGED_IN, SETTING_VALUE_OFF);
- mPreference.setChecked(false);
- mPreference.setEnabled(false);
- }
-
@Override
public void onResume() {
if (mPreference != null) {
@@ -127,6 +114,19 @@ public class StayAwakePreferenceController extends DeveloperOptionsPreferenceCon
}
}
+ @Override
+ protected void onDeveloperOptionsSwitchEnabled() {
+ mPreference.setEnabled(true);
+ }
+
+ @Override
+ protected void onDeveloperOptionsSwitchDisabled() {
+ Settings.Global.putInt(mContext.getContentResolver(),
+ Settings.Global.STAY_ON_WHILE_PLUGGED_IN, SETTING_VALUE_OFF);
+ mPreference.setChecked(false);
+ mPreference.setEnabled(false);
+ }
+
@VisibleForTesting
RestrictedLockUtils.EnforcedAdmin checkIfMaximumTimeToLockSetByAdmin() {
// A DeviceAdmin has specified a maximum time until the device
diff --git a/src/com/android/settings/display/NightDisplayPreference.java b/src/com/android/settings/display/NightDisplayPreference.java
index 38b57a2460b..b966530d9f1 100644
--- a/src/com/android/settings/display/NightDisplayPreference.java
+++ b/src/com/android/settings/display/NightDisplayPreference.java
@@ -22,6 +22,7 @@ import com.android.internal.app.NightDisplayController;
import com.android.settings.R;
import java.text.DateFormat;
+import java.time.LocalTime;
import java.util.Calendar;
import java.util.TimeZone;
@@ -58,11 +59,11 @@ public class NightDisplayPreference extends SwitchPreference
mController.setListener(null);
}
- private String getFormattedTimeString(NightDisplayController.LocalTime localTime) {
+ private String getFormattedTimeString(LocalTime localTime) {
final Calendar c = Calendar.getInstance();
c.setTimeZone(mTimeFormatter.getTimeZone());
- c.set(Calendar.HOUR_OF_DAY, localTime.hourOfDay);
- c.set(Calendar.MINUTE, localTime.minute);
+ c.set(Calendar.HOUR_OF_DAY, localTime.getHour());
+ c.set(Calendar.MINUTE, localTime.getMinute());
c.set(Calendar.SECOND, 0);
c.set(Calendar.MILLISECOND, 0);
return mTimeFormatter.format(c.getTime());
@@ -116,12 +117,12 @@ public class NightDisplayPreference extends SwitchPreference
}
@Override
- public void onCustomStartTimeChanged(NightDisplayController.LocalTime startTime) {
+ public void onCustomStartTimeChanged(LocalTime startTime) {
updateSummary();
}
@Override
- public void onCustomEndTimeChanged(NightDisplayController.LocalTime endTime) {
+ public void onCustomEndTimeChanged(LocalTime endTime) {
updateSummary();
}
}
diff --git a/src/com/android/settings/display/NightDisplaySettings.java b/src/com/android/settings/display/NightDisplaySettings.java
index 23ddf078ac4..f9568fb6f50 100644
--- a/src/com/android/settings/display/NightDisplaySettings.java
+++ b/src/com/android/settings/display/NightDisplaySettings.java
@@ -32,6 +32,7 @@ import com.android.settings.widget.SeekBarPreference;
import com.android.settings.SettingsPreferenceFragment;
import java.text.DateFormat;
+import java.time.LocalTime;
import java.util.Calendar;
import java.util.TimeZone;
@@ -144,7 +145,7 @@ public class NightDisplaySettings extends SettingsPreferenceFragment
@Override
public Dialog onCreateDialog(final int dialogId) {
if (dialogId == DIALOG_START_TIME || dialogId == DIALOG_END_TIME) {
- final NightDisplayController.LocalTime initialTime;
+ final LocalTime initialTime;
if (dialogId == DIALOG_START_TIME) {
initialTime = mController.getCustomStartTime();
} else {
@@ -156,15 +157,14 @@ public class NightDisplaySettings extends SettingsPreferenceFragment
return new TimePickerDialog(context, new TimePickerDialog.OnTimeSetListener() {
@Override
public void onTimeSet(TimePicker view, int hourOfDay, int minute) {
- final NightDisplayController.LocalTime time =
- new NightDisplayController.LocalTime(hourOfDay, minute);
+ final LocalTime time = LocalTime.of(hourOfDay, minute);
if (dialogId == DIALOG_START_TIME) {
mController.setCustomStartTime(time);
} else {
mController.setCustomEndTime(time);
}
}
- }, initialTime.hourOfDay, initialTime.minute, use24HourFormat);
+ }, initialTime.getHour(), initialTime.getMinute(), use24HourFormat);
}
return super.onCreateDialog(dialogId);
}
@@ -201,11 +201,11 @@ public class NightDisplaySettings extends SettingsPreferenceFragment
mTemperaturePreference.setProgress(convertTemperature(colorTemperature));
}
- private String getFormattedTimeString(NightDisplayController.LocalTime localTime) {
+ private String getFormattedTimeString(LocalTime localTime) {
final Calendar c = Calendar.getInstance();
c.setTimeZone(mTimeFormatter.getTimeZone());
- c.set(Calendar.HOUR_OF_DAY, localTime.hourOfDay);
- c.set(Calendar.MINUTE, localTime.minute);
+ c.set(Calendar.HOUR_OF_DAY, localTime.getHour());
+ c.set(Calendar.MINUTE, localTime.getMinute());
c.set(Calendar.SECOND, 0);
c.set(Calendar.MILLISECOND, 0);
return mTimeFormatter.format(c.getTime());
@@ -221,12 +221,12 @@ public class NightDisplaySettings extends SettingsPreferenceFragment
}
@Override
- public void onCustomStartTimeChanged(NightDisplayController.LocalTime startTime) {
+ public void onCustomStartTimeChanged(LocalTime startTime) {
mStartTimePreference.setSummary(getFormattedTimeString(startTime));
}
@Override
- public void onCustomEndTimeChanged(NightDisplayController.LocalTime endTime) {
+ public void onCustomEndTimeChanged(LocalTime endTime) {
mEndTimePreference.setSummary(getFormattedTimeString(endTime));
}
diff --git a/src/com/android/settings/notification/ChannelNotificationSettings.java b/src/com/android/settings/notification/ChannelNotificationSettings.java
index e9f8cafa8ba..5db256d3859 100644
--- a/src/com/android/settings/notification/ChannelNotificationSettings.java
+++ b/src/com/android/settings/notification/ChannelNotificationSettings.java
@@ -360,7 +360,9 @@ public class ChannelNotificationSettings extends NotificationSettingsBase {
}
void updateDependents(boolean banned) {
+ PreferenceGroup parent;
if (mShowLegacyChannelConfig) {
+ parent = getPreferenceScreen();
setVisible(mImportanceToggle, checkCanBeVisible(NotificationManager.IMPORTANCE_MIN));
} else {
setVisible(mAdvanced, checkCanBeVisible(NotificationManager.IMPORTANCE_MIN));
@@ -369,12 +371,13 @@ public class ChannelNotificationSettings extends NotificationSettingsBase {
NotificationManager.IMPORTANCE_DEFAULT) && canPulseLight());
setVisible(mVibrate, checkCanBeVisible(NotificationManager.IMPORTANCE_DEFAULT));
setVisible(mRingtone, checkCanBeVisible(NotificationManager.IMPORTANCE_DEFAULT));
+ parent = mAdvanced;
}
- setVisible(mAdvanced, mBadge, checkCanBeVisible(NotificationManager.IMPORTANCE_MIN));
- setVisible(mAdvanced, mPriority, checkCanBeVisible(NotificationManager.IMPORTANCE_DEFAULT)
+ setVisible(parent, mBadge, checkCanBeVisible(NotificationManager.IMPORTANCE_MIN));
+ setVisible(parent, mPriority, checkCanBeVisible(NotificationManager.IMPORTANCE_DEFAULT)
|| (checkCanBeVisible(NotificationManager.IMPORTANCE_LOW)
&& mDndVisualEffectsSuppressed));
- setVisible(mAdvanced, mVisibilityOverride, isLockScreenSecure()
+ setVisible(parent, mVisibilityOverride, isLockScreenSecure()
&&checkCanBeVisible(NotificationManager.IMPORTANCE_LOW));
setVisible(mBlockedDesc, mChannel.getImportance() == IMPORTANCE_NONE);
if (mAppLink != null) {
diff --git a/src/com/android/settings/wifi/WifiSettings.java b/src/com/android/settings/wifi/WifiSettings.java
index b47391d0dd7..b143f5854de 100644
--- a/src/com/android/settings/wifi/WifiSettings.java
+++ b/src/com/android/settings/wifi/WifiSettings.java
@@ -1070,7 +1070,7 @@ public class WifiSettings extends RestrictedSettingsFragment
protected void connect(final WifiConfiguration config, boolean isSavedNetwork) {
// Log subtype if configuration is a saved network.
- mMetricsFeatureProvider.action(getActivity(), MetricsEvent.ACTION_WIFI_CONNECT,
+ mMetricsFeatureProvider.action(getVisibilityLogger(), MetricsEvent.ACTION_WIFI_CONNECT,
isSavedNetwork);
mWifiManager.connect(config, mConnectListener);
mClickedConnect = true;
diff --git a/src/com/android/settings/wrapper/UserManagerWrapper.java b/src/com/android/settings/wrapper/UserManagerWrapper.java
index eeb648bd102..4b4d2f4166a 100644
--- a/src/com/android/settings/wrapper/UserManagerWrapper.java
+++ b/src/com/android/settings/wrapper/UserManagerWrapper.java
@@ -41,4 +41,8 @@ public class UserManagerWrapper {
public List getUsers() {
return mUserManager.getUsers();
}
+
+ public List getProfiles(int userHandle) {
+ return mUserManager.getProfiles(userHandle);
+ }
}
diff --git a/tests/robotests/src/com/android/settings/applications/PictureInPictureSettingsTest.java b/tests/robotests/src/com/android/settings/applications/PictureInPictureSettingsTest.java
new file mode 100644
index 00000000000..d379dbd10a0
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/applications/PictureInPictureSettingsTest.java
@@ -0,0 +1,170 @@
+/*
+ * 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.settings.applications;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.nullable;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.UserInfo;
+import android.util.Pair;
+
+import com.android.internal.logging.nano.MetricsProto;
+import com.android.settings.TestConfig;
+import com.android.settings.testutils.FakeFeatureFactory;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+import com.android.settings.wrapper.UserManagerWrapper;
+import com.android.settingslib.wrapper.PackageManagerWrapper;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Answers;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.annotation.Config;
+
+import java.util.ArrayList;
+import java.util.Collections;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class PictureInPictureSettingsTest {
+
+ private static final int PRIMARY_USER_ID = 0;
+ private static final int PROFILE_USER_ID = 10;
+
+ @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+ private Context mContext;
+
+ private FakeFeatureFactory mFeatureFactory;
+ private PictureInPictureSettings mFragment;
+ @Mock
+ private PackageManagerWrapper mPackageManager;
+ @Mock
+ private UserManagerWrapper mUserManager;
+ private ArrayList mPrimaryUserPackages;
+ private ArrayList mProfileUserPackages;
+ private ArrayList mUsers;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ FakeFeatureFactory.setupForTest(mContext);
+ mFeatureFactory = (FakeFeatureFactory) FakeFeatureFactory.getFactory(mContext);
+ mFragment = new PictureInPictureSettings(mPackageManager, mUserManager);
+ mPrimaryUserPackages = new ArrayList<>();
+ mProfileUserPackages = new ArrayList<>();
+ mUsers = new ArrayList<>();
+ when(mPackageManager.getInstalledPackagesAsUser(anyInt(), eq(PRIMARY_USER_ID)))
+ .thenReturn(mPrimaryUserPackages);
+ when(mPackageManager.getInstalledPackagesAsUser(anyInt(), eq(PROFILE_USER_ID)))
+ .thenReturn(mProfileUserPackages);
+ when(mUserManager.getProfiles(anyInt())).thenReturn(mUsers);
+ }
+
+ @Test
+ public void testCollectPipApps() {
+ PackageInfo primaryP1 = createPackage("Calculator", true);
+ PackageInfo primaryP2 = createPackage("Clock", false);
+ PackageInfo profileP1 = createPackage("Calculator", false);
+ PackageInfo profileP2 = createPackage("Clock", true);
+
+ mPrimaryUserPackages.add(primaryP1);
+ mPrimaryUserPackages.add(primaryP2);
+ mProfileUserPackages.add(profileP1);
+ mProfileUserPackages.add(profileP2);
+
+ ArrayList> apps = mFragment.collectPipApps(PRIMARY_USER_ID);
+ assertThat(containsPackages(apps, primaryP1, profileP2));
+ assertThat(!containsPackages(apps, primaryP2, profileP1));
+ }
+
+ @Test
+ public void testAppSort() {
+ PackageInfo primaryP1 = createPackage("Android", true);
+ PackageInfo primaryP2 = createPackage("Boop", true);
+ PackageInfo primaryP3 = createPackage("Deck", true);
+ PackageInfo profileP1 = createPackage("Android", true);
+ PackageInfo profileP2 = createPackage("Cool", true);
+ PackageInfo profileP3 = createPackage("Fast", false);
+
+ mPrimaryUserPackages.add(primaryP1);
+ mPrimaryUserPackages.add(primaryP2);
+ mPrimaryUserPackages.add(primaryP3);
+ mProfileUserPackages.add(profileP1);
+ mProfileUserPackages.add(profileP2);
+ mProfileUserPackages.add(profileP3);
+
+ ArrayList> apps = mFragment.collectPipApps(PRIMARY_USER_ID);
+ Collections.sort(apps, new PictureInPictureSettings.AppComparator(null));
+ assertThat(isOrdered(apps, primaryP1, profileP1, primaryP2, profileP2));
+ }
+
+ private boolean containsPackages(ArrayList> apps,
+ PackageInfo... packages) {
+ for (int i = 0; i < packages.length; i++) {
+ boolean found = false;
+ for (int j = 0; j < apps.size(); j++) {
+ if (apps.get(j).first == packages[i].applicationInfo) {
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private boolean isOrdered(ArrayList> apps,
+ PackageInfo... packages) {
+ if (apps.size() != packages.length) {
+ return false;
+ }
+
+ for (int i = 0; i < packages.length; i++) {
+ if (packages[i].applicationInfo != apps.get(i).first) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private PackageInfo createPackage(String appTitle, boolean supportsPip) {
+ PackageInfo pi = new PackageInfo();
+ ActivityInfo ai = new ActivityInfo();
+ if (supportsPip) {
+ ai.flags |= ActivityInfo.FLAG_SUPPORTS_PICTURE_IN_PICTURE;
+ }
+ pi.activities = new ActivityInfo[1];
+ pi.activities[0] = ai;
+ pi.applicationInfo = new ApplicationInfo();
+ pi.applicationInfo.name = appTitle;
+ return pi;
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/applications/UsageAccessDetailsTest.java b/tests/robotests/src/com/android/settings/applications/UsageAccessDetailsTest.java
index 229057fb0a9..07acb13de02 100644
--- a/tests/robotests/src/com/android/settings/applications/UsageAccessDetailsTest.java
+++ b/tests/robotests/src/com/android/settings/applications/UsageAccessDetailsTest.java
@@ -21,6 +21,7 @@ import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.verify;
import android.content.Context;
+import android.os.RemoteException;
import com.android.internal.logging.nano.MetricsProto;
import com.android.settings.testutils.SettingsRobolectricTestRunner;
@@ -65,4 +66,11 @@ public class UsageAccessDetailsTest {
verify(mFeatureFactory.metricsFeatureProvider).action(nullable(Context.class),
eq(MetricsProto.MetricsEvent.APP_SPECIAL_PERMISSION_USAGE_VIEW_DENY), eq("app"));
}
+
+ @Test
+ public void refreshUi_nullPackageInfo_shouldNotCrash() throws RemoteException {
+ mFragment.mPackageInfo = null;
+ mFragment.refreshUi();
+ // should not crash
+ }
}
diff --git a/tests/robotests/src/com/android/settings/bluetooth/BluetoothSummaryUpdaterTest.java b/tests/robotests/src/com/android/settings/bluetooth/BluetoothSummaryUpdaterTest.java
index e3f00d807b6..0c27412c551 100644
--- a/tests/robotests/src/com/android/settings/bluetooth/BluetoothSummaryUpdaterTest.java
+++ b/tests/robotests/src/com/android/settings/bluetooth/BluetoothSummaryUpdaterTest.java
@@ -18,17 +18,25 @@ package com.android.settings.bluetooth;
import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doCallRealMethod;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.content.Context;
+import android.util.Log;
import com.android.settings.R;
-import com.android.settings.testutils.SettingsRobolectricTestRunner;
import com.android.settings.TestConfig;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
import com.android.settings.widget.SummaryUpdater.OnSummaryChangeListener;
-import com.android.settingslib.bluetooth.CachedBluetoothDevice;
-import com.android.settingslib.bluetooth.LocalBluetoothManager;
import com.android.settingslib.bluetooth.LocalBluetoothAdapter;
+import com.android.settingslib.bluetooth.LocalBluetoothManager;
import org.junit.Before;
import org.junit.Test;
@@ -39,19 +47,9 @@ import org.mockito.MockitoAnnotations;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
-import java.util.ArrayList;
import java.util.HashSet;
-import java.util.List;
import java.util.Set;
-import static org.mockito.Matchers.anyString;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
@RunWith(SettingsRobolectricTestRunner.class)
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
public class BluetoothSummaryUpdaterTest {
@@ -70,16 +68,33 @@ public class BluetoothSummaryUpdaterTest {
@Mock
private SummaryListener mListener;
+ // Disabled by default
+ private final boolean[] mAdapterEnabled = {false};
+ // Not connected by default
+ private final int[] mAdapterConnectionState = {BluetoothAdapter.STATE_DISCONNECTED};
+ // Not connected by default
+ private final boolean[] mDeviceConnected = {false, false};
+ private final Set mBondedDevices = new HashSet<>();
private BluetoothSummaryUpdater mSummaryUpdater;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
- when(mBluetoothManager.getBluetoothAdapter()).thenReturn(mBtAdapter);
- when(mBtAdapter.isEnabled()).thenReturn(true);
- when(mBtAdapter.getConnectionState()).thenReturn(BluetoothAdapter.STATE_CONNECTED);
mContext = RuntimeEnvironment.application.getApplicationContext();
+ doCallRealMethod().when(mListener).onSummaryChanged(anyString());
+ // Setup mock adapter
+ when(mBluetoothManager.getBluetoothAdapter()).thenReturn(mBtAdapter);
+ doAnswer(invocation -> mAdapterEnabled[0]).when(mBtAdapter).isEnabled();
+ doAnswer(invocation -> mAdapterConnectionState[0]).when(mBtAdapter).getConnectionState();
mSummaryUpdater = new BluetoothSummaryUpdater(mContext, mListener, mBluetoothManager);
+ // Setup first device
+ doReturn(DEVICE_NAME).when(mConnectedDevice).getName();
+ doAnswer(invocation -> mDeviceConnected[0]).when(mConnectedDevice).isConnected();
+ // Setup second device
+ doReturn(DEVICE_KEYBOARD_NAME).when(mConnectedKeyBoardDevice).getName();
+ doAnswer(invocation -> mDeviceConnected[1]).when(mConnectedKeyBoardDevice)
+ .isConnected();
+ doReturn(mBondedDevices).when(mBtAdapter).getBondedDevices();
}
@Test
@@ -98,7 +113,10 @@ public class BluetoothSummaryUpdaterTest {
@Test
public void register_true_shouldSendSummaryChange() {
- prepareConnectedDevice(false);
+ mAdapterEnabled[0] = true;
+ mAdapterConnectionState[0] = BluetoothAdapter.STATE_CONNECTED;
+ mBondedDevices.add(mConnectedDevice);
+ mDeviceConnected[0] = true;
mSummaryUpdater.register(true);
@@ -108,7 +126,11 @@ public class BluetoothSummaryUpdaterTest {
@Test
public void onBluetoothStateChanged_btDisabled_shouldSendDisabledSummary() {
- mSummaryUpdater.register(true);
+ // These states should be ignored
+ mAdapterConnectionState[0] = BluetoothAdapter.STATE_CONNECTED;
+ mBondedDevices.add(mConnectedDevice);
+ mDeviceConnected[0] = true;
+
mSummaryUpdater.onBluetoothStateChanged(BluetoothAdapter.STATE_OFF);
verify(mListener).onSummaryChanged(mContext.getString(R.string.bluetooth_disabled));
@@ -116,68 +138,83 @@ public class BluetoothSummaryUpdaterTest {
@Test
public void onBluetoothStateChanged_btEnabled_connected_shouldSendConnectedSummary() {
- prepareConnectedDevice(false);
+ mAdapterEnabled[0] = true;
+ mAdapterConnectionState[0] = BluetoothAdapter.STATE_CONNECTED;
+ mBondedDevices.add(mConnectedDevice);
+ mDeviceConnected[0] = true;
- mSummaryUpdater.register(true);
mSummaryUpdater.onBluetoothStateChanged(BluetoothAdapter.STATE_ON);
verify(mListener).onSummaryChanged(
mContext.getString(R.string.bluetooth_connected_summary, DEVICE_NAME));
}
+ @Test
+ public void onBluetoothStateChanged_btEnabled_connectedMisMatch_shouldSendNotConnected() {
+ mAdapterEnabled[0] = true;
+ mAdapterConnectionState[0] = BluetoothAdapter.STATE_CONNECTED;
+ mBondedDevices.add(mConnectedDevice);
+ // State mismatch
+ mDeviceConnected[0] = false;
+
+ mSummaryUpdater.onBluetoothStateChanged(BluetoothAdapter.STATE_ON);
+
+ verify(mListener).onSummaryChanged(mContext.getString(R.string.disconnected));
+ }
+
@Test
public void onBluetoothStateChanged_btEnabled_notConnected_shouldSendDisconnectedMessage() {
- when(mBtAdapter.getConnectionState()).thenReturn(BluetoothAdapter.STATE_DISCONNECTED);
- mSummaryUpdater.register(true);
+ mAdapterEnabled[0] = true;
+ mAdapterConnectionState[0] = BluetoothAdapter.STATE_DISCONNECTED;
+ mBondedDevices.add(mConnectedDevice);
+ // This should be ignored
+ mDeviceConnected[0] = true;
+
mSummaryUpdater.onBluetoothStateChanged(BluetoothAdapter.STATE_TURNING_ON);
- verify(mListener).onSummaryChanged(
- mContext.getString(R.string.disconnected));
+ verify(mListener).onSummaryChanged(mContext.getString(R.string.disconnected));
}
@Test
public void onBluetoothStateChanged_ConnectedDisabledEnabled_shouldSendDisconnectedSummary() {
- final boolean[] connected = {false};
- final List devices = new ArrayList<>();
- devices.add(mock(CachedBluetoothDevice.class));
- doAnswer(invocation -> connected[0]).when(devices.get(0)).isConnected();
- when(mBluetoothManager.getCachedDeviceManager().getCachedDevicesCopy())
- .thenReturn(devices);
- when(mBtAdapter.getConnectionState()).thenReturn(BluetoothAdapter.STATE_DISCONNECTED);
- prepareConnectedDevice(false);
+ mAdapterEnabled[0] = true;
+ mAdapterConnectionState[0] = BluetoothAdapter.STATE_DISCONNECTED;
+ mBondedDevices.add(mConnectedDevice);
+ mDeviceConnected[0] = false;
mSummaryUpdater.register(true);
verify(mListener).onSummaryChanged(mContext.getString(R.string.disconnected));
- connected[0] = true;
- when(mBtAdapter.getConnectionState()).thenReturn(BluetoothAdapter.STATE_CONNECTED);
+ mAdapterConnectionState[0] = BluetoothAdapter.STATE_CONNECTED;
+ mDeviceConnected[0] = true;
mSummaryUpdater.onConnectionStateChanged(null /* device */,
BluetoothAdapter.STATE_CONNECTED);
verify(mListener).onSummaryChanged(
mContext.getString(R.string.bluetooth_connected_summary, DEVICE_NAME));
+ mAdapterEnabled[0] = false;
mSummaryUpdater.onBluetoothStateChanged(BluetoothAdapter.STATE_OFF);
verify(mListener).onSummaryChanged(mContext.getString(R.string.bluetooth_disabled));
- connected[0] = false;
+ // Turning ON means not enabled
mSummaryUpdater.onBluetoothStateChanged(BluetoothAdapter.STATE_TURNING_ON);
+ // There should still be only one invocation of disabled message
+ verify(mListener).onSummaryChanged(mContext.getString(R.string.bluetooth_disabled));
+
+ mAdapterEnabled[0] = true;
+ mDeviceConnected[0] = false;
+ mSummaryUpdater.onBluetoothStateChanged(BluetoothAdapter.STATE_ON);
verify(mListener, times(2)).onSummaryChanged(mContext.getString(R.string.disconnected));
verify(mListener, times(4)).onSummaryChanged(anyString());
}
@Test
public void onConnectionStateChanged_connected_shouldSendConnectedMessage() {
- final List devices = new ArrayList<>();
- devices.add(mock(CachedBluetoothDevice.class));
- when(devices.get(0).isConnected()).thenReturn(true);
- when(mBluetoothManager.getCachedDeviceManager().getCachedDevicesCopy())
- .thenReturn(devices);
- when(mBtAdapter.getConnectionState()).thenReturn(BluetoothAdapter.STATE_DISCONNECTED);
- prepareConnectedDevice(false);
+ mAdapterEnabled[0] = true;
+ mAdapterConnectionState[0] = BluetoothAdapter.STATE_CONNECTED;
+ mBondedDevices.add(mConnectedDevice);
+ mDeviceConnected[0] = true;
- mSummaryUpdater.register(true);
-
- when(mBtAdapter.getConnectionState()).thenReturn(BluetoothAdapter.STATE_CONNECTED);
mSummaryUpdater.onConnectionStateChanged(null /* device */,
BluetoothAdapter.STATE_CONNECTED);
@@ -187,7 +224,22 @@ public class BluetoothSummaryUpdaterTest {
@Test
public void onConnectionStateChanged_inconsistentState_shouldSendDisconnectedMessage() {
- mSummaryUpdater.register(true);
+ mAdapterEnabled[0] = true;
+ mAdapterConnectionState[0] = BluetoothAdapter.STATE_DISCONNECTED;
+ mBondedDevices.add(mConnectedDevice);
+ mDeviceConnected[0] = false;
+
+ mSummaryUpdater.onConnectionStateChanged(null /* device */,
+ BluetoothAdapter.STATE_CONNECTED);
+
+ verify(mListener).onSummaryChanged(mContext.getString(R.string.disconnected));
+ }
+
+ @Test
+ public void onConnectionStateChanged_noBondedDevice_shouldSendDisconnectedMessage() {
+ mAdapterEnabled[0] = true;
+ mAdapterConnectionState[0] = BluetoothAdapter.STATE_CONNECTED;
+
mSummaryUpdater.onConnectionStateChanged(null /* device */,
BluetoothAdapter.STATE_CONNECTED);
@@ -197,8 +249,10 @@ public class BluetoothSummaryUpdaterTest {
@Test
public void onConnectionStateChanged_connecting_shouldSendConnectingMessage() {
- mSummaryUpdater.register(true);
- when(mBtAdapter.getConnectionState()).thenReturn(BluetoothAdapter.STATE_CONNECTING);
+ // No need for bonded devices
+ mAdapterEnabled[0] = true;
+ mAdapterConnectionState[0] = BluetoothAdapter.STATE_CONNECTING;
+
mSummaryUpdater.onConnectionStateChanged(null /* device */,
BluetoothAdapter.STATE_CONNECTING);
@@ -207,8 +261,10 @@ public class BluetoothSummaryUpdaterTest {
@Test
public void onConnectionStateChanged_disconnecting_shouldSendDisconnectingMessage() {
- mSummaryUpdater.register(true);
- when(mBtAdapter.getConnectionState()).thenReturn(BluetoothAdapter.STATE_DISCONNECTING);
+ // No need for bonded devices
+ mAdapterEnabled[0] = true;
+ mAdapterConnectionState[0] = BluetoothAdapter.STATE_DISCONNECTING;
+
mSummaryUpdater.onConnectionStateChanged(null /* device */,
BluetoothAdapter.STATE_DISCONNECTING);
@@ -217,7 +273,8 @@ public class BluetoothSummaryUpdaterTest {
@Test
public void getConnectedDeviceSummary_hasConnectedDevice_returnOneDeviceSummary() {
- prepareConnectedDevice(false);
+ mBondedDevices.add(mConnectedDevice);
+ mDeviceConnected[0] = true;
final String expectedSummary = mContext.getString(R.string.bluetooth_connected_summary,
DEVICE_NAME);
@@ -226,28 +283,16 @@ public class BluetoothSummaryUpdaterTest {
@Test
public void getConnectedDeviceSummary_multipleDevices_returnMultipleDevicesSummary() {
- prepareConnectedDevice(true);
+ mBondedDevices.add(mConnectedDevice);
+ mBondedDevices.add(mConnectedKeyBoardDevice);
+ mDeviceConnected[0] = true;
+ mDeviceConnected[1] = true;
final String expectedSummary = mContext.getString(
R.string.bluetooth_connected_multiple_devices_summary);
assertThat(mSummaryUpdater.getConnectedDeviceSummary()).isEqualTo(expectedSummary);
}
- private void prepareConnectedDevice(boolean multipleDevices) {
- final Set devices = new HashSet<>();
- doReturn(DEVICE_NAME).when(mConnectedDevice).getName();
- doReturn(true).when(mConnectedDevice).isConnected();
- devices.add(mConnectedDevice);
- if (multipleDevices) {
- // Add one more device if we need to test multiple devices
- doReturn(DEVICE_KEYBOARD_NAME).when(mConnectedKeyBoardDevice).getName();
- doReturn(true).when(mConnectedKeyBoardDevice).isConnected();
- devices.add(mConnectedKeyBoardDevice);
- }
-
- doReturn(devices).when(mBtAdapter).getBondedDevices();
- }
-
private class SummaryListener implements OnSummaryChangeListener {
String summary;
diff --git a/tests/robotests/src/com/android/settings/core/instrumentation/MetricsFeatureProviderTest.java b/tests/robotests/src/com/android/settings/core/instrumentation/MetricsFeatureProviderTest.java
index ea33c83391f..ff91c4099d3 100644
--- a/tests/robotests/src/com/android/settings/core/instrumentation/MetricsFeatureProviderTest.java
+++ b/tests/robotests/src/com/android/settings/core/instrumentation/MetricsFeatureProviderTest.java
@@ -28,6 +28,8 @@ import com.android.settings.overlay.FeatureFactory;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RuntimeEnvironment;
@@ -42,24 +44,35 @@ import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
@RunWith(SettingsRobolectricTestRunner.class)
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
public class MetricsFeatureProviderTest {
+ private static int CATEGORY = 10;
+ private static boolean SUBTYPE_BOOLEAN = true;
+ private static int SUBTYPE_INTEGER = 1;
+ private static long ELAPSED_TIME = 1000;
+
+ @Mock private LogWriter mockLogWriter;
+ @Mock private VisibilityLoggerMixin mockVisibilityLogger;
- @Mock
- private LogWriter mLogWriter;
private Context mContext;
private MetricsFeatureProvider mProvider;
+ @Captor
+ private ArgumentCaptor mPairCaptor;
+
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = RuntimeEnvironment.application;
mProvider = new MetricsFeatureProvider();
List writers = new ArrayList<>();
- writers.add(mLogWriter);
+ writers.add(mockLogWriter);
ReflectionHelpers.setField(mProvider, "mLoggerWriters", writers);
+
+ when(mockVisibilityLogger.elapsedTimeSinceVisible()).thenReturn(ELAPSED_TIME);
}
@Test
@@ -77,7 +90,7 @@ public class MetricsFeatureProviderTest {
mProvider.logDashboardStartIntent(mContext, null /* intent */,
MetricsEvent.SETTINGS_GESTURES);
- verifyNoMoreInteractions(mLogWriter);
+ verifyNoMoreInteractions(mockLogWriter);
}
@Test
@@ -86,7 +99,7 @@ public class MetricsFeatureProviderTest {
mProvider.logDashboardStartIntent(mContext, intent, MetricsEvent.SETTINGS_GESTURES);
- verify(mLogWriter).action(
+ verify(mockLogWriter).action(
eq(mContext),
eq(MetricsEvent.ACTION_SETTINGS_TILE_CLICK),
anyString(),
@@ -99,10 +112,32 @@ public class MetricsFeatureProviderTest {
mProvider.logDashboardStartIntent(mContext, intent, MetricsEvent.SETTINGS_GESTURES);
- verify(mLogWriter).action(
+ verify(mockLogWriter).action(
eq(mContext),
eq(MetricsEvent.ACTION_SETTINGS_TILE_CLICK),
anyString(),
eq(Pair.create(MetricsEvent.FIELD_CONTEXT, MetricsEvent.SETTINGS_GESTURES)));
}
+
+ @Test
+ public void action_BooleanLogsElapsedTime() {
+ mProvider.action(mockVisibilityLogger, CATEGORY, SUBTYPE_BOOLEAN);
+ verify(mockLogWriter).action(eq(CATEGORY), eq(SUBTYPE_BOOLEAN), mPairCaptor.capture());
+
+ Pair value = mPairCaptor.getValue();
+ assertThat(value.first instanceof Integer).isTrue();
+ assertThat((int) value.first).isEqualTo(MetricsEvent.NOTIFICATION_SINCE_VISIBLE_MILLIS);
+ assertThat(value.second).isEqualTo(ELAPSED_TIME);
+ }
+
+ @Test
+ public void action_IntegerLogsElapsedTime() {
+ mProvider.action(mockVisibilityLogger, CATEGORY, SUBTYPE_INTEGER);
+ verify(mockLogWriter).action(eq(CATEGORY), eq(SUBTYPE_INTEGER), mPairCaptor.capture());
+
+ Pair value = mPairCaptor.getValue();
+ assertThat(value.first instanceof Integer).isTrue();
+ assertThat((int) value.first).isEqualTo(MetricsEvent.NOTIFICATION_SINCE_VISIBLE_MILLIS);
+ assertThat(value.second).isEqualTo(ELAPSED_TIME);
+ }
}
diff --git a/tests/robotests/src/com/android/settings/dashboard/suggestions/SuggestionAdapterTest.java b/tests/robotests/src/com/android/settings/dashboard/suggestions/SuggestionAdapterTest.java
index 1e4ad79afc5..6b804656c90 100644
--- a/tests/robotests/src/com/android/settings/dashboard/suggestions/SuggestionAdapterTest.java
+++ b/tests/robotests/src/com/android/settings/dashboard/suggestions/SuggestionAdapterTest.java
@@ -203,7 +203,7 @@ public class SuggestionAdapterTest {
public void onBindViewHolder_v2_itemViewShouldHandleClick()
throws PendingIntent.CanceledException {
final List packages = makeSuggestionsV2("pkg1");
- setupSuggestions(mActivity, null /* suggestionV1 */ , packages);
+ setupSuggestions(mActivity, null /* suggestionV1 */, packages);
mSuggestionAdapter.onBindViewHolder(mSuggestionHolder, 0);
mSuggestionHolder.itemView.performClick();
@@ -233,6 +233,22 @@ public class SuggestionAdapterTest {
assertThat(itemView.getChildCount()).isEqualTo(1);
}
+ @Test
+ public void getSuggestionsV2_shouldReturnSuggestionWhenMatch() {
+ final List suggestionsV2 = makeSuggestionsV2("pkg1");
+ setupSuggestions(mActivity, null /* suggestionV1 */, suggestionsV2);
+
+ assertThat(mSuggestionAdapter.getSuggestion(0)).isNull();
+ assertThat(mSuggestionAdapter.getSuggestionsV2(0)).isNotNull();
+
+ List suggestionsV1 = makeSuggestions("pkg1");
+ setupSuggestions(mActivity, suggestionsV1, null /* suggestionV2 */);
+
+ assertThat(mSuggestionAdapter.getSuggestionsV2(0)).isNull();
+ assertThat(mSuggestionAdapter.getSuggestion(0)).isNotNull();
+
+ }
+
private void setupSuggestions(Context context, List suggestions,
List suggestionsV2) {
mSuggestionAdapter = new SuggestionAdapter(context, suggestions, suggestionsV2,
diff --git a/tests/robotests/src/com/android/settings/dashboard/suggestions/SuggestionFeatureProviderImplTest.java b/tests/robotests/src/com/android/settings/dashboard/suggestions/SuggestionFeatureProviderImplTest.java
index f1568fe68fc..0650d5b0a22 100644
--- a/tests/robotests/src/com/android/settings/dashboard/suggestions/SuggestionFeatureProviderImplTest.java
+++ b/tests/robotests/src/com/android/settings/dashboard/suggestions/SuggestionFeatureProviderImplTest.java
@@ -72,6 +72,7 @@ import org.mockito.MockitoAnnotations;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
+import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
@@ -430,14 +431,35 @@ public class SuggestionFeatureProviderImplTest {
}
@Test
- public void hasUsedNightDisplay_returnsTrue_ifPreviouslyActivated() {
- Secure.putLong(mContext.getContentResolver(), Secure.NIGHT_DISPLAY_LAST_ACTIVATED_TIME, 1L);
+ public void hasUsedNightDisplay_returnsTrue_ifPreviouslyActivatedAndManual() {
+ Secure.putString(mContext.getContentResolver(), Secure.NIGHT_DISPLAY_LAST_ACTIVATED_TIME,
+ LocalDateTime.now().toString());
+ Secure.putInt(mContext.getContentResolver(), Secure.NIGHT_DISPLAY_AUTO_MODE, 1);
assertThat(mProvider.hasUsedNightDisplay(mContext)).isTrue();
}
@Test
public void nightDisplaySuggestion_isCompleted_ifPreviouslyActivated() {
- Secure.putLong(mContext.getContentResolver(), Secure.NIGHT_DISPLAY_LAST_ACTIVATED_TIME, 1L);
+ Secure.putString(mContext.getContentResolver(), Secure.NIGHT_DISPLAY_LAST_ACTIVATED_TIME,
+ LocalDateTime.now().toString());
+ final ComponentName componentName =
+ new ComponentName(mContext, NightDisplaySuggestionActivity.class);
+ assertThat(mProvider.isSuggestionCompleted(mContext, componentName)).isTrue();
+ }
+
+ @Test
+ public void nightDisplaySuggestion_isCompleted_ifNonManualMode() {
+ Secure.putInt(mContext.getContentResolver(), Secure.NIGHT_DISPLAY_AUTO_MODE, 1);
+ final ComponentName componentName =
+ new ComponentName(mContext, NightDisplaySuggestionActivity.class);
+ assertThat(mProvider.isSuggestionCompleted(mContext, componentName)).isTrue();
+ }
+
+ @Test
+ public void nightDisplaySuggestion_isCompleted_ifPreviouslyCleared() {
+ Secure.putString(mContext.getContentResolver(), Secure.NIGHT_DISPLAY_LAST_ACTIVATED_TIME,
+ null);
+ Secure.putInt(mContext.getContentResolver(), Secure.NIGHT_DISPLAY_AUTO_MODE, 1);
final ComponentName componentName =
new ComponentName(mContext, NightDisplaySuggestionActivity.class);
assertThat(mProvider.isSuggestionCompleted(mContext, componentName)).isTrue();
diff --git a/tests/robotests/src/com/android/settings/development/DevelopmentSettingsDashboardFragmentTest.java b/tests/robotests/src/com/android/settings/development/DevelopmentSettingsDashboardFragmentTest.java
index a001aafcdb1..13f73742d65 100644
--- a/tests/robotests/src/com/android/settings/development/DevelopmentSettingsDashboardFragmentTest.java
+++ b/tests/robotests/src/com/android/settings/development/DevelopmentSettingsDashboardFragmentTest.java
@@ -17,7 +17,11 @@
package com.android.settings.development;
import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.Context;
@@ -108,6 +112,9 @@ public class DevelopmentSettingsDashboardFragmentTest {
}
@Test
+ @Config(shadows = {
+ ShadowPictureColorModePreferenceController.class
+ })
public void searchIndex_pageEnabled_shouldNotAddKeysToNonIndexable() {
final Context appContext = RuntimeEnvironment.application;
DevelopmentSettingsEnabler.setDevelopmentSettingsEnabled(appContext, true);
@@ -161,6 +168,24 @@ public class DevelopmentSettingsDashboardFragmentTest {
.isFalse();
}
+ @Test
+ public void onOemUnlockDialogConfirmed_shouldCallControllerOemConfirmed() {
+ final OemUnlockPreferenceController controller = mock(OemUnlockPreferenceController.class);
+ doReturn(controller).when(mDashboard).getDevelopmentOptionsController(
+ OemUnlockPreferenceController.class);
+ mDashboard.onOemUnlockDialogConfirmed();
+ verify(controller).onOemUnlockConfirmed();
+ }
+
+ @Test
+ public void onOemUnlockDialogConfirmed_shouldCallControllerOemDismissed() {
+ final OemUnlockPreferenceController controller = mock(OemUnlockPreferenceController.class);
+ doReturn(controller).when(mDashboard).getDevelopmentOptionsController(
+ OemUnlockPreferenceController.class);
+ mDashboard.onOemUnlockDialogDismissed();
+ verify(controller).onOemUnlockDismissed();
+ }
+
@Implements(EnableDevelopmentSettingWarningDialog.class)
public static class ShadowEnableDevelopmentSettingWarningDialog {
@@ -176,4 +201,13 @@ public class DevelopmentSettingsDashboardFragmentTest {
mShown = true;
}
}
+
+ @Implements(PictureColorModePreferenceController.class)
+ public static class ShadowPictureColorModePreferenceController {
+
+ @Implementation
+ public boolean isAvailable() {
+ return true;
+ }
+ }
}
diff --git a/tests/robotests/src/com/android/settings/development/OemUnlockPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/development/OemUnlockPreferenceControllerTest.java
new file mode 100644
index 00000000000..13678703bd8
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/development/OemUnlockPreferenceControllerTest.java
@@ -0,0 +1,201 @@
+/*
+ * 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.settings.development;
+
+import static com.android.settings.development.DevelopmentOptionsActivityRequestCodes
+ .REQUEST_CODE_ENABLE_OEM_UNLOCK;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.res.Resources;
+import android.os.UserManager;
+import android.service.oemlock.OemLockManager;
+import android.support.v7.preference.PreferenceScreen;
+import android.telephony.TelephonyManager;
+
+import com.android.settings.TestConfig;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+import com.android.settingslib.RestrictedSwitchPreference;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.annotation.Config;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class OemUnlockPreferenceControllerTest {
+
+ @Mock
+ private Context mContext;
+ @Mock
+ private Activity mActivity;
+ @Mock
+ private DevelopmentSettingsDashboardFragment mFragment;
+ @Mock
+ private RestrictedSwitchPreference mPreference;
+ @Mock
+ private PreferenceScreen mPreferenceScreen;
+ @Mock
+ private OemLockManager mOemLockManager;
+ @Mock
+ private UserManager mUserManager;
+ @Mock
+ private TelephonyManager mTelephonyManager;
+ @Mock
+ private Resources mResources;
+ private OemUnlockPreferenceController mController;
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ when(mContext.getSystemService(Context.OEM_LOCK_SERVICE)).thenReturn(mOemLockManager);
+ when(mContext.getSystemService(Context.USER_SERVICE)).thenReturn(mUserManager);
+ when(mContext.getSystemService(Context.TELEPHONY_SERVICE)).thenReturn(mTelephonyManager);
+ when(mContext.getResources()).thenReturn(mResources);
+ mController = new OemUnlockPreferenceController(mContext, mActivity, mFragment);
+ when(mPreferenceScreen.findPreference(mController.getPreferenceKey())).thenReturn(
+ mPreference);
+ mController.displayPreference(mPreferenceScreen);
+ }
+
+ @Test
+ public void isAvailable_shouldReturnTrueWhenOemLockManagerIsNotNull() {
+ boolean returnValue = mController.isAvailable();
+
+ assertThat(returnValue).isTrue();
+ }
+
+ @Test
+ public void isAvailable_shouldReturnFalseWhenOemLockManagerIsNull() {
+ when(mContext.getSystemService(Context.OEM_LOCK_SERVICE)).thenReturn(null);
+ mController = new OemUnlockPreferenceController(mContext, mActivity, mFragment);
+ boolean returnValue = mController.isAvailable();
+
+ assertThat(returnValue).isFalse();
+ }
+
+ @Test
+ public void onPreferenceChanged_turnOnUnlock() {
+ mController = spy(mController);
+ doReturn(false).when(mController).showKeyguardConfirmation(mResources,
+ REQUEST_CODE_ENABLE_OEM_UNLOCK);
+ doNothing().when(mController).confirmEnableOemUnlock();
+ mController.onPreferenceChange(null, true);
+
+ verify(mController).confirmEnableOemUnlock();
+ }
+
+ @Test
+ public void onPreferenceChanged_turnOffUnlock() {
+ mController.onPreferenceChange(null, false);
+
+ verify(mOemLockManager).setOemUnlockAllowedByUser(false);
+ }
+
+ @Test
+ public void updateState_preferenceShouldBeCheckedAndShouldBeDisabled() {
+ mController = spy(mController);
+ when(mOemLockManager.isOemUnlockAllowed()).thenReturn(true);
+ doReturn(true).when(mController).isOemUnlockAllowedByUserAndCarrier();
+ when(mOemLockManager.isDeviceOemUnlocked()).thenReturn(true);
+ mController.updateState(mPreference);
+
+ verify(mPreference).setChecked(true);
+ verify(mPreference).setEnabled(false);
+ }
+
+ @Test
+ public void updateState_preferenceShouldBeUncheckedAndShouldBeDisabled() {
+ mController = spy(mController);
+ when(mOemLockManager.isOemUnlockAllowed()).thenReturn(false);
+ doReturn(true).when(mController).isOemUnlockAllowedByUserAndCarrier();
+ when(mOemLockManager.isDeviceOemUnlocked()).thenReturn(true);
+ mController.updateState(mPreference);
+
+ verify(mPreference).setChecked(false);
+ verify(mPreference).setEnabled(false);
+ }
+
+ @Test
+ public void updateState_preferenceShouldBeCheckedAndShouldBeEnabled() {
+ mController = spy(mController);
+ when(mOemLockManager.isOemUnlockAllowed()).thenReturn(true);
+ doReturn(true).when(mController).isOemUnlockAllowedByUserAndCarrier();
+ when(mOemLockManager.isDeviceOemUnlocked()).thenReturn(false);
+ mController.updateState(mPreference);
+
+ verify(mPreference).setChecked(true);
+ verify(mPreference).setEnabled(true);
+ }
+
+ @Test
+ public void onActivityResult_shouldReturnTrue() {
+ final boolean result = mController.onActivityResult(REQUEST_CODE_ENABLE_OEM_UNLOCK,
+ Activity.RESULT_OK, null);
+
+ assertThat(result).isTrue();
+ }
+
+ @Test
+ public void onActivityResult_shouldReturnFalse() {
+ final boolean result = mController.onActivityResult(123454,
+ 1434, null);
+
+ assertThat(result).isFalse();
+ }
+
+ @Test
+ public void onDeveloperOptionsEnabled_preferenceShouldCheckRestriction() {
+ mController = spy(mController);
+ doReturn(false).when(mController).isOemUnlockAllowedByUserAndCarrier();
+ when(mPreference.isEnabled()).thenReturn(true);
+ mController.onDeveloperOptionsEnabled();
+
+ verify(mPreference).checkRestrictionAndSetDisabled(UserManager.DISALLOW_FACTORY_RESET);
+
+ }
+
+ @Test
+ public void onDeveloperOptionsDisabled_preferenceShouldCheckRestriction() {
+ mController = spy(mController);
+ doReturn(false).when(mController).isOemUnlockAllowedByUserAndCarrier();
+ when(mPreference.isEnabled()).thenReturn(true);
+ mController.onDeveloperOptionsDisabled();
+
+ verify(mPreference).checkRestrictionAndSetDisabled(UserManager.DISALLOW_FACTORY_RESET);
+
+ }
+
+ @Test
+ public void onOemUnlockConfirmed_oemManagerShouldSetUnlockAllowedByUser() {
+ mController.onOemUnlockConfirmed();
+
+ verify(mOemLockManager).setOemUnlockAllowedByUser(true);
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/development/PictureColorModePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/development/PictureColorModePreferenceControllerTest.java
new file mode 100644
index 00000000000..5cf4e10a4f2
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/development/PictureColorModePreferenceControllerTest.java
@@ -0,0 +1,137 @@
+/*
+ * 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.settings.development;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.support.v7.preference.PreferenceScreen;
+
+import com.android.settings.R;
+import com.android.settings.TestConfig;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+import com.android.settingslib.core.lifecycle.Lifecycle;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.annotation.Config;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class PictureColorModePreferenceControllerTest {
+
+ @Mock
+ private ColorModePreference mPreference;
+ @Mock
+ private Context mContext;
+ @Mock
+ private PreferenceScreen mPreferenceScreen;
+ @Mock
+ private Resources mResources;
+
+ private Lifecycle mLifecycle;
+ private PictureColorModePreferenceController mController;
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ mLifecycle = new Lifecycle();
+ mController = new PictureColorModePreferenceController(mContext, mLifecycle);
+ when(mPreferenceScreen.findPreference(mController.getPreferenceKey())).thenReturn(
+ mPreference);
+ when(mContext.getResources()).thenReturn(mResources);
+ when(mResources.getIntArray(R.array.color_mode_ids)).thenReturn(new int[0]);
+ mController.displayPreference(mPreferenceScreen);
+ }
+
+ @Test
+ public void isAvailable_shouldReturnFalseWhenWideColorGambit() {
+ mController = spy(mController);
+ doReturn(2).when(mController).getColorModeDescriptionsSize();
+ doReturn(true).when(mController).isWideColorGamut();
+
+ assertThat(mController.isAvailable()).isFalse();
+ }
+
+ @Test
+ public void isAvailable_shouldReturnTrueWhenNotWideColorGambit() {
+ mController = spy(mController);
+ doReturn(2).when(mController).getColorModeDescriptionsSize();
+ doReturn(false).when(mController).isWideColorGamut();
+
+ assertThat(mController.isAvailable()).isTrue();
+ }
+
+ @Test
+ public void isAvailable_shouldReturnFalseWhenColorCountIsOne() {
+ mController = spy(mController);
+ doReturn(1).when(mController).getColorModeDescriptionsSize();
+ doReturn(true).when(mController).isWideColorGamut();
+
+ assertThat(mController.isAvailable()).isFalse();
+ }
+
+ @Test
+ public void isAvailable_shouldReturnTrueWhenColorCountIsTwo() {
+ mController = spy(mController);
+ doReturn(2).when(mController).getColorModeDescriptionsSize();
+ doReturn(false).when(mController).isWideColorGamut();
+
+ assertThat(mController.isAvailable()).isTrue();
+ }
+
+ @Test
+ public void onDeveloperOptionEnabled_shouldEnablePreference() {
+ mController = spy(mController);
+ doReturn(true).when(mController).isAvailable();
+ mController.onDeveloperOptionsEnabled();
+
+ verify(mPreference).setEnabled(true);
+ }
+
+ @Test
+ public void onDeveloperOptionDisabled_shouldDisablePreference() {
+ mController = spy(mController);
+ doReturn(true).when(mController).isAvailable();
+ mController.onDeveloperOptionsDisabled();
+
+ verify(mPreference).setEnabled(false);
+ }
+
+ @Test
+ public void onResume_shouldStartListening() {
+ mLifecycle.onResume();
+
+ verify(mPreference).startListening();
+ }
+
+ @Test
+ public void onPause_shouldStopListening() {
+ mLifecycle.onPause();
+
+ verify(mPreference).stopListening();
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowSecureSettings.java b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowSecureSettings.java
index 0f61a5d816e..8b2a27bd1fa 100644
--- a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowSecureSettings.java
+++ b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowSecureSettings.java
@@ -36,7 +36,11 @@ public class ShadowSecureSettings {
int userHandle) {
final Table userTable = getUserTable(resolver);
synchronized (userTable) {
- userTable.put(userHandle, name, value);
+ if (value != null) {
+ userTable.put(userHandle, name, value);
+ } else {
+ userTable.remove(userHandle, name);
+ }
return true;
}
}