release-request-ed8626b9-4479-4c76-9207-5f511f136606-for-git_pi-release-4354758 snap-temp-L65900000105223161

Change-Id: I119935112476adee0e3843a076abb66626afd7d7
This commit is contained in:
android-build-team Robot
2017-09-22 08:08:34 +00:00
39 changed files with 1651 additions and 235 deletions

View File

@@ -15,7 +15,14 @@
limitations under the License.
-->
<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
android:src="@drawable/ic_sim_sd"
android:tint="?android:attr/colorControlNormal" />
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24.0dp"
android:height="24.0dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0"
android:tint="?android:attr/colorControlNormal">
<path
android:fillColor="#FFFFFFFF"
android:pathData="M19.99 4c0-1.1-.89-2-1.99-2h-8L4 8v12c0 1.1.9 2 2 2h12.01c1.1 0 1.99-.9 1.99-2l-.01-16zM9 19H7v-2h2v2zm8 0h-2v-2h2v2zm-8-4H7v-4h2v4zm4 4h-2v-4h2v4zm0-6h-2v-2h2v2zm4 2h-2v-4h2v4z"/>
<path android:pathData="M0 0h24v24H0z"/>
</vector>

View File

@@ -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">
<com.android.internal.widget.LinearLayoutWithDefaultTouchRecepient
android:id="@+id/topLayout"

View File

@@ -217,7 +217,9 @@ public abstract class AppInfoBase extends SettingsPreferenceFragment
@Override
public void onPackageListChanged() {
refreshUi();
if (!refreshUi()) {
setIntentAndFinish(true, true);
}
}
public static void startAppInfoFragment(Class<?> fragment, int titleRes,

View File

@@ -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<Pair<ApplicationInfo, Integer>> {
private final Collator mCollator = Collator.getInstance();
private final PackageManager mPm;
public AppComparator(PackageManager pm) {
mPm = pm;
}
public final int compare(Pair<ApplicationInfo, Integer> a,
Pair<ApplicationInfo, Integer> 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<String, Boolean> packageToState = new ArrayMap<>();
final ArrayList<ApplicationInfo> pipApps = new ArrayList<>();
final List<PackageInfo> 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<Pair<ApplicationInfo, Integer>> 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<ApplicationInfo, Integer> 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<Pair<ApplicationInfo, Integer>> collectPipApps(int userId) {
final ArrayList<Pair<ApplicationInfo, Integer>> pipApps = new ArrayList<>();
final ArrayList<Integer> userIds = new ArrayList<>();
for (UserInfo user : mUserManager.getProfiles(userId)) {
userIds.add(user.id);
}
for (int id : userIds) {
final List<PackageInfo> 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;
}
}

View File

@@ -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);

View File

@@ -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<CachedBluetoothDevice> 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<CachedBluetoothDevice> getDevices() {
return mBluetoothManager != null
? mBluetoothManager.getCachedDeviceManager().getCachedDevicesCopy()
: null;
}
@VisibleForTesting
String getConnectedDeviceSummary() {
String deviceName = null;
int count = 0;
final Set<BluetoothDevice> 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);

View File

@@ -65,4 +65,8 @@ public abstract class InstrumentedPreferenceFragment extends ObservablePreferenc
protected final Context getPrefContext() {
return getPreferenceManager().getContext();
}
protected final VisibilityLoggerMixin getVisibilityLogger() {
return mVisibilityLoggerMixin;
}
}

View File

@@ -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<Integer, Object>... 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<Integer, Object> pair : taggedData) {
logMaker.addTaggedData(pair.first, pair.second);
}
mMetricsLogger.write(logMaker);
}
}
public void action(int category, boolean value, Pair<Integer, Object>... taggedData) {
action(category, value ? 1 : 0, taggedData);
}
public void action(Context context, int category, Pair<Integer, Object>... 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,

View File

@@ -33,6 +33,16 @@ public interface LogWriter {
*/
void hidden(Context context, int category);
/**
* Logs a user action.
*/
void action(int category, int value, Pair<Integer, Object>... taggedData);
/**
* Logs a user action.
*/
void action(int category, boolean value, Pair<Integer, Object>... 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);
/**

View File

@@ -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<Integer, Object>... 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<Integer, Object> sinceVisibleTaggedData(long timestamp) {
return Pair.create(MetricsEvent.NOTIFICATION_SINCE_VISIBLE_MILLIS, timestamp);
}
}

View File

@@ -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<Integer, Object>... taggedData) {
}
@Override
public void action(int category, boolean value, Pair<Integer, Object>... taggedData) {
}
@Override
public void action(Context context, int category, int value) {
}

View File

@@ -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;
}
}

View File

@@ -220,6 +220,9 @@ public class SuggestionAdapter extends RecyclerView.Adapter<DashboardItemHolder>
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<DashboardItemHolder>
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;

View File

@@ -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;
}
}

View File

@@ -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);

View File

@@ -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<ColorModeDescription> mDescriptions;
private List<ColorModeDescription> mDescriptions;
public static List<ColorModeDescription> getColorModeDescriptions(Context context) {
List<ColorModeDescription> 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;

View File

@@ -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();
}

View File

@@ -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;
}

View File

@@ -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<AbstractPreferenceController> 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<AbstractPreferenceController> buildPreferenceControllers(Context context,
Lifecycle lifecycle) {
Activity activity, Lifecycle lifecycle, DevelopmentSettingsDashboardFragment fragment) {
final List<AbstractPreferenceController> 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 extends AbstractPreferenceController> T getDevelopmentOptionsController(Class<T> clazz) {
return getPreferenceController(clazz);
}
/**
* For Search.
*/
@@ -171,7 +280,8 @@ public class DevelopmentSettingsDashboardFragment extends RestrictedDashboardFra
@Override
public List<AbstractPreferenceController> getPreferenceControllers(Context
context) {
return buildPreferenceControllers(context, null /* lifecycle */);
return buildPreferenceControllers(context, null /* activity */,
null /* lifecycle */, null /* devOptionsDashboardFragment */);
}
};
}

View File

@@ -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();
}
}

View File

@@ -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();
}

View File

@@ -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);
}
}

View File

@@ -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();
}
}

View File

@@ -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

View File

@@ -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();
}
}

View File

@@ -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));
}

View File

@@ -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) {

View File

@@ -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;

View File

@@ -41,4 +41,8 @@ public class UserManagerWrapper {
public List<UserInfo> getUsers() {
return mUserManager.getUsers();
}
public List<UserInfo> getProfiles(int userHandle) {
return mUserManager.getProfiles(userHandle);
}
}

View File

@@ -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<PackageInfo> mPrimaryUserPackages;
private ArrayList<PackageInfo> mProfileUserPackages;
private ArrayList<UserInfo> 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<Pair<ApplicationInfo, Integer>> 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<Pair<ApplicationInfo, Integer>> apps = mFragment.collectPipApps(PRIMARY_USER_ID);
Collections.sort(apps, new PictureInPictureSettings.AppComparator(null));
assertThat(isOrdered(apps, primaryP1, profileP1, primaryP2, profileP2));
}
private boolean containsPackages(ArrayList<Pair<ApplicationInfo, Integer>> 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<Pair<ApplicationInfo, Integer>> 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;
}
}

View File

@@ -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
}
}

View File

@@ -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<BluetoothDevice> 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<CachedBluetoothDevice> 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<CachedBluetoothDevice> 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<BluetoothDevice> 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;

View File

@@ -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<Pair> mPairCaptor;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = RuntimeEnvironment.application;
mProvider = new MetricsFeatureProvider();
List<LogWriter> 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);
}
}

View File

@@ -203,7 +203,7 @@ public class SuggestionAdapterTest {
public void onBindViewHolder_v2_itemViewShouldHandleClick()
throws PendingIntent.CanceledException {
final List<Suggestion> 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<Suggestion> suggestionsV2 = makeSuggestionsV2("pkg1");
setupSuggestions(mActivity, null /* suggestionV1 */, suggestionsV2);
assertThat(mSuggestionAdapter.getSuggestion(0)).isNull();
assertThat(mSuggestionAdapter.getSuggestionsV2(0)).isNotNull();
List<Tile> suggestionsV1 = makeSuggestions("pkg1");
setupSuggestions(mActivity, suggestionsV1, null /* suggestionV2 */);
assertThat(mSuggestionAdapter.getSuggestionsV2(0)).isNull();
assertThat(mSuggestionAdapter.getSuggestion(0)).isNotNull();
}
private void setupSuggestions(Context context, List<Tile> suggestions,
List<Suggestion> suggestionsV2) {
mSuggestionAdapter = new SuggestionAdapter(context, suggestions, suggestionsV2,

View File

@@ -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();

View File

@@ -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;
}
}
}

View File

@@ -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);
}
}

View File

@@ -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();
}
}

View File

@@ -36,7 +36,11 @@ public class ShadowSecureSettings {
int userHandle) {
final Table<Integer, String, Object> 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;
}
}