From dd6d478c4f0db2b81c7ce601a3e78aaf48c6825f Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 17 Jun 2024 13:17:08 +0500 Subject: [PATCH 1/3] Updated on 2026-08-14 --- .../local/preferences/PreferencesKeys.kt | 10 +++++----- .../settings/DefaultPermissionRepository.kt | 19 ++++++++++--------- .../settings/DelayPermissionRequestUseCase.kt | 2 +- .../IsFirstTimeAskingPermissionUseCase.kt | 2 +- .../settings/NeverRequestPermissionUseCase.kt | 2 +- .../SetFirstTimeAskingPermissionUseCase.kt | 2 +- .../settings/ShouldAskPermissionUseCase.kt | 2 +- .../ShouldInitiallyAskPermissionUseCase.kt | 2 +- .../repositories/PermissionRepository.kt | 12 ++++++------ 9 files changed, 27 insertions(+), 26 deletions(-) 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 4d357add91..42df77c07d 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 @@ -96,17 +96,17 @@ object PreferencesKeys { fun getStart2CoinTOSAcceptedKey(region: String?) = booleanPreferencesKey(name = "start2Coin_tos_accepted_$region") // region Permission - fun getShouldShowPermission(permission: Int) = booleanPreferencesKey("shouldShowPushPermission_$permission") + fun getShouldShowPermission(permission: String) = booleanPreferencesKey("shouldShowPushPermission_$permission") - fun getShouldShowInitialPermissionScreen(permission: Int) = + fun getShouldShowInitialPermissionScreen(permission: String) = booleanPreferencesKey("shouldShowInitialPushPermissionScreen_$permission") - fun getIsFirstTimeAskingPermission(permission: Int) = + fun getIsFirstTimeAskingPermission(permission: String) = booleanPreferencesKey("shouldAskInitialPushPermission_$permission") - fun getPermissionLaunchCount(permission: Int) = intPreferencesKey("pushPermissionLaunchCount_$permission") + fun getPermissionLaunchCount(permission: String) = intPreferencesKey("pushPermissionLaunchCount_$permission") - fun getPermissionDaysCount(permission: Int) = longPreferencesKey("pushPermissionDaysCount_$permission") + fun getPermissionDaysCount(permission: String) = longPreferencesKey("pushPermissionDaysCount_$permission") // endregion } 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 index c2f07bae1c..b73d5d069f 100644 --- a/data/settings/src/main/java/com/tangem/data/settings/DefaultPermissionRepository.kt +++ b/data/settings/src/main/java/com/tangem/data/settings/DefaultPermissionRepository.kt @@ -16,26 +16,27 @@ internal class DefaultPermissionRepository( private val appPreferencesStore: AppPreferencesStore, ) : PermissionRepository { - override suspend fun shouldInitiallyShowPermissionScreen(permission: Int): Boolean { + override suspend fun shouldInitiallyShowPermissionScreen(permission: String): 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 isFirstTimeAskingPermission(permission: String): Boolean = + appPreferencesStore.getSyncOrDefault( + key = getIsFirstTimeAskingPermission(permission), + default = true, + ) - override suspend fun setFirstTimeAskingPermission(permission: Int, value: Boolean) { + override suspend fun setFirstTimeAskingPermission(permission: String, value: Boolean) { appPreferencesStore.store( key = getIsFirstTimeAskingPermission(permission), value = value, ) } - override suspend fun shouldAskPermission(permission: Int): Boolean { + override suspend fun shouldAskPermission(permission: String): Boolean { val shouldAskPermission = appPreferencesStore.getSyncOrDefault(getShouldShowPermission(permission), true) val delayedLaunches = appPreferencesStore.getSyncOrDefault(getPermissionLaunchCount(permission), 0) val delayedDays = appPreferencesStore.getSyncOrDefault(getPermissionDaysCount(permission), 0) @@ -47,11 +48,11 @@ internal class DefaultPermissionRepository( return shouldAskPermission && isDaysDelayed && isLaunchesDelayed } - override suspend fun neverAskPermission(permission: Int) { + override suspend fun neverAskPermission(permission: String) { appPreferencesStore.store(key = getShouldShowPermission(permission), value = false) } - override suspend fun delayPermissionAsking(permission: Int) { + override suspend fun delayPermissionAsking(permission: String) { appPreferencesStore.editData { val appLaunchCounter = it.getOrDefault(PreferencesKeys.APP_LAUNCH_COUNT_KEY, 0) val nowMillis = SystemClock.elapsedRealtime() 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 index 7c4c74baba..bf300cb779 100644 --- a/domain/settings/src/main/java/com/tangem/domain/settings/DelayPermissionRequestUseCase.kt +++ b/domain/settings/src/main/java/com/tangem/domain/settings/DelayPermissionRequestUseCase.kt @@ -7,7 +7,7 @@ class DelayPermissionRequestUseCase( private val repository: PermissionRepository, ) { - suspend operator fun invoke(permission: Int): Either = Either.catch { + suspend operator fun invoke(permission: String): 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 index f6b9f562d8..000bfd7ed1 100644 --- a/domain/settings/src/main/java/com/tangem/domain/settings/IsFirstTimeAskingPermissionUseCase.kt +++ b/domain/settings/src/main/java/com/tangem/domain/settings/IsFirstTimeAskingPermissionUseCase.kt @@ -5,7 +5,7 @@ import com.tangem.domain.settings.repositories.PermissionRepository class IsFirstTimeAskingPermissionUseCase(private val repository: PermissionRepository) { - suspend operator fun invoke(permission: Int): Either = Either.catch { + suspend operator fun invoke(permission: String): 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 index 7b9f0219b5..84d654dc42 100644 --- a/domain/settings/src/main/java/com/tangem/domain/settings/NeverRequestPermissionUseCase.kt +++ b/domain/settings/src/main/java/com/tangem/domain/settings/NeverRequestPermissionUseCase.kt @@ -7,7 +7,7 @@ class NeverRequestPermissionUseCase( private val repository: PermissionRepository, ) { - suspend operator fun invoke(permission: Int): Either = Either.catch { + suspend operator fun invoke(permission: String): 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 index 6a82495cf4..fa5d057e77 100644 --- a/domain/settings/src/main/java/com/tangem/domain/settings/SetFirstTimeAskingPermissionUseCase.kt +++ b/domain/settings/src/main/java/com/tangem/domain/settings/SetFirstTimeAskingPermissionUseCase.kt @@ -5,7 +5,7 @@ import com.tangem.domain.settings.repositories.PermissionRepository class SetFirstTimeAskingPermissionUseCase(private val repository: PermissionRepository) { - suspend operator fun invoke(permission: Int): Either = Either.catch { + suspend operator fun invoke(permission: String): 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 index 2cf3378e84..44f063119a 100644 --- a/domain/settings/src/main/java/com/tangem/domain/settings/ShouldAskPermissionUseCase.kt +++ b/domain/settings/src/main/java/com/tangem/domain/settings/ShouldAskPermissionUseCase.kt @@ -6,5 +6,5 @@ class ShouldAskPermissionUseCase( private val repository: PermissionRepository, ) { - suspend operator fun invoke(permission: Int): Boolean = repository.shouldAskPermission(permission) + suspend operator fun invoke(permission: String): 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 index b020fc8c9e..b7d5a6f1ca 100644 --- a/domain/settings/src/main/java/com/tangem/domain/settings/ShouldInitiallyAskPermissionUseCase.kt +++ b/domain/settings/src/main/java/com/tangem/domain/settings/ShouldInitiallyAskPermissionUseCase.kt @@ -5,7 +5,7 @@ import com.tangem.domain.settings.repositories.PermissionRepository class ShouldInitiallyAskPermissionUseCase(private val repository: PermissionRepository) { - suspend operator fun invoke(permission: Int): Either = Either.catch { + suspend operator fun invoke(permission: String): 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 index 390868c8d4..fd136cf651 100644 --- 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 @@ -6,34 +6,34 @@ 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 + suspend fun shouldInitiallyShowPermissionScreen(permission: String): 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 + suspend fun isFirstTimeAskingPermission(permission: String): 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) + suspend fun setFirstTimeAskingPermission(permission: String, 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 + suspend fun shouldAskPermission(permission: String): Boolean /** * Permanently deny [permission] and never request again */ - suspend fun neverAskPermission(permission: Int) + suspend fun neverAskPermission(permission: String) /** * Delay next [permission] request for some time or active sessions */ - suspend fun delayPermissionAsking(permission: Int) + suspend fun delayPermissionAsking(permission: String) } \ No newline at end of file From 7f131732ffcbeb32f64dc30c14694a5197ad8764 Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 17 Jun 2024 13:17:56 +0500 Subject: [PATCH 2/3] Updated on 2026-08-14 --- .../common/settings/IntentSettingsManager.kt | 20 +++++++++ .../java/com/tangem/tap/di/UtilsModule.kt | 6 +++ .../configs/feature_toggles_config.json | 4 ++ .../settings/DummySettingsManager.kt | 5 +++ .../navigation/settings/SettingsManager.kt | 5 +++ core/ui/build.gradle.kts | 1 + features/push-notifications/api/.gitignore | 1 + .../push-notifications/api/build.gradle.kts | 15 +++++++ .../PushNotificationsFeatureToggles.kt | 9 ++++ features/push-notifications/impl/.gitignore | 1 + .../push-notifications/impl/build.gradle.kts | 42 +++++++++++++++++++ .../PushNotificationsFeatureTogglesModule.kt | 24 +++++++++++ .../DefaultPushNotificationsFeatureToggles.kt | 11 +++++ settings.gradle.kts | 3 ++ 14 files changed, 147 insertions(+) create mode 100644 app/src/main/java/com/tangem/tap/common/settings/IntentSettingsManager.kt create mode 100644 core/navigation/src/main/java/com/tangem/core/navigation/settings/DummySettingsManager.kt create mode 100644 core/navigation/src/main/java/com/tangem/core/navigation/settings/SettingsManager.kt create mode 100644 features/push-notifications/api/.gitignore create mode 100644 features/push-notifications/api/build.gradle.kts create mode 100644 features/push-notifications/api/src/main/java/com/tangem/features/pushnotifications/api/featuretoggles/PushNotificationsFeatureToggles.kt create mode 100644 features/push-notifications/impl/.gitignore create mode 100644 features/push-notifications/impl/build.gradle.kts create mode 100644 features/push-notifications/impl/src/main/java/com/tangem/features/pushnotifications/impl/di/PushNotificationsFeatureTogglesModule.kt create mode 100644 features/push-notifications/impl/src/main/java/com/tangem/features/pushnotifications/impl/featuretoggles/DefaultPushNotificationsFeatureToggles.kt diff --git a/app/src/main/java/com/tangem/tap/common/settings/IntentSettingsManager.kt b/app/src/main/java/com/tangem/tap/common/settings/IntentSettingsManager.kt new file mode 100644 index 0000000000..7dbed67b24 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/common/settings/IntentSettingsManager.kt @@ -0,0 +1,20 @@ +package com.tangem.tap.common.settings + +import android.content.Intent +import android.net.Uri +import android.provider.Settings +import com.tangem.core.navigation.settings.SettingsManager +import com.tangem.tap.foregroundActivityObserver +import com.tangem.tap.withForegroundActivity + +internal class IntentSettingsManager : SettingsManager { + override fun openSettings() { + foregroundActivityObserver.withForegroundActivity { activity -> + val openSettingsIntent = Intent( + Settings.ACTION_APPLICATION_DETAILS_SETTINGS, + Uri.fromParts("package", activity.packageName, null), + ) + activity.startActivity(openSettingsIntent) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/di/UtilsModule.kt b/app/src/main/java/com/tangem/tap/di/UtilsModule.kt index d5565743e9..e5c898b899 100644 --- a/app/src/main/java/com/tangem/tap/di/UtilsModule.kt +++ b/app/src/main/java/com/tangem/tap/di/UtilsModule.kt @@ -3,10 +3,12 @@ package com.tangem.tap.di import android.content.Context import com.tangem.core.navigation.feedback.FeedbackManager import com.tangem.core.navigation.finisher.AppFinisher +import com.tangem.core.navigation.settings.SettingsManager import com.tangem.core.navigation.share.ShareManager import com.tangem.core.navigation.url.UrlOpener import com.tangem.tap.common.feedback.ProxyFeedbackManager import com.tangem.tap.common.finisher.AndroidAppFinisher +import com.tangem.tap.common.settings.IntentSettingsManager import com.tangem.tap.common.share.IntentShareManager import com.tangem.tap.common.url.CustomTabsUrlOpener import dagger.Module @@ -35,4 +37,8 @@ internal object UtilsModule { @Provides @Singleton fun provideAppFinisher(@ApplicationContext context: Context): AppFinisher = AndroidAppFinisher(context) + + @Provides + @Singleton + fun provideSettingsManager(): SettingsManager = IntentSettingsManager() } \ No newline at end of file diff --git a/core/featuretoggles/src/main/assets/configs/feature_toggles_config.json b/core/featuretoggles/src/main/assets/configs/feature_toggles_config.json index 015ca0a117..1f8dd14df9 100644 --- a/core/featuretoggles/src/main/assets/configs/feature_toggles_config.json +++ b/core/featuretoggles/src/main/assets/configs/feature_toggles_config.json @@ -42,5 +42,9 @@ { "name": "DETAILS_REDESIGN_ENABLED", "version": "undefined" + }, + { + "name": "PUSH_NOTIFICATIONS_ENABLED", + "version": "undefined" } ] diff --git a/core/navigation/src/main/java/com/tangem/core/navigation/settings/DummySettingsManager.kt b/core/navigation/src/main/java/com/tangem/core/navigation/settings/DummySettingsManager.kt new file mode 100644 index 0000000000..25c4706fb9 --- /dev/null +++ b/core/navigation/src/main/java/com/tangem/core/navigation/settings/DummySettingsManager.kt @@ -0,0 +1,5 @@ +package com.tangem.core.navigation.settings + +class DummySettingsManager : SettingsManager { + override fun openSettings() { /* no-op */ } +} \ No newline at end of file diff --git a/core/navigation/src/main/java/com/tangem/core/navigation/settings/SettingsManager.kt b/core/navigation/src/main/java/com/tangem/core/navigation/settings/SettingsManager.kt new file mode 100644 index 0000000000..4be904c9cb --- /dev/null +++ b/core/navigation/src/main/java/com/tangem/core/navigation/settings/SettingsManager.kt @@ -0,0 +1,5 @@ +package com.tangem.core.navigation.settings + +interface SettingsManager { + fun openSettings() +} \ No newline at end of file diff --git a/core/ui/build.gradle.kts b/core/ui/build.gradle.kts index 9933f9f8df..cb8f7f15ee 100644 --- a/core/ui/build.gradle.kts +++ b/core/ui/build.gradle.kts @@ -43,6 +43,7 @@ dependencies { /** Other libraries */ implementation(deps.compose.accompanist.systemUiController) + implementation(deps.compose.accompanist.permission) implementation(deps.material) implementation(deps.compose.shimmer) implementation(deps.kotlin.immutable.collections) diff --git a/features/push-notifications/api/.gitignore b/features/push-notifications/api/.gitignore new file mode 100644 index 0000000000..42afabfd2a --- /dev/null +++ b/features/push-notifications/api/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/features/push-notifications/api/build.gradle.kts b/features/push-notifications/api/build.gradle.kts new file mode 100644 index 0000000000..4335917f36 --- /dev/null +++ b/features/push-notifications/api/build.gradle.kts @@ -0,0 +1,15 @@ +plugins { + alias(deps.plugins.android.library) + alias(deps.plugins.kotlin.android) + id("configuration") +} + +android { + namespace = "com.tangem.features.pushnotifications.api" +} + +dependencies { + + /** AndroidX */ + implementation(deps.androidx.fragment.ktx) +} \ No newline at end of file diff --git a/features/push-notifications/api/src/main/java/com/tangem/features/pushnotifications/api/featuretoggles/PushNotificationsFeatureToggles.kt b/features/push-notifications/api/src/main/java/com/tangem/features/pushnotifications/api/featuretoggles/PushNotificationsFeatureToggles.kt new file mode 100644 index 0000000000..06346f3b01 --- /dev/null +++ b/features/push-notifications/api/src/main/java/com/tangem/features/pushnotifications/api/featuretoggles/PushNotificationsFeatureToggles.kt @@ -0,0 +1,9 @@ +package com.tangem.features.pushnotifications.api.featuretoggles + +/** + * Push notifications feature toggles + */ +interface PushNotificationsFeatureToggles { + /** Availability of push notifications */ + val isRedesignedSendEnabled: Boolean +} \ No newline at end of file diff --git a/features/push-notifications/impl/.gitignore b/features/push-notifications/impl/.gitignore new file mode 100644 index 0000000000..42afabfd2a --- /dev/null +++ b/features/push-notifications/impl/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/features/push-notifications/impl/build.gradle.kts b/features/push-notifications/impl/build.gradle.kts new file mode 100644 index 0000000000..4524106ece --- /dev/null +++ b/features/push-notifications/impl/build.gradle.kts @@ -0,0 +1,42 @@ +plugins { + alias(deps.plugins.android.library) + alias(deps.plugins.kotlin.android) + alias(deps.plugins.kotlin.kapt) + alias(deps.plugins.hilt.android) + id("configuration") +} + +android { + namespace = "com.tangem.feature.pushnotifications.impl" +} + +dependencies { + /** AndroidX */ + implementation(deps.androidx.fragment.ktx) + implementation(deps.androidx.activity.compose) + + /** Compose */ + implementation(deps.compose.foundation) + implementation(deps.compose.accompanist.systemUiController) + implementation(deps.compose.accompanist.permission) + + /** Other dependencies */ + implementation(deps.timber) + implementation(deps.arrow.core) + implementation(deps.kotlin.immutable.collections) + + /** Core modules */ + implementation(projects.core.ui) + implementation(projects.core.featuretoggles) + + /** Domain module */ + implementation(projects.domain.settings) + + /** Feature modules */ + implementation(projects.features.pushNotifications.api) + + /** DI */ + implementation(deps.hilt.android) + implementation(project(":core:navigation")) + kapt(deps.hilt.kapt) +} \ No newline at end of file diff --git a/features/push-notifications/impl/src/main/java/com/tangem/features/pushnotifications/impl/di/PushNotificationsFeatureTogglesModule.kt b/features/push-notifications/impl/src/main/java/com/tangem/features/pushnotifications/impl/di/PushNotificationsFeatureTogglesModule.kt new file mode 100644 index 0000000000..0df7b0a222 --- /dev/null +++ b/features/push-notifications/impl/src/main/java/com/tangem/features/pushnotifications/impl/di/PushNotificationsFeatureTogglesModule.kt @@ -0,0 +1,24 @@ +package com.tangem.features.pushnotifications.impl.di + +import com.tangem.core.featuretoggle.manager.FeatureTogglesManager +import com.tangem.features.pushnotifications.api.featuretoggles.PushNotificationsFeatureToggles +import com.tangem.features.pushnotifications.impl.featuretoggles.DefaultPushNotificationsFeatureToggles +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent +import javax.inject.Singleton + +/** + * DI module provides implementation of [PushNotificationsFeatureToggles] + */ +@Module +@InstallIn(SingletonComponent::class) +internal object PushNotificationsFeatureTogglesModule { + + @Provides + @Singleton + fun provideSendFeatureToggles(featureTogglesManager: FeatureTogglesManager): PushNotificationsFeatureToggles { + return DefaultPushNotificationsFeatureToggles(featureTogglesManager = featureTogglesManager) + } +} \ No newline at end of file diff --git a/features/push-notifications/impl/src/main/java/com/tangem/features/pushnotifications/impl/featuretoggles/DefaultPushNotificationsFeatureToggles.kt b/features/push-notifications/impl/src/main/java/com/tangem/features/pushnotifications/impl/featuretoggles/DefaultPushNotificationsFeatureToggles.kt new file mode 100644 index 0000000000..05c8d6dfc4 --- /dev/null +++ b/features/push-notifications/impl/src/main/java/com/tangem/features/pushnotifications/impl/featuretoggles/DefaultPushNotificationsFeatureToggles.kt @@ -0,0 +1,11 @@ +package com.tangem.features.pushnotifications.impl.featuretoggles + +import com.tangem.core.featuretoggle.manager.FeatureTogglesManager +import com.tangem.features.pushnotifications.api.featuretoggles.PushNotificationsFeatureToggles + +internal class DefaultPushNotificationsFeatureToggles( + private val featureTogglesManager: FeatureTogglesManager, +) : PushNotificationsFeatureToggles { + override val isRedesignedSendEnabled: Boolean + get() = featureTogglesManager.isFeatureEnabled("PUSH_NOTIFICATIONS_ENABLED") +} \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts index ab66fbb133..acbedb8b55 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -180,6 +180,9 @@ include(":features:details:impl") include(":features:disclaimer:api") include(":features:disclaimer:impl") + +include(":features:push-notifications:api") +include(":features:push-notifications:impl") // endregion Feature modules // region Domain modules From 7619e9706b229b96d64dc7161969fed7f01a8541 Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 17 Jun 2024 13:18:35 +0500 Subject: [PATCH 3/3] Updated on 2026-08-14 --- .../common/settings/IntentSettingsManager.kt | 17 +++--- .../java/com/tangem/tap/di/UtilsModule.kt | 2 +- .../core/ui/utils/RequestPushPermission.kt | 55 +++++++++++++++++ .../api/navigation/PushNotificationsRouter.kt | 8 +++ .../api/utils/PermissionUtils.kt | 10 ++++ .../push-notifications/impl/build.gradle.kts | 3 + .../impl/PushNotificationsFragment.kt | 39 ++++++++++++ .../impl/di/PushNotificationsModule.kt | 24 ++++++++ .../DefaultPushNotificationsRouter.kt | 18 ++++++ .../InnerPushNotificationsRouter.kt | 8 +++ .../ui/PushNotificationsScreen.kt | 59 +++++++++++++++++++ .../viewmodel/PushNotificationViewModel.kt | 46 +++++++++++++++ .../PushNotificationsClickIntents.kt | 11 ++++ 13 files changed, 289 insertions(+), 11 deletions(-) create mode 100644 core/ui/src/main/java/com/tangem/core/ui/utils/RequestPushPermission.kt create mode 100644 features/push-notifications/api/src/main/java/com/tangem/features/pushnotifications/api/navigation/PushNotificationsRouter.kt create mode 100644 features/push-notifications/api/src/main/java/com/tangem/features/pushnotifications/api/utils/PermissionUtils.kt create mode 100644 features/push-notifications/impl/src/main/java/com/tangem/features/pushnotifications/impl/PushNotificationsFragment.kt create mode 100644 features/push-notifications/impl/src/main/java/com/tangem/features/pushnotifications/impl/di/PushNotificationsModule.kt create mode 100644 features/push-notifications/impl/src/main/java/com/tangem/features/pushnotifications/impl/navigation/DefaultPushNotificationsRouter.kt create mode 100644 features/push-notifications/impl/src/main/java/com/tangem/features/pushnotifications/impl/navigation/InnerPushNotificationsRouter.kt create mode 100644 features/push-notifications/impl/src/main/java/com/tangem/features/pushnotifications/impl/presentation/ui/PushNotificationsScreen.kt create mode 100644 features/push-notifications/impl/src/main/java/com/tangem/features/pushnotifications/impl/presentation/viewmodel/PushNotificationViewModel.kt create mode 100644 features/push-notifications/impl/src/main/java/com/tangem/features/pushnotifications/impl/presentation/viewmodel/PushNotificationsClickIntents.kt diff --git a/app/src/main/java/com/tangem/tap/common/settings/IntentSettingsManager.kt b/app/src/main/java/com/tangem/tap/common/settings/IntentSettingsManager.kt index 7dbed67b24..09305fd59e 100644 --- a/app/src/main/java/com/tangem/tap/common/settings/IntentSettingsManager.kt +++ b/app/src/main/java/com/tangem/tap/common/settings/IntentSettingsManager.kt @@ -1,20 +1,17 @@ package com.tangem.tap.common.settings +import android.content.Context import android.content.Intent import android.net.Uri import android.provider.Settings import com.tangem.core.navigation.settings.SettingsManager -import com.tangem.tap.foregroundActivityObserver -import com.tangem.tap.withForegroundActivity -internal class IntentSettingsManager : SettingsManager { +internal class IntentSettingsManager(val context: Context) : SettingsManager { override fun openSettings() { - foregroundActivityObserver.withForegroundActivity { activity -> - val openSettingsIntent = Intent( - Settings.ACTION_APPLICATION_DETAILS_SETTINGS, - Uri.fromParts("package", activity.packageName, null), - ) - activity.startActivity(openSettingsIntent) - } + val openSettingsIntent = Intent( + Settings.ACTION_APPLICATION_DETAILS_SETTINGS, + Uri.fromParts("package", context.packageName, null), + ) + context.startActivity(openSettingsIntent) } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/di/UtilsModule.kt b/app/src/main/java/com/tangem/tap/di/UtilsModule.kt index e5c898b899..2db04791cf 100644 --- a/app/src/main/java/com/tangem/tap/di/UtilsModule.kt +++ b/app/src/main/java/com/tangem/tap/di/UtilsModule.kt @@ -40,5 +40,5 @@ internal object UtilsModule { @Provides @Singleton - fun provideSettingsManager(): SettingsManager = IntentSettingsManager() + fun provideSettingsManager(@ApplicationContext context: Context): SettingsManager = IntentSettingsManager(context) } \ No newline at end of file diff --git a/core/ui/src/main/java/com/tangem/core/ui/utils/RequestPushPermission.kt b/core/ui/src/main/java/com/tangem/core/ui/utils/RequestPushPermission.kt new file mode 100644 index 0000000000..134039ed07 --- /dev/null +++ b/core/ui/src/main/java/com/tangem/core/ui/utils/RequestPushPermission.kt @@ -0,0 +1,55 @@ +package com.tangem.core.ui.utils + +import android.Manifest +import android.os.Build +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.MutableState +import com.google.accompanist.permissions.ExperimentalPermissionsApi +import com.google.accompanist.permissions.isGranted +import com.google.accompanist.permissions.rememberPermissionState +import com.google.accompanist.permissions.shouldShowRationale + +/** + * Returns push permission requester. + * Handles granting permission from app settings. + */ +@OptIn(ExperimentalPermissionsApi::class) +@Composable +fun requestPushPermission( + isFirstTimeAsking: Boolean, + isClicked: MutableState, + onAllow: () -> Unit, + onDeny: () -> Unit, + onOpenSettings: () -> Unit, +): () -> Unit { + val permissionState = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + val permission = Manifest.permission.POST_NOTIFICATIONS + val tempPermissionState = rememberPermissionState(permission = permission) + rememberPermissionState(permission = permission) { + when { + it -> onAllow() + !tempPermissionState.status.shouldShowRationale && !isFirstTimeAsking -> onOpenSettings() + else -> onDeny() + } + } + } else { + null + } + + // Check if user granted permission and close bottom sheet + LaunchedEffect(key1 = permissionState?.status, isClicked) { + if (!isClicked.value) return@LaunchedEffect + if (permissionState?.status?.isGranted == true) { + onAllow() + } else { + onDeny() + } + } + + return if (permissionState == null) { + onOpenSettings + } else { + permissionState::launchPermissionRequest + } +} \ No newline at end of file diff --git a/features/push-notifications/api/src/main/java/com/tangem/features/pushnotifications/api/navigation/PushNotificationsRouter.kt b/features/push-notifications/api/src/main/java/com/tangem/features/pushnotifications/api/navigation/PushNotificationsRouter.kt new file mode 100644 index 0000000000..6881437671 --- /dev/null +++ b/features/push-notifications/api/src/main/java/com/tangem/features/pushnotifications/api/navigation/PushNotificationsRouter.kt @@ -0,0 +1,8 @@ +package com.tangem.features.pushnotifications.api.navigation + +import androidx.fragment.app.Fragment + +interface PushNotificationsRouter { + + fun entryFragment(): Fragment +} \ No newline at end of file diff --git a/features/push-notifications/api/src/main/java/com/tangem/features/pushnotifications/api/utils/PermissionUtils.kt b/features/push-notifications/api/src/main/java/com/tangem/features/pushnotifications/api/utils/PermissionUtils.kt new file mode 100644 index 0000000000..6092954f2a --- /dev/null +++ b/features/push-notifications/api/src/main/java/com/tangem/features/pushnotifications/api/utils/PermissionUtils.kt @@ -0,0 +1,10 @@ +package com.tangem.features.pushnotifications.api.utils + +import android.Manifest +import android.os.Build + +val PUSH_PERMISSION = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + Manifest.permission.POST_NOTIFICATIONS +} else { + "android.permission.POST_NOTIFICATIONS" +} \ No newline at end of file diff --git a/features/push-notifications/impl/build.gradle.kts b/features/push-notifications/impl/build.gradle.kts index 4524106ece..35c5535827 100644 --- a/features/push-notifications/impl/build.gradle.kts +++ b/features/push-notifications/impl/build.gradle.kts @@ -29,6 +29,9 @@ dependencies { implementation(projects.core.ui) implementation(projects.core.featuretoggles) + /** Common modules */ + implementation(projects.common.routing) + /** Domain module */ implementation(projects.domain.settings) diff --git a/features/push-notifications/impl/src/main/java/com/tangem/features/pushnotifications/impl/PushNotificationsFragment.kt b/features/push-notifications/impl/src/main/java/com/tangem/features/pushnotifications/impl/PushNotificationsFragment.kt new file mode 100644 index 0000000000..0b8fedc562 --- /dev/null +++ b/features/push-notifications/impl/src/main/java/com/tangem/features/pushnotifications/impl/PushNotificationsFragment.kt @@ -0,0 +1,39 @@ +package com.tangem.features.pushnotifications.impl + +import androidx.activity.compose.BackHandler +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.fragment.app.viewModels +import com.tangem.core.ui.UiDependencies +import com.tangem.core.ui.components.NavigationBar3ButtonsScrim +import com.tangem.core.ui.screen.ComposeFragment +import com.tangem.features.pushnotifications.impl.presentation.ui.PushNotificationsScreen +import com.tangem.features.pushnotifications.impl.presentation.viewmodel.PushNotificationViewModel +import dagger.hilt.android.AndroidEntryPoint +import javax.inject.Inject + +@AndroidEntryPoint +internal class PushNotificationsFragment : ComposeFragment() { + + @Inject + override lateinit var uiDependencies: UiDependencies + + private val viewModel by viewModels() + + @Composable + override fun ScreenContent(modifier: Modifier) { + BackHandler(onBack = requireActivity()::finish) + NavigationBar3ButtonsScrim() + PushNotificationsScreen( + onShowAllow = viewModel::onAllowPermission, + onAllow = viewModel::onAllowedPermission, + onLater = viewModel::onAskLater, + onOpenSettings = viewModel::openSettings, + ) + } + + companion object { + /** Create push notifications fragment instance */ + fun create(): PushNotificationsFragment = PushNotificationsFragment() + } +} \ No newline at end of file diff --git a/features/push-notifications/impl/src/main/java/com/tangem/features/pushnotifications/impl/di/PushNotificationsModule.kt b/features/push-notifications/impl/src/main/java/com/tangem/features/pushnotifications/impl/di/PushNotificationsModule.kt new file mode 100644 index 0000000000..b568880483 --- /dev/null +++ b/features/push-notifications/impl/src/main/java/com/tangem/features/pushnotifications/impl/di/PushNotificationsModule.kt @@ -0,0 +1,24 @@ +package com.tangem.features.pushnotifications.impl.di + +import com.tangem.common.routing.AppRouter +import com.tangem.features.pushnotifications.api.navigation.PushNotificationsRouter +import com.tangem.features.pushnotifications.impl.navigation.DefaultPushNotificationsRouter +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.android.components.ActivityComponent +import dagger.hilt.android.scopes.ActivityScoped + +/** + * DI module provides implementation of [PushNotificationsRouter] + */ +@Module +@InstallIn(ActivityComponent::class) +object PushNotificationsModule { + + @Provides + @ActivityScoped + fun provideDisclaimerRouter(appRouter: AppRouter): PushNotificationsRouter { + return DefaultPushNotificationsRouter(appRouter) + } +} \ No newline at end of file diff --git a/features/push-notifications/impl/src/main/java/com/tangem/features/pushnotifications/impl/navigation/DefaultPushNotificationsRouter.kt b/features/push-notifications/impl/src/main/java/com/tangem/features/pushnotifications/impl/navigation/DefaultPushNotificationsRouter.kt new file mode 100644 index 0000000000..b2536cfe47 --- /dev/null +++ b/features/push-notifications/impl/src/main/java/com/tangem/features/pushnotifications/impl/navigation/DefaultPushNotificationsRouter.kt @@ -0,0 +1,18 @@ +package com.tangem.features.pushnotifications.impl.navigation + +import androidx.fragment.app.Fragment +import com.tangem.common.routing.AppRoute +import com.tangem.common.routing.AppRouter +import com.tangem.features.pushnotifications.impl.PushNotificationsFragment +import javax.inject.Inject + +internal class DefaultPushNotificationsRouter @Inject constructor( + private val appRouter: AppRouter, +) : InnerPushNotificationsRouter { + + override fun entryFragment(): Fragment = PushNotificationsFragment.create() + + override fun openHome() { + appRouter.push(AppRoute.Home) + } +} \ No newline at end of file diff --git a/features/push-notifications/impl/src/main/java/com/tangem/features/pushnotifications/impl/navigation/InnerPushNotificationsRouter.kt b/features/push-notifications/impl/src/main/java/com/tangem/features/pushnotifications/impl/navigation/InnerPushNotificationsRouter.kt new file mode 100644 index 0000000000..20050eca1e --- /dev/null +++ b/features/push-notifications/impl/src/main/java/com/tangem/features/pushnotifications/impl/navigation/InnerPushNotificationsRouter.kt @@ -0,0 +1,8 @@ +package com.tangem.features.pushnotifications.impl.navigation + +import com.tangem.features.pushnotifications.api.navigation.PushNotificationsRouter + +interface InnerPushNotificationsRouter : PushNotificationsRouter { + + fun openHome() +} \ No newline at end of file diff --git a/features/push-notifications/impl/src/main/java/com/tangem/features/pushnotifications/impl/presentation/ui/PushNotificationsScreen.kt b/features/push-notifications/impl/src/main/java/com/tangem/features/pushnotifications/impl/presentation/ui/PushNotificationsScreen.kt new file mode 100644 index 0000000000..02c7036aad --- /dev/null +++ b/features/push-notifications/impl/src/main/java/com/tangem/features/pushnotifications/impl/presentation/ui/PushNotificationsScreen.kt @@ -0,0 +1,59 @@ +package com.tangem.features.pushnotifications.impl.presentation.ui + +import androidx.compose.foundation.layout.systemBarsPadding +import androidx.compose.runtime.Composable +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.ui.Modifier +import com.tangem.core.ui.components.showcase.Showcase +import com.tangem.core.ui.components.showcase.model.ShowcaseButtonModel +import com.tangem.core.ui.components.showcase.model.ShowcaseItemModel +import com.tangem.core.ui.extensions.resourceReference +import com.tangem.core.ui.utils.requestPushPermission +import com.tangem.feature.pushnotifications.impl.R +import kotlinx.collections.immutable.persistentListOf + +@Composable +internal fun PushNotificationsScreen( + onShowAllow: () -> Unit, + onAllow: () -> Unit, + onLater: () -> Unit, + onOpenSettings: () -> Unit, +) { + val isClicked = remember { mutableStateOf(false) } + val requestPushPermission = requestPushPermission( + isFirstTimeAsking = true, + isClicked = isClicked, + onAllow = onAllow, + onDeny = onLater, + onOpenSettings = onOpenSettings, + ) + + Showcase( + headerIconRes = R.drawable.ic_notifications_unread_24, + headerText = resourceReference(R.string.user_push_notification_agreement_header), + showcaseItems = persistentListOf( + ShowcaseItemModel( + R.drawable.ic_rocket_launch_24, + resourceReference(R.string.user_push_notification_agreement_argument_one), + ), + ShowcaseItemModel( + R.drawable.ic_storefront_24, + resourceReference(R.string.user_push_notification_agreement_argument_two), + ), + ), + primaryButton = ShowcaseButtonModel( + buttonText = resourceReference(R.string.common_allow), + onClick = { + isClicked.value = true + onShowAllow() + requestPushPermission() + }, + ), + secondaryButton = ShowcaseButtonModel( + buttonText = resourceReference(R.string.common_later), + onClick = onLater, + ), + modifier = Modifier.systemBarsPadding(), + ) +} \ No newline at end of file diff --git a/features/push-notifications/impl/src/main/java/com/tangem/features/pushnotifications/impl/presentation/viewmodel/PushNotificationViewModel.kt b/features/push-notifications/impl/src/main/java/com/tangem/features/pushnotifications/impl/presentation/viewmodel/PushNotificationViewModel.kt new file mode 100644 index 0000000000..6653043296 --- /dev/null +++ b/features/push-notifications/impl/src/main/java/com/tangem/features/pushnotifications/impl/presentation/viewmodel/PushNotificationViewModel.kt @@ -0,0 +1,46 @@ +package com.tangem.features.pushnotifications.impl.presentation.viewmodel + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.tangem.core.navigation.settings.SettingsManager +import com.tangem.domain.settings.DelayPermissionRequestUseCase +import com.tangem.domain.settings.NeverRequestPermissionUseCase +import com.tangem.domain.settings.SetFirstTimeAskingPermissionUseCase +import com.tangem.features.pushnotifications.api.utils.PUSH_PERMISSION +import com.tangem.features.pushnotifications.impl.navigation.DefaultPushNotificationsRouter +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.launch +import javax.inject.Inject + +@HiltViewModel +internal class PushNotificationViewModel @Inject constructor( + private val setFirstTimeAskingPermissionUseCase: SetFirstTimeAskingPermissionUseCase, + private val delayPermissionRequestUseCase: DelayPermissionRequestUseCase, + private val neverRequestPermissionUseCase: NeverRequestPermissionUseCase, + private val router: DefaultPushNotificationsRouter, + private val settingsManager: SettingsManager, +) : ViewModel(), PushNotificationsClickIntents { + + override fun onAskLater() { + viewModelScope.launch { + delayPermissionRequestUseCase(PUSH_PERMISSION) + setFirstTimeAskingPermissionUseCase(PUSH_PERMISSION) + } + router.openHome() + } + + override fun onAllowPermission() { + viewModelScope.launch { + setFirstTimeAskingPermissionUseCase(PUSH_PERMISSION) + } + } + + override fun onAllowedPermission() { + viewModelScope.launch { + neverRequestPermissionUseCase(PUSH_PERMISSION) + } + router.openHome() + } + + override fun openSettings() = settingsManager.openSettings() +} \ No newline at end of file diff --git a/features/push-notifications/impl/src/main/java/com/tangem/features/pushnotifications/impl/presentation/viewmodel/PushNotificationsClickIntents.kt b/features/push-notifications/impl/src/main/java/com/tangem/features/pushnotifications/impl/presentation/viewmodel/PushNotificationsClickIntents.kt new file mode 100644 index 0000000000..f101e15e7a --- /dev/null +++ b/features/push-notifications/impl/src/main/java/com/tangem/features/pushnotifications/impl/presentation/viewmodel/PushNotificationsClickIntents.kt @@ -0,0 +1,11 @@ +package com.tangem.features.pushnotifications.impl.presentation.viewmodel + +internal interface PushNotificationsClickIntents { + fun onAskLater() + + fun onAllowPermission() + + fun onAllowedPermission() + + fun openSettings() +} \ No newline at end of file