Skip to content

Commit 4acfc3c

Browse files
nomanrangelix
authored andcommitted
feat: add deep link handler for blockstream://redirect/transactions
1 parent 0eae72a commit 4acfc3c

File tree

9 files changed

+105
-29
lines changed

9 files changed

+105
-29
lines changed

androidApp/src/main/AndroidManifest.xml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,14 @@
154154
<data android:host="blockstream.com" />
155155
<data android:path="/ramps/redirect" />
156156
</intent-filter>
157+
<intent-filter>
158+
<action android:name="android.intent.action.VIEW" />
159+
160+
<category android:name="android.intent.category.DEFAULT" />
161+
<category android:name="android.intent.category.BROWSABLE" />
162+
163+
<data android:scheme="blockstream" />
164+
</intent-filter>
157165
</activity>
158166

159167
<provider

androidApp/src/main/java/com/blockstream/green/GreenActivity.kt

Lines changed: 21 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ import com.blockstream.compose.utils.compatTestTagsAsResourceId
3636
import com.blockstream.green.data.CountlyAndroid
3737
import com.blockstream.green.data.config.AppInfo
3838
import com.blockstream.green.services.TaskService
39+
import com.blockstream.green.utils.DeepLinkHandler
3940
import com.blockstream.green.utils.Loggable
4041
import kotlinx.coroutines.launch
4142
import kotlinx.serialization.json.Json
@@ -122,8 +123,7 @@ class GreenActivity : AppCompatActivity() {
122123
val json = Json.parseToJsonElement(
123124
String(
124125
Base64.decode(
125-
it.getStringExtra(ADD_WALLET)!!,
126-
Base64.DEFAULT
126+
it.getStringExtra(ADD_WALLET)!!, Base64.DEFAULT
127127
)!!
128128
)
129129
)
@@ -196,30 +196,33 @@ class GreenActivity : AppCompatActivity() {
196196

197197
override fun onNewIntent(intent: Intent) {
198198
super.onNewIntent(intent)
199+
logger.d { "onNewIntent called with: ${intent.data}" }
200+
setIntent(intent) // Important: Update the activity's intent
199201
handleIntent(intent)
200202
}
201203

202204
private fun handleIntent(intent: Intent?) {
203-
if (
204-
intent?.action == Intent.ACTION_VIEW &&
205-
intent.data?.toString()?.let { it.contains("/jade/setup") || it.contains("/j/s") } == true
205+
logger.d { "handleIntent called with action: ${intent?.action}, data: ${intent?.data}" }
206+
207+
// Handle blockstream:// scheme with DeepLinkHandler
208+
if (intent?.action == Intent.ACTION_VIEW && intent.data?.scheme == "blockstream") {
209+
logger.d { "Handling blockstream:// deep link" }
210+
if (DeepLinkHandler.handleDeepLink(intent.data, mainViewModel)) {
211+
return
212+
}
213+
}
214+
215+
if (intent?.action == Intent.ACTION_VIEW && intent.data?.toString()
216+
?.let { it.contains("/jade/setup") || it.contains("/j/s") } == true
206217
) {
207218
mainViewModel.postEvent(Events.NavigateTo(NavigateDestinations.DeviceList(isJade = true)))
208-
} else if (
209-
intent?.action == Intent.ACTION_VIEW &&
210-
intent.data?.toString()?.contains("/ramps/redirect") == true
211-
) {
212-
mainViewModel.postEvent(Events.EventSideEffect(sideEffect = SideEffects.Dialog(message = StringHolder(stringResource = Res.string.id_success))))
213219
} else if (intent?.action == OPEN_WALLET) {
214220

215-
intent.getStringExtra(WALLET)
216-
?.let { GreenJson.json.decodeFromString<GreenWallet>(it) }
217-
?.let { wallet ->
218-
mainViewModel.navigate(
219-
wallet = wallet,
220-
deviceId = intent.getStringExtra(DEVICE_ID)
221-
)
222-
}
221+
intent.getStringExtra(WALLET)?.let { GreenJson.json.decodeFromString<GreenWallet>(it) }?.let { wallet ->
222+
mainViewModel.navigate(
223+
wallet = wallet, deviceId = intent.getStringExtra(DEVICE_ID)
224+
)
225+
}
223226
} else if (intent?.action == HIDE_AMOUNTS) {
224227
settingsManager.saveApplicationSettings(
225228
settingsManager.getApplicationSettings().copy(hideAmounts = true)
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package com.blockstream.green.utils
2+
3+
import android.net.Uri
4+
import com.blockstream.common.events.Events
5+
import com.blockstream.common.models.MainViewModel
6+
import com.blockstream.common.sideeffects.SideEffects
7+
8+
object DeepLinkHandler : Loggable() {
9+
10+
fun handleDeepLink(uri: Uri?, mainViewModel: MainViewModel): Boolean {
11+
uri ?: return false
12+
13+
logger.i { "Handling deep link: $uri" }
14+
15+
return when {
16+
uri.scheme == "blockstream" -> handleBlockstreamScheme(uri, mainViewModel)
17+
else -> false
18+
}
19+
}
20+
21+
private fun handleBlockstreamScheme(uri: Uri, mainViewModel: MainViewModel): Boolean {
22+
return when (uri.host) {
23+
"redirect" -> handleRedirect(uri, mainViewModel)
24+
else -> {
25+
logger.w { "Unknown blockstream:// host: ${uri.host}" }
26+
false
27+
}
28+
}
29+
}
30+
31+
private fun handleRedirect(uri: Uri, mainViewModel: MainViewModel): Boolean {
32+
return when (uri.path) {
33+
"/transactions" -> {
34+
mainViewModel.postEvent(Events.EventSideEffect(sideEffect = SideEffects.NavigateToTransactTab))
35+
true
36+
}
37+
38+
else -> {
39+
logger.w { "Unknown redirect path: ${uri.path}" }
40+
false
41+
}
42+
}
43+
}
44+
}

common/src/commonMain/kotlin/com/blockstream/common/sideeffects/SideEffects.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ object SideEffects {
4848
val supportData: SupportData? = null,
4949
) : SideEffect
5050

51+
data object NavigateToTransactTab : SideEffect
5152
data class NavigateToRoot(val popTo: PopTo? = null) : SideEffect
5253
object CloseDrawer : SideEffect
5354
data class TransactionSent(val data: ProcessedTransactionDetails) : SideEffect

compose/src/commonMain/kotlin/com/blockstream/compose/GreenApp.kt

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,6 @@ import com.blockstream.compose.sideeffects.DialogState
5050
import com.blockstream.compose.sideeffects.rememberBiometricsState
5151
import com.blockstream.compose.theme.GreenChrome
5252
import com.blockstream.compose.theme.GreenTheme
53-
import com.blockstream.compose.utils.HandleSideEffect
5453
import com.blockstream.green.data.config.AppInfo
5554
import com.blockstream.ui.navigation.LocalNavData
5655
import com.blockstream.ui.navigation.LocalNavigator
@@ -171,6 +170,7 @@ fun GreenApp(mainViewModel: MainViewModel, modifier: Modifier = Modifier) {
171170
AppScaffold(
172171
navData = navData,
173172
snackbarHostState = snackbarHostState,
173+
mainViewModel = mainViewModel,
174174
navigate = {
175175
navigate(navController, it)
176176
},
@@ -193,9 +193,6 @@ fun GreenApp(mainViewModel: MainViewModel, modifier: Modifier = Modifier) {
193193
innerPadding = innerPadding,
194194
startDestination = NavigateDestinations.Home
195195
)
196-
197-
// Handle side effects from MainViewModel like navigating from handled intent
198-
HandleSideEffect(mainViewModel)
199196
}
200197
}
201198
}

compose/src/commonMain/kotlin/com/blockstream/compose/navigation/AppScaffold.kt

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import androidx.compose.runtime.derivedStateOf
2424
import androidx.compose.runtime.getValue
2525
import androidx.compose.runtime.mutableStateOf
2626
import androidx.compose.runtime.remember
27+
import androidx.compose.runtime.rememberCoroutineScope
2728
import androidx.compose.runtime.setValue
2829
import androidx.compose.ui.Alignment
2930
import androidx.compose.ui.Modifier
@@ -44,10 +45,13 @@ import com.adamglin.phosphoricons.regular.ArrowsDownUp
4445
import com.adamglin.phosphoricons.regular.Gear
4546
import com.adamglin.phosphoricons.regular.House
4647
import com.adamglin.phosphoricons.regular.ShieldCheck
48+
import com.blockstream.common.models.MainViewModel
4749
import com.blockstream.common.navigation.NavigateDestination
4850
import com.blockstream.common.navigation.NavigateDestinations
51+
import com.blockstream.common.sideeffects.SideEffects
4952
import com.blockstream.compose.components.GreenTopAppBar
5053
import com.blockstream.compose.theme.bodyMedium
54+
import com.blockstream.compose.utils.HandleSideEffect
5155
import com.blockstream.ui.navigation.LocalNavigator
5256
import com.blockstream.ui.navigation.NavData
5357
import org.jetbrains.compose.resources.StringResource
@@ -93,6 +97,7 @@ val TopLevelRoutes = listOf(
9397
fun AppScaffold(
9498
navData: NavData = NavData(),
9599
snackbarHostState: SnackbarHostState? = null,
100+
mainViewModel: MainViewModel,
96101
navigate: (destination: NavigateDestination) -> Unit = {},
97102
goBack: () -> Unit = { },
98103
content: @Composable (PaddingValues) -> Unit
@@ -101,6 +106,7 @@ fun AppScaffold(
101106
val navBackStackEntry by navigator.currentBackStackEntryAsState()
102107
val currentDestination = navBackStackEntry?.destination
103108
val currentBackStack by navigator.currentBackStack.collectAsStateWithLifecycle()
109+
val scope = rememberCoroutineScope()
104110
var showNavigationBar by mutableStateOf(true)
105111

106112
navigator.addOnDestinationChangedListener { _, destination, _ ->
@@ -131,6 +137,28 @@ fun AppScaffold(
131137
}
132138
}
133139

140+
// Handle side effects from MainViewModel like navigating from handled intent
141+
HandleSideEffect(mainViewModel) {
142+
if (it is SideEffects.NavigateToTransactTab) {
143+
greenWallet?.also {
144+
while (navigator.currentBackStack.value.size > 2 && navigator.currentBackStackEntry?.destination?.hasRoute(
145+
NavigateDestinations.Transact::class
146+
) != true
147+
) {
148+
navigator.navigateUp()
149+
}
150+
151+
if (navigator.currentBackStackEntry?.destination?.hasRoute(NavigateDestinations.Transact::class) != true) {
152+
navigate(
153+
NavigateDestinations.Transact(
154+
greenWallet = it
155+
)
156+
)
157+
}
158+
}
159+
}
160+
}
161+
134162
// val scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior(rememberTopAppBarState())
135163
// val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState())
136164
val scrollBehavior = null

data/src/commonMain/kotlin/com/blockstream/green/data/meld/MeldHttpClient.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ class MeldHttpClient(appInfo: AppInfo) : AppHttpClient(appInfo.isDevelopmentOrDe
1515
}
1616

1717
defaultRequest {
18-
url(MELD_PRODUCTION.takeIf { appInfo.isProduction } ?: MELD_PRODUCTION)
18+
url(MELD_PRODUCTION.takeIf { appInfo.isProduction } ?: MELD_SANDBOX)
1919
contentType(ContentType.Application.Json)
2020
}
2121
}) {

data/src/commonMain/kotlin/com/blockstream/green/data/meld/data/SessionData.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,5 @@ data class SessionData(
1010
val destinationCurrencyCode: String = "BTC",
1111
val walletAddress: String,
1212
val serviceProvider: String,
13-
val redirectUrl: String = "https://blockstream.com/ramps/redirect",
13+
val redirectUrl: String = "https://green-webhooks.blockstream.com/thank-you",
1414
)

data/src/commonMain/kotlin/com/blockstream/green/data/meld/models/Transaction.kt

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,10 @@ data class MeldTransaction(
1111
val status: String,
1212
val sourceAmount: Double,
1313
val sourceCurrencyCode: String,
14-
val destinationAmount: Double,
15-
val destinationCurrencyCode: String,
1614
val paymentMethodType: String,
1715
val serviceProvider: String,
18-
val serviceTransactionId: String,
1916
val createdAt: String,
2017
val updatedAt: String,
21-
val countryCode: String,
22-
val fiatAmountInUsd: Double,
2318
val cryptoDetails: MeldCryptoDetails? = null
2419
)
2520

0 commit comments

Comments
 (0)