Add fullscreen battery bar scenarios and dual SIM signal handling
This commit is contained in:
+557
-10
@@ -3,14 +3,20 @@ package se.ajpanton.statusbartweak.runtime.features.battery;
|
|||||||
import android.content.BroadcastReceiver;
|
import android.content.BroadcastReceiver;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
|
import android.content.res.Configuration;
|
||||||
import android.graphics.Canvas;
|
import android.graphics.Canvas;
|
||||||
import android.graphics.Color;
|
import android.graphics.Color;
|
||||||
import android.graphics.Paint;
|
import android.graphics.Paint;
|
||||||
|
import android.graphics.PixelFormat;
|
||||||
import android.graphics.Rect;
|
import android.graphics.Rect;
|
||||||
import android.os.BatteryManager;
|
import android.os.BatteryManager;
|
||||||
|
import android.view.Gravity;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.view.ViewParent;
|
import android.view.ViewParent;
|
||||||
|
import android.view.WindowInsets;
|
||||||
|
import android.view.WindowManager;
|
||||||
|
import android.view.WindowMetrics;
|
||||||
import android.widget.FrameLayout;
|
import android.widget.FrameLayout;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
@@ -25,13 +31,17 @@ import se.ajpanton.statusbartweak.runtime.ViewIdNames;
|
|||||||
import se.ajpanton.statusbartweak.runtime.features.RuntimeContext;
|
import se.ajpanton.statusbartweak.runtime.features.RuntimeContext;
|
||||||
import se.ajpanton.statusbartweak.runtime.hooks.ReflectionSupport;
|
import se.ajpanton.statusbartweak.runtime.hooks.ReflectionSupport;
|
||||||
import se.ajpanton.statusbartweak.runtime.hooks.XposedHookSupport;
|
import se.ajpanton.statusbartweak.runtime.hooks.XposedHookSupport;
|
||||||
|
import se.ajpanton.statusbartweak.runtime.mode.SceneKey;
|
||||||
import se.ajpanton.statusbartweak.runtime.settings.BroadcastReceiverSupport;
|
import se.ajpanton.statusbartweak.runtime.settings.BroadcastReceiverSupport;
|
||||||
import se.ajpanton.statusbartweak.runtime.settings.RuntimeSettingsCache;
|
import se.ajpanton.statusbartweak.runtime.settings.RuntimeSettingsCache;
|
||||||
|
import se.ajpanton.statusbartweak.shell.settings.BatteryBarGeometry;
|
||||||
import se.ajpanton.statusbartweak.shell.settings.BatteryBarStyle;
|
import se.ajpanton.statusbartweak.shell.settings.BatteryBarStyle;
|
||||||
import se.ajpanton.statusbartweak.shell.settings.SbtSettings;
|
import se.ajpanton.statusbartweak.shell.settings.SbtSettings;
|
||||||
|
|
||||||
final class BatteryBarController {
|
final class BatteryBarController {
|
||||||
|
|
||||||
|
private static final int APPEARANCE_LIGHT_STATUS_BARS = 8;
|
||||||
|
|
||||||
private static final String[] ROOT_CLASSES = new String[] {
|
private static final String[] ROOT_CLASSES = new String[] {
|
||||||
"com.android.systemui.statusbar.phone.PhoneStatusBarView",
|
"com.android.systemui.statusbar.phone.PhoneStatusBarView",
|
||||||
"com.android.systemui.statusbar.phone.KeyguardStatusBarView"
|
"com.android.systemui.statusbar.phone.KeyguardStatusBarView"
|
||||||
@@ -40,8 +50,16 @@ final class BatteryBarController {
|
|||||||
private final RuntimeContext runtimeContext;
|
private final RuntimeContext runtimeContext;
|
||||||
private final Set<View> roots = Collections.newSetFromMap(new WeakHashMap<>());
|
private final Set<View> roots = Collections.newSetFromMap(new WeakHashMap<>());
|
||||||
private final Map<View, BatteryBarView> batteryBarViews = new WeakHashMap<>();
|
private final Map<View, BatteryBarView> batteryBarViews = new WeakHashMap<>();
|
||||||
|
private final Map<View, OverlayBatteryBar> overlayBatteryBars = new WeakHashMap<>();
|
||||||
private final Map<View, View.OnLayoutChangeListener> rootLayoutListeners = new WeakHashMap<>();
|
private final Map<View, View.OnLayoutChangeListener> rootLayoutListeners = new WeakHashMap<>();
|
||||||
|
private final Set<View> pendingOverlayRetries = Collections.newSetFromMap(new WeakHashMap<>());
|
||||||
private final Map<View, String> lastSignatures = new WeakHashMap<>();
|
private final Map<View, String> lastSignatures = new WeakHashMap<>();
|
||||||
|
private final Map<View, String> lastOverlaySignatures = new WeakHashMap<>();
|
||||||
|
private int systemBarAppearance;
|
||||||
|
private int systemBarBehavior;
|
||||||
|
private int systemBarRequestedVisibleTypes = WindowInsets.Type.statusBars();
|
||||||
|
private boolean statusBarTransientShown;
|
||||||
|
private String systemBarPackageName = "";
|
||||||
|
|
||||||
private boolean hooksInstalled;
|
private boolean hooksInstalled;
|
||||||
private boolean receiverRegistered;
|
private boolean receiverRegistered;
|
||||||
@@ -59,6 +77,10 @@ final class BatteryBarController {
|
|||||||
for (String className : ROOT_CLASSES) {
|
for (String className : ROOT_CLASSES) {
|
||||||
installHooksForClass(classLoader, className);
|
installHooksForClass(classLoader, className);
|
||||||
}
|
}
|
||||||
|
installRootTransformHooks();
|
||||||
|
installSystemBarAttributeListener(classLoader);
|
||||||
|
installTransientBarListener(classLoader);
|
||||||
|
runtimeContext.getModeStateRepository().addSceneListener((previous, current) -> refreshAllRoots());
|
||||||
hooksInstalled = true;
|
hooksInstalled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -100,6 +122,113 @@ final class BatteryBarController {
|
|||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
});
|
});
|
||||||
|
XposedHookSupport.hookAllMethodsIfExists(framework, rootClass, "onApplyWindowInsets", chain -> {
|
||||||
|
Object result = chain.proceed();
|
||||||
|
Object target = chain.getThisObject();
|
||||||
|
if (target instanceof View root) {
|
||||||
|
applyBatteryBar(root);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void installRootTransformHooks() {
|
||||||
|
XposedInterface framework = runtimeContext.getFramework();
|
||||||
|
XposedHookSupport.hookAllMethodsIfExists(framework, View.class, "setTranslationY", chain -> {
|
||||||
|
Object result = chain.proceed();
|
||||||
|
Object target = chain.getThisObject();
|
||||||
|
if (target instanceof View root && roots.contains(root)) {
|
||||||
|
applyBatteryBar(root);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
});
|
||||||
|
XposedHookSupport.hookAllMethodsIfExists(framework, View.class, "setY", chain -> {
|
||||||
|
Object result = chain.proceed();
|
||||||
|
Object target = chain.getThisObject();
|
||||||
|
if (target instanceof View root && roots.contains(root)) {
|
||||||
|
applyBatteryBar(root);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
});
|
||||||
|
XposedHookSupport.hookAllMethodsIfExists(framework, View.class, "setAlpha", chain -> {
|
||||||
|
Object result = chain.proceed();
|
||||||
|
Object target = chain.getThisObject();
|
||||||
|
if (target instanceof View root && roots.contains(root)) {
|
||||||
|
applyBatteryBar(root);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
});
|
||||||
|
XposedHookSupport.hookAllMethodsIfExists(framework, View.class, "setVisibility", chain -> {
|
||||||
|
Object result = chain.proceed();
|
||||||
|
Object target = chain.getThisObject();
|
||||||
|
if (target instanceof View root && roots.contains(root)) {
|
||||||
|
applyBatteryBar(root);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void installSystemBarAttributeListener(ClassLoader classLoader) {
|
||||||
|
Class<?> commandQueueClass = ReflectionSupport.findClassIfExists(
|
||||||
|
"com.android.systemui.statusbar.CommandQueue",
|
||||||
|
classLoader);
|
||||||
|
if (commandQueueClass == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
XposedHookSupport.hookAllMethodsIfExists(
|
||||||
|
runtimeContext.getFramework(),
|
||||||
|
commandQueueClass,
|
||||||
|
"onSystemBarAttributesChanged",
|
||||||
|
chain -> {
|
||||||
|
Object result = chain.proceed();
|
||||||
|
updateSystemBarState(chain.getArgs());
|
||||||
|
return result;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void installTransientBarListener(ClassLoader classLoader) {
|
||||||
|
Class<?> commandQueueClass = ReflectionSupport.findClassIfExists(
|
||||||
|
"com.android.systemui.statusbar.CommandQueue",
|
||||||
|
classLoader);
|
||||||
|
if (commandQueueClass != null) {
|
||||||
|
XposedHookSupport.hookAllMethodsIfExists(
|
||||||
|
runtimeContext.getFramework(),
|
||||||
|
commandQueueClass,
|
||||||
|
"showTransient",
|
||||||
|
chain -> {
|
||||||
|
Object result = chain.proceed();
|
||||||
|
updateTransientBarState(chain.getArgs(), true);
|
||||||
|
return result;
|
||||||
|
});
|
||||||
|
XposedHookSupport.hookAllMethodsIfExists(
|
||||||
|
runtimeContext.getFramework(),
|
||||||
|
commandQueueClass,
|
||||||
|
"abortTransient",
|
||||||
|
chain -> {
|
||||||
|
Object result = chain.proceed();
|
||||||
|
updateTransientBarState(chain.getArgs(), false);
|
||||||
|
return result;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
installStatusBarAutoHideElementListener(classLoader);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void installStatusBarAutoHideElementListener(ClassLoader classLoader) {
|
||||||
|
Class<?> statusBarAutoHideElementClass = ReflectionSupport.findClassIfExists(
|
||||||
|
"com.android.systemui.statusbar.phone.CentralSurfacesImpl$5",
|
||||||
|
classLoader);
|
||||||
|
if (statusBarAutoHideElementClass == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
XposedHookSupport.hookAllMethodsIfExists(
|
||||||
|
runtimeContext.getFramework(),
|
||||||
|
statusBarAutoHideElementClass,
|
||||||
|
"hide",
|
||||||
|
chain -> {
|
||||||
|
Object result = chain.proceed();
|
||||||
|
updateStatusBarTransientShown(false);
|
||||||
|
return result;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void trackAndApplyRoot(View root) {
|
private void trackAndApplyRoot(View root) {
|
||||||
@@ -115,6 +244,7 @@ final class BatteryBarController {
|
|||||||
root.removeOnLayoutChangeListener(listener);
|
root.removeOnLayoutChangeListener(listener);
|
||||||
}
|
}
|
||||||
roots.remove(root);
|
roots.remove(root);
|
||||||
|
pendingOverlayRetries.remove(root);
|
||||||
removeBatteryBarView(root);
|
removeBatteryBarView(root);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -210,19 +340,88 @@ final class BatteryBarController {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
int color = resolveBarColor(root, settings);
|
int color = resolveBarColor(root, settings);
|
||||||
String signature = buildSignature(root, settings, color);
|
BatteryBarGeometry.Scenario scenario = scenarioForRoot(root);
|
||||||
|
if (!shouldRenderOnRoot(root)) {
|
||||||
|
removeBatteryBarView(root);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
boolean transientStatusBarReveal = isFullscreenScenario(scenario)
|
||||||
|
&& shouldRenderVisibleFullscreenBar(root);
|
||||||
|
if (transientStatusBarReveal) {
|
||||||
|
scenario = visibleStatusBarScenarioForRoot(root);
|
||||||
|
}
|
||||||
|
BatteryBarGeometry geometry = BatteryBarGeometry.resolve(settings, scenario);
|
||||||
|
if (isFullscreenScenario(scenario)) {
|
||||||
|
removeEmbeddedBatteryBarView(root);
|
||||||
|
applyOverlayBatteryBar(root, settings, geometry, scenario, color);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
removeOverlayBatteryBar(root);
|
||||||
|
String signature = buildSignature(root, settings, color, geometry, scenario);
|
||||||
BatteryBarView existingView = batteryBarViews.get(root);
|
BatteryBarView existingView = batteryBarViews.get(root);
|
||||||
String lastSignature = lastSignatures.get(root);
|
String lastSignature = lastSignatures.get(root);
|
||||||
if (signature.equals(lastSignature) && existingView != null && existingView.getParent() == rootGroup) {
|
if (signature.equals(lastSignature) && existingView != null && existingView.getParent() == rootGroup) {
|
||||||
existingView.bringToFront();
|
existingView.bringToFront();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
applyEmbeddedBatteryBar(rootGroup, settings, geometry, scenario, color);
|
||||||
|
lastSignatures.put(root, signature);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void applyEmbeddedBatteryBar(
|
||||||
|
ViewGroup rootGroup,
|
||||||
|
SbtSettings settings,
|
||||||
|
BatteryBarGeometry geometry,
|
||||||
|
BatteryBarGeometry.Scenario scenario,
|
||||||
|
int color
|
||||||
|
) {
|
||||||
BatteryBarView barView = ensureBatteryBarView(rootGroup);
|
BatteryBarView barView = ensureBatteryBarView(rootGroup);
|
||||||
layoutBatteryBarView(rootGroup, barView);
|
layoutBatteryBarView(rootGroup, barView);
|
||||||
barView.update(rootGroup, settings, batteryLevel, plugged, color);
|
barView.update(rootGroup, settings, geometry, batteryLevel, plugged, color);
|
||||||
barView.bringToFront();
|
barView.bringToFront();
|
||||||
barView.invalidate();
|
barView.invalidate();
|
||||||
lastSignatures.put(root, signature);
|
lastSignatures.put(rootGroup, buildSignature(rootGroup, settings, color, geometry, scenario));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void applyOverlayBatteryBar(
|
||||||
|
View root,
|
||||||
|
SbtSettings settings,
|
||||||
|
BatteryBarGeometry geometry,
|
||||||
|
BatteryBarGeometry.Scenario scenario,
|
||||||
|
int color
|
||||||
|
) {
|
||||||
|
String signature = buildSignature(root, settings, color, geometry, scenario);
|
||||||
|
OverlayBatteryBar overlay = overlayBatteryBars.get(root);
|
||||||
|
if (overlay == null) {
|
||||||
|
overlay = new OverlayBatteryBar(root.getContext());
|
||||||
|
overlayBatteryBars.put(root, overlay);
|
||||||
|
}
|
||||||
|
OverlayBounds bounds = fullscreenOverlayBounds(root);
|
||||||
|
boolean attached = overlay.ensureAttached(bounds.width, bounds.height);
|
||||||
|
if (!attached) {
|
||||||
|
scheduleOverlayRetry(root);
|
||||||
|
removeOverlayBatteryBar(root);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
pendingOverlayRetries.remove(root);
|
||||||
|
overlay.view.update(overlay.host, settings, geometry, batteryLevel, plugged, color);
|
||||||
|
if (!signature.equals(lastOverlaySignatures.get(root))) {
|
||||||
|
overlay.updateLayout(bounds.width, bounds.height);
|
||||||
|
lastOverlaySignatures.put(root, signature);
|
||||||
|
}
|
||||||
|
overlay.view.invalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void scheduleOverlayRetry(View root) {
|
||||||
|
if (root == null || !root.isAttachedToWindow() || !pendingOverlayRetries.add(root)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
root.postDelayed(() -> {
|
||||||
|
pendingOverlayRetries.remove(root);
|
||||||
|
if (roots.contains(root) && root.isAttachedToWindow()) {
|
||||||
|
applyBatteryBar(root);
|
||||||
|
}
|
||||||
|
}, 250L);
|
||||||
}
|
}
|
||||||
|
|
||||||
private BatteryBarView ensureBatteryBarView(ViewGroup root) {
|
private BatteryBarView ensureBatteryBarView(ViewGroup root) {
|
||||||
@@ -278,9 +477,16 @@ final class BatteryBarController {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private String buildSignature(View root, SbtSettings settings, int color) {
|
private String buildSignature(
|
||||||
|
View root,
|
||||||
|
SbtSettings settings,
|
||||||
|
int color,
|
||||||
|
BatteryBarGeometry geometry,
|
||||||
|
BatteryBarGeometry.Scenario scenario
|
||||||
|
) {
|
||||||
return root.getWidth() + "|"
|
return root.getWidth() + "|"
|
||||||
+ root.getHeight() + "|"
|
+ root.getHeight() + "|"
|
||||||
|
+ scenario + "|"
|
||||||
+ batteryLevel + "|"
|
+ batteryLevel + "|"
|
||||||
+ plugged + "|"
|
+ plugged + "|"
|
||||||
+ color + "|"
|
+ color + "|"
|
||||||
@@ -289,6 +495,7 @@ final class BatteryBarController {
|
|||||||
+ settings.batteryBarAlignment + "|"
|
+ settings.batteryBarAlignment + "|"
|
||||||
+ settings.batteryBarThicknessDp + "|"
|
+ settings.batteryBarThicknessDp + "|"
|
||||||
+ settings.batteryBarEdgeOffsetDp + "|"
|
+ settings.batteryBarEdgeOffsetDp + "|"
|
||||||
|
+ (geometry != null ? geometry.signature() : "") + "|"
|
||||||
+ settings.batteryBarMinLevel + "|"
|
+ settings.batteryBarMinLevel + "|"
|
||||||
+ settings.batteryBarMaxLevel + "|"
|
+ settings.batteryBarMaxLevel + "|"
|
||||||
+ settings.batteryBarDefaultDischargeColor + "|"
|
+ settings.batteryBarDefaultDischargeColor + "|"
|
||||||
@@ -296,7 +503,228 @@ final class BatteryBarController {
|
|||||||
+ BatteryBarStyle.encodeThresholds(settings.batteryBarThresholds);
|
+ BatteryBarStyle.encodeThresholds(settings.batteryBarThresholds);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private BatteryBarGeometry.Scenario scenarioForRoot(View root) {
|
||||||
|
if (isKeyguardRoot(root)) {
|
||||||
|
return BatteryBarGeometry.Scenario.LOCKSCREEN;
|
||||||
|
}
|
||||||
|
boolean fullscreen = isPhoneRoot(root)
|
||||||
|
&& isUnlockedScene()
|
||||||
|
&& (isStatusBarSurfaceHidden(root) || isFullscreenSystemBarState());
|
||||||
|
boolean landscape = isLandscapeForRoot(root, fullscreen);
|
||||||
|
if (fullscreen) {
|
||||||
|
return landscape
|
||||||
|
? BatteryBarGeometry.Scenario.FULLSCREEN_LANDSCAPE
|
||||||
|
: BatteryBarGeometry.Scenario.FULLSCREEN_PORTRAIT;
|
||||||
|
}
|
||||||
|
if (isPhoneRoot(root) && isUnlockedScene() && isTransparentStatusBarActive()) {
|
||||||
|
return landscape
|
||||||
|
? BatteryBarGeometry.Scenario.TRANSPARENT_LANDSCAPE
|
||||||
|
: BatteryBarGeometry.Scenario.TRANSPARENT_PORTRAIT;
|
||||||
|
}
|
||||||
|
return landscape
|
||||||
|
? BatteryBarGeometry.Scenario.UNLOCKED_LANDSCAPE
|
||||||
|
: BatteryBarGeometry.Scenario.UNLOCKED_PORTRAIT;
|
||||||
|
}
|
||||||
|
|
||||||
|
private BatteryBarGeometry.Scenario visibleStatusBarScenarioForRoot(View root) {
|
||||||
|
boolean landscape = isLandscapeForRoot(root, isFullscreenSystemBarState());
|
||||||
|
if (isPhoneRoot(root) && isUnlockedScene() && isTransparentStatusBarActive()) {
|
||||||
|
return landscape
|
||||||
|
? BatteryBarGeometry.Scenario.TRANSPARENT_LANDSCAPE
|
||||||
|
: BatteryBarGeometry.Scenario.TRANSPARENT_PORTRAIT;
|
||||||
|
}
|
||||||
|
return landscape
|
||||||
|
? BatteryBarGeometry.Scenario.UNLOCKED_LANDSCAPE
|
||||||
|
: BatteryBarGeometry.Scenario.UNLOCKED_PORTRAIT;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isFullscreenScenario(BatteryBarGeometry.Scenario scenario) {
|
||||||
|
return scenario == BatteryBarGeometry.Scenario.FULLSCREEN_PORTRAIT
|
||||||
|
|| scenario == BatteryBarGeometry.Scenario.FULLSCREEN_LANDSCAPE;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean shouldRenderOnRoot(View root) {
|
||||||
|
SceneKey scene = runtimeContext.getModeStateRepository().getActiveScene();
|
||||||
|
if (isKeyguardRoot(root)) {
|
||||||
|
return scene == null || !scene.isUnlocked();
|
||||||
|
}
|
||||||
|
if (isPhoneRoot(root)) {
|
||||||
|
return scene == null || scene.isUnlocked();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isUnlockedScene() {
|
||||||
|
SceneKey scene = runtimeContext.getModeStateRepository().getActiveScene();
|
||||||
|
return scene == null || scene.isUnlocked();
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isPhoneRoot(View root) {
|
||||||
|
return root != null && root.getClass().getName().contains("PhoneStatusBarView");
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isKeyguardRoot(View root) {
|
||||||
|
return root != null && root.getClass().getName().contains("KeyguardStatusBarView");
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isLandscapeForRoot(View root, boolean preferDisplayBounds) {
|
||||||
|
if (root == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (preferDisplayBounds) {
|
||||||
|
Rect bounds = currentWindowBounds(root.getContext());
|
||||||
|
if (bounds.width() > 0 && bounds.height() > 0 && bounds.width() != bounds.height()) {
|
||||||
|
return bounds.width() > bounds.height();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return root.getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isTransparentStatusBarActive() {
|
||||||
|
return (systemBarAppearance & APPEARANCE_LIGHT_STATUS_BARS) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isFullscreenSystemBarState() {
|
||||||
|
return (systemBarRequestedVisibleTypes & WindowInsets.Type.statusBars()) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean shouldRenderVisibleFullscreenBar(View root) {
|
||||||
|
return root != null && isFullscreenSystemBarState() && statusBarTransientShown;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateTransientBarState(java.util.List<Object> args, boolean visible) {
|
||||||
|
Integer displayId = args != null && !args.isEmpty() ? asInteger(args.get(0)) : null;
|
||||||
|
boolean statusBarsAffected = transientArgsIncludeStatusBars(args);
|
||||||
|
if (displayId == null || displayId != 0 || !statusBarsAffected) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
updateStatusBarTransientShown(visible);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateStatusBarTransientShown(boolean shown) {
|
||||||
|
if (statusBarTransientShown == shown) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
statusBarTransientShown = shown;
|
||||||
|
postRefreshAllRoots();
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean transientArgsIncludeStatusBars(java.util.List<Object> args) {
|
||||||
|
if (args == null || args.size() < 2) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
Object typesArg = args.get(1);
|
||||||
|
if (!(typesArg instanceof int[] types)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
int statusBars = WindowInsets.Type.statusBars();
|
||||||
|
for (int type : types) {
|
||||||
|
if ((type & statusBars) != 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private OverlayBounds fullscreenOverlayBounds(View root) {
|
||||||
|
int width = root.getWidth();
|
||||||
|
int height = root.getHeight();
|
||||||
|
Rect bounds = currentWindowBounds(root.getContext());
|
||||||
|
if (bounds.width() > 0) {
|
||||||
|
width = bounds.width();
|
||||||
|
}
|
||||||
|
return new OverlayBounds(width, height);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Rect currentWindowBounds(Context context) {
|
||||||
|
if (context == null) {
|
||||||
|
return new Rect();
|
||||||
|
}
|
||||||
|
WindowManager windowManager = context.getSystemService(WindowManager.class);
|
||||||
|
if (windowManager == null) {
|
||||||
|
return new Rect();
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
WindowMetrics metrics = windowManager.getCurrentWindowMetrics();
|
||||||
|
return metrics != null ? metrics.getBounds() : new Rect();
|
||||||
|
} catch (Throwable ignored) {
|
||||||
|
return new Rect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private record OverlayBounds(int width, int height) {
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateSystemBarState(java.util.List<Object> args) {
|
||||||
|
if (args == null || args.size() < 2) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Integer displayId = asInteger(args.get(0));
|
||||||
|
Integer appearance = asInteger(args.get(1));
|
||||||
|
Integer behavior = args.size() > 4 ? asInteger(args.get(4)) : null;
|
||||||
|
Integer requestedVisibleTypes = args.size() > 5 ? asInteger(args.get(5)) : null;
|
||||||
|
if (displayId == null || displayId != 0 || appearance == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
String packageName = args.size() > 6 && args.get(6) instanceof String value ? value : "";
|
||||||
|
boolean changed = systemBarAppearance != appearance
|
||||||
|
|| (behavior != null && systemBarBehavior != behavior)
|
||||||
|
|| (requestedVisibleTypes != null
|
||||||
|
&& systemBarRequestedVisibleTypes != requestedVisibleTypes)
|
||||||
|
|| !systemBarPackageName.equals(packageName);
|
||||||
|
systemBarAppearance = appearance;
|
||||||
|
if (behavior != null) {
|
||||||
|
systemBarBehavior = behavior;
|
||||||
|
}
|
||||||
|
if (requestedVisibleTypes != null) {
|
||||||
|
systemBarRequestedVisibleTypes = requestedVisibleTypes;
|
||||||
|
}
|
||||||
|
systemBarPackageName = packageName;
|
||||||
|
if (changed) {
|
||||||
|
postRefreshAllRoots();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void postRefreshAllRoots() {
|
||||||
|
for (View root : new ArrayList<>(roots)) {
|
||||||
|
if (root != null && root.isAttachedToWindow()) {
|
||||||
|
root.post(() -> {
|
||||||
|
if (roots.contains(root) && root.isAttachedToWindow()) {
|
||||||
|
applyBatteryBar(root);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isStatusBarSurfaceHidden(View root) {
|
||||||
|
if (root == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!root.isShown()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
int[] location = new int[2];
|
||||||
|
root.getLocationOnScreen(location);
|
||||||
|
float left = location[0] + root.getTranslationX();
|
||||||
|
float top = location[1] + root.getTranslationY();
|
||||||
|
float right = left + root.getWidth();
|
||||||
|
float bottom = top + root.getHeight();
|
||||||
|
int screenWidth = root.getResources().getDisplayMetrics().widthPixels;
|
||||||
|
int screenHeight = root.getResources().getDisplayMetrics().heightPixels;
|
||||||
|
return bottom <= 0f || top >= screenHeight || right <= 0f || left >= screenWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Integer asInteger(Object value) {
|
||||||
|
return value instanceof Integer integer ? integer : null;
|
||||||
|
}
|
||||||
|
|
||||||
private void removeBatteryBarView(View root) {
|
private void removeBatteryBarView(View root) {
|
||||||
|
removeEmbeddedBatteryBarView(root);
|
||||||
|
removeOverlayBatteryBar(root);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void removeEmbeddedBatteryBarView(View root) {
|
||||||
BatteryBarView view = batteryBarViews.remove(root);
|
BatteryBarView view = batteryBarViews.remove(root);
|
||||||
lastSignatures.remove(root);
|
lastSignatures.remove(root);
|
||||||
if (view != null) {
|
if (view != null) {
|
||||||
@@ -304,6 +732,14 @@ final class BatteryBarController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void removeOverlayBatteryBar(View root) {
|
||||||
|
OverlayBatteryBar overlay = overlayBatteryBars.remove(root);
|
||||||
|
lastOverlaySignatures.remove(root);
|
||||||
|
if (overlay != null) {
|
||||||
|
overlay.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private int resolveBarColor(View root, SbtSettings settings) {
|
private int resolveBarColor(View root, SbtSettings settings) {
|
||||||
int fallback = resolveClockColor(root);
|
int fallback = resolveClockColor(root);
|
||||||
return BatteryBarStyle.resolveColor(
|
return BatteryBarStyle.resolveColor(
|
||||||
@@ -358,11 +794,117 @@ final class BatteryBarController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static final class OverlayBatteryBar {
|
||||||
|
private final WindowManager windowManager;
|
||||||
|
private final FrameLayout host;
|
||||||
|
private final BatteryBarView view;
|
||||||
|
private boolean attached;
|
||||||
|
private int width;
|
||||||
|
private int height;
|
||||||
|
|
||||||
|
OverlayBatteryBar(Context context) {
|
||||||
|
Context safeContext = context != null ? context : null;
|
||||||
|
windowManager = safeContext != null
|
||||||
|
? safeContext.getSystemService(WindowManager.class)
|
||||||
|
: null;
|
||||||
|
host = new FrameLayout(context);
|
||||||
|
host.setClickable(false);
|
||||||
|
host.setFocusable(false);
|
||||||
|
host.setFocusableInTouchMode(false);
|
||||||
|
view = new BatteryBarView(context);
|
||||||
|
view.setClickable(false);
|
||||||
|
view.setFocusable(false);
|
||||||
|
view.setFocusableInTouchMode(false);
|
||||||
|
host.addView(view, new FrameLayout.LayoutParams(
|
||||||
|
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||||
|
ViewGroup.LayoutParams.MATCH_PARENT));
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean ensureAttached(int width, int height) {
|
||||||
|
if (windowManager == null || width <= 0 || height <= 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (attached) {
|
||||||
|
return updateLayout(width, height);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
windowManager.addView(host, layoutParams(width, height));
|
||||||
|
attached = true;
|
||||||
|
this.width = width;
|
||||||
|
this.height = height;
|
||||||
|
layoutHost(width, height);
|
||||||
|
return true;
|
||||||
|
} catch (Throwable ignored) {
|
||||||
|
attached = false;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean updateLayout(int width, int height) {
|
||||||
|
if (!attached || windowManager == null || width <= 0 || height <= 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (this.width == width && this.height == height) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
this.width = width;
|
||||||
|
this.height = height;
|
||||||
|
try {
|
||||||
|
windowManager.updateViewLayout(host, layoutParams(width, height));
|
||||||
|
layoutHost(width, height);
|
||||||
|
return true;
|
||||||
|
} catch (Throwable ignored) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void remove() {
|
||||||
|
if (!attached || windowManager == null) {
|
||||||
|
attached = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
windowManager.removeViewImmediate(host);
|
||||||
|
} catch (Throwable ignored) {
|
||||||
|
} finally {
|
||||||
|
attached = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private WindowManager.LayoutParams layoutParams(int width, int height) {
|
||||||
|
WindowManager.LayoutParams params = new WindowManager.LayoutParams(
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY,
|
||||||
|
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
|
||||||
|
| WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
|
||||||
|
| WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
|
||||||
|
| WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
|
||||||
|
| WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS,
|
||||||
|
PixelFormat.TRANSLUCENT);
|
||||||
|
params.gravity = Gravity.TOP | Gravity.START;
|
||||||
|
params.x = 0;
|
||||||
|
params.y = 0;
|
||||||
|
params.layoutInDisplayCutoutMode =
|
||||||
|
WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
|
||||||
|
params.setTitle("StatusBarTweakBatteryBar");
|
||||||
|
return params;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void layoutHost(int width, int height) {
|
||||||
|
int widthSpec = View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.EXACTLY);
|
||||||
|
int heightSpec = View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.EXACTLY);
|
||||||
|
host.measure(widthSpec, heightSpec);
|
||||||
|
host.layout(0, 0, width, height);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static final class BatteryBarView extends View {
|
private static final class BatteryBarView extends View {
|
||||||
private final Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
private final Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||||
private final Rect drawRect = new Rect();
|
private final Rect drawRect = new Rect();
|
||||||
private ViewGroup host;
|
private ViewGroup host;
|
||||||
private SbtSettings settings;
|
private SbtSettings settings;
|
||||||
|
private BatteryBarGeometry geometry;
|
||||||
private int batteryLevel = -1;
|
private int batteryLevel = -1;
|
||||||
private boolean charging;
|
private boolean charging;
|
||||||
|
|
||||||
@@ -375,12 +917,14 @@ final class BatteryBarController {
|
|||||||
void update(
|
void update(
|
||||||
ViewGroup host,
|
ViewGroup host,
|
||||||
SbtSettings settings,
|
SbtSettings settings,
|
||||||
|
BatteryBarGeometry geometry,
|
||||||
int batteryLevel,
|
int batteryLevel,
|
||||||
boolean charging,
|
boolean charging,
|
||||||
int color
|
int color
|
||||||
) {
|
) {
|
||||||
this.host = host;
|
this.host = host;
|
||||||
this.settings = settings;
|
this.settings = settings;
|
||||||
|
this.geometry = geometry;
|
||||||
this.batteryLevel = batteryLevel;
|
this.batteryLevel = batteryLevel;
|
||||||
this.charging = charging;
|
this.charging = charging;
|
||||||
paint.setColor(color);
|
paint.setColor(color);
|
||||||
@@ -398,14 +942,17 @@ final class BatteryBarController {
|
|||||||
if (width <= 0 || height <= 0) {
|
if (width <= 0 || height <= 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
int thickness = Math.max(1, dpToPx(getContext(), settings.batteryBarThicknessDp));
|
BatteryBarGeometry activeGeometry = geometry != null
|
||||||
|
? geometry
|
||||||
|
: BatteryBarGeometry.global(settings);
|
||||||
|
int thickness = Math.max(1, dpToPx(getContext(), activeGeometry.thicknessDp));
|
||||||
int edgeOffset = Math.max(0, dpToPx(getContext(), settings.batteryBarEdgeOffsetDp));
|
int edgeOffset = Math.max(0, dpToPx(getContext(), settings.batteryBarEdgeOffsetDp));
|
||||||
int left = edgeOffset;
|
int left = edgeOffset;
|
||||||
int right = width - edgeOffset;
|
int right = width - edgeOffset;
|
||||||
if (right <= left) {
|
if (right <= left) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
int top = resolveTop(settings, thickness, height);
|
int top = resolveTop(activeGeometry, thickness, height);
|
||||||
int bottom = Math.min(height, top + thickness);
|
int bottom = Math.min(height, top + thickness);
|
||||||
if (bottom <= top) {
|
if (bottom <= top) {
|
||||||
return;
|
return;
|
||||||
@@ -416,9 +963,9 @@ final class BatteryBarController {
|
|||||||
if (barWidth <= 0) {
|
if (barWidth <= 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if ("rtl".equals(settings.batteryBarAlignment)) {
|
if ("rtl".equals(activeGeometry.alignment)) {
|
||||||
drawRect.set(right - barWidth, top, right, bottom);
|
drawRect.set(right - barWidth, top, right, bottom);
|
||||||
} else if ("center".equals(settings.batteryBarAlignment)) {
|
} else if ("center".equals(activeGeometry.alignment)) {
|
||||||
int centerX = (left + right) / 2;
|
int centerX = (left + right) / 2;
|
||||||
int half = barWidth / 2;
|
int half = barWidth / 2;
|
||||||
drawRect.set(centerX - half, top, centerX - half + barWidth, bottom);
|
drawRect.set(centerX - half, top, centerX - half + barWidth, bottom);
|
||||||
@@ -427,8 +974,8 @@ final class BatteryBarController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private int resolveTop(SbtSettings settings, int thickness, int height) {
|
private int resolveTop(BatteryBarGeometry geometry, int thickness, int height) {
|
||||||
if ("bottom".equals(settings.batteryBarPosition)) {
|
if (geometry != null && "bottom".equals(geometry.position)) {
|
||||||
return Math.max(0, height - thickness);
|
return Math.max(0, height - thickness);
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
@@ -444,6 +444,9 @@ final class SnapshotRenderer {
|
|||||||
if (lowerKey.contains("wifi")) {
|
if (lowerKey.contains("wifi")) {
|
||||||
return "wifi";
|
return "wifi";
|
||||||
}
|
}
|
||||||
|
if (lowerKey.contains("mobile2")) {
|
||||||
|
return "mobile2";
|
||||||
|
}
|
||||||
if (lowerKey.contains("mobile")) {
|
if (lowerKey.contains("mobile")) {
|
||||||
return "mobile";
|
return "mobile";
|
||||||
}
|
}
|
||||||
@@ -452,6 +455,9 @@ final class SnapshotRenderer {
|
|||||||
if (idName.contains("wifi") || className.contains("wifi")) {
|
if (idName.contains("wifi") || className.contains("wifi")) {
|
||||||
return "wifi";
|
return "wifi";
|
||||||
}
|
}
|
||||||
|
if (idName.contains("mobile2") || className.contains("mobile2")) {
|
||||||
|
return "mobile2";
|
||||||
|
}
|
||||||
if (idName.contains("mobile") || className.contains("mobile")) {
|
if (idName.contains("mobile") || className.contains("mobile")) {
|
||||||
return "mobile";
|
return "mobile";
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -174,7 +174,7 @@ public final class UnlockedIconSnapshotRenderController {
|
|||||||
}
|
}
|
||||||
CollectedIcons collectedIcons = previousIcons;
|
CollectedIcons collectedIcons = previousIcons;
|
||||||
if (forceStockRefresh || sceneChanged || stockSourcesChanged || collectedIcons == null) {
|
if (forceStockRefresh || sceneChanged || stockSourcesChanged || collectedIcons == null) {
|
||||||
collectedIcons = sourceCollector.collect(root, anchors, scene);
|
collectedIcons = sourceCollector.collect(root, anchors, scene, settings);
|
||||||
lastCollectedIconsByRoot.put(root, collectedIcons);
|
lastCollectedIconsByRoot.put(root, collectedIcons);
|
||||||
} else if (forceChipRefresh && collectedIcons != null) {
|
} else if (forceChipRefresh && collectedIcons != null) {
|
||||||
collectedIcons = sourceCollector.collectChips(root, anchors, collectedIcons);
|
collectedIcons = sourceCollector.collectChips(root, anchors, collectedIcons);
|
||||||
|
|||||||
+288
-1
@@ -23,6 +23,7 @@ import se.ajpanton.statusbartweak.runtime.mode.NotificationDisplayStyle;
|
|||||||
import se.ajpanton.statusbartweak.runtime.mode.SceneKey;
|
import se.ajpanton.statusbartweak.runtime.mode.SceneKey;
|
||||||
import se.ajpanton.statusbartweak.runtime.notifications.NotificationActiveKeyStore;
|
import se.ajpanton.statusbartweak.runtime.notifications.NotificationActiveKeyStore;
|
||||||
import se.ajpanton.statusbartweak.runtime.notifications.NotificationUnreadTracker;
|
import se.ajpanton.statusbartweak.runtime.notifications.NotificationUnreadTracker;
|
||||||
|
import se.ajpanton.statusbartweak.shell.settings.SbtDefaults;
|
||||||
import se.ajpanton.statusbartweak.shell.settings.SbtSettings;
|
import se.ajpanton.statusbartweak.shell.settings.SbtSettings;
|
||||||
|
|
||||||
final class UnlockedStockSourceCollector {
|
final class UnlockedStockSourceCollector {
|
||||||
@@ -52,7 +53,7 @@ final class UnlockedStockSourceCollector {
|
|||||||
return anchors;
|
return anchors;
|
||||||
}
|
}
|
||||||
|
|
||||||
CollectedIcons collect(View root, RootAnchors anchors, SceneKey scene) {
|
CollectedIcons collect(View root, RootAnchors anchors, SceneKey scene, SbtSettings settings) {
|
||||||
ArrayList<SnapshotSource> statusIcons = new ArrayList<>();
|
ArrayList<SnapshotSource> statusIcons = new ArrayList<>();
|
||||||
ArrayList<SnapshotSource> notificationIcons = new ArrayList<>();
|
ArrayList<SnapshotSource> notificationIcons = new ArrayList<>();
|
||||||
ArrayList<SnapshotSource> statusChips = new ArrayList<>();
|
ArrayList<SnapshotSource> statusChips = new ArrayList<>();
|
||||||
@@ -73,6 +74,7 @@ final class UnlockedStockSourceCollector {
|
|||||||
if (anchors.battery != null && !ViewGeometry.isDescendantOf(anchors.battery, anchors.statusRoot)) {
|
if (anchors.battery != null && !ViewGeometry.isDescendantOf(anchors.battery, anchors.statusRoot)) {
|
||||||
collectStatusSnapshotViews(anchors.battery, statusIcons);
|
collectStatusSnapshotViews(anchors.battery, statusIcons);
|
||||||
}
|
}
|
||||||
|
selectDualSimStatusSignals(statusIcons, settings);
|
||||||
normalizeStatusIconSourceBounds(root, statusIcons);
|
normalizeStatusIconSourceBounds(root, statusIcons);
|
||||||
if (scene != null && scene.isAod()) {
|
if (scene != null && scene.isAod()) {
|
||||||
if (scene.notificationDisplayStyle() == NotificationDisplayStyle.ICONS
|
if (scene.notificationDisplayStyle() == NotificationDisplayStyle.ICONS
|
||||||
@@ -551,6 +553,290 @@ final class UnlockedStockSourceCollector {
|
|||||||
Math.max(1, height));
|
Math.max(1, height));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void selectDualSimStatusSignals(ArrayList<SnapshotSource> sources, SbtSettings settings) {
|
||||||
|
if (sources == null || sources.size() < 2) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
String mode = settings != null
|
||||||
|
? settings.systemIconDualSimSignalMode
|
||||||
|
: SbtDefaults.SYSTEM_ICON_DUAL_SIM_SIGNAL_MODE_DEFAULT;
|
||||||
|
if (SbtSettings.SYSTEM_ICON_DUAL_SIM_SEPARATE.equals(mode)) {
|
||||||
|
deduplicateSameStatusSignalViews(sources);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
selectMobileStatusSignal(sources, mode);
|
||||||
|
deduplicateSameStatusSignalViews(sources);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void deduplicateSameStatusSignalViews(ArrayList<SnapshotSource> sources) {
|
||||||
|
LinkedHashMap<View, Integer> firstIndexByView = new LinkedHashMap<>();
|
||||||
|
for (int i = 0; i < sources.size(); i++) {
|
||||||
|
SnapshotSource source = sources.get(i);
|
||||||
|
if (statusSignalKind(source) == null || source.view() == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
Integer firstIndex = firstIndexByView.get(source.view());
|
||||||
|
if (firstIndex == null) {
|
||||||
|
firstIndexByView.put(source.view(), i);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
SnapshotSource existing = sources.get(firstIndex);
|
||||||
|
if (statusSignalSourceScore(source) > statusSignalSourceScore(existing)) {
|
||||||
|
sources.set(firstIndex, source);
|
||||||
|
}
|
||||||
|
sources.remove(i);
|
||||||
|
i--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void selectMobileStatusSignal(ArrayList<SnapshotSource> sources, String mode) {
|
||||||
|
ArrayList<Integer> mobileIndexes = new ArrayList<>();
|
||||||
|
for (int i = 0; i < sources.size(); i++) {
|
||||||
|
String signal = statusSignalKind(sources.get(i));
|
||||||
|
if (signal != null
|
||||||
|
&& signal.startsWith("mobile")
|
||||||
|
&& !isMobileRadioTechnologySource(sources.get(i).view())) {
|
||||||
|
mobileIndexes.add(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (mobileIndexes.size() < 2) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int keptIndex = selectedMobileStatusSignalIndex(sources, mobileIndexes, mode);
|
||||||
|
ArrayList<SnapshotSource> preservedRadioTechSources = new ArrayList<>();
|
||||||
|
for (int i = mobileIndexes.size() - 1; i >= 0; i--) {
|
||||||
|
int index = mobileIndexes.get(i);
|
||||||
|
if (index != keptIndex) {
|
||||||
|
SnapshotSource radioTechSource = mobileRadioTechnologyChildSource(sources.get(index));
|
||||||
|
if (radioTechSource != null) {
|
||||||
|
preservedRadioTechSources.add(radioTechSource);
|
||||||
|
}
|
||||||
|
sources.remove(index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sources.addAll(preservedRadioTechSources);
|
||||||
|
}
|
||||||
|
|
||||||
|
private int selectedMobileStatusSignalIndex(
|
||||||
|
ArrayList<SnapshotSource> sources,
|
||||||
|
ArrayList<Integer> mobileIndexes,
|
||||||
|
String mode
|
||||||
|
) {
|
||||||
|
if (SbtSettings.SYSTEM_ICON_DUAL_SIM_SECOND.equals(mode)) {
|
||||||
|
return mobileIndexes.get(1);
|
||||||
|
}
|
||||||
|
if (SbtSettings.SYSTEM_ICON_DUAL_SIM_STRONGEST.equals(mode)
|
||||||
|
|| SbtSettings.SYSTEM_ICON_DUAL_SIM_WEAKEST.equals(mode)) {
|
||||||
|
int selected = selectedBySignalStrength(sources, mobileIndexes,
|
||||||
|
SbtSettings.SYSTEM_ICON_DUAL_SIM_STRONGEST.equals(mode));
|
||||||
|
if (selected >= 0) {
|
||||||
|
return selected;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (SbtSettings.SYSTEM_ICON_DUAL_SIM_DATA.equals(mode)) {
|
||||||
|
int selected = selectedByRole(sources, mobileIndexes, "data");
|
||||||
|
if (selected >= 0) {
|
||||||
|
return selected;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (SbtSettings.SYSTEM_ICON_DUAL_SIM_PHONE.equals(mode)) {
|
||||||
|
int selected = selectedByRole(sources, mobileIndexes, "phone", "voice", "call");
|
||||||
|
if (selected >= 0) {
|
||||||
|
return selected;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return mobileIndexes.get(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private int selectedBySignalStrength(
|
||||||
|
ArrayList<SnapshotSource> sources,
|
||||||
|
ArrayList<Integer> indexes,
|
||||||
|
boolean strongest
|
||||||
|
) {
|
||||||
|
int selectedIndex = -1;
|
||||||
|
int selectedStrength = 0;
|
||||||
|
for (int index : indexes) {
|
||||||
|
int strength = signalStrength(sources.get(index));
|
||||||
|
if (strength < 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (selectedIndex < 0
|
||||||
|
|| (strongest && strength > selectedStrength)
|
||||||
|
|| (!strongest && strength < selectedStrength)) {
|
||||||
|
selectedIndex = index;
|
||||||
|
selectedStrength = strength;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return selectedIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int selectedByRole(
|
||||||
|
ArrayList<SnapshotSource> sources,
|
||||||
|
ArrayList<Integer> indexes,
|
||||||
|
String... needles
|
||||||
|
) {
|
||||||
|
for (int index : indexes) {
|
||||||
|
String text = statusSignalText(sources.get(index)).toLowerCase(Locale.ROOT);
|
||||||
|
for (String needle : needles) {
|
||||||
|
if (text.contains(needle)) {
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String statusSignalKind(SnapshotSource source) {
|
||||||
|
String value = statusSignalText(source).toLowerCase(Locale.ROOT);
|
||||||
|
if (value.contains("wifi")) {
|
||||||
|
return "wifi";
|
||||||
|
}
|
||||||
|
if (value.contains("mobile2")) {
|
||||||
|
return "mobile2";
|
||||||
|
}
|
||||||
|
if (value.contains("mobile")) {
|
||||||
|
return "mobile";
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String statusSignalText(SnapshotSource source) {
|
||||||
|
View view = source != null ? source.view() : null;
|
||||||
|
if (view == null) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
StringBuilder text = new StringBuilder();
|
||||||
|
text.append(safeText(source.keyHint())).append(' ');
|
||||||
|
text.append(reflectString(view, "getSlot", "mSlot", "slot")).append(' ');
|
||||||
|
text.append(ViewIdNames.idName(view)).append(' ');
|
||||||
|
CharSequence contentDescription = view.getContentDescription();
|
||||||
|
if (contentDescription != null) {
|
||||||
|
text.append(contentDescription).append(' ');
|
||||||
|
}
|
||||||
|
text.append(view.getClass().getName()).append(' ');
|
||||||
|
Object parent = view.getParent();
|
||||||
|
int depth = 0;
|
||||||
|
while (parent instanceof View parentView && depth++ < 3) {
|
||||||
|
text.append(ViewIdNames.idName(parentView)).append(' ');
|
||||||
|
CharSequence parentDescription = parentView.getContentDescription();
|
||||||
|
if (parentDescription != null) {
|
||||||
|
text.append(parentDescription).append(' ');
|
||||||
|
}
|
||||||
|
text.append(parentView.getClass().getName()).append(' ');
|
||||||
|
parent = parentView.getParent();
|
||||||
|
}
|
||||||
|
return text.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isMobileRadioTechnologySource(View view) {
|
||||||
|
if (view == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
String idName = ViewIdNames.idName(view).toLowerCase(Locale.ROOT);
|
||||||
|
String className = view.getClass().getName().toLowerCase(Locale.ROOT);
|
||||||
|
CharSequence contentDescription = view.getContentDescription();
|
||||||
|
String content = contentDescription != null
|
||||||
|
? contentDescription.toString().toLowerCase(Locale.ROOT)
|
||||||
|
: "";
|
||||||
|
return idName.contains("mobile_type")
|
||||||
|
|| idName.contains("network_type")
|
||||||
|
|| idName.contains("data_type")
|
||||||
|
|| className.contains("mobiletype")
|
||||||
|
|| className.contains("networktype")
|
||||||
|
|| className.contains("datatype")
|
||||||
|
|| isRadioTechnologyText(content);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isRadioTechnologyText(String text) {
|
||||||
|
if (text == null || text.isEmpty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
String value = text.toLowerCase(Locale.ROOT);
|
||||||
|
return value.equals("5g")
|
||||||
|
|| value.equals("4g")
|
||||||
|
|| value.equals("4g+")
|
||||||
|
|| value.equals("3g")
|
||||||
|
|| value.equals("2g")
|
||||||
|
|| value.equals("lte")
|
||||||
|
|| value.equals("h+")
|
||||||
|
|| value.equals("nr");
|
||||||
|
}
|
||||||
|
|
||||||
|
private SnapshotSource mobileRadioTechnologyChildSource(SnapshotSource source) {
|
||||||
|
View sourceView = source != null ? source.view() : null;
|
||||||
|
if (!(sourceView instanceof ViewGroup group)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
View radioTechView = firstMobileRadioTechnologyChild(group);
|
||||||
|
if (radioTechView == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return new SnapshotSource(radioTechView, SnapshotMode.VIEW, "mobile_type");
|
||||||
|
}
|
||||||
|
|
||||||
|
private View firstMobileRadioTechnologyChild(ViewGroup group) {
|
||||||
|
for (int i = 0; i < group.getChildCount(); i++) {
|
||||||
|
View child = group.getChildAt(i);
|
||||||
|
if (isMobileRadioTechnologySource(child)
|
||||||
|
&& child.getVisibility() == View.VISIBLE
|
||||||
|
&& measuredWidth(child) > 0
|
||||||
|
&& measuredHeight(child) > 0) {
|
||||||
|
return child;
|
||||||
|
}
|
||||||
|
if (child instanceof ViewGroup childGroup) {
|
||||||
|
View nested = firstMobileRadioTechnologyChild(childGroup);
|
||||||
|
if (nested != null) {
|
||||||
|
return nested;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int signalStrength(SnapshotSource source) {
|
||||||
|
String text = statusSignalText(source).toLowerCase(Locale.ROOT);
|
||||||
|
int parsed = parseSignalBars(text);
|
||||||
|
if (parsed >= 0) {
|
||||||
|
return parsed;
|
||||||
|
}
|
||||||
|
View view = source != null ? source.view() : null;
|
||||||
|
Drawable drawable = view instanceof ImageView imageView ? imageView.getDrawable() : null;
|
||||||
|
return drawable != null ? drawable.getLevel() : -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int parseSignalBars(String text) {
|
||||||
|
if (text == null || text.isEmpty()) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < text.length(); i++) {
|
||||||
|
char c = text.charAt(i);
|
||||||
|
if (!Character.isDigit(c)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
int value = Character.digit(c, 10);
|
||||||
|
String suffix = text.substring(i + 1, Math.min(text.length(), i + 10));
|
||||||
|
if (suffix.contains("bar") || suffix.contains("level")) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int statusSignalSourceScore(SnapshotSource source) {
|
||||||
|
View view = source != null ? source.view() : null;
|
||||||
|
if (view == null) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
String className = view.getClass().getName();
|
||||||
|
int score = Math.max(0, view.getWidth()) * Math.max(0, view.getHeight());
|
||||||
|
if (isModernSignalContainer(view)) {
|
||||||
|
score += 1_000_000;
|
||||||
|
} else if (className.contains("StatusBarMobile") || className.contains("StatusBarWifi")) {
|
||||||
|
score += 100_000;
|
||||||
|
}
|
||||||
|
return score;
|
||||||
|
}
|
||||||
|
|
||||||
private int syntheticStatusIconWidth(View view, int fallbackSize) {
|
private int syntheticStatusIconWidth(View view, int fallbackSize) {
|
||||||
int width = measuredWidth(view);
|
int width = measuredWidth(view);
|
||||||
if (width > 0) {
|
if (width > 0) {
|
||||||
@@ -1262,6 +1548,7 @@ final class UnlockedStockSourceCollector {
|
|||||||
settings.layoutStatusMiddleSide,
|
settings.layoutStatusMiddleSide,
|
||||||
settings.layoutStatusVerticalOffsetPx,
|
settings.layoutStatusVerticalOffsetPx,
|
||||||
settings.systemIconBlockedModes,
|
settings.systemIconBlockedModes,
|
||||||
|
settings.systemIconDualSimSignalMode,
|
||||||
settings.appIconBlockedModes,
|
settings.appIconBlockedModes,
|
||||||
settings.appIconExceptionRules,
|
settings.appIconExceptionRules,
|
||||||
settings.statusChipsHideAll,
|
settings.statusChipsHideAll,
|
||||||
|
|||||||
@@ -0,0 +1,118 @@
|
|||||||
|
package se.ajpanton.statusbartweak.shell.settings;
|
||||||
|
|
||||||
|
public final class BatteryBarGeometry {
|
||||||
|
|
||||||
|
public enum Scenario {
|
||||||
|
UNLOCKED_PORTRAIT("unlocked_portrait"),
|
||||||
|
UNLOCKED_LANDSCAPE("unlocked_landscape"),
|
||||||
|
TRANSPARENT_PORTRAIT("transparent_portrait"),
|
||||||
|
TRANSPARENT_LANDSCAPE("transparent_landscape"),
|
||||||
|
FULLSCREEN_PORTRAIT("fullscreen_portrait"),
|
||||||
|
FULLSCREEN_LANDSCAPE("fullscreen_landscape"),
|
||||||
|
LOCKSCREEN("lockscreen");
|
||||||
|
|
||||||
|
public final String id;
|
||||||
|
|
||||||
|
Scenario(String id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public final String position;
|
||||||
|
public final String alignment;
|
||||||
|
public final int thicknessDp;
|
||||||
|
|
||||||
|
public BatteryBarGeometry(
|
||||||
|
String position,
|
||||||
|
String alignment,
|
||||||
|
int thicknessDp
|
||||||
|
) {
|
||||||
|
this.position = sanitizePosition(position);
|
||||||
|
this.alignment = sanitizeAlignment(alignment);
|
||||||
|
this.thicknessDp = clamp(
|
||||||
|
thicknessDp,
|
||||||
|
SbtDefaults.BATTERY_BAR_THICKNESS_DP_MIN,
|
||||||
|
SbtDefaults.BATTERY_BAR_THICKNESS_DP_MAX);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static BatteryBarGeometry global(SbtSettings settings) {
|
||||||
|
if (settings == null) {
|
||||||
|
return new BatteryBarGeometry(
|
||||||
|
SbtDefaults.BATTERY_BAR_POSITION_DEFAULT,
|
||||||
|
SbtDefaults.BATTERY_BAR_ALIGNMENT_DEFAULT,
|
||||||
|
SbtDefaults.BATTERY_BAR_THICKNESS_DP_DEFAULT);
|
||||||
|
}
|
||||||
|
return new BatteryBarGeometry(
|
||||||
|
settings.batteryBarPosition,
|
||||||
|
settings.batteryBarAlignment,
|
||||||
|
settings.batteryBarThicknessDp);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static BatteryBarGeometry resolve(
|
||||||
|
SbtSettings settings,
|
||||||
|
Scenario scenario
|
||||||
|
) {
|
||||||
|
BatteryBarGeometry fallback = global(settings);
|
||||||
|
if (settings == null || scenario == null) {
|
||||||
|
return fallback;
|
||||||
|
}
|
||||||
|
if (!settings.batteryBarGeometryOverrideEnabledScenarios.contains(scenario.id)) {
|
||||||
|
return fallback;
|
||||||
|
}
|
||||||
|
return decode(settings.batteryBarGeometryOverrides.get(scenario.id), fallback);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static BatteryBarGeometry decode(String value, BatteryBarGeometry fallback) {
|
||||||
|
BatteryBarGeometry safeFallback = fallback != null ? fallback : global(null);
|
||||||
|
if (value == null || value.isEmpty()) {
|
||||||
|
return safeFallback;
|
||||||
|
}
|
||||||
|
String[] parts = value.split("\\|", -1);
|
||||||
|
if (parts.length != 3 && parts.length != 4) {
|
||||||
|
return safeFallback;
|
||||||
|
}
|
||||||
|
return new BatteryBarGeometry(
|
||||||
|
parts[0],
|
||||||
|
parts[1],
|
||||||
|
parseInt(parts[2], safeFallback.thicknessDp));
|
||||||
|
}
|
||||||
|
|
||||||
|
public String encode() {
|
||||||
|
return position + "|" + alignment + "|" + thicknessDp;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String signature() {
|
||||||
|
return encode();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String enabledKey(Scenario scenario) {
|
||||||
|
return SbtSettings.batteryBarGeometryOverrideEnabledKey(scenario.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String valueKey(Scenario scenario) {
|
||||||
|
return SbtSettings.batteryBarGeometryKey(scenario.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String sanitizePosition(String position) {
|
||||||
|
return "bottom".equals(position) ? "bottom" : "top";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String sanitizeAlignment(String alignment) {
|
||||||
|
if ("rtl".equals(alignment) || "center".equals(alignment)) {
|
||||||
|
return alignment;
|
||||||
|
}
|
||||||
|
return "ltr";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int parseInt(String value, int fallback) {
|
||||||
|
try {
|
||||||
|
return Integer.parseInt(value);
|
||||||
|
} catch (NumberFormatException ignored) {
|
||||||
|
return fallback;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int clamp(int value, int min, int max) {
|
||||||
|
return Math.max(min, Math.min(max, value));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -134,6 +134,7 @@ public final class SbtDefaults {
|
|||||||
public static final String LAYOUT_STATUS_MIDDLE_SIDE_DEFAULT = "left";
|
public static final String LAYOUT_STATUS_MIDDLE_SIDE_DEFAULT = "left";
|
||||||
public static final int LAYOUT_STATUS_VERTICAL_OFFSET_PX_DEFAULT = 0;
|
public static final int LAYOUT_STATUS_VERTICAL_OFFSET_PX_DEFAULT = 0;
|
||||||
public static final int LAYOUT_STATUS_ICON_HEIGHT_STEPS_DEFAULT = 38;
|
public static final int LAYOUT_STATUS_ICON_HEIGHT_STEPS_DEFAULT = 38;
|
||||||
|
public static final String SYSTEM_ICON_DUAL_SIM_SIGNAL_MODE_DEFAULT = "separate";
|
||||||
public static final int LAYOUT_ICON_HEIGHT_STEPS_MIN = 10;
|
public static final int LAYOUT_ICON_HEIGHT_STEPS_MIN = 10;
|
||||||
public static final int LAYOUT_ICON_HEIGHT_STEPS_MAX = 100;
|
public static final int LAYOUT_ICON_HEIGHT_STEPS_MAX = 100;
|
||||||
public static final int LAYOUT_ICON_CONTAINER_COUNT_MIN = 0;
|
public static final int LAYOUT_ICON_CONTAINER_COUNT_MIN = 0;
|
||||||
|
|||||||
@@ -72,6 +72,9 @@ public final class SbtSettings {
|
|||||||
public static final String KEY_BATTERY_BAR_ALIGNMENT = "battery_bar_alignment";
|
public static final String KEY_BATTERY_BAR_ALIGNMENT = "battery_bar_alignment";
|
||||||
public static final String KEY_BATTERY_BAR_THICKNESS_DP = "battery_bar_thickness_dp";
|
public static final String KEY_BATTERY_BAR_THICKNESS_DP = "battery_bar_thickness_dp";
|
||||||
public static final String KEY_BATTERY_BAR_EDGE_OFFSET_DP = "battery_bar_edge_offset_dp";
|
public static final String KEY_BATTERY_BAR_EDGE_OFFSET_DP = "battery_bar_edge_offset_dp";
|
||||||
|
public static final String KEY_BATTERY_BAR_GEOMETRY_OVERRIDE_ENABLED_PREFIX =
|
||||||
|
"battery_bar_geometry_override_enabled_";
|
||||||
|
public static final String KEY_BATTERY_BAR_GEOMETRY_PREFIX = "battery_bar_geometry_";
|
||||||
public static final String KEY_BATTERY_BAR_MIN_LEVEL = "battery_bar_min_level";
|
public static final String KEY_BATTERY_BAR_MIN_LEVEL = "battery_bar_min_level";
|
||||||
public static final String KEY_BATTERY_BAR_MAX_LEVEL = "battery_bar_max_level";
|
public static final String KEY_BATTERY_BAR_MAX_LEVEL = "battery_bar_max_level";
|
||||||
public static final String KEY_BATTERY_BAR_DEFAULT_DISCHARGE_COLOR = "battery_bar_default_discharge_color";
|
public static final String KEY_BATTERY_BAR_DEFAULT_DISCHARGE_COLOR = "battery_bar_default_discharge_color";
|
||||||
@@ -156,6 +159,14 @@ public final class SbtSettings {
|
|||||||
public static final String KEY_LAYOUT_STATUS_ICON_HEIGHT_STEPS = "layout_status_icon_height_steps";
|
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;
|
public static final String KEY_SYSTEM_ICON_BLOCKED_MODES = SystemIconRules.PREF_KEY_BLOCKED_MODES;
|
||||||
public static final String KEY_SYSTEM_ICON_HIDE_SLOTS = "system_icon_hide_slots";
|
public static final String KEY_SYSTEM_ICON_HIDE_SLOTS = "system_icon_hide_slots";
|
||||||
|
public static final String KEY_SYSTEM_ICON_DUAL_SIM_SIGNAL_MODE = "system_icon_dual_sim_signal_mode";
|
||||||
|
public static final String SYSTEM_ICON_DUAL_SIM_SEPARATE = "separate";
|
||||||
|
public static final String SYSTEM_ICON_DUAL_SIM_FIRST = "first";
|
||||||
|
public static final String SYSTEM_ICON_DUAL_SIM_SECOND = "second";
|
||||||
|
public static final String SYSTEM_ICON_DUAL_SIM_STRONGEST = "strongest";
|
||||||
|
public static final String SYSTEM_ICON_DUAL_SIM_WEAKEST = "weakest";
|
||||||
|
public static final String SYSTEM_ICON_DUAL_SIM_DATA = "data";
|
||||||
|
public static final String SYSTEM_ICON_DUAL_SIM_PHONE = "phone";
|
||||||
public static final String KEY_APP_ICON_BLOCKED_MODES = AppIconRules.PREF_KEY_BLOCKED_MODES;
|
public static final String KEY_APP_ICON_BLOCKED_MODES = AppIconRules.PREF_KEY_BLOCKED_MODES;
|
||||||
public static final String KEY_APP_ICON_EXCEPTION_RULES = AppIconRules.PREF_KEY_EXCEPTION_RULES;
|
public static final String KEY_APP_ICON_EXCEPTION_RULES = AppIconRules.PREF_KEY_EXCEPTION_RULES;
|
||||||
|
|
||||||
@@ -251,6 +262,14 @@ public final class SbtSettings {
|
|||||||
return base == null || index <= 0 ? base : base + "_" + (index + 1);
|
return base == null || index <= 0 ? base : base + "_" + (index + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static String batteryBarGeometryOverrideEnabledKey(String scenarioId) {
|
||||||
|
return KEY_BATTERY_BAR_GEOMETRY_OVERRIDE_ENABLED_PREFIX + scenarioId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String batteryBarGeometryKey(String scenarioId) {
|
||||||
|
return KEY_BATTERY_BAR_GEOMETRY_PREFIX + scenarioId;
|
||||||
|
}
|
||||||
|
|
||||||
private static String suffixedKey(String base, int index) {
|
private static String suffixedKey(String base, int index) {
|
||||||
return indexedKey(base, index);
|
return indexedKey(base, index);
|
||||||
}
|
}
|
||||||
@@ -281,6 +300,8 @@ public final class SbtSettings {
|
|||||||
public final String batteryBarAlignment;
|
public final String batteryBarAlignment;
|
||||||
public final int batteryBarThicknessDp;
|
public final int batteryBarThicknessDp;
|
||||||
public final int batteryBarEdgeOffsetDp;
|
public final int batteryBarEdgeOffsetDp;
|
||||||
|
public final Set<String> batteryBarGeometryOverrideEnabledScenarios;
|
||||||
|
public final Map<String, String> batteryBarGeometryOverrides;
|
||||||
public final int batteryBarMinLevel;
|
public final int batteryBarMinLevel;
|
||||||
public final int batteryBarMaxLevel;
|
public final int batteryBarMaxLevel;
|
||||||
public final String batteryBarDefaultDischargeColor;
|
public final String batteryBarDefaultDischargeColor;
|
||||||
@@ -362,6 +383,7 @@ public final class SbtSettings {
|
|||||||
public final int layoutStatusAodIconHeightSteps;
|
public final int layoutStatusAodIconHeightSteps;
|
||||||
public final Map<String, String> clockCameraTypeOverrides;
|
public final Map<String, String> clockCameraTypeOverrides;
|
||||||
public final Map<String, Integer> systemIconBlockedModes;
|
public final Map<String, Integer> systemIconBlockedModes;
|
||||||
|
public final String systemIconDualSimSignalMode;
|
||||||
public final boolean statusChipsHideAll;
|
public final boolean statusChipsHideAll;
|
||||||
public final boolean statusChipsHideMediaUnlocked;
|
public final boolean statusChipsHideMediaUnlocked;
|
||||||
public final boolean statusChipsHideNavUnlocked;
|
public final boolean statusChipsHideNavUnlocked;
|
||||||
@@ -403,6 +425,8 @@ public final class SbtSettings {
|
|||||||
String batteryBarAlignment,
|
String batteryBarAlignment,
|
||||||
int batteryBarThicknessDp,
|
int batteryBarThicknessDp,
|
||||||
int batteryBarEdgeOffsetDp,
|
int batteryBarEdgeOffsetDp,
|
||||||
|
Set<String> batteryBarGeometryOverrideEnabledScenarios,
|
||||||
|
Map<String, String> batteryBarGeometryOverrides,
|
||||||
int batteryBarMinLevel,
|
int batteryBarMinLevel,
|
||||||
int batteryBarMaxLevel,
|
int batteryBarMaxLevel,
|
||||||
String batteryBarDefaultDischargeColor,
|
String batteryBarDefaultDischargeColor,
|
||||||
@@ -484,6 +508,7 @@ public final class SbtSettings {
|
|||||||
int[] layoutStatusAodVerticalOffsetsPx,
|
int[] layoutStatusAodVerticalOffsetsPx,
|
||||||
Map<String, String> clockCameraTypeOverrides,
|
Map<String, String> clockCameraTypeOverrides,
|
||||||
Map<String, Integer> systemIconBlockedModes,
|
Map<String, Integer> systemIconBlockedModes,
|
||||||
|
String systemIconDualSimSignalMode,
|
||||||
boolean statusChipsHideAll,
|
boolean statusChipsHideAll,
|
||||||
boolean statusChipsHideMediaUnlocked,
|
boolean statusChipsHideMediaUnlocked,
|
||||||
boolean statusChipsHideNavUnlocked,
|
boolean statusChipsHideNavUnlocked,
|
||||||
@@ -534,6 +559,9 @@ public final class SbtSettings {
|
|||||||
batteryBarEdgeOffsetDp,
|
batteryBarEdgeOffsetDp,
|
||||||
SbtDefaults.BATTERY_BAR_EDGE_OFFSET_DP_MIN,
|
SbtDefaults.BATTERY_BAR_EDGE_OFFSET_DP_MIN,
|
||||||
SbtDefaults.BATTERY_BAR_EDGE_OFFSET_DP_MAX);
|
SbtDefaults.BATTERY_BAR_EDGE_OFFSET_DP_MAX);
|
||||||
|
this.batteryBarGeometryOverrideEnabledScenarios =
|
||||||
|
sanitizeBatteryBarScenarioSet(batteryBarGeometryOverrideEnabledScenarios);
|
||||||
|
this.batteryBarGeometryOverrides = sanitizeBatteryBarScenarioMap(batteryBarGeometryOverrides);
|
||||||
int clampedMinLevel = clampInt(
|
int clampedMinLevel = clampInt(
|
||||||
batteryBarMinLevel,
|
batteryBarMinLevel,
|
||||||
SbtDefaults.BATTERY_BAR_MIN_LEVEL_MIN,
|
SbtDefaults.BATTERY_BAR_MIN_LEVEL_MIN,
|
||||||
@@ -753,6 +781,9 @@ public final class SbtSettings {
|
|||||||
this.systemIconBlockedModes = systemIconBlockedModes != null
|
this.systemIconBlockedModes = systemIconBlockedModes != null
|
||||||
? Collections.unmodifiableMap(new HashMap<>(systemIconBlockedModes))
|
? Collections.unmodifiableMap(new HashMap<>(systemIconBlockedModes))
|
||||||
: Collections.emptyMap();
|
: Collections.emptyMap();
|
||||||
|
this.systemIconDualSimSignalMode = validSystemIconDualSimSignalMode(systemIconDualSimSignalMode)
|
||||||
|
? systemIconDualSimSignalMode
|
||||||
|
: SbtDefaults.SYSTEM_ICON_DUAL_SIM_SIGNAL_MODE_DEFAULT;
|
||||||
this.statusChipsHideAll = statusChipsHideAll;
|
this.statusChipsHideAll = statusChipsHideAll;
|
||||||
this.statusChipsHideMediaUnlocked = statusChipsHideMediaUnlocked;
|
this.statusChipsHideMediaUnlocked = statusChipsHideMediaUnlocked;
|
||||||
this.statusChipsHideNavUnlocked = statusChipsHideNavUnlocked;
|
this.statusChipsHideNavUnlocked = statusChipsHideNavUnlocked;
|
||||||
@@ -810,6 +841,8 @@ public final class SbtSettings {
|
|||||||
SbtDefaults.BATTERY_BAR_ALIGNMENT_DEFAULT,
|
SbtDefaults.BATTERY_BAR_ALIGNMENT_DEFAULT,
|
||||||
SbtDefaults.BATTERY_BAR_THICKNESS_DP_DEFAULT,
|
SbtDefaults.BATTERY_BAR_THICKNESS_DP_DEFAULT,
|
||||||
SbtDefaults.BATTERY_BAR_EDGE_OFFSET_DP_DEFAULT,
|
SbtDefaults.BATTERY_BAR_EDGE_OFFSET_DP_DEFAULT,
|
||||||
|
Collections.emptySet(),
|
||||||
|
Collections.emptyMap(),
|
||||||
SbtDefaults.BATTERY_BAR_MIN_LEVEL_DEFAULT,
|
SbtDefaults.BATTERY_BAR_MIN_LEVEL_DEFAULT,
|
||||||
SbtDefaults.BATTERY_BAR_MAX_LEVEL_DEFAULT,
|
SbtDefaults.BATTERY_BAR_MAX_LEVEL_DEFAULT,
|
||||||
SbtDefaults.BATTERY_BAR_DEFAULT_DISCHARGE_COLOR_DEFAULT,
|
SbtDefaults.BATTERY_BAR_DEFAULT_DISCHARGE_COLOR_DEFAULT,
|
||||||
@@ -891,6 +924,7 @@ public final class SbtSettings {
|
|||||||
null,
|
null,
|
||||||
Collections.emptyMap(),
|
Collections.emptyMap(),
|
||||||
Collections.emptyMap(),
|
Collections.emptyMap(),
|
||||||
|
SbtDefaults.SYSTEM_ICON_DUAL_SIM_SIGNAL_MODE_DEFAULT,
|
||||||
false,
|
false,
|
||||||
false,
|
false,
|
||||||
false,
|
false,
|
||||||
@@ -914,6 +948,22 @@ public final class SbtSettings {
|
|||||||
return "dot";
|
return "dot";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean validSystemIconDualSimSignalMode(String mode) {
|
||||||
|
return SYSTEM_ICON_DUAL_SIM_SEPARATE.equals(mode)
|
||||||
|
|| SYSTEM_ICON_DUAL_SIM_FIRST.equals(mode)
|
||||||
|
|| SYSTEM_ICON_DUAL_SIM_SECOND.equals(mode)
|
||||||
|
|| SYSTEM_ICON_DUAL_SIM_STRONGEST.equals(mode)
|
||||||
|
|| SYSTEM_ICON_DUAL_SIM_WEAKEST.equals(mode)
|
||||||
|
|| SYSTEM_ICON_DUAL_SIM_DATA.equals(mode)
|
||||||
|
|| SYSTEM_ICON_DUAL_SIM_PHONE.equals(mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String readSystemIconDualSimSignalMode(String mode) {
|
||||||
|
return validSystemIconDualSimSignalMode(mode)
|
||||||
|
? mode
|
||||||
|
: SbtDefaults.SYSTEM_ICON_DUAL_SIM_SIGNAL_MODE_DEFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
public static SbtSettings from(Context context) {
|
public static SbtSettings from(Context context) {
|
||||||
if (context == null) {
|
if (context == null) {
|
||||||
return defaults();
|
return defaults();
|
||||||
@@ -1057,6 +1107,8 @@ public final class SbtSettings {
|
|||||||
clampInt(prefs.getInt(KEY_BATTERY_BAR_EDGE_OFFSET_DP, SbtDefaults.BATTERY_BAR_EDGE_OFFSET_DP_DEFAULT),
|
clampInt(prefs.getInt(KEY_BATTERY_BAR_EDGE_OFFSET_DP, SbtDefaults.BATTERY_BAR_EDGE_OFFSET_DP_DEFAULT),
|
||||||
SbtDefaults.BATTERY_BAR_EDGE_OFFSET_DP_MIN,
|
SbtDefaults.BATTERY_BAR_EDGE_OFFSET_DP_MIN,
|
||||||
SbtDefaults.BATTERY_BAR_EDGE_OFFSET_DP_MAX),
|
SbtDefaults.BATTERY_BAR_EDGE_OFFSET_DP_MAX),
|
||||||
|
readBatteryBarScenarioSetFromPrefs(prefs),
|
||||||
|
readBatteryBarScenarioMapFromPrefs(prefs),
|
||||||
clampInt(prefs.getInt(KEY_BATTERY_BAR_MIN_LEVEL, SbtDefaults.BATTERY_BAR_MIN_LEVEL_DEFAULT),
|
clampInt(prefs.getInt(KEY_BATTERY_BAR_MIN_LEVEL, SbtDefaults.BATTERY_BAR_MIN_LEVEL_DEFAULT),
|
||||||
SbtDefaults.BATTERY_BAR_MIN_LEVEL_MIN,
|
SbtDefaults.BATTERY_BAR_MIN_LEVEL_MIN,
|
||||||
SbtDefaults.BATTERY_BAR_MIN_LEVEL_MAX),
|
SbtDefaults.BATTERY_BAR_MIN_LEVEL_MAX),
|
||||||
@@ -1262,6 +1314,9 @@ public final class SbtSettings {
|
|||||||
SbtDefaults.LAYOUT_STATUS_VERTICAL_OFFSET_PX_DEFAULT),
|
SbtDefaults.LAYOUT_STATUS_VERTICAL_OFFSET_PX_DEFAULT),
|
||||||
ClockCameraTypeSupport.loadOverrides(prefs, KEY_CLOCK_CAMERA_TYPE_OVERRIDES),
|
ClockCameraTypeSupport.loadOverrides(prefs, KEY_CLOCK_CAMERA_TYPE_OVERRIDES),
|
||||||
systemIconBlockedModes,
|
systemIconBlockedModes,
|
||||||
|
readSystemIconDualSimSignalMode(prefs.getString(
|
||||||
|
KEY_SYSTEM_ICON_DUAL_SIM_SIGNAL_MODE,
|
||||||
|
SbtDefaults.SYSTEM_ICON_DUAL_SIM_SIGNAL_MODE_DEFAULT)),
|
||||||
prefs.getBoolean(KEY_STATUS_CHIPS_HIDE_ALL, false),
|
prefs.getBoolean(KEY_STATUS_CHIPS_HIDE_ALL, false),
|
||||||
prefs.getBoolean(KEY_STATUS_CHIPS_HIDE_MEDIA_UNLOCKED, false),
|
prefs.getBoolean(KEY_STATUS_CHIPS_HIDE_MEDIA_UNLOCKED, false),
|
||||||
prefs.getBoolean(KEY_STATUS_CHIPS_HIDE_NAV_UNLOCKED, false),
|
prefs.getBoolean(KEY_STATUS_CHIPS_HIDE_NAV_UNLOCKED, false),
|
||||||
@@ -1355,6 +1410,8 @@ public final class SbtSettings {
|
|||||||
clampInt(bundle.getInt(KEY_BATTERY_BAR_EDGE_OFFSET_DP, SbtDefaults.BATTERY_BAR_EDGE_OFFSET_DP_DEFAULT),
|
clampInt(bundle.getInt(KEY_BATTERY_BAR_EDGE_OFFSET_DP, SbtDefaults.BATTERY_BAR_EDGE_OFFSET_DP_DEFAULT),
|
||||||
SbtDefaults.BATTERY_BAR_EDGE_OFFSET_DP_MIN,
|
SbtDefaults.BATTERY_BAR_EDGE_OFFSET_DP_MIN,
|
||||||
SbtDefaults.BATTERY_BAR_EDGE_OFFSET_DP_MAX),
|
SbtDefaults.BATTERY_BAR_EDGE_OFFSET_DP_MAX),
|
||||||
|
readBatteryBarScenarioSetFromBundle(bundle),
|
||||||
|
readBatteryBarScenarioMapFromBundle(bundle),
|
||||||
clampInt(bundle.getInt(KEY_BATTERY_BAR_MIN_LEVEL, SbtDefaults.BATTERY_BAR_MIN_LEVEL_DEFAULT),
|
clampInt(bundle.getInt(KEY_BATTERY_BAR_MIN_LEVEL, SbtDefaults.BATTERY_BAR_MIN_LEVEL_DEFAULT),
|
||||||
SbtDefaults.BATTERY_BAR_MIN_LEVEL_MIN,
|
SbtDefaults.BATTERY_BAR_MIN_LEVEL_MIN,
|
||||||
SbtDefaults.BATTERY_BAR_MIN_LEVEL_MAX),
|
SbtDefaults.BATTERY_BAR_MIN_LEVEL_MAX),
|
||||||
@@ -1560,6 +1617,9 @@ public final class SbtSettings {
|
|||||||
SbtDefaults.LAYOUT_STATUS_VERTICAL_OFFSET_PX_DEFAULT),
|
SbtDefaults.LAYOUT_STATUS_VERTICAL_OFFSET_PX_DEFAULT),
|
||||||
ClockCameraTypeSupport.parseOverrides(readStringSetFromBundle(bundle, KEY_CLOCK_CAMERA_TYPE_OVERRIDES)),
|
ClockCameraTypeSupport.parseOverrides(readStringSetFromBundle(bundle, KEY_CLOCK_CAMERA_TYPE_OVERRIDES)),
|
||||||
systemIconBlockedModes,
|
systemIconBlockedModes,
|
||||||
|
readSystemIconDualSimSignalMode(bundle.getString(
|
||||||
|
KEY_SYSTEM_ICON_DUAL_SIM_SIGNAL_MODE,
|
||||||
|
SbtDefaults.SYSTEM_ICON_DUAL_SIM_SIGNAL_MODE_DEFAULT)),
|
||||||
bundle.getBoolean(KEY_STATUS_CHIPS_HIDE_ALL, false),
|
bundle.getBoolean(KEY_STATUS_CHIPS_HIDE_ALL, false),
|
||||||
bundle.getBoolean(KEY_STATUS_CHIPS_HIDE_MEDIA_UNLOCKED, false),
|
bundle.getBoolean(KEY_STATUS_CHIPS_HIDE_MEDIA_UNLOCKED, false),
|
||||||
bundle.getBoolean(KEY_STATUS_CHIPS_HIDE_NAV_UNLOCKED, false),
|
bundle.getBoolean(KEY_STATUS_CHIPS_HIDE_NAV_UNLOCKED, false),
|
||||||
@@ -1728,6 +1788,100 @@ public final class SbtSettings {
|
|||||||
return new HashSet<>(list);
|
return new HashSet<>(list);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static Set<String> readBatteryBarScenarioSetFromPrefs(SharedPreferences prefs) {
|
||||||
|
if (prefs == null) {
|
||||||
|
return Collections.emptySet();
|
||||||
|
}
|
||||||
|
HashSet<String> result = new HashSet<>();
|
||||||
|
for (BatteryBarGeometry.Scenario scenario : BatteryBarGeometry.Scenario.values()) {
|
||||||
|
if (prefs.getBoolean(batteryBarGeometryOverrideEnabledKey(scenario.id), false)) {
|
||||||
|
result.add(scenario.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Set<String> readBatteryBarScenarioSetFromBundle(Bundle bundle) {
|
||||||
|
if (bundle == null) {
|
||||||
|
return Collections.emptySet();
|
||||||
|
}
|
||||||
|
HashSet<String> result = new HashSet<>();
|
||||||
|
for (BatteryBarGeometry.Scenario scenario : BatteryBarGeometry.Scenario.values()) {
|
||||||
|
if (bundle.getBoolean(batteryBarGeometryOverrideEnabledKey(scenario.id), false)) {
|
||||||
|
result.add(scenario.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Map<String, String> readBatteryBarScenarioMapFromPrefs(SharedPreferences prefs) {
|
||||||
|
if (prefs == null) {
|
||||||
|
return Collections.emptyMap();
|
||||||
|
}
|
||||||
|
HashMap<String, String> result = new HashMap<>();
|
||||||
|
for (BatteryBarGeometry.Scenario scenario : BatteryBarGeometry.Scenario.values()) {
|
||||||
|
String value = prefs.getString(batteryBarGeometryKey(scenario.id), null);
|
||||||
|
if (value != null && !value.isEmpty()) {
|
||||||
|
result.put(scenario.id, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Map<String, String> readBatteryBarScenarioMapFromBundle(Bundle bundle) {
|
||||||
|
if (bundle == null) {
|
||||||
|
return Collections.emptyMap();
|
||||||
|
}
|
||||||
|
HashMap<String, String> result = new HashMap<>();
|
||||||
|
for (BatteryBarGeometry.Scenario scenario : BatteryBarGeometry.Scenario.values()) {
|
||||||
|
String value = bundle.getString(batteryBarGeometryKey(scenario.id), null);
|
||||||
|
if (value != null && !value.isEmpty()) {
|
||||||
|
result.put(scenario.id, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Set<String> sanitizeBatteryBarScenarioSet(Set<String> source) {
|
||||||
|
if (source == null || source.isEmpty()) {
|
||||||
|
return Collections.emptySet();
|
||||||
|
}
|
||||||
|
HashSet<String> result = new HashSet<>();
|
||||||
|
for (String id : source) {
|
||||||
|
if (isBatteryBarScenarioId(id)) {
|
||||||
|
result.add(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result.isEmpty() ? Collections.emptySet() : Collections.unmodifiableSet(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Map<String, String> sanitizeBatteryBarScenarioMap(Map<String, String> source) {
|
||||||
|
if (source == null || source.isEmpty()) {
|
||||||
|
return Collections.emptyMap();
|
||||||
|
}
|
||||||
|
HashMap<String, String> result = new HashMap<>();
|
||||||
|
for (Map.Entry<String, String> entry : source.entrySet()) {
|
||||||
|
String id = entry.getKey();
|
||||||
|
String value = entry.getValue();
|
||||||
|
if (isBatteryBarScenarioId(id) && value != null && !value.isEmpty()) {
|
||||||
|
result.put(id, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result.isEmpty() ? Collections.emptyMap() : Collections.unmodifiableMap(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isBatteryBarScenarioId(String id) {
|
||||||
|
if (id == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (BatteryBarGeometry.Scenario scenario : BatteryBarGeometry.Scenario.values()) {
|
||||||
|
if (scenario.id.equals(id)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
private static Map<String, Integer> readSystemIconBlockedModesFromPrefs(SharedPreferences prefs) {
|
private static Map<String, Integer> readSystemIconBlockedModesFromPrefs(SharedPreferences prefs) {
|
||||||
if (prefs == null) {
|
if (prefs == null) {
|
||||||
return Collections.emptyMap();
|
return Collections.emptyMap();
|
||||||
|
|||||||
@@ -99,6 +99,12 @@ public class SbtSettingsProvider extends ContentProvider {
|
|||||||
prefs.getInt(SbtSettings.KEY_BATTERY_BAR_THICKNESS_DP, SbtDefaults.BATTERY_BAR_THICKNESS_DP_DEFAULT));
|
prefs.getInt(SbtSettings.KEY_BATTERY_BAR_THICKNESS_DP, SbtDefaults.BATTERY_BAR_THICKNESS_DP_DEFAULT));
|
||||||
out.putInt(SbtSettings.KEY_BATTERY_BAR_EDGE_OFFSET_DP,
|
out.putInt(SbtSettings.KEY_BATTERY_BAR_EDGE_OFFSET_DP,
|
||||||
prefs.getInt(SbtSettings.KEY_BATTERY_BAR_EDGE_OFFSET_DP, SbtDefaults.BATTERY_BAR_EDGE_OFFSET_DP_DEFAULT));
|
prefs.getInt(SbtSettings.KEY_BATTERY_BAR_EDGE_OFFSET_DP, SbtDefaults.BATTERY_BAR_EDGE_OFFSET_DP_DEFAULT));
|
||||||
|
for (BatteryBarGeometry.Scenario scenario : BatteryBarGeometry.Scenario.values()) {
|
||||||
|
String enabledKey = BatteryBarGeometry.enabledKey(scenario);
|
||||||
|
String valueKey = BatteryBarGeometry.valueKey(scenario);
|
||||||
|
out.putBoolean(enabledKey, prefs.getBoolean(enabledKey, false));
|
||||||
|
out.putString(valueKey, prefs.getString(valueKey, null));
|
||||||
|
}
|
||||||
out.putInt(SbtSettings.KEY_BATTERY_BAR_MIN_LEVEL,
|
out.putInt(SbtSettings.KEY_BATTERY_BAR_MIN_LEVEL,
|
||||||
prefs.getInt(SbtSettings.KEY_BATTERY_BAR_MIN_LEVEL, SbtDefaults.BATTERY_BAR_MIN_LEVEL_DEFAULT));
|
prefs.getInt(SbtSettings.KEY_BATTERY_BAR_MIN_LEVEL, SbtDefaults.BATTERY_BAR_MIN_LEVEL_DEFAULT));
|
||||||
out.putInt(SbtSettings.KEY_BATTERY_BAR_MAX_LEVEL,
|
out.putInt(SbtSettings.KEY_BATTERY_BAR_MAX_LEVEL,
|
||||||
@@ -328,6 +334,10 @@ public class SbtSettingsProvider extends ContentProvider {
|
|||||||
SbtDefaults.LAYOUT_STATUS_MIDDLE_SIDE_DEFAULT,
|
SbtDefaults.LAYOUT_STATUS_MIDDLE_SIDE_DEFAULT,
|
||||||
SbtSettings::layoutStatusVerticalOffsetKey,
|
SbtSettings::layoutStatusVerticalOffsetKey,
|
||||||
SbtDefaults.LAYOUT_STATUS_VERTICAL_OFFSET_PX_DEFAULT);
|
SbtDefaults.LAYOUT_STATUS_VERTICAL_OFFSET_PX_DEFAULT);
|
||||||
|
out.putString(SbtSettings.KEY_SYSTEM_ICON_DUAL_SIM_SIGNAL_MODE,
|
||||||
|
SbtSettings.readSystemIconDualSimSignalMode(prefs.getString(
|
||||||
|
SbtSettings.KEY_SYSTEM_ICON_DUAL_SIM_SIGNAL_MODE,
|
||||||
|
SbtDefaults.SYSTEM_ICON_DUAL_SIM_SIGNAL_MODE_DEFAULT)));
|
||||||
out.putInt(SbtSettings.KEY_LAYOUT_STATUS_LOCK_COUNT,
|
out.putInt(SbtSettings.KEY_LAYOUT_STATUS_LOCK_COUNT,
|
||||||
prefs.getInt(SbtSettings.KEY_LAYOUT_STATUS_LOCK_COUNT,
|
prefs.getInt(SbtSettings.KEY_LAYOUT_STATUS_LOCK_COUNT,
|
||||||
prefs.getBoolean(
|
prefs.getBoolean(
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ import java.util.LinkedHashSet;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import se.ajpanton.statusbartweak.R;
|
import se.ajpanton.statusbartweak.R;
|
||||||
|
import se.ajpanton.statusbartweak.shell.settings.BatteryBarGeometry;
|
||||||
import se.ajpanton.statusbartweak.shell.settings.BatteryBarStyle;
|
import se.ajpanton.statusbartweak.shell.settings.BatteryBarStyle;
|
||||||
import se.ajpanton.statusbartweak.shell.settings.SbtDefaults;
|
import se.ajpanton.statusbartweak.shell.settings.SbtDefaults;
|
||||||
import se.ajpanton.statusbartweak.shell.settings.SbtSettings;
|
import se.ajpanton.statusbartweak.shell.settings.SbtSettings;
|
||||||
@@ -75,6 +76,8 @@ public final class BatteryBarFragment extends Fragment {
|
|||||||
MaterialButton addThresholdButton = root.findViewById(R.id.battery_bar_add_threshold_button);
|
MaterialButton addThresholdButton = root.findViewById(R.id.battery_bar_add_threshold_button);
|
||||||
LinearLayout thresholdsContainer = root.findViewById(R.id.battery_bar_thresholds_container);
|
LinearLayout thresholdsContainer = root.findViewById(R.id.battery_bar_thresholds_container);
|
||||||
View geometrySection = root.findViewById(R.id.battery_bar_geometry_section);
|
View geometrySection = root.findViewById(R.id.battery_bar_geometry_section);
|
||||||
|
LinearLayout scenarioOverridesContainer =
|
||||||
|
root.findViewById(R.id.battery_bar_scenario_overrides_container);
|
||||||
mappingSlider.setValueFrom(0f);
|
mappingSlider.setValueFrom(0f);
|
||||||
mappingSlider.setValueTo(100f);
|
mappingSlider.setValueTo(100f);
|
||||||
mappingSlider.setStepSize(1f);
|
mappingSlider.setStepSize(1f);
|
||||||
@@ -167,6 +170,8 @@ public final class BatteryBarFragment extends Fragment {
|
|||||||
SbtDefaults.BATTERY_BAR_EDGE_OFFSET_DP_MIN,
|
SbtDefaults.BATTERY_BAR_EDGE_OFFSET_DP_MIN,
|
||||||
SbtDefaults.BATTERY_BAR_EDGE_OFFSET_DP_MAX,
|
SbtDefaults.BATTERY_BAR_EDGE_OFFSET_DP_MAX,
|
||||||
SbtDefaults.BATTERY_BAR_EDGE_OFFSET_DP_DEFAULT);
|
SbtDefaults.BATTERY_BAR_EDGE_OFFSET_DP_DEFAULT);
|
||||||
|
renderScenarioOverrides(scenarioOverridesContainer, prefs);
|
||||||
|
setSectionEnabled(geometrySection, enabled.isChecked());
|
||||||
|
|
||||||
mappingSlider.addOnChangeListener((slider, value, fromUser) -> {
|
mappingSlider.addOnChangeListener((slider, value, fromUser) -> {
|
||||||
if (!fromUser) {
|
if (!fromUser) {
|
||||||
@@ -347,6 +352,252 @@ public final class BatteryBarFragment extends Fragment {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void renderScenarioOverrides(LinearLayout container, SharedPreferences prefs) {
|
||||||
|
if (container == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
container.removeAllViews();
|
||||||
|
for (BatteryBarGeometry.Scenario scenario : BatteryBarGeometry.Scenario.values()) {
|
||||||
|
addScenarioOverrideRow(container, prefs, scenario, scenarioLabelRes(scenario));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addScenarioOverrideRow(
|
||||||
|
LinearLayout container,
|
||||||
|
SharedPreferences prefs,
|
||||||
|
BatteryBarGeometry.Scenario scenario,
|
||||||
|
int labelRes
|
||||||
|
) {
|
||||||
|
Context context = container.getContext();
|
||||||
|
LinearLayout row = new LinearLayout(context);
|
||||||
|
row.setGravity(Gravity.CENTER_VERTICAL);
|
||||||
|
row.setOrientation(LinearLayout.HORIZONTAL);
|
||||||
|
LinearLayout.LayoutParams rowParams = new LinearLayout.LayoutParams(
|
||||||
|
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||||
|
ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||||
|
rowParams.topMargin = ViewTreeSupport.dp(context, 8);
|
||||||
|
row.setLayoutParams(rowParams);
|
||||||
|
|
||||||
|
LinearLayout textColumn = new LinearLayout(context);
|
||||||
|
textColumn.setOrientation(LinearLayout.VERTICAL);
|
||||||
|
textColumn.setLayoutParams(new LinearLayout.LayoutParams(
|
||||||
|
0,
|
||||||
|
ViewGroup.LayoutParams.WRAP_CONTENT,
|
||||||
|
1f));
|
||||||
|
|
||||||
|
SwitchMaterial enabledSwitch = new SwitchMaterial(context);
|
||||||
|
enabledSwitch.setText(labelRes);
|
||||||
|
enabledSwitch.setLayoutParams(new LinearLayout.LayoutParams(
|
||||||
|
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||||
|
ViewGroup.LayoutParams.WRAP_CONTENT));
|
||||||
|
textColumn.addView(enabledSwitch);
|
||||||
|
|
||||||
|
TextView summary = new TextView(context);
|
||||||
|
summary.setTextAppearance(android.R.style.TextAppearance_Material_Body2);
|
||||||
|
summary.setPadding(ViewTreeSupport.dp(context, 48), 0, 0, 0);
|
||||||
|
textColumn.addView(summary);
|
||||||
|
|
||||||
|
MaterialButton editButton = new MaterialButton(context, null, com.google.android.material.R.attr.materialButtonOutlinedStyle);
|
||||||
|
editButton.setText(R.string.battery_bar_edit_override);
|
||||||
|
|
||||||
|
row.addView(textColumn);
|
||||||
|
row.addView(editButton);
|
||||||
|
container.addView(row);
|
||||||
|
bindScenarioOverride(scenario, labelRes, enabledSwitch, editButton, summary, prefs);
|
||||||
|
}
|
||||||
|
|
||||||
|
private int scenarioLabelRes(BatteryBarGeometry.Scenario scenario) {
|
||||||
|
if (scenario == BatteryBarGeometry.Scenario.UNLOCKED_LANDSCAPE) {
|
||||||
|
return R.string.battery_bar_unlocked_landscape_override;
|
||||||
|
}
|
||||||
|
if (scenario == BatteryBarGeometry.Scenario.TRANSPARENT_PORTRAIT) {
|
||||||
|
return R.string.battery_bar_transparent_portrait_override;
|
||||||
|
}
|
||||||
|
if (scenario == BatteryBarGeometry.Scenario.TRANSPARENT_LANDSCAPE) {
|
||||||
|
return R.string.battery_bar_transparent_landscape_override;
|
||||||
|
}
|
||||||
|
if (scenario == BatteryBarGeometry.Scenario.FULLSCREEN_PORTRAIT) {
|
||||||
|
return R.string.battery_bar_fullscreen_portrait_override;
|
||||||
|
}
|
||||||
|
if (scenario == BatteryBarGeometry.Scenario.FULLSCREEN_LANDSCAPE) {
|
||||||
|
return R.string.battery_bar_fullscreen_landscape_override;
|
||||||
|
}
|
||||||
|
if (scenario == BatteryBarGeometry.Scenario.LOCKSCREEN) {
|
||||||
|
return R.string.battery_bar_lockscreen_override;
|
||||||
|
}
|
||||||
|
return R.string.battery_bar_unlocked_portrait_override;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void bindScenarioOverride(
|
||||||
|
BatteryBarGeometry.Scenario scenario,
|
||||||
|
int labelRes,
|
||||||
|
SwitchMaterial enabledSwitch,
|
||||||
|
MaterialButton editButton,
|
||||||
|
TextView summary,
|
||||||
|
SharedPreferences prefs
|
||||||
|
) {
|
||||||
|
if (scenario == null || prefs == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
String enabledKey = BatteryBarGeometry.enabledKey(scenario);
|
||||||
|
boolean enabled = prefs.getBoolean(enabledKey, false);
|
||||||
|
if (enabledSwitch != null) {
|
||||||
|
enabledSwitch.setChecked(enabled);
|
||||||
|
enabledSwitch.setOnCheckedChangeListener((buttonView, isChecked) -> {
|
||||||
|
SharedPreferences.Editor editor = prefs.edit().putBoolean(enabledKey, isChecked);
|
||||||
|
if (isChecked && !prefs.contains(BatteryBarGeometry.valueKey(scenario))) {
|
||||||
|
editor.putString(
|
||||||
|
BatteryBarGeometry.valueKey(scenario),
|
||||||
|
globalGeometryFromPrefs(prefs).encode());
|
||||||
|
}
|
||||||
|
editor.apply();
|
||||||
|
SbtSettings.ensureReadable(requireContext());
|
||||||
|
updateScenarioSummary(scenario, summary, editButton, prefs);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (editButton != null) {
|
||||||
|
editButton.setOnClickListener(v -> showScenarioGeometryDialog(
|
||||||
|
scenario,
|
||||||
|
labelRes,
|
||||||
|
prefs,
|
||||||
|
summary,
|
||||||
|
editButton));
|
||||||
|
}
|
||||||
|
updateScenarioSummary(scenario, summary, editButton, prefs);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateScenarioSummary(
|
||||||
|
BatteryBarGeometry.Scenario scenario,
|
||||||
|
TextView summary,
|
||||||
|
MaterialButton editButton,
|
||||||
|
SharedPreferences prefs
|
||||||
|
) {
|
||||||
|
if (scenario == null || prefs == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
boolean enabled = prefs.getBoolean(BatteryBarGeometry.enabledKey(scenario), false);
|
||||||
|
BatteryBarGeometry geometry = scenarioGeometryFromPrefs(prefs, scenario);
|
||||||
|
if (summary != null) {
|
||||||
|
summary.setText(getString(
|
||||||
|
R.string.battery_bar_override_summary,
|
||||||
|
geometry.position,
|
||||||
|
geometry.alignment,
|
||||||
|
geometry.thicknessDp));
|
||||||
|
summary.setAlpha(enabled ? 1f : 0.65f);
|
||||||
|
}
|
||||||
|
if (editButton != null) {
|
||||||
|
editButton.setEnabled(enabled);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private BatteryBarGeometry globalGeometryFromPrefs(SharedPreferences prefs) {
|
||||||
|
return new BatteryBarGeometry(
|
||||||
|
prefs.getString(
|
||||||
|
SbtSettings.KEY_BATTERY_BAR_POSITION,
|
||||||
|
SbtDefaults.BATTERY_BAR_POSITION_DEFAULT),
|
||||||
|
prefs.getString(
|
||||||
|
SbtSettings.KEY_BATTERY_BAR_ALIGNMENT,
|
||||||
|
SbtDefaults.BATTERY_BAR_ALIGNMENT_DEFAULT),
|
||||||
|
prefs.getInt(
|
||||||
|
SbtSettings.KEY_BATTERY_BAR_THICKNESS_DP,
|
||||||
|
SbtDefaults.BATTERY_BAR_THICKNESS_DP_DEFAULT));
|
||||||
|
}
|
||||||
|
|
||||||
|
private BatteryBarGeometry scenarioGeometryFromPrefs(
|
||||||
|
SharedPreferences prefs,
|
||||||
|
BatteryBarGeometry.Scenario scenario
|
||||||
|
) {
|
||||||
|
return BatteryBarGeometry.decode(
|
||||||
|
prefs.getString(BatteryBarGeometry.valueKey(scenario), null),
|
||||||
|
globalGeometryFromPrefs(prefs));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void showScenarioGeometryDialog(
|
||||||
|
BatteryBarGeometry.Scenario scenario,
|
||||||
|
int labelRes,
|
||||||
|
SharedPreferences prefs,
|
||||||
|
TextView summary,
|
||||||
|
MaterialButton editButton
|
||||||
|
) {
|
||||||
|
BatteryBarGeometry current = scenarioGeometryFromPrefs(prefs, scenario);
|
||||||
|
LinearLayout content = new LinearLayout(requireContext());
|
||||||
|
content.setOrientation(LinearLayout.VERTICAL);
|
||||||
|
int padding = Math.round(20 * getResources().getDisplayMetrics().density);
|
||||||
|
content.setPadding(padding, 0, padding, 0);
|
||||||
|
|
||||||
|
TextView positionTitle = dialogLabel(R.string.battery_bar_position_title);
|
||||||
|
content.addView(positionTitle);
|
||||||
|
RadioGroup positionGroup = new RadioGroup(requireContext());
|
||||||
|
RadioButton positionTop = dialogRadio(R.string.battery_bar_position_top);
|
||||||
|
RadioButton positionBottom = dialogRadio(R.string.battery_bar_position_bottom);
|
||||||
|
positionGroup.addView(positionTop);
|
||||||
|
positionGroup.addView(positionBottom);
|
||||||
|
applyPositionSelection(positionTop, positionBottom, current.position);
|
||||||
|
content.addView(positionGroup);
|
||||||
|
|
||||||
|
TextView alignmentTitle = dialogLabel(R.string.battery_bar_alignment_title);
|
||||||
|
alignmentTitle.setPadding(0, padding / 2, 0, 0);
|
||||||
|
content.addView(alignmentTitle);
|
||||||
|
RadioGroup alignmentGroup = new RadioGroup(requireContext());
|
||||||
|
RadioButton alignmentLtr = dialogRadio(R.string.battery_bar_alignment_ltr);
|
||||||
|
RadioButton alignmentCenter = dialogRadio(R.string.battery_bar_alignment_center);
|
||||||
|
RadioButton alignmentRtl = dialogRadio(R.string.battery_bar_alignment_rtl);
|
||||||
|
alignmentGroup.addView(alignmentLtr);
|
||||||
|
alignmentGroup.addView(alignmentCenter);
|
||||||
|
alignmentGroup.addView(alignmentRtl);
|
||||||
|
applyAlignmentSelection(alignmentLtr, alignmentRtl, alignmentCenter, current.alignment);
|
||||||
|
content.addView(alignmentGroup);
|
||||||
|
|
||||||
|
EditText thicknessInput = dialogNumberInput(current.thicknessDp);
|
||||||
|
content.addView(dialogLabel(R.string.battery_bar_thickness_title));
|
||||||
|
content.addView(thicknessInput);
|
||||||
|
|
||||||
|
new MaterialAlertDialogBuilder(requireContext())
|
||||||
|
.setTitle(getString(R.string.battery_bar_override_dialog_title, getString(labelRes)))
|
||||||
|
.setView(content)
|
||||||
|
.setNegativeButton(android.R.string.cancel, null)
|
||||||
|
.setPositiveButton(android.R.string.ok, (dialog, which) -> {
|
||||||
|
BatteryBarGeometry updated = new BatteryBarGeometry(
|
||||||
|
positionBottom.isChecked() ? "bottom" : "top",
|
||||||
|
alignmentRtl.isChecked()
|
||||||
|
? "rtl"
|
||||||
|
: alignmentCenter.isChecked() ? "center" : "ltr",
|
||||||
|
ViewTreeSupport.clamp(
|
||||||
|
ViewTreeSupport.parseInt(thicknessInput, current.thicknessDp),
|
||||||
|
SbtDefaults.BATTERY_BAR_THICKNESS_DP_MIN,
|
||||||
|
SbtDefaults.BATTERY_BAR_THICKNESS_DP_MAX));
|
||||||
|
prefs.edit()
|
||||||
|
.putString(BatteryBarGeometry.valueKey(scenario), updated.encode())
|
||||||
|
.apply();
|
||||||
|
SbtSettings.ensureReadable(requireContext());
|
||||||
|
updateScenarioSummary(scenario, summary, editButton, prefs);
|
||||||
|
})
|
||||||
|
.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
private TextView dialogLabel(int resId) {
|
||||||
|
TextView view = new TextView(requireContext());
|
||||||
|
view.setText(resId);
|
||||||
|
view.setTextAppearance(android.R.style.TextAppearance_Material_Body1);
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
|
||||||
|
private RadioButton dialogRadio(int resId) {
|
||||||
|
RadioButton radioButton = new RadioButton(requireContext());
|
||||||
|
radioButton.setId(View.generateViewId());
|
||||||
|
radioButton.setText(resId);
|
||||||
|
return radioButton;
|
||||||
|
}
|
||||||
|
|
||||||
|
private EditText dialogNumberInput(int value) {
|
||||||
|
EditText input = new EditText(requireContext());
|
||||||
|
input.setInputType(InputType.TYPE_CLASS_NUMBER);
|
||||||
|
input.setSingleLine(true);
|
||||||
|
input.setGravity(Gravity.CENTER);
|
||||||
|
input.setText(String.valueOf(value));
|
||||||
|
return input;
|
||||||
|
}
|
||||||
|
|
||||||
private void adjustMapping(SharedPreferences prefs,
|
private void adjustMapping(SharedPreferences prefs,
|
||||||
RangeSlider slider,
|
RangeSlider slider,
|
||||||
TextView minLabel,
|
TextView minLabel,
|
||||||
|
|||||||
@@ -10,9 +10,11 @@ import android.content.Context;
|
|||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.content.res.ColorStateList;
|
import android.content.res.ColorStateList;
|
||||||
import android.graphics.drawable.BitmapDrawable;
|
import android.graphics.drawable.BitmapDrawable;
|
||||||
|
import android.graphics.drawable.ColorDrawable;
|
||||||
import android.graphics.drawable.Drawable;
|
import android.graphics.drawable.Drawable;
|
||||||
import android.graphics.drawable.NinePatchDrawable;
|
import android.graphics.drawable.NinePatchDrawable;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.text.TextUtils;
|
||||||
import android.view.Gravity;
|
import android.view.Gravity;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
@@ -20,6 +22,7 @@ import android.view.ViewGroup;
|
|||||||
import android.widget.FrameLayout;
|
import android.widget.FrameLayout;
|
||||||
import android.widget.ImageView;
|
import android.widget.ImageView;
|
||||||
import android.widget.LinearLayout;
|
import android.widget.LinearLayout;
|
||||||
|
import android.widget.PopupWindow;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
@@ -40,6 +43,8 @@ public class SystemIconsFragment extends Fragment {
|
|||||||
|
|
||||||
private static final String QUICKSTAR_PACKAGE = "com.samsung.android.qstuner";
|
private static final String QUICKSTAR_PACKAGE = "com.samsung.android.qstuner";
|
||||||
private static final int TOGGLE_ICON_DP = 24;
|
private static final int TOGGLE_ICON_DP = 24;
|
||||||
|
private static final int MODE_TOGGLE_COLUMN_DP = 96;
|
||||||
|
private static final int DROPDOWN_ARROW_DP = 18;
|
||||||
|
|
||||||
private static final List<SystemIconSection> SECTIONS = Arrays.asList(
|
private static final List<SystemIconSection> SECTIONS = Arrays.asList(
|
||||||
new SystemIconSection(
|
new SystemIconSection(
|
||||||
@@ -124,6 +129,7 @@ public class SystemIconsFragment extends Fragment {
|
|||||||
.getSharedPreferences(SbtSettings.PREFS_NAME, Context.MODE_PRIVATE);
|
.getSharedPreferences(SbtSettings.PREFS_NAME, Context.MODE_PRIVATE);
|
||||||
SbtSettings.ensureReadable(requireContext());
|
SbtSettings.ensureReadable(requireContext());
|
||||||
Map<String, Integer> blockedModes = new HashMap<>(SbtSettings.from(requireContext()).systemIconBlockedModes);
|
Map<String, Integer> blockedModes = new HashMap<>(SbtSettings.from(requireContext()).systemIconBlockedModes);
|
||||||
|
int leadingColumnWidth = leadingColumnWidth(requireContext());
|
||||||
|
|
||||||
Map<Integer, LinearLayout> sectionContainers = new HashMap<>();
|
Map<Integer, LinearLayout> sectionContainers = new HashMap<>();
|
||||||
sectionContainers.put(R.string.system_icons_group_connectivity,
|
sectionContainers.put(R.string.system_icons_group_connectivity,
|
||||||
@@ -139,13 +145,189 @@ public class SystemIconsFragment extends Fragment {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
for (SystemIconEntry entry : section.entries) {
|
for (SystemIconEntry entry : section.entries) {
|
||||||
sectionContainer.addView(createRow(sectionContainer, prefs, blockedModes, entry));
|
sectionContainer.addView(createRow(
|
||||||
|
sectionContainer,
|
||||||
|
prefs,
|
||||||
|
blockedModes,
|
||||||
|
entry,
|
||||||
|
leadingColumnWidth));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
applyLayoutAvailability(root, prefs);
|
applyLayoutAvailability(root, prefs);
|
||||||
return root;
|
return root;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private int leadingColumnWidth(Context context) {
|
||||||
|
int toggleColumnWidth = ViewTreeSupport.dp(context, MODE_TOGGLE_COLUMN_DP);
|
||||||
|
TextView probe = new TextView(context);
|
||||||
|
probe.setTextAppearance(com.google.android.material.R.style.TextAppearance_MaterialComponents_Body1);
|
||||||
|
float textWidth = 0f;
|
||||||
|
for (String label : dualSimSignalModeLabels(context)) {
|
||||||
|
textWidth = Math.max(textWidth, probe.getPaint().measureText(label));
|
||||||
|
}
|
||||||
|
int dropdownWidth = Math.round(textWidth)
|
||||||
|
+ ViewTreeSupport.dp(context, DROPDOWN_ARROW_DP + 4);
|
||||||
|
return Math.max(toggleColumnWidth, dropdownWidth);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void bindDualSimSignalMode(
|
||||||
|
LinearLayout section,
|
||||||
|
SharedPreferences prefs,
|
||||||
|
int leadingColumnWidth
|
||||||
|
) {
|
||||||
|
if (section == null || prefs == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Context context = section.getContext();
|
||||||
|
LinearLayout row = new LinearLayout(context);
|
||||||
|
row.setOrientation(LinearLayout.HORIZONTAL);
|
||||||
|
row.setGravity(Gravity.CENTER_VERTICAL);
|
||||||
|
LinearLayout.LayoutParams rowParams = new LinearLayout.LayoutParams(
|
||||||
|
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||||
|
ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||||
|
rowParams.topMargin = ViewTreeSupport.dp(context, 4);
|
||||||
|
section.addView(row, rowParams);
|
||||||
|
|
||||||
|
String[] labels = dualSimSignalModeLabels(context);
|
||||||
|
String current = SbtSettings.readSystemIconDualSimSignalMode(prefs.getString(
|
||||||
|
SbtSettings.KEY_SYSTEM_ICON_DUAL_SIM_SIGNAL_MODE,
|
||||||
|
SbtDefaults.SYSTEM_ICON_DUAL_SIM_SIGNAL_MODE_DEFAULT));
|
||||||
|
LinearLayout dropdownCell = new LinearLayout(context);
|
||||||
|
dropdownCell.setOrientation(LinearLayout.HORIZONTAL);
|
||||||
|
dropdownCell.setGravity(Gravity.CENTER_VERTICAL);
|
||||||
|
dropdownCell.setClickable(true);
|
||||||
|
dropdownCell.setFocusable(true);
|
||||||
|
LinearLayout.LayoutParams dropdownCellParams = new LinearLayout.LayoutParams(
|
||||||
|
leadingColumnWidth,
|
||||||
|
ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||||
|
row.addView(dropdownCell, dropdownCellParams);
|
||||||
|
|
||||||
|
TextView selected = new TextView(context);
|
||||||
|
selected.setText(labels[indexForDualSimSignalMode(current)]);
|
||||||
|
selected.setTextAppearance(com.google.android.material.R.style.TextAppearance_MaterialComponents_Body1);
|
||||||
|
selected.setSingleLine(true);
|
||||||
|
selected.setEllipsize(TextUtils.TruncateAt.END);
|
||||||
|
LinearLayout.LayoutParams selectedParams = new LinearLayout.LayoutParams(
|
||||||
|
0,
|
||||||
|
ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||||
|
selectedParams.weight = 1f;
|
||||||
|
dropdownCell.addView(selected, selectedParams);
|
||||||
|
|
||||||
|
TextView arrow = new TextView(context);
|
||||||
|
arrow.setText("\u25BE");
|
||||||
|
arrow.setGravity(Gravity.CENTER);
|
||||||
|
arrow.setTextAppearance(com.google.android.material.R.style.TextAppearance_MaterialComponents_Body1);
|
||||||
|
dropdownCell.addView(arrow, new LinearLayout.LayoutParams(
|
||||||
|
ViewTreeSupport.dp(context, DROPDOWN_ARROW_DP),
|
||||||
|
ViewGroup.LayoutParams.WRAP_CONTENT));
|
||||||
|
dropdownCell.setOnClickListener(v -> showDualSimSignalModePopup(
|
||||||
|
dropdownCell,
|
||||||
|
selected,
|
||||||
|
prefs));
|
||||||
|
|
||||||
|
TextView label = new TextView(context);
|
||||||
|
label.setText(R.string.system_icons_dual_sim_signal_mode);
|
||||||
|
label.setTextAppearance(com.google.android.material.R.style.TextAppearance_MaterialComponents_Body1);
|
||||||
|
LinearLayout.LayoutParams labelParams = new LinearLayout.LayoutParams(
|
||||||
|
0,
|
||||||
|
ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||||
|
labelParams.weight = 1f;
|
||||||
|
labelParams.leftMargin = ViewTreeSupport.dp(context, 8);
|
||||||
|
row.addView(label, labelParams);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void showDualSimSignalModePopup(
|
||||||
|
View anchor,
|
||||||
|
TextView selected,
|
||||||
|
SharedPreferences prefs
|
||||||
|
) {
|
||||||
|
if (anchor == null || selected == null || prefs == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Context context = anchor.getContext();
|
||||||
|
LinearLayout content = new LinearLayout(context);
|
||||||
|
content.setOrientation(LinearLayout.VERTICAL);
|
||||||
|
int background = MaterialColors.getColor(
|
||||||
|
context,
|
||||||
|
com.google.android.material.R.attr.colorSurface,
|
||||||
|
0xFF202020);
|
||||||
|
content.setBackgroundColor(background);
|
||||||
|
String[] labels = dualSimSignalModeLabels(context);
|
||||||
|
for (int i = 0; i < labels.length; i++) {
|
||||||
|
int index = i;
|
||||||
|
TextView item = new TextView(context);
|
||||||
|
item.setText(labels[i]);
|
||||||
|
item.setTextAppearance(com.google.android.material.R.style.TextAppearance_MaterialComponents_Body1);
|
||||||
|
item.setSingleLine(true);
|
||||||
|
item.setPadding(
|
||||||
|
0,
|
||||||
|
ViewTreeSupport.dp(context, 12),
|
||||||
|
0,
|
||||||
|
ViewTreeSupport.dp(context, 12));
|
||||||
|
content.addView(item, new LinearLayout.LayoutParams(
|
||||||
|
Math.max(anchor.getWidth(), ViewTreeSupport.dp(context, MODE_TOGGLE_COLUMN_DP)),
|
||||||
|
ViewGroup.LayoutParams.WRAP_CONTENT));
|
||||||
|
item.setOnClickListener(v -> {
|
||||||
|
String mode = dualSimSignalModeForIndex(index);
|
||||||
|
prefs.edit()
|
||||||
|
.putString(SbtSettings.KEY_SYSTEM_ICON_DUAL_SIM_SIGNAL_MODE, mode)
|
||||||
|
.apply();
|
||||||
|
SbtSettings.ensureReadable(requireContext());
|
||||||
|
selected.setText(labels[index]);
|
||||||
|
Object popup = content.getTag();
|
||||||
|
if (popup instanceof PopupWindow popupWindow) {
|
||||||
|
popupWindow.dismiss();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
PopupWindow popupWindow = new PopupWindow(
|
||||||
|
content,
|
||||||
|
ViewGroup.LayoutParams.WRAP_CONTENT,
|
||||||
|
ViewGroup.LayoutParams.WRAP_CONTENT,
|
||||||
|
true);
|
||||||
|
popupWindow.setBackgroundDrawable(new ColorDrawable(0x00000000));
|
||||||
|
popupWindow.setOutsideTouchable(true);
|
||||||
|
popupWindow.setElevation(ViewTreeSupport.dp(context, 8));
|
||||||
|
content.setTag(popupWindow);
|
||||||
|
popupWindow.showAsDropDown(anchor);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String[] dualSimSignalModeLabels(Context context) {
|
||||||
|
return new String[] {
|
||||||
|
context.getString(R.string.system_icons_dual_sim_separate),
|
||||||
|
context.getString(R.string.system_icons_dual_sim_sim1),
|
||||||
|
context.getString(R.string.system_icons_dual_sim_sim2),
|
||||||
|
context.getString(R.string.system_icons_dual_sim_strongest),
|
||||||
|
context.getString(R.string.system_icons_dual_sim_weakest),
|
||||||
|
context.getString(R.string.system_icons_dual_sim_data),
|
||||||
|
context.getString(R.string.system_icons_dual_sim_phone)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private String dualSimSignalModeForIndex(int index) {
|
||||||
|
return switch (index) {
|
||||||
|
case 1 -> SbtSettings.SYSTEM_ICON_DUAL_SIM_FIRST;
|
||||||
|
case 2 -> SbtSettings.SYSTEM_ICON_DUAL_SIM_SECOND;
|
||||||
|
case 3 -> SbtSettings.SYSTEM_ICON_DUAL_SIM_STRONGEST;
|
||||||
|
case 4 -> SbtSettings.SYSTEM_ICON_DUAL_SIM_WEAKEST;
|
||||||
|
case 5 -> SbtSettings.SYSTEM_ICON_DUAL_SIM_DATA;
|
||||||
|
case 6 -> SbtSettings.SYSTEM_ICON_DUAL_SIM_PHONE;
|
||||||
|
default -> SbtSettings.SYSTEM_ICON_DUAL_SIM_SEPARATE;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private int indexForDualSimSignalMode(String mode) {
|
||||||
|
return switch (mode) {
|
||||||
|
case SbtSettings.SYSTEM_ICON_DUAL_SIM_FIRST -> 1;
|
||||||
|
case SbtSettings.SYSTEM_ICON_DUAL_SIM_SECOND -> 2;
|
||||||
|
case SbtSettings.SYSTEM_ICON_DUAL_SIM_STRONGEST -> 3;
|
||||||
|
case SbtSettings.SYSTEM_ICON_DUAL_SIM_WEAKEST -> 4;
|
||||||
|
case SbtSettings.SYSTEM_ICON_DUAL_SIM_DATA -> 5;
|
||||||
|
case SbtSettings.SYSTEM_ICON_DUAL_SIM_PHONE -> 6;
|
||||||
|
default -> 0;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
private void applyLayoutAvailability(View root, SharedPreferences prefs) {
|
private void applyLayoutAvailability(View root, SharedPreferences prefs) {
|
||||||
if (root == null || prefs == null) {
|
if (root == null || prefs == null) {
|
||||||
return;
|
return;
|
||||||
@@ -168,7 +350,8 @@ public class SystemIconsFragment extends Fragment {
|
|||||||
private View createRow(ViewGroup parent,
|
private View createRow(ViewGroup parent,
|
||||||
SharedPreferences prefs,
|
SharedPreferences prefs,
|
||||||
Map<String, Integer> blockedModes,
|
Map<String, Integer> blockedModes,
|
||||||
SystemIconEntry entry) {
|
SystemIconEntry entry,
|
||||||
|
int leadingColumnWidth) {
|
||||||
Context context = parent.getContext();
|
Context context = parent.getContext();
|
||||||
LinearLayout row = new LinearLayout(context);
|
LinearLayout row = new LinearLayout(context);
|
||||||
row.setOrientation(LinearLayout.HORIZONTAL);
|
row.setOrientation(LinearLayout.HORIZONTAL);
|
||||||
@@ -181,7 +364,7 @@ public class SystemIconsFragment extends Fragment {
|
|||||||
LinearLayout modeStrip = new LinearLayout(context);
|
LinearLayout modeStrip = new LinearLayout(context);
|
||||||
modeStrip.setOrientation(LinearLayout.HORIZONTAL);
|
modeStrip.setOrientation(LinearLayout.HORIZONTAL);
|
||||||
LinearLayout.LayoutParams modeStripParams = new LinearLayout.LayoutParams(
|
LinearLayout.LayoutParams modeStripParams = new LinearLayout.LayoutParams(
|
||||||
ViewGroup.LayoutParams.WRAP_CONTENT,
|
leadingColumnWidth,
|
||||||
ViewGroup.LayoutParams.WRAP_CONTENT);
|
ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||||
modeStrip.setLayoutParams(modeStripParams);
|
modeStrip.setLayoutParams(modeStripParams);
|
||||||
modeStrip.addView(createModeToggle(context, prefs, blockedModes, entry,
|
modeStrip.addView(createModeToggle(context, prefs, blockedModes, entry,
|
||||||
@@ -202,6 +385,16 @@ public class SystemIconsFragment extends Fragment {
|
|||||||
label.setText(context.getString(entry.labelRes));
|
label.setText(context.getString(entry.labelRes));
|
||||||
label.setTextAppearance(com.google.android.material.R.style.TextAppearance_MaterialComponents_Body1);
|
label.setTextAppearance(com.google.android.material.R.style.TextAppearance_MaterialComponents_Body1);
|
||||||
row.addView(label);
|
row.addView(label);
|
||||||
|
if (entry.slots.contains("mobile")) {
|
||||||
|
LinearLayout container = new LinearLayout(context);
|
||||||
|
container.setOrientation(LinearLayout.VERTICAL);
|
||||||
|
container.setLayoutParams(new LinearLayout.LayoutParams(
|
||||||
|
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||||
|
ViewGroup.LayoutParams.WRAP_CONTENT));
|
||||||
|
container.addView(row);
|
||||||
|
bindDualSimSignalMode(container, prefs, leadingColumnWidth);
|
||||||
|
return container;
|
||||||
|
}
|
||||||
return row;
|
return row;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -299,6 +299,45 @@
|
|||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</com.google.android.material.card.MaterialCardView>
|
</com.google.android.material.card.MaterialCardView>
|
||||||
|
|
||||||
|
<com.google.android.material.card.MaterialCardView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
app:cardUseCompatPadding="true"
|
||||||
|
app:strokeColor="@color/sbt_card_outline"
|
||||||
|
app:strokeWidth="1dp">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:padding="16dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:letterSpacing="0.03"
|
||||||
|
android:text="@string/battery_bar_scenario_overrides_title"
|
||||||
|
android:textAllCaps="true"
|
||||||
|
android:textAppearance="?attr/textAppearanceSubtitle1"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:text="@string/battery_bar_scenario_overrides_hint"
|
||||||
|
android:textAppearance="?attr/textAppearanceBody2" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/battery_bar_scenario_overrides_container"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:orientation="vertical" />
|
||||||
|
</LinearLayout>
|
||||||
|
</com.google.android.material.card.MaterialCardView>
|
||||||
|
|
||||||
<com.google.android.material.card.MaterialCardView
|
<com.google.android.material.card.MaterialCardView
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
|||||||
@@ -53,6 +53,14 @@
|
|||||||
<string name="system_icons_title">Hide status icons</string>
|
<string name="system_icons_title">Hide status icons</string>
|
||||||
<string name="system_icons_hint">Status-icon rules for the custom layout engine.</string>
|
<string name="system_icons_hint">Status-icon rules for the custom layout engine.</string>
|
||||||
<string name="system_icons_hide_hint">Icons represent, in order:\nAOD, Lockscreen, Unlocked.</string>
|
<string name="system_icons_hide_hint">Icons represent, in order:\nAOD, Lockscreen, Unlocked.</string>
|
||||||
|
<string name="system_icons_dual_sim_signal_mode">Dual-SIM signal icons</string>
|
||||||
|
<string name="system_icons_dual_sim_separate">Separate</string>
|
||||||
|
<string name="system_icons_dual_sim_sim1">SIM1</string>
|
||||||
|
<string name="system_icons_dual_sim_sim2">SIM2</string>
|
||||||
|
<string name="system_icons_dual_sim_strongest">Strongest</string>
|
||||||
|
<string name="system_icons_dual_sim_weakest">Weakest</string>
|
||||||
|
<string name="system_icons_dual_sim_data">Data SIM</string>
|
||||||
|
<string name="system_icons_dual_sim_phone">Phone SIM</string>
|
||||||
<string name="system_icons_group_connectivity">Connectivity</string>
|
<string name="system_icons_group_connectivity">Connectivity</string>
|
||||||
<string name="system_icons_group_notifications">Notifications</string>
|
<string name="system_icons_group_notifications">Notifications</string>
|
||||||
<string name="system_icons_group_system">System</string>
|
<string name="system_icons_group_system">System</string>
|
||||||
@@ -135,6 +143,18 @@
|
|||||||
<string name="battery_bar_alignment_center">↔</string>
|
<string name="battery_bar_alignment_center">↔</string>
|
||||||
<string name="battery_bar_thickness_title">Thickness (dp)</string>
|
<string name="battery_bar_thickness_title">Thickness (dp)</string>
|
||||||
<string name="battery_bar_edge_offset_title">Edge offset (dp)</string>
|
<string name="battery_bar_edge_offset_title">Edge offset (dp)</string>
|
||||||
|
<string name="battery_bar_scenario_overrides_title">Scenario overrides</string>
|
||||||
|
<string name="battery_bar_scenario_overrides_hint">Optional geometry overrides. Disabled scenarios use the default geometry above.</string>
|
||||||
|
<string name="battery_bar_unlocked_portrait_override">Unlocked portrait</string>
|
||||||
|
<string name="battery_bar_unlocked_landscape_override">Unlocked landscape</string>
|
||||||
|
<string name="battery_bar_transparent_portrait_override">Transparent statusbar portrait</string>
|
||||||
|
<string name="battery_bar_transparent_landscape_override">Transparent statusbar landscape</string>
|
||||||
|
<string name="battery_bar_fullscreen_portrait_override">Fullscreen portrait</string>
|
||||||
|
<string name="battery_bar_fullscreen_landscape_override">Fullscreen landscape</string>
|
||||||
|
<string name="battery_bar_lockscreen_override">Lockscreen</string>
|
||||||
|
<string name="battery_bar_edit_override">Edit</string>
|
||||||
|
<string name="battery_bar_override_dialog_title">Edit %1$s geometry</string>
|
||||||
|
<string name="battery_bar_override_summary">%1$s, %2$s, %3$ddp thick</string>
|
||||||
<string name="battery_bar_mapping_title">Battery mapping</string>
|
<string name="battery_bar_mapping_title">Battery mapping</string>
|
||||||
<string name="battery_bar_mapping_min_label">...0 at %1$d%%</string>
|
<string name="battery_bar_mapping_min_label">...0 at %1$d%%</string>
|
||||||
<string name="battery_bar_mapping_max_label">...full at %1$d%%</string>
|
<string name="battery_bar_mapping_max_label">...full at %1$d%%</string>
|
||||||
|
|||||||
Reference in New Issue
Block a user