Skip to content
Open
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
25 changes: 19 additions & 6 deletions app/src/main/java/com/electricdreams/numo/BalanceCheckActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -149,17 +149,23 @@ class BalanceCheckActivity : AppCompatActivity() {
try {
Log.d(TAG, "1. Creating Satocash client...")
satocashClient = SatocashNfcClient(tag)
val client = satocashClient
if (client == null) {
Log.e(TAG, "❌ Failed to create Satocash client")
handleBalanceCheckError(getString(R.string.balance_check_error_invalid_nfc_tag))
return@Thread
}

Log.d(TAG, "2. Connecting to NFC card...")
satocashClient!!.connect()
client.connect()
Log.d(TAG, "✅ Successfully connected to NFC card")

Log.d(TAG, "3. Selecting Satocash applet...")
satocashClient!!.selectApplet(SatocashNfcClient.SATOCASH_AID)
client.selectApplet(SatocashNfcClient.SATOCASH_AID)
Log.d(TAG, "✅ Satocash Applet found and selected!")

Log.d(TAG, "4. Initializing secure channel...")
satocashClient!!.initSecureChannel()
client.initSecureChannel()
Log.d(TAG, "✅ Secure Channel Initialized!")

Log.d(TAG, "5. Getting accurate card balance (no PIN authentication)...")
Expand Down Expand Up @@ -191,8 +197,15 @@ class BalanceCheckActivity : AppCompatActivity() {
private val cardBalance: Long
get() {
Log.d(TAG, "Getting card balance using getProofInfo (no PIN required)...")
val client = satocashClient
if (client == null) {
Log.e(TAG, "Satocash client is null when reading balance")
updateCardInfoDisplay(getString(R.string.balance_check_info_no_state_data))
return 0
}

return try {
val status = satocashClient!!.status
val status = client.status
Log.d(TAG, "Card status: $status")

val nbProofsUnspent = status.getOrDefault("nb_proofs_unspent", 0) as Int
Expand All @@ -207,7 +220,7 @@ class BalanceCheckActivity : AppCompatActivity() {

Log.d(TAG, "Total proofs in card: $totalProofs ($nbProofsUnspent unspent, $nbProofsSpent spent)")

val proofStates = satocashClient!!.getProofInfo(
val proofStates = client.getProofInfo(
SatocashNfcClient.Unit.SAT,
SatocashNfcClient.ProofInfoType.METADATA_STATE,
0,
Expand All @@ -222,7 +235,7 @@ class BalanceCheckActivity : AppCompatActivity() {
return 0
}

val amounts = satocashClient!!.getProofInfo(
val amounts = client.getProofInfo(
SatocashNfcClient.Unit.SAT,
SatocashNfcClient.ProofInfoType.METADATA_AMOUNT_EXPONENT,
0,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,8 +78,9 @@ class PaymentReceivedActivity : AppCompatActivity() {
amount = intent.getLongExtra(EXTRA_AMOUNT, 0)

// Parse token to extract amount and unit if not provided
if (amount == 0L && tokenString != null) {
parseToken(tokenString!!)
val token = tokenString
if (amount == 0L && token != null) {
parseToken(token)
}

// Set up UI
Expand Down Expand Up @@ -196,13 +197,14 @@ class PaymentReceivedActivity : AppCompatActivity() {
}

private fun shareToken() {
if (tokenString.isNullOrEmpty()) {
val token = tokenString
if (token.isNullOrEmpty()) {
Toast.makeText(this, R.string.payment_received_error_no_token, Toast.LENGTH_SHORT).show()
return
}

// Create intent to share/export the token
val cashuUri = "cashu:$tokenString"
val cashuUri = "cashu:$token"

// Create intent for viewing the URI
val uriIntent = Intent(Intent.ACTION_VIEW, android.net.Uri.parse(cashuUri)).apply {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -471,9 +471,12 @@ class PaymentRequestActivity : AppCompatActivity() {
}
}

if (isResumingPayment && resumeNostrSecretHex != null && resumeNostrNprofile != null) {
val secretHex = resumeNostrSecretHex
val nprofile = resumeNostrNprofile

if (isResumingPayment && secretHex != null && nprofile != null) {
// Resume with stored keys
handler.resume(paymentAmount, resumeNostrSecretHex!!, resumeNostrNprofile!!, callback)
handler.resume(paymentAmount, secretHex, nprofile, callback)
} else {
// Start fresh
handler.start(paymentAmount, pendingPaymentId, callback)
Expand All @@ -484,13 +487,17 @@ class PaymentRequestActivity : AppCompatActivity() {
lightningStarted = true

// Check if we're resuming with existing Lightning quote
if (resumeLightningQuoteId != null && resumeLightningMintUrl != null && resumeLightningInvoice != null) {
Log.d(TAG, "Resuming Lightning quote: id=$resumeLightningQuoteId")
val quoteId = resumeLightningQuoteId
val mintUrl = resumeLightningMintUrl
val invoice = resumeLightningInvoice

if (quoteId != null && mintUrl != null && invoice != null) {
Log.d(TAG, "Resuming Lightning quote: id=$quoteId")

lightningHandler?.resume(
quoteId = resumeLightningQuoteId!!,
mintUrlStr = resumeLightningMintUrl!!,
invoice = resumeLightningInvoice!!,
quoteId = quoteId,
mintUrlStr = mintUrl,
invoice = invoice,
callback = createLightningCallback()
)
} else {
Expand Down
64 changes: 51 additions & 13 deletions app/src/main/java/com/electricdreams/numo/TopUpActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -352,16 +352,35 @@ class TopUpActivity : AppCompatActivity() {
Log.d(TAG, "Connected to NFC card")

satocashClient = tempClient
satocashWallet = SatocashWallet(satocashClient!!)
val client = satocashClient
if (client == null) {
Log.e(TAG, "Satocash client is null after connection")
showStatusMessage(
getString(R.string.top_up_status_nfc_error, "Client init failed"),
success = false
)
return@Thread
}

satocashWallet = SatocashWallet(client)

satocashClient!!.selectApplet(SatocashNfcClient.SATOCASH_AID)
client.selectApplet(SatocashNfcClient.SATOCASH_AID)
Log.d(TAG, "Satocash Applet found and selected!")

satocashClient!!.initSecureChannel()
client.initSecureChannel()
Log.d(TAG, "Secure Channel Initialized!")

try {
val importedCount = satocashWallet!!.importProofsFromToken(token).join()
val wallet = satocashWallet
if (wallet == null) {
showStatusMessage(
getString(R.string.top_up_status_generic_error, "Wallet not initialized"),
success = false
)
return@Thread
}

val importedCount = wallet.importProofsFromToken(token).join()
showStatusMessage(
getString(R.string.top_up_status_success_imported, importedCount),
success = true
Expand All @@ -383,8 +402,8 @@ class TopUpActivity : AppCompatActivity() {
Log.d(TAG, "PIN authentication needed")

try {
satocashClient?.let { client ->
client.close()
satocashClient?.let { clientToClose ->
clientToClose.close()
Log.d(TAG, "NFC connection closed before PIN entry")
satocashClient = null
}
Expand Down Expand Up @@ -478,22 +497,41 @@ class TopUpActivity : AppCompatActivity() {
Log.d(TAG, "Connected to NFC card for PIN import")

satocashClient = tempClient
satocashWallet = SatocashWallet(satocashClient!!)
val client = satocashClient
if (client == null) {
Log.e(TAG, "Satocash client is null for PIN import")
showStatusMessage(
getString(R.string.top_up_status_nfc_error, "Client init failed"),
success = false
)
return@Thread
}

satocashClient!!.selectApplet(SatocashNfcClient.SATOCASH_AID)
satocashWallet = SatocashWallet(client)

client.selectApplet(SatocashNfcClient.SATOCASH_AID)
Log.d(TAG, "Satocash Applet found and selected!")

satocashClient!!.initSecureChannel()
client.initSecureChannel()
Log.d(TAG, "Secure Channel Initialized!")

Log.d(TAG, "Authenticating with saved PIN...")
val authenticated = satocashWallet!!.authenticatePIN(pin).join()
val wallet = satocashWallet
if (wallet == null) {
showStatusMessage(
getString(R.string.top_up_status_generic_error, "Wallet not initialized"),
success = false
)
return@Thread
}

val authenticated = wallet.authenticatePIN(pin).join()

if (authenticated) {
Log.d(TAG, "PIN Verified! Card Ready.")

val token = pendingProofToken ?: ""
val importedCount = satocashWallet!!.importProofsFromToken(token).join()
val importedCount = wallet.importProofsFromToken(token).join()

waitingForRescan = false
savedPin = null
Expand Down Expand Up @@ -540,8 +578,8 @@ class TopUpActivity : AppCompatActivity() {
showStatusMessage(message, success = false)
} finally {
try {
satocashClient?.let { client ->
client.close()
satocashClient?.let { clientToClose ->
clientToClose.close()
Log.d(TAG, "NFC connection closed.")
satocashClient = null
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,8 @@ class BitcoinPriceWorker private constructor(context: Context) {
}

fun start() {
if (scheduler != null && !scheduler!!.isShutdown) return
val currentScheduler = scheduler
if (currentScheduler != null && !currentScheduler.isShutdown) return

scheduler = Executors.newSingleThreadScheduledExecutor().also { exec ->
exec.scheduleAtFixedRate(
Expand Down Expand Up @@ -210,7 +211,9 @@ class BitcoinPriceWorker private constructor(context: Context) {
reader = BufferedReader(InputStreamReader(connection.inputStream))
val response = buildString {
var line: String?
while (reader!!.readLine().also { line = it } != null) {
while (true) {
line = reader?.readLine()
if (line == null) break
append(line)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -368,11 +368,9 @@ class TransactionDetailActivity : AppCompatActivity() {

private fun openBasketReceipt() {
// Use saved basket data if available, otherwise fall back to checkoutBasketJson
val basketJsonToUse = if (savedBasket != null) {
convertSavedBasketToCheckoutBasket(savedBasket!!).toJson()
} else {
checkoutBasketJson
}
val basketJsonToUse = savedBasket?.let { basket ->
convertSavedBasketToCheckoutBasket(basket).toJson()
} ?: checkoutBasketJson

val intent = Intent(this, BasketReceiptActivity::class.java).apply {
putExtra(BasketReceiptActivity.EXTRA_CHECKOUT_BASKET_JSON, basketJsonToUse)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -194,11 +194,16 @@ class CheckoutScannerActivity : AppCompatActivity() {

private fun updateBasketForCurrentItem() {
val item = currentItem ?: return
val itemId = item.id
if (itemId == null) {
Toast.makeText(this, "Item ID missing", Toast.LENGTH_SHORT).show()
return
}

if (currentQuantity <= 0) {
basketManager.removeItem(item.id!!)
basketManager.removeItem(itemId)
} else {
val updated = basketManager.updateItemQuantity(item.id!!, currentQuantity)
val updated = basketManager.updateItemQuantity(itemId, currentQuantity)
if (!updated) {
basketManager.addItem(item, currentQuantity)
}
Expand Down Expand Up @@ -380,8 +385,9 @@ class CheckoutScannerActivity : AppCompatActivity() {
updateQuantityDisplay()

// Load image
if (!item.imagePath.isNullOrEmpty()) {
val imageFile = File(item.imagePath!!)
val path = item.imagePath
if (!path.isNullOrEmpty()) {
val imageFile = File(path)
if (imageFile.exists()) {
val bitmap: Bitmap? = BitmapFactory.decodeFile(imageFile.absolutePath)
if (bitmap != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -298,12 +298,16 @@ class ItemEntryActivity : AppCompatActivity() {
dialog.dismiss()
}
dialogView.findViewById<Button>(R.id.dialog_confirm_button).setOnClickListener {
currentItem?.let { item ->
itemManager.removeItem(item.id!!)
val item = currentItem
val itemId = item?.id
if (item != null && itemId != null) {
itemManager.removeItem(itemId)
setResult(RESULT_OK)
dialog.dismiss()
finish()
} else {
Toast.makeText(this, "Unable to delete item: missing ID", Toast.LENGTH_SHORT).show()
}
dialog.dismiss()
finish()
}
dialog.show()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,8 +78,10 @@ class SelectionBasketAdapter(
}

removeButton.setOnClickListener {
val itemId = item.id!!
onItemRemoved(itemId)
val itemId = item.id
if (itemId != null) {
onItemRemoved(itemId)
}
}
}
}
Expand Down
Loading