Skip to content

Commit

Permalink
feat: replace webview auth in CoinbaseServicesFragment
Browse files Browse the repository at this point in the history
  • Loading branch information
Syn-McJ committed Feb 23, 2024
1 parent 3878098 commit 047e672
Show file tree
Hide file tree
Showing 6 changed files with 89 additions and 56 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package org.dash.wallet.integrations.coinbase

import android.content.Context
import org.dash.wallet.integrations.coinbase.service.CoinBaseClientConstants
import java.io.File

object CoinbaseConstants {
Expand All @@ -37,6 +38,19 @@ object CoinbaseConstants {
const val BASE_IDS_REQUEST_URL = "v2/assets/prices?filter=holdable&resolution=latest"
const val BUY_FEE = 0.006
const val REDIRECT_URL = "dashwallet://brokers/coinbase/connect"
const val AUTH_RESULT_ACTION = "Coinbase.AUTH_RESULT"
val LINK_URL = "https://www.coinbase.com/oauth/authorize?client_id=${CoinBaseClientConstants.CLIENT_ID}" +
"&redirect_uri=${REDIRECT_URL}&response_type" +
"=code&scope=wallet:accounts:read,wallet:user:read,wallet:payment-methods:read," +
"wallet:buys:read,wallet:buys:create,wallet:transactions:transfer," +
"wallet:sells:create,wallet:sells:read,wallet:deposits:create," +
"wallet:transactions:request,wallet:transactions:read,wallet:trades:create," +
"wallet:supported-assets:read,wallet:transactions:send," +
"wallet:addresses:read,wallet:addresses:create" +
"&meta[send_limit_amount]=10" +
"&meta[send_limit_currency]=USD" +
"&meta[send_limit_period]=month" +
"&account=all"

fun getCacheDir(context: Context): File {
return File(context.cacheDir, "coinbase")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,12 @@ interface CoinBaseAuthApi {
suspend fun getToken(
@Field("client_id") clientId: String = CoinBaseClientConstants.CLIENT_ID,
@Field("redirect_uri") redirectUri: String = CoinbaseConstants.REDIRECT_URL,
@Field("grant_type") grant_type: String = "authorization_code",
@Field("client_secret") client_secret: String = CoinBaseClientConstants.CLIENT_SECRET,
@Field("grant_type") grantType: String = "authorization_code",
@Field("client_secret") clientSecret: String = CoinBaseClientConstants.CLIENT_SECRET,
@Field("code") code: String
): TokenResponse?

@FormUrlEncoded
@POST("oauth/revoke")
suspend fun revokeToken(@Field("token") token: String)
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,10 @@ package org.dash.wallet.integrations.coinbase.ui

import android.animation.ObjectAnimator
import android.app.Activity
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.net.Uri
import android.os.Bundle
import android.view.View
Expand All @@ -27,17 +30,22 @@ import androidx.core.view.isVisible
import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.withResumed
import androidx.localbroadcastmanager.content.LocalBroadcastManager
import androidx.navigation.fragment.findNavController
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.launch
import org.bitcoinj.core.Coin
import org.dash.wallet.common.databinding.FragmentIntegrationPortalBinding
import org.dash.wallet.common.services.analytics.AnalyticsConstants
import org.dash.wallet.common.ui.blinkAnimator
import org.dash.wallet.common.ui.dialogs.AdaptiveDialog
import org.dash.wallet.common.ui.viewBinding
import org.dash.wallet.common.util.observe
import org.dash.wallet.common.util.openCustomTab
import org.dash.wallet.common.util.safeNavigate
import org.dash.wallet.common.util.toFormattedString
import org.dash.wallet.integrations.coinbase.CoinbaseConstants
import org.dash.wallet.integrations.coinbase.R
import org.dash.wallet.integrations.coinbase.model.CoinbaseErrorType
import org.dash.wallet.integrations.coinbase.viewmodels.CoinbaseViewModel
Expand All @@ -51,17 +59,20 @@ class CoinbaseServicesFragment : Fragment(R.layout.fragment_integration_portal)
private val sharedViewModel by coinbaseViewModels<CoinbaseViewModel>()
private var balanceAnimator: ObjectAnimator? = null

private val coinbaseAuthLauncher = registerForActivityResult(
ActivityResultContracts.StartActivityForResult()
) { result ->
val data = result.data
private val coinbaseAuthResultReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
val uri = intent.extras?.get("uri") as Uri?
val code = uri?.getQueryParameter("code")

if (result.resultCode == Activity.RESULT_OK) {
data?.extras?.getString(CoinBaseWebClientActivity.RESULT_TEXT)?.let { code ->
lifecycleScope.launchWhenResumed {
handleCoinbaseAuthResult(code)
if (code != null) {
lifecycleScope.launch {
withResumed {
handleCoinbaseAuthResult(code)
}
}
}

startActivity(intent)
}
}

Expand Down Expand Up @@ -125,9 +136,7 @@ class CoinbaseServicesFragment : Fragment(R.layout.fragment_integration_portal)
it.isCancelable = false
}.show(requireActivity()) { login ->
if (login == true) {
coinbaseAuthLauncher.launch(
Intent(requireContext(), CoinBaseWebClientActivity::class.java)
)
requireActivity().openCustomTab(CoinbaseConstants.LINK_URL)
} else {
findNavController().popBackStack()
}
Expand Down Expand Up @@ -170,6 +179,11 @@ class CoinbaseServicesFragment : Fragment(R.layout.fragment_integration_portal)

viewModel.refreshBalance()
sharedViewModel.getBaseIdForFiatModel()

LocalBroadcastManager.getInstance(requireContext()).registerReceiver(
coinbaseAuthResultReceiver,
IntentFilter(CoinbaseConstants.AUTH_RESULT_ACTION)
)
}

private fun setNetworkState(hasInternet: Boolean) {
Expand All @@ -186,30 +200,39 @@ class CoinbaseServicesFragment : Fragment(R.layout.fragment_integration_portal)
startActivity(defaultBrowser)
}

private suspend fun handleCoinbaseAuthResult(code: String) {
val success = AdaptiveDialog.withProgress(getString(R.string.loading), requireActivity()) {
sharedViewModel.loginToCoinbase(code)
}
private fun handleCoinbaseAuthResult(code: String) {
lifecycleScope.launch {
val success = AdaptiveDialog.withProgress(getString(R.string.loading), requireActivity()) {
sharedViewModel.loginToCoinbase(code)
}

if (success) {
return
}
if (success) {
return@launch
}

val retry = AdaptiveDialog.create(
R.drawable.ic_error,
getString(R.string.login_error_title, getString(R.string.coinbase)),
getString(R.string.login_error_message, getString(R.string.coinbase)),
getString(android.R.string.cancel),
getString(R.string.retry)
).showAsync(requireActivity())

if (retry == true) {
handleCoinbaseAuthResult(code)
} else {
findNavController().popBackStack()
val retry = AdaptiveDialog.create(
R.drawable.ic_error,
getString(R.string.login_error_title, getString(R.string.coinbase)),
getString(R.string.login_error_message, getString(R.string.coinbase)),
getString(android.R.string.cancel),
getString(R.string.retry)
).showAsync(requireActivity())

if (retry == true) {
handleCoinbaseAuthResult(code)
} else {
findNavController().popBackStack()
}
}
}

override fun onDestroyView() {
super.onDestroyView()
LocalBroadcastManager.getInstance(requireContext()).unregisterReceiver(
coinbaseAuthResultReceiver
)
}

override fun onDestroy() {
super.onDestroy()
this.balanceAnimator = null
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import de.schildbach.wallet_test.R
import org.bitcoinj.wallet.Wallet
import org.dash.wallet.common.ui.BaseAlertDialogBuilder
import org.dash.wallet.common.ui.formatString
import org.dash.wallet.integrations.coinbase.CoinbaseConstants
import org.dash.wallet.integrations.uphold.ui.UpholdPortalFragment

/**
Expand Down Expand Up @@ -82,7 +83,7 @@ class WalletUriHandlerActivity : AppCompatActivity() {
activityIntent.setAction(UpholdPortalFragment.AUTH_RESULT_ACTION)
LocalBroadcastManager.getInstance(this).sendBroadcast(activityIntent)
} else if (intentUri.path?.contains("coinbase") == true) {
activityIntent.setAction(IntegrationOverviewFragment.AUTH_RESULT_ACTION)
activityIntent.setAction(CoinbaseConstants.AUTH_RESULT_ACTION)
LocalBroadcastManager.getInstance(this).sendBroadcast(activityIntent)
}
finish()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import androidx.core.view.isVisible
import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.withResumed
import androidx.localbroadcastmanager.content.LocalBroadcastManager
import androidx.navigation.fragment.findNavController
import dagger.hilt.android.AndroidEntryPoint
Expand All @@ -38,13 +39,10 @@ import org.dash.wallet.common.ui.dialogs.AdaptiveDialog
import org.dash.wallet.common.ui.viewBinding
import org.dash.wallet.common.util.openCustomTab
import org.dash.wallet.common.util.safeNavigate
import org.dash.wallet.integrations.coinbase.CoinbaseConstants

@AndroidEntryPoint
class IntegrationOverviewFragment : Fragment(R.layout.fragment_integration_overview) {
companion object {
const val AUTH_RESULT_ACTION = "IntegrationOverviewFragment.AUTH_RESULT"
}

private val binding by viewBinding(FragmentIntegrationOverviewBinding::bind)
private val viewModel by viewModels<IntegrationOverviewViewModel>()

Expand All @@ -54,7 +52,11 @@ class IntegrationOverviewFragment : Fragment(R.layout.fragment_integration_overv
val code = uri?.getQueryParameter("code")

if (code != null) {
handleCoinbaseAuthResult(code)
lifecycleScope.launch {
withResumed {
handleCoinbaseAuthResult(code)
}
}
}

startActivity(intent)
Expand All @@ -78,7 +80,7 @@ class IntegrationOverviewFragment : Fragment(R.layout.fragment_integration_overv

LocalBroadcastManager.getInstance(requireContext()).registerReceiver(
coinbaseAuthResultReceiver,
IntentFilter(AUTH_RESULT_ACTION)
IntentFilter(CoinbaseConstants.AUTH_RESULT_ACTION)
)
}

Expand All @@ -100,8 +102,7 @@ class IntegrationOverviewFragment : Fragment(R.layout.fragment_integration_overv
}

private fun linkCoinbaseAccount() {
val url = viewModel.getCoinbaseLinkAccountUrl()
requireActivity().openCustomTab(url)
requireActivity().openCustomTab(CoinbaseConstants.LINK_URL)
}

private fun handleCoinbaseAuthResult(code: String) {
Expand All @@ -127,4 +128,12 @@ class IntegrationOverviewFragment : Fragment(R.layout.fragment_integration_overv
}
}
}

override fun onDestroyView() {
super.onDestroyView()

LocalBroadcastManager.getInstance(requireContext()).unregisterReceiver(
coinbaseAuthResultReceiver
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -60,19 +60,4 @@ class IntegrationOverviewViewModel @Inject constructor(
fun logEvent(eventName: String) {
analyticsService.logEvent(eventName, mapOf())
}

fun getCoinbaseLinkAccountUrl(): String {
return "https://www.coinbase.com/oauth/authorize?client_id=${CoinBaseClientConstants.CLIENT_ID}" +
"&redirect_uri=${CoinbaseConstants.REDIRECT_URL}&response_type" +
"=code&scope=wallet:accounts:read,wallet:user:read,wallet:payment-methods:read," +
"wallet:buys:read,wallet:buys:create,wallet:transactions:transfer," +
"wallet:sells:create,wallet:sells:read,wallet:deposits:create," +
"wallet:transactions:request,wallet:transactions:read,wallet:trades:create," +
"wallet:supported-assets:read,wallet:transactions:send," +
"wallet:addresses:read,wallet:addresses:create" +
"&meta[send_limit_amount]=10" +
"&meta[send_limit_currency]=USD" +
"&meta[send_limit_period]=month" +
"&account=all"
}
}

0 comments on commit 047e672

Please sign in to comment.