Merge "Introduce StayAwakePreferenceController"
diff --git a/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java b/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java
index 919dc3a..5768299 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.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 @@
 
     @Override
     protected List<AbstractPreferenceController> getPreferenceControllers(Context context) {
-        return buildPreferenceControllers(context);
+        return buildPreferenceControllers(context, getLifecycle());
     }
 
     void onEnableDevelopmentOptionsConfirmed() {
@@ -129,8 +131,12 @@
         mSwitchBar.setChecked(false);
     }
 
-    private static List<AbstractPreferenceController> buildPreferenceControllers(Context context) {
-        return null;
+    private static List<AbstractPreferenceController> buildPreferenceControllers(Context context,
+            Lifecycle lifecycle) {
+        final List<AbstractPreferenceController> controllers = new ArrayList<>();
+        controllers.add(new StayAwakePreferenceController(context, lifecycle));
+
+        return controllers;
     }
 
     /**
@@ -156,7 +162,7 @@
                 @Override
                 public List<AbstractPreferenceController> 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 0000000..ebba9e5
--- /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 0000000..cbef531
--- /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);
+    }
+}