Handle app surfaces over lockscreen

This commit is contained in:
ajp_anton
2026-07-11 12:59:04 +00:00
parent 682782c232
commit 560f4e19ba
5 changed files with 139 additions and 21 deletions
@@ -566,6 +566,15 @@ final class BatteryBarController {
if (isKeyguardRoot(root)) { if (isKeyguardRoot(root)) {
return BatteryBarGeometry.Scenario.LOCKSCREEN; return BatteryBarGeometry.Scenario.LOCKSCREEN;
} }
SceneKey scene = runtimeContext.getModeStateRepository().getActiveScene();
if (isPhoneRoot(root)
&& scene != null
&& scene.isLockscreen()
&& isKeyguardLocked(root.getContext())
&& !isStatusBarSurfaceHidden(root)
&& !isFullscreenSystemBarState()) {
return BatteryBarGeometry.Scenario.LOCKSCREEN;
}
boolean fullscreen = isPhoneRoot(root) boolean fullscreen = isPhoneRoot(root)
&& (isUnlockedScene() || isLockedPhoneFullscreenRoot(root)) && (isUnlockedScene() || isLockedPhoneFullscreenRoot(root))
&& (isStatusBarSurfaceHidden(root) || isFullscreenSystemBarState()); && (isStatusBarSurfaceHidden(root) || isFullscreenSystemBarState());
@@ -623,10 +632,14 @@ final class BatteryBarController {
return scene == null || !scene.isUnlocked(); return scene == null || !scene.isUnlocked();
} }
if (isPhoneRoot(root)) { if (isPhoneRoot(root)) {
if (isLockedPhoneStatusBarRoot(root)) { if (scene == null || scene.isUnlocked()) {
return isLockedPhoneFullscreenRoot(root); return true;
} }
return scene == null || scene.isUnlocked() || isLockedPhoneFullscreenRoot(root); if (isLockedPhoneStatusBarRoot(root)) {
return isLockedPhoneFullscreenRoot(root)
|| (!isStatusBarSurfaceHidden(root) && !isFullscreenSystemBarState());
}
return false;
} }
return true; return true;
} }
@@ -17,6 +17,7 @@ import se.ajpanton.statusbartweak.runtime.render.StatusBarTintTarget;
final class DarkIconDispatcherGuard { final class DarkIconDispatcherGuard {
private final RuntimeContext runtimeContext; private final RuntimeContext runtimeContext;
private final BooleanSupplier lockscreenSceneSupplier; private final BooleanSupplier lockscreenSceneSupplier;
private final BooleanSupplier lockscreenDarkIconsSupplier;
private final Runnable refreshAllRoots; private final Runnable refreshAllRoots;
private final Set<Object> dispatchers = private final Set<Object> dispatchers =
Collections.newSetFromMap(new WeakHashMap<>()); Collections.newSetFromMap(new WeakHashMap<>());
@@ -25,10 +26,12 @@ final class DarkIconDispatcherGuard {
DarkIconDispatcherGuard( DarkIconDispatcherGuard(
RuntimeContext runtimeContext, RuntimeContext runtimeContext,
BooleanSupplier lockscreenSceneSupplier, BooleanSupplier lockscreenSceneSupplier,
BooleanSupplier lockscreenDarkIconsSupplier,
Runnable refreshAllRoots Runnable refreshAllRoots
) { ) {
this.runtimeContext = runtimeContext; this.runtimeContext = runtimeContext;
this.lockscreenSceneSupplier = lockscreenSceneSupplier; this.lockscreenSceneSupplier = lockscreenSceneSupplier;
this.lockscreenDarkIconsSupplier = lockscreenDarkIconsSupplier;
this.refreshAllRoots = refreshAllRoots; this.refreshAllRoots = refreshAllRoots;
} }
@@ -48,7 +51,7 @@ final class DarkIconDispatcherGuard {
chain -> { chain -> {
Object dispatcher = chain.getThisObject(); Object dispatcher = chain.getThisObject();
track(dispatcher); track(dispatcher);
if (isLockscreenScene()) { if (isLockscreenScene() && !shouldUseDarkLockscreenIcons()) {
forceLightState(dispatcher); forceLightState(dispatcher);
} }
Object result = chain.proceed(); Object result = chain.proceed();
@@ -63,7 +66,11 @@ final class DarkIconDispatcherGuard {
return; return;
} }
for (Object dispatcher : new ArrayList<>(dispatchers)) { for (Object dispatcher : new ArrayList<>(dispatchers)) {
if (shouldUseDarkLockscreenIcons()) {
restoreState(dispatcher, forcedStates.remove(dispatcher));
} else {
forceLightState(dispatcher); forceLightState(dispatcher);
}
ReflectionSupport.invokeMethod(dispatcher, "applyIconTint"); ReflectionSupport.invokeMethod(dispatcher, "applyIconTint");
} }
} }
@@ -85,6 +92,10 @@ final class DarkIconDispatcherGuard {
return lockscreenSceneSupplier.getAsBoolean(); return lockscreenSceneSupplier.getAsBoolean();
} }
private boolean shouldUseDarkLockscreenIcons() {
return lockscreenDarkIconsSupplier.getAsBoolean();
}
private void track(Object dispatcher) { private void track(Object dispatcher) {
if (dispatcher != null) { if (dispatcher != null) {
dispatchers.add(dispatcher); dispatchers.add(dispatcher);
@@ -92,6 +103,10 @@ final class DarkIconDispatcherGuard {
} }
private void updateTintTarget(Object dispatcher) { private void updateTintTarget(Object dispatcher) {
if (isLockscreenScene() && shouldUseDarkLockscreenIcons()) {
StatusBarTintTarget.setDarkIcons(true);
return;
}
Object intensity = ReflectionSupport.getFieldValue(dispatcher, "mDarkIntensity"); Object intensity = ReflectionSupport.getFieldValue(dispatcher, "mDarkIntensity");
if (intensity instanceof Number number) { if (intensity instanceof Number number) {
StatusBarTintTarget.setDarkIcons(number.floatValue() > 0.5f); StatusBarTintTarget.setDarkIcons(number.floatValue() > 0.5f);
@@ -80,6 +80,7 @@ final class StockLayoutCanvasController {
"mEntries", "mEntries",
"entries" "entries"
}; };
private static final int APPEARANCE_LIGHT_STATUS_BARS = 8;
private static final Set<String> TARGET_ID_NAMES = Set.of( private static final Set<String> TARGET_ID_NAMES = Set.of(
"clock", "clock",
"left_clock_container", "left_clock_container",
@@ -198,6 +199,7 @@ final class StockLayoutCanvasController {
private int notificationDrawerCloseGeneration; private int notificationDrawerCloseGeneration;
private boolean notificationDrawerClosePending; private boolean notificationDrawerClosePending;
private int aodVisualAlphaGeneration; private int aodVisualAlphaGeneration;
private int systemBarAppearance;
private int systemBarRequestedVisibleTypes = WindowInsets.Type.statusBars(); private int systemBarRequestedVisibleTypes = WindowInsets.Type.statusBars();
private boolean statusBarTransientShown; private boolean statusBarTransientShown;
@@ -207,6 +209,7 @@ final class StockLayoutCanvasController {
darkIconDispatcherGuard = new DarkIconDispatcherGuard( darkIconDispatcherGuard = new DarkIconDispatcherGuard(
runtimeContext, runtimeContext,
this::isLockscreenScene, this::isLockscreenScene,
this::shouldUseDarkLockscreenStatusbarTint,
this::scheduleUnlockedAppearanceRefreshForAllRoots); this::scheduleUnlockedAppearanceRefreshForAllRoots);
lockscreenCardsNotificationStripRenderer = lockscreenCardsNotificationStripRenderer =
new LockscreenCardsNotificationStripRenderer(); new LockscreenCardsNotificationStripRenderer();
@@ -247,29 +250,39 @@ final class StockLayoutCanvasController {
"onSystemBarAttributesChanged", "onSystemBarAttributesChanged",
chain -> { chain -> {
Object result = chain.proceed(); Object result = chain.proceed();
updateSystemBarRequestedVisibleTypes(chain.getArgs()); updateSystemBarAttributes(chain.getArgs());
return result; return result;
}); });
} }
private void updateSystemBarRequestedVisibleTypes(List<Object> args) { private void updateSystemBarAttributes(List<Object> args) {
if (args == null || args.size() <= 5) { if (args == null || args.size() < 2) {
return; return;
} }
Integer displayId = asInteger(args.get(0)); Integer displayId = asInteger(args.get(0));
Integer requestedVisibleTypes = asInteger(args.get(5)); Integer appearance = asInteger(args.get(1));
if (displayId == null || displayId != 0 || requestedVisibleTypes == null) { Integer requestedVisibleTypes = args.size() > 5 ? asInteger(args.get(5)) : null;
return; if (displayId == null || displayId != 0 || appearance == null) {
}
if (systemBarRequestedVisibleTypes == requestedVisibleTypes) {
return; return;
} }
boolean appearanceChanged = systemBarAppearance != appearance;
systemBarAppearance = appearance;
if (requestedVisibleTypes != null
&& systemBarRequestedVisibleTypes != requestedVisibleTypes) {
systemBarRequestedVisibleTypes = requestedVisibleTypes; systemBarRequestedVisibleTypes = requestedVisibleTypes;
if (statusBarsRequestedVisible()) { if (statusBarsRequestedVisible()) {
releaseSuppressedHiddenStatusBarRoots(); releaseSuppressedHiddenStatusBarRoots();
privacyOverlayHiddenStatusBarRoots.clear(); privacyOverlayHiddenStatusBarRoots.clear();
} }
} }
if (appearanceChanged) {
darkIconDispatcherGuard.forceLightStateForAll();
requestUnlockedLayoutRefreshForAllRoots();
} else if (statusBarsRequestedVisible()) {
releaseSuppressedHiddenStatusBarRoots();
privacyOverlayHiddenStatusBarRoots.clear();
}
}
private void installTransientBarListener(ClassLoader classLoader) { private void installTransientBarListener(ClassLoader classLoader) {
Class<?> commandQueueClass = ReflectionSupport.findClassIfExists( Class<?> commandQueueClass = ReflectionSupport.findClassIfExists(
@@ -1807,9 +1820,18 @@ final class StockLayoutCanvasController {
return; return;
} }
dirtyUnlockedLayoutRoots.add(root); dirtyUnlockedLayoutRoots.add(root);
Runnable refresh = () -> {
if (root.isAttachedToWindow()) {
root.requestLayout(); root.requestLayout();
root.invalidate(); root.invalidate();
} }
};
if (isViewThread(root)) {
refresh.run();
} else if (root.getHandler() != null) {
root.getHandler().post(refresh);
}
}
private View rootForShadeTarget(Object target) { private View rootForShadeTarget(Object target) {
if (!(target instanceof View view)) { if (!(target instanceof View view)) {
@@ -2157,6 +2179,15 @@ final class StockLayoutCanvasController {
return scene != null && scene.isLockscreen(); return scene != null && scene.isLockscreen();
} }
private boolean statusBarTintTargetEnabledForScene(SceneKey scene) {
return scene != null && (scene.isUnlocked() || shouldUseDarkLockscreenStatusbarTint());
}
private boolean shouldUseDarkLockscreenStatusbarTint() {
return isLockscreenScene()
&& (systemBarAppearance & APPEARANCE_LIGHT_STATUS_BARS) != 0;
}
private void scheduleUnlockedAppearanceRefreshForRoot(View root) { private void scheduleUnlockedAppearanceRefreshForRoot(View root) {
scheduleUnlockedAppearanceRefreshForRoot(root, false); scheduleUnlockedAppearanceRefreshForRoot(root, false);
} }
@@ -2282,7 +2313,8 @@ final class StockLayoutCanvasController {
if (unlockedIconRenderController.refreshUnlockedClockText( if (unlockedIconRenderController.refreshUnlockedClockText(
root, root,
unlockedHosts.clockHost, unlockedHosts.clockHost,
activeScene)) { activeScene,
statusBarTintTargetEnabledForScene(activeScene))) {
bringUnlockedHostsAndDebugOverlayToFront(root); bringUnlockedHostsAndDebugOverlayToFront(root);
} }
} }
@@ -2491,6 +2523,7 @@ final class StockLayoutCanvasController {
unlockedHosts.statusHost, unlockedHosts.statusHost,
unlockedHosts.notificationHost, unlockedHosts.notificationHost,
activeScene, activeScene,
statusBarTintTargetEnabledForScene(activeScene),
rootDirty, rootDirty,
lockedCarrierTextSource()); lockedCarrierTextSource());
boolean shouldShowUnlockedHosts = unlockedScene boolean shouldShowUnlockedHosts = unlockedScene
@@ -3493,8 +3526,13 @@ final class StockLayoutCanvasController {
boolean shelfSurface = isCardsNotificationIconSurface(view); boolean shelfSurface = isCardsNotificationIconSurface(view);
boolean shelfBackgroundSurface = isCardsNotificationShelfBackgroundSurface(view); boolean shelfBackgroundSurface = isCardsNotificationShelfBackgroundSurface(view);
boolean shadeIconOnlySurface = "keyguard_icononly_container_view".equals(idName); boolean shadeIconOnlySurface = "keyguard_icononly_container_view".equals(idName);
boolean clockSurface = "clock".equals(idName)
|| "left_clock_container".equals(idName)
|| viewClassName.contains("QSClockIndicatorView")
|| viewClassName.contains("statusbar.policy.Clock");
if (!("system_icons".equals(idName) if (!("system_icons".equals(idName)
|| carrierSurface || carrierSurface
|| clockSurface
|| "system_icon_area".equals(idName) || "system_icon_area".equals(idName)
|| "statusIcons".equals(idName) || "statusIcons".equals(idName)
|| "battery".equals(idName) || "battery".equals(idName)
@@ -4916,7 +4954,8 @@ final class StockLayoutCanvasController {
lockscreenCardsNotificationRenderGenerationByRoot.remove(root); lockscreenCardsNotificationRenderGenerationByRoot.remove(root);
return; return;
} }
if (runtimeContext.getModeStateRepository().isSystemUiShadeLocked()) { if (runtimeContext.getModeStateRepository().isSystemUiShadeLocked()
|| hasVisibleLockscreenShadeHeader(root)) {
removeLockscreenCardsNotificationHost(root); removeLockscreenCardsNotificationHost(root);
lockscreenCardsNotificationRenderGenerationByRoot.remove(root); lockscreenCardsNotificationRenderGenerationByRoot.remove(root);
return; return;
@@ -4956,6 +4995,27 @@ final class StockLayoutCanvasController {
} }
} }
private boolean hasVisibleLockscreenShadeHeader(View root) {
if (root == null || !isNotificationShadeWindowRoot(root)) {
return false;
}
final boolean[] found = {false};
walkTree(root, view -> {
if (found[0] || !(view instanceof TextView textView)) {
return;
}
if (!textView.isShown() || textView.getAlpha() <= 0f) {
return;
}
String idName = ViewIdNames.idName(textView);
String className = textView.getClass().getName();
if ("header_clock".equals(idName) || className.contains("QSClockHeaderView")) {
found[0] = true;
}
});
return found[0];
}
private void renderAodCardsNotificationStripIfNeeded( private void renderAodCardsNotificationStripIfNeeded(
View root, View root,
SbtSettings settings, SbtSettings settings,
@@ -484,4 +484,13 @@ final class LayoutPlan {
notificationPlacements, notificationPlacements,
carrierPlacement); carrierPlacement);
} }
LayoutPlan withCarrierPlacement(ClockPlacement placement) {
return new LayoutPlan(
clockPlacement,
chipPlacements,
statusPlacements,
notificationPlacements,
placement);
}
} }
@@ -65,6 +65,7 @@ public final class UnlockedIconSnapshotRenderController {
ViewGroup statusHost, ViewGroup statusHost,
ViewGroup notificationHost, ViewGroup notificationHost,
SceneKey scene, SceneKey scene,
boolean statusBarTintTargetEnabled,
boolean forceStockRefresh, boolean forceStockRefresh,
TextView externalCarrierView TextView externalCarrierView
) { ) {
@@ -80,7 +81,7 @@ public final class UnlockedIconSnapshotRenderController {
return new RenderResult(true, true, true); return new RenderResult(true, true, true);
} }
SbtSettings settings = RuntimeSettingsCache.get(root.getContext()); SbtSettings settings = RuntimeSettingsCache.get(root.getContext());
setStatusBarTintTargetEnabled(scene.isUnlocked()); setStatusBarTintTargetEnabled(statusBarTintTargetEnabled);
layoutHostToRoot(root, clockHost); layoutHostToRoot(root, clockHost);
layoutHostToRoot(root, carrierHost); layoutHostToRoot(root, carrierHost);
layoutHostToRoot(root, chipHost); layoutHostToRoot(root, chipHost);
@@ -235,6 +236,7 @@ public final class UnlockedIconSnapshotRenderController {
lastClockSourceGeometryByRoot.put(root, currentClockSourceGeometry); lastClockSourceGeometryByRoot.put(root, currentClockSourceGeometry);
layoutPlan = withCurrentDotColors(layoutPlan, collectedIcons); layoutPlan = withCurrentDotColors(layoutPlan, collectedIcons);
layoutPlan = withCurrentClockAppearance(root, layoutPlan, collectedIcons, settings); layoutPlan = withCurrentClockAppearance(root, layoutPlan, collectedIcons, settings);
layoutPlan = withCurrentCarrierAppearance(layoutPlan);
lastLayoutPlanByRoot.put(root, layoutPlan); lastLayoutPlanByRoot.put(root, layoutPlan);
if (clockEnabledForScene(settings, scene) && layoutPlan.clockPlacement != null) { if (clockEnabledForScene(settings, scene) && layoutPlan.clockPlacement != null) {
clockRenderer.render(clockHost, layoutPlan.clockPlacement); clockRenderer.render(clockHost, layoutPlan.clockPlacement);
@@ -347,7 +349,8 @@ public final class UnlockedIconSnapshotRenderController {
public boolean refreshUnlockedClockText( public boolean refreshUnlockedClockText(
View root, View root,
ViewGroup clockHost, ViewGroup clockHost,
SceneKey scene SceneKey scene,
boolean statusBarTintTargetEnabled
) { ) {
if (root == null if (root == null
|| clockHost == null || clockHost == null
@@ -361,7 +364,7 @@ public final class UnlockedIconSnapshotRenderController {
return false; return false;
} }
SbtSettings settings = RuntimeSettingsCache.get(root.getContext()); SbtSettings settings = RuntimeSettingsCache.get(root.getContext());
setStatusBarTintTargetEnabled(scene.isUnlocked()); setStatusBarTintTargetEnabled(statusBarTintTargetEnabled);
if (!clockEnabledForScene(settings, scene)) { if (!clockEnabledForScene(settings, scene)) {
return false; return false;
} }
@@ -595,6 +598,24 @@ public final class UnlockedIconSnapshotRenderController {
return plan.withClockPlacement(updatedClock); return plan.withClockPlacement(updatedClock);
} }
private LayoutPlan withCurrentCarrierAppearance(LayoutPlan plan) {
int targetColor = statusBarTintTargetColor();
if (plan == null
|| plan.carrierPlacement == null
|| !hasAlpha(targetColor)
|| plan.carrierPlacement.textColorOverride == targetColor) {
return plan;
}
ClockPlacement previous = plan.carrierPlacement;
return plan.withCarrierPlacement(new ClockPlacement(
previous.source,
previous.contentDescription,
previous.rows,
previous.bounds,
targetColor,
previous.textSizePx));
}
private int clockReferenceColor(ClockPlacement placement, CollectedIcons icons) { private int clockReferenceColor(ClockPlacement placement, CollectedIcons icons) {
int targetColor = statusBarTintTargetColor(); int targetColor = statusBarTintTargetColor();
if (hasAlpha(targetColor)) { if (hasAlpha(targetColor)) {