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) {
|
||||
if (!hideTrackedLockedStatusBarIconSurfaces(root)) {
|
||||
hideTrackedLockedStatusBarIconSurfaces(root);
|
||||
if (root != null) {
|
||||
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.Objects;
|
||||
|
||||
import se.ajpanton.statusbartweak.R;
|
||||
import se.ajpanton.statusbartweak.runtime.layoutsolver.StatusBarLayoutSolver;
|
||||
import se.ajpanton.statusbartweak.shell.settings.SbtSettings;
|
||||
|
||||
public final class DebugBoundsOverlayController {
|
||||
@@ -55,37 +57,31 @@ public final class DebugBoundsOverlayController {
|
||||
addCutoutSpecs(root, specs);
|
||||
}
|
||||
if (layoutEnabled && settings.debugVisualizeNotificationContainer) {
|
||||
Bounds bounds = unionHostChildren(
|
||||
addHostContainerSpecs(
|
||||
root,
|
||||
parent,
|
||||
OwnedIconHostManager.TAG_UNLOCKED_NOTIFICATION_HOST,
|
||||
true);
|
||||
if (bounds != null) {
|
||||
specs.add(new OverlaySpec(bounds, COLOR_NOTIFICATION));
|
||||
}
|
||||
COLOR_NOTIFICATION,
|
||||
specs);
|
||||
}
|
||||
if (layoutEnabled && settings.debugVisualizeStatusContainer) {
|
||||
Bounds bounds = unionHostChildren(
|
||||
addHostContainerSpecs(
|
||||
root,
|
||||
parent,
|
||||
OwnedIconHostManager.TAG_UNLOCKED_STATUS_HOST,
|
||||
false);
|
||||
if (bounds != null) {
|
||||
specs.add(new OverlaySpec(bounds, COLOR_STATUS));
|
||||
}
|
||||
COLOR_STATUS,
|
||||
specs);
|
||||
}
|
||||
if (layoutEnabled && settings.debugVisualizeClockContainer) {
|
||||
addHostChildSpecs(root, parent, TAG_CLOCK_HOST, COLOR_CLOCK, specs);
|
||||
}
|
||||
if (layoutEnabled && settings.debugVisualizeChipContainer) {
|
||||
Bounds bounds = unionHostChildren(
|
||||
addHostContainerSpecs(
|
||||
root,
|
||||
parent,
|
||||
OwnedIconHostManager.TAG_UNLOCKED_CHIP_HOST,
|
||||
false);
|
||||
if (bounds != null) {
|
||||
specs.add(new OverlaySpec(bounds, COLOR_CHIP));
|
||||
}
|
||||
COLOR_CHIP,
|
||||
specs);
|
||||
}
|
||||
if (specs.isEmpty()) {
|
||||
removeOverlay(parent);
|
||||
@@ -144,15 +140,25 @@ public final class DebugBoundsOverlayController {
|
||||
return new Bounds(left, top, right - left, bottom - top);
|
||||
}
|
||||
|
||||
private Bounds unionHostChildren(
|
||||
private void addHostContainerSpecs(
|
||||
View root,
|
||||
ViewGroup parent,
|
||||
String hostTag,
|
||||
boolean stableRegularIconSlots
|
||||
int color,
|
||||
ArrayList<OverlaySpec> specs
|
||||
) {
|
||||
View host = findDirectTaggedChild(parent, hostTag);
|
||||
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;
|
||||
for (int i = 0; i < group.getChildCount(); i++) {
|
||||
@@ -160,13 +166,30 @@ public final class DebugBoundsOverlayController {
|
||||
if (child == null || child.getVisibility() == View.GONE) {
|
||||
continue;
|
||||
}
|
||||
Bounds bounds = overlayBoundsRelativeTo(root, child, stableRegularIconSlots);
|
||||
Bounds bounds = overlayBoundsRelativeTo(root, child, false);
|
||||
if (bounds == null || bounds.width <= 0 || bounds.height <= 0) {
|
||||
continue;
|
||||
}
|
||||
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) {
|
||||
|
||||
@@ -577,11 +577,10 @@ final class SnapshotRenderer {
|
||||
|
||||
private void drawOverflowDot(Bitmap bitmap, int color) {
|
||||
bitmap.eraseColor(Color.TRANSPARENT);
|
||||
int base = Math.min(bitmap.getWidth(), bitmap.getHeight());
|
||||
if (base <= 0) {
|
||||
if (bitmap.getWidth() <= 0 || bitmap.getHeight() <= 0) {
|
||||
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.setColor(color);
|
||||
Canvas canvas = new Canvas(bitmap);
|
||||
|
||||
@@ -164,6 +164,32 @@ final class UnlockedLayoutBand extends StatusBarLayoutSolver.LayoutBand {
|
||||
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
|
||||
protected int wallDotSlack(AnchorSide side) {
|
||||
SnapshotPlacement placement = wallPlacement(side);
|
||||
@@ -238,6 +264,25 @@ final class UnlockedLayoutBand extends StatusBarLayoutSolver.LayoutBand {
|
||||
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
|
||||
protected Object captureState() {
|
||||
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));
|
||||
}
|
||||
|
||||
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) {
|
||||
for (SnapshotPlacement icon : icons) {
|
||||
if (icon.overflowDot) {
|
||||
@@ -476,6 +561,18 @@ final class UnlockedLayoutBand extends StatusBarLayoutSolver.LayoutBand {
|
||||
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) {
|
||||
if (!icons.isEmpty()) {
|
||||
return icons.get(0).bounds.height;
|
||||
|
||||
+311
-90
@@ -5,19 +5,18 @@ import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
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.Anchor;
|
||||
import se.ajpanton.statusbartweak.runtime.layoutsolver.StatusBarLayoutSolver.AnchorKind;
|
||||
import se.ajpanton.statusbartweak.runtime.layoutsolver.StatusBarLayoutSolver.AnchorSide;
|
||||
import se.ajpanton.statusbartweak.runtime.layoutsolver.StatusBarLayoutSolver.Bounds;
|
||||
import se.ajpanton.statusbartweak.runtime.layoutsolver.StatusBarLayoutSolver.LayoutEdges;
|
||||
import se.ajpanton.statusbartweak.runtime.layoutsolver.StatusBarLayoutSolver.LayoutItemKind;
|
||||
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.PackedStatusBarLayoutSolver;
|
||||
import se.ajpanton.statusbartweak.shell.settings.SbtSettings;
|
||||
|
||||
final class UnlockedLayoutPlanner {
|
||||
@@ -81,106 +80,68 @@ final class UnlockedLayoutPlanner {
|
||||
false));
|
||||
}
|
||||
}
|
||||
if (settings.layoutStatusEnabledUnlocked) {
|
||||
if (settings.layoutStatusUnlockedCount > 0) {
|
||||
ArrayList<SnapshotPlacement> rawPlacements =
|
||||
statusRenderer.rawPlacements(root, collectedIcons.statusIcons);
|
||||
ArrayList<SnapshotPlacement> placements =
|
||||
offsetPlacements(
|
||||
rawPlacements,
|
||||
offsetScaler.status(
|
||||
settings.layoutStatusVerticalOffsetPx,
|
||||
representativeHeight(rawPlacements)));
|
||||
if (!placements.isEmpty()) {
|
||||
bands.add(UnlockedLayoutBand.icons(
|
||||
LayoutItemKind.STATUS,
|
||||
statusHost,
|
||||
statusRenderer,
|
||||
placements,
|
||||
positionFor(settings.layoutStatusPosition),
|
||||
settings.layoutStatusMiddleAutoNearestSide,
|
||||
sideFor(settings.layoutStatusMiddleSide),
|
||||
settings.layoutStatusShowDotIfTruncated));
|
||||
int representativeHeight = representativeHeight(rawPlacements);
|
||||
for (int i = 0; i < settings.layoutStatusUnlockedCount; i++) {
|
||||
ArrayList<SnapshotPlacement> placements =
|
||||
offsetPlacements(
|
||||
rawPlacements,
|
||||
offsetScaler.status(
|
||||
intAt(settings.layoutStatusVerticalOffsetsPx, i, settings.layoutStatusVerticalOffsetPx),
|
||||
representativeHeight));
|
||||
if (!placements.isEmpty()) {
|
||||
bands.add(UnlockedLayoutBand.icons(
|
||||
LayoutItemKind.STATUS,
|
||||
statusHost,
|
||||
statusRenderer,
|
||||
placements,
|
||||
positionFor(stringAt(settings.layoutStatusPositions, i, settings.layoutStatusPosition)),
|
||||
booleanAt(
|
||||
settings.layoutStatusMiddleAutoNearestSides,
|
||||
i,
|
||||
settings.layoutStatusMiddleAutoNearestSide),
|
||||
sideFor(stringAt(settings.layoutStatusMiddleSides, i, settings.layoutStatusMiddleSide)),
|
||||
true));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (settings.layoutNotifEnabledUnlocked) {
|
||||
if (settings.layoutNotifUnlockedCount > 0) {
|
||||
ArrayList<SnapshotPlacement> rawPlacements =
|
||||
notificationRenderer.rawPlacements(root, collectedIcons.notificationIcons);
|
||||
ArrayList<SnapshotPlacement> placements =
|
||||
offsetPlacements(
|
||||
rawPlacements,
|
||||
offsetScaler.notification(
|
||||
settings.layoutNotifVerticalOffsetPx,
|
||||
representativeHeight(rawPlacements)));
|
||||
if (!placements.isEmpty()) {
|
||||
bands.add(UnlockedLayoutBand.icons(
|
||||
LayoutItemKind.NOTIFICATIONS,
|
||||
notificationHost,
|
||||
notificationRenderer,
|
||||
placements,
|
||||
positionFor(settings.layoutNotifPosition),
|
||||
settings.layoutNotifMiddleAutoNearestSide,
|
||||
sideFor(settings.layoutNotifMiddleSide),
|
||||
settings.layoutNotifShowDotIfTruncated));
|
||||
int representativeHeight = representativeHeight(rawPlacements);
|
||||
for (int i = 0; i < settings.layoutNotifUnlockedCount; i++) {
|
||||
ArrayList<SnapshotPlacement> placements =
|
||||
offsetPlacements(
|
||||
rawPlacements,
|
||||
offsetScaler.notification(
|
||||
intAt(settings.layoutNotifVerticalOffsetsPx, i, settings.layoutNotifVerticalOffsetPx),
|
||||
representativeHeight));
|
||||
if (!placements.isEmpty()) {
|
||||
bands.add(UnlockedLayoutBand.icons(
|
||||
LayoutItemKind.NOTIFICATIONS,
|
||||
notificationHost,
|
||||
notificationRenderer,
|
||||
placements,
|
||||
positionFor(stringAt(settings.layoutNotifPositions, i, settings.layoutNotifPosition)),
|
||||
booleanAt(
|
||||
settings.layoutNotifMiddleAutoNearestSides,
|
||||
i,
|
||||
settings.layoutNotifMiddleAutoNearestSide),
|
||||
sideFor(stringAt(settings.layoutNotifMiddleSides, i, settings.layoutNotifMiddleSide)),
|
||||
true));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (bands.isEmpty()) {
|
||||
return new LayoutPlan(null, new ArrayList<>(), new ArrayList<>(), new ArrayList<>());
|
||||
}
|
||||
ArrayList<PlacedBand> placedBands = new ArrayList<>();
|
||||
ArrayList<LayoutItemKind> itemOrder = orderedKinds(settings.layoutItemOrder);
|
||||
splitClockIfNeeded(root, bands, itemOrder, clockCutoutBounds, settings);
|
||||
LayoutEdges edges = layoutEdges(root, settings, anchors);
|
||||
int itemPadding = Math.max(0, settings.layoutItemPaddingPx);
|
||||
RegionPlan<UnlockedLayoutBand> regionPlan = StatusBarLayoutSolver.planRegions(
|
||||
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);
|
||||
}
|
||||
solveWithPackedLayout(root, edges, bands, layoutCutoutBounds, settings, itemPadding);
|
||||
|
||||
ArrayList<SnapshotPlacement> statusPlacements = new ArrayList<>();
|
||||
ArrayList<SnapshotPlacement> notificationPlacements = new ArrayList<>();
|
||||
@@ -204,6 +165,266 @@ final class UnlockedLayoutPlanner {
|
||||
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) {
|
||||
if (placements == null || placements.isEmpty() || verticalOffsetPx == 0) {
|
||||
return placements;
|
||||
|
||||
+10
-2
@@ -718,14 +718,22 @@ final class UnlockedStockSourceCollector {
|
||||
settings.layoutChipVerticalOffsetPx,
|
||||
settings.layoutNotifPosition,
|
||||
settings.layoutNotifSortOrder,
|
||||
settings.layoutNotifShowDotIfTruncated,
|
||||
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.layoutNotifMiddleAutoNearestSide,
|
||||
settings.layoutNotifVerticalOffsetPx,
|
||||
settings.layoutStatusPosition,
|
||||
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.layoutStatusMiddleAutoNearestSide,
|
||||
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;
|
||||
|
||||
// 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_MAX = 15;
|
||||
public static final int CARDS_AOD_MAX_ROWS_DEFAULT = 3;
|
||||
public static final int CARDS_AOD_MAX_ROWS_MIN = 1;
|
||||
public static final int CARDS_AOD_MAX_ROWS_DEFAULT = 1;
|
||||
public static final int CARDS_AOD_MAX_ROWS_MIN = 0;
|
||||
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_LIMIT_TO_WIDTH_DEFAULT = false;
|
||||
public static final boolean CARDS_AOD_LIMIT_TO_WIDTH_DEFAULT = true;
|
||||
|
||||
// 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_MAX = 15;
|
||||
public static final int CARDS_LOCK_MAX_ROWS_DEFAULT = 3;
|
||||
public static final int CARDS_LOCK_MAX_ROWS_MIN = 1;
|
||||
public static final int CARDS_LOCK_MAX_ROWS_DEFAULT = 1;
|
||||
public static final int CARDS_LOCK_MAX_ROWS_MIN = 0;
|
||||
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_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 BATTERY_LOCKSCREEN_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_LOCKSCREEN_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 boolean LAYOUT_NOTIF_MIDDLE_AUTO_NEAREST_SIDE_DEFAULT = true;
|
||||
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_LOCKSCREEN_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 String LAYOUT_STATUS_MIDDLE_SIDE_DEFAULT = "left";
|
||||
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_ICON_CONTAINER_COUNT_MIN = 0;
|
||||
public static final int LAYOUT_ICON_CONTAINER_COUNT_MAX = 3;
|
||||
|
||||
private SbtDefaults() {
|
||||
}
|
||||
|
||||
@@ -13,6 +13,10 @@ import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
public final class SbtSettings {
|
||||
private interface IndexedKey {
|
||||
String key(int index);
|
||||
}
|
||||
|
||||
public static final String MODULE_PACKAGE = "se.ajpanton.statusbartweak";
|
||||
public static final String PREFS_NAME = "sbt_settings";
|
||||
public static final String ACTION_SETTINGS_CHANGED =
|
||||
@@ -32,6 +36,8 @@ public final class SbtSettings {
|
||||
"debug_visualize_chip_container";
|
||||
public static final String KEY_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_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";
|
||||
@@ -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_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_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_AUTO_NEAREST_SIDE = "layout_notif_middle_auto_nearest_side";
|
||||
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_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_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_MIDDLE_SIDE = "layout_status_middle_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_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 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 enableLockCutoutLimit;
|
||||
public final int unlockedMaxIconsPerRow;
|
||||
@@ -180,6 +224,11 @@ public final class SbtSettings {
|
||||
public final boolean layoutNotifEnabledAod;
|
||||
public final boolean layoutNotifEnabledLockscreen;
|
||||
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 boolean layoutNotifMiddleAutoNearestSide;
|
||||
public final int layoutNotifVerticalOffsetPx;
|
||||
@@ -187,6 +236,11 @@ public final class SbtSettings {
|
||||
public final boolean layoutStatusEnabledAod;
|
||||
public final boolean layoutStatusEnabledLockscreen;
|
||||
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 String layoutStatusMiddleSide;
|
||||
public final boolean layoutStatusMiddleAutoNearestSide;
|
||||
@@ -272,6 +326,11 @@ public final class SbtSettings {
|
||||
String layoutNotifMiddleSide,
|
||||
boolean layoutNotifMiddleAutoNearestSide,
|
||||
int layoutNotifVerticalOffsetPx,
|
||||
int layoutNotifUnlockedCount,
|
||||
String[] layoutNotifPositions,
|
||||
String[] layoutNotifMiddleSides,
|
||||
boolean[] layoutNotifMiddleAutoNearestSides,
|
||||
int[] layoutNotifVerticalOffsetsPx,
|
||||
String layoutStatusPosition,
|
||||
boolean layoutStatusEnabledAod,
|
||||
boolean layoutStatusEnabledLockscreen,
|
||||
@@ -280,6 +339,11 @@ public final class SbtSettings {
|
||||
String layoutStatusMiddleSide,
|
||||
boolean layoutStatusMiddleAutoNearestSide,
|
||||
int layoutStatusVerticalOffsetPx,
|
||||
int layoutStatusUnlockedCount,
|
||||
String[] layoutStatusPositions,
|
||||
String[] layoutStatusMiddleSides,
|
||||
boolean[] layoutStatusMiddleAutoNearestSides,
|
||||
int[] layoutStatusVerticalOffsetsPx,
|
||||
Map<String, String> clockCameraTypeOverrides,
|
||||
Map<String, Integer> systemIconBlockedModes,
|
||||
boolean statusChipsHideAll,
|
||||
@@ -424,6 +488,27 @@ public final class SbtSettings {
|
||||
layoutNotifVerticalOffsetPx,
|
||||
SbtDefaults.CLOCK_VERTICAL_OFFSET_PX_MIN,
|
||||
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
|
||||
? layoutStatusPosition
|
||||
: SbtDefaults.LAYOUT_STATUS_POSITION_DEFAULT;
|
||||
@@ -439,6 +524,27 @@ public final class SbtSettings {
|
||||
layoutStatusVerticalOffsetPx,
|
||||
SbtDefaults.CLOCK_VERTICAL_OFFSET_PX_MIN,
|
||||
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
|
||||
? Collections.unmodifiableMap(new HashMap<>(clockCameraTypeOverrides))
|
||||
: Collections.emptyMap();
|
||||
@@ -540,6 +646,11 @@ public final class SbtSettings {
|
||||
SbtDefaults.LAYOUT_NOTIF_MIDDLE_SIDE_DEFAULT,
|
||||
SbtDefaults.LAYOUT_NOTIF_MIDDLE_AUTO_NEAREST_SIDE_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_ENABLED_AOD_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_AUTO_NEAREST_SIDE_DEFAULT,
|
||||
SbtDefaults.LAYOUT_STATUS_VERTICAL_OFFSET_PX_DEFAULT,
|
||||
SbtDefaults.LAYOUT_STATUS_UNLOCKED_COUNT_DEFAULT,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
Collections.emptyMap(),
|
||||
Collections.emptyMap(),
|
||||
false,
|
||||
@@ -787,6 +903,28 @@ public final class SbtSettings {
|
||||
SbtDefaults.LAYOUT_NOTIF_VERTICAL_OFFSET_PX_DEFAULT),
|
||||
SbtDefaults.CLOCK_VERTICAL_OFFSET_PX_MIN,
|
||||
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.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),
|
||||
@@ -798,6 +936,28 @@ public final class SbtSettings {
|
||||
SbtDefaults.LAYOUT_STATUS_VERTICAL_OFFSET_PX_DEFAULT),
|
||||
SbtDefaults.CLOCK_VERTICAL_OFFSET_PX_MIN,
|
||||
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(
|
||||
prefs.getStringSet(KEY_CLOCK_CAMERA_TYPE_OVERRIDES, Collections.emptySet()))),
|
||||
systemIconBlockedModes,
|
||||
@@ -940,6 +1100,28 @@ public final class SbtSettings {
|
||||
SbtDefaults.LAYOUT_NOTIF_VERTICAL_OFFSET_PX_DEFAULT),
|
||||
SbtDefaults.CLOCK_VERTICAL_OFFSET_PX_MIN,
|
||||
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.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),
|
||||
@@ -951,6 +1133,28 @@ public final class SbtSettings {
|
||||
SbtDefaults.LAYOUT_STATUS_VERTICAL_OFFSET_PX_DEFAULT),
|
||||
SbtDefaults.CLOCK_VERTICAL_OFFSET_PX_MIN,
|
||||
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)),
|
||||
systemIconBlockedModes,
|
||||
bundle.getBoolean(KEY_STATUS_CHIPS_HIDE_ALL, false),
|
||||
@@ -985,6 +1189,116 @@ public final class SbtSettings {
|
||||
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) {
|
||||
if (source == null || source.isEmpty()) {
|
||||
return Collections.emptySet();
|
||||
|
||||
@@ -12,6 +12,10 @@ import java.util.ArrayList;
|
||||
import java.util.Map;
|
||||
|
||||
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 METHOD_GET_UNLOCKED = "get_unlocked";
|
||||
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,
|
||||
prefs.getBoolean(SbtSettings.KEY_LAYOUT_NOTIF_ENABLED_UNLOCKED,
|
||||
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,
|
||||
prefs.getString(SbtSettings.KEY_LAYOUT_NOTIF_MIDDLE_SIDE,
|
||||
SbtDefaults.LAYOUT_NOTIF_MIDDLE_SIDE_DEFAULT));
|
||||
@@ -223,6 +234,17 @@ public class SbtSettingsProvider extends ContentProvider {
|
||||
out.putInt(SbtSettings.KEY_LAYOUT_NOTIF_VERTICAL_OFFSET_PX,
|
||||
prefs.getInt(SbtSettings.KEY_LAYOUT_NOTIF_VERTICAL_OFFSET_PX,
|
||||
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,
|
||||
prefs.getString(SbtSettings.KEY_LAYOUT_STATUS_POSITION,
|
||||
SbtDefaults.LAYOUT_STATUS_POSITION_DEFAULT));
|
||||
@@ -235,6 +257,13 @@ public class SbtSettingsProvider extends ContentProvider {
|
||||
out.putBoolean(SbtSettings.KEY_LAYOUT_STATUS_ENABLED_UNLOCKED,
|
||||
prefs.getBoolean(SbtSettings.KEY_LAYOUT_STATUS_ENABLED_UNLOCKED,
|
||||
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,
|
||||
prefs.getBoolean(SbtSettings.KEY_LAYOUT_STATUS_SHOW_DOT_IF_TRUNCATED,
|
||||
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,
|
||||
prefs.getInt(SbtSettings.KEY_LAYOUT_STATUS_VERTICAL_OFFSET_PX,
|
||||
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(
|
||||
SbtSettings.KEY_CLOCK_CAMERA_TYPE_OVERRIDES,
|
||||
new ArrayList<>(prefs.getStringSet(
|
||||
@@ -307,6 +347,26 @@ public class SbtSettingsProvider extends ContentProvider {
|
||||
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
|
||||
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
|
||||
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.shell.debug.DebugNotifications;
|
||||
import se.ajpanton.statusbartweak.shell.debug.NotificationDisplayStyleSettings;
|
||||
import se.ajpanton.statusbartweak.shell.settings.ClockCameraTypeSupport;
|
||||
import se.ajpanton.statusbartweak.shell.settings.SbtDefaults;
|
||||
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);
|
||||
MaterialButton restartSystemUiButton = root.findViewById(R.id.debug_restart_systemui_button);
|
||||
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);
|
||||
LinearLayout currentCameraOverrideRow = root.findViewById(R.id.debug_camera_current_override_row);
|
||||
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,
|
||||
SbtDefaults.DEBUG_VISUALIZE_CAMERA_CUTOUT_DEFAULT);
|
||||
bindRestartSystemUiButton(restartSystemUiButton);
|
||||
bindNotificationStyleControls(
|
||||
notificationStyleCurrent,
|
||||
notificationStyleCards,
|
||||
notificationStyleIcons,
|
||||
notificationStyleDot);
|
||||
updateVisualizerSwitchAvailability(
|
||||
prefs,
|
||||
visualizeNotificationContainerSwitch,
|
||||
@@ -188,6 +198,76 @@ public class IconsDebugFragment extends Fragment {
|
||||
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,
|
||||
SwitchMaterial notificationSwitch,
|
||||
SwitchMaterial statusSwitch,
|
||||
|
||||
@@ -17,6 +17,7 @@ import android.view.ViewGroup;
|
||||
import android.view.inputmethod.EditorInfo;
|
||||
import android.widget.CheckBox;
|
||||
import android.widget.EditText;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.RadioGroup;
|
||||
import android.widget.TextView;
|
||||
|
||||
@@ -28,6 +29,7 @@ import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.google.android.material.button.MaterialButton;
|
||||
import com.google.android.material.card.MaterialCardView;
|
||||
import com.google.android.material.switchmaterial.SwitchMaterial;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
@@ -45,6 +47,7 @@ public final class LayoutFragment extends Fragment {
|
||||
View root = inflater.inflate(R.layout.fragment_layout, container, false);
|
||||
SharedPreferences prefs = requireContext()
|
||||
.getSharedPreferences(SbtSettings.PREFS_NAME, Context.MODE_PRIVATE);
|
||||
hideUnlockedSubpageObsoleteControls(root);
|
||||
|
||||
SwitchMaterial enabledSwitch = root.findViewById(R.id.clock_enabled_switch);
|
||||
enabledSwitch.setChecked(prefs.getBoolean(
|
||||
@@ -198,19 +201,400 @@ public final class LayoutFragment extends Fragment {
|
||||
bindCheckBoxPref(prefs, root.findViewById(R.id.status_position_mode_unlocked),
|
||||
SbtSettings.KEY_LAYOUT_STATUS_ENABLED_UNLOCKED,
|
||||
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));
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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,
|
||||
SharedPreferences prefs,
|
||||
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) {
|
||||
fragment = new SystemIconsFragment();
|
||||
} else if (itemId == R.id.nav_layout) {
|
||||
fragment = new LayoutHomeFragment();
|
||||
} else if (itemId == R.id.nav_layout_unlocked) {
|
||||
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) {
|
||||
fragment = new BatteryBarFragment();
|
||||
} else if (itemId == R.id.nav_clock) {
|
||||
fragment = new ClockFragment();
|
||||
} else if (itemId == R.id.nav_notification_icons) {
|
||||
fragment = new NotificationIconsFragment();
|
||||
} else if (itemId == R.id.nav_block_app_notification_icons) {
|
||||
fragment = new BlockAppNotificationIconsFragment();
|
||||
} 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;
|
||||
} else if (itemId == R.id.nav_layout) {
|
||||
return R.string.nav_layout;
|
||||
} else if (itemId == R.id.nav_notification_icons) {
|
||||
return R.string.nav_notification_icons;
|
||||
} else if (itemId == R.id.nav_layout_unlocked) {
|
||||
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) {
|
||||
return R.string.hidden_apps_page_title;
|
||||
} 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>
|
||||
</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
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
android:textAppearance="?attr/textAppearanceHeadline5" />
|
||||
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
android:id="@+id/layout_master_card"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
@@ -49,6 +50,7 @@
|
||||
</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_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
@@ -261,6 +263,7 @@
|
||||
</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_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
@@ -474,6 +477,7 @@
|
||||
</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_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
@@ -698,18 +702,11 @@
|
||||
android:paddingEnd="0dp"
|
||||
android:text="0" />
|
||||
</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>
|
||||
</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_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
@@ -934,18 +931,11 @@
|
||||
android:paddingEnd="0dp"
|
||||
android:text="0" />
|
||||
</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>
|
||||
</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_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
@@ -1031,6 +1021,7 @@
|
||||
</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_height="wrap_content"
|
||||
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:title="@string/nav_layout" />
|
||||
<item
|
||||
android:id="@+id/nav_notification_icons"
|
||||
android:id="@+id/nav_layout_unlocked"
|
||||
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
|
||||
android:id="@+id/nav_block_app_notification_icons"
|
||||
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_icons_debug">Debugging</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_status_icons">    Hide status icons</string>
|
||||
<string name="nav_clock">Clock</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_notification_icons">Notification icons layout</string>
|
||||
<string name="nav_block_app_notification_icons">Hide notification icons</string>
|
||||
<string name="nav_status_chips">Status chips</string>
|
||||
<string name="nav_misc">Misc</string>
|
||||
@@ -29,6 +30,15 @@
|
||||
<string name="debug_restart_systemui">Restart SystemUI</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_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_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>
|
||||
@@ -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_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="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="layout_master_toggle_title">Master toggle</string>
|
||||
<string name="clock_enabled">Use custom clock</string>
|
||||
<string name="layout_enabled">Use custom layout</string>
|
||||
<string name="clock_position_title">Clock position</string>
|
||||
<string name="layout_clock_position_title">Clock position</string>
|
||||
<string name="layout_chip_position_title">Status chip position</string>
|
||||
<string name="layout_notification_position_title">Notification icons position</string>
|
||||
<string name="layout_status_position_title">Status icons position</string>
|
||||
<string name="layout_show_dot_if_truncated">Show dot if truncated</string>
|
||||
<string name="layout_clock_position_title">Clock</string>
|
||||
<string name="layout_chip_position_title">Status chip</string>
|
||||
<string name="layout_notification_position_title">Notification icons</string>
|
||||
<string name="layout_status_position_title">Status icons</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_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_left_padding_label">Left 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