Snap for 4806815 from 1c7ae169ac to qt-release

Change-Id: I0ff798fbef20cc4ea8199c06f747ab65f0603c39
This commit is contained in:
android-build-team Robot
2018-05-26 09:33:31 +00:00
31 changed files with 627 additions and 309 deletions

View File

@@ -134,4 +134,9 @@
<!-- Whether or not to show the night light suggestion. -->
<bool name="config_night_light_suggestion_enabled">true</bool>
<!-- Whether or not the device is capable of multiple levels of vibration intensity.
Note that this is different from whether it can control the vibration amplitude as some
devices will be able to vary their amplitude but do not possess enough dynamic range to
have distinct intensity levels -->
<bool name="config_vibration_supports_multiple_intensities">false</bool>
</resources>

View File

@@ -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">
<com.android.settings.applications.LayoutPreference
android:key="my_device_info_header"

View File

@@ -30,6 +30,7 @@
android:key="smart_battery"
android:title="@string/smart_battery_title"
android:summary="@string/smart_battery_summary"
settings:controller="com.android.settings.fuelgauge.SmartBatteryPreferenceController"
settings:allowDividerAbove="true"/>
<SwitchPreference

View File

@@ -781,17 +781,27 @@ public class AccessibilitySettings extends SettingsPreferenceFragment implements
}
private String getVibrationSummary(Context context, @VibrationIntensity int intensity) {
switch (intensity) {
case Vibrator.VIBRATION_INTENSITY_OFF:
return context.getString(R.string.accessibility_vibration_summary_off);
case Vibrator.VIBRATION_INTENSITY_LOW:
return context.getString(R.string.accessibility_vibration_summary_low);
case Vibrator.VIBRATION_INTENSITY_MEDIUM:
return context.getString(R.string.accessibility_vibration_summary_medium);
case Vibrator.VIBRATION_INTENSITY_HIGH:
return context.getString(R.string.accessibility_vibration_summary_high);
default:
return "";
final boolean supportsMultipleIntensities = context.getResources().getBoolean(
R.bool.config_vibration_supports_multiple_intensities);
if (supportsMultipleIntensities) {
switch (intensity) {
case Vibrator.VIBRATION_INTENSITY_OFF:
return context.getString(R.string.accessibility_vibration_summary_off);
case Vibrator.VIBRATION_INTENSITY_LOW:
return context.getString(R.string.accessibility_vibration_summary_low);
case Vibrator.VIBRATION_INTENSITY_MEDIUM:
return context.getString(R.string.accessibility_vibration_summary_medium);
case Vibrator.VIBRATION_INTENSITY_HIGH:
return context.getString(R.string.accessibility_vibration_summary_high);
default:
return "";
}
} else {
if (intensity == Vibrator.VIBRATION_INTENSITY_OFF) {
return context.getString(R.string.switch_on_text);
} else {
return context.getString(R.string.switch_off_text);
}
}
}

View File

@@ -16,6 +16,8 @@ package com.android.settings.accessibility;
import android.content.Context;
import android.os.Bundle;
import android.provider.Settings;
import android.text.TextUtils;
import androidx.preference.Preference;
import com.android.settings.R;
@@ -61,6 +63,12 @@ public class MagnificationGesturesPreferenceController extends TogglePreferenceC
return AVAILABLE;
}
@Override
public boolean isSliceable() {
return TextUtils.equals(getPreferenceKey(),
"screen_magnification_gestures_preference_screen");
}
@Override
public CharSequence getSummary() {
int resId = 0;

View File

@@ -16,6 +16,8 @@ package com.android.settings.accessibility;
import android.content.Context;
import android.os.Bundle;
import android.provider.Settings;
import android.text.TextUtils;
import androidx.preference.Preference;
import com.android.settings.R;
@@ -68,6 +70,12 @@ public class MagnificationNavbarPreferenceController extends TogglePreferenceCon
: UNSUPPORTED_ON_DEVICE;
}
@Override
public boolean isSliceable() {
return TextUtils.equals(getPreferenceKey(),
"screen_magnification_navbar_preference_screen");
}
@Override
public CharSequence getSummary() {
int resId = 0;

View File

@@ -15,7 +15,9 @@
*/
package com.android.settings.accessibility;
import android.media.AudioAttributes;
import android.os.Vibrator;
import android.os.VibrationEffect;
import android.provider.Settings;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
@@ -43,6 +45,11 @@ public class NotificationVibrationPreferenceFragment extends VibrationPreference
return Settings.System.NOTIFICATION_VIBRATION_INTENSITY;
}
@Override
protected int getPreviewVibrationAudioAttributesUsage() {
return AudioAttributes.USAGE_NOTIFICATION;
}
@Override
protected int getDefaultVibrationIntensity() {
Vibrator vibrator = getContext().getSystemService(Vibrator.class);

View File

@@ -1,3 +1,6 @@
# Default reviewers for this and subdirectories.
pweaver@google.com
zork@google.com
zork@google.com
per-file HapticFeedbackIntensityPreferenceController.java = michaelwr@google.com
per-file *Vibration* = michaelwr@google.com

View File

@@ -16,7 +16,9 @@
package com.android.settings.accessibility;
import android.graphics.drawable.Drawable;
import android.media.AudioAttributes;
import android.os.Vibrator;
import android.os.VibrationEffect;
import android.provider.Settings;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
@@ -50,6 +52,11 @@ public class TouchVibrationPreferenceFragment extends VibrationPreferenceFragmen
return vibrator.getDefaultHapticFeedbackIntensity();
}
@Override
protected int getPreviewVibrationAudioAttributesUsage() {
return AudioAttributes.USAGE_ASSISTANCE_SONIFICATION;
}
@Override
public void onVibrationIntensitySelected(int intensity) {
// We want to keep HAPTIC_FEEDBACK_ENABLED consistent with this setting since some

View File

@@ -81,17 +81,27 @@ public abstract class VibrationIntensityPreferenceController extends BasePrefere
}
public static CharSequence getIntensityString(Context context, int intensity) {
switch (intensity) {
case Vibrator.VIBRATION_INTENSITY_OFF:
return context.getText(R.string.accessibility_vibration_intensity_off);
case Vibrator.VIBRATION_INTENSITY_LOW:
return context.getText(R.string.accessibility_vibration_intensity_low);
case Vibrator.VIBRATION_INTENSITY_MEDIUM:
return context.getText(R.string.accessibility_vibration_intensity_medium);
case Vibrator.VIBRATION_INTENSITY_HIGH:
return context.getText(R.string.accessibility_vibration_intensity_high);
default:
return "";
final boolean supportsMultipleIntensities = context.getResources().getBoolean(
R.bool.config_vibration_supports_multiple_intensities);
if (supportsMultipleIntensities) {
switch (intensity) {
case Vibrator.VIBRATION_INTENSITY_OFF:
return context.getString(R.string.accessibility_vibration_intensity_off);
case Vibrator.VIBRATION_INTENSITY_LOW:
return context.getString(R.string.accessibility_vibration_intensity_low);
case Vibrator.VIBRATION_INTENSITY_MEDIUM:
return context.getString(R.string.accessibility_vibration_intensity_medium);
case Vibrator.VIBRATION_INTENSITY_HIGH:
return context.getString(R.string.accessibility_vibration_intensity_high);
default:
return "";
}
} else {
if (intensity == Vibrator.VIBRATION_INTENSITY_OFF) {
return context.getString(R.string.switch_off_text);
} else {
return context.getString(R.string.switch_on_text);
}
}
}

View File

@@ -21,8 +21,10 @@ import androidx.annotation.VisibleForTesting;
import android.content.Context;
import android.database.ContentObserver;
import android.graphics.drawable.Drawable;
import android.media.AudioAttributes;
import android.net.Uri;
import android.os.Handler;
import android.os.VibrationEffect;
import android.os.Vibrator;
import android.provider.Settings;
import android.util.ArrayMap;
@@ -53,28 +55,15 @@ public abstract class VibrationPreferenceFragment extends RadioButtonPickerFragm
final static String KEY_INTENSITY_MEDIUM = "intensity_medium";
@VisibleForTesting
final static String KEY_INTENSITY_HIGH = "intensity_high";
// KEY_INTENSITY_ON is only used when the device doesn't support multiple intensity levels.
@VisibleForTesting
final static String KEY_INTENSITY_ON = "intensity_on";
private final Map<String, VibrationIntensityCandidateInfo> 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<? extends CandidateInfo> getCandidates() {
List<VibrationIntensityCandidateInfo> 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();
}
}
}

View File

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

View File

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

View File

@@ -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<OverlayInfo> 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<OverlayInfo> infos = mOverlayService.getOverlayInfosForTarget("android",
UserHandle.myUserId());
List<String> 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<OverlayInfo> infos;
List<String> 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()]);
}

View File

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

View File

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

View File

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

View File

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

View File

@@ -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<OverlayInfo> getOverlayInfosForTarget(String overlay, int userId) {
if (mOverlayManager == null) {
return new ArrayList<>();
}
try {
List<android.content.om.OverlayInfo> infos
= mOverlayManager.getOverlayInfosForTarget(overlay, userId);
ArrayList<OverlayInfo> 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;
}
}
}

View File

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

View File

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

View File

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

View File

@@ -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<Integer, String> 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<? extends CandidateInfo> 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;
}
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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<OverlayManagerWrapper.OverlayInfo> list(OverlayInfo... infos) {
ArrayList<OverlayManagerWrapper.OverlayInfo> list = new ArrayList<>();
private ArrayList<OverlayInfo> list(OverlayInfo... infos) {
ArrayList<OverlayInfo> list = new ArrayList<>();
for (OverlayInfo info : infos) {
list.add(new OverlayManagerWrapper.OverlayInfo(info));
list.add(info);
}
return list;
}