Merge "Add a new flag so that we can switch and avoid teamfood problems" into main
diff --git a/core/java/android/view/InsetsFlags.java b/core/java/android/view/InsetsFlags.java
index ca8a7a8..2fa5768 100644
--- a/core/java/android/view/InsetsFlags.java
+++ b/core/java/android/view/InsetsFlags.java
@@ -17,6 +17,7 @@
package android.view;
import static android.view.WindowInsetsController.APPEARANCE_FORCE_LIGHT_NAVIGATION_BARS;
+import static android.view.WindowInsetsController.APPEARANCE_LIGHT_CAPTION_BARS;
import static android.view.WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS;
import static android.view.WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS;
import static android.view.WindowInsetsController.APPEARANCE_LOW_PROFILE_BARS;
@@ -24,6 +25,7 @@
import static android.view.WindowInsetsController.APPEARANCE_OPAQUE_STATUS_BARS;
import static android.view.WindowInsetsController.APPEARANCE_SEMI_TRANSPARENT_NAVIGATION_BARS;
import static android.view.WindowInsetsController.APPEARANCE_SEMI_TRANSPARENT_STATUS_BARS;
+import static android.view.WindowInsetsController.APPEARANCE_TRANSPARENT_CAPTION_BAR_BACKGROUND;
import static android.view.WindowInsetsController.BEHAVIOR_DEFAULT;
import static android.view.WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE;
@@ -69,7 +71,15 @@
@ViewDebug.FlagToString(
mask = APPEARANCE_FORCE_LIGHT_NAVIGATION_BARS,
equals = APPEARANCE_FORCE_LIGHT_NAVIGATION_BARS,
- name = "FORCE_LIGHT_NAVIGATION_BARS")
+ name = "FORCE_LIGHT_NAVIGATION_BARS"),
+ @ViewDebug.FlagToString(
+ mask = APPEARANCE_TRANSPARENT_CAPTION_BAR_BACKGROUND,
+ equals = APPEARANCE_TRANSPARENT_CAPTION_BAR_BACKGROUND,
+ name = "APPEARANCE_TRANSPARENT_CAPTION_BAR_BACKGROUND"),
+ @ViewDebug.FlagToString(
+ mask = APPEARANCE_LIGHT_CAPTION_BARS,
+ equals = APPEARANCE_LIGHT_CAPTION_BARS,
+ name = "APPEARANCE_LIGHT_CAPTION_BARS")
})
public @Appearance int appearance;
diff --git a/core/java/android/view/NativeVectorDrawableAnimator.java b/core/java/android/view/NativeVectorDrawableAnimator.java
index b0556a3..e92bd1f 100644
--- a/core/java/android/view/NativeVectorDrawableAnimator.java
+++ b/core/java/android/view/NativeVectorDrawableAnimator.java
@@ -16,6 +16,8 @@
package android.view;
+import android.animation.Animator;
+
/**
* Exists just to allow for android.graphics & android.view package separation
*
@@ -26,4 +28,7 @@
public interface NativeVectorDrawableAnimator {
/** @hide */
long getAnimatorNativePtr();
+
+ /** @hide */
+ void setThreadedRendererAnimatorListener(Animator.AnimatorListener listener);
}
diff --git a/core/java/android/view/ViewAnimationHostBridge.java b/core/java/android/view/ViewAnimationHostBridge.java
index e0fae21..62b2b6c 100644
--- a/core/java/android/view/ViewAnimationHostBridge.java
+++ b/core/java/android/view/ViewAnimationHostBridge.java
@@ -16,14 +16,19 @@
package android.view;
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
import android.graphics.RenderNode;
+import androidx.annotation.NonNull;
+
/**
* Maps a View to a RenderNode's AnimationHost
*
* @hide
*/
-public class ViewAnimationHostBridge implements RenderNode.AnimationHost {
+public class ViewAnimationHostBridge extends AnimatorListenerAdapter
+ implements RenderNode.AnimationHost {
private final View mView;
/**
@@ -34,17 +39,35 @@
}
@Override
- public void registerAnimatingRenderNode(RenderNode animator) {
- mView.mAttachInfo.mViewRootImpl.registerAnimatingRenderNode(animator);
+ public void registerAnimatingRenderNode(RenderNode renderNode, Animator animator) {
+ mView.mAttachInfo.mViewRootImpl.registerAnimatingRenderNode(renderNode);
+ animator.addListener(this);
}
@Override
public void registerVectorDrawableAnimator(NativeVectorDrawableAnimator animator) {
mView.mAttachInfo.mViewRootImpl.registerVectorDrawableAnimator(animator);
+ animator.setThreadedRendererAnimatorListener(this);
}
@Override
public boolean isAttached() {
return mView.mAttachInfo != null;
}
+
+ @Override
+ public void onAnimationStart(@NonNull Animator animation) {
+ ViewRootImpl viewRoot = mView.getViewRootImpl();
+ if (viewRoot != null) {
+ viewRoot.addThreadedRendererView(mView);
+ }
+ }
+
+ @Override
+ public void onAnimationEnd(@NonNull Animator animation) {
+ ViewRootImpl viewRoot = mView.getViewRootImpl();
+ if (viewRoot != null) {
+ viewRoot.removeThreadedRendererView(mView);
+ }
+ }
}
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index bdada11..139285a 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -427,6 +427,12 @@
private static final long NANOS_PER_SEC = 1000000000;
+ // If the ViewRootImpl has been idle for more than 750ms, clear the preferred
+ // frame rate category and frame rate.
+ private static final int IDLE_TIME_MILLIS = 750;
+
+ private static final long NANOS_PER_MILLI = 1_000_000;
+
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
static final ThreadLocal<HandlerActionQueue> sRunQueues = new ThreadLocal<HandlerActionQueue>();
@@ -659,6 +665,10 @@
private int mMinusOneFrameIntervalMillis = 0;
// VRR interval between the previous and the frame before
private int mMinusTwoFrameIntervalMillis = 0;
+ // VRR has the invalidation idle message been posted?
+ private boolean mInvalidationIdleMessagePosted = false;
+ // VRR: List of all Views that are animating with the threaded render
+ private ArrayList<View> mThreadedRendererViews = new ArrayList();
/**
* Update the Choreographer's FrameInfo object with the timing information for the current
@@ -1184,6 +1194,8 @@
toolkitFrameRateVelocityMappingReadOnly();
private static boolean sToolkitEnableInvalidateCheckThreadFlagValue =
Flags.enableInvalidateCheckThread();
+ private static boolean sSurfaceFlingerBugfixFlagValue =
+ com.android.graphics.surfaceflinger.flags.Flags.vrrBugfix24q4();
static {
sToolkitSetFrameRateReadOnlyFlagValue = toolkitSetFrameRateReadOnly();
@@ -4261,8 +4273,13 @@
// when the values are applicable.
if (mDrawnThisFrame) {
mDrawnThisFrame = false;
+ if (!mInvalidationIdleMessagePosted && sSurfaceFlingerBugfixFlagValue) {
+ mInvalidationIdleMessagePosted = true;
+ mHandler.sendEmptyMessageDelayed(MSG_CHECK_INVALIDATION_IDLE, IDLE_TIME_MILLIS);
+ }
setCategoryFromCategoryCounts();
updateInfrequentCount();
+ updateFrameRateFromThreadedRendererViews();
setPreferredFrameRate(mPreferredFrameRate);
setPreferredFrameRateCategory(mPreferredFrameRateCategory);
if (mPreferredFrameRate > 0
@@ -6499,6 +6516,8 @@
return "MSG_WINDOW_TOUCH_MODE_CHANGED";
case MSG_KEEP_CLEAR_RECTS_CHANGED:
return "MSG_KEEP_CLEAR_RECTS_CHANGED";
+ case MSG_CHECK_INVALIDATION_IDLE:
+ return "MSG_CHECK_INVALIDATION_IDLE";
case MSG_REFRESH_POINTER_ICON:
return "MSG_REFRESH_POINTER_ICON";
case MSG_TOUCH_BOOST_TIMEOUT:
@@ -6759,6 +6778,31 @@
mNumPausedForSync = 0;
scheduleTraversals();
break;
+ case MSG_CHECK_INVALIDATION_IDLE: {
+ long delta;
+ if (mIsTouchBoosting || mIsFrameRateBoosting || mInsetsAnimationRunning) {
+ delta = 0;
+ } else {
+ delta = System.nanoTime() / NANOS_PER_MILLI - mLastUpdateTimeMillis;
+ }
+ if (delta >= IDLE_TIME_MILLIS) {
+ mFrameRateCategoryHighCount = 0;
+ mFrameRateCategoryHighHintCount = 0;
+ mFrameRateCategoryNormalCount = 0;
+ mFrameRateCategoryLowCount = 0;
+ mPreferredFrameRate = 0;
+ mPreferredFrameRateCategory = FRAME_RATE_CATEGORY_NO_PREFERENCE;
+ updateFrameRateFromThreadedRendererViews();
+ setPreferredFrameRate(mPreferredFrameRate);
+ setPreferredFrameRateCategory(mPreferredFrameRateCategory);
+ mInvalidationIdleMessagePosted = false;
+ } else {
+ mInvalidationIdleMessagePosted = true;
+ mHandler.sendEmptyMessageDelayed(MSG_CHECK_INVALIDATION_IDLE,
+ IDLE_TIME_MILLIS - delta);
+ }
+ break;
+ }
case MSG_TOUCH_BOOST_TIMEOUT:
/**
* Lower the frame rate after the boosting period (FRAME_RATE_TOUCH_BOOST_TIME).
@@ -12581,6 +12625,24 @@
}
/**
+ * Views that are animating with the ThreadedRenderer don't use the normal invalidation
+ * path, so the value won't be updated through performTraversals. This reads the votes
+ * from those views.
+ */
+ private void updateFrameRateFromThreadedRendererViews() {
+ ArrayList<View> views = mThreadedRendererViews;
+ for (int i = views.size() - 1; i >= 0; i--) {
+ View view = views.get(i);
+ View.AttachInfo attachInfo = view.mAttachInfo;
+ if (attachInfo == null || attachInfo.mViewRootImpl != this) {
+ views.remove(i);
+ } else {
+ view.votePreferredFrameRate();
+ }
+ }
+ }
+
+ /**
* Sets the mPreferredFrameRateCategory from the high, high_hint, normal, and low counts.
*/
private void setCategoryFromCategoryCounts() {
@@ -12761,6 +12823,31 @@
}
/**
+ * Mark a View as having an active ThreadedRenderer animation. This is used for
+ * RenderNodeAnimators and AnimatedVectorDrawables. When the animation stops,
+ * {@link #removeThreadedRendererView(View)} must be called.
+ * @param view The View with the ThreadedRenderer animation that started.
+ */
+ public void addThreadedRendererView(View view) {
+ if (!mThreadedRendererViews.contains(view)) {
+ mThreadedRendererViews.add(view);
+ }
+ }
+
+ /**
+ * When a ThreadedRenderer animation ends, the View that is associated with it using
+ * {@link #addThreadedRendererView(View)} must be removed with a call to this method.
+ * @param view The View whose ThreadedRender animation has stopped.
+ */
+ public void removeThreadedRendererView(View view) {
+ mThreadedRendererViews.remove(view);
+ if (!mInvalidationIdleMessagePosted && sSurfaceFlingerBugfixFlagValue) {
+ mInvalidationIdleMessagePosted = true;
+ mHandler.sendEmptyMessageDelayed(MSG_CHECK_INVALIDATION_IDLE, IDLE_TIME_MILLIS);
+ }
+ }
+
+ /**
* Returns {@link #INTERMITTENT_STATE_INTERMITTENT} when the ViewRootImpl has only been
* updated intermittently, {@link #INTERMITTENT_STATE_NOT_INTERMITTENT} when it is
* not updated intermittently, and {@link #INTERMITTENT_STATE_IN_TRANSITION} when it
@@ -12983,6 +13070,10 @@
private void removeVrrMessages() {
mHandler.removeMessages(MSG_TOUCH_BOOST_TIMEOUT);
mHandler.removeMessages(MSG_FRAME_RATE_SETTING);
+ if (mInvalidationIdleMessagePosted && sSurfaceFlingerBugfixFlagValue) {
+ mInvalidationIdleMessagePosted = false;
+ mHandler.removeMessages(MSG_CHECK_INVALIDATION_IDLE);
+ }
}
/**
@@ -13001,7 +13092,7 @@
mMinusOneFrameIntervalMillis = timeIntervalMillis;
mLastUpdateTimeMillis = currentTimeMillis;
- if (timeIntervalMillis + mMinusTwoFrameIntervalMillis
+ if (mThreadedRendererViews.isEmpty() && timeIntervalMillis + mMinusTwoFrameIntervalMillis
>= INFREQUENT_UPDATE_INTERVAL_MILLIS) {
int infrequentUpdateCount = mInfrequentUpdateCount;
mInfrequentUpdateCount = infrequentUpdateCount == INFREQUENT_UPDATE_COUNTS
diff --git a/core/tests/coretests/src/android/view/ViewFrameRateTest.java b/core/tests/coretests/src/android/view/ViewFrameRateTest.java
index 0b1b40c..07446e7 100644
--- a/core/tests/coretests/src/android/view/ViewFrameRateTest.java
+++ b/core/tests/coretests/src/android/view/ViewFrameRateTest.java
@@ -41,11 +41,13 @@
import android.os.Handler;
import android.os.Looper;
import android.os.SystemClock;
+import android.platform.test.annotations.LargeTest;
import android.platform.test.annotations.RequiresFlagsEnabled;
import android.platform.test.flag.junit.CheckFlagsRule;
import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.util.DisplayMetrics;
import android.widget.FrameLayout;
+import android.widget.ProgressBar;
import androidx.test.annotation.UiThreadTest;
import androidx.test.filters.SmallTest;
@@ -623,6 +625,162 @@
assertEquals(FRAME_RATE_CATEGORY_HIGH_HINT, mViewRoot.getLastPreferredFrameRateCategory());
}
+ @LargeTest
+ @Test
+ @RequiresFlagsEnabled({FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY,
+ FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY,
+ com.android.graphics.surfaceflinger.flags.Flags.FLAG_VRR_BUGFIX_24Q4
+ })
+ public void idleDetected() throws Throwable {
+ waitForFrameRateCategoryToSettle();
+ mActivityRule.runOnUiThread(() -> {
+ mMovingView.setRequestedFrameRate(View.REQUESTED_FRAME_RATE_CATEGORY_HIGH);
+ mMovingView.setFrameContentVelocity(Float.MAX_VALUE);
+ mMovingView.invalidate();
+ runAfterDraw(() -> assertEquals(FRAME_RATE_CATEGORY_HIGH,
+ mViewRoot.getLastPreferredFrameRateCategory()));
+ });
+ waitForAfterDraw();
+
+ // Wait for idle timeout
+ Thread.sleep(1000);
+ assertEquals(0f, mViewRoot.getLastPreferredFrameRate());
+ assertEquals(FRAME_RATE_CATEGORY_NO_PREFERENCE,
+ mViewRoot.getLastPreferredFrameRateCategory());
+ }
+
+ @LargeTest
+ @Test
+ @RequiresFlagsEnabled({FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY,
+ FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY,
+ com.android.graphics.surfaceflinger.flags.Flags.FLAG_VRR_BUGFIX_24Q4
+ })
+ public void vectorDrawableFrameRate() throws Throwable {
+ final ProgressBar[] progressBars = new ProgressBar[3];
+ final ViewGroup[] parents = new ViewGroup[1];
+ mActivityRule.runOnUiThread(() -> {
+ ViewGroup parent = (ViewGroup) mMovingView.getParent();
+ parents[0] = parent;
+ ProgressBar progressBar1 = new ProgressBar(mActivity);
+ parent.addView(progressBar1);
+ progressBar1.setRequestedFrameRate(View.REQUESTED_FRAME_RATE_CATEGORY_LOW);
+ progressBar1.setIndeterminate(true);
+ progressBars[0] = progressBar1;
+
+ ProgressBar progressBar2 = new ProgressBar(mActivity);
+ parent.addView(progressBar2);
+ progressBar2.setRequestedFrameRate(View.REQUESTED_FRAME_RATE_CATEGORY_NORMAL);
+ progressBar2.setIndeterminate(true);
+ progressBars[1] = progressBar2;
+
+ ProgressBar progressBar3 = new ProgressBar(mActivity);
+ parent.addView(progressBar3);
+ progressBar3.setRequestedFrameRate(45f);
+ progressBar3.setIndeterminate(true);
+ progressBars[2] = progressBar3;
+ });
+ waitForFrameRateCategoryToSettle();
+
+ // Wait for idle timeout
+ Thread.sleep(1000);
+ assertEquals(45f, mViewRoot.getLastPreferredFrameRate());
+ assertEquals(FRAME_RATE_CATEGORY_NORMAL, mViewRoot.getLastPreferredFrameRateCategory());
+
+ // Removing the vector drawable with NORMAL should drop the category to LOW
+ mActivityRule.runOnUiThread(() -> parents[0].removeView(progressBars[1]));
+ Thread.sleep(1000);
+ assertEquals(45f, mViewRoot.getLastPreferredFrameRate());
+ assertEquals(FRAME_RATE_CATEGORY_LOW,
+ mViewRoot.getLastPreferredFrameRateCategory());
+ // Removing the one voting for frame rate should leave only the category
+ mActivityRule.runOnUiThread(() -> parents[0].removeView(progressBars[2]));
+ Thread.sleep(1000);
+ assertEquals(0f, mViewRoot.getLastPreferredFrameRate());
+ assertEquals(FRAME_RATE_CATEGORY_LOW,
+ mViewRoot.getLastPreferredFrameRateCategory());
+ // Removing the last one should leave it with no preference
+ mActivityRule.runOnUiThread(() -> parents[0].removeView(progressBars[0]));
+ Thread.sleep(1000);
+ assertEquals(0f, mViewRoot.getLastPreferredFrameRate());
+ assertEquals(FRAME_RATE_CATEGORY_NO_PREFERENCE,
+ mViewRoot.getLastPreferredFrameRateCategory());
+ }
+
+ @LargeTest
+ @Test
+ @RequiresFlagsEnabled({FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY,
+ FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY,
+ com.android.graphics.surfaceflinger.flags.Flags.FLAG_VRR_BUGFIX_24Q4
+ })
+ public void renderNodeAnimatorFrameRateCanceled() throws Throwable {
+ mMovingView.setRequestedFrameRate(View.REQUESTED_FRAME_RATE_CATEGORY_NO_PREFERENCE);
+ waitForFrameRateCategoryToSettle();
+
+ RenderNodeAnimator[] renderNodeAnimator = new RenderNodeAnimator[1];
+ renderNodeAnimator[0] = new RenderNodeAnimator(RenderNodeAnimator.ALPHA, 0f);
+ renderNodeAnimator[0].setDuration(100000);
+
+ mActivityRule.runOnUiThread(() -> {
+ renderNodeAnimator[0].setTarget(mMovingView);
+ renderNodeAnimator[0].start();
+ mMovingView.setRequestedFrameRate(View.REQUESTED_FRAME_RATE_CATEGORY_LOW);
+ runAfterDraw(() -> {
+ assertEquals(0f, mViewRoot.getLastPreferredFrameRate());
+ assertEquals(FRAME_RATE_CATEGORY_LOW,
+ mViewRoot.getLastPreferredFrameRateCategory());
+ });
+ });
+ waitForAfterDraw();
+
+ mActivityRule.runOnUiThread(() -> {
+ renderNodeAnimator[0].cancel();
+ });
+
+ // Wait for idle timeout
+ Thread.sleep(1000);
+ assertEquals(0f, mViewRoot.getLastPreferredFrameRate());
+ assertEquals(FRAME_RATE_CATEGORY_NO_PREFERENCE,
+ mViewRoot.getLastPreferredFrameRateCategory());
+ }
+
+ @LargeTest
+ @Test
+ @RequiresFlagsEnabled({FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY,
+ FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY,
+ com.android.graphics.surfaceflinger.flags.Flags.FLAG_VRR_BUGFIX_24Q4
+ })
+ public void renderNodeAnimatorFrameRateRemoved() throws Throwable {
+ mMovingView.setRequestedFrameRate(View.REQUESTED_FRAME_RATE_CATEGORY_NO_PREFERENCE);
+ waitForFrameRateCategoryToSettle();
+
+ RenderNodeAnimator[] renderNodeAnimator = new RenderNodeAnimator[1];
+ renderNodeAnimator[0] = new RenderNodeAnimator(RenderNodeAnimator.ALPHA, 0f);
+ renderNodeAnimator[0].setDuration(100000);
+
+ mActivityRule.runOnUiThread(() -> {
+ renderNodeAnimator[0].setTarget(mMovingView);
+ renderNodeAnimator[0].start();
+ mMovingView.setRequestedFrameRate(View.REQUESTED_FRAME_RATE_CATEGORY_LOW);
+ runAfterDraw(() -> {
+ assertEquals(0f, mViewRoot.getLastPreferredFrameRate());
+ assertEquals(FRAME_RATE_CATEGORY_LOW,
+ mViewRoot.getLastPreferredFrameRateCategory());
+ });
+ });
+ waitForAfterDraw();
+
+ mActivityRule.runOnUiThread(() -> {
+ ViewGroup parent = (ViewGroup) mMovingView.getParent();
+ assert parent != null;
+ parent.removeView(mMovingView);
+ });
+
+ Thread.sleep(1000);
+ assertEquals(0f, mViewRoot.getLastPreferredFrameRate());
+ assertEquals(FRAME_RATE_CATEGORY_NO_PREFERENCE,
+ mViewRoot.getLastPreferredFrameRateCategory());
+ }
+
private void runAfterDraw(@NonNull Runnable runnable) {
Handler handler = new Handler(Looper.getMainLooper());
mAfterDrawLatch = new CountDownLatch(1);
diff --git a/graphics/java/android/graphics/RenderNode.java b/graphics/java/android/graphics/RenderNode.java
index 0650b78..211f74a 100644
--- a/graphics/java/android/graphics/RenderNode.java
+++ b/graphics/java/android/graphics/RenderNode.java
@@ -16,6 +16,7 @@
package android.graphics;
+import android.animation.Animator;
import android.annotation.BytesLong;
import android.annotation.ColorInt;
import android.annotation.FloatRange;
@@ -1639,7 +1640,7 @@
*/
public interface AnimationHost {
/** @hide */
- void registerAnimatingRenderNode(RenderNode animator);
+ void registerAnimatingRenderNode(RenderNode renderNode, Animator animator);
/** @hide */
void registerVectorDrawableAnimator(NativeVectorDrawableAnimator animator);
@@ -1654,7 +1655,7 @@
throw new IllegalStateException("Cannot start this animator on a detached view!");
}
nAddAnimator(mNativeRenderNode, animator.getNativeAnimator());
- mAnimationHost.registerAnimatingRenderNode(this);
+ mAnimationHost.registerAnimatingRenderNode(this, animator);
}
/** @hide */
diff --git a/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java b/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java
index 55f205b..d4bb461 100644
--- a/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java
@@ -1266,6 +1266,7 @@
private final IntArray mPendingAnimationActions = new IntArray();
private final AnimatedVectorDrawable mDrawable;
private long mTotalDuration;
+ private AnimatorListener mThreadedRendererAnimatorListener;
VectorDrawableAnimatorRT(AnimatedVectorDrawable drawable) {
mDrawable = drawable;
@@ -1689,6 +1690,9 @@
if (mListener != null) {
mListener.onAnimationStart(null);
}
+ if (mThreadedRendererAnimatorListener != null) {
+ mThreadedRendererAnimatorListener.onAnimationStart(null);
+ }
}
// This should only be called after animator has been added to the RenderNode target.
@@ -1717,6 +1721,9 @@
if (mListener != null) {
mListener.onAnimationStart(null);
}
+ if (mThreadedRendererAnimatorListener != null) {
+ mThreadedRendererAnimatorListener.onAnimationStart(null);
+ }
}
@Override
@@ -1725,6 +1732,11 @@
}
@Override
+ public void setThreadedRendererAnimatorListener(AnimatorListener animatorListener) {
+ mThreadedRendererAnimatorListener = animatorListener;
+ }
+
+ @Override
public boolean canReverse() {
return mIsReversible;
}
@@ -1788,6 +1800,9 @@
if (mListener != null) {
mListener.onAnimationEnd(null);
}
+ if (mThreadedRendererAnimatorListener != null) {
+ mThreadedRendererAnimatorListener.onAnimationEnd(null);
+ }
}
// onFinished: should be called from native
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index 25ba9c5..1df9c88e 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -997,6 +997,13 @@
}
flag {
+ name: "glanceable_hub_shortcut_button"
+ namespace: "systemui"
+ description: "Shows a button over the dream and lock screen to open the glanceable hub"
+ bug: "339667383"
+}
+
+flag {
name: "glanceable_hub_gesture_handle"
namespace: "systemui"
description: "Shows a vertical bar at the right edge to indicate the user can swipe to open the glanceable hub"
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/TextAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/TextAnimator.kt
index b124025..978943ae 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/TextAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/TextAnimator.kt
@@ -179,8 +179,15 @@
private val fontVariationUtils = FontVariationUtils()
- fun updateLayout(layout: Layout) {
+ fun updateLayout(layout: Layout, textSize: Float = -1f) {
textInterpolator.layout = layout
+
+ if (textSize >= 0) {
+ textInterpolator.targetPaint.textSize = textSize
+ textInterpolator.basePaint.textSize = textSize
+ textInterpolator.onTargetPaintModified()
+ textInterpolator.onBasePaintModified()
+ }
}
fun isRunning(): Boolean {
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/AnimatableClockView.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/AnimatableClockView.kt
index bdeab79..079c1d9 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/AnimatableClockView.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/AnimatableClockView.kt
@@ -17,7 +17,6 @@
import android.animation.TimeInterpolator
import android.annotation.ColorInt
-import android.annotation.FloatRange
import android.annotation.IntRange
import android.annotation.SuppressLint
import android.content.Context
@@ -27,7 +26,7 @@
import android.text.format.DateFormat
import android.util.AttributeSet
import android.util.MathUtils.constrainedMap
-import android.util.TypedValue
+import android.util.TypedValue.COMPLEX_UNIT_PX
import android.view.View
import android.view.View.MeasureSpec.EXACTLY
import android.widget.TextView
@@ -219,9 +218,7 @@
override fun setTextSize(type: Int, size: Float) {
super.setTextSize(type, size)
- if (type == TypedValue.COMPLEX_UNIT_PX) {
- lastUnconstrainedTextSize = size
- }
+ lastUnconstrainedTextSize = if (type == COMPLEX_UNIT_PX) size else Float.MAX_VALUE
}
@SuppressLint("DrawAllocation")
@@ -234,23 +231,19 @@
MeasureSpec.getMode(heightMeasureSpec) == EXACTLY
) {
// Call straight into TextView.setTextSize to avoid setting lastUnconstrainedTextSize
- super.setTextSize(
- TypedValue.COMPLEX_UNIT_PX,
- min(lastUnconstrainedTextSize, MeasureSpec.getSize(heightMeasureSpec) / 2F)
- )
+ val size = min(lastUnconstrainedTextSize, MeasureSpec.getSize(heightMeasureSpec) / 2F)
+ super.setTextSize(COMPLEX_UNIT_PX, size)
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
- val animator = textAnimator
- if (animator == null) {
- textAnimator =
- textAnimatorFactory(layout, ::invalidate)?.also {
- onTextAnimatorInitialized?.invoke(it)
- onTextAnimatorInitialized = null
- }
- } else {
- animator.updateLayout(layout)
- }
+ textAnimator?.let { animator -> animator.updateLayout(layout, textSize) }
+ ?: run {
+ textAnimator =
+ textAnimatorFactory(layout, ::invalidate).also {
+ onTextAnimatorInitialized?.invoke(it)
+ onTextAnimatorInitialized = null
+ }
+ }
if (migratedClocks && hasCustomPositionUpdatedAnimation) {
// Expand width to avoid clock being clipped during stepping animation
@@ -307,18 +300,18 @@
logger.d("animateColorChange")
setTextStyle(
weight = lockScreenWeight,
- textSize = -1f,
color = null, /* using current color */
animate = false,
+ interpolator = null,
duration = 0,
delay = 0,
onAnimationEnd = null
)
setTextStyle(
weight = lockScreenWeight,
- textSize = -1f,
color = lockScreenColor,
animate = true,
+ interpolator = null,
duration = COLOR_ANIM_DURATION,
delay = 0,
onAnimationEnd = null
@@ -329,16 +322,15 @@
logger.d("animateAppearOnLockscreen")
setTextStyle(
weight = dozingWeight,
- textSize = -1f,
color = lockScreenColor,
animate = false,
+ interpolator = null,
duration = 0,
delay = 0,
onAnimationEnd = null
)
setTextStyle(
weight = lockScreenWeight,
- textSize = -1f,
color = lockScreenColor,
animate = true,
duration = APPEAR_ANIM_DURATION,
@@ -356,16 +348,15 @@
logger.d("animateFoldAppear")
setTextStyle(
weight = lockScreenWeightInternal,
- textSize = -1f,
color = lockScreenColor,
animate = false,
+ interpolator = null,
duration = 0,
delay = 0,
onAnimationEnd = null
)
setTextStyle(
weight = dozingWeightInternal,
- textSize = -1f,
color = dozingColor,
animate = animate,
interpolator = Interpolators.EMPHASIZED_DECELERATE,
@@ -385,9 +376,9 @@
val startAnimPhase2 = Runnable {
setTextStyle(
weight = if (isDozing()) dozingWeight else lockScreenWeight,
- textSize = -1f,
color = null,
animate = true,
+ interpolator = null,
duration = CHARGE_ANIM_DURATION_PHASE_1,
delay = 0,
onAnimationEnd = null
@@ -395,9 +386,9 @@
}
setTextStyle(
weight = if (isDozing()) lockScreenWeight else dozingWeight,
- textSize = -1f,
color = null,
animate = true,
+ interpolator = null,
duration = CHARGE_ANIM_DURATION_PHASE_0,
delay = chargeAnimationDelay.toLong(),
onAnimationEnd = startAnimPhase2
@@ -408,9 +399,9 @@
logger.d("animateDoze")
setTextStyle(
weight = if (isDozing) dozingWeight else lockScreenWeight,
- textSize = -1f,
color = if (isDozing) dozingColor else lockScreenColor,
animate = animate,
+ interpolator = null,
duration = DOZE_ANIM_DURATION,
delay = 0,
onAnimationEnd = null
@@ -448,7 +439,6 @@
*/
private fun setTextStyle(
@IntRange(from = 0, to = 1000) weight: Int,
- @FloatRange(from = 0.0) textSize: Float,
color: Int?,
animate: Boolean,
interpolator: TimeInterpolator?,
@@ -459,7 +449,6 @@
textAnimator?.let {
it.setTextStyle(
weight = weight,
- textSize = textSize,
color = color,
animate = animate && isAnimationEnabled,
duration = duration,
@@ -474,7 +463,6 @@
onTextAnimatorInitialized = { textAnimator ->
textAnimator.setTextStyle(
weight = weight,
- textSize = textSize,
color = color,
animate = false,
duration = duration,
@@ -487,27 +475,6 @@
}
}
- private fun setTextStyle(
- @IntRange(from = 0, to = 1000) weight: Int,
- @FloatRange(from = 0.0) textSize: Float,
- color: Int?,
- animate: Boolean,
- duration: Long,
- delay: Long,
- onAnimationEnd: Runnable?
- ) {
- setTextStyle(
- weight = weight,
- textSize = textSize,
- color = color,
- animate = animate,
- interpolator = null,
- duration = duration,
- delay = delay,
- onAnimationEnd = onAnimationEnd
- )
- }
-
fun refreshFormat() = refreshFormat(DateFormat.is24HourFormat(context))
fun refreshFormat(use24HourFormat: Boolean) {
Patterns.update(context)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/touch/CommunalTouchHandlerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/touch/CommunalTouchHandlerTest.java
index 29fbee0..7936ccc 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/touch/CommunalTouchHandlerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/touch/CommunalTouchHandlerTest.java
@@ -108,7 +108,7 @@
mTouchHandler.onSessionStart(mTouchSession);
verify(mTouchSession).registerInputListener(inputEventListenerArgumentCaptor.capture());
inputEventListenerArgumentCaptor.getValue().onInputEvent(motionEvent);
- verify(mCentralSurfaces).handleDreamTouch(motionEvent);
+ verify(mCentralSurfaces).handleCommunalHubTouch(motionEvent);
}
@Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLegacySettingSyncerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLegacySettingSyncerTest.kt
index 26fcb23..49d0399 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLegacySettingSyncerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLegacySettingSyncerTest.kt
@@ -90,6 +90,7 @@
.thenReturn(FakeSharedPreferences())
},
userTracker = FakeUserTracker(),
+ systemSettings = FakeSettings(),
broadcastDispatcher = fakeBroadcastDispatcher,
)
settings = FakeSettings()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLocalUserSelectionManagerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLocalUserSelectionManagerTest.kt
index 99a0185..9ab1ac1 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLocalUserSelectionManagerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLocalUserSelectionManagerTest.kt
@@ -22,13 +22,14 @@
import android.content.pm.UserInfo
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.backup.BackupHelper
+import com.android.systemui.res.R
import com.android.systemui.settings.FakeUserTracker
import com.android.systemui.settings.UserFileManager
import com.android.systemui.util.FakeSharedPreferences
import com.android.systemui.util.mockito.whenever
+import com.android.systemui.util.settings.FakeSettings
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -80,6 +81,7 @@
context = context,
userFileManager = userFileManager,
userTracker = userTracker,
+ systemSettings = FakeSettings(),
broadcastDispatcher = fakeBroadcastDispatcher,
)
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepositoryTest.kt
index 567e0a9..159ce36 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepositoryTest.kt
@@ -21,7 +21,6 @@
import android.os.UserHandle
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.keyguard.data.quickaffordance.FakeKeyguardQuickAffordanceConfig
@@ -32,6 +31,7 @@
import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceRemoteUserSelectionManager
import com.android.systemui.keyguard.shared.model.KeyguardQuickAffordancePickerRepresentation
import com.android.systemui.keyguard.shared.model.KeyguardSlotPickerRepresentation
+import com.android.systemui.res.R
import com.android.systemui.settings.FakeUserTracker
import com.android.systemui.settings.UserFileManager
import com.android.systemui.shared.customization.data.content.FakeCustomizationProviderClient
@@ -91,6 +91,7 @@
.thenReturn(FakeSharedPreferences())
},
userTracker = userTracker,
+ systemSettings = FakeSettings(),
broadcastDispatcher = fakeBroadcastDispatcher,
)
client1 = FakeCustomizationProviderClient()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt
index 2d77f4f..78a1167 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt
@@ -148,6 +148,7 @@
.thenReturn(FakeSharedPreferences())
},
userTracker = userTracker,
+ systemSettings = FakeSettings(),
broadcastDispatcher = fakeBroadcastDispatcher,
)
val remoteUserSelectionManager =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorTest.kt
similarity index 78%
rename from packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorTest.kt
index 2b8a644..9dc930b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorTest.kt
@@ -27,18 +27,22 @@
import com.android.systemui.deviceentry.domain.interactor.deviceUnlockedInteractor
import com.android.systemui.flags.DisableSceneContainer
import com.android.systemui.flags.EnableSceneContainer
+import com.android.systemui.keyguard.data.repository.deviceEntryFingerprintAuthRepository
import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticationStatus
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep
import com.android.systemui.kosmos.testScope
import com.android.systemui.scene.data.repository.sceneContainerRepository
+import com.android.systemui.scene.data.repository.setSceneTransition
import com.android.systemui.scene.domain.interactor.sceneInteractor
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
+import com.google.common.truth.Truth.assertWithMessage
import junit.framework.Assert.assertEquals
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.flowOf
@@ -192,6 +196,175 @@
}
@Test
+ @EnableSceneContainer
+ fun surfaceBehindVisibility_fromLockscreenToGone_trueThroughout() =
+ testScope.runTest {
+ val isSurfaceBehindVisible by collectLastValue(underTest.value.surfaceBehindVisibility)
+ val currentScene by collectLastValue(kosmos.sceneInteractor.currentScene)
+
+ // Before the transition, we start on Lockscreen so the surface should start invisible.
+ kosmos.setSceneTransition(ObservableTransitionState.Idle(Scenes.Lockscreen))
+ assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
+ assertThat(isSurfaceBehindVisible).isFalse()
+
+ // Unlocked with fingerprint.
+ kosmos.deviceEntryFingerprintAuthRepository.setAuthenticationStatus(
+ SuccessFingerprintAuthenticationStatus(0, true)
+ )
+
+ // Start the transition to Gone, the surface should become immediately visible.
+ kosmos.setSceneTransition(
+ ObservableTransitionState.Transition(
+ fromScene = Scenes.Lockscreen,
+ toScene = Scenes.Gone,
+ isInitiatedByUserInput = false,
+ isUserInputOngoing = flowOf(false),
+ progress = flowOf(0.3f),
+ currentScene = flowOf(Scenes.Lockscreen),
+ )
+ )
+ assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
+ assertThat(isSurfaceBehindVisible).isTrue()
+
+ // Towards the end of the transition, the surface should continue to be visible.
+ kosmos.setSceneTransition(
+ ObservableTransitionState.Transition(
+ fromScene = Scenes.Lockscreen,
+ toScene = Scenes.Gone,
+ isInitiatedByUserInput = false,
+ isUserInputOngoing = flowOf(false),
+ progress = flowOf(0.9f),
+ currentScene = flowOf(Scenes.Gone),
+ )
+ )
+ assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
+ assertThat(isSurfaceBehindVisible).isTrue()
+
+ // After the transition, settles on Gone. Surface behind should stay visible now.
+ kosmos.setSceneTransition(ObservableTransitionState.Idle(Scenes.Gone))
+ kosmos.sceneInteractor.changeScene(Scenes.Gone, "")
+ assertThat(currentScene).isEqualTo(Scenes.Gone)
+ assertThat(isSurfaceBehindVisible).isTrue()
+ }
+
+ @Test
+ @EnableSceneContainer
+ fun surfaceBehindVisibility_fromBouncerToGone_becomesTrue() =
+ testScope.runTest {
+ val isSurfaceBehindVisible by collectLastValue(underTest.value.surfaceBehindVisibility)
+ val currentScene by collectLastValue(kosmos.sceneInteractor.currentScene)
+
+ // Before the transition, we start on Bouncer so the surface should start invisible.
+ kosmos.setSceneTransition(ObservableTransitionState.Idle(Scenes.Bouncer))
+ kosmos.sceneInteractor.changeScene(Scenes.Bouncer, "")
+ assertThat(currentScene).isEqualTo(Scenes.Bouncer)
+ assertThat(isSurfaceBehindVisible).isFalse()
+
+ // Unlocked with fingerprint.
+ kosmos.deviceEntryFingerprintAuthRepository.setAuthenticationStatus(
+ SuccessFingerprintAuthenticationStatus(0, true)
+ )
+
+ // Start the transition to Gone, the surface should remain invisible prior to hitting
+ // the
+ // threshold.
+ kosmos.setSceneTransition(
+ ObservableTransitionState.Transition(
+ fromScene = Scenes.Bouncer,
+ toScene = Scenes.Gone,
+ isInitiatedByUserInput = false,
+ isUserInputOngoing = flowOf(false),
+ progress =
+ flowOf(
+ FromPrimaryBouncerTransitionInteractor
+ .TO_GONE_SURFACE_BEHIND_VISIBLE_THRESHOLD
+ ),
+ currentScene = flowOf(Scenes.Bouncer),
+ )
+ )
+ assertThat(currentScene).isEqualTo(Scenes.Bouncer)
+ assertThat(isSurfaceBehindVisible).isFalse()
+
+ // Once the transition passes the threshold, the surface should become visible.
+ kosmos.setSceneTransition(
+ ObservableTransitionState.Transition(
+ fromScene = Scenes.Bouncer,
+ toScene = Scenes.Gone,
+ isInitiatedByUserInput = false,
+ isUserInputOngoing = flowOf(false),
+ progress =
+ flowOf(
+ FromPrimaryBouncerTransitionInteractor
+ .TO_GONE_SURFACE_BEHIND_VISIBLE_THRESHOLD + 0.01f
+ ),
+ currentScene = flowOf(Scenes.Gone),
+ )
+ )
+ assertThat(currentScene).isEqualTo(Scenes.Bouncer)
+ assertThat(isSurfaceBehindVisible).isTrue()
+
+ // After the transition, settles on Gone. Surface behind should stay visible now.
+ kosmos.setSceneTransition(ObservableTransitionState.Idle(Scenes.Gone))
+ kosmos.sceneInteractor.changeScene(Scenes.Gone, "")
+ assertThat(currentScene).isEqualTo(Scenes.Gone)
+ assertThat(isSurfaceBehindVisible).isTrue()
+ }
+
+ @Test
+ @EnableSceneContainer
+ fun surfaceBehindVisibility_idleWhileUnlocked_alwaysTrue() =
+ testScope.runTest {
+ val isSurfaceBehindVisible by collectLastValue(underTest.value.surfaceBehindVisibility)
+ val currentScene by collectLastValue(kosmos.sceneInteractor.currentScene)
+
+ // Unlocked with fingerprint.
+ kosmos.deviceEntryFingerprintAuthRepository.setAuthenticationStatus(
+ SuccessFingerprintAuthenticationStatus(0, true)
+ )
+ kosmos.setSceneTransition(ObservableTransitionState.Idle(Scenes.Gone))
+ kosmos.sceneInteractor.changeScene(Scenes.Gone, "")
+ assertThat(currentScene).isEqualTo(Scenes.Gone)
+
+ listOf(
+ Scenes.Shade,
+ Scenes.QuickSettings,
+ Scenes.Shade,
+ Scenes.Gone,
+ )
+ .forEach { scene ->
+ kosmos.setSceneTransition(ObservableTransitionState.Idle(scene))
+ kosmos.sceneInteractor.changeScene(scene, "")
+ assertThat(currentScene).isEqualTo(scene)
+ assertWithMessage("Unexpected visibility for scene \"${scene.debugName}\"")
+ .that(isSurfaceBehindVisible)
+ .isTrue()
+ }
+ }
+
+ @Test
+ @EnableSceneContainer
+ fun surfaceBehindVisibility_idleWhileLocked_alwaysFalse() =
+ testScope.runTest {
+ val isSurfaceBehindVisible by collectLastValue(underTest.value.surfaceBehindVisibility)
+ val currentScene by collectLastValue(kosmos.sceneInteractor.currentScene)
+ assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
+
+ listOf(
+ Scenes.Shade,
+ Scenes.QuickSettings,
+ Scenes.Shade,
+ Scenes.Lockscreen,
+ )
+ .forEach { scene ->
+ kosmos.setSceneTransition(ObservableTransitionState.Idle(scene))
+ kosmos.sceneInteractor.changeScene(scene, "")
+ assertWithMessage("Unexpected visibility for scene \"${scene.debugName}\"")
+ .that(isSurfaceBehindVisible)
+ .isFalse()
+ }
+ }
+
+ @Test
@DisableSceneContainer
fun testUsingGoingAwayAnimation_duringTransitionToGone() =
testScope.runTest {
diff --git a/packages/SystemUI/res/drawable/ic_widgets.xml b/packages/SystemUI/res/drawable/ic_widgets.xml
new file mode 100644
index 0000000..b21d047
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_widgets.xml
@@ -0,0 +1,27 @@
+<!--
+ ~ Copyright (C) 2024 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<!-- go/gm2-icons, from gs_widgets_vd_theme_24.xml -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:tint="?attr/colorControlNormal"
+ android:viewportHeight="960"
+ android:viewportWidth="960">
+ <path
+ android:fillColor="@android:color/black"
+ android:pathData="M666,520L440,294L666,68L892,294L666,520ZM120,440L120,120L440,120L440,440L120,440ZM520,840L520,520L840,520L840,840L520,840ZM120,840L120,520L440,520L440,840L120,840ZM200,360L360,360L360,200L200,200L200,360ZM667,408L780,295L667,182L554,295L667,408ZM600,760L760,760L760,600L600,600L600,760ZM200,760L360,760L360,600L200,600L200,760ZM360,360L360,360L360,360L360,360L360,360ZM554,295L554,295L554,295L554,295L554,295ZM360,600L360,600L360,600L360,600L360,600ZM600,600L600,600L600,600L600,600L600,600Z" />
+</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/dream_overlay_open_hub_chip.xml b/packages/SystemUI/res/layout/dream_overlay_open_hub_chip.xml
new file mode 100644
index 0000000..be063a9
--- /dev/null
+++ b/packages/SystemUI/res/layout/dream_overlay_open_hub_chip.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2024 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+-->
+<com.android.systemui.animation.view.LaunchableImageView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_height="@dimen/dream_overlay_bottom_affordance_height"
+ android:layout_width="@dimen/dream_overlay_bottom_affordance_width"
+ android:layout_gravity="bottom|start"
+ android:padding="@dimen/dream_overlay_bottom_affordance_padding"
+ android:scaleType="fitCenter"
+ android:tint="?android:attr/textColorPrimary"
+ android:src="@drawable/ic_widgets"
+ android:contentDescription="@string/accessibility_action_open_communal_hub" />
diff --git a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
index 3f3bb0b..86c807b 100644
--- a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
+++ b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
@@ -15,12 +15,12 @@
*/
package com.android.keyguard
-import android.os.Trace
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.content.res.Resources
+import android.os.Trace
import android.text.format.DateFormat
import android.util.Log
import android.util.TypedValue
@@ -466,15 +466,11 @@
largeRegionSampler?.stopRegionSampler()
smallTimeListener?.stop()
largeTimeListener?.stop()
- clock
- ?.smallClock
- ?.view
- ?.removeOnAttachStateChangeListener(smallClockOnAttachStateChangeListener)
+ clock?.apply {
+ smallClock.view.removeOnAttachStateChangeListener(smallClockOnAttachStateChangeListener)
+ largeClock.view.removeOnAttachStateChangeListener(largeClockOnAttachStateChangeListener)
+ }
smallClockFrame?.viewTreeObserver?.removeOnGlobalLayoutListener(onGlobalLayoutListener)
- clock
- ?.largeClock
- ?.view
- ?.removeOnAttachStateChangeListener(largeClockOnAttachStateChangeListener)
}
/**
@@ -505,15 +501,17 @@
}
}
- private fun updateFontSizes() {
+ fun updateFontSizes() {
clock?.run {
- smallClock.events.onFontSettingChanged(
- resources.getDimensionPixelSize(R.dimen.small_clock_text_size).toFloat()
- )
+ smallClock.events.onFontSettingChanged(getSmallClockSizePx())
largeClock.events.onFontSettingChanged(getLargeClockSizePx())
}
}
+ private fun getSmallClockSizePx(): Float {
+ return resources.getDimensionPixelSize(R.dimen.small_clock_text_size).toFloat()
+ }
+
private fun getLargeClockSizePx(): Float {
return if (largeClockOnSecondaryDisplay) {
resources.getDimensionPixelSize(R.dimen.presentation_clock_text_size).toFloat()
@@ -549,9 +547,8 @@
it.copy(value = 1f - it.value)
},
keyguardTransitionInteractor.transition(Edge.create(LOCKSCREEN, AOD)),
- ).filter {
- it.transitionState != TransitionState.FINISHED
- }
+ )
+ .filter { it.transitionState != TransitionState.FINISHED }
.collect { handleDoze(it.value) }
}
}
@@ -574,28 +571,27 @@
internal fun listenForAnyStateToLockscreenTransition(scope: CoroutineScope): Job {
return scope.launch {
keyguardTransitionInteractor
- .transitionStepsToState(LOCKSCREEN)
- .filter { it.transitionState == TransitionState.STARTED }
- .filter { it.from != AOD }
- .collect { handleDoze(0f) }
+ .transitionStepsToState(LOCKSCREEN)
+ .filter { it.transitionState == TransitionState.STARTED }
+ .filter { it.from != AOD }
+ .collect { handleDoze(0f) }
}
}
/**
- * When keyguard is displayed due to pulsing notifications when AOD is off,
- * we should make sure clock is in dozing state instead of LS state
+ * When keyguard is displayed due to pulsing notifications when AOD is off, we should make sure
+ * clock is in dozing state instead of LS state
*/
@VisibleForTesting
internal fun listenForAnyStateToDozingTransition(scope: CoroutineScope): Job {
return scope.launch {
keyguardTransitionInteractor
- .transitionStepsToState(DOZING)
- .filter { it.transitionState == TransitionState.FINISHED }
- .collect { handleDoze(1f) }
+ .transitionStepsToState(DOZING)
+ .filter { it.transitionState == TransitionState.FINISHED }
+ .collect { handleDoze(1f) }
}
}
-
@VisibleForTesting
internal fun listenForDozing(scope: CoroutineScope): Job {
return scope.launch {
diff --git a/packages/SystemUI/src/com/android/systemui/complication/DreamHomeControlsComplication.java b/packages/SystemUI/src/com/android/systemui/complication/DreamHomeControlsComplication.java
index afa2375..e284bc7 100644
--- a/packages/SystemUI/src/com/android/systemui/complication/DreamHomeControlsComplication.java
+++ b/packages/SystemUI/src/com/android/systemui/complication/DreamHomeControlsComplication.java
@@ -18,6 +18,7 @@
import static com.android.systemui.complication.dagger.DreamHomeControlsComplicationComponent.DreamHomeControlsModule.DREAM_HOME_CONTROLS_CHIP_VIEW;
import static com.android.systemui.complication.dagger.RegisteredComplicationsModule.DREAM_HOME_CONTROLS_CHIP_LAYOUT_PARAMS;
+import static com.android.systemui.complication.dagger.RegisteredComplicationsModule.OPEN_HUB_CHIP_REPLACE_HOME_CONTROLS;
import static com.android.systemui.controls.dagger.ControlsComponent.Visibility.AVAILABLE;
import static com.android.systemui.controls.dagger.ControlsComponent.Visibility.AVAILABLE_AFTER_UNLOCK;
import static com.android.systemui.controls.dagger.ControlsComponent.Visibility.UNAVAILABLE;
@@ -35,6 +36,7 @@
import com.android.internal.logging.UiEventLogger;
import com.android.settingslib.Utils;
import com.android.systemui.CoreStartable;
+import com.android.systemui.Flags;
import com.android.systemui.animation.ActivityTransitionAnimator;
import com.android.systemui.complication.dagger.DreamHomeControlsComplicationComponent;
import com.android.systemui.controls.ControlsServiceInfo;
@@ -89,6 +91,7 @@
private final DreamHomeControlsComplication mComplication;
private final DreamOverlayStateController mDreamOverlayStateController;
private final ControlsComponent mControlsComponent;
+ private final boolean mReplacedByOpenHub;
private boolean mOverlayActive = false;
@@ -116,11 +119,13 @@
public Registrant(DreamHomeControlsComplication complication,
DreamOverlayStateController dreamOverlayStateController,
ControlsComponent controlsComponent,
- @SystemUser Monitor monitor) {
+ @SystemUser Monitor monitor,
+ @Named(OPEN_HUB_CHIP_REPLACE_HOME_CONTROLS) boolean replacedByOpenHub) {
super(monitor);
mComplication = complication;
mControlsComponent = controlsComponent;
mDreamOverlayStateController = dreamOverlayStateController;
+ mReplacedByOpenHub = replacedByOpenHub;
}
@Override
@@ -132,7 +137,9 @@
private void updateHomeControlsComplication() {
mControlsComponent.getControlsListingController().ifPresent(c -> {
- if (isHomeControlsAvailable(c.getCurrentServices())) {
+ final boolean replacedWithOpenHub =
+ Flags.glanceableHubShortcutButton() && mReplacedByOpenHub;
+ if (isHomeControlsAvailable(c.getCurrentServices()) && !replacedWithOpenHub) {
mDreamOverlayStateController.addComplication(mComplication);
} else {
mDreamOverlayStateController.removeComplication(mComplication);
diff --git a/packages/SystemUI/src/com/android/systemui/complication/OpenHubComplication.java b/packages/SystemUI/src/com/android/systemui/complication/OpenHubComplication.java
new file mode 100644
index 0000000..3cf22b1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/complication/OpenHubComplication.java
@@ -0,0 +1,212 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.complication;
+
+import static com.android.systemui.complication.dagger.OpenHubComplicationComponent.OpenHubModule.OPEN_HUB_CHIP_VIEW;
+import static com.android.systemui.complication.dagger.RegisteredComplicationsModule.OPEN_HUB_CHIP_LAYOUT_PARAMS;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.drawable.Drawable;
+import android.util.Log;
+import android.view.View;
+import android.widget.ImageView;
+
+import com.android.settingslib.Utils;
+import com.android.systemui.CoreStartable;
+import com.android.systemui.Flags;
+import com.android.systemui.communal.domain.interactor.CommunalInteractor;
+import com.android.systemui.communal.shared.model.CommunalScenes;
+import com.android.systemui.complication.dagger.OpenHubComplicationComponent;
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.dagger.qualifiers.SystemUser;
+import com.android.systemui.dreams.DreamOverlayStateController;
+import com.android.systemui.shared.condition.Monitor;
+import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.systemui.util.ViewController;
+import com.android.systemui.util.condition.ConditionalCoreStartable;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+
+/**
+ * A dream complication that shows a chip to open the glanceable hub.
+ */
+// TODO(b/339667383): delete or properly implement this once a product decision is made
+public class OpenHubComplication implements Complication {
+ private final Resources mResources;
+ private final OpenHubComplicationComponent.Factory mComponentFactory;
+
+ @Inject
+ public OpenHubComplication(
+ @Main Resources resources,
+ OpenHubComplicationComponent.Factory componentFactory) {
+ mResources = resources;
+ mComponentFactory = componentFactory;
+ }
+
+ @Override
+ public ViewHolder createView(ComplicationViewModel model) {
+ return mComponentFactory.create(mResources).getViewHolder();
+ }
+
+ @Override
+ public int getRequiredTypeAvailability() {
+ // TODO(b/339667383): create a new complication type if we decide to productionize this
+ return COMPLICATION_TYPE_HOME_CONTROLS;
+ }
+
+ /**
+ * {@link CoreStartable} for registering the complication with SystemUI on startup.
+ */
+ public static class Registrant extends ConditionalCoreStartable {
+ private final OpenHubComplication mComplication;
+ private final DreamOverlayStateController mDreamOverlayStateController;
+
+ private boolean mOverlayActive = false;
+
+ private final DreamOverlayStateController.Callback mOverlayStateCallback =
+ new DreamOverlayStateController.Callback() {
+ @Override
+ public void onStateChanged() {
+ if (mOverlayActive == mDreamOverlayStateController.isOverlayActive()) {
+ return;
+ }
+
+ mOverlayActive = !mOverlayActive;
+
+ if (mOverlayActive) {
+ updateOpenHubComplication();
+ }
+ }
+ };
+
+ @Inject
+ public Registrant(OpenHubComplication complication,
+ DreamOverlayStateController dreamOverlayStateController,
+ @SystemUser Monitor monitor) {
+ super(monitor);
+ mComplication = complication;
+ mDreamOverlayStateController = dreamOverlayStateController;
+ }
+
+ @Override
+ public void onStart() {
+ mDreamOverlayStateController.addCallback(mOverlayStateCallback);
+ }
+
+ private void updateOpenHubComplication() {
+ // TODO(b/339667383): don't show the complication if glanceable hub is disabled
+ if (Flags.glanceableHubShortcutButton()) {
+ mDreamOverlayStateController.addComplication(mComplication);
+ } else {
+ mDreamOverlayStateController.removeComplication(mComplication);
+ }
+ }
+ }
+
+ /**
+ * Contains values/logic associated with the dream complication view.
+ */
+ public static class OpenHubChipViewHolder implements ViewHolder {
+ private final ImageView mView;
+ private final ComplicationLayoutParams mLayoutParams;
+ private final OpenHubChipViewController mViewController;
+
+ @Inject
+ OpenHubChipViewHolder(
+ OpenHubChipViewController dreamOpenHubChipViewController,
+ @Named(OPEN_HUB_CHIP_VIEW) ImageView view,
+ @Named(OPEN_HUB_CHIP_LAYOUT_PARAMS) ComplicationLayoutParams layoutParams
+ ) {
+ mView = view;
+ mLayoutParams = layoutParams;
+ mViewController = dreamOpenHubChipViewController;
+ mViewController.init();
+ }
+
+ @Override
+ public ImageView getView() {
+ return mView;
+ }
+
+ @Override
+ public ComplicationLayoutParams getLayoutParams() {
+ return mLayoutParams;
+ }
+ }
+
+ /**
+ * Controls behavior of the dream complication.
+ */
+ static class OpenHubChipViewController extends ViewController<ImageView> {
+ private static final String TAG = "OpenHubCtrl";
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+ private final Context mContext;
+ private final ConfigurationController mConfigurationController;
+
+ private final ConfigurationController.ConfigurationListener mConfigurationListener =
+ new ConfigurationController.ConfigurationListener() {
+ @Override
+ public void onUiModeChanged() {
+ reloadResources();
+ }
+ };
+ private final CommunalInteractor mCommunalInteractor;
+
+ @Inject
+ OpenHubChipViewController(
+ @Named(OPEN_HUB_CHIP_VIEW) ImageView view,
+ Context context,
+ ConfigurationController configurationController,
+ CommunalInteractor communalInteractor) {
+ super(view);
+
+ mContext = context;
+ mConfigurationController = configurationController;
+ mCommunalInteractor = communalInteractor;
+ }
+
+ @Override
+ protected void onViewAttached() {
+ reloadResources();
+ mView.setOnClickListener(this::onClickOpenHub);
+ mConfigurationController.addCallback(mConfigurationListener);
+ }
+
+ @Override
+ protected void onViewDetached() {
+ mConfigurationController.removeCallback(mConfigurationListener);
+ }
+
+ private void reloadResources() {
+ mView.setImageTintList(Utils.getColorAttr(mContext, android.R.attr.textColorPrimary));
+ final Drawable background = mView.getBackground();
+ if (background != null) {
+ background.setTintList(
+ Utils.getColorAttr(mContext, com.android.internal.R.attr.colorSurface));
+ }
+ }
+
+ private void onClickOpenHub(View v) {
+ if (DEBUG) Log.d(TAG, "open hub complication tapped");
+
+ mCommunalInteractor.changeScene(CommunalScenes.Communal, null);
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/complication/dagger/OpenHubComplicationComponent.java b/packages/SystemUI/src/com/android/systemui/complication/dagger/OpenHubComplicationComponent.java
new file mode 100644
index 0000000..501601e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/complication/dagger/OpenHubComplicationComponent.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.complication.dagger;
+
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.drawable.Drawable;
+import android.view.LayoutInflater;
+import android.widget.ImageView;
+
+import com.android.systemui.complication.OpenHubComplication;
+import com.android.systemui.res.R;
+import com.android.systemui.shared.shadow.DoubleShadowIconDrawable;
+import com.android.systemui.shared.shadow.DoubleShadowTextHelper;
+
+import dagger.BindsInstance;
+import dagger.Module;
+import dagger.Provides;
+import dagger.Subcomponent;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+
+import javax.inject.Named;
+import javax.inject.Scope;
+
+/**
+ * Responsible for generating dependencies for the {@link OpenHubComplication}.
+ */
+@Subcomponent(modules = OpenHubComplicationComponent.OpenHubModule.class)
+@OpenHubComplicationComponent.OpenHubComplicationScope
+public interface OpenHubComplicationComponent {
+ /**
+ * Creates a view holder for the open hub complication.
+ */
+ OpenHubComplication.OpenHubChipViewHolder getViewHolder();
+
+ /**
+ * Scope of the open hub complication.
+ */
+ @Documented
+ @Retention(RUNTIME)
+ @Scope
+ @interface OpenHubComplicationScope {
+ }
+
+ /**
+ * Factory that generates a {@link OpenHubComplicationComponent}.
+ */
+ @Subcomponent.Factory
+ interface Factory {
+ /**
+ * Creates an instance of {@link OpenHubComplicationComponent}.
+ */
+ OpenHubComplicationComponent create(@BindsInstance Resources resources);
+ }
+
+ /**
+ * Scoped injected values for the {@link OpenHubComplicationComponent}.
+ */
+ @Module
+ interface OpenHubModule {
+ String OPEN_HUB_CHIP_VIEW = "open_hub_chip_view";
+ String OPEN_HUB_BACKGROUND_DRAWABLE = "open_hub_background_drawable";
+
+ /**
+ * Provides the dream open hub chip view.
+ */
+ @Provides
+ @OpenHubComplicationScope
+ @Named(OPEN_HUB_CHIP_VIEW)
+ static ImageView provideOpenHubChipView(
+ LayoutInflater layoutInflater,
+ @Named(OPEN_HUB_BACKGROUND_DRAWABLE) Drawable backgroundDrawable) {
+ final ImageView chip =
+ (ImageView) layoutInflater.inflate(R.layout.dream_overlay_open_hub_chip,
+ null, false);
+ chip.setBackground(backgroundDrawable);
+
+ return chip;
+ }
+
+ /**
+ * Provides the background drawable for the open hub chip.
+ */
+ @Provides
+ @OpenHubComplicationScope
+ @Named(OPEN_HUB_BACKGROUND_DRAWABLE)
+ static Drawable providesOpenHubBackground(Context context, Resources resources) {
+ return new DoubleShadowIconDrawable(createShadowInfo(
+ resources,
+ R.dimen.dream_overlay_bottom_affordance_key_text_shadow_radius,
+ R.dimen.dream_overlay_bottom_affordance_key_text_shadow_dx,
+ R.dimen.dream_overlay_bottom_affordance_key_text_shadow_dy,
+ R.dimen.dream_overlay_bottom_affordance_key_shadow_alpha
+ ),
+ createShadowInfo(
+ resources,
+ R.dimen.dream_overlay_bottom_affordance_ambient_text_shadow_radius,
+ R.dimen.dream_overlay_bottom_affordance_ambient_text_shadow_dx,
+ R.dimen.dream_overlay_bottom_affordance_ambient_text_shadow_dy,
+ R.dimen.dream_overlay_bottom_affordance_ambient_shadow_alpha
+ ),
+ resources.getDrawable(R.drawable.dream_overlay_bottom_affordance_bg),
+ resources.getDimensionPixelOffset(
+ R.dimen.dream_overlay_bottom_affordance_width),
+ resources.getDimensionPixelSize(R.dimen.dream_overlay_bottom_affordance_inset)
+ );
+ }
+
+ private static DoubleShadowTextHelper.ShadowInfo createShadowInfo(Resources resources,
+ int blurId, int offsetXId, int offsetYId, int alphaId) {
+
+ return new DoubleShadowTextHelper.ShadowInfo(
+ resources.getDimension(blurId),
+ resources.getDimension(offsetXId),
+ resources.getDimension(offsetYId),
+ resources.getFloat(alphaId)
+ );
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/complication/dagger/RegisteredComplicationsModule.java b/packages/SystemUI/src/com/android/systemui/complication/dagger/RegisteredComplicationsModule.java
index 6f1b098..edb5ff7 100644
--- a/packages/SystemUI/src/com/android/systemui/complication/dagger/RegisteredComplicationsModule.java
+++ b/packages/SystemUI/src/com/android/systemui/complication/dagger/RegisteredComplicationsModule.java
@@ -25,6 +25,7 @@
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
import com.android.systemui.res.R;
+import com.android.systemui.util.settings.SystemSettings;
import dagger.Module;
import dagger.Provides;
@@ -39,6 +40,7 @@
subcomponents = {
DreamClockTimeComplicationComponent.class,
DreamHomeControlsComplicationComponent.class,
+ OpenHubComplicationComponent.class,
DreamMediaEntryComplicationComponent.class
})
public interface RegisteredComplicationsModule {
@@ -46,6 +48,8 @@
String DREAM_SMARTSPACE_LAYOUT_PARAMS = "smartspace_layout_params";
String DREAM_HOME_CONTROLS_CHIP_LAYOUT_PARAMS = "home_controls_chip_layout_params";
String DREAM_MEDIA_ENTRY_LAYOUT_PARAMS = "media_entry_layout_params";
+ String OPEN_HUB_CHIP_LAYOUT_PARAMS = "open_hub_chip_layout_params";
+ String OPEN_HUB_CHIP_REPLACE_HOME_CONTROLS = "open_hub_chip_replace_home_controls";
int DREAM_CLOCK_TIME_COMPLICATION_WEIGHT = 1;
int DREAM_CLOCK_TIME_COMPLICATION_WEIGHT_NO_SMARTSPACE = 2;
@@ -109,6 +113,26 @@
}
/**
+ * Provides layout parameters for the open hub complication.
+ */
+ @Provides
+ @Named(OPEN_HUB_CHIP_LAYOUT_PARAMS)
+ static ComplicationLayoutParams provideOpenHubLayoutParams(
+ @Named(OPEN_HUB_CHIP_REPLACE_HOME_CONTROLS) boolean replaceHomeControls) {
+ int position = ComplicationLayoutParams.POSITION_BOTTOM | (replaceHomeControls
+ ? ComplicationLayoutParams.POSITION_START
+ : ComplicationLayoutParams.POSITION_END);
+ int direction = replaceHomeControls ? ComplicationLayoutParams.DIRECTION_END
+ : ComplicationLayoutParams.DIRECTION_START;
+ return new ComplicationLayoutParams(
+ ViewGroup.LayoutParams.WRAP_CONTENT,
+ ViewGroup.LayoutParams.WRAP_CONTENT,
+ position,
+ direction,
+ DREAM_HOME_CONTROLS_CHIP_COMPLICATION_WEIGHT);
+ }
+
+ /**
* Provides layout parameters for the smartspace complication.
*/
@Provides
@@ -124,4 +148,14 @@
res.getDimensionPixelSize(R.dimen.dream_overlay_complication_smartspace_padding),
res.getDimensionPixelSize(R.dimen.dream_overlay_complication_smartspace_max_width));
}
+
+ /**
+ * If true, the home controls chip should not be shown and the open hub chip should be shown in
+ * its place.
+ */
+ @Provides
+ @Named(OPEN_HUB_CHIP_REPLACE_HOME_CONTROLS)
+ static boolean providesOpenHubChipReplaceHomeControls(SystemSettings systemSettings) {
+ return systemSettings.getBool(OPEN_HUB_CHIP_REPLACE_HOME_CONTROLS, false);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java
index 3e98fc1..7aab37e 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java
@@ -32,6 +32,8 @@
import com.android.systemui.dock.DockManagerImpl;
import com.android.systemui.doze.DozeHost;
import com.android.systemui.keyguard.ui.composable.blueprint.DefaultBlueprintModule;
+import com.android.systemui.keyguard.ui.view.layout.blueprints.KeyguardBlueprintModule;
+import com.android.systemui.keyguard.ui.view.layout.sections.KeyguardSectionsModule;
import com.android.systemui.media.dagger.MediaModule;
import com.android.systemui.media.muteawait.MediaMuteAwaitConnectionCli;
import com.android.systemui.media.nearby.NearbyMediaDevicesManager;
@@ -112,6 +114,8 @@
GestureModule.class,
HeadsUpModule.class,
KeyboardShortcutsModule.class,
+ KeyguardBlueprintModule.class,
+ KeyguardSectionsModule.class,
MediaModule.class,
MediaMuteAwaitConnectionCli.StartableModule.class,
MultiUserUtilsModule.class,
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index 339e8f0..2ebb94f 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -70,8 +70,6 @@
import com.android.systemui.keyboard.KeyboardModule;
import com.android.systemui.keyevent.data.repository.KeyEventRepositoryModule;
import com.android.systemui.keyguard.ui.composable.LockscreenContent;
-import com.android.systemui.keyguard.ui.view.layout.blueprints.KeyguardBlueprintModule;
-import com.android.systemui.keyguard.ui.view.layout.sections.KeyguardSectionsModule;
import com.android.systemui.log.dagger.LogModule;
import com.android.systemui.log.dagger.MonitorLog;
import com.android.systemui.log.table.TableLogBuffer;
@@ -222,8 +220,6 @@
InputMethodModule.class,
KeyEventRepositoryModule.class,
KeyboardModule.class,
- KeyguardBlueprintModule.class,
- KeyguardSectionsModule.class,
LetterboxModule.class,
LogModule.class,
MediaProjectionActivitiesModule.class,
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/touch/CommunalTouchHandler.java b/packages/SystemUI/src/com/android/systemui/dreams/touch/CommunalTouchHandler.java
index 1c047dd..04fda33 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/touch/CommunalTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/touch/CommunalTouchHandler.java
@@ -98,7 +98,7 @@
// Notification shade window has its own logic to be visible if the hub is open, no need to
// do anything here other than send touch events over.
session.registerInputListener(ev -> {
- surfaces.handleDreamTouch((MotionEvent) ev);
+ surfaces.handleCommunalHubTouch((MotionEvent) ev);
if (ev != null && ((MotionEvent) ev).getAction() == MotionEvent.ACTION_UP) {
var unused = session.pop();
}
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index 2e49919..84980c1 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -170,12 +170,6 @@
val WALLPAPER_PICKER_GRID_APPLY_BUTTON =
unreleasedFlag("wallpaper_picker_grid_apply_button")
- /** Keyguard Migration */
-
- // TODO(b/297037052): Tracking bug.
- @JvmField
- val REMOVE_NPVC_BOTTOM_AREA_USAGE = unreleasedFlag("remove_npvc_bottom_area_usage")
-
/** Flag meant to guard the talkback fix for the KeyguardIndicationTextView */
// TODO(b/286563884): Tracking bug
@JvmField val KEYGUARD_TALKBACK_FIX = unreleasedFlag("keyguard_talkback_fix")
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
index c32c226..a50cc8f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
@@ -53,7 +53,6 @@
import com.android.systemui.keyguard.ui.composable.blueprint.ComposableLockscreenSceneBlueprint
import com.android.systemui.keyguard.ui.view.KeyguardIndicationArea
import com.android.systemui.keyguard.ui.view.KeyguardRootView
-import com.android.systemui.keyguard.ui.view.layout.KeyguardBlueprintCommandListener
import com.android.systemui.keyguard.ui.viewmodel.KeyguardBlueprintViewModel
import com.android.systemui.keyguard.ui.viewmodel.KeyguardClockViewModel
import com.android.systemui.keyguard.ui.viewmodel.KeyguardIndicationAreaViewModel
@@ -89,7 +88,6 @@
private val screenOffAnimationController: ScreenOffAnimationController,
private val occludingAppDeviceEntryMessageViewModel: OccludingAppDeviceEntryMessageViewModel,
private val chipbarCoordinator: ChipbarCoordinator,
- private val keyguardBlueprintCommandListener: KeyguardBlueprintCommandListener,
private val keyguardBlueprintViewModel: KeyguardBlueprintViewModel,
private val keyguardStatusViewComponentFactory: KeyguardStatusViewComponent.Factory,
private val configuration: ConfigurationState,
@@ -160,7 +158,6 @@
)
}
}
- keyguardBlueprintCommandListener.start()
}
fun bindIndicationArea() {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/BuiltInKeyguardQuickAffordanceKeys.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/BuiltInKeyguardQuickAffordanceKeys.kt
index 80675d3..0863cd7 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/BuiltInKeyguardQuickAffordanceKeys.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/BuiltInKeyguardQuickAffordanceKeys.kt
@@ -28,6 +28,8 @@
const val CREATE_NOTE = "create_note"
const val DO_NOT_DISTURB = "do_not_disturb"
const val FLASHLIGHT = "flashlight"
+ // TODO(b/339667383): delete or properly implement this once a product decision is made
+ const val GLANCEABLE_HUB = "glanceable_hub"
const val HOME_CONTROLS = "home"
const val MUTE = "mute"
const val QR_CODE_SCANNER = "qr_code_scanner"
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/GlanceableHubQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/GlanceableHubQuickAffordanceConfig.kt
new file mode 100644
index 0000000..d09b9f6
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/GlanceableHubQuickAffordanceConfig.kt
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.data.quickaffordance
+
+import com.android.systemui.Flags
+import com.android.systemui.animation.Expandable
+import com.android.systemui.common.shared.model.ContentDescription
+import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.communal.data.repository.CommunalSceneRepository
+import com.android.systemui.communal.shared.model.CommunalScenes
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.res.R
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.flowOf
+
+/** Shortcut that opens the glanceable hub. */
+// TODO(b/339667383): delete or properly implement this once a product decision is made
+@SysUISingleton
+class GlanceableHubQuickAffordanceConfig
+@Inject
+constructor(
+ private val communalRepository: CommunalSceneRepository,
+) : KeyguardQuickAffordanceConfig {
+
+ override val key: String = BuiltInKeyguardQuickAffordanceKeys.GLANCEABLE_HUB
+ override fun pickerName(): String = "Glanceable hub"
+
+ override val pickerIconResourceId = R.drawable.ic_widgets
+
+ override val lockScreenState: Flow<KeyguardQuickAffordanceConfig.LockScreenState> by lazy {
+ if (Flags.glanceableHubShortcutButton()) {
+ val contentDescription = ContentDescription.Loaded(pickerName())
+ val icon = Icon.Resource(pickerIconResourceId, contentDescription)
+ flowOf(KeyguardQuickAffordanceConfig.LockScreenState.Visible(icon))
+ } else {
+ flowOf(KeyguardQuickAffordanceConfig.LockScreenState.Hidden)
+ }
+ }
+
+ override fun onTriggered(
+ expandable: Expandable?
+ ): KeyguardQuickAffordanceConfig.OnTriggeredResult {
+ communalRepository.changeScene(CommunalScenes.Communal, null)
+ return KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardDataQuickAffordanceModule.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardDataQuickAffordanceModule.kt
index 4556195..93296f0 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardDataQuickAffordanceModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardDataQuickAffordanceModule.kt
@@ -36,6 +36,7 @@
camera: CameraQuickAffordanceConfig,
doNotDisturb: DoNotDisturbQuickAffordanceConfig,
flashlight: FlashlightQuickAffordanceConfig,
+ glanceableHub: GlanceableHubQuickAffordanceConfig,
home: HomeControlsKeyguardQuickAffordanceConfig,
mute: MuteQuickAffordanceConfig,
quickAccessWallet: QuickAccessWalletKeyguardQuickAffordanceConfig,
@@ -46,6 +47,7 @@
camera,
doNotDisturb,
flashlight,
+ glanceableHub,
home,
mute,
quickAccessWallet,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLocalUserSelectionManager.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLocalUserSelectionManager.kt
index deedbdb..0748979 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLocalUserSelectionManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLocalUserSelectionManager.kt
@@ -20,15 +20,17 @@
import android.content.Context
import android.content.IntentFilter
import android.content.SharedPreferences
-import com.android.systemui.res.R
+import com.android.systemui.Flags
import com.android.systemui.backup.BackupHelper
import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.res.R
import com.android.systemui.settings.UserFileManager
import com.android.systemui.settings.UserTracker
+import com.android.systemui.util.settings.SystemSettings
import javax.inject.Inject
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.channels.awaitClose
@@ -50,6 +52,7 @@
@Application private val context: Context,
private val userFileManager: UserFileManager,
private val userTracker: UserTracker,
+ private val systemSettings: SystemSettings,
broadcastDispatcher: BroadcastDispatcher,
) : KeyguardQuickAffordanceSelectionManager {
@@ -70,6 +73,22 @@
}
private val defaults: Map<String, List<String>> by lazy {
+ // Quick hack to allow testing out a lock screen shortcut to open the glanceable hub. This
+ // flag will not be rolled out and is only used for local testing.
+ // TODO(b/339667383): delete or properly implement this once a product decision is made
+ if (Flags.glanceableHubShortcutButton()) {
+ if (systemSettings.getBool("open_hub_chip_replace_home_controls", false)) {
+ return@lazy mapOf(
+ "bottom_start" to listOf("glanceable_hub"),
+ "bottom_end" to listOf("create_note")
+ )
+ } else {
+ return@lazy mapOf(
+ "bottom_start" to listOf("home"),
+ "bottom_end" to listOf("glanceable_hub")
+ )
+ }
+ }
context.resources
.getStringArray(R.array.config_keyguardQuickAffordanceDefaults)
.associate { item ->
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardBlueprintRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardBlueprintRepository.kt
index c11c49c..b826a00 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardBlueprintRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardBlueprintRepository.kt
@@ -91,9 +91,9 @@
*/
fun refreshBlueprint(config: Config = Config.DEFAULT) {
fun scheduleCallback() {
- // We use a handler here instead of a CoroutineDipsatcher because the one provided by
+ // We use a handler here instead of a CoroutineDispatcher because the one provided by
// @Main CoroutineDispatcher is currently Dispatchers.Main.immediate, which doesn't
- // delay the callback, and instead runs it imemdiately.
+ // delay the callback, and instead runs it immediately.
handler.post {
assert.isMainThread()
targetTransitionConfig?.let {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt
index 53a0c32..76a8223 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt
@@ -86,7 +86,7 @@
return@combine null
}
- fromBouncerStep.value > 0.5f
+ fromBouncerStep.value > TO_GONE_SURFACE_BEHIND_VISIBLE_THRESHOLD
}
.onStart {
// Default to null ("don't care, use a reasonable default").
@@ -232,5 +232,6 @@
val TO_AOD_DURATION = DEFAULT_DURATION
val TO_LOCKSCREEN_DURATION = DEFAULT_DURATION
val TO_DOZING_DURATION = DEFAULT_DURATION
+ val TO_GONE_SURFACE_BEHIND_VISIBLE_THRESHOLD = 0.5f
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractor.kt
index 7cee258..41c3959 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractor.kt
@@ -20,6 +20,7 @@
package com.android.systemui.keyguard.domain.interactor
import android.content.Context
+import com.android.systemui.CoreStartable
import com.android.systemui.biometrics.domain.interactor.FingerprintPropertyInteractor
import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
import com.android.systemui.dagger.SysUISingleton
@@ -31,17 +32,17 @@
import com.android.systemui.keyguard.ui.view.layout.blueprints.SplitShadeKeyguardBlueprint
import com.android.systemui.keyguard.ui.view.layout.blueprints.transitions.IntraBlueprintTransition.Config
import com.android.systemui.keyguard.ui.view.layout.blueprints.transitions.IntraBlueprintTransition.Type
+import com.android.systemui.keyguard.ui.view.layout.sections.ClockSection
+import com.android.systemui.keyguard.ui.view.layout.sections.SmartspaceSection
import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.shade.shared.model.ShadeMode
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.map
-import kotlinx.coroutines.flow.merge
import kotlinx.coroutines.launch
@SysUISingleton
@@ -53,9 +54,11 @@
private val context: Context,
private val shadeInteractor: ShadeInteractor,
private val clockInteractor: KeyguardClockInteractor,
- configurationInteractor: ConfigurationInteractor,
- fingerprintPropertyInteractor: FingerprintPropertyInteractor,
-) {
+ private val configurationInteractor: ConfigurationInteractor,
+ private val fingerprintPropertyInteractor: FingerprintPropertyInteractor,
+ private val smartspaceSection: SmartspaceSection,
+ private val clockSection: ClockSection,
+) : CoreStartable {
/** The current blueprint for the lockscreen. */
val blueprint: StateFlow<KeyguardBlueprint> = keyguardBlueprintRepository.blueprint
@@ -75,15 +78,23 @@
}
}
- private val refreshEvents: Flow<Unit> =
- merge(
- configurationInteractor.onAnyConfigurationChange,
- fingerprintPropertyInteractor.propertiesInitialized.filter { it }.map {},
- )
-
- init {
+ override fun start() {
applicationScope.launch { blueprintId.collect { transitionToBlueprint(it) } }
- applicationScope.launch { refreshEvents.collect { refreshBlueprint() } }
+ applicationScope.launch {
+ fingerprintPropertyInteractor.propertiesInitialized
+ .filter { it }
+ .collect { refreshBlueprint() }
+ }
+ applicationScope.launch {
+ val refreshConfig =
+ Config(
+ Type.NoTransition,
+ rebuildSections = listOf(smartspaceSection),
+ )
+ configurationInteractor.onAnyConfigurationChange.collect {
+ refreshBlueprint(refreshConfig)
+ }
+ }
}
/**
@@ -120,4 +131,8 @@
fun getCurrentBlueprint(): KeyguardBlueprint {
return keyguardBlueprintRepository.blueprint.value
}
+
+ companion object {
+ private val TAG = "KeyguardBlueprintInteractor"
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt
index fb65a6d..88e6602 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt
@@ -18,6 +18,7 @@
package com.android.systemui.keyguard.domain.interactor
+import com.android.compose.animation.scene.ObservableTransitionState
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor
import com.android.systemui.keyguard.shared.model.BiometricUnlockMode
@@ -29,6 +30,7 @@
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.statusbar.notification.domain.interactor.NotificationLaunchAnimationInteractor
import com.android.systemui.util.kotlin.sample
+import com.android.systemui.utils.coroutines.flow.flatMapLatestConflated
import dagger.Lazy
import javax.inject.Inject
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -84,25 +86,52 @@
}
.distinctUntilChanged()
+ private val isDeviceEntered: Flow<Boolean> by lazy {
+ deviceEntryInteractor.get().isDeviceEntered
+ }
+
+ private val isDeviceNotEntered: Flow<Boolean> by lazy { isDeviceEntered.map { !it } }
+
/**
- * Surface visibility, which is either determined by the default visibility in the FINISHED
- * KeyguardState, or the transition-specific visibility used during certain RUNNING transitions.
+ * Surface visibility, which is either determined by the default visibility when not
+ * transitioning between [KeyguardState]s or [Scenes] or the transition-specific visibility used
+ * during certain ongoing transitions.
*/
@OptIn(ExperimentalCoroutinesApi::class)
val surfaceBehindVisibility: Flow<Boolean> =
- transitionInteractor.isInTransitionToAnyState
- .flatMapLatest { isInTransition ->
- if (!isInTransition) {
- defaultSurfaceBehindVisibility
- } else {
- combine(
- transitionSpecificSurfaceBehindVisibility,
- defaultSurfaceBehindVisibility,
- ) { transitionVisibility, defaultVisibility ->
- // Defer to the transition-specific visibility since we're RUNNING a
- // transition, but fall back to the default visibility if the current
- // transition's interactor did not specify a visibility.
- transitionVisibility ?: defaultVisibility
+ if (SceneContainerFlag.isEnabled) {
+ sceneInteractor.get().transitionState.flatMapLatestConflated { transitionState ->
+ when (transitionState) {
+ is ObservableTransitionState.Transition ->
+ when {
+ transitionState.fromScene == Scenes.Lockscreen &&
+ transitionState.toScene == Scenes.Gone -> flowOf(true)
+ transitionState.fromScene == Scenes.Bouncer &&
+ transitionState.toScene == Scenes.Gone ->
+ transitionState.progress.map { progress ->
+ progress >
+ FromPrimaryBouncerTransitionInteractor
+ .TO_GONE_SURFACE_BEHIND_VISIBLE_THRESHOLD
+ }
+ else -> isDeviceEntered
+ }
+ is ObservableTransitionState.Idle -> isDeviceEntered
+ }
+ }
+ } else {
+ transitionInteractor.isInTransitionToAnyState.flatMapLatest { isInTransition ->
+ if (!isInTransition) {
+ defaultSurfaceBehindVisibility
+ } else {
+ combine(
+ transitionSpecificSurfaceBehindVisibility,
+ defaultSurfaceBehindVisibility,
+ ) { transitionVisibility, defaultVisibility ->
+ // Defer to the transition-specific visibility since we're RUNNING a
+ // transition, but fall back to the default visibility if the current
+ // transition's interactor did not specify a visibility.
+ transitionVisibility ?: defaultVisibility
+ }
}
}
}
@@ -162,7 +191,7 @@
*/
val lockscreenVisibility: Flow<Boolean> =
if (SceneContainerFlag.isEnabled) {
- deviceEntryInteractor.get().isDeviceEntered.map { !it }
+ isDeviceNotEntered
} else {
transitionInteractor.currentKeyguardState
.sample(transitionInteractor.startedStepWithPrecedingStep, ::Pair)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardBlueprint.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardBlueprint.kt
index 7ca2eba..6d579f3 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardBlueprint.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardBlueprint.kt
@@ -37,16 +37,32 @@
fun replaceViews(
constraintLayout: ConstraintLayout,
previousBlueprint: KeyguardBlueprint? = null,
+ rebuildSections: List<KeyguardSection> = listOf(),
bindData: Boolean = true
) {
- val prevSections =
- previousBlueprint?.let { prev ->
- prev.sections.subtract(sections).forEach { it.removeViews(constraintLayout) }
- prev.sections
+ val prevSections = previousBlueprint?.sections ?: listOf()
+ val skipSections = sections.intersect(prevSections).subtract(rebuildSections)
+ prevSections.subtract(skipSections).forEach { it.removeViews(constraintLayout) }
+ sections.subtract(skipSections).forEach {
+ it.addViews(constraintLayout)
+ if (bindData) {
+ it.bindData(constraintLayout)
}
- ?: listOf()
+ }
+ }
- sections.subtract(prevSections).forEach {
+ /** Rebuilds views for the target sections, or all of them if unspecified. */
+ fun rebuildViews(
+ constraintLayout: ConstraintLayout,
+ rebuildSections: List<KeyguardSection> = sections,
+ bindData: Boolean = true
+ ) {
+ if (rebuildSections.isEmpty()) {
+ return
+ }
+
+ rebuildSections.forEach { it.removeViews(constraintLayout) }
+ rebuildSections.forEach {
it.addViews(constraintLayout)
if (bindData) {
it.bindData(constraintLayout)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBlueprintViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBlueprintViewBinder.kt
index 52d7519..8160335 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBlueprintViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBlueprintViewBinder.kt
@@ -95,17 +95,8 @@
null as KeyguardBlueprint?,
)
.collect { (prevBlueprint, blueprint) ->
- val cs =
- ConstraintSet().apply {
- clone(constraintLayout)
- val emptyLayout = ConstraintSet.Layout()
- knownIds.forEach {
- getConstraint(it).layout.copyFrom(emptyLayout)
- }
- blueprint.applyConstraints(this)
- }
-
- var transition =
+ val config = Config.DEFAULT
+ val transition =
if (
!KeyguardBottomAreaRefactor.isEnabled &&
prevBlueprint != null &&
@@ -114,23 +105,37 @@
BaseBlueprintTransition(clockViewModel)
.addTransition(
IntraBlueprintTransition(
- Config.DEFAULT,
+ config,
clockViewModel,
smartspaceViewModel
)
)
} else {
IntraBlueprintTransition(
- Config.DEFAULT,
+ config,
clockViewModel,
smartspaceViewModel
)
}
- runTransition(constraintLayout, transition, Config.DEFAULT) {
- // Add and remove views of sections that are not contained by the
- // other.
- blueprint.replaceViews(constraintLayout, prevBlueprint)
+ runTransition(constraintLayout, transition, config) {
+ // Replace sections from the previous blueprint with the new ones
+ blueprint.replaceViews(
+ constraintLayout,
+ prevBlueprint,
+ config.rebuildSections
+ )
+
+ val cs =
+ ConstraintSet().apply {
+ clone(constraintLayout)
+ val emptyLayout = ConstraintSet.Layout()
+ knownIds.forEach {
+ getConstraint(it).layout.copyFrom(emptyLayout)
+ }
+ blueprint.applyConstraints(this)
+ }
+
logAlphaVisibilityOfAppliedConstraintSet(cs, clockViewModel)
cs.applyTo(constraintLayout)
}
@@ -138,22 +143,21 @@
}
launch("$TAG#viewModel.refreshTransition") {
- viewModel.refreshTransition.collect { transition ->
- val cs =
- ConstraintSet().apply {
- clone(constraintLayout)
- viewModel.blueprint.value.applyConstraints(this)
- }
+ viewModel.refreshTransition.collect { config ->
+ val blueprint = viewModel.blueprint.value
runTransition(
constraintLayout,
- IntraBlueprintTransition(
- transition,
- clockViewModel,
- smartspaceViewModel
- ),
- transition,
+ IntraBlueprintTransition(config, clockViewModel, smartspaceViewModel),
+ config,
) {
+ blueprint.rebuildViews(constraintLayout, config.rebuildSections)
+
+ val cs =
+ ConstraintSet().apply {
+ clone(constraintLayout)
+ blueprint.applyConstraints(this)
+ }
logAlphaVisibilityOfAppliedConstraintSet(cs, clockViewModel)
cs.applyTo(constraintLayout)
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/KeyguardBlueprintCommandListener.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/KeyguardBlueprintCommandListener.kt
index 962cdf1..c026656 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/KeyguardBlueprintCommandListener.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/KeyguardBlueprintCommandListener.kt
@@ -17,6 +17,7 @@
package com.android.systemui.keyguard.ui.view.layout
import androidx.core.text.isDigitsOnly
+import com.android.systemui.CoreStartable
import com.android.systemui.keyguard.data.repository.KeyguardBlueprintRepository
import com.android.systemui.keyguard.domain.interactor.KeyguardBlueprintInteractor
import com.android.systemui.statusbar.commandline.Command
@@ -31,10 +32,10 @@
private val commandRegistry: CommandRegistry,
private val keyguardBlueprintRepository: KeyguardBlueprintRepository,
private val keyguardBlueprintInteractor: KeyguardBlueprintInteractor,
-) {
+) : CoreStartable {
private val layoutCommand = KeyguardLayoutManagerCommand()
- fun start() {
+ override fun start() {
commandRegistry.registerCommand(COMMAND) { layoutCommand }
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/KeyguardBlueprintModule.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/KeyguardBlueprintModule.kt
index 04ac7bf..2dc9301 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/KeyguardBlueprintModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/KeyguardBlueprintModule.kt
@@ -17,9 +17,14 @@
package com.android.systemui.keyguard.ui.view.layout.blueprints
+import com.android.systemui.CoreStartable
+import com.android.systemui.keyguard.domain.interactor.KeyguardBlueprintInteractor
import com.android.systemui.keyguard.shared.model.KeyguardBlueprint
+import com.android.systemui.keyguard.ui.view.layout.KeyguardBlueprintCommandListener
import dagger.Binds
import dagger.Module
+import dagger.multibindings.ClassKey
+import dagger.multibindings.IntoMap
import dagger.multibindings.IntoSet
@Module
@@ -41,4 +46,18 @@
abstract fun bindShortcutsBesideUdfpsLockscreenBlueprint(
shortcutsBesideUdfpsLockscreenBlueprint: ShortcutsBesideUdfpsKeyguardBlueprint
): KeyguardBlueprint
+
+ @Binds
+ @IntoMap
+ @ClassKey(KeyguardBlueprintInteractor::class)
+ abstract fun bindsKeyguardBlueprintInteractor(
+ keyguardBlueprintInteractor: KeyguardBlueprintInteractor
+ ): CoreStartable
+
+ @Binds
+ @IntoMap
+ @ClassKey(KeyguardBlueprintCommandListener::class)
+ abstract fun bindsKeyguardBlueprintCommandListener(
+ keyguardBlueprintCommandListener: KeyguardBlueprintCommandListener
+ ): CoreStartable
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/transitions/IntraBlueprintTransition.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/transitions/IntraBlueprintTransition.kt
index c69d868..02e9ca5 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/transitions/IntraBlueprintTransition.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/transitions/IntraBlueprintTransition.kt
@@ -17,6 +17,7 @@
package com.android.systemui.keyguard.ui.view.layout.blueprints.transitions
import android.transition.TransitionSet
+import com.android.systemui.keyguard.shared.model.KeyguardSection
import com.android.systemui.keyguard.ui.view.layout.sections.transitions.ClockSizeTransition
import com.android.systemui.keyguard.ui.view.layout.sections.transitions.DefaultClockSteppingTransition
import com.android.systemui.keyguard.ui.viewmodel.KeyguardClockViewModel
@@ -46,6 +47,7 @@
val type: Type,
val checkPriority: Boolean = true,
val terminatePrevious: Boolean = true,
+ val rebuildSections: List<KeyguardSection> = listOf(),
) {
companion object {
val DEFAULT = Config(Type.NoTransition)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSection.kt
index b367715..34a1da5 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSection.kt
@@ -32,6 +32,7 @@
import androidx.constraintlayout.widget.ConstraintSet.VISIBLE
import androidx.constraintlayout.widget.ConstraintSet.WRAP_CONTENT
import com.android.systemui.customization.R as custR
+import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.MigrateClocksToBlueprint
import com.android.systemui.keyguard.domain.interactor.KeyguardBlueprintInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor
@@ -57,6 +58,7 @@
alpha: Float,
) = views.forEach { view -> this.setAlpha(view.id, alpha) }
+@SysUISingleton
class ClockSection
@Inject
constructor(
@@ -72,6 +74,7 @@
if (!MigrateClocksToBlueprint.isEnabled) {
return
}
+
KeyguardClockViewBinder.bind(
this,
constraintLayout,
@@ -86,6 +89,7 @@
if (!MigrateClocksToBlueprint.isEnabled) {
return
}
+
keyguardClockViewModel.currentClock.value?.let { clock ->
constraintSet.applyDeltaFrom(buildConstraints(clock, constraintSet))
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySection.kt
index 0b8376a..c5fab8f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySection.kt
@@ -219,5 +219,37 @@
sensorRect.left
)
}
+
+ // This is only intended to be here until the KeyguardBottomAreaRefactor flag is enabled
+ // Without this logic, the lock icon location changes but the KeyguardBottomAreaView is not
+ // updated and visible ui layout jank occurs. This is due to AmbientIndicationContainer
+ // being in NPVC and laying out prior to the KeyguardRootView.
+ // Remove when both DeviceEntryUdfpsRefactor and KeyguardBottomAreaRefactor are enabled.
+ if (DeviceEntryUdfpsRefactor.isEnabled && !KeyguardBottomAreaRefactor.isEnabled) {
+ with(notificationPanelView) {
+ val isUdfpsSupported = deviceEntryIconViewModel.get().isUdfpsSupported.value
+ val bottomAreaViewRight = findViewById<View>(R.id.keyguard_bottom_area)?.right ?: 0
+ findViewById<View>(R.id.ambient_indication_container)?.let {
+ val (ambientLeft, ambientTop) = it.locationOnScreen
+ if (isUdfpsSupported) {
+ // make top of ambient indication view the bottom of the lock icon
+ it.layout(
+ ambientLeft,
+ sensorRect.bottom,
+ bottomAreaViewRight - ambientLeft,
+ ambientTop + it.measuredHeight
+ )
+ } else {
+ // make bottom of ambient indication view the top of the lock icon
+ it.layout(
+ ambientLeft,
+ sensorRect.top - it.measuredHeight,
+ bottomAreaViewRight - ambientLeft,
+ sensorRect.top
+ )
+ }
+ }
+ }
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSection.kt
index 487c2e9..2d6690f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSection.kt
@@ -22,6 +22,7 @@
import androidx.constraintlayout.widget.Barrier
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.constraintlayout.widget.ConstraintSet
+import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.KeyguardUnlockAnimationController
import com.android.systemui.keyguard.MigrateClocksToBlueprint
import com.android.systemui.keyguard.domain.interactor.KeyguardBlueprintInteractor
@@ -36,6 +37,7 @@
import dagger.Lazy
import javax.inject.Inject
+@SysUISingleton
open class SmartspaceSection
@Inject
constructor(
diff --git a/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt b/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt
index 22aa492..1d8b7e5b 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt
@@ -43,6 +43,7 @@
import com.android.systemui.communal.ui.compose.CommunalContent
import com.android.systemui.communal.ui.viewmodel.CommunalViewModel
import com.android.systemui.communal.util.CommunalColors
+import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.shared.model.KeyguardState
@@ -64,6 +65,7 @@
*
* This will be used until the glanceable hub is integrated into Flexiglass.
*/
+@SysUISingleton
class GlanceableHubContainerController
@Inject
constructor(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
index 7b7a35b..05a4391 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
@@ -327,6 +327,11 @@
@Deprecated
float getDisplayDensity();
+ /**
+ * Forwards touch events to communal hub
+ */
+ void handleCommunalHubTouch(MotionEvent event);
+
public static class KeyboardShortcutsMessage {
final int mDeviceId;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesEmptyImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesEmptyImpl.kt
index 5ab56ae..a7b5484 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesEmptyImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesEmptyImpl.kt
@@ -81,6 +81,7 @@
override fun shouldIgnoreTouch() = false
override fun isDeviceInteractive() = false
override fun handleDreamTouch(event: MotionEvent?) {}
+ override fun handleCommunalHubTouch(event: MotionEvent?) {}
override fun awakenDreams() {}
override fun isBouncerShowing() = false
override fun isBouncerShowingScrimmed() = false
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
index e0da2fe..78a8036 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -172,6 +172,7 @@
import com.android.systemui.settings.brightness.BrightnessSliderController;
import com.android.systemui.settings.brightness.domain.interactor.BrightnessMirrorShowingInteractor;
import com.android.systemui.shade.CameraLauncher;
+import com.android.systemui.shade.GlanceableHubContainerController;
import com.android.systemui.shade.NotificationShadeWindowView;
import com.android.systemui.shade.NotificationShadeWindowViewController;
import com.android.systemui.shade.QuickSettingsController;
@@ -595,6 +596,7 @@
private final ColorExtractor.OnColorsChangedListener mOnColorsChangedListener =
(extractor, which) -> updateTheme();
private final BrightnessMirrorShowingInteractor mBrightnessMirrorShowingInteractor;
+ private final GlanceableHubContainerController mGlanceableHubContainerController;
/**
* Public constructor for CentralSurfaces.
@@ -707,7 +709,8 @@
UserTracker userTracker,
Provider<FingerprintManager> fingerprintManager,
ActivityStarter activityStarter,
- BrightnessMirrorShowingInteractor brightnessMirrorShowingInteractor
+ BrightnessMirrorShowingInteractor brightnessMirrorShowingInteractor,
+ GlanceableHubContainerController glanceableHubContainerController
) {
mContext = context;
mNotificationsController = notificationsController;
@@ -802,6 +805,7 @@
mFingerprintManager = fingerprintManager;
mActivityStarter = activityStarter;
mBrightnessMirrorShowingInteractor = brightnessMirrorShowingInteractor;
+ mGlanceableHubContainerController = glanceableHubContainerController;
mLockscreenShadeTransitionController = lockscreenShadeTransitionController;
mStartingSurfaceOptional = startingSurfaceOptional;
@@ -2951,6 +2955,11 @@
}
@Override
+ public void handleCommunalHubTouch(MotionEvent event) {
+ mGlanceableHubContainerController.onTouchEvent(event);
+ }
+
+ @Override
public void awakenDreams() {
mUiBgExecutor.execute(() -> {
try {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/IndividualSensorPrivacyControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/IndividualSensorPrivacyControllerImpl.java
index 58b82f1..da928a3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/IndividualSensorPrivacyControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/IndividualSensorPrivacyControllerImpl.java
@@ -32,6 +32,7 @@
import androidx.annotation.NonNull;
import com.android.internal.camera.flags.Flags;
+import com.android.systemui.util.ListenerSet;
import java.util.Set;
@@ -43,7 +44,7 @@
private final SparseBooleanArray mSoftwareToggleState = new SparseBooleanArray();
private final SparseBooleanArray mHardwareToggleState = new SparseBooleanArray();
private Boolean mRequiresAuthentication;
- private final Set<Callback> mCallbacks = new ArraySet<>();
+ private final ListenerSet<Callback> mCallbacks = new ListenerSet<>();
public IndividualSensorPrivacyControllerImpl(
@NonNull SensorPrivacyManager sensorPrivacyManager) {
@@ -115,7 +116,7 @@
@Override
public void addCallback(@NonNull Callback listener) {
- mCallbacks.add(listener);
+ mCallbacks.addIfAbsent(listener);
}
@Override
diff --git a/packages/SystemUI/tests/src/com/android/systemui/complication/DreamHomeControlsComplicationTest.java b/packages/SystemUI/tests/src/com/android/systemui/complication/DreamHomeControlsComplicationTest.java
index 07c980b..18bd960b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/complication/DreamHomeControlsComplicationTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/complication/DreamHomeControlsComplicationTest.java
@@ -132,7 +132,7 @@
public void complicationAvailability_serviceNotAvailable_noFavorites_doNotAddComplication() {
final DreamHomeControlsComplication.Registrant registrant =
new DreamHomeControlsComplication.Registrant(mComplication,
- mDreamOverlayStateController, mControlsComponent, mMonitor);
+ mDreamOverlayStateController, mControlsComponent, mMonitor, false);
registrant.start();
setHaveFavorites(false);
@@ -145,7 +145,7 @@
public void complicationAvailability_serviceAvailable_noFavorites_doNotAddComplication() {
final DreamHomeControlsComplication.Registrant registrant =
new DreamHomeControlsComplication.Registrant(mComplication,
- mDreamOverlayStateController, mControlsComponent, mMonitor);
+ mDreamOverlayStateController, mControlsComponent, mMonitor, false);
registrant.start();
setHaveFavorites(false);
@@ -158,7 +158,7 @@
public void complicationAvailability_serviceAvailable_noFavorites_panel_addComplication() {
final DreamHomeControlsComplication.Registrant registrant =
new DreamHomeControlsComplication.Registrant(mComplication,
- mDreamOverlayStateController, mControlsComponent, mMonitor);
+ mDreamOverlayStateController, mControlsComponent, mMonitor, false);
registrant.start();
setHaveFavorites(false);
@@ -171,7 +171,7 @@
public void complicationAvailability_serviceNotAvailable_haveFavorites_doNotAddComplication() {
final DreamHomeControlsComplication.Registrant registrant =
new DreamHomeControlsComplication.Registrant(mComplication,
- mDreamOverlayStateController, mControlsComponent, mMonitor);
+ mDreamOverlayStateController, mControlsComponent, mMonitor, false);
registrant.start();
setHaveFavorites(true);
@@ -184,7 +184,7 @@
public void complicationAvailability_serviceAvailable_haveFavorites_addComplication() {
final DreamHomeControlsComplication.Registrant registrant =
new DreamHomeControlsComplication.Registrant(mComplication,
- mDreamOverlayStateController, mControlsComponent, mMonitor);
+ mDreamOverlayStateController, mControlsComponent, mMonitor, false);
registrant.start();
setHaveFavorites(true);
@@ -197,7 +197,7 @@
public void complicationAvailability_checkAvailabilityWhenDreamOverlayBecomesActive() {
final DreamHomeControlsComplication.Registrant registrant =
new DreamHomeControlsComplication.Registrant(mComplication,
- mDreamOverlayStateController, mControlsComponent, mMonitor);
+ mDreamOverlayStateController, mControlsComponent, mMonitor, false);
registrant.start();
setServiceAvailable(true);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt
index 36bfa09..90ac05f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt
@@ -135,6 +135,7 @@
.thenReturn(FakeSharedPreferences())
},
userTracker = userTracker,
+ systemSettings = FakeSettings(),
broadcastDispatcher = fakeBroadcastDispatcher,
)
val remoteUserSelectionManager =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractorTest.kt
index 0bdf47a..f0ad510 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractorTest.kt
@@ -38,6 +38,7 @@
import com.android.systemui.shade.data.repository.shadeRepository
import com.android.systemui.shade.shared.model.ShadeMode
import com.android.systemui.testKosmos
+import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -48,6 +49,9 @@
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
+import org.mockito.Mockito.reset
+import org.mockito.Mockito.times
+import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
@ExperimentalCoroutinesApi
@@ -57,6 +61,7 @@
private val kosmos = testKosmos()
private val testScope = kosmos.testScope
private val underTest = kosmos.keyguardBlueprintInteractor
+ private val keyguardBlueprintRepository = kosmos.keyguardBlueprintRepository
private val clockRepository by lazy { kosmos.fakeKeyguardClockRepository }
private val configurationRepository by lazy { kosmos.fakeConfigurationRepository }
private val fingerprintPropertyRepository by lazy { kosmos.fakeFingerprintPropertyRepository }
@@ -103,20 +108,6 @@
}
@Test
- @DisableFlags(Flags.FLAG_COMPOSE_LOCKSCREEN)
- fun fingerprintPropertyInitialized_updatesBlueprint() {
- testScope.runTest {
- assertThat(kosmos.keyguardBlueprintRepository.targetTransitionConfig).isNull()
-
- fingerprintPropertyRepository.supportsUdfps() // initialize properties
-
- runCurrent()
- advanceUntilIdle()
- assertThat(kosmos.keyguardBlueprintRepository.targetTransitionConfig).isNotNull()
- }
- }
-
- @Test
@EnableFlags(Flags.FLAG_COMPOSE_LOCKSCREEN)
fun testDoesNotApplySplitShadeBlueprint() {
testScope.runTest {
@@ -132,15 +123,31 @@
}
@Test
+ @DisableFlags(Flags.FLAG_COMPOSE_LOCKSCREEN)
+ fun fingerprintPropertyInitialized_updatesBlueprint() {
+ testScope.runTest {
+ underTest.start()
+ reset(keyguardBlueprintRepository)
+
+ fingerprintPropertyRepository.supportsUdfps() // initialize properties
+
+ runCurrent()
+ advanceUntilIdle()
+ verify(keyguardBlueprintRepository, times(2)).refreshBlueprint(any())
+ }
+ }
+
+ @Test
fun testRefreshFromConfigChange() {
testScope.runTest {
- assertThat(kosmos.keyguardBlueprintRepository.targetTransitionConfig).isNull()
+ underTest.start()
+ reset(keyguardBlueprintRepository)
configurationRepository.onConfigurationChange()
runCurrent()
advanceUntilIdle()
- assertThat(kosmos.keyguardBlueprintRepository.targetTransitionConfig).isNotNull()
+ verify(keyguardBlueprintRepository, times(2)).refreshBlueprint(any())
}
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt
index 35659c1..14d9548 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt
@@ -281,6 +281,7 @@
.thenReturn(FakeSharedPreferences())
},
userTracker = userTracker,
+ systemSettings = FakeSettings(),
broadcastDispatcher = fakeBroadcastDispatcher,
)
val remoteUserSelectionManager =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorSceneContainerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorSceneContainerTest.kt
index ef3183a8..ced3526 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorSceneContainerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorSceneContainerTest.kt
@@ -281,6 +281,7 @@
.thenReturn(FakeSharedPreferences())
},
userTracker = userTracker,
+ systemSettings = FakeSettings(),
broadcastDispatcher = fakeBroadcastDispatcher,
)
val remoteUserSelectionManager =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprintTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprintTest.kt
index 616aac7..344e0fc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprintTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprintTest.kt
@@ -44,6 +44,7 @@
import com.android.systemui.keyguard.ui.view.layout.sections.SplitShadeGuidelines
import com.android.systemui.util.mockito.whenever
import java.util.Optional
+import kotlinx.coroutines.ExperimentalCoroutinesApi
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -55,6 +56,7 @@
@RunWith(AndroidTestingRunner::class)
@RunWithLooper(setAsMainLooper = true)
+@ExperimentalCoroutinesApi
@SmallTest
class DefaultKeyguardBlueprintTest : SysuiTestCase() {
private lateinit var underTest: DefaultKeyguardBlueprint
@@ -112,17 +114,66 @@
@Test
fun replaceViews_withPrevBlueprint() {
val prevBlueprint = mock(KeyguardBlueprint::class.java)
- val someSection = mock(KeyguardSection::class.java)
- whenever(prevBlueprint.sections)
- .thenReturn(underTest.sections.minus(mDefaultDeviceEntrySection).plus(someSection))
+ val removedSection = mock(KeyguardSection::class.java)
+ val addedSection = mDefaultDeviceEntrySection
+ val rebuildSection = clockSection
+ val prevSections = underTest.sections.minus(addedSection).plus(removedSection)
+ val unchangedSections = underTest.sections.subtract(listOf(addedSection, rebuildSection))
+ whenever(prevBlueprint.sections).thenReturn(prevSections)
+
val constraintLayout = ConstraintLayout(context, null)
underTest.replaceViews(constraintLayout, prevBlueprint)
- underTest.sections.minus(mDefaultDeviceEntrySection).forEach {
- verify(it, never())?.addViews(constraintLayout)
+
+ unchangedSections.forEach {
+ verify(it, never()).addViews(constraintLayout)
+ verify(it, never()).removeViews(constraintLayout)
}
- verify(mDefaultDeviceEntrySection).addViews(constraintLayout)
- verify(someSection).removeViews(constraintLayout)
+ verify(addedSection).addViews(constraintLayout)
+ verify(removedSection).removeViews(constraintLayout)
+ }
+
+ @Test
+ fun replaceViews_withPrevBlueprint_withRebuildTargets() {
+ val prevBlueprint = mock(KeyguardBlueprint::class.java)
+ val removedSection = mock(KeyguardSection::class.java)
+ val addedSection = mDefaultDeviceEntrySection
+ val rebuildSection = clockSection
+ val prevSections = underTest.sections.minus(addedSection).plus(removedSection)
+ val unchangedSections = underTest.sections.subtract(listOf(addedSection, rebuildSection))
+ whenever(prevBlueprint.sections).thenReturn(prevSections)
+
+ val constraintLayout = ConstraintLayout(context, null)
+ underTest.replaceViews(constraintLayout, prevBlueprint, listOf(rebuildSection))
+
+ unchangedSections.forEach {
+ verify(it, never()).addViews(constraintLayout)
+ verify(it, never()).removeViews(constraintLayout)
+ }
+
+ verify(addedSection).addViews(constraintLayout)
+ verify(rebuildSection).addViews(constraintLayout)
+ verify(rebuildSection).removeViews(constraintLayout)
+ verify(removedSection).removeViews(constraintLayout)
+ }
+
+ @Test
+ fun rebuildViews() {
+ val rebuildSections = listOf(mDefaultDeviceEntrySection, clockSection)
+ val unchangedSections = underTest.sections.subtract(rebuildSections)
+
+ val constraintLayout = ConstraintLayout(context, null)
+ underTest.rebuildViews(constraintLayout, rebuildSections)
+
+ unchangedSections.forEach {
+ verify(it, never()).addViews(constraintLayout)
+ verify(it, never()).removeViews(constraintLayout)
+ }
+
+ rebuildSections.forEach {
+ verify(it).addViews(constraintLayout)
+ verify(it).removeViews(constraintLayout)
+ }
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt
index 16421a0..bdc5fc3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt
@@ -178,6 +178,7 @@
.thenReturn(FakeSharedPreferences())
},
userTracker = userTracker,
+ systemSettings = FakeSettings(),
broadcastDispatcher = fakeBroadcastDispatcher,
)
val remoteUserSelectionManager =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt
index 83382207..8a12a90 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt
@@ -221,6 +221,7 @@
.thenReturn(FakeSharedPreferences())
},
userTracker = userTracker,
+ systemSettings = FakeSettings(),
broadcastDispatcher = fakeBroadcastDispatcher,
)
val remoteUserSelectionManager =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
index 62804ed1..cb9790b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
@@ -138,6 +138,7 @@
import com.android.systemui.settings.brightness.BrightnessSliderController;
import com.android.systemui.settings.brightness.domain.interactor.BrightnessMirrorShowingInteractor;
import com.android.systemui.shade.CameraLauncher;
+import com.android.systemui.shade.GlanceableHubContainerController;
import com.android.systemui.shade.NotificationPanelView;
import com.android.systemui.shade.NotificationPanelViewController;
import com.android.systemui.shade.NotificationShadeWindowViewController;
@@ -337,6 +338,7 @@
@Mock private KeyboardShortcuts mKeyboardShortcuts;
@Mock private KeyboardShortcutListSearch mKeyboardShortcutListSearch;
@Mock private PackageManager mPackageManager;
+ @Mock private GlanceableHubContainerController mGlanceableHubContainerController;
private ShadeController mShadeController;
private final FakeSystemClock mFakeSystemClock = new FakeSystemClock();
@@ -590,7 +592,8 @@
mUserTracker,
() -> mFingerprintManager,
mActivityStarter,
- mBrightnessMirrorShowingInteractor
+ mBrightnessMirrorShowingInteractor,
+ mGlanceableHubContainerController
);
mScreenLifecycle.addObserver(mCentralSurfaces.mScreenObserver);
mCentralSurfaces.initShadeVisibilityListener();
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/KeyguardBlueprintRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/KeyguardBlueprintRepositoryKosmos.kt
index a6b40df..fb12897 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/KeyguardBlueprintRepositoryKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/KeyguardBlueprintRepositoryKosmos.kt
@@ -23,12 +23,14 @@
import com.android.systemui.keyguard.ui.view.layout.blueprints.DefaultKeyguardBlueprint
import com.android.systemui.keyguard.ui.view.layout.blueprints.SplitShadeKeyguardBlueprint
import com.android.systemui.keyguard.ui.view.layout.sections.ClockSection
+import com.android.systemui.keyguard.ui.view.layout.sections.SmartspaceSection
import com.android.systemui.keyguard.ui.viewmodel.keyguardClockViewModel
import com.android.systemui.keyguard.ui.viewmodel.keyguardRootViewModel
import com.android.systemui.keyguard.ui.viewmodel.keyguardSmartspaceViewModel
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.util.mockito.mock
import java.util.Optional
+import org.mockito.Mockito.spy
val Kosmos.keyguardClockSection: ClockSection by
Kosmos.Fixture {
@@ -42,6 +44,9 @@
)
}
+val Kosmos.keyguardSmartspaceSection: SmartspaceSection by
+ Kosmos.Fixture { mock<SmartspaceSection>() }
+
val Kosmos.defaultKeyguardBlueprint by
Kosmos.Fixture {
DefaultKeyguardBlueprint(
@@ -57,7 +62,7 @@
aodBurnInSection = mock(),
communalTutorialIndicatorSection = mock(),
clockSection = keyguardClockSection,
- smartspaceSection = mock(),
+ smartspaceSection = keyguardSmartspaceSection,
keyguardSliceViewSection = mock(),
udfpsAccessibilityOverlaySection = mock(),
accessibilityActionsSection = mock(),
@@ -80,7 +85,7 @@
aodBurnInSection = mock(),
communalTutorialIndicatorSection = mock(),
clockSection = keyguardClockSection,
- smartspaceSection = mock(),
+ smartspaceSection = keyguardSmartspaceSection,
mediaSection = mock(),
accessibilityActionsSection = mock(),
)
@@ -88,13 +93,15 @@
val Kosmos.keyguardBlueprintRepository by
Kosmos.Fixture {
- KeyguardBlueprintRepository(
- blueprints =
- setOf(
- defaultKeyguardBlueprint,
- splitShadeBlueprint,
- ),
- handler = fakeExecutorHandler,
- assert = mock(),
+ spy(
+ KeyguardBlueprintRepository(
+ blueprints =
+ setOf(
+ defaultKeyguardBlueprint,
+ splitShadeBlueprint,
+ ),
+ handler = fakeExecutorHandler,
+ assert = mock(),
+ )
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractorKosmos.kt
index 5256ce4..4328ca1 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractorKosmos.kt
@@ -20,6 +20,8 @@
import com.android.systemui.biometrics.domain.interactor.fingerprintPropertyInteractor
import com.android.systemui.common.ui.domain.interactor.configurationInteractor
import com.android.systemui.keyguard.data.repository.keyguardBlueprintRepository
+import com.android.systemui.keyguard.data.repository.keyguardClockSection
+import com.android.systemui.keyguard.data.repository.keyguardSmartspaceSection
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.applicationCoroutineScope
import com.android.systemui.shade.domain.interactor.shadeInteractor
@@ -34,5 +36,7 @@
clockInteractor = keyguardClockInteractor,
configurationInteractor = configurationInteractor,
fingerprintPropertyInteractor = fingerprintPropertyInteractor,
+ clockSection = keyguardClockSection,
+ smartspaceSection = keyguardSmartspaceSection,
)
}
diff --git a/services/backup/java/com/android/server/backup/UserBackupManagerService.java b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
index dc1cfb9..ddccb37 100644
--- a/services/backup/java/com/android/server/backup/UserBackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
@@ -4008,20 +4008,10 @@
}
private PackageInfo getPackageInfoForBMMLogging(String packageName) {
- try {
- return mPackageManager.getPackageInfoAsUser(packageName, 0, mUserId);
- } catch (NameNotFoundException e) {
- Slog.w(
- TAG,
- addUserIdToLogMessage(
- mUserId, "Asked to get PackageInfo for BMM logging of nonexistent pkg "
- + packageName));
+ PackageInfo packageInfo = new PackageInfo();
+ packageInfo.packageName = packageName;
- PackageInfo packageInfo = new PackageInfo();
- packageInfo.packageName = packageName;
-
- return packageInfo;
- }
+ return packageInfo;
}
/** Hand off a restore session. */