Mirror stock statusbar transitions

This commit is contained in:
ajp_anton
2026-06-30 18:16:51 +00:00
parent 18f1c6f20f
commit 9dfca417bd
2 changed files with 199 additions and 6 deletions
@@ -13,6 +13,7 @@ import android.service.notification.StatusBarNotification;
import android.telecom.TelecomManager;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowInsets;
import android.widget.ImageView;
import android.widget.TextView;
@@ -189,6 +190,7 @@ final class StockLayoutCanvasController {
private int notificationDrawerCloseGeneration;
private boolean notificationDrawerClosePending;
private int aodVisualAlphaGeneration;
private int systemBarRequestedVisibleTypes = WindowInsets.Type.statusBars();
StockLayoutCanvasController(RuntimeContext runtimeContext) {
this.runtimeContext = runtimeContext;
@@ -217,10 +219,44 @@ final class StockLayoutCanvasController {
statusIconModelTracker.installHooks(classLoader);
installShadeExpansionHooks(classLoader);
installShadeHeaderAnimationGuardHook(classLoader);
installSystemBarAttributeListener(classLoader);
installViewRootHook(classLoader);
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) {
for (String className : List.of(
"com.android.systemui.statusbar.notification.collection.NotifCollection",
@@ -428,7 +464,11 @@ final class StockLayoutCanvasController {
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 -> {
Object target = chain.getThisObject();
@@ -444,6 +484,7 @@ final class StockLayoutCanvasController {
Object result = chain.proceed();
if (target instanceof View view && visibilityArg instanceof Integer visibility) {
scheduleStatusChipRefreshIfContentChanged(view);
syncUnlockedHostsFromStockStatusBar(view);
}
return result;
});
@@ -455,7 +496,11 @@ final class StockLayoutCanvasController {
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 -> {
Object target = chain.getThisObject();
@@ -465,7 +510,11 @@ final class StockLayoutCanvasController {
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 -> {
Object target = chain.getThisObject();
@@ -482,15 +531,21 @@ final class StockLayoutCanvasController {
right,
bottom);
if (override != null) {
return chain.proceed(new Object[]{
Object result = chain.proceed(new Object[]{
override.left,
override.top,
override.right,
override.bottom
});
syncUnlockedHostsFromStockStatusBar(view);
return result;
}
}
return chain.proceed();
Object result = chain.proceed();
if (target instanceof View view) {
syncUnlockedHostsFromStockStatusBar(view);
}
return result;
});
}
@@ -2137,6 +2192,83 @@ final class StockLayoutCanvasController {
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) {
if (root == null) {
return;
@@ -2249,11 +2381,14 @@ final class StockLayoutCanvasController {
activeScene,
rootDirty,
lockedCarrierTextSource());
boolean shouldShowUnlockedHosts = unlockedScene || aodScene || renderResult.shouldOwnLockscreen();
boolean shouldShowUnlockedHosts = unlockedScene
|| aodScene
|| (!unlockedScene && renderResult.shouldOwnLockscreen());
ownedIconHostManager.setUnlockedHostsVisible(root, shouldShowUnlockedHosts);
if (shouldShowUnlockedHosts) {
bringUnlockedHostsAndDebugOverlayToFront(root);
}
syncUnlockedHostsToStockStatusBar(root, activeScene);
syncAodOwnedHostVisualState(root, unlockedHosts);
}
}
@@ -2353,6 +2488,17 @@ final class StockLayoutCanvasController {
|| 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) {
return root instanceof ViewGroup group
&& findDirectOwnedHost(
@@ -3388,6 +3534,9 @@ final class StockLayoutCanvasController {
|| ongoingPrivacyChipInRoot(view) == null) {
return false;
}
if (alpha <= 0f && !statusBarsRequestedVisible()) {
return false;
}
return true;
}
@@ -3402,6 +3551,14 @@ final class StockLayoutCanvasController {
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) {
return view != null
&& view.getClass().getName().contains("PhoneStatusBarView");
@@ -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) {
if (!(root instanceof ViewGroup parent)) {
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) {
ArrayList<View> hosts = new ArrayList<>();
for (String tag : UNLOCKED_HOST_ORDER) {