Say a build is signed with the wrong key before the installer does
Three things. A download whose signing certificate does not match the installed copy is stopped with a dialog naming both digests and offering the one thing that gets past it: removing the old app. Android's own answer is "App not installed" with no cause, which reads as the download having failed. Where either certificate cannot be read the answer is "don't know" and the install goes ahead as before. The download is carried on the component's state so that the removal is followed by the install it was for rather than by a second download. Pressing that revealed that ACTION_DELETE now needs REQUEST_DELETE_PACKAGES, and fails invisibly without it -- so the "Remove the old app" offer for a renamed package had presumably never worked either. The build mode and the installed variant are one dropdown, not two. They answer to the same words, so two pickers offering debug and release read as one choice asked twice. Where a component declares modes the mode is the whole answer, and the server serves the build named after it rather than the newest. Every dropdown now hangs from one outlined pill with a chevron. And a checkout parked on a chosen commit is no longer called out of date, with HEAD in the commit picker as the way back to following the branch. The commit list comes from that branch rather than from HEAD, so parking no longer hides the commits after it -- the same one-way door the tracked-only dirty check closed, in a place that check did not reach. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
3aa6f2f290
commit
f983db154d
14 files changed
+895
-111
No files matched your search
@@ -79,6 +79,12 @@ fun registerPackageChangeReceiver(
|
||||
object : BroadcastReceiver() {
|
||||
override fun onReceive(context: Context, intent: Intent) {
|
||||
val packageName = intent.data?.schemeSpecificPart ?: return
|
||||
// An install over an existing copy sends REMOVED and then
|
||||
// ADDED, with EXTRA_REPLACING marking the pair. Reporting
|
||||
// the first would put "this package is gone" on screen for
|
||||
// the moment between them, which is a state nothing here
|
||||
// is in.
|
||||
if (intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) return
|
||||
onPackageChanged(packageName)
|
||||
}
|
||||
}
|
||||
@@ -86,6 +92,11 @@ fun registerPackageChangeReceiver(
|
||||
IntentFilter().apply {
|
||||
addAction(Intent.ACTION_PACKAGE_ADDED)
|
||||
addAction(Intent.ACTION_PACKAGE_REPLACED)
|
||||
// Removals as well as arrivals, because one of them is a step
|
||||
// in something this app started: an app signed with a
|
||||
// different key has to be removed before the download can go
|
||||
// on, and this is what says the way is clear.
|
||||
addAction(Intent.ACTION_PACKAGE_REMOVED)
|
||||
addDataScheme("package")
|
||||
}
|
||||
ContextCompat.registerReceiver(context, receiver, filter, ContextCompat.RECEIVER_NOT_EXPORTED)
|
||||
|
||||
Reference in new issue
Block a user