Releases - #2
Open
yukinine-dev wants to merge 8 commits into
Open
Conversation
Baseline is v3.13 (windowAnimationStyle fix) — untouched, this only adds the swipe gesture on top of it. Root of the "bottom sheet": BottomAlertActivity already configures its Window as WRAP_CONTENT + gravity=BOTTOM, so the DecorView's first child IS the visible dialog (icon/title/content/buttons together). Drag handle: @id/imageView, the existing 48x4dp pin ImageView at the top of install_content_view(.xml/_kchi.xml), present on all five screens that extend BottomAlertActivity (InstallStaging, InstallInstalling, InstallFailed, InstallSuccess, PackageInstallerActivity) since they all inflate one of those two layouts. New class SwipeDismissController (View.OnTouchListener + Runnable, hand-written smali, no material/library dependency): - ACTION_DOWN on the handle: snapshot raw Y, current translationY, sheet height; cancel any in-flight settle animation. - ACTION_MOVE: gated by ViewConfiguration's touch slop so a small jitter never moves anything; once past slop, sheet.translationY follows the finger 1:1, clamped at 0 so it can't be dragged above its resting position (this doubles as the swipe-up-to-restore path — there's no separate "collapsed" state in a one-shot confirmation dialog, so "restore" and "rest position" are the same target). - ACTION_UP: dismisses only if the gesture was a real drag AND (translationY passed 35% of sheet height OR fling velocity exceeds ViewConfiguration's scaled minimum) — otherwise animates back to 0. Dismiss finish()/overridePendingTransition(0,0) fires via postDelayed(this, ...) after the manual drag animation completes, so it never fights the swipe motion or replays the (still intact) theme animation on top of it. - ACTION_CANCEL: always settles back to 0, never dismisses. Touch handling is attached ONLY to the pill via setOnTouchListener, never to the sheet root or any container, so buttons, ScrollView, RecyclerView and any other in-content gestures are structurally unreachable by it — no onInterceptTouchEvent anywhere in this patch. Single integration point: BottomAlertActivity.setupAlert(), right after installContent(), calling the new static SwipeDismissController.attach(Activity). Applies to all three apks uniformly since their classes.dex were already confirmed identical in v3.13 and stayed identical after this patch (verified by hash). Did not touch PackageInstallerActivity's cancellation logic (setPermissionsResult/abandonSession) or the v3.13 windowAnimationStyle fix. Verified statically: apktool build succeeded (valid smali), and the assembled classes.dex was disassembled with androguard and manually traced instruction-by-instruction against the intended control flow (branch targets, register lifetimes, clamp/threshold logic) — no device/emulator was available in this environment to confirm the actual on-screen feel, hit-target comfort of the 4dp-tall handle, or interaction with any OEM overlay. Needs a real swipe test on device before wider rollout.
Baseline is v3.14, untouched — entrance animation (windowAnimationStyle/ NewUIDialogAnimation/styles.xml), PackageInstaller cancellation logic, module structure and signing key are all unchanged. This only replaces the swipe mechanism. v3.14's pill-only OnTouchListener is gone (SwipeDismissController removed) — it can't satisfy "drag from anywhere in the sheet," since OnTouchListener on a single small child never gets first look at touches landing elsewhere. Replaced with SwipeSheetContainer, a FrameLayout subclass inserted between the DecorView and its original child (icon/title/content/buttons together) at the same hook point as before (BottomAlertActivity.setupAlert(), right after installContent()). Being an ancestor of everything, it gets real ViewGroup arbitration via onInterceptTouchEvent — the only correct way to let an outer drag and inner taps/scrolls/clicks coexist. It doesn't intercept ACTION_DOWN, so every child (buttons, and any ScrollView/RecyclerView — none exist in our own layouts today, but the mechanism is general) sees it and behaves exactly as before. It only starts stealing a gesture once movement crosses touch slop AND is predominantly vertical AND downward; a child already mid-scroll can still call requestDisallowInterceptTouchEvent (standard ViewGroup behavior, inherited free) to keep it. Once stolen, the sheet's translationY tracks the finger 1:1 every MOVE, clamped at 0 (recomputed from the down point each time, not incremental — no drift). Threshold is exactly what was asked: 25% visible height / 75% of the sheet's own opened height, snapshotted once when the drag begins and never recomputed against a shrinking "current" height mid-drag. No velocity anywhere in this file — a fast short flick that doesn't cross 75% animates back to 0, full stop. ACTION_UP and ACTION_CANCEL share one code path: if the threshold was already passed, cancel still completes the dismiss instead of snapping an almost-closed sheet back open. Dismiss finish()/overridePendingTransition(0,0) fires through postDelayed(this, 220) after the manual animation ends, so it never fights the drag or double-animates against the (untouched) entrance theme. No hidden collapsed state: settle always lands exactly at 0, dismiss always ends in finish(). Install-mode lock (the "критически важно" part) needed no new gating logic at all: SwipeSheetContainer.attach() checks `instanceof InstallInstalling` before touching the view tree and returns immediately if so — that screen is never wrapped, so swipe/ drag/tap-there literally cannot do anything, by construction, without this patch going anywhere near the existing cancellation code. Back and outside-tap on InstallInstalling were already correctly gated by existing code found during this pass: onBackPressed() already checks mCancelButton.isEnabled(), and InstallingAsyncTask.onPostExecute() already calls setFinishOnTouchOutside(false) the moment session.commit() returns — both untouched. The real Cancel button's click handler (AlertController's negative button, wired in onCreate) is untouched. Swipe code calls abandonSession() nowhere — it doesn't even exist on this screen. All three apks patched identically; classes.dex hash-verified equal after the rebuild, same as after every prior patch. Same debug keystore as v3.13/v3.14 (SHA-1 cfd32bf8...), so the upgrade chain isn't broken. Verified statically only (no device in this environment): apktool build succeeded, and every branch of onInterceptTouchEvent/onTouchEvent/ attach/beginDrag was disassembled from the built classes.dex with androguard and traced instruction-by-instruction against the intended control flow — branch targets, the 0.75f threshold constant's bit pattern, clamp logic, and the UP/CANCEL convergence all confirmed against the actual assembled bytecode. What's not verified: on-screen feel (whether tracking feels 1:1 without jank), real interaction with a genuinely scrollable child (none exist in this app's own layouts to test against), and any OEM touch-handling quirks. Needs a real device pass before wider rollout.
…agnosed
Built on v3.14/v3.15 (both untouched). Three device-reported issues,
each investigated to root cause before any change.
PROBLEM A — background/shape stayed put while content dragged.
Root cause: the rounded sheet is the WINDOW background drawable
(Window.setBackgroundDrawable(dialog_background_inset), set in
BottomAlertActivity.onCreate) — it lives on the DecorView, not on any
child. v3.15 translated an inner child (the AlertController alert
layout that holds icon/text/buttons), so content moved but the window
background never did. Verified by reading onCreate + resolving the
drawable id.
Fix: translate the DecorView itself, which carries BOTH its background
(the rounded shape) and all content, so the whole sheet moves as one
unit. No inner-child translation, no separate targets.
PROBLEM C — blur looked worse ("soapy") on v3.15.
Root cause: decompiled v3.14 and v3.15 and diffed every file — the ONLY
differences were smali (the gesture classes + one hook line). Zero
resource/style/drawable/dimen/window changes. So the blur regression
was caused purely by v3.15 inserting a wrapping FrameLayout
(SwipeSheetContainer) between the DecorView and its child, which
restructured the exact view tree the ROM's window blur/composition
depends on.
Fix: stop restructuring the tree entirely. v3.16 inserts NO view. The
DecorView → alert-layout hierarchy is now identical to stock/v3.14, so
the window background + FLAG_BLUR_BEHIND/setBlurBehindRadius composite
exactly as before. Blur is not "tuned" — the cause was removed.
New gesture architecture (fixes A and C together, satisfies all v3.15
drag requirements):
SwipeSheetController is a plain controller object (no View, nothing
added to the tree). BottomAlertActivity overrides dispatchTouchEvent
and delegates to it:
- ACTION_DOWN is always passed through to super, so children (buttons)
press and taps work exactly as before.
- A gesture is only stolen once movement crosses ViewConfiguration
touch slop AND is predominantly vertical AND downward. On steal, one
ACTION_CANCEL is dispatched to the children (so a half-pressed button
resets) and the controller consumes from then on. This is real
tap/scroll/drag arbitration, not a blanket root OnTouchListener.
- While dragging, DecorView.translationY tracks the finger 1:1 via
getRawY (screen coords, drift-free), clamped >= 0 so the sheet can
never go above its resting position; dragging down then back up
returns it toward 0 with the finger.
- Threshold is 75% of the sheet's opened height, snapshotted once at
drag start (getHeight at the moment the drag begins), never
recomputed mid-drag. No velocity is used anywhere — a fast short
flick under 75% settles back.
- ACTION_UP and ACTION_CANCEL share one decision path: past 75% ->
animate down and finish(); otherwise animate back to 0. So CANCEL
after the threshold still completes the dismiss instead of snapping
an almost-closed sheet back open.
- Dismiss finish()/overridePendingTransition(0,0) fires via
postDelayed(this, 200) after the manual slide, so it never fights the
drag or the (untouched) entrance animation.
Install mode is locked exactly as required and by construction:
SwipeSheetController.attach() returns null for InstallInstalling, so
mSwipe is null there and dispatchTouchEvent falls straight through to
super — swipe/drag/tap-outside do nothing on the installing screen,
and Back/outside remain governed by the existing (untouched) code
(onBackPressed checks mCancelButton.isEnabled(); onPostExecute sets
setFinishOnTouchOutside(false) at commit).
PROBLEM B — Cancel button doesn't cancel (device: button is grey).
Traced the full path, did not assume: the negative-button listener
(lambda$onCreate$0) is correctly wired via AlertController.setButton(-2)
and calls AsyncTask.cancel(true) + PackageInstaller.abandonSession(
mSessionId) + setResult(CANCELED) + finish(). doInBackground polls
isCancelled() each 1MB chunk (cooperative pre-commit cancel).
session.commit() runs in onPostExecute (UI thread) AFTER the whole APK
is written, and the button is disabled (setEnabled(false)) right after
that commit. The reported symptom — a GREY button — means the install
has already passed commit by the time the screen is interactive: for a
local APK on a privileged system installer the write+commit is
near-instant, so the button is correctly disabled post-commit.
abandonSession() after commit cannot un-install (Android limitation).
This is NOT a wiring bug and is NOT caused by the swipe work
(InstallInstalling is never wrapped/handled by the gesture code). No
change made to the cancellation path — the only way to make Cancel
usable would be to deliberately add a pre-commit confirmation/hold,
which is a flow change the spec said not to make; left for a separate
decision.
All three apks (MIUI/Google/AOSP) patched identically; classes.dex
hash-verified equal after rebuild. Same debug keystore as v3.13-v3.15
(SHA-1 cfd32bf8...). Module structure and signing unchanged.
Static verification only (no device here): apktool build clean, and
dispatchTouchEvent + onDispatch + apply + settle/dismiss disassembled
from the built classes.dex with androguard and traced branch-by-branch
against the intended flow (slop/direction gate, clamp, 0.75f threshold
bit pattern, UP/CANCEL convergence, InstallInstalling null-gate). What
still needs a real device: that translating the DecorView slides the
whole sheet cleanly on the target ROM, drag feel, and that removing the
wrapper restores blur as expected.
Built on v3.14/v3.15/v3.16 (all untouched). Two targeted changes; blur deliberately left alone. PILL (resource-only, all 3 apks): Investigation: @id/imageView is 48dp x 4dp, no padding, default scaleType (fitCenter); @drawable/pin is a shape with intrinsic size 48dp x 2dp. Under fitCenter the pin already spans the full 48dp view width (scale=min(48/48,4/2)=1.0 -> 48x2 centered), so there is NO internal letterbox — the perceived side space was the short 48dp handle sitting in the full-width sheet. Per decision, widen the handle to 64dp WITHOUT fitXY (so height is never stretched): - res/drawable/pin.xml <size> width 48dp -> 64dp (height stays 2dp, corners 2dp). - install_content_view.xml + install_content_view_kchi.xml imageView layout_width 48dp -> 64dp (height stays 4dp). fitCenter then renders the pin at 64x2 centered vertically in the 64x4 view: full 64dp width, unchanged 2dp visual height. Sheet size untouched. CANCEL — real 3s pre-commit window (flow change, authorized): Root cause confirmed by full lifecycle trace: mCancelButton is created enabled in onCreate; it is only meaningfully enabled during the async APK write; InstallingAsyncTask.onPostExecute called session.commit() the instant the write finished and disabled the button on the very next lines, so on a fast/privileged local install the button was grey before the user could act. Change (does NOT delay the APK write, only defers commit): - New state field mCommitState (0 idle, 1 PENDING, 2 CANCELLED, 3 COMMITTED) + mPendingCommit runnable. - onPostExecute no longer commits inline. After the write completes it builds the IntentSender as before and calls the new host method scheduleCommit(session, sender). - scheduleCommit(): state=PENDING, creates PendingCommit(host, session, sender), and mCancelButton.postDelayed(it, 3000). The button stays ENABLED for this whole window; it is NOT disabled here. - After 3s PendingCommit.run() -> host.commitNow(session, sender): proceeds only if state==PENDING and !isFinishing(); then state= COMMITTED, session.commit(sender), mCancelButton.setEnabled(false), setFinishOnTouchOutside(false). Post-commit behaviour is exactly the original. - Cancel (existing lambda$onCreate$0) now first sets state=CANCELLED and mCancelButton.removeCallbacks(mPendingCommit), THEN runs the existing AsyncTask.cancel + abandonSession + setResult + finish path. Race safety (two independent barriers): Cancel both removes the queued callback AND flips state to CANCELLED; commitNow refuses to commit unless state is still PENDING, so even a callback that already dequeued cannot commit after Cancel. State is one-way into COMMITTED / CANCELLED, so commit runs at most once and never after Cancel. No visible countdown (per decision) — the button simply stays active for 3s. No new cancellation mechanism: the real cancel is still the existing abandonSession path, now reachable because the point of no return (commit) is deferred behind the window. Swipe controller (v3.16) is unchanged and still skips InstallInstalling (mSwipe null there), so the gesture layer never interferes with this screen. Blur untouched: investigation showed all resources AND BottomAlertActivity.onCreate (window bg / setBlurBehindRadius / flags) are byte-identical across v3.14/v3.15/v3.16, so there is no proven version-to-version cause to fix — not changing it blindly. All three apks (MIUI/Google/AOSP) patched identically; classes.dex hash-verified equal after rebuild. Same debug keystore (SHA-1 cfd32bf8...). Module structure and signing unchanged. Static verification only (no device here): apktool build clean; scheduleCommit / commitNow / PendingCommit.run / the modified onPostExecute / the modified Cancel lambda were disassembled from the built classes.dex with androguard and traced branch-by-branch (state transitions, PENDING/CANCELLED/COMMITTED guards, removeCallbacks, 3000ms delay, isFinishing guard). Pill width 64dp confirmed in the rebuilt resources. What still needs a device: that the 3s window feels right and that Cancel within it actually aborts on the target ROM, and the pill's on-screen width.
…lletin
Built on v3.14-v3.17 (all untouched). Two minimal, local fixes; the 3s
pre-commit cancel logic, swipe, pill (64dp) and blur are unchanged.
1) Outside-tap no longer closes InstallInstalling.
Cause: setFinishOnTouchOutside(false) was only called after commit()
(and in an onResume side-path), so during onCreate + the whole 3s
pre-commit window the theme default windowCloseOnTouchOutside=true was
in effect and a tap outside the sheet finished the activity.
Fix: one call to setFinishOnTouchOutside(false) in
InstallInstalling.onCreate, right after super.onCreate, so it holds for
the entire lifecycle. Standard window mechanism (no touch interception);
governs only taps outside the window, so Cancel, in-sheet buttons and
swipe are unaffected. Scoped to InstallInstalling only — the confirm
screen (PackageInstallerActivity) keeps its normal outside-tap-to-close.
2) Version bulletin ("old -> new version") no longer stretches.
Cause: version_view in install_content_view_kchi.xml used
layout_height="0dp" + layout_weight="1.0" inside a fill_parent height
chain, so it expanded to absorb all leftover vertical space and the
short version info was centered in the surplus.
Fix: version_view -> layout_height="wrap_content" and weight removed, so
the block wraps its content. Text sizes, backgrounds, chips and the
inner rows are untouched; the residual inner weights become inert in a
wrap_content parent.
Cancel 3s logic verified unchanged: scheduleCommit, commitNow,
PendingCommit and the modified onPostExecute/onCreate cancel lambda are
byte-identical between the v3.17 and v3.18 builds (apples-to-apples
apktool decompile). Only new bytecode in InstallInstalling is the two
onCreate instructions; method set otherwise identical.
All three apks (MIUI/Google/AOSP) patched identically; classes.dex
hash-verified equal after rebuild. Same debug keystore (SHA-1
cfd32bf8...). Module structure unchanged.
Static verification only (no device): apktool build clean; onCreate now
runs super.onCreate -> setFinishOnTouchOutside(false) -> getIntent
(confirmed in the built dex); version_view is wrap_content with no
weight in the rebuilt resource. Needs a device to confirm the
outside-tap is dead during the 3s window and the bulletin height looks
right.
Built on v3.14-v3.18 (all untouched). Layout-only change; classes.dex is
byte-identical to v3.18 (md5 5e0e5723...) across all three apks.
Problem (device): big empty horizontal space to the LEFT of the version
value ("<app> (<versionCode>)") inside the version bulletin.
Root cause: the value chips (version_old_layout / version_new_layout,
wrap_content) sit inside their columns (installed_info_header and the
new-version column), which are layout_width=fill_parent + layout_weight=1
(each half the row) and carried android:gravity="center". Since the
column is much wider than the short version text, center gravity placed
the narrow chip in the middle -> large empty space beside it. Not
padding/margin/inset/translationX: dialog_sub_background has no inset,
RunningTextView (marquee) is wrap_content and doesn't force width, the
chips are wrap_content.
Fix (exactly 3 attributes in install_content_view_kchi.xml):
- installed_info_header: gravity "center" -> "center_vertical|start"
- new-version column: gravity "center" -> "center_vertical|start"
- version_old_layout: layout_gravity "center|top" -> "start|top"
version_new_layout has no layout_gravity, so it follows its now
start-aligned parent column. Vertical centering preserved; plate size,
pill, swipe, Cancel/3s window, outside-tap, blur all untouched. The
"Old Version"/"New version" labels are intentionally left centered.
Verified statically: diff against v3.18 shows exactly these 3 attribute
changes and nothing else in the layout; classes.dex unchanged vs v3.18;
same debug keystore (SHA-1 cfd32bf8...); module structure unchanged;
v3.14-v3.18 release zips untouched. Needs a device to confirm the value
now starts at the left in both fresh-install and update scenarios.
…d, cmp & verifyres dumps, apktool build output; keep latest release zip
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Релизы