From d8597f057805bb110c499e49b4b1bc7738e82ef2 Mon Sep 17 00:00:00 2001 From: Prashan Dharmasena Date: Wed, 18 Sep 2024 20:56:01 -0400 Subject: [PATCH 1/3] More FCM infra: wire up token registration, analytics, toasts, primer dialog. --- v4/build.gradle | 2 +- v4/core/build.gradle | 1 + .../exchange/dydx/trading/TradingActivity.kt | 17 +++- .../dydx/trading/core/CoreViewModel.kt | 3 + v4/feature/profile/build.gradle | 1 + .../profile/language/DydxLanguageViewModel.kt | 3 + v4/feature/trade/build.gradle | 1 + .../DydxTradeStatusCtaButtonViewModel.kt | 3 + .../feature/workers/DydxGlobalWorkers.kt | 4 +- .../globalworkers/DydxUserTrackingWorker.kt | 13 +++ .../dydxstatemanager/AbacusStateManager.kt | 9 +- v4/integration/fcm/build.gradle | 5 + .../integration/fcm/ActivityDelegate.kt | 24 +++++ .../trading/integration/fcm/DydxFCMService.kt | 59 ++++++++---- .../fcm/PushPermissionRequester.kt | 94 +++++++++++++++++++ .../container/PlatformInfoContainer.kt | 18 ++-- 16 files changed, 223 insertions(+), 34 deletions(-) create mode 100644 v4/integration/fcm/src/main/java/exchange/dydx/trading/integration/fcm/ActivityDelegate.kt create mode 100644 v4/integration/fcm/src/main/java/exchange/dydx/trading/integration/fcm/PushPermissionRequester.kt diff --git a/v4/build.gradle b/v4/build.gradle index cf189b94..db05759d 100644 --- a/v4/build.gradle +++ b/v4/build.gradle @@ -88,7 +88,7 @@ ext { compileSdkVersion = 34 // App dependencies - abacusVersion = '1.11.2' + abacusVersion = '1.11.10' carteraVersion = '0.1.15' kollectionsVersion = '2.0.16' diff --git a/v4/core/build.gradle b/v4/core/build.gradle index 24715615..797f6030 100644 --- a/v4/core/build.gradle +++ b/v4/core/build.gradle @@ -49,6 +49,7 @@ dependencies { api project(':v4:integration:analytics') implementation project(':v4:integration:dydxStateManager') + implementation project(':v4:integration:fcm') implementation project(':v4:utilities') implementation project(':v4:feature:onboarding') implementation project(':v4:feature:profile') diff --git a/v4/core/src/main/java/exchange/dydx/trading/TradingActivity.kt b/v4/core/src/main/java/exchange/dydx/trading/TradingActivity.kt index 1c37edd5..594a2d75 100644 --- a/v4/core/src/main/java/exchange/dydx/trading/TradingActivity.kt +++ b/v4/core/src/main/java/exchange/dydx/trading/TradingActivity.kt @@ -34,6 +34,7 @@ import exchange.dydx.trading.core.biometric.DydxBiometricPrompt import exchange.dydx.trading.core.biometric.DydxBiometricView import exchange.dydx.trading.feature.shared.PreferenceKeys import exchange.dydx.trading.feature.shared.analytics.AnalyticsEvent +import exchange.dydx.trading.integration.fcm.PushPermissionRequester import exchange.dydx.utilities.utils.SharedPreferencesStore import kotlinx.coroutines.launch import javax.inject.Inject @@ -57,9 +58,11 @@ class TradingActivity : FragmentActivity() { @Inject lateinit var abacusStateManager: AbacusStateManager + @Inject lateinit var pushPermissionRequester: PushPermissionRequester + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - + pushPermissionRequester.takeActivity(this) viewModel.logger.d(TAG, "TradingActivity#onCreate") CarteraSetup.run(this, viewModel.logger) @@ -105,6 +108,11 @@ class TradingActivity : FragmentActivity() { abacusStateManager.setReadyToConnect(true) } + override fun onDestroy() { + super.onDestroy() + pushPermissionRequester.dropActivity(this) + } + private fun setContentWithJS( content: @Composable () -> Unit, ) { @@ -165,13 +173,12 @@ class TradingActivity : FragmentActivity() { } } - override fun onNewIntent(intent: Intent?) { + override fun onNewIntent(intent: Intent) { super.onNewIntent(intent) - if (intent != null) { - viewModel.router.handleIntent(intent) - } + viewModel.router.handleIntent(intent) } + @Deprecated("Deprecated in Java") override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { super.onActivityResult(requestCode, resultCode, data) diff --git a/v4/core/src/main/java/exchange/dydx/trading/core/CoreViewModel.kt b/v4/core/src/main/java/exchange/dydx/trading/core/CoreViewModel.kt index 76984d4a..086c1e88 100644 --- a/v4/core/src/main/java/exchange/dydx/trading/core/CoreViewModel.kt +++ b/v4/core/src/main/java/exchange/dydx/trading/core/CoreViewModel.kt @@ -1,5 +1,6 @@ package exchange.dydx.trading.core +import android.app.Application import android.content.Context import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope @@ -41,6 +42,7 @@ class CoreViewModel @Inject constructor( val logger: CompositeLogging, val compositeTracking: CompositeTracking, private val preferencesStore: SharedPreferencesStore, + application: Application, ) : ViewModel() { private var globalWorkers: DydxGlobalWorkers? = null @@ -59,6 +61,7 @@ class CoreViewModel @Inject constructor( tracker = tracker, logger = logger, preferencesStore = preferencesStore, + application = application, ) } diff --git a/v4/feature/profile/build.gradle b/v4/feature/profile/build.gradle index 41a2710c..018fe29a 100644 --- a/v4/feature/profile/build.gradle +++ b/v4/feature/profile/build.gradle @@ -46,6 +46,7 @@ dependencies { implementation project(':v4:common') implementation project(':v4:utilities') implementation project(path: ':v4:integration:dydxStateManager') + implementation project(path: ':v4:integration:fcm') implementation project(path: ':v4:platformUI') implementation project(path: ':v4:feature:shared') implementation project(path: ':v4:feature:portfolio') diff --git a/v4/feature/profile/src/main/java/exchange/dydx/trading/feature/profile/language/DydxLanguageViewModel.kt b/v4/feature/profile/src/main/java/exchange/dydx/trading/feature/profile/language/DydxLanguageViewModel.kt index 0f08c485..68812d73 100644 --- a/v4/feature/profile/src/main/java/exchange/dydx/trading/feature/profile/language/DydxLanguageViewModel.kt +++ b/v4/feature/profile/src/main/java/exchange/dydx/trading/feature/profile/language/DydxLanguageViewModel.kt @@ -9,6 +9,7 @@ import exchange.dydx.trading.common.navigation.DydxRouter import exchange.dydx.trading.common.navigation.MarketRoutes import exchange.dydx.trading.feature.shared.PreferenceKeys import exchange.dydx.trading.feature.shared.views.SettingsView +import exchange.dydx.trading.integration.fcm.FCMRegistrar import exchange.dydx.utilities.utils.SharedPreferencesStore import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow @@ -19,6 +20,7 @@ class DydxLanguageViewModel @Inject constructor( val localizer: AbacusLocalizerProtocol, private val preferencesStore: SharedPreferencesStore, private val router: DydxRouter, + private val fcmRegistrar: FCMRegistrar, ) : ViewModel(), DydxViewModel { private val mutableState = MutableStateFlow(createViewState()) @@ -49,6 +51,7 @@ class DydxLanguageViewModel @Inject constructor( preferencesStore.save(value, PreferenceKeys.Language) localizer.language = value mutableState.value = createViewState() + fcmRegistrar.registerToken() // need to pass up language for push notifs to be translated. router.tabTo(MarketRoutes.marketList) }, diff --git a/v4/feature/trade/build.gradle b/v4/feature/trade/build.gradle index 057c198a..a0dc59cc 100644 --- a/v4/feature/trade/build.gradle +++ b/v4/feature/trade/build.gradle @@ -47,6 +47,7 @@ dependencies { implementation project(':v4:common') implementation project(path: ':v4:utilities') implementation project(path: ':v4:integration:dydxStateManager') + implementation project(path: ':v4:integration:fcm') implementation project(path: ':v4:platformUI') implementation project(path: ':v4:feature:shared') implementation project(path: ':v4:feature:receipt') diff --git a/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/tradestatus/components/DydxTradeStatusCtaButtonViewModel.kt b/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/tradestatus/components/DydxTradeStatusCtaButtonViewModel.kt index 4433e41c..291c42e7 100644 --- a/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/tradestatus/components/DydxTradeStatusCtaButtonViewModel.kt +++ b/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/tradestatus/components/DydxTradeStatusCtaButtonViewModel.kt @@ -9,6 +9,7 @@ import exchange.dydx.platformui.components.buttons.PlatformButtonState import exchange.dydx.trading.common.DydxViewModel import exchange.dydx.trading.common.navigation.DydxRouter import exchange.dydx.trading.feature.trade.streams.MutableTradeStreaming +import exchange.dydx.trading.integration.fcm.PushPermissionRequester import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.map @@ -20,6 +21,7 @@ class DydxTradeStatusCtaButtonViewModel @Inject constructor( private val router: DydxRouter, private val tradeStream: MutableTradeStreaming, private val savedStateHandle: SavedStateHandle, + private val pushPermissionRequester: PushPermissionRequester, ) : ViewModel(), DydxViewModel { private enum class TradeType { @@ -57,6 +59,7 @@ class DydxTradeStatusCtaButtonViewModel @Inject constructor( ctaButtonState = PlatformButtonState.Secondary, ctaButtonAction = { router.navigateBack() + pushPermissionRequester.requestPushPermission() }, ) is AbacusStateManagerProtocol.SubmissionStatus.Failed -> diff --git a/v4/feature/workers/src/main/java/exchange/dydx/trading/feature/workers/DydxGlobalWorkers.kt b/v4/feature/workers/src/main/java/exchange/dydx/trading/feature/workers/DydxGlobalWorkers.kt index 15ef4ae6..caa2fcc3 100644 --- a/v4/feature/workers/src/main/java/exchange/dydx/trading/feature/workers/DydxGlobalWorkers.kt +++ b/v4/feature/workers/src/main/java/exchange/dydx/trading/feature/workers/DydxGlobalWorkers.kt @@ -1,5 +1,6 @@ package exchange.dydx.trading.feature.workers +import android.app.Application import android.content.Context import exchange.dydx.abacus.protocols.AbacusLocalizerProtocol import exchange.dydx.abacus.protocols.ParserProtocol @@ -37,6 +38,7 @@ class DydxGlobalWorkers( private val tracker: Tracking, private val logger: CompositeLogging, private val preferencesStore: SharedPreferencesStore, + application: Application, ) : WorkerProtocol { private val workers = listOf( @@ -46,7 +48,7 @@ class DydxGlobalWorkers( DydxRestrictionsWorker(scope, abacusStateManager, localizer, toaster), DydxCarteraConfigWorker(abacusStateManager, cachedFileLoader, context, logger), DydxTransferSubaccountWorker(scope, abacusStateManager, cosmosClient, formatter, parser, tracker, logger), - DydxUserTrackingWorker(scope, abacusStateManager, localizer, tracker), + DydxUserTrackingWorker(scope, abacusStateManager, localizer, tracker, application), DydxGasTokenWorker(preferencesStore, abacusStateManager, logger), ) diff --git a/v4/feature/workers/src/main/java/exchange/dydx/trading/feature/workers/globalworkers/DydxUserTrackingWorker.kt b/v4/feature/workers/src/main/java/exchange/dydx/trading/feature/workers/globalworkers/DydxUserTrackingWorker.kt index 519b7fd9..1d435e57 100644 --- a/v4/feature/workers/src/main/java/exchange/dydx/trading/feature/workers/globalworkers/DydxUserTrackingWorker.kt +++ b/v4/feature/workers/src/main/java/exchange/dydx/trading/feature/workers/globalworkers/DydxUserTrackingWorker.kt @@ -1,5 +1,9 @@ package exchange.dydx.trading.feature.workers.globalworkers +import android.Manifest +import android.app.Application +import android.content.pm.PackageManager +import androidx.core.content.ContextCompat import exchange.dydx.abacus.protocols.AbacusLocalizerProtocol import exchange.dydx.cartera.CarteraConfig import exchange.dydx.dydxstatemanager.AbacusStateManagerProtocol @@ -18,6 +22,7 @@ class DydxUserTrackingWorker( private val abacusStateManager: AbacusStateManagerProtocol, private val localizer: AbacusLocalizerProtocol, private val tracker: Tracking, + private val application: Application, ) : WorkerProtocol { override var isStarted = false @@ -66,6 +71,14 @@ class DydxUserTrackingWorker( UserProperty.selectedLocale.rawValue to localizer.language, ), ) + + val pushEnabled = ContextCompat.checkSelfPermission(application, Manifest.permission.POST_NOTIFICATIONS) == + PackageManager.PERMISSION_GRANTED + tracker.setUserProperties( + mapOf( + "pushNotificationsEnabled" to pushEnabled.toString(), + ), + ) } } diff --git a/v4/integration/dydxStateManager/src/main/java/exchange/dydx/dydxstatemanager/AbacusStateManager.kt b/v4/integration/dydxStateManager/src/main/java/exchange/dydx/dydxstatemanager/AbacusStateManager.kt index 49d5c5e7..e499bcba 100644 --- a/v4/integration/dydxStateManager/src/main/java/exchange/dydx/dydxstatemanager/AbacusStateManager.kt +++ b/v4/integration/dydxStateManager/src/main/java/exchange/dydx/dydxstatemanager/AbacusStateManager.kt @@ -36,7 +36,6 @@ import exchange.dydx.abacus.state.model.TransferInputField import exchange.dydx.abacus.state.model.TriggerOrdersInputField import exchange.dydx.abacus.state.v2.manager.AsyncAbacusStateManagerV2 import exchange.dydx.abacus.state.v2.supervisor.AppConfigsV2 -import exchange.dydx.abacus.state.v2.supervisor.OnboardingConfigs import exchange.dydx.abacus.utils.IList import exchange.dydx.abacus.utils.IOImplementations import exchange.dydx.dydxstatemanager.clientState.transfers.DydxTransferStateManagerProtocol @@ -138,6 +137,8 @@ interface AbacusStateManagerProtocol { triggerOrders(null, field) } } + + fun registerPushToken(token: String, language: String) } // Temporary location, should probably make a separate dagger-qualifiers module. @@ -189,8 +190,6 @@ class AbacusStateManager @Inject constructor( } } - appConfigsV2.onboardingConfigs.squidVersion = OnboardingConfigs.SquidVersion.V2 - // Disable Abacus logging since it's too verbose. Enable it if you need to debug Abacus. if (BuildConfig.DEBUG) { appConfigsV2.enableLogger = false @@ -452,6 +451,10 @@ class AbacusStateManager @Inject constructor( asyncStateManager.gasToken = gasToken } + override fun registerPushToken(token: String, language: String) { + asyncStateManager.registerPushNotification(token, language) + } + // MARK: StateNotificationProtocol override fun apiStateChanged(apiState: ApiState?) { diff --git a/v4/integration/fcm/build.gradle b/v4/integration/fcm/build.gradle index 4ecbbc72..a9996c14 100644 --- a/v4/integration/fcm/build.gradle +++ b/v4/integration/fcm/build.gradle @@ -39,11 +39,16 @@ android { dependencies { api project(':v4:integration:dydxStateManager') + api project(':v4:integration:analytics') + api project(':v4:platformUI') + api project(':v4:utilities') // Firebase implementation platform ("com.google.firebase:firebase-bom:$firebaseBomVersion") implementation 'com.google.firebase:firebase-messaging-ktx' + implementation 'androidx.activity:activity-ktx:1.9.2' + // Hilt kapt "com.google.dagger:hilt-compiler:$hiltVersion" api "com.google.dagger:hilt-core:$hiltVersion" diff --git a/v4/integration/fcm/src/main/java/exchange/dydx/trading/integration/fcm/ActivityDelegate.kt b/v4/integration/fcm/src/main/java/exchange/dydx/trading/integration/fcm/ActivityDelegate.kt new file mode 100644 index 00000000..c743f898 --- /dev/null +++ b/v4/integration/fcm/src/main/java/exchange/dydx/trading/integration/fcm/ActivityDelegate.kt @@ -0,0 +1,24 @@ +package exchange.dydx.trading.integration.fcm + +import android.app.Activity + +/** + * General activity delegate that allows accessing activity from Hilt components higher than + * ActivityComponent. + * + * Feel free to move this somewhere more general if you need to use it for other stuff. + */ +interface ActivityDelegate { + var activity: Activity? + + fun takeActivity(activity: Activity) { + this.activity = activity + } + + fun dropActivity(activity: Activity) { + // outgoing activity can be destroyed after incoming activity has already been created + if (activity == this.activity) { + this.activity = null + } + } +} diff --git a/v4/integration/fcm/src/main/java/exchange/dydx/trading/integration/fcm/DydxFCMService.kt b/v4/integration/fcm/src/main/java/exchange/dydx/trading/integration/fcm/DydxFCMService.kt index 32617919..8e149fea 100644 --- a/v4/integration/fcm/src/main/java/exchange/dydx/trading/integration/fcm/DydxFCMService.kt +++ b/v4/integration/fcm/src/main/java/exchange/dydx/trading/integration/fcm/DydxFCMService.kt @@ -1,46 +1,73 @@ package exchange.dydx.trading.integration.fcm +import android.Manifest +import android.app.Application +import android.content.pm.PackageManager import android.util.Log +import androidx.core.content.ContextCompat import com.google.android.gms.tasks.OnCompleteListener import com.google.firebase.messaging.FirebaseMessaging import com.google.firebase.messaging.FirebaseMessagingService +import com.google.firebase.messaging.RemoteMessage import dagger.hilt.android.AndroidEntryPoint import exchange.dydx.dydxstatemanager.AbacusStateManager +import exchange.dydx.dydxstatemanager.protocolImplementations.AbacusLocalizerImp +import exchange.dydx.platformui.components.container.PlatformInfo import javax.inject.Inject @AndroidEntryPoint class DydxFCMService : FirebaseMessagingService() { - @Inject - internal lateinit var fcmRegistrar: FCMRegistrar + @Inject internal lateinit var fcmRegistrar: FCMRegistrar + + @Inject internal lateinit var platformInfo: PlatformInfo override fun onCreate() { super.onCreate() + fcmRegistrar.registerToken() + } + + override fun onNewToken(token: String) { + super.onNewToken(token) + fcmRegistrar.registerToken(token) + } + + override fun onMessageReceived(message: RemoteMessage) { + super.onMessageReceived(message) + message.notification?.let { notification -> + platformInfo.show( + title = notification.title, + message = notification.body, + ) + } + } +} +class FCMRegistrar @Inject constructor( + private val application: Application, + private val abacusStateManager: AbacusStateManager, + private val abacusLocalizerImp: AbacusLocalizerImp, +) { + + fun registerToken() { FirebaseMessaging.getInstance().token.addOnCompleteListener( OnCompleteListener { task -> if (!task.isSuccessful) { return@OnCompleteListener } - // Get new FCM registration token - val token = task.result - fcmRegistrar.registerToken(token) + registerToken(task.result) }, ) } - override fun onNewToken(token: String) { - super.onNewToken(token) - fcmRegistrar.registerToken(token) - } -} - -internal class FCMRegistrar @Inject constructor( - private val abacusStateManager: AbacusStateManager -) { fun registerToken(token: String) { - Log.d("FCMRegistrar", "registering token: $token") - // call abacus method to register token + // Only register token if permission has been granted. + if (ContextCompat.checkSelfPermission(application, Manifest.permission.POST_NOTIFICATIONS) == + PackageManager.PERMISSION_GRANTED + ) { + Log.d("FCMRegistrar", "registering token: $token") + abacusStateManager.registerPushToken(token, abacusLocalizerImp.language ?: "en") + } } } diff --git a/v4/integration/fcm/src/main/java/exchange/dydx/trading/integration/fcm/PushPermissionRequester.kt b/v4/integration/fcm/src/main/java/exchange/dydx/trading/integration/fcm/PushPermissionRequester.kt new file mode 100644 index 00000000..da57a31a --- /dev/null +++ b/v4/integration/fcm/src/main/java/exchange/dydx/trading/integration/fcm/PushPermissionRequester.kt @@ -0,0 +1,94 @@ +package exchange.dydx.trading.integration.fcm + +import android.Manifest +import android.app.Activity +import android.content.pm.PackageManager +import android.os.Build +import androidx.activity.result.ActivityResultCaller +import androidx.activity.result.ActivityResultLauncher +import androidx.activity.result.contract.ActivityResultContracts +import androidx.annotation.RequiresApi +import androidx.core.app.ActivityCompat.shouldShowRequestPermissionRationale +import androidx.core.content.ContextCompat +import dagger.Binds +import dagger.Module +import dagger.hilt.InstallIn +import dagger.hilt.android.components.ActivityRetainedComponent +import dagger.hilt.android.scopes.ActivityRetainedScoped +import exchange.dydx.dydxstatemanager.protocolImplementations.AbacusLocalizerImp +import exchange.dydx.platformui.components.PlatformDialog +import exchange.dydx.platformui.components.container.PlatformInfo +import exchange.dydx.utilities.utils.SharedPreferencesStore +import javax.inject.Inject + +@ActivityRetainedScoped +class RealPushPermissionRequester @Inject constructor( + private val platformDialog: PlatformDialog, + private val platformInfo: PlatformInfo, + private val abacusLocalizerImp: AbacusLocalizerImp, + private val sharedPreferencesStore: SharedPreferencesStore, +) : PushPermissionRequester { + + var requestPermissionLauncher: ActivityResultLauncher? = null + + override var activity: Activity? = null + set(value) { + field = value + (field as ActivityResultCaller).registerForActivityResult( + ActivityResultContracts.RequestPermission(), + ) { isGranted: Boolean -> + if (isGranted) { + platformInfo.show(title = abacusLocalizerImp.localize("APP.PUSH_NOTIFICATIONS.ENABLED")) + } else { + platformInfo.show( + title = abacusLocalizerImp.localize("APP.PUSH_NOTIFICATIONS.DISABLED"), + message = abacusLocalizerImp.localize("APP.PUSH_NOTIFICATIONS.DISABLED_BODY"), + ) + } + } + } + + // Mostly copy pasted from Firebase docs + override fun requestPushPermission() { + val localActivity = activity ?: return + // This is only necessary for API level >= 33 (TIRAMISU) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + if (ContextCompat.checkSelfPermission(localActivity, Manifest.permission.POST_NOTIFICATIONS) == + PackageManager.PERMISSION_GRANTED + ) { + // Permission granted already. Do nothing + return + } else if (shouldShowRequestPermissionRationale(localActivity, Manifest.permission.POST_NOTIFICATIONS) && sharedPreferencesStore.read(PRIMER_SHOWN_KEY) != "true") { + // Show primer if needed. + platformDialog.showMessage( + title = abacusLocalizerImp.localize("APP.PUSH_NOTIFICATIONS_PRIMER_TITLE"), + message = abacusLocalizerImp.localize("APP.PUSH_NOTIFICATIONS_PRIMER_MESSAGE"), + confirmTitle = abacusLocalizerImp.localize("APP.GENERAL.OK"), + cancelTitle = abacusLocalizerImp.localize("APP.GENERAL.NOT_NOW"), + confirmAction = ::request, + ) + sharedPreferencesStore.save("true", PRIMER_SHOWN_KEY) + } else { + // Directly ask for the permission + request() + } + } + } + + @RequiresApi(Build.VERSION_CODES.TIRAMISU) + private fun request() { + requestPermissionLauncher?.launch(Manifest.permission.POST_NOTIFICATIONS) + } +} + +interface PushPermissionRequester : ActivityDelegate { + fun requestPushPermission() +} + +@InstallIn(ActivityRetainedComponent::class) +@Module +interface PushPermissionRequesterModule { + @Binds fun bindPushPermissionRequester(real: RealPushPermissionRequester): PushPermissionRequester +} + +private const val PRIMER_SHOWN_KEY = "push_primer_shown" diff --git a/v4/platformUI/src/main/java/exchange/dydx/platformui/components/container/PlatformInfoContainer.kt b/v4/platformUI/src/main/java/exchange/dydx/platformui/components/container/PlatformInfoContainer.kt index ab7d595b..8d1fb168 100644 --- a/v4/platformUI/src/main/java/exchange/dydx/platformui/components/container/PlatformInfoContainer.kt +++ b/v4/platformUI/src/main/java/exchange/dydx/platformui/components/container/PlatformInfoContainer.kt @@ -103,12 +103,14 @@ fun PlatformInfoContainer( .themeFont(fontSize = ThemeFont.FontSize.base, fontType = ThemeFont.FontType.plus), ) } - Text( - text = message, - color = type.foregroundColor, - style = TextStyle.dydxDefault - .themeFont(fontSize = ThemeFont.FontSize.small), - ) + message?.let { + Text( + text = message, + color = type.foregroundColor, + style = TextStyle.dydxDefault + .themeFont(fontSize = ThemeFont.FontSize.small), + ) + } } buttonTitle?.let { val shape = RoundedCornerShape(6.dp) @@ -138,7 +140,7 @@ fun PlatformInfoContainer( data class Toast( val title: String? = null, - val message: String, + val message: String?, val buttonTitle: String? = null, val type: Type = Type.Info, val duration: Duration, @@ -202,7 +204,7 @@ class PlatformInfo @Inject constructor( fun show( title: String? = null, - message: String, + message: String? = null, buttonTitle: String? = null, type: Toast.Type = Toast.Type.Info, duration: Toast.Duration = Toast.Duration.Short, From c2d6400f98f13bc7a43f5cc4a78a08a41d86c536 Mon Sep 17 00:00:00 2001 From: Prashan Dharmasena Date: Thu, 19 Sep 2024 11:17:26 -0400 Subject: [PATCH 2/3] Fix unset permission requester and rename interface --- .../java/exchange/dydx/trading/TradingActivity.kt | 4 ++-- .../components/DydxTradeStatusCtaButtonViewModel.kt | 4 ++-- ...quester.kt => PushPermissionRequesterProtocol.kt} | 12 +++++++----- 3 files changed, 11 insertions(+), 9 deletions(-) rename v4/integration/fcm/src/main/java/exchange/dydx/trading/integration/fcm/{PushPermissionRequester.kt => PushPermissionRequesterProtocol.kt} (90%) diff --git a/v4/core/src/main/java/exchange/dydx/trading/TradingActivity.kt b/v4/core/src/main/java/exchange/dydx/trading/TradingActivity.kt index 594a2d75..d279c6a6 100644 --- a/v4/core/src/main/java/exchange/dydx/trading/TradingActivity.kt +++ b/v4/core/src/main/java/exchange/dydx/trading/TradingActivity.kt @@ -34,7 +34,7 @@ import exchange.dydx.trading.core.biometric.DydxBiometricPrompt import exchange.dydx.trading.core.biometric.DydxBiometricView import exchange.dydx.trading.feature.shared.PreferenceKeys import exchange.dydx.trading.feature.shared.analytics.AnalyticsEvent -import exchange.dydx.trading.integration.fcm.PushPermissionRequester +import exchange.dydx.trading.integration.fcm.PushPermissionRequesterProtocol import exchange.dydx.utilities.utils.SharedPreferencesStore import kotlinx.coroutines.launch import javax.inject.Inject @@ -58,7 +58,7 @@ class TradingActivity : FragmentActivity() { @Inject lateinit var abacusStateManager: AbacusStateManager - @Inject lateinit var pushPermissionRequester: PushPermissionRequester + @Inject lateinit var pushPermissionRequester: PushPermissionRequesterProtocol override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) diff --git a/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/tradestatus/components/DydxTradeStatusCtaButtonViewModel.kt b/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/tradestatus/components/DydxTradeStatusCtaButtonViewModel.kt index 291c42e7..0b286f82 100644 --- a/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/tradestatus/components/DydxTradeStatusCtaButtonViewModel.kt +++ b/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/tradestatus/components/DydxTradeStatusCtaButtonViewModel.kt @@ -9,7 +9,7 @@ import exchange.dydx.platformui.components.buttons.PlatformButtonState import exchange.dydx.trading.common.DydxViewModel import exchange.dydx.trading.common.navigation.DydxRouter import exchange.dydx.trading.feature.trade.streams.MutableTradeStreaming -import exchange.dydx.trading.integration.fcm.PushPermissionRequester +import exchange.dydx.trading.integration.fcm.PushPermissionRequesterProtocol import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.map @@ -21,7 +21,7 @@ class DydxTradeStatusCtaButtonViewModel @Inject constructor( private val router: DydxRouter, private val tradeStream: MutableTradeStreaming, private val savedStateHandle: SavedStateHandle, - private val pushPermissionRequester: PushPermissionRequester, + private val pushPermissionRequester: PushPermissionRequesterProtocol, ) : ViewModel(), DydxViewModel { private enum class TradeType { diff --git a/v4/integration/fcm/src/main/java/exchange/dydx/trading/integration/fcm/PushPermissionRequester.kt b/v4/integration/fcm/src/main/java/exchange/dydx/trading/integration/fcm/PushPermissionRequesterProtocol.kt similarity index 90% rename from v4/integration/fcm/src/main/java/exchange/dydx/trading/integration/fcm/PushPermissionRequester.kt rename to v4/integration/fcm/src/main/java/exchange/dydx/trading/integration/fcm/PushPermissionRequesterProtocol.kt index da57a31a..15a57db0 100644 --- a/v4/integration/fcm/src/main/java/exchange/dydx/trading/integration/fcm/PushPermissionRequester.kt +++ b/v4/integration/fcm/src/main/java/exchange/dydx/trading/integration/fcm/PushPermissionRequesterProtocol.kt @@ -22,19 +22,21 @@ import exchange.dydx.utilities.utils.SharedPreferencesStore import javax.inject.Inject @ActivityRetainedScoped -class RealPushPermissionRequester @Inject constructor( +class PushPermissionRequester @Inject constructor( private val platformDialog: PlatformDialog, private val platformInfo: PlatformInfo, private val abacusLocalizerImp: AbacusLocalizerImp, private val sharedPreferencesStore: SharedPreferencesStore, -) : PushPermissionRequester { +) : PushPermissionRequesterProtocol { var requestPermissionLauncher: ActivityResultLauncher? = null override var activity: Activity? = null set(value) { field = value - (field as ActivityResultCaller).registerForActivityResult( + + requestPermissionLauncher?.unregister() + requestPermissionLauncher = (field as ActivityResultCaller).registerForActivityResult( ActivityResultContracts.RequestPermission(), ) { isGranted: Boolean -> if (isGranted) { @@ -81,14 +83,14 @@ class RealPushPermissionRequester @Inject constructor( } } -interface PushPermissionRequester : ActivityDelegate { +interface PushPermissionRequesterProtocol : ActivityDelegate { fun requestPushPermission() } @InstallIn(ActivityRetainedComponent::class) @Module interface PushPermissionRequesterModule { - @Binds fun bindPushPermissionRequester(real: RealPushPermissionRequester): PushPermissionRequester + @Binds fun bindPushPermissionRequester(real: PushPermissionRequester): PushPermissionRequesterProtocol } private const val PRIMER_SHOWN_KEY = "push_primer_shown" From c021be689e77403edb7ae02d8681be86f15656ce Mon Sep 17 00:00:00 2001 From: Prashan Dharmasena Date: Thu, 19 Sep 2024 16:15:48 -0400 Subject: [PATCH 3/3] move delegate to utilities --- .../trading/integration/fcm/PushPermissionRequesterProtocol.kt | 3 ++- .../java/exchange/dydx/utilities/utils}/ActivityDelegate.kt | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) rename v4/{integration/fcm/src/main/java/exchange/dydx/trading/integration/fcm => utilities/src/main/java/exchange/dydx/utilities/utils}/ActivityDelegate.kt (93%) diff --git a/v4/integration/fcm/src/main/java/exchange/dydx/trading/integration/fcm/PushPermissionRequesterProtocol.kt b/v4/integration/fcm/src/main/java/exchange/dydx/trading/integration/fcm/PushPermissionRequesterProtocol.kt index 15a57db0..a7d9de53 100644 --- a/v4/integration/fcm/src/main/java/exchange/dydx/trading/integration/fcm/PushPermissionRequesterProtocol.kt +++ b/v4/integration/fcm/src/main/java/exchange/dydx/trading/integration/fcm/PushPermissionRequesterProtocol.kt @@ -18,6 +18,7 @@ import dagger.hilt.android.scopes.ActivityRetainedScoped import exchange.dydx.dydxstatemanager.protocolImplementations.AbacusLocalizerImp import exchange.dydx.platformui.components.PlatformDialog import exchange.dydx.platformui.components.container.PlatformInfo +import exchange.dydx.utilities.utils.ActivityDelegate import exchange.dydx.utilities.utils.SharedPreferencesStore import javax.inject.Inject @@ -29,7 +30,7 @@ class PushPermissionRequester @Inject constructor( private val sharedPreferencesStore: SharedPreferencesStore, ) : PushPermissionRequesterProtocol { - var requestPermissionLauncher: ActivityResultLauncher? = null + private var requestPermissionLauncher: ActivityResultLauncher? = null override var activity: Activity? = null set(value) { diff --git a/v4/integration/fcm/src/main/java/exchange/dydx/trading/integration/fcm/ActivityDelegate.kt b/v4/utilities/src/main/java/exchange/dydx/utilities/utils/ActivityDelegate.kt similarity index 93% rename from v4/integration/fcm/src/main/java/exchange/dydx/trading/integration/fcm/ActivityDelegate.kt rename to v4/utilities/src/main/java/exchange/dydx/utilities/utils/ActivityDelegate.kt index c743f898..1b7686de 100644 --- a/v4/integration/fcm/src/main/java/exchange/dydx/trading/integration/fcm/ActivityDelegate.kt +++ b/v4/utilities/src/main/java/exchange/dydx/utilities/utils/ActivityDelegate.kt @@ -1,4 +1,4 @@ -package exchange.dydx.trading.integration.fcm +package exchange.dydx.utilities.utils import android.app.Activity