Fix foldable camera cutout overrides

This commit is contained in:
ajp_anton
2026-06-17 19:46:35 +00:00
parent f16e1a8ad5
commit 28cc72b7ea
8 changed files with 161 additions and 22 deletions
@@ -0,0 +1,85 @@
package se.ajpanton.statusbartweak.runtime.render;
import android.graphics.Rect;
import android.view.DisplayCutout;
import android.view.View;
import android.view.WindowInsets;
import java.util.Objects;
import java.util.WeakHashMap;
import se.ajpanton.statusbartweak.shell.settings.ClockCameraTypeSupport;
import se.ajpanton.statusbartweak.shell.settings.SbtSettings;
final class EffectiveCutoutSupport {
private static final WeakHashMap<View, CameraTypeCache> CAMERA_TYPE_BY_ROOT = new WeakHashMap<>();
private EffectiveCutoutSupport() {
}
static Rect middleCutout(View root, SbtSettings settings, boolean ignoreUnderDisplayCamera) {
Rect cutout = ViewGeometry.middleCutout(root);
if (cutout == null || !ignoreUnderDisplayCamera) {
return cutout;
}
String effectiveType = effectiveCameraType(root, cutout, settings);
return ClockCameraTypeSupport.TYPE_UDC.equals(effectiveType) ? null : cutout;
}
static Rect layoutMiddleCutout(View root, SbtSettings settings) {
return middleCutout(
root,
settings,
settings != null && settings.layoutIgnoreUnderDisplayCameras);
}
static Rect clockMiddleCutout(View root, SbtSettings settings) {
return middleCutout(
root,
settings,
settings != null && settings.clockIgnoreUnderDisplayCameras);
}
private static String effectiveCameraType(View root, Rect cutout, SbtSettings settings) {
int signature = cameraTypeSignature(root, cutout, settings);
CameraTypeCache cached = CAMERA_TYPE_BY_ROOT.get(root);
if (cached != null && cached.signature == signature) {
return cached.effectiveType;
}
WindowInsets insets = root != null ? root.getRootWindowInsets() : null;
DisplayCutout displayCutout = insets != null ? insets.getDisplayCutout() : null;
ClockCameraTypeSupport.CameraInfo cameraInfo =
ClockCameraTypeSupport.resolveCameraInfo(root, displayCutout, settings);
String effectiveType = cameraInfo.effectiveType;
if (root != null) {
SbtSettings.setClockCameraDebugState(
root.getContext(),
cameraInfo.cameraId,
cameraInfo.automaticType,
cameraInfo.effectiveType);
}
CAMERA_TYPE_BY_ROOT.put(root, new CameraTypeCache(signature, effectiveType));
return effectiveType;
}
private static int cameraTypeSignature(View root, Rect cutout, SbtSettings settings) {
return Objects.hash(
root != null ? root.getWidth() : 0,
root != null ? root.getHeight() : 0,
cutout != null ? cutout.left : 0,
cutout != null ? cutout.top : 0,
cutout != null ? cutout.right : 0,
cutout != null ? cutout.bottom : 0,
settings != null ? settings.clockCameraTypeOverrides : null);
}
private static final class CameraTypeCache {
final int signature;
final String effectiveType;
CameraTypeCache(int signature, String effectiveType) {
this.signature = signature;
this.effectiveType = effectiveType;
}
}
}
@@ -96,7 +96,7 @@ public final class UnlockedIconSnapshotRenderController {
root,
settings,
scene,
ViewGeometry.middleCutout(root),
EffectiveCutoutSupport.layoutMiddleCutout(root, settings),
anchors,
previousIcons);
LayoutInputSignature previousSignature = lastLayoutSignatureByRoot.get(root);
@@ -154,12 +154,12 @@ public final class UnlockedIconSnapshotRenderController {
root,
currentClockLayout,
settings,
UnlockedLayoutPlanner.cutoutBounds(ViewGeometry.middleCutout(root), settings.clockMiddleCutoutGapPx));
clockCutoutBounds(root, settings));
ClockPlacement textPlacement = clockTextPlacementForPrevious(
root,
currentClockLayout,
settings,
UnlockedLayoutPlanner.cutoutBounds(ViewGeometry.middleCutout(root), settings.clockMiddleCutoutGapPx),
clockCutoutBounds(root, settings),
previousPlan != null ? previousPlan.clockPlacement : null);
ClockPlacement updatedClock = clockPlacementWithUpdatedText(
previousPlan != null ? previousPlan.clockPlacement : null,
@@ -193,7 +193,7 @@ public final class UnlockedIconSnapshotRenderController {
root,
currentClockLayout,
settings,
UnlockedLayoutPlanner.cutoutBounds(ViewGeometry.middleCutout(root), settings.clockMiddleCutoutGapPx));
clockCutoutBounds(root, settings));
}
if (scene.isLockscreen() && clockEnabledForScene(settings, scene) && currentClockLayout == null) {
clearLockscreenHosts(clockHost, carrierHost, chipHost, statusHost, notificationHost);
@@ -724,7 +724,13 @@ public final class UnlockedIconSnapshotRenderController {
anchors,
settings,
scene,
UnlockedLayoutPlanner.cutoutBounds(ViewGeometry.middleCutout(root), settings != null ? settings.clockMiddleCutoutGapPx : 0));
clockCutoutBounds(root, settings));
}
private Bounds clockCutoutBounds(View root, SbtSettings settings) {
return UnlockedLayoutPlanner.cutoutBounds(
EffectiveCutoutSupport.clockMiddleCutout(root, settings),
settings != null ? settings.clockMiddleCutoutGapPx : 0);
}
private ClockLayout buildClockLayout(
@@ -68,13 +68,14 @@ final class UnlockedLayoutPlanner {
aodInitialStatusbarHeightByRoot.remove(root);
}
ArrayList<UnlockedLayoutBand> bands = new ArrayList<>();
Rect cutout = ViewGeometry.middleCutout(root);
Rect layoutCutout = EffectiveCutoutSupport.layoutMiddleCutout(root, settings);
Rect clockCutout = EffectiveCutoutSupport.clockMiddleCutout(root, settings);
Bounds layoutCutoutBounds = scene != null && scene.isAod()
? aodCutoutBounds(root, cutout, settings.layoutPaddingCutoutPx)
: cutoutBounds(cutout, settings.layoutPaddingCutoutPx);
? aodCutoutBounds(root, layoutCutout, settings.layoutPaddingCutoutPx)
: cutoutBounds(layoutCutout, settings.layoutPaddingCutoutPx);
Bounds clockCutoutBounds = scene != null && scene.isAod()
? aodCutoutBounds(root, cutout, settings.clockMiddleCutoutGapPx)
: cutoutBounds(cutout, settings.clockMiddleCutoutGapPx);
? aodCutoutBounds(root, clockCutout, settings.clockMiddleCutoutGapPx)
: cutoutBounds(clockCutout, settings.clockMiddleCutoutGapPx);
Bounds aodStatusAnchorBounds = scene != null && scene.isAod()
? aodStatusAnchorBounds(root, anchors, scene)
: null;
@@ -1626,6 +1626,7 @@ final class UnlockedStockSourceCollector {
settings.clockCustomFormatEnabled,
settings.clockCustomFormat,
settings.clockIgnoreUnderDisplayCameras,
settings.clockCameraTypeOverrides,
settings.layoutPaddingCutoutPx,
settings.layoutPaddingLeftPx,
settings.layoutPaddingRightPx,
@@ -29,6 +29,9 @@ public final class ClockCameraTypeSupport {
public static final class CameraInfo {
public int semDisplayDeviceType = Integer.MIN_VALUE;
public int fillUdcDisplayCutout = Integer.MIN_VALUE;
public int rootWidth = Integer.MIN_VALUE;
public int rootHeight = Integer.MIN_VALUE;
public int rotation = Integer.MIN_VALUE;
public boolean isUdcMainDisplay;
public String cutoutType;
public String cutoutString;
@@ -59,8 +62,15 @@ public final class ClockCameraTypeSupport {
: "blocking cutout";
}
public static String buildCameraId(int semDisplayDeviceType, String cutoutType, String cutoutString) {
private static String buildCameraId(int semDisplayDeviceType,
int rootWidth,
int rootHeight,
int rotation,
String cutoutType,
String cutoutString) {
String descriptor = semDisplayDeviceType + "|"
+ rootWidth + "x" + rootHeight + "|"
+ rotation + "|"
+ safe(cutoutType) + "|"
+ safe(cutoutString);
return "cam_" + shortSha1(descriptor);
@@ -78,6 +88,20 @@ public final class ClockCameraTypeSupport {
finalizeCameraInfo(info, settings);
return info;
}
Display display = root.getDisplay();
if (display != null) {
info.rotation = display.getRotation();
}
int width = root.getWidth();
if (width <= 0 && root.getRootView() != null) {
width = root.getRootView().getWidth();
}
info.rootWidth = width;
int height = root.getHeight();
if (height <= 0 && root.getRootView() != null) {
height = root.getRootView().getHeight();
}
info.rootHeight = height;
info.semDisplayDeviceType = readSemDisplayDeviceType(context);
info.fillUdcDisplayCutout = readGlobalInt(context, "fill_udc_display_cutout");
Object displayLifecycle = resolveDisplayLifecycle(context);
@@ -93,15 +117,9 @@ public final class ClockCameraTypeSupport {
finalizeCameraInfo(info, settings);
return info;
}
Object input = newInstance(inputClass, context);
Display display = root.getDisplay();
if (display != null) {
setIntField(input, "rotation", display.getRotation());
}
int width = root.getWidth();
if (width <= 0 && root.getRootView() != null) {
width = root.getRootView().getWidth();
setIntField(input, "rotation", info.rotation);
}
if (width > 0) {
setIntField(input, "statusBarWidth", width);
@@ -196,6 +214,9 @@ public final class ClockCameraTypeSupport {
}
info.cameraId = buildCameraId(
info.semDisplayDeviceType,
info.rootWidth,
info.rootHeight,
info.rotation,
info.cutoutType,
info.cutoutString);
boolean explicitUdc = info.isUdcMainDisplay || "UDC".equals(info.cutoutType);
@@ -1129,6 +1129,29 @@ public final class SbtSettings {
}
}
public static void setClockCameraDebugState(Context context,
String cameraId,
String autoType,
String effectiveType) {
if (context == null || MODULE_PACKAGE.equals(context.getPackageName()) || !hasSettingsProvider(context)) {
return;
}
try {
Bundle extras = new Bundle();
extras.putString(KEY_DEBUG_CURRENT_CAMERA_ID, cameraId != null ? cameraId : "");
extras.putString(KEY_DEBUG_CURRENT_CAMERA_AUTO_TYPE, autoType != null ? autoType : "");
extras.putString(KEY_DEBUG_CURRENT_CAMERA_EFFECTIVE_TYPE,
effectiveType != null ? effectiveType : "");
context.getContentResolver()
.call(settingsProviderUri(),
SbtSettingsProvider.METHOD_SET_CLOCK_CAMERA_DEBUG_STATE,
null,
extras);
} catch (Throwable ignored) {
// Debug camera metadata is advisory UI state only.
}
}
private static boolean hasSettingsProvider(Context context) {
if (context == null) {
return false;
@@ -338,7 +338,8 @@ public class IconsDebugFragment extends Fragment {
if (TextUtils.isEmpty(cameraId)) {
identifiedView.setText(getString(R.string.debug_camera_identified_as,
getString(R.string.debug_camera_unknown)));
getString(R.string.debug_camera_unknown),
""));
if (overrideRow != null) {
overrideRow.setVisibility(View.GONE);
}
@@ -352,7 +353,8 @@ public class IconsDebugFragment extends Fragment {
: ClockCameraTypeSupport.normalizeType(effectiveTypeFromReport));
identifiedView.setText(getString(
R.string.debug_camera_identified_as,
readableType(currentAutoType)));
readableType(currentAutoType),
cameraId));
if (overrideRow != null && overrideView != null && actionButton != null) {
overrideRow.setVisibility(View.VISIBLE);
+2 -2
View File
@@ -44,7 +44,7 @@
<string name="debug_notification_permission_required">Notification permission is required for debug icons.</string>
<string name="debug_camera_current_title">Current camera</string>
<string name="debug_camera_other_title">Other overridden cameras</string>
<string name="debug_camera_identified_as">Identified as %1$s</string>
<string name="debug_camera_identified_as">Identified as %1$s\n%2$s</string>
<string name="debug_camera_overridden_as">Overridden as %1$s</string>
<string name="debug_camera_action_wrong">This is wrong</string>
<string name="debug_camera_action_undo"></string>
@@ -131,7 +131,7 @@
<string name="status_chips_hide_media_unlocked_label">Hide media chip (unlocked)</string>
<string name="status_chips_hide_navigation_unlocked_label">Hide navigation chip (unlocked)</string>
<string name="status_chips_hide_call_unlocked_label">Hide call chip (unlocked)</string>
<string name="status_chips_hide_battery_label">Hide charging battery chip</string>
<string name="status_chips_hide_battery_label">Hide charging battery chip (unlocked)</string>
<string name="misc_title">Misc</string>
<string name="battery_bar_title">Battery bar</string>
<string name="battery_bar_master_toggle_title">Master toggle</string>