diff --git a/app/src/main/java/com/electricdreams/numo/BalanceCheckActivity.kt b/app/src/main/java/com/electricdreams/numo/BalanceCheckActivity.kt index 91f40597..61e4356e 100644 --- a/app/src/main/java/com/electricdreams/numo/BalanceCheckActivity.kt +++ b/app/src/main/java/com/electricdreams/numo/BalanceCheckActivity.kt @@ -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)...") @@ -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 @@ -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, @@ -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, diff --git a/app/src/main/java/com/electricdreams/numo/PaymentReceivedActivity.kt b/app/src/main/java/com/electricdreams/numo/PaymentReceivedActivity.kt index 7d2c91f8..c1b3d70a 100644 --- a/app/src/main/java/com/electricdreams/numo/PaymentReceivedActivity.kt +++ b/app/src/main/java/com/electricdreams/numo/PaymentReceivedActivity.kt @@ -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 @@ -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 { diff --git a/app/src/main/java/com/electricdreams/numo/PaymentRequestActivity.kt b/app/src/main/java/com/electricdreams/numo/PaymentRequestActivity.kt index f7bacb71..30a915ff 100644 --- a/app/src/main/java/com/electricdreams/numo/PaymentRequestActivity.kt +++ b/app/src/main/java/com/electricdreams/numo/PaymentRequestActivity.kt @@ -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) @@ -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 { diff --git a/app/src/main/java/com/electricdreams/numo/TopUpActivity.kt b/app/src/main/java/com/electricdreams/numo/TopUpActivity.kt index 8e09fd5c..b0d349c2 100644 --- a/app/src/main/java/com/electricdreams/numo/TopUpActivity.kt +++ b/app/src/main/java/com/electricdreams/numo/TopUpActivity.kt @@ -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 @@ -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 } @@ -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 @@ -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 } diff --git a/app/src/main/java/com/electricdreams/numo/core/worker/BitcoinPriceWorker.kt b/app/src/main/java/com/electricdreams/numo/core/worker/BitcoinPriceWorker.kt index 76315751..2f5b2c88 100644 --- a/app/src/main/java/com/electricdreams/numo/core/worker/BitcoinPriceWorker.kt +++ b/app/src/main/java/com/electricdreams/numo/core/worker/BitcoinPriceWorker.kt @@ -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( @@ -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) } } diff --git a/app/src/main/java/com/electricdreams/numo/feature/history/TransactionDetailActivity.kt b/app/src/main/java/com/electricdreams/numo/feature/history/TransactionDetailActivity.kt index 6270dd36..1d788324 100644 --- a/app/src/main/java/com/electricdreams/numo/feature/history/TransactionDetailActivity.kt +++ b/app/src/main/java/com/electricdreams/numo/feature/history/TransactionDetailActivity.kt @@ -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) diff --git a/app/src/main/java/com/electricdreams/numo/feature/items/CheckoutScannerActivity.kt b/app/src/main/java/com/electricdreams/numo/feature/items/CheckoutScannerActivity.kt index 1b1eb265..6e7d15bc 100644 --- a/app/src/main/java/com/electricdreams/numo/feature/items/CheckoutScannerActivity.kt +++ b/app/src/main/java/com/electricdreams/numo/feature/items/CheckoutScannerActivity.kt @@ -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) } @@ -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) { diff --git a/app/src/main/java/com/electricdreams/numo/feature/items/ItemEntryActivity.kt b/app/src/main/java/com/electricdreams/numo/feature/items/ItemEntryActivity.kt index db30a6a4..fc6d29e6 100644 --- a/app/src/main/java/com/electricdreams/numo/feature/items/ItemEntryActivity.kt +++ b/app/src/main/java/com/electricdreams/numo/feature/items/ItemEntryActivity.kt @@ -298,12 +298,16 @@ class ItemEntryActivity : AppCompatActivity() { dialog.dismiss() } dialogView.findViewById