diff --git a/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java b/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java index 919dc3a4bef..576829946ed 100644 --- a/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java +++ b/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java @@ -32,8 +32,10 @@ import com.android.settings.search.BaseSearchIndexProvider; import com.android.settings.search.Indexable; import com.android.settings.widget.SwitchBar; import com.android.settingslib.core.AbstractPreferenceController; +import com.android.settingslib.core.lifecycle.Lifecycle; import com.android.settingslib.development.DevelopmentSettingsEnabler; +import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -116,7 +118,7 @@ public class DevelopmentSettingsDashboardFragment extends RestrictedDashboardFra @Override protected List getPreferenceControllers(Context context) { - return buildPreferenceControllers(context); + return buildPreferenceControllers(context, getLifecycle()); } void onEnableDevelopmentOptionsConfirmed() { @@ -129,8 +131,12 @@ public class DevelopmentSettingsDashboardFragment extends RestrictedDashboardFra mSwitchBar.setChecked(false); } - private static List buildPreferenceControllers(Context context) { - return null; + private static List buildPreferenceControllers(Context context, + Lifecycle lifecycle) { + final List controllers = new ArrayList<>(); + controllers.add(new StayAwakePreferenceController(context, lifecycle)); + + return controllers; } /** @@ -156,7 +162,7 @@ public class DevelopmentSettingsDashboardFragment extends RestrictedDashboardFra @Override public List getPreferenceControllers(Context context) { - return buildPreferenceControllers(context); + return buildPreferenceControllers(context, null /* lifecycle */); } }; } diff --git a/src/com/android/settings/development/StayAwakePreferenceController.java b/src/com/android/settings/development/StayAwakePreferenceController.java new file mode 100644 index 00000000000..ebba9e51496 --- /dev/null +++ b/src/com/android/settings/development/StayAwakePreferenceController.java @@ -0,0 +1,139 @@ +package com.android.settings.development; + +import android.content.ContentResolver; +import android.content.Context; +import android.database.ContentObserver; +import android.net.Uri; +import android.os.BatteryManager; +import android.os.Handler; +import android.provider.Settings; +import android.support.annotation.VisibleForTesting; +import android.support.v7.preference.Preference; +import android.support.v7.preference.PreferenceScreen; + +import com.android.settings.core.PreferenceControllerMixin; +import com.android.settingslib.RestrictedLockUtils; +import com.android.settingslib.RestrictedSwitchPreference; +import com.android.settingslib.core.AbstractPreferenceController; +import com.android.settingslib.core.lifecycle.Lifecycle; +import com.android.settingslib.core.lifecycle.LifecycleObserver; +import com.android.settingslib.core.lifecycle.events.OnPause; +import com.android.settingslib.core.lifecycle.events.OnResume; + + +public class StayAwakePreferenceController extends AbstractPreferenceController implements + PreferenceControllerMixin, Preference.OnPreferenceChangeListener, LifecycleObserver, + OnResume, OnPause { + + private static final String TAG = "StayAwakeCtrl"; + private static final String PREFERENCE_KEY = "keep_screen_on"; + @VisibleForTesting + static final int SETTING_VALUE_OFF = 0; + @VisibleForTesting + static final int SETTING_VALUE_ON = + BatteryManager.BATTERY_PLUGGED_AC | BatteryManager.BATTERY_PLUGGED_USB + | BatteryManager.BATTERY_PLUGGED_WIRELESS; + @VisibleForTesting + SettingsObserver mSettingsObserver; + + private RestrictedSwitchPreference mPreference; + + public StayAwakePreferenceController(Context context, Lifecycle lifecycle) { + super(context); + mSettingsObserver = new SettingsObserver(); + + if (lifecycle != null) { + lifecycle.addObserver(this); + } + } + + @Override + public boolean isAvailable() { + return true; + } + + @Override + public String getPreferenceKey() { + return PREFERENCE_KEY; + } + + @Override + public void displayPreference(PreferenceScreen screen) { + super.displayPreference(screen); + mPreference = (RestrictedSwitchPreference) screen.findPreference(getPreferenceKey()); + } + + @Override + public boolean onPreferenceChange(Preference preference, Object newValue) { + final boolean stayAwake = (Boolean) newValue; + Settings.Global.putInt(mContext.getContentResolver(), + Settings.Global.STAY_ON_WHILE_PLUGGED_IN, + stayAwake ? SETTING_VALUE_ON : SETTING_VALUE_OFF); + return true; + } + + @Override + public void updateState(Preference preference) { + final RestrictedLockUtils.EnforcedAdmin admin = checkIfMaximumTimeToLockSetByAdmin(); + if (admin != null) { + mPreference.setDisabledByAdmin(admin); + return; + } + + final int stayAwakeMode = Settings.Global.getInt(mContext.getContentResolver(), + Settings.Global.STAY_ON_WHILE_PLUGGED_IN, + SETTING_VALUE_OFF); + mPreference.setChecked(stayAwakeMode != SETTING_VALUE_OFF); + } + + @Override + public void onResume() { + if (mPreference != null) { + mSettingsObserver.register(true /* register */); + } + } + + @Override + public void onPause() { + if (mPreference != null) { + mSettingsObserver.register(false /* unregister */); + } + } + + @VisibleForTesting + RestrictedLockUtils.EnforcedAdmin checkIfMaximumTimeToLockSetByAdmin() { + // A DeviceAdmin has specified a maximum time until the device + // will lock... in this case we can't allow the user to turn + // on "stay awake when plugged in" because that would defeat the + // restriction. + return RestrictedLockUtils.checkIfMaximumTimeToLockIsSet(mContext); + } + + @VisibleForTesting + class SettingsObserver extends ContentObserver { + private final Uri mStayAwakeUri = Settings.Global.getUriFor( + Settings.Global.STAY_ON_WHILE_PLUGGED_IN); + + public SettingsObserver() { + super(new Handler()); + } + + public void register(boolean register) { + final ContentResolver cr = mContext.getContentResolver(); + if (register) { + cr.registerContentObserver( + mStayAwakeUri, false, this); + } else { + cr.unregisterContentObserver(this); + } + } + + @Override + public void onChange(boolean selfChange, Uri uri) { + super.onChange(selfChange, uri); + if (mStayAwakeUri.equals(uri)) { + updateState(mPreference); + } + } + } +} diff --git a/tests/robotests/src/com/android/settings/development/StayAwakePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/development/StayAwakePreferenceControllerTest.java new file mode 100644 index 00000000000..cbef53173e1 --- /dev/null +++ b/tests/robotests/src/com/android/settings/development/StayAwakePreferenceControllerTest.java @@ -0,0 +1,117 @@ +package com.android.settings.development; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.content.ContentResolver; +import android.content.Context; +import android.provider.Settings; +import android.support.v7.preference.PreferenceScreen; + +import com.android.settings.TestConfig; +import com.android.settings.testutils.SettingsRobolectricTestRunner; +import com.android.settingslib.RestrictedLockUtils; +import com.android.settingslib.RestrictedSwitchPreference; +import com.android.settingslib.core.lifecycle.Lifecycle; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; +import org.robolectric.RuntimeEnvironment; +import org.robolectric.annotation.Config; + +@RunWith(SettingsRobolectricTestRunner.class) +@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION) +public class StayAwakePreferenceControllerTest { + + @Mock + private Context mContext; + @Mock + private RestrictedSwitchPreference mPreference; + @Mock + private PreferenceScreen mPreferenceScreen; + @Mock + private Lifecycle mLifecycle; + private ContentResolver mContentResolver; + private StayAwakePreferenceController mController; + + @Before + public void setup() { + MockitoAnnotations.initMocks(this); + mContentResolver = RuntimeEnvironment.application.getContentResolver(); + mController = new StayAwakePreferenceController(mContext, mLifecycle); + when(mContext.getContentResolver()).thenReturn(mContentResolver); + when(mPreferenceScreen.findPreference(mController.getPreferenceKey())).thenReturn( + mPreference); + mController.displayPreference(mPreferenceScreen); + } + + @Test + public void onPreferenceChanged_turnOnStayAwake() { + mController.onPreferenceChange(null, true); + + final int mode = Settings.System.getInt(mContentResolver, + Settings.Global.STAY_ON_WHILE_PLUGGED_IN, -1); + assertThat(mode).isEqualTo(StayAwakePreferenceController.SETTING_VALUE_ON); + } + + @Test + public void onPreferenceChanged_turnOffStayAwake() { + mController.onPreferenceChange(null, false); + + final int mode = Settings.System.getInt(mContentResolver, + Settings.Global.STAY_ON_WHILE_PLUGGED_IN, -1); + assertThat(mode).isEqualTo(StayAwakePreferenceController.SETTING_VALUE_OFF); + } + + @Test + public void updateState_preferenceShouldBeChecked() { + Settings.System.putInt(mContentResolver, Settings.Global.STAY_ON_WHILE_PLUGGED_IN, + StayAwakePreferenceController.SETTING_VALUE_ON); + mController.updateState(mPreference); + verify(mPreference).setChecked(true); + } + + @Test + public void updateState_preferenceShouldNotBeChecked() { + Settings.System.putInt(mContentResolver, Settings.Global.STAY_ON_WHILE_PLUGGED_IN, + StayAwakePreferenceController.SETTING_VALUE_OFF); + mController.updateState(mPreference); + verify(mPreference).setChecked(false); + } + + @Test + public void displayPreference_expectSetDisabledByAdminToBeCalled() { + mController = spy(mController); + RestrictedLockUtils.EnforcedAdmin admin = Mockito.mock( + RestrictedLockUtils.EnforcedAdmin.class); + doReturn(admin).when(mController).checkIfMaximumTimeToLockSetByAdmin(); + mController.updateState(mPreference); + verify(mPreference).setDisabledByAdmin(admin); + } + + @Test + public void observerOnChangeCalledWithSameUri_preferenceShouldBeUpdated() { + Settings.System.putInt(mContentResolver, Settings.Global.STAY_ON_WHILE_PLUGGED_IN, + StayAwakePreferenceController.SETTING_VALUE_ON); + mController.mSettingsObserver.onChange(false, + Settings.Global.getUriFor(Settings.Global.STAY_ON_WHILE_PLUGGED_IN)); + verify(mPreference).setChecked(true); + } + + @Test + public void observerOnChangeCalledWithDifferentUri_preferenceShouldNotBeUpdated() { + Settings.System.putInt(mContentResolver, Settings.Global.STAY_ON_WHILE_PLUGGED_IN, + StayAwakePreferenceController.SETTING_VALUE_ON); + mController.mSettingsObserver.onChange(false, null); + verify(mPreference, never()).setChecked(true); + } +}