Add clock size controls and logical clock collisions

This commit is contained in:
ajp_anton
2026-06-08 03:28:56 +00:00
parent 805d12c62e
commit cb32644cc8
14 changed files with 293 additions and 43 deletions
@@ -22,6 +22,7 @@ final class ClockLayout {
private final boolean fixedPlacement;
private final int width;
private final int height;
private final float textSizePx;
private int placedLeft;
private ClockLayout(
@@ -33,7 +34,8 @@ final class ClockLayout {
int placedLeft,
boolean fixedPlacement,
int width,
int height
int height,
float textSizePx
) {
this.source = source;
this.model = model;
@@ -44,6 +46,7 @@ final class ClockLayout {
this.fixedPlacement = fixedPlacement;
this.width = width;
this.height = height;
this.textSizePx = textSizePx;
}
static ClockLayout measure(
@@ -54,7 +57,8 @@ final class ClockLayout {
int rootWidth,
Bounds cutoutBounds,
boolean middleAutoNearestSide,
AnchorSide middleSide
AnchorSide middleSide,
float textSizePx
) {
if (source == null || model == null || model.rows.isEmpty()) {
return null;
@@ -64,6 +68,7 @@ final class ClockLayout {
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT));
ClockProxyRenderer.copyTextStyleIfChanged(source, probe);
applyTextSizeOverride(probe, textSizePx);
ArrayList<MeasuredClockRow> measuredRows = new ArrayList<>();
for (int i = 0; i < model.rows.size(); i++) {
ClockLayoutTextFactory.Row row = model.rows.get(i);
@@ -121,7 +126,8 @@ final class ClockLayout {
0,
false,
alignment.width,
bounds.height);
bounds.height,
effectiveTextSize(source, textSizePx));
}
static ClockLayout simpleText(
@@ -172,7 +178,8 @@ final class ClockLayout {
0,
false,
width,
measured.height);
measured.height,
source.getTextSize());
}
static int centeredTextBaseline(TextView source, int containerHeight) {
@@ -338,7 +345,8 @@ final class ClockLayout {
bounds.left,
true,
bounds.width,
bounds.height);
bounds.height,
textSizePx);
}
private static void addSplitSegment(
@@ -493,7 +501,26 @@ final class ClockLayout {
ClockPlacement placement() {
String description = model != null ? model.contentDescription : String.valueOf(source.getText());
return new ClockPlacement(source, description, rows, new Bounds(placedLeft, top, width, height));
return new ClockPlacement(
source,
description,
rows,
new Bounds(placedLeft, top, width, height),
android.graphics.Color.TRANSPARENT,
textSizePx);
}
private static void applyTextSizeOverride(TextView view, float textSizePx) {
if (view != null && textSizePx > 0f && view.getTextSize() != textSizePx) {
view.setTextSize(android.util.TypedValue.COMPLEX_UNIT_PX, textSizePx);
}
}
private static float effectiveTextSize(TextView source, float textSizePx) {
if (textSizePx > 0f) {
return textSizePx;
}
return source != null ? source.getTextSize() : 0f;
}
private record MeasuredClockRow(
@@ -51,7 +51,7 @@ final class ClockProxyRenderer {
for (int i = 0; i < placement.rows.size(); i++) {
ClockRow row = placement.rows.get(i);
TextView view = views.get(i);
if (copyTextStyleIfChanged(placement.source, view)) {
if (copyTextStyleIfChanged(placement.source, view, placement.textSizePx)) {
hostChanged = true;
}
if (applyTextColorOverrideIfNeeded(view, placement.textColorOverride)) {
@@ -176,13 +176,18 @@ final class ClockProxyRenderer {
}
static boolean copyTextStyleIfChanged(TextView source, TextView target) {
return copyTextStyleIfChanged(source, target, 0f);
}
static boolean copyTextStyleIfChanged(TextView source, TextView target, float textSizePx) {
boolean changed = false;
if (target.getCurrentTextColor() != source.getCurrentTextColor()) {
target.setTextColor(source.getCurrentTextColor());
changed = true;
}
if (target.getTextSize() != source.getTextSize()) {
target.setTextSize(android.util.TypedValue.COMPLEX_UNIT_PX, source.getTextSize());
float desiredTextSize = textSizePx > 0f ? textSizePx : source.getTextSize();
if (target.getTextSize() != desiredTextSize) {
target.setTextSize(android.util.TypedValue.COMPLEX_UNIT_PX, desiredTextSize);
changed = true;
}
if (!Objects.equals(target.getTypeface(), source.getTypeface())) {
@@ -12,6 +12,7 @@ final class ClockPlacement {
final ArrayList<ClockRow> rows;
final Bounds bounds;
final int textColorOverride;
final float textSizePx;
ClockPlacement(
TextView source,
@@ -19,7 +20,7 @@ final class ClockPlacement {
ArrayList<ClockRow> rows,
Bounds bounds
) {
this(source, contentDescription, rows, bounds, android.graphics.Color.TRANSPARENT);
this(source, contentDescription, rows, bounds, android.graphics.Color.TRANSPARENT, 0f);
}
ClockPlacement(
@@ -28,12 +29,24 @@ final class ClockPlacement {
ArrayList<ClockRow> rows,
Bounds bounds,
int textColorOverride
) {
this(source, contentDescription, rows, bounds, textColorOverride, 0f);
}
ClockPlacement(
TextView source,
String contentDescription,
ArrayList<ClockRow> rows,
Bounds bounds,
int textColorOverride,
float textSizePx
) {
this.source = source;
this.contentDescription = contentDescription;
this.rows = rows;
this.bounds = bounds;
this.textColorOverride = textColorOverride;
this.textSizePx = textSizePx;
}
}
@@ -145,7 +145,7 @@ public final class UnlockedIconSnapshotRenderController {
return new RenderResult(false, false, false);
}
}
currentClockLayout = buildClockLayout(root, anchors, settings);
currentClockLayout = buildClockLayout(root, anchors, settings, scene);
currentClockGeometry = clockGeometrySignature(
root,
currentClockLayout,
@@ -184,7 +184,7 @@ public final class UnlockedIconSnapshotRenderController {
lastCollectedIconsByRoot.put(root, collectedIcons);
}
if (currentClockLayout == null && clockEnabledForScene(settings, scene)) {
currentClockLayout = buildClockLayout(root, anchors, settings);
currentClockLayout = buildClockLayout(root, anchors, settings, scene);
currentClockGeometry = clockGeometrySignature(
root,
currentClockLayout,
@@ -694,13 +694,16 @@ public final class UnlockedIconSnapshotRenderController {
private ClockLayout buildClockLayout(
View root,
RootAnchors anchors,
SbtSettings settings
SbtSettings settings,
SceneKey scene
) {
return buildClockLayout(
root,
anchors != null ? anchors.clockView : null,
anchors != null ? anchors.clockContainer : null,
anchors,
settings,
scene,
UnlockedLayoutPlanner.cutoutBounds(ViewGeometry.middleCutout(root), settings != null ? settings.clockMiddleCutoutGapPx : 0));
}
@@ -708,7 +711,9 @@ public final class UnlockedIconSnapshotRenderController {
View root,
TextView source,
View clockContainer,
RootAnchors anchors,
SbtSettings settings,
SceneKey scene,
Bounds cutoutBounds
) {
if (root == null || source == null || settings == null) {
@@ -723,15 +728,53 @@ public final class UnlockedIconSnapshotRenderController {
return null;
}
int baselineY = stableClockBaselineY(root, source, clockContainer);
int statusbarHeight = statusbarHeightForClock(root, sourceBounds, anchors, scene);
float textSizePx = clockTextSizePx(statusbarHeight, settings, scene);
int verticalOffsetPx = stepsToPx(settings.clockVerticalOffsetPx, statusbarHeight);
return ClockLayout.measure(
source,
model,
UnlockedLayoutPlanner.positionFor(settings.clockPosition),
baselineY - textOffsetScaler.scale(settings.clockVerticalOffsetPx, source),
baselineY - verticalOffsetPx,
root.getWidth(),
cutoutBounds,
settings.clockMiddleAutoNearestSide,
UnlockedLayoutPlanner.sideFor(settings.clockMiddleCutoutSide));
UnlockedLayoutPlanner.sideFor(settings.clockMiddleCutoutSide),
textSizePx);
}
private float clockTextSizePx(int statusbarHeight, SbtSettings settings, SceneKey scene) {
int steps = scene != null && scene.isLockscreen()
? settings.clockLockTextSizeSteps
: settings.clockTextSizeSteps;
return Math.max(1f, Math.max(1, statusbarHeight) * Math.max(1, steps) / 100f);
}
private int statusbarHeightForClock(
View root,
Bounds sourceBounds,
RootAnchors anchors,
SceneKey scene
) {
int stored = StatusbarHeightStore.get(root);
if (stored > 0) {
return stored;
}
Bounds statusBounds = StatusbarHeightSource.bounds(root, scene, anchors);
if (statusBounds != null && statusBounds.top >= 0 && statusBounds.height > 0) {
return Math.max(1, statusBounds.top + statusBounds.height);
}
if (sourceBounds != null && sourceBounds.top >= 0 && sourceBounds.height > 0) {
return Math.max(1, sourceBounds.top + sourceBounds.height);
}
return Math.max(1, root != null ? root.getHeight() : 1);
}
private int stepsToPx(int steps, int statusbarHeight) {
if (steps == 0 || statusbarHeight <= 0) {
return 0;
}
return Math.round((float) steps * statusbarHeight / 100f);
}
private Integer clockGeometrySignature(
@@ -870,7 +913,8 @@ public final class UnlockedIconSnapshotRenderController {
model.contentDescription,
rows,
previousPlacement.bounds,
statusBarTintTargetColor());
statusBarTintTargetColor(),
previousPlacement.textSizePx);
}
private Integer clockSourceGeometrySignature(TextView source) {
@@ -930,7 +974,8 @@ public final class UnlockedIconSnapshotRenderController {
currentText.contentDescription,
rows,
previous.bounds,
currentText.textColorOverride);
currentText.textColorOverride,
currentText.textSizePx);
}
private ClockRow matchingClockRow(ClockRow previousRow, ArrayList<ClockRow> currentRows) {
@@ -30,6 +30,7 @@ final class UnlockedLayoutBand {
int clipEndPx;
private int solverCollisionTop = Integer.MIN_VALUE;
private int solverCollisionHeight = Integer.MIN_VALUE;
private int solverVerticalScaleHeight = 0;
private UnlockedLayoutBand(
LayoutItemKind kind,
@@ -133,6 +134,11 @@ final class UnlockedLayoutBand {
return this;
}
UnlockedLayoutBand withSolverVerticalScale(int sourceHeight) {
solverVerticalScaleHeight = Math.max(0, sourceHeight);
return this;
}
static UnlockedLayoutBand dotIcons(
LayoutItemKind kind,
ViewGroup host,
@@ -373,8 +379,20 @@ final class UnlockedLayoutBand {
ArrayList<Bounds> solverCollisionBoxes() {
ArrayList<Bounds> boxes = collisionBoxes();
if (isTextKind()
|| solverCollisionTop == Integer.MIN_VALUE
if (solverVerticalScaleHeight > 0 && !boxes.isEmpty()) {
ArrayList<Bounds> solverBoxes = new ArrayList<>();
for (Bounds box : boxes) {
int top = pixelToStepFloor(box.top, solverVerticalScaleHeight);
int bottom = pixelToStepCeil(box.top + box.height, solverVerticalScaleHeight);
solverBoxes.add(new Bounds(
box.left,
top,
box.width,
Math.max(1, bottom - top)));
}
return solverBoxes;
}
if (solverCollisionTop == Integer.MIN_VALUE
|| solverCollisionHeight <= 0
|| boxes.isEmpty()) {
return boxes;
@@ -390,6 +408,20 @@ final class UnlockedLayoutBand {
return solverBoxes;
}
private int pixelToStepFloor(int px, int sourceHeight) {
if (sourceHeight <= 0) {
return px;
}
return (int) Math.floor((px * 100.0) / sourceHeight);
}
private int pixelToStepCeil(int px, int sourceHeight) {
if (sourceHeight <= 0) {
return px;
}
return (int) Math.ceil((px * 100.0) / sourceHeight);
}
private Bounds collisionBounds(SnapshotPlacement placement) {
Bounds relative = relativeCollisionBounds(placement);
Bounds bounds = placement.bounds;
@@ -708,7 +740,8 @@ final class UnlockedLayoutBand {
placement.contentDescription,
rows,
new Bounds(clipLeft, placement.bounds.top, Math.max(0, clipRight - clipLeft), placement.bounds.height),
placement.textColorOverride);
placement.textColorOverride,
placement.textSizePx);
}
private boolean isTextKind() {
@@ -21,6 +21,7 @@ import se.ajpanton.statusbartweak.runtime.layoutsolver.PackedStatusBarLayoutSolv
import se.ajpanton.statusbartweak.runtime.mode.NotificationDisplayStyle;
import se.ajpanton.statusbartweak.runtime.mode.SceneKey;
import se.ajpanton.statusbartweak.runtime.notifications.NotificationUnreadTracker;
import se.ajpanton.statusbartweak.shell.settings.SbtDefaults;
import se.ajpanton.statusbartweak.shell.settings.SbtSettings;
final class UnlockedLayoutPlanner {
@@ -90,11 +91,15 @@ final class UnlockedLayoutPlanner {
LayoutEdges edges = layoutEdges(root, settings, anchors, scene, aodStatusAnchorBounds);
int statusbarHeight = statusbarHeight(root, anchors, scene, aodStatusContentBounds);
if (clockEnabledForScene(settings, scene) && clockLayout != null && clockLayout.width() > 0) {
int clockHeightSteps = clockTextSizeSteps(settings, scene);
bands.add(UnlockedLayoutBand.clock(
clockHost,
clockLayout,
positionFor(settings.clockPosition),
sideFor(settings.clockMiddleCutoutSide)));
sideFor(settings.clockMiddleCutoutSide))
.withSolverCollision(
logicalRowTop(clockHeightSteps, settings.clockVerticalOffsetPx),
clockHeightSteps));
}
ClockLayout carrierLayout = carrierLayout(
root,
@@ -109,7 +114,8 @@ final class UnlockedLayoutPlanner {
carrierHost,
carrierLayout,
positionFor(settings.layoutCarrierPosition),
sideFor(settings.layoutCarrierMiddleSide)));
sideFor(settings.layoutCarrierMiddleSide))
.withSolverVerticalScale(statusbarHeight));
}
int statusCount = statusCountForScene(settings, scene);
if (scene != null && scene.isAod() && !isUsableAodAnchor(aodStatusAnchorBounds)) {
@@ -147,16 +153,16 @@ final class UnlockedLayoutPlanner {
placements,
stepsToPx(verticalOffsetSteps, statusbarHeight));
if (!placements.isEmpty()) {
bands.add(UnlockedLayoutBand.icons(
UnlockedLayoutBand band = UnlockedLayoutBand.icons(
LayoutItemKind.STATUS,
statusHost,
statusRenderer,
placements,
positionFor(stringAt(positions, i, settings.layoutStatusPosition)),
sideFor(stringAt(middleSides, i, settings.layoutStatusMiddleSide)))
.withSolverCollision(
logicalRowTop(statusHeightSteps, verticalOffsetSteps),
statusHeightSteps));
sideFor(stringAt(middleSides, i, settings.layoutStatusMiddleSide)));
bands.add(band.withSolverCollision(
logicalRowTop(statusHeightSteps, verticalOffsetSteps),
statusHeightSteps));
}
}
}
@@ -200,7 +206,7 @@ final class UnlockedLayoutPlanner {
if (!placements.isEmpty()) {
LayoutPosition position = positionFor(stringAt(positions, i, settings.layoutNotifPosition));
AnchorSide middleSide = sideFor(stringAt(middleSides, i, settings.layoutNotifMiddleSide));
bands.add(dotNotificationScene
UnlockedLayoutBand band = dotNotificationScene
? UnlockedLayoutBand.dotIcons(
LayoutItemKind.NOTIFICATIONS,
notificationHost,
@@ -208,19 +214,16 @@ final class UnlockedLayoutPlanner {
placements,
position,
middleSide)
.withSolverCollision(
logicalRowTop(notificationHeightSteps, verticalOffsetSteps),
notificationHeightSteps)
: UnlockedLayoutBand.icons(
LayoutItemKind.NOTIFICATIONS,
notificationHost,
notificationRenderer,
placements,
position,
middleSide)
.withSolverCollision(
logicalRowTop(notificationHeightSteps, verticalOffsetSteps),
notificationHeightSteps));
middleSide);
bands.add(band.withSolverCollision(
logicalRowTop(notificationHeightSteps, verticalOffsetSteps),
notificationHeightSteps));
}
}
}
@@ -263,10 +266,17 @@ final class UnlockedLayoutPlanner {
return new LayoutPlan(null, new ArrayList<>(), new ArrayList<>(), new ArrayList<>(), null);
}
ArrayList<LayoutItemKind> itemOrder = orderedKinds(settings.layoutItemOrder);
splitClockIfNeeded(root, bands, itemOrder, clockCutoutBounds, settings);
splitClockIfNeeded(root, bands, itemOrder, clockCutoutBounds, settings, scene);
sortBandsForItemOrder(bands, itemOrder);
int itemPadding = Math.max(0, settings.layoutItemPaddingPx);
solveWithPackedLayout(root, edges, bands, layoutCutoutBounds, settings, itemPadding, scene);
solveWithPackedLayout(
root,
edges,
bands,
layoutCutoutBounds,
settings,
itemPadding,
scene);
ArrayList<SnapshotPlacement> statusPlacements = new ArrayList<>();
ArrayList<SnapshotPlacement> notificationPlacements = new ArrayList<>();
@@ -1120,6 +1130,15 @@ final class UnlockedLayoutPlanner {
return Math.max(1, height);
}
private int clockTextSizeSteps(SbtSettings settings, SceneKey scene) {
if (settings == null) {
return SbtDefaults.CLOCK_TEXT_SIZE_STEPS_DEFAULT;
}
return scene != null && scene.isLockscreen()
? settings.clockLockTextSizeSteps
: settings.clockTextSizeSteps;
}
private int statusIconHeightSteps(SbtSettings settings, SceneKey scene) {
if (scene != null && scene.isLockscreen()) {
return settings.layoutStatusLockIconHeightSteps;
@@ -1260,7 +1279,8 @@ final class UnlockedLayoutPlanner {
ArrayList<UnlockedLayoutBand> bands,
ArrayList<LayoutItemKind> itemOrder,
Bounds cutoutBounds,
SbtSettings settings
SbtSettings settings,
SceneKey scene
) {
if (root == null || cutoutBounds == null || bands == null || settings == null) {
return;
@@ -1291,16 +1311,23 @@ final class UnlockedLayoutPlanner {
return;
}
bands.remove(index);
int clockHeightSteps = clockTextSizeSteps(settings, scene);
bands.add(index, UnlockedLayoutBand.fixedClock(
clockBand.host,
splitLayouts.get(1),
SplitRegion.RIGHT,
clockBand.middleSide));
clockBand.middleSide)
.withSolverCollision(
logicalRowTop(clockHeightSteps, settings.clockVerticalOffsetPx),
clockHeightSteps));
bands.add(index, UnlockedLayoutBand.fixedClock(
clockBand.host,
splitLayouts.get(0),
SplitRegion.LEFT,
clockBand.middleSide));
clockBand.middleSide)
.withSolverCollision(
logicalRowTop(clockHeightSteps, settings.clockVerticalOffsetPx),
clockHeightSteps));
}
private ArrayList<UnlockedLayoutBand> bandsForPosition(
@@ -1643,7 +1670,8 @@ final class UnlockedLayoutPlanner {
first.contentDescription,
rows,
union,
first.textColorOverride);
first.textColorOverride,
first.textSizePx);
}
static Bounds cutoutBounds(Rect cutout, int cutoutGap) {
@@ -1487,6 +1487,8 @@ final class UnlockedStockSourceCollector {
settings.clockMiddleAutoNearestSide,
settings.clockMiddleCutoutGapPx,
settings.clockVerticalOffsetPx,
settings.clockTextSizeSteps,
settings.clockLockTextSizeSteps,
settings.clockShowSeconds,
settings.clockShowDatePrefix,
settings.clockCustomFormatEnabled,
@@ -82,6 +82,7 @@ public final class SbtDefaults {
public static final int CLOCK_VERTICAL_OFFSET_PX_DEFAULT = 0;
public static final int CLOCK_VERTICAL_OFFSET_PX_MIN = -99;
public static final int CLOCK_VERTICAL_OFFSET_PX_MAX = 99;
public static final int CLOCK_TEXT_SIZE_STEPS_DEFAULT = 40;
public static final boolean CLOCK_SHOW_SECONDS_DEFAULT = false;
public static final boolean CLOCK_SHOW_DATE_PREFIX_DEFAULT = false;
public static final boolean CLOCK_CUSTOM_FORMAT_ENABLED_DEFAULT = false;
@@ -94,6 +94,8 @@ public final class SbtSettings {
public static final String KEY_CLOCK_MIDDLE_AUTO_NEAREST_SIDE = "clock_middle_auto_nearest_side";
public static final String KEY_CLOCK_MIDDLE_CUTOUT_GAP_PX = "clock_middle_cutout_gap_px";
public static final String KEY_CLOCK_VERTICAL_OFFSET_PX = "clock_vertical_offset_px";
public static final String KEY_CLOCK_TEXT_SIZE_STEPS = "clock_text_size_steps";
public static final String KEY_CLOCK_LOCK_TEXT_SIZE_STEPS = "layout_lock_clock_text_size_steps";
public static final String KEY_CLOCK_SHOW_SECONDS = "clock_show_seconds";
public static final String KEY_CLOCK_SHOW_DATE_PREFIX = "clock_show_date_prefix";
public static final String KEY_CLOCK_CUSTOM_FORMAT_ENABLED = "clock_custom_format_enabled";
@@ -328,6 +330,8 @@ public final class SbtSettings {
public final boolean clockMiddleAutoNearestSide;
public final int clockMiddleCutoutGapPx;
public final int clockVerticalOffsetPx;
public final int clockTextSizeSteps;
public final int clockLockTextSizeSteps;
public final boolean clockShowSeconds;
public final boolean clockShowDatePrefix;
public final boolean clockCustomFormatEnabled;
@@ -458,6 +462,8 @@ public final class SbtSettings {
boolean clockMiddleAutoNearestSide,
int clockMiddleCutoutGapPx,
int clockVerticalOffsetPx,
int clockTextSizeSteps,
int clockLockTextSizeSteps,
boolean clockShowSeconds,
boolean clockShowDatePrefix,
boolean clockCustomFormatEnabled,
@@ -630,6 +636,8 @@ public final class SbtSettings {
clockVerticalOffsetPx,
SbtDefaults.CLOCK_VERTICAL_OFFSET_PX_MIN,
SbtDefaults.CLOCK_VERTICAL_OFFSET_PX_MAX);
this.clockTextSizeSteps = clampIconHeightSteps(clockTextSizeSteps);
this.clockLockTextSizeSteps = clampIconHeightSteps(clockLockTextSizeSteps);
this.clockShowSeconds = clockShowSeconds;
this.clockShowDatePrefix = clockShowDatePrefix;
this.clockCustomFormatEnabled = clockCustomFormatEnabled;
@@ -889,6 +897,8 @@ public final class SbtSettings {
SbtDefaults.CLOCK_MIDDLE_AUTO_NEAREST_SIDE_DEFAULT,
SbtDefaults.CLOCK_MIDDLE_CUTOUT_GAP_PX_DEFAULT,
SbtDefaults.CLOCK_VERTICAL_OFFSET_PX_DEFAULT,
SbtDefaults.CLOCK_TEXT_SIZE_STEPS_DEFAULT,
SbtDefaults.CLOCK_TEXT_SIZE_STEPS_DEFAULT,
SbtDefaults.CLOCK_SHOW_SECONDS_DEFAULT,
SbtDefaults.CLOCK_SHOW_DATE_PREFIX_DEFAULT,
SbtDefaults.CLOCK_CUSTOM_FORMAT_ENABLED_DEFAULT,
@@ -1212,6 +1222,10 @@ public final class SbtSettings {
clampInt(prefs.getInt(KEY_CLOCK_VERTICAL_OFFSET_PX, SbtDefaults.CLOCK_VERTICAL_OFFSET_PX_DEFAULT),
SbtDefaults.CLOCK_VERTICAL_OFFSET_PX_MIN,
SbtDefaults.CLOCK_VERTICAL_OFFSET_PX_MAX),
clampIconHeightSteps(prefs.getInt(KEY_CLOCK_TEXT_SIZE_STEPS,
SbtDefaults.CLOCK_TEXT_SIZE_STEPS_DEFAULT)),
clampIconHeightSteps(prefs.getInt(KEY_CLOCK_LOCK_TEXT_SIZE_STEPS,
SbtDefaults.CLOCK_TEXT_SIZE_STEPS_DEFAULT)),
prefs.getBoolean(KEY_CLOCK_SHOW_SECONDS, SbtDefaults.CLOCK_SHOW_SECONDS_DEFAULT),
prefs.getBoolean(KEY_CLOCK_SHOW_DATE_PREFIX, SbtDefaults.CLOCK_SHOW_DATE_PREFIX_DEFAULT),
prefs.getBoolean(KEY_CLOCK_CUSTOM_FORMAT_ENABLED, SbtDefaults.CLOCK_CUSTOM_FORMAT_ENABLED_DEFAULT),
@@ -1526,6 +1540,10 @@ public final class SbtSettings {
clampInt(bundle.getInt(KEY_CLOCK_VERTICAL_OFFSET_PX, SbtDefaults.CLOCK_VERTICAL_OFFSET_PX_DEFAULT),
SbtDefaults.CLOCK_VERTICAL_OFFSET_PX_MIN,
SbtDefaults.CLOCK_VERTICAL_OFFSET_PX_MAX),
clampIconHeightSteps(bundle.getInt(KEY_CLOCK_TEXT_SIZE_STEPS,
SbtDefaults.CLOCK_TEXT_SIZE_STEPS_DEFAULT)),
clampIconHeightSteps(bundle.getInt(KEY_CLOCK_LOCK_TEXT_SIZE_STEPS,
SbtDefaults.CLOCK_TEXT_SIZE_STEPS_DEFAULT)),
bundle.getBoolean(KEY_CLOCK_SHOW_SECONDS, SbtDefaults.CLOCK_SHOW_SECONDS_DEFAULT),
bundle.getBoolean(KEY_CLOCK_SHOW_DATE_PREFIX, SbtDefaults.CLOCK_SHOW_DATE_PREFIX_DEFAULT),
bundle.getBoolean(KEY_CLOCK_CUSTOM_FORMAT_ENABLED, SbtDefaults.CLOCK_CUSTOM_FORMAT_ENABLED_DEFAULT),
@@ -139,6 +139,10 @@ public class SbtSettingsProvider extends ContentProvider {
prefs.getInt(SbtSettings.KEY_CLOCK_MIDDLE_CUTOUT_GAP_PX, SbtDefaults.CLOCK_MIDDLE_CUTOUT_GAP_PX_DEFAULT));
out.putInt(SbtSettings.KEY_CLOCK_VERTICAL_OFFSET_PX,
prefs.getInt(SbtSettings.KEY_CLOCK_VERTICAL_OFFSET_PX, SbtDefaults.CLOCK_VERTICAL_OFFSET_PX_DEFAULT));
out.putInt(SbtSettings.KEY_CLOCK_TEXT_SIZE_STEPS,
prefs.getInt(SbtSettings.KEY_CLOCK_TEXT_SIZE_STEPS, SbtDefaults.CLOCK_TEXT_SIZE_STEPS_DEFAULT));
out.putInt(SbtSettings.KEY_CLOCK_LOCK_TEXT_SIZE_STEPS,
prefs.getInt(SbtSettings.KEY_CLOCK_LOCK_TEXT_SIZE_STEPS, SbtDefaults.CLOCK_TEXT_SIZE_STEPS_DEFAULT));
out.putBoolean(SbtSettings.KEY_CLOCK_SHOW_SECONDS,
prefs.getBoolean(SbtSettings.KEY_CLOCK_SHOW_SECONDS, SbtDefaults.CLOCK_SHOW_SECONDS_DEFAULT));
out.putBoolean(SbtSettings.KEY_CLOCK_SHOW_DATE_PREFIX,
@@ -92,6 +92,7 @@ public final class LayoutFragment extends Fragment {
root.findViewById(R.id.clock_vertical_offset_plus_fast),
root.findViewById(R.id.clock_vertical_offset_reset),
SbtDefaults.CLOCK_VERTICAL_OFFSET_PX_DEFAULT);
addClockTextSizeControl(root, prefs);
bindStepper(prefs,
SbtSettings.KEY_LAYOUT_NOTIF_VERTICAL_OFFSET_PX,
root.findViewById(R.id.notif_vertical_offset_input),
@@ -226,6 +227,7 @@ public final class LayoutFragment extends Fragment {
countDefault,
legacyEnabledDefault);
LayoutXmlCardSupport.setIndexedCardTitle(requireContext(), sourceCard, sectionTitleId, 0, count);
setCompatPadding(sourceCard, count <= 1);
boolean cardsMode = LockedNotificationModeUi.MODE_CARDS.equals(
LockedNotificationModeUi.currentMode(requireContext()));
LayoutSectionCopySupport.addDropdownRow(
@@ -273,6 +275,8 @@ public final class LayoutFragment extends Fragment {
middleSideDefault,
verticalOffsetDefault,
copyTag);
setTopMargin(copy, 0);
setCompatPadding(copy, false);
cardsParent.addView(copy, Math.min(insertIndex, cardsParent.getChildCount()));
}
};
@@ -319,6 +323,19 @@ public final class LayoutFragment extends Fragment {
null);
}
private void addClockTextSizeControl(View root, SharedPreferences prefs) {
View row = LayoutXmlCardSupport.directSectionChild(root.findViewById(R.id.clock_position_group));
if (row == null || !(row.getParent() instanceof LinearLayout parent)) {
return;
}
int index = parent.indexOfChild(row);
parent.addView(iconHeightControl(
prefs,
SbtSettings.KEY_CLOCK_TEXT_SIZE_STEPS,
SbtDefaults.CLOCK_TEXT_SIZE_STEPS_DEFAULT,
R.string.layout_clock_text_size_steps), Math.max(0, index));
}
private void addChipHeightControl(View root, SharedPreferences prefs) {
View offsetInput = root.findViewById(R.id.chip_vertical_offset_input);
if (offsetInput == null) {
@@ -473,6 +490,20 @@ public final class LayoutFragment extends Fragment {
return null;
}
private void setTopMargin(View view, int topDp) {
if (view == null || !(view.getLayoutParams() instanceof ViewGroup.MarginLayoutParams params)) {
return;
}
params.topMargin = ViewTreeSupport.dp(requireContext(), topDp);
view.setLayoutParams(params);
}
private void setCompatPadding(View view, boolean enabled) {
if (view instanceof MaterialCardView card) {
card.setUseCompatPadding(enabled);
}
}
private void removeGeneratedCopyCards(@Nullable LinearLayout parent, String tag) {
if (parent == null) {
return;
@@ -246,12 +246,14 @@ final class LayoutSectionCopySupport {
"layout_lock_clock_middle_auto_nearest_side",
"layout_lock_clock_middle_side",
"layout_lock_clock_vertical_offset_px",
SbtSettings.KEY_CLOCK_LOCK_TEXT_SIZE_STEPS,
null,
SbtDefaults.CLOCK_ENABLED_LOCKSCREEN_DEFAULT,
SbtDefaults.CLOCK_POSITION_DEFAULT,
SbtDefaults.CLOCK_MIDDLE_AUTO_NEAREST_SIDE_DEFAULT,
SbtDefaults.CLOCK_MIDDLE_CUTOUT_SIDE_DEFAULT,
SbtDefaults.CLOCK_VERTICAL_OFFSET_PX_DEFAULT,
SbtDefaults.CLOCK_TEXT_SIZE_STEPS_DEFAULT,
1);
}
return new KeySet(
@@ -260,12 +262,14 @@ final class LayoutSectionCopySupport {
SbtSettings.KEY_CLOCK_MIDDLE_AUTO_NEAREST_SIDE,
SbtSettings.KEY_CLOCK_MIDDLE_CUTOUT_SIDE,
SbtSettings.KEY_CLOCK_VERTICAL_OFFSET_PX,
SbtSettings.KEY_CLOCK_TEXT_SIZE_STEPS,
null,
SbtDefaults.CLOCK_ENABLED_UNLOCKED_DEFAULT,
SbtDefaults.CLOCK_POSITION_DEFAULT,
SbtDefaults.CLOCK_MIDDLE_AUTO_NEAREST_SIDE_DEFAULT,
SbtDefaults.CLOCK_MIDDLE_CUTOUT_SIDE_DEFAULT,
SbtDefaults.CLOCK_VERTICAL_OFFSET_PX_DEFAULT,
SbtDefaults.CLOCK_TEXT_SIZE_STEPS_DEFAULT,
1);
}
@@ -114,6 +114,7 @@ abstract class LockedSceneLayoutFragment extends Fragment {
SbtDefaults.CLOCK_MIDDLE_CUTOUT_SIDE_DEFAULT,
sceneKey("clock_vertical_offset_px"),
SbtDefaults.CLOCK_VERTICAL_OFFSET_PX_DEFAULT);
addClockTextSizeControl(context, prefs, clockCard);
LayoutSectionCopySupport.addDropdown(
context,
prefs,
@@ -357,10 +358,11 @@ abstract class LockedSceneLayoutFragment extends Fragment {
countKey,
count,
rebuild[0]);
setCompatPadding(primary, count <= 1);
addCopyDropdownToCard(context, prefs, primary, copySection, cardsMode, refresh);
wrapper.addView(primary);
for (int i = 1; i < count; i++) {
wrapper.addView(configuredIconContainerCard(
View copy = configuredIconContainerCard(
context,
prefs,
cardId,
@@ -390,7 +392,10 @@ abstract class LockedSceneLayoutFragment extends Fragment {
null,
null,
count,
null));
null);
setTopMargin(context, copy, 0);
setCompatPadding(copy, false);
wrapper.addView(copy);
}
};
rebuild[0].run();
@@ -513,6 +518,25 @@ abstract class LockedSceneLayoutFragment extends Fragment {
return card;
}
private void addClockTextSizeControl(Context context, SharedPreferences prefs, View card) {
View row = LayoutXmlCardSupport.directSectionChild(
card != null ? card.findViewById(R.id.clock_position_group) : null);
if (row == null || !(row.getParent() instanceof LinearLayout parent)) {
return;
}
int index = parent.indexOfChild(row);
parent.addView(
sizeStepper(
context,
prefs,
R.string.layout_clock_text_size_steps,
SbtSettings.KEY_CLOCK_LOCK_TEXT_SIZE_STEPS,
SbtDefaults.CLOCK_TEXT_SIZE_STEPS_DEFAULT,
SbtDefaults.LAYOUT_ICON_HEIGHT_STEPS_MIN,
SbtDefaults.LAYOUT_ICON_HEIGHT_STEPS_MAX),
Math.max(0, index));
}
private void configureSingleEnabledCheckbox(
View root,
SharedPreferences prefs,
@@ -839,6 +863,20 @@ abstract class LockedSceneLayoutFragment extends Fragment {
return content instanceof LinearLayout layout ? layout : null;
}
private void setTopMargin(Context context, View view, int topDp) {
if (view == null || !(view.getLayoutParams() instanceof ViewGroup.MarginLayoutParams params)) {
return;
}
params.topMargin = ViewTreeSupport.dp(context, topDp);
view.setLayoutParams(params);
}
private void setCompatPadding(View view, boolean enabled) {
if (view instanceof com.google.android.material.card.MaterialCardView card) {
card.setUseCompatPadding(enabled);
}
}
private View outlinedCard(Context context, int titleId, View content) {
return ViewTreeSupport.outlinedCard(
context,
+1
View File
@@ -198,6 +198,7 @@
<string name="layout_copy_from">Copy from</string>
<string name="layout_icon_container_count">Number of containers</string>
<string name="layout_icon_height_steps">Icon height (% of statusbar)</string>
<string name="layout_clock_text_size_steps">Clock text size (% of statusbar)</string>
<string name="layout_chip_height_steps">Chip height (% of statusbar)</string>
<string name="layout_locked_notifications_title">Locked notifications mode</string>
<string name="layout_locked_notifications_hint">Mirrors the Samsung notification display style for AOD and lockscreen.</string>