Skip to content

fix: UI crashes#1472

Open
HashEngineering wants to merge 8 commits intomasterfrom
fix/ui-crashes
Open

fix: UI crashes#1472
HashEngineering wants to merge 8 commits intomasterfrom
fix/ui-crashes

Conversation

@HashEngineering
Copy link
Collaborator

@HashEngineering HashEngineering commented Mar 11, 2026

Issue being fixed or feature implemented

Related PR's and Dependencies

Screenshots / Videos

How Has This Been Tested?

  • QA (Mobile Team)

Checklist:

  • I have performed a self-review of my own code and added comments where necessary
  • I have added or updated relevant unit/integration/functional/e2e tests

Summary by CodeRabbit

  • Bug Fixes

    • Amount input now normalizes and rejects entries with no numeric characters or leading minus; fiat input also guarded against negative values.
    • Prevented layout work during activity teardown to avoid UI issues at shutdown.
    • Improved Send/Receive shortcuts to prevent duplicate screens and stabilize navigation.
  • New Features

    • Added a close button to the scan screen to exit scanning quickly.
  • Localization

    • Updated masternode-related labels (capitalization and wording).
  • Improvements

    • Contacts refresh now reacts to identity updates for more timely transaction contact info.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 11, 2026

📝 Walkthrough

Walkthrough

Normalizes AmountView input to guard non-digit and negative values; adds lifecycle guards in LockScreenActivity; makes SEND/RECEIVE shortcut navigation single-top without popping wallet; introduces flow-backed blockchain identity and updateIdentity(), hooks identity updates into sync and MainViewModel; adds scan close button and handler; removes a platform init call; updates masternode strings.

Changes

Cohort / File(s) Summary
Input Validation
common/src/main/java/org/dash/wallet/common/ui/enter_amount/AmountView.kt
Input setter now treats values with no digits or leading minus as "0" and applies fiat negative-value guard when dash-to-fiat is inactive; normalization logic expanded.
Lifecycle Guards
wallet/src/de/schildbach/wallet/ui/LockScreenActivity.kt
Added early-return guards in two setContentView overrides to skip work if the activity is finishing.
Navigation Flow Control
wallet/src/de/schildbach/wallet/ui/main/WalletFragment.kt
SEND/RECEIVE shortcut navigation now uses NavOptions with setLaunchSingleTop(true) and setPopUpTo(R.id.walletFragment, false) when navigating to paymentsFragment.
Identity (flows & sync)
wallet/src/de/schildbach/wallet/service/platform/IdentityRepository.kt, wallet/src/de/schildbach/wallet/service/platform/PlatformSyncService.kt
Replaced scalar identity with MutableStateFlow/StateFlow (blockchainIdentityFlow) and added updateIdentity() API; PlatformSyncService now calls identityRepository.updateIdentity() on initial sync path.
ViewModel subscription
wallet/src/de/schildbach/wallet/ui/main/MainViewModel.kt
Subscribes to blockchainIdentityFlow (non-null, distinct) to refresh transaction contacts; avoids force-unwrap by using a local nullable identity for contact lookups.
Scan UI + handler
wallet/res/layout/scan_activity.xml, wallet/src/de/schildbach/wallet/ui/scan/ScanActivity.java
Added ImageButton @+id/scan_close_button to layout and wired a click listener that calls onBackPressed() to close the activity.
Platform init change
wallet/src/de/schildbach/wallet/WalletApplication.java
Removed/commented out PlatformRepo.getInstance().initGlobal() from platform initialization flow.
Strings
wallet/res/values/strings-extra.xml
Updated Masternode-related string text (capitalization and label changes, e.g., "EvoNode operator keys" → "HPMN operator keys").

Sequence Diagram(s)

mermaid
sequenceDiagram
participant User as "User"
participant WalletFrag as "WalletFragment"
participant NavCtrl as "NavController"
participant Payments as "PaymentsFragment"

User->>WalletFrag: tap SEND / RECEIVE shortcut
WalletFrag->>NavCtrl: navigate(to: paymentsFragment, bundle, navOptions{launchSingleTop=true, popUpTo(walletFragment,false)})
NavCtrl->>Payments: deliver navigation + bundle
Payments-->>User: show payments UI

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐇 I hopped through code with careful paws,
I tamed the inputs and fixed the flaws,
Closed screens politely as they fade,
Kept nav neat—single-top parade,
Hooray for flows and a tidy cause! 🥕

🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The PR title 'fix: UI crashes' is vague and overly broad. While the changeset includes several bug fixes, the title does not specify which UI crashes are being addressed or which components are involved. Consider a more specific title that identifies the primary fix, such as 'fix: prevent UI crashes in platform identity loading' or 'fix: add safety guards in scan and lock screen activities'.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/ui-crashes
📝 Coding Plan
  • Generate coding plan for human review comments

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
wallet/src/de/schildbach/wallet/ui/main/WalletFragment.kt (1)

427-451: ⚠️ Potential issue | 🟠 Major

Tab switching will not work when PaymentsFragment is reused via launchSingleTop.

The launchSingleTop(true) option correctly prevents back stack overload, but it introduces a functional regression: when PaymentsFragment is already on screen and the user taps a different shortcut (e.g., SEND then RECEIVE), the ARG_ACTIVE_TAB argument will be ignored.

PaymentsFragment.activateTab() (lines 126-136) reads the tab argument only in onViewCreated(), which does not execute when the fragment instance is reused. The fragment has no mechanism to detect and apply argument changes on reuse—there is no onResume() override, savedStateHandle observation, or currentBackStackEntry listener.

Consider one of these approaches:

  1. Observe findNavController().currentBackStackEntry?.savedStateHandle in PaymentsFragment to detect tab changes
  2. Use a shared ViewModel to communicate the desired tab
  3. Remove launchSingleTop if tab switching is a priority (accepting some back stack growth)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@wallet/src/de/schildbach/wallet/ui/main/WalletFragment.kt` around lines 427 -
451, When reusing PaymentsFragment with launchSingleTop the fragment instance
doesn't re-run onViewCreated so ARG_ACTIVE_TAB passed in the navigate bundle is
ignored; update PaymentsFragment to observe navigation state and react to tab
changes (e.g. in onViewCreated subscribe to
findNavController().currentBackStackEntry?.savedStateHandle or the fragment's
savedStateHandle for PaymentsFragment.ARG_ACTIVE_TAB) and call
PaymentsFragment.activateTab() when that value changes, or alternatively stop
using NavOptions.Builder().setLaunchSingleTop(true) from the shortcut handlers
if you prefer to force a new fragment instance; reference
PaymentsFragment.activateTab(), ARG_ACTIVE_TAB and the
findNavController().currentBackStackEntry?.savedStateHandle API to implement the
observer and apply the tab switch on reuse.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@wallet/src/de/schildbach/wallet/ui/main/WalletFragment.kt`:
- Around line 427-451: When reusing PaymentsFragment with launchSingleTop the
fragment instance doesn't re-run onViewCreated so ARG_ACTIVE_TAB passed in the
navigate bundle is ignored; update PaymentsFragment to observe navigation state
and react to tab changes (e.g. in onViewCreated subscribe to
findNavController().currentBackStackEntry?.savedStateHandle or the fragment's
savedStateHandle for PaymentsFragment.ARG_ACTIVE_TAB) and call
PaymentsFragment.activateTab() when that value changes, or alternatively stop
using NavOptions.Builder().setLaunchSingleTop(true) from the shortcut handlers
if you prefer to force a new fragment instance; reference
PaymentsFragment.activateTab(), ARG_ACTIVE_TAB and the
findNavController().currentBackStackEntry?.savedStateHandle API to implement the
observer and apply the tab switch on reuse.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 18f6db1b-4eb6-44e1-bb99-d55892fe97d2

📥 Commits

Reviewing files that changed from the base of the PR and between 1a438df and 4e4b7a6.

📒 Files selected for processing (3)
  • common/src/main/java/org/dash/wallet/common/ui/enter_amount/AmountView.kt
  • wallet/src/de/schildbach/wallet/ui/LockScreenActivity.kt
  • wallet/src/de/schildbach/wallet/ui/main/WalletFragment.kt

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
common/src/main/java/org/dash/wallet/common/ui/enter_amount/AmountView.kt (1)

60-65: Silent conversion of empty/invalid input to "0" may mask caller bugs.

The defensive check is appropriate for preventing crashes from negative values. However, the condition value.none { it.isDigit() } will also convert empty strings to "0". As noted in ConfirmTransactionDialog.kt (line 165), callers use fallback patterns like getString(ARG_AMOUNT) ?: "" — this will now silently become "0" rather than propagating as-is, potentially masking cases where a required amount argument is missing.

Consider whether logging or explicitly handling the empty-string case separately would aid debugging:

💡 Suggested refinement
 var input: String
     get() = _input
     set(value) {
-        _input = if (value.none { it.isDigit() } || value.trimStart().startsWith("-")) "0" else value
+        _input = when {
+            value.trimStart().startsWith("-") -> "0"
+            value.none { it.isDigit() } -> {
+                if (value.isNotEmpty()) {
+                    Log.w(TAG, "AmountView input has no digits: '$value', defaulting to 0")
+                }
+                "0"
+            }
+            else -> value
+        }
         updateAmount()
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@common/src/main/java/org/dash/wallet/common/ui/enter_amount/AmountView.kt`
around lines 60 - 65, The input setter in AmountView (the var input { get/set })
currently replaces any value with no digits (including "") with "0", which can
silently mask missing-argument bugs; change the setter to treat empty string
separately: if value.isEmpty() then preserve it (set _input = value) or emit a
debug/log warning before preserving, while still converting only truly
invalid/negative inputs (e.g., value.none { it.isDigit() } &&
value.isNotEmpty(), or value.trimStart().startsWith("-")) to "0"; keep calling
updateAmount() after setting _input so UI updates; refer to the input setter,
_input field, and updateAmount() when making the change.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@wallet/src/de/schildbach/wallet/service/platform/IdentityRepository.kt`:
- Line 109: updateIdentity() mutates the existing _blockchainIdentity instance
in place so blockchainIdentityFlow observers using distinctUntilChanged() don't
see a change; after calling _blockchainIdentity?.updateIdentity() (and any
in-place mutation), reassign the flow with a new object to force emission (e.g.
create a new BlockchainIdentity instance or call a copy/clone and set the
backing MutableStateFlow/_blockchainIdentityFlow.value to that new instance).
Update the code in IdentityRepository around updateIdentity(),
_blockchainIdentity and blockchainIdentityFlow so the flow is set to a new
instance after mutation so MainViewModel (distinctUntilChanged()) will detect
the change and trigger refreshContactsForAllTransactions().

---

Nitpick comments:
In `@common/src/main/java/org/dash/wallet/common/ui/enter_amount/AmountView.kt`:
- Around line 60-65: The input setter in AmountView (the var input { get/set })
currently replaces any value with no digits (including "") with "0", which can
silently mask missing-argument bugs; change the setter to treat empty string
separately: if value.isEmpty() then preserve it (set _input = value) or emit a
debug/log warning before preserving, while still converting only truly
invalid/negative inputs (e.g., value.none { it.isDigit() } &&
value.isNotEmpty(), or value.trimStart().startsWith("-")) to "0"; keep calling
updateAmount() after setting _input so UI updates; refer to the input setter,
_input field, and updateAmount() when making the change.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: b15c442f-4e6b-4e95-825e-947900dc60fd

📥 Commits

Reviewing files that changed from the base of the PR and between 2a6b899 and 71bceed.

📒 Files selected for processing (7)
  • common/src/main/java/org/dash/wallet/common/ui/enter_amount/AmountView.kt
  • wallet/res/layout/scan_activity.xml
  • wallet/src/de/schildbach/wallet/WalletApplication.java
  • wallet/src/de/schildbach/wallet/service/platform/IdentityRepository.kt
  • wallet/src/de/schildbach/wallet/service/platform/PlatformSyncService.kt
  • wallet/src/de/schildbach/wallet/ui/main/MainViewModel.kt
  • wallet/src/de/schildbach/wallet/ui/scan/ScanActivity.java
💤 Files with no reviewable changes (1)
  • wallet/src/de/schildbach/wallet/WalletApplication.java

suspend fun shouldShowAlert(): Boolean
fun getNextContactAddress(userId: String, accountReference: Int): Address?
suspend fun clearDatabase(includeInvitations: Boolean)
fun updateIdentity()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify that updateIdentity() mutates identity without publishing a guaranteed new flow emission.
# Expected finding:
# - updateIdentity() does not re-publish a semantically new flow value
# - MainViewModel relies on blockchainIdentityFlow emissions for refresh

rg -n -C3 --type=kt 'override fun updateIdentity\(\)|_blockchainIdentityFlow\.value\s*=' wallet/src/de/schildbach/wallet/service/platform/IdentityRepository.kt
rg -n -C3 --type=kt 'blockchainIdentityFlow|distinctUntilChanged\(\)' wallet/src/de/schildbach/wallet/ui/main/MainViewModel.kt

Repository: dashpay/dash-wallet

Length of output: 116


🏁 Script executed:

#!/bin/bash
# Find and read the IdentityRepository.kt file
fd "IdentityRepository.kt" wallet/src

Repository: dashpay/dash-wallet

Length of output: 133


🏁 Script executed:

#!/bin/bash
# Read the IdentityRepository.kt file to understand updateIdentity() and blockchainIdentityFlow
cat -n wallet/src/de/schildbach/wallet/service/platform/IdentityRepository.kt

Repository: dashpay/dash-wallet

Length of output: 48829


🏁 Script executed:

#!/bin/bash
# Search for updateIdentity and blockchainIdentityFlow in IdentityRepository
rg -n -C5 'updateIdentity|blockchainIdentityFlow' wallet/src/de/schildbach/wallet/service/platform/IdentityRepository.kt

Repository: dashpay/dash-wallet

Length of output: 3668


🏁 Script executed:

#!/bin/bash
# Check MainViewModel.kt for blockchainIdentityFlow and distinctUntilChanged usage
rg -n -C5 'blockchainIdentityFlow|distinctUntilChanged' wallet/src/de/schildbach/wallet/ui/main/MainViewModel.kt

Repository: dashpay/dash-wallet

Length of output: 3419


updateIdentity() mutates identity without notifying blockchainIdentityFlow observers.

At lines 827-829, updateIdentity() calls _blockchainIdentity?.updateIdentity(), which mutates the existing BlockchainIdentity instance in place. Because the flow reference remains unchanged, MainViewModel's subscription (line 339-343) using distinctUntilChanged() will not detect the update, causing refreshContactsForAllTransactions() to be skipped.

Force a new emission by reassigning the flow value:

Fix pattern
 override fun updateIdentity() {
-    _blockchainIdentity?.updateIdentity()
+    _blockchainIdentity?.let { identity ->
+        identity.updateIdentity()
+        _blockchainIdentityFlow.value = identity
+    }
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@wallet/src/de/schildbach/wallet/service/platform/IdentityRepository.kt` at
line 109, updateIdentity() mutates the existing _blockchainIdentity instance in
place so blockchainIdentityFlow observers using distinctUntilChanged() don't see
a change; after calling _blockchainIdentity?.updateIdentity() (and any in-place
mutation), reassign the flow with a new object to force emission (e.g. create a
new BlockchainIdentity instance or call a copy/clone and set the backing
MutableStateFlow/_blockchainIdentityFlow.value to that new instance). Update the
code in IdentityRepository around updateIdentity(), _blockchainIdentity and
blockchainIdentityFlow so the flow is set to a new instance after mutation so
MainViewModel (distinctUntilChanged()) will detect the change and trigger
refreshContactsForAllTransactions().

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant