From 5566331bdb0c475c0da0f4cbef7042323d0d3d9c Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 19 Jun 2024 21:10:01 +0500 Subject: [PATCH 1/3] Updated on 2026-08-14 --- app/build.gradle.kts | 4 ++++ .../java/com/tangem/tap/ApplicationEntryPoint.kt | 3 +++ .../main/java/com/tangem/tap/TangemApplication.kt | 5 +++++ .../tangem/tap/proxy/redux/DaggerGraphAction.kt | 4 ++++ .../tangem/tap/proxy/redux/DaggerGraphReducer.kt | 2 ++ .../tangem/tap/proxy/redux/DaggerGraphState.kt | 6 ++++++ .../com/tangem/tap/routing/utils/ChildFactory.kt | 15 ++++++++++++++- .../kotlin/com/tangem/common/routing/AppRoute.kt | 3 +++ features/details/impl/build.gradle.kts | 1 + features/disclaimer/impl/build.gradle.kts | 3 ++- .../PushNotificationsFeatureToggles.kt | 2 +- features/push-notifications/impl/build.gradle.kts | 5 ++++- .../DefaultPushNotificationsFeatureToggles.kt | 2 +- features/wallet/impl/build.gradle.kts | 1 + 14 files changed, 51 insertions(+), 5 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 3362aa5bbb..fa383da707 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -136,6 +136,10 @@ dependencies { implementation(projects.features.staking.impl) implementation(projects.features.details.api) implementation(projects.features.details.impl) + implementation(projects.features.disclaimer.api) + implementation(projects.features.disclaimer.impl) + implementation(projects.features.pushNotifications.api) + implementation(projects.features.pushNotifications.impl) /** AndroidX libraries */ implementation(deps.androidx.core.ktx) diff --git a/app/src/main/java/com/tangem/tap/ApplicationEntryPoint.kt b/app/src/main/java/com/tangem/tap/ApplicationEntryPoint.kt index 24ce4e1822..038f6f9a6a 100644 --- a/app/src/main/java/com/tangem/tap/ApplicationEntryPoint.kt +++ b/app/src/main/java/com/tangem/tap/ApplicationEntryPoint.kt @@ -31,6 +31,7 @@ import com.tangem.domain.wallets.repository.WalletsRepository import com.tangem.domain.wallets.usecase.GenerateWalletNameUseCase import com.tangem.features.details.DetailsFeatureToggles import com.tangem.features.managetokens.featuretoggles.ManageTokensFeatureToggles +import com.tangem.features.pushnotifications.api.featuretoggles.PushNotificationsFeatureToggles import com.tangem.features.send.api.featuretoggles.SendFeatureToggles import com.tangem.tap.domain.walletconnect2.domain.WalletConnectSessionsRepository import com.tangem.tap.features.customtoken.api.featuretoggles.CustomTokenFeatureToggles @@ -116,4 +117,6 @@ interface ApplicationEntryPoint { fun getShareManager(): ShareManager fun getAppRouter(): AppRouter + + fun getPushNotificationsFeatureToggles(): PushNotificationsFeatureToggles } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/TangemApplication.kt b/app/src/main/java/com/tangem/tap/TangemApplication.kt index e4c530273b..2b87ffb1f0 100644 --- a/app/src/main/java/com/tangem/tap/TangemApplication.kt +++ b/app/src/main/java/com/tangem/tap/TangemApplication.kt @@ -46,6 +46,7 @@ import com.tangem.domain.wallets.repository.WalletsRepository import com.tangem.domain.wallets.usecase.GenerateWalletNameUseCase import com.tangem.features.details.DetailsFeatureToggles import com.tangem.features.managetokens.featuretoggles.ManageTokensFeatureToggles +import com.tangem.features.pushnotifications.api.featuretoggles.PushNotificationsFeatureToggles import com.tangem.features.send.api.featuretoggles.SendFeatureToggles import com.tangem.tap.common.analytics.AnalyticsFactory import com.tangem.tap.common.analytics.api.AnalyticsHandlerBuilder @@ -193,6 +194,9 @@ abstract class TangemApplication : Application(), ImageLoaderFactory { private val appRouter: AppRouter get() = entryPoint.getAppRouter() + + private val pushNotificationsFeatureToggles: PushNotificationsFeatureToggles + get() = entryPoint.getPushNotificationsFeatureToggles() // endregion override fun onCreate() { @@ -284,6 +288,7 @@ abstract class TangemApplication : Application(), ImageLoaderFactory { urlOpener = urlOpener, shareManager = shareManager, appRouter = appRouter, + pushNotificationsFeatureToggles = pushNotificationsFeatureToggles, ), ), ) diff --git a/app/src/main/java/com/tangem/tap/proxy/redux/DaggerGraphAction.kt b/app/src/main/java/com/tangem/tap/proxy/redux/DaggerGraphAction.kt index 3b479b590c..cfd8b41b5f 100644 --- a/app/src/main/java/com/tangem/tap/proxy/redux/DaggerGraphAction.kt +++ b/app/src/main/java/com/tangem/tap/proxy/redux/DaggerGraphAction.kt @@ -4,7 +4,9 @@ import com.tangem.core.navigation.email.EmailSender import com.tangem.domain.card.ScanCardUseCase import com.tangem.domain.card.repository.CardSdkConfigRepository import com.tangem.feature.qrscanning.QrScanningRouter +import com.tangem.features.disclaimer.api.DisclaimerRouter import com.tangem.features.managetokens.navigation.ManageTokensUi +import com.tangem.features.pushnotifications.api.navigation.PushNotificationsRouter import com.tangem.features.send.api.navigation.SendRouter import com.tangem.features.staking.api.navigation.StakingRouter import com.tangem.features.tester.api.TesterRouter @@ -27,5 +29,7 @@ sealed interface DaggerGraphAction : Action { val qrScanningRouter: QrScanningRouter, val emailSender: EmailSender, val stakingRouter: StakingRouter, + val disclaimerRouter: DisclaimerRouter, + val pushNotificationsRouter: PushNotificationsRouter, ) : DaggerGraphAction } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/proxy/redux/DaggerGraphReducer.kt b/app/src/main/java/com/tangem/tap/proxy/redux/DaggerGraphReducer.kt index 9f1f5fddca..0f98fea5e6 100644 --- a/app/src/main/java/com/tangem/tap/proxy/redux/DaggerGraphReducer.kt +++ b/app/src/main/java/com/tangem/tap/proxy/redux/DaggerGraphReducer.kt @@ -24,6 +24,8 @@ object DaggerGraphReducer { qrScanningRouter = action.qrScanningRouter, emailSender = action.emailSender, stakingRouter = action.stakingRouter, + disclaimerRouter = action.disclaimerRouter, + pushNotificationsRouter = action.pushNotificationsRouter, ) } } diff --git a/app/src/main/java/com/tangem/tap/proxy/redux/DaggerGraphState.kt b/app/src/main/java/com/tangem/tap/proxy/redux/DaggerGraphState.kt index 8a555f904d..1e0ddd0477 100644 --- a/app/src/main/java/com/tangem/tap/proxy/redux/DaggerGraphState.kt +++ b/app/src/main/java/com/tangem/tap/proxy/redux/DaggerGraphState.kt @@ -28,8 +28,11 @@ import com.tangem.domain.wallets.repository.WalletsRepository import com.tangem.domain.wallets.usecase.GenerateWalletNameUseCase import com.tangem.feature.qrscanning.QrScanningRouter import com.tangem.features.details.DetailsFeatureToggles +import com.tangem.features.disclaimer.api.DisclaimerRouter import com.tangem.features.managetokens.featuretoggles.ManageTokensFeatureToggles import com.tangem.features.managetokens.navigation.ManageTokensUi +import com.tangem.features.pushnotifications.api.featuretoggles.PushNotificationsFeatureToggles +import com.tangem.features.pushnotifications.api.navigation.PushNotificationsRouter import com.tangem.features.send.api.featuretoggles.SendFeatureToggles import com.tangem.features.send.api.navigation.SendRouter import com.tangem.features.staking.api.navigation.StakingRouter @@ -85,4 +88,7 @@ data class DaggerGraphState( val urlOpener: UrlOpener? = null, val shareManager: ShareManager? = null, val appRouter: AppRouter? = null, + val disclaimerRouter: DisclaimerRouter? = null, + val pushNotificationsFeatureToggles: PushNotificationsFeatureToggles? = null, + val pushNotificationsRouter: PushNotificationsRouter? = null, ) : StateType \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/routing/utils/ChildFactory.kt b/app/src/main/java/com/tangem/tap/routing/utils/ChildFactory.kt index 3199a16968..3a6bcba601 100644 --- a/app/src/main/java/com/tangem/tap/routing/utils/ChildFactory.kt +++ b/app/src/main/java/com/tangem/tap/routing/utils/ChildFactory.kt @@ -7,6 +7,9 @@ import com.tangem.feature.referral.ReferralFragment import com.tangem.feature.swap.presentation.SwapFragment import com.tangem.features.details.DetailsFeatureToggles import com.tangem.features.details.component.DetailsComponent +import com.tangem.features.disclaimer.api.DisclaimerRouter +import com.tangem.features.pushnotifications.api.featuretoggles.PushNotificationsFeatureToggles +import com.tangem.features.pushnotifications.api.navigation.PushNotificationsRouter import com.tangem.features.send.api.navigation.SendRouter import com.tangem.features.staking.api.navigation.StakingRouter import com.tangem.features.tester.api.TesterRouter @@ -48,6 +51,9 @@ internal class ChildFactory @Inject constructor( private val stakingRouter: StakingRouter, private val testerRouter: TesterRouter, private val detailsFeatureToggles: DetailsFeatureToggles, + private val pushNotificationsFeatureToggles: PushNotificationsFeatureToggles, + private val pushNotificationRouter: PushNotificationsRouter, + private val disclaimerRouter: DisclaimerRouter, ) { @Suppress("LongMethod", "CyclomaticComplexMethod") @@ -97,7 +103,11 @@ internal class ChildFactory @Inject constructor( route.asFragmentChild(Provider { SecurityModeFragment() }) } is AppRoute.Disclaimer -> { - route.asFragmentChild(Provider { DisclaimerFragment() }) + if (pushNotificationsFeatureToggles.isPushNotificationsEnabled) { + route.asFragmentChild(Provider { disclaimerRouter.entryFragment() }) + } else { + route.asFragmentChild(Provider { DisclaimerFragment() }) + } } is AppRoute.Home -> { route.asFragmentChild(Provider { HomeFragment() }) @@ -147,6 +157,9 @@ internal class ChildFactory @Inject constructor( is AppRoute.Staking -> { route.asFragmentChild(Provider { stakingRouter.getEntryFragment() }) } + is AppRoute.PushNotification -> { + route.asFragmentChild(Provider { pushNotificationRouter.entryFragment() }) + } } } diff --git a/common/routing/src/main/kotlin/com/tangem/common/routing/AppRoute.kt b/common/routing/src/main/kotlin/com/tangem/common/routing/AppRoute.kt index 0720f58f9d..2b6831a967 100644 --- a/common/routing/src/main/kotlin/com/tangem/common/routing/AppRoute.kt +++ b/common/routing/src/main/kotlin/com/tangem/common/routing/AppRoute.kt @@ -197,4 +197,7 @@ sealed class AppRoute(val path: String) : Route { const val YIELD_KEY = "yield" } } + + @Serializable + data object PushNotification : AppRoute(path = "/push_notification") } \ No newline at end of file diff --git a/features/details/impl/build.gradle.kts b/features/details/impl/build.gradle.kts index 8b6951e1e9..1edff72903 100644 --- a/features/details/impl/build.gradle.kts +++ b/features/details/impl/build.gradle.kts @@ -15,6 +15,7 @@ dependencies { /* Project - API */ implementation(projects.features.details.api) + implementation(projects.features.disclaimer.api) implementation(projects.features.tester.api) /* Project - Core */ diff --git a/features/disclaimer/impl/build.gradle.kts b/features/disclaimer/impl/build.gradle.kts index a2053b346f..c82364e03f 100644 --- a/features/disclaimer/impl/build.gradle.kts +++ b/features/disclaimer/impl/build.gradle.kts @@ -24,6 +24,7 @@ dependencies { implementation(deps.compose.accompanist.systemUiController) implementation(deps.compose.accompanist.permission) implementation(deps.compose.material3) + implementation(deps.compose.material) /** Core modules */ implementation(projects.core.ui) @@ -35,12 +36,12 @@ dependencies { /** Domain modules */ implementation(projects.domain.models) implementation(projects.domain.card) + implementation(projects.domain.settings) /** Feature modules */ implementation(projects.features.disclaimer.api) /** DI */ implementation(deps.hilt.android) - implementation(project(":domain:settings")) kapt(deps.hilt.kapt) } \ 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 index 06346f3b01..18024f464f 100644 --- 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 @@ -5,5 +5,5 @@ package com.tangem.features.pushnotifications.api.featuretoggles */ interface PushNotificationsFeatureToggles { /** Availability of push notifications */ - val isRedesignedSendEnabled: Boolean + val isPushNotificationsEnabled: Boolean } \ 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 35c5535827..7cf8d0915d 100644 --- a/features/push-notifications/impl/build.gradle.kts +++ b/features/push-notifications/impl/build.gradle.kts @@ -28,6 +28,10 @@ dependencies { /** Core modules */ implementation(projects.core.ui) implementation(projects.core.featuretoggles) + implementation(projects.core.navigation) + + /** Common modules */ + implementation(projects.common.routing) /** Common modules */ implementation(projects.common.routing) @@ -40,6 +44,5 @@ dependencies { /** 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/featuretoggles/DefaultPushNotificationsFeatureToggles.kt b/features/push-notifications/impl/src/main/java/com/tangem/features/pushnotifications/impl/featuretoggles/DefaultPushNotificationsFeatureToggles.kt index 05c8d6dfc4..296ae7ca81 100644 --- 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 @@ -6,6 +6,6 @@ import com.tangem.features.pushnotifications.api.featuretoggles.PushNotification internal class DefaultPushNotificationsFeatureToggles( private val featureTogglesManager: FeatureTogglesManager, ) : PushNotificationsFeatureToggles { - override val isRedesignedSendEnabled: Boolean + override val isPushNotificationsEnabled: Boolean get() = featureTogglesManager.isFeatureEnabled("PUSH_NOTIFICATIONS_ENABLED") } \ No newline at end of file diff --git a/features/wallet/impl/build.gradle.kts b/features/wallet/impl/build.gradle.kts index 7022b31c98..f9f232db02 100644 --- a/features/wallet/impl/build.gradle.kts +++ b/features/wallet/impl/build.gradle.kts @@ -89,4 +89,5 @@ dependencies { implementation(projects.features.tester.api) implementation(projects.features.manageTokens.api) implementation(projects.features.details.api) + implementation(projects.features.pushNotifications.api) } \ No newline at end of file From 76c5fd40323f65eb428f4bd2a6e0d15bad9a4b2b Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 19 Jun 2024 21:11:19 +0500 Subject: [PATCH 2/3] Updated on 2026-08-14 --- .../main/java/com/tangem/tap/MainActivity.kt | 39 +++--- .../domain/scanCard/UseCaseScanProcessor.kt | 6 +- .../com/tangem/core/ui/components/Buttons.kt | 3 +- .../appbar/AppBarWithAdditionalButtons.kt | 13 +- .../disclaimer/api/DisclaimerRouter.kt | 4 - .../disclaimer/impl/DisclaimerFragment.kt | 4 +- .../navigation/DefaultDisclaimerRouter.kt | 5 +- .../impl/presentation/ui/DisclaimerScreen.kt | 84 +++++++------ .../ui/DisclaimerWebViewClient.kt | 24 +++- .../viewmodel/DisclaimerViewModel.kt | 5 +- .../PushNotificationsBottomSheetConfig.kt | 11 ++ .../presentation/wallet/ui/WalletScreen.kt | 29 +++-- .../PushNotificationsBottomSheet.kt | 117 ++++++++++++++++++ .../wallet/viewmodels/WalletViewModel.kt | 37 +++++- .../viewmodels/intents/WalletClickIntents.kt | 5 +- .../WalletPushPermissionClickIntents.kt | 44 +++++++ 16 files changed, 338 insertions(+), 92 deletions(-) create mode 100644 features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/PushNotificationsBottomSheetConfig.kt create mode 100644 features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/PushNotificationsBottomSheet.kt create mode 100644 features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/intents/WalletPushPermissionClickIntents.kt diff --git a/app/src/main/java/com/tangem/tap/MainActivity.kt b/app/src/main/java/com/tangem/tap/MainActivity.kt index cf81714141..dddfa0a863 100644 --- a/app/src/main/java/com/tangem/tap/MainActivity.kt +++ b/app/src/main/java/com/tangem/tap/MainActivity.kt @@ -1,15 +1,12 @@ package com.tangem.tap -import android.Manifest import android.annotation.SuppressLint import android.app.PendingIntent import android.content.Intent import android.content.IntentFilter import android.content.pm.ActivityInfo -import android.content.pm.PackageManager import android.content.res.Configuration import android.nfc.NfcAdapter -import android.os.Build import android.os.Bundle import android.view.View import androidx.activity.SystemBarStyle @@ -21,8 +18,6 @@ import androidx.appcompat.app.AppCompatDelegate import androidx.appcompat.app.AppCompatDelegate.setDefaultNightMode import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.toArgb -import androidx.core.app.ActivityCompat -import androidx.core.content.ContextCompat import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen import androidx.lifecycle.Lifecycle import androidx.lifecycle.flowWithLifecycle @@ -46,6 +41,7 @@ import com.tangem.core.ui.extensions.resolveReference import com.tangem.data.card.sdk.CardSdkLifecycleObserver import com.tangem.domain.apptheme.model.AppThemeMode import com.tangem.domain.card.ScanCardUseCase +import com.tangem.domain.card.repository.CardRepository import com.tangem.domain.card.repository.CardSdkConfigRepository import com.tangem.domain.settings.repositories.SettingsRepository import com.tangem.domain.tokens.GetPolkadotCheckHasImmortalUseCase @@ -53,7 +49,9 @@ import com.tangem.domain.tokens.GetPolkadotCheckHasResetUseCase import com.tangem.domain.wallets.legacy.UserWalletsListManager import com.tangem.feature.qrscanning.QrScanningRouter import com.tangem.feature.wallet.presentation.wallet.analytics.WalletScreenAnalyticsEvent +import com.tangem.features.disclaimer.api.DisclaimerRouter import com.tangem.features.managetokens.navigation.ManageTokensUi +import com.tangem.features.pushnotifications.api.navigation.PushNotificationsRouter import com.tangem.features.send.api.navigation.SendRouter import com.tangem.features.staking.api.navigation.StakingRouter import com.tangem.features.tester.api.TesterRouter @@ -67,6 +65,7 @@ import com.tangem.tap.common.OnActivityResultCallback import com.tangem.tap.common.SnackbarHandler import com.tangem.tap.common.apptheme.MutableAppThemeModeHolder import com.tangem.tap.common.extensions.dispatchNavigationAction +import com.tangem.tap.common.extensions.inject import com.tangem.tap.common.extensions.showFragmentAllowingStateLoss import com.tangem.tap.common.redux.NotificationsHandler import com.tangem.tap.domain.sdk.TangemSdkManager @@ -79,10 +78,10 @@ import com.tangem.tap.features.main.model.Toast import com.tangem.tap.features.onboarding.products.wallet.redux.BackupAction import com.tangem.tap.proxy.AppStateHolder import com.tangem.tap.proxy.redux.DaggerGraphAction +import com.tangem.tap.proxy.redux.DaggerGraphState import com.tangem.tap.routing.RoutingComponent import com.tangem.tap.routing.configurator.AppRouterConfig import com.tangem.utils.coroutines.FeatureCoroutineExceptionHandler -import com.tangem.wallet.BuildConfig import com.tangem.wallet.R import com.tangem.wallet.databinding.ActivityMainBinding import dagger.hilt.android.AndroidEntryPoint @@ -180,6 +179,15 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac @Inject internal lateinit var routingComponentFactory: RoutingComponent.Factory + @Inject + lateinit var disclaimerRouter: DisclaimerRouter + + @Inject + lateinit var pushNotificationsRouter: PushNotificationsRouter + + @Inject + lateinit var cardRepository: CardRepository + internal val viewModel: MainViewModel by viewModels() private lateinit var appThemeModeFlow: SharedFlow @@ -216,7 +224,6 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac installRouting() initContent() - checkForNotificationPermission() observeStateUpdates() observePolkadotAccountHealthCheck() @@ -296,6 +303,8 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac qrScanningRouter = qrScanningRouter, emailSender = emailSender, stakingRouter = stakingRouter, + disclaimerRouter = disclaimerRouter, + pushNotificationsRouter = pushNotificationsRouter, ), ) } @@ -527,8 +536,12 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac replaceAll(AppRoute.Welcome(intentWhichStartedActivity?.let(::SerializableIntent))) } } else { - store.dispatchNavigationAction { replaceAll(AppRoute.Home) } lifecycleScope.launch { + val toggles = store.inject(getDependency = DaggerGraphState::pushNotificationsFeatureToggles) + val isEnabled = toggles.isPushNotificationsEnabled && !cardRepository.isTangemTOSAccepted() + val route = if (isEnabled) AppRoute.Disclaimer(isTosAccepted = false) else AppRoute.Home + + store.dispatchNavigationAction { replaceAll(route) } intentProcessor.handleIntent(intentWhichStartedActivity, false) } } @@ -536,16 +549,6 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac store.dispatch(BackupAction.CheckForUnfinishedBackup) } - private fun checkForNotificationPermission() { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU && - BuildConfig.LOG_ENABLED && - ContextCompat.checkSelfPermission(this, Manifest.permission.POST_NOTIFICATIONS) != - PackageManager.PERMISSION_GRANTED - ) { - ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.POST_NOTIFICATIONS), 0) - } - } - private fun observePolkadotAccountHealthCheck() { lifecycleScope.launch { getPolkadotCheckHasResetUseCase() diff --git a/app/src/main/java/com/tangem/tap/domain/scanCard/UseCaseScanProcessor.kt b/app/src/main/java/com/tangem/tap/domain/scanCard/UseCaseScanProcessor.kt index 10646e2bfa..381d348980 100644 --- a/app/src/main/java/com/tangem/tap/domain/scanCard/UseCaseScanProcessor.kt +++ b/app/src/main/java/com/tangem/tap/domain/scanCard/UseCaseScanProcessor.kt @@ -59,7 +59,11 @@ internal object UseCaseScanProcessor { ), ) add(AnalyticsChain(Basic.CardWasScanned(analyticsSource))) - add(DisclaimerChain(store, disclaimerWillShow)) + val pushNotificationsToggles = + store.inject(getDependency = DaggerGraphState::pushNotificationsFeatureToggles) + if (pushNotificationsToggles.isPushNotificationsEnabled) { + add(DisclaimerChain(store, disclaimerWillShow)) + } add(CheckForOnboardingChain(store, store.state.globalState.tapWalletManager)) } diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/Buttons.kt b/core/ui/src/main/java/com/tangem/core/ui/components/Buttons.kt index bb17fcf7b6..7ef56f015e 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/Buttons.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/Buttons.kt @@ -86,6 +86,7 @@ fun PrimaryButton( onClick: () -> Unit, modifier: Modifier = Modifier, size: TangemButtonSize = TangemButtonSize.Default, + colors: ButtonColors = TangemButtonsDefaults.primaryButtonColors, showProgress: Boolean = false, enabled: Boolean = true, ) { @@ -94,7 +95,7 @@ fun PrimaryButton( text = text, icon = TangemButtonIconPosition.None, onClick = onClick, - colors = TangemButtonsDefaults.primaryButtonColors, + colors = colors, enabled = enabled, showProgress = showProgress, size = size, diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/appbar/AppBarWithAdditionalButtons.kt b/core/ui/src/main/java/com/tangem/core/ui/components/appbar/AppBarWithAdditionalButtons.kt index dd23347e63..07f48d4787 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/appbar/AppBarWithAdditionalButtons.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/appbar/AppBarWithAdditionalButtons.kt @@ -14,6 +14,7 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.painterResource import androidx.compose.ui.semantics.Role import androidx.compose.ui.tooling.preview.Preview @@ -39,12 +40,16 @@ fun AppBarWithAdditionalButtons( modifier: Modifier = Modifier, startButton: AdditionalButton? = null, endButton: AdditionalButton? = null, + textColor: Color = TangemTheme.colors.text.primary1, + iconColor: Color = TangemTheme.colors.icon.primary1, ) { AppBarWithAdditionalButtons( text = text.resolveReference(), startButton = startButton, endButton = endButton, modifier = modifier, + textColor = textColor, + iconColor = iconColor, ) } @@ -63,6 +68,8 @@ fun AppBarWithAdditionalButtons( modifier: Modifier = Modifier, startButton: AdditionalButton? = null, endButton: AdditionalButton? = null, + textColor: Color = TangemTheme.colors.text.primary1, + iconColor: Color = TangemTheme.colors.icon.primary1, ) { Box( modifier = modifier @@ -73,7 +80,7 @@ fun AppBarWithAdditionalButtons( Icon( painter = painterResource(id = startButton.iconRes), contentDescription = null, - tint = TangemTheme.colors.icon.primary1, + tint = iconColor, modifier = Modifier .size(TangemTheme.dimens.size24) .align(Alignment.CenterStart) @@ -92,7 +99,7 @@ fun AppBarWithAdditionalButtons( Text( text = text, modifier = Modifier.align(Alignment.Center), - color = TangemTheme.colors.text.primary1, + color = textColor, maxLines = 1, style = TangemTheme.typography.subtitle1, ) @@ -101,7 +108,7 @@ fun AppBarWithAdditionalButtons( Icon( painter = painterResource(id = endButton.iconRes), contentDescription = null, - tint = TangemTheme.colors.icon.primary1, + tint = iconColor, modifier = Modifier .size(TangemTheme.dimens.size24) .align(Alignment.CenterEnd) diff --git a/features/disclaimer/api/src/main/java/com/tangem/features/disclaimer/api/DisclaimerRouter.kt b/features/disclaimer/api/src/main/java/com/tangem/features/disclaimer/api/DisclaimerRouter.kt index 5575d38b51..e990639fc8 100644 --- a/features/disclaimer/api/src/main/java/com/tangem/features/disclaimer/api/DisclaimerRouter.kt +++ b/features/disclaimer/api/src/main/java/com/tangem/features/disclaimer/api/DisclaimerRouter.kt @@ -5,8 +5,4 @@ import androidx.fragment.app.Fragment interface DisclaimerRouter { fun entryFragment(): Fragment - - companion object { - const val IS_TOS_ACCEPTED_KEY = "is_tos_accepted" - } } \ No newline at end of file diff --git a/features/disclaimer/impl/src/main/java/com/tangem/features/disclaimer/impl/DisclaimerFragment.kt b/features/disclaimer/impl/src/main/java/com/tangem/features/disclaimer/impl/DisclaimerFragment.kt index e17e88299f..b53e0353d2 100644 --- a/features/disclaimer/impl/src/main/java/com/tangem/features/disclaimer/impl/DisclaimerFragment.kt +++ b/features/disclaimer/impl/src/main/java/com/tangem/features/disclaimer/impl/DisclaimerFragment.kt @@ -5,10 +5,10 @@ import androidx.activity.compose.BackHandler import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.fragment.app.viewModels +import com.tangem.common.routing.AppRoute.Disclaimer.Companion.IS_TOS_ACCEPTED_KEY import com.tangem.common.routing.AppRouter import com.tangem.core.ui.UiDependencies import com.tangem.core.ui.screen.ComposeFragment -import com.tangem.features.disclaimer.api.DisclaimerRouter import com.tangem.features.disclaimer.impl.presentation.ui.DisclaimerScreen import com.tangem.features.disclaimer.impl.presentation.viewmodel.DisclaimerViewModel import dagger.hilt.android.AndroidEntryPoint @@ -26,7 +26,7 @@ internal class DisclaimerFragment : ComposeFragment() { private val viewModel by viewModels() private val isTosAccepted: Boolean - get() = arguments?.getBoolean(DisclaimerRouter.IS_TOS_ACCEPTED_KEY) ?: false + get() = arguments?.getBoolean(IS_TOS_ACCEPTED_KEY) ?: false override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) diff --git a/features/disclaimer/impl/src/main/java/com/tangem/features/disclaimer/impl/navigation/DefaultDisclaimerRouter.kt b/features/disclaimer/impl/src/main/java/com/tangem/features/disclaimer/impl/navigation/DefaultDisclaimerRouter.kt index a49d4a94c4..2897635472 100644 --- a/features/disclaimer/impl/src/main/java/com/tangem/features/disclaimer/impl/navigation/DefaultDisclaimerRouter.kt +++ b/features/disclaimer/impl/src/main/java/com/tangem/features/disclaimer/impl/navigation/DefaultDisclaimerRouter.kt @@ -4,15 +4,16 @@ import androidx.fragment.app.Fragment import com.tangem.common.routing.AppRoute import com.tangem.common.routing.AppRouter import com.tangem.features.disclaimer.impl.DisclaimerFragment +import javax.inject.Inject -internal class DefaultDisclaimerRouter( +internal class DefaultDisclaimerRouter @Inject constructor( private val appRouter: AppRouter, ) : InnerDisclaimerRouter { override fun entryFragment(): Fragment = DisclaimerFragment.create() override fun openPushNotificationPermission() { - // todo [REDACTED_JIRA] + appRouter.push(AppRoute.PushNotification) } override fun openHome() { diff --git a/features/disclaimer/impl/src/main/java/com/tangem/features/disclaimer/impl/presentation/ui/DisclaimerScreen.kt b/features/disclaimer/impl/src/main/java/com/tangem/features/disclaimer/impl/presentation/ui/DisclaimerScreen.kt index e9a40a7a03..7b89328a8c 100644 --- a/features/disclaimer/impl/src/main/java/com/tangem/features/disclaimer/impl/presentation/ui/DisclaimerScreen.kt +++ b/features/disclaimer/impl/src/main/java/com/tangem/features/disclaimer/impl/presentation/ui/DisclaimerScreen.kt @@ -1,6 +1,7 @@ package com.tangem.features.disclaimer.impl.presentation.ui import android.Manifest +import android.annotation.SuppressLint import android.content.res.Configuration import android.os.Build import android.view.View @@ -9,12 +10,12 @@ import android.webkit.WebView import androidx.compose.foundation.background import androidx.compose.foundation.layout.* import androidx.compose.material3.CircularProgressIndicator -import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.toArgb import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.res.stringResource @@ -28,6 +29,7 @@ import com.tangem.core.ui.components.NavigationBar3ButtonsScrim import com.tangem.core.ui.components.PrimaryButton import com.tangem.core.ui.components.appbar.AppBarWithAdditionalButtons import com.tangem.core.ui.components.appbar.models.AdditionalButton +import com.tangem.core.ui.components.buttons.common.TangemButtonColors import com.tangem.core.ui.extensions.resourceReference import com.tangem.core.ui.res.TangemColorPalette import com.tangem.core.ui.res.TangemTheme @@ -39,36 +41,37 @@ import com.tangem.features.disclaimer.impl.presentation.state.DummyDisclaimer @Composable internal fun DisclaimerScreen(state: DisclaimerState, onBackClick: () -> Unit) { val bottomBarHeight = with(LocalDensity.current) { WindowInsets.systemBars.getBottom(this).toDp() } - + val bottomPadding = if (state.isTosAccepted) { + bottomBarHeight + TangemTheme.dimens.size16 + } else { + bottomBarHeight + TangemTheme.dimens.size64 + } + val backgroundColor = if (state.isTosAccepted) TangemTheme.colors.background.primary else TangemColorPalette.Dark6 + val (textColor, iconColor) = if (state.isTosAccepted) { + TangemTheme.colors.text.primary1 to TangemTheme.colors.icon.primary1 + } else { + TangemColorPalette.Light4 to TangemColorPalette.Light4 + } Box( modifier = Modifier - .background(TangemColorPalette.Dark6) + .background(backgroundColor) .statusBarsPadding(), ) { - Column { + Column(modifier = Modifier.padding(bottom = bottomPadding)) { AppBarWithAdditionalButtons( text = resourceReference(R.string.disclaimer_title), startButton = AdditionalButton( iconRes = R.drawable.ic_back_24, onIconClicked = onBackClick, ).takeIf { state.isTosAccepted }, + textColor = textColor, + iconColor = iconColor, ) - DisclaimerContent(state.url) - Spacer( - modifier = Modifier - .background(TangemColorPalette.Dark6) - .height( - height = if (!state.isTosAccepted) { - bottomBarHeight - } else { - bottomBarHeight + TangemTheme.dimens.size64 - }, - ), - ) + DisclaimerContent(state.url, state.isTosAccepted) } if (!state.isTosAccepted) { - BottomFade(Modifier.align(Alignment.BottomCenter), backgroundColor = TangemColorPalette.Dark6) + BottomFade(Modifier.align(Alignment.BottomCenter), backgroundColor = backgroundColor) DisclaimerButton(state.onAccept) } else { NavigationBar3ButtonsScrim() @@ -76,10 +79,13 @@ internal fun DisclaimerScreen(state: DisclaimerState, onBackClick: () -> Unit) { } } +@SuppressLint("SetJavaScriptEnabled") @Composable -private fun ColumnScope.DisclaimerContent(url: String) { +private fun DisclaimerContent(url: String, isTosAccepted: Boolean) { val progressState = remember { mutableStateOf(ProgressState.Loading) } val webClient = remember { DisclaimerWebViewClient(progressState) } + val transparent = Color.Transparent + val backgroundColor = if (isTosAccepted) TangemTheme.colors.background.primary else TangemColorPalette.Dark6 Box( modifier = Modifier, ) { @@ -90,12 +96,17 @@ private fun ColumnScope.DisclaimerContent(url: String) { ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT, ) - setBackgroundColor(TangemColorPalette.Dark6.toArgb()) + setBackgroundColor(transparent.toArgb()) settings.allowFileAccess = false - settings.javaScriptEnabled = false - + // to inject css style to display only in dark theme + settings.javaScriptEnabled = !isTosAccepted overScrollMode = View.OVER_SCROLL_NEVER webViewClient = webClient + + clearHistory() + clearFormData() + clearCache(true) + loadUrl(url) } }, @@ -103,7 +114,11 @@ private fun ColumnScope.DisclaimerContent(url: String) { when (progressState.value) { ProgressState.Loading -> { - Box(modifier = Modifier.fillMaxSize()) { + Box( + modifier = Modifier + .fillMaxSize() + .background(backgroundColor), + ) { CircularProgressIndicator( color = TangemTheme.colors.icon.informative, modifier = Modifier @@ -112,25 +127,6 @@ private fun ColumnScope.DisclaimerContent(url: String) { ) } } - ProgressState.Error -> Box(modifier = Modifier.fillMaxSize()) { - Column( - verticalArrangement = Arrangement.Center, - modifier = Modifier - .fillMaxWidth() - .padding(TangemTheme.dimens.spacing16) - .align(Alignment.Center), - ) { - Text( - text = stringResource(id = R.string.disclaimer_error_loading), - style = TangemTheme.typography.body1, - color = TangemTheme.colors.text.constantWhite, - ) - PrimaryButton( - text = stringResource(R.string.common_retry), - onClick = { webClient.reset() }, - ) - } - } else -> Unit } } @@ -147,6 +143,12 @@ private fun BoxScope.DisclaimerButton(onAccept: (Boolean) -> Unit) { PrimaryButton( text = stringResource(id = R.string.common_accept), onClick = { onAccept(shouldAskPushPermission) }, + colors = TangemButtonColors( + backgroundColor = TangemColorPalette.Light4, + contentColor = TangemColorPalette.Dark6, + disabledBackgroundColor = TangemColorPalette.Light4, + disabledContentColor = TangemColorPalette.Dark6, + ), modifier = Modifier .align(Alignment.BottomCenter) .navigationBarsPadding() diff --git a/features/disclaimer/impl/src/main/java/com/tangem/features/disclaimer/impl/presentation/ui/DisclaimerWebViewClient.kt b/features/disclaimer/impl/src/main/java/com/tangem/features/disclaimer/impl/presentation/ui/DisclaimerWebViewClient.kt index 348cad4107..a0b568833b 100644 --- a/features/disclaimer/impl/src/main/java/com/tangem/features/disclaimer/impl/presentation/ui/DisclaimerWebViewClient.kt +++ b/features/disclaimer/impl/src/main/java/com/tangem/features/disclaimer/impl/presentation/ui/DisclaimerWebViewClient.kt @@ -10,6 +10,22 @@ internal enum class ProgressState { Error, } +/** + * Workaround to display web view with ToS only in dark theme + */ +private fun WebView.injectCSS() { + val code = "javascript:(function() {" + + "var node = document.createElement('style');" + + "node.type = 'text/css';" + + " node.innerHTML = 'body, label,th,p,a, td, tr,li,ul,span,table,h1,h2,h3,h4,h5,h6,h7,div,small {" + + " color: #C9C9C9;" + + "background-color: #1E1E1E;" + + " } ';" + + " document.head.appendChild(node);})();" + + evaluateJavascript(code, null) +} + internal class DisclaimerWebViewClient(private val progressState: MutableState) : WebViewClient() { private var loadingUrl: String? = null @@ -22,20 +38,23 @@ internal class DisclaimerWebViewClient(private val progressState: MutableState

Unit, + val onAllowed: () -> Unit, + val onDeny: () -> Unit, + val openSettings: () -> Unit, +) : TangemBottomSheetConfigContent \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletScreen.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletScreen.kt index 3dde33fa2b..0a72f0cf4e 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletScreen.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletScreen.kt @@ -37,6 +37,7 @@ import com.google.accompanist.systemuicontroller.rememberSystemUiController import com.tangem.core.ui.components.* import com.tangem.core.ui.components.atoms.Hand import com.tangem.core.ui.components.atoms.handComposableComponentHeight +import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig import com.tangem.core.ui.components.bottomsheets.chooseaddress.ChooseAddressBottomSheet import com.tangem.core.ui.components.bottomsheets.chooseaddress.ChooseAddressBottomSheetConfig import com.tangem.core.ui.components.bottomsheets.tokenreceive.TokenReceiveBottomSheet @@ -53,6 +54,7 @@ import com.tangem.feature.wallet.impl.R import com.tangem.feature.wallet.presentation.common.preview.WalletScreenPreviewData.walletScreenState import com.tangem.feature.wallet.presentation.wallet.state.model.* import com.tangem.feature.wallet.presentation.wallet.state.model.holder.TxHistoryStateHolder +import com.tangem.feature.wallet.presentation.wallet.ui.components.PushNotificationsBottomSheet import com.tangem.feature.wallet.presentation.wallet.ui.components.TokenActionsBottomSheet import com.tangem.feature.wallet.presentation.wallet.ui.components.WalletsList import com.tangem.feature.wallet.presentation.wallet.ui.components.common.* @@ -204,17 +206,7 @@ private fun WalletContent( organizeTokens(state = selectedWallet, itemModifier = itemModifier) } - val bottomSheetConfig = selectedWallet.bottomSheetConfig - if (bottomSheetConfig != null) { - when (bottomSheetConfig.content) { - is WalletBottomSheetConfig -> WalletBottomSheet(config = bottomSheetConfig) - is TokenReceiveBottomSheetConfig -> TokenReceiveBottomSheet(config = bottomSheetConfig) - is ActionsBottomSheetConfig -> TokenActionsBottomSheet(config = bottomSheetConfig) - is ChooseAddressBottomSheetConfig -> ChooseAddressBottomSheet(config = bottomSheetConfig) - is BalancesAndLimitsBottomSheetConfig -> BalancesAndLimitsBottomSheet(config = bottomSheetConfig) - is VisaTxDetailsBottomSheetConfig -> VisaTxDetailsBottomSheet(config = bottomSheetConfig) - } - } + ShowBottomSheet(bottomSheetConfig = selectedWallet.bottomSheetConfig) WalletsListEffects( lazyListState = walletsListState, @@ -606,6 +598,21 @@ internal fun LazyListScope.organizeTokens(state: WalletState, itemModifier: Modi } } +@Composable +private fun ShowBottomSheet(bottomSheetConfig: TangemBottomSheetConfig?) { + if (bottomSheetConfig != null) { + when (bottomSheetConfig.content) { + is WalletBottomSheetConfig -> WalletBottomSheet(config = bottomSheetConfig) + is TokenReceiveBottomSheetConfig -> TokenReceiveBottomSheet(config = bottomSheetConfig) + is ActionsBottomSheetConfig -> TokenActionsBottomSheet(config = bottomSheetConfig) + is ChooseAddressBottomSheetConfig -> ChooseAddressBottomSheet(config = bottomSheetConfig) + is BalancesAndLimitsBottomSheetConfig -> BalancesAndLimitsBottomSheet(config = bottomSheetConfig) + is VisaTxDetailsBottomSheetConfig -> VisaTxDetailsBottomSheet(config = bottomSheetConfig) + is PushNotificationsBottomSheetConfig -> PushNotificationsBottomSheet(config = bottomSheetConfig) + } + } +} + // region Preview @Preview(showBackground = true, widthDp = 360) @Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES) diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/PushNotificationsBottomSheet.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/PushNotificationsBottomSheet.kt new file mode 100644 index 0000000000..8522dc3b72 --- /dev/null +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/PushNotificationsBottomSheet.kt @@ -0,0 +1,117 @@ +package com.tangem.feature.wallet.presentation.wallet.ui.components + +import android.content.res.Configuration +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.padding +import androidx.compose.runtime.Composable +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.tooling.preview.Preview +import com.tangem.core.ui.components.PrimaryButton +import com.tangem.core.ui.components.SecondaryButton +import com.tangem.core.ui.components.bottomsheets.TangemBottomSheet +import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig +import com.tangem.core.ui.components.showcase.ShowcaseContent +import com.tangem.core.ui.components.showcase.model.ShowcaseItemModel +import com.tangem.core.ui.extensions.resourceReference +import com.tangem.core.ui.res.TangemTheme +import com.tangem.core.ui.res.TangemThemePreview +import com.tangem.core.ui.utils.requestPushPermission +import com.tangem.feature.wallet.impl.R +import com.tangem.feature.wallet.presentation.wallet.state.model.PushNotificationsBottomSheetConfig +import kotlinx.collections.immutable.persistentListOf + +@Composable +internal fun PushNotificationsBottomSheet(config: TangemBottomSheetConfig) { + TangemBottomSheet(config = config) { + PushNotificationsSheetContent(content = it, onDismiss = config.onDismissRequest) + } +} + +@Composable +private fun PushNotificationsSheetContent(content: PushNotificationsBottomSheetConfig, onDismiss: () -> Unit) { + val isClicked = remember { mutableStateOf(false) } + val requestPushPermission = requestPushPermission( + isFirstTimeAsking = content.isFirstTimeAsking, + isClicked = isClicked, + onAllow = { + content.onAllowed() + onDismiss() + }, + onDeny = { + content.onDeny() + onDismiss() + }, + onOpenSettings = content.openSettings, + ) + + Column(modifier = Modifier.background(TangemTheme.colors.background.primary)) { + ShowcaseContent( + headerIconRes = R.drawable.ic_notifications_unread_24, + headerText = resourceReference(R.string.user_push_notification_agreement_header), + showcaseItems = persistentListOf( + ShowcaseItemModel( + iconRes = R.drawable.ic_rocket_launch_24, + text = resourceReference(R.string.user_push_notification_agreement_argument_one), + ), + ShowcaseItemModel( + iconRes = R.drawable.ic_storefront_24, + text = resourceReference(R.string.user_push_notification_agreement_argument_two), + ), + ), + modifier = Modifier.padding(top = TangemTheme.dimens.spacing40), + ) + Row( + horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing12), + modifier = Modifier.padding( + start = TangemTheme.dimens.spacing16, + end = TangemTheme.dimens.spacing16, + top = TangemTheme.dimens.spacing40, + bottom = TangemTheme.dimens.spacing16, + ), + ) { + SecondaryButton( + text = stringResource(R.string.common_later), + onClick = { + content.onDeny() + onDismiss() + }, + modifier = Modifier.weight(1f), + ) + PrimaryButton( + text = stringResource(R.string.common_allow), + onClick = { + isClicked.value = true + content.onAllow() + requestPushPermission() + }, + modifier = Modifier.weight(1f), + ) + } + } +} + +// region Preview +@Preview(showBackground = true, widthDp = 360) +@Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES) +@Composable +private fun PushNotificationsSheetContent_Preview() { + TangemThemePreview { + PushNotificationsSheetContent( + PushNotificationsBottomSheetConfig( + isFirstTimeAsking = false, + onAllow = {}, + onAllowed = {}, + onDeny = {}, + openSettings = {}, + ), + onDismiss = {}, + ) + } +} +// endregion \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModel.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModel.kt index 6d6961b577..c4242a3c48 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModel.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModel.kt @@ -4,11 +4,11 @@ import androidx.compose.runtime.MutableState import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope +import arrow.core.getOrElse import com.tangem.core.analytics.api.AnalyticsEventHandler +import com.tangem.core.navigation.settings.SettingsManager import com.tangem.domain.balancehiding.GetBalanceHidingSettingsUseCase -import com.tangem.domain.settings.CanUseBiometryUseCase -import com.tangem.domain.settings.IsWalletsScrollPreviewEnabled -import com.tangem.domain.settings.ShouldShowSaveWalletScreenUseCase +import com.tangem.domain.settings.* import com.tangem.domain.wallets.models.UserWalletId import com.tangem.domain.wallets.usecase.GetSelectedWalletUseCase import com.tangem.domain.wallets.usecase.GetWalletsUseCase @@ -19,6 +19,7 @@ import com.tangem.feature.wallet.presentation.wallet.analytics.utils.SelectedWal import com.tangem.feature.wallet.presentation.wallet.domain.WalletNameMigrationUseCase import com.tangem.feature.wallet.presentation.wallet.loaders.WalletScreenContentLoader import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController +import com.tangem.feature.wallet.presentation.wallet.state.model.PushNotificationsBottomSheetConfig import com.tangem.feature.wallet.presentation.wallet.state.model.WalletEvent import com.tangem.feature.wallet.presentation.wallet.state.model.WalletEvent.DemonstrateWalletsScrollPreview.Direction import com.tangem.feature.wallet.presentation.wallet.state.model.WalletPullToRefreshConfig @@ -28,6 +29,7 @@ import com.tangem.feature.wallet.presentation.wallet.state.utils.WalletEventSend import com.tangem.feature.wallet.presentation.wallet.utils.ScreenLifecycleProvider import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntents import com.tangem.features.managetokens.navigation.ExpandableState +import com.tangem.features.pushnotifications.api.utils.PUSH_PERMISSION import com.tangem.utils.Provider import com.tangem.utils.coroutines.CoroutineDispatcherProvider import com.tangem.utils.coroutines.JobHolder @@ -60,6 +62,10 @@ internal class WalletViewModel @Inject constructor( private val selectedWalletAnalyticsSender: SelectedWalletAnalyticsSender, private val walletDeepLinksHandler: WalletDeepLinksHandler, private val walletNameMigrationUseCase: WalletNameMigrationUseCase, + private val shouldInitiallyAskPermissionUseCase: ShouldInitiallyAskPermissionUseCase, + private val isFirstTimeAskingPermissionUseCase: IsFirstTimeAskingPermissionUseCase, + private val shouldAskPermissionUseCase: ShouldAskPermissionUseCase, + private val settingsManager: SettingsManager, analyticsEventsHandler: AnalyticsEventHandler, ) : ViewModel() { @@ -80,6 +86,7 @@ internal class WalletViewModel @Inject constructor( subscribeOnBalanceHiding() subscribeOnSelectedWalletFlow() subscribeToScreenBackgroundState() + subscribeOnPushNotificationsPermission() } private fun maybeMigrateNames() { @@ -145,6 +152,30 @@ internal class WalletViewModel @Inject constructor( .launchIn(viewModelScope) } + private fun subscribeOnPushNotificationsPermission() { + viewModelScope.launch { + if (!shouldAskPermissionUseCase(PUSH_PERMISSION)) return@launch + withContext(dispatchers.io) { delay(timeMillis = 1_800) } + + val isFirstTimeAsking = isFirstTimeAskingPermissionUseCase(PUSH_PERMISSION).getOrElse { true } + val wasInitiallyAsk = shouldInitiallyAskPermissionUseCase(PUSH_PERMISSION).getOrElse { true } + val onDenyClick: () -> Unit = if (wasInitiallyAsk) { + clickIntents::onDelayAskPushPermission + } else { + clickIntents::onNeverAskPushPermission + } + stateHolder.showBottomSheet( + PushNotificationsBottomSheetConfig( + isFirstTimeAsking = isFirstTimeAsking, + onAllow = clickIntents::onAllowPushPermission, + onAllowed = clickIntents::onNeverAskPushPermission, + onDeny = onDenyClick, + openSettings = settingsManager::openSettings, + ), + ) + } + } + private fun subscribeOnSelectedWalletFlow() { getSelectedWalletUseCase().onRight { it diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/intents/WalletClickIntents.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/intents/WalletClickIntents.kt index 3266406422..712975635b 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/intents/WalletClickIntents.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/intents/WalletClickIntents.kt @@ -30,6 +30,7 @@ internal class WalletClickIntents @Inject constructor( private val currencyActionsClickIntentsImplementor: WalletCurrencyActionsClickIntentsImplementor, private val contentClickIntentsImplementor: WalletContentClickIntentsImplementor, private val visaWalletIntentsImplementor: VisaWalletIntentsImplementor, + private val pushPermissionClickIntentsImplementor: WalletPushPermissionClickIntentsImplementor, private val stateHolder: WalletStateController, private val walletScreenContentLoader: WalletScreenContentLoader, private val getSelectedWalletSyncUseCase: GetSelectedWalletSyncUseCase, @@ -45,7 +46,8 @@ internal class WalletClickIntents @Inject constructor( WalletWarningsClickIntents by warningsClickIntentsImplementer, WalletCurrencyActionsClickIntents by currencyActionsClickIntentsImplementor, WalletContentClickIntents by contentClickIntentsImplementor, - VisaWalletIntents by visaWalletIntentsImplementor { + VisaWalletIntents by visaWalletIntentsImplementor, + WalletPushPermissionClickIntents by pushPermissionClickIntentsImplementor { override fun initialize(router: InnerWalletRouter, coroutineScope: CoroutineScope) { super.initialize(router, coroutineScope) @@ -55,6 +57,7 @@ internal class WalletClickIntents @Inject constructor( currencyActionsClickIntentsImplementor.initialize(router, coroutineScope) contentClickIntentsImplementor.initialize(router, coroutineScope) visaWalletIntentsImplementor.initialize(router, coroutineScope) + pushPermissionClickIntentsImplementor.initialize(router, coroutineScope) } fun onWalletChange(index: Int) { diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/intents/WalletPushPermissionClickIntents.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/intents/WalletPushPermissionClickIntents.kt new file mode 100644 index 0000000000..3f5138ca6d --- /dev/null +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/intents/WalletPushPermissionClickIntents.kt @@ -0,0 +1,44 @@ +package com.tangem.feature.wallet.presentation.wallet.viewmodels.intents + +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 dagger.hilt.android.scopes.ViewModelScoped +import kotlinx.coroutines.launch +import javax.inject.Inject + +internal interface WalletPushPermissionClickIntents { + + fun onAllowPushPermission() + + fun onDelayAskPushPermission() + + fun onNeverAskPushPermission() +} + +@ViewModelScoped +internal class WalletPushPermissionClickIntentsImplementor @Inject constructor( + private val setFirstTimeAskingPermissionUseCase: SetFirstTimeAskingPermissionUseCase, + private val neverRequestPermissionUseCase: NeverRequestPermissionUseCase, + private val delayPermissionRequestUseCase: DelayPermissionRequestUseCase, +) : BaseWalletClickIntents(), WalletPushPermissionClickIntents { + + override fun onAllowPushPermission() { + viewModelScope.launch { + setFirstTimeAskingPermissionUseCase(PUSH_PERMISSION) + } + } + + override fun onDelayAskPushPermission() { + viewModelScope.launch { + delayPermissionRequestUseCase(PUSH_PERMISSION) + } + } + + override fun onNeverAskPushPermission() { + viewModelScope.launch { + neverRequestPermissionUseCase(PUSH_PERMISSION) + } + } +} \ No newline at end of file From 9b665a76b340ccf9ff6c0646f5b1c45a3dc8b92f Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 20 Jun 2024 12:30:53 +0500 Subject: [PATCH 3/3] Updated on 2026-08-14 --- .../tangem/core/ui/utils/RequestPushPermission.kt | 9 +++------ features/disclaimer/impl/build.gradle.kts | 1 + .../impl/presentation/ui/DisclaimerScreen.kt | 11 ++++------- .../pushnotifications/api/utils/PermissionUtils.kt | 12 +++++++++++- .../impl/presentation/ui/PushNotificationsScreen.kt | 2 ++ .../model/PushNotificationsBottomSheetConfig.kt | 2 +- .../ui/components/PushNotificationsBottomSheet.kt | 8 +++++--- .../wallet/viewmodels/WalletViewModel.kt | 7 ++++--- .../intents/WalletPushPermissionClickIntents.kt | 4 ++-- 9 files changed, 33 insertions(+), 23 deletions(-) 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 index 134039ed07..575485aae9 100644 --- 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 @@ -1,7 +1,5 @@ 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 @@ -14,17 +12,18 @@ import com.google.accompanist.permissions.shouldShowRationale * Returns push permission requester. * Handles granting permission from app settings. */ +@Suppress("LongParameterList") @OptIn(ExperimentalPermissionsApi::class) @Composable fun requestPushPermission( isFirstTimeAsking: Boolean, + pushPermission: String?, 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 permissionState = pushPermission?.let { permission -> val tempPermissionState = rememberPermissionState(permission = permission) rememberPermissionState(permission = permission) { when { @@ -33,8 +32,6 @@ fun requestPushPermission( else -> onDeny() } } - } else { - null } // Check if user granted permission and close bottom sheet diff --git a/features/disclaimer/impl/build.gradle.kts b/features/disclaimer/impl/build.gradle.kts index c82364e03f..b9bb0c5263 100644 --- a/features/disclaimer/impl/build.gradle.kts +++ b/features/disclaimer/impl/build.gradle.kts @@ -40,6 +40,7 @@ dependencies { /** Feature modules */ implementation(projects.features.disclaimer.api) + implementation(projects.features.pushNotifications.api) /** DI */ implementation(deps.hilt.android) diff --git a/features/disclaimer/impl/src/main/java/com/tangem/features/disclaimer/impl/presentation/ui/DisclaimerScreen.kt b/features/disclaimer/impl/src/main/java/com/tangem/features/disclaimer/impl/presentation/ui/DisclaimerScreen.kt index 7b89328a8c..516eab4ef4 100644 --- a/features/disclaimer/impl/src/main/java/com/tangem/features/disclaimer/impl/presentation/ui/DisclaimerScreen.kt +++ b/features/disclaimer/impl/src/main/java/com/tangem/features/disclaimer/impl/presentation/ui/DisclaimerScreen.kt @@ -1,9 +1,7 @@ package com.tangem.features.disclaimer.impl.presentation.ui -import android.Manifest import android.annotation.SuppressLint import android.content.res.Configuration -import android.os.Build import android.view.View import android.view.ViewGroup import android.webkit.WebView @@ -37,6 +35,7 @@ import com.tangem.core.ui.res.TangemThemePreview import com.tangem.features.disclaimer.impl.R import com.tangem.features.disclaimer.impl.presentation.state.DisclaimerState import com.tangem.features.disclaimer.impl.presentation.state.DummyDisclaimer +import com.tangem.features.pushnotifications.api.utils.getPushPermissionOrNull @Composable internal fun DisclaimerScreen(state: DisclaimerState, onBackClick: () -> Unit) { @@ -135,11 +134,9 @@ private fun DisclaimerContent(url: String, isTosAccepted: Boolean) { @OptIn(ExperimentalPermissionsApi::class) @Composable private fun BoxScope.DisclaimerButton(onAccept: (Boolean) -> Unit) { - val shouldAskPushPermission = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { - rememberPermissionState(permission = Manifest.permission.POST_NOTIFICATIONS).status.isGranted - } else { - true - } + val shouldAskPushPermission = getPushPermissionOrNull()?.let { permission -> + rememberPermissionState(permission = permission).status.isGranted + } ?: true PrimaryButton( text = stringResource(id = R.string.common_accept), onClick = { onAccept(shouldAskPushPermission) }, 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 index 6092954f2a..c3d27e3ef5 100644 --- 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 @@ -2,9 +2,19 @@ package com.tangem.features.pushnotifications.api.utils import android.Manifest import android.os.Build +import androidx.annotation.ChecksSdkIntAtLeast -val PUSH_PERMISSION = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { +@ChecksSdkIntAtLeast(api = Build.VERSION_CODES.TIRAMISU) +private val isRequirePushPermission = Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU + +val PUSH_PERMISSION = if (isRequirePushPermission) { Manifest.permission.POST_NOTIFICATIONS } else { "android.permission.POST_NOTIFICATIONS" +} + +fun getPushPermissionOrNull() = if (isRequirePushPermission) { + Manifest.permission.POST_NOTIFICATIONS +} else { + null } \ 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 index 02c7036aad..9fe0f81030 100644 --- 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 @@ -11,6 +11,7 @@ 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 com.tangem.features.pushnotifications.api.utils.getPushPermissionOrNull import kotlinx.collections.immutable.persistentListOf @Composable @@ -27,6 +28,7 @@ internal fun PushNotificationsScreen( onAllow = onAllow, onDeny = onLater, onOpenSettings = onOpenSettings, + pushPermission = getPushPermissionOrNull(), ) Showcase( diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/PushNotificationsBottomSheetConfig.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/PushNotificationsBottomSheetConfig.kt index ed962bf9bd..71638bc6ce 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/PushNotificationsBottomSheetConfig.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/PushNotificationsBottomSheetConfig.kt @@ -4,8 +4,8 @@ import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent data class PushNotificationsBottomSheetConfig( val isFirstTimeAsking: Boolean, + val onRequest: () -> Unit, val onAllow: () -> Unit, - val onAllowed: () -> Unit, val onDeny: () -> Unit, val openSettings: () -> Unit, ) : TangemBottomSheetConfigContent \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/PushNotificationsBottomSheet.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/PushNotificationsBottomSheet.kt index 8522dc3b72..15035df238 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/PushNotificationsBottomSheet.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/PushNotificationsBottomSheet.kt @@ -24,6 +24,7 @@ import com.tangem.core.ui.res.TangemThemePreview import com.tangem.core.ui.utils.requestPushPermission import com.tangem.feature.wallet.impl.R import com.tangem.feature.wallet.presentation.wallet.state.model.PushNotificationsBottomSheetConfig +import com.tangem.features.pushnotifications.api.utils.getPushPermissionOrNull import kotlinx.collections.immutable.persistentListOf @Composable @@ -37,10 +38,11 @@ internal fun PushNotificationsBottomSheet(config: TangemBottomSheetConfig) { private fun PushNotificationsSheetContent(content: PushNotificationsBottomSheetConfig, onDismiss: () -> Unit) { val isClicked = remember { mutableStateOf(false) } val requestPushPermission = requestPushPermission( + pushPermission = getPushPermissionOrNull(), isFirstTimeAsking = content.isFirstTimeAsking, isClicked = isClicked, onAllow = { - content.onAllowed() + content.onAllow() onDismiss() }, onDeny = { @@ -87,7 +89,7 @@ private fun PushNotificationsSheetContent(content: PushNotificationsBottomSheetC text = stringResource(R.string.common_allow), onClick = { isClicked.value = true - content.onAllow() + content.onRequest() requestPushPermission() }, modifier = Modifier.weight(1f), @@ -105,8 +107,8 @@ private fun PushNotificationsSheetContent_Preview() { PushNotificationsSheetContent( PushNotificationsBottomSheetConfig( isFirstTimeAsking = false, + onRequest = {}, onAllow = {}, - onAllowed = {}, onDeny = {}, openSettings = {}, ), diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModel.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModel.kt index c4242a3c48..131bc32cd4 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModel.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModel.kt @@ -155,7 +155,8 @@ internal class WalletViewModel @Inject constructor( private fun subscribeOnPushNotificationsPermission() { viewModelScope.launch { if (!shouldAskPermissionUseCase(PUSH_PERMISSION)) return@launch - withContext(dispatchers.io) { delay(timeMillis = 1_800) } + + delay(timeMillis = 1_800) val isFirstTimeAsking = isFirstTimeAskingPermissionUseCase(PUSH_PERMISSION).getOrElse { true } val wasInitiallyAsk = shouldInitiallyAskPermissionUseCase(PUSH_PERMISSION).getOrElse { true } @@ -167,8 +168,8 @@ internal class WalletViewModel @Inject constructor( stateHolder.showBottomSheet( PushNotificationsBottomSheetConfig( isFirstTimeAsking = isFirstTimeAsking, - onAllow = clickIntents::onAllowPushPermission, - onAllowed = clickIntents::onNeverAskPushPermission, + onRequest = clickIntents::onRequestPushPermission, + onAllow = clickIntents::onNeverAskPushPermission, onDeny = onDenyClick, openSettings = settingsManager::openSettings, ), diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/intents/WalletPushPermissionClickIntents.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/intents/WalletPushPermissionClickIntents.kt index 3f5138ca6d..d2f05b0f2a 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/intents/WalletPushPermissionClickIntents.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/intents/WalletPushPermissionClickIntents.kt @@ -10,7 +10,7 @@ import javax.inject.Inject internal interface WalletPushPermissionClickIntents { - fun onAllowPushPermission() + fun onRequestPushPermission() fun onDelayAskPushPermission() @@ -24,7 +24,7 @@ internal class WalletPushPermissionClickIntentsImplementor @Inject constructor( private val delayPermissionRequestUseCase: DelayPermissionRequestUseCase, ) : BaseWalletClickIntents(), WalletPushPermissionClickIntents { - override fun onAllowPushPermission() { + override fun onRequestPushPermission() { viewModelScope.launch { setFirstTimeAskingPermissionUseCase(PUSH_PERMISSION) }