Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
1 change: 0 additions & 1 deletion app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,6 @@
<action android:name="android.nfc.action.NDEF_DISCOVERED" />

<category android:name="android.intent.category.DEFAULT" />
Comment thread
jvsena42 marked this conversation as resolved.
<category android:name="android.intent.category.BROWSABLE" />

<data android:scheme="bitkit" />
<data android:scheme="slash" />
Expand Down
2 changes: 1 addition & 1 deletion app/src/main/java/to/bitkit/ui/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -297,7 +297,7 @@ class MainActivity : FragmentActivity() {
internal fun Intent?.launchKey(): String? {
this ?: return null
return when (action) {
Intent.ACTION_VIEW -> data?.toString()?.let {
in AppViewModel.DEEPLINK_ACTIONS -> data?.toString()?.let {
SamRockSetupRequest.sanitizedLaunchKey(it) ?: it
}
UsbManager.ACTION_USB_DEVICE_ATTACHED -> listOfNotNull(action, usbDevice()?.deviceName).joinToString(":")
Expand Down
6 changes: 5 additions & 1 deletion app/src/main/java/to/bitkit/viewmodels/AppViewModel.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package to.bitkit.viewmodels
import android.content.Context
import android.content.Intent
import android.net.Uri
import android.nfc.NfcAdapter
import androidx.annotation.StringRes
import androidx.compose.runtime.Stable
import androidx.compose.runtime.getValue
Expand Down Expand Up @@ -3064,7 +3065,7 @@ class AppViewModel @Inject constructor(
}

fun handleDeeplinkIntent(intent: Intent) {
if (intent.action != Intent.ACTION_VIEW) return
if (intent.action !in DEEPLINK_ACTIONS) return
Comment thread
jvsena42 marked this conversation as resolved.
intent.data?.let { uri ->
Logger.debug("Received deeplink '${uri.toString().sanitizedDeeplinkLogValue()}'", context = TAG)
processDeeplink(uri)
Expand Down Expand Up @@ -3290,6 +3291,9 @@ class AppViewModel @Inject constructor(
private const val PUBKYAUTH_SCHEME = "pubkyauth"
private const val RECOVERY_MODE_DEEPLINK = "recovery-mode"
private val LNURL_WITHDRAW_EXPIRY_SEC = 1.hours.inWholeSeconds.toUInt()

/** Intent actions carrying a deeplink URI: browsers and apps send VIEW, NFC tag taps send NDEF_DISCOVERED. */
internal val DEEPLINK_ACTIONS = setOf(Intent.ACTION_VIEW, NfcAdapter.ACTION_NDEF_DISCOVERED)
}
}

Expand Down
15 changes: 15 additions & 0 deletions app/src/test/java/to/bitkit/ui/MainActivityLaunchKeyTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package to.bitkit.ui
import android.content.Intent
import android.hardware.usb.UsbManager
import android.net.Uri
import android.nfc.NfcAdapter
import org.junit.Test
import org.mockito.kotlin.mock
import java.net.URLEncoder
Expand Down Expand Up @@ -60,6 +61,20 @@ class MainActivityLaunchKeyTest {
assertNull(intent.launchKey())
}

@Test
fun `launch key tracks NFC tag intents so they are consumed once`() {
val giftUrl = "bitkit://gift-abc123-3000"
val uri = mock<Uri> {
on { toString() }.thenReturn(giftUrl)
}
val intent = mock<Intent> {
on { action }.thenReturn(NfcAdapter.ACTION_NDEF_DISCOVERED)
on { data }.thenReturn(uri)
}

assertEquals(giftUrl, intent.launchKey())
}

@Test
fun `launch key tracks usb attach intents`() {
val intent = mock<Intent> {
Expand Down
21 changes: 19 additions & 2 deletions app/src/test/java/to/bitkit/viewmodels/AppViewModelSendFlowTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import android.content.ClipboardManager
import android.content.Context
import android.content.Intent
import android.net.Uri
import android.nfc.NfcAdapter
import androidx.core.net.toUri
import app.cash.turbine.test
import com.synonym.bitkitcore.LightningInvoice
Expand Down Expand Up @@ -497,6 +498,22 @@ class AppViewModelSendFlowTest : BaseUnitTest() {
verify(lightningRepo).setRecoveryMode(true)
}

@Test
fun `deeplink from NFC tag tap is processed`() = test {
sut.handleDeeplinkIntent(recoveryModeIntent(action = NfcAdapter.ACTION_NDEF_DISCOVERED))
advanceUntilIdle()

verify(lightningRepo).setRecoveryMode(true)
}

@Test
fun `intent without deeplink action is ignored`() = test {
sut.handleDeeplinkIntent(recoveryModeIntent(action = Intent.ACTION_MAIN))
advanceUntilIdle()

verify(lightningRepo, never()).setRecoveryMode(any())
}

@Test
fun `connectBTCPay hides sheet and shows success toast`() = test {
val setup = samRockSetupRequest()
Expand Down Expand Up @@ -1400,15 +1417,15 @@ class AppViewModelSendFlowTest : BaseUnitTest() {
return URLEncoder.encode(this, StandardCharsets.UTF_8.name()).replace("+", "%20")
}

private fun recoveryModeIntent(): Intent {
private fun recoveryModeIntent(action: String = Intent.ACTION_VIEW): Intent {
val uri = mock<Uri> {
on { toString() }.thenReturn("bitkit://recovery-mode")
on { scheme }.thenReturn("bitkit")
on { host }.thenReturn("recovery-mode")
on { pathSegments }.thenReturn(emptyList())
}
return mock {
on { action }.thenReturn(Intent.ACTION_VIEW)
on { this.action }.thenReturn(action)
on { data }.thenReturn(uri)
}
}
Expand Down
1 change: 1 addition & 0 deletions changelog.d/next/1054.fixed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Tapping an NFC tag now opens the gift claim and other deep links instead of being ignored, and the NFC intent filter no longer declares the unused BROWSABLE category that triggered a Play Console deep link warning.
Loading