geekdroid: add lowBatteryFlow() and isPowerSaveModeFlow()
authorDa Risk <da_risk@geekorum.com>
Tue, 18 Feb 2025 00:03:44 -0400
changeset 86 84e46eaf1ea0
parent 85 3b8739febbfe
child 87 5de92f006da7
geekdroid: add lowBatteryFlow() and isPowerSaveModeFlow()
geekdroid/src/main/java/com/geekorum/geekdroid/battery/LiveData.kt
geekdroid/src/test/java/com/geekorum/geekdroid/battery/LowBatteryFlowTest.kt
geekdroid/src/test/java/com/geekorum/geekdroid/battery/PowerSaveModeFlowTest.kt
--- a/geekdroid/src/main/java/com/geekorum/geekdroid/battery/LiveData.kt	Mon Feb 17 22:28:33 2025 -0400
+++ b/geekdroid/src/main/java/com/geekorum/geekdroid/battery/LiveData.kt	Tue Feb 18 00:03:44 2025 -0400
@@ -30,11 +30,14 @@
 import android.os.Build
 import android.os.PowerManager
 import androidx.lifecycle.LiveData
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.callbackFlow
 import kotlin.math.roundToInt
 
 /**
  * Observe the battery to know if a low level was reached
  */
+@Deprecated("Use Application.lowBatteryFlow()")
 class LowBatteryLiveData(
     private val application: Application
 ) : LiveData<Boolean>() {
@@ -70,10 +73,42 @@
     }
 }
 
+fun Application.lowBatteryFlow() = callbackFlow {
+    val broadcastReceiver = object : BroadcastReceiver() {
+        override fun onReceive(context: Context?, intent: Intent?) {
+            when (intent?.action) {
+                Intent.ACTION_BATTERY_OKAY -> trySend(false)
+                Intent.ACTION_BATTERY_LOW -> trySend(true)
+            }
+        }
+    }
+    val intentFilter = IntentFilter().apply {
+        addAction(Intent.ACTION_BATTERY_LOW)
+        addAction(Intent.ACTION_BATTERY_OKAY)
+    }
+    registerReceiver(broadcastReceiver, intentFilter)
+
+    val batteryStatus = registerReceiver(null, IntentFilter(Intent.ACTION_BATTERY_CHANGED))
+    val value  = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
+        batteryStatus?.getBooleanExtra(BatteryManager.EXTRA_BATTERY_LOW, false) == true
+    } else {
+        val level = batteryStatus?.getIntExtra(BatteryManager.EXTRA_LEVEL, 0) ?: 0
+        val scale = batteryStatus?.getIntExtra(BatteryManager.EXTRA_SCALE, 100) ?: 100
+        val percent = level.toFloat() / scale * 100
+        percent.roundToInt() <= 15
+    }
+    trySend(value)
+
+    awaitClose {
+        unregisterReceiver(broadcastReceiver)
+    }
+}
+
 
 /**
  * Observe the [PowerManager] to know if the system is in Power saving mode.
  */
+@Deprecated("Use isPowerSaveModeFlow(application, powerManager)")
 class BatterySaverLiveData(
     private val application: Application,
     private val powerManager: PowerManager
@@ -96,3 +131,18 @@
         application.unregisterReceiver(broadcastReceiver)
     }
 }
+
+fun isPowerSaveModeFlow(application: Application, powerManager: PowerManager) = callbackFlow {
+    val broadcastReceiver = object : BroadcastReceiver() {
+        override fun onReceive(context: Context?, intent: Intent?) {
+            trySend(powerManager.isPowerSaveMode)
+        }
+    }
+    application.registerReceiver(broadcastReceiver, IntentFilter(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED))
+
+    trySend(powerManager.isPowerSaveMode)
+
+    awaitClose {
+        application.unregisterReceiver(broadcastReceiver)
+    }
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/geekdroid/src/test/java/com/geekorum/geekdroid/battery/LowBatteryFlowTest.kt	Tue Feb 18 00:03:44 2025 -0400
@@ -0,0 +1,91 @@
+/*
+ * Geekdroid is a utility library for development on the Android
+ * Platform.
+ *
+ * Copyright (C) 2017-2025 by Frederic-Charles Barthelery.
+ *
+ * This file is part of Geekdroid.
+ *
+ * Geekdroid is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Geekdroid is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Geekdroid.  If not, see <http://www.gnu.org/licenses/>.
+ */
+package com.geekorum.geekdroid.battery
+
+import android.app.Application
+import android.content.Intent
+import android.os.BatteryManager
+import android.os.Build
+import android.os.Looper
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import app.cash.turbine.test
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.runTest
+import org.junit.runner.RunWith
+import org.robolectric.Shadows.shadowOf
+import org.robolectric.annotation.Config
+import kotlin.test.BeforeTest
+import kotlin.test.Test
+
+@RunWith(AndroidJUnit4::class)
+@Config(minSdk = Build.VERSION_CODES.Q)
+class LowBatteryFlowTest {
+
+    lateinit var application: Application
+
+    @BeforeTest
+    fun setUp() {
+        application = ApplicationProvider.getApplicationContext()
+    }
+
+    @Test
+    fun testThatBatteryGetLowEmitValue() = runTest {
+        application.lowBatteryFlow().test {
+            assertThat(awaitItem()).isFalse()
+
+            application.sendBroadcast(Intent(Intent.ACTION_BATTERY_LOW))
+            shadowOf(Looper.getMainLooper()).idle()
+
+            assertThat(awaitItem()).isTrue()
+            cancelAndIgnoreRemainingEvents()
+        }
+    }
+
+    @Test
+    fun testThatBatteryGetOkayEmitValue() = runTest {
+        application.lowBatteryFlow().test {
+            // first battery is okay
+            assertThat(awaitItem()).isFalse()
+
+            // second when broadcast
+            application.sendBroadcast(Intent(Intent.ACTION_BATTERY_OKAY))
+            shadowOf(Looper.getMainLooper()).idle()
+
+            assertThat(awaitItem()).isFalse()
+            cancelAndIgnoreRemainingEvents()
+        }
+    }
+
+    @Test
+    @Config(minSdk = Build.VERSION_CODES.P)
+    fun testThatOnPWhenBatteryIsAlreadyLowFirstEmitValueIsCorrect() = runTest {
+        application.sendStickyBroadcast(Intent(Intent.ACTION_BATTERY_CHANGED).apply {
+            putExtra(BatteryManager.EXTRA_BATTERY_LOW, true)
+        })
+
+        application.lowBatteryFlow().test {
+            assertThat(awaitItem()).isTrue()
+            cancelAndIgnoreRemainingEvents()
+        }
+    }
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/geekdroid/src/test/java/com/geekorum/geekdroid/battery/PowerSaveModeFlowTest.kt	Tue Feb 18 00:03:44 2025 -0400
@@ -0,0 +1,75 @@
+/*
+ * Geekdroid is a utility library for development on the Android
+ * Platform.
+ *
+ * Copyright (C) 2017-2025 by Frederic-Charles Barthelery.
+ *
+ * This file is part of Geekdroid.
+ *
+ * Geekdroid is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Geekdroid is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Geekdroid.  If not, see <http://www.gnu.org/licenses/>.
+ */
+package com.geekorum.geekdroid.battery
+
+import android.app.Application
+import android.content.Intent
+import android.os.Build
+import android.os.Looper.getMainLooper
+import android.os.PowerManager
+import androidx.core.content.getSystemService
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import app.cash.turbine.test
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.robolectric.Shadows.shadowOf
+import org.robolectric.annotation.Config
+import org.robolectric.shadows.ShadowPowerManager
+import kotlin.test.BeforeTest
+
+
+@RunWith(AndroidJUnit4::class)
+@Config(minSdk = Build.VERSION_CODES.Q)
+class PowerSaveModeFlowTest {
+    lateinit var shadowPowerManager: ShadowPowerManager
+    lateinit var application: Application
+    lateinit var powerManager: PowerManager
+
+    @BeforeTest
+    fun setUp() {
+        application = ApplicationProvider.getApplicationContext()
+        powerManager = application.getSystemService()!!
+        shadowPowerManager = shadowOf(powerManager)
+    }
+
+    @Test
+    fun testThatWhenPowerSaveModeChangedEmitValue() = runTest {
+        isPowerSaveModeFlow(application, powerManager).test {
+            assertThat(awaitItem()).isFalse()
+
+            shadowPowerManager.setIsPowerSaveMode(true)
+            application.sendBroadcast(Intent(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED))
+            shadowOf(getMainLooper()).idle()
+            assertThat(awaitItem()).isTrue()
+
+            shadowPowerManager.setIsPowerSaveMode(false)
+            application.sendBroadcast(Intent(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED))
+            shadowOf(getMainLooper()).idle()
+            assertThat(awaitItem()).isFalse()
+
+        }
+    }
+
+}
\ No newline at end of file