Port packed layout solver and overhaul layout settings
This commit is contained in:
+2
-1
@@ -423,7 +423,8 @@ final class StockLayoutCanvasController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void hideOrDiscoverLockedStatusBarIconSurfaces(View root) {
|
private void hideOrDiscoverLockedStatusBarIconSurfaces(View root) {
|
||||||
if (!hideTrackedLockedStatusBarIconSurfaces(root)) {
|
hideTrackedLockedStatusBarIconSurfaces(root);
|
||||||
|
if (root != null) {
|
||||||
trackAndHideLockedStatusBarIconSurfaces(root);
|
trackAndHideLockedStatusBarIconSurfaces(root);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+1523
File diff suppressed because it is too large
Load Diff
+43
-20
@@ -17,6 +17,8 @@ import java.util.ArrayList;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
|
import se.ajpanton.statusbartweak.R;
|
||||||
|
import se.ajpanton.statusbartweak.runtime.layoutsolver.StatusBarLayoutSolver;
|
||||||
import se.ajpanton.statusbartweak.shell.settings.SbtSettings;
|
import se.ajpanton.statusbartweak.shell.settings.SbtSettings;
|
||||||
|
|
||||||
public final class DebugBoundsOverlayController {
|
public final class DebugBoundsOverlayController {
|
||||||
@@ -55,37 +57,31 @@ public final class DebugBoundsOverlayController {
|
|||||||
addCutoutSpecs(root, specs);
|
addCutoutSpecs(root, specs);
|
||||||
}
|
}
|
||||||
if (layoutEnabled && settings.debugVisualizeNotificationContainer) {
|
if (layoutEnabled && settings.debugVisualizeNotificationContainer) {
|
||||||
Bounds bounds = unionHostChildren(
|
addHostContainerSpecs(
|
||||||
root,
|
root,
|
||||||
parent,
|
parent,
|
||||||
OwnedIconHostManager.TAG_UNLOCKED_NOTIFICATION_HOST,
|
OwnedIconHostManager.TAG_UNLOCKED_NOTIFICATION_HOST,
|
||||||
true);
|
COLOR_NOTIFICATION,
|
||||||
if (bounds != null) {
|
specs);
|
||||||
specs.add(new OverlaySpec(bounds, COLOR_NOTIFICATION));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (layoutEnabled && settings.debugVisualizeStatusContainer) {
|
if (layoutEnabled && settings.debugVisualizeStatusContainer) {
|
||||||
Bounds bounds = unionHostChildren(
|
addHostContainerSpecs(
|
||||||
root,
|
root,
|
||||||
parent,
|
parent,
|
||||||
OwnedIconHostManager.TAG_UNLOCKED_STATUS_HOST,
|
OwnedIconHostManager.TAG_UNLOCKED_STATUS_HOST,
|
||||||
false);
|
COLOR_STATUS,
|
||||||
if (bounds != null) {
|
specs);
|
||||||
specs.add(new OverlaySpec(bounds, COLOR_STATUS));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (layoutEnabled && settings.debugVisualizeClockContainer) {
|
if (layoutEnabled && settings.debugVisualizeClockContainer) {
|
||||||
addHostChildSpecs(root, parent, TAG_CLOCK_HOST, COLOR_CLOCK, specs);
|
addHostChildSpecs(root, parent, TAG_CLOCK_HOST, COLOR_CLOCK, specs);
|
||||||
}
|
}
|
||||||
if (layoutEnabled && settings.debugVisualizeChipContainer) {
|
if (layoutEnabled && settings.debugVisualizeChipContainer) {
|
||||||
Bounds bounds = unionHostChildren(
|
addHostContainerSpecs(
|
||||||
root,
|
root,
|
||||||
parent,
|
parent,
|
||||||
OwnedIconHostManager.TAG_UNLOCKED_CHIP_HOST,
|
OwnedIconHostManager.TAG_UNLOCKED_CHIP_HOST,
|
||||||
false);
|
COLOR_CHIP,
|
||||||
if (bounds != null) {
|
specs);
|
||||||
specs.add(new OverlaySpec(bounds, COLOR_CHIP));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (specs.isEmpty()) {
|
if (specs.isEmpty()) {
|
||||||
removeOverlay(parent);
|
removeOverlay(parent);
|
||||||
@@ -144,15 +140,25 @@ public final class DebugBoundsOverlayController {
|
|||||||
return new Bounds(left, top, right - left, bottom - top);
|
return new Bounds(left, top, right - left, bottom - top);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Bounds unionHostChildren(
|
private void addHostContainerSpecs(
|
||||||
View root,
|
View root,
|
||||||
ViewGroup parent,
|
ViewGroup parent,
|
||||||
String hostTag,
|
String hostTag,
|
||||||
boolean stableRegularIconSlots
|
int color,
|
||||||
|
ArrayList<OverlaySpec> specs
|
||||||
) {
|
) {
|
||||||
View host = findDirectTaggedChild(parent, hostTag);
|
View host = findDirectTaggedChild(parent, hostTag);
|
||||||
if (!(host instanceof ViewGroup group)) {
|
if (!(host instanceof ViewGroup group)) {
|
||||||
return null;
|
return;
|
||||||
|
}
|
||||||
|
ArrayList<Bounds> containerBounds = taggedContainerBounds(host);
|
||||||
|
if (!containerBounds.isEmpty()) {
|
||||||
|
for (Bounds bounds : containerBounds) {
|
||||||
|
if (bounds.width > 0 && bounds.height > 0) {
|
||||||
|
specs.add(new OverlaySpec(bounds, color));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
Bounds union = null;
|
Bounds union = null;
|
||||||
for (int i = 0; i < group.getChildCount(); i++) {
|
for (int i = 0; i < group.getChildCount(); i++) {
|
||||||
@@ -160,13 +166,30 @@ public final class DebugBoundsOverlayController {
|
|||||||
if (child == null || child.getVisibility() == View.GONE) {
|
if (child == null || child.getVisibility() == View.GONE) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
Bounds bounds = overlayBoundsRelativeTo(root, child, stableRegularIconSlots);
|
Bounds bounds = overlayBoundsRelativeTo(root, child, false);
|
||||||
if (bounds == null || bounds.width <= 0 || bounds.height <= 0) {
|
if (bounds == null || bounds.width <= 0 || bounds.height <= 0) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
union = union == null ? bounds : union.union(bounds);
|
union = union == null ? bounds : union.union(bounds);
|
||||||
}
|
}
|
||||||
return union;
|
if (union != null) {
|
||||||
|
specs.add(new OverlaySpec(union, color));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private ArrayList<Bounds> taggedContainerBounds(View host) {
|
||||||
|
Object tag = host != null ? host.getTag(R.id.sbt_debug_container_bounds) : null;
|
||||||
|
if (!(tag instanceof ArrayList<?> values)) {
|
||||||
|
return new ArrayList<>();
|
||||||
|
}
|
||||||
|
ArrayList<Bounds> out = new ArrayList<>();
|
||||||
|
for (Object value : values) {
|
||||||
|
if (value instanceof StatusBarLayoutSolver.Bounds bounds) {
|
||||||
|
out.add(new Bounds(bounds.left, bounds.top, bounds.width, bounds.height));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isOverflowDot(View view) {
|
private boolean isOverflowDot(View view) {
|
||||||
|
|||||||
@@ -577,11 +577,10 @@ final class SnapshotRenderer {
|
|||||||
|
|
||||||
private void drawOverflowDot(Bitmap bitmap, int color) {
|
private void drawOverflowDot(Bitmap bitmap, int color) {
|
||||||
bitmap.eraseColor(Color.TRANSPARENT);
|
bitmap.eraseColor(Color.TRANSPARENT);
|
||||||
int base = Math.min(bitmap.getWidth(), bitmap.getHeight());
|
if (bitmap.getWidth() <= 0 || bitmap.getHeight() <= 0) {
|
||||||
if (base <= 0) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
float radius = Math.max(1f, base * 0.16f);
|
float radius = Math.max(1f, Math.min(bitmap.getWidth() / 2f, bitmap.getHeight() * 0.16f));
|
||||||
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||||
paint.setColor(color);
|
paint.setColor(color);
|
||||||
Canvas canvas = new Canvas(bitmap);
|
Canvas canvas = new Canvas(bitmap);
|
||||||
|
|||||||
@@ -164,6 +164,32 @@ final class UnlockedLayoutBand extends StatusBarLayoutSolver.LayoutBand {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void applyPackedContinuousWidth(int solvedWidth, AnchorSide wallSide) {
|
||||||
|
if (!canClipContinuously()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
clipStartPx = 0;
|
||||||
|
clipEndPx = 0;
|
||||||
|
int amount = Math.max(0, naturalWidth() - Math.max(0, solvedWidth));
|
||||||
|
if (amount > 0) {
|
||||||
|
clipContinuous(wallSide, amount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void applyPackedIconState(int firstIconIndex, int visibleIcons, boolean dot, int solvedWidth) {
|
||||||
|
if (kind != LayoutItemKind.NOTIFICATIONS && kind != LayoutItemKind.STATUS) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int targetVisible = Math.max(0, visibleIcons);
|
||||||
|
placements = iconSlice(rawPlacements, firstIconIndex, targetVisible);
|
||||||
|
if (dot) {
|
||||||
|
replaceOverflowDot(dotWidthForSolvedState(solvedWidth), dotHeight(placements));
|
||||||
|
}
|
||||||
|
if (targetVisible == 0 && !dot && placements.isEmpty()) {
|
||||||
|
placements.add(emptyIconContainerPlacement());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected int wallDotSlack(AnchorSide side) {
|
protected int wallDotSlack(AnchorSide side) {
|
||||||
SnapshotPlacement placement = wallPlacement(side);
|
SnapshotPlacement placement = wallPlacement(side);
|
||||||
@@ -238,6 +264,25 @@ final class UnlockedLayoutBand extends StatusBarLayoutSolver.LayoutBand {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void replaceOverflowDot(int width, int height) {
|
||||||
|
if (placements == null) {
|
||||||
|
placements = new ArrayList<>();
|
||||||
|
}
|
||||||
|
ArrayList<SnapshotPlacement> withoutDot = new ArrayList<>();
|
||||||
|
for (SnapshotPlacement placement : placements) {
|
||||||
|
if (!placement.overflowDot) {
|
||||||
|
withoutDot.add(placement);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SnapshotPlacement dotPlacement = dotPlacement(width, height);
|
||||||
|
if (kind == LayoutItemKind.STATUS) {
|
||||||
|
withoutDot.add(0, dotPlacement);
|
||||||
|
} else {
|
||||||
|
withoutDot.add(dotPlacement);
|
||||||
|
}
|
||||||
|
placements = withoutDot;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Object captureState() {
|
protected Object captureState() {
|
||||||
return new LayoutBandState(placements == null, new ArrayList<>(activePlacements()));
|
return new LayoutBandState(placements == null, new ArrayList<>(activePlacements()));
|
||||||
@@ -450,6 +495,46 @@ final class UnlockedLayoutBand extends StatusBarLayoutSolver.LayoutBand {
|
|||||||
&& (placement.key == null || !placement.key.startsWith(SnapshotKeys.EMPTY_ICON_CONTAINER));
|
&& (placement.key == null || !placement.key.startsWith(SnapshotKeys.EMPTY_ICON_CONTAINER));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private ArrayList<SnapshotPlacement> iconSlice(
|
||||||
|
ArrayList<SnapshotPlacement> source,
|
||||||
|
int firstIconIndex,
|
||||||
|
int count
|
||||||
|
) {
|
||||||
|
ArrayList<SnapshotPlacement> result = new ArrayList<>();
|
||||||
|
if (source == null || count <= 0) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
int skipped = 0;
|
||||||
|
int start = Math.max(0, firstIconIndex);
|
||||||
|
for (SnapshotPlacement placement : source) {
|
||||||
|
if (!isShrinkableIcon(placement)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (skipped < start) {
|
||||||
|
skipped++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
result.add(placement);
|
||||||
|
if (result.size() >= count) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int shrinkableIconCount(ArrayList<SnapshotPlacement> icons) {
|
||||||
|
int count = 0;
|
||||||
|
if (icons == null) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
for (SnapshotPlacement icon : icons) {
|
||||||
|
if (isShrinkableIcon(icon)) {
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
private boolean hasOverflowDot(ArrayList<SnapshotPlacement> icons) {
|
private boolean hasOverflowDot(ArrayList<SnapshotPlacement> icons) {
|
||||||
for (SnapshotPlacement icon : icons) {
|
for (SnapshotPlacement icon : icons) {
|
||||||
if (icon.overflowDot) {
|
if (icon.overflowDot) {
|
||||||
@@ -476,6 +561,18 @@ final class UnlockedLayoutBand extends StatusBarLayoutSolver.LayoutBand {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private int dotWidthForSolvedState(int solvedWidth) {
|
||||||
|
int visibleWidth = 0;
|
||||||
|
for (SnapshotPlacement placement : activePlacements()) {
|
||||||
|
if (!placement.overflowDot) {
|
||||||
|
visibleWidth += collisionBounds(placement).width;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
int solvedDotWidth = Math.max(1, solvedWidth - visibleWidth);
|
||||||
|
int fallbackWidth = Math.max(1, dotWidth(activePlacements()));
|
||||||
|
return Math.min(fallbackWidth, solvedDotWidth);
|
||||||
|
}
|
||||||
|
|
||||||
private int dotHeight(ArrayList<SnapshotPlacement> icons) {
|
private int dotHeight(ArrayList<SnapshotPlacement> icons) {
|
||||||
if (!icons.isEmpty()) {
|
if (!icons.isEmpty()) {
|
||||||
return icons.get(0).bounds.height;
|
return icons.get(0).bounds.height;
|
||||||
|
|||||||
+291
-70
@@ -5,19 +5,18 @@ import android.view.View;
|
|||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import se.ajpanton.statusbartweak.R;
|
||||||
import se.ajpanton.statusbartweak.runtime.layoutsolver.StatusBarLayoutSolver;
|
import se.ajpanton.statusbartweak.runtime.layoutsolver.StatusBarLayoutSolver;
|
||||||
import se.ajpanton.statusbartweak.runtime.layoutsolver.StatusBarLayoutSolver.Anchor;
|
|
||||||
import se.ajpanton.statusbartweak.runtime.layoutsolver.StatusBarLayoutSolver.AnchorKind;
|
|
||||||
import se.ajpanton.statusbartweak.runtime.layoutsolver.StatusBarLayoutSolver.AnchorSide;
|
import se.ajpanton.statusbartweak.runtime.layoutsolver.StatusBarLayoutSolver.AnchorSide;
|
||||||
import se.ajpanton.statusbartweak.runtime.layoutsolver.StatusBarLayoutSolver.Bounds;
|
import se.ajpanton.statusbartweak.runtime.layoutsolver.StatusBarLayoutSolver.Bounds;
|
||||||
import se.ajpanton.statusbartweak.runtime.layoutsolver.StatusBarLayoutSolver.LayoutEdges;
|
import se.ajpanton.statusbartweak.runtime.layoutsolver.StatusBarLayoutSolver.LayoutEdges;
|
||||||
import se.ajpanton.statusbartweak.runtime.layoutsolver.StatusBarLayoutSolver.LayoutItemKind;
|
import se.ajpanton.statusbartweak.runtime.layoutsolver.StatusBarLayoutSolver.LayoutItemKind;
|
||||||
import se.ajpanton.statusbartweak.runtime.layoutsolver.StatusBarLayoutSolver.LayoutPosition;
|
import se.ajpanton.statusbartweak.runtime.layoutsolver.StatusBarLayoutSolver.LayoutPosition;
|
||||||
import se.ajpanton.statusbartweak.runtime.layoutsolver.StatusBarLayoutSolver.PlacedBand;
|
|
||||||
import se.ajpanton.statusbartweak.runtime.layoutsolver.StatusBarLayoutSolver.Region;
|
|
||||||
import se.ajpanton.statusbartweak.runtime.layoutsolver.StatusBarLayoutSolver.RegionPlan;
|
|
||||||
import se.ajpanton.statusbartweak.runtime.layoutsolver.StatusBarLayoutSolver.SplitRegion;
|
import se.ajpanton.statusbartweak.runtime.layoutsolver.StatusBarLayoutSolver.SplitRegion;
|
||||||
|
import se.ajpanton.statusbartweak.runtime.layoutsolver.PackedStatusBarLayoutSolver;
|
||||||
import se.ajpanton.statusbartweak.shell.settings.SbtSettings;
|
import se.ajpanton.statusbartweak.shell.settings.SbtSettings;
|
||||||
|
|
||||||
final class UnlockedLayoutPlanner {
|
final class UnlockedLayoutPlanner {
|
||||||
@@ -81,106 +80,68 @@ final class UnlockedLayoutPlanner {
|
|||||||
false));
|
false));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (settings.layoutStatusEnabledUnlocked) {
|
if (settings.layoutStatusUnlockedCount > 0) {
|
||||||
ArrayList<SnapshotPlacement> rawPlacements =
|
ArrayList<SnapshotPlacement> rawPlacements =
|
||||||
statusRenderer.rawPlacements(root, collectedIcons.statusIcons);
|
statusRenderer.rawPlacements(root, collectedIcons.statusIcons);
|
||||||
|
int representativeHeight = representativeHeight(rawPlacements);
|
||||||
|
for (int i = 0; i < settings.layoutStatusUnlockedCount; i++) {
|
||||||
ArrayList<SnapshotPlacement> placements =
|
ArrayList<SnapshotPlacement> placements =
|
||||||
offsetPlacements(
|
offsetPlacements(
|
||||||
rawPlacements,
|
rawPlacements,
|
||||||
offsetScaler.status(
|
offsetScaler.status(
|
||||||
settings.layoutStatusVerticalOffsetPx,
|
intAt(settings.layoutStatusVerticalOffsetsPx, i, settings.layoutStatusVerticalOffsetPx),
|
||||||
representativeHeight(rawPlacements)));
|
representativeHeight));
|
||||||
if (!placements.isEmpty()) {
|
if (!placements.isEmpty()) {
|
||||||
bands.add(UnlockedLayoutBand.icons(
|
bands.add(UnlockedLayoutBand.icons(
|
||||||
LayoutItemKind.STATUS,
|
LayoutItemKind.STATUS,
|
||||||
statusHost,
|
statusHost,
|
||||||
statusRenderer,
|
statusRenderer,
|
||||||
placements,
|
placements,
|
||||||
positionFor(settings.layoutStatusPosition),
|
positionFor(stringAt(settings.layoutStatusPositions, i, settings.layoutStatusPosition)),
|
||||||
settings.layoutStatusMiddleAutoNearestSide,
|
booleanAt(
|
||||||
sideFor(settings.layoutStatusMiddleSide),
|
settings.layoutStatusMiddleAutoNearestSides,
|
||||||
settings.layoutStatusShowDotIfTruncated));
|
i,
|
||||||
|
settings.layoutStatusMiddleAutoNearestSide),
|
||||||
|
sideFor(stringAt(settings.layoutStatusMiddleSides, i, settings.layoutStatusMiddleSide)),
|
||||||
|
true));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (settings.layoutNotifEnabledUnlocked) {
|
}
|
||||||
|
if (settings.layoutNotifUnlockedCount > 0) {
|
||||||
ArrayList<SnapshotPlacement> rawPlacements =
|
ArrayList<SnapshotPlacement> rawPlacements =
|
||||||
notificationRenderer.rawPlacements(root, collectedIcons.notificationIcons);
|
notificationRenderer.rawPlacements(root, collectedIcons.notificationIcons);
|
||||||
|
int representativeHeight = representativeHeight(rawPlacements);
|
||||||
|
for (int i = 0; i < settings.layoutNotifUnlockedCount; i++) {
|
||||||
ArrayList<SnapshotPlacement> placements =
|
ArrayList<SnapshotPlacement> placements =
|
||||||
offsetPlacements(
|
offsetPlacements(
|
||||||
rawPlacements,
|
rawPlacements,
|
||||||
offsetScaler.notification(
|
offsetScaler.notification(
|
||||||
settings.layoutNotifVerticalOffsetPx,
|
intAt(settings.layoutNotifVerticalOffsetsPx, i, settings.layoutNotifVerticalOffsetPx),
|
||||||
representativeHeight(rawPlacements)));
|
representativeHeight));
|
||||||
if (!placements.isEmpty()) {
|
if (!placements.isEmpty()) {
|
||||||
bands.add(UnlockedLayoutBand.icons(
|
bands.add(UnlockedLayoutBand.icons(
|
||||||
LayoutItemKind.NOTIFICATIONS,
|
LayoutItemKind.NOTIFICATIONS,
|
||||||
notificationHost,
|
notificationHost,
|
||||||
notificationRenderer,
|
notificationRenderer,
|
||||||
placements,
|
placements,
|
||||||
positionFor(settings.layoutNotifPosition),
|
positionFor(stringAt(settings.layoutNotifPositions, i, settings.layoutNotifPosition)),
|
||||||
settings.layoutNotifMiddleAutoNearestSide,
|
booleanAt(
|
||||||
sideFor(settings.layoutNotifMiddleSide),
|
settings.layoutNotifMiddleAutoNearestSides,
|
||||||
settings.layoutNotifShowDotIfTruncated));
|
i,
|
||||||
|
settings.layoutNotifMiddleAutoNearestSide),
|
||||||
|
sideFor(stringAt(settings.layoutNotifMiddleSides, i, settings.layoutNotifMiddleSide)),
|
||||||
|
true));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (bands.isEmpty()) {
|
if (bands.isEmpty()) {
|
||||||
return new LayoutPlan(null, new ArrayList<>(), new ArrayList<>(), new ArrayList<>());
|
return new LayoutPlan(null, new ArrayList<>(), new ArrayList<>(), new ArrayList<>());
|
||||||
}
|
}
|
||||||
ArrayList<PlacedBand> placedBands = new ArrayList<>();
|
|
||||||
ArrayList<LayoutItemKind> itemOrder = orderedKinds(settings.layoutItemOrder);
|
ArrayList<LayoutItemKind> itemOrder = orderedKinds(settings.layoutItemOrder);
|
||||||
splitClockIfNeeded(root, bands, itemOrder, clockCutoutBounds, settings);
|
splitClockIfNeeded(root, bands, itemOrder, clockCutoutBounds, settings);
|
||||||
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);
|
||||||
RegionPlan<UnlockedLayoutBand> regionPlan = StatusBarLayoutSolver.planRegions(
|
solveWithPackedLayout(root, edges, bands, layoutCutoutBounds, settings, itemPadding);
|
||||||
root.getWidth(),
|
|
||||||
edges,
|
|
||||||
bands,
|
|
||||||
itemOrder,
|
|
||||||
layoutCutoutBounds,
|
|
||||||
itemPadding);
|
|
||||||
for (Region<UnlockedLayoutBand> region : regionPlan.regions()) {
|
|
||||||
StatusBarLayoutSolver.truncateToFit(
|
|
||||||
region.bands,
|
|
||||||
region.width(),
|
|
||||||
regionPlan.middleSide(),
|
|
||||||
settings.layoutNotifSortOrder,
|
|
||||||
itemPadding);
|
|
||||||
}
|
|
||||||
|
|
||||||
StatusBarLayoutSolver.placeStackedBands(
|
|
||||||
StatusBarLayoutSolver.bandsForPosition(bands, LayoutPosition.LEFT, itemOrder),
|
|
||||||
new Anchor(edges.left(), AnchorSide.RIGHT, AnchorKind.WALL),
|
|
||||||
placedBands,
|
|
||||||
settings.layoutNotifSortOrder,
|
|
||||||
false,
|
|
||||||
itemPadding);
|
|
||||||
StatusBarLayoutSolver.placeStackedBands(
|
|
||||||
StatusBarLayoutSolver.bandsForPosition(bands, LayoutPosition.RIGHT, itemOrder),
|
|
||||||
new Anchor(edges.right(), AnchorSide.LEFT, AnchorKind.WALL),
|
|
||||||
placedBands,
|
|
||||||
settings.layoutNotifSortOrder,
|
|
||||||
false,
|
|
||||||
itemPadding);
|
|
||||||
ArrayList<UnlockedLayoutBand> middleBands = StatusBarLayoutSolver.middleBandsForPlacement(
|
|
||||||
bands,
|
|
||||||
itemOrder,
|
|
||||||
regionPlan.middleSide());
|
|
||||||
if (!middleBands.isEmpty()) {
|
|
||||||
Anchor anchor = StatusBarLayoutSolver.anchorForMiddle(
|
|
||||||
root.getWidth(),
|
|
||||||
middleBands,
|
|
||||||
placedBands,
|
|
||||||
layoutCutoutBounds,
|
|
||||||
regionPlan.middleSide(),
|
|
||||||
itemPadding);
|
|
||||||
StatusBarLayoutSolver.placeStackedBands(
|
|
||||||
middleBands,
|
|
||||||
anchor,
|
|
||||||
placedBands,
|
|
||||||
settings.layoutNotifSortOrder,
|
|
||||||
true,
|
|
||||||
itemPadding);
|
|
||||||
}
|
|
||||||
|
|
||||||
ArrayList<SnapshotPlacement> statusPlacements = new ArrayList<>();
|
ArrayList<SnapshotPlacement> statusPlacements = new ArrayList<>();
|
||||||
ArrayList<SnapshotPlacement> notificationPlacements = new ArrayList<>();
|
ArrayList<SnapshotPlacement> notificationPlacements = new ArrayList<>();
|
||||||
@@ -204,6 +165,266 @@ final class UnlockedLayoutPlanner {
|
|||||||
notificationPlacements);
|
notificationPlacements);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void solveWithPackedLayout(
|
||||||
|
View root,
|
||||||
|
LayoutEdges edges,
|
||||||
|
ArrayList<UnlockedLayoutBand> bands,
|
||||||
|
Bounds cutoutBounds,
|
||||||
|
SbtSettings settings,
|
||||||
|
int itemPadding
|
||||||
|
) {
|
||||||
|
ArrayList<PackedStatusBarLayoutSolver.LayoutItem> items = new ArrayList<>();
|
||||||
|
ArrayList<UnlockedLayoutBand> itemBands = new ArrayList<>();
|
||||||
|
for (int i = 0; i < bands.size(); i++) {
|
||||||
|
UnlockedLayoutBand band = bands.get(i);
|
||||||
|
AnchorSide side = packedAnchorSide(band);
|
||||||
|
band.prepareForSide(side, settings.layoutNotifSortOrder);
|
||||||
|
PackedStatusBarLayoutSolver.LayoutItem item = packedItemForBand(band, i);
|
||||||
|
items.add(item);
|
||||||
|
itemBands.add(band);
|
||||||
|
}
|
||||||
|
configurePackedIconGroups(items);
|
||||||
|
int dx = edges.left();
|
||||||
|
int availableWidth = Math.max(0, edges.right() - edges.left());
|
||||||
|
Bounds relativeCutout = relativeCutout(cutoutBounds, edges);
|
||||||
|
PackedStatusBarLayoutSolver.Spec spec = new PackedStatusBarLayoutSolver.Spec(
|
||||||
|
availableWidth,
|
||||||
|
relativeCutout != null ? relativeCutout.left : 0,
|
||||||
|
relativeCutout != null ? relativeCutout.width : 0,
|
||||||
|
itemPadding,
|
||||||
|
packedShrinkOrder());
|
||||||
|
PackedStatusBarLayoutSolver.solve(spec, items);
|
||||||
|
for (PackedStatusBarLayoutSolver.LayoutItem item : items) {
|
||||||
|
item.x = Math.max(0, Math.min(availableWidth, item.x)) + dx;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < items.size(); i++) {
|
||||||
|
UnlockedLayoutBand band = itemBands.get(i);
|
||||||
|
PackedStatusBarLayoutSolver.LayoutItem item = items.get(i);
|
||||||
|
if (band.kind == LayoutItemKind.NOTIFICATIONS || band.kind == LayoutItemKind.STATUS) {
|
||||||
|
band.applyPackedIconState(
|
||||||
|
item.firstVisibleIconIndex,
|
||||||
|
item.visibleIcons,
|
||||||
|
item.dot,
|
||||||
|
Math.round((float) item.width()));
|
||||||
|
} else if (band.kind == LayoutItemKind.CHIP || band.kind == LayoutItemKind.CLOCK) {
|
||||||
|
band.applyPackedContinuousWidth(Math.round((float) item.width()), packedAnchorSide(band));
|
||||||
|
}
|
||||||
|
band.place(Math.round((float) item.x));
|
||||||
|
}
|
||||||
|
updateDebugContainerBounds(itemBands);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateDebugContainerBounds(ArrayList<UnlockedLayoutBand> bands) {
|
||||||
|
LinkedHashMap<ViewGroup, ArrayList<Bounds>> boundsByHost = new LinkedHashMap<>();
|
||||||
|
for (UnlockedLayoutBand band : bands) {
|
||||||
|
if (band.host == null
|
||||||
|
|| band.kind == LayoutItemKind.CLOCK) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
ArrayList<Bounds> hostBounds = boundsByHost.computeIfAbsent(
|
||||||
|
band.host,
|
||||||
|
ignored -> new ArrayList<>());
|
||||||
|
if (band.placedBounds == null
|
||||||
|
|| band.placedBounds.width <= 0
|
||||||
|
|| band.placedBounds.height <= 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
hostBounds.add(band.placedBounds);
|
||||||
|
}
|
||||||
|
for (Map.Entry<ViewGroup, ArrayList<Bounds>> entry : boundsByHost.entrySet()) {
|
||||||
|
entry.getKey().setTag(R.id.sbt_debug_container_bounds, entry.getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Bounds relativeCutout(Bounds cutoutBounds, LayoutEdges edges) {
|
||||||
|
if (cutoutBounds == null || edges == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
int left = Math.max(edges.left(), cutoutBounds.left);
|
||||||
|
int right = Math.min(edges.right(), cutoutBounds.left + cutoutBounds.width);
|
||||||
|
if (right <= left) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return new Bounds(left - edges.left(), cutoutBounds.top, right - left, cutoutBounds.height);
|
||||||
|
}
|
||||||
|
|
||||||
|
private PackedStatusBarLayoutSolver.LayoutItem packedItemForBand(UnlockedLayoutBand band, int index) {
|
||||||
|
ArrayList<PackedStatusBarLayoutSolver.Box> boxes = new ArrayList<>();
|
||||||
|
for (Bounds box : band.collisionBoxes()) {
|
||||||
|
boxes.add(new PackedStatusBarLayoutSolver.Box(box.left, box.top, box.width, box.height));
|
||||||
|
}
|
||||||
|
if (boxes.isEmpty()) {
|
||||||
|
boxes.add(new PackedStatusBarLayoutSolver.Box(0, band.top(), band.width(), band.height()));
|
||||||
|
}
|
||||||
|
PackedStatusBarLayoutSolver.LayoutItem item = new PackedStatusBarLayoutSolver.LayoutItem(
|
||||||
|
packedName(band, index),
|
||||||
|
packedKind(band.kind),
|
||||||
|
packedPosition(band.position),
|
||||||
|
packedFallback(band),
|
||||||
|
boxes);
|
||||||
|
if (band.kind == LayoutItemKind.NOTIFICATIONS || band.kind == LayoutItemKind.STATUS) {
|
||||||
|
item.iconGroup = packedKind(band.kind).toLowerCase(java.util.Locale.ROOT);
|
||||||
|
item.iconCount = band.rawPlacements != null ? band.rawPlacements.size() : 0;
|
||||||
|
item.visibleIcons = item.iconCount;
|
||||||
|
item.poolRemaining = item.iconCount;
|
||||||
|
item.iconWidth = representativeIconWidth(band);
|
||||||
|
item.iconWidths = iconWidths(band);
|
||||||
|
item.dotWidth = Math.max(1, Math.round(band.height() * 0.75f));
|
||||||
|
item.compactDotWidth = representativeCompactDotWidth(band);
|
||||||
|
item.allowZero = false;
|
||||||
|
item.allowDot = true;
|
||||||
|
item.direction = band.kind == LayoutItemKind.STATUS
|
||||||
|
? PackedStatusBarLayoutSolver.Direction.LEFT
|
||||||
|
: PackedStatusBarLayoutSolver.Direction.RIGHT;
|
||||||
|
item.widthLimit = sumIconWidths(item.iconWidths, item.iconCount, item.iconWidth);
|
||||||
|
}
|
||||||
|
item.y = 0;
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int representativeIconWidth(UnlockedLayoutBand band) {
|
||||||
|
ArrayList<SnapshotPlacement> placements = band.rawPlacements;
|
||||||
|
if (placements == null || placements.isEmpty()) {
|
||||||
|
return Math.max(1, band.width());
|
||||||
|
}
|
||||||
|
return Math.max(1, placements.get(0).bounds.width);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ArrayList<Integer> iconWidths(UnlockedLayoutBand band) {
|
||||||
|
ArrayList<Integer> widths = new ArrayList<>();
|
||||||
|
ArrayList<SnapshotPlacement> placements = band.rawPlacements;
|
||||||
|
if (placements == null) {
|
||||||
|
return widths;
|
||||||
|
}
|
||||||
|
for (SnapshotPlacement placement : placements) {
|
||||||
|
Bounds bounds;
|
||||||
|
if (band.kind == LayoutItemKind.NOTIFICATIONS) {
|
||||||
|
bounds = placement != null ? placement.bounds : null;
|
||||||
|
} else {
|
||||||
|
bounds = placement != null ? placement.contentBounds : null;
|
||||||
|
if (bounds == null || bounds.width < 0) {
|
||||||
|
bounds = placement != null ? placement.bounds : null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
widths.add(Math.max(0, bounds != null ? bounds.width : 0));
|
||||||
|
}
|
||||||
|
return widths;
|
||||||
|
}
|
||||||
|
|
||||||
|
private double sumIconWidths(ArrayList<Integer> widths, int count, int fallbackWidth) {
|
||||||
|
double total = 0;
|
||||||
|
for (int i = 0; i < Math.max(0, count); i++) {
|
||||||
|
if (widths != null && i < widths.size()) {
|
||||||
|
total += Math.max(0, widths.get(i));
|
||||||
|
} else {
|
||||||
|
total += Math.max(0, fallbackWidth);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return total;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int representativeCompactDotWidth(UnlockedLayoutBand band) {
|
||||||
|
ArrayList<SnapshotPlacement> placements = band.rawPlacements;
|
||||||
|
if (placements == null || placements.isEmpty()) {
|
||||||
|
return Math.max(1, band.width());
|
||||||
|
}
|
||||||
|
Bounds bounds = placements.get(0).bounds;
|
||||||
|
int slotWidth = Math.max(1, bounds.width);
|
||||||
|
int base = Math.max(1, Math.min(slotWidth, bounds.height));
|
||||||
|
int radius = Math.max(1, Math.round(base * 0.16f));
|
||||||
|
int visualWidth = Math.min(slotWidth, radius * 2 + 2);
|
||||||
|
return Math.max(visualWidth, slotWidth - Math.max(0, (slotWidth - visualWidth) / 2));
|
||||||
|
}
|
||||||
|
|
||||||
|
private String stringAt(String[] values, int index, String fallback) {
|
||||||
|
return values != null && index >= 0 && index < values.length && values[index] != null
|
||||||
|
? values[index]
|
||||||
|
: fallback;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean booleanAt(boolean[] values, int index, boolean fallback) {
|
||||||
|
return values != null && index >= 0 && index < values.length ? values[index] : fallback;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int intAt(int[] values, int index, int fallback) {
|
||||||
|
return values != null && index >= 0 && index < values.length ? values[index] : fallback;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void configurePackedIconGroups(ArrayList<PackedStatusBarLayoutSolver.LayoutItem> items) {
|
||||||
|
java.util.LinkedHashMap<String, ArrayList<PackedStatusBarLayoutSolver.LayoutItem>> groups =
|
||||||
|
new java.util.LinkedHashMap<>();
|
||||||
|
for (PackedStatusBarLayoutSolver.LayoutItem item : items) {
|
||||||
|
if (item.iconGroup != null) {
|
||||||
|
groups.computeIfAbsent(item.iconGroup, ignored -> new ArrayList<>()).add(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (ArrayList<PackedStatusBarLayoutSolver.LayoutItem> groupItems : groups.values()) {
|
||||||
|
for (int i = 0; i < groupItems.size(); i++) {
|
||||||
|
PackedStatusBarLayoutSolver.LayoutItem item = groupItems.get(i);
|
||||||
|
item.allowZero = i > 0;
|
||||||
|
item.allowDot = i == 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private ArrayList<String> packedShrinkOrder() {
|
||||||
|
ArrayList<String> order = new ArrayList<>();
|
||||||
|
order.add("Notifs");
|
||||||
|
order.add("Status");
|
||||||
|
order.add("Chip");
|
||||||
|
order.add("Clock");
|
||||||
|
return order;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String packedName(UnlockedLayoutBand band, int index) {
|
||||||
|
return packedKind(band.kind) + index;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String packedKind(LayoutItemKind kind) {
|
||||||
|
if (kind == LayoutItemKind.NOTIFICATIONS) {
|
||||||
|
return "Notifs";
|
||||||
|
}
|
||||||
|
if (kind == LayoutItemKind.STATUS) {
|
||||||
|
return "Status";
|
||||||
|
}
|
||||||
|
if (kind == LayoutItemKind.CHIP) {
|
||||||
|
return "Chip";
|
||||||
|
}
|
||||||
|
return "Clock";
|
||||||
|
}
|
||||||
|
|
||||||
|
private PackedStatusBarLayoutSolver.Position packedPosition(LayoutPosition position) {
|
||||||
|
if (position == LayoutPosition.RIGHT) {
|
||||||
|
return PackedStatusBarLayoutSolver.Position.RIGHT;
|
||||||
|
}
|
||||||
|
if (position == LayoutPosition.MIDDLE) {
|
||||||
|
return PackedStatusBarLayoutSolver.Position.MIDDLE;
|
||||||
|
}
|
||||||
|
return PackedStatusBarLayoutSolver.Position.LEFT;
|
||||||
|
}
|
||||||
|
|
||||||
|
private PackedStatusBarLayoutSolver.Fallback packedFallback(UnlockedLayoutBand band) {
|
||||||
|
return band.middleSide == AnchorSide.RIGHT
|
||||||
|
? PackedStatusBarLayoutSolver.Fallback.RIGHT
|
||||||
|
: PackedStatusBarLayoutSolver.Fallback.LEFT;
|
||||||
|
}
|
||||||
|
|
||||||
|
private AnchorSide packedAnchorSide(UnlockedLayoutBand band) {
|
||||||
|
if (band.splitRegion == SplitRegion.LEFT) {
|
||||||
|
return AnchorSide.LEFT;
|
||||||
|
}
|
||||||
|
if (band.splitRegion == SplitRegion.RIGHT) {
|
||||||
|
return AnchorSide.RIGHT;
|
||||||
|
}
|
||||||
|
if (band.position == LayoutPosition.LEFT) {
|
||||||
|
return AnchorSide.RIGHT;
|
||||||
|
}
|
||||||
|
if (band.position == LayoutPosition.RIGHT) {
|
||||||
|
return AnchorSide.LEFT;
|
||||||
|
}
|
||||||
|
return band.middleSide;
|
||||||
|
}
|
||||||
|
|
||||||
private ArrayList<SnapshotPlacement> offsetPlacements(ArrayList<SnapshotPlacement> placements, int verticalOffsetPx) {
|
private ArrayList<SnapshotPlacement> offsetPlacements(ArrayList<SnapshotPlacement> placements, int verticalOffsetPx) {
|
||||||
if (placements == null || placements.isEmpty() || verticalOffsetPx == 0) {
|
if (placements == null || placements.isEmpty() || verticalOffsetPx == 0) {
|
||||||
return placements;
|
return placements;
|
||||||
|
|||||||
+10
-2
@@ -718,14 +718,22 @@ final class UnlockedStockSourceCollector {
|
|||||||
settings.layoutChipVerticalOffsetPx,
|
settings.layoutChipVerticalOffsetPx,
|
||||||
settings.layoutNotifPosition,
|
settings.layoutNotifPosition,
|
||||||
settings.layoutNotifSortOrder,
|
settings.layoutNotifSortOrder,
|
||||||
settings.layoutNotifShowDotIfTruncated,
|
|
||||||
settings.layoutNotifEnabledUnlocked,
|
settings.layoutNotifEnabledUnlocked,
|
||||||
|
settings.layoutNotifUnlockedCount,
|
||||||
|
java.util.Arrays.hashCode(settings.layoutNotifPositions),
|
||||||
|
java.util.Arrays.hashCode(settings.layoutNotifMiddleSides),
|
||||||
|
java.util.Arrays.hashCode(settings.layoutNotifMiddleAutoNearestSides),
|
||||||
|
java.util.Arrays.hashCode(settings.layoutNotifVerticalOffsetsPx),
|
||||||
settings.layoutNotifMiddleSide,
|
settings.layoutNotifMiddleSide,
|
||||||
settings.layoutNotifMiddleAutoNearestSide,
|
settings.layoutNotifMiddleAutoNearestSide,
|
||||||
settings.layoutNotifVerticalOffsetPx,
|
settings.layoutNotifVerticalOffsetPx,
|
||||||
settings.layoutStatusPosition,
|
settings.layoutStatusPosition,
|
||||||
settings.layoutStatusEnabledUnlocked,
|
settings.layoutStatusEnabledUnlocked,
|
||||||
settings.layoutStatusShowDotIfTruncated,
|
settings.layoutStatusUnlockedCount,
|
||||||
|
java.util.Arrays.hashCode(settings.layoutStatusPositions),
|
||||||
|
java.util.Arrays.hashCode(settings.layoutStatusMiddleSides),
|
||||||
|
java.util.Arrays.hashCode(settings.layoutStatusMiddleAutoNearestSides),
|
||||||
|
java.util.Arrays.hashCode(settings.layoutStatusVerticalOffsetsPx),
|
||||||
settings.layoutStatusMiddleSide,
|
settings.layoutStatusMiddleSide,
|
||||||
settings.layoutStatusMiddleAutoNearestSide,
|
settings.layoutStatusMiddleAutoNearestSide,
|
||||||
settings.layoutStatusVerticalOffsetPx,
|
settings.layoutStatusVerticalOffsetPx,
|
||||||
|
|||||||
+87
@@ -0,0 +1,87 @@
|
|||||||
|
package se.ajpanton.statusbartweak.shell.debug;
|
||||||
|
|
||||||
|
import android.content.ContentResolver;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.provider.Settings;
|
||||||
|
import android.text.TextUtils;
|
||||||
|
|
||||||
|
public final class NotificationDisplayStyleSettings {
|
||||||
|
private static final String KEY_LOCKSCREEN_MINIMIZING_NOTIFICATION =
|
||||||
|
"lockscreen_minimizing_notification";
|
||||||
|
|
||||||
|
private NotificationDisplayStyleSettings() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static State read(Context context) {
|
||||||
|
ContentResolver resolver = context != null ? context.getContentResolver() : null;
|
||||||
|
Integer minimizing = getSystemInt(resolver, KEY_LOCKSCREEN_MINIMIZING_NOTIFICATION);
|
||||||
|
return new State(minimizing, styleFor(minimizing));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean write(Context context, Style style) {
|
||||||
|
if (context == null || style == null || style == Style.UNKNOWN) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
int minimizing = switch (style) {
|
||||||
|
case CARDS -> 0;
|
||||||
|
case ICONS -> 1;
|
||||||
|
case DOT -> 2;
|
||||||
|
case UNKNOWN -> -1;
|
||||||
|
};
|
||||||
|
if (minimizing < 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return runRootCommand(
|
||||||
|
"settings put system " + KEY_LOCKSCREEN_MINIMIZING_NOTIFICATION + " " + minimizing);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Style styleFor(Integer minimizing) {
|
||||||
|
if (minimizing == null) {
|
||||||
|
return Style.UNKNOWN;
|
||||||
|
}
|
||||||
|
return switch (minimizing) {
|
||||||
|
case 0 -> Style.CARDS;
|
||||||
|
case 1 -> Style.ICONS;
|
||||||
|
case 2 -> Style.DOT;
|
||||||
|
default -> Style.UNKNOWN;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Integer getSystemInt(ContentResolver resolver, String key) {
|
||||||
|
if (resolver == null || TextUtils.isEmpty(key)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return Settings.System.getInt(resolver, key);
|
||||||
|
} catch (Throwable ignored) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean runRootCommand(String command) {
|
||||||
|
if (TextUtils.isEmpty(command)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
Process process = new ProcessBuilder("su", "-c", command)
|
||||||
|
.redirectErrorStream(true)
|
||||||
|
.start();
|
||||||
|
return process.waitFor() == 0;
|
||||||
|
} catch (Throwable ignored) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum Style {
|
||||||
|
UNKNOWN,
|
||||||
|
CARDS,
|
||||||
|
ICONS,
|
||||||
|
DOT
|
||||||
|
}
|
||||||
|
|
||||||
|
public record State(
|
||||||
|
Integer minimizingNotification,
|
||||||
|
Style style
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -30,24 +30,24 @@ public final class SbtDefaults {
|
|||||||
public static final boolean ICONS_LOCK_EVEN_DISTRIBUTION_DEFAULT = false;
|
public static final boolean ICONS_LOCK_EVEN_DISTRIBUTION_DEFAULT = false;
|
||||||
|
|
||||||
// Cards mode (AOD).
|
// Cards mode (AOD).
|
||||||
public static final int CARDS_AOD_MAX_ICONS_PER_ROW_DEFAULT = 8;
|
public static final int CARDS_AOD_MAX_ICONS_PER_ROW_DEFAULT = 5;
|
||||||
public static final int CARDS_AOD_MAX_ICONS_PER_ROW_MIN = 1;
|
public static final int CARDS_AOD_MAX_ICONS_PER_ROW_MIN = 1;
|
||||||
public static final int CARDS_AOD_MAX_ICONS_PER_ROW_MAX = 15;
|
public static final int CARDS_AOD_MAX_ICONS_PER_ROW_MAX = 15;
|
||||||
public static final int CARDS_AOD_MAX_ROWS_DEFAULT = 3;
|
public static final int CARDS_AOD_MAX_ROWS_DEFAULT = 1;
|
||||||
public static final int CARDS_AOD_MAX_ROWS_MIN = 1;
|
public static final int CARDS_AOD_MAX_ROWS_MIN = 0;
|
||||||
public static final int CARDS_AOD_MAX_ROWS_MAX = 5;
|
public static final int CARDS_AOD_MAX_ROWS_MAX = 5;
|
||||||
public static final boolean CARDS_AOD_EVEN_DISTRIBUTION_DEFAULT = true;
|
public static final boolean CARDS_AOD_EVEN_DISTRIBUTION_DEFAULT = true;
|
||||||
public static final boolean CARDS_AOD_LIMIT_TO_WIDTH_DEFAULT = false;
|
public static final boolean CARDS_AOD_LIMIT_TO_WIDTH_DEFAULT = true;
|
||||||
|
|
||||||
// Cards mode (Lockscreen).
|
// Cards mode (Lockscreen).
|
||||||
public static final int CARDS_LOCK_MAX_ICONS_PER_ROW_DEFAULT = 8;
|
public static final int CARDS_LOCK_MAX_ICONS_PER_ROW_DEFAULT = 5;
|
||||||
public static final int CARDS_LOCK_MAX_ICONS_PER_ROW_MIN = 1;
|
public static final int CARDS_LOCK_MAX_ICONS_PER_ROW_MIN = 1;
|
||||||
public static final int CARDS_LOCK_MAX_ICONS_PER_ROW_MAX = 15;
|
public static final int CARDS_LOCK_MAX_ICONS_PER_ROW_MAX = 15;
|
||||||
public static final int CARDS_LOCK_MAX_ROWS_DEFAULT = 3;
|
public static final int CARDS_LOCK_MAX_ROWS_DEFAULT = 1;
|
||||||
public static final int CARDS_LOCK_MAX_ROWS_MIN = 1;
|
public static final int CARDS_LOCK_MAX_ROWS_MIN = 0;
|
||||||
public static final int CARDS_LOCK_MAX_ROWS_MAX = 5;
|
public static final int CARDS_LOCK_MAX_ROWS_MAX = 5;
|
||||||
public static final boolean CARDS_LOCK_EVEN_DISTRIBUTION_DEFAULT = true;
|
public static final boolean CARDS_LOCK_EVEN_DISTRIBUTION_DEFAULT = true;
|
||||||
public static final boolean CARDS_LOCK_LIMIT_TO_WIDTH_DEFAULT = false;
|
public static final boolean CARDS_LOCK_LIMIT_TO_WIDTH_DEFAULT = true;
|
||||||
public static final boolean AOD_KEEP_VISIBLE_DURING_CALL_DEFAULT = false;
|
public static final boolean AOD_KEEP_VISIBLE_DURING_CALL_DEFAULT = false;
|
||||||
public static final boolean BATTERY_LOCKSCREEN_UNPLUGGED_PERCENT_DEFAULT = true;
|
public static final boolean BATTERY_LOCKSCREEN_UNPLUGGED_PERCENT_DEFAULT = true;
|
||||||
public static final boolean BATTERY_AOD_UNPLUGGED_PERCENT_DEFAULT = true;
|
public static final boolean BATTERY_AOD_UNPLUGGED_PERCENT_DEFAULT = true;
|
||||||
@@ -115,6 +115,7 @@ public final class SbtDefaults {
|
|||||||
public static final boolean LAYOUT_NOTIF_ENABLED_AOD_DEFAULT = true;
|
public static final boolean LAYOUT_NOTIF_ENABLED_AOD_DEFAULT = true;
|
||||||
public static final boolean LAYOUT_NOTIF_ENABLED_LOCKSCREEN_DEFAULT = true;
|
public static final boolean LAYOUT_NOTIF_ENABLED_LOCKSCREEN_DEFAULT = true;
|
||||||
public static final boolean LAYOUT_NOTIF_ENABLED_UNLOCKED_DEFAULT = true;
|
public static final boolean LAYOUT_NOTIF_ENABLED_UNLOCKED_DEFAULT = true;
|
||||||
|
public static final int LAYOUT_NOTIF_UNLOCKED_COUNT_DEFAULT = 1;
|
||||||
public static final String LAYOUT_NOTIF_MIDDLE_SIDE_DEFAULT = "left";
|
public static final String LAYOUT_NOTIF_MIDDLE_SIDE_DEFAULT = "left";
|
||||||
public static final boolean LAYOUT_NOTIF_MIDDLE_AUTO_NEAREST_SIDE_DEFAULT = true;
|
public static final boolean LAYOUT_NOTIF_MIDDLE_AUTO_NEAREST_SIDE_DEFAULT = true;
|
||||||
public static final int LAYOUT_NOTIF_VERTICAL_OFFSET_PX_DEFAULT = 0;
|
public static final int LAYOUT_NOTIF_VERTICAL_OFFSET_PX_DEFAULT = 0;
|
||||||
@@ -122,10 +123,13 @@ public final class SbtDefaults {
|
|||||||
public static final boolean LAYOUT_STATUS_ENABLED_AOD_DEFAULT = true;
|
public static final boolean LAYOUT_STATUS_ENABLED_AOD_DEFAULT = true;
|
||||||
public static final boolean LAYOUT_STATUS_ENABLED_LOCKSCREEN_DEFAULT = true;
|
public static final boolean LAYOUT_STATUS_ENABLED_LOCKSCREEN_DEFAULT = true;
|
||||||
public static final boolean LAYOUT_STATUS_ENABLED_UNLOCKED_DEFAULT = true;
|
public static final boolean LAYOUT_STATUS_ENABLED_UNLOCKED_DEFAULT = true;
|
||||||
|
public static final int LAYOUT_STATUS_UNLOCKED_COUNT_DEFAULT = 1;
|
||||||
public static final boolean LAYOUT_STATUS_SHOW_DOT_IF_TRUNCATED_DEFAULT = true;
|
public static final boolean LAYOUT_STATUS_SHOW_DOT_IF_TRUNCATED_DEFAULT = true;
|
||||||
public static final String LAYOUT_STATUS_MIDDLE_SIDE_DEFAULT = "left";
|
public static final String LAYOUT_STATUS_MIDDLE_SIDE_DEFAULT = "left";
|
||||||
public static final boolean LAYOUT_STATUS_MIDDLE_AUTO_NEAREST_SIDE_DEFAULT = true;
|
public static final boolean LAYOUT_STATUS_MIDDLE_AUTO_NEAREST_SIDE_DEFAULT = true;
|
||||||
public static final int LAYOUT_STATUS_VERTICAL_OFFSET_PX_DEFAULT = 0;
|
public static final int LAYOUT_STATUS_VERTICAL_OFFSET_PX_DEFAULT = 0;
|
||||||
|
public static final int LAYOUT_ICON_CONTAINER_COUNT_MIN = 0;
|
||||||
|
public static final int LAYOUT_ICON_CONTAINER_COUNT_MAX = 3;
|
||||||
|
|
||||||
private SbtDefaults() {
|
private SbtDefaults() {
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,10 @@ import java.util.Map;
|
|||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
public final class SbtSettings {
|
public final class SbtSettings {
|
||||||
|
private interface IndexedKey {
|
||||||
|
String key(int index);
|
||||||
|
}
|
||||||
|
|
||||||
public static final String MODULE_PACKAGE = "se.ajpanton.statusbartweak";
|
public static final String MODULE_PACKAGE = "se.ajpanton.statusbartweak";
|
||||||
public static final String PREFS_NAME = "sbt_settings";
|
public static final String PREFS_NAME = "sbt_settings";
|
||||||
public static final String ACTION_SETTINGS_CHANGED =
|
public static final String ACTION_SETTINGS_CHANGED =
|
||||||
@@ -32,6 +36,8 @@ public final class SbtSettings {
|
|||||||
"debug_visualize_chip_container";
|
"debug_visualize_chip_container";
|
||||||
public static final String KEY_DEBUG_VISUALIZE_CAMERA_CUTOUT =
|
public static final String KEY_DEBUG_VISUALIZE_CAMERA_CUTOUT =
|
||||||
"debug_visualize_camera_cutout";
|
"debug_visualize_camera_cutout";
|
||||||
|
public static final String KEY_LOCKED_NOTIFICATION_MODE =
|
||||||
|
"locked_notification_mode";
|
||||||
public static final String KEY_UNLOCKED_MAX_ICONS_PER_ROW = "unlocked_max_icons_per_row";
|
public static final String KEY_UNLOCKED_MAX_ICONS_PER_ROW = "unlocked_max_icons_per_row";
|
||||||
public static final String KEY_ICONS_AOD_MAX_ICONS_PER_ROW = "icons_aod_max_icons_per_row";
|
public static final String KEY_ICONS_AOD_MAX_ICONS_PER_ROW = "icons_aod_max_icons_per_row";
|
||||||
public static final String KEY_ICONS_AOD_MAX_ROWS = "icons_aod_max_rows";
|
public static final String KEY_ICONS_AOD_MAX_ROWS = "icons_aod_max_rows";
|
||||||
@@ -101,6 +107,7 @@ public final class SbtSettings {
|
|||||||
public static final String KEY_LAYOUT_NOTIF_ENABLED_AOD = "layout_notif_enabled_aod";
|
public static final String KEY_LAYOUT_NOTIF_ENABLED_AOD = "layout_notif_enabled_aod";
|
||||||
public static final String KEY_LAYOUT_NOTIF_ENABLED_LOCKSCREEN = "layout_notif_enabled_lockscreen";
|
public static final String KEY_LAYOUT_NOTIF_ENABLED_LOCKSCREEN = "layout_notif_enabled_lockscreen";
|
||||||
public static final String KEY_LAYOUT_NOTIF_ENABLED_UNLOCKED = "layout_notif_enabled_unlocked";
|
public static final String KEY_LAYOUT_NOTIF_ENABLED_UNLOCKED = "layout_notif_enabled_unlocked";
|
||||||
|
public static final String KEY_LAYOUT_NOTIF_UNLOCKED_COUNT = "layout_notif_unlocked_count";
|
||||||
public static final String KEY_LAYOUT_NOTIF_MIDDLE_SIDE = "layout_notif_middle_side";
|
public static final String KEY_LAYOUT_NOTIF_MIDDLE_SIDE = "layout_notif_middle_side";
|
||||||
public static final String KEY_LAYOUT_NOTIF_MIDDLE_AUTO_NEAREST_SIDE = "layout_notif_middle_auto_nearest_side";
|
public static final String KEY_LAYOUT_NOTIF_MIDDLE_AUTO_NEAREST_SIDE = "layout_notif_middle_auto_nearest_side";
|
||||||
public static final String KEY_LAYOUT_NOTIF_VERTICAL_OFFSET_PX = "layout_notif_vertical_offset_px";
|
public static final String KEY_LAYOUT_NOTIF_VERTICAL_OFFSET_PX = "layout_notif_vertical_offset_px";
|
||||||
@@ -108,6 +115,7 @@ public final class SbtSettings {
|
|||||||
public static final String KEY_LAYOUT_STATUS_ENABLED_AOD = "layout_status_enabled_aod";
|
public static final String KEY_LAYOUT_STATUS_ENABLED_AOD = "layout_status_enabled_aod";
|
||||||
public static final String KEY_LAYOUT_STATUS_ENABLED_LOCKSCREEN = "layout_status_enabled_lockscreen";
|
public static final String KEY_LAYOUT_STATUS_ENABLED_LOCKSCREEN = "layout_status_enabled_lockscreen";
|
||||||
public static final String KEY_LAYOUT_STATUS_ENABLED_UNLOCKED = "layout_status_enabled_unlocked";
|
public static final String KEY_LAYOUT_STATUS_ENABLED_UNLOCKED = "layout_status_enabled_unlocked";
|
||||||
|
public static final String KEY_LAYOUT_STATUS_UNLOCKED_COUNT = "layout_status_unlocked_count";
|
||||||
public static final String KEY_LAYOUT_STATUS_SHOW_DOT_IF_TRUNCATED = "layout_status_show_dot_if_truncated";
|
public static final String KEY_LAYOUT_STATUS_SHOW_DOT_IF_TRUNCATED = "layout_status_show_dot_if_truncated";
|
||||||
public static final String KEY_LAYOUT_STATUS_MIDDLE_SIDE = "layout_status_middle_side";
|
public static final String KEY_LAYOUT_STATUS_MIDDLE_SIDE = "layout_status_middle_side";
|
||||||
public static final String KEY_LAYOUT_STATUS_MIDDLE_AUTO_NEAREST_SIDE = "layout_status_middle_auto_nearest_side";
|
public static final String KEY_LAYOUT_STATUS_MIDDLE_AUTO_NEAREST_SIDE = "layout_status_middle_auto_nearest_side";
|
||||||
@@ -116,6 +124,42 @@ public final class SbtSettings {
|
|||||||
public static final String KEY_SYSTEM_ICON_HIDE_SLOTS = "system_icon_hide_slots";
|
public static final String KEY_SYSTEM_ICON_HIDE_SLOTS = "system_icon_hide_slots";
|
||||||
public static final String KEY_APP_ICON_BLOCKED_MODES = AppIconRules.PREF_KEY_BLOCKED_MODES;
|
public static final String KEY_APP_ICON_BLOCKED_MODES = AppIconRules.PREF_KEY_BLOCKED_MODES;
|
||||||
public static final String KEY_APP_ICON_EXCEPTION_RULES = AppIconRules.PREF_KEY_EXCEPTION_RULES;
|
public static final String KEY_APP_ICON_EXCEPTION_RULES = AppIconRules.PREF_KEY_EXCEPTION_RULES;
|
||||||
|
|
||||||
|
public static String layoutNotifPositionKey(int index) {
|
||||||
|
return suffixedKey(KEY_LAYOUT_NOTIF_POSITION, index);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String layoutNotifMiddleSideKey(int index) {
|
||||||
|
return suffixedKey(KEY_LAYOUT_NOTIF_MIDDLE_SIDE, index);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String layoutNotifMiddleAutoNearestSideKey(int index) {
|
||||||
|
return suffixedKey(KEY_LAYOUT_NOTIF_MIDDLE_AUTO_NEAREST_SIDE, index);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String layoutNotifVerticalOffsetKey(int index) {
|
||||||
|
return suffixedKey(KEY_LAYOUT_NOTIF_VERTICAL_OFFSET_PX, index);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String layoutStatusPositionKey(int index) {
|
||||||
|
return suffixedKey(KEY_LAYOUT_STATUS_POSITION, index);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String layoutStatusMiddleSideKey(int index) {
|
||||||
|
return suffixedKey(KEY_LAYOUT_STATUS_MIDDLE_SIDE, index);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String layoutStatusMiddleAutoNearestSideKey(int index) {
|
||||||
|
return suffixedKey(KEY_LAYOUT_STATUS_MIDDLE_AUTO_NEAREST_SIDE, index);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String layoutStatusVerticalOffsetKey(int index) {
|
||||||
|
return suffixedKey(KEY_LAYOUT_STATUS_VERTICAL_OFFSET_PX, index);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String suffixedKey(String base, int index) {
|
||||||
|
return index <= 0 ? base : base + "_" + (index + 1);
|
||||||
|
}
|
||||||
public final boolean enableAodCutoutLimit;
|
public final boolean enableAodCutoutLimit;
|
||||||
public final boolean enableLockCutoutLimit;
|
public final boolean enableLockCutoutLimit;
|
||||||
public final int unlockedMaxIconsPerRow;
|
public final int unlockedMaxIconsPerRow;
|
||||||
@@ -180,6 +224,11 @@ public final class SbtSettings {
|
|||||||
public final boolean layoutNotifEnabledAod;
|
public final boolean layoutNotifEnabledAod;
|
||||||
public final boolean layoutNotifEnabledLockscreen;
|
public final boolean layoutNotifEnabledLockscreen;
|
||||||
public final boolean layoutNotifEnabledUnlocked;
|
public final boolean layoutNotifEnabledUnlocked;
|
||||||
|
public final int layoutNotifUnlockedCount;
|
||||||
|
public final String[] layoutNotifPositions;
|
||||||
|
public final String[] layoutNotifMiddleSides;
|
||||||
|
public final boolean[] layoutNotifMiddleAutoNearestSides;
|
||||||
|
public final int[] layoutNotifVerticalOffsetsPx;
|
||||||
public final String layoutNotifMiddleSide;
|
public final String layoutNotifMiddleSide;
|
||||||
public final boolean layoutNotifMiddleAutoNearestSide;
|
public final boolean layoutNotifMiddleAutoNearestSide;
|
||||||
public final int layoutNotifVerticalOffsetPx;
|
public final int layoutNotifVerticalOffsetPx;
|
||||||
@@ -187,6 +236,11 @@ public final class SbtSettings {
|
|||||||
public final boolean layoutStatusEnabledAod;
|
public final boolean layoutStatusEnabledAod;
|
||||||
public final boolean layoutStatusEnabledLockscreen;
|
public final boolean layoutStatusEnabledLockscreen;
|
||||||
public final boolean layoutStatusEnabledUnlocked;
|
public final boolean layoutStatusEnabledUnlocked;
|
||||||
|
public final int layoutStatusUnlockedCount;
|
||||||
|
public final String[] layoutStatusPositions;
|
||||||
|
public final String[] layoutStatusMiddleSides;
|
||||||
|
public final boolean[] layoutStatusMiddleAutoNearestSides;
|
||||||
|
public final int[] layoutStatusVerticalOffsetsPx;
|
||||||
public final boolean layoutStatusShowDotIfTruncated;
|
public final boolean layoutStatusShowDotIfTruncated;
|
||||||
public final String layoutStatusMiddleSide;
|
public final String layoutStatusMiddleSide;
|
||||||
public final boolean layoutStatusMiddleAutoNearestSide;
|
public final boolean layoutStatusMiddleAutoNearestSide;
|
||||||
@@ -272,6 +326,11 @@ public final class SbtSettings {
|
|||||||
String layoutNotifMiddleSide,
|
String layoutNotifMiddleSide,
|
||||||
boolean layoutNotifMiddleAutoNearestSide,
|
boolean layoutNotifMiddleAutoNearestSide,
|
||||||
int layoutNotifVerticalOffsetPx,
|
int layoutNotifVerticalOffsetPx,
|
||||||
|
int layoutNotifUnlockedCount,
|
||||||
|
String[] layoutNotifPositions,
|
||||||
|
String[] layoutNotifMiddleSides,
|
||||||
|
boolean[] layoutNotifMiddleAutoNearestSides,
|
||||||
|
int[] layoutNotifVerticalOffsetsPx,
|
||||||
String layoutStatusPosition,
|
String layoutStatusPosition,
|
||||||
boolean layoutStatusEnabledAod,
|
boolean layoutStatusEnabledAod,
|
||||||
boolean layoutStatusEnabledLockscreen,
|
boolean layoutStatusEnabledLockscreen,
|
||||||
@@ -280,6 +339,11 @@ public final class SbtSettings {
|
|||||||
String layoutStatusMiddleSide,
|
String layoutStatusMiddleSide,
|
||||||
boolean layoutStatusMiddleAutoNearestSide,
|
boolean layoutStatusMiddleAutoNearestSide,
|
||||||
int layoutStatusVerticalOffsetPx,
|
int layoutStatusVerticalOffsetPx,
|
||||||
|
int layoutStatusUnlockedCount,
|
||||||
|
String[] layoutStatusPositions,
|
||||||
|
String[] layoutStatusMiddleSides,
|
||||||
|
boolean[] layoutStatusMiddleAutoNearestSides,
|
||||||
|
int[] layoutStatusVerticalOffsetsPx,
|
||||||
Map<String, String> clockCameraTypeOverrides,
|
Map<String, String> clockCameraTypeOverrides,
|
||||||
Map<String, Integer> systemIconBlockedModes,
|
Map<String, Integer> systemIconBlockedModes,
|
||||||
boolean statusChipsHideAll,
|
boolean statusChipsHideAll,
|
||||||
@@ -424,6 +488,27 @@ public final class SbtSettings {
|
|||||||
layoutNotifVerticalOffsetPx,
|
layoutNotifVerticalOffsetPx,
|
||||||
SbtDefaults.CLOCK_VERTICAL_OFFSET_PX_MIN,
|
SbtDefaults.CLOCK_VERTICAL_OFFSET_PX_MIN,
|
||||||
SbtDefaults.CLOCK_VERTICAL_OFFSET_PX_MAX);
|
SbtDefaults.CLOCK_VERTICAL_OFFSET_PX_MAX);
|
||||||
|
this.layoutNotifUnlockedCount = Math.max(
|
||||||
|
SbtDefaults.LAYOUT_ICON_CONTAINER_COUNT_MIN,
|
||||||
|
Math.min(SbtDefaults.LAYOUT_ICON_CONTAINER_COUNT_MAX, layoutNotifUnlockedCount));
|
||||||
|
this.layoutNotifPositions = sanitizeStringArray(
|
||||||
|
layoutNotifPositions,
|
||||||
|
this.layoutNotifPosition,
|
||||||
|
SbtDefaults.LAYOUT_ICON_CONTAINER_COUNT_MAX);
|
||||||
|
this.layoutNotifMiddleSides = sanitizeStringArray(
|
||||||
|
layoutNotifMiddleSides,
|
||||||
|
this.layoutNotifMiddleSide,
|
||||||
|
SbtDefaults.LAYOUT_ICON_CONTAINER_COUNT_MAX);
|
||||||
|
this.layoutNotifMiddleAutoNearestSides = sanitizeBooleanArray(
|
||||||
|
layoutNotifMiddleAutoNearestSides,
|
||||||
|
this.layoutNotifMiddleAutoNearestSide,
|
||||||
|
SbtDefaults.LAYOUT_ICON_CONTAINER_COUNT_MAX);
|
||||||
|
this.layoutNotifVerticalOffsetsPx = sanitizeIntArray(
|
||||||
|
layoutNotifVerticalOffsetsPx,
|
||||||
|
this.layoutNotifVerticalOffsetPx,
|
||||||
|
SbtDefaults.LAYOUT_ICON_CONTAINER_COUNT_MAX,
|
||||||
|
SbtDefaults.CLOCK_VERTICAL_OFFSET_PX_MIN,
|
||||||
|
SbtDefaults.CLOCK_VERTICAL_OFFSET_PX_MAX);
|
||||||
this.layoutStatusPosition = layoutStatusPosition != null
|
this.layoutStatusPosition = layoutStatusPosition != null
|
||||||
? layoutStatusPosition
|
? layoutStatusPosition
|
||||||
: SbtDefaults.LAYOUT_STATUS_POSITION_DEFAULT;
|
: SbtDefaults.LAYOUT_STATUS_POSITION_DEFAULT;
|
||||||
@@ -439,6 +524,27 @@ public final class SbtSettings {
|
|||||||
layoutStatusVerticalOffsetPx,
|
layoutStatusVerticalOffsetPx,
|
||||||
SbtDefaults.CLOCK_VERTICAL_OFFSET_PX_MIN,
|
SbtDefaults.CLOCK_VERTICAL_OFFSET_PX_MIN,
|
||||||
SbtDefaults.CLOCK_VERTICAL_OFFSET_PX_MAX);
|
SbtDefaults.CLOCK_VERTICAL_OFFSET_PX_MAX);
|
||||||
|
this.layoutStatusUnlockedCount = Math.max(
|
||||||
|
SbtDefaults.LAYOUT_ICON_CONTAINER_COUNT_MIN,
|
||||||
|
Math.min(SbtDefaults.LAYOUT_ICON_CONTAINER_COUNT_MAX, layoutStatusUnlockedCount));
|
||||||
|
this.layoutStatusPositions = sanitizeStringArray(
|
||||||
|
layoutStatusPositions,
|
||||||
|
this.layoutStatusPosition,
|
||||||
|
SbtDefaults.LAYOUT_ICON_CONTAINER_COUNT_MAX);
|
||||||
|
this.layoutStatusMiddleSides = sanitizeStringArray(
|
||||||
|
layoutStatusMiddleSides,
|
||||||
|
this.layoutStatusMiddleSide,
|
||||||
|
SbtDefaults.LAYOUT_ICON_CONTAINER_COUNT_MAX);
|
||||||
|
this.layoutStatusMiddleAutoNearestSides = sanitizeBooleanArray(
|
||||||
|
layoutStatusMiddleAutoNearestSides,
|
||||||
|
this.layoutStatusMiddleAutoNearestSide,
|
||||||
|
SbtDefaults.LAYOUT_ICON_CONTAINER_COUNT_MAX);
|
||||||
|
this.layoutStatusVerticalOffsetsPx = sanitizeIntArray(
|
||||||
|
layoutStatusVerticalOffsetsPx,
|
||||||
|
this.layoutStatusVerticalOffsetPx,
|
||||||
|
SbtDefaults.LAYOUT_ICON_CONTAINER_COUNT_MAX,
|
||||||
|
SbtDefaults.CLOCK_VERTICAL_OFFSET_PX_MIN,
|
||||||
|
SbtDefaults.CLOCK_VERTICAL_OFFSET_PX_MAX);
|
||||||
this.clockCameraTypeOverrides = clockCameraTypeOverrides != null
|
this.clockCameraTypeOverrides = clockCameraTypeOverrides != null
|
||||||
? Collections.unmodifiableMap(new HashMap<>(clockCameraTypeOverrides))
|
? Collections.unmodifiableMap(new HashMap<>(clockCameraTypeOverrides))
|
||||||
: Collections.emptyMap();
|
: Collections.emptyMap();
|
||||||
@@ -540,6 +646,11 @@ public final class SbtSettings {
|
|||||||
SbtDefaults.LAYOUT_NOTIF_MIDDLE_SIDE_DEFAULT,
|
SbtDefaults.LAYOUT_NOTIF_MIDDLE_SIDE_DEFAULT,
|
||||||
SbtDefaults.LAYOUT_NOTIF_MIDDLE_AUTO_NEAREST_SIDE_DEFAULT,
|
SbtDefaults.LAYOUT_NOTIF_MIDDLE_AUTO_NEAREST_SIDE_DEFAULT,
|
||||||
SbtDefaults.LAYOUT_NOTIF_VERTICAL_OFFSET_PX_DEFAULT,
|
SbtDefaults.LAYOUT_NOTIF_VERTICAL_OFFSET_PX_DEFAULT,
|
||||||
|
SbtDefaults.LAYOUT_NOTIF_UNLOCKED_COUNT_DEFAULT,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
SbtDefaults.LAYOUT_STATUS_POSITION_DEFAULT,
|
SbtDefaults.LAYOUT_STATUS_POSITION_DEFAULT,
|
||||||
SbtDefaults.LAYOUT_STATUS_ENABLED_AOD_DEFAULT,
|
SbtDefaults.LAYOUT_STATUS_ENABLED_AOD_DEFAULT,
|
||||||
SbtDefaults.LAYOUT_STATUS_ENABLED_LOCKSCREEN_DEFAULT,
|
SbtDefaults.LAYOUT_STATUS_ENABLED_LOCKSCREEN_DEFAULT,
|
||||||
@@ -548,6 +659,11 @@ public final class SbtSettings {
|
|||||||
SbtDefaults.LAYOUT_STATUS_MIDDLE_SIDE_DEFAULT,
|
SbtDefaults.LAYOUT_STATUS_MIDDLE_SIDE_DEFAULT,
|
||||||
SbtDefaults.LAYOUT_STATUS_MIDDLE_AUTO_NEAREST_SIDE_DEFAULT,
|
SbtDefaults.LAYOUT_STATUS_MIDDLE_AUTO_NEAREST_SIDE_DEFAULT,
|
||||||
SbtDefaults.LAYOUT_STATUS_VERTICAL_OFFSET_PX_DEFAULT,
|
SbtDefaults.LAYOUT_STATUS_VERTICAL_OFFSET_PX_DEFAULT,
|
||||||
|
SbtDefaults.LAYOUT_STATUS_UNLOCKED_COUNT_DEFAULT,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
Collections.emptyMap(),
|
Collections.emptyMap(),
|
||||||
Collections.emptyMap(),
|
Collections.emptyMap(),
|
||||||
false,
|
false,
|
||||||
@@ -787,6 +903,28 @@ public final class SbtSettings {
|
|||||||
SbtDefaults.LAYOUT_NOTIF_VERTICAL_OFFSET_PX_DEFAULT),
|
SbtDefaults.LAYOUT_NOTIF_VERTICAL_OFFSET_PX_DEFAULT),
|
||||||
SbtDefaults.CLOCK_VERTICAL_OFFSET_PX_MIN,
|
SbtDefaults.CLOCK_VERTICAL_OFFSET_PX_MIN,
|
||||||
SbtDefaults.CLOCK_VERTICAL_OFFSET_PX_MAX),
|
SbtDefaults.CLOCK_VERTICAL_OFFSET_PX_MAX),
|
||||||
|
readIconContainerCount(
|
||||||
|
prefs,
|
||||||
|
KEY_LAYOUT_NOTIF_UNLOCKED_COUNT,
|
||||||
|
KEY_LAYOUT_NOTIF_ENABLED_UNLOCKED,
|
||||||
|
SbtDefaults.LAYOUT_NOTIF_UNLOCKED_COUNT_DEFAULT,
|
||||||
|
SbtDefaults.LAYOUT_NOTIF_ENABLED_UNLOCKED_DEFAULT),
|
||||||
|
readStringArrayFromPrefs(
|
||||||
|
prefs,
|
||||||
|
SbtSettings::layoutNotifPositionKey,
|
||||||
|
SbtDefaults.LAYOUT_NOTIF_POSITION_DEFAULT),
|
||||||
|
readStringArrayFromPrefs(
|
||||||
|
prefs,
|
||||||
|
SbtSettings::layoutNotifMiddleSideKey,
|
||||||
|
SbtDefaults.LAYOUT_NOTIF_MIDDLE_SIDE_DEFAULT),
|
||||||
|
readBooleanArrayFromPrefs(
|
||||||
|
prefs,
|
||||||
|
SbtSettings::layoutNotifMiddleAutoNearestSideKey,
|
||||||
|
SbtDefaults.LAYOUT_NOTIF_MIDDLE_AUTO_NEAREST_SIDE_DEFAULT),
|
||||||
|
readIntArrayFromPrefs(
|
||||||
|
prefs,
|
||||||
|
SbtSettings::layoutNotifVerticalOffsetKey,
|
||||||
|
SbtDefaults.LAYOUT_NOTIF_VERTICAL_OFFSET_PX_DEFAULT),
|
||||||
prefs.getString(KEY_LAYOUT_STATUS_POSITION, SbtDefaults.LAYOUT_STATUS_POSITION_DEFAULT),
|
prefs.getString(KEY_LAYOUT_STATUS_POSITION, SbtDefaults.LAYOUT_STATUS_POSITION_DEFAULT),
|
||||||
prefs.getBoolean(KEY_LAYOUT_STATUS_ENABLED_AOD, SbtDefaults.LAYOUT_STATUS_ENABLED_AOD_DEFAULT),
|
prefs.getBoolean(KEY_LAYOUT_STATUS_ENABLED_AOD, SbtDefaults.LAYOUT_STATUS_ENABLED_AOD_DEFAULT),
|
||||||
prefs.getBoolean(KEY_LAYOUT_STATUS_ENABLED_LOCKSCREEN, SbtDefaults.LAYOUT_STATUS_ENABLED_LOCKSCREEN_DEFAULT),
|
prefs.getBoolean(KEY_LAYOUT_STATUS_ENABLED_LOCKSCREEN, SbtDefaults.LAYOUT_STATUS_ENABLED_LOCKSCREEN_DEFAULT),
|
||||||
@@ -798,6 +936,28 @@ public final class SbtSettings {
|
|||||||
SbtDefaults.LAYOUT_STATUS_VERTICAL_OFFSET_PX_DEFAULT),
|
SbtDefaults.LAYOUT_STATUS_VERTICAL_OFFSET_PX_DEFAULT),
|
||||||
SbtDefaults.CLOCK_VERTICAL_OFFSET_PX_MIN,
|
SbtDefaults.CLOCK_VERTICAL_OFFSET_PX_MIN,
|
||||||
SbtDefaults.CLOCK_VERTICAL_OFFSET_PX_MAX),
|
SbtDefaults.CLOCK_VERTICAL_OFFSET_PX_MAX),
|
||||||
|
readIconContainerCount(
|
||||||
|
prefs,
|
||||||
|
KEY_LAYOUT_STATUS_UNLOCKED_COUNT,
|
||||||
|
KEY_LAYOUT_STATUS_ENABLED_UNLOCKED,
|
||||||
|
SbtDefaults.LAYOUT_STATUS_UNLOCKED_COUNT_DEFAULT,
|
||||||
|
SbtDefaults.LAYOUT_STATUS_ENABLED_UNLOCKED_DEFAULT),
|
||||||
|
readStringArrayFromPrefs(
|
||||||
|
prefs,
|
||||||
|
SbtSettings::layoutStatusPositionKey,
|
||||||
|
SbtDefaults.LAYOUT_STATUS_POSITION_DEFAULT),
|
||||||
|
readStringArrayFromPrefs(
|
||||||
|
prefs,
|
||||||
|
SbtSettings::layoutStatusMiddleSideKey,
|
||||||
|
SbtDefaults.LAYOUT_STATUS_MIDDLE_SIDE_DEFAULT),
|
||||||
|
readBooleanArrayFromPrefs(
|
||||||
|
prefs,
|
||||||
|
SbtSettings::layoutStatusMiddleAutoNearestSideKey,
|
||||||
|
SbtDefaults.LAYOUT_STATUS_MIDDLE_AUTO_NEAREST_SIDE_DEFAULT),
|
||||||
|
readIntArrayFromPrefs(
|
||||||
|
prefs,
|
||||||
|
SbtSettings::layoutStatusVerticalOffsetKey,
|
||||||
|
SbtDefaults.LAYOUT_STATUS_VERTICAL_OFFSET_PX_DEFAULT),
|
||||||
ClockCameraTypeSupport.parseOverrides(copyStringSet(
|
ClockCameraTypeSupport.parseOverrides(copyStringSet(
|
||||||
prefs.getStringSet(KEY_CLOCK_CAMERA_TYPE_OVERRIDES, Collections.emptySet()))),
|
prefs.getStringSet(KEY_CLOCK_CAMERA_TYPE_OVERRIDES, Collections.emptySet()))),
|
||||||
systemIconBlockedModes,
|
systemIconBlockedModes,
|
||||||
@@ -940,6 +1100,28 @@ public final class SbtSettings {
|
|||||||
SbtDefaults.LAYOUT_NOTIF_VERTICAL_OFFSET_PX_DEFAULT),
|
SbtDefaults.LAYOUT_NOTIF_VERTICAL_OFFSET_PX_DEFAULT),
|
||||||
SbtDefaults.CLOCK_VERTICAL_OFFSET_PX_MIN,
|
SbtDefaults.CLOCK_VERTICAL_OFFSET_PX_MIN,
|
||||||
SbtDefaults.CLOCK_VERTICAL_OFFSET_PX_MAX),
|
SbtDefaults.CLOCK_VERTICAL_OFFSET_PX_MAX),
|
||||||
|
readIconContainerCount(
|
||||||
|
bundle,
|
||||||
|
KEY_LAYOUT_NOTIF_UNLOCKED_COUNT,
|
||||||
|
KEY_LAYOUT_NOTIF_ENABLED_UNLOCKED,
|
||||||
|
SbtDefaults.LAYOUT_NOTIF_UNLOCKED_COUNT_DEFAULT,
|
||||||
|
SbtDefaults.LAYOUT_NOTIF_ENABLED_UNLOCKED_DEFAULT),
|
||||||
|
readStringArrayFromBundle(
|
||||||
|
bundle,
|
||||||
|
SbtSettings::layoutNotifPositionKey,
|
||||||
|
SbtDefaults.LAYOUT_NOTIF_POSITION_DEFAULT),
|
||||||
|
readStringArrayFromBundle(
|
||||||
|
bundle,
|
||||||
|
SbtSettings::layoutNotifMiddleSideKey,
|
||||||
|
SbtDefaults.LAYOUT_NOTIF_MIDDLE_SIDE_DEFAULT),
|
||||||
|
readBooleanArrayFromBundle(
|
||||||
|
bundle,
|
||||||
|
SbtSettings::layoutNotifMiddleAutoNearestSideKey,
|
||||||
|
SbtDefaults.LAYOUT_NOTIF_MIDDLE_AUTO_NEAREST_SIDE_DEFAULT),
|
||||||
|
readIntArrayFromBundle(
|
||||||
|
bundle,
|
||||||
|
SbtSettings::layoutNotifVerticalOffsetKey,
|
||||||
|
SbtDefaults.LAYOUT_NOTIF_VERTICAL_OFFSET_PX_DEFAULT),
|
||||||
bundle.getString(KEY_LAYOUT_STATUS_POSITION, SbtDefaults.LAYOUT_STATUS_POSITION_DEFAULT),
|
bundle.getString(KEY_LAYOUT_STATUS_POSITION, SbtDefaults.LAYOUT_STATUS_POSITION_DEFAULT),
|
||||||
bundle.getBoolean(KEY_LAYOUT_STATUS_ENABLED_AOD, SbtDefaults.LAYOUT_STATUS_ENABLED_AOD_DEFAULT),
|
bundle.getBoolean(KEY_LAYOUT_STATUS_ENABLED_AOD, SbtDefaults.LAYOUT_STATUS_ENABLED_AOD_DEFAULT),
|
||||||
bundle.getBoolean(KEY_LAYOUT_STATUS_ENABLED_LOCKSCREEN, SbtDefaults.LAYOUT_STATUS_ENABLED_LOCKSCREEN_DEFAULT),
|
bundle.getBoolean(KEY_LAYOUT_STATUS_ENABLED_LOCKSCREEN, SbtDefaults.LAYOUT_STATUS_ENABLED_LOCKSCREEN_DEFAULT),
|
||||||
@@ -951,6 +1133,28 @@ public final class SbtSettings {
|
|||||||
SbtDefaults.LAYOUT_STATUS_VERTICAL_OFFSET_PX_DEFAULT),
|
SbtDefaults.LAYOUT_STATUS_VERTICAL_OFFSET_PX_DEFAULT),
|
||||||
SbtDefaults.CLOCK_VERTICAL_OFFSET_PX_MIN,
|
SbtDefaults.CLOCK_VERTICAL_OFFSET_PX_MIN,
|
||||||
SbtDefaults.CLOCK_VERTICAL_OFFSET_PX_MAX),
|
SbtDefaults.CLOCK_VERTICAL_OFFSET_PX_MAX),
|
||||||
|
readIconContainerCount(
|
||||||
|
bundle,
|
||||||
|
KEY_LAYOUT_STATUS_UNLOCKED_COUNT,
|
||||||
|
KEY_LAYOUT_STATUS_ENABLED_UNLOCKED,
|
||||||
|
SbtDefaults.LAYOUT_STATUS_UNLOCKED_COUNT_DEFAULT,
|
||||||
|
SbtDefaults.LAYOUT_STATUS_ENABLED_UNLOCKED_DEFAULT),
|
||||||
|
readStringArrayFromBundle(
|
||||||
|
bundle,
|
||||||
|
SbtSettings::layoutStatusPositionKey,
|
||||||
|
SbtDefaults.LAYOUT_STATUS_POSITION_DEFAULT),
|
||||||
|
readStringArrayFromBundle(
|
||||||
|
bundle,
|
||||||
|
SbtSettings::layoutStatusMiddleSideKey,
|
||||||
|
SbtDefaults.LAYOUT_STATUS_MIDDLE_SIDE_DEFAULT),
|
||||||
|
readBooleanArrayFromBundle(
|
||||||
|
bundle,
|
||||||
|
SbtSettings::layoutStatusMiddleAutoNearestSideKey,
|
||||||
|
SbtDefaults.LAYOUT_STATUS_MIDDLE_AUTO_NEAREST_SIDE_DEFAULT),
|
||||||
|
readIntArrayFromBundle(
|
||||||
|
bundle,
|
||||||
|
SbtSettings::layoutStatusVerticalOffsetKey,
|
||||||
|
SbtDefaults.LAYOUT_STATUS_VERTICAL_OFFSET_PX_DEFAULT),
|
||||||
ClockCameraTypeSupport.parseOverrides(readStringSetFromBundle(bundle, KEY_CLOCK_CAMERA_TYPE_OVERRIDES)),
|
ClockCameraTypeSupport.parseOverrides(readStringSetFromBundle(bundle, KEY_CLOCK_CAMERA_TYPE_OVERRIDES)),
|
||||||
systemIconBlockedModes,
|
systemIconBlockedModes,
|
||||||
bundle.getBoolean(KEY_STATUS_CHIPS_HIDE_ALL, false),
|
bundle.getBoolean(KEY_STATUS_CHIPS_HIDE_ALL, false),
|
||||||
@@ -985,6 +1189,116 @@ public final class SbtSettings {
|
|||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static int readIconContainerCount(
|
||||||
|
SharedPreferences prefs,
|
||||||
|
String countKey,
|
||||||
|
String legacyEnabledKey,
|
||||||
|
int defaultCount,
|
||||||
|
boolean legacyEnabledDefault
|
||||||
|
) {
|
||||||
|
int fallback = prefs.getBoolean(legacyEnabledKey, legacyEnabledDefault) ? defaultCount : 0;
|
||||||
|
int value = prefs.getInt(countKey, fallback);
|
||||||
|
return Math.max(
|
||||||
|
SbtDefaults.LAYOUT_ICON_CONTAINER_COUNT_MIN,
|
||||||
|
Math.min(SbtDefaults.LAYOUT_ICON_CONTAINER_COUNT_MAX, value));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int readIconContainerCount(
|
||||||
|
Bundle bundle,
|
||||||
|
String countKey,
|
||||||
|
String legacyEnabledKey,
|
||||||
|
int defaultCount,
|
||||||
|
boolean legacyEnabledDefault
|
||||||
|
) {
|
||||||
|
int fallback = bundle.getBoolean(legacyEnabledKey, legacyEnabledDefault) ? defaultCount : 0;
|
||||||
|
int value = bundle.getInt(countKey, fallback);
|
||||||
|
return Math.max(
|
||||||
|
SbtDefaults.LAYOUT_ICON_CONTAINER_COUNT_MIN,
|
||||||
|
Math.min(SbtDefaults.LAYOUT_ICON_CONTAINER_COUNT_MAX, value));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String[] readStringArrayFromPrefs(
|
||||||
|
SharedPreferences prefs,
|
||||||
|
IndexedKey key,
|
||||||
|
String fallback
|
||||||
|
) {
|
||||||
|
String[] result = new String[SbtDefaults.LAYOUT_ICON_CONTAINER_COUNT_MAX];
|
||||||
|
for (int i = 0; i < result.length; i++) {
|
||||||
|
result[i] = prefs.getString(key.key(i), fallback);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean[] readBooleanArrayFromPrefs(
|
||||||
|
SharedPreferences prefs,
|
||||||
|
IndexedKey key,
|
||||||
|
boolean fallback
|
||||||
|
) {
|
||||||
|
boolean[] result = new boolean[SbtDefaults.LAYOUT_ICON_CONTAINER_COUNT_MAX];
|
||||||
|
for (int i = 0; i < result.length; i++) {
|
||||||
|
result[i] = prefs.getBoolean(key.key(i), fallback);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int[] readIntArrayFromPrefs(SharedPreferences prefs, IndexedKey key, int fallback) {
|
||||||
|
int[] result = new int[SbtDefaults.LAYOUT_ICON_CONTAINER_COUNT_MAX];
|
||||||
|
for (int i = 0; i < result.length; i++) {
|
||||||
|
result[i] = prefs.getInt(key.key(i), fallback);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String[] readStringArrayFromBundle(Bundle bundle, IndexedKey key, String fallback) {
|
||||||
|
String[] result = new String[SbtDefaults.LAYOUT_ICON_CONTAINER_COUNT_MAX];
|
||||||
|
for (int i = 0; i < result.length; i++) {
|
||||||
|
result[i] = bundle.getString(key.key(i), fallback);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean[] readBooleanArrayFromBundle(Bundle bundle, IndexedKey key, boolean fallback) {
|
||||||
|
boolean[] result = new boolean[SbtDefaults.LAYOUT_ICON_CONTAINER_COUNT_MAX];
|
||||||
|
for (int i = 0; i < result.length; i++) {
|
||||||
|
result[i] = bundle.getBoolean(key.key(i), fallback);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int[] readIntArrayFromBundle(Bundle bundle, IndexedKey key, int fallback) {
|
||||||
|
int[] result = new int[SbtDefaults.LAYOUT_ICON_CONTAINER_COUNT_MAX];
|
||||||
|
for (int i = 0; i < result.length; i++) {
|
||||||
|
result[i] = bundle.getInt(key.key(i), fallback);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String[] sanitizeStringArray(String[] source, String fallback, int length) {
|
||||||
|
String[] result = new String[Math.max(0, length)];
|
||||||
|
for (int i = 0; i < result.length; i++) {
|
||||||
|
String value = source != null && i < source.length ? source[i] : null;
|
||||||
|
result[i] = value != null ? value : fallback;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean[] sanitizeBooleanArray(boolean[] source, boolean fallback, int length) {
|
||||||
|
boolean[] result = new boolean[Math.max(0, length)];
|
||||||
|
for (int i = 0; i < result.length; i++) {
|
||||||
|
result[i] = source != null && i < source.length ? source[i] : fallback;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int[] sanitizeIntArray(int[] source, int fallback, int length, int min, int max) {
|
||||||
|
int[] result = new int[Math.max(0, length)];
|
||||||
|
for (int i = 0; i < result.length; i++) {
|
||||||
|
int value = source != null && i < source.length ? source[i] : fallback;
|
||||||
|
result[i] = clampInt(value, min, max);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
private static Set<String> copyStringSet(Set<String> source) {
|
private static Set<String> copyStringSet(Set<String> source) {
|
||||||
if (source == null || source.isEmpty()) {
|
if (source == null || source.isEmpty()) {
|
||||||
return Collections.emptySet();
|
return Collections.emptySet();
|
||||||
|
|||||||
@@ -12,6 +12,10 @@ import java.util.ArrayList;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
public class SbtSettingsProvider extends ContentProvider {
|
public class SbtSettingsProvider extends ContentProvider {
|
||||||
|
private interface IndexedKey {
|
||||||
|
String key(int index);
|
||||||
|
}
|
||||||
|
|
||||||
public static final String AUTHORITY = "se.ajpanton.statusbartweak.settings";
|
public static final String AUTHORITY = "se.ajpanton.statusbartweak.settings";
|
||||||
public static final String METHOD_GET_UNLOCKED = "get_unlocked";
|
public static final String METHOD_GET_UNLOCKED = "get_unlocked";
|
||||||
public static final String METHOD_GET_ALL = "get_all";
|
public static final String METHOD_GET_ALL = "get_all";
|
||||||
@@ -214,6 +218,13 @@ public class SbtSettingsProvider extends ContentProvider {
|
|||||||
out.putBoolean(SbtSettings.KEY_LAYOUT_NOTIF_ENABLED_UNLOCKED,
|
out.putBoolean(SbtSettings.KEY_LAYOUT_NOTIF_ENABLED_UNLOCKED,
|
||||||
prefs.getBoolean(SbtSettings.KEY_LAYOUT_NOTIF_ENABLED_UNLOCKED,
|
prefs.getBoolean(SbtSettings.KEY_LAYOUT_NOTIF_ENABLED_UNLOCKED,
|
||||||
SbtDefaults.LAYOUT_NOTIF_ENABLED_UNLOCKED_DEFAULT));
|
SbtDefaults.LAYOUT_NOTIF_ENABLED_UNLOCKED_DEFAULT));
|
||||||
|
out.putInt(SbtSettings.KEY_LAYOUT_NOTIF_UNLOCKED_COUNT,
|
||||||
|
prefs.getInt(SbtSettings.KEY_LAYOUT_NOTIF_UNLOCKED_COUNT,
|
||||||
|
prefs.getBoolean(
|
||||||
|
SbtSettings.KEY_LAYOUT_NOTIF_ENABLED_UNLOCKED,
|
||||||
|
SbtDefaults.LAYOUT_NOTIF_ENABLED_UNLOCKED_DEFAULT)
|
||||||
|
? SbtDefaults.LAYOUT_NOTIF_UNLOCKED_COUNT_DEFAULT
|
||||||
|
: 0));
|
||||||
out.putString(SbtSettings.KEY_LAYOUT_NOTIF_MIDDLE_SIDE,
|
out.putString(SbtSettings.KEY_LAYOUT_NOTIF_MIDDLE_SIDE,
|
||||||
prefs.getString(SbtSettings.KEY_LAYOUT_NOTIF_MIDDLE_SIDE,
|
prefs.getString(SbtSettings.KEY_LAYOUT_NOTIF_MIDDLE_SIDE,
|
||||||
SbtDefaults.LAYOUT_NOTIF_MIDDLE_SIDE_DEFAULT));
|
SbtDefaults.LAYOUT_NOTIF_MIDDLE_SIDE_DEFAULT));
|
||||||
@@ -223,6 +234,17 @@ public class SbtSettingsProvider extends ContentProvider {
|
|||||||
out.putInt(SbtSettings.KEY_LAYOUT_NOTIF_VERTICAL_OFFSET_PX,
|
out.putInt(SbtSettings.KEY_LAYOUT_NOTIF_VERTICAL_OFFSET_PX,
|
||||||
prefs.getInt(SbtSettings.KEY_LAYOUT_NOTIF_VERTICAL_OFFSET_PX,
|
prefs.getInt(SbtSettings.KEY_LAYOUT_NOTIF_VERTICAL_OFFSET_PX,
|
||||||
SbtDefaults.LAYOUT_NOTIF_VERTICAL_OFFSET_PX_DEFAULT));
|
SbtDefaults.LAYOUT_NOTIF_VERTICAL_OFFSET_PX_DEFAULT));
|
||||||
|
putIconCopySettings(
|
||||||
|
out,
|
||||||
|
prefs,
|
||||||
|
SbtSettings::layoutNotifPositionKey,
|
||||||
|
SbtDefaults.LAYOUT_NOTIF_POSITION_DEFAULT,
|
||||||
|
SbtSettings::layoutNotifMiddleSideKey,
|
||||||
|
SbtDefaults.LAYOUT_NOTIF_MIDDLE_SIDE_DEFAULT,
|
||||||
|
SbtSettings::layoutNotifMiddleAutoNearestSideKey,
|
||||||
|
SbtDefaults.LAYOUT_NOTIF_MIDDLE_AUTO_NEAREST_SIDE_DEFAULT,
|
||||||
|
SbtSettings::layoutNotifVerticalOffsetKey,
|
||||||
|
SbtDefaults.LAYOUT_NOTIF_VERTICAL_OFFSET_PX_DEFAULT);
|
||||||
out.putString(SbtSettings.KEY_LAYOUT_STATUS_POSITION,
|
out.putString(SbtSettings.KEY_LAYOUT_STATUS_POSITION,
|
||||||
prefs.getString(SbtSettings.KEY_LAYOUT_STATUS_POSITION,
|
prefs.getString(SbtSettings.KEY_LAYOUT_STATUS_POSITION,
|
||||||
SbtDefaults.LAYOUT_STATUS_POSITION_DEFAULT));
|
SbtDefaults.LAYOUT_STATUS_POSITION_DEFAULT));
|
||||||
@@ -235,6 +257,13 @@ public class SbtSettingsProvider extends ContentProvider {
|
|||||||
out.putBoolean(SbtSettings.KEY_LAYOUT_STATUS_ENABLED_UNLOCKED,
|
out.putBoolean(SbtSettings.KEY_LAYOUT_STATUS_ENABLED_UNLOCKED,
|
||||||
prefs.getBoolean(SbtSettings.KEY_LAYOUT_STATUS_ENABLED_UNLOCKED,
|
prefs.getBoolean(SbtSettings.KEY_LAYOUT_STATUS_ENABLED_UNLOCKED,
|
||||||
SbtDefaults.LAYOUT_STATUS_ENABLED_UNLOCKED_DEFAULT));
|
SbtDefaults.LAYOUT_STATUS_ENABLED_UNLOCKED_DEFAULT));
|
||||||
|
out.putInt(SbtSettings.KEY_LAYOUT_STATUS_UNLOCKED_COUNT,
|
||||||
|
prefs.getInt(SbtSettings.KEY_LAYOUT_STATUS_UNLOCKED_COUNT,
|
||||||
|
prefs.getBoolean(
|
||||||
|
SbtSettings.KEY_LAYOUT_STATUS_ENABLED_UNLOCKED,
|
||||||
|
SbtDefaults.LAYOUT_STATUS_ENABLED_UNLOCKED_DEFAULT)
|
||||||
|
? SbtDefaults.LAYOUT_STATUS_UNLOCKED_COUNT_DEFAULT
|
||||||
|
: 0));
|
||||||
out.putBoolean(SbtSettings.KEY_LAYOUT_STATUS_SHOW_DOT_IF_TRUNCATED,
|
out.putBoolean(SbtSettings.KEY_LAYOUT_STATUS_SHOW_DOT_IF_TRUNCATED,
|
||||||
prefs.getBoolean(SbtSettings.KEY_LAYOUT_STATUS_SHOW_DOT_IF_TRUNCATED,
|
prefs.getBoolean(SbtSettings.KEY_LAYOUT_STATUS_SHOW_DOT_IF_TRUNCATED,
|
||||||
SbtDefaults.LAYOUT_STATUS_SHOW_DOT_IF_TRUNCATED_DEFAULT));
|
SbtDefaults.LAYOUT_STATUS_SHOW_DOT_IF_TRUNCATED_DEFAULT));
|
||||||
@@ -247,6 +276,17 @@ public class SbtSettingsProvider extends ContentProvider {
|
|||||||
out.putInt(SbtSettings.KEY_LAYOUT_STATUS_VERTICAL_OFFSET_PX,
|
out.putInt(SbtSettings.KEY_LAYOUT_STATUS_VERTICAL_OFFSET_PX,
|
||||||
prefs.getInt(SbtSettings.KEY_LAYOUT_STATUS_VERTICAL_OFFSET_PX,
|
prefs.getInt(SbtSettings.KEY_LAYOUT_STATUS_VERTICAL_OFFSET_PX,
|
||||||
SbtDefaults.LAYOUT_STATUS_VERTICAL_OFFSET_PX_DEFAULT));
|
SbtDefaults.LAYOUT_STATUS_VERTICAL_OFFSET_PX_DEFAULT));
|
||||||
|
putIconCopySettings(
|
||||||
|
out,
|
||||||
|
prefs,
|
||||||
|
SbtSettings::layoutStatusPositionKey,
|
||||||
|
SbtDefaults.LAYOUT_STATUS_POSITION_DEFAULT,
|
||||||
|
SbtSettings::layoutStatusMiddleSideKey,
|
||||||
|
SbtDefaults.LAYOUT_STATUS_MIDDLE_SIDE_DEFAULT,
|
||||||
|
SbtSettings::layoutStatusMiddleAutoNearestSideKey,
|
||||||
|
SbtDefaults.LAYOUT_STATUS_MIDDLE_AUTO_NEAREST_SIDE_DEFAULT,
|
||||||
|
SbtSettings::layoutStatusVerticalOffsetKey,
|
||||||
|
SbtDefaults.LAYOUT_STATUS_VERTICAL_OFFSET_PX_DEFAULT);
|
||||||
out.putStringArrayList(
|
out.putStringArrayList(
|
||||||
SbtSettings.KEY_CLOCK_CAMERA_TYPE_OVERRIDES,
|
SbtSettings.KEY_CLOCK_CAMERA_TYPE_OVERRIDES,
|
||||||
new ArrayList<>(prefs.getStringSet(
|
new ArrayList<>(prefs.getStringSet(
|
||||||
@@ -307,6 +347,26 @@ public class SbtSettingsProvider extends ContentProvider {
|
|||||||
return super.call(method, arg, extras);
|
return super.call(method, arg, extras);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void putIconCopySettings(
|
||||||
|
Bundle out,
|
||||||
|
SharedPreferences prefs,
|
||||||
|
IndexedKey positionKey,
|
||||||
|
String positionDefault,
|
||||||
|
IndexedKey middleSideKey,
|
||||||
|
String middleSideDefault,
|
||||||
|
IndexedKey autoNearestKey,
|
||||||
|
boolean autoNearestDefault,
|
||||||
|
IndexedKey verticalOffsetKey,
|
||||||
|
int verticalOffsetDefault
|
||||||
|
) {
|
||||||
|
for (int i = 0; i < SbtDefaults.LAYOUT_ICON_CONTAINER_COUNT_MAX; i++) {
|
||||||
|
out.putString(positionKey.key(i), prefs.getString(positionKey.key(i), positionDefault));
|
||||||
|
out.putString(middleSideKey.key(i), prefs.getString(middleSideKey.key(i), middleSideDefault));
|
||||||
|
out.putBoolean(autoNearestKey.key(i), prefs.getBoolean(autoNearestKey.key(i), autoNearestDefault));
|
||||||
|
out.putInt(verticalOffsetKey.key(i), prefs.getInt(verticalOffsetKey.key(i), verticalOffsetDefault));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
|
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
package se.ajpanton.statusbartweak.shell.ui;
|
||||||
|
|
||||||
|
public final class AodLayoutFragment extends LockedSceneLayoutFragment {
|
||||||
|
public AodLayoutFragment() {
|
||||||
|
super(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -26,6 +26,7 @@ import java.util.Map;
|
|||||||
|
|
||||||
import se.ajpanton.statusbartweak.R;
|
import se.ajpanton.statusbartweak.R;
|
||||||
import se.ajpanton.statusbartweak.shell.debug.DebugNotifications;
|
import se.ajpanton.statusbartweak.shell.debug.DebugNotifications;
|
||||||
|
import se.ajpanton.statusbartweak.shell.debug.NotificationDisplayStyleSettings;
|
||||||
import se.ajpanton.statusbartweak.shell.settings.ClockCameraTypeSupport;
|
import se.ajpanton.statusbartweak.shell.settings.ClockCameraTypeSupport;
|
||||||
import se.ajpanton.statusbartweak.shell.settings.SbtDefaults;
|
import se.ajpanton.statusbartweak.shell.settings.SbtDefaults;
|
||||||
import se.ajpanton.statusbartweak.shell.settings.SbtSettings;
|
import se.ajpanton.statusbartweak.shell.settings.SbtSettings;
|
||||||
@@ -61,6 +62,10 @@ public class IconsDebugFragment extends Fragment {
|
|||||||
root.findViewById(R.id.debug_visualize_camera_cutout_switch);
|
root.findViewById(R.id.debug_visualize_camera_cutout_switch);
|
||||||
MaterialButton restartSystemUiButton = root.findViewById(R.id.debug_restart_systemui_button);
|
MaterialButton restartSystemUiButton = root.findViewById(R.id.debug_restart_systemui_button);
|
||||||
SwitchMaterial cycleIconsSwitch = root.findViewById(R.id.debug_cycle_icons_switch);
|
SwitchMaterial cycleIconsSwitch = root.findViewById(R.id.debug_cycle_icons_switch);
|
||||||
|
TextView notificationStyleCurrent = root.findViewById(R.id.debug_notification_style_current);
|
||||||
|
MaterialButton notificationStyleCards = root.findViewById(R.id.debug_notification_style_cards);
|
||||||
|
MaterialButton notificationStyleIcons = root.findViewById(R.id.debug_notification_style_icons);
|
||||||
|
MaterialButton notificationStyleDot = root.findViewById(R.id.debug_notification_style_dot);
|
||||||
TextView currentCameraIdentified = root.findViewById(R.id.debug_camera_current_identified);
|
TextView currentCameraIdentified = root.findViewById(R.id.debug_camera_current_identified);
|
||||||
LinearLayout currentCameraOverrideRow = root.findViewById(R.id.debug_camera_current_override_row);
|
LinearLayout currentCameraOverrideRow = root.findViewById(R.id.debug_camera_current_override_row);
|
||||||
TextView currentCameraOverride = root.findViewById(R.id.debug_camera_current_override);
|
TextView currentCameraOverride = root.findViewById(R.id.debug_camera_current_override);
|
||||||
@@ -107,6 +112,11 @@ public class IconsDebugFragment extends Fragment {
|
|||||||
SbtSettings.KEY_DEBUG_VISUALIZE_CAMERA_CUTOUT,
|
SbtSettings.KEY_DEBUG_VISUALIZE_CAMERA_CUTOUT,
|
||||||
SbtDefaults.DEBUG_VISUALIZE_CAMERA_CUTOUT_DEFAULT);
|
SbtDefaults.DEBUG_VISUALIZE_CAMERA_CUTOUT_DEFAULT);
|
||||||
bindRestartSystemUiButton(restartSystemUiButton);
|
bindRestartSystemUiButton(restartSystemUiButton);
|
||||||
|
bindNotificationStyleControls(
|
||||||
|
notificationStyleCurrent,
|
||||||
|
notificationStyleCards,
|
||||||
|
notificationStyleIcons,
|
||||||
|
notificationStyleDot);
|
||||||
updateVisualizerSwitchAvailability(
|
updateVisualizerSwitchAvailability(
|
||||||
prefs,
|
prefs,
|
||||||
visualizeNotificationContainerSwitch,
|
visualizeNotificationContainerSwitch,
|
||||||
@@ -188,6 +198,76 @@ public class IconsDebugFragment extends Fragment {
|
|||||||
restartButton.setOnClickListener(v -> restartSystemUi());
|
restartButton.setOnClickListener(v -> restartSystemUi());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void bindNotificationStyleControls(
|
||||||
|
TextView currentView,
|
||||||
|
MaterialButton cardsButton,
|
||||||
|
MaterialButton iconsButton,
|
||||||
|
MaterialButton dotButton
|
||||||
|
) {
|
||||||
|
updateNotificationStyleUi(currentView);
|
||||||
|
if (cardsButton != null) {
|
||||||
|
cardsButton.setOnClickListener(v -> writeNotificationStyle(
|
||||||
|
NotificationDisplayStyleSettings.Style.CARDS,
|
||||||
|
currentView));
|
||||||
|
}
|
||||||
|
if (iconsButton != null) {
|
||||||
|
iconsButton.setOnClickListener(v -> writeNotificationStyle(
|
||||||
|
NotificationDisplayStyleSettings.Style.ICONS,
|
||||||
|
currentView));
|
||||||
|
}
|
||||||
|
if (dotButton != null) {
|
||||||
|
dotButton.setOnClickListener(v -> writeNotificationStyle(
|
||||||
|
NotificationDisplayStyleSettings.Style.DOT,
|
||||||
|
currentView));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateNotificationStyleUi(TextView currentView) {
|
||||||
|
Context context = requireContext();
|
||||||
|
NotificationDisplayStyleSettings.State state =
|
||||||
|
NotificationDisplayStyleSettings.read(context);
|
||||||
|
if (currentView != null) {
|
||||||
|
currentView.setText(getString(
|
||||||
|
R.string.debug_notification_style_current,
|
||||||
|
readableNotificationStyle(state.style())));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void writeNotificationStyle(
|
||||||
|
NotificationDisplayStyleSettings.Style style,
|
||||||
|
TextView currentView
|
||||||
|
) {
|
||||||
|
Context appContext = requireContext().getApplicationContext();
|
||||||
|
new Thread(() -> {
|
||||||
|
boolean success = NotificationDisplayStyleSettings.write(appContext, style);
|
||||||
|
if (!isAdded()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
requireActivity().runOnUiThread(() -> {
|
||||||
|
updateNotificationStyleUi(currentView);
|
||||||
|
Toast.makeText(
|
||||||
|
requireContext(),
|
||||||
|
success
|
||||||
|
? R.string.debug_notification_style_write_done
|
||||||
|
: R.string.debug_notification_style_write_failed,
|
||||||
|
success ? Toast.LENGTH_SHORT : Toast.LENGTH_LONG).show();
|
||||||
|
});
|
||||||
|
}, "StatusBarTweak:NotificationStyleWrite").start();
|
||||||
|
}
|
||||||
|
|
||||||
|
private String readableNotificationStyle(NotificationDisplayStyleSettings.Style style) {
|
||||||
|
if (style == NotificationDisplayStyleSettings.Style.CARDS) {
|
||||||
|
return getString(R.string.debug_notification_style_cards);
|
||||||
|
}
|
||||||
|
if (style == NotificationDisplayStyleSettings.Style.ICONS) {
|
||||||
|
return getString(R.string.debug_notification_style_icons);
|
||||||
|
}
|
||||||
|
if (style == NotificationDisplayStyleSettings.Style.DOT) {
|
||||||
|
return getString(R.string.debug_notification_style_dot);
|
||||||
|
}
|
||||||
|
return getString(R.string.debug_notification_style_unknown);
|
||||||
|
}
|
||||||
|
|
||||||
private void updateVisualizerSwitchAvailability(SharedPreferences prefs,
|
private void updateVisualizerSwitchAvailability(SharedPreferences prefs,
|
||||||
SwitchMaterial notificationSwitch,
|
SwitchMaterial notificationSwitch,
|
||||||
SwitchMaterial statusSwitch,
|
SwitchMaterial statusSwitch,
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import android.view.ViewGroup;
|
|||||||
import android.view.inputmethod.EditorInfo;
|
import android.view.inputmethod.EditorInfo;
|
||||||
import android.widget.CheckBox;
|
import android.widget.CheckBox;
|
||||||
import android.widget.EditText;
|
import android.widget.EditText;
|
||||||
|
import android.widget.LinearLayout;
|
||||||
import android.widget.RadioGroup;
|
import android.widget.RadioGroup;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
@@ -28,6 +29,7 @@ import androidx.recyclerview.widget.LinearLayoutManager;
|
|||||||
import androidx.recyclerview.widget.RecyclerView;
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
import com.google.android.material.button.MaterialButton;
|
import com.google.android.material.button.MaterialButton;
|
||||||
|
import com.google.android.material.card.MaterialCardView;
|
||||||
import com.google.android.material.switchmaterial.SwitchMaterial;
|
import com.google.android.material.switchmaterial.SwitchMaterial;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
@@ -45,6 +47,7 @@ public final class LayoutFragment extends Fragment {
|
|||||||
View root = inflater.inflate(R.layout.fragment_layout, container, false);
|
View root = inflater.inflate(R.layout.fragment_layout, container, false);
|
||||||
SharedPreferences prefs = requireContext()
|
SharedPreferences prefs = requireContext()
|
||||||
.getSharedPreferences(SbtSettings.PREFS_NAME, Context.MODE_PRIVATE);
|
.getSharedPreferences(SbtSettings.PREFS_NAME, Context.MODE_PRIVATE);
|
||||||
|
hideUnlockedSubpageObsoleteControls(root);
|
||||||
|
|
||||||
SwitchMaterial enabledSwitch = root.findViewById(R.id.clock_enabled_switch);
|
SwitchMaterial enabledSwitch = root.findViewById(R.id.clock_enabled_switch);
|
||||||
enabledSwitch.setChecked(prefs.getBoolean(
|
enabledSwitch.setChecked(prefs.getBoolean(
|
||||||
@@ -198,19 +201,400 @@ public final class LayoutFragment extends Fragment {
|
|||||||
bindCheckBoxPref(prefs, root.findViewById(R.id.status_position_mode_unlocked),
|
bindCheckBoxPref(prefs, root.findViewById(R.id.status_position_mode_unlocked),
|
||||||
SbtSettings.KEY_LAYOUT_STATUS_ENABLED_UNLOCKED,
|
SbtSettings.KEY_LAYOUT_STATUS_ENABLED_UNLOCKED,
|
||||||
SbtDefaults.LAYOUT_STATUS_ENABLED_UNLOCKED_DEFAULT);
|
SbtDefaults.LAYOUT_STATUS_ENABLED_UNLOCKED_DEFAULT);
|
||||||
bindCheckBoxPref(prefs, root.findViewById(R.id.notif_show_dot_if_truncated),
|
|
||||||
SbtSettings.KEY_LAYOUT_NOTIF_SHOW_DOT_IF_TRUNCATED,
|
|
||||||
SbtDefaults.LAYOUT_NOTIF_SHOW_DOT_IF_TRUNCATED_DEFAULT);
|
|
||||||
bindCheckBoxPref(prefs, root.findViewById(R.id.status_show_dot_if_truncated),
|
|
||||||
SbtSettings.KEY_LAYOUT_STATUS_SHOW_DOT_IF_TRUNCATED,
|
|
||||||
SbtDefaults.LAYOUT_STATUS_SHOW_DOT_IF_TRUNCATED_DEFAULT);
|
|
||||||
|
|
||||||
bindSortOrderGroup(prefs, root.findViewById(R.id.layout_sort_icons_group));
|
bindSortOrderGroup(prefs, root.findViewById(R.id.layout_sort_icons_group));
|
||||||
|
|
||||||
setupOrderingList(root, prefs);
|
setupOrderingList(root, prefs);
|
||||||
|
setupUnlockedIconContainerCount(
|
||||||
|
root,
|
||||||
|
prefs,
|
||||||
|
R.id.notif_position_mode_unlocked,
|
||||||
|
R.string.layout_notification_position_title,
|
||||||
|
SbtSettings.KEY_LAYOUT_NOTIF_UNLOCKED_COUNT,
|
||||||
|
SbtDefaults.LAYOUT_NOTIF_UNLOCKED_COUNT_DEFAULT,
|
||||||
|
SbtSettings.KEY_LAYOUT_NOTIF_ENABLED_UNLOCKED,
|
||||||
|
SbtDefaults.LAYOUT_NOTIF_ENABLED_UNLOCKED_DEFAULT,
|
||||||
|
SbtSettings::layoutNotifPositionKey,
|
||||||
|
SbtSettings::layoutNotifMiddleAutoNearestSideKey,
|
||||||
|
SbtSettings::layoutNotifMiddleSideKey,
|
||||||
|
SbtSettings::layoutNotifVerticalOffsetKey,
|
||||||
|
SbtDefaults.LAYOUT_NOTIF_POSITION_DEFAULT,
|
||||||
|
SbtDefaults.LAYOUT_NOTIF_MIDDLE_AUTO_NEAREST_SIDE_DEFAULT,
|
||||||
|
SbtDefaults.LAYOUT_NOTIF_MIDDLE_SIDE_DEFAULT,
|
||||||
|
SbtDefaults.LAYOUT_NOTIF_VERTICAL_OFFSET_PX_DEFAULT);
|
||||||
|
setupUnlockedIconContainerCount(
|
||||||
|
root,
|
||||||
|
prefs,
|
||||||
|
R.id.status_position_mode_unlocked,
|
||||||
|
R.string.layout_status_position_title,
|
||||||
|
SbtSettings.KEY_LAYOUT_STATUS_UNLOCKED_COUNT,
|
||||||
|
SbtDefaults.LAYOUT_STATUS_UNLOCKED_COUNT_DEFAULT,
|
||||||
|
SbtSettings.KEY_LAYOUT_STATUS_ENABLED_UNLOCKED,
|
||||||
|
SbtDefaults.LAYOUT_STATUS_ENABLED_UNLOCKED_DEFAULT,
|
||||||
|
SbtSettings::layoutStatusPositionKey,
|
||||||
|
SbtSettings::layoutStatusMiddleAutoNearestSideKey,
|
||||||
|
SbtSettings::layoutStatusMiddleSideKey,
|
||||||
|
SbtSettings::layoutStatusVerticalOffsetKey,
|
||||||
|
SbtDefaults.LAYOUT_STATUS_POSITION_DEFAULT,
|
||||||
|
SbtDefaults.LAYOUT_STATUS_MIDDLE_AUTO_NEAREST_SIDE_DEFAULT,
|
||||||
|
SbtDefaults.LAYOUT_STATUS_MIDDLE_SIDE_DEFAULT,
|
||||||
|
SbtDefaults.LAYOUT_STATUS_VERTICAL_OFFSET_PX_DEFAULT);
|
||||||
return root;
|
return root;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private interface IndexedKey {
|
||||||
|
String key(int index);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupUnlockedIconContainerCount(
|
||||||
|
View root,
|
||||||
|
SharedPreferences prefs,
|
||||||
|
int checkboxId,
|
||||||
|
int sectionTitleId,
|
||||||
|
String countKey,
|
||||||
|
int countDefault,
|
||||||
|
String legacyEnabledKey,
|
||||||
|
boolean legacyEnabledDefault,
|
||||||
|
IndexedKey positionKey,
|
||||||
|
IndexedKey autoNearestKey,
|
||||||
|
IndexedKey middleSideKey,
|
||||||
|
IndexedKey verticalOffsetKey,
|
||||||
|
String positionDefault,
|
||||||
|
boolean autoNearestDefault,
|
||||||
|
String middleSideDefault,
|
||||||
|
int verticalOffsetDefault
|
||||||
|
) {
|
||||||
|
CheckBox checkbox = root.findViewById(checkboxId);
|
||||||
|
if (checkbox == null || !(checkbox.getParent() instanceof ViewGroup modeRow)
|
||||||
|
|| !(modeRow.getParent() instanceof LinearLayout sectionContent)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int rowIndex = sectionContent.indexOfChild(modeRow);
|
||||||
|
sectionContent.removeView(modeRow);
|
||||||
|
MaterialCardView sourceCard = findAncestorCard(sectionContent);
|
||||||
|
LinearLayout cardsParent = sourceCard != null && sourceCard.getParent() instanceof LinearLayout parent
|
||||||
|
? parent
|
||||||
|
: null;
|
||||||
|
int sourceCardIndex = cardsParent != null ? cardsParent.indexOfChild(sourceCard) : -1;
|
||||||
|
String copyTag = "layout-copy-" + countKey;
|
||||||
|
|
||||||
|
LinearLayout countContainer = new LinearLayout(requireContext());
|
||||||
|
countContainer.setOrientation(LinearLayout.VERTICAL);
|
||||||
|
sectionContent.addView(countContainer, Math.max(0, rowIndex));
|
||||||
|
|
||||||
|
Runnable[] rebuild = new Runnable[1];
|
||||||
|
rebuild[0] = () -> {
|
||||||
|
countContainer.removeAllViews();
|
||||||
|
removeGeneratedCopyCards(cardsParent, copyTag);
|
||||||
|
int count = iconContainerCount(prefs, countKey, countDefault, legacyEnabledKey, legacyEnabledDefault);
|
||||||
|
countContainer.addView(iconContainerCountControl(
|
||||||
|
prefs,
|
||||||
|
countKey,
|
||||||
|
count,
|
||||||
|
legacyEnabledKey,
|
||||||
|
() -> {
|
||||||
|
if (rebuild[0] != null) {
|
||||||
|
rebuild[0].run();
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
if (cardsParent == null || sourceCardIndex < 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int insertIndex = sourceCardIndex + 1;
|
||||||
|
for (int i = 1; i < count; i++, insertIndex++) {
|
||||||
|
View copy = iconCopyCardFromLayout(
|
||||||
|
checkboxId,
|
||||||
|
sectionTitleId,
|
||||||
|
i,
|
||||||
|
positionKey,
|
||||||
|
autoNearestKey,
|
||||||
|
middleSideKey,
|
||||||
|
verticalOffsetKey,
|
||||||
|
positionDefault,
|
||||||
|
autoNearestDefault,
|
||||||
|
middleSideDefault,
|
||||||
|
verticalOffsetDefault,
|
||||||
|
copyTag);
|
||||||
|
cardsParent.addView(copy, Math.min(insertIndex, cardsParent.getChildCount()));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
rebuild[0].run();
|
||||||
|
}
|
||||||
|
|
||||||
|
private View iconContainerCountControl(
|
||||||
|
SharedPreferences prefs,
|
||||||
|
String countKey,
|
||||||
|
int current,
|
||||||
|
String legacyEnabledKey,
|
||||||
|
Runnable onChanged
|
||||||
|
) {
|
||||||
|
LinearLayout outer = new LinearLayout(requireContext());
|
||||||
|
outer.setOrientation(LinearLayout.VERTICAL);
|
||||||
|
TextView label = new TextView(requireContext());
|
||||||
|
label.setText(R.string.layout_icon_container_count);
|
||||||
|
label.setPadding(0, dp(8), 0, 0);
|
||||||
|
outer.addView(label);
|
||||||
|
|
||||||
|
LinearLayout row = new LinearLayout(requireContext());
|
||||||
|
row.setOrientation(LinearLayout.HORIZONTAL);
|
||||||
|
row.setGravity(android.view.Gravity.CENTER_VERTICAL);
|
||||||
|
MaterialButton minus = smallButton("-");
|
||||||
|
MaterialButton plus = smallButton("+");
|
||||||
|
EditText input = new EditText(requireContext());
|
||||||
|
input.setGravity(android.view.Gravity.CENTER);
|
||||||
|
input.setSingleLine(true);
|
||||||
|
input.setInputType(android.text.InputType.TYPE_CLASS_NUMBER);
|
||||||
|
input.setFilters(new InputFilter[]{new InputFilter.LengthFilter(1)});
|
||||||
|
input.setText(String.valueOf(current));
|
||||||
|
row.addView(minus, compactButtonParams());
|
||||||
|
row.addView(input, compactInputParams());
|
||||||
|
row.addView(plus, compactButtonParams());
|
||||||
|
outer.addView(row);
|
||||||
|
|
||||||
|
View.OnClickListener listener = view -> {
|
||||||
|
int next = clampIconContainerCount(parseInt(input, current) + (view == minus ? -1 : 1));
|
||||||
|
persistIconContainerCount(prefs, countKey, legacyEnabledKey, next);
|
||||||
|
onChanged.run();
|
||||||
|
};
|
||||||
|
minus.setOnClickListener(listener);
|
||||||
|
plus.setOnClickListener(listener);
|
||||||
|
input.setOnFocusChangeListener((v, hasFocus) -> {
|
||||||
|
if (!hasFocus) {
|
||||||
|
int next = clampIconContainerCount(parseInt(input, current));
|
||||||
|
persistIconContainerCount(prefs, countKey, legacyEnabledKey, next);
|
||||||
|
onChanged.run();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return outer;
|
||||||
|
}
|
||||||
|
|
||||||
|
private View iconCopyCardFromLayout(
|
||||||
|
int sourceCheckboxId,
|
||||||
|
int titleId,
|
||||||
|
int index,
|
||||||
|
IndexedKey positionKey,
|
||||||
|
IndexedKey autoNearestKey,
|
||||||
|
IndexedKey middleSideKey,
|
||||||
|
IndexedKey verticalOffsetKey,
|
||||||
|
String positionDefault,
|
||||||
|
boolean autoNearestDefault,
|
||||||
|
String middleSideDefault,
|
||||||
|
int verticalOffsetDefault,
|
||||||
|
String copyTag
|
||||||
|
) {
|
||||||
|
int cardId = sourceCheckboxId == R.id.notif_position_mode_unlocked
|
||||||
|
? R.id.layout_notif_card
|
||||||
|
: R.id.layout_status_card;
|
||||||
|
View card = inflateLayoutCard(cardId);
|
||||||
|
card.setTag(copyTag);
|
||||||
|
TextView title = findCardTitle(card);
|
||||||
|
if (title != null) {
|
||||||
|
title.setText(getString(titleId) + " " + (index + 1));
|
||||||
|
}
|
||||||
|
removeModeContainerForCheckbox(card, sourceCheckboxId);
|
||||||
|
SharedPreferences prefs = requireContext()
|
||||||
|
.getSharedPreferences(SbtSettings.PREFS_NAME, Context.MODE_PRIVATE);
|
||||||
|
if (sourceCheckboxId == R.id.notif_position_mode_unlocked) {
|
||||||
|
setupPositionSection(card,
|
||||||
|
prefs,
|
||||||
|
R.id.notif_position_group,
|
||||||
|
R.id.notif_position_middle_options_container,
|
||||||
|
R.id.notif_middle_auto_nearest,
|
||||||
|
R.id.notif_middle_side_prompt,
|
||||||
|
positionKey.key(index),
|
||||||
|
positionDefault,
|
||||||
|
autoNearestKey.key(index),
|
||||||
|
middleSideKey.key(index));
|
||||||
|
bindStepper(prefs,
|
||||||
|
verticalOffsetKey.key(index),
|
||||||
|
card.findViewById(R.id.notif_vertical_offset_input),
|
||||||
|
card.findViewById(R.id.notif_vertical_offset_minus_fast),
|
||||||
|
card.findViewById(R.id.notif_vertical_offset_minus),
|
||||||
|
card.findViewById(R.id.notif_vertical_offset_plus),
|
||||||
|
card.findViewById(R.id.notif_vertical_offset_plus_fast),
|
||||||
|
card.findViewById(R.id.notif_vertical_offset_reset),
|
||||||
|
verticalOffsetDefault);
|
||||||
|
} else {
|
||||||
|
setupPositionSection(card,
|
||||||
|
prefs,
|
||||||
|
R.id.status_position_group,
|
||||||
|
R.id.status_position_middle_options_container,
|
||||||
|
R.id.status_middle_auto_nearest,
|
||||||
|
R.id.status_middle_side_prompt,
|
||||||
|
positionKey.key(index),
|
||||||
|
positionDefault,
|
||||||
|
autoNearestKey.key(index),
|
||||||
|
middleSideKey.key(index));
|
||||||
|
bindStepper(prefs,
|
||||||
|
verticalOffsetKey.key(index),
|
||||||
|
card.findViewById(R.id.status_vertical_offset_input),
|
||||||
|
card.findViewById(R.id.status_vertical_offset_minus_fast),
|
||||||
|
card.findViewById(R.id.status_vertical_offset_minus),
|
||||||
|
card.findViewById(R.id.status_vertical_offset_plus),
|
||||||
|
card.findViewById(R.id.status_vertical_offset_plus_fast),
|
||||||
|
card.findViewById(R.id.status_vertical_offset_reset),
|
||||||
|
verticalOffsetDefault);
|
||||||
|
}
|
||||||
|
return card;
|
||||||
|
}
|
||||||
|
|
||||||
|
private MaterialButton smallButton(String text) {
|
||||||
|
MaterialButton button = new MaterialButton(requireContext(), null, com.google.android.material.R.attr.materialButtonOutlinedStyle);
|
||||||
|
button.setText(text);
|
||||||
|
button.setMinWidth(dp(40));
|
||||||
|
button.setMinHeight(dp(40));
|
||||||
|
button.setPadding(0, button.getPaddingTop(), 0, button.getPaddingBottom());
|
||||||
|
return button;
|
||||||
|
}
|
||||||
|
|
||||||
|
private LinearLayout.LayoutParams compactButtonParams() {
|
||||||
|
return new LinearLayout.LayoutParams(dp(40), ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||||
|
}
|
||||||
|
|
||||||
|
private LinearLayout.LayoutParams compactInputParams() {
|
||||||
|
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
|
||||||
|
dp(48),
|
||||||
|
ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||||
|
params.leftMargin = dp(8);
|
||||||
|
params.rightMargin = dp(8);
|
||||||
|
return params;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private MaterialCardView findAncestorCard(View view) {
|
||||||
|
View current = view;
|
||||||
|
while (current != null) {
|
||||||
|
if (current instanceof MaterialCardView card) {
|
||||||
|
return card;
|
||||||
|
}
|
||||||
|
current = current.getParent() instanceof View parent ? parent : null;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void removeGeneratedCopyCards(@Nullable LinearLayout parent, String tag) {
|
||||||
|
if (parent == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (int i = parent.getChildCount() - 1; i >= 0; i--) {
|
||||||
|
View child = parent.getChildAt(i);
|
||||||
|
if (tag.equals(child.getTag())) {
|
||||||
|
parent.removeViewAt(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private View inflateLayoutCard(int cardId) {
|
||||||
|
View fullLayout = LayoutInflater.from(requireContext()).inflate(R.layout.fragment_layout, null, false);
|
||||||
|
View card = fullLayout.findViewById(cardId);
|
||||||
|
if (card == null) {
|
||||||
|
return new View(requireContext());
|
||||||
|
}
|
||||||
|
if (card.getParent() instanceof ViewGroup parent) {
|
||||||
|
parent.removeView(card);
|
||||||
|
}
|
||||||
|
return card;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private TextView findCardTitle(View card) {
|
||||||
|
if (!(card instanceof ViewGroup cardGroup) || cardGroup.getChildCount() <= 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
View inner = cardGroup.getChildAt(0);
|
||||||
|
if (!(inner instanceof ViewGroup innerGroup) || innerGroup.getChildCount() <= 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
View title = innerGroup.getChildAt(0);
|
||||||
|
return title instanceof TextView textView ? textView : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private LinearLayout removeModeContainerForCheckbox(View root, int checkboxId) {
|
||||||
|
CheckBox checkbox = root.findViewById(checkboxId);
|
||||||
|
View modeContainer = directSectionChild(checkbox);
|
||||||
|
if (modeContainer == null || !(modeContainer.getParent() instanceof LinearLayout sectionContent)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
sectionContent.removeView(modeContainer);
|
||||||
|
return sectionContent;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private View directSectionChild(@Nullable View child) {
|
||||||
|
View current = child;
|
||||||
|
while (current != null && current.getParent() instanceof View parent) {
|
||||||
|
if (parent.getParent() instanceof MaterialCardView) {
|
||||||
|
return current;
|
||||||
|
}
|
||||||
|
current = parent;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int iconContainerCount(
|
||||||
|
SharedPreferences prefs,
|
||||||
|
String countKey,
|
||||||
|
int countDefault,
|
||||||
|
String legacyEnabledKey,
|
||||||
|
boolean legacyEnabledDefault
|
||||||
|
) {
|
||||||
|
int fallback = prefs.getBoolean(legacyEnabledKey, legacyEnabledDefault) ? countDefault : 0;
|
||||||
|
return clampIconContainerCount(prefs.getInt(countKey, fallback));
|
||||||
|
}
|
||||||
|
|
||||||
|
private int clampIconContainerCount(int value) {
|
||||||
|
return Math.max(
|
||||||
|
SbtDefaults.LAYOUT_ICON_CONTAINER_COUNT_MIN,
|
||||||
|
Math.min(SbtDefaults.LAYOUT_ICON_CONTAINER_COUNT_MAX, value));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void persistIconContainerCount(
|
||||||
|
SharedPreferences prefs,
|
||||||
|
String countKey,
|
||||||
|
String legacyEnabledKey,
|
||||||
|
int count
|
||||||
|
) {
|
||||||
|
prefs.edit()
|
||||||
|
.putInt(countKey, clampIconContainerCount(count))
|
||||||
|
.putBoolean(legacyEnabledKey, count > 0)
|
||||||
|
.apply();
|
||||||
|
SbtSettings.ensureReadable(requireContext());
|
||||||
|
}
|
||||||
|
|
||||||
|
private int dp(int value) {
|
||||||
|
return Math.round(value * getResources().getDisplayMetrics().density);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void hideUnlockedSubpageObsoleteControls(View root) {
|
||||||
|
hide(root, R.id.layout_master_card);
|
||||||
|
hide(root, R.id.layout_padding_card);
|
||||||
|
hide(root, R.id.layout_ordering_card);
|
||||||
|
hide(root, R.id.clock_position_mode_lockscreen);
|
||||||
|
hide(root, R.id.notif_position_mode_aod);
|
||||||
|
hide(root, R.id.notif_position_mode_lockscreen);
|
||||||
|
hide(root, R.id.chip_position_mode_lockscreen);
|
||||||
|
hide(root, R.id.status_position_mode_aod);
|
||||||
|
hide(root, R.id.status_position_mode_lockscreen);
|
||||||
|
setText(root, R.id.clock_position_mode_unlocked, R.string.layout_item_enabled);
|
||||||
|
setText(root, R.id.notif_position_mode_unlocked, R.string.layout_item_enabled);
|
||||||
|
setText(root, R.id.chip_position_mode_unlocked, R.string.layout_item_enabled);
|
||||||
|
setText(root, R.id.status_position_mode_unlocked, R.string.layout_item_enabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void hide(View root, int id) {
|
||||||
|
View view = root.findViewById(id);
|
||||||
|
if (view != null) {
|
||||||
|
view.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setText(View root, int id, int stringId) {
|
||||||
|
TextView view = root.findViewById(id);
|
||||||
|
if (view != null) {
|
||||||
|
view.setText(stringId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void setupPositionSection(View root,
|
private void setupPositionSection(View root,
|
||||||
SharedPreferences prefs,
|
SharedPreferences prefs,
|
||||||
int groupId,
|
int groupId,
|
||||||
|
|||||||
@@ -0,0 +1,351 @@
|
|||||||
|
package se.ajpanton.statusbartweak.shell.ui;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.SharedPreferences;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.text.InputFilter;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.CheckBox;
|
||||||
|
import android.widget.EditText;
|
||||||
|
import android.widget.LinearLayout;
|
||||||
|
import android.widget.RadioGroup;
|
||||||
|
import android.widget.ScrollView;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.fragment.app.Fragment;
|
||||||
|
|
||||||
|
import com.google.android.material.button.MaterialButton;
|
||||||
|
import com.google.android.material.card.MaterialCardView;
|
||||||
|
import com.google.android.material.switchmaterial.SwitchMaterial;
|
||||||
|
|
||||||
|
import se.ajpanton.statusbartweak.R;
|
||||||
|
import se.ajpanton.statusbartweak.shell.settings.SbtDefaults;
|
||||||
|
import se.ajpanton.statusbartweak.shell.settings.SbtSettings;
|
||||||
|
|
||||||
|
public final class LayoutHomeFragment extends Fragment {
|
||||||
|
private LinearLayout layoutOnContainer;
|
||||||
|
private LinearLayout miscLayoutOnContainer;
|
||||||
|
private LinearLayout miscUnlockedOffOnlyContainer;
|
||||||
|
private RadioGroup lockedModeGroup;
|
||||||
|
private TextView lockedModeHint;
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public View onCreateView(@NonNull LayoutInflater inflater,
|
||||||
|
@Nullable ViewGroup container,
|
||||||
|
@Nullable Bundle savedInstanceState) {
|
||||||
|
Context context = requireContext();
|
||||||
|
SharedPreferences prefs = context.getSharedPreferences(SbtSettings.PREFS_NAME, Context.MODE_PRIVATE);
|
||||||
|
ScrollView scroll = new ScrollView(context);
|
||||||
|
LinearLayout root = new LinearLayout(context);
|
||||||
|
root.setOrientation(LinearLayout.VERTICAL);
|
||||||
|
root.setPadding(dp(20), dp(20), dp(20), dp(20));
|
||||||
|
scroll.addView(root);
|
||||||
|
|
||||||
|
TextView title = title(context, R.string.layout_home_title);
|
||||||
|
root.addView(title);
|
||||||
|
|
||||||
|
SwitchMaterial master = new SwitchMaterial(context);
|
||||||
|
master.setText(R.string.layout_enabled);
|
||||||
|
master.setChecked(prefs.getBoolean(SbtSettings.KEY_CLOCK_ENABLED, SbtDefaults.CLOCK_ENABLED_DEFAULT));
|
||||||
|
root.addView(card(context, R.string.layout_master_toggle_title, master));
|
||||||
|
|
||||||
|
lockedModeGroup = new RadioGroup(context);
|
||||||
|
lockedModeGroup.setOrientation(RadioGroup.VERTICAL);
|
||||||
|
lockedModeHint = body(context, R.string.layout_locked_notifications_hint);
|
||||||
|
LinearLayout lockedContent = vertical(context);
|
||||||
|
lockedContent.addView(lockedModeHint);
|
||||||
|
lockedContent.addView(lockedModeGroup);
|
||||||
|
root.addView(card(context, R.string.layout_locked_notifications_title, lockedContent));
|
||||||
|
|
||||||
|
layoutOnContainer = vertical(context);
|
||||||
|
layoutOnContainer.addView(paddingCard(context, prefs));
|
||||||
|
layoutOnContainer.addView(LayoutOrderingUi.createCard(context, prefs));
|
||||||
|
root.addView(layoutOnContainer);
|
||||||
|
|
||||||
|
root.addView(miscCard(context, prefs));
|
||||||
|
|
||||||
|
Runnable refreshAvailability = () -> refreshAvailability(prefs, master.isChecked());
|
||||||
|
master.setOnCheckedChangeListener((buttonView, isChecked) -> {
|
||||||
|
prefs.edit().putBoolean(SbtSettings.KEY_CLOCK_ENABLED, isChecked).apply();
|
||||||
|
SbtSettings.ensureReadable(context);
|
||||||
|
refreshAvailability.run();
|
||||||
|
});
|
||||||
|
refreshAvailability.run();
|
||||||
|
return scroll;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void refreshAvailability(SharedPreferences prefs, boolean layoutEnabled) {
|
||||||
|
setViewTreeEnabled(layoutOnContainer, layoutEnabled);
|
||||||
|
layoutOnContainer.setAlpha(layoutEnabled ? 1f : 0.45f);
|
||||||
|
setViewTreeEnabled(miscLayoutOnContainer, layoutEnabled);
|
||||||
|
miscLayoutOnContainer.setAlpha(layoutEnabled ? 1f : 0.45f);
|
||||||
|
setViewTreeEnabled(miscUnlockedOffOnlyContainer, !layoutEnabled);
|
||||||
|
miscUnlockedOffOnlyContainer.setAlpha(layoutEnabled ? 0.45f : 1f);
|
||||||
|
LockedNotificationModeUi.bind(
|
||||||
|
requireContext(),
|
||||||
|
prefs,
|
||||||
|
lockedModeGroup,
|
||||||
|
lockedModeHint,
|
||||||
|
layoutEnabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
private View paddingCard(Context context, SharedPreferences prefs) {
|
||||||
|
LinearLayout content = vertical(context);
|
||||||
|
content.addView(stepper(
|
||||||
|
context,
|
||||||
|
prefs,
|
||||||
|
R.string.layout_cutout_gap_label,
|
||||||
|
SbtSettings.KEY_LAYOUT_PADDING_CUTOUT_PX,
|
||||||
|
SbtDefaults.LAYOUT_PADDING_CUTOUT_PX_DEFAULT,
|
||||||
|
-999,
|
||||||
|
999));
|
||||||
|
content.addView(stepper(
|
||||||
|
context,
|
||||||
|
prefs,
|
||||||
|
R.string.layout_left_padding_label,
|
||||||
|
SbtSettings.KEY_LAYOUT_PADDING_LEFT_PX,
|
||||||
|
SbtDefaults.LAYOUT_PADDING_LEFT_PX_DEFAULT,
|
||||||
|
-999,
|
||||||
|
999,
|
||||||
|
checkBox(
|
||||||
|
context,
|
||||||
|
prefs,
|
||||||
|
R.string.layout_padding_add_stock,
|
||||||
|
SbtSettings.KEY_LAYOUT_PADDING_LEFT_ADD_STOCK,
|
||||||
|
SbtDefaults.LAYOUT_PADDING_LEFT_ADD_STOCK_DEFAULT)));
|
||||||
|
content.addView(stepper(
|
||||||
|
context,
|
||||||
|
prefs,
|
||||||
|
R.string.layout_right_padding_label,
|
||||||
|
SbtSettings.KEY_LAYOUT_PADDING_RIGHT_PX,
|
||||||
|
SbtDefaults.LAYOUT_PADDING_RIGHT_PX_DEFAULT,
|
||||||
|
-999,
|
||||||
|
999,
|
||||||
|
checkBox(
|
||||||
|
context,
|
||||||
|
prefs,
|
||||||
|
R.string.layout_padding_add_stock,
|
||||||
|
SbtSettings.KEY_LAYOUT_PADDING_RIGHT_ADD_STOCK,
|
||||||
|
SbtDefaults.LAYOUT_PADDING_RIGHT_ADD_STOCK_DEFAULT)));
|
||||||
|
content.addView(stepper(
|
||||||
|
context,
|
||||||
|
prefs,
|
||||||
|
R.string.layout_item_padding_label,
|
||||||
|
SbtSettings.KEY_LAYOUT_ITEM_PADDING_PX,
|
||||||
|
SbtDefaults.LAYOUT_ITEM_PADDING_PX_DEFAULT,
|
||||||
|
SbtDefaults.LAYOUT_ITEM_PADDING_PX_MIN,
|
||||||
|
SbtDefaults.LAYOUT_ITEM_PADDING_PX_MAX));
|
||||||
|
return card(context, R.string.layout_padding_title, content);
|
||||||
|
}
|
||||||
|
|
||||||
|
private View miscCard(Context context, SharedPreferences prefs) {
|
||||||
|
LinearLayout content = vertical(context);
|
||||||
|
miscLayoutOnContainer = vertical(context);
|
||||||
|
miscLayoutOnContainer.addView(checkBox(
|
||||||
|
context,
|
||||||
|
prefs,
|
||||||
|
R.string.layout_ignore_under_display_cameras,
|
||||||
|
SbtSettings.KEY_LAYOUT_IGNORE_UNDER_DISPLAY_CAMERAS,
|
||||||
|
SbtDefaults.LAYOUT_IGNORE_UNDER_DISPLAY_CAMERAS_DEFAULT));
|
||||||
|
content.addView(miscLayoutOnContainer);
|
||||||
|
miscUnlockedOffOnlyContainer = vertical(context);
|
||||||
|
miscUnlockedOffOnlyContainer.addView(stepper(
|
||||||
|
context,
|
||||||
|
prefs,
|
||||||
|
R.string.label_max_unlocked_icons_per_row,
|
||||||
|
SbtSettings.KEY_UNLOCKED_MAX_ICONS_PER_ROW,
|
||||||
|
SbtDefaults.UNLOCKED_MAX_ICONS_PER_ROW_DEFAULT,
|
||||||
|
SbtDefaults.UNLOCKED_MAX_ICONS_PER_ROW_MIN,
|
||||||
|
SbtDefaults.UNLOCKED_MAX_ICONS_PER_ROW_MAX));
|
||||||
|
content.addView(miscUnlockedOffOnlyContainer);
|
||||||
|
return card(context, R.string.layout_misc_title, content);
|
||||||
|
}
|
||||||
|
|
||||||
|
private View stepper(
|
||||||
|
Context context,
|
||||||
|
SharedPreferences prefs,
|
||||||
|
int labelId,
|
||||||
|
String key,
|
||||||
|
int defaultValue,
|
||||||
|
int min,
|
||||||
|
int max
|
||||||
|
) {
|
||||||
|
return stepper(context, prefs, labelId, key, defaultValue, min, max, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private View stepper(
|
||||||
|
Context context,
|
||||||
|
SharedPreferences prefs,
|
||||||
|
int labelId,
|
||||||
|
String key,
|
||||||
|
int defaultValue,
|
||||||
|
int min,
|
||||||
|
int max,
|
||||||
|
@Nullable View trailingView
|
||||||
|
) {
|
||||||
|
LinearLayout outer = vertical(context);
|
||||||
|
TextView label = body(context, labelId);
|
||||||
|
outer.addView(label);
|
||||||
|
LinearLayout row = new LinearLayout(context);
|
||||||
|
row.setOrientation(LinearLayout.HORIZONTAL);
|
||||||
|
row.setGravity(android.view.Gravity.CENTER_VERTICAL);
|
||||||
|
row.setPadding(0, dp(6), 0, dp(8));
|
||||||
|
MaterialButton minus = button(context, "-");
|
||||||
|
MaterialButton plus = button(context, "+");
|
||||||
|
EditText input = new EditText(context);
|
||||||
|
input.setGravity(android.view.Gravity.CENTER);
|
||||||
|
input.setSingleLine(true);
|
||||||
|
input.setInputType(android.text.InputType.TYPE_CLASS_NUMBER | android.text.InputType.TYPE_NUMBER_FLAG_SIGNED);
|
||||||
|
input.setFilters(new InputFilter[]{new InputFilter.LengthFilter(5)});
|
||||||
|
input.setText(String.valueOf(clamp(prefs.getInt(key, defaultValue), min, max)));
|
||||||
|
row.addView(minus, buttonParams());
|
||||||
|
row.addView(input, inputParams());
|
||||||
|
row.addView(plus, buttonParams());
|
||||||
|
if (trailingView != null) {
|
||||||
|
LinearLayout.LayoutParams trailingParams = new LinearLayout.LayoutParams(
|
||||||
|
ViewGroup.LayoutParams.WRAP_CONTENT,
|
||||||
|
ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||||
|
trailingParams.leftMargin = dp(8);
|
||||||
|
row.addView(trailingView, trailingParams);
|
||||||
|
}
|
||||||
|
outer.addView(row);
|
||||||
|
View.OnClickListener listener = v -> {
|
||||||
|
int delta = v == minus ? -1 : 1;
|
||||||
|
int next = clamp(parseInt(input, defaultValue) + delta, min, max);
|
||||||
|
input.setText(String.valueOf(next));
|
||||||
|
prefs.edit().putInt(key, next).apply();
|
||||||
|
SbtSettings.ensureReadable(context);
|
||||||
|
};
|
||||||
|
minus.setOnClickListener(listener);
|
||||||
|
plus.setOnClickListener(listener);
|
||||||
|
input.setOnFocusChangeListener((v, hasFocus) -> {
|
||||||
|
if (!hasFocus) {
|
||||||
|
int next = clamp(parseInt(input, defaultValue), min, max);
|
||||||
|
input.setText(String.valueOf(next));
|
||||||
|
prefs.edit().putInt(key, next).apply();
|
||||||
|
SbtSettings.ensureReadable(context);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return outer;
|
||||||
|
}
|
||||||
|
|
||||||
|
private CheckBox checkBox(
|
||||||
|
Context context,
|
||||||
|
SharedPreferences prefs,
|
||||||
|
int label,
|
||||||
|
String key,
|
||||||
|
boolean defaultValue
|
||||||
|
) {
|
||||||
|
CheckBox checkBox = new CheckBox(context);
|
||||||
|
checkBox.setText(label);
|
||||||
|
checkBox.setChecked(prefs.getBoolean(key, defaultValue));
|
||||||
|
checkBox.setOnCheckedChangeListener((buttonView, isChecked) -> {
|
||||||
|
prefs.edit().putBoolean(key, isChecked).apply();
|
||||||
|
SbtSettings.ensureReadable(context);
|
||||||
|
});
|
||||||
|
return checkBox;
|
||||||
|
}
|
||||||
|
|
||||||
|
private MaterialButton button(Context context, String text) {
|
||||||
|
MaterialButton button = new MaterialButton(context, null, com.google.android.material.R.attr.materialButtonOutlinedStyle);
|
||||||
|
button.setText(text);
|
||||||
|
button.setMinWidth(dp(40));
|
||||||
|
button.setMinHeight(dp(40));
|
||||||
|
button.setPadding(0, button.getPaddingTop(), 0, button.getPaddingBottom());
|
||||||
|
return button;
|
||||||
|
}
|
||||||
|
|
||||||
|
private LinearLayout.LayoutParams buttonParams() {
|
||||||
|
return new LinearLayout.LayoutParams(dp(40), ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||||
|
}
|
||||||
|
|
||||||
|
private LinearLayout.LayoutParams inputParams() {
|
||||||
|
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
|
||||||
|
dp(48),
|
||||||
|
ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||||
|
params.leftMargin = dp(8);
|
||||||
|
params.rightMargin = dp(8);
|
||||||
|
return params;
|
||||||
|
}
|
||||||
|
|
||||||
|
private MaterialCardView card(Context context, int titleId, View content) {
|
||||||
|
MaterialCardView card = new MaterialCardView(context);
|
||||||
|
LinearLayout.LayoutParams cardParams = new LinearLayout.LayoutParams(
|
||||||
|
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||||
|
ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||||
|
cardParams.topMargin = dp(16);
|
||||||
|
card.setLayoutParams(cardParams);
|
||||||
|
card.setUseCompatPadding(true);
|
||||||
|
card.setStrokeColor(context.getColor(R.color.sbt_card_outline));
|
||||||
|
card.setStrokeWidth(dp(1));
|
||||||
|
LinearLayout inner = vertical(context);
|
||||||
|
inner.setPadding(dp(16), dp(16), dp(16), dp(16));
|
||||||
|
TextView title = sectionTitle(context, titleId);
|
||||||
|
inner.addView(title);
|
||||||
|
inner.addView(content);
|
||||||
|
card.addView(inner);
|
||||||
|
return card;
|
||||||
|
}
|
||||||
|
|
||||||
|
private LinearLayout vertical(Context context) {
|
||||||
|
LinearLayout layout = new LinearLayout(context);
|
||||||
|
layout.setOrientation(LinearLayout.VERTICAL);
|
||||||
|
return layout;
|
||||||
|
}
|
||||||
|
|
||||||
|
private TextView title(Context context, int stringId) {
|
||||||
|
TextView view = new TextView(context);
|
||||||
|
view.setText(stringId);
|
||||||
|
view.setTextAppearance(com.google.android.material.R.style.TextAppearance_MaterialComponents_Headline5);
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
|
||||||
|
private TextView sectionTitle(Context context, int stringId) {
|
||||||
|
TextView view = new TextView(context);
|
||||||
|
view.setText(stringId);
|
||||||
|
view.setTextAppearance(com.google.android.material.R.style.TextAppearance_MaterialComponents_Subtitle1);
|
||||||
|
view.setAllCaps(true);
|
||||||
|
view.setTypeface(view.getTypeface(), android.graphics.Typeface.BOLD);
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
|
||||||
|
private TextView body(Context context, int stringId) {
|
||||||
|
TextView view = new TextView(context);
|
||||||
|
view.setText(stringId);
|
||||||
|
view.setPadding(0, dp(8), 0, 0);
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setViewTreeEnabled(View view, boolean enabled) {
|
||||||
|
if (view == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
view.setEnabled(enabled);
|
||||||
|
if (view instanceof ViewGroup group) {
|
||||||
|
for (int i = 0; i < group.getChildCount(); i++) {
|
||||||
|
setViewTreeEnabled(group.getChildAt(i), enabled);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private int parseInt(EditText input, int fallback) {
|
||||||
|
try {
|
||||||
|
return Integer.parseInt(input.getText().toString().trim());
|
||||||
|
} catch (Throwable ignored) {
|
||||||
|
return fallback;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private int clamp(int value, int min, int max) {
|
||||||
|
return Math.max(min, Math.min(max, value));
|
||||||
|
}
|
||||||
|
|
||||||
|
private int dp(int value) {
|
||||||
|
return Math.round(value * getResources().getDisplayMetrics().density);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,324 @@
|
|||||||
|
package se.ajpanton.statusbartweak.shell.ui;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.SharedPreferences;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.MotionEvent;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.LinearLayout;
|
||||||
|
import android.widget.RadioButton;
|
||||||
|
import android.widget.RadioGroup;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.recyclerview.widget.ItemTouchHelper;
|
||||||
|
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
|
import com.google.android.material.card.MaterialCardView;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import se.ajpanton.statusbartweak.R;
|
||||||
|
import se.ajpanton.statusbartweak.shell.settings.SbtDefaults;
|
||||||
|
import se.ajpanton.statusbartweak.shell.settings.SbtSettings;
|
||||||
|
|
||||||
|
final class LayoutOrderingUi {
|
||||||
|
private LayoutOrderingUi() {
|
||||||
|
}
|
||||||
|
|
||||||
|
static View createCard(Context context, SharedPreferences prefs) {
|
||||||
|
LinearLayout content = vertical(context);
|
||||||
|
TextView hint = body(context, R.string.layout_ordering_hint);
|
||||||
|
content.addView(hint);
|
||||||
|
|
||||||
|
RecyclerView list = new RecyclerView(context);
|
||||||
|
list.setNestedScrollingEnabled(false);
|
||||||
|
LinearLayout.LayoutParams listParams = new LinearLayout.LayoutParams(
|
||||||
|
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||||
|
ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||||
|
listParams.topMargin = dp(context, 12);
|
||||||
|
content.addView(list, listParams);
|
||||||
|
bindOrderingList(context, prefs, list);
|
||||||
|
|
||||||
|
TextView sortTitle = body(context, R.string.layout_sort_icons_title);
|
||||||
|
sortTitle.setTypeface(sortTitle.getTypeface(), android.graphics.Typeface.BOLD);
|
||||||
|
LinearLayout.LayoutParams sortTitleParams = new LinearLayout.LayoutParams(
|
||||||
|
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||||
|
ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||||
|
sortTitleParams.topMargin = dp(context, 16);
|
||||||
|
content.addView(sortTitle, sortTitleParams);
|
||||||
|
|
||||||
|
RadioGroup sortGroup = new RadioGroup(context);
|
||||||
|
sortGroup.setOrientation(RadioGroup.HORIZONTAL);
|
||||||
|
addSortButton(context, sortGroup, R.string.layout_sort_icons_stock, "stock");
|
||||||
|
addSortButton(context, sortGroup, R.string.layout_sort_icons_reversed, "reversed");
|
||||||
|
addSortButton(context, sortGroup, R.string.layout_sort_icons_automatic, "automatic");
|
||||||
|
sortGroup.check(idForTag(sortGroup, prefs.getString(
|
||||||
|
SbtSettings.KEY_LAYOUT_NOTIF_SORT_ORDER,
|
||||||
|
SbtDefaults.LAYOUT_NOTIF_SORT_ORDER_DEFAULT)));
|
||||||
|
sortGroup.setOnCheckedChangeListener((group, checkedId) -> {
|
||||||
|
prefs.edit()
|
||||||
|
.putString(SbtSettings.KEY_LAYOUT_NOTIF_SORT_ORDER, tagForId(group, checkedId))
|
||||||
|
.apply();
|
||||||
|
SbtSettings.ensureReadable(context);
|
||||||
|
});
|
||||||
|
LinearLayout.LayoutParams sortParams = new LinearLayout.LayoutParams(
|
||||||
|
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||||
|
ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||||
|
sortParams.topMargin = dp(context, 8);
|
||||||
|
content.addView(sortGroup, sortParams);
|
||||||
|
|
||||||
|
return card(context, R.string.layout_ordering_title, content);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void bindOrderingList(Context context, SharedPreferences prefs, RecyclerView list) {
|
||||||
|
ArrayList<OrderingItem> items = parseOrderingItems(
|
||||||
|
context,
|
||||||
|
prefs.getString(SbtSettings.KEY_LAYOUT_ITEM_ORDER, SbtDefaults.LAYOUT_ITEM_ORDER_DEFAULT));
|
||||||
|
|
||||||
|
list.setLayoutManager(new LinearLayoutManager(context));
|
||||||
|
OrderingAdapter adapter = new OrderingAdapter(items, order -> {
|
||||||
|
prefs.edit().putString(SbtSettings.KEY_LAYOUT_ITEM_ORDER, order).apply();
|
||||||
|
SbtSettings.ensureReadable(context);
|
||||||
|
});
|
||||||
|
list.setAdapter(adapter);
|
||||||
|
|
||||||
|
final ItemTouchHelper[] helperHolder = new ItemTouchHelper[1];
|
||||||
|
ItemTouchHelper helper = new ItemTouchHelper(new ItemTouchHelper.SimpleCallback(
|
||||||
|
ItemTouchHelper.UP | ItemTouchHelper.DOWN, 0) {
|
||||||
|
@Override
|
||||||
|
public boolean onMove(
|
||||||
|
@NonNull RecyclerView recyclerView,
|
||||||
|
@NonNull RecyclerView.ViewHolder viewHolder,
|
||||||
|
@NonNull RecyclerView.ViewHolder target
|
||||||
|
) {
|
||||||
|
int from = viewHolder.getAdapterPosition();
|
||||||
|
int to = target.getAdapterPosition();
|
||||||
|
if (from == RecyclerView.NO_POSITION || to == RecyclerView.NO_POSITION) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
adapter.moveItem(from, to);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int direction) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isLongPressDragEnabled() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
helper.attachToRecyclerView(list);
|
||||||
|
helperHolder[0] = helper;
|
||||||
|
adapter.setDragStarter(holder -> {
|
||||||
|
ItemTouchHelper current = helperHolder[0];
|
||||||
|
if (current != null) {
|
||||||
|
current.startDrag(holder);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ArrayList<OrderingItem> parseOrderingItems(Context context, @Nullable String encoded) {
|
||||||
|
if (encoded == null || encoded.isEmpty() || "clock,notifications,status".equals(encoded)) {
|
||||||
|
encoded = SbtDefaults.LAYOUT_ITEM_ORDER_DEFAULT;
|
||||||
|
}
|
||||||
|
ArrayList<OrderingItem> items = new ArrayList<>();
|
||||||
|
for (String token : encoded.split(",")) {
|
||||||
|
addOrderingItemIfValid(context, items, token != null ? token.trim() : "");
|
||||||
|
}
|
||||||
|
addOrderingItemIfValid(context, items, "clock");
|
||||||
|
addOrderingItemIfValid(context, items, "chip");
|
||||||
|
addOrderingItemIfValid(context, items, "status");
|
||||||
|
addOrderingItemIfValid(context, items, "notifications");
|
||||||
|
return items;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void addOrderingItemIfValid(Context context, ArrayList<OrderingItem> items, String key) {
|
||||||
|
if (key == null || key.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (OrderingItem item : items) {
|
||||||
|
if (item.key.equals(key)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
int labelRes;
|
||||||
|
if ("clock".equals(key)) {
|
||||||
|
labelRes = R.string.layout_ordering_clock;
|
||||||
|
} else if ("chip".equals(key)) {
|
||||||
|
labelRes = R.string.layout_ordering_chip;
|
||||||
|
} else if ("notifications".equals(key)) {
|
||||||
|
labelRes = R.string.layout_ordering_notif_icons;
|
||||||
|
} else if ("status".equals(key)) {
|
||||||
|
labelRes = R.string.layout_ordering_status_icons;
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
items.add(new OrderingItem(key, context.getString(labelRes)));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void addSortButton(Context context, RadioGroup group, int labelId, String tag) {
|
||||||
|
RadioButton button = new RadioButton(context);
|
||||||
|
button.setId(View.generateViewId());
|
||||||
|
button.setTag(tag);
|
||||||
|
button.setText(labelId);
|
||||||
|
button.setSingleLine(true);
|
||||||
|
button.setMaxLines(1);
|
||||||
|
group.addView(button, new RadioGroup.LayoutParams(
|
||||||
|
0,
|
||||||
|
ViewGroup.LayoutParams.WRAP_CONTENT,
|
||||||
|
1f));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int idForTag(RadioGroup group, @Nullable String tag) {
|
||||||
|
String normalized = tag != null ? tag : "automatic";
|
||||||
|
for (int i = 0; i < group.getChildCount(); i++) {
|
||||||
|
View child = group.getChildAt(i);
|
||||||
|
if (normalized.equals(child.getTag())) {
|
||||||
|
return child.getId();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return group.getChildCount() > 0 ? group.getChildAt(group.getChildCount() - 1).getId() : View.NO_ID;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String tagForId(RadioGroup group, int id) {
|
||||||
|
View child = group.findViewById(id);
|
||||||
|
Object tag = child != null ? child.getTag() : null;
|
||||||
|
return tag instanceof String value ? value : "automatic";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static MaterialCardView card(Context context, int titleId, View content) {
|
||||||
|
MaterialCardView card = new MaterialCardView(context);
|
||||||
|
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
|
||||||
|
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||||
|
ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||||
|
params.topMargin = dp(context, 16);
|
||||||
|
card.setLayoutParams(params);
|
||||||
|
card.setUseCompatPadding(true);
|
||||||
|
card.setStrokeColor(context.getColor(R.color.sbt_card_outline));
|
||||||
|
card.setStrokeWidth(dp(context, 1));
|
||||||
|
|
||||||
|
LinearLayout inner = vertical(context);
|
||||||
|
inner.setPadding(dp(context, 16), dp(context, 16), dp(context, 16), dp(context, 16));
|
||||||
|
inner.addView(sectionTitle(context, titleId));
|
||||||
|
inner.addView(content);
|
||||||
|
card.addView(inner);
|
||||||
|
return card;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static LinearLayout vertical(Context context) {
|
||||||
|
LinearLayout layout = new LinearLayout(context);
|
||||||
|
layout.setOrientation(LinearLayout.VERTICAL);
|
||||||
|
return layout;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static TextView sectionTitle(Context context, int stringId) {
|
||||||
|
TextView view = new TextView(context);
|
||||||
|
view.setText(stringId);
|
||||||
|
view.setTextAppearance(com.google.android.material.R.style.TextAppearance_MaterialComponents_Subtitle1);
|
||||||
|
view.setAllCaps(true);
|
||||||
|
view.setTypeface(view.getTypeface(), android.graphics.Typeface.BOLD);
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static TextView body(Context context, int stringId) {
|
||||||
|
TextView view = new TextView(context);
|
||||||
|
view.setText(stringId);
|
||||||
|
view.setTextAppearance(com.google.android.material.R.style.TextAppearance_MaterialComponents_Body2);
|
||||||
|
view.setPadding(0, dp(context, 8), 0, 0);
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int dp(Context context, int value) {
|
||||||
|
return Math.round(value * context.getResources().getDisplayMetrics().density);
|
||||||
|
}
|
||||||
|
|
||||||
|
private record OrderingItem(String key, String label) {
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class OrderingAdapter extends RecyclerView.Adapter<OrderingViewHolder> {
|
||||||
|
interface DragStarter {
|
||||||
|
void startDrag(OrderingViewHolder holder);
|
||||||
|
}
|
||||||
|
|
||||||
|
interface OrderChangedListener {
|
||||||
|
void onOrderChanged(String encodedOrder);
|
||||||
|
}
|
||||||
|
|
||||||
|
private final List<OrderingItem> items;
|
||||||
|
private final OrderChangedListener orderChangedListener;
|
||||||
|
private DragStarter dragStarter;
|
||||||
|
|
||||||
|
OrderingAdapter(List<OrderingItem> items, OrderChangedListener orderChangedListener) {
|
||||||
|
this.items = items;
|
||||||
|
this.orderChangedListener = orderChangedListener;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setDragStarter(DragStarter dragStarter) {
|
||||||
|
this.dragStarter = dragStarter;
|
||||||
|
}
|
||||||
|
|
||||||
|
void moveItem(int from, int to) {
|
||||||
|
if (from == to || from < 0 || to < 0 || from >= items.size() || to >= items.size()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Collections.swap(items, from, to);
|
||||||
|
notifyItemMoved(from, to);
|
||||||
|
orderChangedListener.onOrderChanged(encodeOrder());
|
||||||
|
}
|
||||||
|
|
||||||
|
private String encodeOrder() {
|
||||||
|
StringBuilder builder = new StringBuilder();
|
||||||
|
for (OrderingItem item : items) {
|
||||||
|
if (builder.length() > 0) {
|
||||||
|
builder.append(',');
|
||||||
|
}
|
||||||
|
builder.append(item.key);
|
||||||
|
}
|
||||||
|
return builder.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public OrderingViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||||
|
View view = LayoutInflater.from(parent.getContext())
|
||||||
|
.inflate(R.layout.item_layout_ordering, parent, false);
|
||||||
|
return new OrderingViewHolder(view);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBindViewHolder(@NonNull OrderingViewHolder holder, int position) {
|
||||||
|
holder.label.setText(items.get(position).label);
|
||||||
|
holder.handle.setOnTouchListener((v, event) -> {
|
||||||
|
if (event.getActionMasked() == MotionEvent.ACTION_DOWN && dragStarter != null) {
|
||||||
|
dragStarter.startDrag(holder);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getItemCount() {
|
||||||
|
return items.size();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class OrderingViewHolder extends RecyclerView.ViewHolder {
|
||||||
|
final TextView label;
|
||||||
|
final TextView handle;
|
||||||
|
|
||||||
|
OrderingViewHolder(@NonNull View itemView) {
|
||||||
|
super(itemView);
|
||||||
|
label = itemView.findViewById(R.id.layout_ordering_item_label);
|
||||||
|
handle = itemView.findViewById(R.id.layout_ordering_item_handle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,157 @@
|
|||||||
|
package se.ajpanton.statusbartweak.shell.ui;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.SharedPreferences;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.RadioButton;
|
||||||
|
import android.widget.RadioGroup;
|
||||||
|
import android.widget.TextView;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import se.ajpanton.statusbartweak.R;
|
||||||
|
import se.ajpanton.statusbartweak.shell.debug.NotificationDisplayStyleSettings;
|
||||||
|
import se.ajpanton.statusbartweak.shell.settings.SbtSettings;
|
||||||
|
|
||||||
|
final class LockedNotificationModeUi {
|
||||||
|
static final String MODE_CARDS = "cards";
|
||||||
|
static final String MODE_ICONS = "icons";
|
||||||
|
static final String MODE_DOT = "dot";
|
||||||
|
static final String MODE_NONE = "none";
|
||||||
|
|
||||||
|
private LockedNotificationModeUi() {
|
||||||
|
}
|
||||||
|
|
||||||
|
static void bind(
|
||||||
|
Context context,
|
||||||
|
SharedPreferences prefs,
|
||||||
|
RadioGroup group,
|
||||||
|
TextView hint,
|
||||||
|
boolean layoutEnabled
|
||||||
|
) {
|
||||||
|
bind(context, prefs, group, hint, layoutEnabled, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void bind(
|
||||||
|
Context context,
|
||||||
|
SharedPreferences prefs,
|
||||||
|
RadioGroup group,
|
||||||
|
TextView hint,
|
||||||
|
boolean layoutEnabled,
|
||||||
|
Runnable onChanged
|
||||||
|
) {
|
||||||
|
if (context == null || prefs == null || group == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
group.setOnCheckedChangeListener(null);
|
||||||
|
RadioButton cards = button(context, R.string.debug_notification_style_cards, MODE_CARDS);
|
||||||
|
RadioButton icons = button(context, R.string.debug_notification_style_icons, MODE_ICONS);
|
||||||
|
RadioButton dot = button(context, R.string.debug_notification_style_dot, MODE_DOT);
|
||||||
|
RadioButton none = button(context, R.string.layout_locked_notifications_none, MODE_NONE);
|
||||||
|
group.removeAllViews();
|
||||||
|
group.addView(cards);
|
||||||
|
group.addView(icons);
|
||||||
|
group.addView(dot);
|
||||||
|
group.addView(none);
|
||||||
|
|
||||||
|
String stored = prefs.getString(SbtSettings.KEY_LOCKED_NOTIFICATION_MODE, MODE_DOT);
|
||||||
|
NotificationDisplayStyleSettings.Style androidStyle =
|
||||||
|
NotificationDisplayStyleSettings.read(context).style();
|
||||||
|
String current = currentMode(androidStyle, stored, layoutEnabled);
|
||||||
|
if (layoutEnabled && MODE_NONE.equals(stored)
|
||||||
|
&& androidStyle != NotificationDisplayStyleSettings.Style.DOT) {
|
||||||
|
writeMode(context, MODE_NONE);
|
||||||
|
}
|
||||||
|
group.check(idForMode(group, current));
|
||||||
|
|
||||||
|
none.setEnabled(layoutEnabled);
|
||||||
|
none.setAlpha(layoutEnabled ? 1f : 0.45f);
|
||||||
|
if (hint != null) {
|
||||||
|
boolean rememberedNone = MODE_NONE.equals(stored) && !layoutEnabled;
|
||||||
|
hint.setText(rememberedNone
|
||||||
|
? R.string.layout_locked_notifications_none_disabled
|
||||||
|
: R.string.layout_locked_notifications_hint);
|
||||||
|
}
|
||||||
|
|
||||||
|
group.setOnCheckedChangeListener((radioGroup, checkedId) -> {
|
||||||
|
String mode = modeForId(radioGroup, checkedId);
|
||||||
|
if (MODE_NONE.equals(mode) && !layoutEnabled) {
|
||||||
|
radioGroup.check(idForMode(radioGroup, MODE_DOT));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
boolean ok = writeMode(context, mode);
|
||||||
|
if (!ok) {
|
||||||
|
Toast.makeText(
|
||||||
|
context,
|
||||||
|
R.string.debug_notification_style_write_failed,
|
||||||
|
Toast.LENGTH_SHORT).show();
|
||||||
|
bind(context, prefs, group, hint, layoutEnabled);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
prefs.edit().putString(SbtSettings.KEY_LOCKED_NOTIFICATION_MODE, mode).apply();
|
||||||
|
SbtSettings.ensureReadable(context);
|
||||||
|
if (onChanged != null) {
|
||||||
|
onChanged.run();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static String currentMode(Context context, SharedPreferences prefs, boolean layoutEnabled) {
|
||||||
|
String stored = prefs.getString(SbtSettings.KEY_LOCKED_NOTIFICATION_MODE, MODE_DOT);
|
||||||
|
NotificationDisplayStyleSettings.Style style =
|
||||||
|
NotificationDisplayStyleSettings.read(context).style();
|
||||||
|
return currentMode(style, stored, layoutEnabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String currentMode(
|
||||||
|
NotificationDisplayStyleSettings.Style androidStyle,
|
||||||
|
String stored,
|
||||||
|
boolean layoutEnabled
|
||||||
|
) {
|
||||||
|
if (MODE_NONE.equals(stored) && layoutEnabled) {
|
||||||
|
return MODE_NONE;
|
||||||
|
}
|
||||||
|
if (androidStyle == NotificationDisplayStyleSettings.Style.CARDS) {
|
||||||
|
return MODE_CARDS;
|
||||||
|
}
|
||||||
|
if (androidStyle == NotificationDisplayStyleSettings.Style.ICONS) {
|
||||||
|
return MODE_ICONS;
|
||||||
|
}
|
||||||
|
return MODE_DOT;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean writeMode(Context context, String mode) {
|
||||||
|
NotificationDisplayStyleSettings.Style style;
|
||||||
|
if (MODE_CARDS.equals(mode)) {
|
||||||
|
style = NotificationDisplayStyleSettings.Style.CARDS;
|
||||||
|
} else if (MODE_ICONS.equals(mode)) {
|
||||||
|
style = NotificationDisplayStyleSettings.Style.ICONS;
|
||||||
|
} else {
|
||||||
|
style = NotificationDisplayStyleSettings.Style.DOT;
|
||||||
|
}
|
||||||
|
return NotificationDisplayStyleSettings.write(context, style);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static RadioButton button(Context context, int text, String mode) {
|
||||||
|
RadioButton button = new RadioButton(context);
|
||||||
|
button.setId(View.generateViewId());
|
||||||
|
button.setTag(mode);
|
||||||
|
button.setText(text);
|
||||||
|
return button;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int idForMode(RadioGroup group, String mode) {
|
||||||
|
for (int i = 0; i < group.getChildCount(); i++) {
|
||||||
|
View child = group.getChildAt(i);
|
||||||
|
if (mode.equals(child.getTag())) {
|
||||||
|
return child.getId();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return group.getChildCount() > 0 ? group.getChildAt(0).getId() : View.NO_ID;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String modeForId(RadioGroup group, int id) {
|
||||||
|
View view = group.findViewById(id);
|
||||||
|
Object tag = view != null ? view.getTag() : null;
|
||||||
|
return tag instanceof String value ? value : MODE_DOT;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,949 @@
|
|||||||
|
package se.ajpanton.statusbartweak.shell.ui;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.SharedPreferences;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.text.InputFilter;
|
||||||
|
import android.view.Gravity;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.CheckBox;
|
||||||
|
import android.widget.EditText;
|
||||||
|
import android.widget.LinearLayout;
|
||||||
|
import android.widget.RadioGroup;
|
||||||
|
import android.widget.ScrollView;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.fragment.app.Fragment;
|
||||||
|
|
||||||
|
import com.google.android.material.button.MaterialButton;
|
||||||
|
import com.google.android.material.card.MaterialCardView;
|
||||||
|
|
||||||
|
import se.ajpanton.statusbartweak.R;
|
||||||
|
import se.ajpanton.statusbartweak.shell.settings.SbtDefaults;
|
||||||
|
import se.ajpanton.statusbartweak.shell.settings.SbtSettings;
|
||||||
|
|
||||||
|
abstract class LockedSceneLayoutFragment extends Fragment {
|
||||||
|
private final boolean aod;
|
||||||
|
private RadioGroup modeGroup;
|
||||||
|
private TextView modeHint;
|
||||||
|
private LinearLayout dynamicContent;
|
||||||
|
|
||||||
|
LockedSceneLayoutFragment(boolean aod) {
|
||||||
|
this.aod = aod;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public View onCreateView(@NonNull LayoutInflater inflater,
|
||||||
|
@Nullable ViewGroup container,
|
||||||
|
@Nullable Bundle savedInstanceState) {
|
||||||
|
Context context = requireContext();
|
||||||
|
SharedPreferences prefs = context.getSharedPreferences(SbtSettings.PREFS_NAME, Context.MODE_PRIVATE);
|
||||||
|
ScrollView scroll = new ScrollView(context);
|
||||||
|
LinearLayout root = vertical(context);
|
||||||
|
root.setPadding(dp(20), dp(20), dp(20), dp(20));
|
||||||
|
scroll.addView(root);
|
||||||
|
|
||||||
|
root.addView(title(context, aod ? R.string.section_aod : R.string.section_lockscreen));
|
||||||
|
modeGroup = new RadioGroup(context);
|
||||||
|
modeGroup.setOrientation(RadioGroup.VERTICAL);
|
||||||
|
modeHint = body(context, R.string.layout_locked_notifications_hint);
|
||||||
|
LinearLayout modeContent = vertical(context);
|
||||||
|
modeContent.addView(modeHint);
|
||||||
|
modeContent.addView(modeGroup);
|
||||||
|
root.addView(card(context, R.string.layout_locked_notifications_title, modeContent));
|
||||||
|
|
||||||
|
dynamicContent = vertical(context);
|
||||||
|
root.addView(dynamicContent);
|
||||||
|
bindMode(context, prefs);
|
||||||
|
return scroll;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void bindMode(Context context, SharedPreferences prefs) {
|
||||||
|
boolean layoutEnabled = prefs.getBoolean(SbtSettings.KEY_CLOCK_ENABLED, SbtDefaults.CLOCK_ENABLED_DEFAULT);
|
||||||
|
LockedNotificationModeUi.bind(
|
||||||
|
context,
|
||||||
|
prefs,
|
||||||
|
modeGroup,
|
||||||
|
modeHint,
|
||||||
|
layoutEnabled,
|
||||||
|
() -> rebuildDynamic(
|
||||||
|
context,
|
||||||
|
prefs,
|
||||||
|
LockedNotificationModeUi.currentMode(context, prefs, layoutEnabled)));
|
||||||
|
rebuildDynamic(context, prefs, LockedNotificationModeUi.currentMode(context, prefs, layoutEnabled));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void rebuildDynamic(Context context, SharedPreferences prefs, String mode) {
|
||||||
|
dynamicContent.removeAllViews();
|
||||||
|
if (LockedNotificationModeUi.MODE_DOT.equals(mode)
|
||||||
|
|| LockedNotificationModeUi.MODE_NONE.equals(mode)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!aod) {
|
||||||
|
View clockCard = configuredPositionCardFromLayout(
|
||||||
|
context,
|
||||||
|
prefs,
|
||||||
|
R.id.layout_clock_card,
|
||||||
|
R.id.clock_position_mode_lockscreen,
|
||||||
|
SbtSettings.KEY_CLOCK_ENABLED_LOCKSCREEN,
|
||||||
|
SbtDefaults.CLOCK_ENABLED_LOCKSCREEN_DEFAULT,
|
||||||
|
R.id.clock_position_group,
|
||||||
|
R.id.clock_position_middle_options_container,
|
||||||
|
R.id.clock_middle_auto_nearest,
|
||||||
|
R.id.clock_middle_side_prompt,
|
||||||
|
R.id.clock_middle_side_group,
|
||||||
|
R.id.clock_middle_side_right,
|
||||||
|
R.id.clock_vertical_offset_input,
|
||||||
|
R.id.clock_vertical_offset_minus_fast,
|
||||||
|
R.id.clock_vertical_offset_minus,
|
||||||
|
R.id.clock_vertical_offset_plus,
|
||||||
|
R.id.clock_vertical_offset_plus_fast,
|
||||||
|
R.id.clock_vertical_offset_reset,
|
||||||
|
sceneKey("clock_position"),
|
||||||
|
SbtDefaults.CLOCK_POSITION_DEFAULT,
|
||||||
|
sceneKey("clock_middle_auto_nearest_side"),
|
||||||
|
SbtDefaults.CLOCK_MIDDLE_AUTO_NEAREST_SIDE_DEFAULT,
|
||||||
|
sceneKey("clock_middle_side"),
|
||||||
|
SbtDefaults.CLOCK_MIDDLE_CUTOUT_SIDE_DEFAULT,
|
||||||
|
sceneKey("clock_vertical_offset_px"),
|
||||||
|
SbtDefaults.CLOCK_VERTICAL_OFFSET_PX_DEFAULT);
|
||||||
|
dynamicContent.addView(clockCard);
|
||||||
|
}
|
||||||
|
dynamicContent.addView(configuredIconContainersCardFromLayout(
|
||||||
|
context,
|
||||||
|
prefs,
|
||||||
|
R.id.layout_status_card,
|
||||||
|
"status",
|
||||||
|
aod ? R.id.status_position_mode_aod : R.id.status_position_mode_lockscreen,
|
||||||
|
R.id.status_position_group,
|
||||||
|
R.id.status_position_middle_options_container,
|
||||||
|
R.id.status_middle_auto_nearest,
|
||||||
|
R.id.status_middle_side_prompt,
|
||||||
|
R.id.status_middle_side_group,
|
||||||
|
R.id.status_middle_side_right,
|
||||||
|
R.id.status_vertical_offset_input,
|
||||||
|
R.id.status_vertical_offset_minus_fast,
|
||||||
|
R.id.status_vertical_offset_minus,
|
||||||
|
R.id.status_vertical_offset_plus,
|
||||||
|
R.id.status_vertical_offset_plus_fast,
|
||||||
|
R.id.status_vertical_offset_reset,
|
||||||
|
R.string.layout_status_position_title,
|
||||||
|
aod ? SbtSettings.KEY_LAYOUT_STATUS_ENABLED_AOD : SbtSettings.KEY_LAYOUT_STATUS_ENABLED_LOCKSCREEN,
|
||||||
|
aod ? SbtDefaults.LAYOUT_STATUS_ENABLED_AOD_DEFAULT : SbtDefaults.LAYOUT_STATUS_ENABLED_LOCKSCREEN_DEFAULT,
|
||||||
|
SbtDefaults.LAYOUT_STATUS_POSITION_DEFAULT,
|
||||||
|
SbtDefaults.LAYOUT_STATUS_MIDDLE_AUTO_NEAREST_SIDE_DEFAULT,
|
||||||
|
SbtDefaults.LAYOUT_STATUS_MIDDLE_SIDE_DEFAULT,
|
||||||
|
SbtDefaults.LAYOUT_STATUS_VERTICAL_OFFSET_PX_DEFAULT));
|
||||||
|
if (LockedNotificationModeUi.MODE_CARDS.equals(mode)) {
|
||||||
|
dynamicContent.addView(cardsNotificationCard(context, prefs));
|
||||||
|
} else {
|
||||||
|
dynamicContent.addView(configuredIconContainersCardFromLayout(
|
||||||
|
context,
|
||||||
|
prefs,
|
||||||
|
R.id.layout_notif_card,
|
||||||
|
"notifications",
|
||||||
|
aod ? R.id.notif_position_mode_aod : R.id.notif_position_mode_lockscreen,
|
||||||
|
R.id.notif_position_group,
|
||||||
|
R.id.notif_position_middle_options_container,
|
||||||
|
R.id.notif_middle_auto_nearest,
|
||||||
|
R.id.notif_middle_side_prompt,
|
||||||
|
R.id.notif_middle_side_group,
|
||||||
|
R.id.notif_middle_side_right,
|
||||||
|
R.id.notif_vertical_offset_input,
|
||||||
|
R.id.notif_vertical_offset_minus_fast,
|
||||||
|
R.id.notif_vertical_offset_minus,
|
||||||
|
R.id.notif_vertical_offset_plus,
|
||||||
|
R.id.notif_vertical_offset_plus_fast,
|
||||||
|
R.id.notif_vertical_offset_reset,
|
||||||
|
R.string.layout_notification_position_title,
|
||||||
|
aod ? SbtSettings.KEY_LAYOUT_NOTIF_ENABLED_AOD : SbtSettings.KEY_LAYOUT_NOTIF_ENABLED_LOCKSCREEN,
|
||||||
|
aod ? SbtDefaults.LAYOUT_NOTIF_ENABLED_AOD_DEFAULT : SbtDefaults.LAYOUT_NOTIF_ENABLED_LOCKSCREEN_DEFAULT,
|
||||||
|
SbtDefaults.LAYOUT_NOTIF_POSITION_DEFAULT,
|
||||||
|
SbtDefaults.LAYOUT_NOTIF_MIDDLE_AUTO_NEAREST_SIDE_DEFAULT,
|
||||||
|
SbtDefaults.LAYOUT_NOTIF_MIDDLE_SIDE_DEFAULT,
|
||||||
|
SbtDefaults.LAYOUT_NOTIF_VERTICAL_OFFSET_PX_DEFAULT));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private View configuredPositionCardFromLayout(
|
||||||
|
Context context,
|
||||||
|
SharedPreferences prefs,
|
||||||
|
int cardId,
|
||||||
|
int enabledCheckboxId,
|
||||||
|
String enabledKey,
|
||||||
|
boolean enabledDefault,
|
||||||
|
int positionGroupId,
|
||||||
|
int middleOptionsId,
|
||||||
|
int autoNearestId,
|
||||||
|
int promptId,
|
||||||
|
int middleSideGroupId,
|
||||||
|
int middleSideRightId,
|
||||||
|
int verticalInputId,
|
||||||
|
int verticalMinusFastId,
|
||||||
|
int verticalMinusId,
|
||||||
|
int verticalPlusId,
|
||||||
|
int verticalPlusFastId,
|
||||||
|
int verticalResetId,
|
||||||
|
String positionKey,
|
||||||
|
String positionDefault,
|
||||||
|
String autoNearestKey,
|
||||||
|
boolean autoNearestDefault,
|
||||||
|
String middleSideKey,
|
||||||
|
String middleSideDefault,
|
||||||
|
String verticalOffsetKey,
|
||||||
|
int verticalOffsetDefault
|
||||||
|
) {
|
||||||
|
View card = inflateLayoutCard(context, cardId);
|
||||||
|
configureSingleEnabledCheckbox(card, prefs, enabledCheckboxId, enabledKey, enabledDefault);
|
||||||
|
bindXmlPositionSection(
|
||||||
|
card,
|
||||||
|
prefs,
|
||||||
|
positionGroupId,
|
||||||
|
middleOptionsId,
|
||||||
|
autoNearestId,
|
||||||
|
promptId,
|
||||||
|
middleSideGroupId,
|
||||||
|
middleSideRightId,
|
||||||
|
positionKey,
|
||||||
|
positionDefault,
|
||||||
|
autoNearestKey,
|
||||||
|
autoNearestDefault,
|
||||||
|
middleSideKey,
|
||||||
|
middleSideDefault);
|
||||||
|
bindXmlStepper(
|
||||||
|
prefs,
|
||||||
|
verticalOffsetKey,
|
||||||
|
card.findViewById(verticalInputId),
|
||||||
|
card.findViewById(verticalMinusFastId),
|
||||||
|
card.findViewById(verticalMinusId),
|
||||||
|
card.findViewById(verticalPlusId),
|
||||||
|
card.findViewById(verticalPlusFastId),
|
||||||
|
card.findViewById(verticalResetId),
|
||||||
|
verticalOffsetDefault,
|
||||||
|
SbtDefaults.CLOCK_VERTICAL_OFFSET_PX_MIN,
|
||||||
|
SbtDefaults.CLOCK_VERTICAL_OFFSET_PX_MAX);
|
||||||
|
return card;
|
||||||
|
}
|
||||||
|
|
||||||
|
private View configuredIconContainersCardFromLayout(
|
||||||
|
Context context,
|
||||||
|
SharedPreferences prefs,
|
||||||
|
int cardId,
|
||||||
|
String base,
|
||||||
|
int modeCheckboxId,
|
||||||
|
int positionGroupId,
|
||||||
|
int middleOptionsId,
|
||||||
|
int autoNearestId,
|
||||||
|
int promptId,
|
||||||
|
int middleSideGroupId,
|
||||||
|
int middleSideRightId,
|
||||||
|
int verticalInputId,
|
||||||
|
int verticalMinusFastId,
|
||||||
|
int verticalMinusId,
|
||||||
|
int verticalPlusId,
|
||||||
|
int verticalPlusFastId,
|
||||||
|
int verticalResetId,
|
||||||
|
int titleId,
|
||||||
|
String legacyEnabledKey,
|
||||||
|
boolean legacyEnabledDefault,
|
||||||
|
String positionDefault,
|
||||||
|
boolean autoNearestDefault,
|
||||||
|
String middleSideDefault,
|
||||||
|
int verticalOffsetDefault
|
||||||
|
) {
|
||||||
|
LinearLayout wrapper = vertical(context);
|
||||||
|
String countKey = sceneKey(base + "_count");
|
||||||
|
Runnable[] rebuild = new Runnable[1];
|
||||||
|
rebuild[0] = () -> {
|
||||||
|
wrapper.removeAllViews();
|
||||||
|
int count = iconContainerCount(prefs, countKey, legacyEnabledKey, legacyEnabledDefault);
|
||||||
|
View primary = configuredIconContainerCard(
|
||||||
|
context,
|
||||||
|
prefs,
|
||||||
|
cardId,
|
||||||
|
modeCheckboxId,
|
||||||
|
positionGroupId,
|
||||||
|
middleOptionsId,
|
||||||
|
autoNearestId,
|
||||||
|
promptId,
|
||||||
|
middleSideGroupId,
|
||||||
|
middleSideRightId,
|
||||||
|
verticalInputId,
|
||||||
|
verticalMinusFastId,
|
||||||
|
verticalMinusId,
|
||||||
|
verticalPlusId,
|
||||||
|
verticalPlusFastId,
|
||||||
|
verticalResetId,
|
||||||
|
titleId,
|
||||||
|
0,
|
||||||
|
indexedSceneKey(base + "_position", 0),
|
||||||
|
positionDefault,
|
||||||
|
indexedSceneKey(base + "_middle_auto_nearest_side", 0),
|
||||||
|
autoNearestDefault,
|
||||||
|
indexedSceneKey(base + "_middle_side", 0),
|
||||||
|
middleSideDefault,
|
||||||
|
indexedSceneKey(base + "_vertical_offset_px", 0),
|
||||||
|
verticalOffsetDefault,
|
||||||
|
legacyEnabledKey,
|
||||||
|
countKey,
|
||||||
|
count,
|
||||||
|
rebuild[0]);
|
||||||
|
wrapper.addView(primary);
|
||||||
|
for (int i = 1; i < count; i++) {
|
||||||
|
wrapper.addView(configuredIconContainerCard(
|
||||||
|
context,
|
||||||
|
prefs,
|
||||||
|
cardId,
|
||||||
|
modeCheckboxId,
|
||||||
|
positionGroupId,
|
||||||
|
middleOptionsId,
|
||||||
|
autoNearestId,
|
||||||
|
promptId,
|
||||||
|
middleSideGroupId,
|
||||||
|
middleSideRightId,
|
||||||
|
verticalInputId,
|
||||||
|
verticalMinusFastId,
|
||||||
|
verticalMinusId,
|
||||||
|
verticalPlusId,
|
||||||
|
verticalPlusFastId,
|
||||||
|
verticalResetId,
|
||||||
|
titleId,
|
||||||
|
i,
|
||||||
|
indexedSceneKey(base + "_position", i),
|
||||||
|
positionDefault,
|
||||||
|
indexedSceneKey(base + "_middle_auto_nearest_side", i),
|
||||||
|
autoNearestDefault,
|
||||||
|
indexedSceneKey(base + "_middle_side", i),
|
||||||
|
middleSideDefault,
|
||||||
|
indexedSceneKey(base + "_vertical_offset_px", i),
|
||||||
|
verticalOffsetDefault,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
count,
|
||||||
|
null));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
rebuild[0].run();
|
||||||
|
return wrapper;
|
||||||
|
}
|
||||||
|
|
||||||
|
private View configuredIconContainerCard(
|
||||||
|
Context context,
|
||||||
|
SharedPreferences prefs,
|
||||||
|
int cardId,
|
||||||
|
int modeCheckboxId,
|
||||||
|
int positionGroupId,
|
||||||
|
int middleOptionsId,
|
||||||
|
int autoNearestId,
|
||||||
|
int promptId,
|
||||||
|
int middleSideGroupId,
|
||||||
|
int middleSideRightId,
|
||||||
|
int verticalInputId,
|
||||||
|
int verticalMinusFastId,
|
||||||
|
int verticalMinusId,
|
||||||
|
int verticalPlusId,
|
||||||
|
int verticalPlusFastId,
|
||||||
|
int verticalResetId,
|
||||||
|
int titleId,
|
||||||
|
int index,
|
||||||
|
String positionKey,
|
||||||
|
String positionDefault,
|
||||||
|
String autoNearestKey,
|
||||||
|
boolean autoNearestDefault,
|
||||||
|
String middleSideKey,
|
||||||
|
String middleSideDefault,
|
||||||
|
String verticalOffsetKey,
|
||||||
|
int verticalOffsetDefault,
|
||||||
|
@Nullable String legacyEnabledKey,
|
||||||
|
@Nullable String countKey,
|
||||||
|
int count,
|
||||||
|
@Nullable Runnable onCountChanged
|
||||||
|
) {
|
||||||
|
View card = inflateLayoutCard(context, cardId);
|
||||||
|
removeSiblingModeCheckboxes(card, modeCheckboxId);
|
||||||
|
LinearLayout sectionContent = removeModeContainerForCheckbox(card, modeCheckboxId);
|
||||||
|
if (sectionContent != null && countKey != null && onCountChanged != null) {
|
||||||
|
int insertIndex = Math.min(1, sectionContent.getChildCount());
|
||||||
|
sectionContent.addView(
|
||||||
|
iconContainerCountControl(context, prefs, countKey, legacyEnabledKey, count, onCountChanged),
|
||||||
|
insertIndex);
|
||||||
|
}
|
||||||
|
if (index > 0) {
|
||||||
|
TextView title = findCardTitle(card);
|
||||||
|
if (title != null) {
|
||||||
|
title.setText(getString(titleId) + " " + (index + 1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bindXmlPositionSection(
|
||||||
|
card,
|
||||||
|
prefs,
|
||||||
|
positionGroupId,
|
||||||
|
middleOptionsId,
|
||||||
|
autoNearestId,
|
||||||
|
promptId,
|
||||||
|
middleSideGroupId,
|
||||||
|
middleSideRightId,
|
||||||
|
positionKey,
|
||||||
|
positionDefault,
|
||||||
|
autoNearestKey,
|
||||||
|
autoNearestDefault,
|
||||||
|
middleSideKey,
|
||||||
|
middleSideDefault);
|
||||||
|
bindXmlStepper(
|
||||||
|
prefs,
|
||||||
|
verticalOffsetKey,
|
||||||
|
card.findViewById(verticalInputId),
|
||||||
|
card.findViewById(verticalMinusFastId),
|
||||||
|
card.findViewById(verticalMinusId),
|
||||||
|
card.findViewById(verticalPlusId),
|
||||||
|
card.findViewById(verticalPlusFastId),
|
||||||
|
card.findViewById(verticalResetId),
|
||||||
|
verticalOffsetDefault,
|
||||||
|
SbtDefaults.CLOCK_VERTICAL_OFFSET_PX_MIN,
|
||||||
|
SbtDefaults.CLOCK_VERTICAL_OFFSET_PX_MAX);
|
||||||
|
return card;
|
||||||
|
}
|
||||||
|
|
||||||
|
private View inflateLayoutCard(Context context, int cardId) {
|
||||||
|
View fullLayout = LayoutInflater.from(context).inflate(R.layout.fragment_layout, dynamicContent, false);
|
||||||
|
View card = fullLayout.findViewById(cardId);
|
||||||
|
if (card == null) {
|
||||||
|
return new View(context);
|
||||||
|
}
|
||||||
|
if (card.getParent() instanceof ViewGroup parent) {
|
||||||
|
parent.removeView(card);
|
||||||
|
}
|
||||||
|
return card;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private TextView findCardTitle(View card) {
|
||||||
|
if (!(card instanceof ViewGroup cardGroup) || cardGroup.getChildCount() <= 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
View inner = cardGroup.getChildAt(0);
|
||||||
|
if (!(inner instanceof ViewGroup innerGroup) || innerGroup.getChildCount() <= 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
View title = innerGroup.getChildAt(0);
|
||||||
|
return title instanceof TextView textView ? textView : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void configureSingleEnabledCheckbox(
|
||||||
|
View root,
|
||||||
|
SharedPreferences prefs,
|
||||||
|
int enabledCheckboxId,
|
||||||
|
String key,
|
||||||
|
boolean defaultValue
|
||||||
|
) {
|
||||||
|
CheckBox enabled = root.findViewById(enabledCheckboxId);
|
||||||
|
if (enabled == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
hideSiblingCheckBoxes(enabled);
|
||||||
|
enabled.setText(R.string.layout_item_enabled);
|
||||||
|
enabled.setChecked(prefs.getBoolean(key, defaultValue));
|
||||||
|
enabled.setOnCheckedChangeListener((buttonView, isChecked) -> {
|
||||||
|
prefs.edit().putBoolean(key, isChecked).apply();
|
||||||
|
SbtSettings.ensureReadable(requireContext());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void removeSiblingModeCheckboxes(View root, int keptCheckboxId) {
|
||||||
|
CheckBox kept = root.findViewById(keptCheckboxId);
|
||||||
|
hideSiblingCheckBoxes(kept);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void hideSiblingCheckBoxes(@Nullable CheckBox kept) {
|
||||||
|
if (kept == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
View modeContainer = directSectionChild(kept);
|
||||||
|
if (modeContainer instanceof ViewGroup group) {
|
||||||
|
hideOtherCheckBoxes(group, kept);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void hideOtherCheckBoxes(View view, CheckBox kept) {
|
||||||
|
if (view instanceof CheckBox checkBox && checkBox != kept) {
|
||||||
|
checkBox.setVisibility(View.GONE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (view instanceof ViewGroup group) {
|
||||||
|
for (int i = 0; i < group.getChildCount(); i++) {
|
||||||
|
hideOtherCheckBoxes(group.getChildAt(i), kept);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private LinearLayout removeModeContainerForCheckbox(View root, int checkboxId) {
|
||||||
|
CheckBox checkbox = root.findViewById(checkboxId);
|
||||||
|
View modeContainer = directSectionChild(checkbox);
|
||||||
|
if (modeContainer == null || !(modeContainer.getParent() instanceof LinearLayout sectionContent)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
sectionContent.removeView(modeContainer);
|
||||||
|
return sectionContent;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private View directSectionChild(@Nullable View child) {
|
||||||
|
View current = child;
|
||||||
|
while (current != null && current.getParent() instanceof View parent) {
|
||||||
|
if (parent.getParent() instanceof MaterialCardView) {
|
||||||
|
return current;
|
||||||
|
}
|
||||||
|
current = parent;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private View cardsNotificationCard(Context context, SharedPreferences prefs) {
|
||||||
|
LinearLayout content = vertical(context);
|
||||||
|
content.addView(stepper(
|
||||||
|
context,
|
||||||
|
prefs,
|
||||||
|
R.string.label_max_rows,
|
||||||
|
aod ? SbtSettings.KEY_CARDS_AOD_MAX_ROWS : SbtSettings.KEY_CARDS_LOCK_MAX_ROWS,
|
||||||
|
aod ? SbtDefaults.CARDS_AOD_MAX_ROWS_DEFAULT : SbtDefaults.CARDS_LOCK_MAX_ROWS_DEFAULT,
|
||||||
|
aod ? SbtDefaults.CARDS_AOD_MAX_ROWS_MIN : SbtDefaults.CARDS_LOCK_MAX_ROWS_MIN,
|
||||||
|
aod ? SbtDefaults.CARDS_AOD_MAX_ROWS_MAX : SbtDefaults.CARDS_LOCK_MAX_ROWS_MAX));
|
||||||
|
content.addView(stepper(
|
||||||
|
context,
|
||||||
|
prefs,
|
||||||
|
R.string.label_max_icons_per_row,
|
||||||
|
aod ? SbtSettings.KEY_CARDS_AOD_MAX_ICONS_PER_ROW : SbtSettings.KEY_CARDS_LOCK_MAX_ICONS_PER_ROW,
|
||||||
|
aod ? SbtDefaults.CARDS_AOD_MAX_ICONS_PER_ROW_DEFAULT : SbtDefaults.CARDS_LOCK_MAX_ICONS_PER_ROW_DEFAULT,
|
||||||
|
aod ? SbtDefaults.CARDS_AOD_MAX_ICONS_PER_ROW_MIN : SbtDefaults.CARDS_LOCK_MAX_ICONS_PER_ROW_MIN,
|
||||||
|
aod ? SbtDefaults.CARDS_AOD_MAX_ICONS_PER_ROW_MAX : SbtDefaults.CARDS_LOCK_MAX_ICONS_PER_ROW_MAX));
|
||||||
|
content.addView(checkBox(
|
||||||
|
context,
|
||||||
|
prefs,
|
||||||
|
R.string.label_even_distribution,
|
||||||
|
aod ? SbtSettings.KEY_CARDS_AOD_EVEN_DISTRIBUTION : SbtSettings.KEY_CARDS_LOCK_EVEN_DISTRIBUTION,
|
||||||
|
aod ? SbtDefaults.CARDS_AOD_EVEN_DISTRIBUTION_DEFAULT : SbtDefaults.CARDS_LOCK_EVEN_DISTRIBUTION_DEFAULT));
|
||||||
|
content.addView(checkBox(
|
||||||
|
context,
|
||||||
|
prefs,
|
||||||
|
R.string.label_limit_screen_width,
|
||||||
|
aod ? SbtSettings.KEY_CARDS_AOD_LIMIT_TO_WIDTH : SbtSettings.KEY_CARDS_LOCK_LIMIT_TO_WIDTH,
|
||||||
|
aod ? SbtDefaults.CARDS_AOD_LIMIT_TO_WIDTH_DEFAULT : SbtDefaults.CARDS_LOCK_LIMIT_TO_WIDTH_DEFAULT));
|
||||||
|
return card(context, R.string.layout_notification_position_title, content);
|
||||||
|
}
|
||||||
|
|
||||||
|
private View iconContainerCountControl(
|
||||||
|
Context context,
|
||||||
|
SharedPreferences prefs,
|
||||||
|
String countKey,
|
||||||
|
String legacyEnabledKey,
|
||||||
|
int current,
|
||||||
|
Runnable onChanged
|
||||||
|
) {
|
||||||
|
LinearLayout outer = vertical(context);
|
||||||
|
outer.addView(body(context, R.string.layout_icon_container_count));
|
||||||
|
LinearLayout row = new LinearLayout(context);
|
||||||
|
row.setOrientation(LinearLayout.HORIZONTAL);
|
||||||
|
row.setGravity(Gravity.CENTER_VERTICAL);
|
||||||
|
MaterialButton minus = button(context, "-");
|
||||||
|
MaterialButton plus = button(context, "+");
|
||||||
|
EditText input = numericInput(context, false);
|
||||||
|
input.setFilters(new InputFilter[]{new InputFilter.LengthFilter(1)});
|
||||||
|
input.setText(String.valueOf(current));
|
||||||
|
row.addView(minus, buttonParams());
|
||||||
|
row.addView(input, inputParams());
|
||||||
|
row.addView(plus, buttonParams());
|
||||||
|
outer.addView(row);
|
||||||
|
|
||||||
|
View.OnClickListener listener = view -> {
|
||||||
|
int next = clampIconContainerCount(parseInt(input, current) + (view == minus ? -1 : 1));
|
||||||
|
persistIconContainerCount(prefs, countKey, legacyEnabledKey, next);
|
||||||
|
onChanged.run();
|
||||||
|
};
|
||||||
|
minus.setOnClickListener(listener);
|
||||||
|
plus.setOnClickListener(listener);
|
||||||
|
input.setOnFocusChangeListener((v, hasFocus) -> {
|
||||||
|
if (!hasFocus) {
|
||||||
|
int next = clampIconContainerCount(parseInt(input, current));
|
||||||
|
persistIconContainerCount(prefs, countKey, legacyEnabledKey, next);
|
||||||
|
onChanged.run();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return outer;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void bindXmlPositionSection(
|
||||||
|
View root,
|
||||||
|
SharedPreferences prefs,
|
||||||
|
int groupId,
|
||||||
|
int middleOptionsId,
|
||||||
|
int autoNearestId,
|
||||||
|
int promptId,
|
||||||
|
int middleSideGroupId,
|
||||||
|
int middleSideRightId,
|
||||||
|
String positionKey,
|
||||||
|
String positionDefault,
|
||||||
|
String autoNearestKey,
|
||||||
|
boolean autoNearestDefault,
|
||||||
|
String middleSideKey,
|
||||||
|
String middleSideDefault
|
||||||
|
) {
|
||||||
|
RadioGroup position = root.findViewById(groupId);
|
||||||
|
View middleOptions = root.findViewById(middleOptionsId);
|
||||||
|
CheckBox autoNearest = root.findViewById(autoNearestId);
|
||||||
|
TextView prompt = root.findViewById(promptId);
|
||||||
|
RadioGroup side = root.findViewById(middleSideGroupId);
|
||||||
|
if (position == null || middleOptions == null || autoNearest == null || prompt == null || side == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
position.check(positionIdForValue(groupId, prefs.getString(positionKey, positionDefault)));
|
||||||
|
autoNearest.setChecked(prefs.getBoolean(autoNearestKey, autoNearestDefault));
|
||||||
|
side.check("right".equals(prefs.getString(middleSideKey, middleSideDefault))
|
||||||
|
? middleSideRightId
|
||||||
|
: firstSideId(side, middleSideRightId));
|
||||||
|
updateMiddleOptions(middleOptions, "middle".equals(positionValueForId(position.getCheckedRadioButtonId())));
|
||||||
|
prompt.setText(autoNearest.isChecked()
|
||||||
|
? R.string.layout_middle_side_prompt_centred
|
||||||
|
: R.string.layout_middle_side_prompt_attach_only);
|
||||||
|
|
||||||
|
position.setOnCheckedChangeListener((group, checkedId) -> {
|
||||||
|
prefs.edit().putString(positionKey, positionValueForId(checkedId)).apply();
|
||||||
|
updateMiddleOptions(middleOptions, "middle".equals(positionValueForId(checkedId)));
|
||||||
|
SbtSettings.ensureReadable(requireContext());
|
||||||
|
});
|
||||||
|
autoNearest.setOnCheckedChangeListener((buttonView, isChecked) -> {
|
||||||
|
prefs.edit().putBoolean(autoNearestKey, isChecked).apply();
|
||||||
|
prompt.setText(isChecked
|
||||||
|
? R.string.layout_middle_side_prompt_centred
|
||||||
|
: R.string.layout_middle_side_prompt_attach_only);
|
||||||
|
SbtSettings.ensureReadable(requireContext());
|
||||||
|
});
|
||||||
|
side.setOnCheckedChangeListener((group, checkedId) -> {
|
||||||
|
prefs.edit().putString(middleSideKey, checkedId == middleSideRightId ? "right" : "left").apply();
|
||||||
|
SbtSettings.ensureReadable(requireContext());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void bindXmlStepper(
|
||||||
|
SharedPreferences prefs,
|
||||||
|
String key,
|
||||||
|
@Nullable EditText input,
|
||||||
|
@Nullable MaterialButton minusFast,
|
||||||
|
@Nullable MaterialButton minus,
|
||||||
|
@Nullable MaterialButton plus,
|
||||||
|
@Nullable MaterialButton plusFast,
|
||||||
|
@Nullable MaterialButton reset,
|
||||||
|
int defaultValue,
|
||||||
|
int min,
|
||||||
|
int max
|
||||||
|
) {
|
||||||
|
if (input == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int start = clamp(prefs.getInt(key, defaultValue), min, max);
|
||||||
|
input.setFilters(new InputFilter[]{new InputFilter.LengthFilter(4)});
|
||||||
|
input.setText(String.valueOf(start));
|
||||||
|
bindXmlStepperButton(prefs, key, input, minusFast, -5, min, max);
|
||||||
|
bindXmlStepperButton(prefs, key, input, minus, -1, min, max);
|
||||||
|
bindXmlStepperButton(prefs, key, input, plus, 1, min, max);
|
||||||
|
bindXmlStepperButton(prefs, key, input, plusFast, 5, min, max);
|
||||||
|
if (reset != null) {
|
||||||
|
reset.setOnClickListener(v -> {
|
||||||
|
input.setText(String.valueOf(defaultValue));
|
||||||
|
persistIntPref(prefs, key, defaultValue);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
input.setOnFocusChangeListener((v, hasFocus) -> {
|
||||||
|
if (!hasFocus) {
|
||||||
|
int next = clamp(parseInt(input, defaultValue), min, max);
|
||||||
|
input.setText(String.valueOf(next));
|
||||||
|
persistIntPref(prefs, key, next);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void bindXmlStepperButton(
|
||||||
|
SharedPreferences prefs,
|
||||||
|
String key,
|
||||||
|
EditText input,
|
||||||
|
@Nullable MaterialButton button,
|
||||||
|
int delta,
|
||||||
|
int min,
|
||||||
|
int max
|
||||||
|
) {
|
||||||
|
if (button == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
button.setOnClickListener(v -> {
|
||||||
|
int next = clamp(parseInt(input, 0) + delta, min, max);
|
||||||
|
input.setText(String.valueOf(next));
|
||||||
|
persistIntPref(prefs, key, next);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private int positionIdForValue(int groupId, @Nullable String value) {
|
||||||
|
String normalized = value != null ? value : "left";
|
||||||
|
if (groupId == R.id.clock_position_group) {
|
||||||
|
if ("right".equals(normalized)) {
|
||||||
|
return R.id.clock_position_right;
|
||||||
|
}
|
||||||
|
if ("middle".equals(normalized)) {
|
||||||
|
return R.id.clock_position_middle;
|
||||||
|
}
|
||||||
|
return R.id.clock_position_left;
|
||||||
|
}
|
||||||
|
if (groupId == R.id.notif_position_group) {
|
||||||
|
if ("right".equals(normalized)) {
|
||||||
|
return R.id.notif_position_right;
|
||||||
|
}
|
||||||
|
if ("middle".equals(normalized)) {
|
||||||
|
return R.id.notif_position_middle;
|
||||||
|
}
|
||||||
|
return R.id.notif_position_left;
|
||||||
|
}
|
||||||
|
if (groupId == R.id.status_position_group) {
|
||||||
|
if ("right".equals(normalized)) {
|
||||||
|
return R.id.status_position_right;
|
||||||
|
}
|
||||||
|
if ("middle".equals(normalized)) {
|
||||||
|
return R.id.status_position_middle;
|
||||||
|
}
|
||||||
|
return R.id.status_position_left;
|
||||||
|
}
|
||||||
|
return View.NO_ID;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String positionValueForId(int id) {
|
||||||
|
if (id == R.id.clock_position_right
|
||||||
|
|| id == R.id.notif_position_right
|
||||||
|
|| id == R.id.status_position_right) {
|
||||||
|
return "right";
|
||||||
|
}
|
||||||
|
if (id == R.id.clock_position_middle
|
||||||
|
|| id == R.id.notif_position_middle
|
||||||
|
|| id == R.id.status_position_middle) {
|
||||||
|
return "middle";
|
||||||
|
}
|
||||||
|
return "left";
|
||||||
|
}
|
||||||
|
|
||||||
|
private int firstSideId(RadioGroup group, int rightId) {
|
||||||
|
for (int i = 0; i < group.getChildCount(); i++) {
|
||||||
|
View child = group.getChildAt(i);
|
||||||
|
if (child.getId() != rightId) {
|
||||||
|
return child.getId();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return View.NO_ID;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateMiddleOptions(View view, boolean enabled) {
|
||||||
|
if (view.getVisibility() != View.VISIBLE) {
|
||||||
|
view.setVisibility(View.VISIBLE);
|
||||||
|
}
|
||||||
|
view.setEnabled(enabled);
|
||||||
|
view.setAlpha(enabled ? 1f : 0.45f);
|
||||||
|
if (view instanceof ViewGroup group) {
|
||||||
|
for (int i = 0; i < group.getChildCount(); i++) {
|
||||||
|
updateMiddleOptionChild(group.getChildAt(i), enabled);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateMiddleOptionChild(View view, boolean enabled) {
|
||||||
|
view.setEnabled(enabled);
|
||||||
|
if (view instanceof ViewGroup group) {
|
||||||
|
for (int i = 0; i < group.getChildCount(); i++) {
|
||||||
|
updateMiddleOptionChild(group.getChildAt(i), enabled);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private View stepper(Context context, SharedPreferences prefs, int label, String key, int def, int min, int max) {
|
||||||
|
LinearLayout outer = vertical(context);
|
||||||
|
outer.addView(body(context, label));
|
||||||
|
LinearLayout row = new LinearLayout(context);
|
||||||
|
row.setOrientation(LinearLayout.HORIZONTAL);
|
||||||
|
row.setGravity(Gravity.CENTER_VERTICAL);
|
||||||
|
MaterialButton minus = button(context, "-");
|
||||||
|
MaterialButton plus = button(context, "+");
|
||||||
|
EditText input = numericInput(context, true);
|
||||||
|
input.setText(String.valueOf(clamp(prefs.getInt(key, def), min, max)));
|
||||||
|
row.addView(minus, buttonParams());
|
||||||
|
row.addView(input, inputParams());
|
||||||
|
row.addView(plus, buttonParams());
|
||||||
|
outer.addView(row);
|
||||||
|
View.OnClickListener listener = view -> {
|
||||||
|
int delta = view == minus ? -1 : 1;
|
||||||
|
int next = clamp(parseInt(input, def) + delta, min, max);
|
||||||
|
input.setText(String.valueOf(next));
|
||||||
|
persistIntPref(prefs, key, next);
|
||||||
|
};
|
||||||
|
minus.setOnClickListener(listener);
|
||||||
|
plus.setOnClickListener(listener);
|
||||||
|
input.setOnFocusChangeListener((v, hasFocus) -> {
|
||||||
|
if (!hasFocus) {
|
||||||
|
int next = clamp(parseInt(input, def), min, max);
|
||||||
|
input.setText(String.valueOf(next));
|
||||||
|
persistIntPref(prefs, key, next);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return outer;
|
||||||
|
}
|
||||||
|
|
||||||
|
private EditText numericInput(Context context, boolean signed) {
|
||||||
|
EditText input = new EditText(context);
|
||||||
|
input.setSingleLine(true);
|
||||||
|
input.setGravity(Gravity.CENTER);
|
||||||
|
input.setInputType(signed
|
||||||
|
? android.text.InputType.TYPE_CLASS_NUMBER | android.text.InputType.TYPE_NUMBER_FLAG_SIGNED
|
||||||
|
: android.text.InputType.TYPE_CLASS_NUMBER);
|
||||||
|
input.setFilters(new InputFilter[]{new InputFilter.LengthFilter(4)});
|
||||||
|
return input;
|
||||||
|
}
|
||||||
|
|
||||||
|
private CheckBox checkBox(Context context, SharedPreferences prefs, int text, String key, boolean def) {
|
||||||
|
CheckBox checkBox = new CheckBox(context);
|
||||||
|
checkBox.setText(text);
|
||||||
|
checkBox.setChecked(prefs.getBoolean(key, def));
|
||||||
|
checkBox.setOnCheckedChangeListener((buttonView, isChecked) -> {
|
||||||
|
prefs.edit().putBoolean(key, isChecked).apply();
|
||||||
|
SbtSettings.ensureReadable(context);
|
||||||
|
});
|
||||||
|
return checkBox;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void persistIntPref(SharedPreferences prefs, String key, int value) {
|
||||||
|
prefs.edit().putInt(key, value).apply();
|
||||||
|
SbtSettings.ensureReadable(requireContext());
|
||||||
|
}
|
||||||
|
|
||||||
|
private int iconContainerCount(
|
||||||
|
SharedPreferences prefs,
|
||||||
|
String countKey,
|
||||||
|
String legacyEnabledKey,
|
||||||
|
boolean legacyEnabledDefault
|
||||||
|
) {
|
||||||
|
int fallback = prefs.getBoolean(legacyEnabledKey, legacyEnabledDefault) ? 1 : 0;
|
||||||
|
return clampIconContainerCount(prefs.getInt(countKey, fallback));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void persistIconContainerCount(
|
||||||
|
SharedPreferences prefs,
|
||||||
|
String countKey,
|
||||||
|
String legacyEnabledKey,
|
||||||
|
int count
|
||||||
|
) {
|
||||||
|
int clamped = clampIconContainerCount(count);
|
||||||
|
prefs.edit()
|
||||||
|
.putInt(countKey, clamped)
|
||||||
|
.putBoolean(legacyEnabledKey, clamped > 0)
|
||||||
|
.apply();
|
||||||
|
SbtSettings.ensureReadable(requireContext());
|
||||||
|
}
|
||||||
|
|
||||||
|
private int clampIconContainerCount(int value) {
|
||||||
|
return clamp(
|
||||||
|
value,
|
||||||
|
SbtDefaults.LAYOUT_ICON_CONTAINER_COUNT_MIN,
|
||||||
|
SbtDefaults.LAYOUT_ICON_CONTAINER_COUNT_MAX);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String sceneKey(String suffix) {
|
||||||
|
return (aod ? "layout_aod_" : "layout_lock_") + suffix;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String indexedSceneKey(String suffix, int index) {
|
||||||
|
return index <= 0 ? sceneKey(suffix) : sceneKey(suffix) + "_" + (index + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private MaterialButton button(Context context, String text) {
|
||||||
|
MaterialButton button = new MaterialButton(context, null, com.google.android.material.R.attr.materialButtonOutlinedStyle);
|
||||||
|
button.setText(text);
|
||||||
|
button.setMinWidth(dp(40));
|
||||||
|
button.setMinHeight(dp(40));
|
||||||
|
button.setPadding(0, button.getPaddingTop(), 0, button.getPaddingBottom());
|
||||||
|
return button;
|
||||||
|
}
|
||||||
|
|
||||||
|
private LinearLayout.LayoutParams buttonParams() {
|
||||||
|
return new LinearLayout.LayoutParams(dp(40), ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||||
|
}
|
||||||
|
|
||||||
|
private LinearLayout.LayoutParams inputParams() {
|
||||||
|
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
|
||||||
|
dp(48),
|
||||||
|
ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||||
|
params.leftMargin = dp(8);
|
||||||
|
params.rightMargin = dp(8);
|
||||||
|
return params;
|
||||||
|
}
|
||||||
|
|
||||||
|
private MaterialCardView card(Context context, int titleId, View content) {
|
||||||
|
MaterialCardView card = new MaterialCardView(context);
|
||||||
|
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
|
||||||
|
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||||
|
ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||||
|
params.topMargin = dp(16);
|
||||||
|
card.setLayoutParams(params);
|
||||||
|
card.setUseCompatPadding(true);
|
||||||
|
card.setStrokeColor(context.getColor(R.color.sbt_card_outline));
|
||||||
|
card.setStrokeWidth(dp(1));
|
||||||
|
LinearLayout inner = vertical(context);
|
||||||
|
inner.setPadding(dp(16), dp(16), dp(16), dp(16));
|
||||||
|
inner.addView(sectionTitle(context, titleId));
|
||||||
|
inner.addView(content);
|
||||||
|
card.addView(inner);
|
||||||
|
return card;
|
||||||
|
}
|
||||||
|
|
||||||
|
private LinearLayout vertical(Context context) {
|
||||||
|
LinearLayout layout = new LinearLayout(context);
|
||||||
|
layout.setOrientation(LinearLayout.VERTICAL);
|
||||||
|
return layout;
|
||||||
|
}
|
||||||
|
|
||||||
|
private TextView title(Context context, int text) {
|
||||||
|
TextView view = new TextView(context);
|
||||||
|
view.setText(text);
|
||||||
|
view.setTextAppearance(com.google.android.material.R.style.TextAppearance_MaterialComponents_Headline5);
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
|
||||||
|
private TextView sectionTitle(Context context, int text) {
|
||||||
|
TextView view = new TextView(context);
|
||||||
|
view.setText(text);
|
||||||
|
view.setAllCaps(true);
|
||||||
|
view.setTypeface(view.getTypeface(), android.graphics.Typeface.BOLD);
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
|
||||||
|
private TextView body(Context context, int text) {
|
||||||
|
TextView view = new TextView(context);
|
||||||
|
view.setText(text);
|
||||||
|
view.setPadding(0, dp(8), 0, dp(6));
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int parseInt(EditText input, int fallback) {
|
||||||
|
try {
|
||||||
|
return Integer.parseInt(input.getText().toString().trim());
|
||||||
|
} catch (Throwable ignored) {
|
||||||
|
return fallback;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private int clamp(int value, int min, int max) {
|
||||||
|
return Math.max(min, Math.min(max, value));
|
||||||
|
}
|
||||||
|
|
||||||
|
private int dp(int value) {
|
||||||
|
return Math.round(value * getResources().getDisplayMetrics().density);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
package se.ajpanton.statusbartweak.shell.ui;
|
||||||
|
|
||||||
|
public final class LockscreenLayoutFragment extends LockedSceneLayoutFragment {
|
||||||
|
public LockscreenLayoutFragment() {
|
||||||
|
super(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -85,13 +85,17 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
|
|||||||
if (itemId == R.id.nav_system_icons) {
|
if (itemId == R.id.nav_system_icons) {
|
||||||
fragment = new SystemIconsFragment();
|
fragment = new SystemIconsFragment();
|
||||||
} else if (itemId == R.id.nav_layout) {
|
} else if (itemId == R.id.nav_layout) {
|
||||||
|
fragment = new LayoutHomeFragment();
|
||||||
|
} else if (itemId == R.id.nav_layout_unlocked) {
|
||||||
fragment = new LayoutFragment();
|
fragment = new LayoutFragment();
|
||||||
|
} else if (itemId == R.id.nav_layout_lockscreen) {
|
||||||
|
fragment = new LockscreenLayoutFragment();
|
||||||
|
} else if (itemId == R.id.nav_layout_aod) {
|
||||||
|
fragment = new AodLayoutFragment();
|
||||||
} else if (itemId == R.id.nav_battery_bar) {
|
} else if (itemId == R.id.nav_battery_bar) {
|
||||||
fragment = new BatteryBarFragment();
|
fragment = new BatteryBarFragment();
|
||||||
} else if (itemId == R.id.nav_clock) {
|
} else if (itemId == R.id.nav_clock) {
|
||||||
fragment = new ClockFragment();
|
fragment = new ClockFragment();
|
||||||
} else if (itemId == R.id.nav_notification_icons) {
|
|
||||||
fragment = new NotificationIconsFragment();
|
|
||||||
} else if (itemId == R.id.nav_block_app_notification_icons) {
|
} else if (itemId == R.id.nav_block_app_notification_icons) {
|
||||||
fragment = new BlockAppNotificationIconsFragment();
|
fragment = new BlockAppNotificationIconsFragment();
|
||||||
} else if (itemId == R.id.nav_status_chips) {
|
} else if (itemId == R.id.nav_status_chips) {
|
||||||
@@ -113,8 +117,12 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
|
|||||||
return R.string.nav_system_icons;
|
return R.string.nav_system_icons;
|
||||||
} else if (itemId == R.id.nav_layout) {
|
} else if (itemId == R.id.nav_layout) {
|
||||||
return R.string.nav_layout;
|
return R.string.nav_layout;
|
||||||
} else if (itemId == R.id.nav_notification_icons) {
|
} else if (itemId == R.id.nav_layout_unlocked) {
|
||||||
return R.string.nav_notification_icons;
|
return R.string.section_unlocked;
|
||||||
|
} else if (itemId == R.id.nav_layout_lockscreen) {
|
||||||
|
return R.string.section_lockscreen;
|
||||||
|
} else if (itemId == R.id.nav_layout_aod) {
|
||||||
|
return R.string.section_aod;
|
||||||
} else if (itemId == R.id.nav_block_app_notification_icons) {
|
} else if (itemId == R.id.nav_block_app_notification_icons) {
|
||||||
return R.string.hidden_apps_page_title;
|
return R.string.hidden_apps_page_title;
|
||||||
} else if (itemId == R.id.nav_clock) {
|
} else if (itemId == R.id.nav_clock) {
|
||||||
|
|||||||
@@ -1,489 +0,0 @@
|
|||||||
package se.ajpanton.statusbartweak.shell.ui;
|
|
||||||
|
|
||||||
import se.ajpanton.statusbartweak.R;
|
|
||||||
import se.ajpanton.statusbartweak.shell.settings.SbtDefaults;
|
|
||||||
import se.ajpanton.statusbartweak.shell.settings.SbtSettings;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.SharedPreferences;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.text.Editable;
|
|
||||||
import android.text.InputFilter;
|
|
||||||
import android.text.TextWatcher;
|
|
||||||
import android.view.LayoutInflater;
|
|
||||||
import android.view.View;
|
|
||||||
import android.view.ViewGroup;
|
|
||||||
import android.view.inputmethod.EditorInfo;
|
|
||||||
import android.widget.EditText;
|
|
||||||
import java.util.Arrays;
|
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
import androidx.annotation.Nullable;
|
|
||||||
import androidx.fragment.app.Fragment;
|
|
||||||
|
|
||||||
import com.google.android.material.button.MaterialButton;
|
|
||||||
import com.google.android.material.slider.Slider;
|
|
||||||
import com.google.android.material.switchmaterial.SwitchMaterial;
|
|
||||||
|
|
||||||
public class NotificationIconsFragment extends Fragment {
|
|
||||||
private static final int INF_VALUE = Integer.MAX_VALUE;
|
|
||||||
private static final String INF_LABEL = "\u221e";
|
|
||||||
private static final int SLIDER_STEPS = 200;
|
|
||||||
private static final int INF_GAP_STEPS = 20;
|
|
||||||
private static final double SLIDER_EXPONENT = 2.0;
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
@Override
|
|
||||||
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
|
|
||||||
@Nullable Bundle savedInstanceState) {
|
|
||||||
View root = inflater.inflate(R.layout.fragment_notification_icons, container, false);
|
|
||||||
SharedPreferences prefs = requireContext()
|
|
||||||
.getSharedPreferences(SbtSettings.PREFS_NAME, Context.MODE_PRIVATE);
|
|
||||||
SbtSettings.ensureReadable(requireContext());
|
|
||||||
bindNumberControl(root, prefs,
|
|
||||||
R.id.unlocked_max_icons_slider,
|
|
||||||
R.id.unlocked_max_icons_input,
|
|
||||||
R.id.unlocked_max_icons_minus,
|
|
||||||
R.id.unlocked_max_icons_plus,
|
|
||||||
SbtSettings.KEY_UNLOCKED_MAX_ICONS_PER_ROW,
|
|
||||||
SbtDefaults.UNLOCKED_MAX_ICONS_PER_ROW_DEFAULT,
|
|
||||||
SbtDefaults.UNLOCKED_MAX_ICONS_PER_ROW_MIN,
|
|
||||||
SbtDefaults.UNLOCKED_MAX_ICONS_PER_ROW_MAX,
|
|
||||||
true);
|
|
||||||
|
|
||||||
bindNumberControl(root, prefs,
|
|
||||||
R.id.icons_aod_max_icons_slider,
|
|
||||||
R.id.icons_aod_max_icons_input,
|
|
||||||
R.id.icons_aod_max_icons_minus,
|
|
||||||
R.id.icons_aod_max_icons_plus,
|
|
||||||
SbtSettings.KEY_ICONS_AOD_MAX_ICONS_PER_ROW,
|
|
||||||
SbtDefaults.ICONS_AOD_MAX_ICONS_PER_ROW_DEFAULT,
|
|
||||||
SbtDefaults.ICONS_AOD_MAX_ICONS_PER_ROW_MIN,
|
|
||||||
SbtDefaults.ICONS_AOD_MAX_ICONS_PER_ROW_MAX,
|
|
||||||
true);
|
|
||||||
bindNumberControl(root, prefs,
|
|
||||||
R.id.icons_aod_max_rows_slider,
|
|
||||||
R.id.icons_aod_max_rows_input,
|
|
||||||
R.id.icons_aod_max_rows_minus,
|
|
||||||
R.id.icons_aod_max_rows_plus,
|
|
||||||
SbtSettings.KEY_ICONS_AOD_MAX_ROWS,
|
|
||||||
SbtDefaults.ICONS_AOD_MAX_ROWS_DEFAULT,
|
|
||||||
SbtDefaults.ICONS_AOD_MAX_ROWS_MIN,
|
|
||||||
SbtDefaults.ICONS_AOD_MAX_ROWS_MAX,
|
|
||||||
true);
|
|
||||||
bindSwitch(root, prefs,
|
|
||||||
R.id.icons_aod_even_switch,
|
|
||||||
SbtSettings.KEY_ICONS_AOD_EVEN_DISTRIBUTION,
|
|
||||||
SbtDefaults.ICONS_AOD_EVEN_DISTRIBUTION_DEFAULT);
|
|
||||||
bindSwitch(root, prefs,
|
|
||||||
R.id.icons_aod_cutout_switch,
|
|
||||||
SbtSettings.KEY_ICONS_AOD_CUTOUT_LIMIT,
|
|
||||||
SbtDefaults.ICONS_AOD_CUTOUT_LIMIT_DEFAULT);
|
|
||||||
|
|
||||||
bindNumberControl(root, prefs,
|
|
||||||
R.id.icons_lock_max_icons_slider,
|
|
||||||
R.id.icons_lock_max_icons_input,
|
|
||||||
R.id.icons_lock_max_icons_minus,
|
|
||||||
R.id.icons_lock_max_icons_plus,
|
|
||||||
SbtSettings.KEY_ICONS_LOCK_MAX_ICONS_PER_ROW,
|
|
||||||
SbtDefaults.ICONS_LOCK_MAX_ICONS_PER_ROW_DEFAULT,
|
|
||||||
SbtDefaults.ICONS_LOCK_MAX_ICONS_PER_ROW_MIN,
|
|
||||||
SbtDefaults.ICONS_LOCK_MAX_ICONS_PER_ROW_MAX,
|
|
||||||
true);
|
|
||||||
bindNumberControl(root, prefs,
|
|
||||||
R.id.icons_lock_max_rows_slider,
|
|
||||||
R.id.icons_lock_max_rows_input,
|
|
||||||
R.id.icons_lock_max_rows_minus,
|
|
||||||
R.id.icons_lock_max_rows_plus,
|
|
||||||
SbtSettings.KEY_ICONS_LOCK_MAX_ROWS,
|
|
||||||
SbtDefaults.ICONS_LOCK_MAX_ROWS_DEFAULT,
|
|
||||||
SbtDefaults.ICONS_LOCK_MAX_ROWS_MIN,
|
|
||||||
SbtDefaults.ICONS_LOCK_MAX_ROWS_MAX,
|
|
||||||
true);
|
|
||||||
bindSwitch(root, prefs,
|
|
||||||
R.id.icons_lock_even_switch,
|
|
||||||
SbtSettings.KEY_ICONS_LOCK_EVEN_DISTRIBUTION,
|
|
||||||
SbtDefaults.ICONS_LOCK_EVEN_DISTRIBUTION_DEFAULT);
|
|
||||||
bindSwitch(root, prefs,
|
|
||||||
R.id.icons_lock_cutout_switch,
|
|
||||||
SbtSettings.KEY_ICONS_LOCK_CUTOUT_LIMIT,
|
|
||||||
SbtDefaults.ICONS_LOCK_CUTOUT_LIMIT_DEFAULT);
|
|
||||||
|
|
||||||
bindNumberControl(root, prefs,
|
|
||||||
R.id.cards_aod_max_icons_slider,
|
|
||||||
R.id.cards_aod_max_icons_input,
|
|
||||||
R.id.cards_aod_max_icons_minus,
|
|
||||||
R.id.cards_aod_max_icons_plus,
|
|
||||||
SbtSettings.KEY_CARDS_AOD_MAX_ICONS_PER_ROW,
|
|
||||||
SbtDefaults.CARDS_AOD_MAX_ICONS_PER_ROW_DEFAULT,
|
|
||||||
SbtDefaults.CARDS_AOD_MAX_ICONS_PER_ROW_MIN,
|
|
||||||
SbtDefaults.CARDS_AOD_MAX_ICONS_PER_ROW_MAX,
|
|
||||||
true);
|
|
||||||
bindNumberControl(root, prefs,
|
|
||||||
R.id.cards_aod_max_rows_slider,
|
|
||||||
R.id.cards_aod_max_rows_input,
|
|
||||||
R.id.cards_aod_max_rows_minus,
|
|
||||||
R.id.cards_aod_max_rows_plus,
|
|
||||||
SbtSettings.KEY_CARDS_AOD_MAX_ROWS,
|
|
||||||
SbtDefaults.CARDS_AOD_MAX_ROWS_DEFAULT,
|
|
||||||
SbtDefaults.CARDS_AOD_MAX_ROWS_MIN,
|
|
||||||
SbtDefaults.CARDS_AOD_MAX_ROWS_MAX,
|
|
||||||
true);
|
|
||||||
bindSwitch(root, prefs,
|
|
||||||
R.id.cards_aod_even_switch,
|
|
||||||
SbtSettings.KEY_CARDS_AOD_EVEN_DISTRIBUTION,
|
|
||||||
SbtDefaults.CARDS_AOD_EVEN_DISTRIBUTION_DEFAULT);
|
|
||||||
bindSwitch(root, prefs,
|
|
||||||
R.id.cards_aod_limit_width_switch,
|
|
||||||
SbtSettings.KEY_CARDS_AOD_LIMIT_TO_WIDTH,
|
|
||||||
SbtDefaults.CARDS_AOD_LIMIT_TO_WIDTH_DEFAULT);
|
|
||||||
|
|
||||||
bindNumberControl(root, prefs,
|
|
||||||
R.id.cards_lock_max_icons_slider,
|
|
||||||
R.id.cards_lock_max_icons_input,
|
|
||||||
R.id.cards_lock_max_icons_minus,
|
|
||||||
R.id.cards_lock_max_icons_plus,
|
|
||||||
SbtSettings.KEY_CARDS_LOCK_MAX_ICONS_PER_ROW,
|
|
||||||
SbtDefaults.CARDS_LOCK_MAX_ICONS_PER_ROW_DEFAULT,
|
|
||||||
SbtDefaults.CARDS_LOCK_MAX_ICONS_PER_ROW_MIN,
|
|
||||||
SbtDefaults.CARDS_LOCK_MAX_ICONS_PER_ROW_MAX,
|
|
||||||
true);
|
|
||||||
bindNumberControl(root, prefs,
|
|
||||||
R.id.cards_lock_max_rows_slider,
|
|
||||||
R.id.cards_lock_max_rows_input,
|
|
||||||
R.id.cards_lock_max_rows_minus,
|
|
||||||
R.id.cards_lock_max_rows_plus,
|
|
||||||
SbtSettings.KEY_CARDS_LOCK_MAX_ROWS,
|
|
||||||
SbtDefaults.CARDS_LOCK_MAX_ROWS_DEFAULT,
|
|
||||||
SbtDefaults.CARDS_LOCK_MAX_ROWS_MIN,
|
|
||||||
SbtDefaults.CARDS_LOCK_MAX_ROWS_MAX,
|
|
||||||
true);
|
|
||||||
bindSwitch(root, prefs,
|
|
||||||
R.id.cards_lock_even_switch,
|
|
||||||
SbtSettings.KEY_CARDS_LOCK_EVEN_DISTRIBUTION,
|
|
||||||
SbtDefaults.CARDS_LOCK_EVEN_DISTRIBUTION_DEFAULT);
|
|
||||||
bindSwitch(root, prefs,
|
|
||||||
R.id.cards_lock_limit_width_switch,
|
|
||||||
SbtSettings.KEY_CARDS_LOCK_LIMIT_TO_WIDTH,
|
|
||||||
SbtDefaults.CARDS_LOCK_LIMIT_TO_WIDTH_DEFAULT);
|
|
||||||
applyLayoutAvailability(root, prefs);
|
|
||||||
return root;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void applyLayoutAvailability(View root, SharedPreferences prefs) {
|
|
||||||
if (root == null || prefs == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
boolean layoutEnabled = prefs.getBoolean(
|
|
||||||
SbtSettings.KEY_CLOCK_ENABLED,
|
|
||||||
SbtDefaults.CLOCK_ENABLED_DEFAULT);
|
|
||||||
View unlockedSection = root.findViewById(R.id.notification_icons_unlocked_section);
|
|
||||||
View layoutSections = root.findViewById(R.id.notification_icons_layout_sections);
|
|
||||||
View layoutRequiredHint = root.findViewById(R.id.notification_icons_layout_required_hint);
|
|
||||||
View unlockedObsoleteHint = root.findViewById(R.id.notification_icons_unlocked_obsolete_hint);
|
|
||||||
if (unlockedSection != null) {
|
|
||||||
setViewTreeEnabled(unlockedSection, !layoutEnabled);
|
|
||||||
unlockedSection.setAlpha(layoutEnabled ? 0.45f : 1f);
|
|
||||||
}
|
|
||||||
if (layoutSections != null) {
|
|
||||||
setViewTreeEnabled(layoutSections, layoutEnabled);
|
|
||||||
layoutSections.setAlpha(layoutEnabled ? 1f : 0.45f);
|
|
||||||
}
|
|
||||||
if (layoutRequiredHint != null) {
|
|
||||||
layoutRequiredHint.setVisibility(layoutEnabled ? View.GONE : View.VISIBLE);
|
|
||||||
}
|
|
||||||
if (unlockedObsoleteHint != null) {
|
|
||||||
unlockedObsoleteHint.setVisibility(layoutEnabled ? View.VISIBLE : View.GONE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setViewTreeEnabled(View view, boolean enabled) {
|
|
||||||
if (view == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
view.setEnabled(enabled);
|
|
||||||
if (view instanceof ViewGroup group) {
|
|
||||||
for (int i = 0; i < group.getChildCount(); i++) {
|
|
||||||
setViewTreeEnabled(group.getChildAt(i), enabled);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void bindNumberControl(View root,
|
|
||||||
SharedPreferences prefs,
|
|
||||||
int sliderId,
|
|
||||||
int inputId,
|
|
||||||
int minusId,
|
|
||||||
int plusId,
|
|
||||||
String key,
|
|
||||||
int defaultValue,
|
|
||||||
int min,
|
|
||||||
int max,
|
|
||||||
boolean allowInf) {
|
|
||||||
Slider slider = root.findViewById(sliderId);
|
|
||||||
EditText input = root.findViewById(inputId);
|
|
||||||
MaterialButton minus = root.findViewById(minusId);
|
|
||||||
MaterialButton plus = root.findViewById(plusId);
|
|
||||||
|
|
||||||
int[] positions = buildPositions(min, max);
|
|
||||||
int maxPos = SLIDER_STEPS - INF_GAP_STEPS;
|
|
||||||
|
|
||||||
slider.setValueFrom(0);
|
|
||||||
slider.setValueTo(SLIDER_STEPS);
|
|
||||||
slider.setStepSize(1f);
|
|
||||||
slider.setTickVisible(false);
|
|
||||||
slider.setLabelFormatter(value -> labelForSliderValue(Math.round(value), min, max, allowInf, positions));
|
|
||||||
|
|
||||||
final boolean[] updating = new boolean[]{false};
|
|
||||||
|
|
||||||
int current = clampInt(prefs.getInt(key, defaultValue), min, Integer.MAX_VALUE);
|
|
||||||
applyValueToUi(slider, input, min, max, allowInf, positions, current, updating);
|
|
||||||
input.setFilters(new InputFilter[]{new InputFilter.LengthFilter(6)});
|
|
||||||
|
|
||||||
slider.addOnChangeListener((s, v, fromUser) -> {
|
|
||||||
if (updating[0]) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
int pos = Math.round(v);
|
|
||||||
int snapped = snapPosition(pos, positions, allowInf, maxPos);
|
|
||||||
if (snapped != pos) {
|
|
||||||
updating[0] = true;
|
|
||||||
slider.setValue(snapped);
|
|
||||||
updating[0] = false;
|
|
||||||
}
|
|
||||||
int mapped = positionToValue(snapped, min, max, allowInf, positions, maxPos);
|
|
||||||
int nextValue = mapped;
|
|
||||||
updating[0] = true;
|
|
||||||
if (mapped == INF_VALUE) {
|
|
||||||
input.setText(INF_LABEL);
|
|
||||||
} else {
|
|
||||||
input.setText(String.valueOf(mapped));
|
|
||||||
}
|
|
||||||
updating[0] = false;
|
|
||||||
if (fromUser) {
|
|
||||||
prefs.edit().putInt(key, nextValue).apply();
|
|
||||||
SbtSettings.ensureReadable(requireContext());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
minus.setOnClickListener(v -> {
|
|
||||||
int parsed = parseInput(input, current);
|
|
||||||
int next;
|
|
||||||
if (parsed == INF_VALUE) {
|
|
||||||
next = max;
|
|
||||||
} else {
|
|
||||||
next = clampInt(parsed - 1, min, Integer.MAX_VALUE);
|
|
||||||
}
|
|
||||||
applyValueToUi(slider, input, min, max, allowInf, positions, next, updating);
|
|
||||||
prefs.edit().putInt(key, next).apply();
|
|
||||||
SbtSettings.ensureReadable(requireContext());
|
|
||||||
});
|
|
||||||
|
|
||||||
plus.setOnClickListener(v -> {
|
|
||||||
int parsed = parseInput(input, current);
|
|
||||||
int next;
|
|
||||||
if (parsed == INF_VALUE) {
|
|
||||||
next = INF_VALUE;
|
|
||||||
} else {
|
|
||||||
next = clampInt(parsed + 1, min, Integer.MAX_VALUE);
|
|
||||||
}
|
|
||||||
applyValueToUi(slider, input, min, max, allowInf, positions, next, updating);
|
|
||||||
prefs.edit().putInt(key, next).apply();
|
|
||||||
SbtSettings.ensureReadable(requireContext());
|
|
||||||
});
|
|
||||||
|
|
||||||
input.setOnEditorActionListener((v, actionId, event) -> {
|
|
||||||
if (actionId == EditorInfo.IME_ACTION_DONE) {
|
|
||||||
int next = clampInt(parseInput(input, current), min, Integer.MAX_VALUE);
|
|
||||||
applyValueToUi(slider, input, min, max, allowInf, positions, next, updating);
|
|
||||||
prefs.edit().putInt(key, next).apply();
|
|
||||||
SbtSettings.ensureReadable(requireContext());
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
|
|
||||||
input.addTextChangedListener(new TextWatcher() {
|
|
||||||
@Override
|
|
||||||
public void beforeTextChanged(CharSequence s, int start, int count, int after) { }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onTextChanged(CharSequence s, int start, int before, int count) { }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void afterTextChanged(Editable s) {
|
|
||||||
if (updating[0]) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
String text = s != null ? s.toString().trim() : "";
|
|
||||||
if (text.isEmpty()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
int parsed = parseInput(input, current);
|
|
||||||
int next = clampInt(parsed, min, Integer.MAX_VALUE);
|
|
||||||
if (next != parsed) {
|
|
||||||
applyValueToUi(slider, input, min, max, allowInf, positions, next, updating);
|
|
||||||
} else {
|
|
||||||
updateSliderValue(slider, min, max, allowInf, positions, next, updating);
|
|
||||||
}
|
|
||||||
prefs.edit().putInt(key, next).apply();
|
|
||||||
SbtSettings.ensureReadable(requireContext());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void bindSwitch(View root,
|
|
||||||
SharedPreferences prefs,
|
|
||||||
int switchId,
|
|
||||||
String key,
|
|
||||||
boolean defaultValue) {
|
|
||||||
SwitchMaterial toggle = root.findViewById(switchId);
|
|
||||||
boolean current = prefs.getBoolean(key, defaultValue);
|
|
||||||
toggle.setChecked(current);
|
|
||||||
toggle.setOnCheckedChangeListener((buttonView, isChecked) ->
|
|
||||||
{
|
|
||||||
prefs.edit().putBoolean(key, isChecked).apply();
|
|
||||||
SbtSettings.ensureReadable(requireContext());
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private int clampInt(int value, int min, int max) {
|
|
||||||
if (value < min) {
|
|
||||||
return min;
|
|
||||||
}
|
|
||||||
if (value > max) {
|
|
||||||
return max;
|
|
||||||
}
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
private int parseInput(EditText input, int fallback) {
|
|
||||||
if (input == null) {
|
|
||||||
return fallback;
|
|
||||||
}
|
|
||||||
|
|
||||||
String text = input.getText().toString().trim();
|
|
||||||
if (text.isEmpty()) {
|
|
||||||
return fallback;
|
|
||||||
}
|
|
||||||
if (INF_LABEL.equals(text) || "inf".equalsIgnoreCase(text)) {
|
|
||||||
return INF_VALUE;
|
|
||||||
}
|
|
||||||
return Integer.parseInt(text);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void applyValueToUi(Slider slider,
|
|
||||||
EditText input,
|
|
||||||
int min,
|
|
||||||
int max,
|
|
||||||
boolean allowInf,
|
|
||||||
int[] positions,
|
|
||||||
int value,
|
|
||||||
boolean[] updating) {
|
|
||||||
updateSliderValue(slider, min, max, allowInf, positions, value, updating);
|
|
||||||
updating[0] = true;
|
|
||||||
if (value == INF_VALUE) {
|
|
||||||
input.setText(INF_LABEL);
|
|
||||||
} else {
|
|
||||||
input.setText(String.valueOf(value));
|
|
||||||
}
|
|
||||||
updating[0] = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updateSliderValue(Slider slider,
|
|
||||||
int min,
|
|
||||||
int max,
|
|
||||||
boolean allowInf,
|
|
||||||
int[] positions,
|
|
||||||
int value,
|
|
||||||
boolean[] updating) {
|
|
||||||
int idx = valueToPosition(value, min, max, allowInf, positions);
|
|
||||||
updating[0] = true;
|
|
||||||
slider.setValue(idx);
|
|
||||||
updating[0] = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private int[] buildPositions(int min, int max) {
|
|
||||||
int count = max - min + 1;
|
|
||||||
int maxPos = SLIDER_STEPS - INF_GAP_STEPS;
|
|
||||||
int[] positions = new int[count];
|
|
||||||
if (count <= 1) {
|
|
||||||
positions[0] = 0;
|
|
||||||
return positions;
|
|
||||||
}
|
|
||||||
for (int i = 0; i < count; i++) {
|
|
||||||
double t = (double) i / (double) (count - 1);
|
|
||||||
double shaped = Math.pow(t, 1.0 / SLIDER_EXPONENT);
|
|
||||||
positions[i] = (int) Math.round(shaped * maxPos);
|
|
||||||
}
|
|
||||||
positions[0] = 0;
|
|
||||||
positions[count - 1] = maxPos;
|
|
||||||
for (int i = 1; i < count; i++) {
|
|
||||||
if (positions[i] <= positions[i - 1]) {
|
|
||||||
positions[i] = positions[i - 1] + 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
positions[count - 1] = maxPos;
|
|
||||||
for (int i = count - 2; i >= 0; i--) {
|
|
||||||
if (positions[i] >= positions[i + 1]) {
|
|
||||||
positions[i] = positions[i + 1] - 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return positions;
|
|
||||||
}
|
|
||||||
|
|
||||||
private int valueToPosition(int value, int min, int max, boolean allowInf, int[] positions) {
|
|
||||||
if (allowInf && value == INF_VALUE) {
|
|
||||||
return SLIDER_STEPS;
|
|
||||||
}
|
|
||||||
if (allowInf && value > max) {
|
|
||||||
return SLIDER_STEPS;
|
|
||||||
}
|
|
||||||
int clamped = clampInt(value, min, max);
|
|
||||||
return positions[clamped - min];
|
|
||||||
}
|
|
||||||
|
|
||||||
private int positionToValue(int pos,
|
|
||||||
int min,
|
|
||||||
int max,
|
|
||||||
boolean allowInf,
|
|
||||||
int[] positions,
|
|
||||||
int maxPos) {
|
|
||||||
if (allowInf && pos >= maxPos + (INF_GAP_STEPS / 2)) {
|
|
||||||
return INF_VALUE;
|
|
||||||
}
|
|
||||||
int idx = nearestIndex(pos, positions);
|
|
||||||
int value = min + idx;
|
|
||||||
return clampInt(value, min, max);
|
|
||||||
}
|
|
||||||
|
|
||||||
private int nearestIndex(int pos, int[] positions) {
|
|
||||||
int idx = Arrays.binarySearch(positions, pos);
|
|
||||||
if (idx >= 0) {
|
|
||||||
return idx;
|
|
||||||
}
|
|
||||||
int insert = -idx - 1;
|
|
||||||
if (insert <= 0) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
if (insert >= positions.length) {
|
|
||||||
return positions.length - 1;
|
|
||||||
}
|
|
||||||
int left = positions[insert - 1];
|
|
||||||
int right = positions[insert];
|
|
||||||
return (pos - left <= right - pos) ? (insert - 1) : insert;
|
|
||||||
}
|
|
||||||
|
|
||||||
private int snapPosition(int pos, int[] positions, boolean allowInf, int maxPos) {
|
|
||||||
if (allowInf && pos >= maxPos + (INF_GAP_STEPS / 2)) {
|
|
||||||
return SLIDER_STEPS;
|
|
||||||
}
|
|
||||||
int idx = nearestIndex(pos, positions);
|
|
||||||
return positions[idx];
|
|
||||||
}
|
|
||||||
|
|
||||||
private String labelForSliderValue(int pos, int min, int max, boolean allowInf, int[] positions) {
|
|
||||||
int maxPos = SLIDER_STEPS - INF_GAP_STEPS;
|
|
||||||
int value = positionToValue(pos, min, max, allowInf, positions, maxPos);
|
|
||||||
return value == INF_VALUE ? INF_LABEL : String.valueOf(value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -73,6 +73,81 @@
|
|||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</com.google.android.material.card.MaterialCardView>
|
</com.google.android.material.card.MaterialCardView>
|
||||||
|
|
||||||
|
<com.google.android.material.card.MaterialCardView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
app:cardUseCompatPadding="true"
|
||||||
|
app:strokeColor="@color/sbt_card_outline"
|
||||||
|
app:strokeWidth="1dp">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:padding="16dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:letterSpacing="0.03"
|
||||||
|
android:text="@string/debug_section_android_notification_style"
|
||||||
|
android:textAllCaps="true"
|
||||||
|
android:textAppearance="?attr/textAppearanceSubtitle1"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/debug_notification_style_current"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="12dp"
|
||||||
|
android:textAppearance="?attr/textAppearanceSubtitle2" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/debug_notification_style_hint"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:text="@string/debug_notification_style_hint"
|
||||||
|
android:textAppearance="?attr/textAppearanceBody2" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="12dp"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/debug_notification_style_cards"
|
||||||
|
style="?attr/materialButtonOutlinedStyle"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:text="@string/debug_notification_style_cards" />
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/debug_notification_style_icons"
|
||||||
|
style="?attr/materialButtonOutlinedStyle"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="8dp"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:text="@string/debug_notification_style_icons" />
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/debug_notification_style_dot"
|
||||||
|
style="?attr/materialButtonOutlinedStyle"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="8dp"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:text="@string/debug_notification_style_dot" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
</com.google.android.material.card.MaterialCardView>
|
||||||
|
|
||||||
<com.google.android.material.card.MaterialCardView
|
<com.google.android.material.card.MaterialCardView
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
android:textAppearance="?attr/textAppearanceHeadline5" />
|
android:textAppearance="?attr/textAppearanceHeadline5" />
|
||||||
|
|
||||||
<com.google.android.material.card.MaterialCardView
|
<com.google.android.material.card.MaterialCardView
|
||||||
|
android:id="@+id/layout_master_card"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="16dp"
|
android:layout_marginTop="16dp"
|
||||||
@@ -49,6 +50,7 @@
|
|||||||
</com.google.android.material.card.MaterialCardView>
|
</com.google.android.material.card.MaterialCardView>
|
||||||
|
|
||||||
<com.google.android.material.card.MaterialCardView
|
<com.google.android.material.card.MaterialCardView
|
||||||
|
android:id="@+id/layout_clock_card"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="16dp"
|
android:layout_marginTop="16dp"
|
||||||
@@ -261,6 +263,7 @@
|
|||||||
</com.google.android.material.card.MaterialCardView>
|
</com.google.android.material.card.MaterialCardView>
|
||||||
|
|
||||||
<com.google.android.material.card.MaterialCardView
|
<com.google.android.material.card.MaterialCardView
|
||||||
|
android:id="@+id/layout_chip_card"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="16dp"
|
android:layout_marginTop="16dp"
|
||||||
@@ -474,6 +477,7 @@
|
|||||||
</com.google.android.material.card.MaterialCardView>
|
</com.google.android.material.card.MaterialCardView>
|
||||||
|
|
||||||
<com.google.android.material.card.MaterialCardView
|
<com.google.android.material.card.MaterialCardView
|
||||||
|
android:id="@+id/layout_notif_card"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="16dp"
|
android:layout_marginTop="16dp"
|
||||||
@@ -698,18 +702,11 @@
|
|||||||
android:paddingEnd="0dp"
|
android:paddingEnd="0dp"
|
||||||
android:text="0" />
|
android:text="0" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<CheckBox
|
|
||||||
android:id="@+id/notif_show_dot_if_truncated"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="8dp"
|
|
||||||
android:checked="true"
|
|
||||||
android:text="@string/layout_show_dot_if_truncated" />
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</com.google.android.material.card.MaterialCardView>
|
</com.google.android.material.card.MaterialCardView>
|
||||||
|
|
||||||
<com.google.android.material.card.MaterialCardView
|
<com.google.android.material.card.MaterialCardView
|
||||||
|
android:id="@+id/layout_status_card"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="16dp"
|
android:layout_marginTop="16dp"
|
||||||
@@ -934,18 +931,11 @@
|
|||||||
android:paddingEnd="0dp"
|
android:paddingEnd="0dp"
|
||||||
android:text="0" />
|
android:text="0" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<CheckBox
|
|
||||||
android:id="@+id/status_show_dot_if_truncated"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="8dp"
|
|
||||||
android:checked="true"
|
|
||||||
android:text="@string/layout_show_dot_if_truncated" />
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</com.google.android.material.card.MaterialCardView>
|
</com.google.android.material.card.MaterialCardView>
|
||||||
|
|
||||||
<com.google.android.material.card.MaterialCardView
|
<com.google.android.material.card.MaterialCardView
|
||||||
|
android:id="@+id/layout_ordering_card"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="16dp"
|
android:layout_marginTop="16dp"
|
||||||
@@ -1031,6 +1021,7 @@
|
|||||||
</com.google.android.material.card.MaterialCardView>
|
</com.google.android.material.card.MaterialCardView>
|
||||||
|
|
||||||
<com.google.android.material.card.MaterialCardView
|
<com.google.android.material.card.MaterialCardView
|
||||||
|
android:id="@+id/layout_padding_card"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="16dp"
|
android:layout_marginTop="16dp"
|
||||||
|
|||||||
@@ -1,650 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<ScrollView
|
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent">
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:orientation="vertical"
|
|
||||||
android:padding="20dp">
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="@string/notification_icons_title"
|
|
||||||
android:textAppearance="?attr/textAppearanceHeadline5" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/notification_icons_unlocked_obsolete_hint"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="10dp"
|
|
||||||
android:text="@string/notification_icons_unlocked_obsolete_hint"
|
|
||||||
android:textAppearance="?attr/textAppearanceBody2"
|
|
||||||
android:visibility="gone" />
|
|
||||||
|
|
||||||
<com.google.android.material.card.MaterialCardView
|
|
||||||
android:id="@+id/notification_icons_unlocked_section"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="16dp"
|
|
||||||
app:cardUseCompatPadding="true"
|
|
||||||
app:strokeColor="@color/sbt_card_outline"
|
|
||||||
app:strokeWidth="1dp">
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:orientation="vertical"
|
|
||||||
android:padding="16dp">
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="@string/section_unlocked"
|
|
||||||
android:textAppearance="?attr/textAppearanceSubtitle1"
|
|
||||||
android:textAllCaps="true"
|
|
||||||
android:letterSpacing="0.03"
|
|
||||||
android:textStyle="bold" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="12dp"
|
|
||||||
android:text="@string/label_max_icons_per_row"
|
|
||||||
android:textAppearance="?attr/textAppearanceBody2" />
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="6dp"
|
|
||||||
android:orientation="horizontal">
|
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
|
||||||
android:id="@+id/unlocked_max_icons_minus"
|
|
||||||
style="?attr/materialButtonOutlinedStyle"
|
|
||||||
android:layout_width="48dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="-" />
|
|
||||||
|
|
||||||
<EditText
|
|
||||||
android:id="@+id/unlocked_max_icons_input"
|
|
||||||
android:layout_width="72dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="8dp"
|
|
||||||
android:layout_marginEnd="8dp"
|
|
||||||
android:ems="3"
|
|
||||||
android:gravity="center"
|
|
||||||
android:importantForAutofill="no"
|
|
||||||
android:imeOptions="actionDone"
|
|
||||||
android:inputType="number"
|
|
||||||
android:maxLines="1"
|
|
||||||
android:singleLine="true"
|
|
||||||
android:textAppearance="?attr/textAppearanceSubtitle2" />
|
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
|
||||||
android:id="@+id/unlocked_max_icons_plus"
|
|
||||||
style="?attr/materialButtonOutlinedStyle"
|
|
||||||
android:layout_width="48dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="+" />
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
<com.google.android.material.slider.Slider
|
|
||||||
android:id="@+id/unlocked_max_icons_slider"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="8dp" />
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
</com.google.android.material.card.MaterialCardView>
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/notification_icons_layout_required_hint"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="10dp"
|
|
||||||
android:text="@string/notification_icons_layout_required_hint"
|
|
||||||
android:textAppearance="?attr/textAppearanceBody2"
|
|
||||||
android:visibility="gone" />
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:id="@+id/notification_icons_layout_sections"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:orientation="vertical">
|
|
||||||
|
|
||||||
<com.google.android.material.card.MaterialCardView
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="16dp"
|
|
||||||
app:cardUseCompatPadding="true"
|
|
||||||
app:strokeColor="@color/sbt_card_outline"
|
|
||||||
app:strokeWidth="1dp">
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:orientation="vertical"
|
|
||||||
android:padding="16dp">
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="@string/section_cards_mode"
|
|
||||||
android:textAppearance="?attr/textAppearanceSubtitle1"
|
|
||||||
android:textAllCaps="true"
|
|
||||||
android:letterSpacing="0.03"
|
|
||||||
android:textStyle="bold" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="12dp"
|
|
||||||
android:text="@string/section_aod"
|
|
||||||
android:textAppearance="?attr/textAppearanceSubtitle2" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="8dp"
|
|
||||||
android:text="@string/label_max_icons_per_row"
|
|
||||||
android:textAppearance="?attr/textAppearanceBody2" />
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="6dp"
|
|
||||||
android:orientation="horizontal">
|
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
|
||||||
android:id="@+id/cards_aod_max_icons_minus"
|
|
||||||
style="?attr/materialButtonOutlinedStyle"
|
|
||||||
android:layout_width="48dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="-" />
|
|
||||||
|
|
||||||
<EditText
|
|
||||||
android:id="@+id/cards_aod_max_icons_input"
|
|
||||||
android:layout_width="72dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="8dp"
|
|
||||||
android:layout_marginEnd="8dp"
|
|
||||||
android:ems="3"
|
|
||||||
android:gravity="center"
|
|
||||||
android:importantForAutofill="no"
|
|
||||||
android:imeOptions="actionDone"
|
|
||||||
android:inputType="number"
|
|
||||||
android:maxLines="1"
|
|
||||||
android:singleLine="true"
|
|
||||||
android:textAppearance="?attr/textAppearanceSubtitle2" />
|
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
|
||||||
android:id="@+id/cards_aod_max_icons_plus"
|
|
||||||
style="?attr/materialButtonOutlinedStyle"
|
|
||||||
android:layout_width="48dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="+" />
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
<com.google.android.material.slider.Slider
|
|
||||||
android:id="@+id/cards_aod_max_icons_slider"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="8dp" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="12dp"
|
|
||||||
android:text="@string/label_max_rows"
|
|
||||||
android:textAppearance="?attr/textAppearanceBody2" />
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="6dp"
|
|
||||||
android:orientation="horizontal">
|
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
|
||||||
android:id="@+id/cards_aod_max_rows_minus"
|
|
||||||
style="?attr/materialButtonOutlinedStyle"
|
|
||||||
android:layout_width="48dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="-" />
|
|
||||||
|
|
||||||
<EditText
|
|
||||||
android:id="@+id/cards_aod_max_rows_input"
|
|
||||||
android:layout_width="72dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="8dp"
|
|
||||||
android:layout_marginEnd="8dp"
|
|
||||||
android:ems="3"
|
|
||||||
android:gravity="center"
|
|
||||||
android:importantForAutofill="no"
|
|
||||||
android:imeOptions="actionDone"
|
|
||||||
android:inputType="number"
|
|
||||||
android:maxLines="1"
|
|
||||||
android:singleLine="true"
|
|
||||||
android:textAppearance="?attr/textAppearanceSubtitle2" />
|
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
|
||||||
android:id="@+id/cards_aod_max_rows_plus"
|
|
||||||
style="?attr/materialButtonOutlinedStyle"
|
|
||||||
android:layout_width="48dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="+" />
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
<com.google.android.material.slider.Slider
|
|
||||||
android:id="@+id/cards_aod_max_rows_slider"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="8dp" />
|
|
||||||
|
|
||||||
<com.google.android.material.switchmaterial.SwitchMaterial
|
|
||||||
android:id="@+id/cards_aod_even_switch"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="8dp"
|
|
||||||
android:text="@string/label_even_distribution" />
|
|
||||||
|
|
||||||
<com.google.android.material.switchmaterial.SwitchMaterial
|
|
||||||
android:id="@+id/cards_aod_limit_width_switch"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="6dp"
|
|
||||||
android:text="@string/label_limit_screen_width" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="16dp"
|
|
||||||
android:text="@string/section_lockscreen"
|
|
||||||
android:textAppearance="?attr/textAppearanceSubtitle2" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="8dp"
|
|
||||||
android:text="@string/label_max_icons_per_row"
|
|
||||||
android:textAppearance="?attr/textAppearanceBody2" />
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="6dp"
|
|
||||||
android:orientation="horizontal">
|
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
|
||||||
android:id="@+id/cards_lock_max_icons_minus"
|
|
||||||
style="?attr/materialButtonOutlinedStyle"
|
|
||||||
android:layout_width="48dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="-" />
|
|
||||||
|
|
||||||
<EditText
|
|
||||||
android:id="@+id/cards_lock_max_icons_input"
|
|
||||||
android:layout_width="72dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="8dp"
|
|
||||||
android:layout_marginEnd="8dp"
|
|
||||||
android:ems="3"
|
|
||||||
android:gravity="center"
|
|
||||||
android:importantForAutofill="no"
|
|
||||||
android:imeOptions="actionDone"
|
|
||||||
android:inputType="number"
|
|
||||||
android:maxLines="1"
|
|
||||||
android:singleLine="true"
|
|
||||||
android:textAppearance="?attr/textAppearanceSubtitle2" />
|
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
|
||||||
android:id="@+id/cards_lock_max_icons_plus"
|
|
||||||
style="?attr/materialButtonOutlinedStyle"
|
|
||||||
android:layout_width="48dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="+" />
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
<com.google.android.material.slider.Slider
|
|
||||||
android:id="@+id/cards_lock_max_icons_slider"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="8dp" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="12dp"
|
|
||||||
android:text="@string/label_max_rows"
|
|
||||||
android:textAppearance="?attr/textAppearanceBody2" />
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="6dp"
|
|
||||||
android:orientation="horizontal">
|
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
|
||||||
android:id="@+id/cards_lock_max_rows_minus"
|
|
||||||
style="?attr/materialButtonOutlinedStyle"
|
|
||||||
android:layout_width="48dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="-" />
|
|
||||||
|
|
||||||
<EditText
|
|
||||||
android:id="@+id/cards_lock_max_rows_input"
|
|
||||||
android:layout_width="72dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="8dp"
|
|
||||||
android:layout_marginEnd="8dp"
|
|
||||||
android:ems="3"
|
|
||||||
android:gravity="center"
|
|
||||||
android:importantForAutofill="no"
|
|
||||||
android:imeOptions="actionDone"
|
|
||||||
android:inputType="number"
|
|
||||||
android:maxLines="1"
|
|
||||||
android:singleLine="true"
|
|
||||||
android:textAppearance="?attr/textAppearanceSubtitle2" />
|
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
|
||||||
android:id="@+id/cards_lock_max_rows_plus"
|
|
||||||
style="?attr/materialButtonOutlinedStyle"
|
|
||||||
android:layout_width="48dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="+" />
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
<com.google.android.material.slider.Slider
|
|
||||||
android:id="@+id/cards_lock_max_rows_slider"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="8dp" />
|
|
||||||
|
|
||||||
<com.google.android.material.switchmaterial.SwitchMaterial
|
|
||||||
android:id="@+id/cards_lock_even_switch"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="8dp"
|
|
||||||
android:text="@string/label_even_distribution" />
|
|
||||||
|
|
||||||
<com.google.android.material.switchmaterial.SwitchMaterial
|
|
||||||
android:id="@+id/cards_lock_limit_width_switch"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="6dp"
|
|
||||||
android:text="@string/label_limit_screen_width" />
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
</com.google.android.material.card.MaterialCardView>
|
|
||||||
|
|
||||||
<com.google.android.material.card.MaterialCardView
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="16dp"
|
|
||||||
app:cardUseCompatPadding="true"
|
|
||||||
app:strokeColor="@color/sbt_card_outline"
|
|
||||||
app:strokeWidth="1dp">
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:orientation="vertical"
|
|
||||||
android:padding="16dp">
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="@string/section_icons_mode"
|
|
||||||
android:textAppearance="?attr/textAppearanceSubtitle1"
|
|
||||||
android:textAllCaps="true"
|
|
||||||
android:letterSpacing="0.03"
|
|
||||||
android:textStyle="bold" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="12dp"
|
|
||||||
android:text="@string/section_aod"
|
|
||||||
android:textAppearance="?attr/textAppearanceSubtitle2" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="8dp"
|
|
||||||
android:text="@string/label_max_icons_per_row"
|
|
||||||
android:textAppearance="?attr/textAppearanceBody2" />
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="6dp"
|
|
||||||
android:orientation="horizontal">
|
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
|
||||||
android:id="@+id/icons_aod_max_icons_minus"
|
|
||||||
style="?attr/materialButtonOutlinedStyle"
|
|
||||||
android:layout_width="48dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="-" />
|
|
||||||
|
|
||||||
<EditText
|
|
||||||
android:id="@+id/icons_aod_max_icons_input"
|
|
||||||
android:layout_width="72dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="8dp"
|
|
||||||
android:layout_marginEnd="8dp"
|
|
||||||
android:ems="3"
|
|
||||||
android:gravity="center"
|
|
||||||
android:importantForAutofill="no"
|
|
||||||
android:imeOptions="actionDone"
|
|
||||||
android:inputType="number"
|
|
||||||
android:maxLines="1"
|
|
||||||
android:singleLine="true"
|
|
||||||
android:textAppearance="?attr/textAppearanceSubtitle2" />
|
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
|
||||||
android:id="@+id/icons_aod_max_icons_plus"
|
|
||||||
style="?attr/materialButtonOutlinedStyle"
|
|
||||||
android:layout_width="48dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="+" />
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
<com.google.android.material.slider.Slider
|
|
||||||
android:id="@+id/icons_aod_max_icons_slider"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="8dp" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="12dp"
|
|
||||||
android:text="@string/label_max_rows"
|
|
||||||
android:textAppearance="?attr/textAppearanceBody2" />
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="6dp"
|
|
||||||
android:orientation="horizontal">
|
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
|
||||||
android:id="@+id/icons_aod_max_rows_minus"
|
|
||||||
style="?attr/materialButtonOutlinedStyle"
|
|
||||||
android:layout_width="48dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="-" />
|
|
||||||
|
|
||||||
<EditText
|
|
||||||
android:id="@+id/icons_aod_max_rows_input"
|
|
||||||
android:layout_width="72dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="8dp"
|
|
||||||
android:layout_marginEnd="8dp"
|
|
||||||
android:ems="3"
|
|
||||||
android:gravity="center"
|
|
||||||
android:importantForAutofill="no"
|
|
||||||
android:imeOptions="actionDone"
|
|
||||||
android:inputType="number"
|
|
||||||
android:maxLines="1"
|
|
||||||
android:singleLine="true"
|
|
||||||
android:textAppearance="?attr/textAppearanceSubtitle2" />
|
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
|
||||||
android:id="@+id/icons_aod_max_rows_plus"
|
|
||||||
style="?attr/materialButtonOutlinedStyle"
|
|
||||||
android:layout_width="48dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="+" />
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
<com.google.android.material.slider.Slider
|
|
||||||
android:id="@+id/icons_aod_max_rows_slider"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="8dp" />
|
|
||||||
|
|
||||||
<com.google.android.material.switchmaterial.SwitchMaterial
|
|
||||||
android:id="@+id/icons_aod_even_switch"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="8dp"
|
|
||||||
android:text="@string/label_even_distribution" />
|
|
||||||
|
|
||||||
<com.google.android.material.switchmaterial.SwitchMaterial
|
|
||||||
android:id="@+id/icons_aod_cutout_switch"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="4dp"
|
|
||||||
android:text="@string/label_cutout_aware" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="16dp"
|
|
||||||
android:text="@string/section_lockscreen"
|
|
||||||
android:textAppearance="?attr/textAppearanceSubtitle2" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="8dp"
|
|
||||||
android:text="@string/label_max_icons_per_row"
|
|
||||||
android:textAppearance="?attr/textAppearanceBody2" />
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="6dp"
|
|
||||||
android:orientation="horizontal">
|
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
|
||||||
android:id="@+id/icons_lock_max_icons_minus"
|
|
||||||
style="?attr/materialButtonOutlinedStyle"
|
|
||||||
android:layout_width="48dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="-" />
|
|
||||||
|
|
||||||
<EditText
|
|
||||||
android:id="@+id/icons_lock_max_icons_input"
|
|
||||||
android:layout_width="72dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="8dp"
|
|
||||||
android:layout_marginEnd="8dp"
|
|
||||||
android:ems="3"
|
|
||||||
android:gravity="center"
|
|
||||||
android:importantForAutofill="no"
|
|
||||||
android:imeOptions="actionDone"
|
|
||||||
android:inputType="number"
|
|
||||||
android:maxLines="1"
|
|
||||||
android:singleLine="true"
|
|
||||||
android:textAppearance="?attr/textAppearanceSubtitle2" />
|
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
|
||||||
android:id="@+id/icons_lock_max_icons_plus"
|
|
||||||
style="?attr/materialButtonOutlinedStyle"
|
|
||||||
android:layout_width="48dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="+" />
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
<com.google.android.material.slider.Slider
|
|
||||||
android:id="@+id/icons_lock_max_icons_slider"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="8dp" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="12dp"
|
|
||||||
android:text="@string/label_max_rows"
|
|
||||||
android:textAppearance="?attr/textAppearanceBody2" />
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="6dp"
|
|
||||||
android:orientation="horizontal">
|
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
|
||||||
android:id="@+id/icons_lock_max_rows_minus"
|
|
||||||
style="?attr/materialButtonOutlinedStyle"
|
|
||||||
android:layout_width="48dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="-" />
|
|
||||||
|
|
||||||
<EditText
|
|
||||||
android:id="@+id/icons_lock_max_rows_input"
|
|
||||||
android:layout_width="72dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="8dp"
|
|
||||||
android:layout_marginEnd="8dp"
|
|
||||||
android:ems="3"
|
|
||||||
android:gravity="center"
|
|
||||||
android:importantForAutofill="no"
|
|
||||||
android:imeOptions="actionDone"
|
|
||||||
android:inputType="number"
|
|
||||||
android:maxLines="1"
|
|
||||||
android:singleLine="true"
|
|
||||||
android:textAppearance="?attr/textAppearanceSubtitle2" />
|
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
|
||||||
android:id="@+id/icons_lock_max_rows_plus"
|
|
||||||
style="?attr/materialButtonOutlinedStyle"
|
|
||||||
android:layout_width="48dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="+" />
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
<com.google.android.material.slider.Slider
|
|
||||||
android:id="@+id/icons_lock_max_rows_slider"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="8dp" />
|
|
||||||
|
|
||||||
<com.google.android.material.switchmaterial.SwitchMaterial
|
|
||||||
android:id="@+id/icons_lock_even_switch"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="8dp"
|
|
||||||
android:text="@string/label_even_distribution" />
|
|
||||||
|
|
||||||
<com.google.android.material.switchmaterial.SwitchMaterial
|
|
||||||
android:id="@+id/icons_lock_cutout_switch"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="4dp"
|
|
||||||
android:text="@string/label_cutout_aware" />
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
</com.google.android.material.card.MaterialCardView>
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
</ScrollView>
|
|
||||||
@@ -6,9 +6,17 @@
|
|||||||
android:checkable="true"
|
android:checkable="true"
|
||||||
android:title="@string/nav_layout" />
|
android:title="@string/nav_layout" />
|
||||||
<item
|
<item
|
||||||
android:id="@+id/nav_notification_icons"
|
android:id="@+id/nav_layout_unlocked"
|
||||||
android:checkable="true"
|
android:checkable="true"
|
||||||
android:title="@string/nav_layout_notification_icons" />
|
android:title="@string/nav_layout_unlocked" />
|
||||||
|
<item
|
||||||
|
android:id="@+id/nav_layout_lockscreen"
|
||||||
|
android:checkable="true"
|
||||||
|
android:title="@string/nav_layout_lockscreen" />
|
||||||
|
<item
|
||||||
|
android:id="@+id/nav_layout_aod"
|
||||||
|
android:checkable="true"
|
||||||
|
android:title="@string/nav_layout_aod" />
|
||||||
<item
|
<item
|
||||||
android:id="@+id/nav_block_app_notification_icons"
|
android:id="@+id/nav_block_app_notification_icons"
|
||||||
android:checkable="true"
|
android:checkable="true"
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
<resources>
|
||||||
|
<item name="sbt_debug_container_bounds" type="id" />
|
||||||
|
</resources>
|
||||||
@@ -5,13 +5,14 @@
|
|||||||
<string name="nav_close">Close navigation</string>
|
<string name="nav_close">Close navigation</string>
|
||||||
<string name="nav_icons_debug">Debugging</string>
|
<string name="nav_icons_debug">Debugging</string>
|
||||||
<string name="nav_system_icons">Hide status icons</string>
|
<string name="nav_system_icons">Hide status icons</string>
|
||||||
<string name="nav_layout_notification_icons">    Notification icons</string>
|
|
||||||
<string name="nav_layout_hide_notification_icons">    Hide notification icons</string>
|
<string name="nav_layout_hide_notification_icons">    Hide notification icons</string>
|
||||||
<string name="nav_layout_hide_status_icons">    Hide status icons</string>
|
<string name="nav_layout_hide_status_icons">    Hide status icons</string>
|
||||||
<string name="nav_clock">Clock</string>
|
<string name="nav_clock">Clock</string>
|
||||||
<string name="nav_layout">Layout</string>
|
<string name="nav_layout">Layout</string>
|
||||||
|
<string name="nav_layout_unlocked">    Unlocked</string>
|
||||||
|
<string name="nav_layout_lockscreen">    Lockscreen</string>
|
||||||
|
<string name="nav_layout_aod">    AOD</string>
|
||||||
<string name="nav_battery_bar">Battery bar</string>
|
<string name="nav_battery_bar">Battery bar</string>
|
||||||
<string name="nav_notification_icons">Notification icons layout</string>
|
|
||||||
<string name="nav_block_app_notification_icons">Hide notification icons</string>
|
<string name="nav_block_app_notification_icons">Hide notification icons</string>
|
||||||
<string name="nav_status_chips">Status chips</string>
|
<string name="nav_status_chips">Status chips</string>
|
||||||
<string name="nav_misc">Misc</string>
|
<string name="nav_misc">Misc</string>
|
||||||
@@ -29,6 +30,15 @@
|
|||||||
<string name="debug_restart_systemui">Restart SystemUI</string>
|
<string name="debug_restart_systemui">Restart SystemUI</string>
|
||||||
<string name="debug_restart_systemui_started">SystemUI restart requested.</string>
|
<string name="debug_restart_systemui_started">SystemUI restart requested.</string>
|
||||||
<string name="debug_restart_systemui_failed">Could not restart SystemUI. Root shell failed.</string>
|
<string name="debug_restart_systemui_failed">Could not restart SystemUI. Root shell failed.</string>
|
||||||
|
<string name="debug_section_android_notification_style">Android notification style</string>
|
||||||
|
<string name="debug_notification_style_current">Current: %1$s</string>
|
||||||
|
<string name="debug_notification_style_hint">Temporary control for Samsung lockscreen/AOD notification style. Writes use root shell.</string>
|
||||||
|
<string name="debug_notification_style_cards">Cards</string>
|
||||||
|
<string name="debug_notification_style_icons">Icons</string>
|
||||||
|
<string name="debug_notification_style_dot">Dot</string>
|
||||||
|
<string name="debug_notification_style_unknown">unknown</string>
|
||||||
|
<string name="debug_notification_style_write_failed">Could not write notification style. Root shell failed.</string>
|
||||||
|
<string name="debug_notification_style_write_done">Notification style changed.</string>
|
||||||
<string name="debug_section_notification_icons">Notification icons</string>
|
<string name="debug_section_notification_icons">Notification icons</string>
|
||||||
<string name="debug_cycle_icons_label">Cycle debug icons</string>
|
<string name="debug_cycle_icons_label">Cycle debug icons</string>
|
||||||
<string name="debug_cycle_icons_hint">Cycles visible debug icons between 0 and the configured count every 2 seconds.</string>
|
<string name="debug_cycle_icons_hint">Cycles visible debug icons between 0 and the configured count every 2 seconds.</string>
|
||||||
@@ -151,21 +161,27 @@
|
|||||||
<string name="battery_bar_threshold_reordered">That threshold moved to a new position in the list.</string>
|
<string name="battery_bar_threshold_reordered">That threshold moved to a new position in the list.</string>
|
||||||
<string name="battery_bar_threshold_charge_note">Leave the field empty and press OK for \"Same as discharge\", or use the button below for \"Same as default\".</string>
|
<string name="battery_bar_threshold_charge_note">Leave the field empty and press OK for \"Same as discharge\", or use the button below for \"Same as default\".</string>
|
||||||
<string name="clock_title">Clock</string>
|
<string name="clock_title">Clock</string>
|
||||||
<string name="layout_title">Layout</string>
|
<string name="layout_home_title">Layout</string>
|
||||||
|
<string name="layout_title">Unlocked</string>
|
||||||
<string name="clock_master_toggles_title">Master toggles</string>
|
<string name="clock_master_toggles_title">Master toggles</string>
|
||||||
<string name="layout_master_toggle_title">Master toggle</string>
|
<string name="layout_master_toggle_title">Master toggle</string>
|
||||||
<string name="clock_enabled">Use custom clock</string>
|
<string name="clock_enabled">Use custom clock</string>
|
||||||
<string name="layout_enabled">Use custom layout</string>
|
<string name="layout_enabled">Use custom layout</string>
|
||||||
<string name="clock_position_title">Clock position</string>
|
<string name="clock_position_title">Clock position</string>
|
||||||
<string name="layout_clock_position_title">Clock position</string>
|
<string name="layout_clock_position_title">Clock</string>
|
||||||
<string name="layout_chip_position_title">Status chip position</string>
|
<string name="layout_chip_position_title">Status chip</string>
|
||||||
<string name="layout_notification_position_title">Notification icons position</string>
|
<string name="layout_notification_position_title">Notification icons</string>
|
||||||
<string name="layout_status_position_title">Status icons position</string>
|
<string name="layout_status_position_title">Status icons</string>
|
||||||
<string name="layout_show_dot_if_truncated">Show dot if truncated</string>
|
<string name="layout_item_enabled">Enabled</string>
|
||||||
|
<string name="layout_icon_container_count">Number of containers</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>
|
||||||
|
<string name="layout_locked_notifications_none">None</string>
|
||||||
|
<string name="layout_locked_notifications_none_disabled">None is remembered, but Layout OFF uses Dot.</string>
|
||||||
|
<string name="layout_misc_title">Misc</string>
|
||||||
|
<string name="label_max_unlocked_icons_per_row">Max unlocked icons per row</string>
|
||||||
<string name="layout_padding_title">Padding</string>
|
<string name="layout_padding_title">Padding</string>
|
||||||
<string name="layout_subpage_disabled_hint">Requires Layout to be ON</string>
|
<string name="layout_subpage_disabled_hint">Requires Layout to be ON</string>
|
||||||
<string name="notification_icons_layout_required_hint">Requires Layout to be ON</string>
|
|
||||||
<string name="notification_icons_unlocked_obsolete_hint">Obsolete when Layout is ON</string>
|
|
||||||
<string name="layout_cutout_gap_label">Gap from cutout (px)</string>
|
<string name="layout_cutout_gap_label">Gap from cutout (px)</string>
|
||||||
<string name="layout_left_padding_label">Left padding (px)</string>
|
<string name="layout_left_padding_label">Left padding (px)</string>
|
||||||
<string name="layout_right_padding_label">Right padding (px)</string>
|
<string name="layout_right_padding_label">Right padding (px)</string>
|
||||||
|
|||||||
+296
@@ -0,0 +1,296 @@
|
|||||||
|
package se.ajpanton.statusbartweak.runtime.layoutsolver;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertFalse;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import se.ajpanton.statusbartweak.runtime.layoutsolver.PackedStatusBarLayoutSolver.Box;
|
||||||
|
import se.ajpanton.statusbartweak.runtime.layoutsolver.PackedStatusBarLayoutSolver.Fallback;
|
||||||
|
import se.ajpanton.statusbartweak.runtime.layoutsolver.PackedStatusBarLayoutSolver.LayoutItem;
|
||||||
|
import se.ajpanton.statusbartweak.runtime.layoutsolver.PackedStatusBarLayoutSolver.Position;
|
||||||
|
import se.ajpanton.statusbartweak.runtime.layoutsolver.PackedStatusBarLayoutSolver.Spec;
|
||||||
|
|
||||||
|
public final class PackedStatusBarLayoutSolverTest {
|
||||||
|
@Test
|
||||||
|
public void sharedStatusPoolDoesNotExpandAcrossCutout() {
|
||||||
|
ArrayList<LayoutItem> items = baseItems(30, 2);
|
||||||
|
|
||||||
|
PackedStatusBarLayoutSolver.solve(cutoutSpec(), items);
|
||||||
|
|
||||||
|
LayoutItem status0 = find(items, "Status0");
|
||||||
|
LayoutItem status1 = find(items, "Status1");
|
||||||
|
assertFalse(overlapsCutout(status0));
|
||||||
|
assertFalse(overlapsCutout(status1));
|
||||||
|
assertTrue(status0.visibleIcons > 0);
|
||||||
|
assertTrue(status1.visibleIcons > 0);
|
||||||
|
assertTrue(status1.dot);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void sharedNotificationPoolDoesNotExpandAcrossCutout() {
|
||||||
|
ArrayList<LayoutItem> items = baseItems(3, 40);
|
||||||
|
|
||||||
|
PackedStatusBarLayoutSolver.solve(cutoutSpec(), items);
|
||||||
|
|
||||||
|
LayoutItem notifs0 = find(items, "Notifs0");
|
||||||
|
LayoutItem notifs1 = find(items, "Notifs1");
|
||||||
|
LayoutItem notifs2 = find(items, "Notifs2");
|
||||||
|
assertFalse(overlapsCutout(notifs0));
|
||||||
|
assertFalse(overlapsCutout(notifs1));
|
||||||
|
assertFalse(overlapsCutout(notifs2));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void zeroTruncationDoesNotExpandMultiBoxClock() {
|
||||||
|
LayoutItem clock = new LayoutItem(
|
||||||
|
"Clock",
|
||||||
|
"Clock",
|
||||||
|
Position.LEFT,
|
||||||
|
Fallback.LEFT,
|
||||||
|
LayoutItem.boxes(
|
||||||
|
new Box(102, 14, 33, 38),
|
||||||
|
new Box(0, 32, 134, 47)));
|
||||||
|
double beforeWidth = clock.width();
|
||||||
|
|
||||||
|
double actual = clock.truncateAtLeast(0);
|
||||||
|
|
||||||
|
assertTrue(actual == 0);
|
||||||
|
assertTrue(clock.width() == beforeWidth);
|
||||||
|
assertTrue(clock.boxes.get(0).width == 33);
|
||||||
|
assertTrue(clock.boxes.get(1).width == 134);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void discreteTruncationUsesRealIconWidthsAndWallCompactDot() {
|
||||||
|
ArrayList<LayoutItem> items = new ArrayList<>();
|
||||||
|
items.add(new LayoutItem(
|
||||||
|
"Clock",
|
||||||
|
"Clock",
|
||||||
|
Position.LEFT,
|
||||||
|
Fallback.LEFT,
|
||||||
|
LayoutItem.boxes(
|
||||||
|
new Box(102, 14, 33, 38),
|
||||||
|
new Box(0, 32, 134, 47))));
|
||||||
|
LayoutItem status = iconItem("Status", "Status", "status", 5, 35, 197, 18, 35);
|
||||||
|
status.iconWidths = new ArrayList<>(Arrays.asList(35, 35, 35, 35, 57));
|
||||||
|
status.widthLimit = 197.0;
|
||||||
|
LayoutItem notifications = iconItem("Notifs", "Notifs", "notifications", 5, 39, 195, 36, 39);
|
||||||
|
notifications.compactDotWidth = 27;
|
||||||
|
items.add(status);
|
||||||
|
items.add(notifications);
|
||||||
|
|
||||||
|
PackedStatusBarLayoutSolver.solve(productionSpec(834, 387, 59, 11), items);
|
||||||
|
|
||||||
|
assertTrue(status.visibleIcons == 5);
|
||||||
|
assertFalse(status.dot);
|
||||||
|
assertTrue(notifications.visibleIcons == 0);
|
||||||
|
assertTrue(notifications.dot);
|
||||||
|
assertTrue(notifications.right() <= 387);
|
||||||
|
assertTrue(notifications.left() - status.right() >= 11);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void middleLeftItemStacksAgainstCutoutWallInSplitRegion() {
|
||||||
|
ArrayList<LayoutItem> items = new ArrayList<>();
|
||||||
|
items.add(new LayoutItem(
|
||||||
|
"Clock",
|
||||||
|
"Clock",
|
||||||
|
Position.LEFT,
|
||||||
|
Fallback.LEFT,
|
||||||
|
LayoutItem.boxes(
|
||||||
|
new Box(102, 14, 33, 38),
|
||||||
|
new Box(0, 32, 134, 47))));
|
||||||
|
LayoutItem status = iconItem("Status", "Status", "status", 5, 35, 197, 18, 35);
|
||||||
|
status.iconWidths = new ArrayList<>(Arrays.asList(35, 35, 40, 34, 53));
|
||||||
|
status.widthLimit = 197.0;
|
||||||
|
LayoutItem notifications = new LayoutItem(
|
||||||
|
"Notifs",
|
||||||
|
"Notifs",
|
||||||
|
Position.MIDDLE,
|
||||||
|
Fallback.LEFT,
|
||||||
|
LayoutItem.boxes(new Box(0, 36, 156, 39)));
|
||||||
|
notifications.iconGroup = "notifications";
|
||||||
|
notifications.iconCount = 4;
|
||||||
|
notifications.visibleIcons = 4;
|
||||||
|
notifications.poolRemaining = 4;
|
||||||
|
notifications.iconWidth = 39;
|
||||||
|
notifications.iconWidths = new ArrayList<>(Arrays.asList(39, 39, 39, 39));
|
||||||
|
notifications.dotWidth = 39;
|
||||||
|
notifications.compactDotWidth = 27;
|
||||||
|
notifications.allowDot = true;
|
||||||
|
notifications.allowZero = false;
|
||||||
|
notifications.widthLimit = 156.0;
|
||||||
|
items.add(status);
|
||||||
|
items.add(notifications);
|
||||||
|
|
||||||
|
PackedStatusBarLayoutSolver.solve(productionSpec(834, 387, 59, 11), items);
|
||||||
|
|
||||||
|
assertTrue(notifications.right() <= 387);
|
||||||
|
assertTrue(notifications.left() >= status.right() + 11);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void truncatedDotUsesAvailableIconSlotAndWallCroppedDot() {
|
||||||
|
LayoutItem notifications = iconItem(
|
||||||
|
"Notifs",
|
||||||
|
"Notifs",
|
||||||
|
"notifications",
|
||||||
|
2,
|
||||||
|
39,
|
||||||
|
78,
|
||||||
|
36,
|
||||||
|
39);
|
||||||
|
notifications.direction = PackedStatusBarLayoutSolver.Direction.RIGHT;
|
||||||
|
notifications.widthLimit = 70.0;
|
||||||
|
|
||||||
|
notifications.recomputeIconState();
|
||||||
|
|
||||||
|
assertTrue(notifications.visibleIcons == 1);
|
||||||
|
assertTrue(notifications.dot);
|
||||||
|
assertTrue(notifications.width() == 65.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Spec cutoutSpec() {
|
||||||
|
return new Spec(
|
||||||
|
1200,
|
||||||
|
850,
|
||||||
|
100,
|
||||||
|
new ArrayList<>(Arrays.asList("Notifs", "Status", "Chip")));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Spec productionSpec(int screenWidth, int cutoutLeft, int cutoutWidth, int padding) {
|
||||||
|
return new Spec(
|
||||||
|
screenWidth,
|
||||||
|
cutoutLeft,
|
||||||
|
cutoutWidth,
|
||||||
|
padding,
|
||||||
|
new ArrayList<>(Arrays.asList("Notifs", "Status", "Chip", "Clock")));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static LayoutItem iconItem(
|
||||||
|
String name,
|
||||||
|
String kind,
|
||||||
|
String group,
|
||||||
|
int count,
|
||||||
|
int iconWidth,
|
||||||
|
int naturalWidth,
|
||||||
|
int top,
|
||||||
|
int height
|
||||||
|
) {
|
||||||
|
LayoutItem item = new LayoutItem(
|
||||||
|
name,
|
||||||
|
kind,
|
||||||
|
Position.LEFT,
|
||||||
|
Fallback.LEFT,
|
||||||
|
LayoutItem.boxes(new Box(0, top, naturalWidth, height)));
|
||||||
|
item.iconGroup = group;
|
||||||
|
item.iconCount = count;
|
||||||
|
item.visibleIcons = count;
|
||||||
|
item.poolRemaining = count;
|
||||||
|
item.iconWidth = iconWidth;
|
||||||
|
item.dotWidth = iconWidth;
|
||||||
|
item.compactDotWidth = iconWidth;
|
||||||
|
item.allowDot = true;
|
||||||
|
item.allowZero = false;
|
||||||
|
item.widthLimit = count * (double) iconWidth;
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ArrayList<LayoutItem> baseItems(int statusIcons, int notificationIcons) {
|
||||||
|
ArrayList<LayoutItem> items = new ArrayList<>();
|
||||||
|
LayoutItem clock = new LayoutItem(
|
||||||
|
"Clock",
|
||||||
|
"Clock",
|
||||||
|
Position.LEFT,
|
||||||
|
Fallback.LEFT,
|
||||||
|
LayoutItem.boxes(
|
||||||
|
new Box(0, 0, 240, 40),
|
||||||
|
new Box(80, -20, 30, 30)));
|
||||||
|
clock.y = 100;
|
||||||
|
items.add(clock);
|
||||||
|
|
||||||
|
items.add(LayoutItem.fixed(
|
||||||
|
"Chip",
|
||||||
|
"Chip",
|
||||||
|
Position.MIDDLE,
|
||||||
|
Fallback.RIGHT,
|
||||||
|
400,
|
||||||
|
40,
|
||||||
|
90));
|
||||||
|
|
||||||
|
addIcon(items, "Notifs0", "Notifs", Position.LEFT, Fallback.LEFT, "notifications", notificationIcons, 0);
|
||||||
|
addIcon(items, "Notifs1", "Notifs", Position.LEFT, Fallback.LEFT, "notifications", notificationIcons, 50);
|
||||||
|
addIcon(items, "Notifs2", "Notifs", Position.LEFT, Fallback.LEFT, "notifications", notificationIcons, 30);
|
||||||
|
addIcon(items, "Status0", "Status", Position.RIGHT, Fallback.RIGHT, "status", statusIcons, 50);
|
||||||
|
addIcon(items, "Status1", "Status", Position.RIGHT, Fallback.RIGHT, "status", statusIcons, 100);
|
||||||
|
|
||||||
|
items.add(LayoutItem.fixed("Dummy0", "Dummy", Position.MIDDLE, Fallback.LEFT, 100, 30, 50));
|
||||||
|
items.add(LayoutItem.fixed("Dummy1", "Dummy", Position.MIDDLE, Fallback.LEFT, 100, 30, 0));
|
||||||
|
items.add(LayoutItem.fixed("Dummy2", "Dummy", Position.MIDDLE, Fallback.LEFT, 100, 30, 20));
|
||||||
|
configureIconGroups(items);
|
||||||
|
return sortByKindOrder(items, Arrays.asList("Clock", "Chip", "Status", "Notifs", "Dummy"));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void addIcon(
|
||||||
|
ArrayList<LayoutItem> items,
|
||||||
|
String name,
|
||||||
|
String kind,
|
||||||
|
Position position,
|
||||||
|
Fallback fallback,
|
||||||
|
String group,
|
||||||
|
int count,
|
||||||
|
int y
|
||||||
|
) {
|
||||||
|
items.add(LayoutItem.iconStrip(name, kind, position, fallback, group, count, 50, y));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void configureIconGroups(ArrayList<LayoutItem> items) {
|
||||||
|
ArrayList<LayoutItem> status = new ArrayList<>();
|
||||||
|
ArrayList<LayoutItem> notifications = new ArrayList<>();
|
||||||
|
for (LayoutItem item : items) {
|
||||||
|
if ("status".equals(item.iconGroup)) {
|
||||||
|
status.add(item);
|
||||||
|
} else if ("notifications".equals(item.iconGroup)) {
|
||||||
|
notifications.add(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
configureGroup(status);
|
||||||
|
configureGroup(notifications);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void configureGroup(ArrayList<LayoutItem> items) {
|
||||||
|
for (int i = 0; i < items.size(); i++) {
|
||||||
|
LayoutItem item = items.get(i);
|
||||||
|
item.allowZero = i < items.size() - 1;
|
||||||
|
item.allowDot = i == items.size() - 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ArrayList<LayoutItem> sortByKindOrder(ArrayList<LayoutItem> items, List<String> order) {
|
||||||
|
ArrayList<LayoutItem> sorted = new ArrayList<>(items);
|
||||||
|
sorted.sort((a, b) -> {
|
||||||
|
int kind = Integer.compare(order.indexOf(a.kind), order.indexOf(b.kind));
|
||||||
|
return kind != 0 ? kind : a.name.compareTo(b.name);
|
||||||
|
});
|
||||||
|
return sorted;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static LayoutItem find(ArrayList<LayoutItem> items, String name) {
|
||||||
|
for (LayoutItem item : items) {
|
||||||
|
if (name.equals(item.name)) {
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new AssertionError("Missing item " + name);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean overlapsCutout(LayoutItem item) {
|
||||||
|
return Math.max(item.left(), 850) < Math.min(item.right(), 950);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+85
@@ -0,0 +1,85 @@
|
|||||||
|
package se.ajpanton.statusbartweak.runtime.render;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
import se.ajpanton.statusbartweak.runtime.layoutsolver.StatusBarLayoutSolver.AnchorSide;
|
||||||
|
import se.ajpanton.statusbartweak.runtime.layoutsolver.StatusBarLayoutSolver.Bounds;
|
||||||
|
import se.ajpanton.statusbartweak.runtime.layoutsolver.StatusBarLayoutSolver.LayoutItemKind;
|
||||||
|
import se.ajpanton.statusbartweak.runtime.layoutsolver.StatusBarLayoutSolver.LayoutPosition;
|
||||||
|
|
||||||
|
public final class UnlockedLayoutBandTest {
|
||||||
|
@Test
|
||||||
|
public void compactNotificationDotUsesSolverWidthAfterIconShrinking() {
|
||||||
|
UnlockedLayoutBand band = UnlockedLayoutBand.icons(
|
||||||
|
LayoutItemKind.NOTIFICATIONS,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
placements(6, 39, 39),
|
||||||
|
LayoutPosition.LEFT,
|
||||||
|
false,
|
||||||
|
AnchorSide.LEFT,
|
||||||
|
true);
|
||||||
|
|
||||||
|
band.applyPackedIconState(0, 0, true, 14);
|
||||||
|
band.place(388);
|
||||||
|
|
||||||
|
assertEquals(1, band.placements.size());
|
||||||
|
SnapshotPlacement dot = band.placements.get(0);
|
||||||
|
assertTrue(dot.overflowDot);
|
||||||
|
assertEquals(14, dot.bounds.width);
|
||||||
|
assertEquals(388, dot.bounds.left);
|
||||||
|
assertEquals(14, band.placedBounds.width);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void packedIconStateUsesRequestedShrinkableIconSlice() {
|
||||||
|
ArrayList<SnapshotPlacement> placements = placements(6, 39, 39);
|
||||||
|
placements.add(1, new SnapshotPlacement(
|
||||||
|
null,
|
||||||
|
new Bounds(0, 36, 39, 39),
|
||||||
|
"existing-dot",
|
||||||
|
false,
|
||||||
|
true,
|
||||||
|
null,
|
||||||
|
new Bounds(0, 0, 39, 39),
|
||||||
|
0));
|
||||||
|
UnlockedLayoutBand band = UnlockedLayoutBand.icons(
|
||||||
|
LayoutItemKind.NOTIFICATIONS,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
placements,
|
||||||
|
LayoutPosition.LEFT,
|
||||||
|
false,
|
||||||
|
AnchorSide.LEFT,
|
||||||
|
true);
|
||||||
|
|
||||||
|
band.applyPackedIconState(2, 3, false, 117);
|
||||||
|
|
||||||
|
assertEquals(3, band.placements.size());
|
||||||
|
assertEquals("icon2", band.placements.get(0).key);
|
||||||
|
assertEquals("icon3", band.placements.get(1).key);
|
||||||
|
assertEquals("icon4", band.placements.get(2).key);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ArrayList<SnapshotPlacement> placements(int count, int width, int height) {
|
||||||
|
ArrayList<SnapshotPlacement> result = new ArrayList<>();
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
Bounds bounds = new Bounds(0, 36, width, height);
|
||||||
|
result.add(new SnapshotPlacement(
|
||||||
|
null,
|
||||||
|
bounds,
|
||||||
|
"icon" + i,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
null,
|
||||||
|
new Bounds(0, 0, width, height),
|
||||||
|
0));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user