4 Commits
Author SHA1 Message Date
ajp_anton 75861e2480 Release 0.5 2026-06-30 19:44:25 +00:00
ajp_anton 9dfca417bd Mirror stock statusbar transitions 2026-06-30 18:16:51 +00:00
ajp_anton 18f1c6f20f Use statusbar insets for fullscreen battery curves 2026-06-24 23:31:20 +00:00
ajp_anton 2fe90ce3fc Reduce lockscreen stock surface cleanup work 2026-06-23 18:05:36 +00:00
4 changed files with 241 additions and 22 deletions
@@ -434,7 +434,7 @@ final class BatteryBarController {
) { ) {
BatteryBarView barView = ensureBatteryBarView(rootGroup); BatteryBarView barView = ensureBatteryBarView(rootGroup);
layoutBatteryBarView(rootGroup, barView); layoutBatteryBarView(rootGroup, barView);
barView.update(rootGroup, settings, geometry, batteryLevel, plugged, color); barView.update(rootGroup, rootGroup, settings, geometry, batteryLevel, plugged, color);
barView.bringToFront(); barView.bringToFront();
barView.invalidate(); barView.invalidate();
lastSignatures.put(rootGroup, buildSignature(rootGroup, settings, color, geometry, scenario)); lastSignatures.put(rootGroup, buildSignature(rootGroup, settings, color, geometry, scenario));
@@ -461,7 +461,7 @@ final class BatteryBarController {
return; return;
} }
pendingOverlayRetries.remove(root); pendingOverlayRetries.remove(root);
overlay.view.update(overlay.host, settings, geometry, batteryLevel, plugged, color); overlay.view.update(overlay.host, root, settings, geometry, batteryLevel, plugged, color);
if (!signature.equals(lastOverlaySignatures.get(root))) { if (!signature.equals(lastOverlaySignatures.get(root))) {
overlay.updateLayout(bounds.width, bounds.height); overlay.updateLayout(bounds.width, bounds.height);
lastOverlaySignatures.put(root, signature); lastOverlaySignatures.put(root, signature);
@@ -1039,6 +1039,7 @@ final class BatteryBarController {
private final Path drawPath = new Path(); private final Path drawPath = new Path();
private final PathMeasure pathMeasure = new PathMeasure(); private final PathMeasure pathMeasure = new PathMeasure();
private ViewGroup host; private ViewGroup host;
private View roundedCornerSource;
private SbtSettings settings; private SbtSettings settings;
private BatteryBarGeometry geometry; private BatteryBarGeometry geometry;
private int batteryLevel = -1; private int batteryLevel = -1;
@@ -1053,6 +1054,7 @@ final class BatteryBarController {
void update( void update(
ViewGroup host, ViewGroup host,
View roundedCornerSource,
SbtSettings settings, SbtSettings settings,
BatteryBarGeometry geometry, BatteryBarGeometry geometry,
int batteryLevel, int batteryLevel,
@@ -1060,6 +1062,7 @@ final class BatteryBarController {
int color int color
) { ) {
this.host = host; this.host = host;
this.roundedCornerSource = roundedCornerSource;
this.settings = settings; this.settings = settings;
this.geometry = geometry; this.geometry = geometry;
this.batteryLevel = batteryLevel; this.batteryLevel = batteryLevel;
@@ -1226,7 +1229,12 @@ final class BatteryBarController {
} }
private float resolveTopCornerRadius(float fallback) { private float resolveTopCornerRadius(float fallback) {
WindowInsets insets = getRootWindowInsets(); WindowInsets insets = roundedCornerSource != null
? roundedCornerSource.getRootWindowInsets()
: null;
if (insets == null) {
insets = getRootWindowInsets();
}
if (insets == null) { if (insets == null) {
return fallback; return fallback;
} }
@@ -13,6 +13,7 @@ import android.service.notification.StatusBarNotification;
import android.telecom.TelecomManager; import android.telecom.TelecomManager;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.view.WindowInsets;
import android.widget.ImageView; import android.widget.ImageView;
import android.widget.TextView; import android.widget.TextView;
@@ -138,6 +139,8 @@ final class StockLayoutCanvasController {
Collections.newSetFromMap(new WeakHashMap<>()); Collections.newSetFromMap(new WeakHashMap<>());
private final Set<View> trackedLockedStatusBarIconSurfaces = private final Set<View> trackedLockedStatusBarIconSurfaces =
Collections.newSetFromMap(new WeakHashMap<>()); Collections.newSetFromMap(new WeakHashMap<>());
private final Set<View> lockedStatusBarIconSurfaceReadyRoots =
Collections.newSetFromMap(new WeakHashMap<>());
private final Set<View> pendingUnlockedAppearanceRefreshRoots = private final Set<View> pendingUnlockedAppearanceRefreshRoots =
Collections.newSetFromMap(new WeakHashMap<>()); Collections.newSetFromMap(new WeakHashMap<>());
private final Set<View> pendingUnlockedClockTextRefreshRoots = private final Set<View> pendingUnlockedClockTextRefreshRoots =
@@ -187,6 +190,7 @@ final class StockLayoutCanvasController {
private int notificationDrawerCloseGeneration; private int notificationDrawerCloseGeneration;
private boolean notificationDrawerClosePending; private boolean notificationDrawerClosePending;
private int aodVisualAlphaGeneration; private int aodVisualAlphaGeneration;
private int systemBarRequestedVisibleTypes = WindowInsets.Type.statusBars();
StockLayoutCanvasController(RuntimeContext runtimeContext) { StockLayoutCanvasController(RuntimeContext runtimeContext) {
this.runtimeContext = runtimeContext; this.runtimeContext = runtimeContext;
@@ -215,10 +219,44 @@ final class StockLayoutCanvasController {
statusIconModelTracker.installHooks(classLoader); statusIconModelTracker.installHooks(classLoader);
installShadeExpansionHooks(classLoader); installShadeExpansionHooks(classLoader);
installShadeHeaderAnimationGuardHook(classLoader); installShadeHeaderAnimationGuardHook(classLoader);
installSystemBarAttributeListener(classLoader);
installViewRootHook(classLoader); installViewRootHook(classLoader);
hooksInstalled = true; hooksInstalled = true;
} }
private void installSystemBarAttributeListener(ClassLoader classLoader) {
Class<?> commandQueueClass = ReflectionSupport.findClassIfExists(
"com.android.systemui.statusbar.CommandQueue",
classLoader);
if (commandQueueClass == null) {
return;
}
XposedHookSupport.hookAllMethodsIfExists(
runtimeContext.getFramework(),
commandQueueClass,
"onSystemBarAttributesChanged",
chain -> {
Object result = chain.proceed();
updateSystemBarRequestedVisibleTypes(chain.getArgs());
return result;
});
}
private void updateSystemBarRequestedVisibleTypes(List<Object> args) {
if (args == null || args.size() <= 5) {
return;
}
Integer displayId = asInteger(args.get(0));
Integer requestedVisibleTypes = asInteger(args.get(5));
if (displayId == null || displayId != 0 || requestedVisibleTypes == null) {
return;
}
if (systemBarRequestedVisibleTypes == requestedVisibleTypes) {
return;
}
systemBarRequestedVisibleTypes = requestedVisibleTypes;
}
private void installNotificationCollectionHooks(ClassLoader classLoader) { private void installNotificationCollectionHooks(ClassLoader classLoader) {
for (String className : List.of( for (String className : List.of(
"com.android.systemui.statusbar.notification.collection.NotifCollection", "com.android.systemui.statusbar.notification.collection.NotifCollection",
@@ -426,7 +464,11 @@ final class StockLayoutCanvasController {
rememberAodPluginVisualAlpha(view, alpha); rememberAodPluginVisualAlpha(view, alpha);
} }
} }
return chain.proceed(); Object result = chain.proceed();
if (target instanceof View view) {
syncUnlockedHostsFromStockStatusBar(view);
}
return result;
}); });
XposedHookSupport.hookAllMethodsIfExists(runtimeContext.getFramework(), viewClass, "setVisibility", chain -> { XposedHookSupport.hookAllMethodsIfExists(runtimeContext.getFramework(), viewClass, "setVisibility", chain -> {
Object target = chain.getThisObject(); Object target = chain.getThisObject();
@@ -442,6 +484,7 @@ final class StockLayoutCanvasController {
Object result = chain.proceed(); Object result = chain.proceed();
if (target instanceof View view && visibilityArg instanceof Integer visibility) { if (target instanceof View view && visibilityArg instanceof Integer visibility) {
scheduleStatusChipRefreshIfContentChanged(view); scheduleStatusChipRefreshIfContentChanged(view);
syncUnlockedHostsFromStockStatusBar(view);
} }
return result; return result;
}); });
@@ -453,7 +496,11 @@ final class StockLayoutCanvasController {
return null; return null;
} }
} }
return chain.proceed(); Object result = chain.proceed();
if (target instanceof View view) {
syncUnlockedHostsFromStockStatusBar(view);
}
return result;
}); });
XposedHookSupport.hookAllMethodsIfExists(runtimeContext.getFramework(), viewClass, "setTranslationX", chain -> { XposedHookSupport.hookAllMethodsIfExists(runtimeContext.getFramework(), viewClass, "setTranslationX", chain -> {
Object target = chain.getThisObject(); Object target = chain.getThisObject();
@@ -463,7 +510,11 @@ final class StockLayoutCanvasController {
return null; return null;
} }
} }
return chain.proceed(); Object result = chain.proceed();
if (target instanceof View view) {
syncUnlockedHostsFromStockStatusBar(view);
}
return result;
}); });
XposedHookSupport.hookAllMethodsIfExists(runtimeContext.getFramework(), viewClass, "layout", chain -> { XposedHookSupport.hookAllMethodsIfExists(runtimeContext.getFramework(), viewClass, "layout", chain -> {
Object target = chain.getThisObject(); Object target = chain.getThisObject();
@@ -480,15 +531,21 @@ final class StockLayoutCanvasController {
right, right,
bottom); bottom);
if (override != null) { if (override != null) {
return chain.proceed(new Object[]{ Object result = chain.proceed(new Object[]{
override.left, override.left,
override.top, override.top,
override.right, override.right,
override.bottom override.bottom
}); });
syncUnlockedHostsFromStockStatusBar(view);
return result;
} }
} }
return chain.proceed(); Object result = chain.proceed();
if (target instanceof View view) {
syncUnlockedHostsFromStockStatusBar(view);
}
return result;
}); });
} }
@@ -1937,12 +1994,15 @@ final class StockLayoutCanvasController {
if (current.isUnlocked() && !previous.isUnlocked()) { if (current.isUnlocked() && !previous.isUnlocked()) {
darkIconDispatcherGuard.restoreForcedStates(); darkIconDispatcherGuard.restoreForcedStates();
trackedLockedStatusBarIconSurfaces.clear(); trackedLockedStatusBarIconSurfaces.clear();
lockedStatusBarIconSurfaceReadyRoots.clear();
} else if (previous.isLockscreen() && current.isAod()) { } else if (previous.isLockscreen() && current.isAod()) {
darkIconDispatcherGuard.restoreForcedStates(); darkIconDispatcherGuard.restoreForcedStates();
hideTrackedLockedStatusBarIconSurfaces(null); hideTrackedLockedStatusBarIconSurfaces(null);
lockedStatusBarIconSurfaceReadyRoots.clear();
} else if (previous.isLockscreen() && !current.isLockscreen()) { } else if (previous.isLockscreen() && !current.isLockscreen()) {
darkIconDispatcherGuard.restoreForcedStates(); darkIconDispatcherGuard.restoreForcedStates();
restoreTrackedLockedStatusBarIconSurfaces(null); restoreTrackedLockedStatusBarIconSurfaces(null);
lockedStatusBarIconSurfaceReadyRoots.clear();
} }
boolean lockscreenEntry = current.isLockscreen(); boolean lockscreenEntry = current.isLockscreen();
if (lockscreenEntry) { if (lockscreenEntry) {
@@ -1983,6 +2043,7 @@ final class StockLayoutCanvasController {
} }
unlockedIconRenderController.clearRoot(root); unlockedIconRenderController.clearRoot(root);
dirtyUnlockedLayoutRoots.add(root); dirtyUnlockedLayoutRoots.add(root);
lockedStatusBarIconSurfaceReadyRoots.remove(root);
if (isViewThread(root)) { if (isViewThread(root)) {
applyToRoot(root); applyToRoot(root);
} else { } else {
@@ -2131,6 +2192,83 @@ final class StockLayoutCanvasController {
debugBoundsOverlayController.bringToFront(root); debugBoundsOverlayController.bringToFront(root);
} }
private void syncUnlockedHostsFromStockStatusBar(View view) {
if (!isPhoneStatusBarRoot(view)) {
return;
}
View root = unlockedStatusBarRootWithUnlockedHostsFor(view);
if (root == null) {
return;
}
syncUnlockedHostsToStockStatusBar(
root,
runtimeContext.getModeStateRepository().getActiveScene());
}
private void syncUnlockedHostsToStockStatusBar(View root, SceneKey activeScene) {
if (root == null || !hasDirectUnlockedLayoutHost(root)) {
return;
}
if (activeScene == null || !activeScene.isUnlocked()) {
ownedIconHostManager.setUnlockedHostsTransform(root, 1f, 0f, 0f);
return;
}
View stockStatusBar = phoneStatusBarRootIn(root);
if (stockStatusBar == null || stockStatusBar == root) {
ownedIconHostManager.setUnlockedHostsTransform(root, 1f, 0f, 0f);
return;
}
float alpha = stockStatusBar.getVisibility() == View.VISIBLE
? clampAlpha(stockStatusBar.getAlpha())
: 0f;
float translationX = stockStatusBar.getLeft() + stockStatusBar.getTranslationX();
float translationY = stockStatusBar.getTop() + stockStatusBar.getTranslationY();
ownedIconHostManager.setUnlockedHostsTransform(
root,
alpha,
translationX,
translationY);
}
private View unlockedStatusBarRootWithUnlockedHostsFor(View view) {
View current = view;
while (current != null) {
if (isUnlockedStatusBarRoot(current) && hasDirectUnlockedLayoutHost(current)) {
return current;
}
Object parent = current.getParent();
current = parent instanceof View parentView ? parentView : null;
}
View root = view.getRootView();
return isUnlockedStatusBarRoot(root) && hasDirectUnlockedLayoutHost(root) ? root : null;
}
private View phoneStatusBarRootIn(View root) {
if (root == null) {
return null;
}
if (isPhoneStatusBarRoot(root)) {
return root;
}
if (!(root instanceof ViewGroup group)) {
return null;
}
ArrayDeque<View> queue = new ArrayDeque<>();
queue.add(group);
while (!queue.isEmpty()) {
View current = queue.removeFirst();
if (current != root && isPhoneStatusBarRoot(current)) {
return current;
}
if (current instanceof ViewGroup childGroup) {
for (int i = 0; i < childGroup.getChildCount(); i++) {
queue.addLast(childGroup.getChildAt(i));
}
}
}
return null;
}
private void applyToRootInternal(View root) { private void applyToRootInternal(View root) {
if (root == null) { if (root == null) {
return; return;
@@ -2243,11 +2381,14 @@ final class StockLayoutCanvasController {
activeScene, activeScene,
rootDirty, rootDirty,
lockedCarrierTextSource()); lockedCarrierTextSource());
boolean shouldShowUnlockedHosts = unlockedScene || aodScene || renderResult.shouldOwnLockscreen(); boolean shouldShowUnlockedHosts = unlockedScene
|| aodScene
|| (!unlockedScene && renderResult.shouldOwnLockscreen());
ownedIconHostManager.setUnlockedHostsVisible(root, shouldShowUnlockedHosts); ownedIconHostManager.setUnlockedHostsVisible(root, shouldShowUnlockedHosts);
if (shouldShowUnlockedHosts) { if (shouldShowUnlockedHosts) {
bringUnlockedHostsAndDebugOverlayToFront(root); bringUnlockedHostsAndDebugOverlayToFront(root);
} }
syncUnlockedHostsToStockStatusBar(root, activeScene);
syncAodOwnedHostVisualState(root, unlockedHosts); syncAodOwnedHostVisualState(root, unlockedHosts);
} }
} }
@@ -2347,6 +2488,17 @@ final class StockLayoutCanvasController {
|| findDirectOwnedHost(group, OwnedIconHostManager.TAG_AOD_CARDS_NOTIFICATION_HOST) != null; || findDirectOwnedHost(group, OwnedIconHostManager.TAG_AOD_CARDS_NOTIFICATION_HOST) != null;
} }
private boolean hasDirectUnlockedLayoutHost(View root) {
if (!(root instanceof ViewGroup group)) {
return false;
}
return findDirectOwnedHost(group, OwnedIconHostManager.TAG_UNLOCKED_STATUS_HOST) != null
|| findDirectOwnedHost(group, OwnedIconHostManager.TAG_UNLOCKED_NOTIFICATION_HOST) != null
|| findDirectOwnedHost(group, OwnedIconHostManager.TAG_UNLOCKED_CLOCK_HOST) != null
|| findDirectOwnedHost(group, OwnedIconHostManager.TAG_UNLOCKED_CARRIER_HOST) != null
|| findDirectOwnedHost(group, OwnedIconHostManager.TAG_UNLOCKED_CHIP_HOST) != null;
}
private boolean hasLockscreenCardsNotificationHost(View root) { private boolean hasLockscreenCardsNotificationHost(View root) {
return root instanceof ViewGroup group return root instanceof ViewGroup group
&& findDirectOwnedHost( && findDirectOwnedHost(
@@ -2935,9 +3087,16 @@ final class StockLayoutCanvasController {
} }
private void hideOrDiscoverLockedStatusBarIconSurfaces(View root) { private void hideOrDiscoverLockedStatusBarIconSurfaces(View root) {
hideTrackedLockedStatusBarIconSurfaces(root); if (root != null && lockedStatusBarIconSurfaceReadyRoots.contains(root)) {
if (root != null) { return;
trackAndHideLockedStatusBarIconSurfaces(root); }
boolean hasTrackedSurfacesInRoot = hideTrackedLockedStatusBarIconSurfaces(root);
int discovered = 0;
if (!hasTrackedSurfacesInRoot && root != null) {
discovered = trackAndHideLockedStatusBarIconSurfaces(root);
}
if (root != null && (hasTrackedSurfacesInRoot || discovered > 0)) {
lockedStatusBarIconSurfaceReadyRoots.add(root);
} }
} }
@@ -2945,30 +3104,29 @@ final class StockLayoutCanvasController {
if (trackedLockedStatusBarIconSurfaces.isEmpty()) { if (trackedLockedStatusBarIconSurfaces.isEmpty()) {
return false; return false;
} }
boolean foundInRoot = false; boolean foundActiveInRoot = false;
ArrayDeque<View> staleViews = new ArrayDeque<>(); ArrayDeque<View> staleViews = new ArrayDeque<>();
for (View view : trackedLockedStatusBarIconSurfaces) { for (View view : trackedLockedStatusBarIconSurfaces) {
if (view == null || !view.isAttachedToWindow()) { if (view == null || !view.isAttachedToWindow()) {
staleViews.add(view); staleViews.add(view);
continue; continue;
} }
if (root == null || isDescendantOf(view, root)) { boolean inRoot = root == null || isDescendantOf(view, root);
foundInRoot = true;
if (isAlreadySuppressedHiddenView(view)) {
continue;
}
}
if (!isLockedStatusBarIconSurface(view)) { if (!isLockedStatusBarIconSurface(view)) {
restoreView(view); restoreView(view);
staleViews.add(view); staleViews.add(view);
continue; continue;
} }
if (root == null || isDescendantOf(view, root)) { if (inRoot) {
foundActiveInRoot = true;
if (isAlreadySuppressedHiddenView(view)) {
continue;
}
hideView(view, isCarrierView(view)); hideView(view, isCarrierView(view));
} }
} }
removeStaleViews(trackedLockedStatusBarIconSurfaces, staleViews); removeStaleViews(trackedLockedStatusBarIconSurfaces, staleViews);
return foundInRoot; return foundActiveInRoot;
} }
private boolean isAlreadySuppressedHiddenView(View view) { private boolean isAlreadySuppressedHiddenView(View view) {
@@ -3130,6 +3288,11 @@ final class StockLayoutCanvasController {
} }
} }
removeStaleViews(trackedLockedStatusBarIconSurfaces, restoredViews); removeStaleViews(trackedLockedStatusBarIconSurfaces, restoredViews);
if (root != null) {
lockedStatusBarIconSurfaceReadyRoots.remove(root);
} else {
lockedStatusBarIconSurfaceReadyRoots.clear();
}
} }
private boolean hasTrackedStatusChipSources(View root) { private boolean hasTrackedStatusChipSources(View root) {
@@ -3371,6 +3534,9 @@ final class StockLayoutCanvasController {
|| ongoingPrivacyChipInRoot(view) == null) { || ongoingPrivacyChipInRoot(view) == null) {
return false; return false;
} }
if (alpha <= 0f && !statusBarsRequestedVisible()) {
return false;
}
return true; return true;
} }
@@ -3385,6 +3551,14 @@ final class StockLayoutCanvasController {
return true; return true;
} }
private boolean statusBarsRequestedVisible() {
return (systemBarRequestedVisibleTypes & WindowInsets.Type.statusBars()) != 0;
}
private static Integer asInteger(Object value) {
return value instanceof Integer integer ? integer : null;
}
private boolean isPhoneStatusBarRoot(View view) { private boolean isPhoneStatusBarRoot(View view) {
return view != null return view != null
&& view.getClass().getName().contains("PhoneStatusBarView"); && view.getClass().getName().contains("PhoneStatusBarView");
@@ -4345,6 +4519,7 @@ final class StockLayoutCanvasController {
} }
clearTrackedStatusChipState(); clearTrackedStatusChipState();
trackedLockedStatusBarIconSurfaces.clear(); trackedLockedStatusBarIconSurfaces.clear();
lockedStatusBarIconSurfaceReadyRoots.clear();
trackedUnlockedStockSurfaces.clear(); trackedUnlockedStockSurfaces.clear();
confirmedAodPluginViews.clear(); confirmedAodPluginViews.clear();
aodPluginVisualAlphaByView.clear(); aodPluginVisualAlphaByView.clear();
@@ -119,6 +119,17 @@ public final class OwnedIconHostManager {
} }
} }
public void setUnlockedHostsTransform(
View root,
float alpha,
float translationX,
float translationY
) {
for (String tag : UNLOCKED_HOST_ORDER) {
setHostTransform(root, tag, alpha, translationX, translationY);
}
}
public void bringUnlockedHostsToFront(View root) { public void bringUnlockedHostsToFront(View root) {
if (!(root instanceof ViewGroup parent)) { if (!(root instanceof ViewGroup parent)) {
return; return;
@@ -169,6 +180,31 @@ public final class OwnedIconHostManager {
} }
} }
private void setHostTransform(
View root,
String tag,
float alpha,
float translationX,
float translationY
) {
if (!(root instanceof ViewGroup parent) || tag == null) {
return;
}
FrameLayout host = findDirectHost(parent, tag);
if (host == null) {
return;
}
if (host.getAlpha() != alpha) {
host.setAlpha(alpha);
}
if (host.getTranslationX() != translationX) {
host.setTranslationX(translationX);
}
if (host.getTranslationY() != translationY) {
host.setTranslationY(translationY);
}
}
private ArrayList<View> unlockedHosts(ViewGroup parent) { private ArrayList<View> unlockedHosts(ViewGroup parent) {
ArrayList<View> hosts = new ArrayList<>(); ArrayList<View> hosts = new ArrayList<>();
for (String tag : UNLOCKED_HOST_ORDER) { for (String tag : UNLOCKED_HOST_ORDER) {
+1 -1
View File
@@ -1 +1 @@
version=0.4 version=0.5