Refine runtime layout transitions

This commit is contained in:
ajp_anton
2026-06-10 12:16:32 +00:00
parent ce5bd33734
commit ab28b7d8a3
10 changed files with 695 additions and 91 deletions
@@ -57,6 +57,7 @@ final class BatteryBarController {
private final Set<View> pendingOverlayRetries = Collections.newSetFromMap(new WeakHashMap<>()); private final Set<View> pendingOverlayRetries = Collections.newSetFromMap(new WeakHashMap<>());
private final Map<View, String> lastSignatures = new WeakHashMap<>(); private final Map<View, String> lastSignatures = new WeakHashMap<>();
private final Map<View, String> lastOverlaySignatures = new WeakHashMap<>(); private final Map<View, String> lastOverlaySignatures = new WeakHashMap<>();
private final Map<View, Integer> lastRootTransformSignatures = new WeakHashMap<>();
private int systemBarAppearance; private int systemBarAppearance;
private int systemBarBehavior; private int systemBarBehavior;
private int systemBarRequestedVisibleTypes = WindowInsets.Type.statusBars(); private int systemBarRequestedVisibleTypes = WindowInsets.Type.statusBars();
@@ -171,6 +172,11 @@ final class BatteryBarController {
} }
private void applyBatteryBarAfterRootTransform(View root) { private void applyBatteryBarAfterRootTransform(View root) {
int transformSignature = rootTransformSignature(root);
Integer previousSignature = lastRootTransformSignatures.put(root, transformSignature);
if (previousSignature != null && previousSignature == transformSignature) {
return;
}
if (isHiddenKeyguardRoot(root)) { if (isHiddenKeyguardRoot(root)) {
removeBatteryBarView(root); removeBatteryBarView(root);
return; return;
@@ -255,9 +261,23 @@ final class BatteryBarController {
} }
roots.remove(root); roots.remove(root);
pendingOverlayRetries.remove(root); pendingOverlayRetries.remove(root);
lastRootTransformSignatures.remove(root);
removeBatteryBarView(root); removeBatteryBarView(root);
} }
private int rootTransformSignature(View root) {
if (root == null) {
return 0;
}
int result = 17;
result = 31 * result + root.getVisibility();
result = 31 * result + Float.floatToIntBits(root.getY());
result = 31 * result + Float.floatToIntBits(root.getTranslationY());
result = 31 * result + root.getWidth();
result = 31 * result + root.getHeight();
return result;
}
private void registerReceiverIfNeeded(Context context) { private void registerReceiverIfNeeded(Context context) {
if (context == null || receiverRegistered) { if (context == null || receiverRegistered) {
return; return;
@@ -340,6 +360,10 @@ final class BatteryBarController {
} }
private void applyBatteryBar(View root) { private void applyBatteryBar(View root) {
applyBatteryBarInternal(root);
}
private void applyBatteryBarInternal(View root) {
if (!(root instanceof ViewGroup rootGroup)) { if (!(root instanceof ViewGroup rootGroup)) {
removeBatteryBarView(root); removeBatteryBarView(root);
return; return;
@@ -24,6 +24,8 @@ import se.ajpanton.statusbartweak.runtime.ViewIdNames;
import se.ajpanton.statusbartweak.runtime.features.RuntimeContext; import se.ajpanton.statusbartweak.runtime.features.RuntimeContext;
import se.ajpanton.statusbartweak.runtime.hooks.ReflectionSupport; import se.ajpanton.statusbartweak.runtime.hooks.ReflectionSupport;
import se.ajpanton.statusbartweak.runtime.hooks.XposedHookSupport; import se.ajpanton.statusbartweak.runtime.hooks.XposedHookSupport;
import se.ajpanton.statusbartweak.runtime.mode.SceneKey;
import se.ajpanton.statusbartweak.runtime.mode.SurfaceMode;
import se.ajpanton.statusbartweak.runtime.settings.BroadcastReceiverSupport; import se.ajpanton.statusbartweak.runtime.settings.BroadcastReceiverSupport;
import se.ajpanton.statusbartweak.runtime.settings.RuntimeSettingsCache; import se.ajpanton.statusbartweak.runtime.settings.RuntimeSettingsCache;
import se.ajpanton.statusbartweak.shell.settings.SbtSettings; import se.ajpanton.statusbartweak.shell.settings.SbtSettings;
@@ -83,6 +85,11 @@ final class DrawerClockTextController {
private void scanAndApply(View root) { private void scanAndApply(View root) {
SbtSettings settings = RuntimeSettingsCache.get(root.getContext()); SbtSettings settings = RuntimeSettingsCache.get(root.getContext());
SceneKey scene = runtimeContext.getModeStateRepository().getActiveScene();
if (scene != null && scene.isAod()) {
restoreTrackedDescendants(root);
return;
}
boolean clockEnabled = isEnabled(settings, Role.CLOCK); boolean clockEnabled = isEnabled(settings, Role.CLOCK);
boolean dateEnabled = isEnabled(settings, Role.DATE); boolean dateEnabled = isEnabled(settings, Role.DATE);
if (!clockEnabled && !dateEnabled) { if (!clockEnabled && !dateEnabled) {
@@ -95,10 +102,16 @@ final class DrawerClockTextController {
registerReceiverIfNeeded(root.getContext()); registerReceiverIfNeeded(root.getContext());
untrackDisabledRoles(clockEnabled, dateEnabled); untrackDisabledRoles(clockEnabled, dateEnabled);
CandidateSelection candidates = cachedCandidates(root, clockEnabled, dateEnabled); SurfaceMode surfaceMode = scene != null ? scene.surfaceMode() : null;
if (hasValidTrackedCandidates(root, clockEnabled, dateEnabled, surfaceMode)) {
return;
}
CandidateSelection candidates = cachedCandidates(root, clockEnabled, dateEnabled, surfaceMode);
if (candidates == null) { if (candidates == null) {
candidates = findCandidates(root); candidates = findCandidates(root);
candidateCacheByRoot.put(root, new CandidateCache( candidateCacheByRoot.put(root, new CandidateCache(
surfaceMode,
candidates.clock != null ? candidates.clock.view : null, candidates.clock != null ? candidates.clock.view : null,
candidates.date != null ? candidates.date.view : null)); candidates.date != null ? candidates.date.view : null));
} }
@@ -126,6 +139,18 @@ final class DrawerClockTextController {
} }
} }
private int restoreTrackedDescendants(View root) {
int restored = 0;
for (TextView textView : new ArrayList<>(trackedRoles.keySet())) {
if (textView == null || root == null || isDescendantOf(textView, root)) {
restore(textView);
untrack(textView);
restored++;
}
}
return restored;
}
private void untrackDisabledRoles(boolean clockEnabled, boolean dateEnabled) { private void untrackDisabledRoles(boolean clockEnabled, boolean dateEnabled) {
for (TextView textView : new ArrayList<>(trackedRoles.keySet())) { for (TextView textView : new ArrayList<>(trackedRoles.keySet())) {
Role role = trackedRoles.get(textView); Role role = trackedRoles.get(textView);
@@ -136,6 +161,44 @@ final class DrawerClockTextController {
} }
} }
private boolean hasValidTrackedCandidates(
View root,
boolean clockEnabled,
boolean dateEnabled,
SurfaceMode surfaceMode
) {
if (clockEnabled && !hasValidTrackedRole(root, Role.CLOCK, surfaceMode)) {
return false;
}
return !dateEnabled || hasValidTrackedRole(root, Role.DATE, surfaceMode);
}
private boolean hasValidTrackedRole(View root, Role role, SurfaceMode surfaceMode) {
CandidateCache cache = candidateCacheByRoot.get(root);
if (cache == null || cache.surfaceMode != surfaceMode) {
return false;
}
WeakReference<TextView> expectedReference = role == Role.CLOCK
? cache.clockView
: cache.dateView;
TextView expectedView = expectedReference != null ? expectedReference.get() : null;
if (expectedView == null) {
return false;
}
for (Map.Entry<TextView, Role> entry : trackedRoles.entrySet()) {
TextView textView = entry.getKey();
if (entry.getValue() == role
&& textView == expectedView
&& textView.isAttachedToWindow()
&& isDescendantOf(textView, root)
&& !isStatusbarClock(textView)
&& ownedTexts.contains(textView)) {
return true;
}
}
return false;
}
private void track(TextView textView, Role role) { private void track(TextView textView, Role role) {
if (textView == null || role == null) { if (textView == null || role == null) {
return; return;
@@ -159,6 +222,12 @@ final class DrawerClockTextController {
if (role == null) { if (role == null) {
return; return;
} }
SceneKey scene = runtimeContext.getModeStateRepository().getActiveScene();
if (scene != null && scene.isAod()) {
restore(textView);
untrack(textView);
return;
}
if (stockTextJustChanged) { if (stockTextJustChanged) {
captureSnapshot(textView); captureSnapshot(textView);
} }
@@ -416,12 +485,17 @@ final class DrawerClockTextController {
private CandidateSelection cachedCandidates( private CandidateSelection cachedCandidates(
View root, View root,
boolean clockEnabled, boolean clockEnabled,
boolean dateEnabled boolean dateEnabled,
SurfaceMode surfaceMode
) { ) {
CandidateCache cache = candidateCacheByRoot.get(root); CandidateCache cache = candidateCacheByRoot.get(root);
if (cache == null) { if (cache == null) {
return null; return null;
} }
if (cache.surfaceMode != surfaceMode) {
candidateCacheByRoot.remove(root);
return null;
}
Candidate clock = candidateFromCache(root, cache.clockView, Role.CLOCK, clockEnabled); Candidate clock = candidateFromCache(root, cache.clockView, Role.CLOCK, clockEnabled);
Candidate date = candidateFromCache(root, cache.dateView, Role.DATE, dateEnabled); Candidate date = candidateFromCache(root, cache.dateView, Role.DATE, dateEnabled);
if (clockEnabled && clock == null || dateEnabled && date == null) { if (clockEnabled && clock == null || dateEnabled && date == null) {
@@ -554,10 +628,12 @@ final class DrawerClockTextController {
} }
private static final class CandidateCache { private static final class CandidateCache {
final SurfaceMode surfaceMode;
final WeakReference<TextView> clockView; final WeakReference<TextView> clockView;
final WeakReference<TextView> dateView; final WeakReference<TextView> dateView;
CandidateCache(TextView clockView, TextView dateView) { CandidateCache(SurfaceMode surfaceMode, TextView clockView, TextView dateView) {
this.surfaceMode = surfaceMode;
this.clockView = new WeakReference<>(clockView); this.clockView = new WeakReference<>(clockView);
this.dateView = new WeakReference<>(dateView); this.dateView = new WeakReference<>(dateView);
} }
@@ -153,7 +153,7 @@ final class StockClockController {
} }
runtimeContext.getModeStateRepository().addLayoutEnabledListener(enabled -> { runtimeContext.getModeStateRepository().addLayoutEnabledListener(enabled -> {
if (enabled) { if (enabled) {
removeAllRowOverlays(); restoreAllOwnedClocks();
} else { } else {
refreshAllClocks(); refreshAllClocks();
} }
@@ -218,14 +218,18 @@ final class StockClockController {
} }
} }
private void restoreAllOwnedClocks() {
for (TextView clockView : new ArrayList<>(trackedClocks)) {
restoreOwnedClock(clockView);
}
removeAllRowOverlays();
}
private void applyClockText(TextView clockView) { private void applyClockText(TextView clockView) {
SbtSettings settings = RuntimeSettingsCache.get(clockView.getContext()); SbtSettings settings = RuntimeSettingsCache.get(clockView.getContext());
if (!hasCustomClockTextEnabled(settings)) { boolean layoutEnabled = settings.clockEnabled;
stopTicker(clockView); if (layoutEnabled || !hasCustomClockTextEnabled(settings)) {
if (ownedClocks.contains(clockView)) { restoreOwnedClock(clockView);
restoreStockText(clockView);
ownedClocks.remove(clockView);
}
return; return;
} }
captureCurrentStockSnapshot(clockView); captureCurrentStockSnapshot(clockView);
@@ -255,6 +259,14 @@ final class StockClockController {
} }
} }
private void restoreOwnedClock(TextView clockView) {
stopTicker(clockView);
if (ownedClocks.contains(clockView)) {
restoreStockText(clockView);
ownedClocks.remove(clockView);
}
}
private ArrayList<ClockTextRenderer.LayoutRowResult> layoutRowsFromModel( private ArrayList<ClockTextRenderer.LayoutRowResult> layoutRowsFromModel(
ClockLayoutTextFactory.Model model ClockLayoutTextFactory.Model model
) { ) {
@@ -53,6 +53,28 @@ final class LockscreenCardsNotificationRowFilter {
return changed; return changed;
} }
boolean applyRow(View row, boolean allowAfterKeyguardUnlock) {
if (!isNotificationRow(row)) {
return false;
}
Context context = row.getContext();
boolean active = isActive(context, allowAfterKeyguardUnlock);
boolean changed;
if (active && shouldHideRow(context, row, allowAfterKeyguardUnlock)) {
changed = hideRow(row);
} else {
changed = restoreRow(row);
}
if (changed) {
requestLayout(row);
}
return changed;
}
void restoreRemovedRow(View row) {
restoreRow(row);
}
void restoreAll() { void restoreAll() {
if (hiddenRows.isEmpty()) { if (hiddenRows.isEmpty()) {
return; return;
@@ -138,12 +138,15 @@ final class StockLayoutCanvasController {
Collections.newSetFromMap(new WeakHashMap<>()); 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 =
Collections.newSetFromMap(new WeakHashMap<>());
private final Set<View> pendingDrawerAppearanceRefreshRoots = private final Set<View> pendingDrawerAppearanceRefreshRoots =
Collections.newSetFromMap(new WeakHashMap<>()); Collections.newSetFromMap(new WeakHashMap<>());
private final Set<View> dirtyUnlockedLayoutRoots = private final Set<View> dirtyUnlockedLayoutRoots =
Collections.newSetFromMap(new WeakHashMap<>()); Collections.newSetFromMap(new WeakHashMap<>());
private final Set<View> pendingDrawerStatusChipRefreshRoots = private final Set<View> pendingDrawerStatusChipRefreshRoots =
Collections.newSetFromMap(new WeakHashMap<>()); Collections.newSetFromMap(new WeakHashMap<>());
private final Map<View, Runnable> unlockedClockTickersByRoot = new WeakHashMap<>();
private final Set<ClassLoader> hookedPluginClassLoaders = private final Set<ClassLoader> hookedPluginClassLoaders =
Collections.newSetFromMap(new IdentityHashMap<>()); Collections.newSetFromMap(new IdentityHashMap<>());
private final Set<ClassLoader> pendingPluginClassLoaders = private final Set<ClassLoader> pendingPluginClassLoaders =
@@ -479,8 +482,9 @@ final class StockLayoutCanvasController {
"onViewAdded", "onViewAdded",
chain -> { chain -> {
Object result = chain.proceed(); Object result = chain.proceed();
if (chain.getThisObject() instanceof ViewGroup stack) { List<Object> args = chain.getArgs();
applyLockscreenCardsNotificationRowFilter(stack, false); if (!args.isEmpty() && args.get(0) instanceof View row) {
applyLockscreenCardsNotificationRowFilterRow(row, false);
} }
return result; return result;
}); });
@@ -490,8 +494,9 @@ final class StockLayoutCanvasController {
"onViewRemoved", "onViewRemoved",
chain -> { chain -> {
Object result = chain.proceed(); Object result = chain.proceed();
if (chain.getThisObject() instanceof ViewGroup stack) { List<Object> args = chain.getArgs();
applyLockscreenCardsNotificationRowFilter(stack, false); if (!args.isEmpty() && args.get(0) instanceof View row) {
lockscreenCardsNotificationRowFilter.restoreRemovedRow(row);
} }
return result; return result;
}); });
@@ -1780,6 +1785,7 @@ final class StockLayoutCanvasController {
return; return;
} }
SceneKey activeScene = runtimeContext.getModeStateRepository().getActiveScene(); SceneKey activeScene = runtimeContext.getModeStateRepository().getActiveScene();
ensureUnlockedClockTickerState(root, settings, activeScene);
if (!clockTextRefreshEnabledForScene(settings, activeScene)) { if (!clockTextRefreshEnabledForScene(settings, activeScene)) {
return; return;
} }
@@ -1804,6 +1810,80 @@ final class StockLayoutCanvasController {
return System.currentTimeMillis() / divisor; return System.currentTimeMillis() / divisor;
} }
private void ensureUnlockedClockTickerState(View root, SbtSettings settings, SceneKey scene) {
if (!clockNeedsSecondTicker(root, settings, scene)) {
stopUnlockedClockTicker(root);
return;
}
Runnable existing = unlockedClockTickersByRoot.get(root);
if (existing != null) {
return;
}
Runnable ticker = new Runnable() {
@Override
public void run() {
if (!clockNeedsSecondTicker(root, RuntimeSettingsCache.get(root.getContext()),
runtimeContext.getModeStateRepository().getActiveScene())) {
stopUnlockedClockTicker(root);
return;
}
scheduleUnlockedClockTextRefreshForRoot(root);
scheduleNextUnlockedClockTick(root, this);
}
};
unlockedClockTickersByRoot.put(root, ticker);
scheduleNextUnlockedClockTick(root, ticker);
}
private boolean clockNeedsSecondTicker(View root, SbtSettings settings, SceneKey scene) {
if (root == null
|| !root.isAttachedToWindow()
|| !isUnlockedStatusBarRoot(root)
|| settings == null
|| !settings.clockEnabled
|| !clockTextRefreshEnabledForScene(settings, scene)) {
return false;
}
return clockUsesSeconds(settings);
}
private boolean clockUsesSeconds(SbtSettings settings) {
if (settings == null) {
return false;
}
String pattern = settings.clockCustomFormat != null ? settings.clockCustomFormat : "";
return settings.clockShowSeconds
|| (settings.clockCustomFormatEnabled && pattern.contains("s"));
}
private void scheduleNextUnlockedClockTick(View root, Runnable ticker) {
if (root == null || ticker == null) {
return;
}
long now = System.currentTimeMillis();
long next = now - (now % 1000L) + 1000L;
root.postDelayed(ticker, Math.max(1L, next - now));
}
private void stopUnlockedClockTicker(View root) {
if (root == null) {
return;
}
Runnable ticker = unlockedClockTickersByRoot.remove(root);
if (ticker != null) {
root.removeCallbacks(ticker);
}
}
private void clearUnlockedClockRefreshState(View root) {
if (root == null) {
return;
}
stopUnlockedClockTicker(root);
pendingUnlockedClockTextRefreshRoots.remove(root);
clockTimeBucketByRoot.remove(root);
}
private void scheduleUnlockedAppearanceRefreshForAllRoots() { private void scheduleUnlockedAppearanceRefreshForAllRoots() {
scheduleUnlockedAppearanceRefreshForAllRoots(false); scheduleUnlockedAppearanceRefreshForAllRoots(false);
} }
@@ -1830,8 +1910,15 @@ final class StockLayoutCanvasController {
if (previous == null || current == null || previous.equals(current)) { if (previous == null || current == null || previous.equals(current)) {
return; return;
} }
if (previous.isLockscreen() && !current.isLockscreen()) { if (current.isUnlocked() && !previous.isUnlocked()) {
darkIconDispatcherGuard.restoreForcedStates(); darkIconDispatcherGuard.restoreForcedStates();
trackedLockedStatusBarIconSurfaces.clear();
} else if (previous.isLockscreen() && current.isAod()) {
darkIconDispatcherGuard.restoreForcedStates();
hideTrackedLockedStatusBarIconSurfaces(null);
} else if (previous.isLockscreen() && !current.isLockscreen()) {
darkIconDispatcherGuard.restoreForcedStates();
restoreTrackedLockedStatusBarIconSurfaces(null);
} }
boolean lockscreenEntry = current.isLockscreen(); boolean lockscreenEntry = current.isLockscreen();
if (lockscreenEntry) { if (lockscreenEntry) {
@@ -1847,6 +1934,7 @@ final class StockLayoutCanvasController {
if (root == null || !root.isAttachedToWindow()) { if (root == null || !root.isAttachedToWindow()) {
rootGeometries.remove(root); rootGeometries.remove(root);
unlockedHostsByRoot.remove(root); unlockedHostsByRoot.remove(root);
clearUnlockedClockRefreshState(root);
continue; continue;
} }
Runnable refresh = () -> refreshSceneRoot(root); Runnable refresh = () -> refreshSceneRoot(root);
@@ -1866,6 +1954,7 @@ final class StockLayoutCanvasController {
if (root == null || !root.isAttachedToWindow()) { if (root == null || !root.isAttachedToWindow()) {
rootGeometries.remove(root); rootGeometries.remove(root);
unlockedHostsByRoot.remove(root); unlockedHostsByRoot.remove(root);
clearUnlockedClockRefreshState(root);
return; return;
} }
unlockedIconRenderController.clearRoot(root); unlockedIconRenderController.clearRoot(root);
@@ -1926,11 +2015,11 @@ final class StockLayoutCanvasController {
if (root == null if (root == null
|| !root.isAttachedToWindow() || !root.isAttachedToWindow()
|| !isUnlockedStatusBarRoot(root) || !isUnlockedStatusBarRoot(root)
|| !pendingUnlockedAppearanceRefreshRoots.add(root)) { || !pendingUnlockedClockTextRefreshRoots.add(root)) {
return; return;
} }
root.post(() -> { root.post(() -> {
pendingUnlockedAppearanceRefreshRoots.remove(root); pendingUnlockedClockTextRefreshRoots.remove(root);
refreshUnlockedClockTextNow(root); refreshUnlockedClockTextNow(root);
}); });
} }
@@ -2077,11 +2166,19 @@ final class StockLayoutCanvasController {
} }
if (!layoutRoot) { if (!layoutRoot) {
if (activeScene != null if (activeScene != null
&& activeScene.isUnlocked()
&& isNotificationShadeWindowRoot(root)) {
hideUnlockedShadeShelfIconSurfaces(root);
removeSharedLayoutHosts(root);
} else if (activeScene != null
&& activeScene.isLockscreen() && activeScene.isLockscreen()
&& canContainLockedStatusBarIconSurfaces(root)) { && canContainLockedStatusBarIconSurfaces(root)) {
removeUnlockedLayoutHosts(root); removeUnlockedLayoutHosts(root);
removeAodCardsNotificationHost(root); removeAodCardsNotificationHost(root);
renderLockscreenCardsNotificationStripIfNeeded(root, settings, activeScene); if (activeScene.notificationDisplayStyle() != NotificationDisplayStyle.CARDS
&& hasLockscreenCardsNotificationHost(root)) {
removeLockscreenCardsNotificationHost(root);
}
hideOrDiscoverLockedStatusBarIconSurfaces(root); hideOrDiscoverLockedStatusBarIconSurfaces(root);
} else { } else {
removeSharedLayoutHosts(root); removeSharedLayoutHosts(root);
@@ -2100,9 +2197,6 @@ final class StockLayoutCanvasController {
&& (unlockedScene && (unlockedScene
|| activeScene != null && activeScene.isLockscreen() || activeScene != null && activeScene.isLockscreen()
|| aodScene)) { || aodScene)) {
if (!unlockedScene) {
restoreTrackedLockedStatusBarIconSurfaces(root);
}
boolean shouldRender = !unlockedIconRenderController.hasLayoutPlan(root) boolean shouldRender = !unlockedIconRenderController.hasLayoutPlan(root)
|| rootDirty; || rootDirty;
if (shouldRender && shouldDeferUnlockedRenderForDrawer(root)) { if (shouldRender && shouldDeferUnlockedRenderForDrawer(root)) {
@@ -2225,6 +2319,13 @@ final class StockLayoutCanvasController {
|| findDirectOwnedHost(group, OwnedIconHostManager.TAG_AOD_CARDS_NOTIFICATION_HOST) != null; || findDirectOwnedHost(group, OwnedIconHostManager.TAG_AOD_CARDS_NOTIFICATION_HOST) != null;
} }
private boolean hasLockscreenCardsNotificationHost(View root) {
return root instanceof ViewGroup group
&& findDirectOwnedHost(
group,
OwnedIconHostManager.TAG_LOCKSCREEN_CARDS_NOTIFICATION_HOST) != null;
}
private void applyDebugOverlay( private void applyDebugOverlay(
View root, View root,
boolean layoutEnabled, boolean layoutEnabled,
@@ -2346,6 +2447,51 @@ final class StockLayoutCanvasController {
}); });
} }
private void hideUnlockedShadeShelfIconSurfaces(View root) {
if (root == null) {
return;
}
boolean foundTracked = hideTrackedUnlockedShadeShelfIconSurfaces(root);
if (foundTracked) {
return;
}
walkTree(root, view -> {
if (isUnlockedShadeShelfIconSurface(view)) {
trackedUnlockedStockSurfaces.add(view);
hideView(view, false);
}
});
}
private boolean hideTrackedUnlockedShadeShelfIconSurfaces(View root) {
if (trackedUnlockedStockSurfaces.isEmpty()) {
return false;
}
boolean foundIconSurfaceInRoot = false;
ArrayDeque<View> staleViews = new ArrayDeque<>();
for (View view : trackedUnlockedStockSurfaces) {
if (view == null || !view.isAttachedToWindow()) {
staleViews.add(view);
continue;
}
if (!isDescendantOf(view, root)) {
continue;
}
if (!isUnlockedShadeShelfIconSurface(view)) {
continue;
}
foundIconSurfaceInRoot |= isCardsNotificationIconSurface(view);
hideView(view, false);
}
removeStaleViews(trackedUnlockedStockSurfaces, staleViews);
return foundIconSurfaceInRoot;
}
private boolean isUnlockedShadeShelfIconSurface(View view) {
return isCardsNotificationIconSurface(view)
|| isCardsNotificationShelfBackgroundSurface(view);
}
private boolean hasTrackedUnlockedStockSurfaces(View root) { private boolean hasTrackedUnlockedStockSurfaces(View root) {
if (trackedUnlockedStockSurfaces.isEmpty()) { if (trackedUnlockedStockSurfaces.isEmpty()) {
return false; return false;
@@ -2748,14 +2894,17 @@ final class StockLayoutCanvasController {
return false; return false;
} }
private void trackAndHideLockedStatusBarIconSurfaces(View root) { private int trackAndHideLockedStatusBarIconSurfaces(View root) {
final int[] found = {0};
walkTree(root, view -> { walkTree(root, view -> {
if (isLockedStatusBarIconSurface(view)) { if (isLockedStatusBarIconSurface(view)) {
rememberLockedCarrierTextSource(view); rememberLockedCarrierTextSource(view);
trackedLockedStatusBarIconSurfaces.add(view); trackedLockedStatusBarIconSurfaces.add(view);
hideView(view, isCarrierView(view)); hideView(view, isCarrierView(view));
found[0]++;
} }
}); });
return found[0];
} }
private void hideOrDiscoverLockedStatusBarIconSurfaces(View root) { private void hideOrDiscoverLockedStatusBarIconSurfaces(View root) {
@@ -2776,13 +2925,18 @@ final class StockLayoutCanvasController {
staleViews.add(view); staleViews.add(view);
continue; continue;
} }
if (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 (root == null || isDescendantOf(view, root)) {
foundInRoot = true;
hideView(view, isCarrierView(view)); hideView(view, isCarrierView(view));
} }
} }
@@ -2790,6 +2944,13 @@ final class StockLayoutCanvasController {
return foundInRoot; return foundInRoot;
} }
private boolean isAlreadySuppressedHiddenView(View view) {
return view != null
&& hiddenViewStates.containsKey(view)
&& view.getAlpha() == 0f
&& (view.getVisibility() == View.GONE || view.getVisibility() == View.VISIBLE);
}
private boolean shouldSuppressConflictingShadeHeaderAlpha(View view, float alpha) { private boolean shouldSuppressConflictingShadeHeaderAlpha(View view, float alpha) {
return view != null return view != null
&& alpha != view.getAlpha() && alpha != view.getAlpha()
@@ -2928,11 +3089,18 @@ final class StockLayoutCanvasController {
if (trackedLockedStatusBarIconSurfaces.isEmpty()) { if (trackedLockedStatusBarIconSurfaces.isEmpty()) {
return; return;
} }
ArrayDeque<View> restoredViews = new ArrayDeque<>();
for (View view : trackedLockedStatusBarIconSurfaces) { for (View view : trackedLockedStatusBarIconSurfaces) {
if (view != null && view.isAttachedToWindow() && (root == null || isDescendantOf(view, root))) { if (view == null || !view.isAttachedToWindow()) {
restoredViews.add(view);
continue;
}
if (root == null || isDescendantOf(view, root)) {
restoreView(view); restoreView(view);
restoredViews.add(view);
} }
} }
removeStaleViews(trackedLockedStatusBarIconSurfaces, restoredViews);
} }
private boolean hasTrackedStatusChipSources(View root) { private boolean hasTrackedStatusChipSources(View root) {
@@ -3297,7 +3465,7 @@ final class StockLayoutCanvasController {
aodBeforeDrawSignatureByRoot.remove(root); aodBeforeDrawSignatureByRoot.remove(root);
lockscreenCardsRowFilterSignatures.remove(root); lockscreenCardsRowFilterSignatures.remove(root);
lockscreenCardsNotificationRenderGenerationByRoot.remove(root); lockscreenCardsNotificationRenderGenerationByRoot.remove(root);
clockTimeBucketByRoot.remove(root); clearUnlockedClockRefreshState(root);
removeDebugOverlay(root); removeDebugOverlay(root);
root.requestLayout(); root.requestLayout();
root.invalidate(); root.invalidate();
@@ -4046,7 +4214,7 @@ final class StockLayoutCanvasController {
dirtyUnlockedLayoutRoots.remove(root); dirtyUnlockedLayoutRoots.remove(root);
settingsIdentityByRoot.remove(root); settingsIdentityByRoot.remove(root);
lockscreenCardsRowFilterSignatures.remove(root); lockscreenCardsRowFilterSignatures.remove(root);
clockTimeBucketByRoot.remove(root); clearUnlockedClockRefreshState(root);
if (!isUnlockedStatusBarRoot(root) || !layoutOffCleanedRoots.add(root)) { if (!isUnlockedStatusBarRoot(root) || !layoutOffCleanedRoots.add(root)) {
unlockedHostsByRoot.remove(root); unlockedHostsByRoot.remove(root);
rootGeometries.remove(root); rootGeometries.remove(root);
@@ -4117,7 +4285,7 @@ final class StockLayoutCanvasController {
pendingUnlockedAppearanceRefreshRoots.remove(root); pendingUnlockedAppearanceRefreshRoots.remove(root);
dirtyUnlockedLayoutRoots.remove(root); dirtyUnlockedLayoutRoots.remove(root);
settingsIdentityByRoot.remove(root); settingsIdentityByRoot.remove(root);
clockTimeBucketByRoot.remove(root); clearUnlockedClockRefreshState(root);
} }
private void removeUnlockedLayoutHosts(View root) { private void removeUnlockedLayoutHosts(View root) {
@@ -4130,7 +4298,7 @@ final class StockLayoutCanvasController {
pendingUnlockedAppearanceRefreshRoots.remove(root); pendingUnlockedAppearanceRefreshRoots.remove(root);
dirtyUnlockedLayoutRoots.remove(root); dirtyUnlockedLayoutRoots.remove(root);
settingsIdentityByRoot.remove(root); settingsIdentityByRoot.remove(root);
clockTimeBucketByRoot.remove(root); clearUnlockedClockRefreshState(root);
} }
private boolean isDescendantOfNotificationShadeWindow(View view) { private boolean isDescendantOfNotificationShadeWindow(View view) {
@@ -4185,16 +4353,23 @@ final class StockLayoutCanvasController {
SbtSettings settings, SbtSettings settings,
SceneKey activeScene SceneKey activeScene
) { ) {
View visibleBouncerSurface = findVisibleBouncerSurface(root);
boolean suppressForTransition = runtimeContext.getModeStateRepository().isSystemUiShadeLocked()
|| visibleBouncerSurface != null;
if (root == null if (root == null
|| settings == null || settings == null
|| activeScene == null || activeScene == null
|| !activeScene.isLockscreen() || !activeScene.isLockscreen()
|| activeScene.notificationDisplayStyle() != NotificationDisplayStyle.CARDS || activeScene.notificationDisplayStyle() != NotificationDisplayStyle.CARDS
|| !isNotificationShadeWindowRoot(root) || !isNotificationShadeWindowRoot(root)) {
|| suppressForTransition) { removeLockscreenCardsNotificationHost(root);
lockscreenCardsNotificationRenderGenerationByRoot.remove(root);
return;
}
if (runtimeContext.getModeStateRepository().isSystemUiShadeLocked()) {
removeLockscreenCardsNotificationHost(root);
lockscreenCardsNotificationRenderGenerationByRoot.remove(root);
return;
}
View visibleBouncerSurface = findVisibleBouncerSurface(root);
if (visibleBouncerSurface != null) {
removeLockscreenCardsNotificationHost(root); removeLockscreenCardsNotificationHost(root);
lockscreenCardsNotificationRenderGenerationByRoot.remove(root); lockscreenCardsNotificationRenderGenerationByRoot.remove(root);
return; return;
@@ -4210,7 +4385,7 @@ final class StockLayoutCanvasController {
int rootHeight = root.getHeight(); int rootHeight = root.getHeight();
int activeKeyGeneration = NotificationActiveKeyStore.generation(); int activeKeyGeneration = NotificationActiveKeyStore.generation();
int sourceSignature = int sourceSignature =
LockscreenCardsNotificationIconSourceStore.snapshotSourceSignatureForRoot(root); LockscreenCardsNotificationIconSourceStore.lightweightSourceSignatureForRoot(root);
int generation = settingsId; int generation = settingsId;
generation = 31 * generation + rootWidth; generation = 31 * generation + rootWidth;
generation = 31 * generation + rootHeight; generation = 31 * generation + rootHeight;
@@ -4224,7 +4399,7 @@ final class StockLayoutCanvasController {
lockscreenCardsNotificationRenderGenerationByRoot.put(root, generation); lockscreenCardsNotificationRenderGenerationByRoot.put(root, generation);
lockscreenCardsNotificationStripRenderer.render(root, hostGroup, settings, activeScene); lockscreenCardsNotificationStripRenderer.render(root, hostGroup, settings, activeScene);
} }
host.bringToFront(); bringToFrontIfNeeded(host);
} }
} }
@@ -4271,10 +4446,21 @@ final class StockLayoutCanvasController {
lockscreenCardsNotificationStripRenderer.render(root, hostGroup, settings, activeScene); lockscreenCardsNotificationStripRenderer.render(root, hostGroup, settings, activeScene);
} }
setHostAlpha(host, aodVisualAlphaForRoot(root)); setHostAlpha(host, aodVisualAlphaForRoot(root));
host.bringToFront(); bringToFrontIfNeeded(host);
} }
} }
private void bringToFrontIfNeeded(View view) {
if (view == null || !(view.getParent() instanceof ViewGroup group)) {
return;
}
int childCount = group.getChildCount();
if (childCount <= 0 || group.getChildAt(childCount - 1) == view) {
return;
}
view.bringToFront();
}
private void applyLockscreenCardsNotificationRowFilter(ViewGroup stack, boolean allowAfterKeyguardUnlock) { private void applyLockscreenCardsNotificationRowFilter(ViewGroup stack, boolean allowAfterKeyguardUnlock) {
if (stack == null) { if (stack == null) {
return; return;
@@ -4286,6 +4472,17 @@ final class StockLayoutCanvasController {
lockscreenCardsNotificationRowFilter.apply(stack, allowAfterKeyguardUnlock); lockscreenCardsNotificationRowFilter.apply(stack, allowAfterKeyguardUnlock);
} }
private void applyLockscreenCardsNotificationRowFilterRow(View row, boolean allowAfterKeyguardUnlock) {
if (row == null) {
return;
}
if (runtimeContext.getModeStateRepository().isSystemUiShadeLocked()) {
lockscreenCardsNotificationRowFilter.restoreRemovedRow(row);
return;
}
lockscreenCardsNotificationRowFilter.applyRow(row, allowAfterKeyguardUnlock);
}
private boolean applyLockscreenCardsNotificationRowFilterInTreeIfNeeded( private boolean applyLockscreenCardsNotificationRowFilterInTreeIfNeeded(
View root, View root,
SbtSettings settings, SbtSettings settings,
@@ -4311,7 +4508,6 @@ final class StockLayoutCanvasController {
+ (runtimeContext.getModeStateRepository().isSystemUiShadeLocked() ? 1 : 0); + (runtimeContext.getModeStateRepository().isSystemUiShadeLocked() ? 1 : 0);
result = 31 * result + root.getWidth(); result = 31 * result + root.getWidth();
result = 31 * result + root.getHeight(); result = 31 * result + root.getHeight();
result = 31 * result + (root instanceof ViewGroup group ? group.getChildCount() : 0);
return result; return result;
} }
@@ -4471,7 +4667,7 @@ final class StockLayoutCanvasController {
dirtyUnlockedLayoutRoots.remove(root); dirtyUnlockedLayoutRoots.remove(root);
settingsIdentityByRoot.remove(root); settingsIdentityByRoot.remove(root);
lockscreenCardsRowFilterSignatures.remove(root); lockscreenCardsRowFilterSignatures.remove(root);
clockTimeBucketByRoot.remove(root); clearUnlockedClockRefreshState(root);
return null; return null;
} }
@@ -63,7 +63,6 @@ final class ClockLayout {
int baseHeightSteps, int baseHeightSteps,
int autoGapSteps, int autoGapSteps,
int statusbarHeight, int statusbarHeight,
int visualBaselineY,
int verticalOffsetSteps int verticalOffsetSteps
) { ) {
if (source == null || model == null || model.rows.isEmpty()) { if (source == null || model == null || model.rows.isEmpty()) {
@@ -102,10 +101,8 @@ final class ClockLayout {
} }
measuredRows = positionRowsFromLogicalSteps( measuredRows = positionRowsFromLogicalSteps(
measuredRows, measuredRows,
Math.max(1, baseHeightSteps),
Math.max(1, autoGapSteps), Math.max(1, autoGapSteps),
Math.max(1, statusbarHeight), Math.max(1, statusbarHeight),
visualBaselineY,
verticalOffsetSteps); verticalOffsetSteps);
RowAlignment alignment = alignRows(measuredRows, position); RowAlignment alignment = alignRows(measuredRows, position);
@@ -216,10 +213,8 @@ final class ClockLayout {
private static ArrayList<MeasuredClockRow> positionRowsFromLogicalSteps( private static ArrayList<MeasuredClockRow> positionRowsFromLogicalSteps(
ArrayList<MeasuredClockRow> measuredRows, ArrayList<MeasuredClockRow> measuredRows,
int baseHeightSteps,
int autoGapSteps, int autoGapSteps,
int statusbarHeight, int statusbarHeight,
int visualBaselineY,
int verticalOffsetSteps int verticalOffsetSteps
) { ) {
ArrayList<MeasuredClockRow> positionedRows = new ArrayList<>(measuredRows); ArrayList<MeasuredClockRow> positionedRows = new ArrayList<>(measuredRows);
@@ -232,8 +227,7 @@ final class ClockLayout {
if (rowIndex == measuredRows.size() - 1) { if (rowIndex == measuredRows.size() - 1) {
topSteps = 100 - Math.max(1, row.logicalHeightSteps) - verticalOffsetSteps; topSteps = 100 - Math.max(1, row.logicalHeightSteps) - verticalOffsetSteps;
int heightPx = Math.max(1, stepsToPx(Math.max(1, row.logicalHeightSteps), statusbarHeight)); int heightPx = Math.max(1, stepsToPx(Math.max(1, row.logicalHeightSteps), statusbarHeight));
int baselineY = visualBaselineY - stepsToPx(verticalOffsetSteps, statusbarHeight); bottomPx = Math.max(1, statusbarHeight) - stepsToPx(verticalOffsetSteps, statusbarHeight);
bottomPx = baselineY - row.baseline + heightPx;
} else { } else {
MeasuredClockRow nextRow = measuredRows.get(rowIndex + 1); MeasuredClockRow nextRow = measuredRows.get(rowIndex + 1);
int gapSteps = resolveGapSteps(nextRow.source.gapBeforePx, autoGapSteps); int gapSteps = resolveGapSteps(nextRow.source.gapBeforePx, autoGapSteps);
@@ -27,6 +27,7 @@ public final class LockscreenCardsNotificationIconSourceStore {
new WeakHashMap<>(); new WeakHashMap<>();
private static final ArrayList<String> AOD_NOTIFICATION_PACKAGES = new ArrayList<>(); private static final ArrayList<String> AOD_NOTIFICATION_PACKAGES = new ArrayList<>();
private static final int VIRTUAL_ICON_SIZE_PX = 39; private static final int VIRTUAL_ICON_SIZE_PX = 39;
private static int generation;
private LockscreenCardsNotificationIconSourceStore() { private LockscreenCardsNotificationIconSourceStore() {
} }
@@ -34,7 +35,9 @@ public final class LockscreenCardsNotificationIconSourceStore {
public static void put(ViewGroup container, ArrayList<SourceEntry> sources) { public static void put(ViewGroup container, ArrayList<SourceEntry> sources) {
if (container == null || sources == null || sources.isEmpty()) { if (container == null || sources == null || sources.isEmpty()) {
if (container != null) { if (container != null) {
SOURCES_BY_CONTAINER.remove(container); if (SOURCES_BY_CONTAINER.remove(container) != null) {
bumpGeneration();
}
} }
return; return;
} }
@@ -47,7 +50,12 @@ public final class LockscreenCardsNotificationIconSourceStore {
attached.add(source); attached.add(source);
} }
if (attached.isEmpty()) { if (attached.isEmpty()) {
SOURCES_BY_CONTAINER.remove(container); if (SOURCES_BY_CONTAINER.remove(container) != null) {
bumpGeneration();
}
} else if (!sameSourceEntries(SOURCES_BY_CONTAINER.get(container), attached)) {
SOURCES_BY_CONTAINER.put(container, attached);
bumpGeneration();
} else { } else {
SOURCES_BY_CONTAINER.put(container, attached); SOURCES_BY_CONTAINER.put(container, attached);
} }
@@ -56,7 +64,11 @@ public final class LockscreenCardsNotificationIconSourceStore {
public static boolean putPackages(ViewGroup container, ArrayList<String> packages) { public static boolean putPackages(ViewGroup container, ArrayList<String> packages) {
if (container == null || packages == null || packages.isEmpty()) { if (container == null || packages == null || packages.isEmpty()) {
if (container != null) { if (container != null) {
return PACKAGES_BY_CONTAINER.remove(container) != null; boolean changed = PACKAGES_BY_CONTAINER.remove(container) != null;
if (changed) {
bumpGeneration();
}
return changed;
} }
return false; return false;
} }
@@ -71,13 +83,18 @@ public final class LockscreenCardsNotificationIconSourceStore {
} }
} }
if (!hasValid) { if (!hasValid) {
return PACKAGES_BY_CONTAINER.remove(container) != null; boolean changed = PACKAGES_BY_CONTAINER.remove(container) != null;
if (changed) {
bumpGeneration();
}
return changed;
} else { } else {
ArrayList<String> previous = PACKAGES_BY_CONTAINER.get(container); ArrayList<String> previous = PACKAGES_BY_CONTAINER.get(container);
if (previous != null && previous.equals(valid)) { if (previous != null && previous.equals(valid)) {
return false; return false;
} }
PACKAGES_BY_CONTAINER.put(container, valid); PACKAGES_BY_CONTAINER.put(container, valid);
bumpGeneration();
return true; return true;
} }
} }
@@ -85,7 +102,11 @@ public final class LockscreenCardsNotificationIconSourceStore {
public static boolean putAodDrawables(ViewGroup container, ArrayList<? extends View> views) { public static boolean putAodDrawables(ViewGroup container, ArrayList<? extends View> views) {
if (container == null || views == null || views.isEmpty()) { if (container == null || views == null || views.isEmpty()) {
if (container != null) { if (container != null) {
return AOD_DRAWABLES_BY_CONTAINER.remove(container) != null; boolean changed = AOD_DRAWABLES_BY_CONTAINER.remove(container) != null;
if (changed) {
bumpGeneration();
}
return changed;
} }
return false; return false;
} }
@@ -100,18 +121,28 @@ public final class LockscreenCardsNotificationIconSourceStore {
valid.add(view); valid.add(view);
} }
if (valid.isEmpty()) { if (valid.isEmpty()) {
return AOD_DRAWABLES_BY_CONTAINER.remove(container) != null; boolean changed = AOD_DRAWABLES_BY_CONTAINER.remove(container) != null;
if (changed) {
bumpGeneration();
}
return changed;
} else { } else {
ArrayList<View> previous = AOD_DRAWABLES_BY_CONTAINER.get(container); ArrayList<View> previous = AOD_DRAWABLES_BY_CONTAINER.get(container);
if (sameViews(previous, valid)) { if (sameViews(previous, valid)) {
return false; return false;
} }
AOD_DRAWABLES_BY_CONTAINER.put(container, valid); AOD_DRAWABLES_BY_CONTAINER.put(container, valid);
bumpGeneration();
return true; return true;
} }
} }
public static void clearAodSources() { public static void clearAodSources() {
if (!PACKAGES_BY_CONTAINER.isEmpty()
|| !AOD_DRAWABLES_BY_CONTAINER.isEmpty()
|| !AOD_NOTIFICATION_PACKAGES.isEmpty()) {
bumpGeneration();
}
PACKAGES_BY_CONTAINER.clear(); PACKAGES_BY_CONTAINER.clear();
AOD_DRAWABLES_BY_CONTAINER.clear(); AOD_DRAWABLES_BY_CONTAINER.clear();
AOD_NOTIFICATION_PACKAGES.clear(); AOD_NOTIFICATION_PACKAGES.clear();
@@ -121,21 +152,77 @@ public final class LockscreenCardsNotificationIconSourceStore {
if (container == null) { if (container == null) {
return; return;
} }
PACKAGES_BY_CONTAINER.remove(container); boolean changed = PACKAGES_BY_CONTAINER.remove(container) != null;
AOD_DRAWABLES_BY_CONTAINER.remove(container); changed |= AOD_DRAWABLES_BY_CONTAINER.remove(container) != null;
if (changed) {
bumpGeneration();
}
} }
public static void putAodNotificationPackages(ArrayList<String> packages) { public static void putAodNotificationPackages(ArrayList<String> packages) {
ArrayList<String> previous = new ArrayList<>(AOD_NOTIFICATION_PACKAGES);
AOD_NOTIFICATION_PACKAGES.clear(); AOD_NOTIFICATION_PACKAGES.clear();
if (packages == null || packages.isEmpty()) { if (packages != null && !packages.isEmpty()) {
return;
}
for (String pkg : packages) { for (String pkg : packages) {
if (AppIconRules.isValidPackageName(pkg) && !AOD_NOTIFICATION_PACKAGES.contains(pkg)) { if (AppIconRules.isValidPackageName(pkg) && !AOD_NOTIFICATION_PACKAGES.contains(pkg)) {
AOD_NOTIFICATION_PACKAGES.add(pkg); AOD_NOTIFICATION_PACKAGES.add(pkg);
} }
} }
} }
if (!previous.equals(AOD_NOTIFICATION_PACKAGES)) {
bumpGeneration();
}
}
public static int generation() {
return generation;
}
private static void bumpGeneration() {
generation++;
}
private static boolean sameSourceEntries(
ArrayList<SourceEntry> previous,
ArrayList<SourceEntry> current
) {
if (previous == current) {
return true;
}
if (previous == null || current == null || previous.size() != current.size()) {
return false;
}
for (int i = 0; i < previous.size(); i++) {
SourceEntry oldEntry = previous.get(i);
SourceEntry newEntry = current.get(i);
if (oldEntry == newEntry) {
continue;
}
if (oldEntry == null || newEntry == null) {
return false;
}
if (oldEntry.view != newEntry.view) {
return false;
}
if (oldEntry.key == null ? newEntry.key != null : !oldEntry.key.equals(newEntry.key)) {
return false;
}
}
return true;
}
public static int lightweightSourceSignatureForRoot(View root) {
int result = 17;
result = 31 * result + generation();
ViewGroup container = sourceContainerForRoot(root);
Bounds bounds = sourceBoundsRelativeTo(root, container);
if (isZeroSizeDotContainer(container, bounds)) {
bounds = new Bounds(bounds.left, bounds.top, VIRTUAL_ICON_SIZE_PX, VIRTUAL_ICON_SIZE_PX);
}
result = 31 * result + (bounds != null ? bounds.signature() : 0);
result = 31 * result + (sourceContainerHasHiddenAncestor(root, container) ? 1 : 0);
return result;
}
private static boolean sameViews(ArrayList<View> previous, ArrayList<View> current) { private static boolean sameViews(ArrayList<View> previous, ArrayList<View> current) {
if (previous == current) { if (previous == current) {
@@ -348,6 +435,10 @@ public final class LockscreenCardsNotificationIconSourceStore {
public static boolean sourceContainerHasHiddenAncestorForRoot(View root) { public static boolean sourceContainerHasHiddenAncestorForRoot(View root) {
ViewGroup container = sourceContainerForRoot(root); ViewGroup container = sourceContainerForRoot(root);
return sourceContainerHasHiddenAncestor(root, container);
}
private static boolean sourceContainerHasHiddenAncestor(View root, ViewGroup container) {
if (container == null) { if (container == null) {
return false; return false;
} }
@@ -579,11 +579,21 @@ final class SnapshotRenderer {
int result = 17; int result = 17;
result = 31 * result + sourceTreeSignature(source.view()); result = 31 * result + sourceTreeSignature(source.view());
Bounds boundsOverride = source.boundsOverride(); Bounds boundsOverride = source.boundsOverride();
result = 31 * result + (boundsOverride != null ? boundsOverride.signature() : 0); result = 31 * result + boundsSizeSignature(boundsOverride);
result = 31 * result + tintSignatureFor(source); result = 31 * result + tintSignatureFor(source);
return result; return result;
} }
private int boundsSizeSignature(Bounds bounds) {
if (bounds == null) {
return 0;
}
int result = 17;
result = 31 * result + bounds.width;
result = 31 * result + bounds.height;
return result;
}
private int tintSignatureFor(SnapshotSource source) { private int tintSignatureFor(SnapshotSource source) {
return shouldApplyStatusBarTintTarget(source) ? StatusBarTintTarget.signature() : 0; return shouldApplyStatusBarTintTarget(source) ? StatusBarTintTarget.signature() : 0;
} }
@@ -113,7 +113,14 @@ public final class UnlockedIconSnapshotRenderController {
false, false,
false, false,
false, false,
!scene.isLockscreen() || lastLayoutPlanByRoot.get(root) != null); !scene.isLockscreen() || lastLayoutPlanByRoot.get(root) != null,
lockscreenIconDebugSummary(
root,
notificationHost,
scene,
cachedPlan,
lastCollectedIconsByRoot.get(root),
"cacheHit"));
} }
lastLayoutSignatureByRoot.put(root, signature); lastLayoutSignatureByRoot.put(root, signature);
Integer currentClockSourceGeometry = clockSourceGeometrySignature( Integer currentClockSourceGeometry = clockSourceGeometrySignature(
@@ -191,7 +198,18 @@ public final class UnlockedIconSnapshotRenderController {
if (scene.isLockscreen() && clockEnabledForScene(settings, scene) && currentClockLayout == null) { if (scene.isLockscreen() && clockEnabledForScene(settings, scene) && currentClockLayout == null) {
clearLockscreenHosts(clockHost, carrierHost, chipHost, statusHost, notificationHost); clearLockscreenHosts(clockHost, carrierHost, chipHost, statusHost, notificationHost);
lastLayoutPlanByRoot.remove(root); lastLayoutPlanByRoot.remove(root);
return new RenderResult(false, stockSourcesChanged, chipSourcesChanged, false); return new RenderResult(
false,
stockSourcesChanged,
chipSourcesChanged,
false,
lockscreenIconDebugSummary(
root,
notificationHost,
scene,
null,
collectedIcons,
"missingClock"));
} }
LayoutPlan layoutPlan = layoutPlanner.solve( LayoutPlan layoutPlan = layoutPlanner.solve(
root, root,
@@ -237,7 +255,18 @@ public final class UnlockedIconSnapshotRenderController {
} else { } else {
notificationRenderer.clear(notificationHost); notificationRenderer.clear(notificationHost);
} }
return new RenderResult(true, stockSourcesChanged, chipSourcesChanged); return new RenderResult(
true,
stockSourcesChanged,
chipSourcesChanged,
true,
lockscreenIconDebugSummary(
root,
notificationHost,
scene,
layoutPlan,
collectedIcons,
"solved"));
} }
public void refreshUnlockedAppearance( public void refreshUnlockedAppearance(
@@ -722,7 +751,6 @@ public final class UnlockedIconSnapshotRenderController {
int textSizeSteps = clockTextSizeSteps(settings, scene); int textSizeSteps = clockTextSizeSteps(settings, scene);
float textSizePx = clockTextSizePx(statusbarHeight, textSizeSteps); float textSizePx = clockTextSizePx(statusbarHeight, textSizeSteps);
int autoGapSteps = clockAutoGapSteps(source, statusbarHeight, textSizeSteps); int autoGapSteps = clockAutoGapSteps(source, statusbarHeight, textSizeSteps);
int visualBaselineY = clockVisualBaselineY(source, sourceBounds, statusbarHeight);
ClockLayout layout = ClockLayout.measure( ClockLayout layout = ClockLayout.measure(
source, source,
model, model,
@@ -735,7 +763,6 @@ public final class UnlockedIconSnapshotRenderController {
textSizeSteps, textSizeSteps,
autoGapSteps, autoGapSteps,
statusbarHeight, statusbarHeight,
visualBaselineY,
settings.clockVerticalOffsetPx); settings.clockVerticalOffsetPx);
return layout; return layout;
} }
@@ -748,17 +775,6 @@ public final class UnlockedIconSnapshotRenderController {
return Math.max(1, Math.round(lineHeight * 100f / statusbarHeight)); return Math.max(1, Math.round(lineHeight * 100f / statusbarHeight));
} }
private int clockVisualBaselineY(TextView source, Bounds sourceBounds, int fallbackBaselineY) {
if (source == null || sourceBounds == null) {
return Math.max(1, fallbackBaselineY);
}
int baseline = source.getBaseline();
if (baseline <= 0) {
return Math.max(1, sourceBounds.top + sourceBounds.height);
}
return Math.max(1, sourceBounds.top + baseline);
}
private int clockTextSizeSteps(SbtSettings settings, SceneKey scene) { private int clockTextSizeSteps(SbtSettings settings, SceneKey scene) {
return scene != null && scene.isLockscreen() return scene != null && scene.isLockscreen()
? settings.clockLockTextSizeSteps ? settings.clockLockTextSizeSteps
@@ -1039,14 +1055,127 @@ public final class UnlockedIconSnapshotRenderController {
return result; return result;
} }
private String lockscreenIconDebugSummary(
View root,
ViewGroup notificationHost,
SceneKey scene,
LayoutPlan plan,
CollectedIcons icons,
String phase
) {
if (scene == null
|| !scene.isLockscreen()
|| scene.notificationDisplayStyle() != NotificationDisplayStyle.ICONS) {
return null;
}
ArrayList<SnapshotPlacement> notificationPlacements =
plan != null ? plan.notificationPlacements : null;
return "phase=" + phase
+ " root=" + viewBounds(root)
+ " hostChildren=" + (notificationHost != null ? notificationHost.getChildCount() : -1)
+ " sourceNotifs=" + sizeOf(icons != null ? icons.notificationIcons : null)
+ " placementCount=" + sizeOf(notificationPlacements)
+ " renderableCount=" + renderablePlacementCount(notificationPlacements)
+ " clock=" + boundsText(plan != null && plan.clockPlacement != null
? plan.clockPlacement.bounds
: null)
+ " carrier=" + boundsText(plan != null && plan.carrierPlacement != null
? plan.carrierPlacement.bounds
: null)
+ " notifs=" + placementSummary(notificationPlacements);
}
private int sizeOf(ArrayList<?> values) {
return values != null ? values.size() : 0;
}
private int renderablePlacementCount(ArrayList<SnapshotPlacement> placements) {
if (placements == null) {
return 0;
}
int count = 0;
for (SnapshotPlacement placement : placements) {
if (isRenderablePlacement(placement)) {
count++;
}
}
return count;
}
private boolean isRenderablePlacement(SnapshotPlacement placement) {
return placement != null
&& (placement.source != null
|| placement.overflowDot
|| placement.heldSignal != null);
}
private String placementSummary(ArrayList<SnapshotPlacement> placements) {
if (placements == null || placements.isEmpty()) {
return "[]";
}
StringBuilder out = new StringBuilder("[");
int limit = Math.min(placements.size(), 8);
for (int i = 0; i < limit; i++) {
if (i > 0) {
out.append(';');
}
SnapshotPlacement placement = placements.get(i);
out.append(i).append('{');
if (placement == null) {
out.append("null");
} else {
out.append("key=").append(placement.key)
.append(",b=").append(boundsText(placement.bounds))
.append(",source=").append(placement.source != null)
.append(",dot=").append(placement.overflowDot)
.append(",held=").append(placement.heldSignal != null)
.append(",renderable=").append(isRenderablePlacement(placement));
}
out.append('}');
}
if (placements.size() > limit) {
out.append(";...");
}
out.append(']');
return out.toString();
}
private String viewBounds(View view) {
if (view == null) {
return "null";
}
return view.getWidth() + "x" + view.getHeight()
+ "+" + view.getLeft()
+ "," + view.getTop();
}
private String boundsText(Bounds bounds) {
if (bounds == null) {
return "null";
}
return bounds.width + "x" + bounds.height
+ "+" + bounds.left
+ "," + bounds.top;
}
public record RenderResult( public record RenderResult(
boolean layoutChanged,
boolean stockSourcesChanged,
boolean chipSourcesChanged,
boolean shouldOwnLockscreen,
String debugSummary
) {
public RenderResult(boolean layoutChanged, boolean stockSourcesChanged, boolean chipSourcesChanged) {
this(layoutChanged, stockSourcesChanged, chipSourcesChanged, true, null);
}
public RenderResult(
boolean layoutChanged, boolean layoutChanged,
boolean stockSourcesChanged, boolean stockSourcesChanged,
boolean chipSourcesChanged, boolean chipSourcesChanged,
boolean shouldOwnLockscreen boolean shouldOwnLockscreen
) { ) {
public RenderResult(boolean layoutChanged, boolean stockSourcesChanged, boolean chipSourcesChanged) { this(layoutChanged, stockSourcesChanged, chipSourcesChanged, shouldOwnLockscreen, null);
this(layoutChanged, stockSourcesChanged, chipSourcesChanged, true);
} }
} }
@@ -103,7 +103,8 @@ final class UnlockedLayoutPlanner {
scene, scene,
edges, edges,
layoutCutoutBounds, layoutCutoutBounds,
clockLayout); clockLayout,
statusbarHeight);
if (carrierLayout != null && carrierLayout.width() > 0) { if (carrierLayout != null && carrierLayout.width() > 0) {
bands.add(UnlockedLayoutBand.carrier( bands.add(UnlockedLayoutBand.carrier(
carrierHost, carrierHost,
@@ -649,7 +650,8 @@ final class UnlockedLayoutPlanner {
SceneKey scene, SceneKey scene,
LayoutEdges edges, LayoutEdges edges,
Bounds cutoutBounds, Bounds cutoutBounds,
ClockLayout clockLayout ClockLayout clockLayout,
int statusbarHeight
) { ) {
if (root == null if (root == null
|| collectedIcons == null || collectedIcons == null
@@ -687,9 +689,11 @@ final class UnlockedLayoutPlanner {
settings, settings,
edges, edges,
cutoutBounds, cutoutBounds,
scene,
positionFor(settings.layoutCarrierPosition), positionFor(settings.layoutCarrierPosition),
sideFor(settings.layoutCarrierMiddleSide), sideFor(settings.layoutCarrierMiddleSide),
clockLayout)); clockLayout,
statusbarHeight));
} }
private int carrierMaxWidth( private int carrierMaxWidth(
@@ -697,9 +701,11 @@ final class UnlockedLayoutPlanner {
SbtSettings settings, SbtSettings settings,
LayoutEdges edges, LayoutEdges edges,
Bounds cutoutBounds, Bounds cutoutBounds,
SceneKey scene,
LayoutPosition position, LayoutPosition position,
AnchorSide middleSide, AnchorSide middleSide,
ClockLayout clockLayout ClockLayout clockLayout,
int statusbarHeight
) { ) {
int rootWidth = root != null ? root.getWidth() : 0; int rootWidth = root != null ? root.getWidth() : 0;
if (rootWidth <= 0 || edges == null) { if (rootWidth <= 0 || edges == null) {
@@ -723,42 +729,86 @@ final class UnlockedLayoutPlanner {
return carrierMaxWidthWithClockReserve( return carrierMaxWidthWithClockReserve(
Math.max(1, right - minLeft), Math.max(1, right - minLeft),
settings, settings,
scene,
position, position,
middleSide, middleSide,
clockLayout, clockLayout,
AnchorSide.RIGHT); AnchorSide.RIGHT,
statusbarHeight);
} }
int maxRight = usableCutout ? Math.max(left, cutoutBounds.left) : right; int maxRight = usableCutout ? Math.max(left, cutoutBounds.left) : right;
return carrierMaxWidthWithClockReserve( return carrierMaxWidthWithClockReserve(
Math.max(1, maxRight - left), Math.max(1, maxRight - left),
settings, settings,
scene,
position, position,
middleSide, middleSide,
clockLayout, clockLayout,
AnchorSide.LEFT); AnchorSide.LEFT,
statusbarHeight);
} }
private int carrierMaxWidthWithClockReserve( private int carrierMaxWidthWithClockReserve(
int sideWidth, int sideWidth,
SbtSettings settings, SbtSettings settings,
SceneKey scene,
LayoutPosition carrierPosition, LayoutPosition carrierPosition,
AnchorSide carrierMiddleSide, AnchorSide carrierMiddleSide,
ClockLayout clockLayout, ClockLayout clockLayout,
AnchorSide side AnchorSide side,
int statusbarHeight
) { ) {
if (sideWidth <= 0 || settings == null || clockLayout == null || clockLayout.width() <= 0) { if (sideWidth <= 0 || settings == null) {
return Math.max(1, sideWidth); return Math.max(1, sideWidth);
} }
if (!occupiesSide(carrierPosition, carrierMiddleSide, side)) {
return Math.max(1, sideWidth);
}
int reserved = notificationDotReserveForSide(settings, scene, side, statusbarHeight);
if (clockLayout != null && clockLayout.width() > 0) {
LayoutPosition clockPosition = positionFor(settings.clockPosition); LayoutPosition clockPosition = positionFor(settings.clockPosition);
AnchorSide clockMiddleSide = sideFor(settings.clockMiddleCutoutSide); AnchorSide clockMiddleSide = sideFor(settings.clockMiddleCutoutSide);
if (!occupiesSide(clockPosition, clockMiddleSide, side) if (occupiesSide(clockPosition, clockMiddleSide, side)) {
|| !occupiesSide(carrierPosition, carrierMiddleSide, side)) { reserved += clockLayout.width() + Math.max(0, settings.layoutItemPaddingPx);
return Math.max(1, sideWidth); }
} }
int reserved = clockLayout.width() + Math.max(0, settings.layoutItemPaddingPx);
return Math.max(1, sideWidth - reserved); return Math.max(1, sideWidth - reserved);
} }
private int notificationDotReserveForSide(
SbtSettings settings,
SceneKey scene,
AnchorSide side,
int statusbarHeight
) {
if (settings == null
|| scene == null
|| !scene.isLockscreen()
|| !settings.layoutNotifEnabledLockscreen
|| (scene.notificationDisplayStyle() != NotificationDisplayStyle.ICONS
&& scene.notificationDisplayStyle() != NotificationDisplayStyle.DOT)) {
return 0;
}
int count = notificationCountForScene(settings, scene);
if (count <= 0) {
return 0;
}
String[] positions = notificationPositionsForScene(settings, scene);
String[] middleSides = notificationMiddleSidesForScene(settings, scene);
for (int i = 0; i < count; i++) {
LayoutPosition position = positionFor(stringAt(positions, i, settings.layoutNotifPosition));
AnchorSide middleSide = sideFor(stringAt(middleSides, i, settings.layoutNotifMiddleSide));
if (occupiesSide(position, middleSide, side)) {
int iconHeight = iconHeightPx(
statusbarHeight,
notificationIconHeightSteps(settings, scene));
int dotWidth = Math.max(1, Math.round(Math.max(1, iconHeight) * 0.75f));
return dotWidth + Math.max(0, settings.layoutItemPaddingPx);
}
}
return 0;
}
private boolean occupiesSide( private boolean occupiesSide(
LayoutPosition position, LayoutPosition position,
AnchorSide middleSide, AnchorSide middleSide,