Skip to content

Commit

Permalink
Merge branch 'drew/link-parsing' into 'develop'
Browse files Browse the repository at this point in the history
Improve link parsing mechanics

See merge request breadwallet/breadwallet-android!1231
  • Loading branch information
DrewCarlson committed Jun 8, 2020
2 parents 2ca83f7 + 71819d4 commit 254d94f
Show file tree
Hide file tree
Showing 8 changed files with 210 additions and 133 deletions.
9 changes: 9 additions & 0 deletions app/src/main/java/com/breadwallet/breadbox/BreadBox.kt
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
package com.breadwallet.breadbox

import com.breadwallet.crypto.Account
import com.breadwallet.crypto.Network

import com.breadwallet.crypto.Wallet
import com.breadwallet.crypto.System
Expand Down Expand Up @@ -74,6 +75,14 @@ interface BreadBox {
/** Emits the [WalletState] for the [Wallet] of [currencyCode]. */
fun walletState(currencyCode: String): Flow<WalletState>

/**
* Emits the [Network]s discovered by [System].
*
* Setting [whenDiscoveryComplete] to true delays the first
* emission until network discovery is complete.
*/
fun networks(whenDiscoveryComplete: Boolean = false): Flow<List<Network>>

/** Returns [System] when [isOpen] or null when it is not. */
fun getSystemUnsafe(): System?
}
27 changes: 23 additions & 4 deletions app/src/main/java/com/breadwallet/breadbox/CoreBreadBox.kt
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import com.breadwallet.crypto.WalletManager
import com.breadwallet.crypto.WalletManagerState
import com.breadwallet.crypto.blockchaindb.BlockchainDb
import com.breadwallet.crypto.events.network.NetworkEvent
import com.breadwallet.crypto.events.system.SystemDiscoveredNetworksEvent
import com.breadwallet.crypto.events.system.SystemEvent
import com.breadwallet.crypto.events.system.SystemListener
import com.breadwallet.crypto.events.system.SystemNetworkAddedEvent
Expand Down Expand Up @@ -78,6 +79,7 @@ import kotlinx.coroutines.flow.mapNotNull
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.flow.take
import kotlinx.coroutines.flow.transform
import kotlinx.coroutines.launch
import java.io.File
import java.util.Locale
Expand Down Expand Up @@ -121,6 +123,7 @@ internal class CoreBreadBox(

private var networkManager: NetworkManager? = null

private val isDiscoveryComplete = AtomicBoolean(false)
private val _isOpen = AtomicBoolean(false)
override var isOpen: Boolean
get() = _isOpen.get()
Expand Down Expand Up @@ -351,6 +354,17 @@ internal class CoreBreadBox(
}
}

override fun networks(whenDiscoveryComplete: Boolean): Flow<List<Network>> =
system().transform {
if (whenDiscoveryComplete) {
if (isDiscoveryComplete.get()) {
emit(it.networks)
}
} else {
emit(it.networks)
}
}

override fun getSystemUnsafe(): System? = system

override fun handleWalletEvent(
Expand Down Expand Up @@ -448,11 +462,16 @@ internal class CoreBreadBox(
override fun handleNetworkEvent(system: System, network: Network, event: NetworkEvent) = Unit

override fun handleSystemEvent(system: System, event: SystemEvent) {
systemChannel.offer(Unit)
if (event is SystemNetworkAddedEvent) {
logDebug("Network '${event.network.name}' added.")
networkManager?.initializeNetwork(event.network)
when (event) {
is SystemNetworkAddedEvent -> {
logDebug("Network '${event.network.name}' added.")
networkManager?.initializeNetwork(event.network)
}
is SystemDiscoveredNetworksEvent -> {
isDiscoveryComplete.set(true)
}
}
systemChannel.offer(Unit)
}

override fun handleTransferEvent(
Expand Down
26 changes: 10 additions & 16 deletions app/src/main/java/com/breadwallet/tools/util/Link.kt
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@
package com.breadwallet.tools.util

import android.net.Uri
import com.breadwallet.app.BreadApp
import com.breadwallet.breadbox.BreadBox
import com.breadwallet.crypto.Address
import com.breadwallet.crypto.Key
Expand All @@ -38,7 +37,7 @@ import com.breadwallet.util.CryptoUriParser
import com.breadwallet.util.CurrencyCode
import com.platform.HTTPServer
import io.sweers.redacted.annotation.Redacted
import org.kodein.di.erased.instance
import kotlinx.coroutines.flow.first
import java.io.Serializable
import java.math.BigDecimal
import java.net.URLEncoder
Expand Down Expand Up @@ -104,22 +103,25 @@ sealed class Link {

/** Turn the url string into a [Link] or null if it is not supported. */
@Suppress("ComplexMethod")
fun String.asLink(): Link? {
suspend fun String.asLink(
breadBox: BreadBox,
uriParser: CryptoUriParser
): Link? {
if (isBlank()) return null
val uri = Uri.parse(this)
val (address, currencyCode) =
breadBox.getSystemUnsafe()
?.networks
?.mapNotNull { network ->
breadBox.networks(true)
.first()
.mapNotNull { network ->
Address.create(this, network)
.orNull()
?.let { address ->
address to network.currency.code
}
}
?.firstOrNull() ?: null to null
.firstOrNull() ?: null to null

return when {
isBlank() -> null
address != null && currencyCode != null ->
Link.CryptoRequestUrl(
currencyCode = currencyCode,
Expand All @@ -143,14 +145,6 @@ fun String.asLink(): Link? {
}
}

private val uriParser by lazy {
BreadApp.getKodeinInstance().instance<CryptoUriParser>()
}

private val breadBox by lazy {
BreadApp.getKodeinInstance().instance<BreadBox>()
}

fun CryptoRequest.asCryptoRequestUrl() =
Link.CryptoRequestUrl(
address = address,
Expand Down
205 changes: 114 additions & 91 deletions app/src/main/java/com/breadwallet/ui/navigation/RouterNavigator.kt
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import com.bluelinelabs.conductor.changehandler.FadeChangeHandler
import com.bluelinelabs.conductor.changehandler.HorizontalChangeHandler
import com.bluelinelabs.conductor.changehandler.VerticalChangeHandler
import com.breadwallet.R
import com.breadwallet.breadbox.BreadBox
import com.breadwallet.legacy.presenter.settings.NotificationSettingsController
import com.breadwallet.logger.logError
import com.breadwallet.protocols.messageexchange.MessageExchangeService
Expand Down Expand Up @@ -75,17 +76,33 @@ import com.breadwallet.ui.wallet.BrdWalletController
import com.breadwallet.ui.wallet.WalletController
import com.breadwallet.ui.web.WebController
import com.breadwallet.ui.writedownkey.WriteDownKeyController
import com.breadwallet.util.CryptoUriParser
import com.breadwallet.util.isBrd
import com.platform.HTTPServer
import com.platform.util.AppReviewPromptManager
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.launch
import org.kodein.di.KodeinAware
import org.kodein.di.android.closestKodein
import org.kodein.di.erased.instance
import java.util.Locale

@Suppress("TooManyFunctions")
class RouterNavigator(
private val routerProvider: () -> Router
) : NavigationTargetHandlerSpec {
) : NavigationTargetHandlerSpec, KodeinAware {

private val router get() = routerProvider()
private val scope = CoroutineScope(SupervisorJob() + Dispatchers.Main)

override val kodein by closestKodein {
checkNotNull(router.activity?.applicationContext)
}

private val breadBox by instance<BreadBox>()
private val uriParser by instance<CryptoUriParser>()

fun navigateTo(target: NavigationTarget) = patch(target)

Expand Down Expand Up @@ -203,97 +220,13 @@ class RouterNavigator(
}

override fun deepLink(effect: NavigationTarget.DeepLink) {
val link = effect.url?.asLink() ?: effect.link
if (link == null) {
logError("Failed to parse url, ${effect.url}")
showLaunchScreen(effect.authenticated)
return
}
val isTopLogin = router.backstack.lastOrNull()?.controller() is LoginController
if (isTopLogin && effect.authenticated) {
router.popCurrentController()
}
when (link) {
is Link.CryptoRequestUrl -> {
val sendController = SendSheetController(link).asTransaction()
router.pushWithStackIfEmpty(sendController, effect.authenticated) {
listOf(
HomeController().asTransaction(),
WalletController(link.currencyCode).asTransaction(
popChangeHandler = HorizontalChangeHandler(),
pushChangeHandler = HorizontalChangeHandler()
),
sendController
)
}
}
is Link.BreadUrl.ScanQR -> {
val controller = ScannerController().asTransaction()
router.pushWithStackIfEmpty(controller, effect.authenticated) {
listOf(
HomeController().asTransaction(),
controller
)
}
}
is Link.ImportWallet -> {
val controller = ImportController(
privateKey = link.privateKey,
isPasswordProtected = link.passwordProtected
).asTransaction()
router.pushWithStackIfEmpty(controller, effect.authenticated) {
listOf(
HomeController().asTransaction(),
controller
)
}
}
is Link.PlatformUrl -> {
val controller = WebController(link.url).asTransaction()
router.pushWithStackIfEmpty(controller, effect.authenticated) {
listOf(
HomeController().asTransaction(),
controller
)
}
}
is Link.PlatformDebugUrl -> {
val context = router.activity!!.applicationContext
if (!link.webBundleUrl.isNullOrBlank()) {
ServerBundlesHelper.setWebPlatformDebugURL(link.webBundleUrl)
} else if (!link.webBundle.isNullOrBlank()) {
ServerBundlesHelper.setDebugBundle(
context,
ServerBundlesHelper.Type.WEB,
link.webBundle
)
}

showLaunchScreen(effect.authenticated)
}
Link.BreadUrl.ScanQR -> {
val controller = ScannerController().asTransaction()
router.pushWithStackIfEmpty(controller, effect.authenticated) {
listOf(
HomeController().asTransaction(),
controller
)
}
}
is Link.WalletPairUrl -> {
val context = router.activity!!.applicationContext
MessageExchangeService.enqueueWork(
context, MessageExchangeService.createIntent(
context,
MessageExchangeService.ACTION_REQUEST_TO_PAIR,
link.pairingMetaData
)
)
showLaunchScreen(effect.authenticated)
}
else -> {
logError("Failed to route deeplink, going Home.")
scope.launch(Dispatchers.Main) {
val link = effect.url?.asLink(breadBox, uriParser) ?: effect.link
if (link == null) {
logError("Failed to parse url, ${effect.url}")
showLaunchScreen(effect.authenticated)
} else {
processDeepLink(effect, link)
}
}
}
Expand Down Expand Up @@ -582,4 +515,94 @@ class RouterNavigator(
}
}
}

private fun processDeepLink(effect: NavigationTarget.DeepLink, link: Link) {
val isTopLogin = router.backstack.lastOrNull()?.controller() is LoginController
if (isTopLogin && effect.authenticated) {
router.popCurrentController()
}
when (link) {
is Link.CryptoRequestUrl -> {
val sendController = SendSheetController(link).asTransaction()
router.pushWithStackIfEmpty(sendController, effect.authenticated) {
listOf(
HomeController().asTransaction(),
WalletController(link.currencyCode).asTransaction(
popChangeHandler = HorizontalChangeHandler(),
pushChangeHandler = HorizontalChangeHandler()
),
sendController
)
}
}
is Link.BreadUrl.ScanQR -> {
val controller = ScannerController().asTransaction()
router.pushWithStackIfEmpty(controller, effect.authenticated) {
listOf(
HomeController().asTransaction(),
controller
)
}
}
is Link.ImportWallet -> {
val controller = ImportController(
privateKey = link.privateKey,
isPasswordProtected = link.passwordProtected
).asTransaction()
router.pushWithStackIfEmpty(controller, effect.authenticated) {
listOf(
HomeController().asTransaction(),
controller
)
}
}
is Link.PlatformUrl -> {
val controller = WebController(link.url).asTransaction()
router.pushWithStackIfEmpty(controller, effect.authenticated) {
listOf(
HomeController().asTransaction(),
controller
)
}
}
is Link.PlatformDebugUrl -> {
val context = router.activity!!.applicationContext
if (!link.webBundleUrl.isNullOrBlank()) {
ServerBundlesHelper.setWebPlatformDebugURL(link.webBundleUrl)
} else if (!link.webBundle.isNullOrBlank()) {
ServerBundlesHelper.setDebugBundle(
context,
ServerBundlesHelper.Type.WEB,
link.webBundle
)
}

showLaunchScreen(effect.authenticated)
}
Link.BreadUrl.ScanQR -> {
val controller = ScannerController().asTransaction()
router.pushWithStackIfEmpty(controller, effect.authenticated) {
listOf(
HomeController().asTransaction(),
controller
)
}
}
is Link.WalletPairUrl -> {
val context = router.activity!!.applicationContext
MessageExchangeService.enqueueWork(
context, MessageExchangeService.createIntent(
context,
MessageExchangeService.ACTION_REQUEST_TO_PAIR,
link.pairingMetaData
)
)
showLaunchScreen(effect.authenticated)
}
else -> {
logError("Failed to route deeplink, going Home.")
showLaunchScreen(effect.authenticated)
}
}
}
}
Loading

0 comments on commit 254d94f

Please sign in to comment.