Skip to content

feat(appupdates): show a one-time toast when an optional update is out - #1771

Merged
bmc08gt merged 5 commits into
code/cashfrom
feat/update-available-toast
Oct 14, 2026
Merged

bmc08gt merged 5 commits into
code/cashfrom
feat/update-available-toast

Conversation

@bmc08gt

@bmc08gt bmc08gt commented Oct 14, 2026 •

Copy link
Copy Markdown
Collaborator

When Play has a flexible update that's priority 4 or higher, or has been out 7+ days, store builds now show a one-time "New version available" toast with an Update button. Stacked on #1770.

Where it shows (once per version):

  • Chats or Wallet tab home (node 11693:162): 1s after coming back from a pushed screen, or after 3s on the tab.
  • Inside a chat (node 11705:162): 1s after it opens, 8dp above the composer and as wide as the composer row. It times out after 6s and goes away when the keyboard opens.

Update starts Play's flexible download. Once it's downloaded, an indefinite "Update ready" toast offers Restart. If the download finishes while the app is in the background, it installs straight away.

Forced path: availableUpdate and the minimum-version gate are unchanged; the optional update lives in a separate optionalUpdate. One fix: resuming a forced update that's already under way never reopened Play's screen. startUpdate() required isUpdateTypeAllowed(IMMEDIATE), which Play's fake reports as false while an immediate update runs, so the resume failed with "Update type is not allowed". It now skips that check when resuming, as Play's resume sample does. The controller also takes its AppUpdateManager as a constructor parameter (defaulting to the same factory call) so the new ForcedUpdateTest can use Play's fake.

Other changes:

  • FloatingToastHost gains a screen-set horizontal inset next to the existing bottom clearance. The chat sets both, so any toast shown in a chat now sits above the composer rather than behind it.
  • The unsupported-message bubble from fix(chat): show unreadable messages as a "needs a newer version" bubble #1770 shows "Update Flipcash" whenever an optional update exists. It skips the toast's priority, staleness and once-per-version gates.
  • The chat toast uses Indefinite plus a 6s timeout, so the host's accessibility scaling for long toasts doesn't apply to it.

Trying it on a debug build: adb shell run-as com.flipcash.app.android touch files/debug-optional-update fakes a priority-5 update. Delete files/datastore/update-toast.preferences_pb to reset the once-per-version record.

@bmc08gt bmc08gt self-assigned this Oct 14, 2026
@bmc08gt
bmc08gt force-pushed the feat/update-available-toast branch from f59ec08 to f56fdbd Compare October 14, 2026 18:30
@github-actions github-actions Bot added type: feature New functionality area: ui Compose UI, theme, components, resources and removed type: feature New functionality labels Oct 14, 2026
@bmc08gt
bmc08gt added this pull request to stack #1779 October 14, 2026 19:32
Base automatically changed from fix/unsupported-message-hint to code/cash October 14, 2026 19:33
Play's flexible flow, a once-per-version record for the update toast, and
the thresholds both toast hosts share. availableUpdate and the forced path
are unchanged; the optional state lives in optionalUpdate. Debug builds can
fake a priority-5 update with files/debug-optional-update.
When Play reports a flexible update with priority 4+ or 7+ days of
staleness, store builds show "New version available" once per version:
on the Chats or Wallet home 1s after coming back from a pushed screen or
after 3s there (node 11693:162), or in a chat 1s after it opens, above
the composer with a 6s timeout, dismissed when the keyboard opens
(node 11705:162). Update starts the flexible download; once it is
downloaded an indefinite "Update ready" toast offers Restart.

The toast host gains a screen-set horizontal inset, which the chat sets
to the composer row's side padding along with its bottom clearance.
The unsupported-message bubble shows "Update Flipcash" whenever an
optional update exists, without the toast's gates.
The forced controller now takes its AppUpdateManager as a constructor
parameter, defaulting to the same factory call, so its tests can pass
Play's FakeAppUpdateManager. Its behaviour is unchanged.

ForcedUpdateTest covers the minimum-version gate, the staff override,
opening the immediate flow, the guards in startUpdate(), reset(), and
an update already under way being reported on the next check.

FlexibleUpdatesTest adds a failed and a cancelled download, starting
again after declining Play's dialog, and a download that finished
while the app was gone offering Restart on the next check.
checkForUpdate() saw DEVELOPER_TRIGGERED_UPDATE_IN_PROGRESS and called
startUpdate(), but startUpdate() first required
isUpdateTypeAllowed(IMMEDIATE). Play's FakeAppUpdateManager reports
that as false while an immediate update runs, so the resume failed with
"Update type is not allowed" and Play's update screen never reopened.

startUpdate() now skips that check when resuming, as Play's own resume
sample does. The resume test checks that the flow starts a second time.
@bmc08gt
bmc08gt force-pushed the feat/update-available-toast branch from d0cf1d9 to adb3fb4 Compare October 14, 2026 19:33
@bmc08gt
bmc08gt merged commit 78f50c8 into code/cash Oct 14, 2026
2 checks passed
@bmc08gt
bmc08gt deleted the feat/update-available-toast branch October 23, 2026 16:07
bmc08gt added a commit that referenced this pull request Nov 8, 2026
…tional update check (#1981)

FlexibleUpdates.check() passed every appUpdateInfo failure to trace(error = ...),
which sends it to Bugsnag. Since #1771 that has filed InstallException -6 (download
not allowed in the current device state) and RemoteException "AppUpdateService:
Binder has died" (the Play Store process died mid-call) from 2026.10.4. Neither is
something the app can fix.

Those failures, plus the other InstallErrorCodes that describe the device or install
(API not available, Play Store not found, app not owned), now leave a breadcrumb
only. Other codes, such as -4 for a malformed request, are still reported. The check
also rethrows CancellationException instead of logging a cancelled check as a
failure.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: ui Compose UI, theme, components, resources type: feature New functionality

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant