# HG changeset patch # User Da Risk # Date 1739851424 14400 # Node ID 84e46eaf1ea0b2afa9ab5f2eb9c6b58bd3f45e1c # Parent 3b8739febbfe2e54ae22bf407b4fc7b9b901905d geekdroid: add lowBatteryFlow() and isPowerSaveModeFlow() diff -r 3b8739febbfe -r 84e46eaf1ea0 geekdroid/src/main/java/com/geekorum/geekdroid/battery/LiveData.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() { @@ -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 diff -r 3b8739febbfe -r 84e46eaf1ea0 geekdroid/src/test/java/com/geekorum/geekdroid/battery/LowBatteryFlowTest.kt --- /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 . + */ +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 diff -r 3b8739febbfe -r 84e46eaf1ea0 geekdroid/src/test/java/com/geekorum/geekdroid/battery/PowerSaveModeFlowTest.kt --- /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 . + */ +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