From 0f0b4cdaeb2ebe297faa13f5ea1a502fbc279ef0 Mon Sep 17 00:00:00 2001 From: DaVinci9196 Date: Fri, 17 Jul 2026 09:38:00 +0800 Subject: [PATCH 1/3] Fix IAP payment card switching issue --- .../org/microg/vending/billing/core/ui/UIParser.kt | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/vending-app/src/main/java/org/microg/vending/billing/core/ui/UIParser.kt b/vending-app/src/main/java/org/microg/vending/billing/core/ui/UIParser.kt index 7521ae3dcb..9d5c2f4dad 100644 --- a/vending-app/src/main/java/org/microg/vending/billing/core/ui/UIParser.kt +++ b/vending-app/src/main/java/org/microg/vending/billing/core/ui/UIParser.kt @@ -98,7 +98,10 @@ private fun parseAction(action: Action?, result: BAction): Boolean { } action.viewClickAction?.let { viewClickAction -> if (viewClickAction.uiInfo != null) { - result.uiInfo = parseUIInfo(viewClickAction.uiInfo!!) + val parsedUiInfo = parseUIInfo(viewClickAction.uiInfo!!) + if (parsedUiInfo.uiType != UIType.UNKNOWN) { + result.uiInfo = parsedUiInfo + } } return parseAction(viewClickAction.action, result) } @@ -488,7 +491,8 @@ private fun parseViewInfo(viewInfo: ViewInfo): BViewInfo { private fun parseContentComponent(contentComponent: ContentComponent): BComponent { val tag = contentComponent.tag val viewInfo = contentComponent.viewInfo?.let { parseViewInfo(it) } - val uiInfo = contentComponent.uiInfo?.let { parseUIInfo(it) } + val uiInfo = contentComponent.uiInfo?.let { + parseUIInfo(it) } val cc = contentComponent return when { cc.iconTextCombinationView != null -> BComponent(tag, uiInfo, viewInfo, ViewType.ICONTEXTCOMBINATIONVIEW, iconTextCombinationView = parseIconTextCombinationView(cc.iconTextCombinationView!!)) @@ -528,7 +532,8 @@ private fun parseButtonGroupView(buttonGroupView: ButtonGroupView): BButtonGroup private fun parseFooterComponent(footerComponent: FooterComponent): BComponent { val tag = footerComponent.tag val viewInfo = footerComponent.viewInfo?.let { parseViewInfo(it) } - val uiInfo = footerComponent.uiInfo?.let { parseUIInfo(it) } + val uiInfo = footerComponent.uiInfo?.let { + parseUIInfo(it) } val fc = footerComponent return when { fc.buttonGroupView != null -> BComponent(tag, uiInfo, viewInfo, ViewType.BUTTONGROUPVIEW, buttonGroupView = parseButtonGroupView(fc.buttonGroupView!!)) From 099ca075366bb0335462b9a277567239b93481b1 Mon Sep 17 00:00:00 2001 From: Marvin W Date: Tue, 25 Aug 2026 17:22:25 +0200 Subject: [PATCH 2/3] Undo whitespace changes --- .../java/org/microg/vending/billing/core/ui/UIParser.kt | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/vending-app/src/main/java/org/microg/vending/billing/core/ui/UIParser.kt b/vending-app/src/main/java/org/microg/vending/billing/core/ui/UIParser.kt index 9d5c2f4dad..edd5076a80 100644 --- a/vending-app/src/main/java/org/microg/vending/billing/core/ui/UIParser.kt +++ b/vending-app/src/main/java/org/microg/vending/billing/core/ui/UIParser.kt @@ -491,8 +491,7 @@ private fun parseViewInfo(viewInfo: ViewInfo): BViewInfo { private fun parseContentComponent(contentComponent: ContentComponent): BComponent { val tag = contentComponent.tag val viewInfo = contentComponent.viewInfo?.let { parseViewInfo(it) } - val uiInfo = contentComponent.uiInfo?.let { - parseUIInfo(it) } + val uiInfo = contentComponent.uiInfo?.let { parseUIInfo(it) } val cc = contentComponent return when { cc.iconTextCombinationView != null -> BComponent(tag, uiInfo, viewInfo, ViewType.ICONTEXTCOMBINATIONVIEW, iconTextCombinationView = parseIconTextCombinationView(cc.iconTextCombinationView!!)) @@ -532,8 +531,7 @@ private fun parseButtonGroupView(buttonGroupView: ButtonGroupView): BButtonGroup private fun parseFooterComponent(footerComponent: FooterComponent): BComponent { val tag = footerComponent.tag val viewInfo = footerComponent.viewInfo?.let { parseViewInfo(it) } - val uiInfo = footerComponent.uiInfo?.let { - parseUIInfo(it) } + val uiInfo = footerComponent.uiInfo?.let { parseUIInfo(it) } val fc = footerComponent return when { fc.buttonGroupView != null -> BComponent(tag, uiInfo, viewInfo, ViewType.BUTTONGROUPVIEW, buttonGroupView = parseButtonGroupView(fc.buttonGroupView!!)) @@ -681,4 +679,4 @@ private fun extractPurchaseItemFromScreens( ) } return mapOf("RESPONSE_CODE" to 0, "DEBUG_MESSAGE" to "") to null -} \ No newline at end of file +} From 535a377de3b0ae246082e177fffa6367a46f19d8 Mon Sep 17 00:00:00 2001 From: DaVinci9196 Date: Fri, 18 Sep 2026 15:24:15 +0800 Subject: [PATCH 3/3] Fix: Payment flow exits unexpectedly when returning from the payment method selection screen. --- .../billing/ui/logic/BillingScreenHistory.kt | 37 +++++++++++++++++++ .../billing/ui/logic/InAppBillingViewModel.kt | 34 +++++++++++++++-- 2 files changed, 68 insertions(+), 3 deletions(-) create mode 100644 vending-app/src/main/java/org/microg/vending/billing/ui/logic/BillingScreenHistory.kt diff --git a/vending-app/src/main/java/org/microg/vending/billing/ui/logic/BillingScreenHistory.kt b/vending-app/src/main/java/org/microg/vending/billing/ui/logic/BillingScreenHistory.kt new file mode 100644 index 0000000000..d00c4f18f0 --- /dev/null +++ b/vending-app/src/main/java/org/microg/vending/billing/ui/logic/BillingScreenHistory.kt @@ -0,0 +1,37 @@ +/* + * SPDX-FileCopyrightText: 2026 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.microg.vending.billing.ui.logic + +internal class BillingScreenHistory { + private companion object { + const val MAX_SIZE = 32 + } + + private val screenIds = mutableListOf() + + fun recordTransition(fromScreenId: String?, toScreenId: String?): Boolean { + if (fromScreenId == null || toScreenId == null || fromScreenId == toScreenId) return false + + val targetIndex = screenIds.lastIndexOf(toScreenId) + if (targetIndex >= 0) { + screenIds.subList(targetIndex, screenIds.size).clear() + return true + } + + if (screenIds.lastOrNull() == fromScreenId) return false + screenIds.add(fromScreenId) + if (screenIds.size > MAX_SIZE) screenIds.removeAt(0) + return true + } + + fun popPrevious(currentScreenId: String?, isAvailable: (String) -> Boolean): String? { + while (screenIds.isNotEmpty()) { + val screenId = screenIds.removeAt(screenIds.lastIndex) + if (screenId != currentScreenId && isAvailable(screenId)) return screenId + } + return null + } +} diff --git a/vending-app/src/main/java/org/microg/vending/billing/ui/logic/InAppBillingViewModel.kt b/vending-app/src/main/java/org/microg/vending/billing/ui/logic/InAppBillingViewModel.kt index 063d011c98..07f20ed4bf 100644 --- a/vending-app/src/main/java/org/microg/vending/billing/ui/logic/InAppBillingViewModel.kt +++ b/vending-app/src/main/java/org/microg/vending/billing/ui/logic/InAppBillingViewModel.kt @@ -75,6 +75,8 @@ class InAppBillingViewModel : ViewModel() { ) ) private lateinit var lastBuyFlowResult: BuyFlowResult + private val screenHistory = BillingScreenHistory() + private var currentScreenId: String? = null private fun finishWithResult(result: Bundle) { viewModelScope.launch { @@ -268,14 +270,36 @@ class InAppBillingViewModel : ViewModel() { } } + UIType.BILLING_PROFILE_SCREEN_ABANDON -> { + if (showPreviousScreen()) return + finishWithResult(billingUiViewState.result) + } + else -> finishWithResult(billingUiViewState.result) } } } } - private fun showScreen(screenId: String): Boolean { + private fun showPreviousScreen(): Boolean { + val previousScreenId = screenHistory.popPrevious( + currentScreenId, + billingUiViewState.screenMap::containsKey + ) + return previousScreenId != null && showScreen( + previousScreenId, + recordHistory = false + ) + } + + private fun showScreen( + screenId: String, + recordHistory: Boolean = true + ): Boolean { + val previousScreenId = currentScreenId val showScreen = billingUiViewState.screenMap[screenId] ?: return false + if (recordHistory) screenHistory.recordTransition(previousScreenId, screenId) + currentScreenId = screenId billingUiViewState = billingUiViewState.copy( showScreen = showScreen, visible = true @@ -300,11 +324,15 @@ class InAppBillingViewModel : ViewModel() { } val action = buyFlowResult.acquireResult?.action ?: return failAction() val screenMap = buyFlowResult.acquireResult.screenMap - val showScreen = screenMap[action.screenId] ?: return failAction() + val targetScreenId = action.screenId ?: return failAction() + val showScreen = screenMap[targetScreenId] ?: return failAction() if (Log.isLoggable(TAG, Log.DEBUG)) Log.d(TAG, "handleAcquireResult, showScreen:$showScreen result:${buyFlowResult.acquireResult}") if (action.type != ActionType.SHOW) return failAction() lastBuyFlowResult = buyFlowResult + val previousScreenId = currentScreenId billingUiViewState.screenMap.putAll(screenMap) + screenHistory.recordTransition(previousScreenId, targetScreenId) + currentScreenId = targetScreenId billingUiViewState = billingUiViewState.copy( showScreen = showScreen, result = buyFlowResult.result, @@ -400,4 +428,4 @@ class InAppBillingViewModel : ViewModel() { resultBundle(BillingClient.BillingResponseCode.USER_CANCELED, "") finishWithResult(result) } -} \ No newline at end of file +}