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 adb6aa0..c3d9846 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 @@ -140,6 +140,8 @@ final class StockLayoutCanvasController { Collections.newSetFromMap(new WeakHashMap<>()); private final Set dirtyUnlockedLayoutRoots = Collections.newSetFromMap(new WeakHashMap<>()); + private final Set pendingDrawerStatusChipRefreshRoots = + Collections.newSetFromMap(new WeakHashMap<>()); private final Set hookedPluginClassLoaders = Collections.newSetFromMap(new IdentityHashMap<>()); private final Set pendingPluginClassLoaders = @@ -288,6 +290,7 @@ final class StockLayoutCanvasController { } if (expansion <= 0.01f) { notificationDrawerWasOpen = false; + flushDeferredDrawerStatusChipRefreshes(); return; } if (expansion <= 0.05f) { @@ -337,6 +340,7 @@ final class StockLayoutCanvasController { } else if (previousState == STATUS_BAR_STATE_SHADE_LOCKED && currentState != STATUS_BAR_STATE_SHADE_LOCKED) { notificationDrawerWasOpen = false; + flushDeferredDrawerStatusChipRefreshes(); } } @@ -1153,9 +1157,6 @@ final class StockLayoutCanvasController { if (activeScene == null) { return; } - if (!runtimeContext.getModeStateRepository().isSystemUiShadeLocked() && !activeScene.isAod()) { - notificationDrawerWasOpen = false; - } if (isNotificationShadeWindowRoot(root)) { if (shouldPrepareCarrierlessAodNotificationTransition(settings, activeScene)) { prepareCarrierlessAodNotificationTransition(root); @@ -1331,8 +1332,8 @@ final class StockLayoutCanvasController { if (activeScene != null && activeScene.isUnlocked()) { if (isKnownRootGeometryChanged(root)) { ownedIconHostManager.setUnlockedHostsVisible(root, false); + removeDebugOverlay(root); } - removeDebugOverlay(root); } } @@ -1385,13 +1386,19 @@ final class StockLayoutCanvasController { private void markStatusChipLifecycleDirtyIfNeeded(View view, View fallbackRoot) { if (view == null || !isStatusChipCandidate(view)) { View root = view != null ? unlockedStatusBarRootFor(view) : fallbackRoot; + if (deferDrawerStatusChipRefresh(root)) { + return; + } requestUnlockedLayoutRefresh(root); return; } + View root = unlockedStatusBarRootFor(view); + if (deferDrawerStatusChipRefresh(root != null ? root : fallbackRoot)) { + return; + } int signature = statusChipLayoutSignature(view); Integer previous = statusChipLifecycleLayoutSignatureByView.put(view, signature); if (previous == null || previous != signature) { - View root = unlockedStatusBarRootFor(view); requestUnlockedLayoutRefresh(root != null ? root : fallbackRoot); } } @@ -1408,6 +1415,9 @@ final class StockLayoutCanvasController { if (root == null || !root.isAttachedToWindow()) { return; } + if (deferDrawerStatusChipRefresh(root)) { + return; + } int signature = statusChipContentSignature(chipSource); Integer previous = statusChipContentSignatureByRoot.put(root, signature); if (previous != null && previous == signature) { @@ -1434,10 +1444,39 @@ final class StockLayoutCanvasController { requestUnlockedLayoutRefresh(root); } + private boolean deferDrawerStatusChipRefresh(View root) { + if (!notificationDrawerWasOpen || root == null || !root.isAttachedToWindow()) { + return false; + } + SceneKey activeScene = runtimeContext.getModeStateRepository().getActiveScene(); + if (activeScene == null || !activeScene.isUnlocked()) { + return false; + } + pendingDrawerStatusChipRefreshRoots.add(root); + return true; + } + + private void flushDeferredDrawerStatusChipRefreshes() { + if (pendingDrawerStatusChipRefreshRoots.isEmpty()) { + return; + } + ArrayDeque roots = new ArrayDeque<>(pendingDrawerStatusChipRefreshRoots); + pendingDrawerStatusChipRefreshRoots.clear(); + while (!roots.isEmpty()) { + View root = roots.removeFirst(); + if (root != null && root.isAttachedToWindow()) { + requestStatusChipRefresh(root); + } + } + } + private void requestStatusChipRefresh(View root) { if (root == null || !root.isAttachedToWindow()) { return; } + if (deferDrawerStatusChipRefresh(root)) { + return; + } unlockedIconRenderController.forceChipRefresh(root); requestUnlockedLayoutRefresh(root); } @@ -1517,7 +1556,8 @@ final class StockLayoutCanvasController { return 0; } int result = 17; - result = 31 * result + System.identityHashCode(view); + result = 31 * result + view.getClass().getName().hashCode(); + result = 31 * result + view.getId(); result = 31 * result + view.getVisibility(); result = 31 * result + view.getWidth(); result = 31 * result + view.getHeight(); @@ -1528,8 +1568,10 @@ final class StockLayoutCanvasController { } if (view instanceof ImageView imageView) { Drawable drawable = imageView.getDrawable(); - result = 31 * result + System.identityHashCode(drawable); if (drawable != null) { + result = 31 * result + drawable.getClass().getName().hashCode(); + result = 31 * result + drawable.getIntrinsicWidth(); + result = 31 * result + drawable.getIntrinsicHeight(); result = 31 * result + drawable.getBounds().hashCode(); } } @@ -1559,8 +1601,10 @@ final class StockLayoutCanvasController { } if (view instanceof ImageView imageView) { Drawable drawable = imageView.getDrawable(); - result = 31 * result + System.identityHashCode(drawable); if (drawable != null) { + result = 31 * result + drawable.getClass().getName().hashCode(); + result = 31 * result + drawable.getIntrinsicWidth(); + result = 31 * result + drawable.getIntrinsicHeight(); result = 31 * result + drawable.getAlpha(); result = 31 * result + drawable.getLevel(); result = 31 * result + drawable.getBounds().hashCode(); @@ -1936,13 +1980,13 @@ final class StockLayoutCanvasController { if (!aodScene && renderResult.shouldOwnLockscreen()) { hideOrDiscoverLockedStatusBarIconSurfaces(root); } - if (aodScene) { + if (aodScene || renderResult.shouldOwnLockscreen()) { applyDebugOverlayIfNeeded( root, true, settings, renderResult.layoutChanged(), - true, + aodScene, activeScene); } else { removeDebugOverlay(root); @@ -2069,6 +2113,7 @@ final class StockLayoutCanvasController { ) { int result = 17; result = 31 * result + (layoutEnabled ? 1 : 0); + result = 31 * result + (scene != null ? scene.hashCode() : 0); result = 31 * result + (root != null ? root.getWidth() : 0); result = 31 * result + (root != null ? root.getHeight() : 0); if (settings != null) { @@ -3874,6 +3919,7 @@ final class StockLayoutCanvasController { statusChipContentSignatureByRoot.clear(); statusChipLayoutSignatureByRoot.clear(); statusChipLifecycleLayoutSignatureByView.clear(); + pendingDrawerStatusChipRefreshRoots.clear(); } private static boolean containsAny(String value, Set tokens) { diff --git a/app/src/main/java/se/ajpanton/statusbartweak/runtime/render/DebugBoundsOverlayController.java b/app/src/main/java/se/ajpanton/statusbartweak/runtime/render/DebugBoundsOverlayController.java index c11a552..df0b3d9 100644 --- a/app/src/main/java/se/ajpanton/statusbartweak/runtime/render/DebugBoundsOverlayController.java +++ b/app/src/main/java/se/ajpanton/statusbartweak/runtime/render/DebugBoundsOverlayController.java @@ -107,9 +107,8 @@ public final class DebugBoundsOverlayController { removeOverlays(parent); return; } - ViewGroup overlayParent = overlayParent(root, parent, layoutEnabled, aodStockVisualizersEnabled, settings); - removeOverlayFromOtherParent(parent, overlayParent); - OverlayView host = ensureOverlay(overlayParent); + removeOverlaysFromOwnedHosts(parent); + OverlayView host = ensureOverlay(parent); if (host == null) { return; } @@ -122,7 +121,7 @@ public final class DebugBoundsOverlayController { host.layout(0, 0, width, height); } host.setSpecs(specs); - if (overlayParent.getChildAt(overlayParent.getChildCount() - 1) != host) { + if (parent.getChildAt(parent.getChildCount() - 1) != host) { host.bringToFront(); } } @@ -134,18 +133,22 @@ public final class DebugBoundsOverlayController { boolean aodStockVisualizersEnabled, SceneKey scene ) { - if (!layoutEnabled || root == null || settings == null - || !aodStockVisualizersEnabled - || !settings.debugVisualizeAodStockContainers) { + if (!layoutEnabled || root == null || settings == null) { return 0; } int result = 17; - ViewGroup notificationContainer = aodNotificationContainer(root, scene); - result = 31 * result + signature( - aodNotificationBounds(root, scene)); - result = 31 * result + signature(liveAodStatusIconsBounds(root)); - result = 31 * result + signature( - derivedAodStatusbarBounds(root, notificationContainer, settings, scene)); + if (aodStockVisualizersEnabled && settings.debugVisualizeAodStockContainers) { + ViewGroup notificationContainer = aodNotificationContainer(root, scene); + result = 31 * result + signature( + aodNotificationBounds(root, scene)); + result = 31 * result + signature(liveAodStatusIconsBounds(root)); + result = 31 * result + signature( + derivedAodStatusbarBounds(root, notificationContainer, settings, scene)); + } + result = 31 * result + hostSignature(root, OwnedIconHostManager.TAG_UNLOCKED_NOTIFICATION_HOST); + result = 31 * result + hostSignature(root, OwnedIconHostManager.TAG_UNLOCKED_STATUS_HOST); + result = 31 * result + hostSignature(root, OwnedIconHostManager.TAG_UNLOCKED_CLOCK_HOST); + result = 31 * result + hostSignature(root, OwnedIconHostManager.TAG_UNLOCKED_CHIP_HOST); return result; } @@ -255,6 +258,12 @@ public final class DebugBoundsOverlayController { } int right = bounds.left + bounds.width; int bottom = bounds.top + bounds.height; + if (bounds.left == 0 + && bounds.top == 0 + && bounds.width >= root.getWidth() + && bounds.height >= root.getHeight()) { + return false; + } return bounds.left >= 0 && bounds.top >= 0 && right <= root.getWidth() @@ -410,49 +419,15 @@ public final class DebugBoundsOverlayController { return host; } - private ViewGroup overlayParent( - View root, - ViewGroup rootParent, - boolean layoutEnabled, - boolean aodStockVisualizersEnabled, - SbtSettings settings - ) { - if (layoutEnabled - && aodStockVisualizersEnabled - && settings != null - && settings.debugVisualizeAodStockContainers) { - View chipHost = findDirectTaggedChild(rootParent, OwnedIconHostManager.TAG_UNLOCKED_CHIP_HOST); - if (chipHost instanceof ViewGroup chipHostGroup) { - return chipHostGroup; - } - View statusHost = findDirectTaggedChild(rootParent, OwnedIconHostManager.TAG_UNLOCKED_STATUS_HOST); - if (statusHost instanceof ViewGroup statusHostGroup) { - return statusHostGroup; - } - } - return rootParent; - } - - private void removeOverlayFromOtherParent(ViewGroup rootParent, ViewGroup overlayParent) { + private void removeOverlaysFromOwnedHosts(ViewGroup rootParent) { if (rootParent == null) { return; } - removeOverlayIfParentDiffers(rootParent, overlayParent); - removeOverlayIfParentDiffers( - directTaggedViewGroup(rootParent, OwnedIconHostManager.TAG_UNLOCKED_STATUS_HOST), - overlayParent); - removeOverlayIfParentDiffers( - directTaggedViewGroup(rootParent, OwnedIconHostManager.TAG_UNLOCKED_NOTIFICATION_HOST), - overlayParent); - removeOverlayIfParentDiffers( - directTaggedViewGroup(rootParent, OwnedIconHostManager.TAG_UNLOCKED_CLOCK_HOST), - overlayParent); - removeOverlayIfParentDiffers( - directTaggedViewGroup(rootParent, OwnedIconHostManager.TAG_UNLOCKED_CARRIER_HOST), - overlayParent); - removeOverlayIfParentDiffers( - directTaggedViewGroup(rootParent, OwnedIconHostManager.TAG_UNLOCKED_CHIP_HOST), - overlayParent); + removeOverlay(directTaggedViewGroup(rootParent, OwnedIconHostManager.TAG_UNLOCKED_STATUS_HOST)); + removeOverlay(directTaggedViewGroup(rootParent, OwnedIconHostManager.TAG_UNLOCKED_NOTIFICATION_HOST)); + removeOverlay(directTaggedViewGroup(rootParent, OwnedIconHostManager.TAG_UNLOCKED_CLOCK_HOST)); + removeOverlay(directTaggedViewGroup(rootParent, OwnedIconHostManager.TAG_UNLOCKED_CARRIER_HOST)); + removeOverlay(directTaggedViewGroup(rootParent, OwnedIconHostManager.TAG_UNLOCKED_CHIP_HOST)); } private void removeOverlays(ViewGroup rootParent) { @@ -467,12 +442,6 @@ public final class DebugBoundsOverlayController { removeOverlay(directTaggedViewGroup(rootParent, OwnedIconHostManager.TAG_UNLOCKED_CHIP_HOST)); } - private void removeOverlayIfParentDiffers(ViewGroup parent, ViewGroup expectedParent) { - if (parent != null && parent != expectedParent) { - removeOverlay(parent); - } - } - private void removeOverlay(ViewGroup parent) { if (parent == null) { return; @@ -511,6 +480,19 @@ public final class DebugBoundsOverlayController { return null; } + private int hostSignature(View root, String tag) { + if (!(root instanceof ViewGroup parent)) { + return 0; + } + View host = findDirectTaggedChild(parent, tag); + Bounds bounds = boundsRelativeTo(root, host); + int result = 17; + result = 31 * result + signature(bounds); + result = 31 * result + (host instanceof ViewGroup group ? group.getChildCount() : 0); + result = 31 * result + taggedContainerBounds(host).hashCode(); + return result; + } + private Bounds boundsRelativeTo(View root, View view) { if (root == null || view == null) { return null; diff --git a/app/src/main/java/se/ajpanton/statusbartweak/runtime/render/SnapshotRenderer.java b/app/src/main/java/se/ajpanton/statusbartweak/runtime/render/SnapshotRenderer.java index 886cdc5..4af88b7 100644 --- a/app/src/main/java/se/ajpanton/statusbartweak/runtime/render/SnapshotRenderer.java +++ b/app/src/main/java/se/ajpanton/statusbartweak/runtime/render/SnapshotRenderer.java @@ -10,6 +10,7 @@ import android.graphics.PorterDuff; import android.graphics.Rect; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; +import android.text.TextUtils; import android.os.SystemClock; import android.view.View; import android.view.ViewGroup; @@ -946,7 +947,7 @@ final class SnapshotRenderer { int oldMeasuredWidth = view.getMeasuredWidth(); int oldMeasuredHeight = view.getMeasuredHeight(); ViewFrameState frameState = captureFrameState(view); - IdentityHashMap oldMaxWidths = clearTextMaxWidths(view); + IdentityHashMap textSnapshotStates = prepareTextSnapshotState(view); try { int widthSpec = View.MeasureSpec.makeMeasureSpec(Math.max(1, width), View.MeasureSpec.EXACTLY); int heightSpec = View.MeasureSpec.makeMeasureSpec(Math.max(1, height), View.MeasureSpec.EXACTLY); @@ -955,7 +956,7 @@ final class SnapshotRenderer { clampDescendantsToParent(view); view.draw(canvas); } finally { - restoreTextMaxWidths(oldMaxWidths); + restoreTextSnapshotState(textSnapshotStates); restoreFrameState(frameState); if (oldMeasuredWidth > 0 && oldMeasuredHeight > 0) { view.measure( @@ -1052,32 +1053,53 @@ final class SnapshotRenderer { ) { } - private IdentityHashMap clearTextMaxWidths(View view) { - IdentityHashMap oldMaxWidths = new IdentityHashMap<>(); - collectTextMaxWidths(view, oldMaxWidths); - for (Map.Entry entry : oldMaxWidths.entrySet()) { - entry.getKey().setMaxWidth(Integer.MAX_VALUE); + private IdentityHashMap prepareTextSnapshotState(View view) { + IdentityHashMap states = new IdentityHashMap<>(); + collectTextSnapshotState(view, states); + for (Map.Entry entry : states.entrySet()) { + TextView textView = entry.getKey(); + textView.setMaxWidth(Integer.MAX_VALUE); + textView.setHorizontalFadingEdgeEnabled(false); + textView.setSelected(false); + textView.setEllipsize(null); } - return oldMaxWidths; + return states; } - private void collectTextMaxWidths(View view, IdentityHashMap out) { + private void collectTextSnapshotState(View view, IdentityHashMap out) { if (view instanceof TextView textView) { - out.put(textView, textView.getMaxWidth()); + out.put(textView, new TextSnapshotState( + textView.getMaxWidth(), + textView.isHorizontalFadingEdgeEnabled(), + textView.isSelected(), + textView.getEllipsize())); } if (view instanceof ViewGroup group) { for (int i = 0; i < group.getChildCount(); i++) { - collectTextMaxWidths(group.getChildAt(i), out); + collectTextSnapshotState(group.getChildAt(i), out); } } } - private void restoreTextMaxWidths(IdentityHashMap oldMaxWidths) { - for (Map.Entry entry : oldMaxWidths.entrySet()) { - entry.getKey().setMaxWidth(entry.getValue()); + private void restoreTextSnapshotState(IdentityHashMap states) { + for (Map.Entry entry : states.entrySet()) { + TextView textView = entry.getKey(); + TextSnapshotState state = entry.getValue(); + textView.setMaxWidth(state.maxWidth); + textView.setHorizontalFadingEdgeEnabled(state.horizontalFadingEdgeEnabled); + textView.setSelected(state.selected); + textView.setEllipsize(state.ellipsize); } } + private record TextSnapshotState( + int maxWidth, + boolean horizontalFadingEdgeEnabled, + boolean selected, + TextUtils.TruncateAt ellipsize + ) { + } + private void drawAppIcon(Context context, SnapshotSource source, Bitmap bitmap) { bitmap.eraseColor(Color.TRANSPARENT); if (context == null || source == null || source.keyHint() == null) {