Expand emulator compatibility coverage

This commit is contained in:
ajp_anton
2026-07-08 14:59:34 +00:00
parent 18f30fce57
commit 3ebebb5218
6 changed files with 85 additions and 43 deletions
+43 -39
View File
@@ -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<br>One UI 8 / Android 16 | AOSP API 34<br>Android 14 | AOSP API 35<br>Android 15 | AOSP API 36<br>Android 16 | Google APIs API 35<br>Android 15 | Google APIs API 36<br>Android 16 | LineageOS 22<br>Android 15 | LineageOS 23<br>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
+1 -1
View File
@@ -69,7 +69,7 @@ android {
defaultConfig {
applicationId = "se.ajpanton.statusbartweak"
minSdk = 35
minSdk = 34
targetSdk = 36
val buildVersionName = project.findProperty("sbtVersionName")?.toString()
?: fallbackVersionName
@@ -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<XposedInterface.HookHandle> hookAllConstructorsIfExists(
XposedInterface framework,
Class<?> type,
XposedInterface.Hooker hooker
) {
Objects.requireNonNull(framework, "framework");
Objects.requireNonNull(type, "type");
Objects.requireNonNull(hooker, "hooker");
List<XposedInterface.HookHandle> handles = new ArrayList<>();
for (Constructor<?> constructor : type.getDeclaredConstructors()) {
constructor.setAccessible(true);
handles.add(framework.hook(constructor).intercept(hooker));
}
return handles;
}
}
@@ -33,7 +33,7 @@ final class DarkIconDispatcherGuard {
}
void install(Class<?> darkIconDispatcherClass) {
XposedHookSupport.hookAllConstructors(
XposedHookSupport.hookAllConstructorsIfExists(
runtimeContext.getFramework(),
darkIconDispatcherClass,
chain -> {
+2 -1
View File
@@ -27,7 +27,8 @@
<activity
android:name=".ScenarioActivity"
android:configChanges="keyboardHidden|orientation|screenSize"
android:exported="false" />
android:exported="true"
android:launchMode="singleTop" />
<activity
android:name=".MainActivity"
android:exported="true">
@@ -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;
}