From 5cf2819b8ccf6b1ca598ae86789d42b1f9ec4cb4 Mon Sep 17 00:00:00 2001 From: Matthias Urhahn Date: Fri, 15 Jan 2021 15:46:13 +0100 Subject: [PATCH] Fix ForegroundState not emitting updated values. --- .../rki/coronawarnapp/util/WatchdogService.kt | 8 ++- .../util/device/ForegroundState.kt | 14 +++-- .../coronawarnapp/util/di/AndroidModule.kt | 7 +++ .../coronawarnapp/util/di/ProcessLifecycle.kt | 8 +++ .../util/device/ForegroundStateTest.kt | 56 +++++++++++++++++++ 5 files changed, 84 insertions(+), 9 deletions(-) create mode 100644 Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/di/ProcessLifecycle.kt create mode 100644 Corona-Warn-App/src/test/java/de/rki/coronawarnapp/util/device/ForegroundStateTest.kt diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/WatchdogService.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/WatchdogService.kt index 562cfe70cf0..3365de84626 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/WatchdogService.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/WatchdogService.kt @@ -3,7 +3,7 @@ package de.rki.coronawarnapp.util import android.content.Context import android.net.wifi.WifiManager import android.os.PowerManager -import androidx.lifecycle.ProcessLifecycleOwner +import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.lifecycleScope import de.rki.coronawarnapp.diagnosiskeys.download.DownloadDiagnosisKeysTask import de.rki.coronawarnapp.storage.LocalData @@ -12,6 +12,7 @@ import de.rki.coronawarnapp.task.common.DefaultTaskRequest import de.rki.coronawarnapp.task.submitBlocking import de.rki.coronawarnapp.util.device.BackgroundModeStatus import de.rki.coronawarnapp.util.di.AppContext +import de.rki.coronawarnapp.util.di.ProcessLifecycle import de.rki.coronawarnapp.worker.BackgroundWorkScheduler import kotlinx.coroutines.flow.first import kotlinx.coroutines.launch @@ -25,7 +26,8 @@ import javax.inject.Singleton class WatchdogService @Inject constructor( @AppContext private val context: Context, private val taskController: TaskController, - private val backgroundModeStatus: BackgroundModeStatus + private val backgroundModeStatus: BackgroundModeStatus, + @ProcessLifecycle private val processLifecycleOwner: LifecycleOwner ) { private val powerManager by lazy { @@ -44,7 +46,7 @@ class WatchdogService @Inject constructor( } Timber.tag(TAG).v("Acquiring wakelocks for watchdog routine.") - ProcessLifecycleOwner.get().lifecycleScope.launch { + processLifecycleOwner.lifecycleScope.launch { // A wakelock as the OS does not handle this for us like in the background job execution val wakeLock = createWakeLock() // A wifi lock to wake up the wifi connection in case the device is dozing diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/device/ForegroundState.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/device/ForegroundState.kt index 2980ccbc299..1babb17239d 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/device/ForegroundState.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/device/ForegroundState.kt @@ -2,9 +2,9 @@ package de.rki.coronawarnapp.util.device import androidx.lifecycle.Lifecycle import androidx.lifecycle.LifecycleObserver +import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.OnLifecycleEvent -import androidx.lifecycle.ProcessLifecycleOwner -import de.rki.coronawarnapp.CoronaWarnApplication +import de.rki.coronawarnapp.util.di.ProcessLifecycle import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.onCompletion @@ -15,7 +15,9 @@ import javax.inject.Inject import javax.inject.Singleton @Singleton -class ForegroundState @Inject constructor() { +class ForegroundState @Inject constructor( + @ProcessLifecycle val processLifecycleOwner: LifecycleOwner +) { val isInForeground: Flow by lazy { MutableStateFlow(false).apply { @@ -23,19 +25,19 @@ class ForegroundState @Inject constructor() { @Suppress("unused") @OnLifecycleEvent(Lifecycle.Event.ON_START) fun onAppForegrounded() { - CoronaWarnApplication.isAppInForeground = true Timber.v("App is in the foreground") + tryEmit(true) } @Suppress("unused") @OnLifecycleEvent(Lifecycle.Event.ON_STOP) fun onAppBackgrounded() { - CoronaWarnApplication.isAppInForeground = false Timber.v("App is in the background") + tryEmit(false) } } - val processLifecycle = ProcessLifecycleOwner.get().lifecycle + val processLifecycle = processLifecycleOwner.lifecycle processLifecycle.addObserver(foregroundStateUpdater) } .onStart { Timber.v("isInForeground FLOW start") } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/di/AndroidModule.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/di/AndroidModule.kt index 245e67fe97c..3d3cda2e2a3 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/di/AndroidModule.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/di/AndroidModule.kt @@ -8,6 +8,8 @@ import android.content.Context import android.content.SharedPreferences import androidx.core.app.NotificationManagerCompat import androidx.core.content.getSystemService +import androidx.lifecycle.LifecycleOwner +import androidx.lifecycle.ProcessLifecycleOwner import androidx.navigation.NavDeepLinkBuilder import androidx.work.WorkManager import dagger.Module @@ -63,4 +65,9 @@ class AndroidModule { @Provides @Singleton fun activityManager(@AppContext context: Context): ActivityManager = context.getSystemService()!! + + @Provides + @Singleton + @ProcessLifecycle + fun procressLifecycleOwner(): LifecycleOwner = ProcessLifecycleOwner.get() } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/di/ProcessLifecycle.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/di/ProcessLifecycle.kt new file mode 100644 index 00000000000..ada39e1d77a --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/di/ProcessLifecycle.kt @@ -0,0 +1,8 @@ +package de.rki.coronawarnapp.util.di + +import javax.inject.Qualifier + +@Qualifier +@MustBeDocumented +@Retention(AnnotationRetention.RUNTIME) +annotation class ProcessLifecycle diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/util/device/ForegroundStateTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/util/device/ForegroundStateTest.kt new file mode 100644 index 00000000000..57ef31fc932 --- /dev/null +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/util/device/ForegroundStateTest.kt @@ -0,0 +1,56 @@ +package de.rki.coronawarnapp.util.device + +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.LifecycleOwner +import androidx.lifecycle.LifecycleRegistry +import io.kotest.matchers.shouldBe +import io.mockk.MockKAnnotations +import io.mockk.clearAllMocks +import io.mockk.every +import io.mockk.impl.annotations.MockK +import kotlinx.coroutines.test.runBlockingTest +import org.junit.jupiter.api.AfterEach +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import testhelpers.BaseTest +import testhelpers.coroutines.test + +class ForegroundStateTest : BaseTest() { + + @MockK lateinit var lifecycleOwner: LifecycleOwner + lateinit var lifecycle: LifecycleRegistry + + @BeforeEach + fun setup() { + MockKAnnotations.init(this) + lifecycle = LifecycleRegistry(lifecycleOwner) + every { lifecycleOwner.lifecycle } returns lifecycle + } + + @AfterEach + fun teardown() { + clearAllMocks() + } + + fun createInstance() = ForegroundState( + processLifecycleOwner = lifecycleOwner + ) + + @Test + fun `test emissions`() = runBlockingTest { + val instance = createInstance() + + val testCollector = instance.isInForeground.test(startOnScope = this) + + testCollector.latestValue shouldBe false + + lifecycle.handleLifecycleEvent(Lifecycle.Event.ON_START) + testCollector.latestValue shouldBe true + + lifecycle.handleLifecycleEvent(Lifecycle.Event.ON_STOP) + testCollector.latestValue shouldBe false + + testCollector.cancel() + advanceUntilIdle() + } +}