diff --git a/README.md b/README.md index d08c0bf..c7d5c2b 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,13 @@ # StatusBarTweak -StatusBarTweak is an Xposed/LSPosed module for Samsung SystemUI. It focuses on -making the status bar, lockscreen, and Always On Display more configurable while -still keeping Samsung's own icons and visual style where possible. +StatusBarTweak is an Xposed/LSPosed module originally built for Samsung +SystemUI. It focuses on making the status bar, lockscreen, and Always On +Display more configurable while still keeping the system's own icons and visual +style where possible. -The project is currently tested only on a Samsung Galaxy Z Fold5 running One UI -8.0 / Android 16. Expect device- and One UI-version-specific behaviour. +The project is physically tested only on a Samsung Galaxy Z Fold5 running One UI +8.0 / Android 16. Other SystemUI builds have emulator-tested support as shown +in the compatibility matrix. ## AI Assistance @@ -14,7 +16,8 @@ decisions, physical device testing, and final behaviour are still manually revie ## What It Does -- Custom status bar layout for the unlocked screen, lockscreen, and AOD. +- Custom status bar layout for the unlocked screen, lockscreen, and AOD where + supported by the SystemUI build. - Independent placement of clock, carrier text, notification icons, status icons, and Samsung status chips. - Separate notification display handling for Samsung's cards, icons, and dot @@ -52,15 +55,16 @@ The in-app clock page has the full syntax help and examples. ## Requirements -- A rooted Android device or emulator. +- A rooted Android 14+ device or emulator. Actual feature support depends on + the SystemUI implementation; see the compatibility matrix. - A modern Xposed implementation such as LSPosed or another libxposed-compatible runtime. - Xposed API 101 or newer. - SystemUI access for the module scope. -The module is currently developed and manually tested on a Samsung Galaxy Z -Fold5 running One UI 8.0 / Android 16. Other Android versions and non-Samsung -SystemUI builds are being investigated, but should be treated as experimental -until the compatibility matrix says otherwise. +The module is developed and manually tested on a Samsung Galaxy Z Fold5 running +One UI 8.0 / Android 16. Non-Samsung builds have emulator-tested support as +shown below, but hardware-dependent behavior remains experimental unless it has +also been verified on physical hardware. ## Device Compatibility @@ -74,35 +78,35 @@ expose the required hardware or state for real verification. | Feature | Galaxy Z Fold5
One UI 8 / Android 16 | AOSP API 34
Android 14 | AOSP API 35
Android 15 | AOSP API 36
Android 16 | Google APIs API 35
Android 15 | Google APIs API 36
Android 16 | LineageOS 22
Android 15 | LineageOS 23
Android 16 | | --- | --- | --- | --- | --- | --- | --- | --- | --- | -| Module loads in SystemUI | ✅ | Untested | Untested | ✅ | Untested | Untested | Untested | Untested | -| Settings app opens and writes settings | ✅ | Untested | Untested | ✅ | Untested | Untested | Untested | Untested | -| Custom unlocked statusbar layout | ✅ | Untested | Untested | ✅ | Untested | Untested | Untested | Untested | -| Custom lockscreen statusbar layout | ✅ | Untested | Untested | ✅ | Untested | Untested | Untested | Untested | -| Custom AOD statusbar layout | ✅ | Untested | Untested | ⚠️ Experimental | Untested | Untested | Untested | Untested | -| Notification icons mode | ✅ | Untested | Untested | ✅ AOD experimental | Untested | Untested | Untested | Untested | -| Notification cards mode | ✅ | Untested | Untested | ❌ | Untested | Untested | Untested | Untested | -| Notification dot mode and read-state behavior | ✅ | Untested | Untested | ❌ | Untested | Untested | Untested | Untested | -| Hide notification icons by app | ✅ | Untested | Untested | ✅ AOD experimental | Untested | Untested | Untested | Untested | -| Hide status icons | ✅ | Untested | Untested | ✅ AOD experimental | Untested | Untested | Untested | Untested | -| Dual-SIM status icon handling | ✅ | Untested | Untested | ⚠️ Experimental | Untested | Untested | Untested | Untested | -| Samsung status chips: media/navigation/call | ✅ | Untested | Untested | ❌ | Untested | Untested | Untested | Untested | -| Samsung charging chip handling | ✅ | Untested | Untested | ❌ | Untested | Untested | Untested | Untested | -| Privacy indicator stability | ✅ | Untested | Untested | ✅ | Untested | Untested | Untested | Untested | -| Custom statusbar clock text | ✅ | Untested | Untested | ✅ | Untested | Untested | Untested | Untested | -| Custom drawer clock/date text | ✅ | Untested | Untested | ✅ | Untested | Untested | Untested | Untested | -| Stored clock pattern previews | ✅ | Untested | Untested | ✅ | Untested | Untested | Untested | Untested | -| Battery bar: base statusbar modes | ✅ | Untested | Untested | ✅ | Untested | Untested | Untested | Untested | -| Battery bar: fullscreen and transparent statusbar modes | ✅ | Untested | Untested | ✅ | Untested | Untested | Untested | Untested | -| Battery bar: curved top-edge geometry | ✅ | Untested | Untested | ⚠️ Experimental | Untested | Untested | Untested | Untested | -| Battery bar threshold colours and colour math | ✅ | Untested | Untested | ✅ | Untested | Untested | Untested | Untested | -| Camera cutout and under-display-camera handling | ✅ | Untested | Untested | ⚠️ Experimental | Untested | Untested | Untested | Untested | -| Rotation and fold/unfold handling | ✅ | Untested | Untested | ✅ rotation verified | Untested | Untested | Untested | Untested | -| Drawer scrolling and background-work performance | ✅ | Untested | Untested | ✅ | Untested | Untested | Untested | Untested | -| Debug visualizers and debug notifications | ✅ | Untested | Untested | ✅ | Untested | Untested | Untested | Untested | +| Module loads in SystemUI | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| Settings app opens and writes settings | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| Custom unlocked statusbar layout | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| Custom lockscreen statusbar layout | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| Custom AOD statusbar layout | ✅ | ⚠️ Experimental | ⚠️ Experimental | ⚠️ Experimental | ⚠️ Experimental | ⚠️ Experimental | ⚠️ Experimental | ⚠️ Experimental | +| Notification icons mode | ✅ | ✅ AOD experimental | ✅ AOD experimental | ✅ AOD experimental | ✅ AOD experimental | ✅ AOD experimental | ✅ AOD experimental | ✅ AOD experimental | +| Notification cards mode | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | +| Notification dot mode and read-state behavior | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | +| Hide notification icons by app | ✅ | ✅ AOD experimental | ✅ AOD experimental | ✅ AOD experimental | ✅ AOD experimental | ✅ AOD experimental | ✅ AOD experimental | ✅ AOD experimental | +| Hide status icons | ✅ | ✅ AOD experimental | ✅ AOD experimental | ✅ AOD experimental | ✅ AOD experimental | ✅ AOD experimental | ✅ AOD experimental | ✅ AOD experimental | +| Dual-SIM status icon handling | ✅ | ⚠️ Experimental | ⚠️ Experimental | ⚠️ Experimental | ⚠️ Experimental | ⚠️ Experimental | ⚠️ Experimental | ⚠️ Experimental | +| Samsung status chips: media/navigation/call | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | +| Samsung charging chip handling | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | +| Privacy indicator stability | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| Custom statusbar clock text | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| Custom drawer clock/date text | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| Stored clock pattern previews | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| Battery bar: base statusbar modes | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| Battery bar: fullscreen and transparent statusbar modes | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| Battery bar: curved top-edge geometry | ✅ | ⚠️ Experimental | ⚠️ Experimental | ⚠️ Experimental | ⚠️ Experimental | ⚠️ Experimental | ⚠️ Experimental | ⚠️ Experimental | +| Battery bar threshold colours and colour math | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| Camera cutout and under-display-camera handling | ✅ | ⚠️ Experimental | ⚠️ Experimental | ⚠️ Experimental | ⚠️ Experimental | ⚠️ Experimental | ⚠️ Experimental | ⚠️ Experimental | +| Rotation and fold/unfold handling | ✅ | ✅ rotation verified | ✅ rotation verified | ✅ rotation verified | ✅ rotation verified | ✅ rotation verified | ✅ rotation verified | ✅ rotation verified | +| Drawer scrolling and background-work performance | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| Debug visualizers and debug notifications | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | -Current emulator targets use rooted LSPosed snapshots in a local test lab. They -are useful for compatibility work, but the matrix above is intentionally not -optimistic: until a target has been tested, it remains `Untested`. +Current emulator targets use rooted LSPosed snapshots. They are useful for +compatibility work, but hardware-specific entries remain marked as experimental +when the emulator cannot expose the required state. ## Build diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 61ce1a5..dd21bc7 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -69,7 +69,7 @@ android { defaultConfig { applicationId = "se.ajpanton.statusbartweak" - minSdk = 35 + minSdk = 34 targetSdk = 36 val buildVersionName = project.findProperty("sbtVersionName")?.toString() ?: fallbackVersionName diff --git a/app/src/main/java/se/ajpanton/statusbartweak/runtime/hooks/XposedHookSupport.java b/app/src/main/java/se/ajpanton/statusbartweak/runtime/hooks/XposedHookSupport.java index e1cc039..daee6c4 100644 --- a/app/src/main/java/se/ajpanton/statusbartweak/runtime/hooks/XposedHookSupport.java +++ b/app/src/main/java/se/ajpanton/statusbartweak/runtime/hooks/XposedHookSupport.java @@ -2,6 +2,7 @@ package se.ajpanton.statusbartweak.runtime.hooks; import java.lang.reflect.Constructor; import java.lang.reflect.Method; +import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.List; import java.util.Objects; @@ -34,6 +35,9 @@ public final class XposedHookSupport { if (!methodName.equals(method.getName())) { continue; } + if (Modifier.isAbstract(method.getModifiers())) { + continue; + } method.setAccessible(true); handles.add(framework.hook(method).intercept(hooker)); } @@ -59,6 +63,9 @@ public final class XposedHookSupport { if (!methodName.equals(method.getName())) { continue; } + if (Modifier.isAbstract(method.getModifiers())) { + continue; + } method.setAccessible(true); handles.add(framework.hook(method).intercept(hooker)); } @@ -83,4 +90,20 @@ public final class XposedHookSupport { } return handles; } + + public static List hookAllConstructorsIfExists( + XposedInterface framework, + Class type, + XposedInterface.Hooker hooker + ) { + Objects.requireNonNull(framework, "framework"); + Objects.requireNonNull(type, "type"); + Objects.requireNonNull(hooker, "hooker"); + List handles = new ArrayList<>(); + for (Constructor constructor : type.getDeclaredConstructors()) { + constructor.setAccessible(true); + handles.add(framework.hook(constructor).intercept(hooker)); + } + return handles; + } } diff --git a/app/src/main/java/se/ajpanton/statusbartweak/runtime/layout/DarkIconDispatcherGuard.java b/app/src/main/java/se/ajpanton/statusbartweak/runtime/layout/DarkIconDispatcherGuard.java index 4652e40..dba4c52 100644 --- a/app/src/main/java/se/ajpanton/statusbartweak/runtime/layout/DarkIconDispatcherGuard.java +++ b/app/src/main/java/se/ajpanton/statusbartweak/runtime/layout/DarkIconDispatcherGuard.java @@ -33,7 +33,7 @@ final class DarkIconDispatcherGuard { } void install(Class darkIconDispatcherClass) { - XposedHookSupport.hookAllConstructors( + XposedHookSupport.hookAllConstructorsIfExists( runtimeContext.getFramework(), darkIconDispatcherClass, chain -> { diff --git a/test-harness/src/main/AndroidManifest.xml b/test-harness/src/main/AndroidManifest.xml index d29f03e..6ef01f2 100644 --- a/test-harness/src/main/AndroidManifest.xml +++ b/test-harness/src/main/AndroidManifest.xml @@ -27,7 +27,8 @@ + android:exported="true" + android:launchMode="singleTop" /> diff --git a/test-harness/src/main/java/se/ajpanton/statusbartweak/harness/ScenarioActivity.java b/test-harness/src/main/java/se/ajpanton/statusbartweak/harness/ScenarioActivity.java index 79b8399..26cc2b7 100644 --- a/test-harness/src/main/java/se/ajpanton/statusbartweak/harness/ScenarioActivity.java +++ b/test-harness/src/main/java/se/ajpanton/statusbartweak/harness/ScenarioActivity.java @@ -2,6 +2,7 @@ package se.ajpanton.statusbartweak.harness; import android.Manifest; import android.app.Activity; +import android.content.Intent; import android.content.pm.ActivityInfo; import android.content.pm.PackageManager; import android.graphics.Color; @@ -51,7 +52,20 @@ public final class ScenarioActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - String scenario = getIntent().getStringExtra(EXTRA_SCENARIO); + applyScenario(getIntent()); + } + + @Override + protected void onNewIntent(Intent intent) { + super.onNewIntent(intent); + setIntent(intent); + stopCamera(); + stopMicrophone(); + applyScenario(intent); + } + + private void applyScenario(Intent intent) { + String scenario = intent != null ? intent.getStringExtra(EXTRA_SCENARIO) : null; if (scenario == null) { scenario = SCENARIO_OPAQUE_LIGHT; }