Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
75861e2480 | ||
|
|
9dfca417bd | ||
|
|
18f1c6f20f | ||
|
|
2fe90ce3fc | ||
|
|
2249212ae3 | ||
|
|
d1160a2d2d | ||
|
|
28f08eb2b2 | ||
|
|
dbdc8e2af8 | ||
|
|
ea3d8221b5 | ||
|
|
1fa5fd24b1 | ||
|
|
8e7e825516 | ||
|
|
de1c03b20f | ||
|
|
ee309161fd | ||
|
|
9c7707830c | ||
|
|
dd215cde82 | ||
|
|
b0270b8ceb | ||
|
|
3bfcb14079 | ||
|
|
c549560ab7 | ||
|
|
67b82ebd4e | ||
|
|
f36e42c649 | ||
|
|
e1dfe637f8 | ||
|
|
7144b130f7 | ||
|
|
5395635a62 | ||
|
|
8d3aa801d1 | ||
|
|
978346624e | ||
|
|
28cc72b7ea | ||
|
|
f16e1a8ad5 |
@@ -0,0 +1,27 @@
|
|||||||
|
package se.ajpanton.statusbartweak.runtime;
|
||||||
|
|
||||||
|
public final class RuntimeMutationGuard {
|
||||||
|
private static final ThreadLocal<Integer> SNAPSHOT_RENDER_DEPTH =
|
||||||
|
ThreadLocal.withInitial(() -> 0);
|
||||||
|
|
||||||
|
private RuntimeMutationGuard() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void enterSnapshotRenderMutation() {
|
||||||
|
SNAPSHOT_RENDER_DEPTH.set(SNAPSHOT_RENDER_DEPTH.get() + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void exitSnapshotRenderMutation() {
|
||||||
|
int depth = SNAPSHOT_RENDER_DEPTH.get() - 1;
|
||||||
|
if (depth <= 0) {
|
||||||
|
SNAPSHOT_RENDER_DEPTH.remove();
|
||||||
|
} else {
|
||||||
|
SNAPSHOT_RENDER_DEPTH.set(depth);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isSnapshotRenderMutation() {
|
||||||
|
Integer depth = SNAPSHOT_RENDER_DEPTH.get();
|
||||||
|
return depth != null && depth > 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
+239
-87
@@ -1,5 +1,6 @@
|
|||||||
package se.ajpanton.statusbartweak.runtime.features.battery;
|
package se.ajpanton.statusbartweak.runtime.features.battery;
|
||||||
|
|
||||||
|
import android.app.KeyguardManager;
|
||||||
import android.content.BroadcastReceiver;
|
import android.content.BroadcastReceiver;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
@@ -11,8 +12,11 @@ import android.graphics.Path;
|
|||||||
import android.graphics.PathMeasure;
|
import android.graphics.PathMeasure;
|
||||||
import android.graphics.PixelFormat;
|
import android.graphics.PixelFormat;
|
||||||
import android.graphics.Rect;
|
import android.graphics.Rect;
|
||||||
|
import android.graphics.RectF;
|
||||||
import android.os.BatteryManager;
|
import android.os.BatteryManager;
|
||||||
|
import android.util.DisplayMetrics;
|
||||||
import android.view.Gravity;
|
import android.view.Gravity;
|
||||||
|
import android.view.RoundedCorner;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.view.ViewParent;
|
import android.view.ViewParent;
|
||||||
@@ -154,8 +158,17 @@ final class BatteryBarController {
|
|||||||
return result;
|
return result;
|
||||||
});
|
});
|
||||||
XposedHookSupport.hookAllMethodsIfExists(framework, View.class, "setAlpha", chain -> {
|
XposedHookSupport.hookAllMethodsIfExists(framework, View.class, "setAlpha", chain -> {
|
||||||
Object result = chain.proceed();
|
|
||||||
Object target = chain.getThisObject();
|
Object target = chain.getThisObject();
|
||||||
|
Object alphaArg = chain.getArgs() != null && !chain.getArgs().isEmpty()
|
||||||
|
? chain.getArgs().get(0)
|
||||||
|
: null;
|
||||||
|
if (target instanceof View root
|
||||||
|
&& alphaArg instanceof Float alpha
|
||||||
|
&& roots.contains(root)
|
||||||
|
&& shouldBlockChargingChipRootAlpha(root, alpha)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
Object result = chain.proceed();
|
||||||
if (target instanceof View root && roots.contains(root)) {
|
if (target instanceof View root && roots.contains(root)) {
|
||||||
applyBatteryBarAfterRootTransform(root);
|
applyBatteryBarAfterRootTransform(root);
|
||||||
}
|
}
|
||||||
@@ -271,6 +284,7 @@ final class BatteryBarController {
|
|||||||
}
|
}
|
||||||
int result = 17;
|
int result = 17;
|
||||||
result = 31 * result + root.getVisibility();
|
result = 31 * result + root.getVisibility();
|
||||||
|
result = 31 * result + Float.floatToIntBits(root.getAlpha());
|
||||||
result = 31 * result + Float.floatToIntBits(root.getY());
|
result = 31 * result + Float.floatToIntBits(root.getY());
|
||||||
result = 31 * result + Float.floatToIntBits(root.getTranslationY());
|
result = 31 * result + Float.floatToIntBits(root.getTranslationY());
|
||||||
result = 31 * result + root.getWidth();
|
result = 31 * result + root.getWidth();
|
||||||
@@ -373,21 +387,26 @@ final class BatteryBarController {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
SbtSettings settings = RuntimeSettingsCache.get(root.getContext());
|
SbtSettings settings = RuntimeSettingsCache.get(root.getContext());
|
||||||
if (!settings.batteryBarEnabled || root.getWidth() <= 0 || root.getHeight() <= 0) {
|
if (root.getWidth() <= 0 || root.getHeight() <= 0) {
|
||||||
|
removeBatteryBarView(root);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
BatteryBarGeometry.Scenario scenario = scenarioForRoot(root);
|
||||||
|
boolean transientStatusBarReveal = isFullscreenScenario(scenario)
|
||||||
|
&& !isLockedPhoneStatusBarRoot(root)
|
||||||
|
&& shouldRenderVisibleFullscreenBar(root);
|
||||||
|
if (transientStatusBarReveal) {
|
||||||
|
scenario = visibleStatusBarScenarioForRoot(root);
|
||||||
|
}
|
||||||
|
if (!isBatteryBarEnabled(settings, scenario)) {
|
||||||
removeBatteryBarView(root);
|
removeBatteryBarView(root);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
int color = resolveBarColor(root, settings);
|
int color = resolveBarColor(root, settings);
|
||||||
BatteryBarGeometry.Scenario scenario = scenarioForRoot(root);
|
|
||||||
if (!shouldRenderOnRoot(root)) {
|
if (!shouldRenderOnRoot(root)) {
|
||||||
removeBatteryBarView(root);
|
removeBatteryBarView(root);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
boolean transientStatusBarReveal = isFullscreenScenario(scenario)
|
|
||||||
&& shouldRenderVisibleFullscreenBar(root);
|
|
||||||
if (transientStatusBarReveal) {
|
|
||||||
scenario = visibleStatusBarScenarioForRoot(root);
|
|
||||||
}
|
|
||||||
BatteryBarGeometry geometry = BatteryBarGeometry.resolve(settings, scenario);
|
BatteryBarGeometry geometry = BatteryBarGeometry.resolve(settings, scenario);
|
||||||
if (isFullscreenScenario(scenario)) {
|
if (isFullscreenScenario(scenario)) {
|
||||||
removeEmbeddedBatteryBarView(root);
|
removeEmbeddedBatteryBarView(root);
|
||||||
@@ -415,7 +434,7 @@ final class BatteryBarController {
|
|||||||
) {
|
) {
|
||||||
BatteryBarView barView = ensureBatteryBarView(rootGroup);
|
BatteryBarView barView = ensureBatteryBarView(rootGroup);
|
||||||
layoutBatteryBarView(rootGroup, barView);
|
layoutBatteryBarView(rootGroup, barView);
|
||||||
barView.update(rootGroup, settings, geometry, batteryLevel, plugged, color);
|
barView.update(rootGroup, rootGroup, settings, geometry, batteryLevel, plugged, color);
|
||||||
barView.bringToFront();
|
barView.bringToFront();
|
||||||
barView.invalidate();
|
barView.invalidate();
|
||||||
lastSignatures.put(rootGroup, buildSignature(rootGroup, settings, color, geometry, scenario));
|
lastSignatures.put(rootGroup, buildSignature(rootGroup, settings, color, geometry, scenario));
|
||||||
@@ -442,7 +461,7 @@ final class BatteryBarController {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
pendingOverlayRetries.remove(root);
|
pendingOverlayRetries.remove(root);
|
||||||
overlay.view.update(overlay.host, settings, geometry, batteryLevel, plugged, color);
|
overlay.view.update(overlay.host, root, settings, geometry, batteryLevel, plugged, color);
|
||||||
if (!signature.equals(lastOverlaySignatures.get(root))) {
|
if (!signature.equals(lastOverlaySignatures.get(root))) {
|
||||||
overlay.updateLayout(bounds.width, bounds.height);
|
overlay.updateLayout(bounds.width, bounds.height);
|
||||||
lastOverlaySignatures.put(root, signature);
|
lastOverlaySignatures.put(root, signature);
|
||||||
@@ -529,6 +548,7 @@ final class BatteryBarController {
|
|||||||
+ plugged + "|"
|
+ plugged + "|"
|
||||||
+ color + "|"
|
+ color + "|"
|
||||||
+ settings.batteryBarEnabled + "|"
|
+ settings.batteryBarEnabled + "|"
|
||||||
|
+ settings.batteryBarScenarioEnabled + "|"
|
||||||
+ settings.batteryBarPosition + "|"
|
+ settings.batteryBarPosition + "|"
|
||||||
+ settings.batteryBarAlignment + "|"
|
+ settings.batteryBarAlignment + "|"
|
||||||
+ settings.batteryBarThicknessDp + "|"
|
+ settings.batteryBarThicknessDp + "|"
|
||||||
@@ -547,7 +567,7 @@ final class BatteryBarController {
|
|||||||
return BatteryBarGeometry.Scenario.LOCKSCREEN;
|
return BatteryBarGeometry.Scenario.LOCKSCREEN;
|
||||||
}
|
}
|
||||||
boolean fullscreen = isPhoneRoot(root)
|
boolean fullscreen = isPhoneRoot(root)
|
||||||
&& isUnlockedScene()
|
&& (isUnlockedScene() || isLockedPhoneFullscreenRoot(root))
|
||||||
&& (isStatusBarSurfaceHidden(root) || isFullscreenSystemBarState());
|
&& (isStatusBarSurfaceHidden(root) || isFullscreenSystemBarState());
|
||||||
boolean landscape = isLandscapeForRoot(root, fullscreen);
|
boolean landscape = isLandscapeForRoot(root, fullscreen);
|
||||||
if (fullscreen) {
|
if (fullscreen) {
|
||||||
@@ -565,6 +585,21 @@ final class BatteryBarController {
|
|||||||
: BatteryBarGeometry.Scenario.UNLOCKED_PORTRAIT;
|
: BatteryBarGeometry.Scenario.UNLOCKED_PORTRAIT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean isBatteryBarEnabled(
|
||||||
|
SbtSettings settings,
|
||||||
|
BatteryBarGeometry.Scenario scenario
|
||||||
|
) {
|
||||||
|
if (settings == null || scenario == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (scenario == BatteryBarGeometry.Scenario.UNLOCKED_PORTRAIT
|
||||||
|
|| scenario == BatteryBarGeometry.Scenario.UNLOCKED_LANDSCAPE) {
|
||||||
|
return settings.batteryBarEnabled;
|
||||||
|
}
|
||||||
|
Boolean scenarioEnabled = settings.batteryBarScenarioEnabled.get(scenario.id);
|
||||||
|
return scenarioEnabled == null || scenarioEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
private BatteryBarGeometry.Scenario visibleStatusBarScenarioForRoot(View root) {
|
private BatteryBarGeometry.Scenario visibleStatusBarScenarioForRoot(View root) {
|
||||||
boolean landscape = isLandscapeForRoot(root, isFullscreenSystemBarState());
|
boolean landscape = isLandscapeForRoot(root, isFullscreenSystemBarState());
|
||||||
if (isPhoneRoot(root) && isUnlockedScene() && isTransparentStatusBarActive()) {
|
if (isPhoneRoot(root) && isUnlockedScene() && isTransparentStatusBarActive()) {
|
||||||
@@ -588,11 +623,22 @@ final class BatteryBarController {
|
|||||||
return scene == null || !scene.isUnlocked();
|
return scene == null || !scene.isUnlocked();
|
||||||
}
|
}
|
||||||
if (isPhoneRoot(root)) {
|
if (isPhoneRoot(root)) {
|
||||||
return scene == null || scene.isUnlocked();
|
if (isLockedPhoneStatusBarRoot(root)) {
|
||||||
|
return isLockedPhoneFullscreenRoot(root);
|
||||||
|
}
|
||||||
|
return scene == null || scene.isUnlocked() || isLockedPhoneFullscreenRoot(root);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean shouldBlockChargingChipRootAlpha(View root, float alpha) {
|
||||||
|
return alpha < 0.99f
|
||||||
|
&& isPhoneRoot(root)
|
||||||
|
&& isUnlockedScene()
|
||||||
|
&& !isFullscreenSystemBarState()
|
||||||
|
&& hasSamsungBatteryStatusChip(root);
|
||||||
|
}
|
||||||
|
|
||||||
private boolean isUnlockedScene() {
|
private boolean isUnlockedScene() {
|
||||||
SceneKey scene = runtimeContext.getModeStateRepository().getActiveScene();
|
SceneKey scene = runtimeContext.getModeStateRepository().getActiveScene();
|
||||||
return scene == null || scene.isUnlocked();
|
return scene == null || scene.isUnlocked();
|
||||||
@@ -606,6 +652,23 @@ final class BatteryBarController {
|
|||||||
return root != null && root.getClass().getName().contains("KeyguardStatusBarView");
|
return root != null && root.getClass().getName().contains("KeyguardStatusBarView");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean isLockedPhoneStatusBarRoot(View root) {
|
||||||
|
return isPhoneRoot(root) && isKeyguardLocked(root.getContext());
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isLockedPhoneFullscreenRoot(View root) {
|
||||||
|
return isLockedPhoneStatusBarRoot(root)
|
||||||
|
&& (isStatusBarSurfaceHidden(root) || isFullscreenSystemBarState());
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isKeyguardLocked(Context context) {
|
||||||
|
if (context == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
KeyguardManager keyguardManager = context.getSystemService(KeyguardManager.class);
|
||||||
|
return keyguardManager != null && keyguardManager.isKeyguardLocked();
|
||||||
|
}
|
||||||
|
|
||||||
private boolean isHiddenKeyguardRoot(View root) {
|
private boolean isHiddenKeyguardRoot(View root) {
|
||||||
return isKeyguardRoot(root) && !root.isShown();
|
return isKeyguardRoot(root) && !root.isShown();
|
||||||
}
|
}
|
||||||
@@ -758,6 +821,32 @@ final class BatteryBarController {
|
|||||||
return bottom <= 0f || top >= screenHeight || right <= 0f || left >= screenWidth;
|
return bottom <= 0f || top >= screenHeight || right <= 0f || left >= screenWidth;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean hasSamsungBatteryStatusChip(View root) {
|
||||||
|
if (!(root != null && root.getRootView() instanceof ViewGroup rootView)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return findSamsungBatteryStatusChip(rootView) != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private View findSamsungBatteryStatusChip(View view) {
|
||||||
|
if (view == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (view.getClass().getName().contains("SamsungBatteryStatusChip")) {
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
if (!(view instanceof ViewGroup group)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < group.getChildCount(); i++) {
|
||||||
|
View found = findSamsungBatteryStatusChip(group.getChildAt(i));
|
||||||
|
if (found != null) {
|
||||||
|
return found;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
private static Integer asInteger(Object value) {
|
private static Integer asInteger(Object value) {
|
||||||
return value instanceof Integer integer ? integer : null;
|
return value instanceof Integer integer ? integer : null;
|
||||||
}
|
}
|
||||||
@@ -922,6 +1011,7 @@ final class BatteryBarController {
|
|||||||
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
|
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
|
||||||
| WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
|
| WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
|
||||||
| WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
|
| WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
|
||||||
|
| WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
|
||||||
| WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
|
| WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
|
||||||
| WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS,
|
| WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS,
|
||||||
PixelFormat.TRANSLUCENT);
|
PixelFormat.TRANSLUCENT);
|
||||||
@@ -948,8 +1038,8 @@ final class BatteryBarController {
|
|||||||
private final Path fullCurvePath = new Path();
|
private final Path fullCurvePath = new Path();
|
||||||
private final Path drawPath = new Path();
|
private final Path drawPath = new Path();
|
||||||
private final PathMeasure pathMeasure = new PathMeasure();
|
private final PathMeasure pathMeasure = new PathMeasure();
|
||||||
private final float[] pathPoint = new float[2];
|
|
||||||
private ViewGroup host;
|
private ViewGroup host;
|
||||||
|
private View roundedCornerSource;
|
||||||
private SbtSettings settings;
|
private SbtSettings settings;
|
||||||
private BatteryBarGeometry geometry;
|
private BatteryBarGeometry geometry;
|
||||||
private int batteryLevel = -1;
|
private int batteryLevel = -1;
|
||||||
@@ -964,6 +1054,7 @@ final class BatteryBarController {
|
|||||||
|
|
||||||
void update(
|
void update(
|
||||||
ViewGroup host,
|
ViewGroup host,
|
||||||
|
View roundedCornerSource,
|
||||||
SbtSettings settings,
|
SbtSettings settings,
|
||||||
BatteryBarGeometry geometry,
|
BatteryBarGeometry geometry,
|
||||||
int batteryLevel,
|
int batteryLevel,
|
||||||
@@ -971,6 +1062,7 @@ final class BatteryBarController {
|
|||||||
int color
|
int color
|
||||||
) {
|
) {
|
||||||
this.host = host;
|
this.host = host;
|
||||||
|
this.roundedCornerSource = roundedCornerSource;
|
||||||
this.settings = settings;
|
this.settings = settings;
|
||||||
this.geometry = geometry;
|
this.geometry = geometry;
|
||||||
this.batteryLevel = batteryLevel;
|
this.batteryLevel = batteryLevel;
|
||||||
@@ -996,7 +1088,18 @@ final class BatteryBarController {
|
|||||||
? geometry
|
? geometry
|
||||||
: BatteryBarGeometry.global(settings);
|
: BatteryBarGeometry.global(settings);
|
||||||
int thickness = Math.max(1, dpToPx(getContext(), activeGeometry.thicknessDp));
|
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(), activeGeometry.edgeOffsetDp));
|
||||||
|
float fraction = resolveFraction(batteryLevel, settings);
|
||||||
|
if (shouldDrawCurvedTop(activeGeometry)) {
|
||||||
|
buildCurvedPath(
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
thickness,
|
||||||
|
edgeOffset,
|
||||||
|
fraction,
|
||||||
|
activeGeometry.alignment);
|
||||||
|
return;
|
||||||
|
}
|
||||||
int left = edgeOffset;
|
int left = edgeOffset;
|
||||||
int right = width - edgeOffset;
|
int right = width - edgeOffset;
|
||||||
if (right <= left) {
|
if (right <= left) {
|
||||||
@@ -1007,11 +1110,6 @@ final class BatteryBarController {
|
|||||||
if (bottom <= top) {
|
if (bottom <= top) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
float fraction = resolveFraction(batteryLevel, settings);
|
|
||||||
if (shouldDrawCurvedTop(activeGeometry)) {
|
|
||||||
buildCurvedPath(left, right, height, thickness, fraction, activeGeometry.alignment);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
int availableWidth = right - left;
|
int availableWidth = right - left;
|
||||||
int barWidth = Math.round(availableWidth * fraction);
|
int barWidth = Math.round(availableWidth * fraction);
|
||||||
if (barWidth <= 0) {
|
if (barWidth <= 0) {
|
||||||
@@ -1033,53 +1131,47 @@ final class BatteryBarController {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return !SbtSettings.BATTERY_BAR_CURVED_GEOMETRY_OFF.equals(
|
return !SbtSettings.BATTERY_BAR_CURVED_GEOMETRY_OFF.equals(
|
||||||
settings.batteryBarCurvedGeometryMode);
|
geometry.curvedGeometryMode);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void buildCurvedPath(
|
private void buildCurvedPath(
|
||||||
int left,
|
int width,
|
||||||
int right,
|
|
||||||
int height,
|
int height,
|
||||||
int thickness,
|
int thickness,
|
||||||
|
int edgeOffset,
|
||||||
float fraction,
|
float fraction,
|
||||||
String alignment
|
String alignment
|
||||||
) {
|
) {
|
||||||
if (fraction <= 0f || right <= left || height <= 0 || thickness <= 0) {
|
if (fraction <= 0f || width <= 0 || height <= 0 || thickness <= 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
float halfStroke = thickness / 2f;
|
buildFullCurvePath(width, height, thickness);
|
||||||
float leftCenter = left + halfStroke;
|
|
||||||
float rightCenter = right - halfStroke;
|
|
||||||
if (rightCenter <= leftCenter) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
buildFullCurvePath(leftCenter, rightCenter, height, thickness);
|
|
||||||
pathMeasure.setPath(fullCurvePath, false);
|
pathMeasure.setPath(fullCurvePath, false);
|
||||||
float fullLength = pathMeasure.getLength();
|
float fullLength = pathMeasure.getLength();
|
||||||
if (fullLength <= 0f) {
|
if (fullLength <= 0f) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
float targetLength = SbtSettings.BATTERY_BAR_CURVED_GEOMETRY_LENGTH.equals(
|
float pathOffset = Math.min(Math.max(0f, edgeOffset), fullLength / 2f);
|
||||||
settings.batteryBarCurvedGeometryMode)
|
float usableStart = pathOffset;
|
||||||
? fullLength * fraction
|
float usableEnd = fullLength - pathOffset;
|
||||||
: targetDistanceForHorizontalFraction(
|
float usableLength = usableEnd - usableStart;
|
||||||
leftCenter,
|
if (usableLength <= 0f) {
|
||||||
rightCenter,
|
return;
|
||||||
fullLength,
|
}
|
||||||
fraction);
|
float targetLength = usableLength * fraction;
|
||||||
if (targetLength <= 0f) {
|
if (targetLength <= 0f) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
targetLength = Math.min(fullLength, targetLength);
|
targetLength = Math.min(usableLength, targetLength);
|
||||||
float start = 0f;
|
float start = usableStart;
|
||||||
float end = targetLength;
|
float end = usableStart + targetLength;
|
||||||
if ("rtl".equals(alignment)) {
|
if ("rtl".equals(alignment)) {
|
||||||
start = fullLength - targetLength;
|
start = usableEnd - targetLength;
|
||||||
end = fullLength;
|
end = usableEnd;
|
||||||
} else if ("center".equals(alignment)) {
|
} else if ("center".equals(alignment)) {
|
||||||
float center = fullLength / 2f;
|
float center = (usableStart + usableEnd) / 2f;
|
||||||
start = Math.max(0f, center - targetLength / 2f);
|
start = Math.max(usableStart, center - targetLength / 2f);
|
||||||
end = Math.min(fullLength, center + targetLength / 2f);
|
end = Math.min(usableEnd, center + targetLength / 2f);
|
||||||
}
|
}
|
||||||
if (end <= start) {
|
if (end <= start) {
|
||||||
return;
|
return;
|
||||||
@@ -1089,64 +1181,124 @@ final class BatteryBarController {
|
|||||||
pathStrokeWidth = thickness;
|
pathStrokeWidth = thickness;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void buildFullCurvePath(float left, float right, int height, int thickness) {
|
private void buildFullCurvePath(int width, int height, int thickness) {
|
||||||
fullCurvePath.reset();
|
fullCurvePath.reset();
|
||||||
float halfStroke = thickness / 2f;
|
float halfStroke = thickness / 2f;
|
||||||
|
if (width <= thickness) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
float topY = halfStroke;
|
float topY = halfStroke;
|
||||||
float bottomY = Math.max(topY, height - halfStroke);
|
float bottomY = Math.max(topY, height - halfStroke);
|
||||||
float verticalSpan = bottomY - topY;
|
float verticalSpan = bottomY - topY;
|
||||||
float radius = Math.min(verticalSpan, (right - left) / 2f);
|
float outerRadius = Math.min(
|
||||||
fullCurvePath.moveTo(left, bottomY);
|
resolveTopCornerRadius(verticalSpan + halfStroke),
|
||||||
if (radius <= 0f) {
|
width / 2f);
|
||||||
fullCurvePath.lineTo(right, topY);
|
float curveRadius = outerRadius - halfStroke;
|
||||||
|
if (curveRadius <= 0f) {
|
||||||
|
buildSharpTopPath(width, topY, bottomY, halfStroke);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
fullCurvePath.lineTo(left, topY + radius);
|
float leftTopX = outerRadius;
|
||||||
fullCurvePath.quadTo(left, topY, left + radius, topY);
|
float rightTopX = width - outerRadius;
|
||||||
fullCurvePath.lineTo(right - radius, topY);
|
if (rightTopX <= leftTopX) {
|
||||||
fullCurvePath.quadTo(right, topY, right, topY + radius);
|
buildSharpTopPath(width, topY, bottomY, halfStroke);
|
||||||
fullCurvePath.lineTo(right, bottomY);
|
return;
|
||||||
|
}
|
||||||
|
RectF leftCorner = new RectF(
|
||||||
|
halfStroke,
|
||||||
|
halfStroke,
|
||||||
|
outerRadius + curveRadius,
|
||||||
|
outerRadius + curveRadius);
|
||||||
|
RectF rightCorner = new RectF(
|
||||||
|
width - outerRadius - curveRadius,
|
||||||
|
halfStroke,
|
||||||
|
width - halfStroke,
|
||||||
|
outerRadius + curveRadius);
|
||||||
|
|
||||||
|
float leftStartAngle = leftCornerStartAngle(bottomY, outerRadius, curveRadius);
|
||||||
|
float leftStartX = cornerX(leftCorner.centerX(), curveRadius, leftStartAngle);
|
||||||
|
float leftStartY = cornerY(leftCorner.centerY(), curveRadius, leftStartAngle);
|
||||||
|
fullCurvePath.moveTo(halfStroke, bottomY);
|
||||||
|
fullCurvePath.lineTo(leftStartX, leftStartY);
|
||||||
|
fullCurvePath.arcTo(leftCorner, leftStartAngle, 270f - leftStartAngle);
|
||||||
|
fullCurvePath.lineTo(rightTopX, topY);
|
||||||
|
|
||||||
|
float rightEndAngle = rightCornerEndAngle(bottomY, outerRadius, curveRadius);
|
||||||
|
fullCurvePath.arcTo(rightCorner, 270f, rightEndAngle - 270f);
|
||||||
|
fullCurvePath.lineTo(width - halfStroke, bottomY);
|
||||||
}
|
}
|
||||||
|
|
||||||
private float targetDistanceForHorizontalFraction(
|
private float resolveTopCornerRadius(float fallback) {
|
||||||
float left,
|
WindowInsets insets = roundedCornerSource != null
|
||||||
float right,
|
? roundedCornerSource.getRootWindowInsets()
|
||||||
float fullLength,
|
: null;
|
||||||
float fraction
|
if (insets == null) {
|
||||||
) {
|
insets = getRootWindowInsets();
|
||||||
if (fraction >= 1f) {
|
|
||||||
return fullLength;
|
|
||||||
}
|
}
|
||||||
float horizontalSpan = right - left;
|
if (insets == null) {
|
||||||
if (horizontalSpan <= 0f) {
|
return fallback;
|
||||||
return fullLength * fraction;
|
}
|
||||||
|
int leftRadius = roundedCornerRadius(insets, RoundedCorner.POSITION_TOP_LEFT);
|
||||||
|
int rightRadius = roundedCornerRadius(insets, RoundedCorner.POSITION_TOP_RIGHT);
|
||||||
|
if (leftRadius > 0 && rightRadius > 0) {
|
||||||
|
return Math.min(leftRadius, rightRadius);
|
||||||
|
}
|
||||||
|
if (leftRadius > 0 || rightRadius > 0) {
|
||||||
|
return Math.max(leftRadius, rightRadius);
|
||||||
}
|
}
|
||||||
float targetHorizontal = horizontalSpan * fraction;
|
|
||||||
if (targetHorizontal <= 0f) {
|
|
||||||
return 0f;
|
return 0f;
|
||||||
}
|
}
|
||||||
int steps = Math.max(32, Math.min(512, Math.round(fullLength / 2f)));
|
|
||||||
float previousDistance = 0f;
|
private int roundedCornerRadius(WindowInsets insets, int position) {
|
||||||
float previousX = left;
|
RoundedCorner corner = insets.getRoundedCorner(position);
|
||||||
float accumulatedHorizontal = 0f;
|
if (corner == null) {
|
||||||
for (int step = 1; step <= steps; step++) {
|
return 0;
|
||||||
float distance = fullLength * step / steps;
|
|
||||||
if (!pathMeasure.getPosTan(distance, pathPoint, null)) {
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
float dx = Math.abs(pathPoint[0] - previousX);
|
return stableDensityRadius(Math.max(0, corner.getRadius()));
|
||||||
if (accumulatedHorizontal + dx >= targetHorizontal) {
|
|
||||||
if (dx <= 0f) {
|
|
||||||
return distance;
|
|
||||||
}
|
}
|
||||||
float ratio = (targetHorizontal - accumulatedHorizontal) / dx;
|
|
||||||
return previousDistance + (distance - previousDistance) * ratio;
|
private int stableDensityRadius(int radius) {
|
||||||
|
if (radius <= 0 || getResources() == null) {
|
||||||
|
return radius;
|
||||||
}
|
}
|
||||||
accumulatedHorizontal += dx;
|
DisplayMetrics metrics = getResources().getDisplayMetrics();
|
||||||
previousDistance = distance;
|
int currentDpi = metrics != null ? metrics.densityDpi : 0;
|
||||||
previousX = pathPoint[0];
|
int stableDpi = DisplayMetrics.DENSITY_DEVICE_STABLE;
|
||||||
|
if (currentDpi <= 0 || stableDpi <= 0 || currentDpi == stableDpi) {
|
||||||
|
return radius;
|
||||||
}
|
}
|
||||||
return fullLength;
|
return Math.max(1, Math.round(radius * (stableDpi / (float) currentDpi)));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void buildSharpTopPath(int width, float topY, float bottomY, float halfStroke) {
|
||||||
|
fullCurvePath.moveTo(halfStroke, bottomY);
|
||||||
|
fullCurvePath.lineTo(halfStroke, topY);
|
||||||
|
fullCurvePath.lineTo(width - halfStroke, topY);
|
||||||
|
fullCurvePath.lineTo(width - halfStroke, bottomY);
|
||||||
|
}
|
||||||
|
|
||||||
|
private float leftCornerStartAngle(float bottomY, float outerRadius, float curveRadius) {
|
||||||
|
if (bottomY >= outerRadius) {
|
||||||
|
return 180f;
|
||||||
|
}
|
||||||
|
float ratio = Math.max(-1f, Math.min(1f, (outerRadius - bottomY) / curveRadius));
|
||||||
|
return 180f + (float) Math.toDegrees(Math.asin(ratio));
|
||||||
|
}
|
||||||
|
|
||||||
|
private float rightCornerEndAngle(float bottomY, float outerRadius, float curveRadius) {
|
||||||
|
if (bottomY >= outerRadius) {
|
||||||
|
return 360f;
|
||||||
|
}
|
||||||
|
float ratio = Math.max(-1f, Math.min(1f, (outerRadius - bottomY) / curveRadius));
|
||||||
|
return 360f - (float) Math.toDegrees(Math.asin(ratio));
|
||||||
|
}
|
||||||
|
|
||||||
|
private float cornerX(float centerX, float radius, float angleDegrees) {
|
||||||
|
return centerX + radius * (float) Math.cos(Math.toRadians(angleDegrees));
|
||||||
|
}
|
||||||
|
|
||||||
|
private float cornerY(float centerY, float radius, float angleDegrees) {
|
||||||
|
return centerY + radius * (float) Math.sin(Math.toRadians(angleDegrees));
|
||||||
}
|
}
|
||||||
|
|
||||||
private int resolveTop(BatteryBarGeometry geometry, int thickness, int height) {
|
private int resolveTop(BatteryBarGeometry geometry, int thickness, int height) {
|
||||||
|
|||||||
+83
-10
@@ -7,40 +7,66 @@ import java.util.Locale;
|
|||||||
|
|
||||||
import se.ajpanton.statusbartweak.shell.settings.BatteryBarStyle;
|
import se.ajpanton.statusbartweak.shell.settings.BatteryBarStyle;
|
||||||
|
|
||||||
final class ClockColorResolver {
|
public final class ClockColorResolver {
|
||||||
private static final int CHANNEL_ALPHA = 0;
|
private static final int CHANNEL_ALPHA = 0;
|
||||||
private static final int CHANNEL_RED = 1;
|
private static final int CHANNEL_RED = 1;
|
||||||
private static final int CHANNEL_GREEN = 2;
|
private static final int CHANNEL_GREEN = 2;
|
||||||
private static final int CHANNEL_BLUE = 3;
|
private static final int CHANNEL_BLUE = 3;
|
||||||
private static final int CHANNEL_COUNT = 4;
|
private static final int CHANNEL_COUNT = 4;
|
||||||
|
|
||||||
String resolveColorHex(
|
public String resolveColorHex(
|
||||||
Context context,
|
Context context,
|
||||||
String expression,
|
String expression,
|
||||||
int referenceColor,
|
int referenceColor,
|
||||||
int fallbackColor
|
int fallbackColor
|
||||||
) {
|
) {
|
||||||
int resolved = resolveColor(context, expression, referenceColor, fallbackColor);
|
int resolved = resolveColor(context, expression, referenceColor, fallbackColor);
|
||||||
|
return colorHex(resolved);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String resolveColorHex(
|
||||||
|
Context context,
|
||||||
|
String expression,
|
||||||
|
int referenceColor,
|
||||||
|
int fallbackColor,
|
||||||
|
Options options
|
||||||
|
) {
|
||||||
|
int resolved = resolveColor(context, expression, referenceColor, fallbackColor, options);
|
||||||
|
return colorHex(resolved);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String colorHex(int resolved) {
|
||||||
if (alpha(resolved) >= 255) {
|
if (alpha(resolved) >= 255) {
|
||||||
return String.format(Locale.ROOT, "#%06X", resolved & 0xFFFFFF);
|
return String.format(Locale.ROOT, "#%06X", resolved & 0xFFFFFF);
|
||||||
}
|
}
|
||||||
return String.format(Locale.ROOT, "#%08X", resolved);
|
return String.format(Locale.ROOT, "#%08X", resolved);
|
||||||
}
|
}
|
||||||
|
|
||||||
int resolveColor(
|
public int resolveColor(
|
||||||
Context context,
|
Context context,
|
||||||
String expression,
|
String expression,
|
||||||
int referenceColor,
|
int referenceColor,
|
||||||
int fallbackColor
|
int fallbackColor
|
||||||
|
) {
|
||||||
|
return resolveColor(context, expression, referenceColor, fallbackColor, Options.clock());
|
||||||
|
}
|
||||||
|
|
||||||
|
public int resolveColor(
|
||||||
|
Context context,
|
||||||
|
String expression,
|
||||||
|
int referenceColor,
|
||||||
|
int fallbackColor,
|
||||||
|
Options options
|
||||||
) {
|
) {
|
||||||
if (isEmpty(expression)) {
|
if (isEmpty(expression)) {
|
||||||
return fallbackColor;
|
return fallbackColor;
|
||||||
}
|
}
|
||||||
|
Options resolvedOptions = options != null ? options : Options.clock();
|
||||||
VariantExpressions variants = splitVariants(expression);
|
VariantExpressions variants = splitVariants(expression);
|
||||||
Value bright = evaluateExpression(context, variants.brightExpression, null, fallbackColor);
|
Value bright = evaluateExpression(context, variants.brightExpression, null, fallbackColor, resolvedOptions);
|
||||||
Value dark = variants.darkExpression.isEmpty()
|
Value dark = variants.darkExpression.isEmpty()
|
||||||
? bright
|
? bright
|
||||||
: evaluateExpression(context, variants.darkExpression, bright, fallbackColor);
|
: evaluateExpression(context, variants.darkExpression, bright, fallbackColor, resolvedOptions);
|
||||||
return toColor(isDark(referenceColor) ? dark : bright);
|
return toColor(isDark(referenceColor) ? dark : bright);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -48,7 +74,8 @@ final class ClockColorResolver {
|
|||||||
Context context,
|
Context context,
|
||||||
String expression,
|
String expression,
|
||||||
Value inherited,
|
Value inherited,
|
||||||
int fallbackColor
|
int fallbackColor,
|
||||||
|
Options options
|
||||||
) {
|
) {
|
||||||
ArrayList<String> tokens = tokenize(expression);
|
ArrayList<String> tokens = tokenize(expression);
|
||||||
ArrayList<Value> stack = new ArrayList<>();
|
ArrayList<Value> stack = new ArrayList<>();
|
||||||
@@ -56,7 +83,7 @@ final class ClockColorResolver {
|
|||||||
stack.add(inherited.copy());
|
stack.add(inherited.copy());
|
||||||
}
|
}
|
||||||
for (String token : tokens) {
|
for (String token : tokens) {
|
||||||
applyToken(context, stack, token, fallbackColor);
|
applyToken(context, stack, token, fallbackColor, options);
|
||||||
}
|
}
|
||||||
if (stack.isEmpty()) {
|
if (stack.isEmpty()) {
|
||||||
throw new IllegalArgumentException("Empty colour expression");
|
throw new IllegalArgumentException("Empty colour expression");
|
||||||
@@ -64,7 +91,13 @@ final class ClockColorResolver {
|
|||||||
return top(stack).copy();
|
return top(stack).copy();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void applyToken(Context context, ArrayList<Value> stack, String token, int fallbackColor) {
|
private void applyToken(
|
||||||
|
Context context,
|
||||||
|
ArrayList<Value> stack,
|
||||||
|
String token,
|
||||||
|
int fallbackColor,
|
||||||
|
Options options
|
||||||
|
) {
|
||||||
if (isEmpty(token)) {
|
if (isEmpty(token)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -73,7 +106,7 @@ final class ClockColorResolver {
|
|||||||
applyOperation(stack, operation);
|
applyOperation(stack, operation);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
stack.add(parseOperand(context, token, fallbackColor));
|
stack.add(parseOperand(context, token, fallbackColor, options));
|
||||||
}
|
}
|
||||||
|
|
||||||
private Operation parseOperation(String token) {
|
private Operation parseOperation(String token) {
|
||||||
@@ -244,12 +277,15 @@ final class ClockColorResolver {
|
|||||||
stack.add(Value.vector(result));
|
stack.add(Value.vector(result));
|
||||||
}
|
}
|
||||||
|
|
||||||
private Value parseOperand(Context context, String token, int fallbackColor) {
|
private Value parseOperand(Context context, String token, int fallbackColor, Options options) {
|
||||||
String value = token != null ? token.trim() : "";
|
String value = token != null ? token.trim() : "";
|
||||||
if (value.isEmpty()) {
|
if (value.isEmpty()) {
|
||||||
throw new IllegalArgumentException("Empty colour token");
|
throw new IllegalArgumentException("Empty colour token");
|
||||||
}
|
}
|
||||||
if ("#batterybar".equalsIgnoreCase(value)) {
|
if ("#batterybar".equalsIgnoreCase(value)) {
|
||||||
|
if (options == null || !options.allowBatteryBarOperand) {
|
||||||
|
throw new IllegalArgumentException("#batterybar is not supported here");
|
||||||
|
}
|
||||||
return Value.vector(vectorForColor(BatteryBarStyle.resolveCurrentColor(context, fallbackColor, fallbackColor)));
|
return Value.vector(vectorForColor(BatteryBarStyle.resolveCurrentColor(context, fallbackColor, fallbackColor)));
|
||||||
}
|
}
|
||||||
if (value.charAt(0) == '#') {
|
if (value.charAt(0) == '#') {
|
||||||
@@ -258,6 +294,9 @@ final class ClockColorResolver {
|
|||||||
if (value.charAt(0) == '[') {
|
if (value.charAt(0) == '[') {
|
||||||
return Value.vector(parseVector(value));
|
return Value.vector(parseVector(value));
|
||||||
}
|
}
|
||||||
|
if (options != null && options.allowBareHexOperand && looksLikeBareHex(value)) {
|
||||||
|
return Value.vector(vectorForColor(parseHexColor("#" + value)));
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
return Value.number(Double.parseDouble(value));
|
return Value.number(Double.parseDouble(value));
|
||||||
} catch (NumberFormatException e) {
|
} catch (NumberFormatException e) {
|
||||||
@@ -265,6 +304,22 @@ final class ClockColorResolver {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean looksLikeBareHex(String value) {
|
||||||
|
int length = value != null ? value.length() : 0;
|
||||||
|
if (length != 3 && length != 4 && length != 6 && length != 8) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
boolean hasHexLetter = false;
|
||||||
|
for (int i = 0; i < length; i++) {
|
||||||
|
char c = value.charAt(i);
|
||||||
|
if (!isHexDigit(c)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
hasHexLetter |= (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F');
|
||||||
|
}
|
||||||
|
return length != 3 || hasHexLetter;
|
||||||
|
}
|
||||||
|
|
||||||
private double[] parseVector(String token) {
|
private double[] parseVector(String token) {
|
||||||
String value = token != null ? token.trim() : "";
|
String value = token != null ? token.trim() : "";
|
||||||
if (!value.startsWith("[") || !value.endsWith("]")) {
|
if (!value.startsWith("[") || !value.endsWith("]")) {
|
||||||
@@ -608,4 +663,22 @@ final class ClockColorResolver {
|
|||||||
return Math.max(0d, Math.min(255d, value));
|
return Math.max(0d, Math.min(255d, value));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static final class Options {
|
||||||
|
private final boolean allowBatteryBarOperand;
|
||||||
|
private final boolean allowBareHexOperand;
|
||||||
|
|
||||||
|
private Options(boolean allowBatteryBarOperand, boolean allowBareHexOperand) {
|
||||||
|
this.allowBatteryBarOperand = allowBatteryBarOperand;
|
||||||
|
this.allowBareHexOperand = allowBareHexOperand;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Options clock() {
|
||||||
|
return new Options(true, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Options batteryBar() {
|
||||||
|
return new Options(false, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+10
-8
@@ -1,7 +1,5 @@
|
|||||||
package se.ajpanton.statusbartweak.runtime.features.clock;
|
package se.ajpanton.statusbartweak.runtime.features.clock;
|
||||||
|
|
||||||
import android.text.TextUtils;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -94,7 +92,7 @@ final class ClockMarkupSupport {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void applyParsedTag(StringBuilder out, TagState state, ParsedTag parsedTag) {
|
static void applyParsedTag(StringBuilder out, TagState state, ParsedTag parsedTag) {
|
||||||
if (TextUtils.isEmpty(parsedTag.name)) {
|
if (isEmpty(parsedTag.name)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (parsedTag.selfClosing) {
|
if (parsedTag.selfClosing) {
|
||||||
@@ -125,7 +123,7 @@ final class ClockMarkupSupport {
|
|||||||
String literal,
|
String literal,
|
||||||
TagCategory category
|
TagCategory category
|
||||||
) {
|
) {
|
||||||
if (TextUtils.isEmpty(name) || TextUtils.isEmpty(literal)) {
|
if (isEmpty(name) || isEmpty(literal)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (category == TagCategory.FONT_COLOR) {
|
if (category == TagCategory.FONT_COLOR) {
|
||||||
@@ -140,7 +138,7 @@ final class ClockMarkupSupport {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static void closeTag(StringBuilder out, TagState state, String name) {
|
private static void closeTag(StringBuilder out, TagState state, String name) {
|
||||||
if (TextUtils.isEmpty(name)) {
|
if (isEmpty(name)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
String normalized = name.toLowerCase(Locale.ROOT);
|
String normalized = name.toLowerCase(Locale.ROOT);
|
||||||
@@ -187,7 +185,7 @@ final class ClockMarkupSupport {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static ParsedTag parseTagLiteral(String literal) {
|
static ParsedTag parseTagLiteral(String literal) {
|
||||||
if (TextUtils.isEmpty(literal)) {
|
if (isEmpty(literal)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
String trimmed = literal.trim();
|
String trimmed = literal.trim();
|
||||||
@@ -232,7 +230,7 @@ final class ClockMarkupSupport {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static List<Integer> findPipePositions(String markup) {
|
static List<Integer> findPipePositions(String markup) {
|
||||||
if (TextUtils.isEmpty(markup)) {
|
if (isEmpty(markup)) {
|
||||||
return Collections.emptyList();
|
return Collections.emptyList();
|
||||||
}
|
}
|
||||||
ArrayList<Integer> positions = new ArrayList<>();
|
ArrayList<Integer> positions = new ArrayList<>();
|
||||||
@@ -262,7 +260,7 @@ final class ClockMarkupSupport {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static boolean containsSecondsPattern(String pattern) {
|
static boolean containsSecondsPattern(String pattern) {
|
||||||
if (TextUtils.isEmpty(pattern)) {
|
if (isEmpty(pattern)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
boolean insideTag = false;
|
boolean insideTag = false;
|
||||||
@@ -300,4 +298,8 @@ final class ClockMarkupSupport {
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static boolean isEmpty(CharSequence value) {
|
||||||
|
return value == null || value.length() == 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+28
-9
@@ -1,7 +1,5 @@
|
|||||||
package se.ajpanton.statusbartweak.runtime.features.clock;
|
package se.ajpanton.statusbartweak.runtime.features.clock;
|
||||||
|
|
||||||
import android.text.TextUtils;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
@@ -42,6 +40,13 @@ final class ClockPatternParser {
|
|||||||
rowMarkup = new StringBuilder();
|
rowMarkup = new StringBuilder();
|
||||||
ClockMarkupSupport.appendOpenTags(rowMarkup, carriedState);
|
ClockMarkupSupport.appendOpenTags(rowMarkup, carriedState);
|
||||||
rowSyntaxError = false;
|
rowSyntaxError = false;
|
||||||
|
if (!isEmpty(lineBreak.trailingShorthand)) {
|
||||||
|
try {
|
||||||
|
appendShorthandBlock(rowMarkup, rowState, lineBreak.trailingShorthand);
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
rowSyntaxError = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
gapBeforePx = lineBreak.gapBeforePx;
|
gapBeforePx = lineBreak.gapBeforePx;
|
||||||
index = lineBreak.endIndex;
|
index = lineBreak.endIndex;
|
||||||
continue;
|
continue;
|
||||||
@@ -111,7 +116,7 @@ final class ClockPatternParser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private ClockParsedPattern.ClockParsedRow buildParsedRow(String markup, int gapBeforePx) {
|
private ClockParsedPattern.ClockParsedRow buildParsedRow(String markup, int gapBeforePx) {
|
||||||
if (TextUtils.isEmpty(markup)) {
|
if (isEmpty(markup)) {
|
||||||
return ClockParsedPattern.ClockParsedRow.plain("", gapBeforePx);
|
return ClockParsedPattern.ClockParsedRow.plain("", gapBeforePx);
|
||||||
}
|
}
|
||||||
List<Integer> pipePositions = ClockMarkupSupport.findPipePositions(markup);
|
List<Integer> pipePositions = ClockMarkupSupport.findPipePositions(markup);
|
||||||
@@ -153,7 +158,7 @@ final class ClockPatternParser {
|
|||||||
}
|
}
|
||||||
ArrayList<String> tokens = tokenizeShorthand(trimmed);
|
ArrayList<String> tokens = tokenizeShorthand(trimmed);
|
||||||
for (String token : tokens) {
|
for (String token : tokens) {
|
||||||
if (TextUtils.isEmpty(token)) {
|
if (isEmpty(token)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if ("/font".equalsIgnoreCase(token)) {
|
if ("/font".equalsIgnoreCase(token)) {
|
||||||
@@ -260,6 +265,10 @@ final class ClockPatternParser {
|
|||||||
&& value.endsWith(")");
|
&& value.endsWith(")");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean isEmpty(CharSequence value) {
|
||||||
|
return value == null || value.length() == 0;
|
||||||
|
}
|
||||||
|
|
||||||
private void openClockColorTag(
|
private void openClockColorTag(
|
||||||
StringBuilder out,
|
StringBuilder out,
|
||||||
ClockMarkupSupport.TagState tagState,
|
ClockMarkupSupport.TagState tagState,
|
||||||
@@ -281,7 +290,7 @@ final class ClockPatternParser {
|
|||||||
|
|
||||||
private LineBreakToken tryParseLineBreak(String pattern, int index) {
|
private LineBreakToken tryParseLineBreak(String pattern, int index) {
|
||||||
if (pattern.startsWith("\\n", index)) {
|
if (pattern.startsWith("\\n", index)) {
|
||||||
return new LineBreakToken(index + 2, false, ROW_GAP_AUTO);
|
return new LineBreakToken(index + 2, false, ROW_GAP_AUTO, "");
|
||||||
}
|
}
|
||||||
if (pattern.charAt(index) != '{') {
|
if (pattern.charAt(index) != '{') {
|
||||||
return null;
|
return null;
|
||||||
@@ -296,10 +305,11 @@ final class ClockPatternParser {
|
|||||||
resetPropagation = true;
|
resetPropagation = true;
|
||||||
body = body.substring(1).trim();
|
body = body.substring(1).trim();
|
||||||
}
|
}
|
||||||
if (!body.startsWith("\\n")) {
|
ArrayList<String> tokens = tokenizeShorthand(body);
|
||||||
|
if (tokens.isEmpty() || !tokens.get(0).startsWith("\\n")) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
String gapText = body.substring(2).trim();
|
String gapText = tokens.get(0).substring(2).trim();
|
||||||
int gapBeforePx = ROW_GAP_AUTO;
|
int gapBeforePx = ROW_GAP_AUTO;
|
||||||
if (!gapText.isEmpty()) {
|
if (!gapText.isEmpty()) {
|
||||||
try {
|
try {
|
||||||
@@ -308,18 +318,27 @@ final class ClockPatternParser {
|
|||||||
throw new IllegalArgumentException("Invalid row gap", e);
|
throw new IllegalArgumentException("Invalid row gap", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return new LineBreakToken(end + 1, resetPropagation, gapBeforePx);
|
StringBuilder trailingShorthand = new StringBuilder();
|
||||||
|
for (int i = 1; i < tokens.size(); i++) {
|
||||||
|
if (trailingShorthand.length() > 0) {
|
||||||
|
trailingShorthand.append(' ');
|
||||||
|
}
|
||||||
|
trailingShorthand.append(tokens.get(i));
|
||||||
|
}
|
||||||
|
return new LineBreakToken(end + 1, resetPropagation, gapBeforePx, trailingShorthand.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final class LineBreakToken {
|
private static final class LineBreakToken {
|
||||||
final int endIndex;
|
final int endIndex;
|
||||||
final boolean resetPropagation;
|
final boolean resetPropagation;
|
||||||
final int gapBeforePx;
|
final int gapBeforePx;
|
||||||
|
final String trailingShorthand;
|
||||||
|
|
||||||
LineBreakToken(int endIndex, boolean resetPropagation, int gapBeforePx) {
|
LineBreakToken(int endIndex, boolean resetPropagation, int gapBeforePx, String trailingShorthand) {
|
||||||
this.endIndex = endIndex;
|
this.endIndex = endIndex;
|
||||||
this.resetPropagation = resetPropagation;
|
this.resetPropagation = resetPropagation;
|
||||||
this.gapBeforePx = gapBeforePx;
|
this.gapBeforePx = gapBeforePx;
|
||||||
|
this.trailingShorthand = trailingShorthand != null ? trailingShorthand : "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+45
@@ -0,0 +1,45 @@
|
|||||||
|
package se.ajpanton.statusbartweak.runtime.features.clock;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.text.SpannableStringBuilder;
|
||||||
|
import android.text.TextUtils;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
public final class ClockPatternPreviewRenderer {
|
||||||
|
public static final int ROW_GAP_AUTO = ClockPatternParser.ROW_GAP_AUTO;
|
||||||
|
private static final ClockPatternParser PATTERN_PARSER = new ClockPatternParser();
|
||||||
|
private static final ClockTextRenderer TEXT_RENDERER = new ClockTextRenderer();
|
||||||
|
|
||||||
|
private ClockPatternPreviewRenderer() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Result render(Context context, String pattern, int referenceColor) {
|
||||||
|
if (context == null || TextUtils.isEmpty(pattern)) {
|
||||||
|
return new Result(new ArrayList<>(), "", false);
|
||||||
|
}
|
||||||
|
ClockParsedPattern parsedPattern = PATTERN_PARSER.parse(pattern);
|
||||||
|
ArrayList<ClockTextRenderer.LayoutRowResult> renderedRows =
|
||||||
|
TEXT_RENDERER.renderLayoutRows(context, parsedPattern, referenceColor);
|
||||||
|
ArrayList<Row> rows = new ArrayList<>();
|
||||||
|
StringBuilder description = new StringBuilder();
|
||||||
|
for (int i = 0; i < renderedRows.size(); i++) {
|
||||||
|
ClockTextRenderer.LayoutRowResult row = renderedRows.get(i);
|
||||||
|
if (i > 0) {
|
||||||
|
description.append('\n');
|
||||||
|
}
|
||||||
|
description.append(row.contentDescription);
|
||||||
|
rows.add(new Row(row.text, row.leftText, row.rightText, row.pipeCount, row.gapBeforePx));
|
||||||
|
}
|
||||||
|
if (rows.isEmpty()) {
|
||||||
|
return new Result(new ArrayList<>(), "", parsedPattern.containsSeconds);
|
||||||
|
}
|
||||||
|
return new Result(rows, description.toString(), parsedPattern.containsSeconds);
|
||||||
|
}
|
||||||
|
|
||||||
|
public record Result(ArrayList<Row> rows, String contentDescription, boolean containsSeconds) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public record Row(CharSequence text, CharSequence leftText, CharSequence rightText, int pipeCount, int gapBeforePx) {
|
||||||
|
}
|
||||||
|
}
|
||||||
+5
@@ -28,6 +28,7 @@ import se.ajpanton.statusbartweak.runtime.mode.SceneKey;
|
|||||||
import se.ajpanton.statusbartweak.runtime.mode.SurfaceMode;
|
import se.ajpanton.statusbartweak.runtime.mode.SurfaceMode;
|
||||||
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.R;
|
||||||
import se.ajpanton.statusbartweak.shell.settings.SbtSettings;
|
import se.ajpanton.statusbartweak.shell.settings.SbtSettings;
|
||||||
|
|
||||||
final class DrawerClockTextController {
|
final class DrawerClockTextController {
|
||||||
@@ -204,6 +205,7 @@ final class DrawerClockTextController {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Role previous = trackedRoles.put(textView, role);
|
Role previous = trackedRoles.put(textView, role);
|
||||||
|
textView.setTag(R.id.sbt_debug_drawer_clock_container, role.name());
|
||||||
if (previous == null || previous != role || !stockSnapshots.containsKey(textView)) {
|
if (previous == null || previous != role || !stockSnapshots.containsKey(textView)) {
|
||||||
captureSnapshot(textView);
|
captureSnapshot(textView);
|
||||||
}
|
}
|
||||||
@@ -214,6 +216,9 @@ final class DrawerClockTextController {
|
|||||||
stockSnapshots.remove(textView);
|
stockSnapshots.remove(textView);
|
||||||
renderCache.remove(textView);
|
renderCache.remove(textView);
|
||||||
ownedTexts.remove(textView);
|
ownedTexts.remove(textView);
|
||||||
|
if (textView != null) {
|
||||||
|
textView.setTag(R.id.sbt_debug_drawer_clock_container, null);
|
||||||
|
}
|
||||||
stopTicker(textView);
|
stopTicker(textView);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+11
-12
@@ -153,7 +153,7 @@ final class StockClockController {
|
|||||||
}
|
}
|
||||||
runtimeContext.getModeStateRepository().addLayoutEnabledListener(enabled -> {
|
runtimeContext.getModeStateRepository().addLayoutEnabledListener(enabled -> {
|
||||||
if (enabled) {
|
if (enabled) {
|
||||||
restoreAllOwnedClocks();
|
suspendOwnedClockTickers();
|
||||||
} else {
|
} else {
|
||||||
refreshAllClocks();
|
refreshAllClocks();
|
||||||
}
|
}
|
||||||
@@ -212,23 +212,22 @@ final class StockClockController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void removeAllRowOverlays() {
|
private void suspendOwnedClockTickers() {
|
||||||
for (TextView clockView : new ArrayList<>(trackedClocks)) {
|
for (TextView clockView : new ArrayList<>(ownedClocks)) {
|
||||||
removeRowOverlay(clockView);
|
stopTicker(clockView);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void restoreAllOwnedClocks() {
|
|
||||||
for (TextView clockView : new ArrayList<>(trackedClocks)) {
|
|
||||||
restoreOwnedClock(clockView);
|
|
||||||
}
|
|
||||||
removeAllRowOverlays();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void applyClockText(TextView clockView) {
|
private void applyClockText(TextView clockView) {
|
||||||
SbtSettings settings = RuntimeSettingsCache.get(clockView.getContext());
|
SbtSettings settings = RuntimeSettingsCache.get(clockView.getContext());
|
||||||
boolean layoutEnabled = settings.clockEnabled;
|
boolean layoutEnabled = settings.clockEnabled;
|
||||||
if (layoutEnabled || !hasCustomClockTextEnabled(settings)) {
|
if (layoutEnabled) {
|
||||||
|
if (ownedClocks.contains(clockView)) {
|
||||||
|
stopTicker(clockView);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!hasCustomClockTextEnabled(settings)) {
|
||||||
restoreOwnedClock(clockView);
|
restoreOwnedClock(clockView);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
+3
@@ -237,6 +237,9 @@ final class StockUnlockedNotificationIconsController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private boolean shouldManageContainer(View view) {
|
private boolean shouldManageContainer(View view) {
|
||||||
|
if (!isFeatureEnabled(view.getContext())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
if (!isUnlockedScene()) {
|
if (!isUnlockedScene()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
+341
-21
@@ -10,8 +10,10 @@ import android.graphics.drawable.Drawable;
|
|||||||
import android.os.Looper;
|
import android.os.Looper;
|
||||||
import android.os.PowerManager;
|
import android.os.PowerManager;
|
||||||
import android.service.notification.StatusBarNotification;
|
import android.service.notification.StatusBarNotification;
|
||||||
|
import android.telecom.TelecomManager;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
import android.view.WindowInsets;
|
||||||
import android.widget.ImageView;
|
import android.widget.ImageView;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
@@ -34,6 +36,7 @@ import java.util.WeakHashMap;
|
|||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
|
||||||
import se.ajpanton.statusbartweak.runtime.NotificationKeyReflection;
|
import se.ajpanton.statusbartweak.runtime.NotificationKeyReflection;
|
||||||
|
import se.ajpanton.statusbartweak.runtime.RuntimeMutationGuard;
|
||||||
import se.ajpanton.statusbartweak.runtime.ViewIdNames;
|
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;
|
||||||
@@ -136,6 +139,8 @@ final class StockLayoutCanvasController {
|
|||||||
Collections.newSetFromMap(new WeakHashMap<>());
|
Collections.newSetFromMap(new WeakHashMap<>());
|
||||||
private final Set<View> trackedLockedStatusBarIconSurfaces =
|
private final Set<View> trackedLockedStatusBarIconSurfaces =
|
||||||
Collections.newSetFromMap(new WeakHashMap<>());
|
Collections.newSetFromMap(new WeakHashMap<>());
|
||||||
|
private final Set<View> lockedStatusBarIconSurfaceReadyRoots =
|
||||||
|
Collections.newSetFromMap(new WeakHashMap<>());
|
||||||
private final Set<View> pendingUnlockedAppearanceRefreshRoots =
|
private final Set<View> pendingUnlockedAppearanceRefreshRoots =
|
||||||
Collections.newSetFromMap(new WeakHashMap<>());
|
Collections.newSetFromMap(new WeakHashMap<>());
|
||||||
private final Set<View> pendingUnlockedClockTextRefreshRoots =
|
private final Set<View> pendingUnlockedClockTextRefreshRoots =
|
||||||
@@ -185,6 +190,7 @@ final class StockLayoutCanvasController {
|
|||||||
private int notificationDrawerCloseGeneration;
|
private int notificationDrawerCloseGeneration;
|
||||||
private boolean notificationDrawerClosePending;
|
private boolean notificationDrawerClosePending;
|
||||||
private int aodVisualAlphaGeneration;
|
private int aodVisualAlphaGeneration;
|
||||||
|
private int systemBarRequestedVisibleTypes = WindowInsets.Type.statusBars();
|
||||||
|
|
||||||
StockLayoutCanvasController(RuntimeContext runtimeContext) {
|
StockLayoutCanvasController(RuntimeContext runtimeContext) {
|
||||||
this.runtimeContext = runtimeContext;
|
this.runtimeContext = runtimeContext;
|
||||||
@@ -213,10 +219,44 @@ final class StockLayoutCanvasController {
|
|||||||
statusIconModelTracker.installHooks(classLoader);
|
statusIconModelTracker.installHooks(classLoader);
|
||||||
installShadeExpansionHooks(classLoader);
|
installShadeExpansionHooks(classLoader);
|
||||||
installShadeHeaderAnimationGuardHook(classLoader);
|
installShadeHeaderAnimationGuardHook(classLoader);
|
||||||
|
installSystemBarAttributeListener(classLoader);
|
||||||
installViewRootHook(classLoader);
|
installViewRootHook(classLoader);
|
||||||
hooksInstalled = true;
|
hooksInstalled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
|
updateSystemBarRequestedVisibleTypes(chain.getArgs());
|
||||||
|
return result;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateSystemBarRequestedVisibleTypes(List<Object> args) {
|
||||||
|
if (args == null || args.size() <= 5) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Integer displayId = asInteger(args.get(0));
|
||||||
|
Integer requestedVisibleTypes = asInteger(args.get(5));
|
||||||
|
if (displayId == null || displayId != 0 || requestedVisibleTypes == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (systemBarRequestedVisibleTypes == requestedVisibleTypes) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
systemBarRequestedVisibleTypes = requestedVisibleTypes;
|
||||||
|
}
|
||||||
|
|
||||||
private void installNotificationCollectionHooks(ClassLoader classLoader) {
|
private void installNotificationCollectionHooks(ClassLoader classLoader) {
|
||||||
for (String className : List.of(
|
for (String className : List.of(
|
||||||
"com.android.systemui.statusbar.notification.collection.NotifCollection",
|
"com.android.systemui.statusbar.notification.collection.NotifCollection",
|
||||||
@@ -405,6 +445,9 @@ final class StockLayoutCanvasController {
|
|||||||
Object target = chain.getThisObject();
|
Object target = chain.getThisObject();
|
||||||
Object alphaArg = firstArg(chain.getArgs());
|
Object alphaArg = firstArg(chain.getArgs());
|
||||||
if (target instanceof View view && alphaArg instanceof Float alpha) {
|
if (target instanceof View view && alphaArg instanceof Float alpha) {
|
||||||
|
if (shouldSuppressPrivacyOverlayStatusBarAlpha(view, alpha)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
if (shouldSuppressConflictingShadeHeaderAlpha(view, alpha)) {
|
if (shouldSuppressConflictingShadeHeaderAlpha(view, alpha)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -414,16 +457,26 @@ final class StockLayoutCanvasController {
|
|||||||
if (shouldSuppressTrackedStatusChipAlpha(view, alpha)) {
|
if (shouldSuppressTrackedStatusChipAlpha(view, alpha)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
if (shouldSuppressSamsungBatteryStatusChipAlpha(view, alpha)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
if (shouldCaptureAodVisualAlpha(view)) {
|
if (shouldCaptureAodVisualAlpha(view)) {
|
||||||
rememberAodPluginVisualAlpha(view, alpha);
|
rememberAodPluginVisualAlpha(view, alpha);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return chain.proceed();
|
Object result = chain.proceed();
|
||||||
|
if (target instanceof View view) {
|
||||||
|
syncUnlockedHostsFromStockStatusBar(view);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
});
|
});
|
||||||
XposedHookSupport.hookAllMethodsIfExists(runtimeContext.getFramework(), viewClass, "setVisibility", chain -> {
|
XposedHookSupport.hookAllMethodsIfExists(runtimeContext.getFramework(), viewClass, "setVisibility", chain -> {
|
||||||
Object target = chain.getThisObject();
|
Object target = chain.getThisObject();
|
||||||
Object visibilityArg = firstArg(chain.getArgs());
|
Object visibilityArg = firstArg(chain.getArgs());
|
||||||
if (target instanceof View view && visibilityArg instanceof Integer visibility) {
|
if (target instanceof View view && visibilityArg instanceof Integer visibility) {
|
||||||
|
if (shouldSuppressSamsungBatteryStatusChipVisibility(view, visibility)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
if (shouldSuppressTrackedStatusChipVisibility(view, visibility)) {
|
if (shouldSuppressTrackedStatusChipVisibility(view, visibility)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -431,6 +484,7 @@ final class StockLayoutCanvasController {
|
|||||||
Object result = chain.proceed();
|
Object result = chain.proceed();
|
||||||
if (target instanceof View view && visibilityArg instanceof Integer visibility) {
|
if (target instanceof View view && visibilityArg instanceof Integer visibility) {
|
||||||
scheduleStatusChipRefreshIfContentChanged(view);
|
scheduleStatusChipRefreshIfContentChanged(view);
|
||||||
|
syncUnlockedHostsFromStockStatusBar(view);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
});
|
});
|
||||||
@@ -442,7 +496,25 @@ final class StockLayoutCanvasController {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return chain.proceed();
|
Object result = chain.proceed();
|
||||||
|
if (target instanceof View view) {
|
||||||
|
syncUnlockedHostsFromStockStatusBar(view);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
});
|
||||||
|
XposedHookSupport.hookAllMethodsIfExists(runtimeContext.getFramework(), viewClass, "setTranslationX", chain -> {
|
||||||
|
Object target = chain.getThisObject();
|
||||||
|
Object translationArg = firstArg(chain.getArgs());
|
||||||
|
if (target instanceof View view && translationArg instanceof Float translationX) {
|
||||||
|
if (shouldSuppressPrivacyOverlayStatusBarTranslationX(view, translationX)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Object result = chain.proceed();
|
||||||
|
if (target instanceof View view) {
|
||||||
|
syncUnlockedHostsFromStockStatusBar(view);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
});
|
});
|
||||||
XposedHookSupport.hookAllMethodsIfExists(runtimeContext.getFramework(), viewClass, "layout", chain -> {
|
XposedHookSupport.hookAllMethodsIfExists(runtimeContext.getFramework(), viewClass, "layout", chain -> {
|
||||||
Object target = chain.getThisObject();
|
Object target = chain.getThisObject();
|
||||||
@@ -459,15 +531,21 @@ final class StockLayoutCanvasController {
|
|||||||
right,
|
right,
|
||||||
bottom);
|
bottom);
|
||||||
if (override != null) {
|
if (override != null) {
|
||||||
return chain.proceed(new Object[]{
|
Object result = chain.proceed(new Object[]{
|
||||||
override.left,
|
override.left,
|
||||||
override.top,
|
override.top,
|
||||||
override.right,
|
override.right,
|
||||||
override.bottom
|
override.bottom
|
||||||
});
|
});
|
||||||
|
syncUnlockedHostsFromStockStatusBar(view);
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return chain.proceed();
|
Object result = chain.proceed();
|
||||||
|
if (target instanceof View view) {
|
||||||
|
syncUnlockedHostsFromStockStatusBar(view);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1485,6 +1563,9 @@ final class StockLayoutCanvasController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void markStatusChipLifecycleDirtyIfNeeded(View view, View fallbackRoot) {
|
private void markStatusChipLifecycleDirtyIfNeeded(View view, View fallbackRoot) {
|
||||||
|
if (RuntimeMutationGuard.isSnapshotRenderMutation()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (view == null || !isStatusChipCandidate(view)) {
|
if (view == null || !isStatusChipCandidate(view)) {
|
||||||
View root = view != null ? unlockedStatusBarRootFor(view) : fallbackRoot;
|
View root = view != null ? unlockedStatusBarRootFor(view) : fallbackRoot;
|
||||||
if (deferDrawerStatusChipRefresh(root)) {
|
if (deferDrawerStatusChipRefresh(root)) {
|
||||||
@@ -1913,12 +1994,15 @@ final class StockLayoutCanvasController {
|
|||||||
if (current.isUnlocked() && !previous.isUnlocked()) {
|
if (current.isUnlocked() && !previous.isUnlocked()) {
|
||||||
darkIconDispatcherGuard.restoreForcedStates();
|
darkIconDispatcherGuard.restoreForcedStates();
|
||||||
trackedLockedStatusBarIconSurfaces.clear();
|
trackedLockedStatusBarIconSurfaces.clear();
|
||||||
|
lockedStatusBarIconSurfaceReadyRoots.clear();
|
||||||
} else if (previous.isLockscreen() && current.isAod()) {
|
} else if (previous.isLockscreen() && current.isAod()) {
|
||||||
darkIconDispatcherGuard.restoreForcedStates();
|
darkIconDispatcherGuard.restoreForcedStates();
|
||||||
hideTrackedLockedStatusBarIconSurfaces(null);
|
hideTrackedLockedStatusBarIconSurfaces(null);
|
||||||
|
lockedStatusBarIconSurfaceReadyRoots.clear();
|
||||||
} else if (previous.isLockscreen() && !current.isLockscreen()) {
|
} else if (previous.isLockscreen() && !current.isLockscreen()) {
|
||||||
darkIconDispatcherGuard.restoreForcedStates();
|
darkIconDispatcherGuard.restoreForcedStates();
|
||||||
restoreTrackedLockedStatusBarIconSurfaces(null);
|
restoreTrackedLockedStatusBarIconSurfaces(null);
|
||||||
|
lockedStatusBarIconSurfaceReadyRoots.clear();
|
||||||
}
|
}
|
||||||
boolean lockscreenEntry = current.isLockscreen();
|
boolean lockscreenEntry = current.isLockscreen();
|
||||||
if (lockscreenEntry) {
|
if (lockscreenEntry) {
|
||||||
@@ -1959,6 +2043,7 @@ final class StockLayoutCanvasController {
|
|||||||
}
|
}
|
||||||
unlockedIconRenderController.clearRoot(root);
|
unlockedIconRenderController.clearRoot(root);
|
||||||
dirtyUnlockedLayoutRoots.add(root);
|
dirtyUnlockedLayoutRoots.add(root);
|
||||||
|
lockedStatusBarIconSurfaceReadyRoots.remove(root);
|
||||||
if (isViewThread(root)) {
|
if (isViewThread(root)) {
|
||||||
applyToRoot(root);
|
applyToRoot(root);
|
||||||
} else {
|
} else {
|
||||||
@@ -2107,6 +2192,83 @@ final class StockLayoutCanvasController {
|
|||||||
debugBoundsOverlayController.bringToFront(root);
|
debugBoundsOverlayController.bringToFront(root);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void syncUnlockedHostsFromStockStatusBar(View view) {
|
||||||
|
if (!isPhoneStatusBarRoot(view)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
View root = unlockedStatusBarRootWithUnlockedHostsFor(view);
|
||||||
|
if (root == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
syncUnlockedHostsToStockStatusBar(
|
||||||
|
root,
|
||||||
|
runtimeContext.getModeStateRepository().getActiveScene());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void syncUnlockedHostsToStockStatusBar(View root, SceneKey activeScene) {
|
||||||
|
if (root == null || !hasDirectUnlockedLayoutHost(root)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (activeScene == null || !activeScene.isUnlocked()) {
|
||||||
|
ownedIconHostManager.setUnlockedHostsTransform(root, 1f, 0f, 0f);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
View stockStatusBar = phoneStatusBarRootIn(root);
|
||||||
|
if (stockStatusBar == null || stockStatusBar == root) {
|
||||||
|
ownedIconHostManager.setUnlockedHostsTransform(root, 1f, 0f, 0f);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
float alpha = stockStatusBar.getVisibility() == View.VISIBLE
|
||||||
|
? clampAlpha(stockStatusBar.getAlpha())
|
||||||
|
: 0f;
|
||||||
|
float translationX = stockStatusBar.getLeft() + stockStatusBar.getTranslationX();
|
||||||
|
float translationY = stockStatusBar.getTop() + stockStatusBar.getTranslationY();
|
||||||
|
ownedIconHostManager.setUnlockedHostsTransform(
|
||||||
|
root,
|
||||||
|
alpha,
|
||||||
|
translationX,
|
||||||
|
translationY);
|
||||||
|
}
|
||||||
|
|
||||||
|
private View unlockedStatusBarRootWithUnlockedHostsFor(View view) {
|
||||||
|
View current = view;
|
||||||
|
while (current != null) {
|
||||||
|
if (isUnlockedStatusBarRoot(current) && hasDirectUnlockedLayoutHost(current)) {
|
||||||
|
return current;
|
||||||
|
}
|
||||||
|
Object parent = current.getParent();
|
||||||
|
current = parent instanceof View parentView ? parentView : null;
|
||||||
|
}
|
||||||
|
View root = view.getRootView();
|
||||||
|
return isUnlockedStatusBarRoot(root) && hasDirectUnlockedLayoutHost(root) ? root : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private View phoneStatusBarRootIn(View root) {
|
||||||
|
if (root == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (isPhoneStatusBarRoot(root)) {
|
||||||
|
return root;
|
||||||
|
}
|
||||||
|
if (!(root instanceof ViewGroup group)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
ArrayDeque<View> queue = new ArrayDeque<>();
|
||||||
|
queue.add(group);
|
||||||
|
while (!queue.isEmpty()) {
|
||||||
|
View current = queue.removeFirst();
|
||||||
|
if (current != root && isPhoneStatusBarRoot(current)) {
|
||||||
|
return current;
|
||||||
|
}
|
||||||
|
if (current instanceof ViewGroup childGroup) {
|
||||||
|
for (int i = 0; i < childGroup.getChildCount(); i++) {
|
||||||
|
queue.addLast(childGroup.getChildAt(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
private void applyToRootInternal(View root) {
|
private void applyToRootInternal(View root) {
|
||||||
if (root == null) {
|
if (root == null) {
|
||||||
return;
|
return;
|
||||||
@@ -2219,11 +2381,14 @@ final class StockLayoutCanvasController {
|
|||||||
activeScene,
|
activeScene,
|
||||||
rootDirty,
|
rootDirty,
|
||||||
lockedCarrierTextSource());
|
lockedCarrierTextSource());
|
||||||
boolean shouldShowUnlockedHosts = unlockedScene || aodScene || renderResult.shouldOwnLockscreen();
|
boolean shouldShowUnlockedHosts = unlockedScene
|
||||||
|
|| aodScene
|
||||||
|
|| (!unlockedScene && renderResult.shouldOwnLockscreen());
|
||||||
ownedIconHostManager.setUnlockedHostsVisible(root, shouldShowUnlockedHosts);
|
ownedIconHostManager.setUnlockedHostsVisible(root, shouldShowUnlockedHosts);
|
||||||
if (shouldShowUnlockedHosts) {
|
if (shouldShowUnlockedHosts) {
|
||||||
bringUnlockedHostsAndDebugOverlayToFront(root);
|
bringUnlockedHostsAndDebugOverlayToFront(root);
|
||||||
}
|
}
|
||||||
|
syncUnlockedHostsToStockStatusBar(root, activeScene);
|
||||||
syncAodOwnedHostVisualState(root, unlockedHosts);
|
syncAodOwnedHostVisualState(root, unlockedHosts);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2323,6 +2488,17 @@ final class StockLayoutCanvasController {
|
|||||||
|| findDirectOwnedHost(group, OwnedIconHostManager.TAG_AOD_CARDS_NOTIFICATION_HOST) != null;
|
|| findDirectOwnedHost(group, OwnedIconHostManager.TAG_AOD_CARDS_NOTIFICATION_HOST) != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean hasDirectUnlockedLayoutHost(View root) {
|
||||||
|
if (!(root instanceof ViewGroup group)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return findDirectOwnedHost(group, OwnedIconHostManager.TAG_UNLOCKED_STATUS_HOST) != null
|
||||||
|
|| findDirectOwnedHost(group, OwnedIconHostManager.TAG_UNLOCKED_NOTIFICATION_HOST) != null
|
||||||
|
|| findDirectOwnedHost(group, OwnedIconHostManager.TAG_UNLOCKED_CLOCK_HOST) != null
|
||||||
|
|| findDirectOwnedHost(group, OwnedIconHostManager.TAG_UNLOCKED_CARRIER_HOST) != null
|
||||||
|
|| findDirectOwnedHost(group, OwnedIconHostManager.TAG_UNLOCKED_CHIP_HOST) != null;
|
||||||
|
}
|
||||||
|
|
||||||
private boolean hasLockscreenCardsNotificationHost(View root) {
|
private boolean hasLockscreenCardsNotificationHost(View root) {
|
||||||
return root instanceof ViewGroup group
|
return root instanceof ViewGroup group
|
||||||
&& findDirectOwnedHost(
|
&& findDirectOwnedHost(
|
||||||
@@ -2431,8 +2607,7 @@ final class StockLayoutCanvasController {
|
|||||||
}
|
}
|
||||||
if (!isTrackableStatusChipSurface(view)
|
if (!isTrackableStatusChipSurface(view)
|
||||||
&& !isTrackedHiddenStatusChipSource(root, view)) {
|
&& !isTrackedHiddenStatusChipSource(root, view)) {
|
||||||
hiddenViewStates.remove(view);
|
forgetTrackedStatusChipState(view);
|
||||||
trackedUnlockedStockSurfaces.remove(view);
|
|
||||||
staleViews.add(view);
|
staleViews.add(view);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -2912,9 +3087,16 @@ final class StockLayoutCanvasController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void hideOrDiscoverLockedStatusBarIconSurfaces(View root) {
|
private void hideOrDiscoverLockedStatusBarIconSurfaces(View root) {
|
||||||
hideTrackedLockedStatusBarIconSurfaces(root);
|
if (root != null && lockedStatusBarIconSurfaceReadyRoots.contains(root)) {
|
||||||
if (root != null) {
|
return;
|
||||||
trackAndHideLockedStatusBarIconSurfaces(root);
|
}
|
||||||
|
boolean hasTrackedSurfacesInRoot = hideTrackedLockedStatusBarIconSurfaces(root);
|
||||||
|
int discovered = 0;
|
||||||
|
if (!hasTrackedSurfacesInRoot && root != null) {
|
||||||
|
discovered = trackAndHideLockedStatusBarIconSurfaces(root);
|
||||||
|
}
|
||||||
|
if (root != null && (hasTrackedSurfacesInRoot || discovered > 0)) {
|
||||||
|
lockedStatusBarIconSurfaceReadyRoots.add(root);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2922,30 +3104,29 @@ final class StockLayoutCanvasController {
|
|||||||
if (trackedLockedStatusBarIconSurfaces.isEmpty()) {
|
if (trackedLockedStatusBarIconSurfaces.isEmpty()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
boolean foundInRoot = false;
|
boolean foundActiveInRoot = false;
|
||||||
ArrayDeque<View> staleViews = new ArrayDeque<>();
|
ArrayDeque<View> staleViews = new ArrayDeque<>();
|
||||||
for (View view : trackedLockedStatusBarIconSurfaces) {
|
for (View view : trackedLockedStatusBarIconSurfaces) {
|
||||||
if (view == null || !view.isAttachedToWindow()) {
|
if (view == null || !view.isAttachedToWindow()) {
|
||||||
staleViews.add(view);
|
staleViews.add(view);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (root == null || isDescendantOf(view, root)) {
|
boolean inRoot = root == null || isDescendantOf(view, root);
|
||||||
foundInRoot = true;
|
|
||||||
if (isAlreadySuppressedHiddenView(view)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!isLockedStatusBarIconSurface(view)) {
|
if (!isLockedStatusBarIconSurface(view)) {
|
||||||
restoreView(view);
|
restoreView(view);
|
||||||
staleViews.add(view);
|
staleViews.add(view);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (root == null || isDescendantOf(view, root)) {
|
if (inRoot) {
|
||||||
|
foundActiveInRoot = true;
|
||||||
|
if (isAlreadySuppressedHiddenView(view)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
hideView(view, isCarrierView(view));
|
hideView(view, isCarrierView(view));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
removeStaleViews(trackedLockedStatusBarIconSurfaces, staleViews);
|
removeStaleViews(trackedLockedStatusBarIconSurfaces, staleViews);
|
||||||
return foundInRoot;
|
return foundActiveInRoot;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isAlreadySuppressedHiddenView(View view) {
|
private boolean isAlreadySuppressedHiddenView(View view) {
|
||||||
@@ -2983,6 +3164,7 @@ final class StockLayoutCanvasController {
|
|||||||
private boolean isTrackedStatusChipWriteTarget(View view) {
|
private boolean isTrackedStatusChipWriteTarget(View view) {
|
||||||
if (view == null
|
if (view == null
|
||||||
|| ownHiddenViewAlphaWriteDepth > 0
|
|| ownHiddenViewAlphaWriteDepth > 0
|
||||||
|
|| RuntimeMutationGuard.isSnapshotRenderMutation()
|
||||||
|| !trackedStatusChipSources.contains(view)
|
|| !trackedStatusChipSources.contains(view)
|
||||||
|| !hiddenViewStates.containsKey(view)
|
|| !hiddenViewStates.containsKey(view)
|
||||||
|| !view.isAttachedToWindow()
|
|| !view.isAttachedToWindow()
|
||||||
@@ -3002,6 +3184,7 @@ final class StockLayoutCanvasController {
|
|||||||
private boolean isStatusChipShellWriteTarget(View view) {
|
private boolean isStatusChipShellWriteTarget(View view) {
|
||||||
if (view == null
|
if (view == null
|
||||||
|| ownHiddenViewAlphaWriteDepth > 0
|
|| ownHiddenViewAlphaWriteDepth > 0
|
||||||
|
|| RuntimeMutationGuard.isSnapshotRenderMutation()
|
||||||
|| !view.isAttachedToWindow()
|
|| !view.isAttachedToWindow()
|
||||||
|| !isLayoutEnabled(view.getContext())
|
|| !isLayoutEnabled(view.getContext())
|
||||||
|| !isStatusChipShell(view)) {
|
|| !isStatusChipShell(view)) {
|
||||||
@@ -3105,6 +3288,11 @@ final class StockLayoutCanvasController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
removeStaleViews(trackedLockedStatusBarIconSurfaces, restoredViews);
|
removeStaleViews(trackedLockedStatusBarIconSurfaces, restoredViews);
|
||||||
|
if (root != null) {
|
||||||
|
lockedStatusBarIconSurfaceReadyRoots.remove(root);
|
||||||
|
} else {
|
||||||
|
lockedStatusBarIconSurfaceReadyRoots.clear();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean hasTrackedStatusChipSources(View root) {
|
private boolean hasTrackedStatusChipSources(View root) {
|
||||||
@@ -3286,6 +3474,9 @@ final class StockLayoutCanvasController {
|
|||||||
if (confirmedAodPluginViews.contains(view)) {
|
if (confirmedAodPluginViews.contains(view)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
if (isSamsungBatteryStatusChipSurface(view)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
String idName = ViewIdNames.idName(view);
|
String idName = ViewIdNames.idName(view);
|
||||||
if (TARGET_ID_NAMES.contains(idName)) {
|
if (TARGET_ID_NAMES.contains(idName)) {
|
||||||
return true;
|
return true;
|
||||||
@@ -3300,6 +3491,106 @@ final class StockLayoutCanvasController {
|
|||||||
return containsAny(className, TARGET_CLASS_SUBSTRINGS);
|
return containsAny(className, TARGET_CLASS_SUBSTRINGS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean isSamsungBatteryStatusChipSurface(View view) {
|
||||||
|
return view != null && view.getClass().getName().contains("SamsungBatteryStatusChip");
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean shouldSuppressSamsungBatteryStatusChipAlpha(View view, float alpha) {
|
||||||
|
if (alpha <= 0f || ownHiddenViewAlphaWriteDepth > 0 || !isSamsungBatteryStatusChipSurface(view)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!shouldSuppressSamsungBatteryStatusChip(view)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
hideView(view, true);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean shouldSuppressSamsungBatteryStatusChipVisibility(View view, int visibility) {
|
||||||
|
if (visibility != View.VISIBLE
|
||||||
|
|| ownHiddenViewAlphaWriteDepth > 0
|
||||||
|
|| !isSamsungBatteryStatusChipSurface(view)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!shouldSuppressSamsungBatteryStatusChip(view)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
hideView(view, true);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean shouldSuppressSamsungBatteryStatusChip(View view) {
|
||||||
|
if (view == null || !view.isAttachedToWindow() || !isLayoutEnabled(view.getContext())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean shouldSuppressPrivacyOverlayStatusBarAlpha(View view, float alpha) {
|
||||||
|
if (alpha >= 0.99f
|
||||||
|
|| ownHiddenViewAlphaWriteDepth > 0
|
||||||
|
|| !isPhoneStatusBarRoot(view)
|
||||||
|
|| !isLayoutEnabled(view.getContext())
|
||||||
|
|| ongoingPrivacyChipInRoot(view) == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (alpha <= 0f && !statusBarsRequestedVisible()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean shouldSuppressPrivacyOverlayStatusBarTranslationX(View view, float translationX) {
|
||||||
|
if (Math.abs(translationX) < 0.5f
|
||||||
|
|| ownHiddenViewAlphaWriteDepth > 0
|
||||||
|
|| !isPhoneStatusBarRoot(view)
|
||||||
|
|| !isLayoutEnabled(view.getContext())
|
||||||
|
|| ongoingPrivacyChipInRoot(view) == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean statusBarsRequestedVisible() {
|
||||||
|
return (systemBarRequestedVisibleTypes & WindowInsets.Type.statusBars()) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Integer asInteger(Object value) {
|
||||||
|
return value instanceof Integer integer ? integer : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isPhoneStatusBarRoot(View view) {
|
||||||
|
return view != null
|
||||||
|
&& view.getClass().getName().contains("PhoneStatusBarView");
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isOngoingPrivacyChip(View view) {
|
||||||
|
return view != null
|
||||||
|
&& view.getClass().getName().contains("OngoingPrivacyChip")
|
||||||
|
&& "privacy_chip".equals(ViewIdNames.idName(view));
|
||||||
|
}
|
||||||
|
|
||||||
|
private View ongoingPrivacyChipInRoot(View view) {
|
||||||
|
View root = view != null ? view.getRootView() : null;
|
||||||
|
if (!(root instanceof ViewGroup group)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
ArrayDeque<View> queue = new ArrayDeque<>();
|
||||||
|
queue.add(group);
|
||||||
|
while (!queue.isEmpty()) {
|
||||||
|
View current = queue.removeFirst();
|
||||||
|
if (isOngoingPrivacyChip(current)) {
|
||||||
|
return current;
|
||||||
|
}
|
||||||
|
if (current instanceof ViewGroup childGroup) {
|
||||||
|
for (int i = 0; i < childGroup.getChildCount(); i++) {
|
||||||
|
queue.addLast(childGroup.getChildAt(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
private boolean isStatusChipSource(View view) {
|
private boolean isStatusChipSource(View view) {
|
||||||
if (view == null || view.getVisibility() != View.VISIBLE) {
|
if (view == null || view.getVisibility() != View.VISIBLE) {
|
||||||
return false;
|
return false;
|
||||||
@@ -3340,6 +3631,9 @@ final class StockLayoutCanvasController {
|
|||||||
if (view == null || ownedIconHostManager.isOwnedHost(view)) {
|
if (view == null || ownedIconHostManager.isOwnedHost(view)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (view.getVisibility() != View.VISIBLE) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
String idName = ViewIdNames.idName(view);
|
String idName = ViewIdNames.idName(view);
|
||||||
if (!"ongoing_activity_capsule".equals(idName)) {
|
if (!"ongoing_activity_capsule".equals(idName)) {
|
||||||
return false;
|
return false;
|
||||||
@@ -3368,9 +3662,26 @@ final class StockLayoutCanvasController {
|
|||||||
|| !isStatusChipCandidate(view)) {
|
|| !isStatusChipCandidate(view)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (isCallStatusChip(view)
|
||||||
|
&& view.getVisibility() != View.VISIBLE
|
||||||
|
&& !isPhoneCallActive(view)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
return hasVisibleStatusChipContent(view);
|
return hasVisibleStatusChipContent(view);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean isPhoneCallActive(View view) {
|
||||||
|
if (view == null || view.getContext() == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
TelecomManager telecomManager = view.getContext().getSystemService(TelecomManager.class);
|
||||||
|
return telecomManager != null && telecomManager.isInCall();
|
||||||
|
} catch (RuntimeException ignored) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private boolean hasVisibleStatusChipContent(View source) {
|
private boolean hasVisibleStatusChipContent(View source) {
|
||||||
if (!(source instanceof ViewGroup group)) {
|
if (!(source instanceof ViewGroup group)) {
|
||||||
return false;
|
return false;
|
||||||
@@ -4186,11 +4497,19 @@ final class StockLayoutCanvasController {
|
|||||||
|| isStatusChipShell(view)) {
|
|| isStatusChipShell(view)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
hiddenViewStates.remove(view);
|
forgetTrackedStatusChip(view);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void forgetTrackedStatusChip(View view) {
|
||||||
|
forgetTrackedStatusChipState(view);
|
||||||
trackedStatusChipSources.remove(view);
|
trackedStatusChipSources.remove(view);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void forgetTrackedStatusChipState(View view) {
|
||||||
|
hiddenViewStates.remove(view);
|
||||||
trackedUnlockedStockSurfaces.remove(view);
|
trackedUnlockedStockSurfaces.remove(view);
|
||||||
statusChipLifecycleLayoutSignatureByView.remove(view);
|
statusChipLifecycleLayoutSignatureByView.remove(view);
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void cleanupLayoutOffRoot(View root, SceneKey activeScene) {
|
private void cleanupLayoutOffRoot(View root, SceneKey activeScene) {
|
||||||
@@ -4200,6 +4519,7 @@ final class StockLayoutCanvasController {
|
|||||||
}
|
}
|
||||||
clearTrackedStatusChipState();
|
clearTrackedStatusChipState();
|
||||||
trackedLockedStatusBarIconSurfaces.clear();
|
trackedLockedStatusBarIconSurfaces.clear();
|
||||||
|
lockedStatusBarIconSurfaceReadyRoots.clear();
|
||||||
trackedUnlockedStockSurfaces.clear();
|
trackedUnlockedStockSurfaces.clear();
|
||||||
confirmedAodPluginViews.clear();
|
confirmedAodPluginViews.clear();
|
||||||
aodPluginVisualAlphaByView.clear();
|
aodPluginVisualAlphaByView.clear();
|
||||||
|
|||||||
+51
@@ -94,6 +94,12 @@ public final class DebugBoundsOverlayController {
|
|||||||
OwnedIconHostManager.TAG_UNLOCKED_CLOCK_HOST,
|
OwnedIconHostManager.TAG_UNLOCKED_CLOCK_HOST,
|
||||||
COLOR_CLOCK,
|
COLOR_CLOCK,
|
||||||
specs);
|
specs);
|
||||||
|
addTaggedDescendantSpecs(
|
||||||
|
root,
|
||||||
|
parent,
|
||||||
|
R.id.sbt_debug_drawer_clock_container,
|
||||||
|
COLOR_CLOCK,
|
||||||
|
specs);
|
||||||
}
|
}
|
||||||
if (layoutEnabled && settings.debugVisualizeChipContainer) {
|
if (layoutEnabled && settings.debugVisualizeChipContainer) {
|
||||||
addHostContainerSpecs(
|
addHostContainerSpecs(
|
||||||
@@ -148,6 +154,9 @@ public final class DebugBoundsOverlayController {
|
|||||||
result = 31 * result + hostSignature(root, OwnedIconHostManager.TAG_UNLOCKED_NOTIFICATION_HOST);
|
result = 31 * result + hostSignature(root, OwnedIconHostManager.TAG_UNLOCKED_NOTIFICATION_HOST);
|
||||||
result = 31 * result + hostSignature(root, OwnedIconHostManager.TAG_UNLOCKED_STATUS_HOST);
|
result = 31 * result + hostSignature(root, OwnedIconHostManager.TAG_UNLOCKED_STATUS_HOST);
|
||||||
result = 31 * result + hostSignature(root, OwnedIconHostManager.TAG_UNLOCKED_CLOCK_HOST);
|
result = 31 * result + hostSignature(root, OwnedIconHostManager.TAG_UNLOCKED_CLOCK_HOST);
|
||||||
|
if (settings.debugVisualizeClockContainer) {
|
||||||
|
result = 31 * result + taggedDescendantSignature(root, root, R.id.sbt_debug_drawer_clock_container);
|
||||||
|
}
|
||||||
result = 31 * result + hostSignature(root, OwnedIconHostManager.TAG_UNLOCKED_CHIP_HOST);
|
result = 31 * result + hostSignature(root, OwnedIconHostManager.TAG_UNLOCKED_CHIP_HOST);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -403,6 +412,30 @@ public final class DebugBoundsOverlayController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void addTaggedDescendantSpecs(
|
||||||
|
View root,
|
||||||
|
View view,
|
||||||
|
int tagKey,
|
||||||
|
int color,
|
||||||
|
ArrayList<OverlaySpec> specs
|
||||||
|
) {
|
||||||
|
if (root == null || view == null || specs == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (view.getTag(tagKey) != null && view.getVisibility() != View.GONE) {
|
||||||
|
Bounds bounds = boundsRelativeTo(root, view);
|
||||||
|
if (bounds != null && bounds.width > 0 && bounds.height > 0) {
|
||||||
|
specs.add(new OverlaySpec(bounds, color));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!(view instanceof ViewGroup group)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < group.getChildCount(); i++) {
|
||||||
|
addTaggedDescendantSpecs(root, group.getChildAt(i), tagKey, color, specs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private OverlayView ensureOverlay(ViewGroup parent) {
|
private OverlayView ensureOverlay(ViewGroup parent) {
|
||||||
View existing = findDirectTaggedChild(parent, TAG_OVERLAY_HOST);
|
View existing = findDirectTaggedChild(parent, TAG_OVERLAY_HOST);
|
||||||
if (existing instanceof OverlayView overlayView) {
|
if (existing instanceof OverlayView overlayView) {
|
||||||
@@ -493,6 +526,24 @@ public final class DebugBoundsOverlayController {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private int taggedDescendantSignature(View root, View view, int tagKey) {
|
||||||
|
if (root == null || view == null) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
int result = 17;
|
||||||
|
Object tag = view.getTag(tagKey);
|
||||||
|
if (tag != null && view.getVisibility() != View.GONE) {
|
||||||
|
result = 31 * result + signature(boundsRelativeTo(root, view));
|
||||||
|
result = 31 * result + tag.hashCode();
|
||||||
|
}
|
||||||
|
if (view instanceof ViewGroup group) {
|
||||||
|
for (int i = 0; i < group.getChildCount(); i++) {
|
||||||
|
result = 31 * result + taggedDescendantSignature(root, group.getChildAt(i), tagKey);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
private Bounds boundsRelativeTo(View root, View view) {
|
private Bounds boundsRelativeTo(View root, View view) {
|
||||||
if (root == null || view == null) {
|
if (root == null || view == null) {
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
+85
@@ -0,0 +1,85 @@
|
|||||||
|
package se.ajpanton.statusbartweak.runtime.render;
|
||||||
|
|
||||||
|
import android.graphics.Rect;
|
||||||
|
import android.view.DisplayCutout;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.WindowInsets;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.WeakHashMap;
|
||||||
|
|
||||||
|
import se.ajpanton.statusbartweak.shell.settings.ClockCameraTypeSupport;
|
||||||
|
import se.ajpanton.statusbartweak.shell.settings.SbtSettings;
|
||||||
|
|
||||||
|
final class EffectiveCutoutSupport {
|
||||||
|
private static final WeakHashMap<View, CameraTypeCache> CAMERA_TYPE_BY_ROOT = new WeakHashMap<>();
|
||||||
|
|
||||||
|
private EffectiveCutoutSupport() {
|
||||||
|
}
|
||||||
|
|
||||||
|
static Rect middleCutout(View root, SbtSettings settings, boolean ignoreUnderDisplayCamera) {
|
||||||
|
Rect cutout = ViewGeometry.middleCutout(root);
|
||||||
|
if (cutout == null || !ignoreUnderDisplayCamera) {
|
||||||
|
return cutout;
|
||||||
|
}
|
||||||
|
String effectiveType = effectiveCameraType(root, cutout, settings);
|
||||||
|
return ClockCameraTypeSupport.TYPE_UDC.equals(effectiveType) ? null : cutout;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Rect layoutMiddleCutout(View root, SbtSettings settings) {
|
||||||
|
return middleCutout(
|
||||||
|
root,
|
||||||
|
settings,
|
||||||
|
settings != null && settings.layoutIgnoreUnderDisplayCameras);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Rect clockMiddleCutout(View root, SbtSettings settings) {
|
||||||
|
return middleCutout(
|
||||||
|
root,
|
||||||
|
settings,
|
||||||
|
settings != null && settings.clockIgnoreUnderDisplayCameras);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String effectiveCameraType(View root, Rect cutout, SbtSettings settings) {
|
||||||
|
int signature = cameraTypeSignature(root, cutout, settings);
|
||||||
|
CameraTypeCache cached = CAMERA_TYPE_BY_ROOT.get(root);
|
||||||
|
if (cached != null && cached.signature == signature) {
|
||||||
|
return cached.effectiveType;
|
||||||
|
}
|
||||||
|
WindowInsets insets = root != null ? root.getRootWindowInsets() : null;
|
||||||
|
DisplayCutout displayCutout = insets != null ? insets.getDisplayCutout() : null;
|
||||||
|
ClockCameraTypeSupport.CameraInfo cameraInfo =
|
||||||
|
ClockCameraTypeSupport.resolveCameraInfo(root, displayCutout, settings);
|
||||||
|
String effectiveType = cameraInfo.effectiveType;
|
||||||
|
if (root != null) {
|
||||||
|
SbtSettings.setClockCameraDebugState(
|
||||||
|
root.getContext(),
|
||||||
|
cameraInfo.cameraId,
|
||||||
|
cameraInfo.automaticType,
|
||||||
|
cameraInfo.effectiveType);
|
||||||
|
}
|
||||||
|
CAMERA_TYPE_BY_ROOT.put(root, new CameraTypeCache(signature, effectiveType));
|
||||||
|
return effectiveType;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int cameraTypeSignature(View root, Rect cutout, SbtSettings settings) {
|
||||||
|
return Objects.hash(
|
||||||
|
root != null ? root.getWidth() : 0,
|
||||||
|
root != null ? root.getHeight() : 0,
|
||||||
|
cutout != null ? cutout.left : 0,
|
||||||
|
cutout != null ? cutout.top : 0,
|
||||||
|
cutout != null ? cutout.right : 0,
|
||||||
|
cutout != null ? cutout.bottom : 0,
|
||||||
|
settings != null ? settings.clockCameraTypeOverrides : null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class CameraTypeCache {
|
||||||
|
final int signature;
|
||||||
|
final String effectiveType;
|
||||||
|
|
||||||
|
CameraTypeCache(int signature, String effectiveType) {
|
||||||
|
this.signature = signature;
|
||||||
|
this.effectiveType = effectiveType;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -119,6 +119,17 @@ public final class OwnedIconHostManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setUnlockedHostsTransform(
|
||||||
|
View root,
|
||||||
|
float alpha,
|
||||||
|
float translationX,
|
||||||
|
float translationY
|
||||||
|
) {
|
||||||
|
for (String tag : UNLOCKED_HOST_ORDER) {
|
||||||
|
setHostTransform(root, tag, alpha, translationX, translationY);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void bringUnlockedHostsToFront(View root) {
|
public void bringUnlockedHostsToFront(View root) {
|
||||||
if (!(root instanceof ViewGroup parent)) {
|
if (!(root instanceof ViewGroup parent)) {
|
||||||
return;
|
return;
|
||||||
@@ -169,6 +180,31 @@ public final class OwnedIconHostManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void setHostTransform(
|
||||||
|
View root,
|
||||||
|
String tag,
|
||||||
|
float alpha,
|
||||||
|
float translationX,
|
||||||
|
float translationY
|
||||||
|
) {
|
||||||
|
if (!(root instanceof ViewGroup parent) || tag == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
FrameLayout host = findDirectHost(parent, tag);
|
||||||
|
if (host == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (host.getAlpha() != alpha) {
|
||||||
|
host.setAlpha(alpha);
|
||||||
|
}
|
||||||
|
if (host.getTranslationX() != translationX) {
|
||||||
|
host.setTranslationX(translationX);
|
||||||
|
}
|
||||||
|
if (host.getTranslationY() != translationY) {
|
||||||
|
host.setTranslationY(translationY);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private ArrayList<View> unlockedHosts(ViewGroup parent) {
|
private ArrayList<View> unlockedHosts(ViewGroup parent) {
|
||||||
ArrayList<View> hosts = new ArrayList<>();
|
ArrayList<View> hosts = new ArrayList<>();
|
||||||
for (String tag : UNLOCKED_HOST_ORDER) {
|
for (String tag : UNLOCKED_HOST_ORDER) {
|
||||||
|
|||||||
@@ -34,6 +34,10 @@ final class SnapshotIconFilter {
|
|||||||
}
|
}
|
||||||
ArrayList<SnapshotPlacement> filtered = new ArrayList<>(placements.size());
|
ArrayList<SnapshotPlacement> filtered = new ArrayList<>(placements.size());
|
||||||
for (SnapshotPlacement placement : placements) {
|
for (SnapshotPlacement placement : placements) {
|
||||||
|
if (isChargingBatteryChipPlacement(placement)) {
|
||||||
|
filtered.add(placement);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if (!isStatusBlocked(placement, settings.systemIconBlockedModes, mode)) {
|
if (!isStatusBlocked(placement, settings.systemIconBlockedModes, mode)) {
|
||||||
filtered.add(placement);
|
filtered.add(placement);
|
||||||
}
|
}
|
||||||
@@ -109,6 +113,11 @@ final class SnapshotIconFilter {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static boolean isChargingBatteryChipPlacement(SnapshotPlacement placement) {
|
||||||
|
return placement != null
|
||||||
|
&& SnapshotKeys.CHARGING_BATTERY_CHIP.equals(placement.key);
|
||||||
|
}
|
||||||
|
|
||||||
private static AppIconRules.ExceptionRule matchingExceptionRule(
|
private static AppIconRules.ExceptionRule matchingExceptionRule(
|
||||||
SnapshotPlacement placement,
|
SnapshotPlacement placement,
|
||||||
ArrayList<AppIconRules.ExceptionRule> rules
|
ArrayList<AppIconRules.ExceptionRule> rules
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ final class SnapshotKeys {
|
|||||||
static final String OVERFLOW_DOT = "statusbartweak:overflow_dot";
|
static final String OVERFLOW_DOT = "statusbartweak:overflow_dot";
|
||||||
static final String EMPTY_CHIP = "statusbartweak:empty_chip";
|
static final String EMPTY_CHIP = "statusbartweak:empty_chip";
|
||||||
static final String EMPTY_ICON_CONTAINER = "statusbartweak:empty_icon_container";
|
static final String EMPTY_ICON_CONTAINER = "statusbartweak:empty_icon_container";
|
||||||
|
static final String CHARGING_BATTERY_CHIP = "statusbartweak:charging_battery_chip";
|
||||||
|
|
||||||
private SnapshotKeys() {
|
private SnapshotKeys() {
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,15 +1,20 @@
|
|||||||
package se.ajpanton.statusbartweak.runtime.render;
|
package se.ajpanton.statusbartweak.runtime.render;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
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.Path;
|
||||||
import android.graphics.PorterDuff;
|
import android.graphics.PorterDuff;
|
||||||
import android.graphics.Rect;
|
import android.graphics.Rect;
|
||||||
|
import android.graphics.RectF;
|
||||||
|
import android.graphics.drawable.AnimationDrawable;
|
||||||
import android.graphics.drawable.BitmapDrawable;
|
import android.graphics.drawable.BitmapDrawable;
|
||||||
import android.graphics.drawable.Drawable;
|
import android.graphics.drawable.Drawable;
|
||||||
|
import android.os.BatteryManager;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.os.SystemClock;
|
import android.os.SystemClock;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
@@ -31,9 +36,11 @@ import java.util.Objects;
|
|||||||
import java.util.WeakHashMap;
|
import java.util.WeakHashMap;
|
||||||
|
|
||||||
import se.ajpanton.statusbartweak.runtime.NotificationKeyReflection;
|
import se.ajpanton.statusbartweak.runtime.NotificationKeyReflection;
|
||||||
|
import se.ajpanton.statusbartweak.runtime.RuntimeMutationGuard;
|
||||||
import se.ajpanton.statusbartweak.runtime.ViewIdNames;
|
import se.ajpanton.statusbartweak.runtime.ViewIdNames;
|
||||||
import se.ajpanton.statusbartweak.runtime.hooks.ReflectionSupport;
|
import se.ajpanton.statusbartweak.runtime.hooks.ReflectionSupport;
|
||||||
import se.ajpanton.statusbartweak.runtime.layoutsolver.StatusBarLayoutSolver.Bounds;
|
import se.ajpanton.statusbartweak.runtime.layoutsolver.StatusBarLayoutSolver.Bounds;
|
||||||
|
import se.ajpanton.statusbartweak.runtime.settings.BroadcastReceiverSupport;
|
||||||
|
|
||||||
final class SnapshotRenderer {
|
final class SnapshotRenderer {
|
||||||
private static final long TRANSITION_SIGNAL_HOLD_MS = 700L;
|
private static final long TRANSITION_SIGNAL_HOLD_MS = 700L;
|
||||||
@@ -233,9 +240,24 @@ final class SnapshotRenderer {
|
|||||||
proxy.setTag(key);
|
proxy.setTag(key);
|
||||||
proxyChanged = true;
|
proxyChanged = true;
|
||||||
}
|
}
|
||||||
|
SnapshotRenderState desiredState = renderStateFor(placement);
|
||||||
|
Drawable animatedDrawable = animatedNotificationDrawable(placement);
|
||||||
|
if (animatedDrawable != null) {
|
||||||
|
SnapshotRenderState currentState = statesByProxy.get(proxy);
|
||||||
|
boolean drawableChanged = !(proxy.getDrawable() instanceof AnimationDrawable)
|
||||||
|
|| !canReuseAnimatedProxy(currentState, desiredState);
|
||||||
|
if (drawableChanged) {
|
||||||
|
Drawable proxyDrawable = newDrawableInstance(animatedDrawable);
|
||||||
|
proxy.setImageDrawable(proxyDrawable);
|
||||||
|
proxyChanged = true;
|
||||||
|
}
|
||||||
|
startAnimationIfNeeded(proxy.getDrawable());
|
||||||
|
if (!desiredState.equals(currentState)) {
|
||||||
|
statesByProxy.put(proxy, desiredState);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
BitmapResult bitmapResult = bitmapFor(proxy, bounds);
|
BitmapResult bitmapResult = bitmapFor(proxy, bounds);
|
||||||
Bitmap bitmap = bitmapResult.bitmap();
|
Bitmap bitmap = bitmapResult.bitmap();
|
||||||
SnapshotRenderState desiredState = renderStateFor(placement);
|
|
||||||
boolean bitmapChanged = bitmapResult.created()
|
boolean bitmapChanged = bitmapResult.created()
|
||||||
|| !desiredState.equals(statesByProxy.get(proxy));
|
|| !desiredState.equals(statesByProxy.get(proxy));
|
||||||
if (bitmapChanged) {
|
if (bitmapChanged) {
|
||||||
@@ -263,6 +285,7 @@ final class SnapshotRenderer {
|
|||||||
proxy.setImageBitmap(bitmap);
|
proxy.setImageBitmap(bitmap);
|
||||||
proxyChanged = true;
|
proxyChanged = true;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if (proxy.getAlpha() != 1f) {
|
if (proxy.getAlpha() != 1f) {
|
||||||
proxy.setAlpha(1f);
|
proxy.setAlpha(1f);
|
||||||
proxyChanged = true;
|
proxyChanged = true;
|
||||||
@@ -299,6 +322,52 @@ final class SnapshotRenderer {
|
|||||||
return renderedBounds;
|
return renderedBounds;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean canReuseAnimatedProxy(
|
||||||
|
SnapshotRenderState currentState,
|
||||||
|
SnapshotRenderState desiredState
|
||||||
|
) {
|
||||||
|
return currentState != null
|
||||||
|
&& desiredState != null
|
||||||
|
&& Objects.equals(currentState.key(), desiredState.key())
|
||||||
|
&& currentState.width() == desiredState.width()
|
||||||
|
&& currentState.height() == desiredState.height()
|
||||||
|
&& currentState.signal() == desiredState.signal()
|
||||||
|
&& currentState.overflowDot() == desiredState.overflowDot()
|
||||||
|
&& currentState.overflowDotColor() == desiredState.overflowDotColor()
|
||||||
|
&& currentState.sourceOffsetX() == desiredState.sourceOffsetX()
|
||||||
|
&& currentState.sourceRenderBoundsSignature() == desiredState.sourceRenderBoundsSignature()
|
||||||
|
&& Objects.equals(currentState.heldSignalKey(), desiredState.heldSignalKey())
|
||||||
|
&& currentState.heldBitmapId() == desiredState.heldBitmapId()
|
||||||
|
&& currentState.sourceMode() == desiredState.sourceMode()
|
||||||
|
&& currentState.tintSignature() == desiredState.tintSignature();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Drawable animatedNotificationDrawable(SnapshotPlacement placement) {
|
||||||
|
if (placement == null
|
||||||
|
|| placement.source() == null
|
||||||
|
|| placement.source().mode() != SnapshotMode.NOTIFICATION_DRAWABLE
|
||||||
|
|| !(placement.source().view() instanceof ImageView imageView)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
Drawable drawable = imageView.getDrawable();
|
||||||
|
return drawable instanceof AnimationDrawable ? drawable : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Drawable newDrawableInstance(Drawable drawable) {
|
||||||
|
Drawable.ConstantState constantState = drawable != null ? drawable.getConstantState() : null;
|
||||||
|
if (constantState != null) {
|
||||||
|
return constantState.newDrawable().mutate();
|
||||||
|
}
|
||||||
|
return drawable != null ? drawable.mutate() : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void startAnimationIfNeeded(Drawable drawable) {
|
||||||
|
if (drawable instanceof AnimationDrawable animationDrawable && !animationDrawable.isRunning()) {
|
||||||
|
animationDrawable.setVisible(true, true);
|
||||||
|
animationDrawable.start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void clear(ViewGroup host) {
|
void clear(ViewGroup host) {
|
||||||
LinkedHashMap<String, ImageView> proxies = proxiesByHost.remove(host);
|
LinkedHashMap<String, ImageView> proxies = proxiesByHost.remove(host);
|
||||||
if (proxies == null) {
|
if (proxies == null) {
|
||||||
@@ -336,6 +405,7 @@ final class SnapshotRenderer {
|
|||||||
private boolean shouldTrimContent(SnapshotSource source) {
|
private boolean shouldTrimContent(SnapshotSource source) {
|
||||||
return trimViewContentForCollision
|
return trimViewContentForCollision
|
||||||
&& source != null
|
&& source != null
|
||||||
|
&& !SnapshotKeys.CHARGING_BATTERY_CHIP.equals(source.keyHint())
|
||||||
&& (source.mode() == SnapshotMode.VIEW || source.mode() == SnapshotMode.STATUS_CHIP);
|
&& (source.mode() == SnapshotMode.VIEW || source.mode() == SnapshotMode.STATUS_CHIP);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -713,6 +783,10 @@ final class SnapshotRenderer {
|
|||||||
if (offsetX != 0) {
|
if (offsetX != 0) {
|
||||||
canvas.translate(-offsetX, 0);
|
canvas.translate(-offsetX, 0);
|
||||||
}
|
}
|
||||||
|
if (isChargingBatteryChipSource(source)) {
|
||||||
|
drawChargingBatteryChip(source, bitmap);
|
||||||
|
return;
|
||||||
|
}
|
||||||
View sourceView = source.view();
|
View sourceView = source.view();
|
||||||
if (sourceView == null) {
|
if (sourceView == null) {
|
||||||
return;
|
return;
|
||||||
@@ -738,6 +812,8 @@ final class SnapshotRenderer {
|
|||||||
bitmap.getHeight())) {
|
bitmap.getHeight())) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
RuntimeMutationGuard.enterSnapshotRenderMutation();
|
||||||
|
try {
|
||||||
sourceView.setVisibility(View.VISIBLE);
|
sourceView.setVisibility(View.VISIBLE);
|
||||||
sourceView.setAlpha(1f);
|
sourceView.setAlpha(1f);
|
||||||
if (source.mode() == SnapshotMode.STATUS_CHIP) {
|
if (source.mode() == SnapshotMode.STATUS_CHIP) {
|
||||||
@@ -746,8 +822,16 @@ final class SnapshotRenderer {
|
|||||||
sourceView.draw(canvas);
|
sourceView.draw(canvas);
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
|
RuntimeMutationGuard.exitSnapshotRenderMutation();
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
RuntimeMutationGuard.enterSnapshotRenderMutation();
|
||||||
|
try {
|
||||||
sourceView.setAlpha(oldAlpha);
|
sourceView.setAlpha(oldAlpha);
|
||||||
sourceView.setVisibility(oldVisibility);
|
sourceView.setVisibility(oldVisibility);
|
||||||
|
} finally {
|
||||||
|
RuntimeMutationGuard.exitSnapshotRenderMutation();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -823,6 +907,152 @@ final class SnapshotRenderer {
|
|||||||
&& source.mode() != SnapshotMode.STATUS_CHIP;
|
&& source.mode() != SnapshotMode.STATUS_CHIP;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean isChargingBatteryChipSource(SnapshotSource source) {
|
||||||
|
return source != null && SnapshotKeys.CHARGING_BATTERY_CHIP.equals(source.keyHint());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void drawChargingBatteryChip(SnapshotSource source, Bitmap bitmap) {
|
||||||
|
if (source == null
|
||||||
|
|| bitmap == null
|
||||||
|
|| bitmap.isRecycled()
|
||||||
|
|| bitmap.getWidth() <= 0
|
||||||
|
|| bitmap.getHeight() <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
bitmap.eraseColor(Color.TRANSPARENT);
|
||||||
|
int width = bitmap.getWidth();
|
||||||
|
int height = bitmap.getHeight();
|
||||||
|
float inset = Math.max(1f, height * 0.05f);
|
||||||
|
RectF pill = new RectF(inset, inset, width - inset, height - inset);
|
||||||
|
float radius = pill.height() / 2f;
|
||||||
|
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG);
|
||||||
|
paint.setColor(Color.argb(235, 42, 45, 49));
|
||||||
|
Canvas canvas = new Canvas(bitmap);
|
||||||
|
canvas.drawRoundRect(pill, radius, radius, paint);
|
||||||
|
|
||||||
|
int level = chargingBatteryLevel(source.view());
|
||||||
|
float gaugeWidth = Math.min(pill.width(), Math.max(height * 1.35f, pill.width() * 0.42f));
|
||||||
|
float fillRight = pill.left + gaugeWidth * Math.max(0f, Math.min(100f, level)) / 100f;
|
||||||
|
if (fillRight > pill.left) {
|
||||||
|
Path clip = new Path();
|
||||||
|
clip.addRoundRect(pill, radius, radius, Path.Direction.CW);
|
||||||
|
int save = canvas.save();
|
||||||
|
canvas.clipPath(clip);
|
||||||
|
paint.setColor(Color.rgb(42, 220, 103));
|
||||||
|
canvas.drawRect(pill.left, pill.top, fillRight, pill.bottom, paint);
|
||||||
|
canvas.restoreToCount(save);
|
||||||
|
}
|
||||||
|
|
||||||
|
paint.setStyle(Paint.Style.STROKE);
|
||||||
|
paint.setStrokeWidth(Math.max(1f, height * 0.035f));
|
||||||
|
paint.setColor(Color.argb(120, 255, 255, 255));
|
||||||
|
canvas.drawRoundRect(pill, radius, radius, paint);
|
||||||
|
paint.setStyle(Paint.Style.FILL);
|
||||||
|
|
||||||
|
drawChargingBolt(canvas, paint, pill.left + height * 0.48f, height / 2f, height * 0.55f);
|
||||||
|
|
||||||
|
String text = chargingBatteryText(source.view(), level);
|
||||||
|
paint.setTextAlign(Paint.Align.RIGHT);
|
||||||
|
paint.setFakeBoldText(true);
|
||||||
|
paint.setColor(Color.WHITE);
|
||||||
|
paint.setTextSize(Math.max(1f, height * 0.42f));
|
||||||
|
Paint.FontMetrics metrics = paint.getFontMetrics();
|
||||||
|
float baseline = height / 2f - (metrics.ascent + metrics.descent) / 2f;
|
||||||
|
canvas.drawText(text, pill.right - Math.max(2f, height * 0.18f), baseline, paint);
|
||||||
|
paint.setFakeBoldText(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void drawChargingBolt(Canvas canvas, Paint paint, float centerX, float centerY, float size) {
|
||||||
|
float half = size / 2f;
|
||||||
|
Path bolt = new Path();
|
||||||
|
bolt.moveTo(centerX + half * 0.10f, centerY - half);
|
||||||
|
bolt.lineTo(centerX - half * 0.42f, centerY + half * 0.08f);
|
||||||
|
bolt.lineTo(centerX - half * 0.04f, centerY + half * 0.08f);
|
||||||
|
bolt.lineTo(centerX - half * 0.18f, centerY + half);
|
||||||
|
bolt.lineTo(centerX + half * 0.44f, centerY - half * 0.18f);
|
||||||
|
bolt.lineTo(centerX + half * 0.04f, centerY - half * 0.18f);
|
||||||
|
bolt.close();
|
||||||
|
paint.setColor(Color.WHITE);
|
||||||
|
paint.setStyle(Paint.Style.FILL);
|
||||||
|
canvas.drawPath(bolt, paint);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String chargingBatteryText(View source, int fallbackLevel) {
|
||||||
|
CharSequence text = findChargingBatteryText(source);
|
||||||
|
if (text != null && text.length() > 0) {
|
||||||
|
return text.toString();
|
||||||
|
}
|
||||||
|
return Math.max(0, Math.min(100, fallbackLevel)) + "%";
|
||||||
|
}
|
||||||
|
|
||||||
|
private int chargingBatteryLevel(View source) {
|
||||||
|
CharSequence text = findChargingBatteryText(source);
|
||||||
|
int parsed = parsePercent(text);
|
||||||
|
if (parsed >= 0) {
|
||||||
|
return parsed;
|
||||||
|
}
|
||||||
|
Integer stickyLevel = stickyBatteryLevel(source != null ? source.getContext() : null);
|
||||||
|
return stickyLevel != null ? stickyLevel : 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Integer stickyBatteryLevel(Context context) {
|
||||||
|
if (context == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
Intent intent = BroadcastReceiverSupport.readExportedSticky(
|
||||||
|
context,
|
||||||
|
BroadcastReceiverSupport.filter(Intent.ACTION_BATTERY_CHANGED));
|
||||||
|
if (intent == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
int level = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1);
|
||||||
|
int scale = intent.getIntExtra(BatteryManager.EXTRA_SCALE, 100);
|
||||||
|
if (level < 0 || scale <= 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return Math.max(0, Math.min(100, Math.round(level * 100f / scale)));
|
||||||
|
}
|
||||||
|
|
||||||
|
private CharSequence findChargingBatteryText(View source) {
|
||||||
|
if (source == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (source instanceof TextView textView) {
|
||||||
|
CharSequence text = textView.getText();
|
||||||
|
if (parsePercent(text) >= 0) {
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!(source instanceof ViewGroup group)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < group.getChildCount(); i++) {
|
||||||
|
CharSequence text = findChargingBatteryText(group.getChildAt(i));
|
||||||
|
if (text != null && text.length() > 0) {
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int parsePercent(CharSequence text) {
|
||||||
|
if (text == null) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
int value = 0;
|
||||||
|
boolean found = false;
|
||||||
|
for (int i = 0; i < text.length(); i++) {
|
||||||
|
char c = text.charAt(i);
|
||||||
|
if (c >= '0' && c <= '9') {
|
||||||
|
value = value * 10 + c - '0';
|
||||||
|
found = true;
|
||||||
|
} else if (found) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return found ? Math.max(0, Math.min(100, value)) : -1;
|
||||||
|
}
|
||||||
|
|
||||||
private boolean isTintableMonochrome(Bitmap bitmap) {
|
private boolean isTintableMonochrome(Bitmap bitmap) {
|
||||||
int coloredPixels = 0;
|
int coloredPixels = 0;
|
||||||
int monochromePixels = 0;
|
int monochromePixels = 0;
|
||||||
@@ -1027,7 +1257,9 @@ final class SnapshotRenderer {
|
|||||||
view.getRight(),
|
view.getRight(),
|
||||||
view.getBottom(),
|
view.getBottom(),
|
||||||
view.getMeasuredWidth(),
|
view.getMeasuredWidth(),
|
||||||
view.getMeasuredHeight()));
|
view.getMeasuredHeight(),
|
||||||
|
view.getVisibility(),
|
||||||
|
view.getAlpha()));
|
||||||
if (view instanceof ViewGroup group) {
|
if (view instanceof ViewGroup group) {
|
||||||
for (int i = 0; i < group.getChildCount(); i++) {
|
for (int i = 0; i < group.getChildCount(); i++) {
|
||||||
captureFrameState(group.getChildAt(i), state);
|
captureFrameState(group.getChildAt(i), state);
|
||||||
@@ -1051,6 +1283,12 @@ final class SnapshotRenderer {
|
|||||||
View.MeasureSpec.makeMeasureSpec(frame.measuredHeight, View.MeasureSpec.EXACTLY));
|
View.MeasureSpec.makeMeasureSpec(frame.measuredHeight, View.MeasureSpec.EXACTLY));
|
||||||
}
|
}
|
||||||
view.layout(frame.left, frame.top, frame.right, frame.bottom);
|
view.layout(frame.left, frame.top, frame.right, frame.bottom);
|
||||||
|
if (view.getAlpha() != frame.alpha) {
|
||||||
|
view.setAlpha(frame.alpha);
|
||||||
|
}
|
||||||
|
if (view.getVisibility() != frame.visibility) {
|
||||||
|
view.setVisibility(frame.visibility);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1065,7 +1303,9 @@ final class SnapshotRenderer {
|
|||||||
int right,
|
int right,
|
||||||
int bottom,
|
int bottom,
|
||||||
int measuredWidth,
|
int measuredWidth,
|
||||||
int measuredHeight
|
int measuredHeight,
|
||||||
|
int visibility,
|
||||||
|
float alpha
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+7
-2
@@ -21,6 +21,10 @@ final class UnlockedChipPlacementController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void clear() {
|
void clear() {
|
||||||
|
resetMissingChipFallback();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void resetMissingChipFallback() {
|
||||||
heldPlacements = null;
|
heldPlacements = null;
|
||||||
lastCollisionBounds = null;
|
lastCollisionBounds = null;
|
||||||
lastRootWidth = 0;
|
lastRootWidth = 0;
|
||||||
@@ -47,10 +51,11 @@ final class UnlockedChipPlacementController {
|
|||||||
return rawPlacements;
|
return rawPlacements;
|
||||||
}
|
}
|
||||||
|
|
||||||
ArrayList<SnapshotPlacement> placements = isAnyChipHidingEnabled(settings)
|
boolean hasMetricSources = metricSources != null && !metricSources.isEmpty();
|
||||||
|
ArrayList<SnapshotPlacement> placements = isAnyChipHidingEnabled(settings) || !hasMetricSources
|
||||||
? new ArrayList<>()
|
? new ArrayList<>()
|
||||||
: heldPlacements(root);
|
: heldPlacements(root);
|
||||||
if (placements.isEmpty()) {
|
if (placements.isEmpty() && hasMetricSources) {
|
||||||
placements = emptyPlacement(root, metricSources);
|
placements = emptyPlacement(root, metricSources);
|
||||||
}
|
}
|
||||||
return placements;
|
return placements;
|
||||||
|
|||||||
+26
-5
@@ -1,5 +1,7 @@
|
|||||||
package se.ajpanton.statusbartweak.runtime.render;
|
package se.ajpanton.statusbartweak.runtime.render;
|
||||||
|
|
||||||
|
import android.app.KeyguardManager;
|
||||||
|
import android.content.Context;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
@@ -71,6 +73,11 @@ public final class UnlockedIconSnapshotRenderController {
|
|||||||
clearAll();
|
clearAll();
|
||||||
return new RenderResult(true, true, true);
|
return new RenderResult(true, true, true);
|
||||||
}
|
}
|
||||||
|
if (scene.isUnlocked() && isKeyguardLocked(root.getContext())) {
|
||||||
|
clearLockscreenHosts(clockHost, carrierHost, chipHost, statusHost, notificationHost);
|
||||||
|
clearRoot(root);
|
||||||
|
return new RenderResult(true, true, true);
|
||||||
|
}
|
||||||
SbtSettings settings = RuntimeSettingsCache.get(root.getContext());
|
SbtSettings settings = RuntimeSettingsCache.get(root.getContext());
|
||||||
setStatusBarTintTargetEnabled(scene.isUnlocked());
|
setStatusBarTintTargetEnabled(scene.isUnlocked());
|
||||||
layoutHostToRoot(root, clockHost);
|
layoutHostToRoot(root, clockHost);
|
||||||
@@ -96,7 +103,7 @@ public final class UnlockedIconSnapshotRenderController {
|
|||||||
root,
|
root,
|
||||||
settings,
|
settings,
|
||||||
scene,
|
scene,
|
||||||
ViewGeometry.middleCutout(root),
|
EffectiveCutoutSupport.layoutMiddleCutout(root, settings),
|
||||||
anchors,
|
anchors,
|
||||||
previousIcons);
|
previousIcons);
|
||||||
LayoutInputSignature previousSignature = lastLayoutSignatureByRoot.get(root);
|
LayoutInputSignature previousSignature = lastLayoutSignatureByRoot.get(root);
|
||||||
@@ -154,12 +161,12 @@ public final class UnlockedIconSnapshotRenderController {
|
|||||||
root,
|
root,
|
||||||
currentClockLayout,
|
currentClockLayout,
|
||||||
settings,
|
settings,
|
||||||
UnlockedLayoutPlanner.cutoutBounds(ViewGeometry.middleCutout(root), settings.clockMiddleCutoutGapPx));
|
clockCutoutBounds(root, settings));
|
||||||
ClockPlacement textPlacement = clockTextPlacementForPrevious(
|
ClockPlacement textPlacement = clockTextPlacementForPrevious(
|
||||||
root,
|
root,
|
||||||
currentClockLayout,
|
currentClockLayout,
|
||||||
settings,
|
settings,
|
||||||
UnlockedLayoutPlanner.cutoutBounds(ViewGeometry.middleCutout(root), settings.clockMiddleCutoutGapPx),
|
clockCutoutBounds(root, settings),
|
||||||
previousPlan != null ? previousPlan.clockPlacement : null);
|
previousPlan != null ? previousPlan.clockPlacement : null);
|
||||||
ClockPlacement updatedClock = clockPlacementWithUpdatedText(
|
ClockPlacement updatedClock = clockPlacementWithUpdatedText(
|
||||||
previousPlan != null ? previousPlan.clockPlacement : null,
|
previousPlan != null ? previousPlan.clockPlacement : null,
|
||||||
@@ -193,7 +200,7 @@ public final class UnlockedIconSnapshotRenderController {
|
|||||||
root,
|
root,
|
||||||
currentClockLayout,
|
currentClockLayout,
|
||||||
settings,
|
settings,
|
||||||
UnlockedLayoutPlanner.cutoutBounds(ViewGeometry.middleCutout(root), settings.clockMiddleCutoutGapPx));
|
clockCutoutBounds(root, settings));
|
||||||
}
|
}
|
||||||
if (scene.isLockscreen() && clockEnabledForScene(settings, scene) && currentClockLayout == null) {
|
if (scene.isLockscreen() && clockEnabledForScene(settings, scene) && currentClockLayout == null) {
|
||||||
clearLockscreenHosts(clockHost, carrierHost, chipHost, statusHost, notificationHost);
|
clearLockscreenHosts(clockHost, carrierHost, chipHost, statusHost, notificationHost);
|
||||||
@@ -392,6 +399,14 @@ public final class UnlockedIconSnapshotRenderController {
|
|||||||
notificationRenderer.clear(notificationHost);
|
notificationRenderer.clear(notificationHost);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean isKeyguardLocked(Context context) {
|
||||||
|
if (context == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
KeyguardManager keyguardManager = context.getSystemService(KeyguardManager.class);
|
||||||
|
return keyguardManager != null && keyguardManager.isKeyguardLocked();
|
||||||
|
}
|
||||||
|
|
||||||
private void layoutHostToRoot(View root, ViewGroup host) {
|
private void layoutHostToRoot(View root, ViewGroup host) {
|
||||||
if (root == null || host == null || root.getWidth() <= 0 || root.getHeight() <= 0) {
|
if (root == null || host == null || root.getWidth() <= 0 || root.getHeight() <= 0) {
|
||||||
return;
|
return;
|
||||||
@@ -724,7 +739,13 @@ public final class UnlockedIconSnapshotRenderController {
|
|||||||
anchors,
|
anchors,
|
||||||
settings,
|
settings,
|
||||||
scene,
|
scene,
|
||||||
UnlockedLayoutPlanner.cutoutBounds(ViewGeometry.middleCutout(root), settings != null ? settings.clockMiddleCutoutGapPx : 0));
|
clockCutoutBounds(root, settings));
|
||||||
|
}
|
||||||
|
|
||||||
|
private Bounds clockCutoutBounds(View root, SbtSettings settings) {
|
||||||
|
return UnlockedLayoutPlanner.cutoutBounds(
|
||||||
|
EffectiveCutoutSupport.clockMiddleCutout(root, settings),
|
||||||
|
settings != null ? settings.clockMiddleCutoutGapPx : 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ClockLayout buildClockLayout(
|
private ClockLayout buildClockLayout(
|
||||||
|
|||||||
+44
-7
@@ -68,13 +68,14 @@ final class UnlockedLayoutPlanner {
|
|||||||
aodInitialStatusbarHeightByRoot.remove(root);
|
aodInitialStatusbarHeightByRoot.remove(root);
|
||||||
}
|
}
|
||||||
ArrayList<UnlockedLayoutBand> bands = new ArrayList<>();
|
ArrayList<UnlockedLayoutBand> bands = new ArrayList<>();
|
||||||
Rect cutout = ViewGeometry.middleCutout(root);
|
Rect layoutCutout = EffectiveCutoutSupport.layoutMiddleCutout(root, settings);
|
||||||
|
Rect clockCutout = EffectiveCutoutSupport.clockMiddleCutout(root, settings);
|
||||||
Bounds layoutCutoutBounds = scene != null && scene.isAod()
|
Bounds layoutCutoutBounds = scene != null && scene.isAod()
|
||||||
? aodCutoutBounds(root, cutout, settings.layoutPaddingCutoutPx)
|
? aodCutoutBounds(root, layoutCutout, settings.layoutPaddingCutoutPx)
|
||||||
: cutoutBounds(cutout, settings.layoutPaddingCutoutPx);
|
: cutoutBounds(layoutCutout, settings.layoutPaddingCutoutPx);
|
||||||
Bounds clockCutoutBounds = scene != null && scene.isAod()
|
Bounds clockCutoutBounds = scene != null && scene.isAod()
|
||||||
? aodCutoutBounds(root, cutout, settings.clockMiddleCutoutGapPx)
|
? aodCutoutBounds(root, clockCutout, settings.clockMiddleCutoutGapPx)
|
||||||
: cutoutBounds(cutout, settings.clockMiddleCutoutGapPx);
|
: cutoutBounds(clockCutout, settings.clockMiddleCutoutGapPx);
|
||||||
Bounds aodStatusAnchorBounds = scene != null && scene.isAod()
|
Bounds aodStatusAnchorBounds = scene != null && scene.isAod()
|
||||||
? aodStatusAnchorBounds(root, anchors, scene)
|
? aodStatusAnchorBounds(root, anchors, scene)
|
||||||
: null;
|
: null;
|
||||||
@@ -87,7 +88,13 @@ final class UnlockedLayoutPlanner {
|
|||||||
Bounds aodStatusbarBounds = scene != null && scene.isAod()
|
Bounds aodStatusbarBounds = scene != null && scene.isAod()
|
||||||
? aodStatusbarBounds(root, settings, aodNotificationBounds, aodStatusAnchorBounds)
|
? aodStatusbarBounds(root, settings, aodNotificationBounds, aodStatusAnchorBounds)
|
||||||
: null;
|
: null;
|
||||||
LayoutEdges edges = layoutEdges(root, settings, anchors, scene, aodStatusAnchorBounds);
|
LayoutEdges edges = layoutEdges(
|
||||||
|
root,
|
||||||
|
settings,
|
||||||
|
anchors,
|
||||||
|
collectedIcons,
|
||||||
|
scene,
|
||||||
|
aodStatusAnchorBounds);
|
||||||
int statusbarHeight = statusbarHeight(root, anchors, scene, aodStatusContentBounds);
|
int statusbarHeight = statusbarHeight(root, anchors, scene, aodStatusContentBounds);
|
||||||
if (clockEnabledForScene(settings, scene) && clockLayout != null && clockLayout.width() > 0) {
|
if (clockEnabledForScene(settings, scene) && clockLayout != null && clockLayout.width() > 0) {
|
||||||
bands.add(UnlockedLayoutBand.clock(
|
bands.add(UnlockedLayoutBand.clock(
|
||||||
@@ -1112,7 +1119,9 @@ final class UnlockedLayoutPlanner {
|
|||||||
if (bounds == null || bounds.height <= 0) {
|
if (bounds == null || bounds.height <= 0) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
int width = Math.max(1, Math.round((float) bounds.width * targetHeight / bounds.height));
|
int width = isChargingBatteryChipPlacement(placement)
|
||||||
|
? chargingBatteryChipWidth(targetHeight)
|
||||||
|
: Math.max(1, Math.round((float) bounds.width * targetHeight / bounds.height));
|
||||||
int bottom = bounds.top + bounds.height;
|
int bottom = bounds.top + bounds.height;
|
||||||
Bounds resized = new Bounds(bounds.left, bottom - targetHeight, width, targetHeight);
|
Bounds resized = new Bounds(bounds.left, bottom - targetHeight, width, targetHeight);
|
||||||
result.add(new SnapshotPlacement(
|
result.add(new SnapshotPlacement(
|
||||||
@@ -1130,6 +1139,15 @@ final class UnlockedLayoutPlanner {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean isChargingBatteryChipPlacement(SnapshotPlacement placement) {
|
||||||
|
return placement != null
|
||||||
|
&& SnapshotKeys.CHARGING_BATTERY_CHIP.equals(placement.key);
|
||||||
|
}
|
||||||
|
|
||||||
|
private int chargingBatteryChipWidth(int height) {
|
||||||
|
return Math.max(1, Math.round(Math.max(1, height) * 2.7f));
|
||||||
|
}
|
||||||
|
|
||||||
private Bounds sourceRenderBoundsForResize(SnapshotPlacement placement) {
|
private Bounds sourceRenderBoundsForResize(SnapshotPlacement placement) {
|
||||||
if (placement == null || placement.source == null || placement.bounds == null) {
|
if (placement == null || placement.source == null || placement.bounds == null) {
|
||||||
return placement != null ? placement.sourceRenderBounds : null;
|
return placement != null ? placement.sourceRenderBounds : null;
|
||||||
@@ -1381,6 +1399,7 @@ final class UnlockedLayoutPlanner {
|
|||||||
View root,
|
View root,
|
||||||
SbtSettings settings,
|
SbtSettings settings,
|
||||||
RootAnchors anchors,
|
RootAnchors anchors,
|
||||||
|
CollectedIcons collectedIcons,
|
||||||
SceneKey scene,
|
SceneKey scene,
|
||||||
Bounds aodStatusAnchorBounds
|
Bounds aodStatusAnchorBounds
|
||||||
) {
|
) {
|
||||||
@@ -1407,11 +1426,29 @@ final class UnlockedLayoutPlanner {
|
|||||||
if (settings.layoutPaddingRightAddStock) {
|
if (settings.layoutPaddingRightAddStock) {
|
||||||
right -= stockInsets.right;
|
right -= stockInsets.right;
|
||||||
}
|
}
|
||||||
|
if (scene != null && scene.isUnlocked() && hasStatusChipSources(collectedIcons)) {
|
||||||
|
LayoutEdges recentEdges = recentNonAodEdgesByRootWidth.get(rootWidth);
|
||||||
|
if (isReusableUnlockedEdge(left, recentEdges)) {
|
||||||
|
left = recentEdges.left();
|
||||||
|
}
|
||||||
|
}
|
||||||
LayoutEdges edges = new LayoutEdges(left, right);
|
LayoutEdges edges = new LayoutEdges(left, right);
|
||||||
rememberNonAodEdges(rootWidth, scene, edges);
|
rememberNonAodEdges(rootWidth, scene, edges);
|
||||||
return edges;
|
return edges;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean hasStatusChipSources(CollectedIcons icons) {
|
||||||
|
return icons != null
|
||||||
|
&& ((icons.statusChips != null && !icons.statusChips.isEmpty())
|
||||||
|
|| (icons.statusChipMetrics != null && !icons.statusChipMetrics.isEmpty()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isReusableUnlockedEdge(int currentLeft, LayoutEdges recentEdges) {
|
||||||
|
return recentEdges != null
|
||||||
|
&& recentEdges.right() > recentEdges.left()
|
||||||
|
&& Math.abs(currentLeft - recentEdges.left()) <= 8;
|
||||||
|
}
|
||||||
|
|
||||||
private LayoutEdges stableAodEdges(int rootWidth, LayoutEdges aodEdges) {
|
private LayoutEdges stableAodEdges(int rootWidth, LayoutEdges aodEdges) {
|
||||||
if (rootWidth <= 0) {
|
if (rootWidth <= 0) {
|
||||||
return aodEdges;
|
return aodEdges;
|
||||||
|
|||||||
+178
-10
@@ -1,7 +1,10 @@
|
|||||||
package se.ajpanton.statusbartweak.runtime.render;
|
package se.ajpanton.statusbartweak.runtime.render;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
import android.graphics.Rect;
|
import android.graphics.Rect;
|
||||||
import android.graphics.drawable.Drawable;
|
import android.graphics.drawable.Drawable;
|
||||||
|
import android.os.BatteryManager;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.widget.ImageView;
|
import android.widget.ImageView;
|
||||||
@@ -23,6 +26,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.runtime.settings.BroadcastReceiverSupport;
|
||||||
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;
|
||||||
|
|
||||||
@@ -74,6 +78,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);
|
||||||
}
|
}
|
||||||
|
addSamsungBatteryStatusChipSource(root, statusIcons, settings);
|
||||||
selectDualSimStatusSignals(statusIcons, settings);
|
selectDualSimStatusSignals(statusIcons, settings);
|
||||||
normalizeStatusIconSourceBounds(root, statusIcons);
|
normalizeStatusIconSourceBounds(root, statusIcons);
|
||||||
if (scene != null && scene.isAod()) {
|
if (scene != null && scene.isAod()) {
|
||||||
@@ -185,6 +190,7 @@ final class UnlockedStockSourceCollector {
|
|||||||
: 0;
|
: 0;
|
||||||
int statusSourceSignature = sourceLayoutListSignature(
|
int statusSourceSignature = sourceLayoutListSignature(
|
||||||
previousIcons != null ? previousIcons.statusIcons : null);
|
previousIcons != null ? previousIcons.statusIcons : null);
|
||||||
|
statusSourceSignature = 31 * statusSourceSignature + batteryChargingSignature(root);
|
||||||
if (scene != null && scene.isAod()) {
|
if (scene != null && scene.isAod()) {
|
||||||
statusSourceSignature = 31 * statusSourceSignature
|
statusSourceSignature = 31 * statusSourceSignature
|
||||||
+ aodLayoutAnchorSignature(root, anchors, scene);
|
+ aodLayoutAnchorSignature(root, anchors, scene);
|
||||||
@@ -283,7 +289,7 @@ final class UnlockedStockSourceCollector {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (isStatusChipMetricCandidate(root, source.view(), true)
|
if (isStatusChipMetricCandidate(root, source.view(), true)
|
||||||
&& hasVisibleStatusChipContent(source.view())) {
|
&& hasRenderableStatusChipContent(source.view())) {
|
||||||
metricOut.add(source);
|
metricOut.add(source);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -880,6 +886,132 @@ final class UnlockedStockSourceCollector {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void addSamsungBatteryStatusChipSource(
|
||||||
|
View root,
|
||||||
|
ArrayList<SnapshotSource> sources,
|
||||||
|
SbtSettings settings
|
||||||
|
) {
|
||||||
|
if (root == null
|
||||||
|
|| sources == null
|
||||||
|
|| settings == null
|
||||||
|
|| settings.statusChipsHideAll
|
||||||
|
|| settings.statusChipsHideBattery
|
||||||
|
|| !isDevicePlugged(root.getContext())) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
View chip = samsungBatteryStatusChip(root);
|
||||||
|
if (chip == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
removeBatteryStatusSources(sources);
|
||||||
|
Bounds bounds = ViewGeometry.boundsRelativeTo(root, chip);
|
||||||
|
sources.add(new SnapshotSource(
|
||||||
|
chip,
|
||||||
|
SnapshotMode.STATUS_CHIP,
|
||||||
|
SnapshotKeys.CHARGING_BATTERY_CHIP,
|
||||||
|
validBounds(bounds) ? bounds : null));
|
||||||
|
}
|
||||||
|
|
||||||
|
private View samsungBatteryStatusChip(View root) {
|
||||||
|
if (root == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
ArrayDeque<View> queue = new ArrayDeque<>();
|
||||||
|
queue.add(root);
|
||||||
|
while (!queue.isEmpty()) {
|
||||||
|
View view = queue.removeFirst();
|
||||||
|
if (isSamsungBatteryStatusChip(view) && hasUsableSamsungBatteryStatusChipBounds(root, view)) {
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
if (view instanceof ViewGroup group) {
|
||||||
|
for (int i = 0; i < group.getChildCount(); i++) {
|
||||||
|
View child = group.getChildAt(i);
|
||||||
|
if (child != null) {
|
||||||
|
queue.addLast(child);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean hasUsableSamsungBatteryStatusChipBounds(View root, View view) {
|
||||||
|
if (root == null
|
||||||
|
|| view == null
|
||||||
|
|| view == root
|
||||||
|
|| !view.isAttachedToWindow()
|
||||||
|
|| view.getVisibility() != View.VISIBLE
|
||||||
|
|| (view.getAlpha() <= 0f && !hasVisibleStatusChipContent(view))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
Bounds bounds = ViewGeometry.boundsRelativeTo(root, view);
|
||||||
|
return validBounds(bounds)
|
||||||
|
&& bounds.top >= 0
|
||||||
|
&& bounds.top <= root.getHeight()
|
||||||
|
&& bounds.width < root.getWidth() / 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean validBounds(Bounds bounds) {
|
||||||
|
return bounds != null && bounds.width > 0 && bounds.height > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void removeBatteryStatusSources(ArrayList<SnapshotSource> sources) {
|
||||||
|
if (sources == null || sources.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (int i = sources.size() - 1; i >= 0; i--) {
|
||||||
|
SnapshotSource source = sources.get(i);
|
||||||
|
if (isBatteryStatusSource(source)) {
|
||||||
|
sources.remove(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isBatteryStatusSource(SnapshotSource source) {
|
||||||
|
View view = source != null ? source.view() : null;
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
append(sb, source != null ? source.keyHint() : null);
|
||||||
|
append(sb, reflectString(view, "getSlot", "mSlot", "slot"));
|
||||||
|
append(sb, view != null ? ViewIdNames.idName(view) : null);
|
||||||
|
append(sb, view != null ? view.getClass().getName() : null);
|
||||||
|
return sb.toString().toLowerCase(Locale.ROOT).contains("battery");
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isSamsungBatteryStatusChip(View view) {
|
||||||
|
return view != null && view.getClass().getName().contains("SamsungBatteryStatusChip");
|
||||||
|
}
|
||||||
|
|
||||||
|
private int batteryChargingSignature(View root) {
|
||||||
|
return root != null && isDevicePlugged(root.getContext()) ? 1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isDevicePlugged(Context context) {
|
||||||
|
Intent intent = batteryIntent(context);
|
||||||
|
if (intent == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
int status = intent.getIntExtra(BatteryManager.EXTRA_STATUS, -1);
|
||||||
|
return intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0) != 0
|
||||||
|
|| status == BatteryManager.BATTERY_STATUS_CHARGING
|
||||||
|
|| status == BatteryManager.BATTERY_STATUS_FULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Intent batteryIntent(Context context) {
|
||||||
|
if (context == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return BroadcastReceiverSupport.readExportedSticky(
|
||||||
|
context,
|
||||||
|
BroadcastReceiverSupport.filter(Intent.ACTION_BATTERY_CHANGED));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void append(StringBuilder sb, String value) {
|
||||||
|
if (sb == null || value == null || value.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
sb.append(';').append(value);
|
||||||
|
}
|
||||||
|
|
||||||
private int normalizedStatusIconWidth(int drawableWidth, int iconHeight) {
|
private int normalizedStatusIconWidth(int drawableWidth, int iconHeight) {
|
||||||
return Math.max(1, drawableWidth + statusIconHorizontalPadding(iconHeight));
|
return Math.max(1, drawableWidth + statusIconHorizontalPadding(iconHeight));
|
||||||
}
|
}
|
||||||
@@ -1056,11 +1188,12 @@ final class UnlockedStockSourceCollector {
|
|||||||
View view = source.view();
|
View view = source.view();
|
||||||
if (view == null
|
if (view == null
|
||||||
|| view == root
|
|| view == root
|
||||||
|
|| view.getVisibility() != View.VISIBLE
|
||||||
|| !view.isAttachedToWindow()
|
|| !view.isAttachedToWindow()
|
||||||
|| !ViewGeometry.isDescendantOf(view, root)
|
|| !ViewGeometry.isDescendantOf(view, root)
|
||||||
|| ReflectionSupport.getBooleanField(view, "sbtStatusChipForcedHidden", false)
|
|| ReflectionSupport.getBooleanField(view, "sbtStatusChipForcedHidden", false)
|
||||||
|| !isStatusChipCandidate(view)
|
|| !isStatusChipCandidate(view)
|
||||||
|| !hasVisibleStatusChipContent(view)) {
|
|| !hasRenderableStatusChipContent(view)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
Bounds bounds = source.boundsOverride() != null
|
Bounds bounds = source.boundsOverride() != null
|
||||||
@@ -1078,7 +1211,7 @@ final class UnlockedStockSourceCollector {
|
|||||||
if (view == null || view == root || view.getVisibility() != View.VISIBLE) {
|
if (view == null || view == root || view.getVisibility() != View.VISIBLE) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!allowHidden && view.getAlpha() <= 0f && !hasVisibleStatusChipContent(view)) {
|
if (!allowHidden && view.getAlpha() <= 0f && !hasRenderableStatusChipContent(view)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (ReflectionSupport.getBooleanField(view, "sbtStatusChipForcedHidden", false)) {
|
if (ReflectionSupport.getBooleanField(view, "sbtStatusChipForcedHidden", false)) {
|
||||||
@@ -1093,7 +1226,7 @@ final class UnlockedStockSourceCollector {
|
|||||||
if (bounds == null || bounds.top < 0 || bounds.top > root.getHeight()) {
|
if (bounds == null || bounds.top < 0 || bounds.top > root.getHeight()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!isStatusChipCandidate(view)) {
|
if (!isStatusChipCandidate(view) || !hasRenderableStatusChipContent(view)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
Object parent = view.getParent();
|
Object parent = view.getParent();
|
||||||
@@ -1106,6 +1239,39 @@ final class UnlockedStockSourceCollector {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean hasRenderableStatusChipContent(View source) {
|
||||||
|
if (!(source instanceof ViewGroup group)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
ArrayDeque<View> queue = new ArrayDeque<>();
|
||||||
|
for (int i = 0; i < group.getChildCount(); i++) {
|
||||||
|
queue.addLast(group.getChildAt(i));
|
||||||
|
}
|
||||||
|
while (!queue.isEmpty()) {
|
||||||
|
View view = queue.removeFirst();
|
||||||
|
if (view == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (view instanceof TextView textView
|
||||||
|
&& view.getVisibility() == View.VISIBLE
|
||||||
|
&& view.getAlpha() > 0f
|
||||||
|
&& textView.getText() != null
|
||||||
|
&& textView.getText().length() > 0) {
|
||||||
|
int width = view.getWidth() > 0 ? view.getWidth() : view.getMeasuredWidth();
|
||||||
|
int height = view.getHeight() > 0 ? view.getHeight() : view.getMeasuredHeight();
|
||||||
|
if (width > 0 && height > 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (view instanceof ViewGroup childGroup) {
|
||||||
|
for (int i = 0; i < childGroup.getChildCount(); i++) {
|
||||||
|
queue.addLast(childGroup.getChildAt(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
private boolean hasVisibleStatusChipContent(View source) {
|
private boolean hasVisibleStatusChipContent(View source) {
|
||||||
if (!(source instanceof ViewGroup group)) {
|
if (!(source instanceof ViewGroup group)) {
|
||||||
return false;
|
return false;
|
||||||
@@ -1136,10 +1302,10 @@ final class UnlockedStockSourceCollector {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private boolean isStatusChipMetricCandidate(View root, View view, boolean allowHidden) {
|
private boolean isStatusChipMetricCandidate(View root, View view, boolean allowHidden) {
|
||||||
if (view == null || view == root || root == null) {
|
if (view == null || view == root || root == null || view.getVisibility() != View.VISIBLE) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!allowHidden && view.getAlpha() <= 0f && !hasVisibleStatusChipContent(view)) {
|
if (!allowHidden && view.getAlpha() <= 0f && !hasRenderableStatusChipContent(view)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
int width = view.getWidth() > 0 ? view.getWidth() : view.getMeasuredWidth();
|
int width = view.getWidth() > 0 ? view.getWidth() : view.getMeasuredWidth();
|
||||||
@@ -1151,7 +1317,7 @@ final class UnlockedStockSourceCollector {
|
|||||||
if (bounds == null || bounds.top < 0 || bounds.top > root.getHeight()) {
|
if (bounds == null || bounds.top < 0 || bounds.top > root.getHeight()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!isStatusChipCandidate(view)) {
|
if (!isStatusChipCandidate(view) || !hasRenderableStatusChipContent(view)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
Object parent = view.getParent();
|
Object parent = view.getParent();
|
||||||
@@ -1350,7 +1516,7 @@ final class UnlockedStockSourceCollector {
|
|||||||
queue.add(root);
|
queue.add(root);
|
||||||
while (!queue.isEmpty()) {
|
while (!queue.isEmpty()) {
|
||||||
View view = queue.removeFirst();
|
View view = queue.removeFirst();
|
||||||
if (view != root && isStatusChipCandidate(view) && hasVisibleStatusChipContent(view)) {
|
if (view != root && isStatusChipCandidate(view) && hasRenderableStatusChipContent(view)) {
|
||||||
result = 31 * result + chipViewLayoutSignature(view);
|
result = 31 * result + chipViewLayoutSignature(view);
|
||||||
}
|
}
|
||||||
if (view instanceof ViewGroup group) {
|
if (view instanceof ViewGroup group) {
|
||||||
@@ -1370,7 +1536,7 @@ final class UnlockedStockSourceCollector {
|
|||||||
result = 31 * result + sources.size();
|
result = 31 * result + sources.size();
|
||||||
for (SnapshotSource source : sources) {
|
for (SnapshotSource source : sources) {
|
||||||
result = 31 * result + source.mode().ordinal();
|
result = 31 * result + source.mode().ordinal();
|
||||||
result = 31 * result + (hasVisibleStatusChipContent(source.view()) ? 1 : 0);
|
result = 31 * result + (hasRenderableStatusChipContent(source.view()) ? 1 : 0);
|
||||||
result = 31 * result + chipViewLayoutSignature(source.view());
|
result = 31 * result + chipViewLayoutSignature(source.view());
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
@@ -1494,6 +1660,7 @@ final class UnlockedStockSourceCollector {
|
|||||||
settings.clockCustomFormatEnabled,
|
settings.clockCustomFormatEnabled,
|
||||||
settings.clockCustomFormat,
|
settings.clockCustomFormat,
|
||||||
settings.clockIgnoreUnderDisplayCameras,
|
settings.clockIgnoreUnderDisplayCameras,
|
||||||
|
settings.clockCameraTypeOverrides,
|
||||||
settings.layoutPaddingCutoutPx,
|
settings.layoutPaddingCutoutPx,
|
||||||
settings.layoutPaddingLeftPx,
|
settings.layoutPaddingLeftPx,
|
||||||
settings.layoutPaddingRightPx,
|
settings.layoutPaddingRightPx,
|
||||||
@@ -1556,7 +1723,8 @@ final class UnlockedStockSourceCollector {
|
|||||||
settings.statusChipsHideAll,
|
settings.statusChipsHideAll,
|
||||||
settings.statusChipsHideMediaUnlocked,
|
settings.statusChipsHideMediaUnlocked,
|
||||||
settings.statusChipsHideNavUnlocked,
|
settings.statusChipsHideNavUnlocked,
|
||||||
settings.statusChipsHideCallUnlocked);
|
settings.statusChipsHideCallUnlocked,
|
||||||
|
settings.statusChipsHideBattery);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -417,7 +417,7 @@ public final class AppIconRules {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
String[] parts = entry.split("\\|", -1);
|
String[] parts = entry.split("\\|", -1);
|
||||||
if (parts.length != 2 && parts.length != 4) {
|
if (parts.length != 4) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
String pkg = parts[0];
|
String pkg = parts[0];
|
||||||
@@ -429,13 +429,8 @@ public final class AppIconRules {
|
|||||||
int lastSeenOffsetMinutes;
|
int lastSeenOffsetMinutes;
|
||||||
try {
|
try {
|
||||||
firstSeen = Long.parseLong(parts[1]);
|
firstSeen = Long.parseLong(parts[1]);
|
||||||
if (parts.length == 4) {
|
|
||||||
lastSeen = Long.parseLong(parts[2]);
|
lastSeen = Long.parseLong(parts[2]);
|
||||||
lastSeenOffsetMinutes = Integer.parseInt(parts[3]);
|
lastSeenOffsetMinutes = Integer.parseInt(parts[3]);
|
||||||
} else {
|
|
||||||
lastSeen = firstSeen;
|
|
||||||
lastSeenOffsetMinutes = currentTimezoneOffsetMinutes();
|
|
||||||
}
|
|
||||||
} catch (NumberFormatException ignored) {
|
} catch (NumberFormatException ignored) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|||||||
+37
-4
@@ -21,11 +21,28 @@ public final class BatteryBarGeometry {
|
|||||||
public final String position;
|
public final String position;
|
||||||
public final String alignment;
|
public final String alignment;
|
||||||
public final int thicknessDp;
|
public final int thicknessDp;
|
||||||
|
public final int edgeOffsetDp;
|
||||||
|
public final String curvedGeometryMode;
|
||||||
|
|
||||||
public BatteryBarGeometry(
|
public BatteryBarGeometry(
|
||||||
String position,
|
String position,
|
||||||
String alignment,
|
String alignment,
|
||||||
int thicknessDp
|
int thicknessDp
|
||||||
|
) {
|
||||||
|
this(
|
||||||
|
position,
|
||||||
|
alignment,
|
||||||
|
thicknessDp,
|
||||||
|
SbtDefaults.BATTERY_BAR_EDGE_OFFSET_DP_DEFAULT,
|
||||||
|
SbtDefaults.BATTERY_BAR_CURVED_GEOMETRY_MODE_DEFAULT);
|
||||||
|
}
|
||||||
|
|
||||||
|
public BatteryBarGeometry(
|
||||||
|
String position,
|
||||||
|
String alignment,
|
||||||
|
int thicknessDp,
|
||||||
|
int edgeOffsetDp,
|
||||||
|
String curvedGeometryMode
|
||||||
) {
|
) {
|
||||||
this.position = sanitizePosition(position);
|
this.position = sanitizePosition(position);
|
||||||
this.alignment = sanitizeAlignment(alignment);
|
this.alignment = sanitizeAlignment(alignment);
|
||||||
@@ -33,6 +50,11 @@ public final class BatteryBarGeometry {
|
|||||||
thicknessDp,
|
thicknessDp,
|
||||||
SbtDefaults.BATTERY_BAR_THICKNESS_DP_MIN,
|
SbtDefaults.BATTERY_BAR_THICKNESS_DP_MIN,
|
||||||
SbtDefaults.BATTERY_BAR_THICKNESS_DP_MAX);
|
SbtDefaults.BATTERY_BAR_THICKNESS_DP_MAX);
|
||||||
|
this.edgeOffsetDp = clamp(
|
||||||
|
edgeOffsetDp,
|
||||||
|
SbtDefaults.BATTERY_BAR_EDGE_OFFSET_DP_MIN,
|
||||||
|
SbtDefaults.BATTERY_BAR_EDGE_OFFSET_DP_MAX);
|
||||||
|
this.curvedGeometryMode = sanitizeCurvedGeometryMode(curvedGeometryMode);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static BatteryBarGeometry global(SbtSettings settings) {
|
public static BatteryBarGeometry global(SbtSettings settings) {
|
||||||
@@ -45,7 +67,9 @@ public final class BatteryBarGeometry {
|
|||||||
return new BatteryBarGeometry(
|
return new BatteryBarGeometry(
|
||||||
settings.batteryBarPosition,
|
settings.batteryBarPosition,
|
||||||
settings.batteryBarAlignment,
|
settings.batteryBarAlignment,
|
||||||
settings.batteryBarThicknessDp);
|
settings.batteryBarThicknessDp,
|
||||||
|
settings.batteryBarEdgeOffsetDp,
|
||||||
|
settings.batteryBarCurvedGeometryMode);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static BatteryBarGeometry resolve(
|
public static BatteryBarGeometry resolve(
|
||||||
@@ -68,17 +92,19 @@ public final class BatteryBarGeometry {
|
|||||||
return safeFallback;
|
return safeFallback;
|
||||||
}
|
}
|
||||||
String[] parts = value.split("\\|", -1);
|
String[] parts = value.split("\\|", -1);
|
||||||
if (parts.length != 3 && parts.length != 4) {
|
if (parts.length != 5) {
|
||||||
return safeFallback;
|
return safeFallback;
|
||||||
}
|
}
|
||||||
return new BatteryBarGeometry(
|
return new BatteryBarGeometry(
|
||||||
parts[0],
|
parts[0],
|
||||||
parts[1],
|
parts[1],
|
||||||
parseInt(parts[2], safeFallback.thicknessDp));
|
parseInt(parts[2], safeFallback.thicknessDp),
|
||||||
|
parseInt(parts[3], safeFallback.edgeOffsetDp),
|
||||||
|
parts[4]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String encode() {
|
public String encode() {
|
||||||
return position + "|" + alignment + "|" + thicknessDp;
|
return position + "|" + alignment + "|" + thicknessDp + "|" + edgeOffsetDp + "|" + curvedGeometryMode;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String signature() {
|
public String signature() {
|
||||||
@@ -104,6 +130,13 @@ public final class BatteryBarGeometry {
|
|||||||
return "ltr";
|
return "ltr";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static String sanitizeCurvedGeometryMode(String mode) {
|
||||||
|
if ("length".equals(mode)) {
|
||||||
|
return "length";
|
||||||
|
}
|
||||||
|
return "off";
|
||||||
|
}
|
||||||
|
|
||||||
private static int parseInt(String value, int fallback) {
|
private static int parseInt(String value, int fallback) {
|
||||||
try {
|
try {
|
||||||
return Integer.parseInt(value);
|
return Integer.parseInt(value);
|
||||||
|
|||||||
+29
-8
@@ -29,6 +29,9 @@ public final class ClockCameraTypeSupport {
|
|||||||
public static final class CameraInfo {
|
public static final class CameraInfo {
|
||||||
public int semDisplayDeviceType = Integer.MIN_VALUE;
|
public int semDisplayDeviceType = Integer.MIN_VALUE;
|
||||||
public int fillUdcDisplayCutout = Integer.MIN_VALUE;
|
public int fillUdcDisplayCutout = Integer.MIN_VALUE;
|
||||||
|
public int rootWidth = Integer.MIN_VALUE;
|
||||||
|
public int rootHeight = Integer.MIN_VALUE;
|
||||||
|
public int rotation = Integer.MIN_VALUE;
|
||||||
public boolean isUdcMainDisplay;
|
public boolean isUdcMainDisplay;
|
||||||
public String cutoutType;
|
public String cutoutType;
|
||||||
public String cutoutString;
|
public String cutoutString;
|
||||||
@@ -59,8 +62,15 @@ public final class ClockCameraTypeSupport {
|
|||||||
: "blocking cutout";
|
: "blocking cutout";
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String buildCameraId(int semDisplayDeviceType, String cutoutType, String cutoutString) {
|
private static String buildCameraId(int semDisplayDeviceType,
|
||||||
|
int rootWidth,
|
||||||
|
int rootHeight,
|
||||||
|
int rotation,
|
||||||
|
String cutoutType,
|
||||||
|
String cutoutString) {
|
||||||
String descriptor = semDisplayDeviceType + "|"
|
String descriptor = semDisplayDeviceType + "|"
|
||||||
|
+ rootWidth + "x" + rootHeight + "|"
|
||||||
|
+ rotation + "|"
|
||||||
+ safe(cutoutType) + "|"
|
+ safe(cutoutType) + "|"
|
||||||
+ safe(cutoutString);
|
+ safe(cutoutString);
|
||||||
return "cam_" + shortSha1(descriptor);
|
return "cam_" + shortSha1(descriptor);
|
||||||
@@ -78,6 +88,20 @@ public final class ClockCameraTypeSupport {
|
|||||||
finalizeCameraInfo(info, settings);
|
finalizeCameraInfo(info, settings);
|
||||||
return info;
|
return info;
|
||||||
}
|
}
|
||||||
|
Display display = root.getDisplay();
|
||||||
|
if (display != null) {
|
||||||
|
info.rotation = display.getRotation();
|
||||||
|
}
|
||||||
|
int width = root.getWidth();
|
||||||
|
if (width <= 0 && root.getRootView() != null) {
|
||||||
|
width = root.getRootView().getWidth();
|
||||||
|
}
|
||||||
|
info.rootWidth = width;
|
||||||
|
int height = root.getHeight();
|
||||||
|
if (height <= 0 && root.getRootView() != null) {
|
||||||
|
height = root.getRootView().getHeight();
|
||||||
|
}
|
||||||
|
info.rootHeight = height;
|
||||||
info.semDisplayDeviceType = readSemDisplayDeviceType(context);
|
info.semDisplayDeviceType = readSemDisplayDeviceType(context);
|
||||||
info.fillUdcDisplayCutout = readGlobalInt(context, "fill_udc_display_cutout");
|
info.fillUdcDisplayCutout = readGlobalInt(context, "fill_udc_display_cutout");
|
||||||
Object displayLifecycle = resolveDisplayLifecycle(context);
|
Object displayLifecycle = resolveDisplayLifecycle(context);
|
||||||
@@ -93,15 +117,9 @@ public final class ClockCameraTypeSupport {
|
|||||||
finalizeCameraInfo(info, settings);
|
finalizeCameraInfo(info, settings);
|
||||||
return info;
|
return info;
|
||||||
}
|
}
|
||||||
|
|
||||||
Object input = newInstance(inputClass, context);
|
Object input = newInstance(inputClass, context);
|
||||||
Display display = root.getDisplay();
|
|
||||||
if (display != null) {
|
if (display != null) {
|
||||||
setIntField(input, "rotation", display.getRotation());
|
setIntField(input, "rotation", info.rotation);
|
||||||
}
|
|
||||||
int width = root.getWidth();
|
|
||||||
if (width <= 0 && root.getRootView() != null) {
|
|
||||||
width = root.getRootView().getWidth();
|
|
||||||
}
|
}
|
||||||
if (width > 0) {
|
if (width > 0) {
|
||||||
setIntField(input, "statusBarWidth", width);
|
setIntField(input, "statusBarWidth", width);
|
||||||
@@ -196,6 +214,9 @@ public final class ClockCameraTypeSupport {
|
|||||||
}
|
}
|
||||||
info.cameraId = buildCameraId(
|
info.cameraId = buildCameraId(
|
||||||
info.semDisplayDeviceType,
|
info.semDisplayDeviceType,
|
||||||
|
info.rootWidth,
|
||||||
|
info.rootHeight,
|
||||||
|
info.rotation,
|
||||||
info.cutoutType,
|
info.cutoutType,
|
||||||
info.cutoutString);
|
info.cutoutString);
|
||||||
boolean explicitUdc = info.isUdcMainDisplay || "UDC".equals(info.cutoutType);
|
boolean explicitUdc = info.isUdcMainDisplay || "UDC".equals(info.cutoutType);
|
||||||
|
|||||||
+53
-342
@@ -1,394 +1,105 @@
|
|||||||
package se.ajpanton.statusbartweak.shell.settings;
|
package se.ajpanton.statusbartweak.shell.settings;
|
||||||
|
|
||||||
import android.graphics.Color;
|
import android.graphics.Color;
|
||||||
import android.text.TextUtils;
|
|
||||||
|
|
||||||
import java.util.Locale;
|
import se.ajpanton.statusbartweak.runtime.features.clock.ClockColorResolver;
|
||||||
import java.util.regex.Matcher;
|
|
||||||
import java.util.regex.Pattern;
|
|
||||||
|
|
||||||
public final class ColourExpressionSupport {
|
public final class ColourExpressionSupport {
|
||||||
private static final Pattern OPERATION_PATTERN =
|
private static final ClockColorResolver RESOLVER = new ClockColorResolver();
|
||||||
Pattern.compile("([+\\-*/])\\s*(" + hexOperandPattern() + "|\\d+(?:\\.\\d+)?|\\.\\d+)");
|
private static final ClockColorResolver.Options OPTIONS = ClockColorResolver.Options.batteryBar();
|
||||||
private static final Pattern LEADING_HEX_PATTERN =
|
|
||||||
Pattern.compile("(?i)^#?(?:[0-9a-f]{8}|[0-9a-f]{6}|[0-9a-f]{4}|[0-9a-f]{3})");
|
|
||||||
|
|
||||||
private ColourExpressionSupport() {
|
private ColourExpressionSupport() {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String normaliseExpression(String raw, boolean assumeHashPrefix) {
|
public static String normaliseExpression(String raw, boolean assumeHashPrefix) {
|
||||||
if (raw == null) {
|
String value = normaliseSeparators(raw);
|
||||||
return "";
|
|
||||||
}
|
|
||||||
String value = raw.trim();
|
|
||||||
if (value.isEmpty()) {
|
if (value.isEmpty()) {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
int variantSplit = findVariantSplit(value);
|
try {
|
||||||
String left = value;
|
RESOLVER.resolveColor(null, value, Color.WHITE, Color.WHITE, OPTIONS);
|
||||||
String right = null;
|
RESOLVER.resolveColor(null, value, Color.BLACK, Color.WHITE, OPTIONS);
|
||||||
if (variantSplit >= 0) {
|
return value;
|
||||||
left = value.substring(0, variantSplit).trim();
|
} catch (IllegalArgumentException ignored) {
|
||||||
right = value.substring(variantSplit + 1).trim();
|
|
||||||
}
|
|
||||||
if (!isValidSegment(left, assumeHashPrefix, false, Color.WHITE)) {
|
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
if (right != null && !isValidSegment(right, assumeHashPrefix, true, Color.WHITE)) {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
if (right == null) {
|
|
||||||
return left;
|
|
||||||
}
|
|
||||||
return left + "|" + right;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int resolveColor(String raw,
|
public static int resolveColor(
|
||||||
|
String raw,
|
||||||
int referenceColor,
|
int referenceColor,
|
||||||
int fallbackColor,
|
int fallbackColor,
|
||||||
boolean assumeHashPrefix) {
|
boolean assumeHashPrefix
|
||||||
String value = normaliseExpression(raw, assumeHashPrefix);
|
) {
|
||||||
|
String value = normaliseSeparators(raw);
|
||||||
if (value.isEmpty()) {
|
if (value.isEmpty()) {
|
||||||
return fallbackColor;
|
return fallbackColor;
|
||||||
}
|
}
|
||||||
int variantSplit = findVariantSplit(value);
|
try {
|
||||||
if (variantSplit < 0) {
|
return RESOLVER.resolveColor(null, value, referenceColor, fallbackColor, OPTIONS);
|
||||||
return resolveSegment(value, fallbackColor, assumeHashPrefix, false);
|
} catch (IllegalArgumentException ignored) {
|
||||||
|
return fallbackColor;
|
||||||
}
|
}
|
||||||
String left = value.substring(0, variantSplit).trim();
|
|
||||||
String right = value.substring(variantSplit + 1).trim();
|
|
||||||
int brightVariant = resolveSegment(left, fallbackColor, assumeHashPrefix, false);
|
|
||||||
if (!isDark(referenceColor)) {
|
|
||||||
return brightVariant;
|
|
||||||
}
|
|
||||||
return resolveSegment(right, brightVariant, assumeHashPrefix, true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String resolveColorHex(String raw,
|
public static String resolveColorHex(
|
||||||
|
String raw,
|
||||||
int referenceColor,
|
int referenceColor,
|
||||||
int fallbackColor,
|
int fallbackColor,
|
||||||
boolean assumeHashPrefix) {
|
boolean assumeHashPrefix
|
||||||
int resolved = resolveColor(raw, referenceColor, fallbackColor, assumeHashPrefix);
|
) {
|
||||||
int alpha = Color.alpha(resolved);
|
String value = normaliseSeparators(raw);
|
||||||
if (alpha >= 255) {
|
if (value.isEmpty()) {
|
||||||
return String.format(Locale.ROOT, "#%06X", resolved & 0xFFFFFF);
|
return hexFor(fallbackColor);
|
||||||
}
|
|
||||||
return String.format(Locale.ROOT, "#%08X", resolved);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static boolean isValidSegment(String segment,
|
|
||||||
boolean assumeHashPrefix,
|
|
||||||
boolean allowInheritedBase,
|
|
||||||
int baseColor) {
|
|
||||||
if (TextUtils.isEmpty(segment)) {
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
resolveSegment(segment, baseColor, assumeHashPrefix, allowInheritedBase);
|
return RESOLVER.resolveColorHex(null, value, referenceColor, fallbackColor, OPTIONS);
|
||||||
return true;
|
|
||||||
} catch (IllegalArgumentException ignored) {
|
} catch (IllegalArgumentException ignored) {
|
||||||
return false;
|
return hexFor(fallbackColor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int resolveSegment(String segment,
|
private static String normaliseSeparators(String raw) {
|
||||||
int baseColor,
|
|
||||||
boolean assumeHashPrefix,
|
|
||||||
boolean allowInheritedBase) {
|
|
||||||
String value = segment != null ? segment.trim() : "";
|
|
||||||
if (value.isEmpty()) {
|
|
||||||
throw new IllegalArgumentException("Empty colour segment");
|
|
||||||
}
|
|
||||||
int index = 0;
|
|
||||||
boolean invert = false;
|
|
||||||
if (value.regionMatches(true, 0, "inv", 0, 3)) {
|
|
||||||
invert = true;
|
|
||||||
index = 3;
|
|
||||||
}
|
|
||||||
double[] channels;
|
|
||||||
double alpha;
|
|
||||||
String remainder;
|
|
||||||
if (startsWithOperator(value, index)) {
|
|
||||||
if (!allowInheritedBase) {
|
|
||||||
throw new IllegalArgumentException("Missing starting colour");
|
|
||||||
}
|
|
||||||
channels = channelsFor(baseColor);
|
|
||||||
alpha = Color.alpha(baseColor);
|
|
||||||
remainder = value.substring(index).trim();
|
|
||||||
} else {
|
|
||||||
LeadingColour leading = parseLeadingColour(value, index, assumeHashPrefix);
|
|
||||||
channels = channelsFor(leading.color);
|
|
||||||
alpha = Color.alpha(leading.color);
|
|
||||||
remainder = value.substring(leading.endIndex).trim();
|
|
||||||
}
|
|
||||||
if (invert) {
|
|
||||||
invertChannels(channels);
|
|
||||||
}
|
|
||||||
alpha = applyOperations(channels, alpha, remainder);
|
|
||||||
return colorFromChannels(channels, alpha);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static LeadingColour parseLeadingColour(String value,
|
|
||||||
int start,
|
|
||||||
boolean assumeHashPrefix) {
|
|
||||||
String remainder = value.substring(start).trim();
|
|
||||||
Matcher matcher = LEADING_HEX_PATTERN.matcher(remainder);
|
|
||||||
if (!matcher.find()) {
|
|
||||||
throw new IllegalArgumentException("Missing starting colour");
|
|
||||||
}
|
|
||||||
String token = matcher.group();
|
|
||||||
if (assumeHashPrefix && token.charAt(0) != '#') {
|
|
||||||
token = "#" + token;
|
|
||||||
}
|
|
||||||
int color = parseHexColor(token, assumeHashPrefix);
|
|
||||||
return new LeadingColour(color, start + matcher.end());
|
|
||||||
}
|
|
||||||
|
|
||||||
private static double applyOperations(double[] channels, double alpha, String remainder) {
|
|
||||||
int index = 0;
|
|
||||||
while (index < remainder.length()) {
|
|
||||||
while (index < remainder.length() && Character.isWhitespace(remainder.charAt(index))) {
|
|
||||||
index++;
|
|
||||||
}
|
|
||||||
if (index >= remainder.length()) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
Matcher matcher = OPERATION_PATTERN.matcher(remainder);
|
|
||||||
matcher.region(index, remainder.length());
|
|
||||||
if (!matcher.lookingAt()) {
|
|
||||||
throw new IllegalArgumentException("Invalid colour operation");
|
|
||||||
}
|
|
||||||
char operator = matcher.group(1).charAt(0);
|
|
||||||
String operandText = matcher.group(2);
|
|
||||||
alpha = applyOperation(channels, alpha, operator, operandText);
|
|
||||||
index = matcher.end();
|
|
||||||
}
|
|
||||||
return alpha;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static double applyOperation(double[] channels, double alpha, char operator, String operandText) {
|
|
||||||
Operand operand = parseOperand(operandText);
|
|
||||||
for (int i = 0; i < 3; i++) {
|
|
||||||
double value = channels[i];
|
|
||||||
double rhs = operand.rgb[i];
|
|
||||||
if (operand.isHex && (operator == '*' || operator == '/')) {
|
|
||||||
rhs = rhs / 255d;
|
|
||||||
}
|
|
||||||
switch (operator) {
|
|
||||||
case '+':
|
|
||||||
channels[i] = value + rhs;
|
|
||||||
break;
|
|
||||||
case '-':
|
|
||||||
channels[i] = value - rhs;
|
|
||||||
break;
|
|
||||||
case '*':
|
|
||||||
channels[i] = value * rhs;
|
|
||||||
break;
|
|
||||||
case '/':
|
|
||||||
channels[i] = rhs == 0d ? value : value / rhs;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new IllegalArgumentException("Unknown operator");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (operand.hasAlpha) {
|
|
||||||
double rhs = operand.alpha;
|
|
||||||
if (operand.isHex && (operator == '*' || operator == '/')) {
|
|
||||||
rhs = rhs / 255d;
|
|
||||||
}
|
|
||||||
switch (operator) {
|
|
||||||
case '+':
|
|
||||||
alpha = alpha + rhs;
|
|
||||||
break;
|
|
||||||
case '-':
|
|
||||||
alpha = alpha - rhs;
|
|
||||||
break;
|
|
||||||
case '*':
|
|
||||||
alpha = alpha * rhs;
|
|
||||||
break;
|
|
||||||
case '/':
|
|
||||||
alpha = rhs == 0d ? alpha : alpha / rhs;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new IllegalArgumentException("Unknown operator");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return alpha;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Operand parseOperand(String operandText) {
|
|
||||||
String value = operandText != null ? operandText.trim() : "";
|
|
||||||
if (value.isEmpty()) {
|
|
||||||
throw new IllegalArgumentException("Missing operand");
|
|
||||||
}
|
|
||||||
if (value.charAt(0) == '#') {
|
|
||||||
return parseHexOperand(value);
|
|
||||||
}
|
|
||||||
double scalar;
|
|
||||||
try {
|
|
||||||
scalar = Double.parseDouble(value);
|
|
||||||
} catch (NumberFormatException e) {
|
|
||||||
throw new IllegalArgumentException("Invalid operand", e);
|
|
||||||
}
|
|
||||||
return new Operand(new double[] { scalar, scalar, scalar }, 0d, false, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int parseHexColor(String raw, boolean assumeHashPrefix) {
|
|
||||||
String value = raw != null ? raw.trim() : "";
|
String value = raw != null ? raw.trim() : "";
|
||||||
if (value.isEmpty()) {
|
if (value.isEmpty()) {
|
||||||
throw new IllegalArgumentException("Empty colour");
|
return "";
|
||||||
}
|
}
|
||||||
if (assumeHashPrefix && value.charAt(0) != '#') {
|
int split = findTopLevelPipe(value);
|
||||||
value = "#" + value;
|
if (split < 0) {
|
||||||
}
|
return value;
|
||||||
if (value.charAt(0) != '#') {
|
|
||||||
throw new IllegalArgumentException("Expected colour literal");
|
|
||||||
}
|
|
||||||
String hex = value.substring(1);
|
|
||||||
if (hex.length() == 3) {
|
|
||||||
hex = expandShortHex(hex);
|
|
||||||
} else if (hex.length() == 4) {
|
|
||||||
hex = expandShortHex(hex);
|
|
||||||
} else if (hex.length() != 6 && hex.length() != 8) {
|
|
||||||
throw new IllegalArgumentException("Unsupported colour length");
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
return Color.parseColor("#" + hex);
|
|
||||||
} catch (IllegalArgumentException e) {
|
|
||||||
throw new IllegalArgumentException("Invalid colour", e);
|
|
||||||
}
|
}
|
||||||
|
return value.substring(0, split).trim() + " ; " + value.substring(split + 1).trim();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String expandShortHex(String hex) {
|
private static int findTopLevelPipe(String value) {
|
||||||
StringBuilder out = new StringBuilder(hex.length() * 2);
|
boolean inVector = false;
|
||||||
for (int i = 0; i < hex.length(); i++) {
|
int split = -1;
|
||||||
char c = hex.charAt(i);
|
|
||||||
out.append(c).append(c);
|
|
||||||
}
|
|
||||||
return out.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static double[] channelsFor(int color) {
|
|
||||||
return new double[] {
|
|
||||||
Color.red(color),
|
|
||||||
Color.green(color),
|
|
||||||
Color.blue(color)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void invertChannels(double[] channels) {
|
|
||||||
for (int i = 0; i < 3; i++) {
|
|
||||||
channels[i] = 255d - channels[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int colorFromChannels(double[] channels, double alpha) {
|
|
||||||
int red = clampChannel(channels[0]);
|
|
||||||
int green = clampChannel(channels[1]);
|
|
||||||
int blue = clampChannel(channels[2]);
|
|
||||||
return Color.argb(clampChannel(alpha), red, green, blue);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Operand parseHexOperand(String raw) {
|
|
||||||
String value = raw != null ? raw.trim() : "";
|
|
||||||
if (value.isEmpty() || value.charAt(0) != '#') {
|
|
||||||
throw new IllegalArgumentException("Expected colour literal");
|
|
||||||
}
|
|
||||||
String hex = value.substring(1);
|
|
||||||
if (hex.length() == 3 || hex.length() == 4) {
|
|
||||||
hex = expandShortHex(hex);
|
|
||||||
} else if (hex.length() != 6 && hex.length() != 8) {
|
|
||||||
throw new IllegalArgumentException("Unsupported colour length");
|
|
||||||
}
|
|
||||||
boolean hasAlpha = hex.length() == 8;
|
|
||||||
int offset = hasAlpha ? 2 : 0;
|
|
||||||
double alpha = 0d;
|
|
||||||
if (hasAlpha) {
|
|
||||||
alpha = parseHexByte(hex.substring(0, 2));
|
|
||||||
}
|
|
||||||
double[] rgb = new double[] {
|
|
||||||
parseHexByte(hex.substring(offset, offset + 2)),
|
|
||||||
parseHexByte(hex.substring(offset + 2, offset + 4)),
|
|
||||||
parseHexByte(hex.substring(offset + 4, offset + 6))
|
|
||||||
};
|
|
||||||
return new Operand(rgb, alpha, hasAlpha, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static double parseHexByte(String value) {
|
|
||||||
return Integer.parseInt(value, 16);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int clampChannel(double value) {
|
|
||||||
if (Double.isNaN(value) || Double.isInfinite(value)) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
if (value <= 0d) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
if (value >= 255d) {
|
|
||||||
return 255;
|
|
||||||
}
|
|
||||||
return (int) Math.round(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static boolean startsWithOperator(String value, int start) {
|
|
||||||
if (start < 0 || start >= value.length()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
char c = value.charAt(start);
|
|
||||||
return c == '+' || c == '-' || c == '*' || c == '/';
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int findVariantSplit(String value) {
|
|
||||||
if (TextUtils.isEmpty(value)) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
boolean quoted = false;
|
|
||||||
for (int i = 0; i < value.length(); i++) {
|
for (int i = 0; i < value.length(); i++) {
|
||||||
char c = value.charAt(i);
|
char c = value.charAt(i);
|
||||||
if (c == '\'') {
|
if (inVector) {
|
||||||
quoted = !quoted;
|
if (c == ']') {
|
||||||
|
inVector = false;
|
||||||
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (!quoted && c == '|') {
|
if (c == '[') {
|
||||||
return i;
|
inVector = true;
|
||||||
}
|
continue;
|
||||||
}
|
}
|
||||||
|
if (c == '|') {
|
||||||
|
if (split >= 0) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
split = i;
|
||||||
private static boolean isDark(int color) {
|
}
|
||||||
double brightness = (Color.red(color) * 299d
|
}
|
||||||
+ Color.green(color) * 587d
|
return split;
|
||||||
+ Color.blue(color) * 114d) / 1000d;
|
|
||||||
return brightness < 128d;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String hexOperandPattern() {
|
private static String hexFor(int color) {
|
||||||
return "#(?:[0-9a-fA-F]{8}|[0-9a-fA-F]{6}|[0-9a-fA-F]{4}|[0-9a-fA-F]{3})";
|
if (Color.alpha(color) >= 255) {
|
||||||
}
|
return String.format(java.util.Locale.ROOT, "#%06X", color & 0xFFFFFF);
|
||||||
|
|
||||||
private static final class LeadingColour {
|
|
||||||
final int color;
|
|
||||||
final int endIndex;
|
|
||||||
|
|
||||||
LeadingColour(int color, int endIndex) {
|
|
||||||
this.color = color;
|
|
||||||
this.endIndex = endIndex;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static final class Operand {
|
|
||||||
final double[] rgb;
|
|
||||||
final double alpha;
|
|
||||||
final boolean hasAlpha;
|
|
||||||
final boolean isHex;
|
|
||||||
|
|
||||||
Operand(double[] rgb, double alpha, boolean hasAlpha, boolean isHex) {
|
|
||||||
this.rgb = rgb;
|
|
||||||
this.alpha = alpha;
|
|
||||||
this.hasAlpha = hasAlpha;
|
|
||||||
this.isHex = isHex;
|
|
||||||
}
|
}
|
||||||
|
return String.format(java.util.Locale.ROOT, "#%08X", color);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ public final class SbtDefaults {
|
|||||||
public static final boolean AOD_KEEP_VISIBLE_DURING_CALL_DEFAULT = false;
|
public static final boolean AOD_KEEP_VISIBLE_DURING_CALL_DEFAULT = false;
|
||||||
public static final boolean BATTERY_LOCKSCREEN_UNPLUGGED_PERCENT_DEFAULT = true;
|
public static final boolean BATTERY_LOCKSCREEN_UNPLUGGED_PERCENT_DEFAULT = true;
|
||||||
public static final boolean BATTERY_AOD_UNPLUGGED_PERCENT_DEFAULT = true;
|
public static final boolean BATTERY_AOD_UNPLUGGED_PERCENT_DEFAULT = true;
|
||||||
public static final boolean BATTERY_BAR_ENABLED_DEFAULT = false;
|
public static final boolean BATTERY_BAR_ENABLED_DEFAULT = true;
|
||||||
public static final String BATTERY_BAR_POSITION_DEFAULT = "top";
|
public static final String BATTERY_BAR_POSITION_DEFAULT = "top";
|
||||||
public static final String BATTERY_BAR_ALIGNMENT_DEFAULT = "ltr";
|
public static final String BATTERY_BAR_ALIGNMENT_DEFAULT = "ltr";
|
||||||
public static final int BATTERY_BAR_THICKNESS_DP_DEFAULT = 2;
|
public static final int BATTERY_BAR_THICKNESS_DP_DEFAULT = 2;
|
||||||
|
|||||||
@@ -64,6 +64,7 @@ public final class SbtSettings {
|
|||||||
public static final String KEY_STATUS_CHIPS_HIDE_MEDIA_UNLOCKED = "status_chips_hide_media_unlocked";
|
public static final String KEY_STATUS_CHIPS_HIDE_MEDIA_UNLOCKED = "status_chips_hide_media_unlocked";
|
||||||
public static final String KEY_STATUS_CHIPS_HIDE_NAV_UNLOCKED = "status_chips_hide_nav_unlocked";
|
public static final String KEY_STATUS_CHIPS_HIDE_NAV_UNLOCKED = "status_chips_hide_nav_unlocked";
|
||||||
public static final String KEY_STATUS_CHIPS_HIDE_CALL = "status_chips_hide_call";
|
public static final String KEY_STATUS_CHIPS_HIDE_CALL = "status_chips_hide_call";
|
||||||
|
public static final String KEY_STATUS_CHIPS_HIDE_BATTERY = "status_chips_hide_battery";
|
||||||
public static final String KEY_AOD_KEEP_VISIBLE_DURING_CALL = "aod_keep_visible_during_call";
|
public static final String KEY_AOD_KEEP_VISIBLE_DURING_CALL = "aod_keep_visible_during_call";
|
||||||
public static final String KEY_BATTERY_LOCKSCREEN_UNPLUGGED_PERCENT = "battery_lockscreen_unplugged_percent";
|
public static final String KEY_BATTERY_LOCKSCREEN_UNPLUGGED_PERCENT = "battery_lockscreen_unplugged_percent";
|
||||||
public static final String KEY_BATTERY_AOD_UNPLUGGED_PERCENT = "battery_aod_unplugged_percent";
|
public static final String KEY_BATTERY_AOD_UNPLUGGED_PERCENT = "battery_aod_unplugged_percent";
|
||||||
@@ -77,13 +78,14 @@ public final class SbtSettings {
|
|||||||
public static final String KEY_BATTERY_BAR_GEOMETRY_OVERRIDE_ENABLED_PREFIX =
|
public static final String KEY_BATTERY_BAR_GEOMETRY_OVERRIDE_ENABLED_PREFIX =
|
||||||
"battery_bar_geometry_override_enabled_";
|
"battery_bar_geometry_override_enabled_";
|
||||||
public static final String KEY_BATTERY_BAR_GEOMETRY_PREFIX = "battery_bar_geometry_";
|
public static final String KEY_BATTERY_BAR_GEOMETRY_PREFIX = "battery_bar_geometry_";
|
||||||
|
public static final String KEY_BATTERY_BAR_SCENARIO_ENABLED_PREFIX =
|
||||||
|
"battery_bar_scenario_enabled_";
|
||||||
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";
|
||||||
public static final String KEY_BATTERY_BAR_DEFAULT_CHARGE_COLOR = "battery_bar_default_charge_color";
|
public static final String KEY_BATTERY_BAR_DEFAULT_CHARGE_COLOR = "battery_bar_default_charge_color";
|
||||||
public static final String KEY_BATTERY_BAR_THRESHOLDS = "battery_bar_thresholds";
|
public static final String KEY_BATTERY_BAR_THRESHOLDS = "battery_bar_thresholds";
|
||||||
public static final String BATTERY_BAR_CURVED_GEOMETRY_OFF = "off";
|
public static final String BATTERY_BAR_CURVED_GEOMETRY_OFF = "off";
|
||||||
public static final String BATTERY_BAR_CURVED_GEOMETRY_HORIZONTAL = "horizontal";
|
|
||||||
public static final String BATTERY_BAR_CURVED_GEOMETRY_LENGTH = "length";
|
public static final String BATTERY_BAR_CURVED_GEOMETRY_LENGTH = "length";
|
||||||
public static final String KEY_CLOCK_ENABLED = "clock_enabled";
|
public static final String KEY_CLOCK_ENABLED = "clock_enabled";
|
||||||
public static final String KEY_CLOCK_ENABLED_LOCKSCREEN = "clock_enabled_lockscreen";
|
public static final String KEY_CLOCK_ENABLED_LOCKSCREEN = "clock_enabled_lockscreen";
|
||||||
@@ -106,6 +108,7 @@ public final class SbtSettings {
|
|||||||
public static final String KEY_DRAWER_DATE_CUSTOM_FORMAT_ENABLED =
|
public static final String KEY_DRAWER_DATE_CUSTOM_FORMAT_ENABLED =
|
||||||
"drawer_date_custom_format_enabled";
|
"drawer_date_custom_format_enabled";
|
||||||
public static final String KEY_DRAWER_DATE_CUSTOM_FORMAT = "drawer_date_custom_format";
|
public static final String KEY_DRAWER_DATE_CUSTOM_FORMAT = "drawer_date_custom_format";
|
||||||
|
public static final String KEY_CLOCK_STORED_PATTERNS = "clock_stored_patterns";
|
||||||
public static final String KEY_CLOCK_IGNORE_UNDER_DISPLAY_CAMERAS = "clock_ignore_under_display_cameras";
|
public static final String KEY_CLOCK_IGNORE_UNDER_DISPLAY_CAMERAS = "clock_ignore_under_display_cameras";
|
||||||
public static final String KEY_CLOCK_CAMERA_TYPE_OVERRIDES = "clock_camera_type_overrides";
|
public static final String KEY_CLOCK_CAMERA_TYPE_OVERRIDES = "clock_camera_type_overrides";
|
||||||
public static final String KEY_LAYOUT_PADDING_CUTOUT_PX = "layout_padding_cutout_px";
|
public static final String KEY_LAYOUT_PADDING_CUTOUT_PX = "layout_padding_cutout_px";
|
||||||
@@ -171,7 +174,6 @@ public final class SbtSettings {
|
|||||||
public static final String KEY_LAYOUT_STATUS_VERTICAL_OFFSET_PX = "layout_status_vertical_offset_px";
|
public static final String KEY_LAYOUT_STATUS_VERTICAL_OFFSET_PX = "layout_status_vertical_offset_px";
|
||||||
public static final String KEY_LAYOUT_STATUS_ICON_HEIGHT_STEPS = "layout_status_icon_height_steps";
|
public static final String KEY_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_DUAL_SIM_SIGNAL_MODE = "system_icon_dual_sim_signal_mode";
|
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_SEPARATE = "separate";
|
||||||
public static final String SYSTEM_ICON_DUAL_SIM_FIRST = "first";
|
public static final String SYSTEM_ICON_DUAL_SIM_FIRST = "first";
|
||||||
@@ -283,6 +285,10 @@ public final class SbtSettings {
|
|||||||
return KEY_BATTERY_BAR_GEOMETRY_PREFIX + scenarioId;
|
return KEY_BATTERY_BAR_GEOMETRY_PREFIX + scenarioId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static String batteryBarScenarioEnabledKey(String scenarioId) {
|
||||||
|
return KEY_BATTERY_BAR_SCENARIO_ENABLED_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);
|
||||||
}
|
}
|
||||||
@@ -316,6 +322,7 @@ public final class SbtSettings {
|
|||||||
public final String batteryBarCurvedGeometryMode;
|
public final String batteryBarCurvedGeometryMode;
|
||||||
public final Set<String> batteryBarGeometryOverrideEnabledScenarios;
|
public final Set<String> batteryBarGeometryOverrideEnabledScenarios;
|
||||||
public final Map<String, String> batteryBarGeometryOverrides;
|
public final Map<String, String> batteryBarGeometryOverrides;
|
||||||
|
public final Map<String, Boolean> batteryBarScenarioEnabled;
|
||||||
public final int batteryBarMinLevel;
|
public final int batteryBarMinLevel;
|
||||||
public final int batteryBarMaxLevel;
|
public final int batteryBarMaxLevel;
|
||||||
public final String batteryBarDefaultDischargeColor;
|
public final String batteryBarDefaultDischargeColor;
|
||||||
@@ -408,6 +415,7 @@ public final class SbtSettings {
|
|||||||
public final boolean statusChipsHideMediaUnlocked;
|
public final boolean statusChipsHideMediaUnlocked;
|
||||||
public final boolean statusChipsHideNavUnlocked;
|
public final boolean statusChipsHideNavUnlocked;
|
||||||
public final boolean statusChipsHideCallUnlocked;
|
public final boolean statusChipsHideCallUnlocked;
|
||||||
|
public final boolean statusChipsHideBattery;
|
||||||
public final Map<String, Integer> appIconBlockedModes;
|
public final Map<String, Integer> appIconBlockedModes;
|
||||||
public final Map<String, ArrayList<AppIconRules.ExceptionRule>> appIconExceptionRules;
|
public final Map<String, ArrayList<AppIconRules.ExceptionRule>> appIconExceptionRules;
|
||||||
public final String lockedNotificationMode;
|
public final String lockedNotificationMode;
|
||||||
@@ -448,6 +456,7 @@ public final class SbtSettings {
|
|||||||
String batteryBarCurvedGeometryMode,
|
String batteryBarCurvedGeometryMode,
|
||||||
Set<String> batteryBarGeometryOverrideEnabledScenarios,
|
Set<String> batteryBarGeometryOverrideEnabledScenarios,
|
||||||
Map<String, String> batteryBarGeometryOverrides,
|
Map<String, String> batteryBarGeometryOverrides,
|
||||||
|
Map<String, Boolean> batteryBarScenarioEnabled,
|
||||||
int batteryBarMinLevel,
|
int batteryBarMinLevel,
|
||||||
int batteryBarMaxLevel,
|
int batteryBarMaxLevel,
|
||||||
String batteryBarDefaultDischargeColor,
|
String batteryBarDefaultDischargeColor,
|
||||||
@@ -540,6 +549,7 @@ public final class SbtSettings {
|
|||||||
boolean statusChipsHideMediaUnlocked,
|
boolean statusChipsHideMediaUnlocked,
|
||||||
boolean statusChipsHideNavUnlocked,
|
boolean statusChipsHideNavUnlocked,
|
||||||
boolean statusChipsHideCallUnlocked,
|
boolean statusChipsHideCallUnlocked,
|
||||||
|
boolean statusChipsHideBattery,
|
||||||
Map<String, Integer> appIconBlockedModes,
|
Map<String, Integer> appIconBlockedModes,
|
||||||
Map<String, ArrayList<AppIconRules.ExceptionRule>> appIconExceptionRules,
|
Map<String, ArrayList<AppIconRules.ExceptionRule>> appIconExceptionRules,
|
||||||
String lockedNotificationMode,
|
String lockedNotificationMode,
|
||||||
@@ -591,6 +601,7 @@ public final class SbtSettings {
|
|||||||
this.batteryBarGeometryOverrideEnabledScenarios =
|
this.batteryBarGeometryOverrideEnabledScenarios =
|
||||||
sanitizeBatteryBarScenarioSet(batteryBarGeometryOverrideEnabledScenarios);
|
sanitizeBatteryBarScenarioSet(batteryBarGeometryOverrideEnabledScenarios);
|
||||||
this.batteryBarGeometryOverrides = sanitizeBatteryBarScenarioMap(batteryBarGeometryOverrides);
|
this.batteryBarGeometryOverrides = sanitizeBatteryBarScenarioMap(batteryBarGeometryOverrides);
|
||||||
|
this.batteryBarScenarioEnabled = sanitizeBatteryBarScenarioEnabledMap(batteryBarScenarioEnabled);
|
||||||
int clampedMinLevel = clampInt(
|
int clampedMinLevel = clampInt(
|
||||||
batteryBarMinLevel,
|
batteryBarMinLevel,
|
||||||
SbtDefaults.BATTERY_BAR_MIN_LEVEL_MIN,
|
SbtDefaults.BATTERY_BAR_MIN_LEVEL_MIN,
|
||||||
@@ -827,6 +838,7 @@ public final class SbtSettings {
|
|||||||
this.statusChipsHideMediaUnlocked = statusChipsHideMediaUnlocked;
|
this.statusChipsHideMediaUnlocked = statusChipsHideMediaUnlocked;
|
||||||
this.statusChipsHideNavUnlocked = statusChipsHideNavUnlocked;
|
this.statusChipsHideNavUnlocked = statusChipsHideNavUnlocked;
|
||||||
this.statusChipsHideCallUnlocked = statusChipsHideCallUnlocked;
|
this.statusChipsHideCallUnlocked = statusChipsHideCallUnlocked;
|
||||||
|
this.statusChipsHideBattery = statusChipsHideBattery;
|
||||||
this.appIconBlockedModes = appIconBlockedModes != null
|
this.appIconBlockedModes = appIconBlockedModes != null
|
||||||
? Collections.unmodifiableMap(new HashMap<>(appIconBlockedModes))
|
? Collections.unmodifiableMap(new HashMap<>(appIconBlockedModes))
|
||||||
: Collections.emptyMap();
|
: Collections.emptyMap();
|
||||||
@@ -883,6 +895,7 @@ public final class SbtSettings {
|
|||||||
SbtDefaults.BATTERY_BAR_CURVED_GEOMETRY_MODE_DEFAULT,
|
SbtDefaults.BATTERY_BAR_CURVED_GEOMETRY_MODE_DEFAULT,
|
||||||
Collections.emptySet(),
|
Collections.emptySet(),
|
||||||
Collections.emptyMap(),
|
Collections.emptyMap(),
|
||||||
|
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,
|
||||||
@@ -975,6 +988,7 @@ public final class SbtSettings {
|
|||||||
false,
|
false,
|
||||||
false,
|
false,
|
||||||
false,
|
false,
|
||||||
|
false,
|
||||||
Collections.emptyMap(),
|
Collections.emptyMap(),
|
||||||
Collections.emptyMap(),
|
Collections.emptyMap(),
|
||||||
"dot",
|
"dot",
|
||||||
@@ -1011,8 +1025,7 @@ public final class SbtSettings {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static String readBatteryBarCurvedGeometryMode(String mode) {
|
public static String readBatteryBarCurvedGeometryMode(String mode) {
|
||||||
if (BATTERY_BAR_CURVED_GEOMETRY_HORIZONTAL.equals(mode)
|
if (BATTERY_BAR_CURVED_GEOMETRY_LENGTH.equals(mode)) {
|
||||||
|| BATTERY_BAR_CURVED_GEOMETRY_LENGTH.equals(mode)) {
|
|
||||||
return mode;
|
return mode;
|
||||||
}
|
}
|
||||||
return BATTERY_BAR_CURVED_GEOMETRY_OFF;
|
return BATTERY_BAR_CURVED_GEOMETRY_OFF;
|
||||||
@@ -1124,6 +1137,29 @@ public final class SbtSettings {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void setClockCameraDebugState(Context context,
|
||||||
|
String cameraId,
|
||||||
|
String autoType,
|
||||||
|
String effectiveType) {
|
||||||
|
if (context == null || MODULE_PACKAGE.equals(context.getPackageName()) || !hasSettingsProvider(context)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
Bundle extras = new Bundle();
|
||||||
|
extras.putString(KEY_DEBUG_CURRENT_CAMERA_ID, cameraId != null ? cameraId : "");
|
||||||
|
extras.putString(KEY_DEBUG_CURRENT_CAMERA_AUTO_TYPE, autoType != null ? autoType : "");
|
||||||
|
extras.putString(KEY_DEBUG_CURRENT_CAMERA_EFFECTIVE_TYPE,
|
||||||
|
effectiveType != null ? effectiveType : "");
|
||||||
|
context.getContentResolver()
|
||||||
|
.call(settingsProviderUri(),
|
||||||
|
SbtSettingsProvider.METHOD_SET_CLOCK_CAMERA_DEBUG_STATE,
|
||||||
|
null,
|
||||||
|
extras);
|
||||||
|
} catch (Throwable ignored) {
|
||||||
|
// Debug camera metadata is advisory UI state only.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static boolean hasSettingsProvider(Context context) {
|
private static boolean hasSettingsProvider(Context context) {
|
||||||
if (context == null) {
|
if (context == null) {
|
||||||
return false;
|
return false;
|
||||||
@@ -1198,6 +1234,7 @@ public final class SbtSettings {
|
|||||||
SbtDefaults.BATTERY_BAR_CURVED_GEOMETRY_MODE_DEFAULT),
|
SbtDefaults.BATTERY_BAR_CURVED_GEOMETRY_MODE_DEFAULT),
|
||||||
readBatteryBarScenarioSetFromPrefs(prefs),
|
readBatteryBarScenarioSetFromPrefs(prefs),
|
||||||
readBatteryBarScenarioMapFromPrefs(prefs),
|
readBatteryBarScenarioMapFromPrefs(prefs),
|
||||||
|
readBatteryBarScenarioEnabledMapFromPrefs(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),
|
||||||
@@ -1287,9 +1324,7 @@ public final class SbtSettings {
|
|||||||
readIconContainerCount(
|
readIconContainerCount(
|
||||||
prefs,
|
prefs,
|
||||||
KEY_LAYOUT_NOTIF_UNLOCKED_COUNT,
|
KEY_LAYOUT_NOTIF_UNLOCKED_COUNT,
|
||||||
KEY_LAYOUT_NOTIF_ENABLED_UNLOCKED,
|
SbtDefaults.LAYOUT_NOTIF_UNLOCKED_COUNT_DEFAULT),
|
||||||
SbtDefaults.LAYOUT_NOTIF_UNLOCKED_COUNT_DEFAULT,
|
|
||||||
SbtDefaults.LAYOUT_NOTIF_ENABLED_UNLOCKED_DEFAULT),
|
|
||||||
readStringArrayFromPrefs(
|
readStringArrayFromPrefs(
|
||||||
prefs,
|
prefs,
|
||||||
SbtSettings::layoutNotifPositionKey,
|
SbtSettings::layoutNotifPositionKey,
|
||||||
@@ -1305,9 +1340,7 @@ public final class SbtSettings {
|
|||||||
readIconContainerCount(
|
readIconContainerCount(
|
||||||
prefs,
|
prefs,
|
||||||
KEY_LAYOUT_NOTIF_LOCK_COUNT,
|
KEY_LAYOUT_NOTIF_LOCK_COUNT,
|
||||||
KEY_LAYOUT_NOTIF_ENABLED_LOCKSCREEN,
|
SbtDefaults.LAYOUT_NOTIF_UNLOCKED_COUNT_DEFAULT),
|
||||||
SbtDefaults.LAYOUT_NOTIF_UNLOCKED_COUNT_DEFAULT,
|
|
||||||
SbtDefaults.LAYOUT_NOTIF_ENABLED_LOCKSCREEN_DEFAULT),
|
|
||||||
readStringArrayFromPrefs(
|
readStringArrayFromPrefs(
|
||||||
prefs,
|
prefs,
|
||||||
SbtSettings::layoutLockNotifPositionKey,
|
SbtSettings::layoutLockNotifPositionKey,
|
||||||
@@ -1323,9 +1356,7 @@ public final class SbtSettings {
|
|||||||
readIconContainerCount(
|
readIconContainerCount(
|
||||||
prefs,
|
prefs,
|
||||||
KEY_LAYOUT_NOTIF_AOD_COUNT,
|
KEY_LAYOUT_NOTIF_AOD_COUNT,
|
||||||
KEY_LAYOUT_NOTIF_ENABLED_AOD,
|
SbtDefaults.LAYOUT_NOTIF_UNLOCKED_COUNT_DEFAULT),
|
||||||
SbtDefaults.LAYOUT_NOTIF_UNLOCKED_COUNT_DEFAULT,
|
|
||||||
SbtDefaults.LAYOUT_NOTIF_ENABLED_AOD_DEFAULT),
|
|
||||||
readStringArrayFromPrefs(
|
readStringArrayFromPrefs(
|
||||||
prefs,
|
prefs,
|
||||||
SbtSettings::layoutAodNotifPositionKey,
|
SbtSettings::layoutAodNotifPositionKey,
|
||||||
@@ -1362,9 +1393,7 @@ public final class SbtSettings {
|
|||||||
readIconContainerCount(
|
readIconContainerCount(
|
||||||
prefs,
|
prefs,
|
||||||
KEY_LAYOUT_STATUS_UNLOCKED_COUNT,
|
KEY_LAYOUT_STATUS_UNLOCKED_COUNT,
|
||||||
KEY_LAYOUT_STATUS_ENABLED_UNLOCKED,
|
SbtDefaults.LAYOUT_STATUS_UNLOCKED_COUNT_DEFAULT),
|
||||||
SbtDefaults.LAYOUT_STATUS_UNLOCKED_COUNT_DEFAULT,
|
|
||||||
SbtDefaults.LAYOUT_STATUS_ENABLED_UNLOCKED_DEFAULT),
|
|
||||||
readStringArrayFromPrefs(
|
readStringArrayFromPrefs(
|
||||||
prefs,
|
prefs,
|
||||||
SbtSettings::layoutStatusPositionKey,
|
SbtSettings::layoutStatusPositionKey,
|
||||||
@@ -1380,9 +1409,7 @@ public final class SbtSettings {
|
|||||||
readIconContainerCount(
|
readIconContainerCount(
|
||||||
prefs,
|
prefs,
|
||||||
KEY_LAYOUT_STATUS_LOCK_COUNT,
|
KEY_LAYOUT_STATUS_LOCK_COUNT,
|
||||||
KEY_LAYOUT_STATUS_ENABLED_LOCKSCREEN,
|
SbtDefaults.LAYOUT_STATUS_UNLOCKED_COUNT_DEFAULT),
|
||||||
SbtDefaults.LAYOUT_STATUS_UNLOCKED_COUNT_DEFAULT,
|
|
||||||
SbtDefaults.LAYOUT_STATUS_ENABLED_LOCKSCREEN_DEFAULT),
|
|
||||||
readStringArrayFromPrefs(
|
readStringArrayFromPrefs(
|
||||||
prefs,
|
prefs,
|
||||||
SbtSettings::layoutLockStatusPositionKey,
|
SbtSettings::layoutLockStatusPositionKey,
|
||||||
@@ -1398,9 +1425,7 @@ public final class SbtSettings {
|
|||||||
readIconContainerCount(
|
readIconContainerCount(
|
||||||
prefs,
|
prefs,
|
||||||
KEY_LAYOUT_STATUS_AOD_COUNT,
|
KEY_LAYOUT_STATUS_AOD_COUNT,
|
||||||
KEY_LAYOUT_STATUS_ENABLED_AOD,
|
SbtDefaults.LAYOUT_STATUS_UNLOCKED_COUNT_DEFAULT),
|
||||||
SbtDefaults.LAYOUT_STATUS_UNLOCKED_COUNT_DEFAULT,
|
|
||||||
SbtDefaults.LAYOUT_STATUS_ENABLED_AOD_DEFAULT),
|
|
||||||
readStringArrayFromPrefs(
|
readStringArrayFromPrefs(
|
||||||
prefs,
|
prefs,
|
||||||
SbtSettings::layoutAodStatusPositionKey,
|
SbtSettings::layoutAodStatusPositionKey,
|
||||||
@@ -1422,6 +1447,7 @@ public final class SbtSettings {
|
|||||||
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),
|
||||||
prefs.getBoolean(KEY_STATUS_CHIPS_HIDE_CALL, false),
|
prefs.getBoolean(KEY_STATUS_CHIPS_HIDE_CALL, false),
|
||||||
|
prefs.getBoolean(KEY_STATUS_CHIPS_HIDE_BATTERY, false),
|
||||||
AppIconRules.parseBlockedModes(readStringSetFromPrefs(
|
AppIconRules.parseBlockedModes(readStringSetFromPrefs(
|
||||||
prefs,
|
prefs,
|
||||||
KEY_APP_ICON_BLOCKED_MODES)),
|
KEY_APP_ICON_BLOCKED_MODES)),
|
||||||
@@ -1516,6 +1542,7 @@ public final class SbtSettings {
|
|||||||
SbtDefaults.BATTERY_BAR_CURVED_GEOMETRY_MODE_DEFAULT),
|
SbtDefaults.BATTERY_BAR_CURVED_GEOMETRY_MODE_DEFAULT),
|
||||||
readBatteryBarScenarioSetFromBundle(bundle),
|
readBatteryBarScenarioSetFromBundle(bundle),
|
||||||
readBatteryBarScenarioMapFromBundle(bundle),
|
readBatteryBarScenarioMapFromBundle(bundle),
|
||||||
|
readBatteryBarScenarioEnabledMapFromBundle(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),
|
||||||
@@ -1605,9 +1632,7 @@ public final class SbtSettings {
|
|||||||
readIconContainerCount(
|
readIconContainerCount(
|
||||||
bundle,
|
bundle,
|
||||||
KEY_LAYOUT_NOTIF_UNLOCKED_COUNT,
|
KEY_LAYOUT_NOTIF_UNLOCKED_COUNT,
|
||||||
KEY_LAYOUT_NOTIF_ENABLED_UNLOCKED,
|
SbtDefaults.LAYOUT_NOTIF_UNLOCKED_COUNT_DEFAULT),
|
||||||
SbtDefaults.LAYOUT_NOTIF_UNLOCKED_COUNT_DEFAULT,
|
|
||||||
SbtDefaults.LAYOUT_NOTIF_ENABLED_UNLOCKED_DEFAULT),
|
|
||||||
readStringArrayFromBundle(
|
readStringArrayFromBundle(
|
||||||
bundle,
|
bundle,
|
||||||
SbtSettings::layoutNotifPositionKey,
|
SbtSettings::layoutNotifPositionKey,
|
||||||
@@ -1623,9 +1648,7 @@ public final class SbtSettings {
|
|||||||
readIconContainerCount(
|
readIconContainerCount(
|
||||||
bundle,
|
bundle,
|
||||||
KEY_LAYOUT_NOTIF_LOCK_COUNT,
|
KEY_LAYOUT_NOTIF_LOCK_COUNT,
|
||||||
KEY_LAYOUT_NOTIF_ENABLED_LOCKSCREEN,
|
SbtDefaults.LAYOUT_NOTIF_UNLOCKED_COUNT_DEFAULT),
|
||||||
SbtDefaults.LAYOUT_NOTIF_UNLOCKED_COUNT_DEFAULT,
|
|
||||||
SbtDefaults.LAYOUT_NOTIF_ENABLED_LOCKSCREEN_DEFAULT),
|
|
||||||
readStringArrayFromBundle(
|
readStringArrayFromBundle(
|
||||||
bundle,
|
bundle,
|
||||||
SbtSettings::layoutLockNotifPositionKey,
|
SbtSettings::layoutLockNotifPositionKey,
|
||||||
@@ -1641,9 +1664,7 @@ public final class SbtSettings {
|
|||||||
readIconContainerCount(
|
readIconContainerCount(
|
||||||
bundle,
|
bundle,
|
||||||
KEY_LAYOUT_NOTIF_AOD_COUNT,
|
KEY_LAYOUT_NOTIF_AOD_COUNT,
|
||||||
KEY_LAYOUT_NOTIF_ENABLED_AOD,
|
SbtDefaults.LAYOUT_NOTIF_UNLOCKED_COUNT_DEFAULT),
|
||||||
SbtDefaults.LAYOUT_NOTIF_UNLOCKED_COUNT_DEFAULT,
|
|
||||||
SbtDefaults.LAYOUT_NOTIF_ENABLED_AOD_DEFAULT),
|
|
||||||
readStringArrayFromBundle(
|
readStringArrayFromBundle(
|
||||||
bundle,
|
bundle,
|
||||||
SbtSettings::layoutAodNotifPositionKey,
|
SbtSettings::layoutAodNotifPositionKey,
|
||||||
@@ -1680,9 +1701,7 @@ public final class SbtSettings {
|
|||||||
readIconContainerCount(
|
readIconContainerCount(
|
||||||
bundle,
|
bundle,
|
||||||
KEY_LAYOUT_STATUS_UNLOCKED_COUNT,
|
KEY_LAYOUT_STATUS_UNLOCKED_COUNT,
|
||||||
KEY_LAYOUT_STATUS_ENABLED_UNLOCKED,
|
SbtDefaults.LAYOUT_STATUS_UNLOCKED_COUNT_DEFAULT),
|
||||||
SbtDefaults.LAYOUT_STATUS_UNLOCKED_COUNT_DEFAULT,
|
|
||||||
SbtDefaults.LAYOUT_STATUS_ENABLED_UNLOCKED_DEFAULT),
|
|
||||||
readStringArrayFromBundle(
|
readStringArrayFromBundle(
|
||||||
bundle,
|
bundle,
|
||||||
SbtSettings::layoutStatusPositionKey,
|
SbtSettings::layoutStatusPositionKey,
|
||||||
@@ -1698,9 +1717,7 @@ public final class SbtSettings {
|
|||||||
readIconContainerCount(
|
readIconContainerCount(
|
||||||
bundle,
|
bundle,
|
||||||
KEY_LAYOUT_STATUS_LOCK_COUNT,
|
KEY_LAYOUT_STATUS_LOCK_COUNT,
|
||||||
KEY_LAYOUT_STATUS_ENABLED_LOCKSCREEN,
|
SbtDefaults.LAYOUT_STATUS_UNLOCKED_COUNT_DEFAULT),
|
||||||
SbtDefaults.LAYOUT_STATUS_UNLOCKED_COUNT_DEFAULT,
|
|
||||||
SbtDefaults.LAYOUT_STATUS_ENABLED_LOCKSCREEN_DEFAULT),
|
|
||||||
readStringArrayFromBundle(
|
readStringArrayFromBundle(
|
||||||
bundle,
|
bundle,
|
||||||
SbtSettings::layoutLockStatusPositionKey,
|
SbtSettings::layoutLockStatusPositionKey,
|
||||||
@@ -1716,9 +1733,7 @@ public final class SbtSettings {
|
|||||||
readIconContainerCount(
|
readIconContainerCount(
|
||||||
bundle,
|
bundle,
|
||||||
KEY_LAYOUT_STATUS_AOD_COUNT,
|
KEY_LAYOUT_STATUS_AOD_COUNT,
|
||||||
KEY_LAYOUT_STATUS_ENABLED_AOD,
|
SbtDefaults.LAYOUT_STATUS_UNLOCKED_COUNT_DEFAULT),
|
||||||
SbtDefaults.LAYOUT_STATUS_UNLOCKED_COUNT_DEFAULT,
|
|
||||||
SbtDefaults.LAYOUT_STATUS_ENABLED_AOD_DEFAULT),
|
|
||||||
readStringArrayFromBundle(
|
readStringArrayFromBundle(
|
||||||
bundle,
|
bundle,
|
||||||
SbtSettings::layoutAodStatusPositionKey,
|
SbtSettings::layoutAodStatusPositionKey,
|
||||||
@@ -1740,6 +1755,7 @@ public final class SbtSettings {
|
|||||||
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),
|
||||||
bundle.getBoolean(KEY_STATUS_CHIPS_HIDE_CALL, false),
|
bundle.getBoolean(KEY_STATUS_CHIPS_HIDE_CALL, false),
|
||||||
|
bundle.getBoolean(KEY_STATUS_CHIPS_HIDE_BATTERY, false),
|
||||||
AppIconRules.parseBlockedModes(readStringSetFromBundle(
|
AppIconRules.parseBlockedModes(readStringSetFromBundle(
|
||||||
bundle,
|
bundle,
|
||||||
KEY_APP_ICON_BLOCKED_MODES)),
|
KEY_APP_ICON_BLOCKED_MODES)),
|
||||||
@@ -1791,35 +1807,21 @@ public final class SbtSettings {
|
|||||||
private static int readIconContainerCount(
|
private static int readIconContainerCount(
|
||||||
SharedPreferences prefs,
|
SharedPreferences prefs,
|
||||||
String countKey,
|
String countKey,
|
||||||
String legacyEnabledKey,
|
int defaultCount
|
||||||
int defaultCount,
|
|
||||||
boolean legacyEnabledDefault
|
|
||||||
) {
|
) {
|
||||||
int fallback = iconContainerCountFallback(
|
int value = prefs.getInt(countKey, defaultCount);
|
||||||
prefs.getBoolean(legacyEnabledKey, legacyEnabledDefault),
|
|
||||||
defaultCount);
|
|
||||||
int value = prefs.getInt(countKey, fallback);
|
|
||||||
return clampIconContainerCount(value);
|
return clampIconContainerCount(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int readIconContainerCount(
|
private static int readIconContainerCount(
|
||||||
Bundle bundle,
|
Bundle bundle,
|
||||||
String countKey,
|
String countKey,
|
||||||
String legacyEnabledKey,
|
int defaultCount
|
||||||
int defaultCount,
|
|
||||||
boolean legacyEnabledDefault
|
|
||||||
) {
|
) {
|
||||||
int fallback = iconContainerCountFallback(
|
int value = bundle.getInt(countKey, defaultCount);
|
||||||
bundle.getBoolean(legacyEnabledKey, legacyEnabledDefault),
|
|
||||||
defaultCount);
|
|
||||||
int value = bundle.getInt(countKey, fallback);
|
|
||||||
return clampIconContainerCount(value);
|
return clampIconContainerCount(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int iconContainerCountFallback(boolean legacyEnabled, int defaultCount) {
|
|
||||||
return legacyEnabled ? defaultCount : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String[] readStringArrayFromPrefs(
|
private static String[] readStringArrayFromPrefs(
|
||||||
SharedPreferences prefs,
|
SharedPreferences prefs,
|
||||||
IndexedKey key,
|
IndexedKey key,
|
||||||
@@ -1958,6 +1960,34 @@ public final class SbtSettings {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static Map<String, Boolean> readBatteryBarScenarioEnabledMapFromPrefs(SharedPreferences prefs) {
|
||||||
|
if (prefs == null) {
|
||||||
|
return Collections.emptyMap();
|
||||||
|
}
|
||||||
|
HashMap<String, Boolean> result = new HashMap<>();
|
||||||
|
for (BatteryBarGeometry.Scenario scenario : BatteryBarGeometry.Scenario.values()) {
|
||||||
|
String key = batteryBarScenarioEnabledKey(scenario.id);
|
||||||
|
if (prefs.contains(key)) {
|
||||||
|
result.put(scenario.id, prefs.getBoolean(key, true));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Map<String, Boolean> readBatteryBarScenarioEnabledMapFromBundle(Bundle bundle) {
|
||||||
|
if (bundle == null) {
|
||||||
|
return Collections.emptyMap();
|
||||||
|
}
|
||||||
|
HashMap<String, Boolean> result = new HashMap<>();
|
||||||
|
for (BatteryBarGeometry.Scenario scenario : BatteryBarGeometry.Scenario.values()) {
|
||||||
|
String key = batteryBarScenarioEnabledKey(scenario.id);
|
||||||
|
if (bundle.containsKey(key)) {
|
||||||
|
result.put(scenario.id, bundle.getBoolean(key, true));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
private static Set<String> sanitizeBatteryBarScenarioSet(Set<String> source) {
|
private static Set<String> sanitizeBatteryBarScenarioSet(Set<String> source) {
|
||||||
if (source == null || source.isEmpty()) {
|
if (source == null || source.isEmpty()) {
|
||||||
return Collections.emptySet();
|
return Collections.emptySet();
|
||||||
@@ -1986,6 +2016,21 @@ public final class SbtSettings {
|
|||||||
return result.isEmpty() ? Collections.emptyMap() : Collections.unmodifiableMap(result);
|
return result.isEmpty() ? Collections.emptyMap() : Collections.unmodifiableMap(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static Map<String, Boolean> sanitizeBatteryBarScenarioEnabledMap(Map<String, Boolean> source) {
|
||||||
|
if (source == null || source.isEmpty()) {
|
||||||
|
return Collections.emptyMap();
|
||||||
|
}
|
||||||
|
HashMap<String, Boolean> result = new HashMap<>();
|
||||||
|
for (Map.Entry<String, Boolean> entry : source.entrySet()) {
|
||||||
|
String id = entry.getKey();
|
||||||
|
Boolean value = entry.getValue();
|
||||||
|
if (isBatteryBarScenarioId(id) && value != null) {
|
||||||
|
result.put(id, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result.isEmpty() ? Collections.emptyMap() : Collections.unmodifiableMap(result);
|
||||||
|
}
|
||||||
|
|
||||||
private static boolean isBatteryBarScenarioId(String id) {
|
private static boolean isBatteryBarScenarioId(String id) {
|
||||||
if (id == null) {
|
if (id == null) {
|
||||||
return false;
|
return false;
|
||||||
@@ -2004,9 +2049,7 @@ public final class SbtSettings {
|
|||||||
}
|
}
|
||||||
Map<String, Integer> parsed = SystemIconRules.parseBlockedModes(
|
Map<String, Integer> parsed = SystemIconRules.parseBlockedModes(
|
||||||
readStringSetFromPrefs(prefs, KEY_SYSTEM_ICON_BLOCKED_MODES));
|
readStringSetFromPrefs(prefs, KEY_SYSTEM_ICON_BLOCKED_MODES));
|
||||||
HashMap<String, Integer> merged = new HashMap<>(parsed);
|
return parsed.isEmpty() ? Collections.emptyMap() : parsed;
|
||||||
mergeLegacySystemIconHidden(merged, readStringSetFromPrefs(prefs, KEY_SYSTEM_ICON_HIDE_SLOTS));
|
|
||||||
return merged.isEmpty() ? Collections.emptyMap() : merged;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Map<String, Integer> readSystemIconBlockedModesFromBundle(Bundle bundle) {
|
private static Map<String, Integer> readSystemIconBlockedModesFromBundle(Bundle bundle) {
|
||||||
@@ -2015,20 +2058,7 @@ public final class SbtSettings {
|
|||||||
}
|
}
|
||||||
Map<String, Integer> parsed = SystemIconRules.parseBlockedModes(
|
Map<String, Integer> parsed = SystemIconRules.parseBlockedModes(
|
||||||
readStringSetFromBundle(bundle, KEY_SYSTEM_ICON_BLOCKED_MODES));
|
readStringSetFromBundle(bundle, KEY_SYSTEM_ICON_BLOCKED_MODES));
|
||||||
HashMap<String, Integer> merged = new HashMap<>(parsed);
|
return parsed.isEmpty() ? Collections.emptyMap() : parsed;
|
||||||
mergeLegacySystemIconHidden(merged, readStringSetFromBundle(bundle, KEY_SYSTEM_ICON_HIDE_SLOTS));
|
|
||||||
return merged.isEmpty() ? Collections.emptyMap() : merged;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void mergeLegacySystemIconHidden(HashMap<String, Integer> merged, Set<String> legacyHidden) {
|
|
||||||
if (merged == null || legacyHidden == null || legacyHidden.isEmpty()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
for (String slot : legacyHidden) {
|
|
||||||
SystemIconRules.setModeBlocked(merged, slot,
|
|
||||||
SystemIconRules.MODE_AOD | SystemIconRules.MODE_LOCK | SystemIconRules.MODE_UNLOCK,
|
|
||||||
true);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void ensureReadable(Context context) {
|
public static void ensureReadable(Context context) {
|
||||||
|
|||||||
+12
-31
@@ -107,8 +107,12 @@ public class SbtSettingsProvider extends ContentProvider {
|
|||||||
for (BatteryBarGeometry.Scenario scenario : BatteryBarGeometry.Scenario.values()) {
|
for (BatteryBarGeometry.Scenario scenario : BatteryBarGeometry.Scenario.values()) {
|
||||||
String enabledKey = BatteryBarGeometry.enabledKey(scenario);
|
String enabledKey = BatteryBarGeometry.enabledKey(scenario);
|
||||||
String valueKey = BatteryBarGeometry.valueKey(scenario);
|
String valueKey = BatteryBarGeometry.valueKey(scenario);
|
||||||
|
String scenarioEnabledKey = SbtSettings.batteryBarScenarioEnabledKey(scenario.id);
|
||||||
out.putBoolean(enabledKey, prefs.getBoolean(enabledKey, false));
|
out.putBoolean(enabledKey, prefs.getBoolean(enabledKey, false));
|
||||||
out.putString(valueKey, prefs.getString(valueKey, null));
|
out.putString(valueKey, prefs.getString(valueKey, null));
|
||||||
|
if (prefs.contains(scenarioEnabledKey)) {
|
||||||
|
out.putBoolean(scenarioEnabledKey, prefs.getBoolean(scenarioEnabledKey, true));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
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));
|
||||||
@@ -261,11 +265,7 @@ public class SbtSettingsProvider extends ContentProvider {
|
|||||||
SbtDefaults.LAYOUT_NOTIF_ENABLED_UNLOCKED_DEFAULT));
|
SbtDefaults.LAYOUT_NOTIF_ENABLED_UNLOCKED_DEFAULT));
|
||||||
out.putInt(SbtSettings.KEY_LAYOUT_NOTIF_UNLOCKED_COUNT,
|
out.putInt(SbtSettings.KEY_LAYOUT_NOTIF_UNLOCKED_COUNT,
|
||||||
prefs.getInt(SbtSettings.KEY_LAYOUT_NOTIF_UNLOCKED_COUNT,
|
prefs.getInt(SbtSettings.KEY_LAYOUT_NOTIF_UNLOCKED_COUNT,
|
||||||
prefs.getBoolean(
|
SbtDefaults.LAYOUT_NOTIF_UNLOCKED_COUNT_DEFAULT));
|
||||||
SbtSettings.KEY_LAYOUT_NOTIF_ENABLED_UNLOCKED,
|
|
||||||
SbtDefaults.LAYOUT_NOTIF_ENABLED_UNLOCKED_DEFAULT)
|
|
||||||
? SbtDefaults.LAYOUT_NOTIF_UNLOCKED_COUNT_DEFAULT
|
|
||||||
: 0));
|
|
||||||
out.putString(SbtSettings.KEY_LAYOUT_NOTIF_MIDDLE_SIDE,
|
out.putString(SbtSettings.KEY_LAYOUT_NOTIF_MIDDLE_SIDE,
|
||||||
prefs.getString(SbtSettings.KEY_LAYOUT_NOTIF_MIDDLE_SIDE,
|
prefs.getString(SbtSettings.KEY_LAYOUT_NOTIF_MIDDLE_SIDE,
|
||||||
SbtDefaults.LAYOUT_NOTIF_MIDDLE_SIDE_DEFAULT));
|
SbtDefaults.LAYOUT_NOTIF_MIDDLE_SIDE_DEFAULT));
|
||||||
@@ -286,11 +286,7 @@ public class SbtSettingsProvider extends ContentProvider {
|
|||||||
SbtDefaults.LAYOUT_NOTIF_VERTICAL_OFFSET_PX_DEFAULT);
|
SbtDefaults.LAYOUT_NOTIF_VERTICAL_OFFSET_PX_DEFAULT);
|
||||||
out.putInt(SbtSettings.KEY_LAYOUT_NOTIF_LOCK_COUNT,
|
out.putInt(SbtSettings.KEY_LAYOUT_NOTIF_LOCK_COUNT,
|
||||||
prefs.getInt(SbtSettings.KEY_LAYOUT_NOTIF_LOCK_COUNT,
|
prefs.getInt(SbtSettings.KEY_LAYOUT_NOTIF_LOCK_COUNT,
|
||||||
prefs.getBoolean(
|
SbtDefaults.LAYOUT_NOTIF_UNLOCKED_COUNT_DEFAULT));
|
||||||
SbtSettings.KEY_LAYOUT_NOTIF_ENABLED_LOCKSCREEN,
|
|
||||||
SbtDefaults.LAYOUT_NOTIF_ENABLED_LOCKSCREEN_DEFAULT)
|
|
||||||
? SbtDefaults.LAYOUT_NOTIF_UNLOCKED_COUNT_DEFAULT
|
|
||||||
: 0));
|
|
||||||
out.putInt(SbtSettings.layoutLockNotifIconHeightKey(),
|
out.putInt(SbtSettings.layoutLockNotifIconHeightKey(),
|
||||||
prefs.getInt(SbtSettings.layoutLockNotifIconHeightKey(),
|
prefs.getInt(SbtSettings.layoutLockNotifIconHeightKey(),
|
||||||
SbtDefaults.LAYOUT_NOTIF_LOCK_ICON_HEIGHT_STEPS_DEFAULT));
|
SbtDefaults.LAYOUT_NOTIF_LOCK_ICON_HEIGHT_STEPS_DEFAULT));
|
||||||
@@ -305,11 +301,7 @@ public class SbtSettingsProvider extends ContentProvider {
|
|||||||
SbtDefaults.LAYOUT_NOTIF_VERTICAL_OFFSET_PX_DEFAULT);
|
SbtDefaults.LAYOUT_NOTIF_VERTICAL_OFFSET_PX_DEFAULT);
|
||||||
out.putInt(SbtSettings.KEY_LAYOUT_NOTIF_AOD_COUNT,
|
out.putInt(SbtSettings.KEY_LAYOUT_NOTIF_AOD_COUNT,
|
||||||
prefs.getInt(SbtSettings.KEY_LAYOUT_NOTIF_AOD_COUNT,
|
prefs.getInt(SbtSettings.KEY_LAYOUT_NOTIF_AOD_COUNT,
|
||||||
prefs.getBoolean(
|
SbtDefaults.LAYOUT_NOTIF_UNLOCKED_COUNT_DEFAULT));
|
||||||
SbtSettings.KEY_LAYOUT_NOTIF_ENABLED_AOD,
|
|
||||||
SbtDefaults.LAYOUT_NOTIF_ENABLED_AOD_DEFAULT)
|
|
||||||
? SbtDefaults.LAYOUT_NOTIF_UNLOCKED_COUNT_DEFAULT
|
|
||||||
: 0));
|
|
||||||
out.putInt(SbtSettings.layoutAodNotifIconHeightKey(),
|
out.putInt(SbtSettings.layoutAodNotifIconHeightKey(),
|
||||||
prefs.getInt(SbtSettings.layoutAodNotifIconHeightKey(),
|
prefs.getInt(SbtSettings.layoutAodNotifIconHeightKey(),
|
||||||
SbtDefaults.LAYOUT_NOTIF_AOD_ICON_HEIGHT_STEPS_DEFAULT));
|
SbtDefaults.LAYOUT_NOTIF_AOD_ICON_HEIGHT_STEPS_DEFAULT));
|
||||||
@@ -336,11 +328,7 @@ public class SbtSettingsProvider extends ContentProvider {
|
|||||||
SbtDefaults.LAYOUT_STATUS_ENABLED_UNLOCKED_DEFAULT));
|
SbtDefaults.LAYOUT_STATUS_ENABLED_UNLOCKED_DEFAULT));
|
||||||
out.putInt(SbtSettings.KEY_LAYOUT_STATUS_UNLOCKED_COUNT,
|
out.putInt(SbtSettings.KEY_LAYOUT_STATUS_UNLOCKED_COUNT,
|
||||||
prefs.getInt(SbtSettings.KEY_LAYOUT_STATUS_UNLOCKED_COUNT,
|
prefs.getInt(SbtSettings.KEY_LAYOUT_STATUS_UNLOCKED_COUNT,
|
||||||
prefs.getBoolean(
|
SbtDefaults.LAYOUT_STATUS_UNLOCKED_COUNT_DEFAULT));
|
||||||
SbtSettings.KEY_LAYOUT_STATUS_ENABLED_UNLOCKED,
|
|
||||||
SbtDefaults.LAYOUT_STATUS_ENABLED_UNLOCKED_DEFAULT)
|
|
||||||
? SbtDefaults.LAYOUT_STATUS_UNLOCKED_COUNT_DEFAULT
|
|
||||||
: 0));
|
|
||||||
out.putString(SbtSettings.KEY_LAYOUT_STATUS_MIDDLE_SIDE,
|
out.putString(SbtSettings.KEY_LAYOUT_STATUS_MIDDLE_SIDE,
|
||||||
prefs.getString(SbtSettings.KEY_LAYOUT_STATUS_MIDDLE_SIDE,
|
prefs.getString(SbtSettings.KEY_LAYOUT_STATUS_MIDDLE_SIDE,
|
||||||
SbtDefaults.LAYOUT_STATUS_MIDDLE_SIDE_DEFAULT));
|
SbtDefaults.LAYOUT_STATUS_MIDDLE_SIDE_DEFAULT));
|
||||||
@@ -365,11 +353,7 @@ public class SbtSettingsProvider extends ContentProvider {
|
|||||||
SbtDefaults.SYSTEM_ICON_DUAL_SIM_SIGNAL_MODE_DEFAULT)));
|
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(
|
SbtDefaults.LAYOUT_STATUS_UNLOCKED_COUNT_DEFAULT));
|
||||||
SbtSettings.KEY_LAYOUT_STATUS_ENABLED_LOCKSCREEN,
|
|
||||||
SbtDefaults.LAYOUT_STATUS_ENABLED_LOCKSCREEN_DEFAULT)
|
|
||||||
? SbtDefaults.LAYOUT_STATUS_UNLOCKED_COUNT_DEFAULT
|
|
||||||
: 0));
|
|
||||||
out.putInt(SbtSettings.layoutLockStatusIconHeightKey(),
|
out.putInt(SbtSettings.layoutLockStatusIconHeightKey(),
|
||||||
prefs.getInt(SbtSettings.layoutLockStatusIconHeightKey(),
|
prefs.getInt(SbtSettings.layoutLockStatusIconHeightKey(),
|
||||||
SbtDefaults.LAYOUT_STATUS_ICON_HEIGHT_STEPS_DEFAULT));
|
SbtDefaults.LAYOUT_STATUS_ICON_HEIGHT_STEPS_DEFAULT));
|
||||||
@@ -384,11 +368,7 @@ public class SbtSettingsProvider extends ContentProvider {
|
|||||||
SbtDefaults.LAYOUT_STATUS_VERTICAL_OFFSET_PX_DEFAULT);
|
SbtDefaults.LAYOUT_STATUS_VERTICAL_OFFSET_PX_DEFAULT);
|
||||||
out.putInt(SbtSettings.KEY_LAYOUT_STATUS_AOD_COUNT,
|
out.putInt(SbtSettings.KEY_LAYOUT_STATUS_AOD_COUNT,
|
||||||
prefs.getInt(SbtSettings.KEY_LAYOUT_STATUS_AOD_COUNT,
|
prefs.getInt(SbtSettings.KEY_LAYOUT_STATUS_AOD_COUNT,
|
||||||
prefs.getBoolean(
|
SbtDefaults.LAYOUT_STATUS_UNLOCKED_COUNT_DEFAULT));
|
||||||
SbtSettings.KEY_LAYOUT_STATUS_ENABLED_AOD,
|
|
||||||
SbtDefaults.LAYOUT_STATUS_ENABLED_AOD_DEFAULT)
|
|
||||||
? SbtDefaults.LAYOUT_STATUS_UNLOCKED_COUNT_DEFAULT
|
|
||||||
: 0));
|
|
||||||
out.putInt(SbtSettings.layoutAodStatusIconHeightKey(),
|
out.putInt(SbtSettings.layoutAodStatusIconHeightKey(),
|
||||||
prefs.getInt(SbtSettings.layoutAodStatusIconHeightKey(),
|
prefs.getInt(SbtSettings.layoutAodStatusIconHeightKey(),
|
||||||
SbtDefaults.LAYOUT_STATUS_ICON_HEIGHT_STEPS_DEFAULT));
|
SbtDefaults.LAYOUT_STATUS_ICON_HEIGHT_STEPS_DEFAULT));
|
||||||
@@ -403,7 +383,6 @@ public class SbtSettingsProvider extends ContentProvider {
|
|||||||
SbtDefaults.LAYOUT_STATUS_VERTICAL_OFFSET_PX_DEFAULT);
|
SbtDefaults.LAYOUT_STATUS_VERTICAL_OFFSET_PX_DEFAULT);
|
||||||
putStringSetArrayList(out, prefs, SbtSettings.KEY_CLOCK_CAMERA_TYPE_OVERRIDES);
|
putStringSetArrayList(out, prefs, SbtSettings.KEY_CLOCK_CAMERA_TYPE_OVERRIDES);
|
||||||
putStringSetArrayList(out, prefs, SbtSettings.KEY_SYSTEM_ICON_BLOCKED_MODES);
|
putStringSetArrayList(out, prefs, SbtSettings.KEY_SYSTEM_ICON_BLOCKED_MODES);
|
||||||
putStringSetArrayList(out, prefs, SbtSettings.KEY_SYSTEM_ICON_HIDE_SLOTS);
|
|
||||||
out.putBoolean(SbtSettings.KEY_STATUS_CHIPS_HIDE_ALL,
|
out.putBoolean(SbtSettings.KEY_STATUS_CHIPS_HIDE_ALL,
|
||||||
prefs.getBoolean(SbtSettings.KEY_STATUS_CHIPS_HIDE_ALL, false));
|
prefs.getBoolean(SbtSettings.KEY_STATUS_CHIPS_HIDE_ALL, false));
|
||||||
out.putBoolean(SbtSettings.KEY_STATUS_CHIPS_HIDE_MEDIA_UNLOCKED,
|
out.putBoolean(SbtSettings.KEY_STATUS_CHIPS_HIDE_MEDIA_UNLOCKED,
|
||||||
@@ -412,6 +391,8 @@ public class SbtSettingsProvider extends ContentProvider {
|
|||||||
prefs.getBoolean(SbtSettings.KEY_STATUS_CHIPS_HIDE_NAV_UNLOCKED, false));
|
prefs.getBoolean(SbtSettings.KEY_STATUS_CHIPS_HIDE_NAV_UNLOCKED, false));
|
||||||
out.putBoolean(SbtSettings.KEY_STATUS_CHIPS_HIDE_CALL,
|
out.putBoolean(SbtSettings.KEY_STATUS_CHIPS_HIDE_CALL,
|
||||||
prefs.getBoolean(SbtSettings.KEY_STATUS_CHIPS_HIDE_CALL, false));
|
prefs.getBoolean(SbtSettings.KEY_STATUS_CHIPS_HIDE_CALL, false));
|
||||||
|
out.putBoolean(SbtSettings.KEY_STATUS_CHIPS_HIDE_BATTERY,
|
||||||
|
prefs.getBoolean(SbtSettings.KEY_STATUS_CHIPS_HIDE_BATTERY, false));
|
||||||
putStringCollectionArrayList(
|
putStringCollectionArrayList(
|
||||||
out,
|
out,
|
||||||
SbtSettings.KEY_APP_ICON_BLOCKED_MODES,
|
SbtSettings.KEY_APP_ICON_BLOCKED_MODES,
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,7 @@
|
|||||||
package se.ajpanton.statusbartweak.shell.ui;
|
package se.ajpanton.statusbartweak.shell.ui;
|
||||||
|
|
||||||
import se.ajpanton.statusbartweak.R;
|
import se.ajpanton.statusbartweak.R;
|
||||||
|
import se.ajpanton.statusbartweak.runtime.features.clock.ClockPatternPreviewRenderer;
|
||||||
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;
|
||||||
|
|
||||||
@@ -8,6 +9,9 @@ import android.content.ClipData;
|
|||||||
import android.content.ClipboardManager;
|
import android.content.ClipboardManager;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
|
import android.graphics.Color;
|
||||||
|
import android.graphics.drawable.GradientDrawable;
|
||||||
|
import android.net.Uri;
|
||||||
import android.graphics.Typeface;
|
import android.graphics.Typeface;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.text.Editable;
|
import android.text.Editable;
|
||||||
@@ -30,18 +34,49 @@ import android.widget.Toast;
|
|||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.appcompat.app.AlertDialog;
|
import androidx.appcompat.app.AlertDialog;
|
||||||
|
import androidx.core.content.ContextCompat;
|
||||||
import androidx.fragment.app.Fragment;
|
import androidx.fragment.app.Fragment;
|
||||||
|
|
||||||
import com.google.android.material.button.MaterialButton;
|
import com.google.android.material.button.MaterialButton;
|
||||||
|
import com.google.android.material.card.MaterialCardView;
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||||
|
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
public final class ClockFragment extends Fragment {
|
public final class ClockFragment extends Fragment {
|
||||||
|
private static final String PATTERN_ORIGIN_STATUSBAR = "statusbar";
|
||||||
|
private static final String PATTERN_ORIGIN_DRAWER_CLOCK = "drawer_clock";
|
||||||
|
private static final String PATTERN_ORIGIN_DRAWER_DATE = "drawer_date";
|
||||||
|
private static final int COLOR_STATUSBAR = 0xffe85050;
|
||||||
|
private static final int COLOR_DRAWER_CLOCK = 0xff4caf50;
|
||||||
|
private static final int COLOR_DRAWER_DATE = 0xff4f8cff;
|
||||||
|
private static final int CARD_STROKE_DP = 2;
|
||||||
|
|
||||||
|
private final ArrayList<PreviewBinding> previewBindings = new ArrayList<>();
|
||||||
|
private final ArrayList<StoredPatternBinding> storedPatternBindings = new ArrayList<>();
|
||||||
|
private final ArrayList<StoredPatternRowBinding> storedPatternRowBindings = new ArrayList<>();
|
||||||
|
private final Runnable previewTicker = new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
boolean needsNextTick = false;
|
||||||
|
HashMap<String, ClockPatternPreviewRenderer.Result> renderCache = new HashMap<>();
|
||||||
|
for (PreviewBinding binding : previewBindings) {
|
||||||
|
needsNextTick |= updatePreview(binding);
|
||||||
|
}
|
||||||
|
for (StoredPatternRowBinding binding : storedPatternRowBindings) {
|
||||||
|
needsNextTick |= updateStoredPatternPreview(binding, renderCache);
|
||||||
|
}
|
||||||
|
if (needsNextTick && getView() != null) {
|
||||||
|
getView().postDelayed(this, nextSecondDelayMillis());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
@Override
|
@Override
|
||||||
@@ -52,22 +87,35 @@ public final class ClockFragment extends Fragment {
|
|||||||
SharedPreferences prefs = requireContext()
|
SharedPreferences prefs = requireContext()
|
||||||
.getSharedPreferences(SbtSettings.PREFS_NAME, Context.MODE_PRIVATE);
|
.getSharedPreferences(SbtSettings.PREFS_NAME, Context.MODE_PRIVATE);
|
||||||
|
|
||||||
|
MaterialCardView customFormatCard = root.findViewById(R.id.clock_custom_format_card);
|
||||||
CheckBox customFormatEnabled = root.findViewById(R.id.clock_custom_format_enabled);
|
CheckBox customFormatEnabled = root.findViewById(R.id.clock_custom_format_enabled);
|
||||||
View customFormatContainer = root.findViewById(R.id.clock_custom_format_container);
|
View customFormatContainer = root.findViewById(R.id.clock_custom_format_container);
|
||||||
EditText customFormatInput = root.findViewById(R.id.clock_custom_format_input);
|
EditText customFormatInput = root.findViewById(R.id.clock_custom_format_input);
|
||||||
|
ClockPatternPreviewView customFormatPreview =
|
||||||
|
root.findViewById(R.id.clock_custom_format_preview);
|
||||||
|
MaterialCardView drawerClockCustomFormatCard =
|
||||||
|
root.findViewById(R.id.drawer_clock_custom_format_card);
|
||||||
CheckBox drawerClockCustomFormatEnabled =
|
CheckBox drawerClockCustomFormatEnabled =
|
||||||
root.findViewById(R.id.drawer_clock_custom_format_enabled);
|
root.findViewById(R.id.drawer_clock_custom_format_enabled);
|
||||||
View drawerClockCustomFormatContainer =
|
View drawerClockCustomFormatContainer =
|
||||||
root.findViewById(R.id.drawer_clock_custom_format_container);
|
root.findViewById(R.id.drawer_clock_custom_format_container);
|
||||||
EditText drawerClockCustomFormatInput =
|
EditText drawerClockCustomFormatInput =
|
||||||
root.findViewById(R.id.drawer_clock_custom_format_input);
|
root.findViewById(R.id.drawer_clock_custom_format_input);
|
||||||
|
ClockPatternPreviewView drawerClockCustomFormatPreview =
|
||||||
|
root.findViewById(R.id.drawer_clock_custom_format_preview);
|
||||||
|
MaterialCardView drawerDateCustomFormatCard =
|
||||||
|
root.findViewById(R.id.drawer_date_custom_format_card);
|
||||||
CheckBox drawerDateCustomFormatEnabled =
|
CheckBox drawerDateCustomFormatEnabled =
|
||||||
root.findViewById(R.id.drawer_date_custom_format_enabled);
|
root.findViewById(R.id.drawer_date_custom_format_enabled);
|
||||||
View drawerDateCustomFormatContainer =
|
View drawerDateCustomFormatContainer =
|
||||||
root.findViewById(R.id.drawer_date_custom_format_container);
|
root.findViewById(R.id.drawer_date_custom_format_container);
|
||||||
EditText drawerDateCustomFormatInput =
|
EditText drawerDateCustomFormatInput =
|
||||||
root.findViewById(R.id.drawer_date_custom_format_input);
|
root.findViewById(R.id.drawer_date_custom_format_input);
|
||||||
|
ClockPatternPreviewView drawerDateCustomFormatPreview =
|
||||||
|
root.findViewById(R.id.drawer_date_custom_format_preview);
|
||||||
MaterialButton fontPickerButton = root.findViewById(R.id.clock_font_picker_button);
|
MaterialButton fontPickerButton = root.findViewById(R.id.clock_font_picker_button);
|
||||||
|
MaterialButton restoreStoredPatternsButton =
|
||||||
|
root.findViewById(R.id.clock_restore_stored_patterns_button);
|
||||||
TextView customFormatHelpToggle = root.findViewById(R.id.clock_custom_format_help_toggle);
|
TextView customFormatHelpToggle = root.findViewById(R.id.clock_custom_format_help_toggle);
|
||||||
View customFormatHelpContainer = root.findViewById(R.id.clock_custom_format_help_container);
|
View customFormatHelpContainer = root.findViewById(R.id.clock_custom_format_help_container);
|
||||||
TextView customFormatHelpCommon = root.findViewById(R.id.clock_custom_format_help);
|
TextView customFormatHelpCommon = root.findViewById(R.id.clock_custom_format_help);
|
||||||
@@ -94,27 +142,54 @@ public final class ClockFragment extends Fragment {
|
|||||||
SbtSettings.KEY_DRAWER_DATE_CUSTOM_FORMAT,
|
SbtSettings.KEY_DRAWER_DATE_CUSTOM_FORMAT,
|
||||||
SbtDefaults.DRAWER_DATE_CUSTOM_FORMAT_DEFAULT);
|
SbtDefaults.DRAWER_DATE_CUSTOM_FORMAT_DEFAULT);
|
||||||
|
|
||||||
customFormatEnabled.setChecked(customFormatEnabledValue);
|
previewBindings.clear();
|
||||||
customFormatInput.setText(customFormatValue);
|
storedPatternBindings.clear();
|
||||||
updateCustomFormatVisibility(customFormatContainer, customFormatEnabledValue);
|
ensureDefaultStoredPatterns(prefs);
|
||||||
bindCustomFormat(
|
bindCustomFormat(
|
||||||
prefs,
|
prefs,
|
||||||
|
customFormatCard,
|
||||||
|
customFormatEnabled,
|
||||||
|
customFormatContainer,
|
||||||
|
customFormatInput,
|
||||||
|
customFormatPreview,
|
||||||
|
PATTERN_ORIGIN_STATUSBAR,
|
||||||
|
COLOR_STATUSBAR,
|
||||||
|
SbtSettings.KEY_CLOCK_CUSTOM_FORMAT_ENABLED,
|
||||||
|
SbtSettings.KEY_CLOCK_CUSTOM_FORMAT,
|
||||||
|
customFormatEnabledValue,
|
||||||
|
customFormatValue,
|
||||||
|
18f,
|
||||||
|
true);
|
||||||
|
bindCustomFormat(
|
||||||
|
prefs,
|
||||||
|
drawerClockCustomFormatCard,
|
||||||
drawerClockCustomFormatEnabled,
|
drawerClockCustomFormatEnabled,
|
||||||
drawerClockCustomFormatContainer,
|
drawerClockCustomFormatContainer,
|
||||||
drawerClockCustomFormatInput,
|
drawerClockCustomFormatInput,
|
||||||
|
drawerClockCustomFormatPreview,
|
||||||
|
PATTERN_ORIGIN_DRAWER_CLOCK,
|
||||||
|
COLOR_DRAWER_CLOCK,
|
||||||
SbtSettings.KEY_DRAWER_CLOCK_CUSTOM_FORMAT_ENABLED,
|
SbtSettings.KEY_DRAWER_CLOCK_CUSTOM_FORMAT_ENABLED,
|
||||||
SbtSettings.KEY_DRAWER_CLOCK_CUSTOM_FORMAT,
|
SbtSettings.KEY_DRAWER_CLOCK_CUSTOM_FORMAT,
|
||||||
drawerClockCustomFormatEnabledValue,
|
drawerClockCustomFormatEnabledValue,
|
||||||
drawerClockCustomFormatValue);
|
drawerClockCustomFormatValue,
|
||||||
|
24f,
|
||||||
|
false);
|
||||||
bindCustomFormat(
|
bindCustomFormat(
|
||||||
prefs,
|
prefs,
|
||||||
|
drawerDateCustomFormatCard,
|
||||||
drawerDateCustomFormatEnabled,
|
drawerDateCustomFormatEnabled,
|
||||||
drawerDateCustomFormatContainer,
|
drawerDateCustomFormatContainer,
|
||||||
drawerDateCustomFormatInput,
|
drawerDateCustomFormatInput,
|
||||||
|
drawerDateCustomFormatPreview,
|
||||||
|
PATTERN_ORIGIN_DRAWER_DATE,
|
||||||
|
COLOR_DRAWER_DATE,
|
||||||
SbtSettings.KEY_DRAWER_DATE_CUSTOM_FORMAT_ENABLED,
|
SbtSettings.KEY_DRAWER_DATE_CUSTOM_FORMAT_ENABLED,
|
||||||
SbtSettings.KEY_DRAWER_DATE_CUSTOM_FORMAT,
|
SbtSettings.KEY_DRAWER_DATE_CUSTOM_FORMAT,
|
||||||
drawerDateCustomFormatEnabledValue,
|
drawerDateCustomFormatEnabledValue,
|
||||||
drawerDateCustomFormatValue);
|
drawerDateCustomFormatValue,
|
||||||
|
16f,
|
||||||
|
false);
|
||||||
updateCustomFormatHelpVisibility(customFormatHelpContainer, customFormatHelpToggle, false);
|
updateCustomFormatHelpVisibility(customFormatHelpContainer, customFormatHelpToggle, false);
|
||||||
applyBulletText(customFormatHelpCommon);
|
applyBulletText(customFormatHelpCommon);
|
||||||
applyBulletText(customFormatHelpStyling);
|
applyBulletText(customFormatHelpStyling);
|
||||||
@@ -123,12 +198,6 @@ public final class ClockFragment extends Fragment {
|
|||||||
customFormatHelpLink.setMovementMethod(LinkMovementMethod.getInstance());
|
customFormatHelpLink.setMovementMethod(LinkMovementMethod.getInstance());
|
||||||
}
|
}
|
||||||
|
|
||||||
customFormatEnabled.setOnCheckedChangeListener((buttonView, isChecked) -> {
|
|
||||||
updateCustomFormatVisibility(customFormatContainer, isChecked);
|
|
||||||
prefs.edit().putBoolean(SbtSettings.KEY_CLOCK_CUSTOM_FORMAT_ENABLED, isChecked).apply();
|
|
||||||
SbtSettings.ensureReadable(requireContext());
|
|
||||||
});
|
|
||||||
|
|
||||||
if (customFormatHelpToggle != null) {
|
if (customFormatHelpToggle != null) {
|
||||||
customFormatHelpToggle.setOnClickListener(v -> updateCustomFormatHelpVisibility(
|
customFormatHelpToggle.setOnClickListener(v -> updateCustomFormatHelpVisibility(
|
||||||
customFormatHelpContainer,
|
customFormatHelpContainer,
|
||||||
@@ -136,27 +205,15 @@ public final class ClockFragment extends Fragment {
|
|||||||
customFormatHelpContainer == null || customFormatHelpContainer.getVisibility() != View.VISIBLE));
|
customFormatHelpContainer == null || customFormatHelpContainer.getVisibility() != View.VISIBLE));
|
||||||
}
|
}
|
||||||
|
|
||||||
customFormatInput.addTextChangedListener(new TextWatcher() {
|
|
||||||
@Override
|
|
||||||
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onTextChanged(CharSequence s, int start, int before, int count) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void afterTextChanged(Editable s) {
|
|
||||||
prefs.edit()
|
|
||||||
.putString(SbtSettings.KEY_CLOCK_CUSTOM_FORMAT, s != null ? s.toString() : "")
|
|
||||||
.apply();
|
|
||||||
SbtSettings.ensureReadable(requireContext());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (fontPickerButton != null) {
|
if (fontPickerButton != null) {
|
||||||
fontPickerButton.setOnClickListener(v -> showFontPickerDialog());
|
fontPickerButton.setOnClickListener(v -> showFontPickerDialog());
|
||||||
}
|
}
|
||||||
|
if (restoreStoredPatternsButton != null) {
|
||||||
|
restoreStoredPatternsButton.setOnClickListener(v -> {
|
||||||
|
persistStoredPatterns(prefs, mergeDefaultPatterns(loadStoredPatterns(prefs)));
|
||||||
|
renderAllStoredPatternLists(prefs);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return root;
|
return root;
|
||||||
}
|
}
|
||||||
@@ -169,23 +226,53 @@ public final class ClockFragment extends Fragment {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void bindCustomFormat(SharedPreferences prefs,
|
private void bindCustomFormat(SharedPreferences prefs,
|
||||||
|
MaterialCardView card,
|
||||||
CheckBox enabledView,
|
CheckBox enabledView,
|
||||||
View container,
|
View container,
|
||||||
EditText input,
|
EditText input,
|
||||||
|
ClockPatternPreviewView preview,
|
||||||
|
String origin,
|
||||||
|
int accentColor,
|
||||||
String enabledKey,
|
String enabledKey,
|
||||||
String formatKey,
|
String formatKey,
|
||||||
boolean enabled,
|
boolean enabled,
|
||||||
String format) {
|
String format,
|
||||||
|
float previewTextSizeSp,
|
||||||
|
boolean multilinePreview) {
|
||||||
if (enabledView == null || input == null) {
|
if (enabledView == null || input == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
styleClockCard(card, accentColor);
|
||||||
|
styleBorderedView(input, accentColor, 6, 8, 6);
|
||||||
|
styleBorderedView(preview, accentColor, 8, 10, 8);
|
||||||
enabledView.setChecked(enabled);
|
enabledView.setChecked(enabled);
|
||||||
input.setText(format != null ? format : "");
|
input.setText(format != null ? format : "");
|
||||||
|
if (preview != null) {
|
||||||
|
preview.setPreviewTextAppearance(
|
||||||
|
requireContext().getColor(R.color.sbt_on_surface),
|
||||||
|
previewTextSizeSp);
|
||||||
|
}
|
||||||
updateCustomFormatVisibility(container, enabled);
|
updateCustomFormatVisibility(container, enabled);
|
||||||
|
PreviewBinding binding = new PreviewBinding(enabledView, input, preview, multilinePreview);
|
||||||
|
previewBindings.add(binding);
|
||||||
|
StoredPatternBinding storedBinding = createStoredPatternSection(
|
||||||
|
prefs,
|
||||||
|
container,
|
||||||
|
input,
|
||||||
|
origin,
|
||||||
|
accentColor,
|
||||||
|
multilinePreview);
|
||||||
|
if (storedBinding != null) {
|
||||||
|
storedPatternBindings.add(storedBinding);
|
||||||
|
renderStoredPatternList(prefs, storedBinding);
|
||||||
|
}
|
||||||
|
updatePreview(binding);
|
||||||
enabledView.setOnCheckedChangeListener((buttonView, isChecked) -> {
|
enabledView.setOnCheckedChangeListener((buttonView, isChecked) -> {
|
||||||
updateCustomFormatVisibility(container, isChecked);
|
updateCustomFormatVisibility(container, isChecked);
|
||||||
prefs.edit().putBoolean(enabledKey, isChecked).apply();
|
prefs.edit().putBoolean(enabledKey, isChecked).apply();
|
||||||
SbtSettings.ensureReadable(requireContext());
|
SbtSettings.ensureReadable(requireContext());
|
||||||
|
updatePreview(binding);
|
||||||
|
schedulePreviewTickerIfNeeded();
|
||||||
});
|
});
|
||||||
input.addTextChangedListener(new TextWatcher() {
|
input.addTextChangedListener(new TextWatcher() {
|
||||||
@Override
|
@Override
|
||||||
@@ -202,10 +289,501 @@ public final class ClockFragment extends Fragment {
|
|||||||
.putString(formatKey, s != null ? s.toString() : "")
|
.putString(formatKey, s != null ? s.toString() : "")
|
||||||
.apply();
|
.apply();
|
||||||
SbtSettings.ensureReadable(requireContext());
|
SbtSettings.ensureReadable(requireContext());
|
||||||
|
updatePreview(binding);
|
||||||
|
schedulePreviewTickerIfNeeded();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void styleClockCard(MaterialCardView card, int accentColor) {
|
||||||
|
if (card == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
card.setStrokeWidth(ViewTreeSupport.dp(card.getContext(), CARD_STROKE_DP));
|
||||||
|
card.setStrokeColor(accentColor);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void styleBorderedView(View view,
|
||||||
|
int accentColor,
|
||||||
|
int radiusDp,
|
||||||
|
int horizontalPaddingDp,
|
||||||
|
int verticalPaddingDp) {
|
||||||
|
if (view == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Context context = view.getContext();
|
||||||
|
GradientDrawable background = new GradientDrawable();
|
||||||
|
background.setColor(Color.TRANSPARENT);
|
||||||
|
background.setCornerRadius(ViewTreeSupport.dp(context, radiusDp));
|
||||||
|
background.setStroke(ViewTreeSupport.dp(context, CARD_STROKE_DP), accentColor);
|
||||||
|
view.setBackground(background);
|
||||||
|
view.setPadding(
|
||||||
|
ViewTreeSupport.dp(context, horizontalPaddingDp),
|
||||||
|
ViewTreeSupport.dp(context, verticalPaddingDp),
|
||||||
|
ViewTreeSupport.dp(context, horizontalPaddingDp),
|
||||||
|
ViewTreeSupport.dp(context, verticalPaddingDp));
|
||||||
|
}
|
||||||
|
|
||||||
|
private StoredPatternBinding createStoredPatternSection(
|
||||||
|
SharedPreferences prefs,
|
||||||
|
View container,
|
||||||
|
EditText input,
|
||||||
|
String origin,
|
||||||
|
int accentColor,
|
||||||
|
boolean multilinePreview
|
||||||
|
) {
|
||||||
|
if (!(container instanceof LinearLayout parent) || input == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
Context context = parent.getContext();
|
||||||
|
MaterialButton toggle = new MaterialButton(
|
||||||
|
context,
|
||||||
|
null,
|
||||||
|
com.google.android.material.R.attr.materialButtonOutlinedStyle);
|
||||||
|
toggle.setText(R.string.clock_stored_patterns_show);
|
||||||
|
LinearLayout.LayoutParams toggleParams = new LinearLayout.LayoutParams(
|
||||||
|
ViewGroup.LayoutParams.WRAP_CONTENT,
|
||||||
|
ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||||
|
toggleParams.topMargin = ViewTreeSupport.dp(context, 12);
|
||||||
|
parent.addView(toggle, toggleParams);
|
||||||
|
|
||||||
|
LinearLayout section = new LinearLayout(context);
|
||||||
|
section.setOrientation(LinearLayout.VERTICAL);
|
||||||
|
section.setVisibility(View.GONE);
|
||||||
|
parent.addView(section, new LinearLayout.LayoutParams(
|
||||||
|
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||||
|
ViewGroup.LayoutParams.WRAP_CONTENT));
|
||||||
|
|
||||||
|
TextView hint = new TextView(context);
|
||||||
|
hint.setText(R.string.clock_stored_patterns_hint);
|
||||||
|
hint.setTextAppearance(android.R.style.TextAppearance_Material_Body2);
|
||||||
|
LinearLayout.LayoutParams hintParams = new LinearLayout.LayoutParams(
|
||||||
|
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||||
|
ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||||
|
hintParams.topMargin = ViewTreeSupport.dp(context, 8);
|
||||||
|
section.addView(hint, hintParams);
|
||||||
|
|
||||||
|
LinearLayout list = new LinearLayout(context);
|
||||||
|
list.setOrientation(LinearLayout.VERTICAL);
|
||||||
|
section.addView(list, new LinearLayout.LayoutParams(
|
||||||
|
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||||
|
ViewGroup.LayoutParams.WRAP_CONTENT));
|
||||||
|
|
||||||
|
MaterialButton storeButton = new MaterialButton(
|
||||||
|
context,
|
||||||
|
null,
|
||||||
|
com.google.android.material.R.attr.materialButtonOutlinedStyle);
|
||||||
|
storeButton.setText(R.string.clock_store_current_pattern);
|
||||||
|
LinearLayout.LayoutParams storeParams = new LinearLayout.LayoutParams(
|
||||||
|
ViewGroup.LayoutParams.WRAP_CONTENT,
|
||||||
|
ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||||
|
storeParams.topMargin = ViewTreeSupport.dp(context, 8);
|
||||||
|
section.addView(storeButton, storeParams);
|
||||||
|
|
||||||
|
StoredPatternBinding binding = new StoredPatternBinding(
|
||||||
|
input,
|
||||||
|
origin,
|
||||||
|
accentColor,
|
||||||
|
multilinePreview,
|
||||||
|
toggle,
|
||||||
|
section,
|
||||||
|
list);
|
||||||
|
toggle.setOnClickListener(v -> {
|
||||||
|
boolean show = section.getVisibility() != View.VISIBLE;
|
||||||
|
section.setVisibility(show ? View.VISIBLE : View.GONE);
|
||||||
|
toggle.setText(show
|
||||||
|
? R.string.clock_stored_patterns_hide
|
||||||
|
: R.string.clock_stored_patterns_show);
|
||||||
|
if (show) {
|
||||||
|
renderStoredPatternList(prefs, binding);
|
||||||
|
} else {
|
||||||
|
removeStoredPatternRowsFor(binding);
|
||||||
|
list.removeAllViews();
|
||||||
|
schedulePreviewTickerIfNeeded();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
storeButton.setOnClickListener(v -> {
|
||||||
|
String pattern = input.getText() != null ? input.getText().toString() : "";
|
||||||
|
storePattern(prefs, pattern, origin);
|
||||||
|
renderAllStoredPatternLists(prefs);
|
||||||
|
});
|
||||||
|
return binding;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void renderAllStoredPatternLists(SharedPreferences prefs) {
|
||||||
|
for (StoredPatternBinding binding : storedPatternBindings) {
|
||||||
|
renderStoredPatternList(prefs, binding);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void renderStoredPatternList(SharedPreferences prefs, StoredPatternBinding binding) {
|
||||||
|
if (binding == null || binding.listContainer == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Context context = binding.listContainer.getContext();
|
||||||
|
removeStoredPatternRowsFor(binding);
|
||||||
|
binding.listContainer.removeAllViews();
|
||||||
|
if (binding.section == null || binding.section.getVisibility() != View.VISIBLE) {
|
||||||
|
schedulePreviewTickerIfNeeded();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ArrayList<StoredPattern> patterns = loadStoredPatterns(prefs);
|
||||||
|
for (StoredPattern pattern : patterns) {
|
||||||
|
binding.listContainer.addView(createStoredPatternRow(context, prefs, binding, pattern));
|
||||||
|
}
|
||||||
|
schedulePreviewTickerIfNeeded();
|
||||||
|
}
|
||||||
|
|
||||||
|
private View createStoredPatternRow(
|
||||||
|
Context context,
|
||||||
|
SharedPreferences prefs,
|
||||||
|
StoredPatternBinding binding,
|
||||||
|
StoredPattern pattern
|
||||||
|
) {
|
||||||
|
LinearLayout row = new LinearLayout(context);
|
||||||
|
row.setOrientation(LinearLayout.HORIZONTAL);
|
||||||
|
row.setGravity(android.view.Gravity.CENTER_VERTICAL);
|
||||||
|
LinearLayout.LayoutParams rowParams = new LinearLayout.LayoutParams(
|
||||||
|
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||||
|
ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||||
|
rowParams.topMargin = ViewTreeSupport.dp(context, 8);
|
||||||
|
row.setLayoutParams(rowParams);
|
||||||
|
|
||||||
|
ClockPatternPreviewView preview = new ClockPatternPreviewView(context);
|
||||||
|
preview.setPreviewTextAppearance(
|
||||||
|
requireContext().getColor(R.color.sbt_on_surface),
|
||||||
|
previewTextSizeForOrigin(pattern.origin));
|
||||||
|
styleBorderedView(preview, colorForOrigin(pattern.origin), 8, 10, 8);
|
||||||
|
StoredPatternRowBinding rowBinding = new StoredPatternRowBinding(
|
||||||
|
binding,
|
||||||
|
preview,
|
||||||
|
pattern.pattern,
|
||||||
|
normaliseOrigin(pattern.origin),
|
||||||
|
binding.multilinePreview);
|
||||||
|
storedPatternRowBindings.add(rowBinding);
|
||||||
|
updateStoredPatternPreview(rowBinding, new HashMap<>());
|
||||||
|
preview.setOnClickListener(v -> {
|
||||||
|
binding.input.setText(pattern.pattern);
|
||||||
|
binding.input.setSelection(binding.input.getText() != null
|
||||||
|
? binding.input.getText().length()
|
||||||
|
: 0);
|
||||||
|
});
|
||||||
|
row.addView(preview, new LinearLayout.LayoutParams(
|
||||||
|
0,
|
||||||
|
ViewGroup.LayoutParams.WRAP_CONTENT,
|
||||||
|
1f));
|
||||||
|
|
||||||
|
MaterialButton deleteButton = new MaterialButton(context);
|
||||||
|
deleteButton.setText("×");
|
||||||
|
deleteButton.setTextColor(Color.WHITE);
|
||||||
|
deleteButton.setBackgroundTintList(android.content.res.ColorStateList.valueOf(
|
||||||
|
ContextCompat.getColor(context, R.color.sbt_danger)));
|
||||||
|
deleteButton.setMinWidth(0);
|
||||||
|
deleteButton.setMinHeight(0);
|
||||||
|
deleteButton.setPadding(0, 0, 0, 0);
|
||||||
|
deleteButton.setOnClickListener(v -> {
|
||||||
|
deleteStoredPattern(prefs, pattern.pattern);
|
||||||
|
renderAllStoredPatternLists(prefs);
|
||||||
|
});
|
||||||
|
LinearLayout.LayoutParams deleteParams = new LinearLayout.LayoutParams(
|
||||||
|
ViewTreeSupport.dp(context, 44),
|
||||||
|
ViewTreeSupport.dp(context, 44));
|
||||||
|
deleteParams.leftMargin = ViewTreeSupport.dp(context, 8);
|
||||||
|
row.addView(deleteButton, deleteParams);
|
||||||
|
return row;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void removeStoredPatternRowsFor(StoredPatternBinding binding) {
|
||||||
|
for (int i = storedPatternRowBindings.size() - 1; i >= 0; i--) {
|
||||||
|
if (storedPatternRowBindings.get(i).owner == binding) {
|
||||||
|
storedPatternRowBindings.remove(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ensureDefaultStoredPatterns(SharedPreferences prefs) {
|
||||||
|
if (prefs == null || prefs.contains(SbtSettings.KEY_CLOCK_STORED_PATTERNS)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
persistStoredPatterns(prefs, defaultStoredPatterns());
|
||||||
|
}
|
||||||
|
|
||||||
|
private ArrayList<StoredPattern> mergeDefaultPatterns(ArrayList<StoredPattern> current) {
|
||||||
|
ArrayList<StoredPattern> result = current != null ? current : new ArrayList<>();
|
||||||
|
for (StoredPattern pattern : defaultStoredPatterns()) {
|
||||||
|
upsertPattern(result, pattern.pattern, pattern.origin);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ArrayList<StoredPattern> defaultStoredPatterns() {
|
||||||
|
ArrayList<StoredPattern> patterns = new ArrayList<>();
|
||||||
|
patterns.add(new StoredPattern("H:mm:ss", PATTERN_ORIGIN_STATUSBAR));
|
||||||
|
patterns.add(new StoredPattern("{big}H:mm:{/big color(#batterybar)}ss", PATTERN_ORIGIN_STATUSBAR));
|
||||||
|
patterns.add(new StoredPattern(
|
||||||
|
"{@sans-serif-condensed-light color(#fff;#000)}|d{\\n30 big}H:mm:|{/big color(#batterybar invRGB 0.5 *RGB invRGB;#batterybar 0.5 *RGB)}ss",
|
||||||
|
PATTERN_ORIGIN_STATUSBAR));
|
||||||
|
patterns.add(new StoredPattern("{@sans-serif-condensed-light}H:mm:{small}ss", PATTERN_ORIGIN_DRAWER_CLOCK));
|
||||||
|
patterns.add(new StoredPattern("EEE, d MMM", PATTERN_ORIGIN_DRAWER_DATE));
|
||||||
|
patterns.add(new StoredPattern("EEE, MMM d", PATTERN_ORIGIN_DRAWER_DATE));
|
||||||
|
patterns.add(new StoredPattern("{@sans-serif-condensed-light}EEEE, MMMM d", PATTERN_ORIGIN_DRAWER_DATE));
|
||||||
|
patterns.add(new StoredPattern("{@sans-serif-condensed-light}EEEE, d MMMM", PATTERN_ORIGIN_DRAWER_DATE));
|
||||||
|
return patterns;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void storePattern(SharedPreferences prefs, String pattern, String origin) {
|
||||||
|
String value = pattern != null ? pattern.trim() : "";
|
||||||
|
if (value.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ArrayList<StoredPattern> patterns = loadStoredPatterns(prefs);
|
||||||
|
upsertPattern(patterns, value, origin);
|
||||||
|
persistStoredPatterns(prefs, patterns);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void upsertPattern(ArrayList<StoredPattern> patterns, String pattern, String origin) {
|
||||||
|
if (patterns == null || pattern == null || pattern.trim().isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
String value = pattern.trim();
|
||||||
|
String resolvedOrigin = normaliseOrigin(origin);
|
||||||
|
for (int i = 0; i < patterns.size(); i++) {
|
||||||
|
StoredPattern existing = patterns.get(i);
|
||||||
|
if (existing != null && value.equals(existing.pattern)) {
|
||||||
|
patterns.set(i, new StoredPattern(value, resolvedOrigin));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
patterns.add(new StoredPattern(value, resolvedOrigin));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void deleteStoredPattern(SharedPreferences prefs, String pattern) {
|
||||||
|
ArrayList<StoredPattern> patterns = loadStoredPatterns(prefs);
|
||||||
|
for (int i = patterns.size() - 1; i >= 0; i--) {
|
||||||
|
StoredPattern existing = patterns.get(i);
|
||||||
|
if (existing != null && existing.pattern.equals(pattern)) {
|
||||||
|
patterns.remove(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
persistStoredPatterns(prefs, patterns);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ArrayList<StoredPattern> loadStoredPatterns(SharedPreferences prefs) {
|
||||||
|
ArrayList<StoredPattern> patterns = new ArrayList<>();
|
||||||
|
if (prefs == null) {
|
||||||
|
return patterns;
|
||||||
|
}
|
||||||
|
String encoded = prefs.getString(SbtSettings.KEY_CLOCK_STORED_PATTERNS, "");
|
||||||
|
if (encoded == null || encoded.isEmpty()) {
|
||||||
|
return patterns;
|
||||||
|
}
|
||||||
|
String[] lines = encoded.split("\\n", -1);
|
||||||
|
for (String line : lines) {
|
||||||
|
StoredPattern pattern = decodeStoredPattern(line);
|
||||||
|
if (pattern == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
upsertPattern(patterns, pattern.pattern, pattern.origin);
|
||||||
|
}
|
||||||
|
return patterns;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void persistStoredPatterns(SharedPreferences prefs, ArrayList<StoredPattern> patterns) {
|
||||||
|
if (prefs == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
StringBuilder encoded = new StringBuilder();
|
||||||
|
if (patterns != null) {
|
||||||
|
for (StoredPattern pattern : patterns) {
|
||||||
|
if (pattern == null || pattern.pattern == null || pattern.pattern.trim().isEmpty()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (encoded.length() > 0) {
|
||||||
|
encoded.append('\n');
|
||||||
|
}
|
||||||
|
encoded.append(normaliseOrigin(pattern.origin))
|
||||||
|
.append('|')
|
||||||
|
.append(Uri.encode(pattern.pattern));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
prefs.edit().putString(SbtSettings.KEY_CLOCK_STORED_PATTERNS, encoded.toString()).apply();
|
||||||
|
}
|
||||||
|
|
||||||
|
private StoredPattern decodeStoredPattern(String line) {
|
||||||
|
if (line == null || line.isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
int split = line.indexOf('|');
|
||||||
|
if (split <= 0 || split >= line.length() - 1) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
String origin = normaliseOrigin(line.substring(0, split));
|
||||||
|
String pattern = Uri.decode(line.substring(split + 1));
|
||||||
|
if (pattern == null || pattern.trim().isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return new StoredPattern(pattern.trim(), origin);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String normaliseOrigin(String origin) {
|
||||||
|
if (PATTERN_ORIGIN_DRAWER_CLOCK.equals(origin)
|
||||||
|
|| PATTERN_ORIGIN_DRAWER_DATE.equals(origin)
|
||||||
|
|| PATTERN_ORIGIN_STATUSBAR.equals(origin)) {
|
||||||
|
return origin;
|
||||||
|
}
|
||||||
|
return PATTERN_ORIGIN_STATUSBAR;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int colorForOrigin(String origin) {
|
||||||
|
switch (normaliseOrigin(origin)) {
|
||||||
|
case PATTERN_ORIGIN_DRAWER_CLOCK:
|
||||||
|
return COLOR_DRAWER_CLOCK;
|
||||||
|
case PATTERN_ORIGIN_DRAWER_DATE:
|
||||||
|
return COLOR_DRAWER_DATE;
|
||||||
|
case PATTERN_ORIGIN_STATUSBAR:
|
||||||
|
default:
|
||||||
|
return COLOR_STATUSBAR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private float previewTextSizeForOrigin(String origin) {
|
||||||
|
switch (normaliseOrigin(origin)) {
|
||||||
|
case PATTERN_ORIGIN_DRAWER_CLOCK:
|
||||||
|
return 24f;
|
||||||
|
case PATTERN_ORIGIN_DRAWER_DATE:
|
||||||
|
return 16f;
|
||||||
|
case PATTERN_ORIGIN_STATUSBAR:
|
||||||
|
default:
|
||||||
|
return 18f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean updatePreview(PreviewBinding binding) {
|
||||||
|
if (binding == null || binding.preview == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
boolean enabled = binding.enabled != null && binding.enabled.isChecked();
|
||||||
|
String pattern = binding.input != null && binding.input.getText() != null
|
||||||
|
? binding.input.getText().toString()
|
||||||
|
: "";
|
||||||
|
if (!enabled || pattern.isEmpty()) {
|
||||||
|
binding.preview.setVisibility(View.GONE);
|
||||||
|
binding.preview.setPreview(null);
|
||||||
|
binding.preview.setContentDescription("");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
ClockPatternPreviewRenderer.Result result = ClockPatternPreviewRenderer.render(
|
||||||
|
binding.preview.getContext(),
|
||||||
|
pattern,
|
||||||
|
binding.preview.getPreviewTextColor());
|
||||||
|
if (!binding.multilinePreview) {
|
||||||
|
result = flattenRows(result);
|
||||||
|
}
|
||||||
|
binding.preview.setPreview(result);
|
||||||
|
binding.preview.setVisibility(result.rows().isEmpty() ? View.GONE : View.VISIBLE);
|
||||||
|
binding.preview.setContentDescription(result.contentDescription());
|
||||||
|
return result.containsSeconds();
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean updateStoredPatternPreview(
|
||||||
|
StoredPatternRowBinding binding,
|
||||||
|
Map<String, ClockPatternPreviewRenderer.Result> cache
|
||||||
|
) {
|
||||||
|
if (binding == null
|
||||||
|
|| binding.preview == null
|
||||||
|
|| binding.owner == null
|
||||||
|
|| binding.owner.section == null
|
||||||
|
|| binding.owner.section.getVisibility() != View.VISIBLE) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
String key = binding.origin + "\u0000" + binding.multilinePreview + "\u0000" + binding.pattern;
|
||||||
|
ClockPatternPreviewRenderer.Result result = cache != null ? cache.get(key) : null;
|
||||||
|
if (result == null) {
|
||||||
|
result = ClockPatternPreviewRenderer.render(
|
||||||
|
binding.preview.getContext(),
|
||||||
|
binding.pattern,
|
||||||
|
binding.preview.getPreviewTextColor());
|
||||||
|
if (!binding.multilinePreview) {
|
||||||
|
result = flattenRows(result);
|
||||||
|
}
|
||||||
|
if (cache != null) {
|
||||||
|
cache.put(key, result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
binding.preview.setPreview(result);
|
||||||
|
binding.preview.setContentDescription(result.contentDescription());
|
||||||
|
return result.containsSeconds();
|
||||||
|
}
|
||||||
|
|
||||||
|
private ClockPatternPreviewRenderer.Result flattenRows(ClockPatternPreviewRenderer.Result result) {
|
||||||
|
if (result == null || result.rows() == null || result.rows().size() <= 1) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
SpannableStringBuilder text = new SpannableStringBuilder();
|
||||||
|
StringBuilder description = new StringBuilder();
|
||||||
|
for (int i = 0; i < result.rows().size(); i++) {
|
||||||
|
ClockPatternPreviewRenderer.Row row = result.rows().get(i);
|
||||||
|
if (i > 0) {
|
||||||
|
text.append(' ');
|
||||||
|
description.append(' ');
|
||||||
|
}
|
||||||
|
if (row != null && row.text() != null) {
|
||||||
|
text.append(row.text());
|
||||||
|
description.append(row.text());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ArrayList<ClockPatternPreviewRenderer.Row> rows = new ArrayList<>();
|
||||||
|
rows.add(new ClockPatternPreviewRenderer.Row(text, "", "", 0, 0));
|
||||||
|
return new ClockPatternPreviewRenderer.Result(rows, description.toString(), result.containsSeconds());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void schedulePreviewTickerIfNeeded() {
|
||||||
|
View view = getView();
|
||||||
|
if (view == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
view.removeCallbacks(previewTicker);
|
||||||
|
for (PreviewBinding binding : previewBindings) {
|
||||||
|
if (updatePreview(binding)) {
|
||||||
|
view.postDelayed(previewTicker, nextSecondDelayMillis());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private long nextSecondDelayMillis() {
|
||||||
|
long now = System.currentTimeMillis();
|
||||||
|
long next = now - (now % 1000L) + 1000L;
|
||||||
|
return Math.max(1L, next - now);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onResume() {
|
||||||
|
super.onResume();
|
||||||
|
schedulePreviewTickerIfNeeded();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPause() {
|
||||||
|
View view = getView();
|
||||||
|
if (view != null) {
|
||||||
|
view.removeCallbacks(previewTicker);
|
||||||
|
}
|
||||||
|
super.onPause();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDestroyView() {
|
||||||
|
View view = getView();
|
||||||
|
if (view != null) {
|
||||||
|
view.removeCallbacks(previewTicker);
|
||||||
|
}
|
||||||
|
previewBindings.clear();
|
||||||
|
storedPatternBindings.clear();
|
||||||
|
storedPatternRowBindings.clear();
|
||||||
|
super.onDestroyView();
|
||||||
|
}
|
||||||
|
|
||||||
private void updateCustomFormatHelpVisibility(View helpContainer,
|
private void updateCustomFormatHelpVisibility(View helpContainer,
|
||||||
TextView helpToggle,
|
TextView helpToggle,
|
||||||
boolean visible) {
|
boolean visible) {
|
||||||
@@ -354,6 +932,32 @@ public final class ClockFragment extends Fragment {
|
|||||||
view.setText(builder);
|
view.setText(builder);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private record PreviewBinding(CheckBox enabled, EditText input, ClockPatternPreviewView preview, boolean multilinePreview) {
|
||||||
|
}
|
||||||
|
|
||||||
|
private record StoredPatternBinding(
|
||||||
|
EditText input,
|
||||||
|
String origin,
|
||||||
|
int accentColor,
|
||||||
|
boolean multilinePreview,
|
||||||
|
MaterialButton toggle,
|
||||||
|
View section,
|
||||||
|
LinearLayout listContainer
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
|
private record StoredPatternRowBinding(
|
||||||
|
StoredPatternBinding owner,
|
||||||
|
ClockPatternPreviewView preview,
|
||||||
|
String pattern,
|
||||||
|
String origin,
|
||||||
|
boolean multilinePreview
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
|
private record StoredPattern(String pattern, String origin) {
|
||||||
|
}
|
||||||
|
|
||||||
private static final class FontFamilyAdapter extends BaseAdapter {
|
private static final class FontFamilyAdapter extends BaseAdapter {
|
||||||
private static final String SAMPLE_PATTERN = "EEEE HH:mm:ss";
|
private static final String SAMPLE_PATTERN = "EEEE HH:mm:ss";
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,228 @@
|
|||||||
|
package se.ajpanton.statusbartweak.shell.ui;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.graphics.Canvas;
|
||||||
|
import android.text.Layout;
|
||||||
|
import android.text.StaticLayout;
|
||||||
|
import android.text.TextPaint;
|
||||||
|
import android.text.TextUtils;
|
||||||
|
import android.util.AttributeSet;
|
||||||
|
import android.util.TypedValue;
|
||||||
|
import android.view.View;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
import se.ajpanton.statusbartweak.runtime.features.clock.ClockPatternPreviewRenderer;
|
||||||
|
import se.ajpanton.statusbartweak.shell.settings.SbtDefaults;
|
||||||
|
|
||||||
|
public final class ClockPatternPreviewView extends View {
|
||||||
|
private final TextPaint textPaint = new TextPaint(TextPaint.ANTI_ALIAS_FLAG);
|
||||||
|
private final ArrayList<MeasuredRow> measuredRows = new ArrayList<>();
|
||||||
|
private ClockPatternPreviewRenderer.Result result;
|
||||||
|
private int textColor;
|
||||||
|
private float textSizePx;
|
||||||
|
private int contentWidth;
|
||||||
|
private int contentHeight;
|
||||||
|
|
||||||
|
public ClockPatternPreviewView(Context context) {
|
||||||
|
super(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ClockPatternPreviewView(Context context, AttributeSet attrs) {
|
||||||
|
super(context, attrs);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPreviewTextAppearance(int color, float sizeSp) {
|
||||||
|
textColor = color;
|
||||||
|
textSizePx = TypedValue.applyDimension(
|
||||||
|
TypedValue.COMPLEX_UNIT_SP,
|
||||||
|
sizeSp,
|
||||||
|
getResources().getDisplayMetrics());
|
||||||
|
textPaint.setColor(textColor);
|
||||||
|
textPaint.setTextSize(textSizePx);
|
||||||
|
rebuildMeasurements();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getPreviewTextColor() {
|
||||||
|
return textColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPreview(ClockPatternPreviewRenderer.Result nextResult) {
|
||||||
|
result = nextResult;
|
||||||
|
rebuildMeasurements();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void rebuildMeasurements() {
|
||||||
|
measuredRows.clear();
|
||||||
|
contentWidth = 0;
|
||||||
|
contentHeight = 0;
|
||||||
|
if (result == null || result.rows() == null || result.rows().isEmpty()) {
|
||||||
|
requestLayout();
|
||||||
|
invalidate();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int leftColumnWidth = 0;
|
||||||
|
int rightColumnWidth = 0;
|
||||||
|
int baseRowHeight = measureText("0").height;
|
||||||
|
for (ClockPatternPreviewRenderer.Row row : result.rows()) {
|
||||||
|
MeasuredRow measured = measureRow(row);
|
||||||
|
measuredRows.add(measured);
|
||||||
|
if (measured.piped) {
|
||||||
|
leftColumnWidth = Math.max(leftColumnWidth, measured.left.width);
|
||||||
|
rightColumnWidth = Math.max(rightColumnWidth, measured.right.width);
|
||||||
|
} else {
|
||||||
|
contentWidth = Math.max(contentWidth, measured.full.width);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (leftColumnWidth > 0 || rightColumnWidth > 0) {
|
||||||
|
contentWidth = Math.max(contentWidth, leftColumnWidth + rightColumnWidth);
|
||||||
|
for (MeasuredRow measured : measuredRows) {
|
||||||
|
measured.leftColumnWidth = leftColumnWidth;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
positionRows(Math.max(1, baseRowHeight));
|
||||||
|
requestLayout();
|
||||||
|
invalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
private MeasuredRow measureRow(ClockPatternPreviewRenderer.Row row) {
|
||||||
|
if (row != null && row.pipeCount() > 0) {
|
||||||
|
MeasuredText left = measureText(row.leftText());
|
||||||
|
MeasuredText right = measureText(row.rightText());
|
||||||
|
return MeasuredRow.piped(left, right, row.gapBeforePx());
|
||||||
|
}
|
||||||
|
return MeasuredRow.full(measureText(row != null ? row.text() : ""), row != null ? row.gapBeforePx() : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private MeasuredText measureText(CharSequence text) {
|
||||||
|
CharSequence safeText = text != null ? text : "";
|
||||||
|
if (TextUtils.isEmpty(safeText)) {
|
||||||
|
safeText = " ";
|
||||||
|
}
|
||||||
|
int width = Math.max(1, (int) Math.ceil(Layout.getDesiredWidth(safeText, textPaint)));
|
||||||
|
StaticLayout layout = StaticLayout.Builder
|
||||||
|
.obtain(safeText, 0, safeText.length(), textPaint, width)
|
||||||
|
.setAlignment(Layout.Alignment.ALIGN_NORMAL)
|
||||||
|
.setIncludePad(false)
|
||||||
|
.setLineSpacing(0f, 1f)
|
||||||
|
.build();
|
||||||
|
if (layout.getLineCount() <= 0) {
|
||||||
|
TextPaint.FontMetricsInt metrics = textPaint.getFontMetricsInt();
|
||||||
|
int ascent = -metrics.ascent;
|
||||||
|
int descent = metrics.descent;
|
||||||
|
return new MeasuredText(layout, width, Math.max(1, ascent + descent), ascent, descent);
|
||||||
|
}
|
||||||
|
int baseline = layout.getLineBaseline(0);
|
||||||
|
int ascent = Math.max(0, baseline - layout.getLineTop(0));
|
||||||
|
int descent = Math.max(0, layout.getLineBottom(0) - baseline);
|
||||||
|
return new MeasuredText(layout, width, Math.max(1, ascent + descent), ascent, descent);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void positionRows(int baseRowHeight) {
|
||||||
|
if (measuredRows.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int minTop = Integer.MAX_VALUE;
|
||||||
|
int maxBottom = Integer.MIN_VALUE;
|
||||||
|
int bottom = 0;
|
||||||
|
for (int i = 0; i < measuredRows.size(); i++) {
|
||||||
|
MeasuredRow row = measuredRows.get(i);
|
||||||
|
if (i == 0) {
|
||||||
|
bottom = row.height;
|
||||||
|
} else {
|
||||||
|
bottom += resolveGapPx(row.gapBeforePx, baseRowHeight);
|
||||||
|
}
|
||||||
|
int top = bottom - row.height;
|
||||||
|
row.drawTop = top;
|
||||||
|
minTop = Math.min(minTop, top);
|
||||||
|
maxBottom = Math.max(maxBottom, bottom);
|
||||||
|
}
|
||||||
|
for (MeasuredRow row : measuredRows) {
|
||||||
|
row.drawTop -= minTop;
|
||||||
|
}
|
||||||
|
contentHeight = Math.max(1, maxBottom - minTop);
|
||||||
|
}
|
||||||
|
|
||||||
|
private int resolveGapPx(int gapBefore, int baseRowHeight) {
|
||||||
|
if (gapBefore == ClockPatternPreviewRenderer.ROW_GAP_AUTO) {
|
||||||
|
return Math.max(1, baseRowHeight);
|
||||||
|
}
|
||||||
|
if (gapBefore == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
int defaultSteps = Math.max(1, SbtDefaults.CLOCK_TEXT_SIZE_STEPS_DEFAULT);
|
||||||
|
return Math.max(1, Math.round(gapBefore * Math.max(1, baseRowHeight) / (float) defaultSteps));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||||
|
int desiredWidth = getPaddingLeft() + contentWidth + getPaddingRight();
|
||||||
|
int desiredHeight = getPaddingTop() + contentHeight + getPaddingBottom();
|
||||||
|
setMeasuredDimension(
|
||||||
|
resolveSize(desiredWidth, widthMeasureSpec),
|
||||||
|
resolveSize(desiredHeight, heightMeasureSpec));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onDraw(Canvas canvas) {
|
||||||
|
super.onDraw(canvas);
|
||||||
|
for (MeasuredRow row : measuredRows) {
|
||||||
|
int y = getPaddingTop() + row.drawTop;
|
||||||
|
if (row.piped) {
|
||||||
|
drawText(canvas, row.left, getPaddingLeft() + row.leftColumnWidth - row.left.width, y + row.ascent - row.left.ascent);
|
||||||
|
drawText(canvas, row.right, getPaddingLeft() + row.leftColumnWidth, y + row.ascent - row.right.ascent);
|
||||||
|
} else {
|
||||||
|
drawText(canvas, row.full, getPaddingLeft(), y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void drawText(Canvas canvas, MeasuredText text, int left, int top) {
|
||||||
|
if (text == null || text.layout == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int save = canvas.save();
|
||||||
|
canvas.translate(left, top);
|
||||||
|
text.layout.draw(canvas);
|
||||||
|
canvas.restoreToCount(save);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class MeasuredRow {
|
||||||
|
final boolean piped;
|
||||||
|
final MeasuredText full;
|
||||||
|
final MeasuredText left;
|
||||||
|
final MeasuredText right;
|
||||||
|
final int height;
|
||||||
|
final int ascent;
|
||||||
|
final int descent;
|
||||||
|
final int gapBeforePx;
|
||||||
|
int leftColumnWidth;
|
||||||
|
int drawTop;
|
||||||
|
|
||||||
|
private MeasuredRow(boolean piped, MeasuredText full, MeasuredText left, MeasuredText right, int gapBeforePx) {
|
||||||
|
this.piped = piped;
|
||||||
|
this.full = full;
|
||||||
|
this.left = left;
|
||||||
|
this.right = right;
|
||||||
|
this.gapBeforePx = gapBeforePx;
|
||||||
|
this.ascent = piped
|
||||||
|
? Math.max(left != null ? left.ascent : 0, right != null ? right.ascent : 0)
|
||||||
|
: full != null ? full.ascent : 0;
|
||||||
|
this.descent = piped
|
||||||
|
? Math.max(left != null ? left.descent : 0, right != null ? right.descent : 0)
|
||||||
|
: full != null ? full.descent : 0;
|
||||||
|
this.height = Math.max(1, ascent + descent);
|
||||||
|
}
|
||||||
|
|
||||||
|
static MeasuredRow full(MeasuredText text, int gapBeforePx) {
|
||||||
|
return new MeasuredRow(false, text, null, null, gapBeforePx);
|
||||||
|
}
|
||||||
|
|
||||||
|
static MeasuredRow piped(MeasuredText left, MeasuredText right, int gapBeforePx) {
|
||||||
|
return new MeasuredRow(true, null, left, right, gapBeforePx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private record MeasuredText(StaticLayout layout, int width, int height, int ascent, int descent) {
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -338,7 +338,8 @@ public class IconsDebugFragment extends Fragment {
|
|||||||
|
|
||||||
if (TextUtils.isEmpty(cameraId)) {
|
if (TextUtils.isEmpty(cameraId)) {
|
||||||
identifiedView.setText(getString(R.string.debug_camera_identified_as,
|
identifiedView.setText(getString(R.string.debug_camera_identified_as,
|
||||||
getString(R.string.debug_camera_unknown)));
|
getString(R.string.debug_camera_unknown),
|
||||||
|
""));
|
||||||
if (overrideRow != null) {
|
if (overrideRow != null) {
|
||||||
overrideRow.setVisibility(View.GONE);
|
overrideRow.setVisibility(View.GONE);
|
||||||
}
|
}
|
||||||
@@ -352,7 +353,8 @@ public class IconsDebugFragment extends Fragment {
|
|||||||
: ClockCameraTypeSupport.normalizeType(effectiveTypeFromReport));
|
: ClockCameraTypeSupport.normalizeType(effectiveTypeFromReport));
|
||||||
identifiedView.setText(getString(
|
identifiedView.setText(getString(
|
||||||
R.string.debug_camera_identified_as,
|
R.string.debug_camera_identified_as,
|
||||||
readableType(currentAutoType)));
|
readableType(currentAutoType),
|
||||||
|
cameraId));
|
||||||
|
|
||||||
if (overrideRow != null && overrideView != null && actionButton != null) {
|
if (overrideRow != null && overrideView != null && actionButton != null) {
|
||||||
overrideRow.setVisibility(View.VISIBLE);
|
overrideRow.setVisibility(View.VISIBLE);
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import android.view.inputmethod.EditorInfo;
|
|||||||
import android.widget.CheckBox;
|
import android.widget.CheckBox;
|
||||||
import android.widget.EditText;
|
import android.widget.EditText;
|
||||||
import android.widget.LinearLayout;
|
import android.widget.LinearLayout;
|
||||||
|
import android.widget.RadioButton;
|
||||||
import android.widget.RadioGroup;
|
import android.widget.RadioGroup;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
@@ -148,8 +149,6 @@ public final class LayoutFragment extends Fragment {
|
|||||||
R.string.layout_notification_position_title,
|
R.string.layout_notification_position_title,
|
||||||
SbtSettings.KEY_LAYOUT_NOTIF_UNLOCKED_COUNT,
|
SbtSettings.KEY_LAYOUT_NOTIF_UNLOCKED_COUNT,
|
||||||
SbtDefaults.LAYOUT_NOTIF_UNLOCKED_COUNT_DEFAULT,
|
SbtDefaults.LAYOUT_NOTIF_UNLOCKED_COUNT_DEFAULT,
|
||||||
SbtSettings.KEY_LAYOUT_NOTIF_ENABLED_UNLOCKED,
|
|
||||||
SbtDefaults.LAYOUT_NOTIF_ENABLED_UNLOCKED_DEFAULT,
|
|
||||||
SbtSettings::layoutNotifPositionKey,
|
SbtSettings::layoutNotifPositionKey,
|
||||||
SbtSettings::layoutNotifMiddleSideKey,
|
SbtSettings::layoutNotifMiddleSideKey,
|
||||||
SbtSettings::layoutNotifVerticalOffsetKey,
|
SbtSettings::layoutNotifVerticalOffsetKey,
|
||||||
@@ -166,8 +165,6 @@ public final class LayoutFragment extends Fragment {
|
|||||||
R.string.layout_status_position_title,
|
R.string.layout_status_position_title,
|
||||||
SbtSettings.KEY_LAYOUT_STATUS_UNLOCKED_COUNT,
|
SbtSettings.KEY_LAYOUT_STATUS_UNLOCKED_COUNT,
|
||||||
SbtDefaults.LAYOUT_STATUS_UNLOCKED_COUNT_DEFAULT,
|
SbtDefaults.LAYOUT_STATUS_UNLOCKED_COUNT_DEFAULT,
|
||||||
SbtSettings.KEY_LAYOUT_STATUS_ENABLED_UNLOCKED,
|
|
||||||
SbtDefaults.LAYOUT_STATUS_ENABLED_UNLOCKED_DEFAULT,
|
|
||||||
SbtSettings::layoutStatusPositionKey,
|
SbtSettings::layoutStatusPositionKey,
|
||||||
SbtSettings::layoutStatusMiddleSideKey,
|
SbtSettings::layoutStatusMiddleSideKey,
|
||||||
SbtSettings::layoutStatusVerticalOffsetKey,
|
SbtSettings::layoutStatusVerticalOffsetKey,
|
||||||
@@ -187,8 +184,6 @@ public final class LayoutFragment extends Fragment {
|
|||||||
int sectionTitleId,
|
int sectionTitleId,
|
||||||
String countKey,
|
String countKey,
|
||||||
int countDefault,
|
int countDefault,
|
||||||
String legacyEnabledKey,
|
|
||||||
boolean legacyEnabledDefault,
|
|
||||||
SbtSettings.IndexedKey positionKey,
|
SbtSettings.IndexedKey positionKey,
|
||||||
SbtSettings.IndexedKey middleSideKey,
|
SbtSettings.IndexedKey middleSideKey,
|
||||||
SbtSettings.IndexedKey verticalOffsetKey,
|
SbtSettings.IndexedKey verticalOffsetKey,
|
||||||
@@ -223,9 +218,7 @@ public final class LayoutFragment extends Fragment {
|
|||||||
int count = ViewTreeSupport.iconContainerCount(
|
int count = ViewTreeSupport.iconContainerCount(
|
||||||
prefs,
|
prefs,
|
||||||
countKey,
|
countKey,
|
||||||
legacyEnabledKey,
|
countDefault);
|
||||||
countDefault,
|
|
||||||
legacyEnabledDefault);
|
|
||||||
LayoutXmlCardSupport.setIndexedCardTitle(requireContext(), sourceCard, sectionTitleId, 0, count);
|
LayoutXmlCardSupport.setIndexedCardTitle(requireContext(), sourceCard, sectionTitleId, 0, count);
|
||||||
setCompatPadding(sourceCard, count <= 1);
|
setCompatPadding(sourceCard, count <= 1);
|
||||||
boolean cardsMode = LockedNotificationModeUi.MODE_CARDS.equals(
|
boolean cardsMode = LockedNotificationModeUi.MODE_CARDS.equals(
|
||||||
@@ -246,7 +239,6 @@ public final class LayoutFragment extends Fragment {
|
|||||||
requireContext(),
|
requireContext(),
|
||||||
prefs,
|
prefs,
|
||||||
countKey,
|
countKey,
|
||||||
legacyEnabledKey,
|
|
||||||
value),
|
value),
|
||||||
() -> {
|
() -> {
|
||||||
if (rebuild[0] != null) {
|
if (rebuild[0] != null) {
|
||||||
@@ -868,15 +860,30 @@ public final class LayoutFragment extends Fragment {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private int resolveLeftSideId(RadioGroup group) {
|
private int resolveLeftSideId(RadioGroup group) {
|
||||||
int id = group.getChildCount() > 0 ? group.getChildAt(0).getId() : View.NO_ID;
|
int id = sideButtonId(group, 0);
|
||||||
return id != View.NO_ID ? id : 0;
|
return id != View.NO_ID ? id : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private int resolveRightSideId(RadioGroup group) {
|
private int resolveRightSideId(RadioGroup group) {
|
||||||
int id = group.getChildCount() > 1 ? group.getChildAt(1).getId() : View.NO_ID;
|
int id = sideButtonId(group, 1);
|
||||||
return id != View.NO_ID ? id : resolveLeftSideId(group);
|
return id != View.NO_ID ? id : resolveLeftSideId(group);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private int sideButtonId(RadioGroup group, int radioIndex) {
|
||||||
|
int seen = 0;
|
||||||
|
for (int i = 0; i < group.getChildCount(); i++) {
|
||||||
|
View child = group.getChildAt(i);
|
||||||
|
if (!(child instanceof RadioButton)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (seen == radioIndex) {
|
||||||
|
return child.getId();
|
||||||
|
}
|
||||||
|
seen++;
|
||||||
|
}
|
||||||
|
return View.NO_ID;
|
||||||
|
}
|
||||||
|
|
||||||
private void setNumericText(EditText input, int value) {
|
private void setNumericText(EditText input, int value) {
|
||||||
if (input == null) {
|
if (input == null) {
|
||||||
return;
|
return;
|
||||||
|
|||||||
+3
-14
@@ -10,6 +10,7 @@ import android.view.ViewGroup;
|
|||||||
import android.widget.CheckBox;
|
import android.widget.CheckBox;
|
||||||
import android.widget.EditText;
|
import android.widget.EditText;
|
||||||
import android.widget.LinearLayout;
|
import android.widget.LinearLayout;
|
||||||
|
import android.widget.RadioButton;
|
||||||
import android.widget.RadioGroup;
|
import android.widget.RadioGroup;
|
||||||
import android.widget.ScrollView;
|
import android.widget.ScrollView;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
@@ -173,8 +174,6 @@ abstract class LockedSceneLayoutFragment extends Fragment {
|
|||||||
R.id.status_vertical_offset_plus_fast,
|
R.id.status_vertical_offset_plus_fast,
|
||||||
R.id.status_vertical_offset_reset,
|
R.id.status_vertical_offset_reset,
|
||||||
R.string.layout_status_position_title,
|
R.string.layout_status_position_title,
|
||||||
aod ? SbtSettings.KEY_LAYOUT_STATUS_ENABLED_AOD : SbtSettings.KEY_LAYOUT_STATUS_ENABLED_LOCKSCREEN,
|
|
||||||
aod ? SbtDefaults.LAYOUT_STATUS_ENABLED_AOD_DEFAULT : SbtDefaults.LAYOUT_STATUS_ENABLED_LOCKSCREEN_DEFAULT,
|
|
||||||
SbtDefaults.LAYOUT_STATUS_POSITION_DEFAULT,
|
SbtDefaults.LAYOUT_STATUS_POSITION_DEFAULT,
|
||||||
SbtDefaults.LAYOUT_STATUS_MIDDLE_SIDE_DEFAULT,
|
SbtDefaults.LAYOUT_STATUS_MIDDLE_SIDE_DEFAULT,
|
||||||
SbtDefaults.LAYOUT_STATUS_VERTICAL_OFFSET_PX_DEFAULT,
|
SbtDefaults.LAYOUT_STATUS_VERTICAL_OFFSET_PX_DEFAULT,
|
||||||
@@ -205,8 +204,6 @@ abstract class LockedSceneLayoutFragment extends Fragment {
|
|||||||
R.id.notif_vertical_offset_plus_fast,
|
R.id.notif_vertical_offset_plus_fast,
|
||||||
R.id.notif_vertical_offset_reset,
|
R.id.notif_vertical_offset_reset,
|
||||||
R.string.layout_notification_position_title,
|
R.string.layout_notification_position_title,
|
||||||
aod ? SbtSettings.KEY_LAYOUT_NOTIF_ENABLED_AOD : SbtSettings.KEY_LAYOUT_NOTIF_ENABLED_LOCKSCREEN,
|
|
||||||
aod ? SbtDefaults.LAYOUT_NOTIF_ENABLED_AOD_DEFAULT : SbtDefaults.LAYOUT_NOTIF_ENABLED_LOCKSCREEN_DEFAULT,
|
|
||||||
SbtDefaults.LAYOUT_NOTIF_POSITION_DEFAULT,
|
SbtDefaults.LAYOUT_NOTIF_POSITION_DEFAULT,
|
||||||
SbtDefaults.LAYOUT_NOTIF_MIDDLE_SIDE_DEFAULT,
|
SbtDefaults.LAYOUT_NOTIF_MIDDLE_SIDE_DEFAULT,
|
||||||
SbtDefaults.LAYOUT_NOTIF_VERTICAL_OFFSET_PX_DEFAULT,
|
SbtDefaults.LAYOUT_NOTIF_VERTICAL_OFFSET_PX_DEFAULT,
|
||||||
@@ -305,8 +302,6 @@ abstract class LockedSceneLayoutFragment extends Fragment {
|
|||||||
int verticalPlusFastId,
|
int verticalPlusFastId,
|
||||||
int verticalResetId,
|
int verticalResetId,
|
||||||
int titleId,
|
int titleId,
|
||||||
String legacyEnabledKey,
|
|
||||||
boolean legacyEnabledDefault,
|
|
||||||
String positionDefault,
|
String positionDefault,
|
||||||
String middleSideDefault,
|
String middleSideDefault,
|
||||||
int verticalOffsetDefault,
|
int verticalOffsetDefault,
|
||||||
@@ -324,9 +319,7 @@ abstract class LockedSceneLayoutFragment extends Fragment {
|
|||||||
int count = ViewTreeSupport.iconContainerCount(
|
int count = ViewTreeSupport.iconContainerCount(
|
||||||
prefs,
|
prefs,
|
||||||
countKey,
|
countKey,
|
||||||
legacyEnabledKey,
|
1);
|
||||||
1,
|
|
||||||
legacyEnabledDefault);
|
|
||||||
View primary = configuredIconContainerCard(
|
View primary = configuredIconContainerCard(
|
||||||
context,
|
context,
|
||||||
prefs,
|
prefs,
|
||||||
@@ -354,7 +347,6 @@ abstract class LockedSceneLayoutFragment extends Fragment {
|
|||||||
verticalOffsetDefault,
|
verticalOffsetDefault,
|
||||||
iconHeightKey,
|
iconHeightKey,
|
||||||
iconHeightDefault,
|
iconHeightDefault,
|
||||||
legacyEnabledKey,
|
|
||||||
countKey,
|
countKey,
|
||||||
count,
|
count,
|
||||||
rebuild[0]);
|
rebuild[0]);
|
||||||
@@ -390,7 +382,6 @@ abstract class LockedSceneLayoutFragment extends Fragment {
|
|||||||
null,
|
null,
|
||||||
0,
|
0,
|
||||||
null,
|
null,
|
||||||
null,
|
|
||||||
count,
|
count,
|
||||||
null);
|
null);
|
||||||
setTopMargin(context, copy, 0);
|
setTopMargin(context, copy, 0);
|
||||||
@@ -452,7 +443,6 @@ abstract class LockedSceneLayoutFragment extends Fragment {
|
|||||||
int verticalOffsetDefault,
|
int verticalOffsetDefault,
|
||||||
@Nullable String iconHeightKey,
|
@Nullable String iconHeightKey,
|
||||||
int iconHeightDefault,
|
int iconHeightDefault,
|
||||||
@Nullable String legacyEnabledKey,
|
|
||||||
@Nullable String countKey,
|
@Nullable String countKey,
|
||||||
int count,
|
int count,
|
||||||
@Nullable Runnable onCountChanged
|
@Nullable Runnable onCountChanged
|
||||||
@@ -470,7 +460,6 @@ abstract class LockedSceneLayoutFragment extends Fragment {
|
|||||||
context,
|
context,
|
||||||
prefs,
|
prefs,
|
||||||
countKey,
|
countKey,
|
||||||
legacyEnabledKey,
|
|
||||||
value),
|
value),
|
||||||
onCountChanged),
|
onCountChanged),
|
||||||
insertIndex);
|
insertIndex);
|
||||||
@@ -787,7 +776,7 @@ abstract class LockedSceneLayoutFragment extends Fragment {
|
|||||||
private int firstSideId(RadioGroup group, int rightId) {
|
private int firstSideId(RadioGroup group, int rightId) {
|
||||||
for (int i = 0; i < group.getChildCount(); i++) {
|
for (int i = 0; i < group.getChildCount(); i++) {
|
||||||
View child = group.getChildAt(i);
|
View child = group.getChildAt(i);
|
||||||
if (child.getId() != rightId) {
|
if (child instanceof RadioButton && child.getId() != rightId) {
|
||||||
return child.getId();
|
return child.getId();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,10 +5,15 @@ import android.content.Intent;
|
|||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import androidx.activity.OnBackPressedCallback;
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.appcompat.app.ActionBarDrawerToggle;
|
import androidx.appcompat.app.ActionBarDrawerToggle;
|
||||||
import androidx.appcompat.app.AppCompatActivity;
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
|
import androidx.drawerlayout.widget.DrawerLayout;
|
||||||
import androidx.core.graphics.Insets;
|
import androidx.core.graphics.Insets;
|
||||||
import androidx.core.view.GravityCompat;
|
import androidx.core.view.GravityCompat;
|
||||||
import androidx.core.view.ViewCompat;
|
import androidx.core.view.ViewCompat;
|
||||||
@@ -22,10 +27,18 @@ import se.ajpanton.statusbartweak.R;
|
|||||||
|
|
||||||
public class MainActivity extends AppCompatActivity implements NavigationView.OnNavigationItemSelectedListener {
|
public class MainActivity extends AppCompatActivity implements NavigationView.OnNavigationItemSelectedListener {
|
||||||
private static final String EXTRA_OPEN_NAV_ITEM = "open_nav_item";
|
private static final String EXTRA_OPEN_NAV_ITEM = "open_nav_item";
|
||||||
|
private static final String STATE_CURRENT_ITEM_ID = "current_item_id";
|
||||||
|
private static final float PERMANENT_SIDEBAR_FOLDED_WIDTH_MULTIPLIER = 1.1f;
|
||||||
|
|
||||||
private SafeInsetDrawerLayout drawerLayout;
|
private SafeInsetDrawerLayout drawerLayout;
|
||||||
private NavigationView navigationView;
|
private NavigationView drawerNavigationView;
|
||||||
private Integer navigationBasePaddingLeft;
|
private NavigationView permanentNavigationView;
|
||||||
|
private MaterialToolbar toolbar;
|
||||||
|
private ActionBarDrawerToggle drawerToggle;
|
||||||
|
private Integer drawerNavigationBasePaddingLeft;
|
||||||
|
private Integer permanentNavigationBasePaddingLeft;
|
||||||
|
private boolean permanentSidebar;
|
||||||
|
private int currentItemId = R.id.nav_layout;
|
||||||
|
|
||||||
public static Intent newOpenDebugIntent(Context context) {
|
public static Intent newOpenDebugIntent(Context context) {
|
||||||
Intent intent = new Intent(context, MainActivity.class);
|
Intent intent = new Intent(context, MainActivity.class);
|
||||||
@@ -39,34 +52,63 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
|
|||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
setContentView(R.layout.activity_main);
|
setContentView(R.layout.activity_main);
|
||||||
|
|
||||||
MaterialToolbar toolbar = findViewById(R.id.toolbar);
|
toolbar = findViewById(R.id.toolbar);
|
||||||
setSupportActionBar(toolbar);
|
setSupportActionBar(toolbar);
|
||||||
|
|
||||||
drawerLayout = findViewById(R.id.drawer_layout);
|
drawerLayout = findViewById(R.id.drawer_layout);
|
||||||
navigationView = findViewById(R.id.nav_view);
|
drawerNavigationView = findViewById(R.id.nav_view_drawer);
|
||||||
navigationView.setNavigationItemSelectedListener(this);
|
permanentNavigationView = findViewById(R.id.nav_view_permanent);
|
||||||
navigationView.getMenu().setGroupCheckable(0, true, true);
|
setupNavigationView(drawerNavigationView);
|
||||||
|
setupNavigationView(permanentNavigationView);
|
||||||
applyNavigationDrawerInsets();
|
applyNavigationDrawerInsets();
|
||||||
|
drawerToggle = new ActionBarDrawerToggle(
|
||||||
ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(
|
|
||||||
this,
|
this,
|
||||||
drawerLayout,
|
drawerLayout,
|
||||||
toolbar,
|
toolbar,
|
||||||
R.string.nav_open,
|
R.string.nav_open,
|
||||||
R.string.nav_close
|
R.string.nav_close
|
||||||
);
|
);
|
||||||
drawerLayout.addDrawerListener(toggle);
|
drawerLayout.addDrawerListener(drawerToggle);
|
||||||
toggle.syncState();
|
drawerToggle.syncState();
|
||||||
|
toolbar.setNavigationOnClickListener(v -> {
|
||||||
|
if (!permanentSidebar) {
|
||||||
|
drawerLayout.openDrawer(GravityCompat.START);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
getOnBackPressedDispatcher().addCallback(this, new OnBackPressedCallback(true) {
|
||||||
|
@Override
|
||||||
|
public void handleOnBackPressed() {
|
||||||
|
if (!permanentSidebar && drawerLayout.isDrawerOpen(GravityCompat.START)) {
|
||||||
|
drawerLayout.closeDrawer(GravityCompat.START);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setEnabled(false);
|
||||||
|
getOnBackPressedDispatcher().onBackPressed();
|
||||||
|
setEnabled(true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (savedInstanceState != null) {
|
||||||
|
currentItemId = savedInstanceState.getInt(STATE_CURRENT_ITEM_ID, currentItemId);
|
||||||
|
}
|
||||||
if (savedInstanceState == null) {
|
if (savedInstanceState == null) {
|
||||||
int initialItemId = resolveInitialItemId(getIntent());
|
int initialItemId = resolveInitialItemId(getIntent());
|
||||||
navigateTo(initialItemId);
|
navigateTo(initialItemId);
|
||||||
consumeOpenNavItemExtra(getIntent());
|
consumeOpenNavItemExtra(getIntent());
|
||||||
} else {
|
} else {
|
||||||
|
syncDrawerSelection(currentItemId);
|
||||||
|
setTitle(resolveTitle(currentItemId));
|
||||||
|
drawerLayout.post(this::applyPageTitleVisibility);
|
||||||
handleOpenNavIntent(getIntent());
|
handleOpenNavIntent(getIntent());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onSaveInstanceState(@NonNull Bundle outState) {
|
||||||
|
outState.putInt(STATE_CURRENT_ITEM_ID, currentItemId);
|
||||||
|
super.onSaveInstanceState(outState);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onNewIntent(Intent intent) {
|
protected void onNewIntent(Intent intent) {
|
||||||
super.onNewIntent(intent);
|
super.onNewIntent(intent);
|
||||||
@@ -77,11 +119,14 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
|
|||||||
@Override
|
@Override
|
||||||
public boolean onNavigationItemSelected(@NonNull android.view.MenuItem item) {
|
public boolean onNavigationItemSelected(@NonNull android.view.MenuItem item) {
|
||||||
navigateTo(item.getItemId());
|
navigateTo(item.getItemId());
|
||||||
|
if (!permanentSidebar) {
|
||||||
drawerLayout.closeDrawer(GravityCompat.START);
|
drawerLayout.closeDrawer(GravityCompat.START);
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void navigateTo(int itemId) {
|
private void navigateTo(int itemId) {
|
||||||
|
currentItemId = itemId;
|
||||||
syncDrawerSelection(itemId);
|
syncDrawerSelection(itemId);
|
||||||
Fragment fragment;
|
Fragment fragment;
|
||||||
if (itemId == R.id.nav_system_icons) {
|
if (itemId == R.id.nav_system_icons) {
|
||||||
@@ -110,6 +155,7 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
|
|||||||
getSupportFragmentManager()
|
getSupportFragmentManager()
|
||||||
.beginTransaction()
|
.beginTransaction()
|
||||||
.replace(R.id.content_frame, fragment)
|
.replace(R.id.content_frame, fragment)
|
||||||
|
.runOnCommit(this::applyPageTitleVisibility)
|
||||||
.commit();
|
.commit();
|
||||||
setTitle(resolveTitle(itemId));
|
setTitle(resolveTitle(itemId));
|
||||||
}
|
}
|
||||||
@@ -149,6 +195,11 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void syncDrawerSelection(int itemId) {
|
private void syncDrawerSelection(int itemId) {
|
||||||
|
syncNavigationSelection(drawerNavigationView, itemId);
|
||||||
|
syncNavigationSelection(permanentNavigationView, itemId);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void syncNavigationSelection(NavigationView navigationView, int itemId) {
|
||||||
if (navigationView == null) {
|
if (navigationView == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -167,6 +218,17 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
|
|||||||
navigationView.invalidate();
|
navigationView.invalidate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void setupNavigationView(NavigationView navigationView) {
|
||||||
|
if (navigationView == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
navigationView.setNavigationItemSelectedListener(this);
|
||||||
|
navigationView.getMenu().setGroupCheckable(0, true, true);
|
||||||
|
navigationView.addOnLayoutChangeListener((view, left, top, right, bottom,
|
||||||
|
oldLeft, oldTop, oldRight, oldBottom) ->
|
||||||
|
updateNavigationHeaderHeight(navigationView));
|
||||||
|
}
|
||||||
|
|
||||||
private int resolveInitialItemId(Intent intent) {
|
private int resolveInitialItemId(Intent intent) {
|
||||||
int requested = resolveOpenItemId(intent);
|
int requested = resolveOpenItemId(intent);
|
||||||
return requested != -1 ? requested : R.id.nav_layout;
|
return requested != -1 ? requested : R.id.nav_layout;
|
||||||
@@ -195,6 +257,7 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
|
|||||||
private void applyNavigationDrawerMarginsFromRootInsets() {
|
private void applyNavigationDrawerMarginsFromRootInsets() {
|
||||||
WindowInsetsCompat insets = ViewCompat.getRootWindowInsets(drawerLayout);
|
WindowInsetsCompat insets = ViewCompat.getRootWindowInsets(drawerLayout);
|
||||||
if (insets == null) {
|
if (insets == null) {
|
||||||
|
applyNavigationDrawerSafeArea(0, 0);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Insets safeInsets = insets.getInsetsIgnoringVisibility(
|
Insets safeInsets = insets.getInsetsIgnoringVisibility(
|
||||||
@@ -206,10 +269,25 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
|
|||||||
int safeLeft = Math.max(Math.max(0, leftInset), Math.max(0, drawerLayout.getPaddingLeft()));
|
int safeLeft = Math.max(Math.max(0, leftInset), Math.max(0, drawerLayout.getPaddingLeft()));
|
||||||
int safeRight = Math.max(Math.max(0, rightInset), Math.max(0, drawerLayout.getPaddingRight()));
|
int safeRight = Math.max(Math.max(0, rightInset), Math.max(0, drawerLayout.getPaddingRight()));
|
||||||
drawerLayout.setDrawerSafeInsets(safeLeft, safeRight);
|
drawerLayout.setDrawerSafeInsets(safeLeft, safeRight);
|
||||||
if (navigationBasePaddingLeft == null) {
|
drawerNavigationBasePaddingLeft = applyNavigationLeftPadding(
|
||||||
navigationBasePaddingLeft = navigationView.getPaddingLeft();
|
drawerNavigationView,
|
||||||
|
drawerNavigationBasePaddingLeft,
|
||||||
|
safeLeft);
|
||||||
|
permanentNavigationBasePaddingLeft = applyNavigationLeftPadding(
|
||||||
|
permanentNavigationView,
|
||||||
|
permanentNavigationBasePaddingLeft,
|
||||||
|
0);
|
||||||
|
updatePermanentSidebarMode();
|
||||||
}
|
}
|
||||||
int leftPadding = navigationBasePaddingLeft + safeLeft;
|
|
||||||
|
private Integer applyNavigationLeftPadding(NavigationView navigationView,
|
||||||
|
Integer basePaddingLeft,
|
||||||
|
int safeLeft) {
|
||||||
|
if (navigationView == null) {
|
||||||
|
return basePaddingLeft;
|
||||||
|
}
|
||||||
|
int base = basePaddingLeft != null ? basePaddingLeft : navigationView.getPaddingLeft();
|
||||||
|
int leftPadding = base + safeLeft;
|
||||||
if (navigationView.getPaddingLeft() != leftPadding
|
if (navigationView.getPaddingLeft() != leftPadding
|
||||||
|| Math.round(navigationView.getTranslationX()) != 0) {
|
|| Math.round(navigationView.getTranslationX()) != 0) {
|
||||||
navigationView.setTranslationX(0f);
|
navigationView.setTranslationX(0f);
|
||||||
@@ -220,5 +298,102 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
|
|||||||
navigationView.getPaddingBottom());
|
navigationView.getPaddingBottom());
|
||||||
navigationView.requestLayout();
|
navigationView.requestLayout();
|
||||||
}
|
}
|
||||||
|
return base;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updatePermanentSidebarMode() {
|
||||||
|
int rootWidth = drawerLayout.getWidth();
|
||||||
|
if (rootWidth <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int drawerWidth = getResources().getDimensionPixelSize(R.dimen.sbt_navigation_drawer_width);
|
||||||
|
int foldedPortraitWidth = getResources().getDimensionPixelSize(R.dimen.sbt_folded_portrait_width);
|
||||||
|
boolean shouldUsePermanentSidebar = rootWidth >= drawerWidth
|
||||||
|
+ Math.round(foldedPortraitWidth * PERMANENT_SIDEBAR_FOLDED_WIDTH_MULTIPLIER);
|
||||||
|
if (permanentSidebar != shouldUsePermanentSidebar) {
|
||||||
|
permanentSidebar = shouldUsePermanentSidebar;
|
||||||
|
drawerToggle.setDrawerIndicatorEnabled(!permanentSidebar);
|
||||||
|
toolbar.setVisibility(permanentSidebar ? View.GONE : View.VISIBLE);
|
||||||
|
setTitle(resolveTitle(currentItemId));
|
||||||
|
if (permanentSidebar) {
|
||||||
|
permanentNavigationView.setVisibility(View.VISIBLE);
|
||||||
|
drawerLayout.closeDrawer(GravityCompat.START, false);
|
||||||
|
} else {
|
||||||
|
drawerLayout.closeDrawer(GravityCompat.START, false);
|
||||||
|
permanentNavigationView.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
drawerToggle.syncState();
|
||||||
|
}
|
||||||
|
if (permanentSidebar) {
|
||||||
|
drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED, GravityCompat.START);
|
||||||
|
drawerNavigationView.setVisibility(View.GONE);
|
||||||
|
} else {
|
||||||
|
drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED, GravityCompat.START);
|
||||||
|
drawerNavigationView.setVisibility(View.VISIBLE);
|
||||||
|
drawerNavigationView.requestLayout();
|
||||||
|
}
|
||||||
|
updateNavigationHeaderHeight(drawerNavigationView);
|
||||||
|
updateNavigationHeaderHeight(permanentNavigationView);
|
||||||
|
applyPageTitleVisibility();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateNavigationHeaderHeight(NavigationView navigationView) {
|
||||||
|
if (navigationView == null || navigationView.getHeaderCount() == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int navigationHeight = navigationView.getHeight();
|
||||||
|
if (navigationHeight <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int menuRows = countVisibleMenuRows(navigationView.getMenu());
|
||||||
|
int menuHeight = menuRows * getResources().getDimensionPixelSize(R.dimen.sbt_navigation_menu_item_height);
|
||||||
|
int minHeaderHeight = getResources().getDimensionPixelSize(R.dimen.sbt_drawer_header_min_height);
|
||||||
|
int maxHeaderHeight = getResources().getDimensionPixelSize(R.dimen.sbt_drawer_header_max_height);
|
||||||
|
int desiredHeaderHeight = Math.max(
|
||||||
|
minHeaderHeight,
|
||||||
|
Math.min(maxHeaderHeight, navigationHeight - menuHeight));
|
||||||
|
View header = navigationView.getHeaderView(0);
|
||||||
|
if (header == null || header.getLayoutParams() == null
|
||||||
|
|| header.getLayoutParams().height == desiredHeaderHeight) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
header.getLayoutParams().height = desiredHeaderHeight;
|
||||||
|
header.requestLayout();
|
||||||
|
}
|
||||||
|
|
||||||
|
private int countVisibleMenuRows(Menu menu) {
|
||||||
|
if (menu == null) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
int count = 0;
|
||||||
|
for (int i = 0; i < menu.size(); i++) {
|
||||||
|
MenuItem item = menu.getItem(i);
|
||||||
|
if (item != null && item.isVisible()) {
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void applyPageTitleVisibility() {
|
||||||
|
View content = findViewById(R.id.content_frame);
|
||||||
|
setTaggedPageTitlesVisible(content, permanentSidebar);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setTaggedPageTitlesVisible(View view, boolean visible) {
|
||||||
|
if (view == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Object tag = view.getTag();
|
||||||
|
if (tag instanceof String string && "statusbartweak.page.title".equals(string)
|
||||||
|
&& view instanceof TextView) {
|
||||||
|
view.setVisibility(visible ? View.VISIBLE : View.GONE);
|
||||||
|
}
|
||||||
|
if (!(view instanceof ViewGroup group)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < group.getChildCount(); i++) {
|
||||||
|
setTaggedPageTitlesVisible(group.getChildAt(i), visible);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,6 +45,11 @@ public class StatusChipsFragment extends Fragment {
|
|||||||
SbtSettings.KEY_STATUS_CHIPS_HIDE_CALL,
|
SbtSettings.KEY_STATUS_CHIPS_HIDE_CALL,
|
||||||
false,
|
false,
|
||||||
true);
|
true);
|
||||||
|
ViewTreeSupport.bindBooleanPreference(requireContext(), prefs, root,
|
||||||
|
R.id.status_chips_hide_battery_switch,
|
||||||
|
SbtSettings.KEY_STATUS_CHIPS_HIDE_BATTERY,
|
||||||
|
false,
|
||||||
|
true);
|
||||||
return root;
|
return root;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -477,8 +477,6 @@ public class SystemIconsFragment extends Fragment {
|
|||||||
Set<String> encoded = SystemIconRules.encodeBlockedModes(blockedModes);
|
Set<String> encoded = SystemIconRules.encodeBlockedModes(blockedModes);
|
||||||
prefs.edit()
|
prefs.edit()
|
||||||
.putStringSet(SbtSettings.KEY_SYSTEM_ICON_BLOCKED_MODES, encoded)
|
.putStringSet(SbtSettings.KEY_SYSTEM_ICON_BLOCKED_MODES, encoded)
|
||||||
// Clear legacy key so migration does not keep re-forcing stale global hides.
|
|
||||||
.putStringSet(SbtSettings.KEY_SYSTEM_ICON_HIDE_SLOTS, Collections.emptySet())
|
|
||||||
.apply();
|
.apply();
|
||||||
SbtSettings.ensureReadable(requireContext());
|
SbtSettings.ensureReadable(requireContext());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -238,25 +238,20 @@ final class ViewTreeSupport {
|
|||||||
static int iconContainerCount(
|
static int iconContainerCount(
|
||||||
SharedPreferences prefs,
|
SharedPreferences prefs,
|
||||||
String countKey,
|
String countKey,
|
||||||
String legacyEnabledKey,
|
int defaultCount
|
||||||
int defaultCount,
|
|
||||||
boolean legacyEnabledDefault
|
|
||||||
) {
|
) {
|
||||||
int fallback = prefs.getBoolean(legacyEnabledKey, legacyEnabledDefault) ? defaultCount : 0;
|
return SbtSettings.clampIconContainerCount(prefs.getInt(countKey, defaultCount));
|
||||||
return SbtSettings.clampIconContainerCount(prefs.getInt(countKey, fallback));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void persistIconContainerCount(
|
static void persistIconContainerCount(
|
||||||
Context context,
|
Context context,
|
||||||
SharedPreferences prefs,
|
SharedPreferences prefs,
|
||||||
String countKey,
|
String countKey,
|
||||||
String legacyEnabledKey,
|
|
||||||
int count
|
int count
|
||||||
) {
|
) {
|
||||||
int clamped = SbtSettings.clampIconContainerCount(count);
|
int clamped = SbtSettings.clampIconContainerCount(count);
|
||||||
prefs.edit()
|
prefs.edit()
|
||||||
.putInt(countKey, clamped)
|
.putInt(countKey, clamped)
|
||||||
.putBoolean(legacyEnabledKey, clamped > 0)
|
|
||||||
.apply();
|
.apply();
|
||||||
SbtSettings.ensureReadable(context);
|
SbtSettings.ensureReadable(context);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<solid android:color="@android:color/transparent" />
|
||||||
|
<stroke
|
||||||
|
android:width="1dp"
|
||||||
|
android:color="@color/sbt_card_outline" />
|
||||||
|
<corners android:radius="6dp" />
|
||||||
|
<padding
|
||||||
|
android:left="8dp"
|
||||||
|
android:top="6dp"
|
||||||
|
android:right="8dp"
|
||||||
|
android:bottom="6dp" />
|
||||||
|
</shape>
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<solid android:color="@android:color/transparent" />
|
||||||
|
<stroke
|
||||||
|
android:width="1dp"
|
||||||
|
android:color="@color/sbt_panel_outline" />
|
||||||
|
<corners android:radius="8dp" />
|
||||||
|
<padding
|
||||||
|
android:left="10dp"
|
||||||
|
android:top="8dp"
|
||||||
|
android:right="10dp"
|
||||||
|
android:bottom="8dp" />
|
||||||
|
</shape>
|
||||||
@@ -9,8 +9,28 @@
|
|||||||
android:fitsSystemWindows="true">
|
android:fitsSystemWindows="true">
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
|
android:id="@+id/main_shell"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<com.google.android.material.navigation.NavigationView
|
||||||
|
android:id="@+id/nav_view_permanent"
|
||||||
|
android:layout_width="@dimen/sbt_navigation_drawer_width"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:background="@color/sbt_surface"
|
||||||
|
android:fitsSystemWindows="true"
|
||||||
|
android:visibility="gone"
|
||||||
|
app:headerLayout="@layout/drawer_header"
|
||||||
|
app:itemBackground="@drawable/drawer_item_background"
|
||||||
|
app:itemTextColor="@color/drawer_item_text_color"
|
||||||
|
app:menu="@menu/drawer_menu" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/main_content"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_weight="1"
|
||||||
android:orientation="vertical">
|
android:orientation="vertical">
|
||||||
|
|
||||||
<com.google.android.material.appbar.MaterialToolbar
|
<com.google.android.material.appbar.MaterialToolbar
|
||||||
@@ -30,9 +50,11 @@
|
|||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
<com.google.android.material.navigation.NavigationView
|
<com.google.android.material.navigation.NavigationView
|
||||||
android:id="@+id/nav_view"
|
android:id="@+id/nav_view_drawer"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="@dimen/sbt_navigation_drawer_width"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:layout_gravity="start"
|
android:layout_gravity="start"
|
||||||
android:background="@color/sbt_surface"
|
android:background="@color/sbt_surface"
|
||||||
|
|||||||
@@ -2,11 +2,11 @@
|
|||||||
<LinearLayout
|
<LinearLayout
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="172dp"
|
android:layout_height="@dimen/sbt_drawer_header_height"
|
||||||
android:background="@drawable/sbt_drawer_header_background"
|
android:background="@drawable/sbt_drawer_header_background"
|
||||||
android:gravity="bottom"
|
android:gravity="bottom"
|
||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
android:padding="20dp">
|
android:padding="@dimen/sbt_drawer_header_padding">
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
|
|||||||
@@ -13,41 +13,10 @@
|
|||||||
<TextView
|
<TextView
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
android:tag="statusbartweak.page.title"
|
||||||
android:text="@string/battery_bar_title"
|
android:text="@string/battery_bar_title"
|
||||||
android:textAppearance="?attr/textAppearanceHeadline5" />
|
android:textAppearance="?attr/textAppearanceHeadline5" />
|
||||||
|
|
||||||
<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:text="@string/battery_bar_master_toggle_title"
|
|
||||||
android:textAppearance="?attr/textAppearanceSubtitle1"
|
|
||||||
android:textAllCaps="true"
|
|
||||||
android:letterSpacing="0.03"
|
|
||||||
android:textStyle="bold" />
|
|
||||||
|
|
||||||
<com.google.android.material.switchmaterial.SwitchMaterial
|
|
||||||
android:id="@+id/battery_bar_enabled_switch"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="8dp"
|
|
||||||
android:text="@string/battery_bar_enabled" />
|
|
||||||
</LinearLayout>
|
|
||||||
</com.google.android.material.card.MaterialCardView>
|
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:id="@+id/battery_bar_geometry_section"
|
android:id="@+id/battery_bar_geometry_section"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
@@ -71,18 +40,45 @@
|
|||||||
<TextView
|
<TextView
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="@string/battery_bar_position_title"
|
android:text="@string/battery_bar_base_settings_title"
|
||||||
android:textAppearance="?attr/textAppearanceSubtitle1"
|
android:textAppearance="?attr/textAppearanceSubtitle1"
|
||||||
android:textAllCaps="true"
|
android:textAllCaps="true"
|
||||||
android:letterSpacing="0.03"
|
android:letterSpacing="0.03"
|
||||||
android:textStyle="bold" />
|
android:textStyle="bold" />
|
||||||
|
|
||||||
|
<com.google.android.material.switchmaterial.SwitchMaterial
|
||||||
|
android:id="@+id/battery_bar_enabled_switch"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:text="@string/battery_bar_enabled" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/battery_bar_base_controls"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="12dp"
|
||||||
|
android:text="@string/battery_bar_position_title"
|
||||||
|
android:textAppearance="?attr/textAppearanceBody2" />
|
||||||
|
|
||||||
<RadioGroup
|
<RadioGroup
|
||||||
android:id="@+id/battery_bar_position_group"
|
android:id="@+id/battery_bar_position_group"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="8dp">
|
android:layout_marginTop="8dp">
|
||||||
|
|
||||||
|
<RadioButton
|
||||||
|
android:id="@+id/battery_bar_curved_geometry_length"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:minHeight="36dp"
|
||||||
|
android:text="@string/battery_bar_curved_geometry_length" />
|
||||||
|
|
||||||
<RadioButton
|
<RadioButton
|
||||||
android:id="@+id/battery_bar_position_top"
|
android:id="@+id/battery_bar_position_top"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
@@ -102,145 +98,8 @@
|
|||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="16dp"
|
android:layout_marginTop="16dp"
|
||||||
android:text="@string/battery_bar_thickness_title"
|
|
||||||
android:textAppearance="?attr/textAppearanceBody2" />
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="8dp"
|
|
||||||
android:gravity="center_vertical"
|
|
||||||
android:orientation="horizontal">
|
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
|
||||||
android:id="@+id/battery_bar_thickness_minus"
|
|
||||||
style="?attr/materialButtonOutlinedStyle"
|
|
||||||
android:layout_width="40dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="-" />
|
|
||||||
|
|
||||||
<EditText
|
|
||||||
android:id="@+id/battery_bar_thickness_input"
|
|
||||||
android:layout_width="72dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="8dp"
|
|
||||||
android:layout_marginEnd="8dp"
|
|
||||||
android:ems="3"
|
|
||||||
android:gravity="center"
|
|
||||||
android:importantForAutofill="no"
|
|
||||||
android:inputType="number"
|
|
||||||
android:maxLines="1"
|
|
||||||
android:singleLine="true" />
|
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
|
||||||
android:id="@+id/battery_bar_thickness_plus"
|
|
||||||
style="?attr/materialButtonOutlinedStyle"
|
|
||||||
android:layout_width="40dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="+" />
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="16dp"
|
|
||||||
android:text="@string/battery_bar_edge_offset_title"
|
|
||||||
android:textAppearance="?attr/textAppearanceBody2" />
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="8dp"
|
|
||||||
android:gravity="center_vertical"
|
|
||||||
android:orientation="horizontal">
|
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
|
||||||
android:id="@+id/battery_bar_edge_offset_minus"
|
|
||||||
style="?attr/materialButtonOutlinedStyle"
|
|
||||||
android:layout_width="40dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="-" />
|
|
||||||
|
|
||||||
<EditText
|
|
||||||
android:id="@+id/battery_bar_edge_offset_input"
|
|
||||||
android:layout_width="72dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="8dp"
|
|
||||||
android:layout_marginEnd="8dp"
|
|
||||||
android:ems="3"
|
|
||||||
android:gravity="center"
|
|
||||||
android:importantForAutofill="no"
|
|
||||||
android:inputType="number"
|
|
||||||
android:maxLines="1"
|
|
||||||
android:singleLine="true" />
|
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
|
||||||
android:id="@+id/battery_bar_edge_offset_plus"
|
|
||||||
style="?attr/materialButtonOutlinedStyle"
|
|
||||||
android:layout_width="40dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="+" />
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="16dp"
|
|
||||||
android:text="@string/battery_bar_curved_geometry_title"
|
|
||||||
android:textAppearance="?attr/textAppearanceBody2" />
|
|
||||||
|
|
||||||
<RadioGroup
|
|
||||||
android:id="@+id/battery_bar_curved_geometry_group"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="8dp">
|
|
||||||
|
|
||||||
<RadioButton
|
|
||||||
android:id="@+id/battery_bar_curved_geometry_off"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:minHeight="36dp"
|
|
||||||
android:text="@string/battery_bar_curved_geometry_off" />
|
|
||||||
|
|
||||||
<RadioButton
|
|
||||||
android:id="@+id/battery_bar_curved_geometry_horizontal"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:minHeight="36dp"
|
|
||||||
android:text="@string/battery_bar_curved_geometry_horizontal" />
|
|
||||||
|
|
||||||
<RadioButton
|
|
||||||
android:id="@+id/battery_bar_curved_geometry_length"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:minHeight="36dp"
|
|
||||||
android:text="@string/battery_bar_curved_geometry_length" />
|
|
||||||
</RadioGroup>
|
|
||||||
</LinearLayout>
|
|
||||||
</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:text="@string/battery_bar_alignment_title"
|
android:text="@string/battery_bar_alignment_title"
|
||||||
android:textAppearance="?attr/textAppearanceSubtitle1"
|
android:textAppearance="?attr/textAppearanceBody2" />
|
||||||
android:textAllCaps="true"
|
|
||||||
android:letterSpacing="0.03"
|
|
||||||
android:textStyle="bold" />
|
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:id="@+id/battery_bar_alignment_row"
|
android:id="@+id/battery_bar_alignment_row"
|
||||||
@@ -333,48 +192,101 @@
|
|||||||
app:srcCompat="@drawable/sbt_align_rtl" />
|
app:srcCompat="@drawable/sbt_align_rtl" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</LinearLayout>
|
|
||||||
</com.google.android.material.card.MaterialCardView>
|
|
||||||
|
|
||||||
<com.google.android.material.card.MaterialCardView
|
<TextView
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="16dp"
|
android:layout_marginTop="16dp"
|
||||||
app:cardUseCompatPadding="true"
|
android:text="@string/battery_bar_thickness_title"
|
||||||
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" />
|
android:textAppearance="?attr/textAppearanceBody2" />
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:id="@+id/battery_bar_scenario_overrides_container"
|
android:layout_width="wrap_content"
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="8dp"
|
android:layout_marginTop="8dp"
|
||||||
android:orientation="vertical" />
|
android:gravity="center_vertical"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/battery_bar_thickness_minus"
|
||||||
|
style="?attr/materialButtonOutlinedStyle"
|
||||||
|
android:layout_width="40dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="-" />
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/battery_bar_thickness_input"
|
||||||
|
android:layout_width="72dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="8dp"
|
||||||
|
android:layout_marginEnd="8dp"
|
||||||
|
android:ems="3"
|
||||||
|
android:gravity="center"
|
||||||
|
android:importantForAutofill="no"
|
||||||
|
android:inputType="number"
|
||||||
|
android:maxLines="1"
|
||||||
|
android:singleLine="true" />
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/battery_bar_thickness_plus"
|
||||||
|
style="?attr/materialButtonOutlinedStyle"
|
||||||
|
android:layout_width="40dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="+" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:text="@string/battery_bar_edge_offset_title"
|
||||||
|
android:textAppearance="?attr/textAppearanceBody2" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/battery_bar_edge_offset_minus"
|
||||||
|
style="?attr/materialButtonOutlinedStyle"
|
||||||
|
android:layout_width="40dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="-" />
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/battery_bar_edge_offset_input"
|
||||||
|
android:layout_width="72dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="8dp"
|
||||||
|
android:layout_marginEnd="8dp"
|
||||||
|
android:ems="3"
|
||||||
|
android:gravity="center"
|
||||||
|
android:importantForAutofill="no"
|
||||||
|
android:inputType="number"
|
||||||
|
android:maxLines="1"
|
||||||
|
android:singleLine="true" />
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/battery_bar_edge_offset_plus"
|
||||||
|
style="?attr/materialButtonOutlinedStyle"
|
||||||
|
android:layout_width="40dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="+" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</com.google.android.material.card.MaterialCardView>
|
</com.google.android.material.card.MaterialCardView>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/battery_bar_scenario_cards_container"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical" />
|
||||||
|
|
||||||
<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"
|
||||||
@@ -569,6 +481,25 @@
|
|||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="8dp"
|
android:layout_marginTop="8dp"
|
||||||
android:text="@string/battery_bar_default_charge_colour" />
|
android:text="@string/battery_bar_default_charge_colour" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/battery_bar_colour_help_toggle"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="12dp"
|
||||||
|
android:text="@string/battery_bar_colour_help_show"
|
||||||
|
android:textAppearance="?attr/textAppearanceBody1"
|
||||||
|
android:textColor="@color/sbt_accent_dark"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/battery_bar_colour_help"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:visibility="gone"
|
||||||
|
android:text="@string/battery_bar_colour_help_text"
|
||||||
|
android:textAppearance="?attr/textAppearanceBody2" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</com.google.android.material.card.MaterialCardView>
|
</com.google.android.material.card.MaterialCardView>
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
android:id="@+id/hidden_apps_title"
|
android:id="@+id/hidden_apps_title"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
android:tag="statusbartweak.page.title"
|
||||||
android:text="@string/hidden_apps_page_title"
|
android:text="@string/hidden_apps_page_title"
|
||||||
android:textAppearance="?attr/textAppearanceHeadline6"
|
android:textAppearance="?attr/textAppearanceHeadline6"
|
||||||
android:textStyle="bold" />
|
android:textStyle="bold" />
|
||||||
|
|||||||
@@ -13,10 +13,12 @@
|
|||||||
<TextView
|
<TextView
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
android:tag="statusbartweak.page.title"
|
||||||
android:text="@string/clock_title"
|
android:text="@string/clock_title"
|
||||||
android:textAppearance="?attr/textAppearanceHeadline5" />
|
android:textAppearance="?attr/textAppearanceHeadline5" />
|
||||||
|
|
||||||
<com.google.android.material.card.MaterialCardView
|
<com.google.android.material.card.MaterialCardView
|
||||||
|
android:id="@+id/clock_custom_format_card"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="16dp"
|
android:layout_marginTop="16dp"
|
||||||
@@ -53,17 +55,24 @@
|
|||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
android:visibility="gone">
|
android:visibility="gone">
|
||||||
|
|
||||||
<EditText
|
<se.ajpanton.statusbartweak.shell.ui.ClockPatternPreviewView
|
||||||
android:id="@+id/clock_custom_format_input"
|
android:id="@+id/clock_custom_format_preview"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="8dp"
|
android:layout_marginTop="8dp"
|
||||||
|
android:background="@drawable/sbt_clock_preview_background" />
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/clock_custom_format_input"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:background="@drawable/sbt_clock_input_background"
|
||||||
android:gravity="top|start"
|
android:gravity="top|start"
|
||||||
android:hint="@string/clock_custom_format_hint"
|
|
||||||
android:importantForAutofill="no"
|
android:importantForAutofill="no"
|
||||||
android:inputType="textMultiLine|textNoSuggestions"
|
android:inputType="textMultiLine|textNoSuggestions"
|
||||||
android:maxLines="4"
|
android:minWidth="120dp"
|
||||||
android:minLines="2"
|
android:minLines="1"
|
||||||
android:scrollHorizontally="false"
|
android:scrollHorizontally="false"
|
||||||
android:singleLine="false" />
|
android:singleLine="false" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
@@ -71,6 +80,7 @@
|
|||||||
</com.google.android.material.card.MaterialCardView>
|
</com.google.android.material.card.MaterialCardView>
|
||||||
|
|
||||||
<com.google.android.material.card.MaterialCardView
|
<com.google.android.material.card.MaterialCardView
|
||||||
|
android:id="@+id/drawer_clock_custom_format_card"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="16dp"
|
android:layout_marginTop="16dp"
|
||||||
@@ -107,17 +117,24 @@
|
|||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
android:visibility="gone">
|
android:visibility="gone">
|
||||||
|
|
||||||
<EditText
|
<se.ajpanton.statusbartweak.shell.ui.ClockPatternPreviewView
|
||||||
android:id="@+id/drawer_clock_custom_format_input"
|
android:id="@+id/drawer_clock_custom_format_preview"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="8dp"
|
android:layout_marginTop="8dp"
|
||||||
|
android:background="@drawable/sbt_clock_preview_background" />
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/drawer_clock_custom_format_input"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:background="@drawable/sbt_clock_input_background"
|
||||||
android:gravity="top|start"
|
android:gravity="top|start"
|
||||||
android:hint="@string/clock_custom_format_hint"
|
|
||||||
android:importantForAutofill="no"
|
android:importantForAutofill="no"
|
||||||
android:inputType="textMultiLine|textNoSuggestions"
|
android:inputType="textMultiLine|textNoSuggestions"
|
||||||
android:maxLines="4"
|
android:minWidth="120dp"
|
||||||
android:minLines="2"
|
android:minLines="1"
|
||||||
android:scrollHorizontally="false"
|
android:scrollHorizontally="false"
|
||||||
android:singleLine="false" />
|
android:singleLine="false" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
@@ -125,6 +142,7 @@
|
|||||||
</com.google.android.material.card.MaterialCardView>
|
</com.google.android.material.card.MaterialCardView>
|
||||||
|
|
||||||
<com.google.android.material.card.MaterialCardView
|
<com.google.android.material.card.MaterialCardView
|
||||||
|
android:id="@+id/drawer_date_custom_format_card"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="16dp"
|
android:layout_marginTop="16dp"
|
||||||
@@ -161,17 +179,24 @@
|
|||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
android:visibility="gone">
|
android:visibility="gone">
|
||||||
|
|
||||||
<EditText
|
<se.ajpanton.statusbartweak.shell.ui.ClockPatternPreviewView
|
||||||
android:id="@+id/drawer_date_custom_format_input"
|
android:id="@+id/drawer_date_custom_format_preview"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="8dp"
|
android:layout_marginTop="8dp"
|
||||||
|
android:background="@drawable/sbt_clock_preview_background" />
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/drawer_date_custom_format_input"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:background="@drawable/sbt_clock_input_background"
|
||||||
android:gravity="top|start"
|
android:gravity="top|start"
|
||||||
android:hint="@string/clock_custom_format_hint"
|
|
||||||
android:importantForAutofill="no"
|
android:importantForAutofill="no"
|
||||||
android:inputType="textMultiLine|textNoSuggestions"
|
android:inputType="textMultiLine|textNoSuggestions"
|
||||||
android:maxLines="4"
|
android:minWidth="120dp"
|
||||||
android:minLines="2"
|
android:minLines="1"
|
||||||
android:scrollHorizontally="false"
|
android:scrollHorizontally="false"
|
||||||
android:singleLine="false" />
|
android:singleLine="false" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
@@ -209,6 +234,14 @@
|
|||||||
android:layout_marginTop="8dp"
|
android:layout_marginTop="8dp"
|
||||||
android:text="@string/clock_font_picker_button" />
|
android:text="@string/clock_font_picker_button" />
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/clock_restore_stored_patterns_button"
|
||||||
|
style="?attr/materialButtonOutlinedStyle"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:text="@string/clock_restore_stored_patterns" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/clock_custom_format_help_toggle"
|
android:id="@+id/clock_custom_format_help_toggle"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
|||||||
@@ -13,6 +13,7 @@
|
|||||||
<TextView
|
<TextView
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
android:tag="statusbartweak.page.title"
|
||||||
android:text="@string/debug_title"
|
android:text="@string/debug_title"
|
||||||
android:textAppearance="?attr/textAppearanceHeadline5" />
|
android:textAppearance="?attr/textAppearanceHeadline5" />
|
||||||
|
|
||||||
|
|||||||
@@ -13,6 +13,7 @@
|
|||||||
<TextView
|
<TextView
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
android:tag="statusbartweak.page.title"
|
||||||
android:text="@string/layout_title"
|
android:text="@string/layout_title"
|
||||||
android:textAppearance="?attr/textAppearanceHeadline5" />
|
android:textAppearance="?attr/textAppearanceHeadline5" />
|
||||||
|
|
||||||
|
|||||||
@@ -13,6 +13,7 @@
|
|||||||
<TextView
|
<TextView
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
android:tag="statusbartweak.page.title"
|
||||||
android:text="@string/misc_title"
|
android:text="@string/misc_title"
|
||||||
android:textAppearance="?attr/textAppearanceHeadline5" />
|
android:textAppearance="?attr/textAppearanceHeadline5" />
|
||||||
|
|
||||||
|
|||||||
@@ -13,6 +13,7 @@
|
|||||||
<TextView
|
<TextView
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
android:tag="statusbartweak.page.title"
|
||||||
android:text="@string/status_chips_title"
|
android:text="@string/status_chips_title"
|
||||||
android:textAppearance="?attr/textAppearanceHeadline5" />
|
android:textAppearance="?attr/textAppearanceHeadline5" />
|
||||||
|
|
||||||
@@ -47,6 +48,12 @@
|
|||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="@string/status_chips_hide_call_unlocked_label" />
|
android:text="@string/status_chips_hide_call_unlocked_label" />
|
||||||
|
|
||||||
|
<com.google.android.material.switchmaterial.SwitchMaterial
|
||||||
|
android:id="@+id/status_chips_hide_battery_switch"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/status_chips_hide_battery_label" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</com.google.android.material.card.MaterialCardView>
|
</com.google.android.material.card.MaterialCardView>
|
||||||
|
|
||||||
|
|||||||
@@ -13,6 +13,7 @@
|
|||||||
<TextView
|
<TextView
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
android:tag="statusbartweak.page.title"
|
||||||
android:text="@string/system_icons_title"
|
android:text="@string/system_icons_title"
|
||||||
android:textAppearance="?attr/textAppearanceHeadline5" />
|
android:textAppearance="?attr/textAppearanceHeadline5" />
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<dimen name="sbt_navigation_drawer_width">216dp</dimen>
|
||||||
|
<dimen name="sbt_folded_portrait_width">360dp</dimen>
|
||||||
|
<dimen name="sbt_navigation_menu_item_height">48dp</dimen>
|
||||||
|
<dimen name="sbt_drawer_header_height">86dp</dimen>
|
||||||
|
<dimen name="sbt_drawer_header_min_height">86dp</dimen>
|
||||||
|
<dimen name="sbt_drawer_header_max_height">172dp</dimen>
|
||||||
|
<dimen name="sbt_drawer_header_padding">16dp</dimen>
|
||||||
|
</resources>
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
<resources>
|
<resources>
|
||||||
<item name="sbt_debug_container_bounds" type="id" />
|
<item name="sbt_debug_container_bounds" type="id" />
|
||||||
|
<item name="sbt_debug_drawer_clock_container" type="id" />
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -44,7 +44,7 @@
|
|||||||
<string name="debug_notification_permission_required">Notification permission is required for debug icons.</string>
|
<string name="debug_notification_permission_required">Notification permission is required for debug icons.</string>
|
||||||
<string name="debug_camera_current_title">Current camera</string>
|
<string name="debug_camera_current_title">Current camera</string>
|
||||||
<string name="debug_camera_other_title">Other overridden cameras</string>
|
<string name="debug_camera_other_title">Other overridden cameras</string>
|
||||||
<string name="debug_camera_identified_as">Identified as %1$s</string>
|
<string name="debug_camera_identified_as">Identified as %1$s\n%2$s</string>
|
||||||
<string name="debug_camera_overridden_as">Overridden as %1$s</string>
|
<string name="debug_camera_overridden_as">Overridden as %1$s</string>
|
||||||
<string name="debug_camera_action_wrong">This is wrong</string>
|
<string name="debug_camera_action_wrong">This is wrong</string>
|
||||||
<string name="debug_camera_action_undo">↶</string>
|
<string name="debug_camera_action_undo">↶</string>
|
||||||
@@ -131,12 +131,14 @@
|
|||||||
<string name="status_chips_hide_media_unlocked_label">Hide media chip (unlocked)</string>
|
<string name="status_chips_hide_media_unlocked_label">Hide media chip (unlocked)</string>
|
||||||
<string name="status_chips_hide_navigation_unlocked_label">Hide navigation chip (unlocked)</string>
|
<string name="status_chips_hide_navigation_unlocked_label">Hide navigation chip (unlocked)</string>
|
||||||
<string name="status_chips_hide_call_unlocked_label">Hide call chip (unlocked)</string>
|
<string name="status_chips_hide_call_unlocked_label">Hide call chip (unlocked)</string>
|
||||||
|
<string name="status_chips_hide_battery_label">Hide charging battery chip (unlocked)</string>
|
||||||
<string name="misc_title">Misc</string>
|
<string name="misc_title">Misc</string>
|
||||||
<string name="battery_bar_title">Battery bar</string>
|
<string name="battery_bar_title">Battery bar</string>
|
||||||
<string name="battery_bar_master_toggle_title">Master toggle</string>
|
<string name="battery_bar_base_settings_title">Base settings</string>
|
||||||
<string name="battery_bar_enabled">Enable battery bar</string>
|
<string name="battery_bar_enabled">Enable battery bar</string>
|
||||||
|
<string name="battery_bar_use_base_settings">Use base settings</string>
|
||||||
<string name="battery_bar_position_title">Position</string>
|
<string name="battery_bar_position_title">Position</string>
|
||||||
<string name="battery_bar_position_top">Top</string>
|
<string name="battery_bar_position_top">Top edge of screen</string>
|
||||||
<string name="battery_bar_position_bottom">Bottom of status bar</string>
|
<string name="battery_bar_position_bottom">Bottom of status bar</string>
|
||||||
<string name="battery_bar_alignment_title">Alignment</string>
|
<string name="battery_bar_alignment_title">Alignment</string>
|
||||||
<string name="battery_bar_alignment_ltr">→</string>
|
<string name="battery_bar_alignment_ltr">→</string>
|
||||||
@@ -144,19 +146,10 @@
|
|||||||
<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_curved_geometry_title">Curved top geometry</string>
|
<string name="battery_bar_curved_geometry_length">Follow screen edges</string>
|
||||||
<string name="battery_bar_curved_geometry_off">Off</string>
|
|
||||||
<string name="battery_bar_curved_geometry_horizontal">On, horizontal position only</string>
|
|
||||||
<string name="battery_bar_curved_geometry_length">On, actual curve length</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_scenario_group_unlocked">Unlocked</string>
|
|
||||||
<string name="battery_bar_scenario_group_transparent">Transparent statusbar</string>
|
<string name="battery_bar_scenario_group_transparent">Transparent statusbar</string>
|
||||||
<string name="battery_bar_scenario_group_fullscreen">Fullscreen</string>
|
<string name="battery_bar_scenario_group_fullscreen">Fullscreen</string>
|
||||||
<string name="battery_bar_lockscreen_override">Lockscreen</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>
|
||||||
@@ -164,11 +157,13 @@
|
|||||||
<string name="battery_bar_colours_title">Default colours</string>
|
<string name="battery_bar_colours_title">Default colours</string>
|
||||||
<string name="battery_bar_default_discharge_colour">Discharge colour</string>
|
<string name="battery_bar_default_discharge_colour">Discharge colour</string>
|
||||||
<string name="battery_bar_default_charge_colour">Charge colour</string>
|
<string name="battery_bar_default_charge_colour">Charge colour</string>
|
||||||
|
<string name="battery_bar_button_label_discharge">Discharge</string>
|
||||||
|
<string name="battery_bar_button_label_charge">Charge</string>
|
||||||
<string name="battery_bar_thresholds_title">Threshold colours</string>
|
<string name="battery_bar_thresholds_title">Threshold colours</string>
|
||||||
<string name="battery_bar_thresholds_hint">Below a threshold, the first matching colour overrides the default.</string>
|
<string name="battery_bar_thresholds_hint">Below a threshold, the first matching colour overrides the default.</string>
|
||||||
<string name="battery_bar_add_threshold">Add threshold</string>
|
<string name="battery_bar_add_threshold">Add threshold</string>
|
||||||
<string name="battery_bar_colour_dialog_title">Set colour</string>
|
<string name="battery_bar_colour_dialog_title">Set colour</string>
|
||||||
<string name="battery_bar_colour_dialog_hint">Examples: FA0, 80FA, or FFAA00</string>
|
<string name="battery_bar_colour_dialog_hint">Examples: #FA0, FA0, or #FA0 ; invRGB 0.5 *RGB</string>
|
||||||
<string name="battery_bar_threshold_percent_title">Threshold percent</string>
|
<string name="battery_bar_threshold_percent_title">Threshold percent</string>
|
||||||
<string name="battery_bar_threshold_percent_hint">Applies when battery is at or below this percent.</string>
|
<string name="battery_bar_threshold_percent_hint">Applies when battery is at or below this percent.</string>
|
||||||
<string name="battery_bar_threshold_change_percent">Change threshold %</string>
|
<string name="battery_bar_threshold_change_percent">Change threshold %</string>
|
||||||
@@ -178,7 +173,10 @@
|
|||||||
<string name="battery_bar_threshold_remove">Remove threshold</string>
|
<string name="battery_bar_threshold_remove">Remove threshold</string>
|
||||||
<string name="battery_bar_colour_auto">Auto</string>
|
<string name="battery_bar_colour_auto">Auto</string>
|
||||||
<string name="battery_bar_colour_same_as_discharge">Same as discharge</string>
|
<string name="battery_bar_colour_same_as_discharge">Same as discharge</string>
|
||||||
<string name="battery_bar_colour_same_as_default">Same as default</string>
|
<string name="battery_bar_colour_same_as_default">Default</string>
|
||||||
|
<string name="battery_bar_colour_help_show">Show colour syntax</string>
|
||||||
|
<string name="battery_bar_colour_help_hide">Hide colour syntax</string>
|
||||||
|
<string name="battery_bar_colour_help_text">Colour values:\n#FA0, #FFAA00, #80FFAA00, FA0, or [255, 255, 170, 0]\n\nPostfix math:\n#FA0 1.2 *RGB clip\n#FA0 invRGB\n\nOperators:\n+ - * / ^ inv min max sqrt clip dup\n\nChannels:\nAdd A, R, G, or B to limit vector operations.\nExamples: invRGB, *A, +RB\n\nTint variants:\nUse ; for a dark-tint expression.\nThe dark expression starts with the result from the bright expression.</string>
|
||||||
<string name="battery_bar_threshold_duplicate">A threshold at that percentage already exists.</string>
|
<string name="battery_bar_threshold_duplicate">A threshold at that percentage already exists.</string>
|
||||||
<string name="battery_bar_threshold_reordered">That threshold moved to a new position in the list.</string>
|
<string name="battery_bar_threshold_reordered">That threshold moved to a new position in the list.</string>
|
||||||
<string name="battery_bar_threshold_charge_note">Leave the field empty and press OK for \"Same as discharge\", or use the button below for \"Same as default\".</string>
|
<string name="battery_bar_threshold_charge_note">Leave the field empty and press OK for \"Same as discharge\", or use the button below for \"Same as default\".</string>
|
||||||
@@ -242,14 +240,18 @@
|
|||||||
<string name="clock_custom_format_enabled">Enable custom date/time pattern</string>
|
<string name="clock_custom_format_enabled">Enable custom date/time pattern</string>
|
||||||
<string name="drawer_clock_custom_format_enabled">Enable custom date/time pattern</string>
|
<string name="drawer_clock_custom_format_enabled">Enable custom date/time pattern</string>
|
||||||
<string name="drawer_date_custom_format_enabled">Enable custom date/time pattern</string>
|
<string name="drawer_date_custom_format_enabled">Enable custom date/time pattern</string>
|
||||||
<string name="clock_custom_format_hint">Examples: HH:mm, EEE d MMM HH:mm:ss, {color(#batterybar 1.2 * ; invRGB 0.5 *RGB) big @sans-serif-condensed}HH:mm</string>
|
|
||||||
<string name="clock_custom_format_help_common">Date/time patterns:\n\u2022 Uses Java SimpleDateFormat-style pattern letters\n\u2022 Common symbols: yyyy yy MMMM MMM MM EEEE EEE dd d HH hh mm ss a\n\nTags:\n\u2022 Supported tags: <i>, <u>, <small>, <big>, <font color=\'...\'>, <font face=\'...\'>\n\u2022 <b> is not supported\n\u2022 Custom font tags: <sans>, <serif>, <mono>, <condensed>\n\u2022 {/font} closes any open <font ...> tag\n\u2022 Opening a new colour or font face automatically closes the previous one</string>
|
<string name="clock_custom_format_help_common">Date/time patterns:\n\u2022 Uses Java SimpleDateFormat-style pattern letters\n\u2022 Common symbols: yyyy yy MMMM MMM MM EEEE EEE dd d HH hh mm ss a\n\nTags:\n\u2022 Supported tags: <i>, <u>, <small>, <big>, <font color=\'...\'>, <font face=\'...\'>\n\u2022 <b> is not supported\n\u2022 Custom font tags: <sans>, <serif>, <mono>, <condensed>\n\u2022 {/font} closes any open <font ...> tag\n\u2022 Opening a new colour or font face automatically closes the previous one</string>
|
||||||
<string name="clock_custom_format_help_styling">Shorthand blocks:\n\u2022 {#FA0 big @sans-serif-condensed} is shorthand for opening multiple tags in order\n\u2022 {#FA0} is shorthand for {color(#FA0)}\n\u2022 In shorthand: / closes a tag, color(...) sets font colour, @ sets font face\n\u2022 Each whitespace-separated token inside {} becomes its own tag, except color(...) which may contain spaces\n\nLine breaks:\n\u2022 Use \\n, {\\n} or {\\n15}; the number is the line-height delta in statusbar units\n\u2022 Omit the number to use the default row spacing\n\u2022 { /\\n } style line breaks are written without spaces: {/\\n} or {/\\n15}\n\u2022 Normal line breaks propagate open tags onto the next row\n\u2022 A / line break ends all currently open tags before the next row starts\n\nPipes and grouping:\n\u2022 Maximum two pipes per row; more than two is a syntax error for that row only\n\u2022 Pipes inside single quotes or {} do not split the row\n\u2022 One pipe splits into left and right segments\n\u2022 Two pipes remove the middle section, then split using the outer pipes\n\u2022 Tags are logically continued across pipe splits, even if the middle section is ignored\n\u2022 Rows without pipes are independent; rows with pipes share one common pipe group\n\u2022 A syntax-error row becomes the text Syntax error, with no whole-clock fallback\n\nColours:\n\u2022 Supported operands: #RGB, #ARGB, #RRGGBB, #AARRGGBB, #batterybar, [R,G,B], [A,R,G,B], and numbers\n\u2022 Colour expressions use reverse Polish notation: operands first, operator last\n\u2022 Use ; to separate bright/dark variants; the dark side starts with the raw result from the bright side on the stack\n\u2022 Operators: + - * / ^ inv min max sqrt clip dup\n\u2022 Operators can be channel-filtered, for example +A, *RGB, invRGB, maxRGB, clipA\n\u2022 Final values are clipped to 0..255; a final number becomes opaque grayscale</string>
|
<string name="clock_custom_format_help_styling">Shorthand blocks:\n\u2022 {#FA0 big @sans-serif-condensed} is shorthand for opening multiple tags in order\n\u2022 {#FA0} is shorthand for {color(#FA0)}\n\u2022 In shorthand: / closes a tag, color(...) sets font colour, @ sets font face\n\u2022 Each whitespace-separated token inside {} becomes its own tag, except color(...) which may contain spaces\n\nLine breaks:\n\u2022 Use \\n, {\\n} or {\\n15}; the number is the line-height delta in statusbar units\n\u2022 Omit the number to use the default row spacing\n\u2022 { /\\n } style line breaks are written without spaces: {/\\n} or {/\\n15}\n\u2022 Normal line breaks propagate open tags onto the next row\n\u2022 A / line break ends all currently open tags before the next row starts\n\nPipes and grouping:\n\u2022 Maximum two pipes per row; more than two is a syntax error for that row only\n\u2022 Pipes inside single quotes or {} do not split the row\n\u2022 One pipe splits into left and right segments\n\u2022 Two pipes remove the middle section, then split using the outer pipes\n\u2022 Tags are logically continued across pipe splits, even if the middle section is ignored\n\u2022 Rows without pipes are independent; rows with pipes share one common pipe group\n\u2022 A syntax-error row becomes the text Syntax error, with no whole-clock fallback\n\nColours:\n\u2022 Supported operands: #RGB, #ARGB, #RRGGBB, #AARRGGBB, #batterybar, [R,G,B], [A,R,G,B], and numbers\n\u2022 Colour expressions use reverse Polish notation: operands first, operator last\n\u2022 Use ; to separate bright/dark variants; the dark side starts with the raw result from the bright side on the stack\n\u2022 Operators: + - * / ^ inv min max sqrt clip dup\n\u2022 Operators can be channel-filtered, for example +A, *RGB, invRGB, maxRGB, clipA\n\u2022 Final values are clipped to 0..255; a final number becomes opaque grayscale</string>
|
||||||
<string name="clock_custom_format_help_examples">Examples:\n\u2022 <small>EEE d MMM</small> HH:mm\n\u2022 HH<small>:mm</small>\n\u2022 <font color=\'#FA0\'>ss</font>\n\u2022 {color(#FA0 ; invRGB 0.8 *RGB)}ss\n\u2022 {color(#batterybar 1.2 * ; invRGB 0.5 *RGB) big u @sans-serif-condensed}HH:mm\n\u2022 {color([255, 255, 170, 0] ; invRGB)}HH:mm\n\u2022 HH:mm{\\n12 small}EEE d MMM\n\u2022 HH|mm aligns the pipe position across every piped row\n\u2022 left||right removes the middle section before split alignment\n\u2022 {/font}{/\\n big}HH:mm starts a new row without carrying the previous font tags forward</string>
|
<string name="clock_custom_format_help_examples">Examples:\n\u2022 <small>EEE d MMM</small> HH:mm\n\u2022 HH<small>:mm</small>\n\u2022 <font color=\'#FA0\'>ss</font>\n\u2022 {color(#FA0 ; invRGB 0.8 *RGB)}ss\n\u2022 {color(#batterybar 1.2 * ; invRGB 0.5 *RGB) big u @sans-serif-condensed}HH:mm\n\u2022 {color([255, 255, 170, 0] ; invRGB)}HH:mm\n\u2022 HH:mm{\\n12 small}EEE d MMM\n\u2022 HH|mm aligns the pipe position across every piped row\n\u2022 left||right removes the middle section before split alignment\n\u2022 {/font}{/\\n big}HH:mm starts a new row without carrying the previous font tags forward</string>
|
||||||
<string name="clock_font_picker_button">Browse font families</string>
|
<string name="clock_font_picker_button">Browse font families</string>
|
||||||
|
<string name="clock_restore_stored_patterns">Restore stored patterns</string>
|
||||||
|
<string name="clock_stored_patterns_show">Show stored patterns</string>
|
||||||
|
<string name="clock_stored_patterns_hide">Hide stored patterns</string>
|
||||||
|
<string name="clock_stored_patterns_hint">Tap a preview to use it</string>
|
||||||
|
<string name="clock_store_current_pattern">Store current pattern</string>
|
||||||
<string name="clock_custom_format_help_show">▾ Show pattern instructions</string>
|
<string name="clock_custom_format_help_show">▾ Show pattern instructions</string>
|
||||||
<string name="clock_custom_format_help_hide">▴ Hide pattern instructions</string>
|
<string name="clock_custom_format_help_hide">▴ Hide pattern instructions</string>
|
||||||
<string name="clock_font_picker_title">Font families</string>
|
<string name="clock_font_picker_title">Font families (tap to copy)</string>
|
||||||
<string name="clock_font_picker_hint">Search font family</string>
|
<string name="clock_font_picker_hint">Search font family</string>
|
||||||
<string name="clock_font_picker_copied">Copied: %1$s</string>
|
<string name="clock_font_picker_copied">Copied: %1$s</string>
|
||||||
<string name="clock_custom_format_help_link">Date/time pattern reference only: https://docs.oracle.com/javase/8/docs/api/java/text/SimpleDateFormat.html</string>
|
<string name="clock_custom_format_help_link">Date/time pattern reference only: https://docs.oracle.com/javase/8/docs/api/java/text/SimpleDateFormat.html</string>
|
||||||
|
|||||||
+34
@@ -49,4 +49,38 @@ public final class ClockColorResolverTest {
|
|||||||
public void rejectsMoreThanTwoColorVariants() {
|
public void rejectsMoreThanTwoColorVariants() {
|
||||||
resolver.resolveColor(null, "#123 ; #456 ; #789", WHITE_REFERENCE, 0xffffffff);
|
resolver.resolveColor(null, "#123 ; #456 ; #789", WHITE_REFERENCE, 0xffffffff);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void batteryBarOptionsAllowBareHexColours() {
|
||||||
|
assertEquals(
|
||||||
|
0xff808080,
|
||||||
|
resolver.resolveColor(
|
||||||
|
null,
|
||||||
|
"808080",
|
||||||
|
WHITE_REFERENCE,
|
||||||
|
0xffffffff,
|
||||||
|
ClockColorResolver.Options.batteryBar()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void batteryBarOptionsKeepThreeDigitNumbersAsNumbers() {
|
||||||
|
assertEquals(
|
||||||
|
0xffffffff,
|
||||||
|
resolver.resolveColor(
|
||||||
|
null,
|
||||||
|
"255",
|
||||||
|
WHITE_REFERENCE,
|
||||||
|
0xffffffff,
|
||||||
|
ClockColorResolver.Options.batteryBar()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = IllegalArgumentException.class)
|
||||||
|
public void batteryBarOptionsRejectBatteryBarOperand() {
|
||||||
|
resolver.resolveColor(
|
||||||
|
null,
|
||||||
|
"#batterybar",
|
||||||
|
WHITE_REFERENCE,
|
||||||
|
0xffffffff,
|
||||||
|
ClockColorResolver.Options.batteryBar());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+21
@@ -0,0 +1,21 @@
|
|||||||
|
package se.ajpanton.statusbartweak.runtime.features.clock;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
public final class ClockPatternParserTest {
|
||||||
|
private final ClockPatternParser parser = new ClockPatternParser();
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void lineBreakBlockAcceptsFollowingShorthandTags() {
|
||||||
|
ClockParsedPattern combined = parser.parse("HH{\\n30 big}mm");
|
||||||
|
ClockParsedPattern split = parser.parse("HH{\\n30}{big}mm");
|
||||||
|
|
||||||
|
assertEquals(split.rows.size(), combined.rows.size());
|
||||||
|
assertEquals(2, combined.rows.size());
|
||||||
|
assertEquals(split.rows.get(1).gapBeforePx, combined.rows.get(1).gapBeforePx);
|
||||||
|
assertEquals(30, combined.rows.get(1).gapBeforePx);
|
||||||
|
assertEquals(split.rows.get(1).markup, combined.rows.get(1).markup);
|
||||||
|
}
|
||||||
|
}
|
||||||
+1
-1
@@ -1 +1 @@
|
|||||||
version=0.1
|
version=0.5
|
||||||
|
|||||||
Reference in New Issue
Block a user