Port packed layout solver and overhaul layout settings
This commit is contained in:
+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