From 2df14a45f90d74f038a4e7f0cff761f85247bd76 Mon Sep 17 00:00:00 2001 From: Aleksandr Zhukov Date: Thu, 18 Jun 2026 18:49:49 +0200 Subject: [PATCH] APPS-3853 Migrate streak display to summaries --- .../core/presenters/HomeStreakPresenter.kt | 12 +- .../repository/UserActivityRepositoryImpl.kt | 4 + .../source/UserActivityRemoteDataSource.kt | 3 + .../interactor/ProfileActivitiesInteractor.kt | 34 ++--- .../streak/interactor/StreakInteractor.kt | 7 +- .../repository/UserActivityRepository.kt | 3 + .../UserActivityRemoteDataSourceImpl.kt | 10 ++ .../model/UserActivitySummaryResponse.kt | 13 ++ .../service/UserActivityService.kt | 4 + .../StreakNotificationDelegate.kt | 55 ++++--- .../ProfileActivitiesInteractorTest.kt | 89 ++++++++++++ .../StreakNotificationDelegateTest.kt | 134 ++++++++++++++++++ .../android/model/user/UserActivitySummary.kt | 20 +++ 13 files changed, 331 insertions(+), 57 deletions(-) create mode 100644 app/src/main/java/org/stepik/android/remote/user_activity/model/UserActivitySummaryResponse.kt create mode 100644 app/src/test/java/org/stepik/android/domain/profile_activities/interactor/ProfileActivitiesInteractorTest.kt create mode 100644 app/src/test/java/org/stepik/android/view/streak/notification/StreakNotificationDelegateTest.kt create mode 100644 model/src/main/java/org/stepik/android/model/user/UserActivitySummary.kt diff --git a/app/src/main/java/org/stepic/droid/core/presenters/HomeStreakPresenter.kt b/app/src/main/java/org/stepic/droid/core/presenters/HomeStreakPresenter.kt index 70debf520c..c1738cfaec 100644 --- a/app/src/main/java/org/stepic/droid/core/presenters/HomeStreakPresenter.kt +++ b/app/src/main/java/org/stepic/droid/core/presenters/HomeStreakPresenter.kt @@ -10,10 +10,8 @@ import org.stepic.droid.di.home.HomeScope import org.stepic.droid.di.qualifiers.BackgroundScheduler import org.stepic.droid.di.qualifiers.MainScheduler import org.stepic.droid.preferences.SharedPreferenceHelper -import org.stepic.droid.util.StepikUtil import org.stepik.android.domain.user_activity.repository.UserActivityRepository import ru.nobird.android.domain.rx.emptyOnErrorStub -import ru.nobird.android.domain.rx.toMaybe import javax.inject.Inject @HomeScope @@ -32,14 +30,8 @@ constructor( fun onNeedShowStreak() { compositeDisposable += Maybe .fromCallable { sharedPreferences.profile?.id } - .flatMapSingleElement(userActivityRepository::getUserActivities) - .flatMap { userActivities -> - userActivities - .firstOrNull() - ?.pins - ?.let(StepikUtil::getCurrentStreak) - .toMaybe() - } + .flatMapSingleElement(userActivityRepository::getUserActivitySummary) + .map { it.recentStrike } .subscribeOn(backgroundScheduler) .observeOn(mainScheduler) .subscribeBy( diff --git a/app/src/main/java/org/stepik/android/data/user_activity/repository/UserActivityRepositoryImpl.kt b/app/src/main/java/org/stepik/android/data/user_activity/repository/UserActivityRepositoryImpl.kt index 833d23739f..9f83580a19 100644 --- a/app/src/main/java/org/stepik/android/data/user_activity/repository/UserActivityRepositoryImpl.kt +++ b/app/src/main/java/org/stepik/android/data/user_activity/repository/UserActivityRepositoryImpl.kt @@ -4,6 +4,7 @@ import io.reactivex.Single import org.stepik.android.data.user_activity.source.UserActivityRemoteDataSource import org.stepik.android.domain.user_activity.repository.UserActivityRepository import org.stepik.android.model.user.UserActivity +import org.stepik.android.model.user.UserActivitySummary import javax.inject.Inject class UserActivityRepositoryImpl @@ -14,4 +15,7 @@ constructor( override fun getUserActivities(userId: Long): Single> = userActivityRemoteDataSource.getUserActivities(userId) + + override fun getUserActivitySummary(userId: Long): Single = + userActivityRemoteDataSource.getUserActivitySummary(userId) } \ No newline at end of file diff --git a/app/src/main/java/org/stepik/android/data/user_activity/source/UserActivityRemoteDataSource.kt b/app/src/main/java/org/stepik/android/data/user_activity/source/UserActivityRemoteDataSource.kt index b5f81241c1..c919a95cef 100644 --- a/app/src/main/java/org/stepik/android/data/user_activity/source/UserActivityRemoteDataSource.kt +++ b/app/src/main/java/org/stepik/android/data/user_activity/source/UserActivityRemoteDataSource.kt @@ -2,7 +2,10 @@ package org.stepik.android.data.user_activity.source import io.reactivex.Single import org.stepik.android.model.user.UserActivity +import org.stepik.android.model.user.UserActivitySummary interface UserActivityRemoteDataSource { fun getUserActivities(userId: Long): Single> + + fun getUserActivitySummary(userId: Long): Single } \ No newline at end of file diff --git a/app/src/main/java/org/stepik/android/domain/profile_activities/interactor/ProfileActivitiesInteractor.kt b/app/src/main/java/org/stepik/android/domain/profile_activities/interactor/ProfileActivitiesInteractor.kt index 35cd0b1e34..64c9a0f9f7 100644 --- a/app/src/main/java/org/stepik/android/domain/profile_activities/interactor/ProfileActivitiesInteractor.kt +++ b/app/src/main/java/org/stepik/android/domain/profile_activities/interactor/ProfileActivitiesInteractor.kt @@ -3,10 +3,8 @@ package org.stepik.android.domain.profile_activities.interactor import io.reactivex.Single import org.stepik.android.domain.profile_activities.model.ProfileActivitiesData import org.stepik.android.domain.user_activity.repository.UserActivityRepository -import org.stepik.android.model.user.UserActivity -import ru.nobird.android.domain.rx.first +import org.stepik.android.model.user.UserActivitySummary import javax.inject.Inject -import kotlin.math.max class ProfileActivitiesInteractor @Inject @@ -15,28 +13,14 @@ constructor( ) { fun getProfileActivities(userId: Long): Single = userActivityRepository - .getUserActivities(userId) - .first() + .getUserActivitySummary(userId) .map(::mapToProfileActivities) - private fun mapToProfileActivities(userActivity: UserActivity): ProfileActivitiesData { - var streak = 0 - var maxStreak = 0 - var buffer = 0 - - for (i in 0..userActivity.pins.size) { - val pin = userActivity.pins.getOrElse(i) { 0 } - if (pin > 0) { - buffer++ - } else { - maxStreak = max(maxStreak, buffer) - if (buffer == i || buffer == i - 1) { // first is solved today, second not solved - streak = buffer - } - buffer = 0 - } - } - - return ProfileActivitiesData(userActivity.pins, streak, maxStreak, isSolvedToday = userActivity.pins[0] > 0) - } + private fun mapToProfileActivities(userActivitySummary: UserActivitySummary): ProfileActivitiesData = + ProfileActivitiesData( + pins = userActivitySummary.pins, + streak = userActivitySummary.recentStrike, + maxStreak = userActivitySummary.maxStrike, + isSolvedToday = userActivitySummary.solvedToday > 0 + ) } \ No newline at end of file diff --git a/app/src/main/java/org/stepik/android/domain/streak/interactor/StreakInteractor.kt b/app/src/main/java/org/stepik/android/domain/streak/interactor/StreakInteractor.kt index f1a56cff4b..68d0225d63 100644 --- a/app/src/main/java/org/stepik/android/domain/streak/interactor/StreakInteractor.kt +++ b/app/src/main/java/org/stepik/android/domain/streak/interactor/StreakInteractor.kt @@ -2,8 +2,6 @@ package org.stepik.android.domain.streak.interactor import io.reactivex.Maybe import org.stepic.droid.preferences.SharedPreferenceHelper -import org.stepic.droid.util.StepikUtil -import ru.nobird.android.domain.rx.toMaybe import org.stepik.android.domain.user_activity.repository.UserActivityRepository import org.stepik.android.view.streak.notification.StreakNotificationDelegate import javax.inject.Inject @@ -24,9 +22,8 @@ constructor( fun onNeedShowStreak(): Maybe = Maybe .fromCallable { sharedPreferenceHelper.profile?.id } - .flatMapSingleElement { userActivityRepository.getUserActivities(it) } - .flatMap { it.firstOrNull()?.pins.toMaybe() } - .map { StepikUtil.getCurrentStreak(it) } + .flatMapSingleElement { userActivityRepository.getUserActivitySummary(it) } + .map { it.recentStrike } fun setStreakTime(timeIntervalCode: Int) { sharedPreferenceHelper.isStreakNotificationEnabled = true diff --git a/app/src/main/java/org/stepik/android/domain/user_activity/repository/UserActivityRepository.kt b/app/src/main/java/org/stepik/android/domain/user_activity/repository/UserActivityRepository.kt index cb9b854929..8a03bac2f8 100644 --- a/app/src/main/java/org/stepik/android/domain/user_activity/repository/UserActivityRepository.kt +++ b/app/src/main/java/org/stepik/android/domain/user_activity/repository/UserActivityRepository.kt @@ -2,7 +2,10 @@ package org.stepik.android.domain.user_activity.repository import io.reactivex.Single import org.stepik.android.model.user.UserActivity +import org.stepik.android.model.user.UserActivitySummary interface UserActivityRepository { fun getUserActivities(userId: Long): Single> + + fun getUserActivitySummary(userId: Long): Single } \ No newline at end of file diff --git a/app/src/main/java/org/stepik/android/remote/user_activity/UserActivityRemoteDataSourceImpl.kt b/app/src/main/java/org/stepik/android/remote/user_activity/UserActivityRemoteDataSourceImpl.kt index 0b4381d85d..d2fb9d2ee0 100644 --- a/app/src/main/java/org/stepik/android/remote/user_activity/UserActivityRemoteDataSourceImpl.kt +++ b/app/src/main/java/org/stepik/android/remote/user_activity/UserActivityRemoteDataSourceImpl.kt @@ -4,7 +4,9 @@ import io.reactivex.Single import io.reactivex.functions.Function import org.stepik.android.data.user_activity.source.UserActivityRemoteDataSource import org.stepik.android.model.user.UserActivity +import org.stepik.android.model.user.UserActivitySummary import org.stepik.android.remote.user_activity.model.UserActivityResponse +import org.stepik.android.remote.user_activity.model.UserActivitySummaryResponse import org.stepik.android.remote.user_activity.service.UserActivityService import javax.inject.Inject @@ -16,8 +18,16 @@ constructor( private val userActivityResponseMapper = Function>(UserActivityResponse::userActivities) + private val userActivitySummaryResponseMapper = + Function { it.userActivitySummaries.first() } + override fun getUserActivities(userId: Long): Single> = userActivityService .getUserActivitiesReactive(userId) .map(userActivityResponseMapper) + + override fun getUserActivitySummary(userId: Long): Single = + userActivityService + .getUserActivitySummaryReactive(userId) + .map(userActivitySummaryResponseMapper) } \ No newline at end of file diff --git a/app/src/main/java/org/stepik/android/remote/user_activity/model/UserActivitySummaryResponse.kt b/app/src/main/java/org/stepik/android/remote/user_activity/model/UserActivitySummaryResponse.kt new file mode 100644 index 0000000000..00e698bcf3 --- /dev/null +++ b/app/src/main/java/org/stepik/android/remote/user_activity/model/UserActivitySummaryResponse.kt @@ -0,0 +1,13 @@ +package org.stepik.android.remote.user_activity.model + +import com.google.gson.annotations.SerializedName +import org.stepik.android.model.Meta +import org.stepik.android.model.user.UserActivitySummary +import org.stepik.android.remote.base.model.MetaResponse + +class UserActivitySummaryResponse( + @SerializedName("meta") + override val meta: Meta, + @SerializedName("user-activity-summaries") + val userActivitySummaries: List +) : MetaResponse \ No newline at end of file diff --git a/app/src/main/java/org/stepik/android/remote/user_activity/service/UserActivityService.kt b/app/src/main/java/org/stepik/android/remote/user_activity/service/UserActivityService.kt index f49734d387..28190596e0 100644 --- a/app/src/main/java/org/stepik/android/remote/user_activity/service/UserActivityService.kt +++ b/app/src/main/java/org/stepik/android/remote/user_activity/service/UserActivityService.kt @@ -2,10 +2,14 @@ package org.stepik.android.remote.user_activity.service import io.reactivex.Single import org.stepik.android.remote.user_activity.model.UserActivityResponse +import org.stepik.android.remote.user_activity.model.UserActivitySummaryResponse import retrofit2.http.GET import retrofit2.http.Path interface UserActivityService { @GET("api/user-activities/{userId}") fun getUserActivitiesReactive(@Path("userId") userId: Long): Single + + @GET("api/user-activity-summaries/{userId}") + fun getUserActivitySummaryReactive(@Path("userId") userId: Long): Single } \ No newline at end of file diff --git a/app/src/main/java/org/stepik/android/view/streak/notification/StreakNotificationDelegate.kt b/app/src/main/java/org/stepik/android/view/streak/notification/StreakNotificationDelegate.kt index 5d7599686e..2e86562ffb 100644 --- a/app/src/main/java/org/stepik/android/view/streak/notification/StreakNotificationDelegate.kt +++ b/app/src/main/java/org/stepik/android/view/streak/notification/StreakNotificationDelegate.kt @@ -9,7 +9,6 @@ import org.stepic.droid.core.ScreenManager import org.stepic.droid.preferences.SharedPreferenceHelper import org.stepic.droid.util.AppConstants import org.stepic.droid.util.DateTimeHelper -import org.stepic.droid.util.StepikUtil import org.stepik.android.domain.base.analytic.BUNDLEABLE_ANALYTIC_EVENT import org.stepik.android.domain.base.analytic.toBundle import org.stepik.android.domain.streak.analytic.StreakNotificationClicked @@ -48,23 +47,35 @@ constructor( val numberOfStreakNotifications = sharedPreferenceHelper.numberOfStreakNotifications if (numberOfStreakNotifications < AppConstants.MAX_NUMBER_OF_NOTIFICATION_STREAK) { try { - val pins: ArrayList = userActivityRepository.getUserActivities(sharedPreferenceHelper.profile?.id ?: throw Exception("User is not auth")) + val userId = sharedPreferenceHelper.profile?.id + ?: throw Exception("User is not auth") + val userActivitySummary = userActivityRepository + .getUserActivitySummary(userId) .blockingGet() - .firstOrNull() - ?.pins!! - val (currentStreak, isSolvedToday) = StepikUtil.getCurrentStreakExtended(pins) - if (currentStreak <= 0) { - analytic.reportEvent(Analytic.Streak.GET_ZERO_STREAK_NOTIFICATION) - showNotificationWithoutStreakInfo(StreakNotificationType.ZERO) - } else { - // if current streak is > 0 -> streaks works! -> continue send it - // it will reset before sending, after sending it will be incremented - sharedPreferenceHelper.resetNumberOfStreakNotifications() - if (isSolvedToday) { - showNotificationStreakImprovement(currentStreak) - } else { - showNotificationWithStreakCallToAction(currentStreak) + + val notificationType = getStreakNotificationType( + userActivitySummary.recentStrike, + userActivitySummary.solvedToday + ) + + when (notificationType) { + StreakNotificationType.ZERO -> { + analytic.reportEvent(Analytic.Streak.GET_ZERO_STREAK_NOTIFICATION) + showNotificationWithoutStreakInfo(StreakNotificationType.ZERO) + } + + StreakNotificationType.SOLVED_TODAY -> { + sharedPreferenceHelper.resetNumberOfStreakNotifications() + showNotificationStreakImprovement(userActivitySummary.recentStrike) } + + StreakNotificationType.NOT_SOLVED_TODAY -> { + sharedPreferenceHelper.resetNumberOfStreakNotifications() + showNotificationWithStreakCallToAction(userActivitySummary.recentStrike) + } + + StreakNotificationType.NO_INTERNET -> + Unit } } catch (exception: Exception) { // no internet || cant get streaks -> show some notification without streak information. @@ -157,4 +168,14 @@ constructor( .createStreakNotificationIntent(context, StreakNotificationDismissed(notificationType.type).toBundle()) return PendingIntentCompat.getBroadcast(context, 0, deleteIntent, PendingIntent.FLAG_CANCEL_CURRENT) } -} \ No newline at end of file +} + +internal fun getStreakNotificationType(recentStrike: Int, solvedToday: Int): StreakNotificationType = + when { + recentStrike <= 0 -> + StreakNotificationType.ZERO + solvedToday > 0 -> + StreakNotificationType.SOLVED_TODAY + else -> + StreakNotificationType.NOT_SOLVED_TODAY + } \ No newline at end of file diff --git a/app/src/test/java/org/stepik/android/domain/profile_activities/interactor/ProfileActivitiesInteractorTest.kt b/app/src/test/java/org/stepik/android/domain/profile_activities/interactor/ProfileActivitiesInteractorTest.kt new file mode 100644 index 0000000000..1a7bde010f --- /dev/null +++ b/app/src/test/java/org/stepik/android/domain/profile_activities/interactor/ProfileActivitiesInteractorTest.kt @@ -0,0 +1,89 @@ +package org.stepik.android.domain.profile_activities.interactor + +import com.nhaarman.mockitokotlin2.doReturn +import com.nhaarman.mockitokotlin2.verify +import com.nhaarman.mockitokotlin2.whenever +import io.reactivex.Single +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mock +import org.mockito.junit.MockitoJUnitRunner +import org.stepik.android.domain.profile_activities.model.ProfileActivitiesData +import org.stepik.android.domain.user_activity.repository.UserActivityRepository +import org.stepik.android.model.user.UserActivitySummary + +@RunWith(MockitoJUnitRunner::class) +class ProfileActivitiesInteractorTest { + @Mock + private lateinit var userActivityRepository: UserActivityRepository + + @Test + fun `summary without solved today maps to continue streak data`() { + val pins = listOf(0L, 2L, 1L, 3L, 0L, 1L, 4L) + val summary = UserActivitySummary( + recentStrike = 377, + solvedToday = 0, + maxStrike = 412, + pins = pins + ) + whenever(userActivityRepository.getUserActivitySummary(USER_ID)) doReturn Single.just(summary) + + ProfileActivitiesInteractor(userActivityRepository) + .getProfileActivities(USER_ID) + .test() + .assertComplete() + .assertResult( + ProfileActivitiesData(pins, streak = 377, maxStreak = 412, isSolvedToday = false) + ) + + verify(userActivityRepository).getUserActivitySummary(USER_ID) + } + + @Test + fun `summary with solved today maps to active streak data`() { + val pins = listOf(0L, 0L, 0L, 0L, 0L, 0L, 0L) + val summary = UserActivitySummary( + recentStrike = 378, + solvedToday = 2, + maxStrike = 412, + pins = pins + ) + whenever(userActivityRepository.getUserActivitySummary(USER_ID)) doReturn Single.just(summary) + + ProfileActivitiesInteractor(userActivityRepository) + .getProfileActivities(USER_ID) + .test() + .assertComplete() + .assertResult( + ProfileActivitiesData(pins, streak = 378, maxStreak = 412, isSolvedToday = true) + ) + + verify(userActivityRepository).getUserActivitySummary(USER_ID) + } + + @Test + fun `zero summary maps to start streak data`() { + val pins = listOf(0L, 0L, 0L, 0L, 0L, 0L, 0L) + val summary = UserActivitySummary( + recentStrike = 0, + solvedToday = 0, + maxStrike = 0, + pins = pins + ) + whenever(userActivityRepository.getUserActivitySummary(USER_ID)) doReturn Single.just(summary) + + ProfileActivitiesInteractor(userActivityRepository) + .getProfileActivities(USER_ID) + .test() + .assertComplete() + .assertResult( + ProfileActivitiesData(pins, streak = 0, maxStreak = 0, isSolvedToday = false) + ) + + verify(userActivityRepository).getUserActivitySummary(USER_ID) + } + + private companion object { + const val USER_ID = 1L + } +} diff --git a/app/src/test/java/org/stepik/android/view/streak/notification/StreakNotificationDelegateTest.kt b/app/src/test/java/org/stepik/android/view/streak/notification/StreakNotificationDelegateTest.kt new file mode 100644 index 0000000000..753a69e8af --- /dev/null +++ b/app/src/test/java/org/stepik/android/view/streak/notification/StreakNotificationDelegateTest.kt @@ -0,0 +1,134 @@ +package org.stepik.android.view.streak.notification + +import android.content.Context +import android.content.Intent +import androidx.core.app.NotificationCompat +import com.nhaarman.mockitokotlin2.any +import com.nhaarman.mockitokotlin2.argumentCaptor +import com.nhaarman.mockitokotlin2.doReturn +import com.nhaarman.mockitokotlin2.eq +import com.nhaarman.mockitokotlin2.never +import com.nhaarman.mockitokotlin2.verify +import com.nhaarman.mockitokotlin2.whenever +import io.reactivex.Single +import org.junit.Assert.assertEquals +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mock +import org.mockito.MockitoAnnotations +import org.robolectric.RobolectricTestRunner +import org.robolectric.RuntimeEnvironment +import org.stepic.droid.R +import org.stepic.droid.analytic.Analytic +import org.stepic.droid.core.ScreenManager +import org.stepic.droid.preferences.SharedPreferenceHelper +import org.stepik.android.domain.base.analytic.AnalyticEvent +import org.stepik.android.domain.user_activity.repository.UserActivityRepository +import org.stepik.android.model.user.Profile +import org.stepik.android.model.user.UserActivitySummary +import org.stepik.android.view.notification.StepikNotificationManager +import org.stepik.android.view.notification.helpers.NotificationHelper +import org.stepik.android.view.streak.model.StreakNotificationType + +@RunWith(RobolectricTestRunner::class) +class StreakNotificationDelegateTest { + @Mock + private lateinit var analytic: Analytic + + @Mock + private lateinit var userActivityRepository: UserActivityRepository + + @Mock + private lateinit var screenManager: ScreenManager + + @Mock + private lateinit var sharedPreferenceHelper: SharedPreferenceHelper + + @Mock + private lateinit var notificationHelper: NotificationHelper + + @Mock + private lateinit var stepikNotificationManager: StepikNotificationManager + + private lateinit var context: Context + private lateinit var streakNotificationDelegate: StreakNotificationDelegate + + @Before + fun setUp() { + MockitoAnnotations.initMocks(this) + + context = RuntimeEnvironment.getApplication() + streakNotificationDelegate = StreakNotificationDelegate( + context = context, + analytic = analytic, + userActivityRepository = userActivityRepository, + screenManager = screenManager, + sharedPreferenceHelper = sharedPreferenceHelper, + notificationHelper = notificationHelper, + stepikNotificationManager = stepikNotificationManager + ) + + whenever(sharedPreferenceHelper.isStreakNotificationEnabled) doReturn true + whenever(sharedPreferenceHelper.numberOfStreakNotifications) doReturn 0 + whenever(sharedPreferenceHelper.timeNotificationCode) doReturn 12 + whenever(sharedPreferenceHelper.profile) doReturn Profile(id = USER_ID) + whenever(screenManager.getMyCoursesIntent(context)) doReturn Intent("test") + whenever(notificationHelper.makeSimpleNotificationBuilder(any(), any(), any(), any(), any(), any())) doReturn + NotificationCompat.Builder(context, "test") + .setSmallIcon(R.drawable.ic_player_notification) + } + + @Test + fun `recent strike less than or equal to zero sends zero streak notification`() { + whenever(userActivityRepository.getUserActivitySummary(USER_ID)) doReturn Single.just( + UserActivitySummary(recentStrike = 0, solvedToday = 0) + ) + + streakNotificationDelegate.onNeedShowNotification() + + verify(analytic).reportEvent(Analytic.Streak.GET_ZERO_STREAK_NOTIFICATION) + verify(sharedPreferenceHelper, never()).resetNumberOfStreakNotifications() + verifyNotificationShown(StreakNotificationType.ZERO) + } + + @Test + fun `positive recent strike with solved today sends improvement notification`() { + whenever(userActivityRepository.getUserActivitySummary(USER_ID)) doReturn Single.just( + UserActivitySummary(recentStrike = 10, solvedToday = 1) + ) + + streakNotificationDelegate.onNeedShowNotification() + + verify(sharedPreferenceHelper).resetNumberOfStreakNotifications() + verifyNotificationShown(StreakNotificationType.SOLVED_TODAY) + } + + @Test + fun `positive recent strike without solved today sends call to action notification`() { + whenever(userActivityRepository.getUserActivitySummary(USER_ID)) doReturn Single.just( + UserActivitySummary(recentStrike = 10, solvedToday = 0) + ) + + streakNotificationDelegate.onNeedShowNotification() + + verify(sharedPreferenceHelper).resetNumberOfStreakNotifications() + verifyNotificationShown(StreakNotificationType.NOT_SOLVED_TODAY) + } + + private fun verifyNotificationShown(notificationType: StreakNotificationType) { + val analyticEventCaptor = argumentCaptor() + + verify(analytic).report(analyticEventCaptor.capture()) + assertEquals( + notificationType.type, + analyticEventCaptor.firstValue.params["type"] + ) + verify(stepikNotificationManager).showNotification(eq(STREAK_NOTIFICATION_ID), any()) + } + + private companion object { + const val USER_ID = 1L + const val STREAK_NOTIFICATION_ID = 3214L + } +} diff --git a/model/src/main/java/org/stepik/android/model/user/UserActivitySummary.kt b/model/src/main/java/org/stepik/android/model/user/UserActivitySummary.kt new file mode 100644 index 0000000000..e105416cc7 --- /dev/null +++ b/model/src/main/java/org/stepik/android/model/user/UserActivitySummary.kt @@ -0,0 +1,20 @@ +package org.stepik.android.model.user + +import com.google.gson.annotations.SerializedName + +data class UserActivitySummary( + @SerializedName("id") + val id: Long = -1, + @SerializedName("recent_strike") + val recentStrike: Int = 0, + @SerializedName("solved_today") + val solvedToday: Int = 0, + @SerializedName("max_strike") + val maxStrike: Int = 0, + @SerializedName("solved") + val solved: Int = 0, + @SerializedName("days") + val days: Int = 0, + @SerializedName("pins") + val pins: List = emptyList() +) \ No newline at end of file