diff --git a/app/release/baselineProfiles/0/app-release.dm b/app/release/baselineProfiles/0/app-release.dm deleted file mode 100644 index 604f8bec..00000000 Binary files a/app/release/baselineProfiles/0/app-release.dm and /dev/null differ diff --git a/app/release/baselineProfiles/1/app-release.dm b/app/release/baselineProfiles/1/app-release.dm deleted file mode 100644 index 57da9f4d..00000000 Binary files a/app/release/baselineProfiles/1/app-release.dm and /dev/null differ diff --git a/app/release/output-metadata.json b/app/release/output-metadata.json deleted file mode 100644 index 261e92cb..00000000 --- a/app/release/output-metadata.json +++ /dev/null @@ -1,37 +0,0 @@ -{ - "version": 3, - "artifactType": { - "type": "APK", - "kind": "Directory" - }, - "applicationId": "com.bitchat.android", - "variantName": "release", - "elements": [ - { - "type": "SINGLE", - "filters": [], - "attributes": [], - "versionCode": 3, - "versionName": "0.7", - "outputFile": "app-release.apk" - } - ], - "elementType": "File", - "baselineProfiles": [ - { - "minApi": 28, - "maxApi": 30, - "baselineProfiles": [ - "baselineProfiles/1/app-release.dm" - ] - }, - { - "minApi": 31, - "maxApi": 2147483647, - "baselineProfiles": [ - "baselineProfiles/0/app-release.dm" - ] - } - ], - "minSdkVersionForDexing": 26 -} \ No newline at end of file diff --git a/app/src/main/java/com/bitchat/android/wallet/service/CashuService.kt b/app/src/main/java/com/bitchat/android/wallet/service/CashuService.kt index c16ccd29..27d0b7b4 100644 --- a/app/src/main/java/com/bitchat/android/wallet/service/CashuService.kt +++ b/app/src/main/java/com/bitchat/android/wallet/service/CashuService.kt @@ -5,6 +5,7 @@ import kotlinx.coroutines.Dispatchers import android.util.Log import android.content.Context import com.bitchat.android.wallet.data.* +import com.bitchat.android.wallet.repository.WalletRepository import com.bitchat.android.parsing.CashuTokenParser import uniffi.cdk_ffi.* import java.math.BigDecimal @@ -21,6 +22,7 @@ class CashuService { private var currentUnit: String = "sat" private var isInitialized = false private var isCdkAvailable = false + private var repository: WalletRepository? = null companion object { private const val TAG = "CashuService" @@ -36,6 +38,108 @@ class CashuService { } } + /** + * Initialize the repository instance + */ + private fun initializeRepository() { + if (repository == null) { + val context = getApplicationContext() + repository = WalletRepository.getInstance(context) + } + } + + /** + * Check if a mint exists in the user's mint list + */ + private fun checkMintExists(mintUrl: String, mints: List): Boolean { + return mints.any { it.url == mintUrl } + } + + /** + * Add a mint if authorized (autoAdd = true) + */ + private suspend fun addMintIfAuthorized(mintUrl: String, autoAdd: Boolean): Result { + return withContext(Dispatchers.IO) { + try { + if (!autoAdd) { + Log.d(TAG, "Auto-add not authorized for mint: $mintUrl") + return@withContext Result.success(false) + } + + initializeRepository() + val repo = repository ?: return@withContext Result.failure(Exception("Repository not available")) + + Log.d(TAG, "Adding mint automatically: $mintUrl") + + val mintInfoResult = getMintInfo(mintUrl) + if (mintInfoResult.isFailure) { + val error = mintInfoResult.exceptionOrNull() ?: Exception("Unknown error") + Log.e(TAG, "Failed to get mint info for: $mintUrl", error) + return@withContext Result.failure(error) + } + + val mintInfo = mintInfoResult.getOrThrow() + val mint = Mint( + url = mintUrl, + nickname = mintInfo.name.ifEmpty { "Auto-added Mint" }, + info = mintInfo, + keysets = emptyList(), // Will be populated by CDK + active = true, + dateAdded = Date() + ) + + val saveMintResult = repo.saveMint(mint) + if (saveMintResult.isFailure) { + val error = saveMintResult.exceptionOrNull() ?: Exception("Unknown error") + Log.e(TAG, "Failed to save mint: $mintUrl", error) + return@withContext Result.failure(error) + } + + Log.d(TAG, "Successfully added mint: $mintUrl") + return@withContext Result.success(true) + + } catch (e: Exception) { + Log.e(TAG, "Exception while adding mint: $mintUrl", e) + return@withContext Result.failure(e) + } + } + } + + /** + * Set the active mint and initialize wallet with it + */ + private suspend fun setActiveMintAndInitialize(mintUrl: String): Result { + return withContext(Dispatchers.IO) { + try { + initializeRepository() + val repo = repository ?: return@withContext Result.failure(Exception("Repository not available")) + + // Set active mint in repository + val setActiveMintResult = repo.setActiveMint(mintUrl) + if (setActiveMintResult.isFailure) { + val error = setActiveMintResult.exceptionOrNull() ?: Exception("Unknown error") + Log.e(TAG, "Failed to set active mint: $mintUrl", error) + return@withContext Result.failure(error) + } + + // Initialize wallet with the mint + val initWalletResult = initializeWallet(mintUrl) + if (initWalletResult.isFailure) { + val error = initWalletResult.exceptionOrNull() ?: Exception("Unknown error") + Log.e(TAG, "Failed to initialize wallet with mint: $mintUrl", error) + return@withContext Result.failure(error) + } + + Log.d(TAG, "Set active mint and initialized wallet: $mintUrl") + return@withContext Result.success(Unit) + + } catch (e: Exception) { + Log.e(TAG, "Exception while setting active mint: $mintUrl", e) + return@withContext Result.failure(e) + } + } + } + /** * Check if CDK is available and initialize appropriately */ @@ -263,31 +367,73 @@ class CashuService { } /** - * Receive a Cashu token - * Note: CDK doesn't have explicit "receive" - tokens are auto-received when sent - * This function decodes and validates the token + * Receive a Cashu token with mint management + * @param token The Cashu token to receive + * @param autoAdd Whether to automatically add unknown mints + * @param currentMints The current list of user's mints + * @param decodedToken Optional pre-decoded token to avoid re-decoding */ - suspend fun receiveToken(token: String): Result { + suspend fun receiveToken( + token: String, + autoAdd: Boolean = true, + currentMints: List = emptyList(), + decodedToken: CashuToken? = null + ): Result { return withContext(Dispatchers.IO) { try { - if (!isInitialized) { - initializeWallet(DEFAULT_MINT_URL).getOrThrow() + // Decode token if not provided + val tokenData = decodedToken ?: decodeToken(token).getOrThrow() + val mintUrl = tokenData.mint + val amount = tokenData.amount.toLong() + + Log.d(TAG, "Processing token: amount=$amount from mint=$mintUrl") + + // Check if mint exists + val mintExists = checkMintExists(mintUrl, currentMints) + + if (!mintExists) { + Log.d(TAG, "Mint $mintUrl not found, attempting to add...") + + // Try to add the mint if authorized + addMintIfAuthorized(mintUrl, autoAdd).fold( + onSuccess = { added -> + if (!added) { + return@withContext Result.failure( + Exception("Mint $mintUrl not found and auto-add not authorized") + ) + } + }, + onFailure = { error -> + Log.e(TAG, "Failed to add mint $mintUrl", error) + return@withContext Result.failure( + Exception("Failed to add mint: $mintUrl - ${error.message}") + ) + } + ) } + // Set active mint and initialize wallet with it + setActiveMintAndInitialize(mintUrl).fold( + onSuccess = { + Log.d(TAG, "Successfully set active mint: $mintUrl") + }, + onFailure = { error -> + Log.e(TAG, "Failed to set active mint: $mintUrl", error) + return@withContext Result.failure( + Exception("Failed to set active mint: $mintUrl - ${error.message}") + ) + } + ) + + // Now receive the token with the properly initialized wallet if (!isCdkAvailable || wallet == null) { return@withContext Result.failure(Exception("CDK not available")) } - // Decode the token to get amount - val decodedToken = decodeToken(token).getOrThrow() - val amount = decodedToken.amount.toLong() - - Log.d(TAG, "Token validation successful, amount: $amount") - + Log.d(TAG, "Receiving token with amount: $amount") val ffiAmount = wallet!!.receive(token) - - Log.d(TAG, "FFI amount: ${ffiAmount}") - + + Log.d(TAG, "Successfully received token: ${ffiAmount.value}") Result.success(ffiAmount.value.toLong()) } catch (e: FfiException) { diff --git a/app/src/main/java/com/bitchat/android/wallet/viewmodel/WalletViewModel.kt b/app/src/main/java/com/bitchat/android/wallet/viewmodel/WalletViewModel.kt index 82fc2eec..cd870581 100644 --- a/app/src/main/java/com/bitchat/android/wallet/viewmodel/WalletViewModel.kt +++ b/app/src/main/java/com/bitchat/android/wallet/viewmodel/WalletViewModel.kt @@ -599,7 +599,30 @@ class WalletViewModel(application: Application) : AndroidViewModel(application) viewModelScope.launch { try { _isLoading.value = true - cashuService.receiveToken(token).onSuccess { amount -> + + // Get decoded token and current mints + val decodedToken = _decodedToken.value + if (decodedToken == null) { + val failureData = FailureAnimationData( + errorMessage = "Failed to decode token", + operationType = "Token Receive" + ) + showFailureAnimation(failureData) + clearTokenInput() + delay(500) + hideReceiveDialog() + return@launch + } + + val currentMints = _mints.value ?: emptyList() + + // Use the new CashuService.receiveToken with mint management + cashuService.receiveToken( + token = token, + autoAdd = true, // Enable automatic mint addition + currentMints = currentMints, + decodedToken = decodedToken + ).onSuccess { amount -> // Add transaction val transaction = WalletTransaction( @@ -615,6 +638,10 @@ class WalletViewModel(application: Application) : AndroidViewModel(application) repository.saveTransaction(transaction).onSuccess { loadTransactions() refreshBalance() + // Reload mints in case a new one was added + repository.getMints().onSuccess { mintList -> + _mints.value = mintList + } // Clear token input immediately clearTokenInput() @@ -836,6 +863,8 @@ class WalletViewModel(application: Application) : AndroidViewModel(application) } } + + /** * Set the active mint */