Skip to content

fix: resolve stuck 'preparing to install' after self-update (#478)#482

Merged
rainxchzed merged 1 commit into
mainfrom
fix/478-self-update-pending-install
May 2, 2026
Merged

fix: resolve stuck 'preparing to install' after self-update (#478)#482
rainxchzed merged 1 commit into
mainfrom
fix/478-self-update-pending-install

Conversation

@rainxchzed
Copy link
Copy Markdown
Member

@rainxchzed rainxchzed commented May 2, 2026

Problem

After GitHub Store updates itself, the install status permanently shows "preparing to install" because Android does not deliver ACTION_PACKAGE_REPLACED to the app's own receivers. The existing registerSelfAsInstalledApp() returned early when the DB row already existed, never clearing the stale isPendingInstall = true flag.

Fix (3-layered)

1. GithubStoreApp.registerSelfAsInstalledApp()

When the existing DB row has isPendingInstall = true, a new resolveSelfPendingInstall() method queries the system PackageManager, updates version info, and clears the pending flag. Runs in Application.onCreate() — the earliest startup point.

2. PackageEventReceiver

Added ACTION_MY_PACKAGE_REPLACED handling — Android's dedicated broadcast for self-updates (guaranteed delivery to the updated app). Since MY_PACKAGE_REPLACED carries no data URI, onReceive() falls back to context.packageName.

3. AndroidManifest.xml

Added a separate <intent-filter> for MY_PACKAGE_REPLACED (without the package data scheme, since this broadcast doesn't use one).

The existing SyncInstalledAppsUseCase remains as a third safety net.

Closes #478


Warp conversation

Co-Authored-By: Oz oz-agent@warp.dev

Summary by CodeRabbit

  • Bug Fixes

    • Improved app self-update detection and properly clears stale pending-install states during startup after device updates.
  • Improvements

    • Enhanced handling of app replacement events to better synchronize installation status with device state.

After GitHub Store updates itself, Android does not deliver
ACTION_PACKAGE_REPLACED to the app's own receivers, leaving
isPendingInstall = true permanently in the database.

Changes:
- GithubStoreApp: registerSelfAsInstalledApp() now detects and
  resolves a stale isPendingInstall flag on every cold start by
  querying the system PackageManager.
- PackageEventReceiver: handle ACTION_MY_PACKAGE_REPLACED (the
  Android-sanctioned broadcast for self-updates) with a fallback
  to context.packageName since it carries no data URI.
- AndroidManifest: add a dedicated intent-filter for
  MY_PACKAGE_REPLACED (no data scheme required).

Closes #478

Co-Authored-By: Oz <oz-agent@warp.dev>
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 2, 2026

Walkthrough

This PR resolves the app remaining in "pending install" status after self-updates. It adds intent-filter support for MY_PACKAGE_REPLACED in the manifest and receiver, implements the handler to extract the package name from context, and adds cold-start logic to clear stale pending-install flags by re-checking package info.

Changes

Self-Update Resolution Flow

Layer / File(s) Summary
Intent Filter Declaration
composeApp/src/androidMain/AndroidManifest.xml
Receiver PackageEventReceiver now declares handling of android.intent.action.MY_PACKAGE_REPLACED broadcasts (without data scheme).
Event Handler Implementation
core/data/src/androidMain/kotlin/zed/rainxch/core/data/services/PackageEventReceiver.kt
onReceive() extracts package name from context for MY_PACKAGE_REPLACED actions when no data URI is present; intent filter creation adds the new action to registered broadcast actions.
Cold Start Resolution
composeApp/src/androidMain/kotlin/zed/rainxch/githubstore/app/GithubStoreApp.kt
registerSelfAsInstalledApp() now detects existing app rows with isPendingInstall == true and calls new resolveSelfPendingInstall() to verify installed package state, update version fields, and clear the stale flag before returning.

Sequence Diagram

sequenceDiagram
    participant System
    participant PackageEventReceiver
    participant GithubStoreApp
    participant PackageMonitor
    participant Database

    System->>PackageEventReceiver: MY_PACKAGE_REPLACED broadcast
    PackageEventReceiver->>PackageEventReceiver: Extract packageName from context
    PackageEventReceiver->>Database: Notify about package replacement
    
    Note over GithubStoreApp: On next app startup
    GithubStoreApp->>Database: Query installed app (isPendingInstall=true)
    Database-->>GithubStoreApp: Return stale pending app
    GithubStoreApp->>PackageMonitor: Fetch current package info
    PackageMonitor-->>GithubStoreApp: Return installed version
    GithubStoreApp->>Database: Update version & clear isPendingInstall flag
    Database-->>GithubStoreApp: Confirmed
    GithubStoreApp->>GithubStoreApp: resolveSelfPendingInstall() complete
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐰 A hop, a replaced package takes flight,
The pending flag fades in the night,
Cold starts detect what once stayed,
Self-updates now properly made!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 25.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'fix: resolve stuck preparing to install after self-update' accurately describes the main change: resolving the persistent pending install status after the app self-updates.
Linked Issues check ✅ Passed The pull request addresses all coding requirements from issue #478: handling ACTION_MY_PACKAGE_REPLACED in PackageEventReceiver, adding manifest intent-filter, and implementing resolveSelfPendingInstall() to clear stale pending install flags.
Out of Scope Changes check ✅ Passed All changes directly address the self-update pending install issue: manifest update for MY_PACKAGE_REPLACED, PackageEventReceiver enhancement, and GithubStoreApp pending install resolution logic.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/478-self-update-pending-install

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
Review rate limit: 7/8 reviews remaining, refill in 7 minutes and 30 seconds.

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

Copy link
Copy Markdown
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.

🧹 Nitpick comments (1)
composeApp/src/androidMain/kotlin/zed/rainxch/githubstore/app/GithubStoreApp.kt (1)

217-242: ⚖️ Poor tradeoff

Potential race condition with stale snapshot (low practical risk).

The existing object is fetched in registerSelfAsInstalledApp() before calling this method. If PackageEventReceiver.onPackageInstalled() concurrently updates the same row (via MY_PACKAGE_REPLACED), the repo.updateApp(existing.copy(...)) here could overwrite those changes with stale data.

Both paths clear isPendingInstall, so the primary fix works. The risk is minor field inconsistencies (e.g., if receiver updated version fields first). Given that self-update is infrequent and the next update check would correct any drift, this is low-impact.

For robustness, consider using updateInstalledVersion() (targeted column write) or re-fetching before update.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@composeApp/src/androidMain/kotlin/zed/rainxch/githubstore/app/GithubStoreApp.kt`
around lines 217 - 242, The update in resolveSelfPendingInstall currently calls
repo.updateApp(existing.copy(...)), which can overwrite concurrent changes from
PackageEventReceiver.onPackageInstalled; instead either re-fetch the current row
from the repository before applying changes or use a targeted column update such
as repo.updateInstalledVersion(packageName, isPendingInstall = false,
installedVersionName = systemInfo.versionName, installedVersionCode =
systemInfo.versionCode, isUpdateAvailable = latestVersionCode >
systemInfo.versionCode) so you only touch the specific fields you intend to
clear/update (reference resolveSelfPendingInstall, repo.updateApp,
updateInstalledVersion, and registerSelfAsInstalledApp).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In
`@composeApp/src/androidMain/kotlin/zed/rainxch/githubstore/app/GithubStoreApp.kt`:
- Around line 217-242: The update in resolveSelfPendingInstall currently calls
repo.updateApp(existing.copy(...)), which can overwrite concurrent changes from
PackageEventReceiver.onPackageInstalled; instead either re-fetch the current row
from the repository before applying changes or use a targeted column update such
as repo.updateInstalledVersion(packageName, isPendingInstall = false,
installedVersionName = systemInfo.versionName, installedVersionCode =
systemInfo.versionCode, isUpdateAvailable = latestVersionCode >
systemInfo.versionCode) so you only touch the specific fields you intend to
clear/update (reference resolveSelfPendingInstall, repo.updateApp,
updateInstalledVersion, and registerSelfAsInstalledApp).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 195014c4-f7ed-412d-be15-186297d5e69b

📥 Commits

Reviewing files that changed from the base of the PR and between d12a5a6 and 97e0a45.

📒 Files selected for processing (3)
  • composeApp/src/androidMain/AndroidManifest.xml
  • composeApp/src/androidMain/kotlin/zed/rainxch/githubstore/app/GithubStoreApp.kt
  • core/data/src/androidMain/kotlin/zed/rainxch/core/data/services/PackageEventReceiver.kt

@rainxchzed rainxchzed merged commit 1cb9453 into main May 2, 2026
1 check passed
@rainxchzed rainxchzed deleted the fix/478-self-update-pending-install branch May 2, 2026 05:55
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.

githubstore 自己安装更新自己之后状态一直准备安装

1 participant