Fix unlocked tint refresh

This commit is contained in:
ajp_anton
2026-05-04 14:03:10 +00:00
parent 3f187e911b
commit 1425012932
11 changed files with 496 additions and 20 deletions
@@ -22,21 +22,30 @@ public final class ClockLayoutTextFactory {
} }
public static Model build(TextView source, SbtSettings settings) { public static Model build(TextView source, SbtSettings settings) {
return build(source, settings, source != null ? source.getCurrentTextColor() : 0);
}
public static Model build(TextView source, SbtSettings settings, int referenceColor) {
Context context = source != null ? source.getContext() : null; Context context = source != null ? source.getContext() : null;
if (context == null || settings == null) { if (context == null || settings == null) {
return Model.empty(); return Model.empty();
} }
CacheKey cacheKey = cacheKey(context, source, settings); CacheKey cacheKey = cacheKey(context, source, settings, referenceColor);
CachedModel cached = MODEL_CACHE.get(source); CachedModel cached = MODEL_CACHE.get(source);
if (cached != null && cached.key.equals(cacheKey)) { if (cached != null && cached.key.equals(cacheKey)) {
return cached.model; return cached.model;
} }
Model model = buildUncached(context, source, settings); Model model = buildUncached(context, source, settings, referenceColor);
MODEL_CACHE.put(source, new CachedModel(cacheKey, model)); MODEL_CACHE.put(source, new CachedModel(cacheKey, model));
return model; return model;
} }
private static Model buildUncached(Context context, TextView source, SbtSettings settings) { private static Model buildUncached(
Context context,
TextView source,
SbtSettings settings,
int referenceColor
) {
if (!hasCustomClockTextEnabled(settings)) { if (!hasCustomClockTextEnabled(settings)) {
ArrayList<Row> rows = new ArrayList<>(); ArrayList<Row> rows = new ArrayList<>();
CharSequence text = source.getText(); CharSequence text = source.getText();
@@ -49,7 +58,7 @@ public final class ClockLayoutTextFactory {
&& !settings.clockCustomFormat.isEmpty()) { && !settings.clockCustomFormat.isEmpty()) {
ClockParsedPattern parsedPattern = PATTERN_PARSER.parse(settings.clockCustomFormat); ClockParsedPattern parsedPattern = PATTERN_PARSER.parse(settings.clockCustomFormat);
ArrayList<ClockTextRenderer.LayoutRowResult> renderedRows = ArrayList<ClockTextRenderer.LayoutRowResult> renderedRows =
TEXT_RENDERER.renderLayoutRows(context, parsedPattern, source.getCurrentTextColor()); TEXT_RENDERER.renderLayoutRows(context, parsedPattern, referenceColor);
ArrayList<Row> rows = new ArrayList<>(); ArrayList<Row> rows = new ArrayList<>();
StringBuilder description = new StringBuilder(); StringBuilder description = new StringBuilder();
for (int i = 0; i < renderedRows.size(); i++) { for (int i = 0; i < renderedRows.size(); i++) {
@@ -73,7 +82,12 @@ public final class ClockLayoutTextFactory {
return new Model(rows, text != null ? text.toString() : "", settings.clockShowSeconds); return new Model(rows, text != null ? text.toString() : "", settings.clockShowSeconds);
} }
private static CacheKey cacheKey(Context context, TextView source, SbtSettings settings) { private static CacheKey cacheKey(
Context context,
TextView source,
SbtSettings settings,
int referenceColor
) {
String pattern = settings.clockCustomFormat != null ? settings.clockCustomFormat : ""; String pattern = settings.clockCustomFormat != null ? settings.clockCustomFormat : "";
boolean customPattern = settings.clockCustomFormatEnabled && !pattern.isEmpty(); boolean customPattern = settings.clockCustomFormatEnabled && !pattern.isEmpty();
boolean generatedText = hasCustomClockTextEnabled(settings); boolean generatedText = hasCustomClockTextEnabled(settings);
@@ -85,7 +99,7 @@ public final class ClockLayoutTextFactory {
return new CacheKey( return new CacheKey(
localeFor(context).toLanguageTag(), localeFor(context).toLanguageTag(),
System.currentTimeMillis() / divisor, System.currentTimeMillis() / divisor,
source.getCurrentTextColor(), referenceColor,
settings.clockShowSeconds, settings.clockShowSeconds,
settings.clockShowDatePrefix, settings.clockShowDatePrefix,
customPattern, customPattern,
@@ -193,6 +193,16 @@ final class StockLayoutCanvasController {
removeDebugOverlay(root); removeDebugOverlay(root);
} }
hideTrackedUnlockedStockSurfaces(root); hideTrackedUnlockedStockSurfaces(root);
UnlockedHosts unlockedHosts = unlockedHostsByRoot.get(root);
if (unlockedHosts != null) {
unlockedIconRenderController.refreshUnlockedAppearance(
root,
unlockedHosts.clockHost,
unlockedHosts.chipHost,
unlockedHosts.statusHost,
unlockedHosts.notificationHost,
activeScene);
}
} }
} }
@@ -1,5 +1,7 @@
package se.ajpanton.statusbartweak.runtime.render; package se.ajpanton.statusbartweak.runtime.render;
import android.text.Spanned;
import android.text.style.ForegroundColorSpan;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.FrameLayout; import android.widget.FrameLayout;
@@ -47,7 +49,7 @@ final class ClockProxyRenderer {
if (copyTextStyleIfChanged(placement.source, view)) { if (copyTextStyleIfChanged(placement.source, view)) {
hostChanged = true; hostChanged = true;
} }
if (!String.valueOf(view.getText()).contentEquals(row.text)) { if (!sameTextAndColorSpans(view.getText(), row.text)) {
view.setText(row.text); view.setText(row.text);
hostChanged = true; hostChanged = true;
} }
@@ -87,6 +89,31 @@ final class ClockProxyRenderer {
} }
} }
private static boolean sameTextAndColorSpans(CharSequence current, CharSequence desired) {
if (!String.valueOf(current).contentEquals(String.valueOf(desired))) {
return false;
}
return colorSpanSignature(current) == colorSpanSignature(desired);
}
private static int colorSpanSignature(CharSequence text) {
if (!(text instanceof Spanned spanned)) {
return 0;
}
ForegroundColorSpan[] spans = spanned.getSpans(0, spanned.length(), ForegroundColorSpan.class);
if (spans == null || spans.length == 0) {
return 0;
}
int result = 17;
for (ForegroundColorSpan span : spans) {
result = 31 * result + span.getForegroundColor();
result = 31 * result + spanned.getSpanStart(span);
result = 31 * result + spanned.getSpanEnd(span);
result = 31 * result + spanned.getSpanFlags(span);
}
return result;
}
void clear(ViewGroup host) { void clear(ViewGroup host) {
if (host == null) { if (host == null) {
return; return;
@@ -0,0 +1,131 @@
package se.ajpanton.statusbartweak.runtime.render;
import android.content.res.ColorStateList;
import android.graphics.Color;
import android.graphics.ColorFilter;
import android.graphics.PorterDuff;
import android.graphics.drawable.Drawable;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
import se.ajpanton.statusbartweak.runtime.hooks.ReflectionSupport;
final class SnapshotAppearanceSignature {
private static final int NO_COLOR = 0;
private static final String[] COLOR_FIELD_NAMES = {
"mIconColor",
"mDecorColor",
"mStaticDrawableColor",
"mIconTint",
"mTint",
"mTintColor",
"mCurrentColor",
"mCurrentIconColor"
};
private SnapshotAppearanceSignature() {
}
static int view(View view) {
if (view == null) {
return 0;
}
int result = 17;
result = 31 * result + java.util.Arrays.hashCode(view.getDrawableState());
if (view instanceof TextView textView) {
result = 31 * result + textView.getCurrentTextColor();
result = 31 * result + textView.getTextColors().hashCode();
}
if (view instanceof ImageView imageView) {
result = 31 * result + drawable(imageView.getDrawable());
result = 31 * result + colorStateList(imageView.getImageTintList());
PorterDuff.Mode tintMode = imageView.getImageTintMode();
result = 31 * result + (tintMode != null ? tintMode.hashCode() : 0);
ColorFilter colorFilter = imageView.getColorFilter();
result = 31 * result + (colorFilter != null ? System.identityHashCode(colorFilter) : 0);
}
result = 31 * result + reflectedColor(view, COLOR_FIELD_NAMES);
return result;
}
static int drawable(Drawable drawable) {
if (drawable == null) {
return 0;
}
int result = 17;
result = 31 * result + System.identityHashCode(drawable);
result = 31 * result + drawable.getLevel();
result = 31 * result + drawable.getAlpha();
result = 31 * result + java.util.Arrays.hashCode(drawable.getState());
ColorFilter colorFilter = drawable.getColorFilter();
result = 31 * result + (colorFilter != null ? System.identityHashCode(colorFilter) : 0);
return result;
}
static int preferredColor(View view, int fallbackColor) {
if (view == null) {
return fallbackColor;
}
if (view instanceof TextView textView) {
return textView.getCurrentTextColor();
}
if (view instanceof ImageView imageView) {
int tintColor = colorStateListColor(imageView.getImageTintList(), view, NO_COLOR);
if (isOpaqueColor(tintColor)) {
return tintColor;
}
}
int reflectedColor = reflectedColor(view, COLOR_FIELD_NAMES);
if (isOpaqueColor(reflectedColor)) {
return reflectedColor;
}
return fallbackColor;
}
private static int reflectedColor(Object target, String[] fieldNames) {
for (String fieldName : fieldNames) {
Object value = ReflectionSupport.getFieldValue(target, fieldName);
int color = colorValue(value, NO_COLOR);
if (isOpaqueColor(color)) {
return color;
}
}
return NO_COLOR;
}
private static int colorValue(Object value, int fallbackColor) {
if (value instanceof Integer integer) {
return integer;
}
if (value instanceof ColorStateList colorStateList) {
return colorStateList.getDefaultColor();
}
return fallbackColor;
}
private static int colorStateListColor(ColorStateList colorStateList, View view, int fallbackColor) {
if (colorStateList == null) {
return fallbackColor;
}
return colorStateList.getColorForState(
view != null ? view.getDrawableState() : null,
colorStateList.getDefaultColor());
}
private static boolean isOpaqueColor(int color) {
return Color.alpha(color) != 0;
}
private static int colorStateList(ColorStateList colorStateList) {
if (colorStateList == null) {
return 0;
}
int result = 17;
result = 31 * result + colorStateList.getDefaultColor();
result = 31 * result + (colorStateList.isStateful() ? 1 : 0);
result = 31 * result + colorStateList.hashCode();
return result;
}
}
@@ -1,6 +1,7 @@
package se.ajpanton.statusbartweak.runtime.render; package se.ajpanton.statusbartweak.runtime.render;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.graphics.Color;
import android.view.View; import android.view.View;
import android.widget.TextView; import android.widget.TextView;
@@ -34,6 +35,7 @@ final class SnapshotPlacement {
final String key; final String key;
final boolean signal; final boolean signal;
final boolean overflowDot; final boolean overflowDot;
final int overflowDotColor;
final HeldSignal heldSignal; final HeldSignal heldSignal;
final Bounds contentBounds; final Bounds contentBounds;
final int sourceOffsetX; final int sourceOffsetX;
@@ -47,12 +49,36 @@ final class SnapshotPlacement {
HeldSignal heldSignal, HeldSignal heldSignal,
Bounds contentBounds, Bounds contentBounds,
int sourceOffsetX int sourceOffsetX
) {
this(
source,
bounds,
key,
signal,
overflowDot,
Color.WHITE,
heldSignal,
contentBounds,
sourceOffsetX);
}
SnapshotPlacement(
SnapshotSource source,
Bounds bounds,
String key,
boolean signal,
boolean overflowDot,
int overflowDotColor,
HeldSignal heldSignal,
Bounds contentBounds,
int sourceOffsetX
) { ) {
this.source = source; this.source = source;
this.bounds = bounds; this.bounds = bounds;
this.key = key; this.key = key;
this.signal = signal; this.signal = signal;
this.overflowDot = overflowDot; this.overflowDot = overflowDot;
this.overflowDotColor = overflowDotColor;
this.heldSignal = heldSignal; this.heldSignal = heldSignal;
this.contentBounds = contentBounds; this.contentBounds = contentBounds;
this.sourceOffsetX = sourceOffsetX; this.sourceOffsetX = sourceOffsetX;
@@ -78,6 +104,10 @@ final class SnapshotPlacement {
return overflowDot; return overflowDot;
} }
int overflowDotColor() {
return overflowDotColor;
}
HeldSignal heldSignal() { HeldSignal heldSignal() {
return heldSignal; return heldSignal;
} }
@@ -102,6 +132,7 @@ record SnapshotRenderState(
int height, int height,
boolean signal, boolean signal,
boolean overflowDot, boolean overflowDot,
int overflowDotColor,
int sourceOffsetX, int sourceOffsetX,
String heldSignalKey, String heldSignalKey,
int heldBitmapId, int heldBitmapId,
@@ -176,7 +176,7 @@ final class SnapshotRenderer {
|| !desiredState.equals(statesByProxy.get(proxy)); || !desiredState.equals(statesByProxy.get(proxy));
if (bitmapChanged) { if (bitmapChanged) {
if (placement.overflowDot()) { if (placement.overflowDot()) {
drawOverflowDot(bitmap); drawOverflowDot(bitmap, placement.overflowDotColor());
} else if (placement.heldSignal() != null) { } else if (placement.heldSignal() != null) {
drawHeldSignal(placement.heldSignal(), bitmap); drawHeldSignal(placement.heldSignal(), bitmap);
} else if (placement.source() != null) { } else if (placement.source() != null) {
@@ -471,6 +471,7 @@ final class SnapshotRenderer {
bounds.height, bounds.height,
placement.signal(), placement.signal(),
placement.overflowDot(), placement.overflowDot(),
placement.overflowDotColor(),
placement.sourceOffsetX(), placement.sourceOffsetX(),
placement.heldSignal() != null ? placement.heldSignal().key : "", placement.heldSignal() != null ? placement.heldSignal().key : "",
placement.heldSignal() != null ? System.identityHashCode(placement.heldSignal().bitmap) : 0, placement.heldSignal() != null ? System.identityHashCode(placement.heldSignal().bitmap) : 0,
@@ -493,6 +494,7 @@ final class SnapshotRenderer {
CharSequence contentDescription = view.getContentDescription(); CharSequence contentDescription = view.getContentDescription();
result = 31 * result + (contentDescription != null ? contentDescription.toString().hashCode() : 0); result = 31 * result + (contentDescription != null ? contentDescription.toString().hashCode() : 0);
result = 31 * result + java.util.Arrays.hashCode(view.getDrawableState()); result = 31 * result + java.util.Arrays.hashCode(view.getDrawableState());
result = 31 * result + SnapshotAppearanceSignature.view(view);
if (view instanceof TextView textView) { if (view instanceof TextView textView) {
CharSequence text = textView.getText(); CharSequence text = textView.getText();
result = 31 * result + (text != null ? text.toString().hashCode() : 0); result = 31 * result + (text != null ? text.toString().hashCode() : 0);
@@ -500,7 +502,7 @@ final class SnapshotRenderer {
result = 31 * result + Float.floatToIntBits(textView.getTextSize()); result = 31 * result + Float.floatToIntBits(textView.getTextSize());
} }
if (view instanceof ImageView imageView) { if (view instanceof ImageView imageView) {
result = 31 * result + drawableSignature(imageView.getDrawable()); result = 31 * result + SnapshotAppearanceSignature.drawable(imageView.getDrawable());
} }
if (view instanceof ViewGroup group) { if (view instanceof ViewGroup group) {
result = 31 * result + group.getChildCount(); result = 31 * result + group.getChildCount();
@@ -516,10 +518,7 @@ final class SnapshotRenderer {
return 0; return 0;
} }
int result = 17; int result = 17;
result = 31 * result + System.identityHashCode(drawable); result = 31 * result + SnapshotAppearanceSignature.drawable(drawable);
result = 31 * result + drawable.getLevel();
result = 31 * result + drawable.getAlpha();
result = 31 * result + java.util.Arrays.hashCode(drawable.getState());
Rect bounds = drawable.getBounds(); Rect bounds = drawable.getBounds();
result = 31 * result + bounds.left; result = 31 * result + bounds.left;
result = 31 * result + bounds.top; result = 31 * result + bounds.top;
@@ -570,7 +569,7 @@ final class SnapshotRenderer {
canvas.drawBitmap(heldSignal.bitmap, src, dst, null); canvas.drawBitmap(heldSignal.bitmap, src, dst, null);
} }
private void drawOverflowDot(Bitmap bitmap) { private void drawOverflowDot(Bitmap bitmap, int color) {
bitmap.eraseColor(Color.TRANSPARENT); bitmap.eraseColor(Color.TRANSPARENT);
int base = Math.min(bitmap.getWidth(), bitmap.getHeight()); int base = Math.min(bitmap.getWidth(), bitmap.getHeight());
if (base <= 0) { if (base <= 0) {
@@ -578,7 +577,7 @@ final class SnapshotRenderer {
} }
float radius = Math.max(1f, base * 0.16f); float radius = Math.max(1f, base * 0.16f);
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setColor(Color.WHITE); paint.setColor(color);
Canvas canvas = new Canvas(bitmap); Canvas canvas = new Canvas(bitmap);
canvas.drawCircle(bitmap.getWidth() / 2f, bitmap.getHeight() / 2f, radius, paint); canvas.drawCircle(bitmap.getWidth() / 2f, bitmap.getHeight() / 2f, radius, paint);
} }
@@ -190,6 +190,7 @@ final class UnlockedChipPlacementController {
placement.key, placement.key,
false, false,
false, false,
placement.overflowDotColor,
null, null,
placement.contentBounds, placement.contentBounds,
placement.sourceOffsetX)); placement.sourceOffsetX));
@@ -211,6 +212,7 @@ final class UnlockedChipPlacementController {
placement.key, placement.key,
placement.signal, placement.signal,
placement.overflowDot, placement.overflowDot,
placement.overflowDotColor,
placement.heldSignal, placement.heldSignal,
placement.contentBounds, placement.contentBounds,
placement.sourceOffsetX)); placement.sourceOffsetX));
@@ -34,6 +34,7 @@ public final class UnlockedIconSnapshotRenderController {
private final WeakHashMap<View, LayoutPlan> lastLayoutPlanByRoot = new WeakHashMap<>(); private final WeakHashMap<View, LayoutPlan> lastLayoutPlanByRoot = new WeakHashMap<>();
private final WeakHashMap<View, Integer> lastClockGeometryByRoot = new WeakHashMap<>(); private final WeakHashMap<View, Integer> lastClockGeometryByRoot = new WeakHashMap<>();
private final WeakHashMap<View, Integer> lastClockSourceGeometryByRoot = new WeakHashMap<>(); private final WeakHashMap<View, Integer> lastClockSourceGeometryByRoot = new WeakHashMap<>();
private final WeakHashMap<View, Integer> lastClockAppearanceByRoot = new WeakHashMap<>();
public UnlockedIconSnapshotRenderController() { public UnlockedIconSnapshotRenderController() {
statusRenderer = new SnapshotRenderer(true, true, false); statusRenderer = new SnapshotRenderer(true, true, false);
@@ -164,6 +165,8 @@ public final class UnlockedIconSnapshotRenderController {
lastLayoutPlanByRoot.put(root, layoutPlan); lastLayoutPlanByRoot.put(root, layoutPlan);
lastClockGeometryByRoot.put(root, currentClockGeometry != null ? currentClockGeometry : 0); lastClockGeometryByRoot.put(root, currentClockGeometry != null ? currentClockGeometry : 0);
lastClockSourceGeometryByRoot.put(root, currentClockSourceGeometry); lastClockSourceGeometryByRoot.put(root, currentClockSourceGeometry);
layoutPlan = withCurrentDotColors(layoutPlan, collectedIcons);
lastLayoutPlanByRoot.put(root, layoutPlan);
if (settings.clockEnabledUnlocked && layoutPlan.clockPlacement != null) { if (settings.clockEnabledUnlocked && layoutPlan.clockPlacement != null) {
clockRenderer.render(clockHost, layoutPlan.clockPlacement); clockRenderer.render(clockHost, layoutPlan.clockPlacement);
} else { } else {
@@ -187,6 +190,47 @@ public final class UnlockedIconSnapshotRenderController {
return new RenderResult(true, stockSourcesChanged, chipSourcesChanged); return new RenderResult(true, stockSourcesChanged, chipSourcesChanged);
} }
public void refreshUnlockedAppearance(
View root,
ViewGroup clockHost,
ViewGroup chipHost,
ViewGroup statusHost,
ViewGroup notificationHost,
SceneKey scene
) {
if (root == null || scene == null || !scene.isUnlocked() || !root.isAttachedToWindow()) {
return;
}
LayoutPlan layoutPlan = lastLayoutPlanByRoot.get(root);
if (layoutPlan == null) {
return;
}
SbtSettings settings = RuntimeSettingsCache.get(root.getContext());
CollectedIcons collectedIcons = lastCollectedIconsByRoot.get(root);
LayoutPlan updatedPlan = withCurrentDotColors(layoutPlan, collectedIcons);
if (updatedPlan != layoutPlan) {
lastLayoutPlanByRoot.put(root, updatedPlan);
layoutPlan = updatedPlan;
}
if (settings.layoutChipEnabledUnlocked && layoutPlan.chipPlacements != null) {
chipRenderer.renderPlacements(chipHost, layoutPlan.chipPlacements);
}
if (settings.layoutStatusEnabledUnlocked && layoutPlan.statusPlacements != null) {
statusRenderer.renderPlacements(statusHost, layoutPlan.statusPlacements);
}
if (settings.layoutNotifEnabledUnlocked && layoutPlan.notificationPlacements != null) {
notificationRenderer.renderPlacements(notificationHost, layoutPlan.notificationPlacements);
}
updatedPlan = withCurrentClockAppearance(root, layoutPlan, collectedIcons, settings);
if (updatedPlan != layoutPlan) {
lastLayoutPlanByRoot.put(root, updatedPlan);
layoutPlan = updatedPlan;
}
if (settings.clockEnabledUnlocked && layoutPlan.clockPlacement != null) {
clockRenderer.render(clockHost, layoutPlan.clockPlacement);
}
}
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;
@@ -205,6 +249,180 @@ public final class UnlockedIconSnapshotRenderController {
host.layout(0, 0, root.getWidth(), root.getHeight()); host.layout(0, 0, root.getWidth(), root.getHeight());
} }
private LayoutPlan withCurrentDotColors(LayoutPlan plan, CollectedIcons icons) {
if (plan == null) {
return null;
}
ArrayList<SnapshotPlacement> status = withCurrentDotColors(
plan.statusPlacements,
icons != null ? icons.statusIcons : null);
ArrayList<SnapshotPlacement> notifications = withCurrentDotColors(
plan.notificationPlacements,
icons != null ? icons.notificationIcons : null);
if (status == plan.statusPlacements && notifications == plan.notificationPlacements) {
return plan;
}
return new LayoutPlan(
plan.clockPlacement,
plan.chipPlacements,
status,
notifications);
}
private ArrayList<SnapshotPlacement> withCurrentDotColors(
ArrayList<SnapshotPlacement> placements,
ArrayList<SnapshotSource> fallbackSources
) {
if (placements == null || placements.isEmpty()) {
return placements;
}
int color = dotColor(placements, fallbackSources);
if (color == 0) {
return placements;
}
ArrayList<SnapshotPlacement> updated = null;
for (int i = 0; i < placements.size(); i++) {
SnapshotPlacement placement = placements.get(i);
if (!placement.overflowDot || placement.overflowDotColor == color) {
continue;
}
if (updated == null) {
updated = new ArrayList<>(placements);
}
updated.set(i, new SnapshotPlacement(
placement.source,
placement.bounds,
placement.key,
placement.signal,
placement.overflowDot,
color,
placement.heldSignal,
placement.contentBounds,
placement.sourceOffsetX));
}
return updated != null ? updated : placements;
}
private int dotColor(
ArrayList<SnapshotPlacement> placements,
ArrayList<SnapshotSource> fallbackSources
) {
for (SnapshotPlacement placement : placements) {
if (placement != null && placement.source != null) {
int color = sourceColor(placement.source);
if (color != 0) {
return color;
}
}
}
if (fallbackSources != null) {
for (SnapshotSource source : fallbackSources) {
int color = sourceColor(source);
if (color != 0) {
return color;
}
}
}
return 0;
}
private int sourceColor(SnapshotSource source) {
if (source == null || source.view() == null) {
return 0;
}
int color = SnapshotAppearanceSignature.preferredColor(
source.view(),
android.graphics.Color.TRANSPARENT);
return android.graphics.Color.alpha(color) != 0 ? color : 0;
}
private LayoutPlan withCurrentClockAppearance(
View root,
LayoutPlan plan,
CollectedIcons icons,
SbtSettings settings
) {
if (root == null
|| plan == null
|| plan.clockPlacement == null
|| !settings.clockEnabledUnlocked) {
return plan;
}
ClockPlacement previous = plan.clockPlacement;
int referenceColor = clockReferenceColor(previous, icons);
int signature = clockAppearanceSignature(previous, referenceColor, settings);
Integer lastSignature = lastClockAppearanceByRoot.get(root);
if (lastSignature != null && lastSignature == signature) {
return plan;
}
ClockPlacement updatedClock = clockPlacementWithTextFromModel(
previous,
previous.source,
settings,
referenceColor);
lastClockAppearanceByRoot.put(root, signature);
if (updatedClock == null) {
return plan;
}
return plan.withClockPlacement(updatedClock);
}
private int clockReferenceColor(ClockPlacement placement, CollectedIcons icons) {
if (icons != null && icons.statusIcons != null) {
for (SnapshotSource source : icons.statusIcons) {
int color = sourceColor(source);
if (color != 0) {
return color;
}
}
}
if (icons != null && icons.notificationIcons != null) {
for (SnapshotSource source : icons.notificationIcons) {
int color = sourceColor(source);
if (color != 0) {
return color;
}
}
}
if (placement != null && placement.source != null) {
return placement.source.getCurrentTextColor();
}
return android.graphics.Color.WHITE;
}
private int clockAppearanceSignature(
ClockPlacement placement,
int referenceColor,
SbtSettings settings
) {
int result = 17;
result = 31 * result + referenceColor;
result = 31 * result + clockTextSettingsSignature(settings);
if (placement != null && placement.source != null) {
result = 31 * result + placement.source.getCurrentTextColor();
}
if (placement != null && placement.rows != null) {
result = 31 * result + placement.rows.size();
for (ClockRow row : placement.rows) {
result = 31 * result + row.rowIndex;
result = 31 * result + row.segment.ordinal();
result = 31 * result + (row.text != null ? row.text.toString().hashCode() : 0);
}
}
return result;
}
private int clockTextSettingsSignature(SbtSettings settings) {
if (settings == null) {
return 0;
}
return java.util.Objects.hash(
settings.clockShowSeconds,
settings.clockShowDatePrefix,
settings.clockCustomFormatEnabled,
settings.clockCustomFormat);
}
public void clearAll() { public void clearAll() {
clockRenderer.clearAll(); clockRenderer.clearAll();
statusRenderer.clearAll(); statusRenderer.clearAll();
@@ -218,6 +436,7 @@ public final class UnlockedIconSnapshotRenderController {
lastLayoutPlanByRoot.clear(); lastLayoutPlanByRoot.clear();
lastClockGeometryByRoot.clear(); lastClockGeometryByRoot.clear();
lastClockSourceGeometryByRoot.clear(); lastClockSourceGeometryByRoot.clear();
lastClockAppearanceByRoot.clear();
chipPlacementController.clear(); chipPlacementController.clear();
} }
@@ -228,6 +447,7 @@ public final class UnlockedIconSnapshotRenderController {
lastCollectedIconsByRoot.clear(); lastCollectedIconsByRoot.clear();
lastLayoutPlanByRoot.clear(); lastLayoutPlanByRoot.clear();
lastClockGeometryByRoot.clear(); lastClockGeometryByRoot.clear();
lastClockAppearanceByRoot.clear();
} }
private boolean isClockOnlySignatureChange( private boolean isClockOnlySignatureChange(
@@ -368,6 +588,19 @@ public final class UnlockedIconSnapshotRenderController {
ClockPlacement previousPlacement, ClockPlacement previousPlacement,
TextView source, TextView source,
SbtSettings settings SbtSettings settings
) {
return clockPlacementWithTextFromModel(
previousPlacement,
source,
settings,
source != null ? source.getCurrentTextColor() : 0);
}
private ClockPlacement clockPlacementWithTextFromModel(
ClockPlacement previousPlacement,
TextView source,
SbtSettings settings,
int referenceColor
) { ) {
if (previousPlacement == null if (previousPlacement == null
|| previousPlacement.rows == null || previousPlacement.rows == null
@@ -375,7 +608,7 @@ public final class UnlockedIconSnapshotRenderController {
|| settings == null) { || settings == null) {
return null; return null;
} }
ClockLayoutTextFactory.Model model = ClockLayoutTextFactory.build(source, settings); ClockLayoutTextFactory.Model model = ClockLayoutTextFactory.build(source, settings, referenceColor);
if (model == null || model.isEmpty()) { if (model == null || model.isEmpty()) {
return null; return null;
} }
@@ -493,11 +493,40 @@ final class UnlockedLayoutBand extends StatusBarLayoutSolver.LayoutBand {
SnapshotKeys.OVERFLOW_DOT + ":" + kind.name().toLowerCase(java.util.Locale.ROOT), SnapshotKeys.OVERFLOW_DOT + ":" + kind.name().toLowerCase(java.util.Locale.ROOT),
false, false,
true, true,
dotColor(),
null, null,
new Bounds(0, 0, Math.max(1, width), Math.max(1, height)), new Bounds(0, 0, Math.max(1, width), Math.max(1, height)),
0); 0);
} }
private int dotColor() {
for (SnapshotPlacement placement : activePlacements()) {
int color = sourceColor(placement);
if (color != 0) {
return color;
}
}
if (rawPlacements != null) {
for (SnapshotPlacement placement : rawPlacements) {
int color = sourceColor(placement);
if (color != 0) {
return color;
}
}
}
return android.graphics.Color.WHITE;
}
private int sourceColor(SnapshotPlacement placement) {
if (placement == null || placement.source == null || placement.source.view() == null) {
return 0;
}
int color = SnapshotAppearanceSignature.preferredColor(
placement.source.view(),
android.graphics.Color.TRANSPARENT);
return android.graphics.Color.alpha(color) != 0 ? color : 0;
}
private SnapshotPlacement emptyIconContainerPlacement() { private SnapshotPlacement emptyIconContainerPlacement() {
int height = Math.max(1, dotHeight(activePlacements())); int height = Math.max(1, dotHeight(activePlacements()));
return new SnapshotPlacement( return new SnapshotPlacement(
@@ -548,6 +577,7 @@ final class UnlockedLayoutBand extends StatusBarLayoutSolver.LayoutBand {
placement.key, placement.key,
placement.signal, placement.signal,
placement.overflowDot, placement.overflowDot,
placement.overflowDotColor,
placement.heldSignal, placement.heldSignal,
clippedContent, clippedContent,
placement.sourceOffsetX + removeLeft); placement.sourceOffsetX + removeLeft);
@@ -218,6 +218,7 @@ final class UnlockedLayoutPlanner {
placement.key, placement.key,
placement.signal, placement.signal,
placement.overflowDot, placement.overflowDot,
placement.overflowDotColor,
placement.heldSignal, placement.heldSignal,
placement.contentBounds, placement.contentBounds,
placement.sourceOffsetX)); placement.sourceOffsetX));
@@ -2,7 +2,6 @@ package se.ajpanton.statusbartweak.runtime.render;
import android.content.res.Resources; import android.content.res.Resources;
import android.graphics.Rect; import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.ImageView; import android.widget.ImageView;
@@ -636,19 +635,18 @@ final class UnlockedStockSourceCollector {
: 0); : 0);
} }
if (view instanceof ImageView imageView) { if (view instanceof ImageView imageView) {
result = 31 * result + drawableSignature(imageView.getDrawable()); result = 31 * result + drawableLayoutSignature(imageView.getDrawable());
} }
return result; return result;
} }
private static int drawableSignature(Drawable drawable) { private static int drawableLayoutSignature(android.graphics.drawable.Drawable drawable) {
if (drawable == null) { if (drawable == null) {
return 0; return 0;
} }
int result = 17; int result = 17;
result = 31 * result + System.identityHashCode(drawable); result = 31 * result + System.identityHashCode(drawable);
result = 31 * result + drawable.getLevel(); result = 31 * result + drawable.getLevel();
result = 31 * result + drawable.getAlpha();
result = 31 * result + java.util.Arrays.hashCode(drawable.getState()); result = 31 * result + java.util.Arrays.hashCode(drawable.getState());
Rect bounds = drawable.getBounds(); Rect bounds = drawable.getBounds();
result = 31 * result + bounds.left; result = 31 * result + bounds.left;