From 38b1602fe80d1ebe2f7b2d550cdd3d615544d25a Mon Sep 17 00:00:00 2001 From: ajp_anton Date: Wed, 15 Jul 2026 18:00:18 +0000 Subject: [PATCH] Streamline battery bar rendering and cleanup --- .../platform/SystemUiCapabilities.java | 3 +- .../statusbartweak/runtime/ViewIdNames.java | 2 + .../battery/BatteryBarController.java | 118 +++++++++--------- .../runtime/hooks/InstanceStateStore.java | 43 ------- .../layout/StockLayoutCanvasController.java | 8 ++ .../shell/settings/BatteryBarGeometry.java | 4 - .../main/res/layout/fragment_battery_bar.xml | 1 + app/src/main/res/layout/item_hidden_app.xml | 1 + app/src/main/res/values-night/colors.xml | 1 - app/src/main/res/values-night/themes.xml | 23 ---- app/src/main/res/values/colors.xml | 2 - app/src/main/res/values/strings.xml | 29 ----- app/src/main/res/values/themes.xml | 22 ---- .../harness/HarnessForegroundService.java | 3 - .../statusbartweak/harness/MainActivity.java | 9 +- .../harness/ScenarioActivity.java | 10 +- 16 files changed, 80 insertions(+), 199 deletions(-) delete mode 100644 app/src/main/java/se/ajpanton/statusbartweak/runtime/hooks/InstanceStateStore.java delete mode 100644 app/src/main/res/values-night/themes.xml diff --git a/app/src/main/java/se/ajpanton/statusbartweak/platform/SystemUiCapabilities.java b/app/src/main/java/se/ajpanton/statusbartweak/platform/SystemUiCapabilities.java index 31a1b2f..feca8f4 100644 --- a/app/src/main/java/se/ajpanton/statusbartweak/platform/SystemUiCapabilities.java +++ b/app/src/main/java/se/ajpanton/statusbartweak/platform/SystemUiCapabilities.java @@ -9,12 +9,13 @@ public final class SystemUiCapabilities { public static final String PACKAGE_SYSTEM = "system"; public static final String PACKAGE_SYSTEMUI = "com.android.systemui"; public static final String PACKAGE_SAMSUNG_AOD_SERVICE = "com.samsung.android.app.aodservice"; + private static final boolean SAMSUNG_ONE_UI = "samsung".equalsIgnoreCase(Build.MANUFACTURER); private SystemUiCapabilities() { } public static boolean isSamsungOneUi() { - return "samsung".equalsIgnoreCase(Build.MANUFACTURER); + return SAMSUNG_ONE_UI; } public static boolean supportsLockscreenShadeWindowRoot() { diff --git a/app/src/main/java/se/ajpanton/statusbartweak/runtime/ViewIdNames.java b/app/src/main/java/se/ajpanton/statusbartweak/runtime/ViewIdNames.java index 38d64ad..576282a 100644 --- a/app/src/main/java/se/ajpanton/statusbartweak/runtime/ViewIdNames.java +++ b/app/src/main/java/se/ajpanton/statusbartweak/runtime/ViewIdNames.java @@ -1,5 +1,6 @@ package se.ajpanton.statusbartweak.runtime; +import android.annotation.SuppressLint; import android.content.res.Resources; import android.view.View; @@ -7,6 +8,7 @@ public final class ViewIdNames { private ViewIdNames() { } + @SuppressLint("ResourceType") // Foreign SystemUI ids are opaque integers, not this app's R.id values. public static String idName(View view) { if (view == null || view.getId() == View.NO_ID || (view.getId() >>> 24) == 0) { return ""; diff --git a/app/src/main/java/se/ajpanton/statusbartweak/runtime/features/battery/BatteryBarController.java b/app/src/main/java/se/ajpanton/statusbartweak/runtime/features/battery/BatteryBarController.java index ba941a8..1e7123f 100644 --- a/app/src/main/java/se/ajpanton/statusbartweak/runtime/features/battery/BatteryBarController.java +++ b/app/src/main/java/se/ajpanton/statusbartweak/runtime/features/battery/BatteryBarController.java @@ -59,8 +59,8 @@ final class BatteryBarController { private final Map overlayBatteryBars = new WeakHashMap<>(); private final Map rootLayoutListeners = new WeakHashMap<>(); private final Set pendingOverlayRetries = Collections.newSetFromMap(new WeakHashMap<>()); - private final Map lastSignatures = new WeakHashMap<>(); - private final Map lastOverlaySignatures = new WeakHashMap<>(); + private final Map lastSignatures = new WeakHashMap<>(); + private final Map lastOverlaySignatures = new WeakHashMap<>(); private final Map lastRootTransformSignatures = new WeakHashMap<>(); private int systemBarAppearance; private int systemBarBehavior; @@ -87,7 +87,7 @@ final class BatteryBarController { installRootTransformHooks(); installSystemBarAttributeListener(classLoader); installTransientBarListener(classLoader); - runtimeContext.getModeStateRepository().addSceneListener((previous, current) -> refreshAllRoots()); + runtimeContext.getModeStateRepository().addSceneListener((previous, current) -> postRefreshAllRoots()); hooksInstalled = true; } @@ -149,14 +149,6 @@ final class BatteryBarController { } 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)) { - applyBatteryBarAfterRootTransform(root); - } - return result; - }); XposedHookSupport.hookAllMethodsIfExists(framework, View.class, "setAlpha", chain -> { Object target = chain.getThisObject(); Object alphaArg = chain.getArgs() != null && !chain.getArgs().isEmpty() @@ -374,10 +366,6 @@ final class BatteryBarController { } private void applyBatteryBar(View root) { - applyBatteryBarInternal(root); - } - - private void applyBatteryBarInternal(View root) { if (!(root instanceof ViewGroup rootGroup)) { removeBatteryBarView(root); return; @@ -414,15 +402,15 @@ final class BatteryBarController { return; } removeOverlayBatteryBar(root); - String signature = buildSignature(root, settings, color, geometry, scenario); + RenderSignature signature = RenderSignature.of( + root, scenario, batteryLevel, plugged, color, geometry, settings); BatteryBarView existingView = batteryBarViews.get(root); - String lastSignature = lastSignatures.get(root); + RenderSignature lastSignature = lastSignatures.get(root); if (signature.equals(lastSignature) && existingView != null && existingView.getParent() == rootGroup) { existingView.bringToFront(); return; } applyEmbeddedBatteryBar(rootGroup, settings, geometry, scenario, color); - lastSignatures.put(root, signature); } private void applyEmbeddedBatteryBar( @@ -437,7 +425,9 @@ final class BatteryBarController { barView.update(rootGroup, rootGroup, settings, geometry, batteryLevel, plugged, color); barView.bringToFront(); barView.invalidate(); - lastSignatures.put(rootGroup, buildSignature(rootGroup, settings, color, geometry, scenario)); + lastSignatures.put( + rootGroup, + RenderSignature.of(rootGroup, scenario, batteryLevel, plugged, color, geometry, settings)); } private void applyOverlayBatteryBar( @@ -447,7 +437,8 @@ final class BatteryBarController { BatteryBarGeometry.Scenario scenario, int color ) { - String signature = buildSignature(root, settings, color, geometry, scenario); + RenderSignature signature = RenderSignature.of( + root, scenario, batteryLevel, plugged, color, geometry, settings); OverlayBatteryBar overlay = overlayBatteryBars.get(root); if (overlay == null) { overlay = new OverlayBatteryBar(root.getContext()); @@ -534,34 +525,6 @@ final class BatteryBarController { ); } - private String buildSignature( - View root, - SbtSettings settings, - int color, - BatteryBarGeometry geometry, - BatteryBarGeometry.Scenario scenario - ) { - return root.getWidth() + "|" - + root.getHeight() + "|" - + scenario + "|" - + batteryLevel + "|" - + plugged + "|" - + color + "|" - + settings.batteryBarEnabled + "|" - + settings.batteryBarScenarioEnabled + "|" - + settings.batteryBarPosition + "|" - + settings.batteryBarAlignment + "|" - + settings.batteryBarThicknessDp + "|" - + settings.batteryBarEdgeOffsetDp + "|" - + settings.batteryBarCurvedGeometryMode + "|" - + (geometry != null ? geometry.signature() : "") + "|" - + settings.batteryBarMinLevel + "|" - + settings.batteryBarMaxLevel + "|" - + settings.batteryBarDefaultDischargeColor + "|" - + settings.batteryBarDefaultChargeColor + "|" - + BatteryBarStyle.encodeThresholds(settings.batteryBarThresholds); - } - private BatteryBarGeometry.Scenario scenarioForRoot(View root) { if (isKeyguardRoot(root)) { return BatteryBarGeometry.Scenario.LOCKSCREEN; @@ -708,7 +671,15 @@ final class BatteryBarController { } private boolean shouldRenderVisibleFullscreenBar(View root) { - return root != null && isFullscreenSystemBarState() && statusBarTransientShown; + return root != null + && isFullscreenSystemBarState() + && statusBarTransientShown + && isStatusBarWindowVisible(root); + } + + private boolean isStatusBarWindowVisible(View root) { + WindowInsets insets = root.getRootWindowInsets(); + return insets == null || insets.isVisible(WindowInsets.Type.statusBars()); } private void updateTransientBarState(java.util.List args, boolean visible) { @@ -746,13 +717,7 @@ final class BatteryBarController { } 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); + return new OverlayBounds(root.getWidth(), root.getHeight()); } private static Rect currentWindowBounds(Context context) { @@ -774,6 +739,47 @@ final class BatteryBarController { private record OverlayBounds(int width, int height) { } + private record RenderSignature( + int width, + int height, + BatteryBarGeometry.Scenario scenario, + int batteryLevel, + boolean plugged, + int color, + String position, + String alignment, + int thicknessDp, + int edgeOffsetDp, + String curvedGeometryMode, + int minLevel, + int maxLevel + ) { + static RenderSignature of( + View root, + BatteryBarGeometry.Scenario scenario, + int batteryLevel, + boolean plugged, + int color, + BatteryBarGeometry geometry, + SbtSettings settings + ) { + return new RenderSignature( + root.getWidth(), + root.getHeight(), + scenario, + batteryLevel, + plugged, + color, + geometry.position, + geometry.alignment, + geometry.thicknessDp, + geometry.edgeOffsetDp, + geometry.curvedGeometryMode, + settings.batteryBarMinLevel, + settings.batteryBarMaxLevel); + } + } + private void updateSystemBarState(java.util.List args) { if (args == null || args.size() < 2) { return; diff --git a/app/src/main/java/se/ajpanton/statusbartweak/runtime/hooks/InstanceStateStore.java b/app/src/main/java/se/ajpanton/statusbartweak/runtime/hooks/InstanceStateStore.java deleted file mode 100644 index 2668e02..0000000 --- a/app/src/main/java/se/ajpanton/statusbartweak/runtime/hooks/InstanceStateStore.java +++ /dev/null @@ -1,43 +0,0 @@ -package se.ajpanton.statusbartweak.runtime.hooks; - -import java.util.HashMap; -import java.util.Map; -import java.util.WeakHashMap; - -/** - * Replacement for legacy Xposed additional-instance-field helpers. - */ -public final class InstanceStateStore { - - private final WeakHashMap> state = new WeakHashMap<>(); - - public synchronized Object get(Object target, String key) { - if (target == null || key == null) { - return null; - } - Map fields = state.get(target); - return fields != null ? fields.get(key) : null; - } - - public synchronized void put(Object target, String key, Object value) { - if (target == null || key == null) { - return; - } - Map fields = state.computeIfAbsent(target, ignored -> new HashMap<>()); - fields.put(key, value); - } - - public synchronized void remove(Object target, String key) { - if (target == null || key == null) { - return; - } - Map fields = state.get(target); - if (fields == null) { - return; - } - fields.remove(key); - if (fields.isEmpty()) { - state.remove(target); - } - } -} diff --git a/app/src/main/java/se/ajpanton/statusbartweak/runtime/layout/StockLayoutCanvasController.java b/app/src/main/java/se/ajpanton/statusbartweak/runtime/layout/StockLayoutCanvasController.java index 1538dc5..c4a0acf 100644 --- a/app/src/main/java/se/ajpanton/statusbartweak/runtime/layout/StockLayoutCanvasController.java +++ b/app/src/main/java/se/ajpanton/statusbartweak/runtime/layout/StockLayoutCanvasController.java @@ -1,8 +1,11 @@ package se.ajpanton.statusbartweak.runtime.layout; +import android.Manifest; +import android.annotation.SuppressLint; import android.app.Notification; import android.app.KeyguardManager; import android.content.Context; +import android.content.pm.PackageManager; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Color; @@ -3937,10 +3940,15 @@ final class StockLayoutCanvasController { return hasVisibleStatusChipContent(view); } + @SuppressLint("MissingPermission") // The SystemUI process owns READ_PHONE_STATE; the explicit check below handles other hosts. private boolean isPhoneCallActive(View view) { if (view == null || view.getContext() == null) { return false; } + if (view.getContext().checkSelfPermission(Manifest.permission.READ_PHONE_STATE) + != PackageManager.PERMISSION_GRANTED) { + return false; + } try { TelecomManager telecomManager = view.getContext().getSystemService(TelecomManager.class); return telecomManager != null && telecomManager.isInCall(); diff --git a/app/src/main/java/se/ajpanton/statusbartweak/shell/settings/BatteryBarGeometry.java b/app/src/main/java/se/ajpanton/statusbartweak/shell/settings/BatteryBarGeometry.java index 3d9a91f..94c49f2 100644 --- a/app/src/main/java/se/ajpanton/statusbartweak/shell/settings/BatteryBarGeometry.java +++ b/app/src/main/java/se/ajpanton/statusbartweak/shell/settings/BatteryBarGeometry.java @@ -107,10 +107,6 @@ public final class BatteryBarGeometry { return position + "|" + alignment + "|" + thicknessDp + "|" + edgeOffsetDp + "|" + curvedGeometryMode; } - public String signature() { - return encode(); - } - public static String enabledKey(Scenario scenario) { return SbtSettings.batteryBarGeometryOverrideEnabledKey(scenario.id); } diff --git a/app/src/main/res/layout/fragment_battery_bar.xml b/app/src/main/res/layout/fragment_battery_bar.xml index 2306f14..7602282 100644 --- a/app/src/main/res/layout/fragment_battery_bar.xml +++ b/app/src/main/res/layout/fragment_battery_bar.xml @@ -106,6 +106,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="8dp" + android:baselineAligned="false" android:orientation="horizontal"> #FF110B07 #FF1B0F08 #FF20140D - #FF302012 #FFFFFFFF #FFEAF0F3 #FFE3D3B6 diff --git a/app/src/main/res/values-night/themes.xml b/app/src/main/res/values-night/themes.xml deleted file mode 100644 index 5c01363..0000000 --- a/app/src/main/res/values-night/themes.xml +++ /dev/null @@ -1,23 +0,0 @@ - - - diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index 1f42b53..9bbdb21 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -8,12 +8,10 @@ #FFFFF4E8 #FFF5E5D2 #FFFFFFFF - #FFFFF1E0 #FFFFFFFF #FF111820 #FF62553F #FF000000 - #FFFFFFFF #24000000 #4D000000 #1AE65100 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 7977c3e..8c26a57 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -14,7 +14,6 @@     Lockscreen     AOD Battery bar - Hide notification icons Status chips Misc Debugging @@ -51,7 +50,6 @@ %1$s • %2$s unknown - Notification icons Hide status icons Status-icon rules for the custom layout engine. Icons represent, in order:\nAOD, Lockscreen, Unlocked. @@ -72,43 +70,27 @@ Bluetooth Data saver Do not disturb - Enhanced processing - Homescreen carrier Mobile hotspot - IMS network icons Location - Lockscreen carrier - Two phone user icon - Managed profile Mobile data signal - Modes NFC - Expanded panel carrier - Power saver - RCS - Two phone mode icon Volume VPN Wi-Fi Unlocked - Icons mode - Cards mode AOD Lockscreen Max icons per row Max rows Even distribution Limit to screen width - Cutout aware Keep AOD visible during calls Prevents SystemUI from suppressing AOD while an active call is in progress. - Notification icon app blocks Hide notification icons Pull down to refresh session apps. List contents are fixed until refresh. Icons represent, in order:\nAOD, Lockscreen, Unlocked Clear session list No apps yet. Generate a notification first. - Close Regex %1$s \u2022 %2$d exception @@ -126,7 +108,6 @@ Unlocked (unknown package) Status chips - Hide Samsung/SystemUI status chips (music, maps/navigation, call) in the top status area. Hide other status chips (unlocked) Hides detected status chips that are not media, navigation or call. Privacy indicators are excluded. Hide media chip (unlocked) @@ -142,9 +123,6 @@ Top edge of screen Bottom of status bar Alignment - - - Thickness (dp) Edge offset (dp) Follow screen edges @@ -163,15 +141,12 @@ Threshold colours Below a threshold, the first matching colour overrides the default. Add threshold - Set colour Examples: #FA0, FA0, or #FA0 ; invRGB 0.5 *RGB Threshold percent Applies when battery is at or below this percent. - Change threshold % Below %1$d%% Discharge colour Charge colour - Remove threshold Auto Same as discharge Default @@ -185,9 +160,7 @@ Layout Unlocked Master toggle - Use custom clock Use custom layout - Clock position Clock Carrier Status chip @@ -234,7 +207,6 @@ Vertical position Left side Right side - Custom format Statusbar clock Drawer clock Drawer date @@ -257,7 +229,6 @@ Search font family Copied: %1$s Date/time pattern reference only: https://docs.oracle.com/javase/8/docs/api/java/text/SimpleDateFormat.html - Invalid pattern Unplugged battery % on lockscreen Shows the plain battery percentage on the lockscreen when unplugged. Unplugged battery % on AOD diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml index 08f8f08..2738ffb 100644 --- a/app/src/main/res/values/themes.xml +++ b/app/src/main/res/values/themes.xml @@ -1,26 +1,4 @@ - -