Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 23367e38 authored by Sunny Shao's avatar Sunny Shao
Browse files

Migrate Use Battery Saver

Test: atest BatterySaverScreenTest BatterySaverMainSwitchPreferenceTest
Bug: 377993674
Flag: com.android.settings.flags.catalyst_battery_saver_screen
Change-Id: I0c788688ed07ddcb5b2c97b2856194fd57c318e0
parent 2c4aec3a
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -38,6 +38,7 @@ import com.android.settingslib.fuelgauge.BatterySaverUtils;
import com.android.settingslib.widget.MainSwitchPreference;

/** Controller to update the battery saver button */
// LINT.IfChange
public class BatterySaverButtonPreferenceController extends TogglePreferenceController
        implements LifecycleObserver, OnStart, OnStop, BatterySaverReceiver.BatterySaverListener {
    private static final long SWITCH_ANIMATION_DURATION = 350L;
@@ -129,3 +130,4 @@ public class BatterySaverButtonPreferenceController extends TogglePreferenceCont
        }
    }
}
// LINT.ThenChange(BatterySaverPreference.kt)
+100 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 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.Context
import android.os.Handler
import android.os.Looper
import android.os.PowerManager
import com.android.settings.R
import com.android.settings.fuelgauge.BatterySaverReceiver
import com.android.settings.fuelgauge.BatterySaverReceiver.BatterySaverListener
import com.android.settingslib.datastore.KeyValueStore
import com.android.settingslib.datastore.NoOpKeyedObservable
import com.android.settingslib.fuelgauge.BatterySaverLogging.SAVER_ENABLED_SETTINGS
import com.android.settingslib.fuelgauge.BatterySaverUtils
import com.android.settingslib.fuelgauge.BatteryStatus
import com.android.settingslib.fuelgauge.BatteryUtils
import com.android.settingslib.metadata.MainSwitchPreference
import com.android.settingslib.metadata.PreferenceLifecycleContext
import com.android.settingslib.metadata.PreferenceLifecycleProvider

// LINT.IfChange
class BatterySaverPreference :
    MainSwitchPreference(KEY, R.string.battery_saver_master_switch_title),
    PreferenceLifecycleProvider {

    private var batterySaverReceiver: BatterySaverReceiver? = null
    private val handler by lazy { Handler(Looper.getMainLooper()) }

    override fun storage(context: Context) = BatterySaverStore(context)

    override fun isEnabled(context: Context) =
        !BatteryStatus(BatteryUtils.getBatteryIntent(context)).isPluggedIn

    override fun onStart(context: PreferenceLifecycleContext) {
        BatterySaverReceiver(context).apply {
            batterySaverReceiver = this
            setBatterySaverListener(
                object : BatterySaverListener {
                    override fun onPowerSaveModeChanged() {
                        handler.postDelayed(
                            { context.notifyPreferenceChange(this@BatterySaverPreference) },
                            SWITCH_ANIMATION_DURATION,
                        )
                    }

                    override fun onBatteryChanged(pluggedIn: Boolean) =
                        context.notifyPreferenceChange(this@BatterySaverPreference)
                }
            )
            setListening(true)
        }
    }

    override fun onStop(context: PreferenceLifecycleContext) {
        batterySaverReceiver?.setListening(false)
        batterySaverReceiver = null
        handler.removeCallbacksAndMessages(null /* token */)
    }

    @Suppress("UNCHECKED_CAST")
    class BatterySaverStore(private val context: Context) :
        NoOpKeyedObservable<String>(), KeyValueStore {
        override fun contains(key: String) = key == KEY

        override fun <T : Any> getValue(key: String, valueType: Class<T>) =
            context.isPowerSaveMode() as T

        override fun <T : Any> setValue(key: String, valueType: Class<T>, value: T?) {
            BatterySaverUtils.setPowerSaveMode(
                context,
                value as Boolean,
                /* needFirstTimeWarning= */ false,
                SAVER_ENABLED_SETTINGS,
            )
        }

        private fun Context.isPowerSaveMode() =
            getSystemService(PowerManager::class.java)?.isPowerSaveMode == true
    }

    companion object {
        private const val KEY = "battery_saver"
        private const val SWITCH_ANIMATION_DURATION: Long = 350L
    }
}
// LINT.ThenChange(BatterySaverButtonPreferenceController.java)
+3 −1
Original line number Diff line number Diff line
@@ -39,7 +39,9 @@ class BatterySaverScreen : PreferenceScreenCreator {

    override fun hasCompleteHierarchy() = false

    override fun getPreferenceHierarchy(context: Context) = preferenceHierarchy(this) {}
    override fun getPreferenceHierarchy(context: Context) = preferenceHierarchy(this) {
        +BatterySaverPreference()
    }

    companion object {
        const val KEY = "battery_saver_screen"
+2 −0
Original line number Diff line number Diff line
@@ -40,6 +40,7 @@ import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;

// LINT.IfChange
@RunWith(RobolectricTestRunner.class)
public class BatterySaverButtonPreferenceControllerTest {

@@ -120,3 +121,4 @@ public class BatterySaverButtonPreferenceControllerTest {
        assertThat(mController.isPublicSlice()).isTrue();
    }
}
// LINT.ThenChange(BatterySaverPreferenceTest.kt)
+107 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 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.BroadcastReceiver
import android.content.Context
import android.content.ContextWrapper
import android.content.Intent
import android.content.IntentFilter
import android.os.BatteryManager.EXTRA_PLUGGED
import android.os.PowerManager
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.settingslib.preference.createAndBindWidget
import com.android.settingslib.widget.MainSwitchPreference
import com.google.common.truth.Truth.assertThat
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.kotlin.doReturn
import org.mockito.kotlin.mock
import org.mockito.kotlin.stub
import org.mockito.kotlin.verify

// LINT.IfChange
@RunWith(AndroidJUnit4::class)
class BatterySaverPreferenceTest {
    private val powerManager = mock<PowerManager>()

    private val context: Context =
        object : ContextWrapper(ApplicationProvider.getApplicationContext()) {
            override fun getSystemService(name: String): Any? =
                when {
                    name == getSystemServiceName(PowerManager::class.java) -> powerManager
                    else -> super.getSystemService(name)
                }

            override fun registerReceiver(receiver: BroadcastReceiver?, filter: IntentFilter?) =
                Intent().putExtra(EXTRA_PLUGGED, 0)
        }

    private val contextPlugIn: Context =
        object : ContextWrapper(ApplicationProvider.getApplicationContext()) {
            override fun registerReceiver(receiver: BroadcastReceiver?, filter: IntentFilter?) =
                Intent().putExtra(EXTRA_PLUGGED, 1)
        }

    private val batterySaverPreference = BatterySaverPreference()

    @Test
    fun lowPowerOn_preferenceIsChecked() {
        powerManager.stub { on { isPowerSaveMode } doReturn true }

        assertThat(getMainSwitchPreference().isChecked).isTrue()
    }

    @Test
    fun lowPowerOff_preferenceIsUnChecked() {
        powerManager.stub { on { isPowerSaveMode } doReturn false }

        assertThat(getMainSwitchPreference().isChecked).isFalse()
    }

    @Test
    fun storeSetOn_setPowerSaveMode() {
        batterySaverPreference
            .storage(context)
            .setValue(batterySaverPreference.key, Boolean::class.javaObjectType, true)

        verify(powerManager).setPowerSaveModeEnabled(true)
    }

    @Test
    fun storeSetOff_unsetPowerSaveMode() {
        batterySaverPreference
            .storage(context)
            .setValue(batterySaverPreference.key, Boolean::class.javaObjectType, false)

        verify(powerManager).setPowerSaveModeEnabled(false)
    }

    @Test
    fun isUnPlugIn_preferenceEnabled() {
        assertThat(getMainSwitchPreference().isEnabled).isTrue()
    }

    @Test
    fun isPlugIn_preferenceDisabled() {
        assertThat(getMainSwitchPreference(contextPlugIn).isEnabled).isFalse()
    }

    private fun getMainSwitchPreference(ctx: Context = context) =
        batterySaverPreference.createAndBindWidget<MainSwitchPreference>(ctx)
}
// LINT.ThenChange(BatterySaverButtonPreferenceControllerTest.java)
Loading