Snap for 4807121 from 8c60fc69b7 to pi-release

Change-Id: Ic7648d7b9e99e2adc9b0b33e5013e1504f590928
This commit is contained in:
android-build-team Robot
2018-05-27 07:23:47 +00:00
44 changed files with 923 additions and 335 deletions

View File

@@ -17,7 +17,7 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:background="?android:attr/colorSecondary" android:background="?android:attr/colorPrimary"
android:gravity="center_vertical" android:gravity="center_vertical"
android:orientation="horizontal" > android:orientation="horizontal" >

View File

@@ -134,4 +134,9 @@
<!-- Whether or not to show the night light suggestion. --> <!-- Whether or not to show the night light suggestion. -->
<bool name="config_night_light_suggestion_enabled">true</bool> <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> </resources>

View File

@@ -32,12 +32,14 @@
android:title="@string/nfc_quick_toggle_title" android:title="@string/nfc_quick_toggle_title"
android:icon="@drawable/ic_nfc" android:icon="@drawable/ic_nfc"
android:summary="@string/nfc_quick_toggle_summary" android:summary="@string/nfc_quick_toggle_summary"
settings:controller="com.android.settings.nfc.NfcPreferenceController"
android:order="-7"/> android:order="-7"/>
<com.android.settingslib.RestrictedPreference <com.android.settingslib.RestrictedPreference
android:fragment="com.android.settings.nfc.AndroidBeam" android:fragment="com.android.settings.nfc.AndroidBeam"
android:key="android_beam_settings" android:key="android_beam_settings"
android:title="@string/android_beam_settings_title" android:title="@string/android_beam_settings_title"
settings:controller="com.android.settings.nfc.AndroidBeamPreferenceController"
android:icon="@drawable/ic_android" android:icon="@drawable/ic_android"
android:order="-6"/> android:order="-6"/>

View File

@@ -19,8 +19,7 @@
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:settings="http://schemas.android.com/apk/res-auto" xmlns:settings="http://schemas.android.com/apk/res-auto"
android:key="my_device_info_pref_screen" android:key="my_device_info_pref_screen"
android:title="@string/about_settings" android:title="@string/about_settings">
settings:initialExpandedChildrenCount="7">
<com.android.settings.applications.LayoutPreference <com.android.settings.applications.LayoutPreference
android:key="my_device_info_header" android:key="my_device_info_header"

View File

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

View File

@@ -779,6 +779,9 @@ public class AccessibilitySettings extends SettingsPreferenceFragment implements
} }
private String getVibrationSummary(Context context, @VibrationIntensity int intensity) { private String getVibrationSummary(Context context, @VibrationIntensity int intensity) {
final boolean supportsMultipleIntensities = context.getResources().getBoolean(
R.bool.config_vibration_supports_multiple_intensities);
if (supportsMultipleIntensities) {
switch (intensity) { switch (intensity) {
case Vibrator.VIBRATION_INTENSITY_OFF: case Vibrator.VIBRATION_INTENSITY_OFF:
return context.getString(R.string.accessibility_vibration_summary_off); return context.getString(R.string.accessibility_vibration_summary_off);
@@ -791,6 +794,13 @@ public class AccessibilitySettings extends SettingsPreferenceFragment implements
default: default:
return ""; 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);
}
}
} }
private void updateLockScreenRotationCheckbox() { private void updateLockScreenRotationCheckbox() {

View File

@@ -17,6 +17,7 @@ import android.content.Context;
import android.os.Bundle; import android.os.Bundle;
import android.provider.Settings; import android.provider.Settings;
import android.support.v7.preference.Preference; import android.support.v7.preference.Preference;
import android.text.TextUtils;
import com.android.settings.R; import com.android.settings.R;
import com.android.settings.core.TogglePreferenceController; import com.android.settings.core.TogglePreferenceController;
@@ -61,6 +62,12 @@ public class MagnificationGesturesPreferenceController extends TogglePreferenceC
return AVAILABLE; return AVAILABLE;
} }
@Override
public boolean isSliceable() {
return TextUtils.equals(getPreferenceKey(),
"screen_magnification_gestures_preference_screen");
}
@Override @Override
public CharSequence getSummary() { public CharSequence getSummary() {
int resId = 0; int resId = 0;

View File

@@ -17,6 +17,7 @@ import android.content.Context;
import android.os.Bundle; import android.os.Bundle;
import android.provider.Settings; import android.provider.Settings;
import android.support.v7.preference.Preference; import android.support.v7.preference.Preference;
import android.text.TextUtils;
import com.android.settings.R; import com.android.settings.R;
import com.android.settings.core.TogglePreferenceController; import com.android.settings.core.TogglePreferenceController;
@@ -68,6 +69,12 @@ public class MagnificationNavbarPreferenceController extends TogglePreferenceCon
: UNSUPPORTED_ON_DEVICE; : UNSUPPORTED_ON_DEVICE;
} }
@Override
public boolean isSliceable() {
return TextUtils.equals(getPreferenceKey(),
"screen_magnification_navbar_preference_screen");
}
@Override @Override
public CharSequence getSummary() { public CharSequence getSummary() {
int resId = 0; int resId = 0;

View File

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

View File

@@ -1,3 +1,6 @@
# Default reviewers for this and subdirectories. # Default reviewers for this and subdirectories.
pweaver@google.com 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; package com.android.settings.accessibility;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
import android.media.AudioAttributes;
import android.os.Vibrator; import android.os.Vibrator;
import android.os.VibrationEffect;
import android.provider.Settings; import android.provider.Settings;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
@@ -50,6 +52,11 @@ public class TouchVibrationPreferenceFragment extends VibrationPreferenceFragmen
return vibrator.getDefaultHapticFeedbackIntensity(); return vibrator.getDefaultHapticFeedbackIntensity();
} }
@Override
protected int getPreviewVibrationAudioAttributesUsage() {
return AudioAttributes.USAGE_ASSISTANCE_SONIFICATION;
}
@Override @Override
public void onVibrationIntensitySelected(int intensity) { public void onVibrationIntensitySelected(int intensity) {
// We want to keep HAPTIC_FEEDBACK_ENABLED consistent with this setting since some // We want to keep HAPTIC_FEEDBACK_ENABLED consistent with this setting since some

View File

@@ -81,18 +81,28 @@ public abstract class VibrationIntensityPreferenceController extends BasePrefere
} }
public static CharSequence getIntensityString(Context context, int intensity) { public static CharSequence getIntensityString(Context context, int intensity) {
final boolean supportsMultipleIntensities = context.getResources().getBoolean(
R.bool.config_vibration_supports_multiple_intensities);
if (supportsMultipleIntensities) {
switch (intensity) { switch (intensity) {
case Vibrator.VIBRATION_INTENSITY_OFF: case Vibrator.VIBRATION_INTENSITY_OFF:
return context.getText(R.string.accessibility_vibration_intensity_off); return context.getString(R.string.accessibility_vibration_intensity_off);
case Vibrator.VIBRATION_INTENSITY_LOW: case Vibrator.VIBRATION_INTENSITY_LOW:
return context.getText(R.string.accessibility_vibration_intensity_low); return context.getString(R.string.accessibility_vibration_intensity_low);
case Vibrator.VIBRATION_INTENSITY_MEDIUM: case Vibrator.VIBRATION_INTENSITY_MEDIUM:
return context.getText(R.string.accessibility_vibration_intensity_medium); return context.getString(R.string.accessibility_vibration_intensity_medium);
case Vibrator.VIBRATION_INTENSITY_HIGH: case Vibrator.VIBRATION_INTENSITY_HIGH:
return context.getText(R.string.accessibility_vibration_intensity_high); return context.getString(R.string.accessibility_vibration_intensity_high);
default: default:
return ""; 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);
}
}
} }
protected abstract int getDefaultIntensity(); protected abstract int getDefaultIntensity();

View File

@@ -21,8 +21,10 @@ import android.support.annotation.VisibleForTesting;
import android.content.Context; import android.content.Context;
import android.database.ContentObserver; import android.database.ContentObserver;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
import android.media.AudioAttributes;
import android.net.Uri; import android.net.Uri;
import android.os.Handler; import android.os.Handler;
import android.os.VibrationEffect;
import android.os.Vibrator; import android.os.Vibrator;
import android.provider.Settings; import android.provider.Settings;
import android.util.ArrayMap; import android.util.ArrayMap;
@@ -53,12 +55,31 @@ public abstract class VibrationPreferenceFragment extends RadioButtonPickerFragm
final static String KEY_INTENSITY_MEDIUM = "intensity_medium"; final static String KEY_INTENSITY_MEDIUM = "intensity_medium";
@VisibleForTesting @VisibleForTesting
final static String KEY_INTENSITY_HIGH = "intensity_high"; 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 Map<String, VibrationIntensityCandidateInfo> mCandidates;
private final SettingsObserver mSettingsObserver; private final SettingsObserver mSettingsObserver;
public VibrationPreferenceFragment() { public VibrationPreferenceFragment() {
mCandidates = new ArrayMap<>(); mCandidates = new ArrayMap<>();
mSettingsObserver = new SettingsObserver();
}
@Override
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, mCandidates.put(KEY_INTENSITY_OFF,
new VibrationIntensityCandidateInfo(KEY_INTENSITY_OFF, new VibrationIntensityCandidateInfo(KEY_INTENSITY_OFF,
R.string.accessibility_vibration_intensity_off, R.string.accessibility_vibration_intensity_off,
@@ -75,13 +96,14 @@ public abstract class VibrationPreferenceFragment extends RadioButtonPickerFragm
new VibrationIntensityCandidateInfo(KEY_INTENSITY_HIGH, new VibrationIntensityCandidateInfo(KEY_INTENSITY_HIGH,
R.string.accessibility_vibration_intensity_high, R.string.accessibility_vibration_intensity_high,
Vibrator.VIBRATION_INTENSITY_HIGH)); Vibrator.VIBRATION_INTENSITY_HIGH));
mSettingsObserver = new SettingsObserver(); } 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
public void onAttach(Context context) {
super.onAttach(context);
mSettingsObserver.register();
} }
@Override @Override
@@ -105,6 +127,24 @@ public abstract class VibrationPreferenceFragment extends RadioButtonPickerFragm
*/ */
protected void onVibrationIntensitySelected(int intensity) { } 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 @Override
protected List<? extends CandidateInfo> getCandidates() { protected List<? extends CandidateInfo> getCandidates() {
List<VibrationIntensityCandidateInfo> candidates = new ArrayList<>(mCandidates.values()); 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(), final int vibrationIntensity = Settings.System.getInt(getContext().getContentResolver(),
getVibrationIntensitySetting(), getDefaultVibrationIntensity()); getVibrationIntensitySetting(), getDefaultVibrationIntensity());
for (VibrationIntensityCandidateInfo candidate : mCandidates.values()) { 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(); return candidate.getKey();
} }
} }
@@ -189,6 +232,7 @@ public abstract class VibrationPreferenceFragment extends RadioButtonPickerFragm
@Override @Override
public void onChange(boolean selfChange, Uri uri) { public void onChange(boolean selfChange, Uri uri) {
updateCandidates(); updateCandidates();
playVibrationPreview();
} }
} }
} }

View File

@@ -17,8 +17,6 @@ package com.android.settings.bluetooth;
import static android.app.slice.Slice.EXTRA_TOGGLE_STATE; import static android.app.slice.Slice.EXTRA_TOGGLE_STATE;
import static androidx.slice.builders.ListBuilder.ICON_IMAGE;
import android.annotation.ColorInt; import android.annotation.ColorInt;
import android.app.PendingIntent; import android.app.PendingIntent;
import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothAdapter;
@@ -28,6 +26,7 @@ import android.content.Intent;
import android.content.IntentFilter; import android.content.IntentFilter;
import android.net.Uri; import android.net.Uri;
import android.provider.SettingsSlicesContract; import android.provider.SettingsSlicesContract;
import android.support.v4.graphics.drawable.IconCompat;
import com.android.internal.logging.nano.MetricsProto; import com.android.internal.logging.nano.MetricsProto;
import com.android.settings.R; import com.android.settings.R;
@@ -42,8 +41,6 @@ import androidx.slice.Slice;
import androidx.slice.builders.ListBuilder; import androidx.slice.builders.ListBuilder;
import androidx.slice.builders.SliceAction; import androidx.slice.builders.SliceAction;
import android.support.v4.graphics.drawable.IconCompat;
public class BluetoothSliceBuilder { public class BluetoothSliceBuilder {
private static final String TAG = "BluetoothSliceBuilder"; private static final String TAG = "BluetoothSliceBuilder";
@@ -71,7 +68,8 @@ public class BluetoothSliceBuilder {
INTENT_FILTER.addAction(BluetoothAdapter.ACTION_STATE_CHANGED); INTENT_FILTER.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
} }
private BluetoothSliceBuilder() {} private BluetoothSliceBuilder() {
}
/** /**
* Return a Bluetooth Slice bound to {@link #BLUETOOTH_URI}. * Return a Bluetooth Slice bound to {@link #BLUETOOTH_URI}.
@@ -80,7 +78,7 @@ public class BluetoothSliceBuilder {
* Bluetooth. * Bluetooth.
*/ */
public static Slice getSlice(Context context) { 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 CharSequence title = context.getText(R.string.bluetooth_settings);
final IconCompat icon = IconCompat.createWithResource(context, final IconCompat icon = IconCompat.createWithResource(context,
R.drawable.ic_settings_bluetooth); R.drawable.ic_settings_bluetooth);
@@ -115,9 +113,8 @@ public class BluetoothSliceBuilder {
// handle it. // handle it.
} }
private static boolean isBluetoothEnabled(Context context) { private static boolean isBluetoothEnabled() {
final LocalBluetoothAdapter adapter = LocalBluetoothManager.getInstance(context, final BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
null /* callback */).getBluetoothAdapter();
return adapter.isEnabled(); return adapter.isEnabled();
} }

View File

@@ -50,7 +50,7 @@ public class AdvancedConnectedDeviceController extends BasePreferenceController
*/ */
public static int getConnectedDevicesSummaryResourceId(Context context) { public static int getConnectedDevicesSummaryResourceId(Context context) {
final NfcPreferenceController nfcPreferenceController = final NfcPreferenceController nfcPreferenceController =
new NfcPreferenceController(context); new NfcPreferenceController(context, NfcPreferenceController.KEY_TOGGLE_NFC);
return getConnectedDevicesSummaryResourceId(nfcPreferenceController, return getConnectedDevicesSummaryResourceId(nfcPreferenceController,
isDrivingModeAvailable(context)); isDrivingModeAvailable(context));

View File

@@ -24,7 +24,6 @@ import com.android.settings.R;
import com.android.settings.bluetooth.BluetoothFilesPreferenceController; import com.android.settings.bluetooth.BluetoothFilesPreferenceController;
import com.android.settings.dashboard.DashboardFragment; import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.nfc.AndroidBeamPreferenceController; import com.android.settings.nfc.AndroidBeamPreferenceController;
import com.android.settings.nfc.NfcPreferenceController;
import com.android.settings.print.PrintSettingPreferenceController; import com.android.settings.print.PrintSettingPreferenceController;
import com.android.settings.search.BaseSearchIndexProvider; import com.android.settings.search.BaseSearchIndexProvider;
import com.android.settingslib.core.AbstractPreferenceController; import com.android.settingslib.core.AbstractPreferenceController;
@@ -70,25 +69,15 @@ public class AdvancedConnectedDeviceDashboardFragment extends DashboardFragment
Lifecycle lifecycle) { Lifecycle lifecycle) {
final List<AbstractPreferenceController> controllers = new ArrayList<>(); final List<AbstractPreferenceController> controllers = new ArrayList<>();
final AndroidBeamPreferenceController beamPreferenceController =
new AndroidBeamPreferenceController(context);
controllers.add(beamPreferenceController);
controllers.add(new BluetoothFilesPreferenceController(context)); controllers.add(new BluetoothFilesPreferenceController(context));
controllers.add(new BluetoothOnWhileDrivingPreferenceController(context)); controllers.add(new BluetoothOnWhileDrivingPreferenceController(context));
final PrintSettingPreferenceController printerController = final PrintSettingPreferenceController printerController =
new PrintSettingPreferenceController(context); new PrintSettingPreferenceController(context);
final NfcPreferenceController nfcPreferenceController =
new NfcPreferenceController(context);
if (lifecycle != null) { if (lifecycle != null) {
lifecycle.addObserver(beamPreferenceController);
lifecycle.addObserver(printerController); lifecycle.addObserver(printerController);
lifecycle.addObserver(nfcPreferenceController);
} }
controllers.add(nfcPreferenceController);
controllers.add(printerController); controllers.add(printerController);
return controllers; return controllers;

View File

@@ -246,6 +246,16 @@ public abstract class BasePreferenceController extends AbstractPreferenceControl
return false; return false;
} }
/**
* @return {@code true} if the setting update asynchronously.
* <p>
* For example, a Wifi controller would return true, because it needs to update the radio
* and wait for it to turn on.
*/
public boolean hasAsyncUpdate() {
return false;
}
/** /**
* Updates non-indexable keys for search provider. * Updates non-indexable keys for search provider.
* *

View File

@@ -57,14 +57,12 @@ import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
public class StorageDashboardFragment extends DashboardFragment public class StorageDashboardFragment extends DashboardFragment implements
implements
LoaderManager.LoaderCallbacks<SparseArray<StorageAsyncLoader.AppsStorageResult>> { LoaderManager.LoaderCallbacks<SparseArray<StorageAsyncLoader.AppsStorageResult>> {
private static final String TAG = "StorageDashboardFrag"; private static final String TAG = "StorageDashboardFrag";
private static final int STORAGE_JOB_ID = 0; private static final int STORAGE_JOB_ID = 0;
private static final int ICON_JOB_ID = 1; private static final int ICON_JOB_ID = 1;
private static final int VOLUME_SIZE_JOB_ID = 2; private static final int VOLUME_SIZE_JOB_ID = 2;
private static final int OPTIONS_MENU_MIGRATE_DATA = 100;
private VolumeInfo mVolume; private VolumeInfo mVolume;
private PrivateStorageInfo mStorageInfo; private PrivateStorageInfo mStorageInfo;

View File

@@ -21,6 +21,7 @@ import android.content.Context;
import android.provider.Settings; import android.provider.Settings;
import android.support.v14.preference.SwitchPreference; import android.support.v14.preference.SwitchPreference;
import android.support.v7.preference.Preference; import android.support.v7.preference.Preference;
import android.text.TextUtils;
import com.android.settings.core.BasePreferenceController; import com.android.settings.core.BasePreferenceController;
import com.android.settings.overlay.FeatureFactory; import com.android.settings.overlay.FeatureFactory;
@@ -48,6 +49,11 @@ public class SmartBatteryPreferenceController extends BasePreferenceController i
: UNSUPPORTED_ON_DEVICE; : UNSUPPORTED_ON_DEVICE;
} }
@Override
public boolean isSliceable() {
return TextUtils.equals(getPreferenceKey(), "smart_battery");
}
@Override @Override
public void updateState(Preference preference) { public void updateState(Preference preference) {
super.updateState(preference); super.updateState(preference);

View File

@@ -20,7 +20,6 @@ import android.content.Context;
import android.nfc.NfcAdapter; import android.nfc.NfcAdapter;
import android.os.UserHandle; import android.os.UserHandle;
import android.os.UserManager; import android.os.UserManager;
import android.support.v7.preference.Preference;
import com.android.settings.R; import com.android.settings.R;
import com.android.settingslib.RestrictedLockUtils; import com.android.settingslib.RestrictedLockUtils;
@@ -36,18 +35,14 @@ public class AndroidBeamEnabler extends BaseNfcEnabler {
public AndroidBeamEnabler(Context context, RestrictedPreference preference) { public AndroidBeamEnabler(Context context, RestrictedPreference preference) {
super(context); super(context);
mPreference = preference; mPreference = preference;
mBeamDisallowedBySystem = RestrictedLockUtils.hasBaseUserRestriction(context, mBeamDisallowedBySystem = RestrictedLockUtils.hasBaseUserRestriction(context,
UserManager.DISALLOW_OUTGOING_BEAM, UserHandle.myUserId()); UserManager.DISALLOW_OUTGOING_BEAM, UserHandle.myUserId());
if (!isNfcAvailable()) { if (!isNfcAvailable()) {
// NFC is not supported // NFC is not supported
mPreference.setEnabled(false); mPreference.setEnabled(false);
return; return;
} }
if (mBeamDisallowedBySystem) { if (mBeamDisallowedBySystem) {
mPreference.setEnabled(false); mPreference.setEnabled(false);
} }

View File

@@ -16,34 +16,75 @@
package com.android.settings.nfc; package com.android.settings.nfc;
import android.content.Context; import android.content.Context;
import android.nfc.NfcAdapter;
import android.support.v7.preference.Preference;
import android.support.v7.preference.PreferenceScreen; import android.support.v7.preference.PreferenceScreen;
import com.android.settings.core.PreferenceControllerMixin; import com.android.settings.core.BasePreferenceController;
import com.android.settingslib.RestrictedPreference; import com.android.settingslib.RestrictedPreference;
import com.android.settingslib.core.lifecycle.LifecycleObserver; import com.android.settingslib.core.lifecycle.LifecycleObserver;
import com.android.settingslib.core.lifecycle.events.OnPause; import com.android.settingslib.core.lifecycle.events.OnPause;
import com.android.settingslib.core.lifecycle.events.OnResume; import com.android.settingslib.core.lifecycle.events.OnResume;
public class AndroidBeamPreferenceController extends BaseNfcPreferenceController { import java.util.List;
public class AndroidBeamPreferenceController extends BasePreferenceController
implements LifecycleObserver, OnResume, OnPause {
public static final String KEY_ANDROID_BEAM_SETTINGS = "android_beam_settings"; public static final String KEY_ANDROID_BEAM_SETTINGS = "android_beam_settings";
private final NfcAdapter mNfcAdapter;
private AndroidBeamEnabler mAndroidBeamEnabler;
private NfcAirplaneModeObserver mAirplaneModeObserver;
public AndroidBeamPreferenceController(Context context) { public AndroidBeamPreferenceController(Context context, String key) {
super(context); super(context, key);
mNfcAdapter = NfcAdapter.getDefaultAdapter(context);
} }
@Override @Override
public void displayPreference(PreferenceScreen screen) { public void displayPreference(PreferenceScreen screen) {
super.displayPreference(screen); super.displayPreference(screen);
if (!isAvailable()) { if (!isAvailable()) {
mAndroidBeamEnabler = null;
return; return;
} }
mNfcEnabler = new AndroidBeamEnabler(mContext, (RestrictedPreference) mPreference); final RestrictedPreference restrictedPreference =
(RestrictedPreference) screen.findPreference(getPreferenceKey());
mAndroidBeamEnabler = new AndroidBeamEnabler(mContext, restrictedPreference);
// Manually set dependencies for NFC when not toggleable.
if (!NfcPreferenceController.isToggleableInAirplaneMode(mContext)) {
mAirplaneModeObserver = new NfcAirplaneModeObserver(mContext, mNfcAdapter,
(Preference) restrictedPreference);
}
} }
@Override @Override
public String getPreferenceKey() { @AvailabilityStatus
return KEY_ANDROID_BEAM_SETTINGS; public int getAvailabilityStatus() {
return mNfcAdapter != null
? AVAILABLE
: UNSUPPORTED_ON_DEVICE;
}
@Override
public void onResume() {
if (mAirplaneModeObserver != null) {
mAirplaneModeObserver.register();
}
if (mAndroidBeamEnabler != null) {
mAndroidBeamEnabler.resume();
}
}
@Override
public void onPause() {
if (mAirplaneModeObserver != null) {
mAirplaneModeObserver.unregister();
}
if (mAndroidBeamEnabler != null) {
mAndroidBeamEnabler.pause();
}
} }
} }

View File

@@ -1,147 +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.nfc;
import android.content.Context;
import android.database.ContentObserver;
import android.net.Uri;
import android.nfc.NfcAdapter;
import android.nfc.NfcManager;
import android.os.Handler;
import android.provider.Settings;
import android.support.v7.preference.Preference;
import android.support.v7.preference.PreferenceScreen;
import com.android.settings.core.PreferenceControllerMixin;
import com.android.settingslib.core.AbstractPreferenceController;
import com.android.settingslib.core.lifecycle.LifecycleObserver;
import com.android.settingslib.core.lifecycle.events.OnPause;
import com.android.settingslib.core.lifecycle.events.OnResume;
import java.util.List;
public abstract class BaseNfcPreferenceController extends AbstractPreferenceController
implements PreferenceControllerMixin, LifecycleObserver, OnResume, OnPause {
protected BaseNfcEnabler mNfcEnabler;
private NfcAdapter mNfcAdapter;
private int mAirplaneMode;
private AirplaneModeObserver mAirplaneModeObserver;
protected Preference mPreference;
public BaseNfcPreferenceController(Context context) {
super(context);
mNfcAdapter = NfcAdapter.getDefaultAdapter(context);
}
@Override
public void displayPreference(PreferenceScreen screen) {
super.displayPreference(screen);
if (!isAvailable()) {
mNfcEnabler = null;
return;
}
mPreference = screen.findPreference(getPreferenceKey());
// Manually set dependencies for NFC when not toggleable.
if (!isToggleableInAirplaneMode(mContext)) {
mAirplaneModeObserver = new AirplaneModeObserver();
updateNfcPreference();
}
}
@Override
public void updateNonIndexableKeys(List<String> keys) {
if (!isAvailable()) {
keys.add(getPreferenceKey());
}
}
@Override
public boolean isAvailable() {
return mNfcAdapter != null;
}
public abstract String getPreferenceKey();
@Override
public void onResume() {
if (mAirplaneModeObserver != null) {
mAirplaneModeObserver.register();
}
if (mNfcEnabler != null) {
mNfcEnabler.resume();
}
}
@Override
public void onPause() {
if (mAirplaneModeObserver != null) {
mAirplaneModeObserver.unregister();
}
if (mNfcEnabler != null) {
mNfcEnabler.pause();
}
}
private void updateNfcPreference() {
final int airplaneMode = Settings.Global.getInt(
mContext.getContentResolver(), Settings.Global.AIRPLANE_MODE_ON, mAirplaneMode);
if (airplaneMode == mAirplaneMode) {
return;
}
mAirplaneMode = airplaneMode;
boolean toggleable = mAirplaneMode != 1;
if (toggleable) {
mNfcAdapter.enable();
} else {
mNfcAdapter.disable();
}
mPreference.setEnabled(toggleable);
}
public static boolean isToggleableInAirplaneMode(Context context) {
String toggleable = Settings.Global.getString(context.getContentResolver(),
Settings.Global.AIRPLANE_MODE_TOGGLEABLE_RADIOS);
return toggleable != null && toggleable.contains(Settings.Global.RADIO_NFC);
}
private final class AirplaneModeObserver extends ContentObserver {
private final Uri AIRPLANE_MODE_URI =
Settings.Global.getUriFor(Settings.Global.AIRPLANE_MODE_ON);
private AirplaneModeObserver() {
super(new Handler());
}
public void register() {
mContext.getContentResolver().registerContentObserver(AIRPLANE_MODE_URI, false, this);
}
public void unregister() {
mContext.getContentResolver().unregisterContentObserver(this);
}
@Override
public void onChange(boolean selfChange, Uri uri) {
super.onChange(selfChange, uri);
updateNfcPreference();
}
}
}

View File

@@ -0,0 +1,81 @@
/*
* 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.nfc;
import android.content.Context;
import android.database.ContentObserver;
import android.net.Uri;
import android.nfc.NfcAdapter;
import android.os.Handler;
import android.os.Looper;
import android.provider.Settings;
import android.support.annotation.VisibleForTesting;
import android.support.v7.preference.Preference;
/**
* NfcAirplaneModeObserver is a helper to manage the Nfc on/off when airplane mode status
* is changed.
*/
public class NfcAirplaneModeObserver extends ContentObserver {
private final Context mContext;
private final NfcAdapter mNfcAdapter;
private final Preference mPreference;
private int mAirplaneMode;
@VisibleForTesting
final static Uri AIRPLANE_MODE_URI =
Settings.Global.getUriFor(Settings.Global.AIRPLANE_MODE_ON);
public NfcAirplaneModeObserver(Context context, NfcAdapter nfcAdapter, Preference preference) {
super(new Handler(Looper.getMainLooper()));
mContext = context;
mNfcAdapter = nfcAdapter;
mPreference = preference;
updateNfcPreference();
}
public void register() {
mContext.getContentResolver().registerContentObserver(AIRPLANE_MODE_URI, false, this);
}
public void unregister() {
mContext.getContentResolver().unregisterContentObserver(this);
}
@Override
public void onChange(boolean selfChange, Uri uri) {
super.onChange(selfChange, uri);
updateNfcPreference();
}
private void updateNfcPreference() {
final int airplaneMode = Settings.Global.getInt(
mContext.getContentResolver(), Settings.Global.AIRPLANE_MODE_ON, mAirplaneMode);
if (airplaneMode == mAirplaneMode) {
return;
}
mAirplaneMode = airplaneMode;
boolean toggleable = mAirplaneMode != 1;
if (toggleable) {
mNfcAdapter.enable();
} else {
mNfcAdapter.disable();
}
mPreference.setEnabled(toggleable);
}
}

View File

@@ -18,52 +18,20 @@ package com.android.settings.nfc;
import android.content.Context; import android.content.Context;
import android.nfc.NfcAdapter; import android.nfc.NfcAdapter;
import android.support.v7.preference.Preference;
import android.support.v14.preference.SwitchPreference; import android.support.v14.preference.SwitchPreference;
/** /**
* NfcEnabler is a helper to manage the Nfc on/off checkbox preference. It turns on/off Nfc * NfcEnabler is a helper to manage the Nfc on/off checkbox preference. It turns on/off Nfc
* and ensures the summary of the preference reflects the current state. * and ensures the summary of the preference reflects the current state.
*/ */
public class NfcEnabler extends BaseNfcEnabler implements Preference.OnPreferenceChangeListener { public class NfcEnabler extends BaseNfcEnabler {
private final SwitchPreference mPreference; private final SwitchPreference mPreference;
public NfcEnabler(Context context, SwitchPreference preference) { public NfcEnabler(Context context, SwitchPreference preference) {
super(context); super(context);
mPreference = preference; mPreference = preference;
} }
public void resume() {
super.resume();
if (isNfcAvailable()) {
mPreference.setOnPreferenceChangeListener(this);
}
}
public void pause() {
super.pause();
if (isNfcAvailable()) {
mPreference.setOnPreferenceChangeListener(null);
}
}
public boolean onPreferenceChange(Preference preference, Object value) {
// Turn NFC on/off
final boolean desiredState = (Boolean) value;
mPreference.setChecked(desiredState);
mPreference.setEnabled(false);
if (desiredState) {
mNfcAdapter.enable();
} else {
mNfcAdapter.disable();
}
return false;
}
@Override @Override
protected void handleNfcStateChanged(int newState) { protected void handleNfcStateChanged(int newState) {
switch (newState) { switch (newState) {

View File

@@ -16,35 +16,119 @@
package com.android.settings.nfc; package com.android.settings.nfc;
import android.content.Context; import android.content.Context;
import android.content.IntentFilter;
import android.nfc.NfcAdapter;
import android.provider.Settings;
import android.text.TextUtils;
import android.support.v7.preference.Preference;
import android.support.v7.preference.PreferenceScreen; import android.support.v7.preference.PreferenceScreen;
import android.support.v14.preference.SwitchPreference; import android.support.v14.preference.SwitchPreference;
import com.android.settings.core.PreferenceControllerMixin; import com.android.settings.core.TogglePreferenceController;
import com.android.settingslib.core.lifecycle.LifecycleObserver; import com.android.settingslib.core.lifecycle.LifecycleObserver;
import com.android.settingslib.core.lifecycle.events.OnPause; import com.android.settingslib.core.lifecycle.events.OnPause;
import com.android.settingslib.core.lifecycle.events.OnResume; import com.android.settingslib.core.lifecycle.events.OnResume;
import java.util.List;
public class NfcPreferenceController extends BaseNfcPreferenceController { public class NfcPreferenceController extends TogglePreferenceController
implements LifecycleObserver, OnResume, OnPause {
public static final String KEY_TOGGLE_NFC = "toggle_nfc"; public static final String KEY_TOGGLE_NFC = "toggle_nfc";
private final NfcAdapter mNfcAdapter;
private NfcEnabler mNfcEnabler;
private NfcAirplaneModeObserver mAirplaneModeObserver;
public NfcPreferenceController(Context context) { public NfcPreferenceController(Context context, String key) {
super(context); super(context, key);
mNfcAdapter = NfcAdapter.getDefaultAdapter(context);
} }
@Override @Override
public void displayPreference(PreferenceScreen screen) { public void displayPreference(PreferenceScreen screen) {
super.displayPreference(screen); super.displayPreference(screen);
if (!isAvailable()) { if (!isAvailable()) {
mNfcEnabler = null;
return; return;
} }
mNfcEnabler = new NfcEnabler(mContext, (SwitchPreference) mPreference); final SwitchPreference switchPreference =
(SwitchPreference) screen.findPreference(getPreferenceKey());
mNfcEnabler = new NfcEnabler(mContext, switchPreference);
// Manually set dependencies for NFC when not toggleable.
if (!isToggleableInAirplaneMode(mContext)) {
mAirplaneModeObserver = new NfcAirplaneModeObserver(mContext,
mNfcAdapter, (Preference) switchPreference);
}
} }
@Override @Override
public String getPreferenceKey() { public boolean isChecked() {
return KEY_TOGGLE_NFC; return mNfcAdapter.isEnabled();
}
@Override
public boolean setChecked(boolean isChecked) {
if (isChecked) {
mNfcAdapter.enable();
} else {
mNfcAdapter.disable();
}
return true;
}
@Override
@AvailabilityStatus
public int getAvailabilityStatus() {
return mNfcAdapter != null
? AVAILABLE
: UNSUPPORTED_ON_DEVICE;
}
@Override
public IntentFilter getIntentFilter() {
final IntentFilter filter = new IntentFilter();
filter.addAction(NfcAdapter.ACTION_ADAPTER_STATE_CHANGED);
filter.addAction(NfcAdapter.EXTRA_ADAPTER_STATE);
return filter;
}
@Override
public boolean hasAsyncUpdate() {
return true;
}
@Override
public boolean isSliceable() {
return TextUtils.equals(getPreferenceKey(), KEY_TOGGLE_NFC);
}
@Override
public void onResume() {
if (mAirplaneModeObserver != null) {
mAirplaneModeObserver.register();
}
if (mNfcEnabler != null) {
mNfcEnabler.resume();
}
}
@Override
public void onPause() {
if (mAirplaneModeObserver != null) {
mAirplaneModeObserver.unregister();
}
if (mNfcEnabler != null) {
mNfcEnabler.pause();
}
}
public static boolean isToggleableInAirplaneMode(Context context) {
final String toggleable = Settings.Global.getString(context.getContentResolver(),
Settings.Global.AIRPLANE_MODE_TOGGLEABLE_RADIOS);
return toggleable != null && toggleable.contains(Settings.Global.RADIO_NFC);
} }
} }

View File

@@ -18,6 +18,7 @@ package com.android.settings.notification;
import android.content.Context; import android.content.Context;
import android.media.AudioManager; import android.media.AudioManager;
import android.text.TextUtils;
import com.android.settings.R; import com.android.settings.R;
@@ -36,6 +37,11 @@ public class AlarmVolumePreferenceController extends
&& !mHelper.isSingleVolume() ? AVAILABLE : UNSUPPORTED_ON_DEVICE; && !mHelper.isSingleVolume() ? AVAILABLE : UNSUPPORTED_ON_DEVICE;
} }
@Override
public boolean isSliceable() {
return TextUtils.equals(getPreferenceKey(), "alarm_volume");
}
@Override @Override
public String getPreferenceKey() { public String getPreferenceKey() {
return KEY_ALARM_VOLUME; return KEY_ALARM_VOLUME;

View File

@@ -23,23 +23,23 @@ import android.content.ContentResolver;
import android.content.Intent; import android.content.Intent;
import android.content.IntentFilter; import android.content.IntentFilter;
import android.net.Uri; import android.net.Uri;
import android.os.StrictMode;
import android.provider.Settings; import android.provider.Settings;
import android.provider.SettingsSlicesContract; import android.provider.SettingsSlicesContract;
import android.support.annotation.VisibleForTesting; import android.support.annotation.VisibleForTesting;
import android.support.v4.graphics.drawable.IconCompat;
import android.text.TextUtils; import android.text.TextUtils;
import android.util.ArraySet; import android.util.ArraySet;
import android.util.KeyValueListParser; import android.util.KeyValueListParser;
import android.util.Log; import android.util.Log;
import android.util.Pair; import android.util.Pair;
import com.android.settings.location.LocationSliceBuilder; import com.android.settings.bluetooth.BluetoothSliceBuilder;
import com.android.settings.overlay.FeatureFactory;
import com.android.settings.core.BasePreferenceController; 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.WifiSliceBuilder;
import com.android.settings.wifi.calling.WifiCallingSliceHelper; 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.SliceBroadcastRelay;
import com.android.settingslib.utils.ThreadUtils; import com.android.settingslib.utils.ThreadUtils;
@@ -150,7 +150,7 @@ public class SettingsSliceProvider extends SliceProvider {
@Override @Override
public void onSlicePinned(Uri sliceUri) { public void onSlicePinned(Uri sliceUri) {
if (WifiSliceBuilder.WIFI_URI.equals(sliceUri)) { if (WifiSliceBuilder.WIFI_URI.equals(sliceUri)) {
registerIntentToUri(WifiSliceBuilder.INTENT_FILTER , sliceUri); registerIntentToUri(WifiSliceBuilder.INTENT_FILTER, sliceUri);
return; return;
} else if (ZenModeSliceBuilder.ZEN_MODE_URI.equals(sliceUri)) { } else if (ZenModeSliceBuilder.ZEN_MODE_URI.equals(sliceUri)) {
registerIntentToUri(ZenModeSliceBuilder.INTENT_FILTER, sliceUri); registerIntentToUri(ZenModeSliceBuilder.INTENT_FILTER, sliceUri);
@@ -176,6 +176,13 @@ public class SettingsSliceProvider extends SliceProvider {
@Override @Override
public Slice onBindSlice(Uri sliceUri) { public Slice onBindSlice(Uri sliceUri) {
final StrictMode.ThreadPolicy oldPolicy = StrictMode.getThreadPolicy();
try {
if (!ThreadUtils.isMainThread()) {
StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
.permitAll()
.build());
}
final Set<String> blockedKeys = getBlockedKeys(); final Set<String> blockedKeys = getBlockedKeys();
final String key = sliceUri.getLastPathSegment(); final String key = sliceUri.getLastPathSegment();
if (blockedKeys.contains(key)) { if (blockedKeys.contains(key)) {
@@ -211,6 +218,9 @@ public class SettingsSliceProvider extends SliceProvider {
mSliceWeakDataCache.remove(sliceUri); mSliceWeakDataCache.remove(sliceUri);
} }
return SliceBuilderUtils.buildSlice(getContext(), cachedSliceData); return SliceBuilderUtils.buildSlice(getContext(), cachedSliceData);
} finally {
StrictMode.setThreadPolicy(oldPolicy);
}
} }
/** /**

View File

@@ -27,6 +27,7 @@ import static com.android.settings.wifi.WifiSliceBuilder.ACTION_WIFI_SLICE_CHANG
import android.app.slice.Slice; import android.app.slice.Slice;
import android.content.BroadcastReceiver; import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.net.Uri; import android.net.Uri;
@@ -106,7 +107,9 @@ public class SliceBroadcastReceiver extends BroadcastReceiver {
if (!controller.isAvailable()) { if (!controller.isAvailable()) {
Log.w(TAG, "Can't update " + key + " since the setting is unavailable"); Log.w(TAG, "Can't update " + key + " since the setting is unavailable");
if (!controller.hasAsyncUpdate()) {
updateUri(context, key, isPlatformSlice); updateUri(context, key, isPlatformSlice);
}
return; return;
} }
@@ -115,8 +118,10 @@ public class SliceBroadcastReceiver extends BroadcastReceiver {
final TogglePreferenceController toggleController = (TogglePreferenceController) controller; final TogglePreferenceController toggleController = (TogglePreferenceController) controller;
toggleController.setChecked(isChecked); toggleController.setChecked(isChecked);
logSliceValueChange(context, key, isChecked ? 1 : 0); logSliceValueChange(context, key, isChecked ? 1 : 0);
if (!controller.hasAsyncUpdate()) {
updateUri(context, key, isPlatformSlice); updateUri(context, key, isPlatformSlice);
} }
}
private void handleSliderAction(Context context, String key, int newPosition, private void handleSliderAction(Context context, String key, int newPosition,
boolean isPlatformSlice) { boolean isPlatformSlice) {
@@ -151,6 +156,7 @@ public class SliceBroadcastReceiver extends BroadcastReceiver {
sliderController.setSliderPosition(newPosition); sliderController.setSliderPosition(newPosition);
logSliceValueChange(context, key, newPosition); logSliceValueChange(context, key, newPosition);
updateUri(context, key, isPlatformSlice);
} }
/** /**
@@ -173,8 +179,15 @@ public class SliceBroadcastReceiver extends BroadcastReceiver {
} }
private void updateUri(Context context, String key, boolean isPlatformDefined) { private void updateUri(Context context, String key, boolean isPlatformDefined) {
final String path = SettingsSlicesContract.PATH_SETTING_ACTION + "/" + key; final String authority = isPlatformDefined
final Uri uri = SliceBuilderUtils.getUri(path, 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 */); context.getContentResolver().notifyChange(uri, null /* observer */);
} }
} }

View File

@@ -114,4 +114,19 @@ public class MagnificationGesturesPreferenceControllerTest {
Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED, -1)) Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED, -1))
.isEqualTo(OFF); .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; 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 android.provider.Settings.System.NOTIFICATION_VIBRATION_INTENSITY;
import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
import android.arch.lifecycle.LifecycleOwner; import android.arch.lifecycle.LifecycleOwner;
import android.content.Context; import android.content.Context;
import android.content.res.Resources;
import android.os.Vibrator; import android.os.Vibrator;
import android.provider.Settings; import android.provider.Settings;
import android.support.v7.preference.Preference; import android.support.v7.preference.Preference;
@@ -48,6 +50,7 @@ public class NotificationVibrationIntensityPreferenceControllerTest {
private LifecycleOwner mLifecycleOwner; private LifecycleOwner mLifecycleOwner;
private Lifecycle mLifecycle; private Lifecycle mLifecycle;
private Context mContext; private Context mContext;
private Resources mResources;
private NotificationVibrationIntensityPreferenceController mController; private NotificationVibrationIntensityPreferenceController mController;
private Preference mPreference; private Preference mPreference;
@@ -56,7 +59,11 @@ public class NotificationVibrationIntensityPreferenceControllerTest {
MockitoAnnotations.initMocks(this); MockitoAnnotations.initMocks(this);
mLifecycleOwner = () -> mLifecycle; mLifecycleOwner = () -> mLifecycle;
mLifecycle = new Lifecycle(mLifecycleOwner); 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) { mController = new NotificationVibrationIntensityPreferenceController(mContext) {
@Override @Override
protected int getDefaultIntensity() { protected int getDefaultIntensity() {
@@ -68,7 +75,6 @@ public class NotificationVibrationIntensityPreferenceControllerTest {
mPreference.setSummary("Test"); mPreference.setSummary("Test");
when(mScreen.findPreference(mController.getPreferenceKey())) when(mScreen.findPreference(mController.getPreferenceKey()))
.thenReturn(mPreference); .thenReturn(mPreference);
mController.displayPreference(mScreen);
} }
@Test @Test
@@ -80,7 +86,10 @@ public class NotificationVibrationIntensityPreferenceControllerTest {
} }
@Test @Test
public void updateState_shouldRefreshSummary() { public void updateState_withMultipleIntensitySuport_shouldRefreshSummary() {
setSupportsMultipleIntensities(true);
showPreference();
Settings.System.putInt(mContext.getContentResolver(), Settings.System.putInt(mContext.getContentResolver(),
NOTIFICATION_VIBRATION_INTENSITY, Vibrator.VIBRATION_INTENSITY_LOW); NOTIFICATION_VIBRATION_INTENSITY, Vibrator.VIBRATION_INTENSITY_LOW);
mController.updateState(mPreference); mController.updateState(mPreference);
@@ -105,4 +114,43 @@ public class NotificationVibrationIntensityPreferenceControllerTest {
assertThat(mPreference.getSummary()) assertThat(mPreference.getSummary())
.isEqualTo(mContext.getString(R.string.accessibility_vibration_intensity_off)); .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_LOW;
import static com.android.settings.accessibility.VibrationPreferenceFragment.KEY_INTENSITY_MEDIUM; 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_OFF;
import static com.android.settings.accessibility.VibrationPreferenceFragment.KEY_INTENSITY_ON;
import static com.google.common.truth.Truth.assertThat; 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.spy;
import static org.mockito.Mockito.when;
import android.app.Activity; import android.app.Activity;
import android.content.Context; import android.content.Context;
import android.content.res.Resources;
import android.os.UserManager; import android.os.UserManager;
import android.os.Vibrator; import android.os.Vibrator;
import android.provider.Settings; import android.provider.Settings;
import com.android.settings.R;
import com.android.settings.accessibility.VibrationPreferenceFragment.VibrationIntensityCandidateInfo; import com.android.settings.accessibility.VibrationPreferenceFragment.VibrationIntensityCandidateInfo;
import com.android.settings.testutils.FakeFeatureFactory; import com.android.settings.testutils.FakeFeatureFactory;
import com.android.settings.testutils.SettingsRobolectricTestRunner; import com.android.settings.testutils.SettingsRobolectricTestRunner;
@@ -58,12 +61,11 @@ public class VibrationPreferenceFragmentTest {
INTENSITY_TO_KEY.put(Vibrator.VIBRATION_INTENSITY_HIGH, KEY_INTENSITY_HIGH); INTENSITY_TO_KEY.put(Vibrator.VIBRATION_INTENSITY_HIGH, KEY_INTENSITY_HIGH);
} }
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
private Activity mActivity;
@Mock @Mock
private UserManager mUserManager; private UserManager mUserManager;
private Context mContext; private Context mContext;
private Resources mResources;
private TestVibrationPreferenceFragment mFragment; private TestVibrationPreferenceFragment mFragment;
@Before @Before
@@ -71,16 +73,18 @@ public class VibrationPreferenceFragmentTest {
MockitoAnnotations.initMocks(this); MockitoAnnotations.initMocks(this);
FakeFeatureFactory.setupForTest(); FakeFeatureFactory.setupForTest();
mContext = RuntimeEnvironment.application; mContext = spy(RuntimeEnvironment.application);
mResources = spy(mContext.getResources());
when(mContext.getResources()).thenReturn(mResources);
mFragment = spy(new TestVibrationPreferenceFragment()); mFragment = spy(new TestVibrationPreferenceFragment());
doReturn(mUserManager).when(mActivity).getSystemService(Context.USER_SERVICE); when(mContext.getSystemService(Context.USER_SERVICE)).thenReturn(mUserManager);
doReturn(mContext).when(mFragment).getContext();
mFragment.onAttach(mActivity);
} }
@Test @Test
public void changeIntensitySetting_shouldResultInCorrespondingKey() { public void changeIntensitySetting_shouldResultInCorrespondingKey() {
setSupportsMultipleIntensities(true);
mFragment.onAttach(mContext);
for (Map.Entry<Integer, String> entry : INTENSITY_TO_KEY.entrySet()) { for (Map.Entry<Integer, String> entry : INTENSITY_TO_KEY.entrySet()) {
Settings.System.putInt(mContext.getContentResolver(), Settings.System.putInt(mContext.getContentResolver(),
Settings.System.HAPTIC_FEEDBACK_INTENSITY, entry.getKey()); 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 @Test
public void initialDefaultKey_shouldBeMedium() { public void initialDefaultKey_shouldBeMedium() {
setSupportsMultipleIntensities(true);
mFragment.onAttach(mContext);
assertThat(mFragment.getDefaultKey()).isEqualTo(KEY_INTENSITY_MEDIUM); 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 @Test
public void candidates_shouldBeSortedByIntensity() { public void candidates_shouldBeSortedByIntensity() {
setSupportsMultipleIntensities(true);
mFragment.onAttach(mContext);
final List<? extends CandidateInfo> candidates = mFragment.getCandidates(); final List<? extends CandidateInfo> candidates = mFragment.getCandidates();
assertThat(candidates.size()).isEqualTo(INTENSITY_TO_KEY.size()); assertThat(candidates.size()).isEqualTo(INTENSITY_TO_KEY.size());
VibrationIntensityCandidateInfo prevCandidate = 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 { private class TestVibrationPreferenceFragment extends VibrationPreferenceFragment {
@Override @Override
protected int getPreferenceScreenResId() { protected int getPreferenceScreenResId() {
@@ -129,5 +163,10 @@ public class VibrationPreferenceFragmentTest {
protected int getDefaultVibrationIntensity() { protected int getDefaultVibrationIntensity() {
return Vibrator.VIBRATION_INTENSITY_MEDIUM; return Vibrator.VIBRATION_INTENSITY_MEDIUM;
} }
@Override
public Context getContext() {
return mContext;
}
} }
} }

View File

@@ -55,7 +55,8 @@ public class AdvancedConnectedDeviceControllerTest {
mContext = spy(RuntimeEnvironment.application); mContext = spy(RuntimeEnvironment.application);
mContentResolver = mContext.getContentResolver(); mContentResolver = mContext.getContentResolver();
mNfcController = new NfcPreferenceController(mContext); mNfcController = new NfcPreferenceController(mContext,
NfcPreferenceController.KEY_TOGGLE_NFC);
mShadowNfcAdapter = shadowOf(ShadowNfcAdapter.getNfcAdapter(mContext)); mShadowNfcAdapter = shadowOf(ShadowNfcAdapter.getNfcAdapter(mContext));
} }

View File

@@ -134,6 +134,11 @@ public class BasePreferenceControllerTest {
assertThat(mPreferenceController.getSliceType()).isEqualTo(SliceData.SliceType.INTENT); assertThat(mPreferenceController.getSliceType()).isEqualTo(SliceData.SliceType.INTENT);
} }
@Test
public void hasAsyncUpdate_shouldReturnFalse() {
assertThat(mPreferenceController.hasAsyncUpdate()).isFalse();
}
@Test @Test
public void settingAvailable_disabledOnDisplayPreference_preferenceEnabled() { public void settingAvailable_disabledOnDisplayPreference_preferenceEnabled() {
final PreferenceScreen screen = mock(PreferenceScreen.class); final PreferenceScreen screen = mock(PreferenceScreen.class);

View File

@@ -115,4 +115,11 @@ public class SmartBatteryPreferenceControllerTest {
return Settings.Global.getInt(mContentResolver, return Settings.Global.getInt(mContentResolver,
Settings.Global.ADAPTIVE_BATTERY_MANAGEMENT_ENABLED, ON); Settings.Global.ADAPTIVE_BATTERY_MANAGEMENT_ENABLED, ON);
} }
@Test
public void isSliceableCorrectKey_returnsTrue() {
final SmartBatteryPreferenceController controller =
new SmartBatteryPreferenceController(mContext);
assertThat(controller.isSliceable()).isTrue();
}
} }

View File

@@ -72,7 +72,8 @@ public class AndroidBeamPreferenceControllerTest {
UserManager.DISALLOW_OUTGOING_BEAM, UserHandle.myUserId())).thenReturn(false); UserManager.DISALLOW_OUTGOING_BEAM, UserHandle.myUserId())).thenReturn(false);
when(NfcAdapter.getDefaultAdapter(mContext)).thenReturn(mNfcAdapter); when(NfcAdapter.getDefaultAdapter(mContext)).thenReturn(mNfcAdapter);
mAndroidBeamController = new AndroidBeamPreferenceController(mContext); mAndroidBeamController = new AndroidBeamPreferenceController(mContext,
AndroidBeamPreferenceController.KEY_ANDROID_BEAM_SETTINGS);
mAndroidBeamPreference = new RestrictedPreference(RuntimeEnvironment.application); mAndroidBeamPreference = new RestrictedPreference(RuntimeEnvironment.application);
when(mScreen.findPreference(mAndroidBeamController.getPreferenceKey())).thenReturn( when(mScreen.findPreference(mAndroidBeamController.getPreferenceKey())).thenReturn(
mAndroidBeamPreference); mAndroidBeamPreference);

View File

@@ -0,0 +1,85 @@
/*
* 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.nfc;
import static com.google.common.truth.Truth.assertThat;
import android.content.Context;
import android.nfc.NfcAdapter;
import android.provider.Settings;
import android.support.v7.preference.Preference;
import android.support.v14.preference.SwitchPreference;
import com.android.settings.testutils.SettingsRobolectricTestRunner;
import com.android.settings.testutils.shadow.ShadowNfcAdapter;
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.shadows.ShadowApplication;
import org.robolectric.util.ReflectionHelpers;
@RunWith(SettingsRobolectricTestRunner.class)
@Config(shadows = {ShadowNfcAdapter.class})
public class NfcAirplaneModeObserverTest {
Context mContext;
private NfcAdapter mNfcAdapter;
private SwitchPreference mNfcPreference;
private NfcAirplaneModeObserver mNfcAirplaneModeObserver;
@Before
public void setUp() {
mContext = ShadowApplication.getInstance().getApplicationContext();
mNfcAdapter = NfcAdapter.getDefaultAdapter(mContext);
mNfcPreference = new SwitchPreference(RuntimeEnvironment.application);
mNfcAirplaneModeObserver = new NfcAirplaneModeObserver(mContext, mNfcAdapter,
(Preference) mNfcPreference);
}
@Test
public void NfcAirplaneModeObserver_airplaneOn_shouldDisableNfc() {
ReflectionHelpers.setField(mNfcAirplaneModeObserver,
"mAirplaneMode", 0);
Settings.Global.putInt(mContext.getContentResolver(),
Settings.Global.AIRPLANE_MODE_ON, 1);
mNfcAirplaneModeObserver.onChange(false,
NfcAirplaneModeObserver.AIRPLANE_MODE_URI);
assertThat(mNfcAdapter.isEnabled()).isFalse();
assertThat(mNfcPreference.isEnabled()).isFalse();
}
@Test
public void NfcAirplaneModeObserver_airplaneOff_shouldEnableNfc() {
ReflectionHelpers.setField(mNfcAirplaneModeObserver,
"mAirplaneMode", 1);
Settings.Global.putInt(mContext.getContentResolver(),
Settings.Global.AIRPLANE_MODE_ON, 0);
mNfcAirplaneModeObserver.onChange(false,
NfcAirplaneModeObserver.AIRPLANE_MODE_URI);
assertThat(mNfcAdapter.isEnabled()).isTrue();
assertThat(mNfcPreference.isEnabled()).isTrue();
}
}

View File

@@ -19,6 +19,7 @@ package com.android.settings.nfc;
import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.spy; import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
import android.content.Context; import android.content.Context;
@@ -26,8 +27,8 @@ import android.nfc.NfcAdapter;
import android.nfc.NfcManager; import android.nfc.NfcManager;
import android.os.UserManager; import android.os.UserManager;
import android.provider.Settings; import android.provider.Settings;
import android.support.v14.preference.SwitchPreference;
import android.support.v7.preference.PreferenceScreen; import android.support.v7.preference.PreferenceScreen;
import android.support.v14.preference.SwitchPreference;
import com.android.settings.testutils.SettingsRobolectricTestRunner; import com.android.settings.testutils.SettingsRobolectricTestRunner;
@@ -68,8 +69,10 @@ public class NfcPreferenceControllerTest {
when(mContext.getSystemService(Context.NFC_SERVICE)).thenReturn(mManager); when(mContext.getSystemService(Context.NFC_SERVICE)).thenReturn(mManager);
when(NfcAdapter.getDefaultAdapter(mContext)).thenReturn(mNfcAdapter); when(NfcAdapter.getDefaultAdapter(mContext)).thenReturn(mNfcAdapter);
mNfcController = new NfcPreferenceController(mContext); mNfcController = new NfcPreferenceController(mContext,
NfcPreferenceController.KEY_TOGGLE_NFC);
mNfcPreference = new SwitchPreference(RuntimeEnvironment.application); mNfcPreference = new SwitchPreference(RuntimeEnvironment.application);
when(mScreen.findPreference(mNfcController.getPreferenceKey())).thenReturn(mNfcPreference); when(mScreen.findPreference(mNfcController.getPreferenceKey())).thenReturn(mNfcPreference);
Settings.Global.putString(mContext.getContentResolver(), Settings.Global.putString(mContext.getContentResolver(),
@@ -83,15 +86,17 @@ public class NfcPreferenceControllerTest {
} }
@Test @Test
public void isAvailable_hasNfc_shouldReturnTrue() { public void getAvailabilityStatus_hasNfc_shouldReturnAvailable() {
when(mNfcAdapter.isEnabled()).thenReturn(true); when(mNfcAdapter.isEnabled()).thenReturn(true);
assertThat(mNfcController.isAvailable()).isTrue(); assertThat(mNfcController.getAvailabilityStatus())
.isEqualTo(NfcPreferenceController.AVAILABLE);
} }
@Test @Test
public void isAvailable_noNfcAdapter_shouldReturnFalse() { public void getAvailabilityStatus_noNfcAdapter_shouldReturnDisabledUnsupported() {
ReflectionHelpers.setField(mNfcController, "mNfcAdapter", null); ReflectionHelpers.setField(mNfcController, "mNfcAdapter", null);
assertThat(mNfcController.isAvailable()).isFalse(); assertThat(mNfcController.getAvailabilityStatus())
.isEqualTo(NfcPreferenceController.UNSUPPORTED_ON_DEVICE);
} }
@Test @Test
@@ -157,4 +162,46 @@ public class NfcPreferenceControllerTest {
assertThat(keys).hasSize(1); assertThat(keys).hasSize(1);
} }
@Test
public void setChecked_True_nfcShouldEnable() {
mNfcController.setChecked(true);
mNfcController.onResume();
verify(mNfcAdapter).enable();
}
@Test
public void setChecked_False_nfcShouldDisable() {
mNfcController.setChecked(false);
mNfcController.onResume();
verify(mNfcAdapter).disable();
}
@Test
public void hasAsyncUpdate_shouldReturnTrue() {
assertThat(mNfcController.hasAsyncUpdate()).isTrue();
}
@Test
public void isToggleableInAirplaneMode_containNfc_shouldReturnTrue() {
Settings.Global.putString(mContext.getContentResolver(),
Settings.Global.AIRPLANE_MODE_TOGGLEABLE_RADIOS,
Settings.Global.RADIO_NFC);
Settings.Global.putInt(mContext.getContentResolver(),
Settings.Global.AIRPLANE_MODE_ON, 1);
assertThat(NfcPreferenceController.isToggleableInAirplaneMode(mContext)).isTrue();
}
@Test
public void isToggleableInAirplaneMode_withoutNfc_shouldReturnFalse() {
Settings.Global.putString(mContext.getContentResolver(),
Settings.Global.AIRPLANE_MODE_TOGGLEABLE_RADIOS,
"null");
Settings.Global.putInt(mContext.getContentResolver(),
Settings.Global.AIRPLANE_MODE_ON, 1);
assertThat(NfcPreferenceController.isToggleableInAirplaneMode(mContext)).isFalse();
}
} }

View File

@@ -73,4 +73,9 @@ public class AlarmVolumePreferenceControllerTest {
public void getAudioStream_shouldReturnAlarm() { public void getAudioStream_shouldReturnAlarm() {
assertThat(mController.getAudioStream()).isEqualTo(AudioManager.STREAM_ALARM); 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; package com.android.settings.slices;
import static android.content.ContentResolver.SCHEME_CONTENT; import static android.content.ContentResolver.SCHEME_CONTENT;
import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
@@ -38,19 +36,24 @@ import android.os.StrictMode;
import android.provider.SettingsSlicesContract; import android.provider.SettingsSlicesContract;
import android.util.ArraySet; 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.bluetooth.BluetoothSliceBuilder;
import com.android.settings.location.LocationSliceBuilder;
import com.android.settings.notification.ZenModeSliceBuilder; import com.android.settings.notification.ZenModeSliceBuilder;
import com.android.settings.testutils.DatabaseTestUtils; import com.android.settings.testutils.DatabaseTestUtils;
import com.android.settings.testutils.FakeToggleController; import com.android.settings.testutils.FakeToggleController;
import com.android.settings.testutils.SettingsRobolectricTestRunner; 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.After;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.robolectric.RuntimeEnvironment; 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.Arrays;
import java.util.Collection; import java.util.Collection;
@@ -66,6 +69,7 @@ import androidx.slice.Slice;
* TODO Investigate using ShadowContentResolver.registerProviderInternal(String, ContentProvider) * TODO Investigate using ShadowContentResolver.registerProviderInternal(String, ContentProvider)
*/ */
@RunWith(SettingsRobolectricTestRunner.class) @RunWith(SettingsRobolectricTestRunner.class)
@Config(shadows = ShadowThreadUtils.class)
public class SettingsSliceProviderTest { public class SettingsSliceProviderTest {
private static final String KEY = "KEY"; private static final String KEY = "KEY";
@@ -98,6 +102,7 @@ public class SettingsSliceProviderTest {
public void setUp() { public void setUp() {
mContext = spy(RuntimeEnvironment.application); mContext = spy(RuntimeEnvironment.application);
mProvider = spy(new SettingsSliceProvider()); mProvider = spy(new SettingsSliceProvider());
ShadowStrictMode.reset();
mProvider.mSliceWeakDataCache = new HashMap<>(); mProvider.mSliceWeakDataCache = new HashMap<>();
mProvider.mSliceDataCache = new HashMap<>(); mProvider.mSliceDataCache = new HashMap<>();
mProvider.mSlicesDatabaseAccessor = new SlicesDatabaseAccessor(mContext); mProvider.mSlicesDatabaseAccessor = new SlicesDatabaseAccessor(mContext);
@@ -112,6 +117,7 @@ public class SettingsSliceProviderTest {
@After @After
public void cleanUp() { public void cleanUp() {
ShadowThreadUtils.reset();
DatabaseTestUtils.clearDb(mContext); DatabaseTestUtils.clearDb(mContext);
} }
@@ -184,7 +190,8 @@ public class SettingsSliceProviderTest {
} }
@Test @Test
public void onBindSlice_shouldNotOverrideStrictMode() { public void onBindSlice_mainThread_shouldNotOverrideStrictMode() {
ShadowThreadUtils.setIsMainThread(true);
final StrictMode.ThreadPolicy oldThreadPolicy = StrictMode.getThreadPolicy(); final StrictMode.ThreadPolicy oldThreadPolicy = StrictMode.getThreadPolicy();
SliceData data = getDummyData(); SliceData data = getDummyData();
mProvider.mSliceWeakDataCache.put(data.getUri(), data); mProvider.mSliceWeakDataCache.put(data.getUri(), data);
@@ -195,6 +202,18 @@ public class SettingsSliceProviderTest {
assertThat(newThreadPolicy.toString()).isEqualTo(oldThreadPolicy.toString()); assertThat(newThreadPolicy.toString()).isEqualTo(oldThreadPolicy.toString());
} }
@Test
@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 @Test
public void onBindSlice_requestsBlockedSlice_retunsNull() { public void onBindSlice_requestsBlockedSlice_retunsNull() {
final String blockedKey = "blocked_key"; final String blockedKey = "blocked_key";
@@ -456,7 +475,7 @@ public class SettingsSliceProviderTest {
mDb.replaceOrThrow(SlicesDatabaseHelper.Tables.TABLE_SLICES_INDEX, null, values); mDb.replaceOrThrow(SlicesDatabaseHelper.Tables.TABLE_SLICES_INDEX, null, values);
} }
private SliceData getDummyData() { private static SliceData getDummyData() {
return new SliceData.Builder() return new SliceData.Builder()
.setKey(KEY) .setKey(KEY)
.setTitle(TITLE) .setTitle(TITLE)
@@ -468,4 +487,24 @@ public class SettingsSliceProviderTest {
.setPreferenceControllerClassName(PREF_CONTROLLER) .setPreferenceControllerClassName(PREF_CONTROLLER)
.build(); .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

@@ -23,6 +23,7 @@ import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy; import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
@@ -95,8 +96,16 @@ public class SliceBroadcastReceiverTest {
@Test @Test
public void onReceive_toggleChanged() { public void onReceive_toggleChanged() {
final String key = "key"; 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(); mSearchFeatureProvider.getSearchIndexableResources().getProviderValues().clear();
insertSpecialCase(key); insertSpecialCase(key);
final ContentResolver resolver = mock(ContentResolver.class);
doReturn(resolver).when(mContext).getContentResolver();
// Turn on toggle setting // Turn on toggle setting
FakeToggleController fakeToggleController = new FakeToggleController(mContext, key); FakeToggleController fakeToggleController = new FakeToggleController(mContext, key);
fakeToggleController.setChecked(true); fakeToggleController.setChecked(true);
@@ -120,14 +129,77 @@ public class SliceBroadcastReceiverTest {
assertThat(namePair.first).isEqualTo(MetricsEvent.FIELD_SETTINGS_PREFERENCE_CHANGE_NAME); assertThat(namePair.first).isEqualTo(MetricsEvent.FIELD_SETTINGS_PREFERENCE_CHANGE_NAME);
assertThat(namePair.second).isEqualTo(fakeToggleController.getPreferenceKey()); assertThat(namePair.second).isEqualTo(fakeToggleController.getPreferenceKey());
verify(resolver).notifyChange(uri, null);
assertThat(valuePair.first) assertThat(valuePair.first)
.isEqualTo(MetricsEvent.FIELD_SETTINGS_PREFERENCE_CHANGE_INT_VALUE); .isEqualTo(MetricsEvent.FIELD_SETTINGS_PREFERENCE_CHANGE_INT_VALUE);
assertThat(valuePair.second).isEqualTo(0); assertThat(valuePair.second).isEqualTo(0);
} }
@Test
public void toggleUpdate_synchronously_notifyChange_should_be_called() {
// Monitor the ContentResolver
final ContentResolver resolver = spy(mContext.getContentResolver());
doReturn(resolver).when(mContext).getContentResolver();
final String key = "key";
mSearchFeatureProvider.getSearchIndexableResources().getProviderValues().clear();
insertSpecialCase(key);
FakeToggleController fakeToggleController = new FakeToggleController(mContext, key);
fakeToggleController.setChecked(true);
// Set the toggle setting update synchronously.
fakeToggleController.setAsyncUpdate(false);
Intent intent = new Intent(SettingsSliceProvider.ACTION_TOGGLE_CHANGED);
intent.putExtra(SettingsSliceProvider.EXTRA_SLICE_KEY, key);
assertThat(fakeToggleController.isChecked()).isTrue();
// Toggle setting
mReceiver.onReceive(mContext, intent);
assertThat(fakeToggleController.isChecked()).isFalse();
final Uri expectedUri = SliceBuilderUtils.getUri(
SettingsSlicesContract.PATH_SETTING_ACTION + "/" + key, false);
verify(resolver).notifyChange(eq(expectedUri), eq(null));
}
@Test
public void toggleUpdate_asynchronously_notifyChange_should_not_be_called() {
// Monitor the ContentResolver
final ContentResolver resolver = spy(mContext.getContentResolver());
doReturn(resolver).when(mContext).getContentResolver();
final String key = "key";
mSearchFeatureProvider.getSearchIndexableResources().getProviderValues().clear();
insertSpecialCase(key);
FakeToggleController fakeToggleController = new FakeToggleController(mContext, key);
fakeToggleController.setChecked(true);
// Set the toggle setting update asynchronously.
fakeToggleController.setAsyncUpdate(true);
Intent intent = new Intent(SettingsSliceProvider.ACTION_TOGGLE_CHANGED);
intent.putExtra(SettingsSliceProvider.EXTRA_SLICE_KEY, key);
assertThat(fakeToggleController.isChecked()).isTrue();
// Toggle setting
mReceiver.onReceive(mContext, intent);
verify(resolver, never()).notifyChange(null, null);
}
@Test @Test
public void onReceive_sliderChanged() { public void onReceive_sliderChanged() {
final String key = "key"; 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 position = FakeSliderController.MAX_STEPS - 1;
final int oldPosition = FakeSliderController.MAX_STEPS; final int oldPosition = FakeSliderController.MAX_STEPS;
mSearchFeatureProvider.getSearchIndexableResources().getProviderValues().clear(); mSearchFeatureProvider.getSearchIndexableResources().getProviderValues().clear();
@@ -158,6 +230,7 @@ public class SliceBroadcastReceiverTest {
assertThat(namePair.first).isEqualTo(MetricsEvent.FIELD_SETTINGS_PREFERENCE_CHANGE_NAME); assertThat(namePair.first).isEqualTo(MetricsEvent.FIELD_SETTINGS_PREFERENCE_CHANGE_NAME);
assertThat(namePair.second).isEqualTo(key); assertThat(namePair.second).isEqualTo(key);
verify(resolver).notifyChange(uri, null);
assertThat(valuePair.first) assertThat(valuePair.first)
.isEqualTo(MetricsEvent.FIELD_SETTINGS_PREFERENCE_CHANGE_INT_VALUE); .isEqualTo(MetricsEvent.FIELD_SETTINGS_PREFERENCE_CHANGE_INT_VALUE);
assertThat(valuePair.second).isEqualTo(position); assertThat(valuePair.second).isEqualTo(position);
@@ -231,8 +304,12 @@ public class SliceBroadcastReceiverTest {
// Check the value is the same and the Uri has been notified. // Check the value is the same and the Uri has been notified.
assertThat(fakeToggleController.isChecked()).isTrue(); assertThat(fakeToggleController.isChecked()).isTrue();
final Uri expectedUri = SliceBuilderUtils.getUri( final Uri expectedUri = new Uri.Builder()
SettingsSlicesContract.PATH_SETTING_ACTION + "/" + key, false); .scheme(ContentResolver.SCHEME_CONTENT)
.authority(SettingsSliceProvider.SLICE_AUTHORITY)
.appendPath(SettingsSlicesContract.PATH_SETTING_ACTION)
.appendPath(key)
.build();
verify(resolver).notifyChange(eq(expectedUri), eq(null)); verify(resolver).notifyChange(eq(expectedUri), eq(null));
} }
@@ -268,8 +345,12 @@ public class SliceBroadcastReceiverTest {
// Check position is the same and the Uri has been notified. // Check position is the same and the Uri has been notified.
assertThat(fakeSliderController.getSliderPosition()).isEqualTo(oldPosition); assertThat(fakeSliderController.getSliderPosition()).isEqualTo(oldPosition);
final Uri expectedUri = SliceBuilderUtils.getUri( final Uri expectedUri = new Uri.Builder()
SettingsSlicesContract.PATH_SETTING_ACTION + "/" + key, false); .scheme(ContentResolver.SCHEME_CONTENT)
.authority(SettingsSliceProvider.SLICE_AUTHORITY)
.appendPath(SettingsSlicesContract.PATH_SETTING_ACTION)
.appendPath(key)
.build();
verify(resolver).notifyChange(eq(expectedUri), eq(null)); verify(resolver).notifyChange(eq(expectedUri), eq(null));
} }

View File

@@ -36,6 +36,8 @@ public class FakeToggleController extends TogglePreferenceController {
private final int ON = 1; private final int ON = 1;
private final int OFF = 0; private final int OFF = 0;
private boolean mIsAsyncUpdate = false;
public FakeToggleController(Context context, String preferenceKey) { public FakeToggleController(Context context, String preferenceKey) {
super(context, preferenceKey); super(context, preferenceKey);
} }
@@ -67,4 +69,13 @@ public class FakeToggleController extends TogglePreferenceController {
public boolean isSliceable() { public boolean isSliceable() {
return true; return true;
} }
@Override
public boolean hasAsyncUpdate() {
return mIsAsyncUpdate;
}
public void setAsyncUpdate(boolean isAsyncUpdate) {
mIsAsyncUpdate = isAsyncUpdate;
}
} }

View File

@@ -33,6 +33,7 @@ import org.robolectric.util.ReflectionHelpers.ClassParameter;
@Implements(NfcAdapter.class) @Implements(NfcAdapter.class)
public class ShadowNfcAdapter { public class ShadowNfcAdapter {
private static boolean sReaderModeEnabled; private static boolean sReaderModeEnabled;
private boolean mIsNfcEnabled = false;
@Implementation @Implementation
public void enableReaderMode(Activity activity, NfcAdapter.ReaderCallback callback, int flags, public void enableReaderMode(Activity activity, NfcAdapter.ReaderCallback callback, int flags,
@@ -46,6 +47,23 @@ public class ShadowNfcAdapter {
NfcAdapter.class, ClassParameter.from(Context.class, context)); NfcAdapter.class, ClassParameter.from(Context.class, context));
} }
@Implementation
public boolean isEnabled() {
return mIsNfcEnabled;
}
@Implementation
public boolean enable() {
mIsNfcEnabled = true;
return true;
}
@Implementation
public boolean disable() {
mIsNfcEnabled = false;
return true;
}
public static boolean isReaderModeEnabled() { public static boolean isReaderModeEnabled() {
return sReaderModeEnabled; return sReaderModeEnabled;
} }

View File

@@ -20,10 +20,18 @@ import com.android.settingslib.utils.ThreadUtils;
import org.robolectric.annotation.Implementation; import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements; import org.robolectric.annotation.Implements;
import org.robolectric.annotation.Resetter;
@Implements(ThreadUtils.class) @Implements(ThreadUtils.class)
public class ShadowThreadUtils { public class ShadowThreadUtils {
private static boolean sIsMainThread = true;
@Resetter
public static void reset() {
sIsMainThread = true;
}
@Implementation @Implementation
public static void postOnBackgroundThread(Runnable runnable) { public static void postOnBackgroundThread(Runnable runnable) {
runnable.run(); runnable.run();
@@ -33,4 +41,14 @@ public class ShadowThreadUtils {
public static void postOnMainThread(Runnable runnable) { public static void postOnMainThread(Runnable runnable) {
runnable.run(); runnable.run();
} }
@Implementation
public static boolean isMainThread() {
return sIsMainThread;
}
public static void setIsMainThread(boolean isMainThread) {
sIsMainThread = isMainThread;
}
} }