diff --git a/src/com/android/settings/fuelgauge/batterysaver/AutoBatterySeekBarPreferenceController.java b/src/com/android/settings/fuelgauge/batterysaver/AutoBatterySeekBarPreferenceController.java new file mode 100644 index 00000000000..1c787abd5d4 --- /dev/null +++ b/src/com/android/settings/fuelgauge/batterysaver/AutoBatterySeekBarPreferenceController.java @@ -0,0 +1,135 @@ +/* + * 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.fuelgauge.batterysaver; + +import android.content.ContentResolver; +import android.content.Context; +import android.database.ContentObserver; +import android.net.Uri; +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.R; +import com.android.settings.Utils; +import com.android.settings.core.BasePreferenceController; +import com.android.settings.widget.SeekBarPreference; +import com.android.settingslib.core.lifecycle.Lifecycle; +import com.android.settingslib.core.lifecycle.LifecycleObserver; +import com.android.settingslib.core.lifecycle.events.OnStart; +import com.android.settingslib.core.lifecycle.events.OnStop; + +/** + * Controller that update the battery saver seekbar + */ +public class AutoBatterySeekBarPreferenceController extends BasePreferenceController implements + LifecycleObserver, OnStart, OnStop, SeekBarPreference.OnPreferenceChangeListener { + @VisibleForTesting + static final String KEY_AUTO_BATTERY_SEEK_BAR = "battery_saver_seek_bar"; + private SeekBarPreference mPreference; + private AutoBatterySaverSettingObserver mContentObserver; + + public AutoBatterySeekBarPreferenceController(Context context, Lifecycle lifecycle) { + super(context, KEY_AUTO_BATTERY_SEEK_BAR); + mContentObserver = new AutoBatterySaverSettingObserver(new Handler()); + if (lifecycle != null) { + lifecycle.addObserver(this); + } + } + + @Override + public void displayPreference(PreferenceScreen screen) { + super.displayPreference(screen); + mPreference = (SeekBarPreference) screen.findPreference( + KEY_AUTO_BATTERY_SEEK_BAR); + updatePreference(mPreference); + } + + @Override + public int getAvailabilityStatus() { + return AVAILABLE; + } + + @Override + public void updateState(Preference preference) { + super.updateState(preference); + updatePreference(preference); + } + + @Override + public void onStart() { + mContentObserver.registerContentObserver(); + } + + @Override + public void onStop() { + mContentObserver.unRegisterContentObserver(); + } + + @Override + public boolean onPreferenceChange(Preference preference, Object newValue) { + final int progress = (int) newValue; + Settings.Global.putInt(mContext.getContentResolver(), + Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL, progress); + return true; + } + + @VisibleForTesting + void updatePreference(Preference preference) { + final ContentResolver contentResolver = mContext.getContentResolver(); + final int level = Settings.Global.getInt(contentResolver, + Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL, 0); + if (level == 0) { + preference.setVisible(false); + } else { + preference.setVisible(true); + preference.setTitle(mContext.getString(R.string.battery_saver_seekbar_title, + Utils.formatPercentage(level))); + ((SeekBarPreference) preference).setProgress(level); + } + } + + /** + * Observer that listens to change from {@link Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL} + */ + private final class AutoBatterySaverSettingObserver extends ContentObserver { + private final Uri mUri = Settings.Global.getUriFor( + Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL); + private final ContentResolver mContentResolver; + + public AutoBatterySaverSettingObserver(Handler handler) { + super(handler); + mContentResolver = mContext.getContentResolver(); + } + + public void registerContentObserver() { + mContentResolver.registerContentObserver(mUri, false, this); + } + + public void unRegisterContentObserver() { + mContentResolver.unregisterContentObserver(this); + } + + @Override + public void onChange(boolean selfChange, Uri uri, int userId) { + if (mUri.equals(uri)) { + updatePreference(mPreference); + } + } + } +} diff --git a/src/com/android/settings/fuelgauge/batterysaver/BatterySaverSettings.java b/src/com/android/settings/fuelgauge/batterysaver/BatterySaverSettings.java index 270c29c943a..392032ce21b 100644 --- a/src/com/android/settings/fuelgauge/batterysaver/BatterySaverSettings.java +++ b/src/com/android/settings/fuelgauge/batterysaver/BatterySaverSettings.java @@ -77,6 +77,7 @@ public class BatterySaverSettings extends DashboardFragment { Context context, Lifecycle lifecycle) { final List controllers = new ArrayList<>(); controllers.add(new AutoBatterySaverPreferenceController(context)); + controllers.add(new AutoBatterySeekBarPreferenceController(context, lifecycle)); return controllers; } diff --git a/src/com/android/settings/widget/SeekBarPreference.java b/src/com/android/settings/widget/SeekBarPreference.java index ee7d4b8587d..5af21b3a004 100644 --- a/src/com/android/settings/widget/SeekBarPreference.java +++ b/src/com/android/settings/widget/SeekBarPreference.java @@ -40,6 +40,7 @@ public class SeekBarPreference extends RestrictedPreference private int mProgress; private int mMax; + private int mMin; private boolean mTrackingTouch; private boolean mContinuousUpdates; @@ -55,6 +56,7 @@ public class SeekBarPreference extends RestrictedPreference TypedArray a = context.obtainStyledAttributes( attrs, com.android.internal.R.styleable.ProgressBar, defStyleAttr, defStyleRes); setMax(a.getInt(com.android.internal.R.styleable.ProgressBar_max, mMax)); + setMin(a.getInt(com.android.internal.R.styleable.ProgressBar_min, mMin)); a.recycle(); a = context.obtainStyledAttributes(attrs, @@ -94,6 +96,7 @@ public class SeekBarPreference extends RestrictedPreference com.android.internal.R.id.seekbar); mSeekBar.setOnSeekBarChangeListener(this); mSeekBar.setMax(mMax); + mSeekBar.setMin(mMin); mSeekBar.setProgress(mProgress); mSeekBar.setEnabled(isEnabled()); final CharSequence title = getTitle(); @@ -154,10 +157,21 @@ public class SeekBarPreference extends RestrictedPreference } } + public void setMin(int min) { + if (min != mMin) { + mMin = min; + notifyChanged(); + } + } + public int getMax() { return mMax; } + public int getMin() { + return mMin; + } + public void setProgress(int progress) { setProgress(progress, true); } @@ -187,8 +201,8 @@ public class SeekBarPreference extends RestrictedPreference if (progress > mMax) { progress = mMax; } - if (progress < 0) { - progress = 0; + if (progress < mMin) { + progress = mMin; } if (progress != mProgress) { mProgress = progress; @@ -257,6 +271,7 @@ public class SeekBarPreference extends RestrictedPreference final SavedState myState = new SavedState(superState); myState.progress = mProgress; myState.max = mMax; + myState.min = mMin; return myState; } @@ -273,6 +288,7 @@ public class SeekBarPreference extends RestrictedPreference super.onRestoreInstanceState(myState.getSuperState()); mProgress = myState.progress; mMax = myState.max; + mMin = myState.min; notifyChanged(); } @@ -285,6 +301,7 @@ public class SeekBarPreference extends RestrictedPreference private static class SavedState extends BaseSavedState { int progress; int max; + int min; public SavedState(Parcel source) { super(source); @@ -292,6 +309,7 @@ public class SeekBarPreference extends RestrictedPreference // Restore the click counter progress = source.readInt(); max = source.readInt(); + min = source.readInt(); } @Override @@ -301,6 +319,7 @@ public class SeekBarPreference extends RestrictedPreference // Save the click counter dest.writeInt(progress); dest.writeInt(max); + dest.writeInt(min); } public SavedState(Parcelable superState) { diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batterysaver/AutoBatterySeekBarPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batterysaver/AutoBatterySeekBarPreferenceControllerTest.java new file mode 100644 index 00000000000..32a4facb6a9 --- /dev/null +++ b/tests/robotests/src/com/android/settings/fuelgauge/batterysaver/AutoBatterySeekBarPreferenceControllerTest.java @@ -0,0 +1,91 @@ +/* + * 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.fuelgauge.batterysaver; + +import static com.google.common.truth.Truth.assertThat; + +import android.arch.lifecycle.LifecycleOwner; +import android.content.Context; +import android.provider.Settings; +import android.support.v14.preference.SwitchPreference; + +import com.android.settings.TestConfig; +import com.android.settings.testutils.SettingsRobolectricTestRunner; +import com.android.settings.widget.SeekBarPreference; +import com.android.settingslib.core.lifecycle.Lifecycle; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +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 AutoBatterySeekBarPreferenceControllerTest { + private static final int TRIGGER_LEVEL = 15; + + private AutoBatterySeekBarPreferenceController mController; + private Context mContext; + private SeekBarPreference mPreference; + private Lifecycle mLifecycle; + private LifecycleOwner mLifecycleOwner; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + mLifecycleOwner = () -> mLifecycle; + mLifecycle = new Lifecycle(mLifecycleOwner); + + mContext = RuntimeEnvironment.application; + mPreference = new SeekBarPreference(mContext); + mPreference.setMax(100); + mController = new AutoBatterySeekBarPreferenceController(mContext, mLifecycle); + } + + @Test + public void testPreference_lowPowerLevelZero_preferenceInvisible() { + Settings.Global.putInt(mContext.getContentResolver(), + Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL, 0); + + mController.updateState(mPreference); + + assertThat(mPreference.isVisible()).isFalse(); + } + + @Test + public void testPreference_lowPowerLevelNotZero_updatePreference() { + Settings.Global.putInt(mContext.getContentResolver(), + Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL, TRIGGER_LEVEL); + mController.updateState(mPreference); + + assertThat(mPreference.isVisible()).isTrue(); + assertThat(mPreference.getTitle()).isEqualTo("Turn on automatically at 15%"); + assertThat(mPreference.getProgress()).isEqualTo(TRIGGER_LEVEL); + } + + @Test + public void testOnPreferenceChange_updateValue() { + Settings.Global.putInt(mContext.getContentResolver(), + Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL, 0); + + mController.onPreferenceChange(mPreference, TRIGGER_LEVEL); + + assertThat(Settings.Global.getInt(mContext.getContentResolver(), + Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL, 0)).isEqualTo(TRIGGER_LEVEL); + } +} diff --git a/tests/robotests/src/com/android/settings/widget/SeekBarPreferenceTest.java b/tests/robotests/src/com/android/settings/widget/SeekBarPreferenceTest.java new file mode 100644 index 00000000000..7a042a06d82 --- /dev/null +++ b/tests/robotests/src/com/android/settings/widget/SeekBarPreferenceTest.java @@ -0,0 +1,65 @@ +/* + * 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.widget; + +import static com.google.common.truth.Truth.assertThat; + +import android.content.Context; +import android.os.Parcelable; + +import com.android.settings.TestConfig; +import com.android.settings.testutils.SettingsRobolectricTestRunner; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RuntimeEnvironment; +import org.robolectric.annotation.Config; + +@RunWith(SettingsRobolectricTestRunner.class) +@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION) +public class SeekBarPreferenceTest { + private static final int MAX = 75; + private static final int MIN = 5; + private static final int PROGRESS = 16; + + private Context mContext; + private SeekBarPreference mSeekBarPreference; + + @Before + public void setUp() { + mContext = RuntimeEnvironment.application; + + mSeekBarPreference = new SeekBarPreference(mContext); + mSeekBarPreference.setMax(MAX); + mSeekBarPreference.setMin(MIN); + mSeekBarPreference.setProgress(PROGRESS); + mSeekBarPreference.setPersistent(false); + } + + @Test + public void testSaveAndRestoreInstanceState() { + final Parcelable parcelable = mSeekBarPreference.onSaveInstanceState(); + + final SeekBarPreference preference = new SeekBarPreference(mContext); + preference.onRestoreInstanceState(parcelable); + + assertThat(preference.getMax()).isEqualTo(MAX); + assertThat(preference.getMin()).isEqualTo(MIN); + assertThat(preference.getProgress()).isEqualTo(PROGRESS); + } +}