"업데이트" streams the published APK into a system install session instead of opening the browser, so nothing is saved to the phone. Progress, cancel and retry are shown. Android 12+ is asked to skip its confirmation, which it does once this app is the installer of the copy being replaced. Adds REQUEST_INSTALL_PACKAGES and UPDATE_PACKAGES_WITHOUT_USER_ACTION. Not run on a device. Agent: main (opus) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
68 lines
2.9 KiB
Markdown
68 lines
2.9 KiB
Markdown
# TASK-073: Update inside the app, without a downloaded file
|
|
|
|
Status: done
|
|
Owner: Claude
|
|
Reviewer: human product owner
|
|
|
|
## Request
|
|
|
|
The product owner asked whether the app could update itself without a file being downloaded, and
|
|
did not want a file left behind (2026-10-12). Told that Android does not allow an update with no
|
|
action at all and that the app closes while it is replaced, they asked for it to be built.
|
|
|
|
## Scope
|
|
|
|
- Included: "업데이트" in the launch prompt and in the settings fetches the published APK inside
|
|
the app and hands it to the system's installer; progress, cancel and failure are shown.
|
|
- The APK is streamed from the network into the system's install session. No file is written to
|
|
the downloads folder or to the app's own storage.
|
|
- From Android 12 the system is asked to skip its confirmation. It does so only once this app is
|
|
the installer of the copy being replaced, so the first update still asks.
|
|
- Excluded: updating while the app is not open; reopening the app after the update (Android does
|
|
not let an app start itself from the background).
|
|
- The browser download from TASK-070 is gone from the app. The address still works by hand.
|
|
|
|
## Writable paths
|
|
|
|
- `app/src/main/java/com/ubisam/ubgitea/core/update/AppInstaller.kt`
|
|
- `app/src/main/java/com/ubisam/ubgitea/core/update/AppUpdate.kt`
|
|
- `app/src/main/java/com/ubisam/ubgitea/core/update/AppUpdateViewModel.kt`
|
|
- `app/src/main/java/com/ubisam/ubgitea/MainActivity.kt`
|
|
- `app/src/main/java/com/ubisam/ubgitea/feature/settings/SettingsScreen.kt`
|
|
- `app/src/main/res/values/strings_update.xml`
|
|
- `app/src/main/AndroidManifest.xml`
|
|
- `app/src/test/java/com/ubisam/ubgitea/core/update/AppUpdateTest.kt`
|
|
- `tasks/073-update-without-a-file.md`
|
|
- `docs/development-log.md`
|
|
|
|
## Acceptance criteria
|
|
|
|
- [x] "업데이트" starts the fetch inside the app; no browser opens and no file is saved.
|
|
- [x] Without the "install unknown apps" allowance the person is sent to that setting and the
|
|
update starts when they return with it on.
|
|
- [x] Progress is shown, the fetch can be cancelled, and a failure can be retried.
|
|
- [x] Unit tests cover the steps of an update, a failure and a declined install.
|
|
- [ ] Seen on a device: first update (with confirmation) and a later one. Not done: no device.
|
|
|
|
## Security and privacy
|
|
|
|
- New permissions: `REQUEST_INSTALL_PACKAGES` and `UPDATE_PACKAGES_WITHOUT_USER_ACTION`.
|
|
- The APK comes over HTTPS from the fixed address of the `apk` branch; the Gitea token is not
|
|
sent. The system refuses a build that is not signed with the key of the installed copy, so a
|
|
build from another PC's debug key fails rather than replacing the app.
|
|
- The receiver for the system's answer is not exported.
|
|
|
|
## Required checks
|
|
|
|
```text
|
|
./gradlew compileDebugKotlin compileDebugUnitTestKotlin testDebugUnitTest assembleDebug lintDebug
|
|
```
|
|
|
|
## Evidence
|
|
|
|
See the development log entry for TASK-073.
|
|
|
|
## Review findings
|
|
|
|
None.
|