Advance lockscreen layout and rotation handling

This commit is contained in:
ajp_anton
2026-05-30 17:51:11 +00:00
parent 0e43cfe004
commit f66326f613
11 changed files with 304 additions and 90 deletions
@@ -6,7 +6,9 @@ import android.content.res.Resources;
import android.os.PowerManager; import android.os.PowerManager;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.TextView;
import java.lang.ref.WeakReference;
import java.util.ArrayDeque; import java.util.ArrayDeque;
import java.util.Collections; import java.util.Collections;
import java.util.IdentityHashMap; import java.util.IdentityHashMap;
@@ -56,7 +58,7 @@ final class StockLayoutCanvasController {
private final RuntimeContext runtimeContext; private final RuntimeContext runtimeContext;
private final Map<View, HiddenViewState> hiddenViewStates = new WeakHashMap<>(); private final Map<View, HiddenViewState> hiddenViewStates = new WeakHashMap<>();
private final Map<View, RootSize> rootSizes = new WeakHashMap<>(); private final Map<View, RootGeometry> rootGeometries = new WeakHashMap<>();
private final Map<View, Integer> debugOverlaySignatures = new WeakHashMap<>(); private final Map<View, Integer> debugOverlaySignatures = new WeakHashMap<>();
private final Set<View> confirmedAodPluginViews = private final Set<View> confirmedAodPluginViews =
Collections.newSetFromMap(new WeakHashMap<>()); Collections.newSetFromMap(new WeakHashMap<>());
@@ -75,6 +77,10 @@ final class StockLayoutCanvasController {
private final DebugBoundsOverlayController debugBoundsOverlayController = private final DebugBoundsOverlayController debugBoundsOverlayController =
new DebugBoundsOverlayController(); new DebugBoundsOverlayController();
private final UnlockedIconSnapshotRenderController unlockedIconRenderController; private final UnlockedIconSnapshotRenderController unlockedIconRenderController;
// The keyguard carrier text lives outside the statusbar root on this device.
// Keep a weak source reference so the lockscreen statusbar can render it when
// normal current-root anchor discovery cannot find it.
private WeakReference<TextView> lockedCarrierTextSource = new WeakReference<>(null);
private boolean hooksInstalled; private boolean hooksInstalled;
@@ -188,7 +194,7 @@ final class StockLayoutCanvasController {
return; return;
} }
if (activeScene != null && (activeScene.isUnlocked() || activeScene.isLockscreen())) { if (activeScene != null && (activeScene.isUnlocked() || activeScene.isLockscreen())) {
if (isKnownRootSizeChanged(root)) { if (isKnownRootGeometryChanged(root)) {
ownedIconHostManager.setUnlockedHostsVisible(root, false); ownedIconHostManager.setUnlockedHostsVisible(root, false);
removeDebugOverlay(root); removeDebugOverlay(root);
} }
@@ -203,6 +209,7 @@ final class StockLayoutCanvasController {
unlockedHosts.statusHost, unlockedHosts.statusHost,
unlockedHosts.notificationHost, unlockedHosts.notificationHost,
activeScene); activeScene);
ownedIconHostManager.bringUnlockedHostsToFront(root);
} }
} }
} }
@@ -236,13 +243,18 @@ final class StockLayoutCanvasController {
removeDebugOverlay(root); removeDebugOverlay(root);
return; return;
} }
if (activeScene != null
&& (activeScene.isUnlocked() || activeScene.isLockscreen())
&& resetUnlockedHostsForRootGeometryChange(root)) {
return;
}
UnlockedHosts unlockedHosts = updateUnlockedOwnedHosts(root, activeScene); UnlockedHosts unlockedHosts = updateUnlockedOwnedHosts(root, activeScene);
RenderResult renderResult = new RenderResult(false, false); RenderResult renderResult = new RenderResult(false, false);
if (unlockedHosts != null && (unlockedScene || activeScene != null && activeScene.isLockscreen())) { if (unlockedHosts != null && (unlockedScene || activeScene != null && activeScene.isLockscreen())) {
if (!unlockedScene) { if (!unlockedScene) {
restoreTrackedLockedStatusBarIconSurfaces(root); restoreTrackedLockedStatusBarIconSurfaces(root);
} }
markRootSizeBeforeDraw(root); markRootGeometryBeforeDraw(root);
renderResult = unlockedIconRenderController.renderUnlocked( renderResult = unlockedIconRenderController.renderUnlocked(
root, root,
unlockedHosts.clockHost, unlockedHosts.clockHost,
@@ -250,13 +262,20 @@ final class StockLayoutCanvasController {
unlockedHosts.chipHost, unlockedHosts.chipHost,
unlockedHosts.statusHost, unlockedHosts.statusHost,
unlockedHosts.notificationHost, unlockedHosts.notificationHost,
activeScene); activeScene,
ownedIconHostManager.setUnlockedHostsVisible(root, true); lockedCarrierTextSource());
boolean shouldShowUnlockedHosts = unlockedScene || renderResult.shouldOwnLockscreen();
ownedIconHostManager.setUnlockedHostsVisible(root, shouldShowUnlockedHosts);
if (shouldShowUnlockedHosts) {
ownedIconHostManager.bringUnlockedHostsToFront(root);
}
} }
if (!unlockedScene) { if (!unlockedScene) {
trackedUnlockedStockSurfaces.clear(); trackedUnlockedStockSurfaces.clear();
restoreTransientUnlockedSources(); restoreTransientUnlockedSources();
hideOrDiscoverLockedStatusBarIconSurfaces(root); if (renderResult.shouldOwnLockscreen()) {
hideOrDiscoverLockedStatusBarIconSurfaces(root);
}
removeDebugOverlay(root); removeDebugOverlay(root);
return; return;
} }
@@ -421,6 +440,7 @@ final class StockLayoutCanvasController {
private void trackAndHideLockedStatusBarIconSurfaces(View root) { private void trackAndHideLockedStatusBarIconSurfaces(View root) {
walkTree(root, view -> { walkTree(root, view -> {
if (isLockedStatusBarIconSurface(view)) { if (isLockedStatusBarIconSurface(view)) {
rememberLockedCarrierTextSource(view);
trackedLockedStatusBarIconSurfaces.add(view); trackedLockedStatusBarIconSurfaces.add(view);
hideView(view, isCarrierView(view)); hideView(view, isCarrierView(view));
} }
@@ -509,13 +529,15 @@ final class StockLayoutCanvasController {
} }
String idName = resolveIdName(view); String idName = resolveIdName(view);
String viewClassName = view.getClass().getName(); String viewClassName = view.getClass().getName();
boolean carrierSurface = idName.toLowerCase(java.util.Locale.ROOT).contains("carrier")
|| viewClassName.toLowerCase(java.util.Locale.ROOT).contains("carrier");
boolean shelfSurface = viewClassName.contains("NotificationShelf"); boolean shelfSurface = viewClassName.contains("NotificationShelf");
boolean shadeIconOnlySurface = "notification_icononly_container".equals(idName) boolean shadeIconOnlySurface = "notification_icononly_container".equals(idName)
|| "notification_icononly_area".equals(idName) || "notification_icononly_area".equals(idName)
|| "keyguard_icononly_container_view".equals(idName) || "keyguard_icononly_container_view".equals(idName)
|| "common_notification_widget".equals(idName); || "common_notification_widget".equals(idName);
if (!("system_icons".equals(idName) if (!("system_icons".equals(idName)
|| idName.toLowerCase(java.util.Locale.ROOT).contains("carrier") || carrierSurface
|| "system_icon_area".equals(idName) || "system_icon_area".equals(idName)
|| "statusIcons".equals(idName) || "statusIcons".equals(idName)
|| "battery".equals(idName) || "battery".equals(idName)
@@ -537,6 +559,9 @@ final class StockLayoutCanvasController {
|| className.contains("StatusBarWindowView")) { || className.contains("StatusBarWindowView")) {
return true; return true;
} }
if (carrierSurface && className.contains("NotificationShadeWindowView")) {
return true;
}
if (shelfSurface && className.contains("NotificationShadeWindowView")) { if (shelfSurface && className.contains("NotificationShadeWindowView")) {
return true; return true;
} }
@@ -568,6 +593,32 @@ final class StockLayoutCanvasController {
|| className.contains("StatusBarWindowView"); || className.contains("StatusBarWindowView");
} }
private void rememberLockedCarrierTextSource(View root) {
if (lockedCarrierTextSource() != null || root == null || !isCarrierView(root)) {
return;
}
walkTree(root, view -> {
if (lockedCarrierTextSource() != null || !(view instanceof TextView textView)) {
return;
}
String idName = resolveIdName(textView);
String className = textView.getClass().getName();
if ("keyguard_carrier_text".equals(idName) || className.contains("CarrierText")) {
lockedCarrierTextSource = new WeakReference<>(textView);
}
});
}
private TextView lockedCarrierTextSource() {
TextView source = lockedCarrierTextSource.get();
if (source == null || !source.isAttachedToWindow()) {
lockedCarrierTextSource.clear();
return null;
}
CharSequence text = source.getText();
return text != null && text.length() > 0 ? source : null;
}
private boolean shouldHideNonChipView(View view) { private boolean shouldHideNonChipView(View view) {
if (view == null) { if (view == null) {
return false; return false;
@@ -637,32 +688,54 @@ final class StockLayoutCanvasController {
|| "ongoing_activity_capsule".equals(idName); || "ongoing_activity_capsule".equals(idName);
} }
private void markRootSizeBeforeDraw(View root) { private void markRootGeometryBeforeDraw(View root) {
if (!isUnlockedStatusBarRoot(root)) { if (!isUnlockedStatusBarRoot(root)) {
return; return;
} }
markRootSize(root, new RootSize(root.getWidth(), root.getHeight())); markRootGeometry(root, rootGeometry(root));
} }
private boolean isKnownRootSizeChanged(View root) { private boolean resetUnlockedHostsForRootGeometryChange(View root) {
if (!isKnownRootGeometryChanged(root)) {
return false;
}
markRootGeometryBeforeDraw(root);
unlockedIconRenderController.clearAll();
ownedIconHostManager.removeUnlockedHosts(root);
unlockedHostsByRoot.remove(root);
removeDebugOverlay(root);
root.requestLayout();
root.invalidate();
return true;
}
private boolean isKnownRootGeometryChanged(View root) {
if (!isUnlockedStatusBarRoot(root)) { if (!isUnlockedStatusBarRoot(root)) {
return false; return false;
} }
RootSize previous = rootSizes.get(root); RootGeometry previous = rootGeometries.get(root);
return previous != null RootGeometry current = rootGeometry(root);
&& (previous.width != root.getWidth() || previous.height != root.getHeight()); return previous != null && !previous.equals(current);
} }
private void markRootSize(View root, RootSize size) { private void markRootGeometry(View root, RootGeometry geometry) {
if (size.width <= 0 || size.height <= 0) { if (geometry.width <= 0 || geometry.height <= 0) {
return; return;
} }
RootSize previous = rootSizes.put(root, size); RootGeometry previous = rootGeometries.put(root, geometry);
if (previous != null && !previous.equals(size)) { if (previous != null && !previous.equals(geometry)) {
unlockedIconRenderController.beginRootTransition(); unlockedIconRenderController.beginRootTransition();
} }
} }
private RootGeometry rootGeometry(View root) {
int rotation = root != null && root.getDisplay() != null ? root.getDisplay().getRotation() : -1;
return new RootGeometry(
root != null ? root.getWidth() : 0,
root != null ? root.getHeight() : 0,
rotation);
}
private void enforceDirectAodControllerPolicy(Object controller) { private void enforceDirectAodControllerPolicy(Object controller) {
if (controller == null) { if (controller == null) {
return; return;
@@ -758,7 +831,7 @@ final class StockLayoutCanvasController {
if (!isUnlockedStatusBarRoot(root) || !layoutOffCleanedRoots.add(root)) { if (!isUnlockedStatusBarRoot(root) || !layoutOffCleanedRoots.add(root)) {
return; return;
} }
rootSizes.remove(root); rootGeometries.remove(root);
unlockedIconRenderController.clearAll(); unlockedIconRenderController.clearAll();
ownedIconHostManager.removeUnlockedHosts(root); ownedIconHostManager.removeUnlockedHosts(root);
} }
@@ -805,7 +878,7 @@ final class StockLayoutCanvasController {
if (!isUnlockedStatusBarRoot(root)) { if (!isUnlockedStatusBarRoot(root)) {
return null; return null;
} }
if (activeScene != null && activeScene.isUnlocked()) { if (activeScene != null && (activeScene.isUnlocked() || activeScene.isLockscreen())) {
UnlockedHosts cached = unlockedHostsByRoot.get(root); UnlockedHosts cached = unlockedHostsByRoot.get(root);
if (areUnlockedHostsAttached(root, cached)) { if (areUnlockedHostsAttached(root, cached)) {
return cached; return cached;
@@ -942,7 +1015,7 @@ final class StockLayoutCanvasController {
) { ) {
} }
private record RootSize(int width, int height) { private record RootGeometry(int width, int height, int rotation) {
} }
} }
@@ -95,6 +95,14 @@ public final class OwnedIconHostManager {
setHostVisible(root, TAG_UNLOCKED_CHIP_HOST, visible); setHostVisible(root, TAG_UNLOCKED_CHIP_HOST, visible);
} }
public void bringUnlockedHostsToFront(View root) {
bringHostToFront(root, TAG_UNLOCKED_STATUS_HOST);
bringHostToFront(root, TAG_UNLOCKED_NOTIFICATION_HOST);
bringHostToFront(root, TAG_UNLOCKED_CLOCK_HOST);
bringHostToFront(root, TAG_UNLOCKED_CARRIER_HOST);
bringHostToFront(root, TAG_UNLOCKED_CHIP_HOST);
}
public boolean isOwnedHost(View view) { public boolean isOwnedHost(View view) {
if (view == null) { if (view == null) {
return false; return false;
@@ -128,6 +136,16 @@ public final class OwnedIconHostManager {
host.setVisibility(visible ? View.VISIBLE : View.INVISIBLE); host.setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
} }
private void bringHostToFront(View root, String tag) {
if (!(root instanceof ViewGroup parent) || tag == null) {
return;
}
FrameLayout host = findDirectHost(parent, tag);
if (host != null) {
host.bringToFront();
}
}
private void disableAncestorClipping(View view) { private void disableAncestorClipping(View view) {
Object current = view; Object current = view;
int depth = 0; int depth = 0;
@@ -196,6 +196,18 @@ final class RootAnchors {
&& (clockContainer == null || isAttached(clockContainer)); && (clockContainer == null || isAttached(clockContainer));
} }
RootAnchors withCarrierView(TextView carrierView) {
return new RootAnchors(
clockContainer,
clockView,
statusRoot,
statusEndContent,
statusIcons,
battery,
notificationRoot,
carrierView);
}
private static boolean isAttached(View view) { private static boolean isAttached(View view) {
return view != null && view.isAttachedToWindow(); return view != null && view.isAttachedToWindow();
} }
@@ -57,7 +57,8 @@ public final class UnlockedIconSnapshotRenderController {
ViewGroup chipHost, ViewGroup chipHost,
ViewGroup statusHost, ViewGroup statusHost,
ViewGroup notificationHost, ViewGroup notificationHost,
SceneKey scene SceneKey scene,
TextView externalCarrierView
) { ) {
if (scene == null || (!scene.isUnlocked() && !scene.isLockscreen()) || !root.isAttachedToWindow()) { if (scene == null || (!scene.isUnlocked() && !scene.isLockscreen()) || !root.isAttachedToWindow()) {
clearAll(); clearAll();
@@ -70,6 +71,18 @@ public final class UnlockedIconSnapshotRenderController {
layoutHostToRoot(root, statusHost); layoutHostToRoot(root, statusHost);
layoutHostToRoot(root, notificationHost); layoutHostToRoot(root, notificationHost);
RootAnchors anchors = sourceCollector.anchorsFor(root); RootAnchors anchors = sourceCollector.anchorsFor(root);
if (scene.isLockscreen()
&& settings.layoutCarrierEnabledLockscreen
&& (anchors == null || anchors.carrierView == null)) {
anchors = sourceCollector.refreshAnchors(root);
}
if (scene.isLockscreen()
&& settings.layoutCarrierEnabledLockscreen
&& anchors != null
&& anchors.carrierView == null
&& externalCarrierView != null) {
anchors = anchors.withCarrierView(externalCarrierView);
}
CollectedIcons previousIcons = lastCollectedIconsByRoot.get(root); CollectedIcons previousIcons = lastCollectedIconsByRoot.get(root);
LayoutInputSignature signature = sourceCollector.layoutInputSignature( LayoutInputSignature signature = sourceCollector.layoutInputSignature(
root, root,
@@ -84,7 +97,11 @@ public final class UnlockedIconSnapshotRenderController {
boolean chipSourcesChanged = previousSignature == null boolean chipSourcesChanged = previousSignature == null
|| signature.chipSources != previousSignature.chipSources; || signature.chipSources != previousSignature.chipSources;
if (signature.equals(previousSignature)) { if (signature.equals(previousSignature)) {
return new RenderResult(false, false); return new RenderResult(
false,
false,
false,
!scene.isLockscreen() || lastLayoutPlanByRoot.get(root) != null);
} }
lastLayoutSignatureByRoot.put(root, signature); lastLayoutSignatureByRoot.put(root, signature);
Integer currentClockSourceGeometry = clockSourceGeometrySignature( Integer currentClockSourceGeometry = clockSourceGeometrySignature(
@@ -156,6 +173,11 @@ public final class UnlockedIconSnapshotRenderController {
settings, settings,
UnlockedLayoutPlanner.cutoutBounds(ViewGeometry.middleCutout(root), settings.clockMiddleCutoutGapPx)); UnlockedLayoutPlanner.cutoutBounds(ViewGeometry.middleCutout(root), settings.clockMiddleCutoutGapPx));
} }
if (scene.isLockscreen() && clockEnabledForScene(settings, scene) && currentClockLayout == null) {
clearLockscreenHosts(clockHost, carrierHost, chipHost, statusHost, notificationHost);
lastLayoutPlanByRoot.remove(root);
return new RenderResult(false, stockSourcesChanged, chipSourcesChanged, false);
}
LayoutPlan layoutPlan = layoutPlanner.solve( LayoutPlan layoutPlan = layoutPlanner.solve(
root, root,
clockHost, clockHost,
@@ -243,6 +265,20 @@ public final class UnlockedIconSnapshotRenderController {
} }
} }
private void clearLockscreenHosts(
ViewGroup clockHost,
ViewGroup carrierHost,
ViewGroup chipHost,
ViewGroup statusHost,
ViewGroup notificationHost
) {
clockRenderer.clear(clockHost);
carrierRenderer.clear(carrierHost);
chipRenderer.clear(chipHost);
statusRenderer.clear(statusHost);
notificationRenderer.clear(notificationHost);
}
private void layoutHostToRoot(View root, ViewGroup host) { private void layoutHostToRoot(View root, ViewGroup host) {
if (root == null || host == null || root.getWidth() <= 0 || root.getHeight() <= 0) { if (root == null || host == null || root.getWidth() <= 0 || root.getHeight() <= 0) {
return; return;
@@ -817,10 +853,15 @@ public final class UnlockedIconSnapshotRenderController {
public record RenderResult( public record RenderResult(
boolean layoutChanged, boolean layoutChanged,
boolean stockSourcesChanged, boolean stockSourcesChanged,
boolean chipSourcesChanged boolean chipSourcesChanged,
boolean shouldOwnLockscreen
) { ) {
public RenderResult(boolean layoutChanged, boolean stockSourcesChanged) { public RenderResult(boolean layoutChanged, boolean stockSourcesChanged) {
this(layoutChanged, stockSourcesChanged, stockSourcesChanged); this(layoutChanged, stockSourcesChanged, stockSourcesChanged, true);
}
public RenderResult(boolean layoutChanged, boolean stockSourcesChanged, boolean chipSourcesChanged) {
this(layoutChanged, stockSourcesChanged, chipSourcesChanged, true);
} }
} }
@@ -168,6 +168,7 @@ final class UnlockedLayoutPlanner {
} }
ArrayList<LayoutItemKind> itemOrder = orderedKinds(settings.layoutItemOrder); ArrayList<LayoutItemKind> itemOrder = orderedKinds(settings.layoutItemOrder);
splitClockIfNeeded(root, bands, itemOrder, clockCutoutBounds, settings); splitClockIfNeeded(root, bands, itemOrder, clockCutoutBounds, settings);
sortBandsForItemOrder(bands, itemOrder);
LayoutEdges edges = layoutEdges(root, settings, anchors); LayoutEdges edges = layoutEdges(root, settings, anchors);
int itemPadding = Math.max(0, settings.layoutItemPaddingPx); int itemPadding = Math.max(0, settings.layoutItemPaddingPx);
solveWithPackedLayout(root, edges, bands, layoutCutoutBounds, settings, itemPadding); solveWithPackedLayout(root, edges, bands, layoutCutoutBounds, settings, itemPadding);
@@ -198,6 +199,30 @@ final class UnlockedLayoutPlanner {
carrierPlacement); carrierPlacement);
} }
private void sortBandsForItemOrder(
ArrayList<UnlockedLayoutBand> bands,
ArrayList<LayoutItemKind> itemOrder
) {
if (bands == null || bands.size() < 2 || itemOrder == null || itemOrder.isEmpty()) {
return;
}
ArrayList<UnlockedLayoutBand> originalOrder = new ArrayList<>(bands);
bands.sort((left, right) -> {
int orderComparison = Integer.compare(
orderIndex(itemOrder, left != null ? left.kind : null),
orderIndex(itemOrder, right != null ? right.kind : null));
if (orderComparison != 0) {
return orderComparison;
}
return Integer.compare(originalOrder.indexOf(left), originalOrder.indexOf(right));
});
}
private int orderIndex(ArrayList<LayoutItemKind> itemOrder, LayoutItemKind kind) {
int index = itemOrder != null ? itemOrder.indexOf(kind) : -1;
return index >= 0 ? index : Integer.MAX_VALUE;
}
private void solveWithPackedLayout( private void solveWithPackedLayout(
View root, View root,
LayoutEdges edges, LayoutEdges edges,
@@ -495,19 +520,25 @@ final class UnlockedLayoutPlanner {
return null; return null;
} }
Bounds bounds = ViewGeometry.boundsRelativeTo(root, collectedIcons.carrierView); Bounds bounds = ViewGeometry.boundsRelativeTo(root, collectedIcons.carrierView);
if (bounds == null || bounds.width <= 0 || bounds.height <= 0) { Bounds anchorBounds = bounds != null
return null; ? bounds
: ViewGeometry.boundsRelativeTo(root, collectedIcons.clockView);
if (anchorBounds == null || anchorBounds.height <= 0) {
anchorBounds = new Bounds(0, 0, Math.max(0, root.getWidth()), Math.max(1, root.getHeight()));
} }
int sourceHeight = collectedIcons.carrierView.getHeight() > 0
? collectedIcons.carrierView.getHeight()
: collectedIcons.carrierView.getMeasuredHeight();
int baseline = collectedIcons.carrierView.getBaseline() > 0 int baseline = collectedIcons.carrierView.getBaseline() > 0
? collectedIcons.carrierView.getBaseline() ? collectedIcons.carrierView.getBaseline()
: Math.max(0, (bounds.height * 3) / 4); : Math.max(0, ((sourceHeight > 0 ? sourceHeight : anchorBounds.height) * 3) / 4);
int verticalOffset = offsetScaler.clock( int verticalOffset = offsetScaler.clock(
settings.layoutCarrierVerticalOffsetPx, settings.layoutCarrierVerticalOffsetPx,
collectedIcons.carrierView); collectedIcons.carrierView);
return ClockLayout.simpleText( return ClockLayout.simpleText(
collectedIcons.carrierView, collectedIcons.carrierView,
positionFor(settings.layoutCarrierPosition), positionFor(settings.layoutCarrierPosition),
bounds.top + baseline - verticalOffset); anchorBounds.top + baseline - verticalOffset);
} }
private int statusCountForScene(SbtSettings settings, SceneKey scene) { private int statusCountForScene(SbtSettings settings, SceneKey scene) {
@@ -707,6 +738,7 @@ final class UnlockedLayoutPlanner {
) { ) {
int left = firstUsableInset( int left = firstUsableInset(
rootWidth, rootWidth,
leftInset(root, anchors != null ? anchors.clockView : null),
leftInset(root, anchors != null ? anchors.clockContainer : null), leftInset(root, anchors != null ? anchors.clockContainer : null),
leftInset(root, anchors != null ? anchors.notificationRoot : null), leftInset(root, anchors != null ? anchors.notificationRoot : null),
root != null ? root.getPaddingLeft() : 0); root != null ? root.getPaddingLeft() : 0);
@@ -28,6 +28,10 @@ final class UnlockedStockSourceCollector {
if (cached != null && cached.isUsable()) { if (cached != null && cached.isUsable()) {
return cached; return cached;
} }
return refreshAnchors(root);
}
RootAnchors refreshAnchors(View root) {
RootAnchors anchors = discoverAnchors(root); RootAnchors anchors = discoverAnchors(root);
anchorsByRoot.put(root, anchors); anchorsByRoot.put(root, anchors);
return anchors; return anchors;
@@ -3,7 +3,6 @@ package se.ajpanton.statusbartweak.shell.settings;
import android.content.Context; import android.content.Context;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.net.Uri; import android.net.Uri;
import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
@@ -862,60 +861,6 @@ public final class SbtSettings {
} }
} }
public static void restoreCredentialProtectedPreferences(Context context) {
if (context == null || Build.VERSION.SDK_INT < 24) {
return;
}
try {
Context deviceContext = context.createDeviceProtectedStorageContext();
if (deviceContext == null) {
return;
}
SharedPreferences devicePrefs = deviceContext.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE);
Map<String, ?> deviceValues = devicePrefs.getAll();
if (deviceValues == null || deviceValues.isEmpty()) {
return;
}
SharedPreferences normalPrefs = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE);
Map<String, ?> normalValues = normalPrefs.getAll();
SharedPreferences.Editor editor = normalPrefs.edit();
boolean changed = false;
for (Map.Entry<String, ?> entry : deviceValues.entrySet()) {
String key = entry.getKey();
if (key == null || normalValues.containsKey(key)) {
continue;
}
Object value = entry.getValue();
if (value instanceof Boolean bool) {
editor.putBoolean(key, bool);
} else if (value instanceof Integer integer) {
editor.putInt(key, integer);
} else if (value instanceof Long longValue) {
editor.putLong(key, longValue);
} else if (value instanceof Float floatValue) {
editor.putFloat(key, floatValue);
} else if (value instanceof String string) {
editor.putString(key, string);
} else if (value instanceof Set<?> set) {
HashSet<String> strings = new HashSet<>();
for (Object item : set) {
if (item instanceof String stringItem) {
strings.add(stringItem);
}
}
editor.putStringSet(key, strings);
} else {
continue;
}
changed = true;
}
if (changed) {
editor.commit();
}
} catch (Throwable ignored) {
}
}
private static boolean hasSettingsProvider(Context context) { private static boolean hasSettingsProvider(Context context) {
if (context == null) { if (context == null) {
return false; return false;
@@ -35,7 +35,6 @@ public class SbtSettingsProvider extends ContentProvider {
if (ctx == null) { if (ctx == null) {
return null; return null;
} }
SbtSettings.restoreCredentialProtectedPreferences(ctx);
SharedPreferences prefs = ctx.getSharedPreferences(SbtSettings.PREFS_NAME, Context.MODE_PRIVATE); SharedPreferences prefs = ctx.getSharedPreferences(SbtSettings.PREFS_NAME, Context.MODE_PRIVATE);
if (METHOD_GET_UNLOCKED.equals(method)) { if (METHOD_GET_UNLOCKED.equals(method)) {
int value = prefs.getInt(SbtSettings.KEY_UNLOCKED_MAX_ICONS_PER_ROW, int value = prefs.getInt(SbtSettings.KEY_UNLOCKED_MAX_ICONS_PER_ROW,
@@ -9,21 +9,23 @@ import android.view.MenuItem;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.appcompat.app.ActionBarDrawerToggle; import androidx.appcompat.app.ActionBarDrawerToggle;
import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.app.AppCompatActivity;
import androidx.core.graphics.Insets;
import androidx.core.view.GravityCompat; import androidx.core.view.GravityCompat;
import androidx.drawerlayout.widget.DrawerLayout; import androidx.core.view.ViewCompat;
import androidx.core.view.WindowInsetsCompat;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
import com.google.android.material.appbar.MaterialToolbar; import com.google.android.material.appbar.MaterialToolbar;
import com.google.android.material.navigation.NavigationView; import com.google.android.material.navigation.NavigationView;
import se.ajpanton.statusbartweak.R; import se.ajpanton.statusbartweak.R;
import se.ajpanton.statusbartweak.shell.settings.SbtSettings;
public class MainActivity extends AppCompatActivity implements NavigationView.OnNavigationItemSelectedListener { public class MainActivity extends AppCompatActivity implements NavigationView.OnNavigationItemSelectedListener {
private static final String EXTRA_OPEN_NAV_ITEM = "open_nav_item"; private static final String EXTRA_OPEN_NAV_ITEM = "open_nav_item";
private DrawerLayout drawerLayout; private SafeInsetDrawerLayout drawerLayout;
private NavigationView navigationView; private NavigationView navigationView;
private Integer navigationBasePaddingLeft;
public static Intent newOpenDebugIntent(Context context) { public static Intent newOpenDebugIntent(Context context) {
Intent intent = new Intent(context, MainActivity.class); Intent intent = new Intent(context, MainActivity.class);
@@ -35,7 +37,6 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
SbtSettings.restoreCredentialProtectedPreferences(this);
setContentView(R.layout.activity_main); setContentView(R.layout.activity_main);
MaterialToolbar toolbar = findViewById(R.id.toolbar); MaterialToolbar toolbar = findViewById(R.id.toolbar);
@@ -45,6 +46,7 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
navigationView = findViewById(R.id.nav_view); navigationView = findViewById(R.id.nav_view);
navigationView.setNavigationItemSelectedListener(this); navigationView.setNavigationItemSelectedListener(this);
navigationView.getMenu().setGroupCheckable(0, true, true); navigationView.getMenu().setGroupCheckable(0, true, true);
applyNavigationDrawerInsets();
ActionBarDrawerToggle toggle = new ActionBarDrawerToggle( ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(
this, this,
@@ -182,4 +184,41 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
intent.removeExtra(EXTRA_OPEN_NAV_ITEM); intent.removeExtra(EXTRA_OPEN_NAV_ITEM);
} }
} }
private void applyNavigationDrawerInsets() {
drawerLayout.addOnLayoutChangeListener((view, left, top, right, bottom,
oldLeft, oldTop, oldRight, oldBottom) ->
applyNavigationDrawerMarginsFromRootInsets());
drawerLayout.post(this::applyNavigationDrawerMarginsFromRootInsets);
}
private void applyNavigationDrawerMarginsFromRootInsets() {
WindowInsetsCompat insets = ViewCompat.getRootWindowInsets(drawerLayout);
if (insets == null) {
return;
}
Insets safeInsets = insets.getInsetsIgnoringVisibility(
WindowInsetsCompat.Type.systemBars() | WindowInsetsCompat.Type.displayCutout());
applyNavigationDrawerSafeArea(safeInsets.left, safeInsets.right);
}
private void applyNavigationDrawerSafeArea(int leftInset, int rightInset) {
int safeLeft = Math.max(Math.max(0, leftInset), Math.max(0, drawerLayout.getPaddingLeft()));
int safeRight = Math.max(Math.max(0, rightInset), Math.max(0, drawerLayout.getPaddingRight()));
drawerLayout.setDrawerSafeInsets(safeLeft, safeRight);
if (navigationBasePaddingLeft == null) {
navigationBasePaddingLeft = navigationView.getPaddingLeft();
}
int leftPadding = navigationBasePaddingLeft + safeLeft;
if (navigationView.getPaddingLeft() != leftPadding
|| Math.round(navigationView.getTranslationX()) != 0) {
navigationView.setTranslationX(0f);
navigationView.setPadding(
leftPadding,
navigationView.getPaddingTop(),
navigationView.getPaddingRight(),
navigationView.getPaddingBottom());
navigationView.requestLayout();
}
}
} }
@@ -0,0 +1,51 @@
package se.ajpanton.statusbartweak.shell.ui;
import android.content.Context;
import android.graphics.Canvas;
import android.util.AttributeSet;
import androidx.drawerlayout.widget.DrawerLayout;
public class SafeInsetDrawerLayout extends DrawerLayout {
private int safeInsetLeft;
private int safeInsetRight;
public SafeInsetDrawerLayout(Context context) {
super(context);
}
public SafeInsetDrawerLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
public SafeInsetDrawerLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public void setDrawerSafeInsets(int left, int right) {
int safeLeft = Math.max(0, left);
int safeRight = Math.max(0, right);
if (safeInsetLeft == safeLeft && safeInsetRight == safeRight) {
return;
}
safeInsetLeft = safeLeft;
safeInsetRight = safeRight;
invalidate();
}
@Override
protected void dispatchDraw(Canvas canvas) {
if (safeInsetLeft == 0 && safeInsetRight == 0) {
super.dispatchDraw(canvas);
return;
}
int save = canvas.save();
canvas.clipRect(
safeInsetLeft,
0,
Math.max(safeInsetLeft, getWidth() - safeInsetRight),
getHeight());
super.dispatchDraw(canvas);
canvas.restoreToCount(save);
}
}
+2 -2
View File
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<androidx.drawerlayout.widget.DrawerLayout <se.ajpanton.statusbartweak.shell.ui.SafeInsetDrawerLayout
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/drawer_layout" android:id="@+id/drawer_layout"
@@ -38,4 +38,4 @@
app:itemTextColor="@color/drawer_item_text_color" app:itemTextColor="@color/drawer_item_text_color"
app:menu="@menu/drawer_menu" /> app:menu="@menu/drawer_menu" />
</androidx.drawerlayout.widget.DrawerLayout> </se.ajpanton.statusbartweak.shell.ui.SafeInsetDrawerLayout>