From c8a0cfd05a1a8864d070aeab057e9367369cdf5f Mon Sep 17 00:00:00 2001 From: AlexanderAlferov <64849422+AlexanderAlferov@users.noreply.github.com> Date: Thu, 10 Dec 2020 19:25:53 +0300 Subject: [PATCH] New Submission Flow: new test result notification (EXPOSUREAPP-4134) (#1825) * Test result available notification added * Refactored NotificationHelper to injectable class Adjusted tests and classes * Use the pending test result fragment as forwarding tool. * Fix test regression. Co-authored-by: Ralf Gehrer <ralfgehrer@users.noreply.github.com> Co-authored-by: Matthias Urhahn <matthias.urhahn@sap.com> --- .../coronawarnapp/CoronaWarnApplication.kt | 3 +- .../notification/NotificationConstants.kt | 1 + .../notification/NotificationHelper.kt | 60 ++++----- .../TestResultAvailableNotification.kt | 48 ++++++++ .../TestResultNotificationService.kt | 11 +- .../risk/RiskLevelChangeDetector.kt | 5 +- .../coronawarnapp/util/di/AndroidModule.kt | 4 + .../NavDeepLinkBuilderFactory.kt | 13 ++ ...gnosisTestResultRetrievalPeriodicWorker.kt | 21 ++-- .../TestResultAvailableNotificationTest.kt | 114 ++++++++++++++++++ .../risk/RiskLevelChangeDetectorTest.kt | 5 +- .../util/worker/WorkerBinderTest.kt | 8 ++ 12 files changed, 234 insertions(+), 59 deletions(-) create mode 100644 Corona-Warn-App/src/main/java/de/rki/coronawarnapp/notification/TestResultAvailableNotification.kt create mode 100644 Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/notifications/NavDeepLinkBuilderFactory.kt create mode 100644 Corona-Warn-App/src/test/java/de/rki/coronawarnapp/notification/TestResultAvailableNotificationTest.kt diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/CoronaWarnApplication.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/CoronaWarnApplication.kt index 7bdbcd80d..faa23d485 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/CoronaWarnApplication.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/CoronaWarnApplication.kt @@ -48,6 +48,7 @@ class CoronaWarnApplication : Application(), HasAndroidInjector { @Inject lateinit var configChangeDetector: ConfigChangeDetector @Inject lateinit var riskLevelChangeDetector: RiskLevelChangeDetector @Inject lateinit var deadmanNotificationScheduler: DeadmanNotificationScheduler + @Inject lateinit var notificationHelper: NotificationHelper @LogHistoryTree @Inject lateinit var rollingLogHistory: Timber.Tree override fun onCreate() { @@ -62,7 +63,7 @@ class CoronaWarnApplication : Application(), HasAndroidInjector { Timber.v("onCreate(): WorkManager setup done: $workManager") - NotificationHelper.createNotificationChannel() + notificationHelper.createNotificationChannel() // Enable Conscrypt for TLS1.3 Support below API Level 29 Security.insertProviderAt(Conscrypt.newProvider(), 1) diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/notification/NotificationConstants.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/notification/NotificationConstants.kt index c8e44982d..66a40de28 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/notification/NotificationConstants.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/notification/NotificationConstants.kt @@ -19,6 +19,7 @@ object NotificationConstants { const val NEW_MESSAGE_RISK_LEVEL_SCORE_NOTIFICATION_ID = 110 const val NEW_MESSAGE_TEST_RESULT_NOTIFICATION_ID = 120 + const val TEST_RESULT_AVAILABLE_NOTIFICATION_ID = 130 /** * Notification channel id String.xml path diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/notification/NotificationHelper.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/notification/NotificationHelper.kt index d33f520cd..f0f2db46c 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/notification/NotificationHelper.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/notification/NotificationHelper.kt @@ -14,14 +14,16 @@ import android.os.Build import androidx.core.app.NotificationCompat import androidx.core.app.NotificationCompat.PRIORITY_HIGH import androidx.core.app.NotificationManagerCompat +import dagger.Reusable import de.rki.coronawarnapp.BuildConfig -import de.rki.coronawarnapp.CoronaWarnApplication import de.rki.coronawarnapp.R import de.rki.coronawarnapp.notification.NotificationConstants.NOTIFICATION_ID import de.rki.coronawarnapp.ui.main.MainActivity +import de.rki.coronawarnapp.util.di.AppContext import org.joda.time.Duration import org.joda.time.Instant import timber.log.Timber +import javax.inject.Inject /** * Singleton class for notification handling @@ -30,23 +32,22 @@ import timber.log.Timber * * @see NotificationConstants */ -object NotificationHelper { +@Reusable +class NotificationHelper @Inject constructor( + @AppContext private val context: Context +) { /** * Notification channel id * * @see NotificationConstants.NOTIFICATION_CHANNEL_ID */ - private val channelId = - CoronaWarnApplication.getAppContext() - .getString(NotificationConstants.NOTIFICATION_CHANNEL_ID) + private val channelId = context.getString(NotificationConstants.NOTIFICATION_CHANNEL_ID) /** * Notification manager */ - private val notificationManager = - CoronaWarnApplication.getAppContext() - .getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager + private val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager /** * Notification channel audio attributes @@ -70,7 +71,7 @@ object NotificationHelper { fun createNotificationChannel() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { val channelName = - CoronaWarnApplication.getAppContext().getString(NotificationConstants.CHANNEL_NAME) + context.getString(NotificationConstants.CHANNEL_NAME) val notificationRingtone = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION) @@ -79,7 +80,7 @@ object NotificationHelper { NotificationChannel(channelId, channelName, NotificationManager.IMPORTANCE_HIGH) channel.description = - CoronaWarnApplication.getAppContext() + context .getString(NotificationConstants.CHANNEL_DESCRIPTION) channel.setSound(notificationRingtone, audioAttributes) notificationManager.createNotificationChannel(channel) @@ -89,13 +90,13 @@ object NotificationHelper { fun cancelFutureNotifications(notificationId: Int) { val pendingIntent = createPendingIntentToScheduleNotification(notificationId) val manager = - CoronaWarnApplication.getAppContext().getSystemService(Context.ALARM_SERVICE) as AlarmManager + context.getSystemService(Context.ALARM_SERVICE) as AlarmManager manager.cancel(pendingIntent) Timber.v("Canceled future notifications with id: %s", notificationId) } fun cancelCurrentNotification(notificationId: Int) { - NotificationManagerCompat.from(CoronaWarnApplication.getAppContext()).cancel(notificationId) + NotificationManagerCompat.from(context).cancel(notificationId) Timber.v("Canceled notifications with id: %s", notificationId) } @@ -106,7 +107,7 @@ object NotificationHelper { ) { val pendingIntent = createPendingIntentToScheduleNotification(notificationId) val manager = - CoronaWarnApplication.getAppContext().getSystemService(Context.ALARM_SERVICE) as AlarmManager + context.getSystemService(Context.ALARM_SERVICE) as AlarmManager manager.setInexactRepeating(AlarmManager.RTC, initialTime.millis, interval.millis, pendingIntent) } @@ -115,9 +116,9 @@ object NotificationHelper { flag: Int = FLAG_CANCEL_CURRENT ) = PendingIntent.getBroadcast( - CoronaWarnApplication.getAppContext(), + context, notificationId, - Intent(CoronaWarnApplication.getAppContext(), NotificationReceiver::class.java).apply { + Intent(context, NotificationReceiver::class.java).apply { putExtra(NOTIFICATION_ID, notificationId) }, flag) @@ -141,7 +142,7 @@ object NotificationHelper { expandableLongText: Boolean = false, pendingIntent: PendingIntent = createPendingIntentToMainActivity() ): Notification? { - val builder = NotificationCompat.Builder(CoronaWarnApplication.getAppContext(), channelId) + val builder = NotificationCompat.Builder(context, channelId) .setSmallIcon(NotificationConstants.NOTIFICATION_SMALL_ICON) .setPriority(NotificationCompat.PRIORITY_MAX) .setVisibility(visibility) @@ -182,9 +183,9 @@ object NotificationHelper { */ private fun createPendingIntentToMainActivity() = PendingIntent.getActivity( - CoronaWarnApplication.getAppContext(), + context, 0, - Intent(CoronaWarnApplication.getAppContext(), MainActivity::class.java), + Intent(context, MainActivity::class.java), 0 ) @@ -199,7 +200,7 @@ object NotificationHelper { * @param pendingIntent: PendingIntent */ fun sendNotification( - title: String = CoronaWarnApplication.getAppContext().getString(R.string.notification_name), + title: String = context.getString(R.string.notification_name), content: String, notificationId: NotificationId, expandableLongText: Boolean = false, @@ -208,30 +209,11 @@ object NotificationHelper { Timber.d("Sending notification with id: %s | title: %s | content: %s", notificationId, title, content) val notification = buildNotification(title, content, PRIORITY_HIGH, expandableLongText, pendingIntent) ?: return - with(NotificationManagerCompat.from(CoronaWarnApplication.getAppContext())) { + with(NotificationManagerCompat.from(context)) { notify(notificationId, notification) } } - /** - * Send notification - * Build and send notification with content and visibility. - * Notification is only sent if app is not in foreground. - * - * @param content: String - * @param notificationId: NotificationId - */ - fun sendNotificationIfAppIsNotInForeground(content: String, notificationId: NotificationId) { - if (!CoronaWarnApplication.isAppInForeground) { - sendNotification( - content = content, - notificationId = notificationId, - expandableLongText = true) - } else { - Timber.d("App is in foreground - not sending the notification with id: %s", notificationId) - } - } - /** * Log notification build * Log success or failure of creating new notification diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/notification/TestResultAvailableNotification.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/notification/TestResultAvailableNotification.kt new file mode 100644 index 000000000..9b99459d2 --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/notification/TestResultAvailableNotification.kt @@ -0,0 +1,48 @@ +package de.rki.coronawarnapp.notification + +import android.content.Context +import androidx.navigation.NavDeepLinkBuilder +import de.rki.coronawarnapp.R +import de.rki.coronawarnapp.ui.main.MainActivity +import de.rki.coronawarnapp.util.ForegroundState +import de.rki.coronawarnapp.util.di.AppContext +import de.rki.coronawarnapp.util.formatter.TestResult +import kotlinx.coroutines.flow.first +import javax.inject.Inject +import javax.inject.Provider + +class TestResultAvailableNotification @Inject constructor( + @AppContext private val context: Context, + private val foregroundState: ForegroundState, + private val navDeepLinkBuilderProvider: Provider<NavDeepLinkBuilder>, + private val notificationHelper: NotificationHelper +) { + + suspend fun showTestResultNotification(testResult: TestResult) { + if (foregroundState.isInForeground.first()) return + + val pendingIntent = navDeepLinkBuilderProvider.get().apply { + setGraph(R.navigation.nav_graph) + setComponentName(MainActivity::class.java) + setDestination(getNotificationDestination(testResult)) + }.createPendingIntent() + + notificationHelper.sendNotification( + title = context.getString(R.string.notification_headline_test_result_ready), + content = context.getString(R.string.notification_body_test_result_ready), + notificationId = NotificationConstants.TEST_RESULT_AVAILABLE_NOTIFICATION_ID, + pendingIntent = pendingIntent + ) + } + + /** + * The pending result fragment will forward to the correct screen + * Because we can't save the test result at the moment (legal), + * it needs to be reloaded each time. + * If we navigate directly to the positive/negative result screen, + * then we also need to add explicit test loading logic there. + * By letting the forwarding happen via the PendingResultFragment, + * we have a common location to retrieve the test result. + */ + fun getNotificationDestination(testResult: TestResult): Int = R.id.submissionTestResultPendingFragment +} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/notification/TestResultNotificationService.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/notification/TestResultNotificationService.kt index ecfeb8f79..4bffacf4c 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/notification/TestResultNotificationService.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/notification/TestResultNotificationService.kt @@ -16,14 +16,15 @@ import javax.inject.Inject class TestResultNotificationService @Inject constructor( @AppContext private val context: Context, - private val timeStamper: TimeStamper + private val timeStamper: TimeStamper, + private val notificationHelper: NotificationHelper ) { fun schedulePositiveTestResultReminder() { if (LocalData.numberOfRemainingPositiveTestResultReminders < 0) { Timber.v("Schedule positive test result notification") LocalData.numberOfRemainingPositiveTestResultReminders = POSITIVE_RESULT_NOTIFICATION_TOTAL_COUNT - NotificationHelper.scheduleRepeatingNotification( + notificationHelper.scheduleRepeatingNotification( timeStamper.nowUTC.plus(POSITIVE_RESULT_NOTIFICATION_INITIAL_OFFSET), POSITIVE_RESULT_NOTIFICATION_INTERVAL, POSITIVE_RESULT_NOTIFICATION_ID @@ -42,19 +43,19 @@ class TestResultNotificationService @Inject constructor( .setDestination(R.id.submissionTestResultAvailableFragment) .createPendingIntent() - NotificationHelper.sendNotification( + notificationHelper.sendNotification( title = context.getString(R.string.notification_headline_share_positive_result), content = context.getString(R.string.notification_body_share_positive_result), notificationId = notificationId, pendingIntent = pendingIntent ) } else { - NotificationHelper.cancelFutureNotifications(notificationId) + notificationHelper.cancelFutureNotifications(notificationId) } } fun cancelPositiveTestResultNotification() { - NotificationHelper.cancelFutureNotifications(POSITIVE_RESULT_NOTIFICATION_ID) + notificationHelper.cancelFutureNotifications(POSITIVE_RESULT_NOTIFICATION_ID) Timber.v("Future positive test result notifications have been canceled") } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/risk/RiskLevelChangeDetector.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/risk/RiskLevelChangeDetector.kt index e2d403a1d..5aff973f1 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/risk/RiskLevelChangeDetector.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/risk/RiskLevelChangeDetector.kt @@ -27,7 +27,8 @@ class RiskLevelChangeDetector @Inject constructor( private val riskLevelStorage: RiskLevelStorage, private val riskLevelSettings: RiskLevelSettings, private val notificationManagerCompat: NotificationManagerCompat, - private val foregroundState: ForegroundState + private val foregroundState: ForegroundState, + private val notificationHelper: NotificationHelper ) { fun launch() { @@ -65,7 +66,7 @@ class RiskLevelChangeDetector @Inject constructor( Timber.d("Notification Permission = ${notificationManagerCompat.areNotificationsEnabled()}") if (!foregroundState.isInForeground.first()) { - NotificationHelper.sendNotification( + notificationHelper.sendNotification( content = context.getString(R.string.notification_body), notificationId = NEW_MESSAGE_RISK_LEVEL_SCORE_NOTIFICATION_ID ) 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 c8a9be0e7..a5d549bff 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 @@ -5,6 +5,7 @@ import android.bluetooth.BluetoothAdapter import android.content.Context import android.content.SharedPreferences import androidx.core.app.NotificationManagerCompat +import androidx.navigation.NavDeepLinkBuilder import androidx.work.WorkManager import dagger.Module import dagger.Provides @@ -46,4 +47,7 @@ class AndroidModule { @Provides @Singleton fun encryptedPreferences(): SharedPreferences = SecurityHelper.globalEncryptedSharedPreferencesInstance + + @Provides + fun navDeepLinkBuilder(@AppContext context: Context): NavDeepLinkBuilder = NavDeepLinkBuilder(context) } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/notifications/NavDeepLinkBuilderFactory.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/notifications/NavDeepLinkBuilderFactory.kt new file mode 100644 index 000000000..91de2b267 --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/notifications/NavDeepLinkBuilderFactory.kt @@ -0,0 +1,13 @@ +package de.rki.coronawarnapp.util.notifications + +import android.content.Context +import androidx.navigation.NavDeepLinkBuilder +import dagger.Reusable +import javax.inject.Inject + +@Reusable +class NavDeepLinkBuilderFactory @Inject constructor() { + fun create(context: Context): NavDeepLinkBuilder { + return NavDeepLinkBuilder(context) + } +} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/worker/DiagnosisTestResultRetrievalPeriodicWorker.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/worker/DiagnosisTestResultRetrievalPeriodicWorker.kt index 1e5a28128..0ced5280b 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/worker/DiagnosisTestResultRetrievalPeriodicWorker.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/worker/DiagnosisTestResultRetrievalPeriodicWorker.kt @@ -5,12 +5,10 @@ import androidx.work.CoroutineWorker import androidx.work.WorkerParameters import com.squareup.inject.assisted.Assisted import com.squareup.inject.assisted.AssistedInject -import de.rki.coronawarnapp.CoronaWarnApplication -import de.rki.coronawarnapp.R import de.rki.coronawarnapp.exception.NoRegistrationTokenSetException -import de.rki.coronawarnapp.notification.NotificationConstants.NEW_MESSAGE_RISK_LEVEL_SCORE_NOTIFICATION_ID -import de.rki.coronawarnapp.notification.NotificationConstants.NEW_MESSAGE_TEST_RESULT_NOTIFICATION_ID +import de.rki.coronawarnapp.notification.NotificationConstants import de.rki.coronawarnapp.notification.NotificationHelper +import de.rki.coronawarnapp.notification.TestResultAvailableNotification import de.rki.coronawarnapp.service.submission.SubmissionService import de.rki.coronawarnapp.storage.LocalData import de.rki.coronawarnapp.util.TimeAndDateExtensions @@ -27,7 +25,9 @@ import timber.log.Timber class DiagnosisTestResultRetrievalPeriodicWorker @AssistedInject constructor( @Assisted val context: Context, @Assisted workerParams: WorkerParameters, - private val submissionService: SubmissionService + private val submissionService: SubmissionService, + private val testResultAvailableNotification: TestResultAvailableNotification, + private val notificationHelper: NotificationHelper ) : CoroutineWorker(context, workerParams) { /** @@ -84,7 +84,7 @@ class DiagnosisTestResultRetrievalPeriodicWorker @AssistedInject constructor( * @see LocalData.initialPollingForTestResultTimeStamp * @see TestResult */ - private fun initiateNotification(testResult: TestResult) { + private suspend fun initiateNotification(testResult: TestResult) { if (LocalData.isTestResultNotificationSent() || LocalData.submissionWasSuccessful()) { Timber.tag(TAG).d("$id: Notification already sent or there was a successful submission") return @@ -93,11 +93,10 @@ class DiagnosisTestResultRetrievalPeriodicWorker @AssistedInject constructor( if (testResult == TestResult.NEGATIVE || testResult == TestResult.POSITIVE || testResult == TestResult.INVALID ) { - NotificationHelper.sendNotificationIfAppIsNotInForeground( - CoronaWarnApplication.getAppContext().getString(R.string.notification_body), - NEW_MESSAGE_TEST_RESULT_NOTIFICATION_ID - ) - NotificationHelper.cancelCurrentNotification(NEW_MESSAGE_RISK_LEVEL_SCORE_NOTIFICATION_ID) + testResultAvailableNotification.showTestResultNotification(testResult) + + notificationHelper.cancelCurrentNotification( + NotificationConstants.NEW_MESSAGE_RISK_LEVEL_SCORE_NOTIFICATION_ID) Timber.tag(TAG).d("$id: Test Result available - notification issued & risk level notification canceled") LocalData.isTestResultNotificationSent(true) diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/notification/TestResultAvailableNotificationTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/notification/TestResultAvailableNotificationTest.kt new file mode 100644 index 000000000..9355950b4 --- /dev/null +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/notification/TestResultAvailableNotificationTest.kt @@ -0,0 +1,114 @@ +package de.rki.coronawarnapp.notification + +import android.app.NotificationManager +import android.app.PendingIntent +import android.content.Context +import androidx.navigation.NavDeepLinkBuilder +import de.rki.coronawarnapp.CoronaWarnApplication +import de.rki.coronawarnapp.R +import de.rki.coronawarnapp.util.ForegroundState +import de.rki.coronawarnapp.util.formatter.TestResult +import io.kotest.matchers.shouldBe +import io.mockk.MockKAnnotations +import io.mockk.Runs +import io.mockk.clearAllMocks +import io.mockk.coEvery +import io.mockk.every +import io.mockk.impl.annotations.MockK +import io.mockk.just +import io.mockk.mockkObject +import io.mockk.verify +import io.mockk.verifyOrder +import kotlinx.coroutines.flow.flow +import kotlinx.coroutines.test.runBlockingTest +import org.junit.jupiter.api.AfterEach +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import javax.inject.Provider + +class TestResultAvailableNotificationTest { + + @MockK(relaxed = true) lateinit var context: Context + @MockK lateinit var foregroundState: ForegroundState + @MockK(relaxed = true) lateinit var navDeepLinkBuilder: NavDeepLinkBuilder + @MockK lateinit var pendingIntent: PendingIntent + @MockK lateinit var navDeepLinkBuilderProvider: Provider<NavDeepLinkBuilder> + @MockK lateinit var notificationManager: NotificationManager + @MockK lateinit var notificationHelper: NotificationHelper + + @BeforeEach + fun setUp() { + MockKAnnotations.init(this) + + mockkObject(CoronaWarnApplication) + + every { CoronaWarnApplication.getAppContext() } returns context + every { context.getString(NotificationConstants.NOTIFICATION_CHANNEL_ID) } returns "notification_channel_id" + every { context.getSystemService(Context.NOTIFICATION_SERVICE) } returns notificationManager + every { navDeepLinkBuilderProvider.get() } returns navDeepLinkBuilder + every { navDeepLinkBuilder.createPendingIntent() } returns pendingIntent + } + + @AfterEach + fun teardown() { + clearAllMocks() + } + + fun createInstance() = TestResultAvailableNotification( + context = context, + foregroundState = foregroundState, + navDeepLinkBuilderProvider = navDeepLinkBuilderProvider, + notificationHelper = notificationHelper + ) + + @Test + fun `check destination`() { + val negative = createInstance().getNotificationDestination(TestResult.NEGATIVE) + negative shouldBe (R.id.submissionTestResultPendingFragment) + + val invalid = createInstance().getNotificationDestination(TestResult.INVALID) + invalid shouldBe (R.id.submissionTestResultPendingFragment) + + val positive = createInstance().getNotificationDestination(TestResult.POSITIVE) + positive shouldBe (R.id.submissionTestResultPendingFragment) + } + + @Test + fun `test notification in foreground`() = runBlockingTest { + coEvery { foregroundState.isInForeground } returns flow { emit(true) } + + createInstance().showTestResultNotification(TestResult.POSITIVE) + + verify(exactly = 0) { navDeepLinkBuilderProvider.get() } + } + + @Test + fun `test notification in background`() = runBlockingTest { + coEvery { foregroundState.isInForeground } returns flow { emit(false) } + every { + notificationHelper.sendNotification( + title = any(), + content = any(), + notificationId = any(), + pendingIntent = any() + ) + } just Runs + + val instance = createInstance() + + instance.showTestResultNotification(TestResult.POSITIVE) + + verifyOrder { + navDeepLinkBuilderProvider.get() + instance.getNotificationDestination(TestResult.POSITIVE) + context.getString(R.string.notification_headline_test_result_ready) + context.getString(R.string.notification_body_test_result_ready) + notificationHelper.sendNotification( + title = any(), + content = any(), + notificationId = any(), + pendingIntent = any() + ) + } + } +} diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/risk/RiskLevelChangeDetectorTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/risk/RiskLevelChangeDetectorTest.kt index f8b920807..f9e7e25d8 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/risk/RiskLevelChangeDetectorTest.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/risk/RiskLevelChangeDetectorTest.kt @@ -3,6 +3,7 @@ package de.rki.coronawarnapp.risk import android.content.Context import androidx.core.app.NotificationManagerCompat import com.google.android.gms.nearby.exposurenotification.ExposureWindow +import de.rki.coronawarnapp.notification.NotificationHelper import de.rki.coronawarnapp.risk.RiskState.CALCULATION_FAILED import de.rki.coronawarnapp.risk.RiskState.INCREASED_RISK import de.rki.coronawarnapp.risk.RiskState.LOW_RISK @@ -38,6 +39,7 @@ class RiskLevelChangeDetectorTest : BaseTest() { @MockK lateinit var notificationManagerCompat: NotificationManagerCompat @MockK lateinit var foregroundState: ForegroundState @MockK lateinit var riskLevelSettings: RiskLevelSettings + @MockK lateinit var notificationHelper: NotificationHelper @BeforeEach fun setup() { @@ -77,7 +79,8 @@ class RiskLevelChangeDetectorTest : BaseTest() { riskLevelStorage = riskLevelStorage, notificationManagerCompat = notificationManagerCompat, foregroundState = foregroundState, - riskLevelSettings = riskLevelSettings + riskLevelSettings = riskLevelSettings, + notificationHelper = notificationHelper ) @Test diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/util/worker/WorkerBinderTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/util/worker/WorkerBinderTest.kt index 385d00586..3dfbb16c0 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/util/worker/WorkerBinderTest.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/util/worker/WorkerBinderTest.kt @@ -7,6 +7,8 @@ import dagger.Provides import de.rki.coronawarnapp.deadman.DeadmanNotificationScheduler import de.rki.coronawarnapp.deadman.DeadmanNotificationSender import de.rki.coronawarnapp.nearby.ENFClient +import de.rki.coronawarnapp.notification.NotificationHelper +import de.rki.coronawarnapp.notification.TestResultAvailableNotification import de.rki.coronawarnapp.playbook.Playbook import de.rki.coronawarnapp.risk.storage.RiskLevelStorage import de.rki.coronawarnapp.task.TaskController @@ -96,4 +98,10 @@ class MockProvider { @Provides fun exposureSummaryRepository(): RiskLevelStorage = mockk() + + @Provides + fun testResultAvailableNotification(): TestResultAvailableNotification = mockk() + + @Provides + fun notificationHelper(): NotificationHelper = mockk() } -- GitLab