Skip to content

Commit

Permalink
More FCM infra: wire up token registration, analytics, toasts, primer…
Browse files Browse the repository at this point in the history
… dialog.
  • Loading branch information
prashanDYDX committed Sep 19, 2024
1 parent 3e9bfab commit d8597f0
Show file tree
Hide file tree
Showing 16 changed files with 223 additions and 34 deletions.
2 changes: 1 addition & 1 deletion v4/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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'

Expand Down
1 change: 1 addition & 0 deletions v4/core/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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')
Expand Down
17 changes: 12 additions & 5 deletions v4/core/src/main/java/exchange/dydx/trading/TradingActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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)
Expand Down Expand Up @@ -105,6 +108,11 @@ class TradingActivity : FragmentActivity() {
abacusStateManager.setReadyToConnect(true)
}

override fun onDestroy() {
super.onDestroy()
pushPermissionRequester.dropActivity(this)
}

private fun setContentWithJS(
content: @Composable () -> Unit,
) {
Expand Down Expand Up @@ -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)

Expand Down
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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

Expand All @@ -59,6 +61,7 @@ class CoreViewModel @Inject constructor(
tracker = tracker,
logger = logger,
preferencesStore = preferencesStore,
application = application,
)
}

Expand Down
1 change: 1 addition & 0 deletions v4/feature/profile/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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())
Expand Down Expand Up @@ -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)
},
Expand Down
1 change: 1 addition & 0 deletions v4/feature/trade/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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 {
Expand Down Expand Up @@ -57,6 +59,7 @@ class DydxTradeStatusCtaButtonViewModel @Inject constructor(
ctaButtonState = PlatformButtonState.Secondary,
ctaButtonAction = {
router.navigateBack()
pushPermissionRequester.requestPushPermission()
},
)
is AbacusStateManagerProtocol.SubmissionStatus.Failed ->
Expand Down
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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(
Expand All @@ -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),
)

Expand Down
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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

Expand Down Expand Up @@ -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(),
),
)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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?) {
Expand Down
5 changes: 5 additions & 0 deletions v4/integration/fcm/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
Original file line number Diff line number Diff line change
@@ -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
}
}
}
Original file line number Diff line number Diff line change
@@ -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")
}
}
}
Loading

0 comments on commit d8597f0

Please sign in to comment.