Skip to content

Commit c543463

Browse files
committed
Apollo: Release source code for 55.6 (1506)
1 parent 59f9826 commit c543463

File tree

5 files changed

+59
-16
lines changed

5 files changed

+59
-16
lines changed

android/CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,13 @@ follow [https://changelog.md/](https://changelog.md/) guidelines.
66

77
## [Unreleased]
88

9+
## [55.6] - 2026-02-18
10+
11+
### FIXED
12+
13+
- Crash due to IntentSanitizer misconfiguration in Android > 10
14+
- Errors in NewOperationActivity onCreate crashing the app
15+
916
## [55.5] - 2026-02-12
1017

1118
### FIXED

android/apolloui/build.gradle

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -101,8 +101,8 @@ android {
101101
applicationId "io.muun.apollo"
102102
minSdk 21
103103
targetSdk 35
104-
versionCode 1505
105-
versionName "55.5"
104+
versionCode 1506
105+
versionName "55.6"
106106

107107
// Use default Proguard file, bundled with Android Gradle Plugin
108108
// See: https://issuetracker.google.com/issues/126772206
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package io.muun.apollo.presentation.ui.new_operation
2+
3+
import io.muun.apollo.domain.errors.MuunError
4+
5+
class InvalidOperationUriException(message: String) : MuunError(message)

android/apolloui/src/main/java/io/muun/apollo/presentation/ui/new_operation/NewOperationActivity.kt

Lines changed: 36 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ import io.muun.apollo.presentation.ui.view.TextInputWithBackHandling
6464
import newop.EnterAmountState
6565
import newop.EnterDescriptionState
6666
import newop.PaymentIntent
67+
import timber.log.Timber
6768
import javax.inject.Inject
6869
import javax.money.MonetaryAmount
6970

@@ -225,36 +226,52 @@ class NewOperationActivity : SingleFragmentActivity<NewOperationPresenter>(),
225226
override fun onCreate(savedInstanceState: Bundle?) {
226227
super.onCreate(savedInstanceState)
227228

229+
// TODO: add timber.i() for intent.getAction() and intent.getData().getScheme() iff not null
230+
228231
if (intent.flags and Intent.FLAG_ACTIVITY_CLEAR_TOP == 0) {
229232
// HACK ALERT
230233
// This Activity was not launched with the CLEAR_TOP flag, because it started from
231234
// an <intent-filter> defined in the manifest. Flags cannot be specified for these
232235
// Intents (pff), and we really need CLEAR_TOP, so... well, this:
233236
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
234237

238+
// We log penalties but don't throw and exception for it because it could happen that
239+
// there are non supported args but the uri can still be usable
240+
val penalties = mutableListOf<String>()
241+
235242
// Sanitize the intent before restarting to prevent UnsafeIntentLaunch
236243
val sanitizedIntent = IntentSanitizer.Builder()
237244
.allowComponent(ComponentName(this, NewOperationActivity::class.java))
238-
.allowData { uri -> uri.scheme in listOf("bitcoin", "lightning", "lnurl") }
245+
.allowData { uri -> uri.scheme in listOf("bitcoin", "lightning", "muun") }
239246
.allowExtra(OPERATION_URI, String::class.java)
240247
.allowExtra(ORIGIN, String::class.java)
241248
.allowFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
242249
.build()
243-
.sanitizeByFiltering(intent)
250+
.sanitize(intent) { error ->
251+
penalties.add(error)
252+
}
253+
254+
if (penalties.isNotEmpty()) {
255+
Timber.e("Invalid NewOp Intent uri: %s", penalties.toString())
256+
257+
// Add 1 breadcrumb per penalty, strings are long and will be cutoff otherwise
258+
penalties.forEach { penalty ->
259+
Timber.i("NewOp Intent Penalty: %s", penalty)
260+
}
261+
}
244262

245263
startActivity(sanitizedIntent)
246264
finishActivity()
265+
return
247266
}
248267

249268
val newOpOrigin = getOrigin(intent)
250-
val uri = getValidIntentUri(intent)
251-
val operationUri = try {
252-
OperationUri.fromString(uri)
253-
} catch (e: Exception) {
254-
null
255-
}
269+
Timber.i("NewOp Origin: %s", newOpOrigin)
256270

257-
if (operationUri != null) {
271+
try {
272+
val uri = getValidIntentUri(intent)
273+
274+
val operationUri: OperationUri = OperationUri.fromString(uri)
258275
when {
259276
operationUri.isLn ->
260277
presenter.startForInvoice(operationUri.lnInvoice.get(), newOpOrigin)
@@ -269,9 +286,12 @@ class NewOperationActivity : SingleFragmentActivity<NewOperationPresenter>(),
269286
presenter.startForBitcoinUri(uri, operationUri, newOpOrigin)
270287
}
271288

272-
} else {
289+
} catch (e: Exception) {
290+
291+
Timber.e(e)
292+
273293
showTextToast(getString(R.string.error_no_valid_payment_details_provided))
274-
finishActivity()
294+
presenter.handleError(e, newOpOrigin)
275295
}
276296
}
277297

@@ -328,9 +348,11 @@ class NewOperationActivity : SingleFragmentActivity<NewOperationPresenter>(),
328348
}
329349

330350
private fun getValidIntentUri(intent: Intent): String {
331-
return intent.getStringExtra(OPERATION_URI) // from startActivity
332-
?: intent.dataString // deeplink
333-
?: throw IllegalStateException("Invalid New Op Intent") // should not happen
351+
return intent.getStringExtra(OPERATION_URI) // from startActivity
352+
?: intent.dataString // deeplink
353+
?: throw InvalidOperationUriException(
354+
"Invalid NewOp Intent uri"
355+
) // uri is not valid for our scheme
334356
}
335357

336358
override fun setLoading(isLoading: Boolean) {

android/apolloui/src/main/java/io/muun/apollo/presentation/ui/new_operation/NewOperationPresenter.kt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,15 @@ class NewOperationPresenter @Inject constructor(
282282
.filter { it is BalanceErrorState }
283283
.map { it as BalanceErrorState }
284284

285+
// Special Error handling for error PRIOR to handleStart() (e.g in Activity#onCreate)
286+
// TODO: this should probably be handled better
287+
fun handleError(error: Throwable, newOpOrigin: NewOperationOrigin) {
288+
if (!::origin.isInitialized) {
289+
origin = newOpOrigin
290+
}
291+
handleError(error)
292+
}
293+
285294
override fun handleError(error: Throwable) {
286295
confirmationInProgress = false
287296
view.setLoading(false)

0 commit comments

Comments
 (0)