diff --git a/app/src/main/java/com/tangem/tap/di/domain/SettingsDomainModule.kt b/app/src/main/java/com/tangem/tap/di/domain/SettingsDomainModule.kt index bac2d55655..90cc3380f0 100644 --- a/app/src/main/java/com/tangem/tap/di/domain/SettingsDomainModule.kt +++ b/app/src/main/java/com/tangem/tap/di/domain/SettingsDomainModule.kt @@ -7,6 +7,7 @@ import com.tangem.domain.balancehiding.UpdateBalanceHidingSettingsUseCase import com.tangem.domain.balancehiding.repositories.BalanceHidingRepository import com.tangem.domain.settings.* import com.tangem.domain.settings.repositories.AppRatingRepository +import com.tangem.domain.settings.repositories.PermissionRepository import com.tangem.domain.settings.repositories.PromoSettingsRepository import com.tangem.domain.settings.repositories.SettingsRepository import com.tangem.tap.domain.sdk.TangemSdkManager @@ -17,6 +18,7 @@ import dagger.hilt.InstallIn import dagger.hilt.components.SingletonComponent import javax.inject.Singleton +@Suppress("TooManyFunctions") @Module @InstallIn(SingletonComponent::class) internal object SettingsDomainModule { @@ -164,4 +166,52 @@ internal object SettingsDomainModule { ): IncrementAppLaunchCounterUseCase { return IncrementAppLaunchCounterUseCase(settingsRepository = settingsRepository) } + + // region PushPermissionRepository + @Provides + @Singleton + fun provideShouldInitiallyAskPermissionUseCase( + permissionRepository: PermissionRepository, + ): ShouldInitiallyAskPermissionUseCase { + return ShouldInitiallyAskPermissionUseCase(repository = permissionRepository) + } + + @Provides + @Singleton + fun provideIsFirstTimeAskingPermissionUseCase( + permissionRepository: PermissionRepository, + ): IsFirstTimeAskingPermissionUseCase { + return IsFirstTimeAskingPermissionUseCase(repository = permissionRepository) + } + + @Provides + @Singleton + fun provideSetFirstTimeAskingPushPermissionUseCase( + permissionRepository: PermissionRepository, + ): SetFirstTimeAskingPermissionUseCase { + return SetFirstTimeAskingPermissionUseCase(repository = permissionRepository) + } + + @Provides + @Singleton + fun provideDelayPermissionRequestUseCase( + permissionRepository: PermissionRepository, + ): DelayPermissionRequestUseCase { + return DelayPermissionRequestUseCase(repository = permissionRepository) + } + + @Provides + @Singleton + fun provideShouldAskPermissionUseCase(permissionRepository: PermissionRepository): ShouldAskPermissionUseCase { + return ShouldAskPermissionUseCase(repository = permissionRepository) + } + + @Provides + @Singleton + fun provideNeverRequestPermissionUseCase( + permissionRepository: PermissionRepository, + ): NeverRequestPermissionUseCase { + return NeverRequestPermissionUseCase(repository = permissionRepository) + } + // endregion } \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/local/preferences/PreferencesKeys.kt b/core/datasource/src/main/java/com/tangem/datasource/local/preferences/PreferencesKeys.kt index e069422752..4d357add91 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/local/preferences/PreferencesKeys.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/local/preferences/PreferencesKeys.kt @@ -94,6 +94,20 @@ object PreferencesKeys { val IS_WALLET_NAMES_MIGRATION_DONE_KEY by lazy { booleanPreferencesKey(name = "isWalletNamesMigrationDone") } fun getStart2CoinTOSAcceptedKey(region: String?) = booleanPreferencesKey(name = "start2Coin_tos_accepted_$region") + + // region Permission + fun getShouldShowPermission(permission: Int) = booleanPreferencesKey("shouldShowPushPermission_$permission") + + fun getShouldShowInitialPermissionScreen(permission: Int) = + booleanPreferencesKey("shouldShowInitialPushPermissionScreen_$permission") + + fun getIsFirstTimeAskingPermission(permission: Int) = + booleanPreferencesKey("shouldAskInitialPushPermission_$permission") + + fun getPermissionLaunchCount(permission: Int) = intPreferencesKey("pushPermissionLaunchCount_$permission") + + fun getPermissionDaysCount(permission: Int) = longPreferencesKey("pushPermissionDaysCount_$permission") + // endregion } /** Preferences keys set that should be migrated from "PreferencesDataSource" to a new DataStore */ diff --git a/data/settings/src/main/java/com/tangem/data/settings/DefaultPermissionRepository.kt b/data/settings/src/main/java/com/tangem/data/settings/DefaultPermissionRepository.kt new file mode 100644 index 0000000000..c2f07bae1c --- /dev/null +++ b/data/settings/src/main/java/com/tangem/data/settings/DefaultPermissionRepository.kt @@ -0,0 +1,68 @@ +package com.tangem.data.settings + +import android.os.SystemClock +import com.tangem.datasource.local.preferences.AppPreferencesStore +import com.tangem.datasource.local.preferences.PreferencesKeys +import com.tangem.datasource.local.preferences.PreferencesKeys.getIsFirstTimeAskingPermission +import com.tangem.datasource.local.preferences.PreferencesKeys.getPermissionDaysCount +import com.tangem.datasource.local.preferences.PreferencesKeys.getPermissionLaunchCount +import com.tangem.datasource.local.preferences.PreferencesKeys.getShouldShowInitialPermissionScreen +import com.tangem.datasource.local.preferences.PreferencesKeys.getShouldShowPermission +import com.tangem.datasource.local.preferences.utils.getSyncOrDefault +import com.tangem.datasource.local.preferences.utils.store +import com.tangem.domain.settings.repositories.PermissionRepository + +internal class DefaultPermissionRepository( + private val appPreferencesStore: AppPreferencesStore, +) : PermissionRepository { + + override suspend fun shouldInitiallyShowPermissionScreen(permission: Int): Boolean { + val key = getShouldShowInitialPermissionScreen(permission) + val initialPermissionScreen = appPreferencesStore.getSyncOrDefault(key = key, default = true) + if (initialPermissionScreen) appPreferencesStore.store(key = key, value = false) + return initialPermissionScreen + } + + override suspend fun isFirstTimeAskingPermission(permission: Int): Boolean = appPreferencesStore.getSyncOrDefault( + key = getIsFirstTimeAskingPermission(permission), + default = true, + ) + + override suspend fun setFirstTimeAskingPermission(permission: Int, value: Boolean) { + appPreferencesStore.store( + key = getIsFirstTimeAskingPermission(permission), + value = value, + ) + } + + override suspend fun shouldAskPermission(permission: Int): Boolean { + val shouldAskPermission = appPreferencesStore.getSyncOrDefault(getShouldShowPermission(permission), true) + val delayedLaunches = appPreferencesStore.getSyncOrDefault(getPermissionLaunchCount(permission), 0) + val delayedDays = appPreferencesStore.getSyncOrDefault(getPermissionDaysCount(permission), 0) + val currentLaunchCounter = appPreferencesStore.getSyncOrDefault(PreferencesKeys.APP_LAUNCH_COUNT_KEY, 0) + + val nowMillis = SystemClock.elapsedRealtime() + val isDaysDelayed = delayedDays < nowMillis + val isLaunchesDelayed = delayedLaunches < currentLaunchCounter + return shouldAskPermission && isDaysDelayed && isLaunchesDelayed + } + + override suspend fun neverAskPermission(permission: Int) { + appPreferencesStore.store(key = getShouldShowPermission(permission), value = false) + } + + override suspend fun delayPermissionAsking(permission: Int) { + appPreferencesStore.editData { + val appLaunchCounter = it.getOrDefault(PreferencesKeys.APP_LAUNCH_COUNT_KEY, 0) + val nowMillis = SystemClock.elapsedRealtime() + + it[getPermissionLaunchCount(permission)] = appLaunchCounter + DELAY_LAUNCH_COUNT + it[getPermissionDaysCount(permission)] = nowMillis + DELAY_DAYS_COUNT + } + } + + private companion object { + const val DELAY_LAUNCH_COUNT = 5 + const val DELAY_DAYS_COUNT = 3L * 24 * 3600 * 1000 // 3 days in millis + } +} \ No newline at end of file diff --git a/data/settings/src/main/java/com/tangem/data/settings/di/SettingsDataModule.kt b/data/settings/src/main/java/com/tangem/data/settings/di/SettingsDataModule.kt index 4f6bf8e17b..da47f1f604 100644 --- a/data/settings/src/main/java/com/tangem/data/settings/di/SettingsDataModule.kt +++ b/data/settings/src/main/java/com/tangem/data/settings/di/SettingsDataModule.kt @@ -1,12 +1,14 @@ package com.tangem.data.settings.di import com.tangem.data.settings.DefaultAppRatingRepository -import com.tangem.data.settings.DefaultSettingsRepository import com.tangem.data.settings.DefaultPromoSettingsRepository +import com.tangem.data.settings.DefaultPermissionRepository +import com.tangem.data.settings.DefaultSettingsRepository import com.tangem.datasource.local.preferences.AppPreferencesStore import com.tangem.domain.settings.repositories.AppRatingRepository -import com.tangem.domain.settings.repositories.SettingsRepository import com.tangem.domain.settings.repositories.PromoSettingsRepository +import com.tangem.domain.settings.repositories.PermissionRepository +import com.tangem.domain.settings.repositories.SettingsRepository import dagger.Module import dagger.Provides import dagger.hilt.InstallIn @@ -34,4 +36,10 @@ internal object SettingsDataModule { fun providePromoSettingsSettingsRepository(appPreferencesStore: AppPreferencesStore): PromoSettingsRepository { return DefaultPromoSettingsRepository(appPreferencesStore = appPreferencesStore) } + + @Provides + @Singleton + fun providePushPermissionRepository(appPreferencesStore: AppPreferencesStore): PermissionRepository { + return DefaultPermissionRepository(appPreferencesStore = appPreferencesStore) + } } \ No newline at end of file diff --git a/domain/settings/src/main/java/com/tangem/domain/settings/DelayPermissionRequestUseCase.kt b/domain/settings/src/main/java/com/tangem/domain/settings/DelayPermissionRequestUseCase.kt new file mode 100644 index 0000000000..7c4c74baba --- /dev/null +++ b/domain/settings/src/main/java/com/tangem/domain/settings/DelayPermissionRequestUseCase.kt @@ -0,0 +1,13 @@ +package com.tangem.domain.settings + +import arrow.core.Either +import com.tangem.domain.settings.repositories.PermissionRepository + +class DelayPermissionRequestUseCase( + private val repository: PermissionRepository, +) { + + suspend operator fun invoke(permission: Int): Either = Either.catch { + repository.delayPermissionAsking(permission) + } +} \ No newline at end of file diff --git a/domain/settings/src/main/java/com/tangem/domain/settings/IsFirstTimeAskingPermissionUseCase.kt b/domain/settings/src/main/java/com/tangem/domain/settings/IsFirstTimeAskingPermissionUseCase.kt new file mode 100644 index 0000000000..f6b9f562d8 --- /dev/null +++ b/domain/settings/src/main/java/com/tangem/domain/settings/IsFirstTimeAskingPermissionUseCase.kt @@ -0,0 +1,11 @@ +package com.tangem.domain.settings + +import arrow.core.Either +import com.tangem.domain.settings.repositories.PermissionRepository + +class IsFirstTimeAskingPermissionUseCase(private val repository: PermissionRepository) { + + suspend operator fun invoke(permission: Int): Either = Either.catch { + repository.isFirstTimeAskingPermission(permission) + } +} \ No newline at end of file diff --git a/domain/settings/src/main/java/com/tangem/domain/settings/NeverRequestPermissionUseCase.kt b/domain/settings/src/main/java/com/tangem/domain/settings/NeverRequestPermissionUseCase.kt new file mode 100644 index 0000000000..7b9f0219b5 --- /dev/null +++ b/domain/settings/src/main/java/com/tangem/domain/settings/NeverRequestPermissionUseCase.kt @@ -0,0 +1,13 @@ +package com.tangem.domain.settings + +import arrow.core.Either +import com.tangem.domain.settings.repositories.PermissionRepository + +class NeverRequestPermissionUseCase( + private val repository: PermissionRepository, +) { + + suspend operator fun invoke(permission: Int): Either = Either.catch { + repository.neverAskPermission(permission) + } +} \ No newline at end of file diff --git a/domain/settings/src/main/java/com/tangem/domain/settings/SetFirstTimeAskingPermissionUseCase.kt b/domain/settings/src/main/java/com/tangem/domain/settings/SetFirstTimeAskingPermissionUseCase.kt new file mode 100644 index 0000000000..6a82495cf4 --- /dev/null +++ b/domain/settings/src/main/java/com/tangem/domain/settings/SetFirstTimeAskingPermissionUseCase.kt @@ -0,0 +1,11 @@ +package com.tangem.domain.settings + +import arrow.core.Either +import com.tangem.domain.settings.repositories.PermissionRepository + +class SetFirstTimeAskingPermissionUseCase(private val repository: PermissionRepository) { + + suspend operator fun invoke(permission: Int): Either = Either.catch { + repository.setFirstTimeAskingPermission(permission, false) + } +} \ No newline at end of file diff --git a/domain/settings/src/main/java/com/tangem/domain/settings/ShouldAskPermissionUseCase.kt b/domain/settings/src/main/java/com/tangem/domain/settings/ShouldAskPermissionUseCase.kt new file mode 100644 index 0000000000..2cf3378e84 --- /dev/null +++ b/domain/settings/src/main/java/com/tangem/domain/settings/ShouldAskPermissionUseCase.kt @@ -0,0 +1,10 @@ +package com.tangem.domain.settings + +import com.tangem.domain.settings.repositories.PermissionRepository + +class ShouldAskPermissionUseCase( + private val repository: PermissionRepository, +) { + + suspend operator fun invoke(permission: Int): Boolean = repository.shouldAskPermission(permission) +} \ No newline at end of file diff --git a/domain/settings/src/main/java/com/tangem/domain/settings/ShouldInitiallyAskPermissionUseCase.kt b/domain/settings/src/main/java/com/tangem/domain/settings/ShouldInitiallyAskPermissionUseCase.kt new file mode 100644 index 0000000000..b020fc8c9e --- /dev/null +++ b/domain/settings/src/main/java/com/tangem/domain/settings/ShouldInitiallyAskPermissionUseCase.kt @@ -0,0 +1,11 @@ +package com.tangem.domain.settings + +import arrow.core.Either +import com.tangem.domain.settings.repositories.PermissionRepository + +class ShouldInitiallyAskPermissionUseCase(private val repository: PermissionRepository) { + + suspend operator fun invoke(permission: Int): Either = Either.catch { + repository.shouldInitiallyShowPermissionScreen(permission) + } +} \ No newline at end of file diff --git a/domain/settings/src/main/java/com/tangem/domain/settings/repositories/PermissionRepository.kt b/domain/settings/src/main/java/com/tangem/domain/settings/repositories/PermissionRepository.kt new file mode 100644 index 0000000000..390868c8d4 --- /dev/null +++ b/domain/settings/src/main/java/com/tangem/domain/settings/repositories/PermissionRepository.kt @@ -0,0 +1,39 @@ +package com.tangem.domain.settings.repositories + +interface PermissionRepository { + + /** + * Return true if should display screen ONCE asking to allow [permission]. + * False otherwise or screen already was displayed + */ + suspend fun shouldInitiallyShowPermissionScreen(permission: Int): Boolean + + /** + * Indicates which time [permission] was asked via platform dialog. + * NOTE: Use this method to indicate either reroute to settings or display platform dialog. + */ + suspend fun isFirstTimeAskingPermission(permission: Int): Boolean + + /** + * Sets value indicating that [permission] was asked via platform dialog. + * NOTE: Use this method to indicate either reroute to settings or display platform dialog. + */ + suspend fun setFirstTimeAskingPermission(permission: Int, value: Boolean) + + /** + * Is clear to ask [permission]. + * User could already granted or permanently denied permission + * Or there is an active delay before next request + */ + suspend fun shouldAskPermission(permission: Int): Boolean + + /** + * Permanently deny [permission] and never request again + */ + suspend fun neverAskPermission(permission: Int) + + /** + * Delay next [permission] request for some time or active sessions + */ + suspend fun delayPermissionAsking(permission: Int) +} \ No newline at end of file