diff --git a/app/src/main/java/se/ajpanton/statusbartweak/runtime/layout/StockLayoutCanvasController.java b/app/src/main/java/se/ajpanton/statusbartweak/runtime/layout/StockLayoutCanvasController.java index 668da58..32f2c0d 100644 --- a/app/src/main/java/se/ajpanton/statusbartweak/runtime/layout/StockLayoutCanvasController.java +++ b/app/src/main/java/se/ajpanton/statusbartweak/runtime/layout/StockLayoutCanvasController.java @@ -141,6 +141,10 @@ final class StockLayoutCanvasController { private final Map aodBeforeDrawSignatureByRoot = new WeakHashMap<>(); private final Set trackedUnlockedStockSurfaces = Collections.newSetFromMap(new WeakHashMap<>()); + private final Set suppressedHiddenStatusBarRoots = + Collections.newSetFromMap(new WeakHashMap<>()); + private final Set privacyOverlayHiddenStatusBarRoots = + Collections.newSetFromMap(new WeakHashMap<>()); private final Set trackedLockedStatusBarIconSurfaces = Collections.newSetFromMap(new WeakHashMap<>()); private final Set lockedStatusBarIconSurfaceReadyRoots = @@ -195,6 +199,7 @@ final class StockLayoutCanvasController { private boolean notificationDrawerClosePending; private int aodVisualAlphaGeneration; private int systemBarRequestedVisibleTypes = WindowInsets.Type.statusBars(); + private boolean statusBarTransientShown; StockLayoutCanvasController(RuntimeContext runtimeContext) { this.runtimeContext = runtimeContext; @@ -224,6 +229,7 @@ final class StockLayoutCanvasController { installShadeExpansionHooks(classLoader); installShadeHeaderAnimationGuardHook(classLoader); installSystemBarAttributeListener(classLoader); + installTransientBarListener(classLoader); installViewRootHook(classLoader); hooksInstalled = true; } @@ -259,6 +265,85 @@ final class StockLayoutCanvasController { return; } systemBarRequestedVisibleTypes = requestedVisibleTypes; + if (statusBarsRequestedVisible()) { + releaseSuppressedHiddenStatusBarRoots(); + privacyOverlayHiddenStatusBarRoots.clear(); + } + } + + private void installTransientBarListener(ClassLoader classLoader) { + Class commandQueueClass = ReflectionSupport.findClassIfExists( + "com.android.systemui.statusbar.CommandQueue", + classLoader); + if (commandQueueClass != null) { + XposedHookSupport.hookAllMethodsIfExists( + runtimeContext.getFramework(), + commandQueueClass, + "showTransient", + chain -> { + Object result = chain.proceed(); + updateTransientBarState(chain.getArgs(), true); + return result; + }); + XposedHookSupport.hookAllMethodsIfExists( + runtimeContext.getFramework(), + commandQueueClass, + "abortTransient", + chain -> { + Object result = chain.proceed(); + updateTransientBarState(chain.getArgs(), false); + return result; + }); + } + installStatusBarAutoHideElementListener(classLoader); + } + + private void installStatusBarAutoHideElementListener(ClassLoader classLoader) { + Class statusBarAutoHideElementClass = ReflectionSupport.findClassIfExists( + "com.android.systemui.statusbar.phone.CentralSurfacesImpl$5", + classLoader); + if (statusBarAutoHideElementClass == null) { + return; + } + XposedHookSupport.hookAllMethodsIfExists( + runtimeContext.getFramework(), + statusBarAutoHideElementClass, + "hide", + chain -> { + Object result = chain.proceed(); + updateStatusBarTransientShown(false); + return result; + }); + } + + private void updateTransientBarState(List args, boolean visible) { + Integer displayId = args != null && !args.isEmpty() ? asInteger(args.get(0)) : null; + boolean statusBarsAffected = transientArgsIncludeStatusBars(args); + if (displayId == null || displayId != 0 || !statusBarsAffected) { + return; + } + updateStatusBarTransientShown(visible); + } + + private void updateStatusBarTransientShown(boolean shown) { + statusBarTransientShown = shown; + } + + private boolean transientArgsIncludeStatusBars(List args) { + if (args == null || args.size() < 2) { + return true; + } + Object typesArg = args.get(1); + if (!(typesArg instanceof int[] types)) { + return true; + } + int statusBars = WindowInsets.Type.statusBars(); + for (int type : types) { + if ((type & statusBars) != 0) { + return true; + } + } + return false; } private void installNotificationCollectionHooks(ClassLoader classLoader) { @@ -449,6 +534,10 @@ final class StockLayoutCanvasController { Object target = chain.getThisObject(); Object alphaArg = firstArg(chain.getArgs()); if (target instanceof View view && alphaArg instanceof Float alpha) { + rememberPrivacyOverlayHiddenStatusBarRoot(view); + if (shouldSuppressHiddenStatusBarRootAlpha(view, alpha)) { + return null; + } if (shouldSuppressPrivacyOverlayStatusBarAlpha(view, alpha)) { return null; } @@ -510,6 +599,7 @@ final class StockLayoutCanvasController { Object target = chain.getThisObject(); Object translationArg = firstArg(chain.getArgs()); if (target instanceof View view && translationArg instanceof Float translationX) { + rememberPrivacyOverlayHiddenStatusBarRoot(view); if (shouldSuppressPrivacyOverlayStatusBarTranslationX(view, translationX)) { return null; } @@ -2219,6 +2309,10 @@ final class StockLayoutCanvasController { if (root == null || !hasDirectUnlockedLayoutHost(root)) { return; } + if (!statusBarsRequestedVisible() && !statusBarTransientShown) { + ownedIconHostManager.setUnlockedHostsTransform(root, 0f, 0f, 0f); + return; + } if (activeScene == null || !activeScene.isUnlocked()) { ownedIconHostManager.setUnlockedHostsTransform(root, 1f, 0f, 0f); return; @@ -3564,6 +3658,64 @@ final class StockLayoutCanvasController { return true; } + private boolean shouldSuppressHiddenStatusBarRootAlpha(View view, float alpha) { + boolean privacyOverlayRoot = privacyOverlayHiddenStatusBarRoots.contains(view); + boolean suppress = alpha > 0f + && ownHiddenViewAlphaWriteDepth <= 0 + && isPhoneStatusBarRoot(view) + && view.isAttachedToWindow() + && isLayoutEnabled(view.getContext()) + && !statusBarsRequestedVisible() + && (privacyOverlayRoot || !statusBarTransientShown); + if (suppress) { + suppressedHiddenStatusBarRoots.add(view); + } + return suppress; + } + + private void rememberPrivacyOverlayHiddenStatusBarRoot(View view) { + if (view == null + || !isPhoneStatusBarRoot(view) + || !view.isAttachedToWindow() + || statusBarsRequestedVisible() + || !isLayoutEnabled(view.getContext()) + || ongoingPrivacyChipInRoot(view) == null) { + return; + } + privacyOverlayHiddenStatusBarRoots.add(view); + } + + private void releaseSuppressedHiddenStatusBarRoots() { + if (suppressedHiddenStatusBarRoots.isEmpty()) { + return; + } + ArrayDeque staleRoots = new ArrayDeque<>(); + for (View root : suppressedHiddenStatusBarRoots) { + if (root == null || !root.isAttachedToWindow()) { + staleRoots.add(root); + continue; + } + if (!isLayoutEnabled(root.getContext())) { + staleRoots.add(root); + continue; + } + Runnable restore = () -> { + if (root.isAttachedToWindow() + && statusBarsRequestedVisible() + && root.getAlpha() <= 0f) { + root.setAlpha(1f); + } + }; + if (root.getHandler() != null) { + root.post(restore); + } else { + restore.run(); + } + staleRoots.add(root); + } + removeStaleViews(suppressedHiddenStatusBarRoots, staleRoots); + } + private boolean shouldSuppressPrivacyOverlayStatusBarAlpha(View view, float alpha) { if (alpha >= 0.99f || ownHiddenViewAlphaWriteDepth > 0