Skip to content

Commit

Permalink
Battery Status flow (#1849)
Browse files Browse the repository at this point in the history
  • Loading branch information
yschimke authored Dec 4, 2023
1 parent 1dcdf49 commit 027cecd
Show file tree
Hide file tree
Showing 4 changed files with 199 additions and 0 deletions.
24 changes: 24 additions & 0 deletions network-awareness/core/api/current.api
Original file line number Diff line number Diff line change
@@ -1,4 +1,28 @@
// Signature format: 4.0
package com.google.android.horologist.networks.battery {

public final class BatteryStatusMonitor {
ctor public BatteryStatusMonitor(android.content.Context context);
method public kotlinx.coroutines.flow.Flow<com.google.android.horologist.networks.battery.BatteryStatusMonitor.BatteryStatus> getStatus();
property public final kotlinx.coroutines.flow.Flow<com.google.android.horologist.networks.battery.BatteryStatusMonitor.BatteryStatus> status;
}

public static final class BatteryStatusMonitor.BatteryStatus {
ctor public BatteryStatusMonitor.BatteryStatus(boolean charging, boolean deviceIdleMode, boolean powerSaveMode);
method public boolean component1();
method public boolean component2();
method public boolean component3();
method public com.google.android.horologist.networks.battery.BatteryStatusMonitor.BatteryStatus copy(boolean charging, boolean deviceIdleMode, boolean powerSaveMode);
method public boolean getCharging();
method public boolean getDeviceIdleMode();
method public boolean getPowerSaveMode();
property public final boolean charging;
property public final boolean deviceIdleMode;
property public final boolean powerSaveMode;
}

}

package com.google.android.horologist.networks.data {

@com.google.android.horologist.annotations.ExperimentalHorologistApi public final class DataRequest {
Expand Down
1 change: 1 addition & 0 deletions network-awareness/core/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ dependencies {
api(libs.kotlin.stdlib)
api(libs.kotlinx.coroutines.core)
implementation(libs.androidx.tracing.ktx)
implementation(libs.androidx.core)

testImplementation(libs.junit)
testImplementation(libs.truth)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
/*
* Copyright 2023 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
*
* https://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.google.android.horologist.networks.battery

import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.Intent.ACTION_BATTERY_LOW
import android.content.Intent.ACTION_BATTERY_OKAY
import android.content.IntentFilter
import android.os.BatteryManager
import android.os.BatteryManager.ACTION_CHARGING
import android.os.BatteryManager.ACTION_DISCHARGING
import android.os.PowerManager
import android.os.PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED
import android.os.PowerManager.ACTION_POWER_SAVE_MODE_CHANGED
import androidx.core.content.ContextCompat
import androidx.core.content.ContextCompat.RECEIVER_EXPORTED
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.flow.emitAll
import kotlinx.coroutines.flow.flow

public class BatteryStatusMonitor(
private val context: Context,
) {
private val powerManager: PowerManager = context.getSystemService(PowerManager::class.java)
private val batteryManager: BatteryManager =
context.getSystemService(BatteryManager::class.java)

private val subscriptionFlow = callbackFlow {
val receiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
trySend(batteryStatus())
}
}

ContextCompat.registerReceiver(
context,
receiver,
IntentFilter().apply {
addAction(ACTION_DEVICE_IDLE_MODE_CHANGED)
addAction(ACTION_POWER_SAVE_MODE_CHANGED)
addAction(ACTION_CHARGING)
addAction(ACTION_DISCHARGING)
addAction(ACTION_BATTERY_LOW)
addAction(ACTION_BATTERY_OKAY)
},
RECEIVER_EXPORTED,
)

awaitClose {
context.unregisterReceiver(receiver)
}
}

public val status: Flow<BatteryStatus> = flow {
emit(batteryStatus())
emitAll(subscriptionFlow)
}

private fun batteryStatus() =
BatteryStatus(
batteryManager.isCharging,
powerManager.isDeviceIdleMode,
powerManager.isPowerSaveMode,
)

public data class BatteryStatus(
val charging: Boolean,
val deviceIdleMode: Boolean,
val powerSaveMode: Boolean,
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
/*
* Copyright 2023 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
*
* https://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.
*/

@file:OptIn(ExperimentalCoroutinesApi::class)

package com.google.android.horologist.networks.battery

import android.app.Application
import android.os.BatteryManager
import android.os.PowerManager
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.Shadows.shadowOf
import org.robolectric.annotation.Config

@RunWith(AndroidJUnit4::class)
@Config(sdk = [33])
class BatteryStatusMonitorTest {
private lateinit var powerManager: PowerManager
private lateinit var batteryManager: BatteryManager
private lateinit var batteryStatusMonitor: BatteryStatusMonitor

@Before
fun setup() {
val applicationContext = ApplicationProvider.getApplicationContext<Application>()
batteryManager = applicationContext.getSystemService(BatteryManager::class.java)
powerManager = applicationContext.getSystemService(PowerManager::class.java)
batteryStatusMonitor = BatteryStatusMonitor(applicationContext)
}

@Test
fun readsFromBatteryManager() = runTest {
var status = batteryStatusMonitor.status.first()

assertThat(status).isEqualTo(
BatteryStatusMonitor.BatteryStatus(
batteryManager.isCharging,
powerManager.isDeviceIdleMode,
powerManager.isPowerSaveMode,
),
)

shadowOf(batteryManager).setIsCharging(false)
status = batteryStatusMonitor.status.first()

assertThat(status).isEqualTo(
BatteryStatusMonitor.BatteryStatus(
false,
powerManager.isDeviceIdleMode,
powerManager.isPowerSaveMode,
),
)

shadowOf(batteryManager).setIsCharging(true)
status = batteryStatusMonitor.status.first()

assertThat(status).isEqualTo(
BatteryStatusMonitor.BatteryStatus(
true,
powerManager.isDeviceIdleMode,
powerManager.isPowerSaveMode,
),
)
}
}

0 comments on commit 027cecd

Please sign in to comment.