7 Commits
11 changed files with 322 additions and 60 deletions
@@ -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;
}
}
@@ -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;
} }
@@ -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;
} }
@@ -10,6 +10,7 @@ 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.widget.ImageView; import android.widget.ImageView;
@@ -34,6 +35,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;
@@ -405,6 +407,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;
} }
@@ -450,6 +455,16 @@ final class StockLayoutCanvasController {
} }
return chain.proceed(); return chain.proceed();
}); });
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;
}
}
return chain.proceed();
});
XposedHookSupport.hookAllMethodsIfExists(runtimeContext.getFramework(), viewClass, "layout", chain -> { XposedHookSupport.hookAllMethodsIfExists(runtimeContext.getFramework(), viewClass, "layout", chain -> {
Object target = chain.getThisObject(); Object target = chain.getThisObject();
List<Object> args = chain.getArgs(); List<Object> args = chain.getArgs();
@@ -1491,6 +1506,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)) {
@@ -2437,8 +2455,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;
} }
@@ -2989,6 +3006,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()
@@ -3008,6 +3026,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)) {
@@ -3344,6 +3363,60 @@ final class StockLayoutCanvasController {
return true; 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;
}
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 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;
@@ -3384,6 +3457,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;
@@ -3412,9 +3488,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;
@@ -4230,11 +4323,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) {
@@ -11,6 +11,7 @@ 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.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.os.BatteryManager;
@@ -35,6 +36,7 @@ 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;
@@ -238,35 +240,51 @@ final class SnapshotRenderer {
proxy.setTag(key); proxy.setTag(key);
proxyChanged = true; proxyChanged = true;
} }
BitmapResult bitmapResult = bitmapFor(proxy, bounds);
Bitmap bitmap = bitmapResult.bitmap();
SnapshotRenderState desiredState = renderStateFor(placement); SnapshotRenderState desiredState = renderStateFor(placement);
boolean bitmapChanged = bitmapResult.created() Drawable animatedDrawable = animatedNotificationDrawable(placement);
|| !desiredState.equals(statesByProxy.get(proxy)); if (animatedDrawable != null) {
if (bitmapChanged) { SnapshotRenderState currentState = statesByProxy.get(proxy);
if (placement.overflowDot()) { boolean drawableChanged = !(proxy.getDrawable() instanceof AnimationDrawable)
drawOverflowDot(bitmap, placement.overflowDotColor(), placement.sourceOffsetX()); || !canReuseAnimatedProxy(currentState, desiredState);
} else if (placement.heldSignal() != null) { if (drawableChanged) {
drawHeldSignal(placement.heldSignal(), bitmap); Drawable proxyDrawable = newDrawableInstance(animatedDrawable);
} else if (placement.source() != null proxy.setImageDrawable(proxyDrawable);
&& placement.source().mode() == SnapshotMode.APP_ICON) { proxyChanged = true;
drawAppIcon(host.getContext(), placement.source(), bitmap); }
} else if (placement.source() != null) { startAnimationIfNeeded(proxy.getDrawable());
drawSnapshot(placement, bitmap); if (!desiredState.equals(currentState)) {
applyStatusBarTintTarget(placement.source(), bitmap); statesByProxy.put(proxy, desiredState);
normalizeSemiTransparentDarkIcon(bitmap); }
if (placement.signal()) { } else {
saveHeldSignal(key, bounds, bitmap); BitmapResult bitmapResult = bitmapFor(proxy, bounds);
Bitmap bitmap = bitmapResult.bitmap();
boolean bitmapChanged = bitmapResult.created()
|| !desiredState.equals(statesByProxy.get(proxy));
if (bitmapChanged) {
if (placement.overflowDot()) {
drawOverflowDot(bitmap, placement.overflowDotColor(), placement.sourceOffsetX());
} else if (placement.heldSignal() != null) {
drawHeldSignal(placement.heldSignal(), bitmap);
} else if (placement.source() != null
&& placement.source().mode() == SnapshotMode.APP_ICON) {
drawAppIcon(host.getContext(), placement.source(), bitmap);
} else if (placement.source() != null) {
drawSnapshot(placement, bitmap);
applyStatusBarTintTarget(placement.source(), bitmap);
normalizeSemiTransparentDarkIcon(bitmap);
if (placement.signal()) {
saveHeldSignal(key, bounds, bitmap);
}
} else {
bitmap.eraseColor(Color.TRANSPARENT);
} }
} else { statesByProxy.put(proxy, desiredState);
bitmap.eraseColor(Color.TRANSPARENT); proxyChanged = true;
}
if (proxy.getDrawable() == null) {
proxy.setImageBitmap(bitmap);
proxyChanged = true;
} }
statesByProxy.put(proxy, desiredState);
proxyChanged = true;
}
if (proxy.getDrawable() == null) {
proxy.setImageBitmap(bitmap);
proxyChanged = true;
} }
if (proxy.getAlpha() != 1f) { if (proxy.getAlpha() != 1f) {
proxy.setAlpha(1f); proxy.setAlpha(1f);
@@ -304,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) {
@@ -748,16 +812,26 @@ final class SnapshotRenderer {
bitmap.getHeight())) { bitmap.getHeight())) {
return; return;
} }
sourceView.setVisibility(View.VISIBLE); RuntimeMutationGuard.enterSnapshotRenderMutation();
sourceView.setAlpha(1f); try {
if (source.mode() == SnapshotMode.STATUS_CHIP) { sourceView.setVisibility(View.VISIBLE);
drawTemporarilySizedView(sourceView, canvas, bitmap.getWidth(), bitmap.getHeight()); sourceView.setAlpha(1f);
} else { if (source.mode() == SnapshotMode.STATUS_CHIP) {
sourceView.draw(canvas); drawTemporarilySizedView(sourceView, canvas, bitmap.getWidth(), bitmap.getHeight());
} else {
sourceView.draw(canvas);
}
} finally {
RuntimeMutationGuard.exitSnapshotRenderMutation();
} }
} finally { } finally {
sourceView.setAlpha(oldAlpha); RuntimeMutationGuard.enterSnapshotRenderMutation();
sourceView.setVisibility(oldVisibility); try {
sourceView.setAlpha(oldAlpha);
sourceView.setVisibility(oldVisibility);
} finally {
RuntimeMutationGuard.exitSnapshotRenderMutation();
}
} }
} }
@@ -1183,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);
@@ -1207,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);
}
} }
} }
@@ -1221,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
) { ) {
} }
@@ -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;
@@ -88,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(
@@ -1393,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
) { ) {
@@ -1419,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;
@@ -1188,6 +1188,7 @@ 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)
@@ -1301,7 +1302,7 @@ 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 && !hasRenderableStatusChipContent(view)) { if (!allowHidden && view.getAlpha() <= 0f && !hasRenderableStatusChipContent(view)) {
@@ -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;
@@ -859,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;
@@ -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;
@@ -775,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();
} }
} }
+1 -1
View File
@@ -1 +1 @@
version=0.3 version=0.4