diff --git a/res/values/config.xml b/res/values/config.xml index 8e55ea95e36..f46134f301b 100755 --- a/res/values/config.xml +++ b/res/values/config.xml @@ -134,4 +134,9 @@ true + + false diff --git a/res/xml/my_device_info.xml b/res/xml/my_device_info.xml index 42151fb6759..634831633ef 100644 --- a/res/xml/my_device_info.xml +++ b/res/xml/my_device_info.xml @@ -19,8 +19,7 @@ xmlns:android="http://schemas.android.com/apk/res/android" xmlns:settings="http://schemas.android.com/apk/res-auto" android:key="my_device_info_pref_screen" - android:title="@string/about_settings" - settings:initialExpandedChildrenCount="7"> + android:title="@string/about_settings"> mCandidates; private final SettingsObserver mSettingsObserver; public VibrationPreferenceFragment() { mCandidates = new ArrayMap<>(); - mCandidates.put(KEY_INTENSITY_OFF, - new VibrationIntensityCandidateInfo(KEY_INTENSITY_OFF, - R.string.accessibility_vibration_intensity_off, - Vibrator.VIBRATION_INTENSITY_OFF)); - mCandidates.put(KEY_INTENSITY_LOW, - new VibrationIntensityCandidateInfo(KEY_INTENSITY_LOW, - R.string.accessibility_vibration_intensity_low, - Vibrator.VIBRATION_INTENSITY_LOW)); - mCandidates.put(KEY_INTENSITY_MEDIUM, - new VibrationIntensityCandidateInfo(KEY_INTENSITY_MEDIUM, - R.string.accessibility_vibration_intensity_medium, - Vibrator.VIBRATION_INTENSITY_MEDIUM)); - mCandidates.put(KEY_INTENSITY_HIGH, - new VibrationIntensityCandidateInfo(KEY_INTENSITY_HIGH, - R.string.accessibility_vibration_intensity_high, - Vibrator.VIBRATION_INTENSITY_HIGH)); mSettingsObserver = new SettingsObserver(); } @@ -82,6 +71,39 @@ public abstract class VibrationPreferenceFragment extends RadioButtonPickerFragm public void onAttach(Context context) { super.onAttach(context); mSettingsObserver.register(); + if (mCandidates.isEmpty()) { + loadCandidates(context); + } + } + + private void loadCandidates(Context context) { + final boolean supportsMultipleIntensities = context.getResources().getBoolean( + R.bool.config_vibration_supports_multiple_intensities); + if (supportsMultipleIntensities) { + mCandidates.put(KEY_INTENSITY_OFF, + new VibrationIntensityCandidateInfo(KEY_INTENSITY_OFF, + R.string.accessibility_vibration_intensity_off, + Vibrator.VIBRATION_INTENSITY_OFF)); + mCandidates.put(KEY_INTENSITY_LOW, + new VibrationIntensityCandidateInfo(KEY_INTENSITY_LOW, + R.string.accessibility_vibration_intensity_low, + Vibrator.VIBRATION_INTENSITY_LOW)); + mCandidates.put(KEY_INTENSITY_MEDIUM, + new VibrationIntensityCandidateInfo(KEY_INTENSITY_MEDIUM, + R.string.accessibility_vibration_intensity_medium, + Vibrator.VIBRATION_INTENSITY_MEDIUM)); + mCandidates.put(KEY_INTENSITY_HIGH, + new VibrationIntensityCandidateInfo(KEY_INTENSITY_HIGH, + R.string.accessibility_vibration_intensity_high, + Vibrator.VIBRATION_INTENSITY_HIGH)); + } else { + mCandidates.put(KEY_INTENSITY_OFF, + new VibrationIntensityCandidateInfo(KEY_INTENSITY_OFF, + R.string.switch_off_text, Vibrator.VIBRATION_INTENSITY_OFF)); + mCandidates.put(KEY_INTENSITY_ON, + new VibrationIntensityCandidateInfo(KEY_INTENSITY_ON, + R.string.switch_on_text, getDefaultVibrationIntensity())); + } } @Override @@ -105,6 +127,24 @@ public abstract class VibrationPreferenceFragment extends RadioButtonPickerFragm */ protected void onVibrationIntensitySelected(int intensity) { } + /** + * Play a vibration effect with intensity just selected by user + */ + protected void playVibrationPreview() { + Vibrator vibrator = getContext().getSystemService(Vibrator.class); + VibrationEffect effect = VibrationEffect.get(VibrationEffect.EFFECT_CLICK); + AudioAttributes.Builder builder = new AudioAttributes.Builder(); + builder.setUsage(getPreviewVibrationAudioAttributesUsage()); + vibrator.vibrate(effect, builder.build()); + } + + /** + * Get the AudioAttributes usage for vibration preview. + */ + protected int getPreviewVibrationAudioAttributesUsage() { + return AudioAttributes.USAGE_UNKNOWN; + } + @Override protected List getCandidates() { List candidates = new ArrayList<>(mCandidates.values()); @@ -118,7 +158,10 @@ public abstract class VibrationPreferenceFragment extends RadioButtonPickerFragm final int vibrationIntensity = Settings.System.getInt(getContext().getContentResolver(), getVibrationIntensitySetting(), getDefaultVibrationIntensity()); for (VibrationIntensityCandidateInfo candidate : mCandidates.values()) { - if (candidate.getIntensity() == vibrationIntensity) { + final boolean matchesIntensity = candidate.getIntensity() == vibrationIntensity; + final boolean matchesOn = candidate.getKey().equals(KEY_INTENSITY_ON) + && vibrationIntensity != Vibrator.VIBRATION_INTENSITY_OFF; + if (matchesIntensity || matchesOn) { return candidate.getKey(); } } @@ -189,6 +232,7 @@ public abstract class VibrationPreferenceFragment extends RadioButtonPickerFragm @Override public void onChange(boolean selfChange, Uri uri) { updateCandidates(); + playVibrationPreview(); } } } diff --git a/src/com/android/settings/bluetooth/BluetoothSliceBuilder.java b/src/com/android/settings/bluetooth/BluetoothSliceBuilder.java index 5ec193c372a..cdc54a6ad58 100644 --- a/src/com/android/settings/bluetooth/BluetoothSliceBuilder.java +++ b/src/com/android/settings/bluetooth/BluetoothSliceBuilder.java @@ -17,8 +17,6 @@ package com.android.settings.bluetooth; import static android.app.slice.Slice.EXTRA_TOGGLE_STATE; -import static androidx.slice.builders.ListBuilder.ICON_IMAGE; - import android.annotation.ColorInt; import android.app.PendingIntent; import android.bluetooth.BluetoothAdapter; @@ -83,7 +81,7 @@ public class BluetoothSliceBuilder { * Bluetooth. */ public static Slice getSlice(Context context) { - final boolean isBluetoothEnabled = isBluetoothEnabled(context); + final boolean isBluetoothEnabled = isBluetoothEnabled(); final CharSequence title = context.getText(R.string.bluetooth_settings); final IconCompat icon = IconCompat.createWithResource(context, R.drawable.ic_settings_bluetooth); @@ -119,9 +117,8 @@ public class BluetoothSliceBuilder { // handle it. } - private static boolean isBluetoothEnabled(Context context) { - final LocalBluetoothAdapter adapter = LocalBluetoothManager.getInstance(context, - null /* callback */).getBluetoothAdapter(); + private static boolean isBluetoothEnabled() { + final BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); return adapter.isEnabled(); } diff --git a/src/com/android/settings/development/EmulateDisplayCutoutPreferenceController.java b/src/com/android/settings/development/EmulateDisplayCutoutPreferenceController.java index 3d551cdefaf..46b493fe046 100644 --- a/src/com/android/settings/development/EmulateDisplayCutoutPreferenceController.java +++ b/src/com/android/settings/development/EmulateDisplayCutoutPreferenceController.java @@ -19,20 +19,23 @@ package com.android.settings.development; import static android.os.UserHandle.USER_SYSTEM; import android.content.Context; +import android.content.om.IOverlayManager; +import android.content.om.OverlayInfo; import android.content.pm.PackageManager; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.text.TextUtils; +import android.view.DisplayCutout; import androidx.annotation.VisibleForTesting; import androidx.preference.ListPreference; import androidx.preference.Preference; import androidx.preference.PreferenceScreen; -import android.text.TextUtils; -import android.view.DisplayCutout; import com.android.settings.R; import com.android.settings.core.PreferenceControllerMixin; -import com.android.settings.wrapper.OverlayManagerWrapper; -import com.android.settings.wrapper.OverlayManagerWrapper.OverlayInfo; import com.android.settingslib.development.DeveloperOptionsPreferenceController; +import java.util.ArrayList; import java.util.List; public class EmulateDisplayCutoutPreferenceController extends @@ -41,7 +44,7 @@ public class EmulateDisplayCutoutPreferenceController extends private static final String KEY = "display_cutout_emulation"; - private final OverlayManagerWrapper mOverlayManager; + private final IOverlayManager mOverlayManager; private final boolean mAvailable; private ListPreference mPreference; @@ -49,7 +52,7 @@ public class EmulateDisplayCutoutPreferenceController extends @VisibleForTesting EmulateDisplayCutoutPreferenceController(Context context, PackageManager packageManager, - OverlayManagerWrapper overlayManager) { + IOverlayManager overlayManager) { super(context); mOverlayManager = overlayManager; mPackageManager = packageManager; @@ -57,7 +60,8 @@ public class EmulateDisplayCutoutPreferenceController extends } public EmulateDisplayCutoutPreferenceController(Context context) { - this(context, context.getPackageManager(), new OverlayManagerWrapper()); + this(context, context.getPackageManager(), IOverlayManager.Stub + .asInterface(ServiceManager.getService(Context.OVERLAY_SERVICE))); } @Override @@ -102,10 +106,14 @@ public class EmulateDisplayCutoutPreferenceController extends } final boolean result; - if (TextUtils.isEmpty(packageName)) { - result = mOverlayManager.setEnabled(currentPackageName, false, USER_SYSTEM); - } else { - result = mOverlayManager.setEnabledExclusiveInCategory(packageName, USER_SYSTEM); + try { + if (TextUtils.isEmpty(packageName)) { + result = mOverlayManager.setEnabled(currentPackageName, false, USER_SYSTEM); + } else { + result = mOverlayManager.setEnabledExclusiveInCategory(packageName, USER_SYSTEM); + } + } catch (RemoteException re) { + throw re.rethrowFromSystemServer(); } updateState(mPreference); return result; @@ -145,13 +153,17 @@ public class EmulateDisplayCutoutPreferenceController extends } private OverlayInfo[] getOverlayInfos() { - @SuppressWarnings("unchecked") List overlayInfos = - mOverlayManager.getOverlayInfosForTarget("android", USER_SYSTEM); - for (int i = overlayInfos.size() - 1; i >= 0; i--) { - if (!DisplayCutout.EMULATION_OVERLAY_CATEGORY.equals( - overlayInfos.get(i).category)) { - overlayInfos.remove(i); + List overlayInfos; + try { + overlayInfos = mOverlayManager.getOverlayInfosForTarget("android", USER_SYSTEM); + for (int i = overlayInfos.size() - 1; i >= 0; i--) { + if (!DisplayCutout.EMULATION_OVERLAY_CATEGORY.equals( + overlayInfos.get(i).category)) { + overlayInfos.remove(i); + } } + } catch (RemoteException re) { + throw re.rethrowFromSystemServer(); } return overlayInfos.toArray(new OverlayInfo[overlayInfos.size()]); } diff --git a/src/com/android/settings/display/ThemePreferenceController.java b/src/com/android/settings/display/ThemePreferenceController.java index 2cdf7e21c30..c71a7abf498 100644 --- a/src/com/android/settings/display/ThemePreferenceController.java +++ b/src/com/android/settings/display/ThemePreferenceController.java @@ -13,23 +13,22 @@ */ package com.android.settings.display; +import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.ACTION_THEME; + import android.content.Context; +import android.content.om.IOverlayManager; +import android.content.om.OverlayInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.os.RemoteException; import android.os.ServiceManager; import android.os.UserHandle; -import androidx.annotation.VisibleForTesting; -import androidx.preference.ListPreference; -import androidx.preference.Preference; import android.text.TextUtils; import com.android.settings.R; import com.android.settings.core.PreferenceControllerMixin; import com.android.settings.overlay.FeatureFactory; -import com.android.settings.wrapper.OverlayManagerWrapper; -import com.android.settings.wrapper.OverlayManagerWrapper.OverlayInfo; import com.android.settingslib.core.AbstractPreferenceController; import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; @@ -37,7 +36,9 @@ import java.util.ArrayList; import java.util.List; import java.util.Objects; -import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.ACTION_THEME; +import androidx.annotation.VisibleForTesting; +import androidx.preference.ListPreference; +import androidx.preference.Preference; public class ThemePreferenceController extends AbstractPreferenceController implements PreferenceControllerMixin, Preference.OnPreferenceChangeListener { @@ -45,16 +46,16 @@ public class ThemePreferenceController extends AbstractPreferenceController impl private static final String KEY_THEME = "theme"; private final MetricsFeatureProvider mMetricsFeatureProvider; - private final OverlayManagerWrapper mOverlayService; + private final IOverlayManager mOverlayService; private final PackageManager mPackageManager; public ThemePreferenceController(Context context) { - this(context, ServiceManager.getService(Context.OVERLAY_SERVICE) != null - ? new OverlayManagerWrapper() : null); + this(context, IOverlayManager.Stub + .asInterface(ServiceManager.getService(Context.OVERLAY_SERVICE))); } @VisibleForTesting - ThemePreferenceController(Context context, OverlayManagerWrapper overlayManager) { + ThemePreferenceController(Context context, IOverlayManager overlayManager) { super(context); mOverlayService = overlayManager; mPackageManager = context.getPackageManager(); @@ -77,7 +78,7 @@ public class ThemePreferenceController extends AbstractPreferenceController impl @Override public void updateState(Preference preference) { ListPreference pref = (ListPreference) preference; - String[] pkgs = getAvailableThemes(); + String[] pkgs = getAvailableThemes(false /* currentThemeOnly */); CharSequence[] labels = new CharSequence[pkgs.length]; for (int i = 0; i < pkgs.length; i++) { try { @@ -109,11 +110,15 @@ public class ThemePreferenceController extends AbstractPreferenceController impl @Override public boolean onPreferenceChange(Preference preference, Object newValue) { - String current = getTheme(); + String current = getCurrentTheme(); if (Objects.equals(newValue, current)) { return true; } - mOverlayService.setEnabledExclusiveInCategory((String) newValue, UserHandle.myUserId()); + try { + mOverlayService.setEnabledExclusiveInCategory((String) newValue, UserHandle.myUserId()); + } catch (RemoteException re) { + throw re.rethrowFromSystemServer(); + } return true; } @@ -129,39 +134,43 @@ public class ThemePreferenceController extends AbstractPreferenceController impl } } - private String getTheme() { - List infos = mOverlayService.getOverlayInfosForTarget("android", - UserHandle.myUserId()); - for (int i = 0, size = infos.size(); i < size; i++) { - if (infos.get(i).isEnabled() && isTheme(infos.get(i))) { - return infos.get(i).packageName; - } - } - return null; - } - @Override public boolean isAvailable() { if (mOverlayService == null) return false; - String[] themes = getAvailableThemes(); + String[] themes = getAvailableThemes(false /* currentThemeOnly */); return themes != null && themes.length > 1; } @VisibleForTesting String getCurrentTheme() { - return getTheme(); + String[] themePackages = getAvailableThemes(true /* currentThemeOnly */); + return themePackages.length < 1 ? null : themePackages[0]; } @VisibleForTesting - String[] getAvailableThemes() { - List infos = mOverlayService.getOverlayInfosForTarget("android", - UserHandle.myUserId()); - List pkgs = new ArrayList<>(infos.size()); - for (int i = 0, size = infos.size(); i < size; i++) { - if (isTheme(infos.get(i))) { - pkgs.add(infos.get(i).packageName); + String[] getAvailableThemes(boolean currentThemeOnly) { + List infos; + List pkgs; + try { + infos = mOverlayService.getOverlayInfosForTarget("android", UserHandle.myUserId()); + pkgs = new ArrayList<>(infos.size()); + for (int i = 0, size = infos.size(); i < size; i++) { + if (isTheme(infos.get(i))) { + if (infos.get(i).isEnabled() && currentThemeOnly) { + return new String[] {infos.get(i).packageName}; + } else { + pkgs.add(infos.get(i).packageName); + } + } } + } catch (RemoteException re) { + throw re.rethrowFromSystemServer(); + } + + // Current enabled theme is not found. + if (currentThemeOnly) { + return new String[0]; } return pkgs.toArray(new String[pkgs.size()]); } diff --git a/src/com/android/settings/fuelgauge/SmartBatteryPreferenceController.java b/src/com/android/settings/fuelgauge/SmartBatteryPreferenceController.java index fbd779c37c0..d4dc4d2ed40 100644 --- a/src/com/android/settings/fuelgauge/SmartBatteryPreferenceController.java +++ b/src/com/android/settings/fuelgauge/SmartBatteryPreferenceController.java @@ -19,6 +19,8 @@ package com.android.settings.fuelgauge; import android.content.Context; import android.provider.Settings; +import android.text.TextUtils; + import androidx.preference.SwitchPreference; import androidx.preference.Preference; @@ -48,6 +50,11 @@ public class SmartBatteryPreferenceController extends BasePreferenceController i : UNSUPPORTED_ON_DEVICE; } + @Override + public boolean isSliceable() { + return TextUtils.equals(getPreferenceKey(), "smart_battery"); + } + @Override public void updateState(Preference preference) { super.updateState(preference); diff --git a/src/com/android/settings/notification/AlarmVolumePreferenceController.java b/src/com/android/settings/notification/AlarmVolumePreferenceController.java index 3c238aa7f29..92a1e907766 100644 --- a/src/com/android/settings/notification/AlarmVolumePreferenceController.java +++ b/src/com/android/settings/notification/AlarmVolumePreferenceController.java @@ -18,6 +18,7 @@ package com.android.settings.notification; import android.content.Context; import android.media.AudioManager; +import android.text.TextUtils; import com.android.settings.R; @@ -36,6 +37,11 @@ public class AlarmVolumePreferenceController extends && !mHelper.isSingleVolume() ? AVAILABLE : UNSUPPORTED_ON_DEVICE; } + @Override + public boolean isSliceable() { + return TextUtils.equals(getPreferenceKey(), "alarm_volume"); + } + @Override public String getPreferenceKey() { return KEY_ALARM_VOLUME; diff --git a/src/com/android/settings/slices/SettingsSliceProvider.java b/src/com/android/settings/slices/SettingsSliceProvider.java index 52888b0e698..f4dfd6ee71d 100644 --- a/src/com/android/settings/slices/SettingsSliceProvider.java +++ b/src/com/android/settings/slices/SettingsSliceProvider.java @@ -23,6 +23,7 @@ import android.content.ContentResolver; import android.content.Intent; import android.content.IntentFilter; import android.net.Uri; +import android.os.StrictMode; import android.provider.Settings; import android.provider.SettingsSlicesContract; import android.text.TextUtils; @@ -31,13 +32,13 @@ import android.util.KeyValueListParser; import android.util.Log; import android.util.Pair; -import com.android.settings.location.LocationSliceBuilder; -import com.android.settings.overlay.FeatureFactory; +import com.android.settings.bluetooth.BluetoothSliceBuilder; import com.android.settings.core.BasePreferenceController; +import com.android.settings.location.LocationSliceBuilder; +import com.android.settings.notification.ZenModeSliceBuilder; +import com.android.settings.overlay.FeatureFactory; import com.android.settings.wifi.WifiSliceBuilder; import com.android.settings.wifi.calling.WifiCallingSliceHelper; -import com.android.settings.bluetooth.BluetoothSliceBuilder; -import com.android.settings.notification.ZenModeSliceBuilder; import com.android.settingslib.SliceBroadcastRelay; import com.android.settingslib.utils.ThreadUtils; @@ -53,7 +54,6 @@ import java.util.WeakHashMap; import java.util.concurrent.ConcurrentHashMap; import androidx.annotation.VisibleForTesting; -import androidx.core.graphics.drawable.IconCompat; import androidx.slice.Slice; import androidx.slice.SliceProvider; @@ -150,7 +150,7 @@ public class SettingsSliceProvider extends SliceProvider { @Override public void onSlicePinned(Uri sliceUri) { if (WifiSliceBuilder.WIFI_URI.equals(sliceUri)) { - registerIntentToUri(WifiSliceBuilder.INTENT_FILTER , sliceUri); + registerIntentToUri(WifiSliceBuilder.INTENT_FILTER, sliceUri); mRegisteredUris.add(sliceUri); return; } else if (ZenModeSliceBuilder.ZEN_MODE_URI.equals(sliceUri)) { @@ -177,41 +177,51 @@ public class SettingsSliceProvider extends SliceProvider { @Override public Slice onBindSlice(Uri sliceUri) { - final Set blockedKeys = getBlockedKeys(); - final String key = sliceUri.getLastPathSegment(); - if (blockedKeys.contains(key)) { - Log.e(TAG, "Requested blocked slice with Uri: " + sliceUri); - return null; - } + final StrictMode.ThreadPolicy oldPolicy = StrictMode.getThreadPolicy(); + try { + if (!ThreadUtils.isMainThread()) { + StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder() + .permitAll() + .build()); + } + final Set blockedKeys = getBlockedKeys(); + final String key = sliceUri.getLastPathSegment(); + if (blockedKeys.contains(key)) { + Log.e(TAG, "Requested blocked slice with Uri: " + sliceUri); + return null; + } - // If adding a new Slice, do not directly match Slice URIs. - // Use {@link SlicesDatabaseAccessor}. - if (WifiCallingSliceHelper.WIFI_CALLING_URI.equals(sliceUri)) { - return FeatureFactory.getFactory(getContext()) - .getSlicesFeatureProvider() - .getNewWifiCallingSliceHelper(getContext()) - .createWifiCallingSlice(sliceUri); - } else if (WifiSliceBuilder.WIFI_URI.equals(sliceUri)) { - return WifiSliceBuilder.getSlice(getContext()); - } else if (ZenModeSliceBuilder.ZEN_MODE_URI.equals(sliceUri)) { - return ZenModeSliceBuilder.getSlice(getContext()); - } else if (BluetoothSliceBuilder.BLUETOOTH_URI.equals(sliceUri)) { - return BluetoothSliceBuilder.getSlice(getContext()); - } else if (LocationSliceBuilder.LOCATION_URI.equals(sliceUri)) { - return LocationSliceBuilder.getSlice(getContext()); - } + // If adding a new Slice, do not directly match Slice URIs. + // Use {@link SlicesDatabaseAccessor}. + if (WifiCallingSliceHelper.WIFI_CALLING_URI.equals(sliceUri)) { + return FeatureFactory.getFactory(getContext()) + .getSlicesFeatureProvider() + .getNewWifiCallingSliceHelper(getContext()) + .createWifiCallingSlice(sliceUri); + } else if (WifiSliceBuilder.WIFI_URI.equals(sliceUri)) { + return WifiSliceBuilder.getSlice(getContext()); + } else if (ZenModeSliceBuilder.ZEN_MODE_URI.equals(sliceUri)) { + return ZenModeSliceBuilder.getSlice(getContext()); + } else if (BluetoothSliceBuilder.BLUETOOTH_URI.equals(sliceUri)) { + return BluetoothSliceBuilder.getSlice(getContext()); + } else if (LocationSliceBuilder.LOCATION_URI.equals(sliceUri)) { + return LocationSliceBuilder.getSlice(getContext()); + } - SliceData cachedSliceData = mSliceWeakDataCache.get(sliceUri); - if (cachedSliceData == null) { - loadSliceInBackground(sliceUri); - return getSliceStub(sliceUri); - } + SliceData cachedSliceData = mSliceWeakDataCache.get(sliceUri); + if (cachedSliceData == null) { + loadSliceInBackground(sliceUri); + return getSliceStub(sliceUri); + } - // Remove the SliceData from the cache after it has been used to prevent a memory-leak. - if (!mSliceDataCache.containsKey(sliceUri)) { - mSliceWeakDataCache.remove(sliceUri); + // Remove the SliceData from the cache after it has been used to prevent a memory-leak. + if (!mSliceDataCache.containsKey(sliceUri)) { + mSliceWeakDataCache.remove(sliceUri); + } + return SliceBuilderUtils.buildSlice(getContext(), cachedSliceData); + } finally { + StrictMode.setThreadPolicy(oldPolicy); } - return SliceBuilderUtils.buildSlice(getContext(), cachedSliceData); } /** @@ -302,14 +312,14 @@ public class SettingsSliceProvider extends SliceProvider { final SliceData sliceData; try { - sliceData = mSlicesDatabaseAccessor.getSliceDataFromUri(uri); + sliceData = mSlicesDatabaseAccessor.getSliceDataFromUri(uri); } catch (IllegalStateException e) { Log.d(TAG, "Could not create slicedata for uri: " + uri); return; } final BasePreferenceController controller = SliceBuilderUtils.getPreferenceController( - getContext(), sliceData); + getContext(), sliceData); final IntentFilter filter = controller.getIntentFilter(); if (filter != null) { diff --git a/src/com/android/settings/slices/SliceBroadcastReceiver.java b/src/com/android/settings/slices/SliceBroadcastReceiver.java index 213bf00c028..d81734a974d 100644 --- a/src/com/android/settings/slices/SliceBroadcastReceiver.java +++ b/src/com/android/settings/slices/SliceBroadcastReceiver.java @@ -27,6 +27,7 @@ import static com.android.settings.wifi.WifiSliceBuilder.ACTION_WIFI_SLICE_CHANG import android.app.slice.Slice; import android.content.BroadcastReceiver; +import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.net.Uri; @@ -155,6 +156,7 @@ public class SliceBroadcastReceiver extends BroadcastReceiver { sliderController.setSliderPosition(newPosition); logSliceValueChange(context, key, newPosition); + updateUri(context, key, isPlatformSlice); } /** @@ -177,8 +179,15 @@ public class SliceBroadcastReceiver extends BroadcastReceiver { } private void updateUri(Context context, String key, boolean isPlatformDefined) { - final String path = SettingsSlicesContract.PATH_SETTING_ACTION + "/" + key; - final Uri uri = SliceBuilderUtils.getUri(path, isPlatformDefined); + final String authority = isPlatformDefined + ? SettingsSlicesContract.AUTHORITY + : SettingsSliceProvider.SLICE_AUTHORITY; + final Uri uri = new Uri.Builder() + .scheme(ContentResolver.SCHEME_CONTENT) + .authority(authority) + .appendPath(SettingsSlicesContract.PATH_SETTING_ACTION) + .appendPath(key) + .build(); context.getContentResolver().notifyChange(uri, null /* observer */); } } diff --git a/src/com/android/settings/wrapper/OverlayManagerWrapper.java b/src/com/android/settings/wrapper/OverlayManagerWrapper.java deleted file mode 100644 index 6e3c2348857..00000000000 --- a/src/com/android/settings/wrapper/OverlayManagerWrapper.java +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright (C) 2018 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.wrapper; - -import android.content.Context; -import android.content.om.IOverlayManager; -import android.content.om.OverlayInfo; -import android.os.RemoteException; -import android.os.ServiceManager; - -import java.util.ArrayList; -import java.util.List; - -public class OverlayManagerWrapper { - - private final IOverlayManager mOverlayManager; - - public OverlayManagerWrapper(IOverlayManager overlayManager) { - mOverlayManager = overlayManager; - } - - public OverlayManagerWrapper() { - this(IOverlayManager.Stub.asInterface(ServiceManager.getService(Context.OVERLAY_SERVICE))); - } - - public List getOverlayInfosForTarget(String overlay, int userId) { - if (mOverlayManager == null) { - return new ArrayList<>(); - } - try { - List infos - = mOverlayManager.getOverlayInfosForTarget(overlay, userId); - ArrayList result = new ArrayList<>(infos.size()); - for (int i = 0; i < infos.size(); i++) { - result.add(new OverlayInfo(infos.get(i))); - } - return result; - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } - - public boolean setEnabled(String overlay, boolean enabled, int userId) { - if (mOverlayManager == null) { - return false; - } - try { - return mOverlayManager.setEnabled(overlay, enabled, userId); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } - - public boolean setEnabledExclusiveInCategory(String overlay, int userId) { - if (mOverlayManager == null) { - return false; - } - try { - return mOverlayManager.setEnabledExclusiveInCategory(overlay, userId); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } - - public static class OverlayInfo { - - public static final String CATEGORY_THEME = android.content.om.OverlayInfo.CATEGORY_THEME; - public final String packageName; - public final String category; - private final boolean mEnabled; - - public OverlayInfo(String packageName, String category, boolean enabled) { - this.packageName = packageName; - this.category = category; - mEnabled = enabled; - } - - public OverlayInfo(android.content.om.OverlayInfo info) { - mEnabled = info.isEnabled(); - category = info.category; - packageName = info.packageName; - } - - public boolean isEnabled() { - return mEnabled; - } - } -} diff --git a/tests/robotests/src/com/android/settings/accessibility/MagnificationGesturesPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/accessibility/MagnificationGesturesPreferenceControllerTest.java index 4cab0282977..98b1770668f 100644 --- a/tests/robotests/src/com/android/settings/accessibility/MagnificationGesturesPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/accessibility/MagnificationGesturesPreferenceControllerTest.java @@ -114,4 +114,19 @@ public class MagnificationGesturesPreferenceControllerTest { Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED, -1)) .isEqualTo(OFF); } + + @Test + public void isSliceableCorrectKey_returnsTrue() { + final MagnificationGesturesPreferenceController controller = + new MagnificationGesturesPreferenceController(mContext, + "screen_magnification_gestures_preference_screen"); + assertThat(controller.isSliceable()).isTrue(); + } + + @Test + public void isSliceableIncorrectKey_returnsFalse() { + final MagnificationGesturesPreferenceController controller = + new MagnificationGesturesPreferenceController(mContext, "bad_key"); + assertThat(controller.isSliceable()).isFalse(); + } } diff --git a/tests/robotests/src/com/android/settings/accessibility/MagnificationNavbarPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/accessibility/MagnificationNavbarPreferenceControllerTest.java index d0d77711c2a..128e33bb351 100644 --- a/tests/robotests/src/com/android/settings/accessibility/MagnificationNavbarPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/accessibility/MagnificationNavbarPreferenceControllerTest.java @@ -158,4 +158,19 @@ public class MagnificationNavbarPreferenceControllerTest { sIsApplicable = applicable; } } + + @Test + public void isSliceableCorrectKey_returnsTrue() { + final MagnificationNavbarPreferenceController controller = + new MagnificationNavbarPreferenceController(mContext, + "screen_magnification_navbar_preference_screen"); + assertThat(controller.isSliceable()).isTrue(); + } + + @Test + public void isSliceableIncorrectKey_returnsFalse() { + final MagnificationNavbarPreferenceController controller = + new MagnificationNavbarPreferenceController(mContext, "bad_key"); + assertThat(controller.isSliceable()).isFalse(); + } } diff --git a/tests/robotests/src/com/android/settings/accessibility/NotificationVibrationIntensityPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/accessibility/NotificationVibrationIntensityPreferenceControllerTest.java index 2a166f3fda2..dda2ae5e780 100644 --- a/tests/robotests/src/com/android/settings/accessibility/NotificationVibrationIntensityPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/accessibility/NotificationVibrationIntensityPreferenceControllerTest.java @@ -18,10 +18,12 @@ package com.android.settings.accessibility; import static android.provider.Settings.System.NOTIFICATION_VIBRATION_INTENSITY; import static com.google.common.truth.Truth.assertThat; +import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; import androidx.lifecycle.LifecycleOwner; import android.content.Context; +import android.content.res.Resources; import android.os.Vibrator; import android.provider.Settings; import androidx.preference.Preference; @@ -48,6 +50,7 @@ public class NotificationVibrationIntensityPreferenceControllerTest { private LifecycleOwner mLifecycleOwner; private Lifecycle mLifecycle; private Context mContext; + private Resources mResources; private NotificationVibrationIntensityPreferenceController mController; private Preference mPreference; @@ -56,7 +59,11 @@ public class NotificationVibrationIntensityPreferenceControllerTest { MockitoAnnotations.initMocks(this); mLifecycleOwner = () -> mLifecycle; mLifecycle = new Lifecycle(mLifecycleOwner); - mContext = RuntimeEnvironment.application; + mContext = spy(RuntimeEnvironment.application); + mResources = spy(mContext.getResources()); + when(mContext.getResources()).thenReturn(mResources); + when(mResources.getBoolean(R.bool.config_vibration_supports_multiple_intensities)) + .thenReturn(true); mController = new NotificationVibrationIntensityPreferenceController(mContext) { @Override protected int getDefaultIntensity() { @@ -68,7 +75,6 @@ public class NotificationVibrationIntensityPreferenceControllerTest { mPreference.setSummary("Test"); when(mScreen.findPreference(mController.getPreferenceKey())) .thenReturn(mPreference); - mController.displayPreference(mScreen); } @Test @@ -80,7 +86,10 @@ public class NotificationVibrationIntensityPreferenceControllerTest { } @Test - public void updateState_shouldRefreshSummary() { + public void updateState_withMultipleIntensitySuport_shouldRefreshSummary() { + setSupportsMultipleIntensities(true); + showPreference(); + Settings.System.putInt(mContext.getContentResolver(), NOTIFICATION_VIBRATION_INTENSITY, Vibrator.VIBRATION_INTENSITY_LOW); mController.updateState(mPreference); @@ -105,4 +114,43 @@ public class NotificationVibrationIntensityPreferenceControllerTest { assertThat(mPreference.getSummary()) .isEqualTo(mContext.getString(R.string.accessibility_vibration_intensity_off)); } + + @Test + public void updateState_withoutMultipleIntensitySupport_shouldRefreshSummary() { + setSupportsMultipleIntensities(false); + showPreference(); + + Settings.System.putInt(mContext.getContentResolver(), + NOTIFICATION_VIBRATION_INTENSITY, Vibrator.VIBRATION_INTENSITY_LOW); + mController.updateState(mPreference); + assertThat(mPreference.getSummary()) + .isEqualTo(mContext.getString(R.string.switch_on_text)); + + Settings.System.putInt(mContext.getContentResolver(), + NOTIFICATION_VIBRATION_INTENSITY, Vibrator.VIBRATION_INTENSITY_HIGH); + mController.updateState(mPreference); + assertThat(mPreference.getSummary()) + .isEqualTo(mContext.getString(R.string.switch_on_text)); + + Settings.System.putInt(mContext.getContentResolver(), + NOTIFICATION_VIBRATION_INTENSITY, Vibrator.VIBRATION_INTENSITY_MEDIUM); + mController.updateState(mPreference); + assertThat(mPreference.getSummary()) + .isEqualTo(mContext.getString(R.string.switch_on_text)); + + Settings.System.putInt(mContext.getContentResolver(), + NOTIFICATION_VIBRATION_INTENSITY, Vibrator.VIBRATION_INTENSITY_OFF); + mController.updateState(mPreference); + assertThat(mPreference.getSummary()) + .isEqualTo(mContext.getString(R.string.switch_off_text)); + } + + private void setSupportsMultipleIntensities(boolean hasSupport) { + when(mResources.getBoolean(R.bool.config_vibration_supports_multiple_intensities)) + .thenReturn(hasSupport); + } + + private void showPreference() { + mController.displayPreference(mScreen); + } } diff --git a/tests/robotests/src/com/android/settings/accessibility/VibrationPreferenceFragmentTest.java b/tests/robotests/src/com/android/settings/accessibility/VibrationPreferenceFragmentTest.java index 8550cf98916..ecd2ff9ce57 100644 --- a/tests/robotests/src/com/android/settings/accessibility/VibrationPreferenceFragmentTest.java +++ b/tests/robotests/src/com/android/settings/accessibility/VibrationPreferenceFragmentTest.java @@ -20,16 +20,19 @@ import static com.android.settings.accessibility.VibrationPreferenceFragment.KEY import static com.android.settings.accessibility.VibrationPreferenceFragment.KEY_INTENSITY_LOW; import static com.android.settings.accessibility.VibrationPreferenceFragment.KEY_INTENSITY_MEDIUM; import static com.android.settings.accessibility.VibrationPreferenceFragment.KEY_INTENSITY_OFF; +import static com.android.settings.accessibility.VibrationPreferenceFragment.KEY_INTENSITY_ON; 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.when; import android.app.Activity; import android.content.Context; +import android.content.res.Resources; import android.os.UserManager; import android.os.Vibrator; import android.provider.Settings; +import com.android.settings.R; import com.android.settings.accessibility.VibrationPreferenceFragment.VibrationIntensityCandidateInfo; import com.android.settings.testutils.FakeFeatureFactory; import com.android.settings.testutils.SettingsRobolectricTestRunner; @@ -58,12 +61,11 @@ public class VibrationPreferenceFragmentTest { INTENSITY_TO_KEY.put(Vibrator.VIBRATION_INTENSITY_HIGH, KEY_INTENSITY_HIGH); } - @Mock(answer = Answers.RETURNS_DEEP_STUBS) - private Activity mActivity; @Mock private UserManager mUserManager; private Context mContext; + private Resources mResources; private TestVibrationPreferenceFragment mFragment; @Before @@ -71,16 +73,18 @@ public class VibrationPreferenceFragmentTest { MockitoAnnotations.initMocks(this); FakeFeatureFactory.setupForTest(); - mContext = RuntimeEnvironment.application; + mContext = spy(RuntimeEnvironment.application); + mResources = spy(mContext.getResources()); + when(mContext.getResources()).thenReturn(mResources); mFragment = spy(new TestVibrationPreferenceFragment()); - doReturn(mUserManager).when(mActivity).getSystemService(Context.USER_SERVICE); - doReturn(mContext).when(mFragment).getContext(); - mFragment.onAttach(mActivity); + when(mContext.getSystemService(Context.USER_SERVICE)).thenReturn(mUserManager); } @Test public void changeIntensitySetting_shouldResultInCorrespondingKey() { + setSupportsMultipleIntensities(true); + mFragment.onAttach(mContext); for (Map.Entry entry : INTENSITY_TO_KEY.entrySet()) { Settings.System.putInt(mContext.getContentResolver(), Settings.System.HAPTIC_FEEDBACK_INTENSITY, entry.getKey()); @@ -88,13 +92,38 @@ public class VibrationPreferenceFragmentTest { } } + @Test + public void changeIntensitySetting_WithoutMultipleIntensitySupport_shouldResultInOn() { + setSupportsMultipleIntensities(false); + mFragment.onAttach(mContext); + for (int intensity : INTENSITY_TO_KEY.keySet()) { + Settings.System.putInt(mContext.getContentResolver(), + Settings.System.HAPTIC_FEEDBACK_INTENSITY, intensity); + final String expectedKey = intensity == Vibrator.VIBRATION_INTENSITY_OFF + ? KEY_INTENSITY_OFF + : KEY_INTENSITY_ON; + assertThat(mFragment.getDefaultKey()).isEqualTo(expectedKey); + } + } + @Test public void initialDefaultKey_shouldBeMedium() { + setSupportsMultipleIntensities(true); + mFragment.onAttach(mContext); assertThat(mFragment.getDefaultKey()).isEqualTo(KEY_INTENSITY_MEDIUM); } + @Test + public void initialDefaultKey_WithoutMultipleIntensitySupport_shouldBeOn() { + setSupportsMultipleIntensities(false); + mFragment.onAttach(mContext); + assertThat(mFragment.getDefaultKey()).isEqualTo(KEY_INTENSITY_ON); + } + @Test public void candidates_shouldBeSortedByIntensity() { + setSupportsMultipleIntensities(true); + mFragment.onAttach(mContext); final List candidates = mFragment.getCandidates(); assertThat(candidates.size()).isEqualTo(INTENSITY_TO_KEY.size()); VibrationIntensityCandidateInfo prevCandidate = @@ -106,6 +135,11 @@ public class VibrationPreferenceFragmentTest { } } + private void setSupportsMultipleIntensities(boolean hasSupport) { + when(mResources.getBoolean(R.bool.config_vibration_supports_multiple_intensities)) + .thenReturn(hasSupport); + } + private class TestVibrationPreferenceFragment extends VibrationPreferenceFragment { @Override protected int getPreferenceScreenResId() { @@ -129,5 +163,10 @@ public class VibrationPreferenceFragmentTest { protected int getDefaultVibrationIntensity() { return Vibrator.VIBRATION_INTENSITY_MEDIUM; } + + @Override + public Context getContext() { + return mContext; + } } } diff --git a/tests/robotests/src/com/android/settings/development/EmulateDisplayCutoutPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/development/EmulateDisplayCutoutPreferenceControllerTest.java index 34a0581e582..fd063cb37b6 100644 --- a/tests/robotests/src/com/android/settings/development/EmulateDisplayCutoutPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/development/EmulateDisplayCutoutPreferenceControllerTest.java @@ -26,14 +26,15 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.content.Context; +import android.content.om.IOverlayManager; +import android.content.om.OverlayInfo; import android.content.pm.PackageManager; +import android.os.RemoteException; import androidx.preference.ListPreference; import androidx.preference.PreferenceScreen; import android.view.DisplayCutout; import com.android.settings.testutils.SettingsRobolectricTestRunner; -import com.android.settings.wrapper.OverlayManagerWrapper; -import com.android.settings.wrapper.OverlayManagerWrapper.OverlayInfo; import org.junit.Before; import org.junit.Test; @@ -41,6 +42,7 @@ import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import java.util.ArrayList; import java.util.Arrays; @RunWith(SettingsRobolectricTestRunner.class) @@ -54,7 +56,7 @@ public class EmulateDisplayCutoutPreferenceControllerTest { @Mock private Context mContext; @Mock - private OverlayManagerWrapper mOverlayManager; + private IOverlayManager mOverlayManager; @Mock private PackageManager mPackageManager; @Mock @@ -64,6 +66,7 @@ public class EmulateDisplayCutoutPreferenceControllerTest { @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); + when(mContext.getSystemService(Context.OVERLAY_SERVICE)).thenReturn(mOverlayManager); mockCurrentOverlays(); when(mPackageManager.getApplicationInfo(any(), anyInt())) .thenThrow(PackageManager.NameNotFoundException.class); @@ -72,8 +75,12 @@ public class EmulateDisplayCutoutPreferenceControllerTest { } Object mockCurrentOverlays(OverlayInfo... overlays) { - return when(mOverlayManager.getOverlayInfosForTarget(eq("android"), anyInt())) - .thenReturn(Arrays.asList(overlays)); + try { + return when(mOverlayManager.getOverlayInfosForTarget(eq("android"), anyInt())) + .thenReturn(Arrays.asList(overlays)); + } catch (RemoteException re) { + return new ArrayList(); + } } @Test @@ -146,6 +153,15 @@ public class EmulateDisplayCutoutPreferenceControllerTest { } private static OverlayInfo createFakeOverlay(String pkg, boolean enabled) { - return new OverlayInfo(pkg, DisplayCutout.EMULATION_OVERLAY_CATEGORY, enabled); + final int state = (enabled) ? OverlayInfo.STATE_ENABLED : OverlayInfo.STATE_DISABLED; + + return new OverlayInfo(pkg /* packageName */, + pkg + ".target" /* targetPackageName */, + DisplayCutout.EMULATION_OVERLAY_CATEGORY /* category */, + pkg + ".baseCodePath" /* baseCodePath */, + state /* state */, + 0 /* userId */, + 0 /* priority */, + true /* isStatic */); } } \ No newline at end of file diff --git a/tests/robotests/src/com/android/settings/display/ThemePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/display/ThemePreferenceControllerTest.java index 47cdd35f6f6..19ffdc6d731 100644 --- a/tests/robotests/src/com/android/settings/display/ThemePreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/display/ThemePreferenceControllerTest.java @@ -16,25 +16,26 @@ package com.android.settings.display; +import static com.google.common.truth.Truth.assertThat; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyInt; import static org.mockito.Matchers.anyString; 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; +import android.content.om.IOverlayManager; +import android.content.om.OverlayInfo; import android.content.pm.ApplicationInfo; +import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; -import androidx.preference.ListPreference; import com.android.settings.R; import com.android.settings.testutils.FakeFeatureFactory; import com.android.settings.testutils.SettingsRobolectricTestRunner; -import com.android.settings.wrapper.OverlayManagerWrapper; import org.junit.Before; import org.junit.Test; @@ -44,6 +45,10 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.robolectric.RuntimeEnvironment; +import java.util.Arrays; + +import androidx.preference.ListPreference; + @RunWith(SettingsRobolectricTestRunner.class) public class ThemePreferenceControllerTest { @@ -55,6 +60,8 @@ public class ThemePreferenceControllerTest { private ApplicationInfo mApplicationInfo; @Mock private ListPreference mPreference; + @Mock + private IOverlayManager mOverlayManager; private ThemePreferenceController mController; @@ -67,8 +74,28 @@ public class ThemePreferenceControllerTest { when(mContext.getString(R.string.default_theme)) .thenReturn(RuntimeEnvironment.application.getString(R.string.default_theme)); - mController = - spy(new ThemePreferenceController(mContext, mock(OverlayManagerWrapper.class))); + when(mContext.getSystemService(Context.OVERLAY_SERVICE)).thenReturn(mOverlayManager); + mController = spy(new ThemePreferenceController(mContext, mOverlayManager)); + } + + @Test + public void testAvailable_false() throws Exception { + when(mPackageManager.getPackageInfo(anyString(), anyInt())).thenReturn( + new PackageInfo()); + when(mOverlayManager.getOverlayInfosForTarget(any(), anyInt())) + .thenReturn(Arrays.asList(new OverlayInfo("", "", "", "", 0, 0, 0, false))); + assertThat(mController.isAvailable()).isFalse(); + } + + @Test + public void testAvailable_true() throws Exception { + when(mPackageManager.getPackageInfo(anyString(), anyInt())).thenReturn( + new PackageInfo()); + when(mOverlayManager.getOverlayInfosForTarget(any(), anyInt())) + .thenReturn(Arrays.asList( + new OverlayInfo("", "", OverlayInfo.CATEGORY_THEME, "", 0, 0, 0, true), + new OverlayInfo("", "", OverlayInfo.CATEGORY_THEME, "", 0, 0, 0, true))); + assertThat(mController.isAvailable()).isTrue(); } @Test @@ -79,7 +106,7 @@ public class ThemePreferenceControllerTest { final String themeLabel2 = "Theme2"; final String[] themes = {pkg1, pkg2}; doReturn("pkg1.theme1").when(mController).getCurrentTheme(); - doReturn(themes).when(mController).getAvailableThemes(); + doReturn(themes).when(mController).getAvailableThemes(false /* currentThemeOnly */); when(mPackageManager.getApplicationInfo(anyString(), anyInt()).loadLabel(mPackageManager)) .thenReturn(themeLabel1) .thenReturn(themeLabel2); @@ -98,7 +125,7 @@ public class ThemePreferenceControllerTest { final String themeLabel2 = "Theme2"; final String[] themes = {pkg1, pkg2}; doReturn(null).when(mController).getCurrentTheme(); - doReturn(themes).when(mController).getAvailableThemes(); + doReturn(themes).when(mController).getAvailableThemes(false /* currentThemeOnly */); when(mPackageManager.getApplicationInfo(anyString(), anyInt()).loadLabel(mPackageManager)) .thenReturn(themeLabel1) .thenReturn(themeLabel2); @@ -109,4 +136,32 @@ public class ThemePreferenceControllerTest { .setSummary(RuntimeEnvironment.application.getString(R.string.default_theme)); verify(mPreference).setValue(null); } + + @Test + public void getCurrentTheme_withEnabledState() throws Exception { + OverlayInfo info1 = new OverlayInfo("com.android.Theme1", "android", + OverlayInfo.CATEGORY_THEME, "", OverlayInfo.STATE_ENABLED, 0, 0, true); + OverlayInfo info2 = new OverlayInfo("com.android.Theme2", "android", + OverlayInfo.CATEGORY_THEME, "", 0, 0, 0, true); + when(mOverlayManager.getOverlayInfosForTarget(any(), anyInt())).thenReturn( + Arrays.asList(info1, info2)); + when(mPackageManager.getPackageInfo(anyString(), anyInt())).thenReturn( + new PackageInfo()); + + assertThat(mController.getCurrentTheme()).isEqualTo(info1.packageName); + } + + @Test + public void testGetCurrentTheme_withoutEnabledState() throws Exception { + OverlayInfo info1 = new OverlayInfo("com.android.Theme1", "android", + OverlayInfo.CATEGORY_THEME, "", OverlayInfo.STATE_DISABLED, 0, 0, true); + OverlayInfo info2 = new OverlayInfo("com.android.Theme2", "android", + OverlayInfo.CATEGORY_THEME, "", 0, 0, 0, true); + when(mOverlayManager.getOverlayInfosForTarget(any(), anyInt())).thenReturn( + Arrays.asList(info1, info2)); + when(mPackageManager.getPackageInfo(anyString(), anyInt())).thenReturn( + new PackageInfo()); + + assertThat(mController.getCurrentTheme()).isNull(); + } } diff --git a/tests/robotests/src/com/android/settings/fuelgauge/SmartBatteryPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/fuelgauge/SmartBatteryPreferenceControllerTest.java index fc8740ef980..163b7c5c43d 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/SmartBatteryPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/SmartBatteryPreferenceControllerTest.java @@ -115,4 +115,11 @@ public class SmartBatteryPreferenceControllerTest { return Settings.Global.getInt(mContentResolver, Settings.Global.ADAPTIVE_BATTERY_MANAGEMENT_ENABLED, ON); } + + @Test + public void isSliceableCorrectKey_returnsTrue() { + final SmartBatteryPreferenceController controller = + new SmartBatteryPreferenceController(mContext); + assertThat(controller.isSliceable()).isTrue(); + } } diff --git a/tests/robotests/src/com/android/settings/notification/AlarmVolumePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/AlarmVolumePreferenceControllerTest.java index 6e8476c4bb5..4f48b779b6c 100644 --- a/tests/robotests/src/com/android/settings/notification/AlarmVolumePreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/notification/AlarmVolumePreferenceControllerTest.java @@ -73,4 +73,9 @@ public class AlarmVolumePreferenceControllerTest { public void getAudioStream_shouldReturnAlarm() { assertThat(mController.getAudioStream()).isEqualTo(AudioManager.STREAM_ALARM); } + + @Test + public void isSliceableCorrectKey_returnsTrue() { + assertThat(mController.isSliceable()).isTrue(); + } } diff --git a/tests/robotests/src/com/android/settings/slices/SettingsSliceProviderTest.java b/tests/robotests/src/com/android/settings/slices/SettingsSliceProviderTest.java index df960d8a912..6b1262a1e2c 100644 --- a/tests/robotests/src/com/android/settings/slices/SettingsSliceProviderTest.java +++ b/tests/robotests/src/com/android/settings/slices/SettingsSliceProviderTest.java @@ -18,9 +18,7 @@ package com.android.settings.slices; import static android.content.ContentResolver.SCHEME_CONTENT; - import static com.google.common.truth.Truth.assertThat; - import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; @@ -38,19 +36,24 @@ import android.os.StrictMode; import android.provider.SettingsSlicesContract; import android.util.ArraySet; -import com.android.settings.location.LocationSliceBuilder; -import com.android.settings.wifi.WifiSliceBuilder; import com.android.settings.bluetooth.BluetoothSliceBuilder; +import com.android.settings.location.LocationSliceBuilder; import com.android.settings.notification.ZenModeSliceBuilder; import com.android.settings.testutils.DatabaseTestUtils; import com.android.settings.testutils.FakeToggleController; import com.android.settings.testutils.SettingsRobolectricTestRunner; +import com.android.settings.testutils.shadow.ShadowThreadUtils; +import com.android.settings.wifi.WifiSliceBuilder; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.robolectric.RuntimeEnvironment; +import org.robolectric.annotation.Config; +import org.robolectric.annotation.Implementation; +import org.robolectric.annotation.Implements; +import org.robolectric.annotation.Resetter; import java.util.Arrays; import java.util.Collection; @@ -66,6 +69,7 @@ import androidx.slice.Slice; * TODO Investigate using ShadowContentResolver.registerProviderInternal(String, ContentProvider) */ @RunWith(SettingsRobolectricTestRunner.class) +@Config(shadows = ShadowThreadUtils.class) public class SettingsSliceProviderTest { private static final String KEY = "KEY"; @@ -98,6 +102,7 @@ public class SettingsSliceProviderTest { public void setUp() { mContext = spy(RuntimeEnvironment.application); mProvider = spy(new SettingsSliceProvider()); + ShadowStrictMode.reset(); mProvider.mSliceWeakDataCache = new HashMap<>(); mProvider.mSliceDataCache = new HashMap<>(); mProvider.mSlicesDatabaseAccessor = new SlicesDatabaseAccessor(mContext); @@ -112,6 +117,7 @@ public class SettingsSliceProviderTest { @After public void cleanUp() { + ShadowThreadUtils.reset(); DatabaseTestUtils.clearDb(mContext); } @@ -184,7 +190,8 @@ public class SettingsSliceProviderTest { } @Test - public void onBindSlice_shouldNotOverrideStrictMode() { + public void onBindSlice_mainThread_shouldNotOverrideStrictMode() { + ShadowThreadUtils.setIsMainThread(true); final StrictMode.ThreadPolicy oldThreadPolicy = StrictMode.getThreadPolicy(); SliceData data = getDummyData(); mProvider.mSliceWeakDataCache.put(data.getUri(), data); @@ -196,7 +203,19 @@ public class SettingsSliceProviderTest { } @Test - public void onBindSlice_requestsBlockedSlice_retunsNull() { + @Config(shadows = ShadowStrictMode.class) + public void onBindSlice_backgroundThread_shouldOverrideStrictMode() { + ShadowThreadUtils.setIsMainThread(false); + + SliceData data = getDummyData(); + mProvider.mSliceWeakDataCache.put(data.getUri(), data); + mProvider.onBindSlice(data.getUri()); + + assertThat(ShadowStrictMode.isThreadPolicyOverridden()).isTrue(); + } + + @Test + public void onBindSlice_requestsBlockedSlice_returnsNull() { final String blockedKey = "blocked_key"; final Set blockedSet = new ArraySet<>(); blockedSet.add(blockedKey); @@ -456,7 +475,7 @@ public class SettingsSliceProviderTest { mDb.replaceOrThrow(SlicesDatabaseHelper.Tables.TABLE_SLICES_INDEX, null, values); } - private SliceData getDummyData() { + private static SliceData getDummyData() { return new SliceData.Builder() .setKey(KEY) .setTitle(TITLE) @@ -468,4 +487,24 @@ public class SettingsSliceProviderTest { .setPreferenceControllerClassName(PREF_CONTROLLER) .build(); } + + @Implements(value = StrictMode.class, inheritImplementationMethods = true) + public static class ShadowStrictMode { + + private static int sSetThreadPolicyCount; + + @Resetter + public static void reset() { + sSetThreadPolicyCount = 0; + } + + @Implementation + public static void setThreadPolicy(final StrictMode.ThreadPolicy policy) { + sSetThreadPolicyCount++; + } + + public static boolean isThreadPolicyOverridden() { + return sSetThreadPolicyCount != 0; + } + } } \ No newline at end of file diff --git a/tests/robotests/src/com/android/settings/slices/SliceBroadcastReceiverTest.java b/tests/robotests/src/com/android/settings/slices/SliceBroadcastReceiverTest.java index 5f0bc962476..6d18449e5f8 100644 --- a/tests/robotests/src/com/android/settings/slices/SliceBroadcastReceiverTest.java +++ b/tests/robotests/src/com/android/settings/slices/SliceBroadcastReceiverTest.java @@ -96,8 +96,16 @@ public class SliceBroadcastReceiverTest { @Test public void onReceive_toggleChanged() { final String key = "key"; + final Uri uri = new Uri.Builder() + .scheme(ContentResolver.SCHEME_CONTENT) + .authority(SettingsSliceProvider.SLICE_AUTHORITY) + .appendPath(SettingsSlicesContract.PATH_SETTING_ACTION) + .appendPath(key) + .build(); mSearchFeatureProvider.getSearchIndexableResources().getProviderValues().clear(); insertSpecialCase(key); + final ContentResolver resolver = mock(ContentResolver.class); + doReturn(resolver).when(mContext).getContentResolver(); // Turn on toggle setting FakeToggleController fakeToggleController = new FakeToggleController(mContext, key); fakeToggleController.setChecked(true); @@ -121,6 +129,7 @@ public class SliceBroadcastReceiverTest { assertThat(namePair.first).isEqualTo(MetricsEvent.FIELD_SETTINGS_PREFERENCE_CHANGE_NAME); assertThat(namePair.second).isEqualTo(fakeToggleController.getPreferenceKey()); + verify(resolver).notifyChange(uri, null); assertThat(valuePair.first) .isEqualTo(MetricsEvent.FIELD_SETTINGS_PREFERENCE_CHANGE_INT_VALUE); assertThat(valuePair.second).isEqualTo(0); @@ -150,9 +159,13 @@ public class SliceBroadcastReceiverTest { assertThat(fakeToggleController.isChecked()).isFalse(); - final Uri expectedUri = SliceBuilderUtils.getUri( - SettingsSlicesContract.PATH_SETTING_ACTION + "/" + key, false); - verify(resolver).notifyChange(eq(expectedUri), eq(null)); + final Uri expectedUri = new Uri.Builder() + .scheme(ContentResolver.SCHEME_CONTENT) + .authority(SettingsSliceProvider.SLICE_AUTHORITY) + .appendPath(SettingsSlicesContract.PATH_SETTING_ACTION) + .appendPath(key) + .build(); + verify(resolver).notifyChange(expectedUri, null); } @Test @@ -183,6 +196,14 @@ public class SliceBroadcastReceiverTest { @Test public void onReceive_sliderChanged() { final String key = "key"; + final Uri uri = new Uri.Builder() + .scheme(ContentResolver.SCHEME_CONTENT) + .authority(SettingsSliceProvider.SLICE_AUTHORITY) + .appendPath(SettingsSlicesContract.PATH_SETTING_ACTION) + .appendPath(key) + .build(); + final ContentResolver resolver = mock(ContentResolver.class); + doReturn(resolver).when(mContext).getContentResolver(); final int position = FakeSliderController.MAX_STEPS - 1; final int oldPosition = FakeSliderController.MAX_STEPS; mSearchFeatureProvider.getSearchIndexableResources().getProviderValues().clear(); @@ -213,6 +234,7 @@ public class SliceBroadcastReceiverTest { assertThat(namePair.first).isEqualTo(MetricsEvent.FIELD_SETTINGS_PREFERENCE_CHANGE_NAME); assertThat(namePair.second).isEqualTo(key); + verify(resolver).notifyChange(uri, null); assertThat(valuePair.first) .isEqualTo(MetricsEvent.FIELD_SETTINGS_PREFERENCE_CHANGE_INT_VALUE); assertThat(valuePair.second).isEqualTo(position); @@ -286,8 +308,12 @@ public class SliceBroadcastReceiverTest { // Check the value is the same and the Uri has been notified. assertThat(fakeToggleController.isChecked()).isTrue(); - final Uri expectedUri = SliceBuilderUtils.getUri( - SettingsSlicesContract.PATH_SETTING_ACTION + "/" + key, false); + final Uri expectedUri = new Uri.Builder() + .scheme(ContentResolver.SCHEME_CONTENT) + .authority(SettingsSliceProvider.SLICE_AUTHORITY) + .appendPath(SettingsSlicesContract.PATH_SETTING_ACTION) + .appendPath(key) + .build(); verify(resolver).notifyChange(eq(expectedUri), eq(null)); } @@ -323,8 +349,12 @@ public class SliceBroadcastReceiverTest { // Check position is the same and the Uri has been notified. assertThat(fakeSliderController.getSliderPosition()).isEqualTo(oldPosition); - final Uri expectedUri = SliceBuilderUtils.getUri( - SettingsSlicesContract.PATH_SETTING_ACTION + "/" + key, false); + final Uri expectedUri = new Uri.Builder() + .scheme(ContentResolver.SCHEME_CONTENT) + .authority(SettingsSliceProvider.SLICE_AUTHORITY) + .appendPath(SettingsSlicesContract.PATH_SETTING_ACTION) + .appendPath(key) + .build(); verify(resolver).notifyChange(eq(expectedUri), eq(null)); } diff --git a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowThreadUtils.java b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowThreadUtils.java index 6b0411e9ab0..9513098e862 100644 --- a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowThreadUtils.java +++ b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowThreadUtils.java @@ -20,10 +20,18 @@ import com.android.settingslib.utils.ThreadUtils; import org.robolectric.annotation.Implementation; import org.robolectric.annotation.Implements; +import org.robolectric.annotation.Resetter; @Implements(ThreadUtils.class) public class ShadowThreadUtils { + private static boolean sIsMainThread = true; + + @Resetter + public static void reset() { + sIsMainThread = true; + } + @Implementation public static void postOnBackgroundThread(Runnable runnable) { runnable.run(); @@ -33,4 +41,14 @@ public class ShadowThreadUtils { public static void postOnMainThread(Runnable runnable) { runnable.run(); } + + @Implementation + public static boolean isMainThread() { + return sIsMainThread; + } + + public static void setIsMainThread(boolean isMainThread) { + sIsMainThread = isMainThread; + } + } diff --git a/tests/unit/src/com/android/settings/display/ThemePreferenceControllerTest.java b/tests/unit/src/com/android/settings/display/ThemePreferenceControllerTest.java index 56552132515..96ffe23fd39 100644 --- a/tests/unit/src/com/android/settings/display/ThemePreferenceControllerTest.java +++ b/tests/unit/src/com/android/settings/display/ThemePreferenceControllerTest.java @@ -26,6 +26,7 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.content.ContextWrapper; +import android.content.om.IOverlayManager; import android.content.om.OverlayInfo; import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; @@ -33,9 +34,6 @@ import android.content.pm.PackageManager; import android.support.test.InstrumentationRegistry; import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; -import androidx.preference.ListPreference; - -import com.android.settings.wrapper.OverlayManagerWrapper; import org.junit.Before; import org.junit.Test; @@ -44,18 +42,20 @@ import org.mockito.ArgumentCaptor; import java.util.ArrayList; +import androidx.preference.ListPreference; + @SmallTest @RunWith(AndroidJUnit4.class) public class ThemePreferenceControllerTest { - private OverlayManagerWrapper mMockOverlayManager; + private IOverlayManager mMockOverlayManager; private ContextWrapper mContext; private ThemePreferenceController mPreferenceController; private PackageManager mMockPackageManager; @Before public void setup() { - mMockOverlayManager = mock(OverlayManagerWrapper.class); + mMockOverlayManager = mock(IOverlayManager.class); mMockPackageManager = mock(PackageManager.class); mContext = new ContextWrapper(InstrumentationRegistry.getTargetContext()) { @Override @@ -69,9 +69,9 @@ public class ThemePreferenceControllerTest { @Test public void testUpdateState() throws Exception { OverlayInfo info1 = new OverlayInfo("com.android.Theme1", "android", - "", "", OverlayInfo.STATE_ENABLED, 0, 0, true); + OverlayInfo.CATEGORY_THEME, "", OverlayInfo.STATE_ENABLED, 0, 0, true); OverlayInfo info2 = new OverlayInfo("com.android.Theme2", "android", - "", "", 0, 0, 0, true); + OverlayInfo.CATEGORY_THEME, "", 0, 0, 0, true); when(mMockPackageManager.getApplicationInfo(any(), anyInt())).thenAnswer(inv -> { ApplicationInfo info = mock(ApplicationInfo.class); if ("com.android.Theme1".equals(inv.getArguments()[0])) { @@ -105,9 +105,9 @@ public class ThemePreferenceControllerTest { @Test public void testUpdateState_withStaticOverlay() throws Exception { OverlayInfo info1 = new OverlayInfo("com.android.Theme1", "android", - "", "", OverlayInfo.STATE_ENABLED, 0, 0, true); + OverlayInfo.CATEGORY_THEME, "", OverlayInfo.STATE_ENABLED, 0, 0, true); OverlayInfo info2 = new OverlayInfo("com.android.Theme2", "android", - "", "", OverlayInfo.STATE_ENABLED, 0, 0, true); + OverlayInfo.CATEGORY_THEME, "", OverlayInfo.STATE_ENABLED, 0, 0, true); when(mMockPackageManager.getApplicationInfo(any(), anyInt())).thenAnswer(inv -> { ApplicationInfo info = mock(ApplicationInfo.class); if ("com.android.Theme1".equals(inv.getArguments()[0])) { @@ -140,29 +140,10 @@ public class ThemePreferenceControllerTest { verify(pref).setValue(eq("com.android.Theme2")); } - @Test - public void testAvailable_false() throws Exception { - when(mMockPackageManager.getPackageInfo(anyString(), anyInt())).thenReturn( - new PackageInfo()); - when(mMockOverlayManager.getOverlayInfosForTarget(any(), anyInt())) - .thenReturn(list(new OverlayInfo("", "", "", "", 0, 0, 0, false))); - assertThat(mPreferenceController.isAvailable()).isFalse(); - } - - @Test - public void testAvailable_true() throws Exception { - when(mMockPackageManager.getPackageInfo(anyString(), anyInt())).thenReturn( - new PackageInfo()); - when(mMockOverlayManager.getOverlayInfosForTarget(any(), anyInt())) - .thenReturn(list(new OverlayInfo("", "", "", "", 0, 0, 0, true), - new OverlayInfo("", "", "", "", 0, 0, 0, true))); - assertThat(mPreferenceController.isAvailable()).isTrue(); - } - - private ArrayList list(OverlayInfo... infos) { - ArrayList list = new ArrayList<>(); + private ArrayList list(OverlayInfo... infos) { + ArrayList list = new ArrayList<>(); for (OverlayInfo info : infos) { - list.add(new OverlayManagerWrapper.OverlayInfo(info)); + list.add(info); } return list; }