From a36cf7ee45d4b9ae68e52ad52e53a69958634a3e Mon Sep 17 00:00:00 2001
From: Matthias Urhahn <matthias.urhahn@sap.com>
Date: Fri, 15 Jan 2021 16:31:38 +0100
Subject: [PATCH] Fix ForegroundState not emitting updated values. (#2112)

---
 .../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 562cfe70c..3365de846 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 2980ccbc2..1babb1723 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<Boolean> 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 245e67fe9..3d3cda2e2 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 000000000..ada39e1d7
--- /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 000000000..57ef31fc9
--- /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()
+    }
+}
-- 
GitLab