Clean up post-AOD layout pipeline

This commit is contained in:
ajp_anton
2026-06-01 01:40:00 +00:00
parent 22dfdf2cb0
commit 9d7cb0b3f9
31 changed files with 1366 additions and 2581 deletions
@@ -1,26 +0,0 @@
package se.ajpanton.statusbartweak;
import android.content.Context;
import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.junit.Assert.*;
/**
* Instrumented test, which will execute on an Android device.
*
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
*/
@RunWith(AndroidJUnit4.class)
public class ExampleInstrumentedTest {
@Test
public void useAppContext() {
// Context of the app under test.
Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
assertEquals("se.ajpanton.statusbartweak", appContext.getPackageName());
}
}
@@ -16,7 +16,9 @@ public final class NotificationKeyReflection {
"mNotificationKey",
"notificationKey",
"mKey",
"key"
"key",
"mSlot",
"slot"
};
private NotificationKeyReflection() {
@@ -53,6 +55,10 @@ public final class NotificationKeyReflection {
if (key != null) {
return key;
}
key = nonEmptyStringFromMethod(view, "getSlot");
if (key != null) {
return key;
}
return nonEmptyStringFromFields(view, ICON_VIEW_KEY_FIELD_NAMES);
}
@@ -37,10 +37,6 @@ public final class RuntimeContext {
return modeStateRepository;
}
public boolean isLogEnabled() {
return RuntimeLogGate.isEnabled();
}
public void logWarning(String message, Throwable throwable) {
logSplit(Log.WARN, message, throwable);
}
@@ -50,7 +46,7 @@ public final class RuntimeContext {
}
private void logSplit(int priority, String message, Throwable throwable) {
if (!isLogEnabled()) {
if (!RuntimeLogGate.isEnabled()) {
return;
}
String safeMessage = message != null ? message : "";
@@ -3,21 +3,14 @@ package se.ajpanton.statusbartweak.runtime.features.chips;
import android.app.KeyguardManager;
import android.content.Context;
import android.content.Intent;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewParent;
import android.widget.TextView;
import java.util.ArrayDeque;
import java.util.Collections;
import java.util.Locale;
import java.util.Set;
import java.util.WeakHashMap;
import se.ajpanton.statusbartweak.runtime.SystemContextProvider;
import se.ajpanton.statusbartweak.runtime.ViewIdNames;
import se.ajpanton.statusbartweak.runtime.features.RuntimeContext;
import se.ajpanton.statusbartweak.runtime.hooks.InstanceStateStore;
import se.ajpanton.statusbartweak.runtime.hooks.ReflectionSupport;
import se.ajpanton.statusbartweak.runtime.hooks.XposedHookSupport;
import se.ajpanton.statusbartweak.runtime.settings.BroadcastReceiverSupport;
@@ -26,25 +19,12 @@ import se.ajpanton.statusbartweak.shell.settings.SbtSettings;
final class StatusChipHider {
private static final String FIELD_FORCED_HIDDEN = "sbtStatusChipForcedHidden";
private static final String FIELD_PREV_VIS = "sbtStatusChipPrevVis";
private static final int TOP_REGION_DP = 220;
private static final int MAX_TEXT_SCAN_LEN = 160;
private static final int MAX_CLASS_SCAN_LEN = 480;
private static final int MAX_TEXT_NODES = 48;
private static final int MAX_ASCENT = 8;
private static final int ZONE_NONE = 0;
private static final int ZONE_TOP = 1;
private static final int KIND_UNKNOWN = 0;
private static final int KIND_MEDIA = 1;
private static final int KIND_NAV = 2;
private static final int KIND_CALL = 3;
private final RuntimeContext runtimeContext;
private final InstanceStateStore instanceStateStore = new InstanceStateStore();
private final Set<View> trackedRoots = Collections.newSetFromMap(new WeakHashMap<>());
private final Set<View> scannedRoots = Collections.newSetFromMap(new WeakHashMap<>());
private final Set<View> forcedHiddenViews = Collections.newSetFromMap(new WeakHashMap<>());
private final Set<Object> ongoingControllers = Collections.newSetFromMap(new WeakHashMap<>());
private boolean hooksInstalled;
@@ -69,8 +49,6 @@ final class StatusChipHider {
installBroadcastReceiver(SystemContextProvider.currentApplication());
refreshSettings();
installOngoingActivityHooks(classLoader);
installTouchInterceptHooks(classLoader);
installViewRootHooks();
hooksInstalled = true;
}
@@ -100,7 +78,6 @@ final class StatusChipHider {
refreshSettings();
if (isAnyChipHidingEnabled()) {
refreshOngoingActivityController(controller);
rescanTrackedRoots();
}
}
return result;
@@ -130,90 +107,11 @@ final class StatusChipHider {
return result;
}
Object data = chain.getArg(1);
boolean suppress = shouldHide(classifyOngoingData(data), ZONE_TOP);
return suppress ? true : result;
int kind = classifyOngoingData(data);
return shouldHideEnabledKind(kind) ? true : result;
});
}
private void installViewRootHooks() {
Class<?> viewRootClass = ReflectionSupport.requireClass("android.view.ViewRootImpl", null);
XposedHookSupport.hookAllMethods(runtimeContext.getFramework(), viewRootClass, "performTraversals", chain -> {
Object result = chain.proceed();
Object rootObj = ReflectionSupport.requireFieldValue(chain.getThisObject(), "mView");
if (!(rootObj instanceof View root)) {
return result;
}
installBroadcastReceiver(root.getContext());
trackedRoots.add(root);
if (!isAnyChipHidingEnabled()) {
restoreForcedHiddenViews();
return result;
}
if (scannedRoots.add(root)) {
applyToViewTree(root);
} else {
keepForcedHiddenViewsHidden();
}
return result;
});
}
private void installTouchInterceptHooks(ClassLoader classLoader) {
Class<?> targetClass = ReflectionSupport.findClassIfExists(
"com.android.systemui.statusbar.phone.TouchInterceptFrameLayout",
classLoader
);
if (targetClass == null) {
return;
}
XposedHookSupport.hookAllMethodsIfExists(runtimeContext.getFramework(), targetClass, "onLayout", chain -> {
Object result = chain.proceed();
if (!isAnyChipHidingEnabled()) {
return result;
}
if (!(chain.getThisObject() instanceof View view)) {
return result;
}
if (view.getWidth() > 0 && view.getHeight() > 0) {
applySourceDecision(view);
}
return result;
});
XposedHookSupport.hookAllMethodsIfExists(runtimeContext.getFramework(), targetClass, "onAttachedToWindow", chain -> {
Object result = chain.proceed();
if (!isAnyChipHidingEnabled()) {
return result;
}
if (chain.getThisObject() instanceof View view && shouldPreemptivelyHide(view)) {
forceHide(view);
}
return result;
});
XposedHookSupport.hookAllMethodsIfExists(runtimeContext.getFramework(), targetClass, "setVisibility", chain -> {
if (!(chain.getThisObject() instanceof View view)) {
return chain.proceed();
}
if (isAnyChipHidingEnabled()
&& !chain.getArgs().isEmpty()
&& chain.getArg(0) instanceof Integer requestedVisibility
&& requestedVisibility != View.GONE
&& shouldPreemptivelyHide(view)) {
markForcedHidden(view, requestedVisibility);
Object[] args = chain.getArgs().toArray();
args[0] = View.GONE;
return chain.proceed(args);
}
Object result = chain.proceed();
if (!isAnyChipHidingEnabled()) {
return result;
}
if (view.getWidth() > 0 && view.getHeight() > 0) {
applySourceDecision(view);
}
return result;
});
}
private void installBroadcastReceiver(Context context) {
if (broadcastInstalled) {
return;
@@ -226,9 +124,7 @@ final class StatusChipHider {
public void onReceive(Context context, Intent intent) {
RuntimeSettingsCache.invalidate();
refreshSettings();
scannedRoots.clear();
refreshOngoingActivityLists();
rescanTrackedRoots();
}
};
BroadcastReceiverSupport.registerExportedOnApplicationContext(
@@ -260,19 +156,6 @@ final class StatusChipHider {
return hideAll || hideMediaUnlocked || hideNavUnlocked || hideCallUnlocked;
}
private void rescanTrackedRoots() {
if (!isAnyChipHidingEnabled()) {
restoreForcedHiddenViews();
return;
}
for (View root : trackedRoots) {
if (root != null) {
scannedRoots.add(root);
applyToViewTree(root);
}
}
}
private void refreshOngoingActivityLists() {
if (ongoingActivityDataHelperClass == null || notificationLockscreenUserManagerClass == null) {
return;
@@ -315,200 +198,14 @@ final class StatusChipHider {
boolean sawItem = false;
for (Object data : iterable) {
sawItem = true;
if (!shouldHide(classifyOngoingData(data), ZONE_TOP)) {
if (!shouldHideEnabledKind(classifyOngoingData(data))) {
return false;
}
}
return sawItem;
}
private void applyToViewTree(View root) {
ArrayDeque<View> stack = new ArrayDeque<>();
stack.push(root);
while (!stack.isEmpty()) {
View source = stack.pop();
if (source == null) {
continue;
}
if (source instanceof ViewGroup group) {
for (int index = group.getChildCount() - 1; index >= 0; index--) {
stack.push(group.getChildAt(index));
}
}
applySourceDecision(source);
}
}
private boolean applySourceDecision(View source) {
if (source == null) {
return false;
}
int zone = detectZone(source);
if (zone == ZONE_NONE || !isSourceCandidate(source)) {
restoreIfForcedHidden(source);
return false;
}
View target = resolveHideTarget(source, zone);
if (target == null) {
restoreIfForcedHidden(source);
return false;
}
int targetZone = detectZone(target);
if (targetZone == ZONE_NONE) {
restoreIfForcedHidden(target);
return false;
}
String sourceText = collectText(source);
String targetText = target == source ? sourceText : collectText(target);
String kindText = sourceText.isEmpty() ? targetText : sourceText;
String classNames = target == source
? collectClassNames(source)
: collectClassNames(source) + " " + collectClassNames(target);
String lowerText = kindText.toLowerCase(Locale.ROOT);
if (looksLikePrivacy(target, kindText) || isDefinitelyNotChipText(lowerText)) {
restoreIfForcedHidden(target);
return false;
}
int kind = classifyKind(source, kindText, classNames);
if (shouldHide(kind, targetZone)) {
forceHide(target);
return true;
} else {
restoreIfForcedHidden(target);
return false;
}
}
private boolean shouldPreemptivelyHide(View source) {
if (source == null) {
return false;
}
if (isForcedHidden(source) && shouldHideTopUnlocked()) {
return true;
}
if (!isTransitionSourceCandidate(source)) {
return false;
}
return shouldHideSource(source, true);
}
private boolean shouldHideSource(View source, boolean allowRelaxedGeometry) {
int zone = detectZone(source);
boolean relaxedGeometry = false;
if (zone == ZONE_NONE) {
if (!allowRelaxedGeometry || !isTransitionSourceCandidate(source)) {
return false;
}
if (source.getWidth() > 0 && source.getHeight() > 0) {
return false;
}
zone = ZONE_TOP;
relaxedGeometry = true;
} else if (!isSourceCandidate(source)) {
return false;
}
String sourceText = collectText(source);
String lowerText = sourceText.toLowerCase(Locale.ROOT);
if (looksLikePrivacy(source, sourceText) || isDefinitelyNotChipText(lowerText)) {
return false;
}
int kind = classifyKind(source, sourceText, collectClassNames(source));
if (relaxedGeometry && kind == KIND_UNKNOWN && lowerText.isEmpty()) {
return false;
}
return shouldHide(kind, zone);
}
private boolean isSourceCandidate(View view) {
return isTransitionSourceCandidate(view) && looksLikeChipGeometry(view);
}
private boolean isTransitionSourceCandidate(View view) {
if (isOngoingActivityCapsule(view)) {
return false;
}
if (!isSystemUiView(view)) {
return false;
}
String className = className(view).toLowerCase(Locale.ROOT);
if (className.contains("privacy") || isDefinitelyNotChipClass(className)) {
return false;
}
return isChipClassName(className);
}
private View resolveHideTarget(View source, int sourceZone) {
String sourceClass = className(source).toLowerCase(Locale.ROOT);
if (sourceClass.contains("touchinterceptframelayout")) {
return source;
}
View best = null;
View current = source;
int depth = 0;
int screenWidth = source.getResources().getDisplayMetrics().widthPixels;
while (current != null && depth < MAX_ASCENT) {
if (!isSystemUiView(current)) {
break;
}
if (detectZone(current) != sourceZone) {
break;
}
if (looksLikeHideTarget(current, source, screenWidth)) {
best = current;
}
ViewParent parent = current.getParent();
current = parent instanceof View view ? view : null;
depth++;
}
return best;
}
private boolean looksLikeHideTarget(View candidate, View source, int screenWidth) {
if (!looksLikeChipGeometry(candidate)) {
return false;
}
String className = className(candidate).toLowerCase(Locale.ROOT);
if (className.contains("privacy") || isDefinitelyNotChipClass(className)) {
return false;
}
if (!isChipClassName(className)) {
return false;
}
int width = candidate.getWidth();
int height = candidate.getHeight();
if (width >= (int) (screenWidth * 0.98f) && height < dp(candidate, 90)) {
return false;
}
if (candidate == source) {
return true;
}
return candidate instanceof ViewGroup && width >= source.getWidth();
}
private boolean isChipClassName(String lowerClass) {
return lowerClass.contains("chip")
|| lowerClass.contains("ongoing")
|| lowerClass.contains("statusevent")
|| lowerClass.contains("touchinterceptframelayout");
}
private int detectZone(View view) {
if (view == null || view.getWidth() <= 0 || view.getHeight() <= 0) {
return ZONE_NONE;
}
int[] location = new int[2];
try {
view.getLocationOnScreen(location);
} catch (Throwable ignored) {
return ZONE_NONE;
}
return location[1] >= 0 && location[1] <= dp(view, TOP_REGION_DP) ? ZONE_TOP : ZONE_NONE;
}
private boolean shouldHide(int kind, int zone) {
if (zone != ZONE_TOP || !shouldHideTopUnlocked()) {
return false;
}
private boolean shouldHideEnabledKind(int kind) {
if (hideAll && kind == KIND_UNKNOWN) {
return true;
}
@@ -528,13 +225,10 @@ final class StatusChipHider {
return isAnyChipHidingEnabled() && !isKeyguardLocked();
}
private int classifyKind(View view, String text, String classNames) {
private int classifyKind(String text, String classNames) {
String lowerText = text != null ? text.toLowerCase(Locale.ROOT) : "";
String lowerClass = classNames != null && !classNames.isEmpty()
? classNames.toLowerCase(Locale.ROOT)
: (view != null ? className(view).toLowerCase(Locale.ROOT) : "");
String lowerClass = classNames != null ? classNames.toLowerCase(Locale.ROOT) : "";
if (containsToken(lowerText, "call")
|| containsPhrase(lowerText, "ongoing call")
|| lowerClass.contains("call")) {
return KIND_CALL;
}
@@ -577,188 +271,7 @@ final class StatusChipHider {
+ textField(data, "mSecondaryInfo") + " "
+ textField(data, "mDescription") + " "
+ textField(data, "mExpandedChipText")).trim();
return classifyKind(null, text, data.getClass().getName());
}
private boolean looksLikePrivacy(View view, String text) {
String lowerClass = className(view).toLowerCase(Locale.ROOT);
String lowerText = text != null ? text.toLowerCase(Locale.ROOT) : "";
return lowerClass.contains("privacy")
|| containsToken(lowerText, "camera")
|| containsToken(lowerText, "microphone")
|| containsPhrase(lowerText, "location in use")
|| containsPhrase(lowerText, "mic in use");
}
private String collectClassNames(View root) {
StringBuilder out = new StringBuilder();
appendClassName(out, root);
if (!(root instanceof ViewGroup)) {
return out.toString();
}
ArrayDeque<View> stack = new ArrayDeque<>();
stack.push(root);
int nodes = 0;
while (!stack.isEmpty() && out.length() < MAX_CLASS_SCAN_LEN && nodes < MAX_TEXT_NODES) {
View current = stack.pop();
nodes++;
if (current != root) {
appendClassName(out, current);
}
if (current instanceof ViewGroup group) {
for (int index = 0; index < group.getChildCount(); index++) {
stack.push(group.getChildAt(index));
}
}
}
return out.toString();
}
private String collectText(View root) {
StringBuilder out = new StringBuilder();
appendText(out, root.getContentDescription());
if (root instanceof TextView textView) {
appendText(out, textView.getText());
}
if (!(root instanceof ViewGroup)) {
return out.toString();
}
ArrayDeque<View> stack = new ArrayDeque<>();
stack.push(root);
int nodes = 0;
while (!stack.isEmpty() && out.length() < MAX_TEXT_SCAN_LEN && nodes < MAX_TEXT_NODES) {
View current = stack.pop();
nodes++;
if (current != root) {
appendText(out, current.getContentDescription());
if (current instanceof TextView textView) {
appendText(out, textView.getText());
}
}
if (current instanceof ViewGroup group) {
for (int index = 0; index < group.getChildCount(); index++) {
stack.push(group.getChildAt(index));
}
}
}
return out.toString();
}
private void forceHide(View view) {
int restoreVisibility = view.getVisibility() != View.GONE ? view.getVisibility() : View.VISIBLE;
markForcedHidden(view, restoreVisibility);
if (view.getVisibility() != View.GONE) {
view.setVisibility(View.GONE);
}
}
private void keepForcedHiddenViewsHidden() {
if (forcedHiddenViews.isEmpty()) {
return;
}
ArrayDeque<View> staleViews = new ArrayDeque<>();
for (View view : forcedHiddenViews) {
if (view == null || !view.isAttachedToWindow()) {
staleViews.add(view);
continue;
}
if (view.getVisibility() != View.GONE) {
view.setVisibility(View.GONE);
}
}
while (!staleViews.isEmpty()) {
forcedHiddenViews.remove(staleViews.removeFirst());
}
}
private void restoreForcedHiddenViews() {
if (forcedHiddenViews.isEmpty()) {
return;
}
ArrayDeque<View> views = new ArrayDeque<>(forcedHiddenViews);
while (!views.isEmpty()) {
restoreIfForcedHidden(views.removeFirst());
}
}
private void markForcedHidden(View view, int restoreVisibility) {
instanceStateStore.put(view, FIELD_PREV_VIS, restoreVisibility);
instanceStateStore.put(view, FIELD_FORCED_HIDDEN, Boolean.TRUE);
forcedHiddenViews.add(view);
}
private void restoreIfForcedHidden(View view) {
if (!isForcedHidden(view)) {
return;
}
Object previous = instanceStateStore.get(view, FIELD_PREV_VIS);
int restoreVisibility = previous instanceof Integer integer ? integer : View.VISIBLE;
instanceStateStore.remove(view, FIELD_FORCED_HIDDEN);
instanceStateStore.remove(view, FIELD_PREV_VIS);
forcedHiddenViews.remove(view);
if (view.getVisibility() != restoreVisibility) {
view.setVisibility(restoreVisibility);
}
if (restoreVisibility == View.VISIBLE && view.getAlpha() == 0f) {
view.setAlpha(1f);
}
}
private boolean isForcedHidden(View view) {
Object marker = instanceStateStore.get(view, FIELD_FORCED_HIDDEN);
return marker instanceof Boolean bool && bool;
}
private boolean isSystemUiView(View view) {
return className(view).contains("systemui");
}
private boolean looksLikeChipGeometry(View view) {
if (view == null || view.getWidth() <= 0 || view.getHeight() <= 0) {
return false;
}
int width = view.getWidth();
int height = view.getHeight();
int screenWidth = view.getResources().getDisplayMetrics().widthPixels;
if (height < dp(view, 18) || height > dp(view, 160)) {
return false;
}
if (width < height * 2) {
return false;
}
return !(width > (int) (screenWidth * 0.95f) && height < dp(view, 110));
}
private boolean isDefinitelyNotChipClass(String lowerClass) {
return lowerClass.contains("clock")
|| lowerClass.contains("date")
|| lowerClass.contains("brightness")
|| lowerClass.contains("tile")
|| lowerClass.contains("qsbuttonscontainer")
|| lowerClass.contains("sectionheaderview")
|| lowerClass.contains("expandablenotificationrow")
|| lowerClass.contains("carrier")
|| lowerClass.contains("statusicon")
|| lowerClass.contains("indicator")
|| lowerClass.contains("launchabletextview")
|| lowerClass.contains("qsshortendate")
|| lowerClass.contains("qsclock")
|| lowerClass.contains("quickqs");
}
private boolean isDefinitelyNotChipText(String lowerText) {
if (lowerText == null || lowerText.isEmpty()) {
return false;
}
return containsPhrase(lowerText, "notification settings")
|| containsToken(lowerText, "brightness")
|| containsToken(lowerText, "wifi")
|| containsPhrase(lowerText, "smart view")
|| containsToken(lowerText, "mirror");
}
private String className(View view) {
return view != null && view.getClass() != null ? view.getClass().getName() : "";
return classifyKind(text, data.getClass().getName());
}
private String textField(Object target, String fieldName) {
@@ -766,44 +279,6 @@ final class StatusChipHider {
return value != null ? String.valueOf(value) : "";
}
private boolean isOngoingActivityCapsule(View view) {
return ViewIdNames.hasIdName(view, "ongoing_activity_capsule");
}
private void appendText(StringBuilder out, CharSequence value) {
if (value == null || value.length() == 0 || out.length() >= MAX_TEXT_SCAN_LEN) {
return;
}
if (out.length() > 0) {
out.append(' ');
}
int remaining = MAX_TEXT_SCAN_LEN - out.length();
if (value.length() > remaining) {
out.append(value, 0, remaining);
} else {
out.append(value);
}
}
private void appendClassName(StringBuilder out, View view) {
if (view == null || out.length() >= MAX_CLASS_SCAN_LEN) {
return;
}
String value = className(view);
if (value.isEmpty()) {
return;
}
if (out.length() > 0) {
out.append(' ');
}
int remaining = MAX_CLASS_SCAN_LEN - out.length();
if (value.length() > remaining) {
out.append(value, 0, remaining);
} else {
out.append(value);
}
}
private boolean containsAny(String value, String... needles) {
if (value == null || value.isEmpty()) {
return false;
@@ -816,10 +291,6 @@ final class StatusChipHider {
return false;
}
private boolean containsPhrase(String value, String phrase) {
return value != null && phrase != null && value.contains(phrase);
}
private boolean containsToken(String value, String token) {
if (value == null || value.isEmpty() || token == null || token.isEmpty()) {
return false;
@@ -850,10 +321,6 @@ final class StatusChipHider {
return value != null && !value.isEmpty() && value.matches(".*\\b\\d+\\s*(m|km|mi|ft)\\b.*");
}
private int dp(View view, int dp) {
return Math.round(dp * view.getResources().getDisplayMetrics().density);
}
private boolean isKeyguardLocked() {
try {
Context context = SystemContextProvider.currentApplication();
@@ -876,4 +343,5 @@ final class StatusChipHider {
}
return keyguardManager;
}
}
@@ -63,6 +63,10 @@ final class LockscreenCardsNotificationRowFilter {
}
}
boolean hasHiddenRows() {
return !hiddenRows.isEmpty();
}
void suppressHiddenRows() {
if (hiddenRows.isEmpty()) {
return;
@@ -16,11 +16,13 @@ import android.widget.TextView;
import java.lang.ref.WeakReference;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Locale;
@@ -42,6 +44,8 @@ import se.ajpanton.statusbartweak.runtime.render.AodStatusbarSourceStore;
import se.ajpanton.statusbartweak.runtime.render.DebugBoundsOverlayController;
import se.ajpanton.statusbartweak.runtime.render.LockscreenCardsNotificationIconSourceStore;
import se.ajpanton.statusbartweak.runtime.render.LockscreenCardsNotificationStripRenderer;
import se.ajpanton.statusbartweak.runtime.notifications.NotificationActiveKeyStore;
import se.ajpanton.statusbartweak.runtime.notifications.NotificationUnreadTracker;
import se.ajpanton.statusbartweak.runtime.render.OwnedIconHostManager;
import se.ajpanton.statusbartweak.runtime.render.UnlockedIconSnapshotRenderController;
import se.ajpanton.statusbartweak.runtime.render.UnlockedIconSnapshotRenderController.RenderResult;
@@ -94,19 +98,7 @@ final class StockLayoutCanvasController {
"notification_icon_area_inner",
"notificationIcons"
);
private static final Set<String> LAYOUT_OFF_EXTRA_RECOVERABLE_IDS = Set.of(
"system_icon_area",
"statusIcons",
"battery"
);
private static final Set<String> LAYOUT_OFF_RECOVERABLE_CLASS_SUBSTRINGS = Set.of(
"NotificationIconContainer",
"StatusIconContainer",
"BatteryMeterView",
"QSClockIndicatorView"
);
private static final int STATUS_BAR_STATE_SHADE_LOCKED = 2;
private final RuntimeContext runtimeContext;
private final Map<View, HiddenViewState> hiddenViewStates = new WeakHashMap<>();
@@ -172,6 +164,7 @@ final class StockLayoutCanvasController {
private boolean hooksInstalled;
private int ownHiddenViewAlphaWriteDepth;
private boolean notificationDrawerWasOpen;
StockLayoutCanvasController(RuntimeContext runtimeContext) {
this.runtimeContext = runtimeContext;
@@ -184,6 +177,8 @@ final class StockLayoutCanvasController {
new LockscreenCardsNotificationStripRenderer();
unlockedIconRenderController = new UnlockedIconSnapshotRenderController();
runtimeContext.getModeStateRepository().addSceneListener(this::scheduleSceneRefresh);
runtimeContext.getModeStateRepository().addSystemUiStatusBarStateListener(
this::handleSystemUiStatusBarStateChanged);
}
void install(ClassLoader classLoader) {
@@ -194,12 +189,147 @@ final class StockLayoutCanvasController {
installCardsNotificationIconSourceHook(classLoader);
installLockscreenCardsNotificationRowFilterHook(classLoader);
installUnlockedAppearanceInvalidationHooks(classLoader);
installNotificationCollectionHooks(classLoader);
statusIconModelTracker.installHooks(classLoader);
installShadeExpansionHooks(classLoader);
installShadeHeaderAnimationGuardHook(classLoader);
installViewRootHook(classLoader);
hooksInstalled = true;
}
private void installNotificationCollectionHooks(ClassLoader classLoader) {
for (String className : List.of(
"com.android.systemui.statusbar.notification.collection.NotifCollection",
"com.android.systemui.statusbar.notification.NotificationEntryManager")) {
Class<?> type = ReflectionSupport.findClassIfExists(className, classLoader);
if (type == null) {
continue;
}
for (Method method : type.getDeclaredMethods()) {
String lowerName = method.getName().toLowerCase(Locale.ROOT);
if (!isNotificationCollectionMutationMethod(lowerName)) {
continue;
}
method.setAccessible(true);
runtimeContext.getFramework().hook(method).intercept(chain -> {
Object result = chain.proceed();
refreshActiveNotificationKeysFromCollection(chain.getThisObject());
return result;
});
}
}
}
private boolean isNotificationCollectionMutationMethod(String lowerName) {
boolean subject = lowerName.contains("notification")
|| lowerName.contains("notif")
|| lowerName.contains("entry");
boolean mutation = lowerName.contains("add")
|| lowerName.contains("post")
|| lowerName.contains("update")
|| lowerName.contains("remove")
|| lowerName.contains("dismiss")
|| lowerName.contains("cancel")
|| lowerName.contains("cleanup")
|| lowerName.contains("clean")
|| lowerName.contains("clear");
return subject
&& mutation
&& !lowerName.contains("listener")
&& !lowerName.contains("lifetime")
&& !lowerName.contains("interceptor");
}
private void installShadeExpansionHooks(ClassLoader classLoader) {
for (String className : List.of(
"com.android.systemui.shade.ShadeExpansionStateManager",
"com.android.systemui.shade.NotificationPanelViewController",
"com.android.systemui.statusbar.phone.NotificationPanelViewController")) {
Class<?> type = ReflectionSupport.findClassIfExists(className, classLoader);
if (type == null) {
continue;
}
for (Method method : type.getDeclaredMethods()) {
String lowerName = method.getName().toLowerCase(Locale.ROOT);
if (!lowerName.contains("expansion") && !lowerName.contains("expandedheight")) {
continue;
}
method.setAccessible(true);
runtimeContext.getFramework().hook(method).intercept(chain -> {
Object result = chain.proceed();
handleShadeExpansionHookCall(chain.getThisObject(), chain.getArgs());
return result;
});
}
}
}
private void handleShadeExpansionHookCall(Object target, List<Object> args) {
SceneKey activeScene = runtimeContext.getModeStateRepository().getActiveScene();
if (activeScene == null || !activeScene.isUnlocked()) {
return;
}
Float expansion = firstExpansionFraction(args);
if (expansion == null) {
expansion = reflectedExpansion(target);
}
if (expansion == null) {
return;
}
if (expansion <= 0.01f) {
notificationDrawerWasOpen = false;
return;
}
if (expansion <= 0.05f) {
return;
}
markNotificationDrawerSeen();
}
private Float firstExpansionFraction(List<Object> args) {
if (args == null || args.isEmpty()) {
return null;
}
for (Object arg : args) {
Float value = fractionValue(arg);
if (value == null) {
value = reflectedFraction(arg);
}
if (value != null) {
return value;
}
}
return null;
}
private void markNotificationDrawerSeen() {
if (notificationDrawerWasOpen) {
return;
}
notificationDrawerWasOpen = true;
boolean changed = NotificationUnreadTracker.markSeen();
if (changed) {
scheduleUnlockedAppearanceRefreshForAllRoots();
}
}
private void handleSystemUiStatusBarStateChanged(
int previousState,
int currentState,
boolean dozing
) {
if (dozing) {
return;
}
if (currentState == STATUS_BAR_STATE_SHADE_LOCKED
&& previousState != STATUS_BAR_STATE_SHADE_LOCKED) {
markNotificationDrawerSeen();
} else if (previousState == STATUS_BAR_STATE_SHADE_LOCKED
&& currentState != STATUS_BAR_STATE_SHADE_LOCKED) {
notificationDrawerWasOpen = false;
}
}
private void installShadeHeaderAnimationGuardHook(ClassLoader classLoader) {
Class<?> viewClass = ReflectionSupport.requireClass("android.view.View", classLoader);
XposedHookSupport.hookAllMethodsIfExists(runtimeContext.getFramework(), viewClass, "setAlpha", chain -> {
@@ -494,6 +624,9 @@ final class StockLayoutCanvasController {
methodName,
chain -> {
ViewGroup parent = chain.getThisObject() instanceof ViewGroup group ? group : null;
if (parent != null && !isLayoutEnabled(parent.getContext())) {
return chain.proceed();
}
View child = firstArg(chain.getArgs()) instanceof View view ? view : null;
View rootBefore = parent != null ? unlockedStatusBarRootFor(parent) : null;
boolean affectedBefore = isStatusChipTreeMutation(parent, child);
@@ -541,17 +674,21 @@ final class StockLayoutCanvasController {
return chain.proceed();
}
if (!isLayoutEnabled(container.getContext())) {
Object result = chain.proceed();
LockscreenCardsNotificationIconSourceStore.put(container, new ArrayList<>());
return result;
}
if (!isCardsNotificationIconSourceContainer(container)) {
Object result = chain.proceed();
markUnlockedLayoutDirty(container);
return result;
return chain.proceed();
}
@SuppressWarnings("unchecked")
Function<Object, Object> original = (Function<Object, Object>) args.get(functionIndex);
if (!isCardsNotificationIconSourceContainer(container)) {
Function<Object, Object> wrapped = entryObj -> {
recordNotificationEntry(entryObj);
return original.apply(entryObj);
};
Object[] newArgs = args.toArray(new Object[0]);
newArgs[functionIndex] = wrapped;
Object result = chain.proceed(newArgs);
requestUnlockedLayoutRefreshFromSource(container);
return result;
}
ArrayList<LockscreenCardsNotificationIconSourceStore.SourceEntry> captured =
new ArrayList<>();
Function<Object, Object> wrapped = entryObj -> {
@@ -563,6 +700,7 @@ final class StockLayoutCanvasController {
return null;
}
Object result = original.apply(entryObj);
recordNotificationEntry(entryObj);
if (result == null) {
result = fallbackStatusBarIcon(entryObj);
}
@@ -578,6 +716,7 @@ final class StockLayoutCanvasController {
newArgs[functionIndex] = wrapped;
Object result = chain.proceed(newArgs);
LockscreenCardsNotificationIconSourceStore.put(container, captured);
requestUnlockedLayoutRefreshFromSource(container);
return result;
});
}
@@ -711,11 +850,39 @@ final class StockLayoutCanvasController {
private void captureAodPackagesFromNotifCollection(Object wrapper) {
Object collection = ReflectionSupport.getFieldValue(wrapper, "mNotifCollection");
ArrayList<StatusBarNotification> notifications = notificationsFromNotifCollection(collection);
if (notifications.isEmpty()) {
if (collection == null) {
return;
}
ArrayList<StatusBarNotification> notifications = notificationsFromNotifCollection(collection);
if (notifications.isEmpty()) {
latestAodNotifications = new ArrayList<>();
latestAodIconImageViews = new ArrayList<>();
NotificationActiveKeyStore.replace(Collections.emptySet());
NotificationUnreadTracker.retainActiveKeys(Collections.emptySet());
LockscreenCardsNotificationIconSourceStore.clearAodSources();
markKnownAodNotificationRootsDirty();
updateKnownAodNotificationSources();
return;
}
boolean changed = false;
ArrayList<String> packages = new ArrayList<>();
HashSet<String> activeKeys = new HashSet<>();
for (StatusBarNotification sbn : notifications) {
changed |= NotificationUnreadTracker.record(sbn);
if (sbn != null && sbn.getKey() != null) {
activeKeys.add(sbn.getKey());
}
if (sbn != null && AppIconRules.isValidPackageName(sbn.getPackageName())) {
packages.add(sbn.getPackageName());
}
}
updateActiveNotificationKeys(activeNotificationIconKeys(notifications));
changed |= NotificationUnreadTracker.retainActiveKeys(activeKeys);
LockscreenCardsNotificationIconSourceStore.putAodNotificationPackages(packages);
latestAodNotifications = notifications;
if (changed) {
markKnownAodNotificationRootsDirty();
}
updateKnownAodNotificationSources();
}
@@ -787,6 +954,52 @@ final class StockLayoutCanvasController {
notifications.add(sbn);
}
private boolean recordNotificationEntry(Object entry) {
StatusBarNotification sbn = NotificationReflection.statusBarNotificationEntry(entry);
return NotificationUnreadTracker.record(sbn);
}
private void refreshActiveNotificationKeysFromCollection(Object collection) {
ArrayList<StatusBarNotification> notifications = notificationsFromNotifCollection(collection);
if (notifications.isEmpty()) {
return;
}
for (StatusBarNotification sbn : notifications) {
if (sbn != null && sbn.getKey() != null && AppIconRules.isValidPackageName(sbn.getPackageName())) {
NotificationUnreadTracker.record(sbn);
}
}
updateActiveNotificationKeys(activeNotificationIconKeys(notifications));
}
private void updateActiveNotificationKeys(ArrayList<String> keys) {
if (!NotificationActiveKeyStore.replace(keys)) {
return;
}
requestUnlockedLayoutRefreshForAllRoots();
markKnownAodNotificationRootsDirty();
}
private ArrayList<String> activeNotificationIconKeys(ArrayList<StatusBarNotification> notifications) {
ArrayList<String> keys = new ArrayList<>();
if (notifications == null) {
return keys;
}
for (StatusBarNotification sbn : notifications) {
if (sbn == null || !AppIconRules.isValidPackageName(sbn.getPackageName())) {
continue;
}
if (sbn.getKey() != null) {
keys.add(sbn.getKey());
}
String packageName = sbn.getPackageName();
int id = sbn.getId();
keys.add(packageName + "/" + id);
keys.add(packageName + "/0x" + Integer.toHexString(id));
}
return keys;
}
private void installViewRootHook(ClassLoader classLoader) {
Class<?> viewRootClass = ReflectionSupport.requireClass("android.view.ViewRootImpl", classLoader);
XposedHookSupport.hookAllMethods(runtimeContext.getFramework(), viewRootClass, "performDraw", chain -> {
@@ -838,6 +1051,9 @@ final class StockLayoutCanvasController {
if (activeScene == null) {
return;
}
if (!runtimeContext.getModeStateRepository().isSystemUiShadeLocked() && !activeScene.isAod()) {
notificationDrawerWasOpen = false;
}
if (isNotificationShadeWindowRoot(root)) {
if (shouldPrepareCarrierlessAodNotificationTransition(settings, activeScene)) {
prepareCarrierlessAodNotificationTransition(root);
@@ -848,12 +1064,14 @@ final class StockLayoutCanvasController {
if (activeScene.isLockscreen()
&& activeScene.notificationDisplayStyle() == NotificationDisplayStyle.CARDS
&& isNotificationShadeWindowRoot(root)) {
applyLockscreenCardsNotificationRowFilterInTreeIfNeeded(
boolean applied = applyLockscreenCardsNotificationRowFilterInTreeIfNeeded(
root,
settings,
activeScene,
true);
lockscreenCardsNotificationRowFilter.suppressHiddenRows();
if (!applied && lockscreenCardsNotificationRowFilter.hasHiddenRows()) {
lockscreenCardsNotificationRowFilter.suppressHiddenRows();
}
} else if (activeScene.isUnlocked() && isNotificationShadeWindowRoot(root)) {
lockscreenCardsNotificationRowFilter.restoreAll();
} else if (activeScene.isAod()
@@ -862,6 +1080,98 @@ final class StockLayoutCanvasController {
}
}
private Float reflectedExpansion(Object target) {
for (String method : List.of(
"getExpandedFraction",
"getExpansionFraction",
"getPanelExpansion",
"getShadeExpansion")) {
Float value = floatValue(ReflectionSupport.invokeMethod(target, method));
if (value != null) {
return value;
}
}
for (String field : List.of(
"mExpandedFraction",
"mExpansionFraction",
"mPanelExpansionFraction",
"mShadeExpansion")) {
Float value = floatValue(ReflectionSupport.getFieldValue(target, field));
if (value != null) {
return value;
}
}
Float height = heightValue(ReflectionSupport.invokeMethod(target, "getExpandedHeight"));
if (height == null) {
height = heightValue(ReflectionSupport.getFieldValue(target, "mExpandedHeight"));
}
if (height != null && target instanceof View view && view.getHeight() > 0) {
return Math.min(1f, Math.max(0f, height / view.getHeight()));
}
return null;
}
private Float reflectedFraction(Object target) {
for (String method : List.of(
"getFraction",
"getExpandedFraction",
"getExpansionFraction",
"getPanelExpansion",
"getShadeExpansion")) {
Float value = fractionValue(ReflectionSupport.invokeMethod(target, method));
if (value != null) {
return value;
}
}
for (String field : List.of(
"fraction",
"expandedFraction",
"expansionFraction",
"panelExpansionFraction",
"shadeExpansion")) {
Float value = fractionValue(ReflectionSupport.getFieldValue(target, field));
if (value != null) {
return value;
}
}
return null;
}
private Float floatValue(Object value) {
return fractionValue(value);
}
private Float fractionValue(Object value) {
if (value instanceof Float floatValue) {
return fraction(floatValue);
}
if (value instanceof Double doubleValue) {
return fraction(doubleValue.floatValue());
}
return null;
}
private Float heightValue(Object value) {
if (value instanceof Number number) {
float floatValue = number.floatValue();
if (Float.isNaN(floatValue) || Float.isInfinite(floatValue) || floatValue < 0f) {
return null;
}
return floatValue;
}
return null;
}
private Float fraction(float value) {
if (Float.isNaN(value) || Float.isInfinite(value)) {
return null;
}
if (value < 0f || value > 1f) {
return null;
}
return value;
}
private void applyBeforeDrawInternal(View root) {
if (root == null) {
return;
@@ -894,11 +1204,13 @@ final class StockLayoutCanvasController {
&& activeScene.isLockscreen()
&& activeScene.notificationDisplayStyle() == NotificationDisplayStyle.CARDS
&& canContainLockedStatusBarIconSurfaces(root)) {
applyLockscreenCardsNotificationRowFilterInTreeIfNeeded(
root,
settings,
activeScene,
isNotificationShadeWindowRoot(root));
if (isNotificationShadeWindowRoot(root)) {
applyLockscreenCardsNotificationRowFilterInTreeIfNeeded(
root,
settings,
activeScene,
true);
}
renderLockscreenCardsNotificationStripIfNeeded(root, settings, activeScene);
} else if (activeScene != null
&& activeScene.isAod()
@@ -943,10 +1255,29 @@ final class StockLayoutCanvasController {
if (root != null) {
dirtyUnlockedLayoutRoots.add(root);
}
View current = sourceView;
while (current != null) {
if (isUnlockedStatusBarRoot(current)) {
dirtyUnlockedLayoutRoots.add(current);
}
Object parent = current.getParent();
current = parent instanceof View ? (View) parent : null;
}
}
private void markStatusChipLifecycleDirtyIfNeeded(View view) {
markStatusChipLifecycleDirtyIfNeeded(view, null);
private void requestUnlockedLayoutRefreshFromSource(View sourceView) {
View root = unlockedStatusBarRootFor(sourceView);
if (root != null) {
requestUnlockedLayoutRefresh(root);
}
View current = sourceView;
while (current != null) {
if (current != root && isUnlockedStatusBarRoot(current)) {
requestUnlockedLayoutRefresh(current);
}
Object parent = current.getParent();
current = parent instanceof View ? (View) parent : null;
}
}
private void markStatusChipLifecycleDirtyIfNeeded(View view, View fallbackRoot) {
@@ -991,9 +1322,9 @@ final class StockLayoutCanvasController {
&& activeScene.isUnlocked()
&& areUnlockedHostsAttached(root, unlockedHosts)
&& unlockedIconRenderController.refreshUnlockedChipContent(
root,
unlockedHosts.chipHost,
activeScene)) {
root,
unlockedHosts.chipHost,
activeScene)) {
bringUnlockedHostsAndDebugOverlayToFront(root);
return;
}
@@ -1189,6 +1520,18 @@ final class StockLayoutCanvasController {
}
}
private void requestUnlockedLayoutRefreshForAllRoots() {
ArrayList<View> roots = new ArrayList<>(rootGeometries.keySet());
for (View root : new ArrayList<>(unlockedHostsByRoot.keySet())) {
if (!roots.contains(root)) {
roots.add(root);
}
}
for (View root : roots) {
requestUnlockedLayoutRefresh(root);
}
}
private void scheduleSceneRefresh(SceneKey previous, SceneKey current) {
if (previous == null || current == null || previous.equals(current)) {
return;
@@ -1436,7 +1779,7 @@ final class StockLayoutCanvasController {
return;
}
UnlockedHosts unlockedHosts = updateUnlockedOwnedHosts(root, activeScene);
RenderResult renderResult = new RenderResult(false, false);
RenderResult renderResult = new RenderResult(false, false, false);
if (unlockedHosts != null
&& (unlockedScene
|| activeScene != null && activeScene.isLockscreen()
@@ -1456,6 +1799,7 @@ final class StockLayoutCanvasController {
unlockedHosts.statusHost,
unlockedHosts.notificationHost,
activeScene,
rootDirty,
lockedCarrierTextSource());
boolean shouldShowUnlockedHosts = unlockedScene || aodScene || renderResult.shouldOwnLockscreen();
ownedIconHostManager.setUnlockedHostsVisible(root, shouldShowUnlockedHosts);
@@ -1532,9 +1876,35 @@ final class StockLayoutCanvasController {
} else {
removeDebugOverlay(root);
}
if (isLayoutOffCleanupNoOp(root)) {
return;
}
cleanupLayoutOffRoot(root, activeScene);
}
private boolean isLayoutOffCleanupNoOp(View root) {
return hiddenViewStates.isEmpty()
&& trackedStatusChipSources.isEmpty()
&& trackedUnlockedStockSurfaces.isEmpty()
&& trackedLockedStatusBarIconSurfaces.isEmpty()
&& !lockscreenCardsNotificationRowFilter.hasHiddenRows()
&& !unlockedHostsByRoot.containsKey(root)
&& !hasDirectOwnedLayoutHost(root);
}
private boolean hasDirectOwnedLayoutHost(View root) {
if (!(root instanceof ViewGroup group)) {
return false;
}
return findDirectOwnedHost(group, OwnedIconHostManager.TAG_UNLOCKED_STATUS_HOST) != null
|| findDirectOwnedHost(group, OwnedIconHostManager.TAG_UNLOCKED_NOTIFICATION_HOST) != null
|| findDirectOwnedHost(group, OwnedIconHostManager.TAG_UNLOCKED_CLOCK_HOST) != null
|| findDirectOwnedHost(group, OwnedIconHostManager.TAG_UNLOCKED_CARRIER_HOST) != null
|| findDirectOwnedHost(group, OwnedIconHostManager.TAG_UNLOCKED_CHIP_HOST) != null
|| findDirectOwnedHost(group, OwnedIconHostManager.TAG_LOCKSCREEN_CARDS_NOTIFICATION_HOST) != null
|| findDirectOwnedHost(group, OwnedIconHostManager.TAG_AOD_CARDS_NOTIFICATION_HOST) != null;
}
private void applyDebugOverlay(
View root,
boolean layoutEnabled,
@@ -2113,7 +2483,8 @@ final class StockLayoutCanvasController {
private boolean shouldSuppressTrackedStatusChipVisibility(View view, int visibility) {
return visibility != View.VISIBLE
&& isTrackedStatusChipWriteTarget(view)
&& hasVisibleStatusChipContent(view);
&& hasVisibleStatusChipContent(view)
&& !isCallStatusChip(view);
}
private boolean isTrackedStatusChipWriteTarget(View view) {
@@ -2528,6 +2899,43 @@ final class StockLayoutCanvasController {
return false;
}
private boolean isCallStatusChip(View source) {
if (source == null) {
return false;
}
ArrayDeque<View> queue = new ArrayDeque<>();
queue.add(source);
int scanned = 0;
while (!queue.isEmpty() && scanned++ < 48) {
View view = queue.removeFirst();
String className = view.getClass().getName().toLowerCase(Locale.ROOT);
String idName = ViewIdNames.idName(view).toLowerCase(Locale.ROOT);
if (className.contains("call") || idName.contains("call")) {
return true;
}
if (containsCallToken(view.getContentDescription())) {
return true;
}
if (view instanceof TextView textView && containsCallToken(textView.getText())) {
return true;
}
if (view instanceof ViewGroup childGroup) {
for (int i = 0; i < childGroup.getChildCount(); i++) {
queue.addLast(childGroup.getChildAt(i));
}
}
}
return false;
}
private boolean containsCallToken(CharSequence value) {
if (value == null || value.length() == 0) {
return false;
}
String text = value.toString().toLowerCase(Locale.ROOT);
return text.contains("call") || text.contains("phone");
}
private boolean isStatusChipCandidate(View view) {
String className = view.getClass().getName().toLowerCase(Locale.ROOT);
String idName = ViewIdNames.idName(view).toLowerCase(Locale.ROOT);
@@ -2798,16 +3206,7 @@ final class StockLayoutCanvasController {
LockscreenCardsNotificationIconSourceStore.clearAodSources(container);
return;
}
boolean changed = false;
ArrayList<String> packages = alignedAodIconPackages(container.getContext());
if (!packages.isEmpty()) {
changed |= LockscreenCardsNotificationIconSourceStore.putPackages(container, packages);
}
boolean prepared = prepareAodNotificationIconSources(container);
changed |= prepared;
changed |= LockscreenCardsNotificationIconSourceStore.putAodDrawables(
container,
latestAodIconImageViews);
boolean changed = refreshAodNotificationSources(container);
knownAodNotificationContainers.add(container);
if (changed) {
View root = container.getRootView();
@@ -2827,16 +3226,7 @@ final class StockLayoutCanvasController {
LockscreenCardsNotificationIconSourceStore.clearAodSources(container);
continue;
}
ArrayList<String> packages = alignedAodIconPackages(container.getContext());
boolean changed = false;
if (!packages.isEmpty()) {
changed |= LockscreenCardsNotificationIconSourceStore.putPackages(container, packages);
}
boolean prepared = prepareAodNotificationIconSources(container);
changed |= prepared;
changed |= LockscreenCardsNotificationIconSourceStore.putAodDrawables(
container,
latestAodIconImageViews);
boolean changed = refreshAodNotificationSources(container);
if (changed) {
View root = container.getRootView();
invalidateAodNotificationRoot(root);
@@ -2844,10 +3234,24 @@ final class StockLayoutCanvasController {
}
}
private boolean refreshAodNotificationSources(ViewGroup container) {
ArrayList<String> packages = alignedAodIconPackages(container.getContext());
if (packages.isEmpty() && isAodDotScene()) {
packages = aodNotificationPackages();
}
boolean changed = false;
if (!packages.isEmpty()) {
changed |= LockscreenCardsNotificationIconSourceStore.putPackages(container, packages);
}
changed |= prepareAodNotificationIconSources(container);
changed |= LockscreenCardsNotificationIconSourceStore.putAodDrawables(
container,
latestAodIconImageViews);
return changed;
}
private boolean prepareAodNotificationIconSources(ViewGroup container) {
if (container == null
|| latestAodIconImageViews == null
|| latestAodIconImageViews.isEmpty()) {
if (container == null || latestAodIconImageViews.isEmpty()) {
return false;
}
boolean invoked = false;
@@ -2867,9 +3271,25 @@ final class StockLayoutCanvasController {
return invoked;
}
private boolean isAodDotScene() {
SceneKey scene = runtimeContext.getModeStateRepository().getActiveScene();
return scene != null
&& scene.isAod()
&& scene.notificationDisplayStyle() == NotificationDisplayStyle.DOT;
}
private ArrayList<String> aodNotificationPackages() {
ArrayList<String> result = new ArrayList<>();
for (StatusBarNotification sbn : latestAodNotifications) {
if (sbn != null && AppIconRules.isValidPackageName(sbn.getPackageName())) {
result.add(sbn.getPackageName());
}
}
return result;
}
private ArrayList<String> alignedAodIconPackages(Context context) {
int imageCount = latestAodIconImageViews != null ? latestAodIconImageViews.size() : 0;
if (imageCount <= 0) {
if (latestAodIconImageViews.isEmpty()) {
return new ArrayList<>();
}
ArrayList<String> aligned = alignAodPackagesByDrawable(context);
@@ -2882,9 +3302,7 @@ final class StockLayoutCanvasController {
private ArrayList<String> alignAodPackagesByDrawable(Context context) {
ArrayList<String> aligned = new ArrayList<>();
if (context == null
|| latestAodIconImageViews == null
|| latestAodIconImageViews.isEmpty()
|| latestAodNotifications == null
|| latestAodNotifications.isEmpty()) {
return aligned;
}
@@ -2989,6 +3407,20 @@ final class StockLayoutCanvasController {
dirtyUnlockedLayoutRoots.add(root);
}
private void markKnownAodNotificationRootsDirty() {
for (ViewGroup container : knownAodNotificationContainers) {
if (container == null || !container.isAttachedToWindow()) {
continue;
}
View root = container.getRootView();
if (root != null) {
dirtyUnlockedLayoutRoots.add(root);
root.requestLayout();
root.invalidate();
}
}
}
private void markAodLayoutDirty(View view) {
if (view == null || !view.isAttachedToWindow()) {
return;
@@ -3151,19 +3583,19 @@ final class StockLayoutCanvasController {
return true;
}
private void restoreAllHiddenViews() {
private void restoreAllHiddenViews(View root, SceneKey activeScene) {
ArrayDeque<View> views = new ArrayDeque<>(hiddenViewStates.keySet());
while (!views.isEmpty()) {
View view = views.removeFirst();
if (forgetInactiveTrackedStatusChip(view)) {
continue;
}
if (shouldDeferLayoutOffRestore(root, activeScene, view)) {
keepViewHidden(view);
continue;
}
restoreView(view);
}
trackedStatusChipSources.clear();
trackedLockedStatusBarIconSurfaces.clear();
trackedUnlockedStockSurfaces.clear();
aodPluginVisualAlphaByView.clear();
}
private boolean forgetInactiveTrackedStatusChip(View view) {
@@ -3176,16 +3608,16 @@ final class StockLayoutCanvasController {
hiddenViewStates.remove(view);
trackedStatusChipSources.remove(view);
trackedUnlockedStockSurfaces.remove(view);
statusChipLifecycleLayoutSignatureByView.remove(view);
return true;
}
private void cleanupLayoutOffRoot(View root, SceneKey activeScene) {
lockscreenCardsNotificationRowFilter.restoreAll();
if (!hiddenViewStates.isEmpty()) {
restoreAllHiddenViews();
restoreAllHiddenViews(root, activeScene);
}
restoreLayoutOffStockSurfaces(root, activeScene);
trackedStatusChipSources.clear();
clearTrackedStatusChipState();
trackedLockedStatusBarIconSurfaces.clear();
trackedUnlockedStockSurfaces.clear();
confirmedAodPluginViews.clear();
@@ -3219,63 +3651,38 @@ final class StockLayoutCanvasController {
ownedIconHostManager.removeOwnedHosts(root);
}
private void restoreLayoutOffStockSurfaces(View root, SceneKey activeScene) {
if (root == null || !isViewThread(root)) {
return;
}
boolean unlockedScene = activeScene == null || activeScene.isUnlocked();
walkTree(root, view -> {
if (!isLayoutOffRecoverableStockSurface(view)) {
return;
}
String idName = ViewIdNames.idName(view);
int beforeVisibility = view.getVisibility();
float beforeAlpha = view.getAlpha();
restoreView(view);
if (!unlockedScene && LAYOUT_OFF_UNLOCKED_ONLY_IDS.contains(idName)) {
if (view.getAlpha() != 0f) {
view.setAlpha(0f);
}
if (view.getVisibility() != View.INVISIBLE) {
view.setVisibility(View.INVISIBLE);
}
return;
}
if (unlockedScene && view.getVisibility() != View.VISIBLE) {
view.setVisibility(View.VISIBLE);
}
if (shouldPreserveUnlockedStockAlpha(idName, beforeVisibility, beforeAlpha)) {
if (view.getAlpha() != beforeAlpha) {
view.setAlpha(beforeAlpha);
}
return;
}
if (unlockedScene && view.getAlpha() <= 0f) {
view.setAlpha(1f);
}
});
}
private boolean shouldPreserveUnlockedStockAlpha(
String idName,
int beforeVisibility,
float beforeAlpha
private boolean shouldDeferLayoutOffRestore(
View root,
SceneKey activeScene,
View view
) {
if (!"notification_icon_area_inner".equals(idName)) {
return false;
}
return beforeVisibility == View.VISIBLE && beforeAlpha < 1f;
return shouldKeepUnlockedShadeShelfOwnedByCustomLayout(root, activeScene, view);
}
private boolean isLayoutOffRecoverableStockSurface(View view) {
if (view == null || ownedIconHostManager.isOwnedHost(view)) {
private void keepViewHidden(View view) {
hideView(view, false);
}
private boolean shouldKeepUnlockedShadeShelfOwnedByCustomLayout(
View root,
SceneKey activeScene,
View view
) {
if (root == null
|| activeScene == null
|| !activeScene.isUnlocked()
|| !isDescendantOfNotificationShadeWindow(view)) {
return false;
}
String idName = ViewIdNames.idName(view);
String className = view.getClass().getName();
return LAYOUT_OFF_UNLOCKED_ONLY_IDS.contains(idName)
|| LAYOUT_OFF_EXTRA_RECOVERABLE_IDS.contains(idName)
|| containsAny(className, LAYOUT_OFF_RECOVERABLE_CLASS_SUBSTRINGS);
return (isCardsNotificationIconSurface(view) || isCardsNotificationShelfBackgroundSurface(view))
&& hasAncestorClass(view, "NotificationShelf");
}
private void clearTrackedStatusChipState() {
trackedStatusChipSources.clear();
statusChipContentSignatureByRoot.clear();
statusChipLayoutSignatureByRoot.clear();
statusChipLifecycleLayoutSignatureByView.clear();
}
private static boolean containsAny(String value, Set<String> tokens) {
@@ -3395,11 +3802,20 @@ final class StockLayoutCanvasController {
generation = 31 * generation + latestAodNotifications.size();
Bounds sourceBounds = LockscreenCardsNotificationIconSourceStore
.sourceContainerBoundsForRoot(root);
boolean hiddenSource = LockscreenCardsNotificationIconSourceStore
.sourceContainerHasHiddenAncestorForRoot(root);
if (hiddenSource) {
// Samsung hides the cards icon source during the clock/date handoff.
// Keep our mirror hidden too instead of following its transient translation.
setHostAlpha(host, 0f);
return;
}
generation = 31 * generation + (sourceBounds != null ? sourceBounds.signature() : 0);
Integer previous = aodCardsNotificationRenderGenerationByRoot.get(root);
boolean dirty = dirtyUnlockedLayoutRoots.contains(root);
boolean shouldRender = previous == null
|| previous != generation
|| dirtyUnlockedLayoutRoots.contains(root)
|| dirty
|| hostGroup.getChildCount() <= 0;
if (shouldRender) {
aodCardsNotificationRenderGenerationByRoot.put(root, generation);
@@ -3421,7 +3837,7 @@ final class StockLayoutCanvasController {
lockscreenCardsNotificationRowFilter.apply(stack, allowAfterKeyguardUnlock);
}
private void applyLockscreenCardsNotificationRowFilterInTreeIfNeeded(
private boolean applyLockscreenCardsNotificationRowFilterInTreeIfNeeded(
View root,
SbtSettings settings,
SceneKey activeScene,
@@ -3429,9 +3845,10 @@ final class StockLayoutCanvasController {
int signature = lockscreenCardsRowFilterSignature(root, settings, activeScene);
Integer previous = lockscreenCardsRowFilterSignatures.put(root, signature);
if (previous != null && previous == signature) {
return;
return false;
}
applyLockscreenCardsNotificationRowFilterInTree(root, allowAfterKeyguardUnlock);
return true;
}
private int lockscreenCardsRowFilterSignature(
@@ -3620,11 +4037,13 @@ final class StockLayoutCanvasController {
}
NotificationDisplayStyle notificationStyle =
SystemNotificationDisplayStyleDetector.read(context);
if (isAodRoot(root) || (isKeyguardLocked(context) && !isInteractive(context))) {
boolean keyguardLocked = isKeyguardLocked(context);
boolean interactive = isInteractive(context);
if (isAodRoot(root) || (keyguardLocked && !interactive)) {
runtimeContext.getModeStateRepository().setAod(notificationStyle);
return;
}
if (isKeyguardLocked(context)) {
if (keyguardLocked) {
runtimeContext.getModeStateRepository().setLockscreen(notificationStyle);
return;
}
@@ -798,7 +798,7 @@ public final class PackedStatusBarLayoutSolver {
}
}
public static void collisionTree(ArrayList<LayoutItem> items) {
private static void collisionTree(ArrayList<LayoutItem> items) {
for (LayoutItem item : items) {
for (Box box : item.boxes) {
box.collisionLeft.clear();
@@ -1165,11 +1165,6 @@ public final class PackedStatusBarLayoutSolver {
return oldWidth - width();
}
public void setPoolRemaining(int remaining) {
poolRemaining = Math.max(0, remaining);
recomputeIconState();
}
public void setPoolIcons(ArrayList<Integer> widths, int startIndex) {
widthLimit = Math.min(currentWidthLimit(), width());
poolIconWidths = widths != null ? new ArrayList<>(widths) : new ArrayList<>();
@@ -1,668 +1,9 @@
package se.ajpanton.statusbartweak.runtime.layoutsolver;
import java.util.ArrayList;
import java.util.Collections;
public final class StatusBarLayoutSolver {
private static final int MIDDLE_COLLISION_MARGIN_PX = 2;
private StatusBarLayoutSolver() {
}
public static <T extends LayoutBand> ArrayList<T> bandsForPosition(
ArrayList<T> bands,
LayoutPosition position,
ArrayList<LayoutItemKind> itemOrder
) {
ArrayList<T> result = new ArrayList<>();
for (LayoutItemKind kind : itemOrder) {
for (T band : bands) {
if (band.kind == kind
&& band.position == position
&& band.splitRegion == SplitRegion.NONE) {
result.add(band);
}
}
}
return result;
}
public static <T extends LayoutBand> ArrayList<T> middleBandsForPlacement(
ArrayList<T> bands,
ArrayList<LayoutItemKind> itemOrder,
AnchorSide side
) {
ArrayList<T> result = new ArrayList<>();
SplitRegion sideRegion = side == AnchorSide.LEFT ? SplitRegion.LEFT : SplitRegion.RIGHT;
for (T band : bands) {
if (band.splitRegion == sideRegion) {
result.add(band);
}
}
result.addAll(bandsForPosition(bands, LayoutPosition.MIDDLE, itemOrder));
return result;
}
public static <T extends LayoutBand> RegionPlan<T> planRegions(
int rootWidth,
LayoutEdges edges,
ArrayList<T> bands,
ArrayList<LayoutItemKind> itemOrder,
Bounds cutoutBounds,
int itemPadding
) {
int leftEdge = Math.max(0, Math.min(rootWidth, edges.left));
int rightEdge = Math.max(leftEdge, Math.min(rootWidth, edges.right));
Region<T> leftRegion;
Region<T> rightRegion = null;
if (cutoutBounds == null) {
leftRegion = new Region<>(leftEdge, rightEdge);
} else {
leftRegion = new Region<>(leftEdge, Math.min(cutoutBounds.left, rightEdge));
rightRegion = new Region<>(
Math.max(cutoutBounds.left + cutoutBounds.width, leftEdge),
rightEdge);
}
ArrayList<T> leftBands = bandsForPosition(bands, LayoutPosition.LEFT, itemOrder);
ArrayList<T> middleBands = bandsForPosition(bands, LayoutPosition.MIDDLE, itemOrder);
ArrayList<T> rightBands = bandsForPosition(bands, LayoutPosition.RIGHT, itemOrder);
AnchorSide middleSide = sideForMiddleRegion(rootWidth, middleBands, cutoutBounds, itemPadding);
if (rightRegion == null) {
addSplitRegionBands(bands, leftRegion, SplitRegion.LEFT);
addSplitRegionBands(bands, leftRegion, SplitRegion.RIGHT);
leftRegion.bands.addAll(leftBands);
leftRegion.bands.addAll(middleBands);
Collections.reverse(rightBands);
leftRegion.bands.addAll(rightBands);
} else {
leftRegion.bands.addAll(leftBands);
ArrayList<T> middleRegionBands = new ArrayList<>(middleBands);
Collections.reverse(middleRegionBands);
if (middleSide == AnchorSide.LEFT) {
leftRegion.bands.addAll(middleRegionBands);
}
addSplitRegionBands(bands, leftRegion, SplitRegion.LEFT);
addSplitRegionBands(bands, rightRegion, SplitRegion.RIGHT);
if (middleSide == AnchorSide.RIGHT) {
rightRegion.bands.addAll(middleRegionBands);
}
Collections.reverse(rightBands);
rightRegion.bands.addAll(rightBands);
}
ArrayList<Region<T>> regions = new ArrayList<>();
regions.add(leftRegion);
if (rightRegion != null) {
regions.add(rightRegion);
}
return new RegionPlan<>(regions, middleSide);
}
private static <T extends LayoutBand> void addSplitRegionBands(
ArrayList<T> source,
Region<T> target,
SplitRegion region
) {
if (source == null || target == null || region == SplitRegion.NONE) {
return;
}
for (T band : source) {
if (band.splitRegion == region) {
target.bands.add(band);
}
}
}
public static <T extends LayoutBand> AnchorSide sideForMiddleRegion(
int rootWidth,
ArrayList<T> middleBands,
Bounds cutoutBounds,
int itemPadding
) {
int middle = rootWidth / 2;
if (middleBands == null || middleBands.isEmpty()) {
return cutoutBounds == null || cutoutBounds.left + cutoutBounds.width / 2 < middle
? AnchorSide.RIGHT
: AnchorSide.LEFT;
}
T firstBand = middleBands.get(0);
int width = packedWidth(middleBands, itemPadding);
int cameraDistance = distanceFromMiddle(cutoutBounds, middle);
boolean centered = cutoutBounds != null
&& cutoutBounds.left <= middle + MIDDLE_COLLISION_MARGIN_PX
&& cutoutBounds.left + cutoutBounds.width >= middle - MIDDLE_COLLISION_MARGIN_PX;
if (cameraDistance < width / 2 && (centered || !firstBand.middleAutoNearestSide)) {
return firstBand.middleSide;
}
if (cutoutBounds == null || cutoutBounds.left + cutoutBounds.width / 2 < middle) {
return AnchorSide.RIGHT;
}
return AnchorSide.LEFT;
}
public static <T extends LayoutBand> Anchor anchorForMiddle(
int rootWidth,
ArrayList<T> bands,
ArrayList<PlacedBand> placedBands,
Bounds cutoutBounds,
AnchorSide side,
int itemPadding
) {
int middle = rootWidth / 2;
int width = packedWidth(bands, itemPadding);
if (coversMiddle(cutoutBounds, middle)) {
return obstacleAnchor(cutoutBounds, side, AnchorKind.WALL);
}
Bounds nearestItem = nearestPlacedBand(placedBands, middle);
int cameraDistance = distanceFromMiddle(cutoutBounds, middle);
int itemDistance = distanceFromMiddle(nearestItem, middle);
if (Math.min(cameraDistance, itemDistance) < width / 2) {
if (cameraDistance < itemDistance) {
return obstacleAnchor(cutoutBounds, side, AnchorKind.WALL);
}
return obstacleAnchor(nearestItem, side, AnchorKind.ITEM);
}
return middleAnchor(rootWidth, width, side);
}
private static Anchor obstacleAnchor(Bounds obstacle, AnchorSide side, AnchorKind kind) {
if (obstacle == null) {
return new Anchor(0, side, kind);
}
int anchorPoint = side == AnchorSide.RIGHT ? obstacle.left + obstacle.width : obstacle.left;
return new Anchor(anchorPoint, side, kind);
}
private static Anchor middleAnchor(int rootWidth, int width, AnchorSide side) {
int middle = rootWidth / 2;
if (side == AnchorSide.RIGHT) {
return new Anchor(middle - width / 2, AnchorSide.RIGHT, AnchorKind.CENTER);
}
return new Anchor(middle + width / 2, AnchorSide.LEFT, AnchorKind.CENTER);
}
public static <T extends LayoutBand> void placeStackedBands(
ArrayList<T> bands,
Anchor anchor,
ArrayList<PlacedBand> placedBands,
String sortOrder,
boolean avoidPlacedBands,
int itemPadding
) {
if (bands == null || bands.isEmpty()) {
return;
}
for (T band : bands) {
band.prepareForSide(anchor.side, sortOrder);
}
ArrayList<StackedBand<T>> stackedBands = stackBands(bands, itemPadding);
int shift = avoidPlacedBands ? middleShift(stackedBands, anchor, placedBands, itemPadding) : 0;
for (StackedBand<T> stackedBand : stackedBands) {
T band = stackedBand.band;
int wallDotSlack = anchor.kind == AnchorKind.WALL
&& stackedBand.left == 0
&& shift == 0
? band.wallDotSlack(anchor.side)
: 0;
int left = stackedLeft(stackedBand, anchor, shift, wallDotSlack, itemPadding);
band.place(left);
ArrayList<Bounds> bounds = band.placedCollisionBounds();
if (!bounds.isEmpty()) {
placedBands.add(new PlacedBand(band.kind, band.position, bounds));
}
}
}
private static <T extends LayoutBand> int middleShift(
ArrayList<StackedBand<T>> stackedBands,
Anchor anchor,
ArrayList<PlacedBand> placedBands,
int itemPadding
) {
int shift = 0;
for (StackedBand<T> stackedBand : stackedBands) {
ArrayList<Bounds> bounds = boundsForStackedBand(stackedBand, anchor, shift, itemPadding);
int leftOverstep = overstepFromPlacedBands(bounds, placedBands, LayoutPosition.LEFT, itemPadding);
if (leftOverstep > 0) {
shift += leftOverstep;
continue;
}
int rightOverstep = overstepFromPlacedBands(bounds, placedBands, LayoutPosition.RIGHT, itemPadding);
if (rightOverstep > 0) {
shift -= rightOverstep;
}
}
return shift;
}
private static <T extends LayoutBand> ArrayList<Bounds> boundsForStackedBand(
StackedBand<T> stackedBand,
Anchor anchor,
int shift,
int itemPadding
) {
ArrayList<Bounds> result = new ArrayList<>();
int left = stackedLeft(stackedBand, anchor, shift, 0, itemPadding);
for (Bounds box : stackedBand.boxes) {
result.add(new Bounds(left + box.left, box.top, box.width, box.height));
}
return result;
}
private static <T extends LayoutBand> int stackedLeft(
StackedBand<T> stackedBand,
Anchor anchor,
int shift,
int wallDotSlack,
int itemPadding
) {
T band = stackedBand.band;
int anchorGap = anchor.kind == AnchorKind.ITEM ? Math.max(0, itemPadding) : 0;
if (anchor.side == AnchorSide.RIGHT) {
return anchor.point + anchorGap + stackedBand.left + shift - wallDotSlack;
}
return anchor.point - anchorGap - stackedBand.left - band.width() + shift + wallDotSlack;
}
private static int overstepFromPlacedBands(
ArrayList<Bounds> bounds,
ArrayList<PlacedBand> placedBands,
LayoutPosition position,
int itemPadding
) {
int overstep = 0;
if (bounds == null || placedBands == null) {
return overstep;
}
for (Bounds boundsItem : bounds) {
if (boundsItem == null) {
continue;
}
for (PlacedBand placedBand : placedBands) {
if (placedBand.position != position) {
continue;
}
for (Bounds obstacle : placedBand.bounds) {
if (obstacle == null
|| !verticalRangesOverlap(
boundsItem.top,
boundsItem.height,
obstacle.top,
obstacle.height)) {
continue;
}
overstep = Math.max(overstep, horizontalOverstep(boundsItem, obstacle, position, itemPadding));
}
}
}
return overstep;
}
private static int horizontalOverstep(
Bounds item,
Bounds obstacle,
LayoutPosition obstaclePosition,
int itemPadding
) {
int gap = Math.max(0, itemPadding);
if (obstaclePosition == LayoutPosition.LEFT) {
return Math.max(0, obstacle.left + blockingWidth(obstacle) + gap - item.left);
}
if (obstaclePosition == LayoutPosition.RIGHT) {
return Math.max(0, item.left + blockingWidth(item) + gap - obstacle.left);
}
return 0;
}
public static <T extends LayoutBand> void truncateToFit(
ArrayList<T> bands,
int freeSpace,
AnchorSide middleSide,
String sortOrder,
int itemPadding
) {
int target = Math.max(0, freeSpace);
for (T band : bands) {
band.prepareForSide(truncationAnchorSide(band, middleSide), sortOrder);
}
int guard = 64;
while (packedWidthForTruncation(bands, middleSide, itemPadding) > target && guard-- > 0) {
if (shrinkToReducePackedWidth(bands, LayoutItemKind.NOTIFICATIONS, middleSide, itemPadding)) {
continue;
}
if (shrinkToReducePackedWidth(bands, LayoutItemKind.STATUS, middleSide, itemPadding)) {
continue;
}
if (clipToReducePackedWidth(bands, LayoutItemKind.CHIP, middleSide, target, itemPadding)) {
continue;
}
if (removeDotToReducePackedWidth(bands, LayoutItemKind.NOTIFICATIONS, middleSide, itemPadding)) {
continue;
}
if (removeDotToReducePackedWidth(bands, LayoutItemKind.STATUS, middleSide, itemPadding)) {
continue;
}
if (clipToReducePackedWidth(bands, LayoutItemKind.CLOCK, middleSide, target, itemPadding)) {
continue;
}
break;
}
}
private static <T extends LayoutBand> boolean shrinkToReducePackedWidth(
ArrayList<T> bands,
LayoutItemKind kind,
AnchorSide middleSide,
int itemPadding
) {
return mutateBandToReducePackedWidth(
bands,
kind,
middleSide,
itemPadding,
LayoutBand::shrinkOneIcon,
false,
true);
}
private static <T extends LayoutBand> boolean removeDotToReducePackedWidth(
ArrayList<T> bands,
LayoutItemKind kind,
AnchorSide middleSide,
int itemPadding
) {
return mutateBandToReducePackedWidth(
bands,
kind,
middleSide,
itemPadding,
LayoutBand::removeOverflowDot,
true,
false);
}
private static <T extends LayoutBand> boolean mutateBandToReducePackedWidth(
ArrayList<T> bands,
LayoutItemKind kind,
AnchorSide middleSide,
int itemPadding,
BandMutation mutation,
boolean acceptEqualWidth,
boolean acceptCreatedDot
) {
T band = findBand(bands, kind);
if (band == null) {
return false;
}
int before = packedWidthForTruncation(bands, middleSide, itemPadding);
Object state = band.captureState();
if (!mutation.apply(band)) {
return false;
}
int after = packedWidthForTruncation(bands, middleSide, itemPadding);
if (after < before
|| (acceptEqualWidth && after == before)
|| (acceptCreatedDot && band.createdOverflowDotSince(state))) {
return true;
}
band.restoreState(state);
return false;
}
private static <T extends LayoutBand> boolean clipToReducePackedWidth(
ArrayList<T> bands,
LayoutItemKind kind,
AnchorSide middleSide,
int target,
int itemPadding
) {
T band = findBand(bands, kind);
if (band == null || !band.canClipContinuously()) {
return false;
}
int before = packedWidthForTruncation(bands, middleSide, itemPadding);
int amount = Math.max(1, before - target);
if (!band.clipContinuous(truncationAnchorSide(band, middleSide), amount)) {
return false;
}
return packedWidthForTruncation(bands, middleSide, itemPadding) <= before;
}
public static <T extends LayoutBand> int packedWidth(ArrayList<T> bands) {
return packedWidth(bands, 0);
}
public static <T extends LayoutBand> int packedWidth(ArrayList<T> bands, int itemPadding) {
return stackBounds(stackBands(bands, itemPadding)).width;
}
private static <T extends LayoutBand> int packedWidthForTruncation(
ArrayList<T> bands,
AnchorSide middleSide,
int itemPadding
) {
ArrayList<StackedBand<T>> stackedBands = stackBands(bands, itemPadding);
Bounds bounds = stackBounds(stackedBands);
int width = bounds.width;
if (width <= 0 || stackedBands.isEmpty()) {
return width;
}
int leftEdge = bounds.left;
int rightEdge = bounds.left + bounds.width;
Integer leftSlack = null;
Integer rightSlack = null;
for (StackedBand<T> stackedBand : stackedBands) {
T band = stackedBand.band;
Bounds bandBounds = shiftedBounds(stackedBand.boxes, stackedBand.left);
int bandLeft = bandBounds != null ? bandBounds.left : stackedBand.left;
int bandRight = bandBounds != null ? bandBounds.left + bandBounds.width : bandLeft;
if (bandLeft == leftEdge) {
int slack = band.wallDotSlack(AnchorSide.RIGHT);
leftSlack = leftSlack == null ? slack : Math.min(leftSlack, slack);
}
if (bandRight == rightEdge) {
int slack = band.wallDotSlack(AnchorSide.LEFT);
rightSlack = rightSlack == null ? slack : Math.min(rightSlack, slack);
}
}
return Math.max(0, width - (leftSlack != null ? leftSlack : 0) - (rightSlack != null ? rightSlack : 0));
}
private static AnchorSide truncationAnchorSide(LayoutBand band, AnchorSide middleSide) {
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 middleSide;
}
private static <T extends LayoutBand> ArrayList<StackedBand<T>> stackBands(
ArrayList<T> bands,
int itemPadding
) {
ArrayList<StackedBand<T>> stackedBands = new ArrayList<>();
if (bands == null) {
return stackedBands;
}
int gap = Math.max(0, itemPadding);
for (T band : bands) {
int left = 0;
ArrayList<Bounds> boxes = band.collisionBoxes();
for (StackedBand<T> stackedBand : stackedBands) {
for (Bounds box : boxes) {
for (Bounds placedBox : stackedBand.boxes) {
if (verticalRangesOverlap(box.top, box.height, placedBox.top, placedBox.height)) {
left = Math.max(
left,
stackedBand.left + placedBox.left + blockingWidth(placedBox) + gap - box.left);
}
}
}
}
stackedBands.add(new StackedBand<>(band, left, boxes));
}
return stackedBands;
}
private static <T extends LayoutBand> Bounds stackBounds(ArrayList<StackedBand<T>> stackedBands) {
Bounds bounds = new Bounds(0, 0, 0, 0);
if (stackedBands == null || stackedBands.isEmpty()) {
return bounds;
}
Bounds union = null;
for (StackedBand<T> stackedBand : stackedBands) {
Bounds bandBounds = shiftedBounds(stackedBand.boxes, stackedBand.left);
if (bandBounds != null) {
union = union == null ? bandBounds : union.union(bandBounds);
}
}
return union != null ? union : bounds;
}
private static Bounds shiftedBounds(ArrayList<Bounds> boxes, int left) {
Bounds union = null;
if (boxes == null) {
return null;
}
for (Bounds box : boxes) {
Bounds shifted = new Bounds(left + box.left, box.top, blockingWidth(box), box.height);
union = union == null ? shifted : union.union(shifted);
}
return union;
}
private static boolean verticalRangesOverlap(int topA, int heightA, int topB, int heightB) {
return topA < topB + heightB && topB < topA + heightA;
}
private static int blockingWidth(Bounds bounds) {
return bounds != null && bounds.height > 0 ? Math.max(1, bounds.width) : 0;
}
private static <T extends LayoutBand> T findBand(ArrayList<T> bands, LayoutItemKind kind) {
for (T band : bands) {
if (band.kind == kind) {
return band;
}
}
return null;
}
private static Bounds nearestPlacedBand(ArrayList<PlacedBand> placedBands, int middle) {
Bounds nearest = null;
int nearestDistance = Integer.MAX_VALUE;
for (PlacedBand placedBand : placedBands) {
for (Bounds bounds : placedBand.bounds) {
int distance = distanceFromMiddle(bounds, middle);
if (distance < nearestDistance) {
nearest = bounds;
nearestDistance = distance;
}
}
}
return nearest;
}
public static int distanceFromMiddle(Bounds bounds, int middle) {
if (bounds == null) {
return Integer.MAX_VALUE;
}
if (bounds.left <= middle && bounds.left + bounds.width >= middle) {
return 0;
}
return Math.min(Math.abs(middle - bounds.left), Math.abs(middle - (bounds.left + bounds.width)));
}
public static boolean coversMiddle(Bounds bounds, int middle) {
return bounds != null
&& bounds.left <= middle + MIDDLE_COLLISION_MARGIN_PX
&& bounds.left + bounds.width >= middle - MIDDLE_COLLISION_MARGIN_PX;
}
public abstract static class LayoutBand {
public final LayoutItemKind kind;
public final LayoutPosition position;
public final boolean middleAutoNearestSide;
public final AnchorSide middleSide;
public final SplitRegion splitRegion;
protected LayoutBand(
LayoutItemKind kind,
LayoutPosition position,
boolean middleAutoNearestSide,
AnchorSide middleSide,
SplitRegion splitRegion
) {
this.kind = kind;
this.position = position;
this.middleAutoNearestSide = middleAutoNearestSide;
this.middleSide = middleSide;
this.splitRegion = splitRegion != null ? splitRegion : SplitRegion.NONE;
}
protected abstract void prepareForSide(AnchorSide side, String sortOrder);
protected abstract int width();
protected abstract ArrayList<Bounds> collisionBoxes();
protected abstract void place(int left);
protected abstract ArrayList<Bounds> placedCollisionBounds();
protected int wallDotSlack(AnchorSide side) {
return 0;
}
protected boolean shrinkOneIcon() {
return false;
}
protected boolean removeOverflowDot() {
return false;
}
protected Object captureState() {
return null;
}
protected void restoreState(Object state) {
}
protected boolean createdOverflowDotSince(Object state) {
return false;
}
protected boolean canClipContinuously() {
return false;
}
protected boolean clipContinuous(AnchorSide wallSide, int amountPx) {
return false;
}
}
private interface BandMutation {
boolean apply(LayoutBand band);
}
private record StackedBand<T extends LayoutBand>(
T band,
int left,
ArrayList<Bounds> boxes
) {
}
public enum LayoutItemKind {
CARRIER,
CLOCK,
@@ -682,21 +23,12 @@ public final class StatusBarLayoutSolver {
RIGHT
}
public enum AnchorKind {
WALL,
ITEM,
CENTER
}
public enum SplitRegion {
NONE,
LEFT,
RIGHT
}
public record Anchor(int point, AnchorSide side, AnchorKind kind) {
}
public static final class Bounds {
public final int left;
public final int top;
@@ -726,36 +58,24 @@ public final class StatusBarLayoutSolver {
result = 31 * result + height;
return result;
}
}
public record PlacedBand(
LayoutItemKind kind,
LayoutPosition position,
ArrayList<Bounds> bounds
) {
}
public record RegionPlan<T extends LayoutBand>(
ArrayList<Region<T>> regions,
AnchorSide middleSide
) {
}
public record LayoutEdges(int left, int right) {
}
public static final class Region<T extends LayoutBand> {
public final int left;
public final int right;
public final ArrayList<T> bands = new ArrayList<>();
Region(int left, int right) {
this.left = left;
this.right = Math.max(left, right);
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof Bounds other)) {
return false;
}
return left == other.left
&& top == other.top
&& width == other.width
&& height == other.height;
}
public int width() {
return Math.max(0, right - left);
@Override
public int hashCode() {
return signature();
}
}
}
@@ -2,15 +2,9 @@ package se.ajpanton.statusbartweak.runtime.mode;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
/**
* Central source of truth for the current scene.
*
* This is intentionally small at the reset stage: it only tracks the active
* scene and whether the custom layout runtime should currently output anything.
* Hook-driven updates can be wired in later without changing the rest of the
* runtime API.
*/
public final class ModeStateRepository {
private SceneKey activeScene = SceneKey.unlocked();
@@ -19,6 +13,7 @@ public final class ModeStateRepository {
private boolean layoutEnabled;
private final List<LayoutEnabledListener> layoutEnabledListeners = new ArrayList<>();
private final List<SceneListener> sceneListeners = new ArrayList<>();
private final List<SystemUiStatusBarStateListener> systemUiStatusBarStateListeners = new ArrayList<>();
public synchronized SceneKey getActiveScene() {
return activeScene;
@@ -68,10 +63,6 @@ public final class ModeStateRepository {
setActiveScene(SceneKey.aod(requireDisplayStyle(style)));
}
public void setScene(SceneKey sceneKey) {
setActiveScene(Objects.requireNonNull(sceneKey, "sceneKey"));
}
public void setSystemUiStatusBarState(
int state,
boolean dozing,
@@ -84,11 +75,23 @@ public final class ModeStateRepository {
SceneKey scene = dozing
? SceneKey.aod(style)
: state == 0 ? SceneKey.unlocked() : SceneKey.lockscreen(style);
boolean changed;
int previousState;
boolean previousKnown;
synchronized (this) {
previousKnown = systemUiStatusBarStateKnown;
previousState = systemUiStatusBarState;
changed = !systemUiStatusBarStateKnown || systemUiStatusBarState != state;
systemUiStatusBarStateKnown = true;
systemUiStatusBarState = state;
}
setActiveScene(scene);
if (changed) {
notifySystemUiStatusBarStateListeners(
previousKnown ? previousState : -1,
state,
dozing);
}
}
public synchronized void addSceneListener(SceneListener listener) {
@@ -97,6 +100,14 @@ public final class ModeStateRepository {
}
}
public synchronized void addSystemUiStatusBarStateListener(
SystemUiStatusBarStateListener listener
) {
if (listener != null && !systemUiStatusBarStateListeners.contains(listener)) {
systemUiStatusBarStateListeners.add(listener);
}
}
private void setActiveScene(SceneKey scene) {
SceneKey previous;
List<SceneListener> listeners;
@@ -108,19 +119,22 @@ public final class ModeStateRepository {
activeScene = scene;
listeners = new ArrayList<>(sceneListeners);
}
notifySceneChanged(previous, scene, listeners);
for (SceneListener listener : listeners) {
listener.onSceneChanged(previous, scene);
}
}
private void notifySceneChanged(
SceneKey previous,
SceneKey current,
List<SceneListener> listeners
private void notifySystemUiStatusBarStateListeners(
int previousState,
int currentState,
boolean dozing
) {
if (listeners == null || listeners.isEmpty()) {
return;
List<SystemUiStatusBarStateListener> listeners;
synchronized (this) {
listeners = new ArrayList<>(systemUiStatusBarStateListeners);
}
for (SceneListener listener : listeners) {
listener.onSceneChanged(previous, current);
for (SystemUiStatusBarStateListener listener : listeners) {
listener.onSystemUiStatusBarStateChanged(previousState, currentState, dozing);
}
}
@@ -138,4 +152,8 @@ public final class ModeStateRepository {
public interface SceneListener {
void onSceneChanged(SceneKey previous, SceneKey current);
}
public interface SystemUiStatusBarStateListener {
void onSystemUiStatusBarStateChanged(int previousState, int currentState, boolean dozing);
}
}
@@ -0,0 +1,68 @@
package se.ajpanton.statusbartweak.runtime.notifications;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
public final class NotificationActiveKeyStore {
private static final HashSet<String> ACTIVE_KEYS = new HashSet<>();
private static int generation;
private NotificationActiveKeyStore() {
}
public static synchronized boolean replace(Collection<String> keys) {
HashSet<String> normalized = new HashSet<>();
if (keys != null) {
for (String key : keys) {
addKey(normalized, key);
}
}
if (ACTIVE_KEYS.equals(normalized)) {
return false;
}
ACTIVE_KEYS.clear();
ACTIVE_KEYS.addAll(normalized);
generation++;
return true;
}
public static synchronized boolean hasSnapshot() {
return !ACTIVE_KEYS.isEmpty();
}
public static synchronized boolean isActive(String key) {
String normalized = normalizeKey(key);
return normalized != null && ACTIVE_KEYS.contains(normalized);
}
public static synchronized int generation() {
return generation;
}
public static synchronized Set<String> snapshot() {
return new HashSet<>(ACTIVE_KEYS);
}
private static void addKey(HashSet<String> out, String key) {
String normalized = normalizeKey(key);
if (normalized != null) {
out.add(normalized);
}
}
private static String normalizeKey(String key) {
if (key == null) {
return null;
}
String normalized = key.trim();
if (normalized.isEmpty()) {
return null;
}
int identitySuffix = normalized.indexOf('@');
if (identitySuffix >= 0) {
normalized = normalized.substring(0, identitySuffix);
}
return normalized.isEmpty() ? null : normalized;
}
}
@@ -0,0 +1,137 @@
package se.ajpanton.statusbartweak.runtime.notifications;
import android.app.Notification;
import android.os.Bundle;
import android.service.notification.StatusBarNotification;
import java.util.HashMap;
import java.util.Objects;
import java.util.Set;
import se.ajpanton.statusbartweak.runtime.NotificationKeyReflection;
import se.ajpanton.statusbartweak.shell.settings.AppIconRules;
public final class NotificationUnreadTracker {
private static final HashMap<String, State> STATE_BY_KEY = new HashMap<>();
private static final HashMap<String, Long> UPDATED_BY_PACKAGE = new HashMap<>();
private static long lastSeenMs;
private static int generation;
private NotificationUnreadTracker() {
}
public static synchronized boolean markSeen() {
long now = System.currentTimeMillis();
if (now <= lastSeenMs) {
return false;
}
lastSeenMs = now;
generation++;
return true;
}
public static synchronized boolean record(StatusBarNotification sbn) {
if (sbn == null || !AppIconRules.isValidPackageName(sbn.getPackageName())) {
return false;
}
String key = sbn.getKey();
if (key == null || key.isEmpty()) {
return false;
}
int signature = signature(sbn);
State previous = STATE_BY_KEY.get(key);
if (previous != null && previous.signature == signature) {
return false;
}
long updateTime = previous == null ? notificationTime(sbn) : System.currentTimeMillis();
State state = new State(sbn.getPackageName(), signature, updateTime);
STATE_BY_KEY.put(key, state);
Long packageTime = UPDATED_BY_PACKAGE.get(state.packageName);
if (packageTime == null || updateTime > packageTime) {
UPDATED_BY_PACKAGE.put(state.packageName, updateTime);
}
generation++;
return updateTime > lastSeenMs;
}
public static synchronized boolean retainActiveKeys(Set<String> activeKeys) {
if (activeKeys == null) {
return false;
}
boolean changed = STATE_BY_KEY.keySet().removeIf(key -> !activeKeys.contains(key));
if (changed) {
rebuildPackageTimes();
generation++;
}
return changed;
}
public static synchronized int generation() {
return generation;
}
public static synchronized boolean isUnread(String key) {
if (key == null || key.isEmpty()) {
return false;
}
State state = STATE_BY_KEY.get(key);
if (state != null) {
return state.updatedMs > lastSeenMs;
}
String packageName = NotificationKeyReflection.packageFromNotificationKey(key);
Long packageTime = packageName != null ? UPDATED_BY_PACKAGE.get(packageName) : null;
return packageTime != null && packageTime > lastSeenMs;
}
private static long notificationTime(StatusBarNotification sbn) {
long result = sbn.getPostTime();
Notification notification = sbn.getNotification();
if (notification != null && notification.when > result) {
result = notification.when;
}
return result > 0L ? result : System.currentTimeMillis();
}
private static int signature(StatusBarNotification sbn) {
Notification notification = sbn.getNotification();
Bundle extras = notification != null ? notification.extras : null;
return Objects.hash(
sbn.getKey(),
sbn.getPostTime(),
notification != null ? notification.when : 0L,
notification != null ? notification.number : 0,
notification != null ? notification.flags : 0,
notification != null ? notification.category : null,
textExtra(extras, Notification.EXTRA_TITLE),
textExtra(extras, Notification.EXTRA_TEXT),
textExtra(extras, Notification.EXTRA_BIG_TEXT),
textExtra(extras, Notification.EXTRA_SUB_TEXT));
}
private static String textExtra(Bundle extras, String key) {
CharSequence value = extras != null ? extras.getCharSequence(key) : null;
return value != null ? value.toString() : null;
}
private static void rebuildPackageTimes() {
UPDATED_BY_PACKAGE.clear();
for (State state : STATE_BY_KEY.values()) {
Long previous = UPDATED_BY_PACKAGE.get(state.packageName);
if (previous == null || state.updatedMs > previous) {
UPDATED_BY_PACKAGE.put(state.packageName, state.updatedMs);
}
}
}
private static final class State {
final String packageName;
final int signature;
final long updatedMs;
State(String packageName, int signature, long updatedMs) {
this.packageName = packageName;
this.signature = signature;
this.updatedMs = updatedMs;
}
}
}
@@ -18,7 +18,7 @@ import java.util.List;
import java.util.Objects;
import se.ajpanton.statusbartweak.R;
import se.ajpanton.statusbartweak.runtime.layoutsolver.StatusBarLayoutSolver;
import se.ajpanton.statusbartweak.runtime.layoutsolver.StatusBarLayoutSolver.Bounds;
import se.ajpanton.statusbartweak.runtime.mode.NotificationDisplayStyle;
import se.ajpanton.statusbartweak.runtime.mode.SceneKey;
import se.ajpanton.statusbartweak.shell.settings.SbtSettings;
@@ -209,9 +209,9 @@ public final class DebugBoundsOverlayController {
Bounds notificationBounds = aodNotificationBounds(root, scene);
Bounds statusBounds = liveAodStatusIconsBounds(root);
if (!isUsableAodAnchor(root, statusBounds)) {
statusBounds = overlayBounds(AodStatusbarSourceStore.statusBoundsForRoot(
statusBounds = AodStatusbarSourceStore.statusBoundsForRoot(
root,
notificationContainer));
notificationContainer);
}
boolean hasNotification = isUsableAodAnchor(root, notificationBounds);
boolean hasStatus = isUsableAodAnchor(root, statusBounds);
@@ -238,8 +238,7 @@ public final class DebugBoundsOverlayController {
&& scene.notificationDisplayStyle() == NotificationDisplayStyle.CARDS) {
return null;
}
return overlayBounds(
LockscreenCardsNotificationIconSourceStore.sourceContainerBoundsForAodIconsRoot(root));
return LockscreenCardsNotificationIconSourceStore.sourceContainerBoundsForAodIconsRoot(root);
}
private ViewGroup aodNotificationContainer(View root, SceneKey scene) {
@@ -266,16 +265,8 @@ public final class DebugBoundsOverlayController {
return bounds != null ? bounds.signature() : 0;
}
private Bounds overlayBounds(StatusBarLayoutSolver.Bounds bounds) {
if (bounds == null) {
return null;
}
return new Bounds(bounds.left, bounds.top, bounds.width, bounds.height);
}
private Bounds liveAodStatusIconsBounds(View root) {
StatusBarLayoutSolver.Bounds bounds = AodLiveStatusbarSource.boundsForRoot(root);
return bounds != null ? new Bounds(bounds.left, bounds.top, bounds.width, bounds.height) : null;
return AodLiveStatusbarSource.boundsForRoot(root);
}
private void addCutoutSpecs(View root, ArrayList<OverlaySpec> specs) {
@@ -352,8 +343,8 @@ public final class DebugBoundsOverlayController {
}
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));
if (value instanceof Bounds bounds) {
out.add(bounds);
}
}
return out;
@@ -606,25 +597,6 @@ public final class DebugBoundsOverlayController {
}
}
private record Bounds(int left, int top, int width, int height) {
Bounds union(Bounds other) {
int right = Math.max(left + width, other.left + other.width);
int bottom = Math.max(top + height, other.top + other.height);
int newLeft = Math.min(left, other.left);
int newTop = Math.min(top, other.top);
return new Bounds(newLeft, newTop, right - newLeft, bottom - newTop);
}
int signature() {
int result = 17;
result = 31 * result + left;
result = 31 * result + top;
result = 31 * result + width;
result = 31 * result + height;
return result;
}
}
private static final class OverlayView extends View {
private final Paint paint = new Paint();
private ArrayList<OverlaySpec> specs = new ArrayList<>();
@@ -25,6 +25,7 @@ public final class LockscreenCardsNotificationIconSourceStore {
new WeakHashMap<>();
private static final WeakHashMap<ViewGroup, ArrayList<View>> AOD_DRAWABLES_BY_CONTAINER =
new WeakHashMap<>();
private static final ArrayList<String> AOD_NOTIFICATION_PACKAGES = new ArrayList<>();
private static final int VIRTUAL_ICON_SIZE_PX = 39;
private LockscreenCardsNotificationIconSourceStore() {
@@ -113,6 +114,7 @@ public final class LockscreenCardsNotificationIconSourceStore {
public static void clearAodSources() {
PACKAGES_BY_CONTAINER.clear();
AOD_DRAWABLES_BY_CONTAINER.clear();
AOD_NOTIFICATION_PACKAGES.clear();
}
public static void clearAodSources(ViewGroup container) {
@@ -123,6 +125,18 @@ public final class LockscreenCardsNotificationIconSourceStore {
AOD_DRAWABLES_BY_CONTAINER.remove(container);
}
public static void putAodNotificationPackages(ArrayList<String> packages) {
AOD_NOTIFICATION_PACKAGES.clear();
if (packages == null || packages.isEmpty()) {
return;
}
for (String pkg : packages) {
if (AppIconRules.isValidPackageName(pkg) && !AOD_NOTIFICATION_PACKAGES.contains(pkg)) {
AOD_NOTIFICATION_PACKAGES.add(pkg);
}
}
}
private static boolean sameViews(ArrayList<View> previous, ArrayList<View> current) {
if (previous == current) {
return true;
@@ -252,6 +266,25 @@ public final class LockscreenCardsNotificationIconSourceStore {
return result;
}
static ArrayList<SnapshotSource> snapshotAodNotificationPackageSources(View root) {
ArrayList<SnapshotSource> result = new ArrayList<>();
if (AOD_NOTIFICATION_PACKAGES.isEmpty()) {
return result;
}
Bounds bounds = sourceContainerBoundsForAodIconsRoot(root);
int top = bounds != null ? bounds.top : 0;
int size = VIRTUAL_ICON_SIZE_PX;
for (int i = 0; i < AOD_NOTIFICATION_PACKAGES.size(); i++) {
String pkg = AOD_NOTIFICATION_PACKAGES.get(i);
result.add(new SnapshotSource(
null,
SnapshotMode.APP_ICON,
pkg + "@aodUnread" + i,
new Bounds(0, top, size, size)));
}
return result;
}
static ViewGroup sourceContainerForRoot(View root) {
ViewGroup container = sourceContainerForRoot(root, AOD_DRAWABLES_BY_CONTAINER.keySet());
if (container != null) {
@@ -284,6 +317,22 @@ public final class LockscreenCardsNotificationIconSourceStore {
return bounds;
}
public static boolean sourceContainerHasHiddenAncestorForRoot(View root) {
ViewGroup container = sourceContainerForRoot(root);
if (container == null) {
return false;
}
View current = container;
while (current != null && current != root) {
if (current.getVisibility() != View.VISIBLE) {
return true;
}
Object parent = current.getParent();
current = parent instanceof View ? (View) parent : null;
}
return current != root;
}
public static Bounds sourceContainerBoundsForAodIconsRoot(View root) {
ViewGroup container = sourceContainerForAodIconsRoot(root);
Bounds bounds = sourceBoundsRelativeTo(root, container);
@@ -294,7 +343,11 @@ public final class LockscreenCardsNotificationIconSourceStore {
}
private static Bounds sourceBoundsRelativeTo(View root, ViewGroup container) {
Bounds bounds = container != null ? ViewGeometry.boundsRelativeTo(root, container) : null;
Bounds bounds = rawSourceBoundsRelativeTo(root, container);
return normalizeSourceBounds(container, bounds);
}
private static Bounds normalizeSourceBounds(ViewGroup container, Bounds bounds) {
if (bounds == null || isZeroSizeDotContainer(container, bounds)) {
return bounds;
}
@@ -308,6 +361,10 @@ public final class LockscreenCardsNotificationIconSourceStore {
return new Bounds(bounds.left, top, bounds.width, VIRTUAL_ICON_SIZE_PX);
}
private static Bounds rawSourceBoundsRelativeTo(View root, ViewGroup container) {
return container != null ? ViewGeometry.boundsRelativeTo(root, container) : null;
}
private static boolean hasAodSources(ViewGroup container) {
return container != null
&& (AOD_DRAWABLES_BY_CONTAINER.containsKey(container)
@@ -460,15 +460,6 @@ final class LayoutPlan {
final ArrayList<SnapshotPlacement> notificationPlacements;
final ClockPlacement carrierPlacement;
LayoutPlan(
ClockPlacement clockPlacement,
ArrayList<SnapshotPlacement> chipPlacements,
ArrayList<SnapshotPlacement> statusPlacements,
ArrayList<SnapshotPlacement> notificationPlacements
) {
this(clockPlacement, chipPlacements, statusPlacements, notificationPlacements, null);
}
LayoutPlan(
ClockPlacement clockPlacement,
ArrayList<SnapshotPlacement> chipPlacements,
@@ -65,13 +65,14 @@ public final class UnlockedIconSnapshotRenderController {
ViewGroup statusHost,
ViewGroup notificationHost,
SceneKey scene,
boolean forceStockRefresh,
TextView externalCarrierView
) {
if (scene == null
|| (!scene.isUnlocked() && !scene.isLockscreen() && !scene.isAod())
|| !root.isAttachedToWindow()) {
clearAll();
return new RenderResult(true, true);
return new RenderResult(true, true, true);
}
SbtSettings settings = RuntimeSettingsCache.get(root.getContext());
layoutHostToRoot(root, clockHost);
@@ -139,7 +140,7 @@ public final class UnlockedIconSnapshotRenderController {
lastLayoutPlanByRoot.put(root, updatedPlan);
lastClockSourceGeometryByRoot.put(root, currentClockSourceGeometry);
clockRenderer.render(clockHost, updatedClock);
return new RenderResult(false, false);
return new RenderResult(false, false, false);
}
}
currentClockLayout = buildClockLayout(root, anchors, settings);
@@ -166,16 +167,16 @@ public final class UnlockedIconSnapshotRenderController {
LayoutPlan updatedPlan = previousPlan.withClockPlacement(updatedClock);
lastLayoutPlanByRoot.put(root, updatedPlan);
clockRenderer.render(clockHost, updatedClock);
return new RenderResult(false, false);
return new RenderResult(false, false, false);
}
}
CollectedIcons collectedIcons = previousIcons;
if (forceChipRefresh && collectedIcons != null) {
collectedIcons = sourceCollector.collectChips(root, anchors, collectedIcons);
lastCollectedIconsByRoot.put(root, collectedIcons);
} else if (sceneChanged || stockSourcesChanged || collectedIcons == null) {
if (forceStockRefresh || sceneChanged || stockSourcesChanged || collectedIcons == null) {
collectedIcons = sourceCollector.collect(root, anchors, scene);
lastCollectedIconsByRoot.put(root, collectedIcons);
} else if (forceChipRefresh && collectedIcons != null) {
collectedIcons = sourceCollector.collectChips(root, anchors, collectedIcons);
lastCollectedIconsByRoot.put(root, collectedIcons);
} else if (chipSourcesChanged) {
collectedIcons = sourceCollector.collectChips(root, anchors, collectedIcons);
lastCollectedIconsByRoot.put(root, collectedIcons);
@@ -993,10 +994,6 @@ public final class UnlockedIconSnapshotRenderController {
boolean chipSourcesChanged,
boolean shouldOwnLockscreen
) {
public RenderResult(boolean layoutChanged, boolean stockSourcesChanged) {
this(layoutChanged, stockSourcesChanged, stockSourcesChanged, true);
}
public RenderResult(boolean layoutChanged, boolean stockSourcesChanged, boolean chipSourcesChanged) {
this(layoutChanged, stockSourcesChanged, chipSourcesChanged, true);
}
@@ -6,21 +6,21 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.Locale;
import se.ajpanton.statusbartweak.runtime.layoutsolver.StatusBarLayoutSolver;
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;
import se.ajpanton.statusbartweak.runtime.layoutsolver.StatusBarLayoutSolver.SplitRegion;
import se.ajpanton.statusbartweak.shell.settings.SbtSettings;
final class UnlockedLayoutBand extends StatusBarLayoutSolver.LayoutBand {
final class UnlockedLayoutBand {
final LayoutItemKind kind;
final LayoutPosition position;
final AnchorSide middleSide;
final SplitRegion splitRegion;
final ViewGroup host;
final SnapshotRenderer renderer;
final ArrayList<SnapshotPlacement> rawPlacements;
final boolean showDotIfTruncated;
final boolean forceOverflowDotOnly;
final Bounds reservedBounds;
final ClockLayout clockLayout;
ArrayList<SnapshotPlacement> placements;
@@ -37,21 +37,19 @@ final class UnlockedLayoutBand extends StatusBarLayoutSolver.LayoutBand {
SnapshotRenderer renderer,
ArrayList<SnapshotPlacement> rawPlacements,
LayoutPosition position,
boolean middleAutoNearestSide,
AnchorSide middleSide,
boolean showDotIfTruncated,
boolean forceOverflowDotOnly,
Bounds reservedBounds,
ClockLayout clockLayout,
SplitRegion splitRegion
) {
super(kind, position, middleAutoNearestSide, middleSide, splitRegion);
this.kind = kind;
this.position = position;
this.middleSide = middleSide;
this.splitRegion = splitRegion != null ? splitRegion : SplitRegion.NONE;
this.host = host;
this.renderer = renderer;
this.rawPlacements = rawPlacements;
this.showDotIfTruncated = showDotIfTruncated;
this.forceOverflowDotOnly = forceOverflowDotOnly;
this.reservedBounds = reservedBounds;
this.clockLayout = clockLayout;
}
@@ -59,7 +57,7 @@ final class UnlockedLayoutBand extends StatusBarLayoutSolver.LayoutBand {
ViewGroup host,
ClockLayout clockLayout,
LayoutPosition position,
SbtSettings settings
AnchorSide middleSide
) {
return new UnlockedLayoutBand(
LayoutItemKind.CLOCK,
@@ -67,11 +65,8 @@ final class UnlockedLayoutBand extends StatusBarLayoutSolver.LayoutBand {
null,
null,
position,
settings.clockMiddleAutoNearestSide,
sideFor(settings.clockMiddleCutoutSide),
middleSide,
false,
false,
null,
clockLayout,
SplitRegion.NONE);
}
@@ -80,7 +75,7 @@ final class UnlockedLayoutBand extends StatusBarLayoutSolver.LayoutBand {
ViewGroup host,
ClockLayout clockLayout,
SplitRegion splitRegion,
SbtSettings settings
AnchorSide middleSide
) {
return new UnlockedLayoutBand(
LayoutItemKind.CLOCK,
@@ -88,11 +83,8 @@ final class UnlockedLayoutBand extends StatusBarLayoutSolver.LayoutBand {
null,
null,
LayoutPosition.MIDDLE,
settings.clockMiddleAutoNearestSide,
sideFor(settings.clockMiddleCutoutSide),
middleSide,
false,
false,
null,
clockLayout,
splitRegion);
}
@@ -101,7 +93,6 @@ final class UnlockedLayoutBand extends StatusBarLayoutSolver.LayoutBand {
ViewGroup host,
ClockLayout carrierLayout,
LayoutPosition position,
boolean middleAutoNearestSide,
AnchorSide middleSide
) {
return new UnlockedLayoutBand(
@@ -110,11 +101,8 @@ final class UnlockedLayoutBand extends StatusBarLayoutSolver.LayoutBand {
null,
null,
position,
middleAutoNearestSide,
middleSide,
false,
false,
null,
carrierLayout,
SplitRegion.NONE);
}
@@ -125,9 +113,7 @@ final class UnlockedLayoutBand extends StatusBarLayoutSolver.LayoutBand {
SnapshotRenderer renderer,
ArrayList<SnapshotPlacement> rawPlacements,
LayoutPosition position,
boolean middleAutoNearestSide,
AnchorSide middleSide,
boolean showDotIfTruncated
AnchorSide middleSide
) {
return new UnlockedLayoutBand(
kind,
@@ -135,12 +121,9 @@ final class UnlockedLayoutBand extends StatusBarLayoutSolver.LayoutBand {
renderer,
rawPlacements,
position,
middleAutoNearestSide,
middleSide,
showDotIfTruncated,
false,
null,
null,
SplitRegion.NONE);
}
@@ -156,7 +139,6 @@ final class UnlockedLayoutBand extends StatusBarLayoutSolver.LayoutBand {
SnapshotRenderer renderer,
ArrayList<SnapshotPlacement> rawPlacements,
LayoutPosition position,
boolean middleAutoNearestSide,
AnchorSide middleSide
) {
return new UnlockedLayoutBand(
@@ -165,20 +147,16 @@ final class UnlockedLayoutBand extends StatusBarLayoutSolver.LayoutBand {
renderer,
rawPlacements,
position,
middleAutoNearestSide,
middleSide,
true,
true,
null,
null,
SplitRegion.NONE);
}
@Override
protected void prepareForSide(AnchorSide side, String sortOrder) {
void prepareForSide(AnchorSide side, String sortOrder) {
if (isTextKind()) {
placements = null;
placedBounds = clockLayout != null ? clockLayout.bounds() : reservedBounds;
placedBounds = clockLayout != null ? clockLayout.bounds() : null;
return;
}
placements = new ArrayList<>(activePlacements());
@@ -189,8 +167,7 @@ final class UnlockedLayoutBand extends StatusBarLayoutSolver.LayoutBand {
placedBounds = null;
}
@Override
protected int width() {
int width() {
return Math.max(0, naturalWidth() - clipStartPx - clipEndPx);
}
@@ -199,19 +176,17 @@ final class UnlockedLayoutBand extends StatusBarLayoutSolver.LayoutBand {
if (clockLayout != null) {
return clockLayout.width();
}
return reservedBounds != null ? reservedBounds.width : 0;
return 0;
}
return sumWidths(activePlacements());
}
@Override
protected boolean canClipContinuously() {
boolean canClipContinuously() {
return (kind == LayoutItemKind.CHIP || isTextKind())
&& naturalWidth() > clipStartPx + clipEndPx;
}
@Override
protected boolean clipContinuous(AnchorSide wallSide, int amountPx) {
boolean clipContinuous(AnchorSide wallSide, int amountPx) {
int remaining = naturalWidth() - clipStartPx - clipEndPx;
if (amountPx <= 0 || remaining <= 0 || !canClipContinuously()) {
return false;
@@ -261,23 +236,12 @@ final class UnlockedLayoutBand extends StatusBarLayoutSolver.LayoutBand {
}
}
@Override
protected int wallDotSlack(AnchorSide side) {
SnapshotPlacement placement = wallPlacement(side);
if (placement == null || !placement.overflowDot) {
return 0;
}
int collisionWidth = relativeCollisionBounds(placement).width;
int dotWidth = overflowDotVisualWidth(placement);
return Math.max(0, (collisionWidth - dotWidth) / 2);
}
int height() {
if (isTextKind()) {
if (clockLayout != null) {
return clockLayout.height();
}
return reservedBounds != null ? reservedBounds.height : 0;
return 0;
}
int height = 0;
for (SnapshotPlacement placement : activePlacements()) {
@@ -291,7 +255,7 @@ final class UnlockedLayoutBand extends StatusBarLayoutSolver.LayoutBand {
if (clockLayout != null) {
return clockLayout.top();
}
return reservedBounds != null ? reservedBounds.top : 0;
return 0;
}
int top = Integer.MAX_VALUE;
for (SnapshotPlacement placement : activePlacements()) {
@@ -300,41 +264,6 @@ final class UnlockedLayoutBand extends StatusBarLayoutSolver.LayoutBand {
return top == Integer.MAX_VALUE ? 0 : top;
}
@Override
protected boolean shrinkOneIcon() {
if (kind == LayoutItemKind.NOTIFICATIONS) {
return shrinkIcon(false);
}
if (kind == LayoutItemKind.STATUS) {
return shrinkIcon(true);
}
return false;
}
@Override
protected boolean removeOverflowDot() {
if (placements == null || placements.isEmpty()) {
return false;
}
ArrayList<SnapshotPlacement> withoutDot = new ArrayList<>();
boolean removed = false;
for (SnapshotPlacement placement : placements) {
if (placement.overflowDot) {
removed = true;
} else {
withoutDot.add(placement);
}
}
if (!removed) {
return false;
}
if (withoutDot.isEmpty()) {
withoutDot.add(emptyIconContainerPlacement());
}
placements = withoutDot;
return true;
}
private void replaceOverflowDot(int width, int height) {
if (placements == null) {
placements = new ArrayList<>();
@@ -354,29 +283,7 @@ final class UnlockedLayoutBand extends StatusBarLayoutSolver.LayoutBand {
placements = withoutDot;
}
@Override
protected Object captureState() {
return new LayoutBandState(placements == null, new ArrayList<>(activePlacements()));
}
@Override
protected void restoreState(Object state) {
if (!(state instanceof LayoutBandState bandState)) {
return;
}
placements = bandState.placementsWereNull ? null : new ArrayList<>(bandState.placements);
}
@Override
protected boolean createdOverflowDotSince(Object state) {
if (!(state instanceof LayoutBandState bandState)) {
return hasOverflowDot(activePlacements());
}
return !hasOverflowDot(bandState.placements) && hasOverflowDot(activePlacements());
}
@Override
protected void place(int left) {
void place(int left) {
if (isTextKind()) {
placedBounds = clockLayout != null
? clockLayout.place(left)
@@ -441,8 +348,7 @@ final class UnlockedLayoutBand extends StatusBarLayoutSolver.LayoutBand {
return union;
}
@Override
protected ArrayList<Bounds> collisionBoxes() {
ArrayList<Bounds> collisionBoxes() {
ArrayList<Bounds> boxes = new ArrayList<>();
if (isTextKind()) {
if (clockLayout != null) {
@@ -455,8 +361,6 @@ final class UnlockedLayoutBand extends StatusBarLayoutSolver.LayoutBand {
boxes.add(new Bounds(left - clipStartPx, box.top, right - left, box.height));
}
}
} else if (reservedBounds != null) {
boxes.add(new Bounds(0, reservedBounds.top, width(), reservedBounds.height));
}
return boxes;
}
@@ -486,47 +390,6 @@ final class UnlockedLayoutBand extends StatusBarLayoutSolver.LayoutBand {
return solverBoxes;
}
@Override
protected ArrayList<Bounds> placedCollisionBounds() {
ArrayList<Bounds> boxes = new ArrayList<>();
int left = isTextKind() && placedBounds != null
? placedBounds.left
: 0;
for (Bounds box : collisionBoxes()) {
boxes.add(new Bounds(left + box.left, box.top, box.width, box.height));
}
if (kind != LayoutItemKind.CLOCK && placedBounds != null) {
boxes.clear();
boxes.add(placedBounds);
}
return boxes;
}
private SnapshotPlacement wallPlacement(AnchorSide side) {
if (isTextKind()) {
return null;
}
ArrayList<SnapshotPlacement> icons = activePlacements();
if (icons.isEmpty()) {
return null;
}
boolean wallAtStart = side == AnchorSide.RIGHT;
int index;
if (wallAtStart) {
index = reverseForPlacement ? icons.size() - 1 : 0;
} else {
index = reverseForPlacement ? 0 : icons.size() - 1;
}
return icons.get(index);
}
private int overflowDotVisualWidth(SnapshotPlacement placement) {
Bounds bounds = placement.bounds;
int base = Math.max(1, Math.min(bounds.width, bounds.height));
int radius = Math.max(1, Math.round(base * 0.125f));
return Math.min(bounds.width, radius * 2);
}
private Bounds collisionBounds(SnapshotPlacement placement) {
Bounds relative = relativeCollisionBounds(placement);
Bounds bounds = placement.bounds;
@@ -547,38 +410,6 @@ final class UnlockedLayoutBand extends StatusBarLayoutSolver.LayoutBand {
return new Bounds(0, 0, bounds.width, bounds.height);
}
private boolean shrinkIcon(boolean fromStart) {
ArrayList<SnapshotPlacement> icons = new ArrayList<>(activePlacements());
int removeIndex = nonDotIndex(icons, fromStart);
if (removeIndex < 0) {
return false;
}
boolean hadDot = hasOverflowDot(icons);
icons.remove(removeIndex);
if (showDotIfTruncated && !hadDot) {
SnapshotPlacement dot = dotPlacement(dotWidth(icons), dotHeight(icons), rowTop(icons));
if (fromStart) {
icons.add(0, dot);
} else {
icons.add(dot);
}
}
placements = icons;
return true;
}
private int nonDotIndex(ArrayList<SnapshotPlacement> icons, boolean fromStart) {
int start = fromStart ? 0 : icons.size() - 1;
int end = fromStart ? icons.size() : -1;
int step = fromStart ? 1 : -1;
for (int i = start; i != end; i += step) {
if (isShrinkableIcon(icons.get(i))) {
return i;
}
}
return -1;
}
private boolean isShrinkableIcon(SnapshotPlacement placement) {
return placement != null
&& !placement.overflowDot
@@ -612,15 +443,6 @@ final class UnlockedLayoutBand extends StatusBarLayoutSolver.LayoutBand {
return result;
}
private boolean hasOverflowDot(ArrayList<SnapshotPlacement> icons) {
for (SnapshotPlacement icon : icons) {
if (icon.overflowDot) {
return true;
}
}
return false;
}
private ArrayList<SnapshotPlacement> activePlacements() {
if (placements != null) {
return placements;
@@ -884,10 +706,4 @@ final class UnlockedLayoutBand extends StatusBarLayoutSolver.LayoutBand {
return kind == LayoutItemKind.CLOCK || kind == LayoutItemKind.CARRIER;
}
private static AnchorSide sideFor(String value) {
return "right".equals(value) ? AnchorSide.RIGHT : AnchorSide.LEFT;
}
private record LayoutBandState(boolean placementsWereNull, ArrayList<SnapshotPlacement> placements) {
}
}
@@ -12,16 +12,15 @@ import java.util.Map;
import java.util.WeakHashMap;
import se.ajpanton.statusbartweak.R;
import se.ajpanton.statusbartweak.runtime.layoutsolver.StatusBarLayoutSolver;
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.SplitRegion;
import se.ajpanton.statusbartweak.runtime.layoutsolver.PackedStatusBarLayoutSolver;
import se.ajpanton.statusbartweak.runtime.mode.NotificationDisplayStyle;
import se.ajpanton.statusbartweak.runtime.mode.SceneKey;
import se.ajpanton.statusbartweak.runtime.notifications.NotificationUnreadTracker;
import se.ajpanton.statusbartweak.shell.settings.SbtSettings;
final class UnlockedLayoutPlanner {
@@ -83,7 +82,7 @@ final class UnlockedLayoutPlanner {
? aodStatusContentBounds(root, aodStatusAnchorBounds)
: null;
Bounds aodNotificationBounds = scene != null && scene.isAod()
? aodNotificationBounds(root, anchors, scene)
? aodNotificationBounds(root, scene)
: null;
Bounds aodStatusbarBounds = scene != null && scene.isAod()
? aodStatusbarBounds(root, settings, aodNotificationBounds, aodStatusAnchorBounds)
@@ -94,7 +93,7 @@ final class UnlockedLayoutPlanner {
clockHost,
clockLayout,
positionFor(settings.clockPosition),
settings));
sideFor(settings.clockMiddleCutoutSide)));
}
ClockLayout carrierLayout = carrierLayout(root, collectedIcons, settings, scene);
if (carrierLayout != null && carrierLayout.width() > 0) {
@@ -102,7 +101,6 @@ final class UnlockedLayoutPlanner {
carrierHost,
carrierLayout,
positionFor(settings.layoutCarrierPosition),
settings.layoutCarrierMiddleAutoNearestSide,
sideFor(settings.layoutCarrierMiddleSide)));
}
int statusCount = statusCountForScene(settings, scene);
@@ -123,7 +121,6 @@ final class UnlockedLayoutPlanner {
iconHeightPx(statusbarHeight, statusIconHeightSteps(settings, scene)));
String[] positions = statusPositionsForScene(settings, scene);
String[] middleSides = statusMiddleSidesForScene(settings, scene);
boolean[] autoNearest = statusAutoNearestForScene(settings, scene);
int[] verticalOffsets = statusVerticalOffsetsForScene(settings, scene);
int statusHeightSteps = statusIconHeightSteps(settings, scene);
for (int i = 0; i < statusCount; i++) {
@@ -148,12 +145,7 @@ final class UnlockedLayoutPlanner {
statusRenderer,
placements,
positionFor(stringAt(positions, i, settings.layoutStatusPosition)),
booleanAt(
autoNearest,
i,
settings.layoutStatusMiddleAutoNearestSide),
sideFor(stringAt(middleSides, i, settings.layoutStatusMiddleSide)),
true)
sideFor(stringAt(middleSides, i, settings.layoutStatusMiddleSide)))
.withSolverCollision(
logicalRowTop(statusHeightSteps, verticalOffsetSteps),
statusHeightSteps));
@@ -168,15 +160,19 @@ final class UnlockedLayoutPlanner {
notificationSeenAppReporter.report(
root != null ? root.getContext() : null,
unfilteredPlacements);
ArrayList<SnapshotPlacement> filteredPlacements =
SnapshotIconFilter.notificationPlacements(unfilteredPlacements, settings, scene);
if (isDotNotificationScene(scene)) {
filteredPlacements = unreadNotificationPlacements(filteredPlacements);
}
ArrayList<SnapshotPlacement> rawPlacements = resizePlacementsToHeight(
SnapshotIconFilter.notificationPlacements(unfilteredPlacements, settings, scene),
filteredPlacements,
iconHeightPx(statusbarHeight, notificationIconHeightSteps(settings, scene)));
notificationRowBounds = placementBounds(rawPlacements);
boolean dotNotificationScene = isDotNotificationScene(scene);
notificationCount = dotNotificationScene && !rawPlacements.isEmpty() ? 1 : notificationCount;
String[] positions = notificationPositionsForScene(settings, scene);
String[] middleSides = notificationMiddleSidesForScene(settings, scene);
boolean[] autoNearest = notificationAutoNearestForScene(settings, scene);
int[] verticalOffsets = notificationVerticalOffsetsForScene(settings, scene);
int notificationHeightSteps = notificationIconHeightSteps(settings, scene);
for (int i = 0; i < notificationCount; i++) {
@@ -195,10 +191,6 @@ final class UnlockedLayoutPlanner {
stepsToPx(verticalOffsetSteps, statusbarHeight));
if (!placements.isEmpty()) {
LayoutPosition position = positionFor(stringAt(positions, i, settings.layoutNotifPosition));
boolean middleAutoNearest = booleanAt(
autoNearest,
i,
settings.layoutNotifMiddleAutoNearestSide);
AnchorSide middleSide = sideFor(stringAt(middleSides, i, settings.layoutNotifMiddleSide));
bands.add(dotNotificationScene
? UnlockedLayoutBand.dotIcons(
@@ -207,7 +199,6 @@ final class UnlockedLayoutPlanner {
notificationRenderer,
placements,
position,
middleAutoNearest,
middleSide)
.withSolverCollision(
logicalRowTop(notificationHeightSteps, verticalOffsetSteps),
@@ -218,9 +209,7 @@ final class UnlockedLayoutPlanner {
notificationRenderer,
placements,
position,
middleAutoNearest,
middleSide,
true)
middleSide)
.withSolverCollision(
logicalRowTop(notificationHeightSteps, verticalOffsetSteps),
notificationHeightSteps));
@@ -254,9 +243,7 @@ final class UnlockedLayoutPlanner {
chipRenderer,
placements,
positionFor(settings.layoutChipPosition),
settings.layoutChipMiddleAutoNearestSide,
sideFor(settings.layoutChipMiddleSide),
false)
sideFor(settings.layoutChipMiddleSide))
.withSolverCollision(
logicalRowTop(
settings.layoutChipHeightSteps,
@@ -265,7 +252,7 @@ final class UnlockedLayoutPlanner {
}
}
if (bands.isEmpty()) {
return new LayoutPlan(null, new ArrayList<>(), new ArrayList<>(), new ArrayList<>());
return new LayoutPlan(null, new ArrayList<>(), new ArrayList<>(), new ArrayList<>(), null);
}
ArrayList<LayoutItemKind> itemOrder = orderedKinds(settings.layoutItemOrder);
splitClockIfNeeded(root, bands, itemOrder, clockCutoutBounds, settings);
@@ -540,10 +527,6 @@ final class UnlockedLayoutPlanner {
: 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;
}
@@ -726,6 +709,21 @@ final class UnlockedLayoutPlanner {
&& scene.notificationDisplayStyle() == NotificationDisplayStyle.DOT;
}
private ArrayList<SnapshotPlacement> unreadNotificationPlacements(
ArrayList<SnapshotPlacement> placements
) {
if (placements == null || placements.isEmpty()) {
return placements != null ? placements : new ArrayList<>();
}
ArrayList<SnapshotPlacement> unread = new ArrayList<>(placements.size());
for (SnapshotPlacement placement : placements) {
if (placement != null && NotificationUnreadTracker.isUnread(placement.key)) {
unread.add(placement);
}
}
return unread;
}
private String[] statusPositionsForScene(SbtSettings settings, SceneKey scene) {
if (scene != null && scene.isLockscreen()) {
return settings.layoutStatusLockPositions;
@@ -746,16 +744,6 @@ final class UnlockedLayoutPlanner {
return settings.layoutStatusMiddleSides;
}
private boolean[] statusAutoNearestForScene(SbtSettings settings, SceneKey scene) {
if (scene != null && scene.isLockscreen()) {
return settings.layoutStatusLockMiddleAutoNearestSides;
}
if (scene != null && scene.isAod()) {
return settings.layoutStatusAodMiddleAutoNearestSides;
}
return settings.layoutStatusMiddleAutoNearestSides;
}
private int[] statusVerticalOffsetsForScene(SbtSettings settings, SceneKey scene) {
if (scene != null && scene.isLockscreen()) {
return settings.layoutStatusLockVerticalOffsetsPx;
@@ -786,16 +774,6 @@ final class UnlockedLayoutPlanner {
return settings.layoutNotifMiddleSides;
}
private boolean[] notificationAutoNearestForScene(SbtSettings settings, SceneKey scene) {
if (scene != null && scene.isLockscreen()) {
return settings.layoutNotifLockMiddleAutoNearestSides;
}
if (scene != null && scene.isAod()) {
return settings.layoutNotifAodMiddleAutoNearestSides;
}
return settings.layoutNotifMiddleAutoNearestSides;
}
private int[] notificationVerticalOffsetsForScene(SbtSettings settings, SceneKey scene) {
if (scene != null && scene.isLockscreen()) {
return settings.layoutNotifLockVerticalOffsetsPx;
@@ -1119,21 +1097,11 @@ final class UnlockedLayoutPlanner {
private ArrayList<SnapshotPlacement> bottomAlignAodPlacements(
ArrayList<SnapshotPlacement> placements,
Bounds aodStatusbarBounds
) {
return bottomAlignAodPlacements(placements, aodStatusbarBounds, 0);
}
private ArrayList<SnapshotPlacement> bottomAlignAodPlacements(
ArrayList<SnapshotPlacement> placements,
Bounds aodStatusbarBounds,
int bottomInsetPx
) {
if (placements == null || placements.isEmpty() || aodStatusbarBounds == null) {
return placements;
}
int bottom = aodStatusbarBounds.top
+ aodStatusbarBounds.height
- Math.max(0, bottomInsetPx);
int bottom = aodStatusbarBounds.top + aodStatusbarBounds.height;
ArrayList<SnapshotPlacement> result = new ArrayList<>();
for (SnapshotPlacement placement : placements) {
Bounds bounds = placement.bounds;
@@ -1198,7 +1166,7 @@ final class UnlockedLayoutPlanner {
if (root == null || cutoutBounds == null || bands == null || settings == null) {
return;
}
ArrayList<UnlockedLayoutBand> middleBands = StatusBarLayoutSolver.bandsForPosition(
ArrayList<UnlockedLayoutBand> middleBands = bandsForPosition(
bands,
LayoutPosition.MIDDLE,
itemOrder);
@@ -1215,7 +1183,7 @@ final class UnlockedLayoutPlanner {
root.getWidth(),
cutoutBounds,
settings.clockMiddleAutoNearestSide,
sideFor(settings.clockMiddleCutoutSide));
clockBand.middleSide);
if (splitLayouts.size() < 2) {
return;
}
@@ -1224,8 +1192,34 @@ final class UnlockedLayoutPlanner {
return;
}
bands.remove(index);
bands.add(index, UnlockedLayoutBand.fixedClock(clockBand.host, splitLayouts.get(1), SplitRegion.RIGHT, settings));
bands.add(index, UnlockedLayoutBand.fixedClock(clockBand.host, splitLayouts.get(0), SplitRegion.LEFT, settings));
bands.add(index, UnlockedLayoutBand.fixedClock(
clockBand.host,
splitLayouts.get(1),
SplitRegion.RIGHT,
clockBand.middleSide));
bands.add(index, UnlockedLayoutBand.fixedClock(
clockBand.host,
splitLayouts.get(0),
SplitRegion.LEFT,
clockBand.middleSide));
}
private ArrayList<UnlockedLayoutBand> bandsForPosition(
ArrayList<UnlockedLayoutBand> bands,
LayoutPosition position,
ArrayList<LayoutItemKind> itemOrder
) {
ArrayList<UnlockedLayoutBand> result = new ArrayList<>();
for (LayoutItemKind kind : itemOrder) {
for (UnlockedLayoutBand band : bands) {
if (band.kind == kind
&& band.position == position
&& band.splitRegion == SplitRegion.NONE) {
result.add(band);
}
}
}
return result;
}
private LayoutEdges layoutEdges(
@@ -1240,7 +1234,6 @@ final class UnlockedLayoutPlanner {
LayoutEdges aodEdges = aodBurnInEdges(
root,
settings,
anchors,
scene,
rootWidth,
aodStatusAnchorBounds);
@@ -1295,7 +1288,6 @@ final class UnlockedLayoutPlanner {
private LayoutEdges aodBurnInEdges(
View root,
SbtSettings settings,
RootAnchors anchors,
SceneKey scene,
int rootWidth,
Bounds statusBounds
@@ -1303,7 +1295,7 @@ final class UnlockedLayoutPlanner {
if (root == null || settings == null || rootWidth <= 0) {
return null;
}
Bounds notificationBounds = aodNotificationBounds(root, anchors, scene);
Bounds notificationBounds = aodNotificationBounds(root, scene);
Bounds bounds = aodStatusbarBounds(root, settings, notificationBounds, statusBounds);
if (bounds == null) {
return null;
@@ -1350,7 +1342,7 @@ final class UnlockedLayoutPlanner {
if (root == null || root.getWidth() <= 0) {
return null;
}
Bounds notificationBounds = aodNotificationBounds(root, anchors, scene);
Bounds notificationBounds = aodNotificationBounds(root, scene);
Bounds liveMovingStatusBounds = AodLiveStatusbarSource.movingBoundsForRoot(root);
Bounds storedStatusBounds = AodStatusbarSourceStore.statusBoundsForRoot(
root,
@@ -1404,7 +1396,7 @@ final class UnlockedLayoutPlanner {
liveMovingStatusBounds.height);
}
private Bounds aodNotificationBounds(View root, RootAnchors anchors, SceneKey scene) {
private Bounds aodNotificationBounds(View root, SceneKey scene) {
if (scene != null
&& scene.notificationDisplayStyle() == NotificationDisplayStyle.CARDS) {
return null;
@@ -1626,6 +1618,9 @@ final class UnlockedLayoutPlanner {
return null;
}
private record LayoutEdges(int left, int right) {
}
private record StockInsets(int left, int right) {
}
}
@@ -15,11 +15,14 @@ import java.util.Locale;
import java.util.Objects;
import java.util.WeakHashMap;
import se.ajpanton.statusbartweak.runtime.NotificationKeyReflection;
import se.ajpanton.statusbartweak.runtime.ViewIdNames;
import se.ajpanton.statusbartweak.runtime.hooks.ReflectionSupport;
import se.ajpanton.statusbartweak.runtime.layoutsolver.StatusBarLayoutSolver.Bounds;
import se.ajpanton.statusbartweak.runtime.mode.NotificationDisplayStyle;
import se.ajpanton.statusbartweak.runtime.mode.SceneKey;
import se.ajpanton.statusbartweak.runtime.notifications.NotificationActiveKeyStore;
import se.ajpanton.statusbartweak.runtime.notifications.NotificationUnreadTracker;
import se.ajpanton.statusbartweak.shell.settings.SbtSettings;
final class UnlockedStockSourceCollector {
@@ -76,6 +79,12 @@ final class UnlockedStockSourceCollector {
|| scene.notificationDisplayStyle() == NotificationDisplayStyle.DOT) {
notificationIcons.addAll(
LockscreenCardsNotificationIconSourceStore.snapshotSourcesForAodIconsRoot(root));
if (notificationIcons.isEmpty()
&& scene.notificationDisplayStyle() == NotificationDisplayStyle.DOT) {
notificationIcons.addAll(
LockscreenCardsNotificationIconSourceStore
.snapshotAodNotificationPackageSources(root));
}
}
} else if (anchors.notificationRoot != null) {
collectNotificationSnapshotViews(anchors.notificationRoot, notificationIcons);
@@ -178,6 +187,16 @@ final class UnlockedStockSourceCollector {
statusSourceSignature = 31 * statusSourceSignature
+ aodLayoutAnchorSignature(root, anchors, scene);
}
int notificationSourceSignature = sourceLayoutListSignature(
previousIcons != null ? previousIcons.notificationIcons : null);
notificationSourceSignature = 31 * notificationSourceSignature
+ NotificationActiveKeyStore.generation();
if (scene != null
&& (scene.isAod() || scene.isLockscreen())
&& scene.notificationDisplayStyle() == NotificationDisplayStyle.DOT) {
notificationSourceSignature = 31 * notificationSourceSignature
+ NotificationUnreadTracker.generation();
}
return new LayoutInputSignature(
scene != null ? scene.toString() : "",
root.getWidth(),
@@ -188,7 +207,7 @@ final class UnlockedStockSourceCollector {
cutout != null ? cutout.top : Integer.MIN_VALUE,
cutout != null ? cutout.right : Integer.MIN_VALUE,
cutout != null ? cutout.bottom : Integer.MIN_VALUE,
loggingAwareSettingsSignature(root, settings),
layoutSettingsSignature(settings),
clockTimeSignature(settings),
viewSignatureOrZero(anchors != null ? anchors.clockView : null),
shallowTreeSignatureExcluding(anchors != null ? anchors.clockContainer : null, null),
@@ -207,7 +226,7 @@ final class UnlockedStockSourceCollector {
notificationRootSignature(anchors != null ? anchors.notificationRoot : null),
viewSignatureOrZero(anchors != null ? anchors.carrierView : null),
statusSourceSignature,
sourceLayoutListSignature(previousIcons != null ? previousIcons.notificationIcons : null),
notificationSourceSignature,
chipSourceSignature);
}
@@ -682,7 +701,13 @@ final class UnlockedStockSourceCollector {
private void collectNotificationSnapshotViews(View source, ArrayList<SnapshotSource> out) {
if (isNotificationSnapshotCandidate(source)) {
out.add(new SnapshotSource(source, SnapshotMode.NOTIFICATION_DRAWABLE));
String key = NotificationKeyReflection.keyForIconView(source);
if (key != null
&& NotificationActiveKeyStore.hasSnapshot()
&& !NotificationActiveKeyStore.isActive(key)) {
return;
}
out.add(new SnapshotSource(source, SnapshotMode.NOTIFICATION_DRAWABLE, key));
return;
}
if (source instanceof ViewGroup group) {
@@ -967,22 +992,7 @@ final class UnlockedStockSourceCollector {
if (view == null) {
return 0;
}
int result = 17;
result = 31 * result + System.identityHashCode(view);
result = 31 * result + view.getClass().getName().hashCode();
result = 31 * result + view.getId();
result = 31 * result + view.getVisibility();
if (view instanceof ViewGroup group) {
result = 31 * result + group.getChildCount();
for (int i = 0; i < group.getChildCount(); i++) {
View child = group.getChildAt(i);
result = 31 * result + System.identityHashCode(child);
result = 31 * result + child.getClass().getName().hashCode();
result = 31 * result + child.getId();
result = 31 * result + child.getVisibility();
}
}
return result;
return sourceLayoutSignature(view);
}
private static int sourceLayoutListSignature(ArrayList<SnapshotSource> sources) {
@@ -1007,6 +1017,7 @@ final class UnlockedStockSourceCollector {
int result = 17;
result = 31 * result + System.identityHashCode(view);
result = 31 * result + view.getClass().getName().hashCode();
result = 31 * result + (view.isAttachedToWindow() ? 1 : 0);
result = 31 * result + view.getId();
result = 31 * result + view.getVisibility();
result = 31 * result + width;
@@ -1207,12 +1218,10 @@ final class UnlockedStockSourceCollector {
settings.layoutCarrierPosition,
settings.layoutCarrierEnabledLockscreen,
settings.layoutCarrierMiddleSide,
settings.layoutCarrierMiddleAutoNearestSide,
settings.layoutCarrierVerticalOffsetPx,
settings.layoutChipPosition,
settings.layoutChipEnabledUnlocked,
settings.layoutChipMiddleSide,
settings.layoutChipMiddleAutoNearestSide,
settings.layoutChipVerticalOffsetPx,
settings.layoutChipHeightSteps,
settings.layoutNotifPosition,
@@ -1221,44 +1230,36 @@ final class UnlockedStockSourceCollector {
settings.layoutNotifUnlockedCount,
Arrays.hashCode(settings.layoutNotifPositions),
Arrays.hashCode(settings.layoutNotifMiddleSides),
Arrays.hashCode(settings.layoutNotifMiddleAutoNearestSides),
Arrays.hashCode(settings.layoutNotifVerticalOffsetsPx),
settings.layoutNotifEnabledLockscreen,
settings.layoutNotifLockCount,
Arrays.hashCode(settings.layoutNotifLockPositions),
Arrays.hashCode(settings.layoutNotifLockMiddleSides),
Arrays.hashCode(settings.layoutNotifLockMiddleAutoNearestSides),
Arrays.hashCode(settings.layoutNotifLockVerticalOffsetsPx),
settings.layoutNotifEnabledAod,
settings.layoutNotifAodCount,
Arrays.hashCode(settings.layoutNotifAodPositions),
Arrays.hashCode(settings.layoutNotifAodMiddleSides),
Arrays.hashCode(settings.layoutNotifAodMiddleAutoNearestSides),
Arrays.hashCode(settings.layoutNotifAodVerticalOffsetsPx),
settings.layoutNotifMiddleSide,
settings.layoutNotifMiddleAutoNearestSide,
settings.layoutNotifVerticalOffsetPx,
settings.layoutStatusPosition,
settings.layoutStatusEnabledUnlocked,
settings.layoutStatusUnlockedCount,
Arrays.hashCode(settings.layoutStatusPositions),
Arrays.hashCode(settings.layoutStatusMiddleSides),
Arrays.hashCode(settings.layoutStatusMiddleAutoNearestSides),
Arrays.hashCode(settings.layoutStatusVerticalOffsetsPx),
settings.layoutStatusEnabledLockscreen,
settings.layoutStatusLockCount,
Arrays.hashCode(settings.layoutStatusLockPositions),
Arrays.hashCode(settings.layoutStatusLockMiddleSides),
Arrays.hashCode(settings.layoutStatusLockMiddleAutoNearestSides),
Arrays.hashCode(settings.layoutStatusLockVerticalOffsetsPx),
settings.layoutStatusEnabledAod,
settings.layoutStatusAodCount,
Arrays.hashCode(settings.layoutStatusAodPositions),
Arrays.hashCode(settings.layoutStatusAodMiddleSides),
Arrays.hashCode(settings.layoutStatusAodMiddleAutoNearestSides),
Arrays.hashCode(settings.layoutStatusAodVerticalOffsetsPx),
settings.layoutStatusMiddleSide,
settings.layoutStatusMiddleAutoNearestSide,
settings.layoutStatusVerticalOffsetPx,
settings.systemIconBlockedModes,
settings.appIconBlockedModes,
@@ -1269,11 +1270,4 @@ final class UnlockedStockSourceCollector {
settings.statusChipsHideCallUnlocked);
}
private int loggingAwareSettingsSignature(View root, SbtSettings settings) {
int result = layoutSettingsSignature(settings);
boolean logsEnabled = root != null
&& root.getContext() != null
&& SbtSettings.isDebugLogWritingEnabled(root.getContext());
return 31 * result + (logsEnabled ? 1 : 0);
}
}
@@ -107,24 +107,19 @@ public final class SbtDefaults {
public static final String LAYOUT_CARRIER_POSITION_DEFAULT = "left";
public static final boolean LAYOUT_CARRIER_ENABLED_LOCKSCREEN_DEFAULT = true;
public static final String LAYOUT_CARRIER_MIDDLE_SIDE_DEFAULT = "left";
public static final boolean LAYOUT_CARRIER_MIDDLE_AUTO_NEAREST_SIDE_DEFAULT = true;
public static final int LAYOUT_CARRIER_VERTICAL_OFFSET_PX_DEFAULT = 0;
public static final String LAYOUT_CHIP_POSITION_DEFAULT = "left";
public static final boolean LAYOUT_CHIP_ENABLED_LOCKSCREEN_DEFAULT = true;
public static final boolean LAYOUT_CHIP_ENABLED_UNLOCKED_DEFAULT = true;
public static final String LAYOUT_CHIP_MIDDLE_SIDE_DEFAULT = "left";
public static final boolean LAYOUT_CHIP_MIDDLE_AUTO_NEAREST_SIDE_DEFAULT = true;
public static final int LAYOUT_CHIP_VERTICAL_OFFSET_PX_DEFAULT = 0;
public static final int LAYOUT_CHIP_HEIGHT_STEPS_DEFAULT = 52;
public static final String LAYOUT_NOTIF_POSITION_DEFAULT = "left";
public static final String LAYOUT_NOTIF_SORT_ORDER_DEFAULT = "automatic";
public static final boolean LAYOUT_NOTIF_SHOW_DOT_IF_TRUNCATED_DEFAULT = true;
public static final boolean LAYOUT_NOTIF_ENABLED_AOD_DEFAULT = true;
public static final boolean LAYOUT_NOTIF_ENABLED_LOCKSCREEN_DEFAULT = true;
public static final boolean LAYOUT_NOTIF_ENABLED_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;
public static final int LAYOUT_NOTIF_ICON_HEIGHT_STEPS_DEFAULT = 46;
public static final int LAYOUT_NOTIF_LOCK_ICON_HEIGHT_STEPS_DEFAULT = 46;
@@ -136,9 +131,7 @@ public final class SbtDefaults {
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_STATUS_ICON_HEIGHT_STEPS_DEFAULT = 38;
public static final int LAYOUT_ICON_HEIGHT_STEPS_MIN = 10;
@@ -103,18 +103,14 @@ public final class SbtSettings {
public static final String KEY_LAYOUT_CARRIER_POSITION = "layout_carrier_position";
public static final String KEY_LAYOUT_CARRIER_ENABLED_LOCKSCREEN = "layout_carrier_enabled_lockscreen";
public static final String KEY_LAYOUT_CARRIER_MIDDLE_SIDE = "layout_carrier_middle_side";
public static final String KEY_LAYOUT_CARRIER_MIDDLE_AUTO_NEAREST_SIDE = "layout_carrier_middle_auto_nearest_side";
public static final String KEY_LAYOUT_CARRIER_VERTICAL_OFFSET_PX = "layout_carrier_vertical_offset_px";
public static final String KEY_LAYOUT_CHIP_POSITION = "layout_chip_position";
public static final String KEY_LAYOUT_CHIP_ENABLED_LOCKSCREEN = "layout_chip_enabled_lockscreen";
public static final String KEY_LAYOUT_CHIP_ENABLED_UNLOCKED = "layout_chip_enabled_unlocked";
public static final String KEY_LAYOUT_CHIP_MIDDLE_SIDE = "layout_chip_middle_side";
public static final String KEY_LAYOUT_CHIP_MIDDLE_AUTO_NEAREST_SIDE = "layout_chip_middle_auto_nearest_side";
public static final String KEY_LAYOUT_CHIP_VERTICAL_OFFSET_PX = "layout_chip_vertical_offset_px";
public static final String KEY_LAYOUT_CHIP_HEIGHT_STEPS = "layout_chip_height_steps";
public static final String KEY_LAYOUT_NOTIF_POSITION = "layout_notif_position";
public static final String KEY_LAYOUT_NOTIF_SORT_ORDER = "layout_notif_sort_order";
public static final String KEY_LAYOUT_NOTIF_SHOW_DOT_IF_TRUNCATED = "layout_notif_show_dot_if_truncated";
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";
@@ -123,22 +119,17 @@ public final class SbtSettings {
public static final String KEY_LAYOUT_NOTIF_AOD_COUNT = "layout_aod_notifications_count";
public static final String KEY_LAYOUT_NOTIF_LOCK_POSITION = "layout_lock_notifications_position";
public static final String KEY_LAYOUT_NOTIF_LOCK_MIDDLE_SIDE = "layout_lock_notifications_middle_side";
public static final String KEY_LAYOUT_NOTIF_LOCK_MIDDLE_AUTO_NEAREST_SIDE =
"layout_lock_notifications_middle_auto_nearest_side";
public static final String KEY_LAYOUT_NOTIF_LOCK_VERTICAL_OFFSET_PX =
"layout_lock_notifications_vertical_offset_px";
public static final String KEY_LAYOUT_NOTIF_LOCK_ICON_HEIGHT_STEPS =
"layout_lock_notifications_icon_height_steps";
public static final String KEY_LAYOUT_NOTIF_AOD_POSITION = "layout_aod_notifications_position";
public static final String KEY_LAYOUT_NOTIF_AOD_MIDDLE_SIDE = "layout_aod_notifications_middle_side";
public static final String KEY_LAYOUT_NOTIF_AOD_MIDDLE_AUTO_NEAREST_SIDE =
"layout_aod_notifications_middle_auto_nearest_side";
public static final String KEY_LAYOUT_NOTIF_AOD_VERTICAL_OFFSET_PX =
"layout_aod_notifications_vertical_offset_px";
public static final String KEY_LAYOUT_NOTIF_AOD_ICON_HEIGHT_STEPS =
"layout_aod_notifications_icon_height_steps";
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";
public static final String KEY_LAYOUT_NOTIF_ICON_HEIGHT_STEPS = "layout_notif_icon_height_steps";
public static final String KEY_LAYOUT_STATUS_POSITION = "layout_status_position";
@@ -150,23 +141,17 @@ public final class SbtSettings {
public static final String KEY_LAYOUT_STATUS_AOD_COUNT = "layout_aod_status_count";
public static final String KEY_LAYOUT_STATUS_LOCK_POSITION = "layout_lock_status_position";
public static final String KEY_LAYOUT_STATUS_LOCK_MIDDLE_SIDE = "layout_lock_status_middle_side";
public static final String KEY_LAYOUT_STATUS_LOCK_MIDDLE_AUTO_NEAREST_SIDE =
"layout_lock_status_middle_auto_nearest_side";
public static final String KEY_LAYOUT_STATUS_LOCK_VERTICAL_OFFSET_PX =
"layout_lock_status_vertical_offset_px";
public static final String KEY_LAYOUT_STATUS_LOCK_ICON_HEIGHT_STEPS =
"layout_lock_status_icon_height_steps";
public static final String KEY_LAYOUT_STATUS_AOD_POSITION = "layout_aod_status_position";
public static final String KEY_LAYOUT_STATUS_AOD_MIDDLE_SIDE = "layout_aod_status_middle_side";
public static final String KEY_LAYOUT_STATUS_AOD_MIDDLE_AUTO_NEAREST_SIDE =
"layout_aod_status_middle_auto_nearest_side";
public static final String KEY_LAYOUT_STATUS_AOD_VERTICAL_OFFSET_PX =
"layout_aod_status_vertical_offset_px";
public static final String KEY_LAYOUT_STATUS_AOD_ICON_HEIGHT_STEPS =
"layout_aod_status_icon_height_steps";
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";
public static final String KEY_LAYOUT_STATUS_VERTICAL_OFFSET_PX = "layout_status_vertical_offset_px";
public static final String KEY_LAYOUT_STATUS_ICON_HEIGHT_STEPS = "layout_status_icon_height_steps";
public static final String KEY_SYSTEM_ICON_BLOCKED_MODES = SystemIconRules.PREF_KEY_BLOCKED_MODES;
@@ -182,10 +167,6 @@ public final class SbtSettings {
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);
}
@@ -198,10 +179,6 @@ public final class SbtSettings {
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);
}
@@ -214,10 +191,6 @@ public final class SbtSettings {
return suffixedKey(KEY_LAYOUT_NOTIF_LOCK_MIDDLE_SIDE, index);
}
public static String layoutLockNotifMiddleAutoNearestSideKey(int index) {
return suffixedKey(KEY_LAYOUT_NOTIF_LOCK_MIDDLE_AUTO_NEAREST_SIDE, index);
}
public static String layoutLockNotifVerticalOffsetKey(int index) {
return suffixedKey(KEY_LAYOUT_NOTIF_LOCK_VERTICAL_OFFSET_PX, index);
}
@@ -234,10 +207,6 @@ public final class SbtSettings {
return suffixedKey(KEY_LAYOUT_NOTIF_AOD_MIDDLE_SIDE, index);
}
public static String layoutAodNotifMiddleAutoNearestSideKey(int index) {
return suffixedKey(KEY_LAYOUT_NOTIF_AOD_MIDDLE_AUTO_NEAREST_SIDE, index);
}
public static String layoutAodNotifVerticalOffsetKey(int index) {
return suffixedKey(KEY_LAYOUT_NOTIF_AOD_VERTICAL_OFFSET_PX, index);
}
@@ -254,10 +223,6 @@ public final class SbtSettings {
return suffixedKey(KEY_LAYOUT_STATUS_LOCK_MIDDLE_SIDE, index);
}
public static String layoutLockStatusMiddleAutoNearestSideKey(int index) {
return suffixedKey(KEY_LAYOUT_STATUS_LOCK_MIDDLE_AUTO_NEAREST_SIDE, index);
}
public static String layoutLockStatusVerticalOffsetKey(int index) {
return suffixedKey(KEY_LAYOUT_STATUS_LOCK_VERTICAL_OFFSET_PX, index);
}
@@ -274,10 +239,6 @@ public final class SbtSettings {
return suffixedKey(KEY_LAYOUT_STATUS_AOD_MIDDLE_SIDE, index);
}
public static String layoutAodStatusMiddleAutoNearestSideKey(int index) {
return suffixedKey(KEY_LAYOUT_STATUS_AOD_MIDDLE_AUTO_NEAREST_SIDE, index);
}
public static String layoutAodStatusVerticalOffsetKey(int index) {
return suffixedKey(KEY_LAYOUT_STATUS_AOD_VERTICAL_OFFSET_PX, index);
}
@@ -350,38 +311,30 @@ public final class SbtSettings {
public final String layoutCarrierPosition;
public final boolean layoutCarrierEnabledLockscreen;
public final String layoutCarrierMiddleSide;
public final boolean layoutCarrierMiddleAutoNearestSide;
public final int layoutCarrierVerticalOffsetPx;
public final String layoutChipPosition;
public final boolean layoutChipEnabledLockscreen;
public final boolean layoutChipEnabledUnlocked;
public final String layoutChipMiddleSide;
public final boolean layoutChipMiddleAutoNearestSide;
public final int layoutChipVerticalOffsetPx;
public final int layoutChipHeightSteps;
public final String layoutNotifPosition;
public final String layoutNotifSortOrder;
public final boolean layoutNotifShowDotIfTruncated;
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 int layoutNotifLockCount;
public final String[] layoutNotifLockPositions;
public final String[] layoutNotifLockMiddleSides;
public final boolean[] layoutNotifLockMiddleAutoNearestSides;
public final int[] layoutNotifLockVerticalOffsetsPx;
public final int layoutNotifAodCount;
public final String[] layoutNotifAodPositions;
public final String[] layoutNotifAodMiddleSides;
public final boolean[] layoutNotifAodMiddleAutoNearestSides;
public final int[] layoutNotifAodVerticalOffsetsPx;
public final String layoutNotifMiddleSide;
public final boolean layoutNotifMiddleAutoNearestSide;
public final int layoutNotifVerticalOffsetPx;
public final int layoutNotifIconHeightSteps;
public final int layoutNotifLockIconHeightSteps;
@@ -393,21 +346,16 @@ public final class SbtSettings {
public final int layoutStatusUnlockedCount;
public final String[] layoutStatusPositions;
public final String[] layoutStatusMiddleSides;
public final boolean[] layoutStatusMiddleAutoNearestSides;
public final int[] layoutStatusVerticalOffsetsPx;
public final int layoutStatusLockCount;
public final String[] layoutStatusLockPositions;
public final String[] layoutStatusLockMiddleSides;
public final boolean[] layoutStatusLockMiddleAutoNearestSides;
public final int[] layoutStatusLockVerticalOffsetsPx;
public final int layoutStatusAodCount;
public final String[] layoutStatusAodPositions;
public final String[] layoutStatusAodMiddleSides;
public final boolean[] layoutStatusAodMiddleAutoNearestSides;
public final int[] layoutStatusAodVerticalOffsetsPx;
public final boolean layoutStatusShowDotIfTruncated;
public final String layoutStatusMiddleSide;
public final boolean layoutStatusMiddleAutoNearestSide;
public final int layoutStatusVerticalOffsetPx;
public final int layoutStatusIconHeightSteps;
public final int layoutStatusLockIconHeightSteps;
@@ -485,23 +433,18 @@ public final class SbtSettings {
String layoutCarrierPosition,
boolean layoutCarrierEnabledLockscreen,
String layoutCarrierMiddleSide,
boolean layoutCarrierMiddleAutoNearestSide,
int layoutCarrierVerticalOffsetPx,
String layoutChipPosition,
boolean layoutChipEnabledLockscreen,
boolean layoutChipEnabledUnlocked,
String layoutChipMiddleSide,
boolean layoutChipMiddleAutoNearestSide,
int layoutChipVerticalOffsetPx,
int layoutChipHeightSteps,
String layoutNotifPosition,
String layoutNotifSortOrder,
boolean layoutNotifShowDotIfTruncated,
boolean layoutNotifEnabledAod,
boolean layoutNotifEnabledLockscreen,
boolean layoutNotifEnabledUnlocked,
String layoutNotifMiddleSide,
boolean layoutNotifMiddleAutoNearestSide,
int layoutNotifVerticalOffsetPx,
int layoutNotifIconHeightSteps,
int layoutNotifLockIconHeightSteps,
@@ -509,25 +452,20 @@ public final class SbtSettings {
int layoutNotifUnlockedCount,
String[] layoutNotifPositions,
String[] layoutNotifMiddleSides,
boolean[] layoutNotifMiddleAutoNearestSides,
int[] layoutNotifVerticalOffsetsPx,
int layoutNotifLockCount,
String[] layoutNotifLockPositions,
String[] layoutNotifLockMiddleSides,
boolean[] layoutNotifLockMiddleAutoNearestSides,
int[] layoutNotifLockVerticalOffsetsPx,
int layoutNotifAodCount,
String[] layoutNotifAodPositions,
String[] layoutNotifAodMiddleSides,
boolean[] layoutNotifAodMiddleAutoNearestSides,
int[] layoutNotifAodVerticalOffsetsPx,
String layoutStatusPosition,
boolean layoutStatusEnabledAod,
boolean layoutStatusEnabledLockscreen,
boolean layoutStatusEnabledUnlocked,
boolean layoutStatusShowDotIfTruncated,
String layoutStatusMiddleSide,
boolean layoutStatusMiddleAutoNearestSide,
int layoutStatusVerticalOffsetPx,
int layoutStatusIconHeightSteps,
int layoutStatusLockIconHeightSteps,
@@ -535,17 +473,14 @@ public final class SbtSettings {
int layoutStatusUnlockedCount,
String[] layoutStatusPositions,
String[] layoutStatusMiddleSides,
boolean[] layoutStatusMiddleAutoNearestSides,
int[] layoutStatusVerticalOffsetsPx,
int layoutStatusLockCount,
String[] layoutStatusLockPositions,
String[] layoutStatusLockMiddleSides,
boolean[] layoutStatusLockMiddleAutoNearestSides,
int[] layoutStatusLockVerticalOffsetsPx,
int layoutStatusAodCount,
String[] layoutStatusAodPositions,
String[] layoutStatusAodMiddleSides,
boolean[] layoutStatusAodMiddleAutoNearestSides,
int[] layoutStatusAodVerticalOffsetsPx,
Map<String, String> clockCameraTypeOverrides,
Map<String, Integer> systemIconBlockedModes,
@@ -671,7 +606,6 @@ public final class SbtSettings {
this.layoutCarrierMiddleSide = layoutCarrierMiddleSide != null
? layoutCarrierMiddleSide
: SbtDefaults.LAYOUT_CARRIER_MIDDLE_SIDE_DEFAULT;
this.layoutCarrierMiddleAutoNearestSide = layoutCarrierMiddleAutoNearestSide;
this.layoutCarrierVerticalOffsetPx = clampInt(
layoutCarrierVerticalOffsetPx,
SbtDefaults.CLOCK_VERTICAL_OFFSET_PX_MIN,
@@ -679,12 +613,10 @@ public final class SbtSettings {
this.layoutChipPosition = layoutChipPosition != null
? layoutChipPosition
: SbtDefaults.LAYOUT_CHIP_POSITION_DEFAULT;
this.layoutChipEnabledLockscreen = layoutChipEnabledLockscreen;
this.layoutChipEnabledUnlocked = layoutChipEnabledUnlocked;
this.layoutChipMiddleSide = layoutChipMiddleSide != null
? layoutChipMiddleSide
: SbtDefaults.LAYOUT_CHIP_MIDDLE_SIDE_DEFAULT;
this.layoutChipMiddleAutoNearestSide = layoutChipMiddleAutoNearestSide;
this.layoutChipVerticalOffsetPx = clampInt(
layoutChipVerticalOffsetPx,
SbtDefaults.CLOCK_VERTICAL_OFFSET_PX_MIN,
@@ -696,14 +628,12 @@ public final class SbtSettings {
this.layoutNotifSortOrder = layoutNotifSortOrder != null
? layoutNotifSortOrder
: SbtDefaults.LAYOUT_NOTIF_SORT_ORDER_DEFAULT;
this.layoutNotifShowDotIfTruncated = layoutNotifShowDotIfTruncated;
this.layoutNotifEnabledAod = layoutNotifEnabledAod;
this.layoutNotifEnabledLockscreen = layoutNotifEnabledLockscreen;
this.layoutNotifEnabledUnlocked = layoutNotifEnabledUnlocked;
this.layoutNotifMiddleSide = layoutNotifMiddleSide != null
? layoutNotifMiddleSide
: SbtDefaults.LAYOUT_NOTIF_MIDDLE_SIDE_DEFAULT;
this.layoutNotifMiddleAutoNearestSide = layoutNotifMiddleAutoNearestSide;
this.layoutNotifVerticalOffsetPx = clampInt(
layoutNotifVerticalOffsetPx,
SbtDefaults.CLOCK_VERTICAL_OFFSET_PX_MIN,
@@ -720,10 +650,6 @@ public final class SbtSettings {
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,
@@ -739,10 +665,6 @@ public final class SbtSettings {
layoutNotifLockMiddleSides,
this.layoutNotifMiddleSide,
SbtDefaults.LAYOUT_ICON_CONTAINER_COUNT_MAX);
this.layoutNotifLockMiddleAutoNearestSides = sanitizeBooleanArray(
layoutNotifLockMiddleAutoNearestSides,
this.layoutNotifMiddleAutoNearestSide,
SbtDefaults.LAYOUT_ICON_CONTAINER_COUNT_MAX);
this.layoutNotifLockVerticalOffsetsPx = sanitizeIntArray(
layoutNotifLockVerticalOffsetsPx,
this.layoutNotifVerticalOffsetPx,
@@ -758,10 +680,6 @@ public final class SbtSettings {
layoutNotifAodMiddleSides,
this.layoutNotifMiddleSide,
SbtDefaults.LAYOUT_ICON_CONTAINER_COUNT_MAX);
this.layoutNotifAodMiddleAutoNearestSides = sanitizeBooleanArray(
layoutNotifAodMiddleAutoNearestSides,
this.layoutNotifMiddleAutoNearestSide,
SbtDefaults.LAYOUT_ICON_CONTAINER_COUNT_MAX);
this.layoutNotifAodVerticalOffsetsPx = sanitizeIntArray(
layoutNotifAodVerticalOffsetsPx,
this.layoutNotifVerticalOffsetPx,
@@ -774,11 +692,9 @@ public final class SbtSettings {
this.layoutStatusEnabledAod = layoutStatusEnabledAod;
this.layoutStatusEnabledLockscreen = layoutStatusEnabledLockscreen;
this.layoutStatusEnabledUnlocked = layoutStatusEnabledUnlocked;
this.layoutStatusShowDotIfTruncated = layoutStatusShowDotIfTruncated;
this.layoutStatusMiddleSide = layoutStatusMiddleSide != null
? layoutStatusMiddleSide
: SbtDefaults.LAYOUT_STATUS_MIDDLE_SIDE_DEFAULT;
this.layoutStatusMiddleAutoNearestSide = layoutStatusMiddleAutoNearestSide;
this.layoutStatusVerticalOffsetPx = clampInt(
layoutStatusVerticalOffsetPx,
SbtDefaults.CLOCK_VERTICAL_OFFSET_PX_MIN,
@@ -795,10 +711,6 @@ public final class SbtSettings {
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,
@@ -814,10 +726,6 @@ public final class SbtSettings {
layoutStatusLockMiddleSides,
this.layoutStatusMiddleSide,
SbtDefaults.LAYOUT_ICON_CONTAINER_COUNT_MAX);
this.layoutStatusLockMiddleAutoNearestSides = sanitizeBooleanArray(
layoutStatusLockMiddleAutoNearestSides,
this.layoutStatusMiddleAutoNearestSide,
SbtDefaults.LAYOUT_ICON_CONTAINER_COUNT_MAX);
this.layoutStatusLockVerticalOffsetsPx = sanitizeIntArray(
layoutStatusLockVerticalOffsetsPx,
this.layoutStatusVerticalOffsetPx,
@@ -833,10 +741,6 @@ public final class SbtSettings {
layoutStatusAodMiddleSides,
this.layoutStatusMiddleSide,
SbtDefaults.LAYOUT_ICON_CONTAINER_COUNT_MAX);
this.layoutStatusAodMiddleAutoNearestSides = sanitizeBooleanArray(
layoutStatusAodMiddleAutoNearestSides,
this.layoutStatusMiddleAutoNearestSide,
SbtDefaults.LAYOUT_ICON_CONTAINER_COUNT_MAX);
this.layoutStatusAodVerticalOffsetsPx = sanitizeIntArray(
layoutStatusAodVerticalOffsetsPx,
this.layoutStatusVerticalOffsetPx,
@@ -936,23 +840,18 @@ public final class SbtSettings {
SbtDefaults.LAYOUT_CARRIER_POSITION_DEFAULT,
SbtDefaults.LAYOUT_CARRIER_ENABLED_LOCKSCREEN_DEFAULT,
SbtDefaults.LAYOUT_CARRIER_MIDDLE_SIDE_DEFAULT,
SbtDefaults.LAYOUT_CARRIER_MIDDLE_AUTO_NEAREST_SIDE_DEFAULT,
SbtDefaults.LAYOUT_CARRIER_VERTICAL_OFFSET_PX_DEFAULT,
SbtDefaults.LAYOUT_CHIP_POSITION_DEFAULT,
SbtDefaults.LAYOUT_CHIP_ENABLED_LOCKSCREEN_DEFAULT,
SbtDefaults.LAYOUT_CHIP_ENABLED_UNLOCKED_DEFAULT,
SbtDefaults.LAYOUT_CHIP_MIDDLE_SIDE_DEFAULT,
SbtDefaults.LAYOUT_CHIP_MIDDLE_AUTO_NEAREST_SIDE_DEFAULT,
SbtDefaults.LAYOUT_CHIP_VERTICAL_OFFSET_PX_DEFAULT,
SbtDefaults.LAYOUT_CHIP_HEIGHT_STEPS_DEFAULT,
SbtDefaults.LAYOUT_NOTIF_POSITION_DEFAULT,
SbtDefaults.LAYOUT_NOTIF_SORT_ORDER_DEFAULT,
SbtDefaults.LAYOUT_NOTIF_SHOW_DOT_IF_TRUNCATED_DEFAULT,
SbtDefaults.LAYOUT_NOTIF_ENABLED_AOD_DEFAULT,
SbtDefaults.LAYOUT_NOTIF_ENABLED_LOCKSCREEN_DEFAULT,
SbtDefaults.LAYOUT_NOTIF_ENABLED_UNLOCKED_DEFAULT,
SbtDefaults.LAYOUT_NOTIF_MIDDLE_SIDE_DEFAULT,
SbtDefaults.LAYOUT_NOTIF_MIDDLE_AUTO_NEAREST_SIDE_DEFAULT,
SbtDefaults.LAYOUT_NOTIF_VERTICAL_OFFSET_PX_DEFAULT,
SbtDefaults.LAYOUT_NOTIF_ICON_HEIGHT_STEPS_DEFAULT,
SbtDefaults.LAYOUT_NOTIF_LOCK_ICON_HEIGHT_STEPS_DEFAULT,
@@ -961,24 +860,19 @@ public final class SbtSettings {
null,
null,
null,
null,
SbtDefaults.LAYOUT_NOTIF_UNLOCKED_COUNT_DEFAULT,
null,
null,
null,
null,
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,
SbtDefaults.LAYOUT_STATUS_ENABLED_UNLOCKED_DEFAULT,
SbtDefaults.LAYOUT_STATUS_SHOW_DOT_IF_TRUNCATED_DEFAULT,
SbtDefaults.LAYOUT_STATUS_MIDDLE_SIDE_DEFAULT,
SbtDefaults.LAYOUT_STATUS_MIDDLE_AUTO_NEAREST_SIDE_DEFAULT,
SbtDefaults.LAYOUT_STATUS_VERTICAL_OFFSET_PX_DEFAULT,
SbtDefaults.LAYOUT_STATUS_ICON_HEIGHT_STEPS_DEFAULT,
SbtDefaults.LAYOUT_STATUS_ICON_HEIGHT_STEPS_DEFAULT,
@@ -987,17 +881,14 @@ public final class SbtSettings {
null,
null,
null,
null,
SbtDefaults.LAYOUT_STATUS_UNLOCKED_COUNT_DEFAULT,
null,
null,
null,
null,
SbtDefaults.LAYOUT_STATUS_UNLOCKED_COUNT_DEFAULT,
null,
null,
null,
null,
Collections.emptyMap(),
Collections.emptyMap(),
false,
@@ -1208,13 +1099,10 @@ public final class SbtSettings {
prefs.getString(KEY_LAYOUT_CARRIER_POSITION, SbtDefaults.LAYOUT_CARRIER_POSITION_DEFAULT),
prefs.getBoolean(KEY_LAYOUT_CARRIER_ENABLED_LOCKSCREEN, SbtDefaults.LAYOUT_CARRIER_ENABLED_LOCKSCREEN_DEFAULT),
prefs.getString(KEY_LAYOUT_CARRIER_MIDDLE_SIDE, SbtDefaults.LAYOUT_CARRIER_MIDDLE_SIDE_DEFAULT),
prefs.getBoolean(KEY_LAYOUT_CARRIER_MIDDLE_AUTO_NEAREST_SIDE, SbtDefaults.LAYOUT_CARRIER_MIDDLE_AUTO_NEAREST_SIDE_DEFAULT),
prefs.getInt(KEY_LAYOUT_CARRIER_VERTICAL_OFFSET_PX, SbtDefaults.LAYOUT_CARRIER_VERTICAL_OFFSET_PX_DEFAULT),
prefs.getString(KEY_LAYOUT_CHIP_POSITION, SbtDefaults.LAYOUT_CHIP_POSITION_DEFAULT),
prefs.getBoolean(KEY_LAYOUT_CHIP_ENABLED_LOCKSCREEN, SbtDefaults.LAYOUT_CHIP_ENABLED_LOCKSCREEN_DEFAULT),
prefs.getBoolean(KEY_LAYOUT_CHIP_ENABLED_UNLOCKED, SbtDefaults.LAYOUT_CHIP_ENABLED_UNLOCKED_DEFAULT),
prefs.getString(KEY_LAYOUT_CHIP_MIDDLE_SIDE, SbtDefaults.LAYOUT_CHIP_MIDDLE_SIDE_DEFAULT),
prefs.getBoolean(KEY_LAYOUT_CHIP_MIDDLE_AUTO_NEAREST_SIDE, SbtDefaults.LAYOUT_CHIP_MIDDLE_AUTO_NEAREST_SIDE_DEFAULT),
clampInt(prefs.getInt(KEY_LAYOUT_CHIP_VERTICAL_OFFSET_PX,
SbtDefaults.LAYOUT_CHIP_VERTICAL_OFFSET_PX_DEFAULT),
SbtDefaults.CLOCK_VERTICAL_OFFSET_PX_MIN,
@@ -1223,12 +1111,10 @@ public final class SbtSettings {
SbtDefaults.LAYOUT_CHIP_HEIGHT_STEPS_DEFAULT)),
prefs.getString(KEY_LAYOUT_NOTIF_POSITION, SbtDefaults.LAYOUT_NOTIF_POSITION_DEFAULT),
prefs.getString(KEY_LAYOUT_NOTIF_SORT_ORDER, SbtDefaults.LAYOUT_NOTIF_SORT_ORDER_DEFAULT),
prefs.getBoolean(KEY_LAYOUT_NOTIF_SHOW_DOT_IF_TRUNCATED, SbtDefaults.LAYOUT_NOTIF_SHOW_DOT_IF_TRUNCATED_DEFAULT),
prefs.getBoolean(KEY_LAYOUT_NOTIF_ENABLED_AOD, SbtDefaults.LAYOUT_NOTIF_ENABLED_AOD_DEFAULT),
prefs.getBoolean(KEY_LAYOUT_NOTIF_ENABLED_LOCKSCREEN, SbtDefaults.LAYOUT_NOTIF_ENABLED_LOCKSCREEN_DEFAULT),
prefs.getBoolean(KEY_LAYOUT_NOTIF_ENABLED_UNLOCKED, SbtDefaults.LAYOUT_NOTIF_ENABLED_UNLOCKED_DEFAULT),
prefs.getString(KEY_LAYOUT_NOTIF_MIDDLE_SIDE, SbtDefaults.LAYOUT_NOTIF_MIDDLE_SIDE_DEFAULT),
prefs.getBoolean(KEY_LAYOUT_NOTIF_MIDDLE_AUTO_NEAREST_SIDE, SbtDefaults.LAYOUT_NOTIF_MIDDLE_AUTO_NEAREST_SIDE_DEFAULT),
clampInt(prefs.getInt(KEY_LAYOUT_NOTIF_VERTICAL_OFFSET_PX,
SbtDefaults.LAYOUT_NOTIF_VERTICAL_OFFSET_PX_DEFAULT),
SbtDefaults.CLOCK_VERTICAL_OFFSET_PX_MIN,
@@ -1259,10 +1145,6 @@ public final class SbtSettings {
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,
@@ -1281,10 +1163,6 @@ public final class SbtSettings {
prefs,
SbtSettings::layoutLockNotifMiddleSideKey,
SbtDefaults.LAYOUT_NOTIF_MIDDLE_SIDE_DEFAULT),
readBooleanArrayFromPrefs(
prefs,
SbtSettings::layoutLockNotifMiddleAutoNearestSideKey,
SbtDefaults.LAYOUT_NOTIF_MIDDLE_AUTO_NEAREST_SIDE_DEFAULT),
readIntArrayFromPrefs(
prefs,
SbtSettings::layoutLockNotifVerticalOffsetKey,
@@ -1303,10 +1181,6 @@ public final class SbtSettings {
prefs,
SbtSettings::layoutAodNotifMiddleSideKey,
SbtDefaults.LAYOUT_NOTIF_MIDDLE_SIDE_DEFAULT),
readBooleanArrayFromPrefs(
prefs,
SbtSettings::layoutAodNotifMiddleAutoNearestSideKey,
SbtDefaults.LAYOUT_NOTIF_MIDDLE_AUTO_NEAREST_SIDE_DEFAULT),
readIntArrayFromPrefs(
prefs,
SbtSettings::layoutAodNotifVerticalOffsetKey,
@@ -1315,9 +1189,7 @@ public final class SbtSettings {
prefs.getBoolean(KEY_LAYOUT_STATUS_ENABLED_AOD, SbtDefaults.LAYOUT_STATUS_ENABLED_AOD_DEFAULT),
prefs.getBoolean(KEY_LAYOUT_STATUS_ENABLED_LOCKSCREEN, SbtDefaults.LAYOUT_STATUS_ENABLED_LOCKSCREEN_DEFAULT),
prefs.getBoolean(KEY_LAYOUT_STATUS_ENABLED_UNLOCKED, SbtDefaults.LAYOUT_STATUS_ENABLED_UNLOCKED_DEFAULT),
prefs.getBoolean(KEY_LAYOUT_STATUS_SHOW_DOT_IF_TRUNCATED, SbtDefaults.LAYOUT_STATUS_SHOW_DOT_IF_TRUNCATED_DEFAULT),
prefs.getString(KEY_LAYOUT_STATUS_MIDDLE_SIDE, SbtDefaults.LAYOUT_STATUS_MIDDLE_SIDE_DEFAULT),
prefs.getBoolean(KEY_LAYOUT_STATUS_MIDDLE_AUTO_NEAREST_SIDE, SbtDefaults.LAYOUT_STATUS_MIDDLE_AUTO_NEAREST_SIDE_DEFAULT),
clampInt(prefs.getInt(KEY_LAYOUT_STATUS_VERTICAL_OFFSET_PX,
SbtDefaults.LAYOUT_STATUS_VERTICAL_OFFSET_PX_DEFAULT),
SbtDefaults.CLOCK_VERTICAL_OFFSET_PX_MIN,
@@ -1348,10 +1220,6 @@ public final class SbtSettings {
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,
@@ -1370,10 +1238,6 @@ public final class SbtSettings {
prefs,
SbtSettings::layoutLockStatusMiddleSideKey,
SbtDefaults.LAYOUT_STATUS_MIDDLE_SIDE_DEFAULT),
readBooleanArrayFromPrefs(
prefs,
SbtSettings::layoutLockStatusMiddleAutoNearestSideKey,
SbtDefaults.LAYOUT_STATUS_MIDDLE_AUTO_NEAREST_SIDE_DEFAULT),
readIntArrayFromPrefs(
prefs,
SbtSettings::layoutLockStatusVerticalOffsetKey,
@@ -1392,10 +1256,6 @@ public final class SbtSettings {
prefs,
SbtSettings::layoutAodStatusMiddleSideKey,
SbtDefaults.LAYOUT_STATUS_MIDDLE_SIDE_DEFAULT),
readBooleanArrayFromPrefs(
prefs,
SbtSettings::layoutAodStatusMiddleAutoNearestSideKey,
SbtDefaults.LAYOUT_STATUS_MIDDLE_AUTO_NEAREST_SIDE_DEFAULT),
readIntArrayFromPrefs(
prefs,
SbtSettings::layoutAodStatusVerticalOffsetKey,
@@ -1537,13 +1397,10 @@ public final class SbtSettings {
bundle.getString(KEY_LAYOUT_CARRIER_POSITION, SbtDefaults.LAYOUT_CARRIER_POSITION_DEFAULT),
bundle.getBoolean(KEY_LAYOUT_CARRIER_ENABLED_LOCKSCREEN, SbtDefaults.LAYOUT_CARRIER_ENABLED_LOCKSCREEN_DEFAULT),
bundle.getString(KEY_LAYOUT_CARRIER_MIDDLE_SIDE, SbtDefaults.LAYOUT_CARRIER_MIDDLE_SIDE_DEFAULT),
bundle.getBoolean(KEY_LAYOUT_CARRIER_MIDDLE_AUTO_NEAREST_SIDE, SbtDefaults.LAYOUT_CARRIER_MIDDLE_AUTO_NEAREST_SIDE_DEFAULT),
bundle.getInt(KEY_LAYOUT_CARRIER_VERTICAL_OFFSET_PX, SbtDefaults.LAYOUT_CARRIER_VERTICAL_OFFSET_PX_DEFAULT),
bundle.getString(KEY_LAYOUT_CHIP_POSITION, SbtDefaults.LAYOUT_CHIP_POSITION_DEFAULT),
bundle.getBoolean(KEY_LAYOUT_CHIP_ENABLED_LOCKSCREEN, SbtDefaults.LAYOUT_CHIP_ENABLED_LOCKSCREEN_DEFAULT),
bundle.getBoolean(KEY_LAYOUT_CHIP_ENABLED_UNLOCKED, SbtDefaults.LAYOUT_CHIP_ENABLED_UNLOCKED_DEFAULT),
bundle.getString(KEY_LAYOUT_CHIP_MIDDLE_SIDE, SbtDefaults.LAYOUT_CHIP_MIDDLE_SIDE_DEFAULT),
bundle.getBoolean(KEY_LAYOUT_CHIP_MIDDLE_AUTO_NEAREST_SIDE, SbtDefaults.LAYOUT_CHIP_MIDDLE_AUTO_NEAREST_SIDE_DEFAULT),
clampInt(bundle.getInt(KEY_LAYOUT_CHIP_VERTICAL_OFFSET_PX,
SbtDefaults.LAYOUT_CHIP_VERTICAL_OFFSET_PX_DEFAULT),
SbtDefaults.CLOCK_VERTICAL_OFFSET_PX_MIN,
@@ -1552,12 +1409,10 @@ public final class SbtSettings {
SbtDefaults.LAYOUT_CHIP_HEIGHT_STEPS_DEFAULT)),
bundle.getString(KEY_LAYOUT_NOTIF_POSITION, SbtDefaults.LAYOUT_NOTIF_POSITION_DEFAULT),
bundle.getString(KEY_LAYOUT_NOTIF_SORT_ORDER, SbtDefaults.LAYOUT_NOTIF_SORT_ORDER_DEFAULT),
bundle.getBoolean(KEY_LAYOUT_NOTIF_SHOW_DOT_IF_TRUNCATED, SbtDefaults.LAYOUT_NOTIF_SHOW_DOT_IF_TRUNCATED_DEFAULT),
bundle.getBoolean(KEY_LAYOUT_NOTIF_ENABLED_AOD, SbtDefaults.LAYOUT_NOTIF_ENABLED_AOD_DEFAULT),
bundle.getBoolean(KEY_LAYOUT_NOTIF_ENABLED_LOCKSCREEN, SbtDefaults.LAYOUT_NOTIF_ENABLED_LOCKSCREEN_DEFAULT),
bundle.getBoolean(KEY_LAYOUT_NOTIF_ENABLED_UNLOCKED, SbtDefaults.LAYOUT_NOTIF_ENABLED_UNLOCKED_DEFAULT),
bundle.getString(KEY_LAYOUT_NOTIF_MIDDLE_SIDE, SbtDefaults.LAYOUT_NOTIF_MIDDLE_SIDE_DEFAULT),
bundle.getBoolean(KEY_LAYOUT_NOTIF_MIDDLE_AUTO_NEAREST_SIDE, SbtDefaults.LAYOUT_NOTIF_MIDDLE_AUTO_NEAREST_SIDE_DEFAULT),
clampInt(bundle.getInt(KEY_LAYOUT_NOTIF_VERTICAL_OFFSET_PX,
SbtDefaults.LAYOUT_NOTIF_VERTICAL_OFFSET_PX_DEFAULT),
SbtDefaults.CLOCK_VERTICAL_OFFSET_PX_MIN,
@@ -1588,10 +1443,6 @@ public final class SbtSettings {
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,
@@ -1610,10 +1461,6 @@ public final class SbtSettings {
bundle,
SbtSettings::layoutLockNotifMiddleSideKey,
SbtDefaults.LAYOUT_NOTIF_MIDDLE_SIDE_DEFAULT),
readBooleanArrayFromBundle(
bundle,
SbtSettings::layoutLockNotifMiddleAutoNearestSideKey,
SbtDefaults.LAYOUT_NOTIF_MIDDLE_AUTO_NEAREST_SIDE_DEFAULT),
readIntArrayFromBundle(
bundle,
SbtSettings::layoutLockNotifVerticalOffsetKey,
@@ -1632,10 +1479,6 @@ public final class SbtSettings {
bundle,
SbtSettings::layoutAodNotifMiddleSideKey,
SbtDefaults.LAYOUT_NOTIF_MIDDLE_SIDE_DEFAULT),
readBooleanArrayFromBundle(
bundle,
SbtSettings::layoutAodNotifMiddleAutoNearestSideKey,
SbtDefaults.LAYOUT_NOTIF_MIDDLE_AUTO_NEAREST_SIDE_DEFAULT),
readIntArrayFromBundle(
bundle,
SbtSettings::layoutAodNotifVerticalOffsetKey,
@@ -1644,9 +1487,7 @@ public final class SbtSettings {
bundle.getBoolean(KEY_LAYOUT_STATUS_ENABLED_AOD, SbtDefaults.LAYOUT_STATUS_ENABLED_AOD_DEFAULT),
bundle.getBoolean(KEY_LAYOUT_STATUS_ENABLED_LOCKSCREEN, SbtDefaults.LAYOUT_STATUS_ENABLED_LOCKSCREEN_DEFAULT),
bundle.getBoolean(KEY_LAYOUT_STATUS_ENABLED_UNLOCKED, SbtDefaults.LAYOUT_STATUS_ENABLED_UNLOCKED_DEFAULT),
bundle.getBoolean(KEY_LAYOUT_STATUS_SHOW_DOT_IF_TRUNCATED, SbtDefaults.LAYOUT_STATUS_SHOW_DOT_IF_TRUNCATED_DEFAULT),
bundle.getString(KEY_LAYOUT_STATUS_MIDDLE_SIDE, SbtDefaults.LAYOUT_STATUS_MIDDLE_SIDE_DEFAULT),
bundle.getBoolean(KEY_LAYOUT_STATUS_MIDDLE_AUTO_NEAREST_SIDE, SbtDefaults.LAYOUT_STATUS_MIDDLE_AUTO_NEAREST_SIDE_DEFAULT),
clampInt(bundle.getInt(KEY_LAYOUT_STATUS_VERTICAL_OFFSET_PX,
SbtDefaults.LAYOUT_STATUS_VERTICAL_OFFSET_PX_DEFAULT),
SbtDefaults.CLOCK_VERTICAL_OFFSET_PX_MIN,
@@ -1677,10 +1518,6 @@ public final class SbtSettings {
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,
@@ -1699,10 +1536,6 @@ public final class SbtSettings {
bundle,
SbtSettings::layoutLockStatusMiddleSideKey,
SbtDefaults.LAYOUT_STATUS_MIDDLE_SIDE_DEFAULT),
readBooleanArrayFromBundle(
bundle,
SbtSettings::layoutLockStatusMiddleAutoNearestSideKey,
SbtDefaults.LAYOUT_STATUS_MIDDLE_AUTO_NEAREST_SIDE_DEFAULT),
readIntArrayFromBundle(
bundle,
SbtSettings::layoutLockStatusVerticalOffsetKey,
@@ -1721,10 +1554,6 @@ public final class SbtSettings {
bundle,
SbtSettings::layoutAodStatusMiddleSideKey,
SbtDefaults.LAYOUT_STATUS_MIDDLE_SIDE_DEFAULT),
readBooleanArrayFromBundle(
bundle,
SbtSettings::layoutAodStatusMiddleAutoNearestSideKey,
SbtDefaults.LAYOUT_STATUS_MIDDLE_AUTO_NEAREST_SIDE_DEFAULT),
readIntArrayFromBundle(
bundle,
SbtSettings::layoutAodStatusVerticalOffsetKey,
@@ -1829,20 +1658,6 @@ public final class SbtSettings {
return result;
}
private static boolean[] readBooleanArrayFromPrefs(
SharedPreferences prefs,
IndexedKey key,
boolean fallback
) {
boolean[] result = new boolean[SbtDefaults.LAYOUT_ICON_CONTAINER_COUNT_MAX];
boolean previous = fallback;
for (int i = 0; i < result.length; i++) {
previous = prefs.contains(key.key(i)) ? prefs.getBoolean(key.key(i), previous) : previous;
result[i] = previous;
}
return result;
}
private static int[] readIntArrayFromPrefs(SharedPreferences prefs, IndexedKey key, int fallback) {
int[] result = new int[SbtDefaults.LAYOUT_ICON_CONTAINER_COUNT_MAX];
int previous = fallback;
@@ -1863,16 +1678,6 @@ public final class SbtSettings {
return result;
}
private static boolean[] readBooleanArrayFromBundle(Bundle bundle, IndexedKey key, boolean fallback) {
boolean[] result = new boolean[SbtDefaults.LAYOUT_ICON_CONTAINER_COUNT_MAX];
boolean previous = fallback;
for (int i = 0; i < result.length; i++) {
previous = bundle.containsKey(key.key(i)) ? bundle.getBoolean(key.key(i), previous) : previous;
result[i] = previous;
}
return result;
}
private static int[] readIntArrayFromBundle(Bundle bundle, IndexedKey key, int fallback) {
int[] result = new int[SbtDefaults.LAYOUT_ICON_CONTAINER_COUNT_MAX];
int previous = fallback;
@@ -1892,14 +1697,6 @@ public final class SbtSettings {
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++) {
@@ -195,27 +195,18 @@ public class SbtSettingsProvider extends ContentProvider {
out.putString(SbtSettings.KEY_LAYOUT_CARRIER_MIDDLE_SIDE,
prefs.getString(SbtSettings.KEY_LAYOUT_CARRIER_MIDDLE_SIDE,
SbtDefaults.LAYOUT_CARRIER_MIDDLE_SIDE_DEFAULT));
out.putBoolean(SbtSettings.KEY_LAYOUT_CARRIER_MIDDLE_AUTO_NEAREST_SIDE,
prefs.getBoolean(SbtSettings.KEY_LAYOUT_CARRIER_MIDDLE_AUTO_NEAREST_SIDE,
SbtDefaults.LAYOUT_CARRIER_MIDDLE_AUTO_NEAREST_SIDE_DEFAULT));
out.putInt(SbtSettings.KEY_LAYOUT_CARRIER_VERTICAL_OFFSET_PX,
prefs.getInt(SbtSettings.KEY_LAYOUT_CARRIER_VERTICAL_OFFSET_PX,
SbtDefaults.LAYOUT_CARRIER_VERTICAL_OFFSET_PX_DEFAULT));
out.putString(SbtSettings.KEY_LAYOUT_CHIP_POSITION,
prefs.getString(SbtSettings.KEY_LAYOUT_CHIP_POSITION,
SbtDefaults.LAYOUT_CHIP_POSITION_DEFAULT));
out.putBoolean(SbtSettings.KEY_LAYOUT_CHIP_ENABLED_LOCKSCREEN,
prefs.getBoolean(SbtSettings.KEY_LAYOUT_CHIP_ENABLED_LOCKSCREEN,
SbtDefaults.LAYOUT_CHIP_ENABLED_LOCKSCREEN_DEFAULT));
out.putBoolean(SbtSettings.KEY_LAYOUT_CHIP_ENABLED_UNLOCKED,
prefs.getBoolean(SbtSettings.KEY_LAYOUT_CHIP_ENABLED_UNLOCKED,
SbtDefaults.LAYOUT_CHIP_ENABLED_UNLOCKED_DEFAULT));
out.putString(SbtSettings.KEY_LAYOUT_CHIP_MIDDLE_SIDE,
prefs.getString(SbtSettings.KEY_LAYOUT_CHIP_MIDDLE_SIDE,
SbtDefaults.LAYOUT_CHIP_MIDDLE_SIDE_DEFAULT));
out.putBoolean(SbtSettings.KEY_LAYOUT_CHIP_MIDDLE_AUTO_NEAREST_SIDE,
prefs.getBoolean(SbtSettings.KEY_LAYOUT_CHIP_MIDDLE_AUTO_NEAREST_SIDE,
SbtDefaults.LAYOUT_CHIP_MIDDLE_AUTO_NEAREST_SIDE_DEFAULT));
out.putInt(SbtSettings.KEY_LAYOUT_CHIP_VERTICAL_OFFSET_PX,
prefs.getInt(SbtSettings.KEY_LAYOUT_CHIP_VERTICAL_OFFSET_PX,
SbtDefaults.LAYOUT_CHIP_VERTICAL_OFFSET_PX_DEFAULT));
@@ -228,9 +219,6 @@ public class SbtSettingsProvider extends ContentProvider {
out.putString(SbtSettings.KEY_LAYOUT_NOTIF_SORT_ORDER,
prefs.getString(SbtSettings.KEY_LAYOUT_NOTIF_SORT_ORDER,
SbtDefaults.LAYOUT_NOTIF_SORT_ORDER_DEFAULT));
out.putBoolean(SbtSettings.KEY_LAYOUT_NOTIF_SHOW_DOT_IF_TRUNCATED,
prefs.getBoolean(SbtSettings.KEY_LAYOUT_NOTIF_SHOW_DOT_IF_TRUNCATED,
SbtDefaults.LAYOUT_NOTIF_SHOW_DOT_IF_TRUNCATED_DEFAULT));
out.putBoolean(SbtSettings.KEY_LAYOUT_NOTIF_ENABLED_AOD,
prefs.getBoolean(SbtSettings.KEY_LAYOUT_NOTIF_ENABLED_AOD,
SbtDefaults.LAYOUT_NOTIF_ENABLED_AOD_DEFAULT));
@@ -250,9 +238,6 @@ public class SbtSettingsProvider extends ContentProvider {
out.putString(SbtSettings.KEY_LAYOUT_NOTIF_MIDDLE_SIDE,
prefs.getString(SbtSettings.KEY_LAYOUT_NOTIF_MIDDLE_SIDE,
SbtDefaults.LAYOUT_NOTIF_MIDDLE_SIDE_DEFAULT));
out.putBoolean(SbtSettings.KEY_LAYOUT_NOTIF_MIDDLE_AUTO_NEAREST_SIDE,
prefs.getBoolean(SbtSettings.KEY_LAYOUT_NOTIF_MIDDLE_AUTO_NEAREST_SIDE,
SbtDefaults.LAYOUT_NOTIF_MIDDLE_AUTO_NEAREST_SIDE_DEFAULT));
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));
@@ -266,8 +251,6 @@ public class SbtSettingsProvider extends ContentProvider {
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.putInt(SbtSettings.KEY_LAYOUT_NOTIF_LOCK_COUNT,
@@ -287,8 +270,6 @@ public class SbtSettingsProvider extends ContentProvider {
SbtDefaults.LAYOUT_NOTIF_POSITION_DEFAULT,
SbtSettings::layoutLockNotifMiddleSideKey,
SbtDefaults.LAYOUT_NOTIF_MIDDLE_SIDE_DEFAULT,
SbtSettings::layoutLockNotifMiddleAutoNearestSideKey,
SbtDefaults.LAYOUT_NOTIF_MIDDLE_AUTO_NEAREST_SIDE_DEFAULT,
SbtSettings::layoutLockNotifVerticalOffsetKey,
SbtDefaults.LAYOUT_NOTIF_VERTICAL_OFFSET_PX_DEFAULT);
out.putInt(SbtSettings.KEY_LAYOUT_NOTIF_AOD_COUNT,
@@ -308,8 +289,6 @@ public class SbtSettingsProvider extends ContentProvider {
SbtDefaults.LAYOUT_NOTIF_POSITION_DEFAULT,
SbtSettings::layoutAodNotifMiddleSideKey,
SbtDefaults.LAYOUT_NOTIF_MIDDLE_SIDE_DEFAULT,
SbtSettings::layoutAodNotifMiddleAutoNearestSideKey,
SbtDefaults.LAYOUT_NOTIF_MIDDLE_AUTO_NEAREST_SIDE_DEFAULT,
SbtSettings::layoutAodNotifVerticalOffsetKey,
SbtDefaults.LAYOUT_NOTIF_VERTICAL_OFFSET_PX_DEFAULT);
out.putString(SbtSettings.KEY_LAYOUT_STATUS_POSITION,
@@ -331,15 +310,9 @@ public class SbtSettingsProvider extends ContentProvider {
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));
out.putString(SbtSettings.KEY_LAYOUT_STATUS_MIDDLE_SIDE,
prefs.getString(SbtSettings.KEY_LAYOUT_STATUS_MIDDLE_SIDE,
SbtDefaults.LAYOUT_STATUS_MIDDLE_SIDE_DEFAULT));
out.putBoolean(SbtSettings.KEY_LAYOUT_STATUS_MIDDLE_AUTO_NEAREST_SIDE,
prefs.getBoolean(SbtSettings.KEY_LAYOUT_STATUS_MIDDLE_AUTO_NEAREST_SIDE,
SbtDefaults.LAYOUT_STATUS_MIDDLE_AUTO_NEAREST_SIDE_DEFAULT));
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));
@@ -353,8 +326,6 @@ public class SbtSettingsProvider extends ContentProvider {
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.putInt(SbtSettings.KEY_LAYOUT_STATUS_LOCK_COUNT,
@@ -374,8 +345,6 @@ public class SbtSettingsProvider extends ContentProvider {
SbtDefaults.LAYOUT_STATUS_POSITION_DEFAULT,
SbtSettings::layoutLockStatusMiddleSideKey,
SbtDefaults.LAYOUT_STATUS_MIDDLE_SIDE_DEFAULT,
SbtSettings::layoutLockStatusMiddleAutoNearestSideKey,
SbtDefaults.LAYOUT_STATUS_MIDDLE_AUTO_NEAREST_SIDE_DEFAULT,
SbtSettings::layoutLockStatusVerticalOffsetKey,
SbtDefaults.LAYOUT_STATUS_VERTICAL_OFFSET_PX_DEFAULT);
out.putInt(SbtSettings.KEY_LAYOUT_STATUS_AOD_COUNT,
@@ -395,8 +364,6 @@ public class SbtSettingsProvider extends ContentProvider {
SbtDefaults.LAYOUT_STATUS_POSITION_DEFAULT,
SbtSettings::layoutAodStatusMiddleSideKey,
SbtDefaults.LAYOUT_STATUS_MIDDLE_SIDE_DEFAULT,
SbtSettings::layoutAodStatusMiddleAutoNearestSideKey,
SbtDefaults.LAYOUT_STATUS_MIDDLE_AUTO_NEAREST_SIDE_DEFAULT,
SbtSettings::layoutAodStatusVerticalOffsetKey,
SbtDefaults.LAYOUT_STATUS_VERTICAL_OFFSET_PX_DEFAULT);
putStringSetArrayList(out, prefs, SbtSettings.KEY_CLOCK_CAMERA_TYPE_OVERRIDES);
@@ -465,14 +432,11 @@ public class SbtSettingsProvider extends ContentProvider {
String positionDefault,
SbtSettings.IndexedKey middleSideKey,
String middleSideDefault,
SbtSettings.IndexedKey autoNearestKey,
boolean autoNearestDefault,
SbtSettings.IndexedKey verticalOffsetKey,
int verticalOffsetDefault
) {
String position = positionDefault;
String middleSide = middleSideDefault;
boolean autoNearest = autoNearestDefault;
int verticalOffset = verticalOffsetDefault;
for (int i = 0; i < SbtDefaults.LAYOUT_ICON_CONTAINER_COUNT_MAX; i++) {
position = prefs.contains(positionKey.key(i))
@@ -481,15 +445,11 @@ public class SbtSettingsProvider extends ContentProvider {
middleSide = prefs.contains(middleSideKey.key(i))
? prefs.getString(middleSideKey.key(i), middleSide)
: middleSide;
autoNearest = prefs.contains(autoNearestKey.key(i))
? prefs.getBoolean(autoNearestKey.key(i), autoNearest)
: autoNearest;
verticalOffset = prefs.contains(verticalOffsetKey.key(i))
? prefs.getInt(verticalOffsetKey.key(i), verticalOffset)
: verticalOffset;
out.putString(positionKey.key(i), position);
out.putString(middleSideKey.key(i), middleSide);
out.putBoolean(autoNearestKey.key(i), autoNearest);
out.putInt(verticalOffsetKey.key(i), verticalOffset);
}
}
@@ -56,31 +56,31 @@ public final class LayoutFragment extends Fragment {
prefs,
R.id.notif_position_group,
R.id.notif_position_middle_options_container,
R.id.notif_middle_auto_nearest,
View.NO_ID,
R.id.notif_middle_side_prompt,
SbtSettings.KEY_LAYOUT_NOTIF_POSITION,
SbtDefaults.LAYOUT_NOTIF_POSITION_DEFAULT,
SbtSettings.KEY_LAYOUT_NOTIF_MIDDLE_AUTO_NEAREST_SIDE,
null,
SbtSettings.KEY_LAYOUT_NOTIF_MIDDLE_SIDE);
setupPositionSection(root,
prefs,
R.id.chip_position_group,
R.id.chip_position_middle_options_container,
R.id.chip_middle_auto_nearest,
View.NO_ID,
R.id.chip_middle_side_prompt,
SbtSettings.KEY_LAYOUT_CHIP_POSITION,
SbtDefaults.LAYOUT_CHIP_POSITION_DEFAULT,
SbtSettings.KEY_LAYOUT_CHIP_MIDDLE_AUTO_NEAREST_SIDE,
null,
SbtSettings.KEY_LAYOUT_CHIP_MIDDLE_SIDE);
setupPositionSection(root,
prefs,
R.id.status_position_group,
R.id.status_position_middle_options_container,
R.id.status_middle_auto_nearest,
View.NO_ID,
R.id.status_middle_side_prompt,
SbtSettings.KEY_LAYOUT_STATUS_POSITION,
SbtDefaults.LAYOUT_STATUS_POSITION_DEFAULT,
SbtSettings.KEY_LAYOUT_STATUS_MIDDLE_AUTO_NEAREST_SIDE,
null,
SbtSettings.KEY_LAYOUT_STATUS_MIDDLE_SIDE);
bindStepper(prefs,
@@ -150,12 +150,10 @@ public final class LayoutFragment extends Fragment {
SbtSettings.KEY_LAYOUT_NOTIF_ENABLED_UNLOCKED,
SbtDefaults.LAYOUT_NOTIF_ENABLED_UNLOCKED_DEFAULT,
SbtSettings::layoutNotifPositionKey,
SbtSettings::layoutNotifMiddleAutoNearestSideKey,
SbtSettings::layoutNotifMiddleSideKey,
SbtSettings::layoutNotifVerticalOffsetKey,
SbtSettings.KEY_LAYOUT_NOTIF_ICON_HEIGHT_STEPS,
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,
SbtDefaults.LAYOUT_NOTIF_ICON_HEIGHT_STEPS_DEFAULT,
@@ -170,12 +168,10 @@ public final class LayoutFragment extends Fragment {
SbtSettings.KEY_LAYOUT_STATUS_ENABLED_UNLOCKED,
SbtDefaults.LAYOUT_STATUS_ENABLED_UNLOCKED_DEFAULT,
SbtSettings::layoutStatusPositionKey,
SbtSettings::layoutStatusMiddleAutoNearestSideKey,
SbtSettings::layoutStatusMiddleSideKey,
SbtSettings::layoutStatusVerticalOffsetKey,
SbtSettings.KEY_LAYOUT_STATUS_ICON_HEIGHT_STEPS,
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,
SbtDefaults.LAYOUT_STATUS_ICON_HEIGHT_STEPS_DEFAULT,
@@ -193,12 +189,10 @@ public final class LayoutFragment extends Fragment {
String legacyEnabledKey,
boolean legacyEnabledDefault,
SbtSettings.IndexedKey positionKey,
SbtSettings.IndexedKey autoNearestKey,
SbtSettings.IndexedKey middleSideKey,
SbtSettings.IndexedKey verticalOffsetKey,
String iconHeightKey,
String positionDefault,
boolean autoNearestDefault,
String middleSideDefault,
int verticalOffsetDefault,
int iconHeightDefault,
@@ -225,7 +219,12 @@ public final class LayoutFragment extends Fragment {
rebuild[0] = () -> {
countContainer.removeAllViews();
removeGeneratedCopyCards(cardsParent, copyTag);
int count = iconContainerCount(prefs, countKey, countDefault, legacyEnabledKey, legacyEnabledDefault);
int count = ViewTreeSupport.iconContainerCount(
prefs,
countKey,
legacyEnabledKey,
countDefault,
legacyEnabledDefault);
LayoutXmlCardSupport.setIndexedCardTitle(requireContext(), sourceCard, sectionTitleId, 0, count);
boolean cardsMode = LockedNotificationModeUi.MODE_CARDS.equals(
LockedNotificationModeUi.currentMode(requireContext()));
@@ -238,11 +237,15 @@ public final class LayoutFragment extends Fragment {
cardsMode,
0,
this::refreshSelf);
countContainer.addView(iconContainerCountControl(
prefs,
countKey,
countContainer.addView(ViewTreeSupport.iconContainerCountControl(
requireContext(),
count,
legacyEnabledKey,
value -> ViewTreeSupport.persistIconContainerCount(
requireContext(),
prefs,
countKey,
legacyEnabledKey,
value),
() -> {
if (rebuild[0] != null) {
rebuild[0].run();
@@ -264,11 +267,9 @@ public final class LayoutFragment extends Fragment {
sectionTitleId,
i,
positionKey,
autoNearestKey,
middleSideKey,
verticalOffsetKey,
positionDefault,
autoNearestDefault,
middleSideDefault,
verticalOffsetDefault,
copyTag);
@@ -294,105 +295,28 @@ public final class LayoutFragment extends Fragment {
});
}
private View iconContainerCountControl(
SharedPreferences prefs,
String countKey,
int current,
String legacyEnabledKey,
Runnable onChanged
) {
Context context = requireContext();
LinearLayout outer = ViewTreeSupport.verticalLayout(context);
TextView label = ViewTreeSupport.bodyText(context, R.string.layout_icon_container_count, 0);
outer.addView(label);
LinearLayout row = new LinearLayout(context);
row.setOrientation(LinearLayout.HORIZONTAL);
row.setGravity(android.view.Gravity.CENTER_VERTICAL);
MaterialButton minus = ViewTreeSupport.outlinedStepperButton(context, "-");
MaterialButton plus = ViewTreeSupport.outlinedStepperButton(context, "+");
EditText input = ViewTreeSupport.numericInput(context, false, 1);
input.setText(String.valueOf(current));
row.addView(minus, ViewTreeSupport.stepperButtonParams(context));
row.addView(input, ViewTreeSupport.stepperInputParams(context));
row.addView(plus, ViewTreeSupport.stepperButtonParams(context));
outer.addView(row);
View.OnClickListener listener = view -> {
int next = SbtSettings.clampIconContainerCount(
ViewTreeSupport.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 = SbtSettings.clampIconContainerCount(ViewTreeSupport.parseInt(input, current));
persistIconContainerCount(prefs, countKey, legacyEnabledKey, next);
onChanged.run();
}
});
return outer;
}
private View iconHeightControl(
SharedPreferences prefs,
String key,
int defaultValue,
int labelRes
) {
Context context = requireContext();
LinearLayout outer = ViewTreeSupport.verticalLayout(context);
TextView label = ViewTreeSupport.bodyText(context, labelRes, 0);
outer.addView(label);
LinearLayout row = new LinearLayout(context);
row.setOrientation(LinearLayout.HORIZONTAL);
row.setGravity(android.view.Gravity.CENTER_VERTICAL);
MaterialButton minus = ViewTreeSupport.outlinedStepperButton(context, "-");
MaterialButton plus = ViewTreeSupport.outlinedStepperButton(context, "+");
MaterialButton reset = ViewTreeSupport.outlinedStepperButton(context, "D");
EditText input = ViewTreeSupport.numericInput(context, false, 3);
int current = SbtSettings.clampIconHeightSteps(prefs.getInt(key, defaultValue));
input.setText(String.valueOf(current));
row.addView(minus, ViewTreeSupport.stepperButtonParams(context));
row.addView(input, ViewTreeSupport.stepperInputParams(context));
row.addView(plus, ViewTreeSupport.stepperButtonParams(context));
LinearLayout.LayoutParams resetParams = ViewTreeSupport.stepperButtonParams(context);
resetParams.leftMargin = ViewTreeSupport.dp(context, 8);
row.addView(reset, resetParams);
outer.addView(row);
View.OnClickListener listener = view -> {
int next = SbtSettings.clampIconHeightSteps(
ViewTreeSupport.parseInt(input, current) + (view == minus ? -1 : 1));
persistIconHeight(prefs, key, input, next);
};
minus.setOnClickListener(listener);
plus.setOnClickListener(listener);
reset.setOnClickListener(v -> {
int next = SbtSettings.clampIconHeightSteps(defaultValue);
persistIconHeight(prefs, key, input, next);
});
input.setOnFocusChangeListener((v, hasFocus) -> {
if (!hasFocus) {
int next = SbtSettings.clampIconHeightSteps(ViewTreeSupport.parseInt(input, current));
persistIconHeight(prefs, key, input, next);
}
});
return outer;
}
private void persistIconHeight(
SharedPreferences prefs,
String key,
EditText input,
int value
) {
prefs.edit().putInt(key, value).apply();
SbtSettings.ensureReadable(requireContext());
input.setText(String.valueOf(value));
return ViewTreeSupport.intPreferenceStepper(
requireContext(),
prefs,
labelRes,
key,
defaultValue,
SbtDefaults.LAYOUT_ICON_HEIGHT_STEPS_MIN,
SbtDefaults.LAYOUT_ICON_HEIGHT_STEPS_MAX,
false,
3,
0,
0,
0,
true,
true,
null);
}
private void addChipHeightControl(View root, SharedPreferences prefs) {
@@ -424,11 +348,9 @@ public final class LayoutFragment extends Fragment {
int titleId,
int index,
SbtSettings.IndexedKey positionKey,
SbtSettings.IndexedKey autoNearestKey,
SbtSettings.IndexedKey middleSideKey,
SbtSettings.IndexedKey verticalOffsetKey,
String positionDefault,
boolean autoNearestDefault,
String middleSideDefault,
int verticalOffsetDefault,
String copyTag
@@ -447,11 +369,11 @@ public final class LayoutFragment extends Fragment {
prefs,
R.id.notif_position_group,
R.id.notif_position_middle_options_container,
R.id.notif_middle_auto_nearest,
View.NO_ID,
R.id.notif_middle_side_prompt,
positionKey.key(index),
positionDefault,
autoNearestKey.key(index),
null,
middleSideKey.key(index));
bindStepper(prefs,
verticalOffsetKey.key(index),
@@ -467,11 +389,11 @@ public final class LayoutFragment extends Fragment {
prefs,
R.id.status_position_group,
R.id.status_position_middle_options_container,
R.id.status_middle_auto_nearest,
View.NO_ID,
R.id.status_middle_side_prompt,
positionKey.key(index),
positionDefault,
autoNearestKey.key(index),
null,
middleSideKey.key(index));
bindStepper(prefs,
verticalOffsetKey.key(index),
@@ -563,30 +485,6 @@ public final class LayoutFragment extends Fragment {
}
}
private int iconContainerCount(
SharedPreferences prefs,
String countKey,
int countDefault,
String legacyEnabledKey,
boolean legacyEnabledDefault
) {
int fallback = prefs.getBoolean(legacyEnabledKey, legacyEnabledDefault) ? countDefault : 0;
return SbtSettings.clampIconContainerCount(prefs.getInt(countKey, fallback));
}
private void persistIconContainerCount(
SharedPreferences prefs,
String countKey,
String legacyEnabledKey,
int count
) {
prefs.edit()
.putInt(countKey, SbtSettings.clampIconContainerCount(count))
.putBoolean(legacyEnabledKey, count > 0)
.apply();
SbtSettings.ensureReadable(requireContext());
}
private void hideUnlockedSubpageObsoleteControls(View root) {
hide(root, R.id.clock_position_mode_lockscreen);
hide(root, R.id.notif_position_mode_aod);
@@ -620,59 +518,57 @@ public final class LayoutFragment extends Fragment {
int middleOptionsId,
int autoNearestId,
int promptId,
@Nullable String positionKey,
@Nullable String positionDefault,
String positionKey,
String positionDefault,
@Nullable String autoNearestKey,
@Nullable String middleSideKey) {
String middleSideKey) {
RadioGroup group = root.findViewById(groupId);
View middleOptions = root.findViewById(middleOptionsId);
CheckBox autoNearest = root.findViewById(autoNearestId);
TextView prompt = root.findViewById(promptId);
if (group == null || middleOptions == null || autoNearest == null || prompt == null) {
if (group == null || middleOptions == null || prompt == null) {
return;
}
if (positionKey != null && positionDefault != null) {
group.check(positionIdForValue(groupId, prefs.getString(positionKey, positionDefault)));
}
if (autoNearestKey != null) {
group.check(positionIdForValue(groupId, prefs.getString(positionKey, positionDefault)));
boolean autoNearestAvailable = autoNearestKey != null && autoNearest != null;
if (autoNearestAvailable) {
autoNearest.setChecked(prefs.getBoolean(autoNearestKey, true));
} else if (autoNearest != null) {
autoNearest.setChecked(false);
autoNearest.setVisibility(View.GONE);
}
updateMiddleOptions(middleOptions, autoNearest, prompt, group.getCheckedRadioButtonId());
group.setOnCheckedChangeListener((radioGroup, id) -> {
updateMiddleOptions(middleOptions, autoNearest, prompt, id);
if (positionKey != null) {
prefs.edit().putString(positionKey, positionValueForId(id)).apply();
SbtSettings.ensureReadable(requireContext());
}
prefs.edit().putString(positionKey, positionValueForId(id)).apply();
SbtSettings.ensureReadable(requireContext());
});
autoNearest.setOnCheckedChangeListener((buttonView, isChecked) -> {
updateMiddlePrompt(prompt, isChecked);
if (autoNearestKey != null) {
if (autoNearestAvailable) {
autoNearest.setOnCheckedChangeListener((buttonView, isChecked) -> {
updateMiddlePrompt(prompt, isChecked);
prefs.edit().putBoolean(autoNearestKey, isChecked).apply();
SbtSettings.ensureReadable(requireContext());
}
});
});
}
if (middleSideKey != null) {
RadioGroup sideGroup = resolveMiddleSideGroup(root, groupId);
if (sideGroup != null) {
sideGroup.check(sideIdForValue(sideGroup, prefs.getString(middleSideKey, "left")));
sideGroup.setOnCheckedChangeListener((g, checkedId) -> {
prefs.edit().putString(middleSideKey, checkedId == resolveRightSideId(g) ? "right" : "left").apply();
SbtSettings.ensureReadable(requireContext());
});
}
RadioGroup sideGroup = resolveMiddleSideGroup(root, groupId);
if (sideGroup != null) {
sideGroup.check(sideIdForValue(sideGroup, prefs.getString(middleSideKey, "left")));
sideGroup.setOnCheckedChangeListener((g, checkedId) -> {
prefs.edit().putString(middleSideKey, checkedId == resolveRightSideId(g) ? "right" : "left").apply();
SbtSettings.ensureReadable(requireContext());
});
}
}
private void updateMiddleOptions(View middleOptions,
CheckBox autoNearest,
@Nullable CheckBox autoNearest,
TextView prompt,
int checkedId) {
if (middleOptions == null || autoNearest == null || prompt == null) {
if (middleOptions == null || prompt == null) {
return;
}
if (middleOptions.getVisibility() != View.VISIBLE) {
@@ -681,7 +577,7 @@ public final class LayoutFragment extends Fragment {
boolean enabled = isMiddleSelection(checkedId);
ViewTreeSupport.setEnabledRecursive(middleOptions, enabled);
middleOptions.setAlpha(enabled ? 1f : 0.45f);
updateMiddlePrompt(prompt, autoNearest.isChecked());
updateMiddlePrompt(prompt, autoNearest != null && autoNearest.isChecked());
}
private boolean isMiddleSelection(int checkedId) {
@@ -700,37 +596,6 @@ public final class LayoutFragment extends Fragment {
: R.string.layout_middle_side_prompt_attach_only);
}
private void bindStepper(SharedPreferences prefs,
@Nullable String key,
@Nullable EditText input,
@Nullable MaterialButton minusButton,
@Nullable MaterialButton plusButton,
int initialValue) {
bindStepper(prefs, key, input, null, minusButton, plusButton, null, null, initialValue);
}
private void bindStepper(SharedPreferences prefs,
@Nullable String key,
@Nullable EditText input,
@Nullable MaterialButton minusButton,
@Nullable MaterialButton plusButton,
int initialValue,
int minValue,
int maxValue) {
bindStepper(
prefs,
key,
input,
null,
minusButton,
plusButton,
null,
null,
initialValue,
minValue,
maxValue);
}
private void bindStepper(SharedPreferences prefs,
@Nullable String key,
@Nullable EditText input,
@@ -880,8 +745,7 @@ public final class LayoutFragment extends Fragment {
if (key == null) {
return;
}
prefs.edit().putInt(key, value).apply();
SbtSettings.ensureReadable(requireContext());
ViewTreeSupport.persistIntPreference(requireContext(), prefs, key, value);
}
private int positionIdForValue(int groupId, @Nullable String value) {
@@ -6,7 +6,6 @@ import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.RadioGroup;
import android.widget.ScrollView;
@@ -16,7 +15,6 @@ 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.switchmaterial.SwitchMaterial;
import se.ajpanton.statusbartweak.R;
@@ -38,7 +36,7 @@ public final class LayoutHomeFragment extends Fragment {
Context context = requireContext();
SharedPreferences prefs = context.getSharedPreferences(SbtSettings.PREFS_NAME, Context.MODE_PRIVATE);
ScrollView scroll = new ScrollView(context);
LinearLayout root = vertical(context);
LinearLayout root = ViewTreeSupport.verticalLayout(context);
root.setPadding(
ViewTreeSupport.dp(context, 20),
ViewTreeSupport.dp(context, 20),
@@ -46,23 +44,23 @@ public final class LayoutHomeFragment extends Fragment {
ViewTreeSupport.dp(context, 20));
scroll.addView(root);
TextView title = title(context, R.string.layout_home_title);
TextView title = ViewTreeSupport.titleText(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));
root.addView(outlinedCard(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);
lockedModeHint = ViewTreeSupport.bodyText(context, R.string.layout_locked_notifications_hint, 0);
LinearLayout lockedContent = ViewTreeSupport.verticalLayout(context);
lockedContent.addView(lockedModeHint);
lockedContent.addView(lockedModeGroup);
root.addView(card(context, R.string.layout_locked_notifications_title, lockedContent));
root.addView(outlinedCard(context, R.string.layout_locked_notifications_title, lockedContent));
layoutOnContainer = vertical(context);
layoutOnContainer = ViewTreeSupport.verticalLayout(context);
layoutOnContainer.addView(paddingCard(context, prefs));
layoutOnContainer.addView(LayoutOrderingUi.createCard(context, prefs));
root.addView(layoutOnContainer);
@@ -94,7 +92,7 @@ public final class LayoutHomeFragment extends Fragment {
}
private View paddingCard(Context context, SharedPreferences prefs) {
LinearLayout content = vertical(context);
LinearLayout content = ViewTreeSupport.verticalLayout(context);
content.addView(stepper(
context,
prefs,
@@ -141,12 +139,12 @@ public final class LayoutHomeFragment extends Fragment {
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);
return outlinedCard(context, R.string.layout_padding_title, content);
}
private View miscCard(Context context, SharedPreferences prefs) {
LinearLayout content = vertical(context);
miscLayoutOnContainer = vertical(context);
LinearLayout content = ViewTreeSupport.verticalLayout(context);
miscLayoutOnContainer = ViewTreeSupport.verticalLayout(context);
miscLayoutOnContainer.addView(ViewTreeSupport.boundCheckBox(
context,
prefs,
@@ -155,7 +153,7 @@ public final class LayoutHomeFragment extends Fragment {
SbtDefaults.LAYOUT_IGNORE_UNDER_DISPLAY_CAMERAS_DEFAULT,
false));
content.addView(miscLayoutOnContainer);
miscUnlockedOffOnlyContainer = vertical(context);
miscUnlockedOffOnlyContainer = ViewTreeSupport.verticalLayout(context);
miscUnlockedOffOnlyContainer.addView(stepper(
context,
prefs,
@@ -165,7 +163,7 @@ public final class LayoutHomeFragment extends Fragment {
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);
return outlinedCard(context, R.string.layout_misc_title, content);
}
private View stepper(
@@ -190,65 +188,28 @@ public final class LayoutHomeFragment extends Fragment {
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, ViewTreeSupport.dp(context, 6), 0, ViewTreeSupport.dp(context, 8));
MaterialButton minus = ViewTreeSupport.outlinedStepperButton(context, "-");
MaterialButton plus = ViewTreeSupport.outlinedStepperButton(context, "+");
EditText input = ViewTreeSupport.numericInput(context, true, 5);
input.setText(String.valueOf(ViewTreeSupport.clamp(prefs.getInt(key, defaultValue), min, max)));
row.addView(minus, ViewTreeSupport.stepperButtonParams(context));
row.addView(input, ViewTreeSupport.stepperInputParams(context));
row.addView(plus, ViewTreeSupport.stepperButtonParams(context));
if (trailingView != null) {
LinearLayout.LayoutParams trailingParams = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT);
trailingParams.leftMargin = ViewTreeSupport.dp(context, 8);
row.addView(trailingView, trailingParams);
}
outer.addView(row);
View.OnClickListener listener = v -> {
int delta = v == minus ? -1 : 1;
int next = ViewTreeSupport.clamp(ViewTreeSupport.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 = ViewTreeSupport.clamp(ViewTreeSupport.parseInt(input, defaultValue), min, max);
input.setText(String.valueOf(next));
prefs.edit().putInt(key, next).apply();
SbtSettings.ensureReadable(context);
}
});
return outer;
return ViewTreeSupport.intPreferenceStepper(
context,
prefs,
labelId,
key,
defaultValue,
min,
max,
true,
5,
0,
6,
8,
false,
false,
trailingView);
}
private View card(Context context, int titleId, View content) {
return ViewTreeSupport.outlinedCard(context, sectionTitle(context, titleId), content);
}
private LinearLayout vertical(Context context) {
return ViewTreeSupport.verticalLayout(context);
}
private TextView title(Context context, int stringId) {
return ViewTreeSupport.titleText(context, stringId);
}
private TextView sectionTitle(Context context, int stringId) {
return ViewTreeSupport.sectionTitleText(context, stringId, true);
}
private TextView body(Context context, int stringId) {
return ViewTreeSupport.bodyText(context, stringId, 0);
private View outlinedCard(Context context, int titleId, View content) {
return ViewTreeSupport.outlinedCard(
context,
ViewTreeSupport.sectionTitleText(context, titleId, true),
content);
}
}
@@ -30,8 +30,8 @@ final class LayoutOrderingUi {
}
static View createCard(Context context, SharedPreferences prefs) {
LinearLayout content = vertical(context);
TextView hint = body(context, R.string.layout_ordering_hint);
LinearLayout content = ViewTreeSupport.verticalLayout(context);
TextView hint = bodyText(context, R.string.layout_ordering_hint);
content.addView(hint);
RecyclerView list = new RecyclerView(context);
@@ -43,7 +43,7 @@ final class LayoutOrderingUi {
content.addView(list, listParams);
bindOrderingList(context, prefs, list);
TextView sortTitle = body(context, R.string.layout_sort_icons_title);
TextView sortTitle = bodyText(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,
@@ -71,7 +71,10 @@ final class LayoutOrderingUi {
sortParams.topMargin = ViewTreeSupport.dp(context, 8);
content.addView(sortGroup, sortParams);
return card(context, R.string.layout_ordering_title, content);
return ViewTreeSupport.outlinedCard(
context,
ViewTreeSupport.sectionTitleText(context, R.string.layout_ordering_title, true),
content);
}
static ArrayList<String> orderedKeys(SharedPreferences prefs) {
@@ -225,18 +228,7 @@ final class LayoutOrderingUi {
return tag instanceof String value ? value : "automatic";
}
private static View card(Context context, int titleId, View content) {
return ViewTreeSupport.outlinedCard(
context,
ViewTreeSupport.sectionTitleText(context, titleId, true),
content);
}
private static LinearLayout vertical(Context context) {
return ViewTreeSupport.verticalLayout(context);
}
private static TextView body(Context context, int stringId) {
private static TextView bodyText(Context context, int stringId) {
TextView view = new TextView(context);
view.setText(stringId);
view.setTextAppearance(com.google.android.material.R.style.TextAppearance_MaterialComponents_Body2);
@@ -197,7 +197,6 @@ final class LayoutSectionCopySupport {
copyInt(editor, prefs, src.iconHeight, dst.iconHeight, src.iconHeightDefault);
for (int i = 0; i < MAX_COPIES; i++) {
copyString(editor, prefs, src.positionAt(i), dst.positionAt(i), src.positionDefault);
copyBoolean(editor, prefs, src.autoNearestAt(i), dst.autoNearestAt(i), src.autoNearestDefault);
copyString(editor, prefs, src.middleSideAt(i), dst.middleSideAt(i), src.middleSideDefault);
copyInt(editor, prefs, src.verticalOffsetAt(i), dst.verticalOffsetAt(i), src.verticalOffsetDefault);
}
@@ -276,14 +275,12 @@ final class LayoutSectionCopySupport {
SbtSettings.KEY_LAYOUT_NOTIF_ENABLED_LOCKSCREEN,
SbtSettings.KEY_LAYOUT_NOTIF_LOCK_COUNT,
SbtSettings.KEY_LAYOUT_NOTIF_LOCK_POSITION,
SbtSettings.KEY_LAYOUT_NOTIF_LOCK_MIDDLE_AUTO_NEAREST_SIDE,
SbtSettings.KEY_LAYOUT_NOTIF_LOCK_MIDDLE_SIDE,
SbtSettings.KEY_LAYOUT_NOTIF_LOCK_VERTICAL_OFFSET_PX,
SbtSettings.layoutLockNotifIconHeightKey(),
SbtDefaults.LAYOUT_NOTIF_ENABLED_LOCKSCREEN_DEFAULT,
SbtDefaults.LAYOUT_NOTIF_UNLOCKED_COUNT_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,
SbtDefaults.LAYOUT_NOTIF_LOCK_ICON_HEIGHT_STEPS_DEFAULT);
@@ -293,14 +290,12 @@ final class LayoutSectionCopySupport {
SbtSettings.KEY_LAYOUT_NOTIF_ENABLED_AOD,
SbtSettings.KEY_LAYOUT_NOTIF_AOD_COUNT,
SbtSettings.KEY_LAYOUT_NOTIF_AOD_POSITION,
SbtSettings.KEY_LAYOUT_NOTIF_AOD_MIDDLE_AUTO_NEAREST_SIDE,
SbtSettings.KEY_LAYOUT_NOTIF_AOD_MIDDLE_SIDE,
SbtSettings.KEY_LAYOUT_NOTIF_AOD_VERTICAL_OFFSET_PX,
SbtSettings.layoutAodNotifIconHeightKey(),
SbtDefaults.LAYOUT_NOTIF_ENABLED_AOD_DEFAULT,
SbtDefaults.LAYOUT_NOTIF_UNLOCKED_COUNT_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,
SbtDefaults.LAYOUT_NOTIF_AOD_ICON_HEIGHT_STEPS_DEFAULT);
@@ -309,14 +304,12 @@ final class LayoutSectionCopySupport {
SbtSettings.KEY_LAYOUT_NOTIF_ENABLED_UNLOCKED,
SbtSettings.KEY_LAYOUT_NOTIF_UNLOCKED_COUNT,
SbtSettings.KEY_LAYOUT_NOTIF_POSITION,
SbtSettings.KEY_LAYOUT_NOTIF_MIDDLE_AUTO_NEAREST_SIDE,
SbtSettings.KEY_LAYOUT_NOTIF_MIDDLE_SIDE,
SbtSettings.KEY_LAYOUT_NOTIF_VERTICAL_OFFSET_PX,
SbtSettings.KEY_LAYOUT_NOTIF_ICON_HEIGHT_STEPS,
SbtDefaults.LAYOUT_NOTIF_ENABLED_UNLOCKED_DEFAULT,
SbtDefaults.LAYOUT_NOTIF_UNLOCKED_COUNT_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,
SbtDefaults.LAYOUT_NOTIF_ICON_HEIGHT_STEPS_DEFAULT);
@@ -328,14 +321,12 @@ final class LayoutSectionCopySupport {
SbtSettings.KEY_LAYOUT_STATUS_ENABLED_LOCKSCREEN,
SbtSettings.KEY_LAYOUT_STATUS_LOCK_COUNT,
SbtSettings.KEY_LAYOUT_STATUS_LOCK_POSITION,
SbtSettings.KEY_LAYOUT_STATUS_LOCK_MIDDLE_AUTO_NEAREST_SIDE,
SbtSettings.KEY_LAYOUT_STATUS_LOCK_MIDDLE_SIDE,
SbtSettings.KEY_LAYOUT_STATUS_LOCK_VERTICAL_OFFSET_PX,
SbtSettings.layoutLockStatusIconHeightKey(),
SbtDefaults.LAYOUT_STATUS_ENABLED_LOCKSCREEN_DEFAULT,
SbtDefaults.LAYOUT_STATUS_UNLOCKED_COUNT_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,
SbtDefaults.LAYOUT_STATUS_ICON_HEIGHT_STEPS_DEFAULT);
@@ -345,14 +336,12 @@ final class LayoutSectionCopySupport {
SbtSettings.KEY_LAYOUT_STATUS_ENABLED_AOD,
SbtSettings.KEY_LAYOUT_STATUS_AOD_COUNT,
SbtSettings.KEY_LAYOUT_STATUS_AOD_POSITION,
SbtSettings.KEY_LAYOUT_STATUS_AOD_MIDDLE_AUTO_NEAREST_SIDE,
SbtSettings.KEY_LAYOUT_STATUS_AOD_MIDDLE_SIDE,
SbtSettings.KEY_LAYOUT_STATUS_AOD_VERTICAL_OFFSET_PX,
SbtSettings.layoutAodStatusIconHeightKey(),
SbtDefaults.LAYOUT_STATUS_ENABLED_AOD_DEFAULT,
SbtDefaults.LAYOUT_STATUS_UNLOCKED_COUNT_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,
SbtDefaults.LAYOUT_STATUS_ICON_HEIGHT_STEPS_DEFAULT);
@@ -361,14 +350,12 @@ final class LayoutSectionCopySupport {
SbtSettings.KEY_LAYOUT_STATUS_ENABLED_UNLOCKED,
SbtSettings.KEY_LAYOUT_STATUS_UNLOCKED_COUNT,
SbtSettings.KEY_LAYOUT_STATUS_POSITION,
SbtSettings.KEY_LAYOUT_STATUS_MIDDLE_AUTO_NEAREST_SIDE,
SbtSettings.KEY_LAYOUT_STATUS_MIDDLE_SIDE,
SbtSettings.KEY_LAYOUT_STATUS_VERTICAL_OFFSET_PX,
SbtSettings.KEY_LAYOUT_STATUS_ICON_HEIGHT_STEPS,
SbtDefaults.LAYOUT_STATUS_ENABLED_UNLOCKED_DEFAULT,
SbtDefaults.LAYOUT_STATUS_UNLOCKED_COUNT_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,
SbtDefaults.LAYOUT_STATUS_ICON_HEIGHT_STEPS_DEFAULT);
@@ -405,14 +392,12 @@ final class LayoutSectionCopySupport {
String enabled,
String count,
String position,
String autoNearest,
String middleSide,
String verticalOffset,
String iconHeight,
boolean enabledDefault,
int countDefault,
String positionDefault,
boolean autoNearestDefault,
String middleSideDefault,
int verticalOffsetDefault,
int iconHeightDefault
@@ -420,14 +405,14 @@ final class LayoutSectionCopySupport {
return new KeySet(
enabled,
position,
autoNearest,
null,
middleSide,
verticalOffset,
iconHeight,
count,
enabledDefault,
positionDefault,
autoNearestDefault,
false,
middleSideDefault,
verticalOffsetDefault,
iconHeightDefault,
@@ -701,10 +686,6 @@ final class LayoutSectionCopySupport {
return suffixed(position, index);
}
String autoNearestAt(int index) {
return suffixed(autoNearest, index);
}
String middleSideAt(int index) {
return suffixed(middleSide, index);
}
@@ -4,7 +4,6 @@ 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;
@@ -20,6 +19,7 @@ import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import com.google.android.material.button.MaterialButton;
import java.util.LinkedHashMap;
import se.ajpanton.statusbartweak.R;
@@ -44,7 +44,7 @@ abstract class LockedSceneLayoutFragment extends Fragment {
Context context = requireContext();
SharedPreferences prefs = context.getSharedPreferences(SbtSettings.PREFS_NAME, Context.MODE_PRIVATE);
ScrollView scroll = new ScrollView(context);
LinearLayout root = vertical(context);
LinearLayout root = ViewTreeSupport.verticalLayout(context);
root.setPadding(
ViewTreeSupport.dp(context, 20),
ViewTreeSupport.dp(context, 20),
@@ -52,16 +52,18 @@ abstract class LockedSceneLayoutFragment extends Fragment {
ViewTreeSupport.dp(context, 20));
scroll.addView(root);
root.addView(title(context, aod ? R.string.section_aod : R.string.section_lockscreen));
root.addView(ViewTreeSupport.titleText(
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);
modeHint = ViewTreeSupport.bodyText(context, R.string.layout_locked_notifications_hint, 6);
LinearLayout modeContent = ViewTreeSupport.verticalLayout(context);
modeContent.addView(modeHint);
modeContent.addView(modeGroup);
root.addView(card(context, R.string.layout_locked_notifications_title, modeContent));
root.addView(outlinedCard(context, R.string.layout_locked_notifications_title, modeContent));
dynamicContent = vertical(context);
dynamicContent = ViewTreeSupport.verticalLayout(context);
root.addView(dynamicContent);
bindMode(context, prefs);
return scroll;
@@ -142,8 +144,8 @@ abstract class LockedSceneLayoutFragment extends Fragment {
R.id.clock_vertical_offset_reset,
SbtSettings.KEY_LAYOUT_CARRIER_POSITION,
SbtDefaults.LAYOUT_CARRIER_POSITION_DEFAULT,
SbtSettings.KEY_LAYOUT_CARRIER_MIDDLE_AUTO_NEAREST_SIDE,
SbtDefaults.LAYOUT_CARRIER_MIDDLE_AUTO_NEAREST_SIDE_DEFAULT,
null,
false,
SbtSettings.KEY_LAYOUT_CARRIER_MIDDLE_SIDE,
SbtDefaults.LAYOUT_CARRIER_MIDDLE_SIDE_DEFAULT,
SbtSettings.KEY_LAYOUT_CARRIER_VERTICAL_OFFSET_PX,
@@ -159,7 +161,7 @@ abstract class LockedSceneLayoutFragment extends Fragment {
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,
View.NO_ID,
R.id.status_middle_side_prompt,
R.id.status_middle_side_group,
R.id.status_middle_side_right,
@@ -173,7 +175,6 @@ abstract class LockedSceneLayoutFragment extends Fragment {
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,
aod ? SbtSettings.layoutAodStatusIconHeightKey() : SbtSettings.layoutLockStatusIconHeightKey(),
@@ -192,7 +193,7 @@ abstract class LockedSceneLayoutFragment extends Fragment {
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,
View.NO_ID,
R.id.notif_middle_side_prompt,
R.id.notif_middle_side_group,
R.id.notif_middle_side_right,
@@ -206,7 +207,6 @@ abstract class LockedSceneLayoutFragment extends Fragment {
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,
aod ? SbtSettings.layoutAodNotifIconHeightKey() : SbtSettings.layoutLockNotifIconHeightKey(),
@@ -307,7 +307,6 @@ abstract class LockedSceneLayoutFragment extends Fragment {
String legacyEnabledKey,
boolean legacyEnabledDefault,
String positionDefault,
boolean autoNearestDefault,
String middleSideDefault,
int verticalOffsetDefault,
String iconHeightKey,
@@ -316,12 +315,17 @@ abstract class LockedSceneLayoutFragment extends Fragment {
boolean cardsMode,
@Nullable Runnable refresh
) {
LinearLayout wrapper = vertical(context);
LinearLayout wrapper = ViewTreeSupport.verticalLayout(context);
String countKey = sceneKey(base + "_count");
Runnable[] rebuild = new Runnable[1];
rebuild[0] = () -> {
wrapper.removeAllViews();
int count = iconContainerCount(prefs, countKey, legacyEnabledKey, legacyEnabledDefault);
int count = ViewTreeSupport.iconContainerCount(
prefs,
countKey,
legacyEnabledKey,
1,
legacyEnabledDefault);
View primary = configuredIconContainerCard(
context,
prefs,
@@ -343,8 +347,6 @@ abstract class LockedSceneLayoutFragment extends Fragment {
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),
@@ -379,8 +381,6 @@ abstract class LockedSceneLayoutFragment extends Fragment {
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),
@@ -441,8 +441,6 @@ abstract class LockedSceneLayoutFragment extends Fragment {
int index,
String positionKey,
String positionDefault,
String autoNearestKey,
boolean autoNearestDefault,
String middleSideKey,
String middleSideDefault,
String verticalOffsetKey,
@@ -460,7 +458,16 @@ abstract class LockedSceneLayoutFragment extends Fragment {
if (sectionContent != null && countKey != null && onCountChanged != null) {
int insertIndex = Math.min(1, sectionContent.getChildCount());
sectionContent.addView(
iconContainerCountControl(context, prefs, countKey, legacyEnabledKey, count, onCountChanged),
ViewTreeSupport.iconContainerCountControl(
context,
count,
value -> ViewTreeSupport.persistIconContainerCount(
context,
prefs,
countKey,
legacyEnabledKey,
value),
onCountChanged),
insertIndex);
if (iconHeightKey != null) {
sectionContent.addView(
@@ -487,8 +494,8 @@ abstract class LockedSceneLayoutFragment extends Fragment {
middleSideRightId,
positionKey,
positionDefault,
autoNearestKey,
autoNearestDefault,
null,
false,
middleSideKey,
middleSideDefault);
bindXmlStepper(
@@ -544,7 +551,7 @@ abstract class LockedSceneLayoutFragment extends Fragment {
}
private View cardsNotificationCard(Context context, SharedPreferences prefs, @Nullable Runnable refresh) {
LinearLayout content = vertical(context);
LinearLayout content = ViewTreeSupport.verticalLayout(context);
LayoutSectionCopySupport.addCardsDropdown(context, prefs, content, copyMode(), refresh);
content.addView(stepper(
context,
@@ -586,48 +593,7 @@ abstract class LockedSceneLayoutFragment extends Fragment {
: SbtDefaults.LAYOUT_CARDS_NOTIF_LOCK_ICON_HEIGHT_STEPS_DEFAULT,
SbtDefaults.LAYOUT_ICON_HEIGHT_STEPS_MIN,
SbtDefaults.LAYOUT_ICON_HEIGHT_STEPS_MAX));
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 = ViewTreeSupport.outlinedStepperButton(context, "-");
MaterialButton plus = ViewTreeSupport.outlinedStepperButton(context, "+");
EditText input = ViewTreeSupport.numericInput(context, false, 4);
input.setFilters(new InputFilter[]{new InputFilter.LengthFilter(1)});
input.setText(String.valueOf(current));
row.addView(minus, ViewTreeSupport.stepperButtonParams(context));
row.addView(input, ViewTreeSupport.stepperInputParams(context));
row.addView(plus, ViewTreeSupport.stepperButtonParams(context));
outer.addView(row);
View.OnClickListener listener = view -> {
int next = SbtSettings.clampIconContainerCount(
ViewTreeSupport.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 = SbtSettings.clampIconContainerCount(ViewTreeSupport.parseInt(input, current));
persistIconContainerCount(prefs, countKey, legacyEnabledKey, next);
onChanged.run();
}
});
return outer;
return outlinedCard(context, R.string.layout_notification_position_title, content);
}
private void bindXmlPositionSection(
@@ -641,7 +607,7 @@ abstract class LockedSceneLayoutFragment extends Fragment {
int middleSideRightId,
String positionKey,
String positionDefault,
String autoNearestKey,
@Nullable String autoNearestKey,
boolean autoNearestDefault,
String middleSideKey,
String middleSideDefault
@@ -651,17 +617,23 @@ abstract class LockedSceneLayoutFragment extends Fragment {
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) {
if (position == null || middleOptions == null || prompt == null || side == null) {
return;
}
position.check(positionIdForValue(groupId, prefs.getString(positionKey, positionDefault)));
autoNearest.setChecked(prefs.getBoolean(autoNearestKey, autoNearestDefault));
boolean autoNearestAvailable = autoNearestKey != null && autoNearest != null;
if (autoNearestAvailable) {
autoNearest.setChecked(prefs.getBoolean(autoNearestKey, autoNearestDefault));
} else if (autoNearest != null) {
autoNearest.setChecked(false);
autoNearest.setVisibility(View.GONE);
}
side.check("right".equals(prefs.getString(middleSideKey, middleSideDefault))
? middleSideRightId
: firstSideId(side, middleSideRightId));
updateMiddleOptions(middleOptions, "middle".equals(positionValueForId(position.getCheckedRadioButtonId())));
prompt.setText(autoNearest.isChecked()
prompt.setText(autoNearest != null && autoNearest.isChecked()
? R.string.layout_middle_side_prompt_centred
: R.string.layout_middle_side_prompt_attach_only);
@@ -670,13 +642,15 @@ abstract class LockedSceneLayoutFragment extends Fragment {
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());
});
if (autoNearestAvailable) {
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());
@@ -709,14 +683,14 @@ abstract class LockedSceneLayoutFragment extends Fragment {
if (reset != null) {
reset.setOnClickListener(v -> {
input.setText(String.valueOf(defaultValue));
persistIntPref(prefs, key, defaultValue);
ViewTreeSupport.persistIntPreference(requireContext(), prefs, key, defaultValue);
});
}
input.setOnFocusChangeListener((v, hasFocus) -> {
if (!hasFocus) {
int next = ViewTreeSupport.clamp(ViewTreeSupport.parseInt(input, defaultValue), min, max);
input.setText(String.valueOf(next));
persistIntPref(prefs, key, next);
ViewTreeSupport.persistIntPreference(requireContext(), prefs, key, next);
}
});
}
@@ -736,7 +710,7 @@ abstract class LockedSceneLayoutFragment extends Fragment {
button.setOnClickListener(v -> {
int next = ViewTreeSupport.clamp(ViewTreeSupport.parseInt(input, 0) + delta, min, max);
input.setText(String.valueOf(next));
persistIntPref(prefs, key, next);
ViewTreeSupport.persistIntPreference(requireContext(), prefs, key, next);
});
}
@@ -822,79 +796,22 @@ abstract class LockedSceneLayoutFragment extends Fragment {
int max,
boolean showDefaultButton
) {
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 = ViewTreeSupport.outlinedStepperButton(context, "-");
MaterialButton plus = ViewTreeSupport.outlinedStepperButton(context, "+");
MaterialButton reset = showDefaultButton
? ViewTreeSupport.outlinedStepperButton(context, "D")
: null;
EditText input = ViewTreeSupport.numericInput(context, true, 4);
input.setText(String.valueOf(ViewTreeSupport.clamp(prefs.getInt(key, def), min, max)));
row.addView(minus, ViewTreeSupport.stepperButtonParams(context));
row.addView(input, ViewTreeSupport.stepperInputParams(context));
row.addView(plus, ViewTreeSupport.stepperButtonParams(context));
if (reset != null) {
LinearLayout.LayoutParams resetParams = ViewTreeSupport.stepperButtonParams(context);
resetParams.leftMargin = ViewTreeSupport.dp(context, 8);
row.addView(reset, resetParams);
}
outer.addView(row);
View.OnClickListener listener = view -> {
int delta = view == minus ? -1 : 1;
int next = ViewTreeSupport.clamp(ViewTreeSupport.parseInt(input, def) + delta, min, max);
input.setText(String.valueOf(next));
persistIntPref(prefs, key, next);
};
minus.setOnClickListener(listener);
plus.setOnClickListener(listener);
if (reset != null) {
reset.setOnClickListener(v -> {
int next = ViewTreeSupport.clamp(def, min, max);
input.setText(String.valueOf(next));
persistIntPref(prefs, key, next);
});
}
input.setOnFocusChangeListener((v, hasFocus) -> {
if (!hasFocus) {
int next = ViewTreeSupport.clamp(ViewTreeSupport.parseInt(input, def), min, max);
input.setText(String.valueOf(next));
persistIntPref(prefs, key, next);
}
});
return outer;
}
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 SbtSettings.clampIconContainerCount(prefs.getInt(countKey, fallback));
}
private void persistIconContainerCount(
SharedPreferences prefs,
String countKey,
String legacyEnabledKey,
int count
) {
int clamped = SbtSettings.clampIconContainerCount(count);
prefs.edit()
.putInt(countKey, clamped)
.putBoolean(legacyEnabledKey, clamped > 0)
.apply();
SbtSettings.ensureReadable(requireContext());
return ViewTreeSupport.intPreferenceStepper(
context,
prefs,
label,
key,
def,
min,
max,
true,
4,
6,
0,
0,
showDefaultButton,
false,
null);
}
private String sceneKey(String suffix) {
@@ -922,23 +839,10 @@ abstract class LockedSceneLayoutFragment extends Fragment {
return content instanceof LinearLayout layout ? layout : null;
}
private View card(Context context, int titleId, View content) {
return ViewTreeSupport.outlinedCard(context, sectionTitle(context, titleId), content);
}
private LinearLayout vertical(Context context) {
return ViewTreeSupport.verticalLayout(context);
}
private TextView title(Context context, int text) {
return ViewTreeSupport.titleText(context, text);
}
private TextView sectionTitle(Context context, int text) {
return ViewTreeSupport.sectionTitleText(context, text, false);
}
private TextView body(Context context, int text) {
return ViewTreeSupport.bodyText(context, text, 6);
private View outlinedCard(Context context, int titleId, View content) {
return ViewTreeSupport.outlinedCard(
context,
ViewTreeSupport.sectionTitleText(context, titleId, false),
content);
}
}
@@ -14,8 +14,9 @@ import android.widget.TextView;
import com.google.android.material.button.MaterialButton;
import com.google.android.material.card.MaterialCardView;
import se.ajpanton.statusbartweak.R;
import java.util.function.IntConsumer;
import se.ajpanton.statusbartweak.R;
import se.ajpanton.statusbartweak.shell.settings.SbtSettings;
final class ViewTreeSupport {
@@ -149,6 +150,160 @@ final class ViewTreeSupport {
return input;
}
static int iconContainerCount(
SharedPreferences prefs,
String countKey,
String legacyEnabledKey,
int defaultCount,
boolean legacyEnabledDefault
) {
int fallback = prefs.getBoolean(legacyEnabledKey, legacyEnabledDefault) ? defaultCount : 0;
return SbtSettings.clampIconContainerCount(prefs.getInt(countKey, fallback));
}
static void persistIconContainerCount(
Context context,
SharedPreferences prefs,
String countKey,
String legacyEnabledKey,
int count
) {
int clamped = SbtSettings.clampIconContainerCount(count);
prefs.edit()
.putInt(countKey, clamped)
.putBoolean(legacyEnabledKey, clamped > 0)
.apply();
SbtSettings.ensureReadable(context);
}
static View iconContainerCountControl(
Context context,
int current,
IntConsumer countPersister,
Runnable onChanged
) {
LinearLayout outer = verticalLayout(context);
outer.addView(bodyText(context, R.string.layout_icon_container_count, 0));
LinearLayout row = new LinearLayout(context);
row.setOrientation(LinearLayout.HORIZONTAL);
row.setGravity(android.view.Gravity.CENTER_VERTICAL);
MaterialButton minus = outlinedStepperButton(context, "-");
MaterialButton plus = outlinedStepperButton(context, "+");
EditText input = numericInput(context, false, 1);
input.setText(String.valueOf(current));
row.addView(minus, stepperButtonParams(context));
row.addView(input, stepperInputParams(context));
row.addView(plus, stepperButtonParams(context));
outer.addView(row);
View.OnClickListener listener = view -> {
int next = SbtSettings.clampIconContainerCount(
parseInt(input, current) + (view == minus ? -1 : 1));
countPersister.accept(next);
onChanged.run();
};
minus.setOnClickListener(listener);
plus.setOnClickListener(listener);
input.setOnFocusChangeListener((v, hasFocus) -> {
if (!hasFocus) {
int next = SbtSettings.clampIconContainerCount(parseInt(input, current));
countPersister.accept(next);
onChanged.run();
}
});
return outer;
}
static View intPreferenceStepper(
Context context,
SharedPreferences prefs,
int labelRes,
String key,
int defaultValue,
int min,
int max,
boolean signed,
int inputMaxLength,
int labelBottomPaddingDp,
int rowTopPaddingDp,
int rowBottomPaddingDp,
boolean showDefaultButton,
boolean useCurrentAsParseFallback,
View trailingView
) {
LinearLayout outer = verticalLayout(context);
outer.addView(bodyText(context, labelRes, labelBottomPaddingDp));
LinearLayout row = new LinearLayout(context);
row.setOrientation(LinearLayout.HORIZONTAL);
row.setGravity(android.view.Gravity.CENTER_VERTICAL);
row.setPadding(0, dp(context, rowTopPaddingDp), 0, dp(context, rowBottomPaddingDp));
MaterialButton minus = outlinedStepperButton(context, "-");
MaterialButton plus = outlinedStepperButton(context, "+");
MaterialButton reset = showDefaultButton ? outlinedStepperButton(context, "D") : null;
EditText input = numericInput(context, signed, inputMaxLength);
int current = clamp(prefs.getInt(key, defaultValue), min, max);
int parseFallback = useCurrentAsParseFallback ? current : defaultValue;
input.setText(String.valueOf(current));
row.addView(minus, stepperButtonParams(context));
row.addView(input, stepperInputParams(context));
row.addView(plus, stepperButtonParams(context));
if (reset != null) {
LinearLayout.LayoutParams resetParams = stepperButtonParams(context);
resetParams.leftMargin = dp(context, 8);
row.addView(reset, resetParams);
}
if (trailingView != null) {
LinearLayout.LayoutParams trailingParams = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT);
trailingParams.leftMargin = dp(context, 8);
row.addView(trailingView, trailingParams);
}
outer.addView(row);
View.OnClickListener listener = view -> {
int delta = view == minus ? -1 : 1;
int next = clamp(parseInt(input, parseFallback) + delta, min, max);
persistIntPreference(context, prefs, key, input, next);
};
minus.setOnClickListener(listener);
plus.setOnClickListener(listener);
if (reset != null) {
reset.setOnClickListener(v -> persistIntPreference(
context,
prefs,
key,
input,
clamp(defaultValue, min, max)));
}
input.setOnFocusChangeListener((v, hasFocus) -> {
if (!hasFocus) {
int next = clamp(parseInt(input, parseFallback), min, max);
persistIntPreference(context, prefs, key, input, next);
}
});
return outer;
}
static void persistIntPreference(
Context context,
SharedPreferences prefs,
String key,
EditText input,
int value
) {
input.setText(String.valueOf(value));
persistIntPreference(context, prefs, key, value);
}
static void persistIntPreference(Context context, SharedPreferences prefs, String key, int value) {
prefs.edit().putInt(key, value).apply();
SbtSettings.ensureReadable(context);
}
static LinearLayout verticalLayout(Context context) {
LinearLayout layout = new LinearLayout(context);
layout.setOrientation(LinearLayout.VERTICAL);
@@ -317,18 +317,10 @@
android:orientation="vertical"
android:visibility="gone">
<CheckBox
android:id="@+id/chip_middle_auto_nearest"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:checked="true"
android:text="@string/layout_middle_auto_nearest" />
<TextView
android:id="@+id/chip_middle_side_prompt"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:text="@string/layout_middle_side_prompt"
android:textAppearance="?attr/textAppearanceBody2" />
@@ -546,18 +538,10 @@
android:orientation="vertical"
android:visibility="gone">
<CheckBox
android:id="@+id/notif_middle_auto_nearest"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:checked="true"
android:text="@string/layout_middle_auto_nearest" />
<TextView
android:id="@+id/notif_middle_side_prompt"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:text="@string/layout_middle_side_prompt"
android:textAppearance="?attr/textAppearanceBody2" />
@@ -775,18 +759,10 @@
android:orientation="vertical"
android:visibility="gone">
<CheckBox
android:id="@+id/status_middle_auto_nearest"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:checked="true"
android:text="@string/layout_middle_auto_nearest" />
<TextView
android:id="@+id/status_middle_side_prompt"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:text="@string/layout_middle_side_prompt"
android:textAppearance="?attr/textAppearanceBody2" />
-2
View File
@@ -209,8 +209,6 @@
<string name="clock_position_right">Right</string>
<string name="clock_position_middle">Middle</string>
<string name="clock_vertical_offset_label">Vertical position</string>
<string name="clock_middle_auto_nearest">If Middle overlaps the cutout, move it to the nearest side.</string>
<string name="clock_middle_cutout_prompt">Else, or if the overlap is centred, move clock to:</string>
<string name="clock_middle_side_left">Left side</string>
<string name="clock_middle_side_right">Right side</string>
<string name="clock_custom_format_title">Custom format</string>
@@ -1,17 +0,0 @@
package se.ajpanton.statusbartweak;
import org.junit.Test;
import static org.junit.Assert.*;
/**
* Example local unit test, which will execute on the development machine (host).
*
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
*/
public class ExampleUnitTest {
@Test
public void addition_isCorrect() {
assertEquals(4, 2 + 2);
}
}
@@ -21,9 +21,7 @@ public final class UnlockedLayoutBandTest {
null,
placements(6, 39, 39),
LayoutPosition.LEFT,
false,
AnchorSide.LEFT,
true);
AnchorSide.LEFT);
band.applyPackedIconState(0, 0, true, 14);
band.place(388);
@@ -54,9 +52,7 @@ public final class UnlockedLayoutBandTest {
null,
placements,
LayoutPosition.LEFT,
false,
AnchorSide.LEFT,
true);
AnchorSide.LEFT);
band.applyPackedIconState(2, 3, false, 117);
@@ -86,9 +82,7 @@ public final class UnlockedLayoutBandTest {
null,
placements,
LayoutPosition.LEFT,
false,
AnchorSide.LEFT,
false);
AnchorSide.LEFT);
band.prepareForSide(AnchorSide.RIGHT, "normal");
band.applyPackedContinuousWidth(211, AnchorSide.RIGHT);