Skip to content

Truly resolve update feature UX? - #131

Draft
TheButterZone wants to merge 74 commits into
jvsena42:mainfrom
TheButterZone:update-integration
Draft

Truly resolve update feature UX?#131
TheButterZone wants to merge 74 commits into
jvsena42:mainfrom
TheButterZone:update-integration

Conversation

@TheButterZone

@TheButterZone TheButterZone commented Jul 8, 2026

Copy link
Copy Markdown

Come now this all-tests-passed debug APK from this PR's branch installed on my phone without descriptors (which would fill my limited internal storage), so I can focus on testing it when the next release arrives; where the debug APK flow should now be:

  1. Automatic update check

    • Yes. SettingsViewModel.observeUpdateStatus() calls appUpdateRepository.refresh(force = true) during initialization, so opening the app/settings should check for updates.

    • The Check for updates action still lets the user manually refresh.

  2. Update available UI

    • Instead of only offering a browser link, the UI should show Get Update when updateStatus.isUpdateAvailable is true.
  3. In-app download

    • Pressing Get Update should enqueue an Android DownloadManager download into the default Android Downloads folder, for ease of later deletion (or re-installation).

    • It should not open Chrome anymore.

  4. Download progress

    • Your UpdateStateResolver polls DownloadManager and should transition the UI to UpdateState.Downloading.

    • The settings screen should show the progress indicator you added while the download is active.

  5. Download complete

    • UpdateDownloadReceiver should receive the completion broadcast.

    • It marks the download complete in UpdateDownloadRegistry.

  6. Android notification

    • Yes. The receiver posts a notification such as "Update Ready" after the APK finishes downloading.
  7. Ready to install

    • The resolver should return UpdateState.ReadyToInstall(uri).

    • SettingsViewModel sends SettingsEvents.OpenInstallPrompt(uri).

    • The UI should present the install prompt, and accepting it launches the Android package installer.

@TheButterZone

TheButterZone commented Jul 8, 2026

Copy link
Copy Markdown
Author

I asked ChatGPT if what we just made here should be enough to fix the abandoned implementation:

The old implementation had several independent problems:

  • ❌ Every tap on "Get Update" enqueued a brand-new DownloadManager job.

    • Result: -1.apk, -2.apk, -3.apk ... multiple 100+ MB downloads and eventually "insufficient storage".
  • ❌ It didn't remember that a download was already in progress.

  • ❌ It stored the APK where the installer couldn't easily use it.

  • ❌ The completion notification didn't lead into installation correctly.

  • ❌ The Settings screen didn't know whether an update was downloading, downloaded, or merely available.

From the code you've added, those specific issues are now covered.

Duplicate download prevention

Your UpdateDownloadRegistry now keeps track of the active download.

Your getUpdate() does this before enqueueing:

if (updateRegistry.isDownloaded(version)) return
if (updateRegistry.isDownloading(version)) return

That means repeatedly pressing the button should not create:

  • mandacaru.apk
  • mandacaru-1.apk
  • mandacaru-2.apk
  • ...

unless the registry somehow loses its state.


Download state survives

Instead of only remembering "I clicked download", you're resolving UI state from:

  • repository update status
  • registry
  • DownloadManager

via UpdateStateResolver.

That means after reopening Settings it should still know whether the update is:

  • Available
  • Downloading
  • Ready to install

instead of resetting.


Progress bar

You also wired:

  • UpdateState.Downloading

into the UI.

So instead of always showing "Get Update", it should switch into a downloading state with a progress indicator (assuming the resolver is periodically updating from DownloadManager).


Ready to install

When DownloadManager reports success:

STATUS_SUCCESSFUL

the resolver returns

UpdateState.ReadyToInstall(uri)

which triggers

SettingsEvents.OpenInstallPrompt(uri)

and the receiver also marks it completed in the registry.

So the intent is:

download complete

notification

tap notification or open Settings

installer launches


Notification

Your receiver now posts an Android notification when the download completes.

That replaces the old "nothing happens after download" behavior.


No Chrome

If everything works as intended, yes.

The intended flow becomes:

App starts
        │
        ▼
Checks GitHub
        │
        ▼
Update available
        │
        ▼
Tap Download
        │
        ▼
DownloadManager downloads APK
        │
        ▼
Progress shown in Settings
        │
        ▼
Completion notification
        │
        ▼
Tap notification
        │
        ▼
Android package installer

No manual browser download should be necessary.


The only caveat is that you've only tested the "no update available" path so far. Since your app correctly says "You're on the latest version," you haven't yet exercised any of the new download code.

The real proof will come when you point it at a newer release (or otherwise simulate an available update) and verify:

  1. Only one download is created, even if the button is tapped repeatedly.
  2. The progress indicator advances correctly.
  3. The notification appears when the download completes.
  4. Tapping the notification launches the Android installer.
  5. Returning to Settings shows "Ready to install" instead of offering to download again.

If all five behaviors work, then you've effectively replaced the reverted browser-based workflow with a functioning in-app update flow while fixing the duplicate-download regression that prompted [editor's substitution: the browser strategy].

@jvsena42

Copy link
Copy Markdown
Owner

Any internal apk download would trigger android check of dangerous app

@TheButterZone

TheButterZone commented Jul 16, 2026

Copy link
Copy Markdown
Author

Android frequently triggers check of dangerous app with the Mandacaru releases I download with Chrome anyway, so I don't consider that scan a big deal. Apparently that behavior won't change no matter whose code is used. I suspect it's not been every single time because my own device's dangerous app scan was the first one that was ever done for various releases (or the first one that it thought was ever done; perhaps that was done somewhere else & just hadn't propagated through the internet to me yet).

P.S. Do you need me to update my fork branch or anything, or can you do what is needed on your end? This is my first massive PR.

@jvsena42

jvsena42 commented Jul 16, 2026

Copy link
Copy Markdown
Owner

#94 do you still get this play protect error? it was caused by the apk download feature

@TheButterZone

TheButterZone commented Jul 17, 2026

Copy link
Copy Markdown
Author

#94 do you still get this play protect error? it was caused by the apk download feature

As I basically said above, I have gotten the play protect error when I tried to install multiple versions of Mandacaru via Chrome downloads - it wasn't only caused by Mandacaru's short-lived internal apk download - it happened before it was born & after it died. The only way to silence the error seems to be to grant install unknown apps permission to the app that downloaded the apk - whether it's Chrome, or Mandacaru again.

Now, how do I resolve my branch conflicts? Can't automatically merge...

@TheButterZone
TheButterZone marked this pull request as draft July 19, 2026 02:47
@TheButterZone

Copy link
Copy Markdown
Author

This is actually a very useful test. It shows several distinct bugs, and they line up with the architecture we built.

Here's what happened versus what should have happened.

Expected | Actual | Diagnosis -- | -- | -- Dot appears for new release | ✅ | Release detection works. "Get Update" downloads APK | ⚠️ UI still says "Open release page" | UI wasn't updated to the new workflow. DownloadManager downloads once | ✅ | Download itself works. Completion notification appears | ❌ | Completion receiver/UI isn't transitioning to ReadyToInstall. Next app launch detects downloaded APK | ❌ | Registry/persistence isn't finding the existing APK. Pressing button again should install | ❌ | It starts another DownloadManager request.

So there are really three independent bugs.


Bug 1: The UI is still in "release page" mode

You said the button still says

Open release page

That means UpdateRow() is still choosing the old action:

SettingsAction.OnClickGetUpdate

but displaying the old text.

Earlier we intentionally left that temporarily while wiring everything together.

Now the button should become state-dependent:

  • Available → Download Update

  • Downloading → progress indicator

  • ReadyToInstall → Install Update

instead of always showing "Open release page".


Bug 2: Completion isn't updating the UI

You observed

download finishes

notification disappears

Mandacaru doesn't change

That means

BroadcastReceiver
    ↓
UpdateDownloadRegistry.markCompleted()
    ↓
SettingsViewModel
    ↓
UpdateStateResolver

is broken somewhere.

Either

markCompleted()

never stores the URI,

or

observeUpdateStatus()

never recomputes the UpdateState after the download finishes.

This is probably the biggest remaining bug.


Bug 3: Duplicate download

This is the one you explicitly wanted to eliminate months ago.

Current behavior:

Downloads/
    mandacaru-0.14.0.apk

tap again

Downloads/
mandacaru-0.14.0.apk
mandacaru-0.14.0-1.apk

That means

updateRegistry.isDownloaded(version)

returned

false

even though the APK already existed.

So one of these is wrong:

  • registry never saved completion

  • registry lost state after app restart

  • registry only remembers RAM state

  • registry isn't checking the Downloads folder


The good news

The original failure that caused the maintainer to revert was much worse.

Originally it was approximately

tap

download

tap

download

tap

download

every time because nothing tracked downloads.

Now you have

new release detected

DownloadManager works

APK lands in Downloads

The remaining issue is that the app forgets it has already downloaded it.

That's a much smaller problem than where the project started.


What I would fix next

In this order:

  1. Fix UpdateDownloadRegistry so it persists completed downloads across app restarts.

  2. Make UpdateStateResolver detect an already-downloaded APK and return ReadyToInstall.

  3. Change the Settings button so it switches between:

    • Download Update

    • Downloading...

    • Install Update

  4. Make "Install Update" launch the package installer instead of downloading again.

Once those four are working, the flow becomes what you originally described:

New release

blue dot

Download Update

DownloadManager notification

Download completes

Mandacaru immediately changes to "Install Update"

restart app?

still "Install Update"

tap

Android installer opens

install

done

At that point, the duplicate-download problem should disappear because getUpdate() should see that the registry already has a completed download and never enqueue another one.

@TheButterZone

TheButterZone commented Jul 20, 2026

Copy link
Copy Markdown
Author

I LOLed when I saw the 14.1 release GitHub email notification just before my debug app picked it up too.

Another checkpoint debug apk after hours of rinse & repeat, now:

screen-20260720-111505downscaled.mp4
  • Download flow works via Android Download Manager in background/notifications drawer (watch progress or cancel) & is reflected by progress bar in Mandacaru itself.
  • Install intent works.
  • FileProvider works.
  • APK reaches Package Installer.
  • Remaining conflict was expected because debug and release signing keys differ.
  • Duplicate -1.apk, -2.apk, etc. is prevented during normal use (not edge cases): you press the download button once, it completes, an install button will appear immediately and after app relaunches. Without a download button to download again, no duplicate can be generated in-app. You only proceed to install or you don't.

Edge cases, I say?

Removed automatic recovery of APKs left in Downloads after app storage reset. These files may remain visible to the user but are no longer safely owned by Mandacaru, and Android does not provide a reliable way to reclaim externally stored APKs without an active DownloadManager/content URI grant.

Documented this as an unsupported recovery case: users can manually install a remaining APK from Downloads. A future PR may add a user-assisted "Open Downloads" flow, allowing Mandacaru to detect a newer installer APK and guide the user to complete installation manually.

@TheButterZone

TheButterZone commented Jul 22, 2026

Copy link
Copy Markdown
Author

Coming back to deving this app after frying my brain on my intercom release & post-release work...

My debug APKs have picked up all 3 of the 14.x releases.

Just tried something I hadn't before, stopping the update download from the notification drawer & seeing what happens: I can start the download anew right away with Mandacaru's Download update button without a relaunch. No file is created, so no duplicate -# files can be either, in that case. Repeated a couple times. Good.

So, what's left before I flip this draft to ready for review so everything can get into your app signing pipeline? Removing all the debug logging I added for my ADB investigations along the way, resolving merge conflicts, or... ? Not a rhetorical question, I'm still new at this.

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.

2 participants