Conversation
Android 16 disables windowOptOutEdgeToEdgeEnforcement, so the opt-out added for the Android 15 enforcement stops working as soon as the target is raised. Google Play requires new submissions to target API 36 since August 31, 2026. - Raise compileSdk/targetSdk to 36 and AGP/Gradle to versions supporting it - Drop the edge-to-edge opt-out and the hardcoded status bar colors - Pad the content view by system bar, cutout and IME insets, reporting those types as zero so WebView does not pad them a second time - Let the app bars draw behind the status bar via fitsSystemWindows - In immersive mode pad by the IME only, so web content reaches the cutout - Move the WebView back handling to OnBackPressedDispatcher, as predictive back stops calling onBackPressed at target 36
The inset padding leaves the bar areas showing the window background, which does not match the page behind them. Read the page colour after load and use it for those areas and for the bar icon appearance, so transparent bars look like part of the page on any site, including ones that do not handle safe-area insets themselves. Also enable edge-to-edge below API 35, where removing the opt-out has no effect, and let AppTheme.WebView and AppTheme.News inherit the night theme.
The overflow icon inherited colorControlNormal, which is dark and left it at 1.4:1 against the red toolbar, below the 3:1 WCAG needs for interface icons. A toolbar-scoped overlay keeps the tint off the light screens.
The settings container carried a 25dp margin and the scrolled content another 20dp at the end, so the content sat off-centre and the outsideOverlay scrollbar floated 25dp away from the screen edge. Move the horizontal spacing onto the content itself and let the ScrollView span the full width. Also drop colorSignatureDark, unused since the status bar colors went away.
The list titles were upper-cased and centred inside a weighted column that starts after the drag handle, which left them visibly off-centre. Align them to the start in a fixed handle column instead. The settings action buttons sat flush against the last setting with nothing between them; give them a divider and drop the negative bottom margin.
Two neutral text buttons are no longer a recommended pairing; make Save a filled button and keep Cancel as a text button. The container also stacked a 25dp bottom margin on top of the gesture inset, leaving 56dp of dead space.
The light scheme matches a Material tonal-spot scheme seeded with the
signature red, but the dark one does not: its primary was #F0BE6D, an amber
from an unrelated seed, which is why the switches and buttons went amber under
the red app bar in dark mode. Only 21 of its 141 values agreed with the seed,
against 92 of 141 in the light scheme.
Regenerate both with material-color-utilities from #B71C1C so the two schemes
belong to the same family and can be reproduced:
new SchemeTonalSpot(Hct.fromInt(argbFromHex('#B71C1C')), isDark, contrast)
The swipe backgrounds were hardcoded outside the theme: #EF6C00 orange next to the signature red, and #FD2C2C for delete, a third red on top of the app bar red and the scheme error red. Neither had a dark variant. Point them at errorContainer and tertiaryContainer, which carry the same meanings and follow the theme, and drop the unused swipe_action_positive.
One sampled colour cannot match both ends: theme-color matches the header but not the content under the bottom bar, and the body background matches the content but not the header. On a page with a dark header over light content the mismatched end was off by 224 of 255 per channel. Take theme-color for the top and the body background for the bottom, and pick each bar's icon colour from its own side.
- restore the @SuppressLint that an insertion stranded on the wrong method - read the page background once instead of twice, and let one place own the colour fallbacks instead of the JS, the Java and the Kotlin each doing it - skip the colour sampling on the error path and in immersive mode, where it paints nothing - reuse the bar drawable across navigations instead of rebuilding it - MaterialDivider, theme button styles and colour roles instead of hand-rolled equivalents; move the toolbar overlay in with the other theme overlays
- keep the app bar from recolouring on scroll: Widget.Material3.AppBarLayout defaults to liftOnScroll with its own colour, which the Toolbar's own background used to hide - never let a page crash the activity: the rgb() digits were unbounded and parsed outside the guard - accept the short hex form, and read the alpha instead of matching the 'rgba(0, 0, 0, 0' prefix, which also caught 0.85 - fall back to the theme surface when nothing parses, instead of leaving the previous page's colours on screen - clear the navigation bar scrim for every screen, not only the web view
dkodr
marked this pull request as ready for review
September 11, 2026 12:33
This was referenced Sep 11, 2026
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.
Android 16 deprecates and disables
windowOptOutEdgeToEdgeEnforcement(behaviour changes), so the opt-out added in e5f97b3 for the Android 15
enforcement stops working the moment the target is raised. Since 31 August 2026
Google Play requires new submissions and updates to target API 36, so raising it
is no longer optional. This handles the insets instead of opting out.
Closes #171. Also fixes #193 and #194. It supersedes #216 and covers the same
ground as #217, which I only found after starting — thanks to @tedgravlin for
both; the back-navigation part here migrates to
OnBackPressedDispatcherratherthan only enabling the callback, because at target 36
onBackPressed()is nolonger called at all.
Target and toolchain
compileSdkandtargetSdkto 36; AGP to 8.13.2 and Gradle to 8.13, theminimum that supports API 36. Deliberately still on the 8.x line, since the
manifest-rewriting build logic does not survive AGP 9.
versionCodeandversionNameare untouched — that is your release step.Edge-to-edge
systemBars | displayCutout | imeand report thosetypes back as
Insets.NONE, per the WebView inset guidance, so theWebView does not pad them a second time.
fitsSystemWindows.setDecorFitsSystemWindows(false)plus transparent bar colours for API 28–34,where removing the opt-out alone does nothing.
That is Immersive mode - black status bar #193.
OnBackPressedDispatcher(Input hidden behind keyboard #194 is thekeyboard part; this is the predictive-back part).
Bars that match the page
Padding the WebView leaves two strips showing the window background, which does
not match the page behind them. After each load the page reports its
theme-colorand its computedbodybackground; the first fills the top strip,the second the bottom, and each bar takes its icon colour from its own side.
One sampled colour is not enough: on a page with a dark header over light
content the mismatched end was off by 224 of 255 per channel. Measured on three
pages, comparing theme-color for both strips, body background for both, and one
each:
Pages that do not handle safe-area insets themselves work the same as any
other, which is why this does not rely on
env(safe-area-inset-*).Visual cleanup
These are separate commits and can be dropped if you would rather keep this PR
to the release blocker.
Material tonal-spot scheme seeded with
#B71C1Cin 92 of 141 values; the darkone matched in 21, and its primary was
#F0BE6D, an amber from an unrelatedseed — hence amber switches under a red app bar in dark mode. Both are
regenerated with
material-color-utilities:new SchemeTonalSpot(Hct.fromInt(argbFromHex('#B71C1C')), isDark, contrast).This does change every dark-mode colour, so it is worth a look.
colorControlNormalon the red app bar, below the 3:1 WCAG needs forinterface icons.
#EF6C00next tothe signature red, and
#FD2C2Cfor delete, a third red. NowcolorErrorContainerandcolorTertiaryContainer, which follow the theme andhave dark variants.
outsideOverlayscrollbar floated 25dp from the screen edge.starts after the drag handle, which left them visibly off-centre.
buttons are also no longer a recommended pairing, so Save is filled now.
colorSignatureDarkandswipe_action_positive, both unreferenced.Testing
Pixel on Android 17 (API 37, WebView 151) and an API 34 emulator, light and dark
system themes: main screen, settings, a light and a dark page, immersive on and
off, back through WebView history → base URL → background, and a page with a
bottom-anchored input and a sticky bottom bar with the keyboard open.
Not tested: landscape, cutouts on the short edges, and
sw >= 600dp, wheretarget 36 additionally stops honouring the portrait lock.
RoboTests.initializationErrorandUtilUnitTests.faviconFromWebManifestfailidentically on unmodified
dev— the first has no runnable methods, the secondasserts against a live favicon URL that has changed.
Two notes on the build
libadblock-client.sofromEdsuns/AdblockAndroid:ad-filter:v0.9.1has 4 KBLOADalignment in all four ABIs. Android 17 shows a compatibility warningnaming it, and since 1 November 2025 Play rejects apps targeting Android 15+
whose native code is not 16 KB aligned. The library has had no release since
v0.9.1, so this needs a rebuilt or replaced dependency — independent of this PR,
but it will block the same release.
Also,
preBuild.dependsOn renameManifestmovesAndroidManifest.xmlout of theway and only
assemble'sdoLastmoves it back, so twoassembletasks in oneGradle invocation delete it, and a failed build leaves the tree dirty. Worth a
line in the README until it changes.