Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

More FCM infra: wire up token registration, analytics, toasts, primer… #226

Merged
merged 3 commits into from
Sep 19, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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.PushPermissionRequesterProtocol
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: PushPermissionRequesterProtocol

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.PushPermissionRequesterProtocol
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: PushPermissionRequesterProtocol,
) : 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 {
prashanDYDX marked this conversation as resolved.
Show resolved Hide resolved
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
Loading