From c7ea8ec2b2824fd390c2d4689edc52f1d36b6047 Mon Sep 17 00:00:00 2001
From: Matthias Urhahn <matthias.urhahn@sap.com>
Date: Fri, 13 Nov 2020 19:35:28 +0100
Subject: [PATCH] Improve cancelation exception handling (DEV) (#1591)

* Catch cancellation exceptions for coroutines launched on the viewModelScope.

* Use the viewmodel provided launch method to use common handling.

Co-authored-by: harambasicluka <64483219+harambasicluka@users.noreply.github.com>
Co-authored-by: chris-cwa <69595386+chris-cwa@users.noreply.github.com>
---
 .../test/crash.ui/SettingsCrashReportViewModel.kt      |  5 +----
 .../debugoptions/ui/DebugOptionsFragmentViewModel.kt   |  5 +----
 .../ui/TestRiskLevelCalculationFragmentCWAViewModel.kt | 10 ++++------
 .../onboarding/OnboardingTracingFragmentViewModel.kt   |  4 +---
 .../rki/coronawarnapp/util/viewmodel/CWAViewModel.kt   | 10 ++++++++--
 5 files changed, 15 insertions(+), 19 deletions(-)

diff --git a/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/crash.ui/SettingsCrashReportViewModel.kt b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/crash.ui/SettingsCrashReportViewModel.kt
index 234022907..44d3e0cd0 100644
--- a/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/crash.ui/SettingsCrashReportViewModel.kt
+++ b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/crash.ui/SettingsCrashReportViewModel.kt
@@ -4,7 +4,6 @@ import androidx.lifecycle.LiveData
 import androidx.lifecycle.MutableLiveData
 import androidx.lifecycle.asLiveData
 import androidx.lifecycle.map
-import androidx.lifecycle.viewModelScope
 import com.squareup.inject.assisted.AssistedInject
 import de.rki.coronawarnapp.bugreporting.event.BugEvent
 import de.rki.coronawarnapp.bugreporting.reportProblem
@@ -12,9 +11,7 @@ import de.rki.coronawarnapp.bugreporting.storage.repository.BugRepository
 import de.rki.coronawarnapp.util.viewmodel.CWAViewModel
 import de.rki.coronawarnapp.util.viewmodel.SimpleCWAViewModelFactory
 import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.launch
 import timber.log.Timber
-import java.lang.Exception
 
 class SettingsCrashReportViewModel @AssistedInject constructor(
     private val crashReportRepository: BugRepository
@@ -28,7 +25,7 @@ class SettingsCrashReportViewModel @AssistedInject constructor(
         createBugEventFormattedText(it)
     }
 
-    fun deleteAllCrashReports() = viewModelScope.launch(Dispatchers.IO) {
+    fun deleteAllCrashReports() = launch(Dispatchers.IO) {
         crashReportRepository.clear()
     }
 
diff --git a/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/debugoptions/ui/DebugOptionsFragmentViewModel.kt b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/debugoptions/ui/DebugOptionsFragmentViewModel.kt
index c58e64556..dd0542685 100644
--- a/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/debugoptions/ui/DebugOptionsFragmentViewModel.kt
+++ b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/debugoptions/ui/DebugOptionsFragmentViewModel.kt
@@ -1,7 +1,6 @@
 package de.rki.coronawarnapp.test.debugoptions.ui
 
 import android.content.Context
-import androidx.lifecycle.viewModelScope
 import com.squareup.inject.assisted.AssistedInject
 import de.rki.coronawarnapp.environment.EnvironmentSetup
 import de.rki.coronawarnapp.environment.EnvironmentSetup.Type.Companion.toEnvironmentType
@@ -19,8 +18,6 @@ import de.rki.coronawarnapp.util.ui.SingleLiveEvent
 import de.rki.coronawarnapp.util.ui.smartLiveData
 import de.rki.coronawarnapp.util.viewmodel.CWAViewModel
 import de.rki.coronawarnapp.util.viewmodel.SimpleCWAViewModelFactory
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.launch
 import java.io.File
 
 class DebugOptionsFragmentViewModel @AssistedInject constructor(
@@ -79,7 +76,7 @@ class DebugOptionsFragmentViewModel @AssistedInject constructor(
 
     fun shareLogFile() {
         CWADebug.fileLogger?.let {
-            viewModelScope.launch(context = Dispatchers.Default) {
+            launch {
                 if (!it.logFile.exists()) return@launch
 
                 val externalPath = File(
diff --git a/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/risklevel/ui/TestRiskLevelCalculationFragmentCWAViewModel.kt b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/risklevel/ui/TestRiskLevelCalculationFragmentCWAViewModel.kt
index d0a85e7f0..765ac237e 100644
--- a/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/risklevel/ui/TestRiskLevelCalculationFragmentCWAViewModel.kt
+++ b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/risklevel/ui/TestRiskLevelCalculationFragmentCWAViewModel.kt
@@ -4,7 +4,6 @@ import android.content.Context
 import androidx.lifecycle.MutableLiveData
 import androidx.lifecycle.SavedStateHandle
 import androidx.lifecycle.asLiveData
-import androidx.lifecycle.viewModelScope
 import com.google.android.gms.nearby.exposurenotification.ExposureInformation
 import com.squareup.inject.assisted.Assisted
 import com.squareup.inject.assisted.AssistedInject
@@ -37,7 +36,6 @@ import de.rki.coronawarnapp.util.viewmodel.CWAViewModel
 import de.rki.coronawarnapp.util.viewmodel.CWAViewModelFactory
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.flow.sample
-import kotlinx.coroutines.launch
 import kotlinx.coroutines.withContext
 import timber.log.Timber
 import java.io.File
@@ -90,7 +88,7 @@ class TestRiskLevelCalculationFragmentCWAViewModel @AssistedInject constructor(
     }
 
     fun resetRiskLevel() {
-        viewModelScope.launch {
+        launch {
             withContext(Dispatchers.IO) {
                 try {
                     // Preference reset
@@ -122,7 +120,7 @@ class TestRiskLevelCalculationFragmentCWAViewModel @AssistedInject constructor(
     )
 
     fun startENFObserver() {
-        viewModelScope.launch {
+        launch {
             try {
                 var workState = riskScoreState.value!!
 
@@ -263,7 +261,7 @@ class TestRiskLevelCalculationFragmentCWAViewModel @AssistedInject constructor(
         dir.mkdirs()
 
         var googleFileList: List<File>
-        viewModelScope.launch {
+        launch {
             googleFileList = KeyFileHelper.asyncCreateExportFiles(appleFiles, dir)
 
             Timber.i("Provide ${googleFileList.count()} files with ${appleKeyList.size} keys with token $token")
@@ -291,7 +289,7 @@ class TestRiskLevelCalculationFragmentCWAViewModel @AssistedInject constructor(
     }
 
     fun clearKeyCache() {
-        viewModelScope.launch { keyCacheRepository.clear() }
+        launch { keyCacheRepository.clear() }
     }
 
     @AssistedInject.Factory
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/onboarding/OnboardingTracingFragmentViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/onboarding/OnboardingTracingFragmentViewModel.kt
index 9d996c01e..a710675c0 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/onboarding/OnboardingTracingFragmentViewModel.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/onboarding/OnboardingTracingFragmentViewModel.kt
@@ -1,6 +1,5 @@
 package de.rki.coronawarnapp.ui.onboarding
 
-import androidx.lifecycle.viewModelScope
 import com.squareup.inject.assisted.AssistedInject
 import de.rki.coronawarnapp.exception.ExceptionCategory
 import de.rki.coronawarnapp.exception.reporting.report
@@ -10,7 +9,6 @@ import de.rki.coronawarnapp.storage.interoperability.InteroperabilityRepository
 import de.rki.coronawarnapp.ui.SingleLiveEvent
 import de.rki.coronawarnapp.util.viewmodel.CWAViewModel
 import de.rki.coronawarnapp.util.viewmodel.SimpleCWAViewModelFactory
-import kotlinx.coroutines.launch
 
 class OnboardingTracingFragmentViewModel @AssistedInject constructor(
     private val interoperabilityRepository: InteroperabilityRepository
@@ -25,7 +23,7 @@ class OnboardingTracingFragmentViewModel @AssistedInject constructor(
 
     // Reset tracing state in onboarding
     fun resetTracing() {
-        viewModelScope.launch {
+        launch {
             try {
                 if (InternalExposureNotificationClient.asyncIsEnabled()) {
                     InternalExposureNotificationClient.asyncStop()
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/viewmodel/CWAViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/viewmodel/CWAViewModel.kt
index 9f3eddff2..40cd6f768 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/viewmodel/CWAViewModel.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/viewmodel/CWAViewModel.kt
@@ -5,8 +5,8 @@ import androidx.lifecycle.ViewModel
 import androidx.lifecycle.viewModelScope
 import de.rki.coronawarnapp.util.coroutine.DefaultDispatcherProvider
 import de.rki.coronawarnapp.util.coroutine.DispatcherProvider
+import kotlinx.coroutines.CancellationException
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.Job
 import kotlinx.coroutines.launch
 import timber.log.Timber
 import kotlin.coroutines.CoroutineContext
@@ -29,7 +29,13 @@ abstract class CWAViewModel constructor(
     fun launch(
         context: CoroutineContext = dispatcherProvider.Default,
         block: suspend CoroutineScope.() -> Unit
-    ): Job = viewModelScope.launch(context = context, block = block)
+    ) {
+        try {
+            viewModelScope.launch(context = context, block = block)
+        } catch (e: CancellationException) {
+            Timber.w(e, "launch()ed coroutine was canceled.")
+        }
+    }
 
     @CallSuper
     override fun onCleared() {
-- 
GitLab