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 Map<View, String> lastSignatures = new WeakHashMap<>();
private final Map<View, String> lastOverlaySignatures = new WeakHashMap<>();
private final Map<View, Integer> lastRootTransformSignatures = new WeakHashMap<>();
private int systemBarAppearance;
private int systemBarBehavior;
private int systemBarRequestedVisibleTypes = WindowInsets.Type.statusBars();
@@ -171,6 +172,11 @@ final class BatteryBarController {
}
private void applyBatteryBarAfterRootTransform(View root) {
int transformSignature = rootTransformSignature(root);
Integer previousSignature = lastRootTransformSignatures.put(root, transformSignature);
if (previousSignature != null && previousSignature == transformSignature) {
return;
}
if (isHiddenKeyguardRoot(root)) {
removeBatteryBarView(root);
return;
@@ -255,9 +261,23 @@ final class BatteryBarController {
}
roots.remove(root);
pendingOverlayRetries.remove(root);
lastRootTransformSignatures.remove(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) {
if (context == null || receiverRegistered) {
return;
@@ -340,6 +360,10 @@ final class BatteryBarController {
}
private void applyBatteryBar(View root) {
applyBatteryBarInternal(root);
}
private void applyBatteryBarInternal(View root) {
if (!(root instanceof ViewGroup rootGroup)) {
removeBatteryBarView(root);
return;
@@ -24,6 +24,8 @@ import se.ajpanton.statusbartweak.runtime.ViewIdNames;
import se.ajpanton.statusbartweak.runtime.features.RuntimeContext;
import se.ajpanton.statusbartweak.runtime.hooks.ReflectionSupport;
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.RuntimeSettingsCache;
import se.ajpanton.statusbartweak.shell.settings.SbtSettings;
@@ -83,6 +85,11 @@ final class DrawerClockTextController {
private void scanAndApply(View root) {
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 dateEnabled = isEnabled(settings, Role.DATE);
if (!clockEnabled && !dateEnabled) {
@@ -95,10 +102,16 @@ final class DrawerClockTextController {
registerReceiverIfNeeded(root.getContext());
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) {
candidates = findCandidates(root);
candidateCacheByRoot.put(root, new CandidateCache(
surfaceMode,
candidates.clock != null ? candidates.clock.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) {
for (TextView textView : new ArrayList<>(trackedRoles.keySet())) {
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) {
if (textView == null || role == null) {
return;
@@ -159,6 +222,12 @@ final class DrawerClockTextController {
if (role == null) {
return;
}
SceneKey scene = runtimeContext.getModeStateRepository().getActiveScene();
if (scene != null && scene.isAod()) {
restore(textView);
untrack(textView);
return;
}
if (stockTextJustChanged) {
captureSnapshot(textView);
}
@@ -416,12 +485,17 @@ final class DrawerClockTextController {
private CandidateSelection cachedCandidates(
View root,
boolean clockEnabled,
boolean dateEnabled
boolean dateEnabled,
SurfaceMode surfaceMode
) {
CandidateCache cache = candidateCacheByRoot.get(root);
if (cache == null) {
return null;
}
if (cache.surfaceMode != surfaceMode) {
candidateCacheByRoot.remove(root);
return null;
}
Candidate clock = candidateFromCache(root, cache.clockView, Role.CLOCK, clockEnabled);
Candidate date = candidateFromCache(root, cache.dateView, Role.DATE, dateEnabled);
if (clockEnabled && clock == null || dateEnabled && date == null) {
@@ -554,10 +628,12 @@ final class DrawerClockTextController {
}
private static final class CandidateCache {
final SurfaceMode surfaceMode;
final WeakReference<TextView> clockView;
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.dateView = new WeakReference<>(dateView);
}
@@ -153,7 +153,7 @@ final class StockClockController {
}
runtimeContext.getModeStateRepository().addLayoutEnabledListener(enabled -> {
if (enabled) {
removeAllRowOverlays();
restoreAllOwnedClocks();
} else {
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) {
SbtSettings settings = RuntimeSettingsCache.get(clockView.getContext());
if (!hasCustomClockTextEnabled(settings)) {
stopTicker(clockView);
if (ownedClocks.contains(clockView)) {
restoreStockText(clockView);
ownedClocks.remove(clockView);
}
boolean layoutEnabled = settings.clockEnabled;
if (layoutEnabled || !hasCustomClockTextEnabled(settings)) {
restoreOwnedClock(clockView);
return;
}
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(
ClockLayoutTextFactory.Model model
) {
@@ -53,6 +53,28 @@ final class LockscreenCardsNotificationRowFilter {
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() {
if (hiddenRows.isEmpty()) {
return;
@@ -138,12 +138,15 @@ final class StockLayoutCanvasController {
Collections.newSetFromMap(new WeakHashMap<>());
private final Set<View> pendingUnlockedAppearanceRefreshRoots =
Collections.newSetFromMap(new WeakHashMap<>());
private final Set<View> pendingUnlockedClockTextRefreshRoots =
Collections.newSetFromMap(new WeakHashMap<>());
private final Set<View> pendingDrawerAppearanceRefreshRoots =
Collections.newSetFromMap(new WeakHashMap<>());
private final Set<View> dirtyUnlockedLayoutRoots =
Collections.newSetFromMap(new WeakHashMap<>());
private final Set<View> pendingDrawerStatusChipRefreshRoots =
Collections.newSetFromMap(new WeakHashMap<>());
private final Map<View, Runnable> unlockedClockTickersByRoot = new WeakHashMap<>();
private final Set<ClassLoader> hookedPluginClassLoaders =
Collections.newSetFromMap(new IdentityHashMap<>());
private final Set<ClassLoader> pendingPluginClassLoaders =
@@ -479,8 +482,9 @@ final class StockLayoutCanvasController {
"onViewAdded",
chain -> {
Object result = chain.proceed();
if (chain.getThisObject() instanceof ViewGroup stack) {
applyLockscreenCardsNotificationRowFilter(stack, false);
List<Object> args = chain.getArgs();
if (!args.isEmpty() && args.get(0) instanceof View row) {
applyLockscreenCardsNotificationRowFilterRow(row, false);
}
return result;
});
@@ -490,8 +494,9 @@ final class StockLayoutCanvasController {
"onViewRemoved",
chain -> {
Object result = chain.proceed();
if (chain.getThisObject() instanceof ViewGroup stack) {
applyLockscreenCardsNotificationRowFilter(stack, false);
List<Object> args = chain.getArgs();
if (!args.isEmpty() && args.get(0) instanceof View row) {
lockscreenCardsNotificationRowFilter.restoreRemovedRow(row);
}
return result;
});
@@ -1780,6 +1785,7 @@ final class StockLayoutCanvasController {
return;
}
SceneKey activeScene = runtimeContext.getModeStateRepository().getActiveScene();
ensureUnlockedClockTickerState(root, settings, activeScene);
if (!clockTextRefreshEnabledForScene(settings, activeScene)) {
return;
}
@@ -1804,6 +1810,80 @@ final class StockLayoutCanvasController {
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() {
scheduleUnlockedAppearanceRefreshForAllRoots(false);
}
@@ -1830,8 +1910,15 @@ final class StockLayoutCanvasController {
if (previous == null || current == null || previous.equals(current)) {
return;
}
if (previous.isLockscreen() && !current.isLockscreen()) {
if (current.isUnlocked() && !previous.isUnlocked()) {
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();
if (lockscreenEntry) {
@@ -1847,6 +1934,7 @@ final class StockLayoutCanvasController {
if (root == null || !root.isAttachedToWindow()) {
rootGeometries.remove(root);
unlockedHostsByRoot.remove(root);
clearUnlockedClockRefreshState(root);
continue;
}
Runnable refresh = () -> refreshSceneRoot(root);
@@ -1866,6 +1954,7 @@ final class StockLayoutCanvasController {
if (root == null || !root.isAttachedToWindow()) {
rootGeometries.remove(root);
unlockedHostsByRoot.remove(root);
clearUnlockedClockRefreshState(root);
return;
}
unlockedIconRenderController.clearRoot(root);
@@ -1926,11 +2015,11 @@ final class StockLayoutCanvasController {
if (root == null
|| !root.isAttachedToWindow()
|| !isUnlockedStatusBarRoot(root)
|| !pendingUnlockedAppearanceRefreshRoots.add(root)) {
|| !pendingUnlockedClockTextRefreshRoots.add(root)) {
return;
}
root.post(() -> {
pendingUnlockedAppearanceRefreshRoots.remove(root);
pendingUnlockedClockTextRefreshRoots.remove(root);
refreshUnlockedClockTextNow(root);
});
}
@@ -2077,11 +2166,19 @@ final class StockLayoutCanvasController {
}
if (!layoutRoot) {
if (activeScene != null
&& activeScene.isUnlocked()
&& isNotificationShadeWindowRoot(root)) {
hideUnlockedShadeShelfIconSurfaces(root);
removeSharedLayoutHosts(root);
} else if (activeScene != null
&& activeScene.isLockscreen()
&& canContainLockedStatusBarIconSurfaces(root)) {
removeUnlockedLayoutHosts(root);
removeAodCardsNotificationHost(root);
renderLockscreenCardsNotificationStripIfNeeded(root, settings, activeScene);
if (activeScene.notificationDisplayStyle() != NotificationDisplayStyle.CARDS
&& hasLockscreenCardsNotificationHost(root)) {
removeLockscreenCardsNotificationHost(root);
}
hideOrDiscoverLockedStatusBarIconSurfaces(root);
} else {
removeSharedLayoutHosts(root);
@@ -2100,9 +2197,6 @@ final class StockLayoutCanvasController {
&& (unlockedScene
|| activeScene != null && activeScene.isLockscreen()
|| aodScene)) {
if (!unlockedScene) {
restoreTrackedLockedStatusBarIconSurfaces(root);
}
boolean shouldRender = !unlockedIconRenderController.hasLayoutPlan(root)
|| rootDirty;
if (shouldRender && shouldDeferUnlockedRenderForDrawer(root)) {
@@ -2225,6 +2319,13 @@ final class StockLayoutCanvasController {
|| 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(
View root,
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) {
if (trackedUnlockedStockSurfaces.isEmpty()) {
return false;
@@ -2748,14 +2894,17 @@ final class StockLayoutCanvasController {
return false;
}
private void trackAndHideLockedStatusBarIconSurfaces(View root) {
private int trackAndHideLockedStatusBarIconSurfaces(View root) {
final int[] found = {0};
walkTree(root, view -> {
if (isLockedStatusBarIconSurface(view)) {
rememberLockedCarrierTextSource(view);
trackedLockedStatusBarIconSurfaces.add(view);
hideView(view, isCarrierView(view));
found[0]++;
}
});
return found[0];
}
private void hideOrDiscoverLockedStatusBarIconSurfaces(View root) {
@@ -2776,13 +2925,18 @@ final class StockLayoutCanvasController {
staleViews.add(view);
continue;
}
if (root == null || isDescendantOf(view, root)) {
foundInRoot = true;
if (isAlreadySuppressedHiddenView(view)) {
continue;
}
}
if (!isLockedStatusBarIconSurface(view)) {
restoreView(view);
staleViews.add(view);
continue;
}
if (root == null || isDescendantOf(view, root)) {
foundInRoot = true;
hideView(view, isCarrierView(view));
}
}
@@ -2790,6 +2944,13 @@ final class StockLayoutCanvasController {
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) {
return view != null
&& alpha != view.getAlpha()
@@ -2928,11 +3089,18 @@ final class StockLayoutCanvasController {
if (trackedLockedStatusBarIconSurfaces.isEmpty()) {
return;
}
ArrayDeque<View> restoredViews = new ArrayDeque<>();
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);
restoredViews.add(view);
}
}
removeStaleViews(trackedLockedStatusBarIconSurfaces, restoredViews);
}
private boolean hasTrackedStatusChipSources(View root) {
@@ -3297,7 +3465,7 @@ final class StockLayoutCanvasController {
aodBeforeDrawSignatureByRoot.remove(root);
lockscreenCardsRowFilterSignatures.remove(root);
lockscreenCardsNotificationRenderGenerationByRoot.remove(root);
clockTimeBucketByRoot.remove(root);
clearUnlockedClockRefreshState(root);
removeDebugOverlay(root);
root.requestLayout();
root.invalidate();
@@ -4046,7 +4214,7 @@ final class StockLayoutCanvasController {
dirtyUnlockedLayoutRoots.remove(root);
settingsIdentityByRoot.remove(root);
lockscreenCardsRowFilterSignatures.remove(root);
clockTimeBucketByRoot.remove(root);
clearUnlockedClockRefreshState(root);
if (!isUnlockedStatusBarRoot(root) || !layoutOffCleanedRoots.add(root)) {
unlockedHostsByRoot.remove(root);
rootGeometries.remove(root);
@@ -4117,7 +4285,7 @@ final class StockLayoutCanvasController {
pendingUnlockedAppearanceRefreshRoots.remove(root);
dirtyUnlockedLayoutRoots.remove(root);
settingsIdentityByRoot.remove(root);
clockTimeBucketByRoot.remove(root);
clearUnlockedClockRefreshState(root);
}
private void removeUnlockedLayoutHosts(View root) {
@@ -4130,7 +4298,7 @@ final class StockLayoutCanvasController {
pendingUnlockedAppearanceRefreshRoots.remove(root);
dirtyUnlockedLayoutRoots.remove(root);
settingsIdentityByRoot.remove(root);
clockTimeBucketByRoot.remove(root);
clearUnlockedClockRefreshState(root);
}
private boolean isDescendantOfNotificationShadeWindow(View view) {
@@ -4185,16 +4353,23 @@ final class StockLayoutCanvasController {
SbtSettings settings,
SceneKey activeScene
) {
View visibleBouncerSurface = findVisibleBouncerSurface(root);
boolean suppressForTransition = runtimeContext.getModeStateRepository().isSystemUiShadeLocked()
|| visibleBouncerSurface != null;
if (root == null
|| settings == null
|| activeScene == null
|| !activeScene.isLockscreen()
|| activeScene.notificationDisplayStyle() != NotificationDisplayStyle.CARDS
|| !isNotificationShadeWindowRoot(root)
|| suppressForTransition) {
|| !isNotificationShadeWindowRoot(root)) {
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);
lockscreenCardsNotificationRenderGenerationByRoot.remove(root);
return;
@@ -4210,7 +4385,7 @@ final class StockLayoutCanvasController {
int rootHeight = root.getHeight();
int activeKeyGeneration = NotificationActiveKeyStore.generation();
int sourceSignature =
LockscreenCardsNotificationIconSourceStore.snapshotSourceSignatureForRoot(root);
LockscreenCardsNotificationIconSourceStore.lightweightSourceSignatureForRoot(root);
int generation = settingsId;
generation = 31 * generation + rootWidth;
generation = 31 * generation + rootHeight;
@@ -4224,7 +4399,7 @@ final class StockLayoutCanvasController {
lockscreenCardsNotificationRenderGenerationByRoot.put(root, generation);
lockscreenCardsNotificationStripRenderer.render(root, hostGroup, settings, activeScene);
}
host.bringToFront();
bringToFrontIfNeeded(host);
}
}
@@ -4271,10 +4446,21 @@ final class StockLayoutCanvasController {
lockscreenCardsNotificationStripRenderer.render(root, hostGroup, settings, activeScene);
}
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) {
if (stack == null) {
return;
@@ -4286,6 +4472,17 @@ final class StockLayoutCanvasController {
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(
View root,
SbtSettings settings,
@@ -4311,7 +4508,6 @@ final class StockLayoutCanvasController {
+ (runtimeContext.getModeStateRepository().isSystemUiShadeLocked() ? 1 : 0);
result = 31 * result + root.getWidth();
result = 31 * result + root.getHeight();
result = 31 * result + (root instanceof ViewGroup group ? group.getChildCount() : 0);
return result;
}
@@ -4471,7 +4667,7 @@ final class StockLayoutCanvasController {
dirtyUnlockedLayoutRoots.remove(root);
settingsIdentityByRoot.remove(root);
lockscreenCardsRowFilterSignatures.remove(root);
clockTimeBucketByRoot.remove(root);
clearUnlockedClockRefreshState(root);
return null;
}
@@ -63,7 +63,6 @@ final class ClockLayout {
int baseHeightSteps,
int autoGapSteps,
int statusbarHeight,
int visualBaselineY,
int verticalOffsetSteps
) {
if (source == null || model == null || model.rows.isEmpty()) {
@@ -102,10 +101,8 @@ final class ClockLayout {
}
measuredRows = positionRowsFromLogicalSteps(
measuredRows,
Math.max(1, baseHeightSteps),
Math.max(1, autoGapSteps),
Math.max(1, statusbarHeight),
visualBaselineY,
verticalOffsetSteps);
RowAlignment alignment = alignRows(measuredRows, position);
@@ -216,10 +213,8 @@ final class ClockLayout {
private static ArrayList<MeasuredClockRow> positionRowsFromLogicalSteps(
ArrayList<MeasuredClockRow> measuredRows,
int baseHeightSteps,
int autoGapSteps,
int statusbarHeight,
int visualBaselineY,
int verticalOffsetSteps
) {
ArrayList<MeasuredClockRow> positionedRows = new ArrayList<>(measuredRows);
@@ -232,8 +227,7 @@ final class ClockLayout {
if (rowIndex == measuredRows.size() - 1) {
topSteps = 100 - Math.max(1, row.logicalHeightSteps) - verticalOffsetSteps;
int heightPx = Math.max(1, stepsToPx(Math.max(1, row.logicalHeightSteps), statusbarHeight));
int baselineY = visualBaselineY - stepsToPx(verticalOffsetSteps, statusbarHeight);
bottomPx = baselineY - row.baseline + heightPx;
bottomPx = Math.max(1, statusbarHeight) - stepsToPx(verticalOffsetSteps, statusbarHeight);
} else {
MeasuredClockRow nextRow = measuredRows.get(rowIndex + 1);
int gapSteps = resolveGapSteps(nextRow.source.gapBeforePx, autoGapSteps);
@@ -27,6 +27,7 @@ public final class LockscreenCardsNotificationIconSourceStore {
new WeakHashMap<>();
private static final ArrayList<String> AOD_NOTIFICATION_PACKAGES = new ArrayList<>();
private static final int VIRTUAL_ICON_SIZE_PX = 39;
private static int generation;
private LockscreenCardsNotificationIconSourceStore() {
}
@@ -34,7 +35,9 @@ public final class LockscreenCardsNotificationIconSourceStore {
public static void put(ViewGroup container, ArrayList<SourceEntry> sources) {
if (container == null || sources == null || sources.isEmpty()) {
if (container != null) {
SOURCES_BY_CONTAINER.remove(container);
if (SOURCES_BY_CONTAINER.remove(container) != null) {
bumpGeneration();
}
}
return;
}
@@ -47,7 +50,12 @@ public final class LockscreenCardsNotificationIconSourceStore {
attached.add(source);
}
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 {
SOURCES_BY_CONTAINER.put(container, attached);
}
@@ -56,7 +64,11 @@ public final class LockscreenCardsNotificationIconSourceStore {
public static boolean putPackages(ViewGroup container, ArrayList<String> packages) {
if (container == null || packages == null || packages.isEmpty()) {
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;
}
@@ -71,13 +83,18 @@ public final class LockscreenCardsNotificationIconSourceStore {
}
}
if (!hasValid) {
return PACKAGES_BY_CONTAINER.remove(container) != null;
boolean changed = PACKAGES_BY_CONTAINER.remove(container) != null;
if (changed) {
bumpGeneration();
}
return changed;
} else {
ArrayList<String> previous = PACKAGES_BY_CONTAINER.get(container);
if (previous != null && previous.equals(valid)) {
return false;
}
PACKAGES_BY_CONTAINER.put(container, valid);
bumpGeneration();
return true;
}
}
@@ -85,7 +102,11 @@ public final class LockscreenCardsNotificationIconSourceStore {
public static boolean putAodDrawables(ViewGroup container, ArrayList<? extends View> views) {
if (container == null || views == null || views.isEmpty()) {
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;
}
@@ -100,18 +121,28 @@ public final class LockscreenCardsNotificationIconSourceStore {
valid.add(view);
}
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 {
ArrayList<View> previous = AOD_DRAWABLES_BY_CONTAINER.get(container);
if (sameViews(previous, valid)) {
return false;
}
AOD_DRAWABLES_BY_CONTAINER.put(container, valid);
bumpGeneration();
return true;
}
}
public static void clearAodSources() {
if (!PACKAGES_BY_CONTAINER.isEmpty()
|| !AOD_DRAWABLES_BY_CONTAINER.isEmpty()
|| !AOD_NOTIFICATION_PACKAGES.isEmpty()) {
bumpGeneration();
}
PACKAGES_BY_CONTAINER.clear();
AOD_DRAWABLES_BY_CONTAINER.clear();
AOD_NOTIFICATION_PACKAGES.clear();
@@ -121,20 +152,76 @@ public final class LockscreenCardsNotificationIconSourceStore {
if (container == null) {
return;
}
PACKAGES_BY_CONTAINER.remove(container);
AOD_DRAWABLES_BY_CONTAINER.remove(container);
boolean changed = PACKAGES_BY_CONTAINER.remove(container) != null;
changed |= AOD_DRAWABLES_BY_CONTAINER.remove(container) != null;
if (changed) {
bumpGeneration();
}
}
public static void putAodNotificationPackages(ArrayList<String> packages) {
ArrayList<String> previous = new ArrayList<>(AOD_NOTIFICATION_PACKAGES);
AOD_NOTIFICATION_PACKAGES.clear();
if (packages == null || packages.isEmpty()) {
return;
}
for (String pkg : packages) {
if (AppIconRules.isValidPackageName(pkg) && !AOD_NOTIFICATION_PACKAGES.contains(pkg)) {
AOD_NOTIFICATION_PACKAGES.add(pkg);
if (packages != null && !packages.isEmpty()) {
for (String pkg : packages) {
if (AppIconRules.isValidPackageName(pkg) && !AOD_NOTIFICATION_PACKAGES.contains(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) {
@@ -348,6 +435,10 @@ public final class LockscreenCardsNotificationIconSourceStore {
public static boolean sourceContainerHasHiddenAncestorForRoot(View root) {
ViewGroup container = sourceContainerForRoot(root);
return sourceContainerHasHiddenAncestor(root, container);
}
private static boolean sourceContainerHasHiddenAncestor(View root, ViewGroup container) {
if (container == null) {
return false;
}
@@ -579,11 +579,21 @@ final class SnapshotRenderer {
int result = 17;
result = 31 * result + sourceTreeSignature(source.view());
Bounds boundsOverride = source.boundsOverride();
result = 31 * result + (boundsOverride != null ? boundsOverride.signature() : 0);
result = 31 * result + boundsSizeSignature(boundsOverride);
result = 31 * result + tintSignatureFor(source);
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) {
return shouldApplyStatusBarTintTarget(source) ? StatusBarTintTarget.signature() : 0;
}
@@ -113,7 +113,14 @@ public final class UnlockedIconSnapshotRenderController {
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);
Integer currentClockSourceGeometry = clockSourceGeometrySignature(
@@ -191,7 +198,18 @@ public final class UnlockedIconSnapshotRenderController {
if (scene.isLockscreen() && clockEnabledForScene(settings, scene) && currentClockLayout == null) {
clearLockscreenHosts(clockHost, carrierHost, chipHost, statusHost, notificationHost);
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(
root,
@@ -237,7 +255,18 @@ public final class UnlockedIconSnapshotRenderController {
} else {
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(
@@ -722,7 +751,6 @@ public final class UnlockedIconSnapshotRenderController {
int textSizeSteps = clockTextSizeSteps(settings, scene);
float textSizePx = clockTextSizePx(statusbarHeight, textSizeSteps);
int autoGapSteps = clockAutoGapSteps(source, statusbarHeight, textSizeSteps);
int visualBaselineY = clockVisualBaselineY(source, sourceBounds, statusbarHeight);
ClockLayout layout = ClockLayout.measure(
source,
model,
@@ -735,7 +763,6 @@ public final class UnlockedIconSnapshotRenderController {
textSizeSteps,
autoGapSteps,
statusbarHeight,
visualBaselineY,
settings.clockVerticalOffsetPx);
return layout;
}
@@ -748,17 +775,6 @@ public final class UnlockedIconSnapshotRenderController {
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) {
return scene != null && scene.isLockscreen()
? settings.clockLockTextSizeSteps
@@ -1039,14 +1055,127 @@ public final class UnlockedIconSnapshotRenderController {
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(
boolean layoutChanged,
boolean stockSourcesChanged,
boolean chipSourcesChanged,
boolean shouldOwnLockscreen
boolean shouldOwnLockscreen,
String debugSummary
) {
public RenderResult(boolean layoutChanged, boolean stockSourcesChanged, boolean chipSourcesChanged) {
this(layoutChanged, stockSourcesChanged, chipSourcesChanged, true);
this(layoutChanged, stockSourcesChanged, chipSourcesChanged, true, null);
}
public RenderResult(
boolean layoutChanged,
boolean stockSourcesChanged,
boolean chipSourcesChanged,
boolean shouldOwnLockscreen
) {
this(layoutChanged, stockSourcesChanged, chipSourcesChanged, shouldOwnLockscreen, null);
}
}
@@ -103,7 +103,8 @@ final class UnlockedLayoutPlanner {
scene,
edges,
layoutCutoutBounds,
clockLayout);
clockLayout,
statusbarHeight);
if (carrierLayout != null && carrierLayout.width() > 0) {
bands.add(UnlockedLayoutBand.carrier(
carrierHost,
@@ -649,7 +650,8 @@ final class UnlockedLayoutPlanner {
SceneKey scene,
LayoutEdges edges,
Bounds cutoutBounds,
ClockLayout clockLayout
ClockLayout clockLayout,
int statusbarHeight
) {
if (root == null
|| collectedIcons == null
@@ -687,9 +689,11 @@ final class UnlockedLayoutPlanner {
settings,
edges,
cutoutBounds,
scene,
positionFor(settings.layoutCarrierPosition),
sideFor(settings.layoutCarrierMiddleSide),
clockLayout));
clockLayout,
statusbarHeight));
}
private int carrierMaxWidth(
@@ -697,9 +701,11 @@ final class UnlockedLayoutPlanner {
SbtSettings settings,
LayoutEdges edges,
Bounds cutoutBounds,
SceneKey scene,
LayoutPosition position,
AnchorSide middleSide,
ClockLayout clockLayout
ClockLayout clockLayout,
int statusbarHeight
) {
int rootWidth = root != null ? root.getWidth() : 0;
if (rootWidth <= 0 || edges == null) {
@@ -723,42 +729,86 @@ final class UnlockedLayoutPlanner {
return carrierMaxWidthWithClockReserve(
Math.max(1, right - minLeft),
settings,
scene,
position,
middleSide,
clockLayout,
AnchorSide.RIGHT);
AnchorSide.RIGHT,
statusbarHeight);
}
int maxRight = usableCutout ? Math.max(left, cutoutBounds.left) : right;
return carrierMaxWidthWithClockReserve(
Math.max(1, maxRight - left),
settings,
scene,
position,
middleSide,
clockLayout,
AnchorSide.LEFT);
AnchorSide.LEFT,
statusbarHeight);
}
private int carrierMaxWidthWithClockReserve(
int sideWidth,
SbtSettings settings,
SceneKey scene,
LayoutPosition carrierPosition,
AnchorSide carrierMiddleSide,
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);
}
LayoutPosition clockPosition = positionFor(settings.clockPosition);
AnchorSide clockMiddleSide = sideFor(settings.clockMiddleCutoutSide);
if (!occupiesSide(clockPosition, clockMiddleSide, side)
|| !occupiesSide(carrierPosition, carrierMiddleSide, side)) {
if (!occupiesSide(carrierPosition, carrierMiddleSide, side)) {
return Math.max(1, sideWidth);
}
int reserved = clockLayout.width() + Math.max(0, settings.layoutItemPaddingPx);
int reserved = notificationDotReserveForSide(settings, scene, side, statusbarHeight);
if (clockLayout != null && clockLayout.width() > 0) {
LayoutPosition clockPosition = positionFor(settings.clockPosition);
AnchorSide clockMiddleSide = sideFor(settings.clockMiddleCutoutSide);
if (occupiesSide(clockPosition, clockMiddleSide, side)) {
reserved += clockLayout.width() + Math.max(0, settings.layoutItemPaddingPx);
}
}
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(
LayoutPosition position,
AnchorSide middleSide,