diff options
57 files changed, 694 insertions, 358 deletions
diff --git a/core/java/android/appwidget/AppWidgetHostView.java b/core/java/android/appwidget/AppWidgetHostView.java index eb10f097f37f..7c2b1b72fbf1 100644 --- a/core/java/android/appwidget/AppWidgetHostView.java +++ b/core/java/android/appwidget/AppWidgetHostView.java @@ -626,7 +626,7 @@ public class AppWidgetHostView extends FrameLayout { // If our stale view has been prepared to match active, and the new // layout matches, try recycling it - if (layoutId == mLayoutId && mView != null) { + if (remoteViews.canRecycleView(mView)) { try { mLastExecutionSignal = remoteViews.reapplyAsync(mContext, mView, diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java index 0f309f10c2b7..2357d13c8d41 100644 --- a/core/java/android/widget/RemoteViews.java +++ b/core/java/android/widget/RemoteViews.java @@ -5795,7 +5795,7 @@ public class RemoteViews implements Parcelable, Filter { // across orientation change, and has the RemoteViews re-applied in the new orientation, // we throw an exception, since the layouts may be completely unrelated. if (hasMultipleLayouts()) { - if ((Integer) v.getTag(R.id.widget_frame) != rvToApply.getLayoutId()) { + if (!rvToApply.canRecycleView(v)) { throw new RuntimeException("Attempting to re-apply RemoteViews to a view that" + " that does not share the same root layout id."); } diff --git a/libs/WindowManager/Shell/res/values-land/styles.xml b/libs/WindowManager/Shell/res/values-land/styles.xml index 9eddac48e6de..0ed9368aa067 100644 --- a/libs/WindowManager/Shell/res/values-land/styles.xml +++ b/libs/WindowManager/Shell/res/values-land/styles.xml @@ -23,7 +23,7 @@ </style> <style name="DockedDividerHandle"> - <item name="android:layout_gravity">center_vertical</item> + <item name="android:layout_gravity">center</item> <item name="android:layout_width">48dp</item> <item name="android:layout_height">96dp</item> </style> diff --git a/libs/WindowManager/Shell/res/values/styles.xml b/libs/WindowManager/Shell/res/values/styles.xml index cb6d4de71a45..7733201d2465 100644 --- a/libs/WindowManager/Shell/res/values/styles.xml +++ b/libs/WindowManager/Shell/res/values/styles.xml @@ -43,7 +43,7 @@ </style> <style name="DockedDividerHandle"> - <item name="android:layout_gravity">center_horizontal</item> + <item name="android:layout_gravity">center</item> <item name="android:layout_width">96dp</item> <item name="android:layout_height">48dp</item> </style> diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java index 6ea806bd3799..4b125b118ceb 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java @@ -19,6 +19,8 @@ package com.android.wm.shell.common.split; import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; import static android.view.WindowManager.LayoutParams.FLAG_SLIPPERY; +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; import android.animation.ObjectAnimator; import android.content.Context; import android.graphics.Rect; @@ -57,6 +59,7 @@ public class DividerView extends FrameLayout implements View.OnTouchListener { private final int mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop(); private SplitLayout mSplitLayout; + private SplitWindowManager mSplitWindowManager; private SurfaceControlViewHost mViewHost; private DividerHandleView mHandle; private View mBackground; @@ -67,6 +70,7 @@ public class DividerView extends FrameLayout implements View.OnTouchListener { private int mStartPos; private GestureDetector mDoubleTapDetector; private boolean mInteractive; + private boolean mSetTouchRegion = true; /** * Tracks divider bar visible bounds in screen-based coordination. Used to calculate with @@ -93,6 +97,18 @@ public class DividerView extends FrameLayout implements View.OnTouchListener { } }; + private AnimatorListenerAdapter mAnimatorListener = new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + mSetTouchRegion = true; + } + + @Override + public void onAnimationCancel(Animator animation) { + mSetTouchRegion = true; + } + }; + public DividerView(@NonNull Context context) { super(context); } @@ -114,9 +130,11 @@ public class DividerView extends FrameLayout implements View.OnTouchListener { /** Sets up essential dependencies of the divider bar. */ public void setup( SplitLayout layout, + SplitWindowManager splitWindowManager, SurfaceControlViewHost viewHost, InsetsState insetsState) { mSplitLayout = layout; + mSplitWindowManager = splitWindowManager; mViewHost = viewHost; mDividerBounds.set(layout.getDividerBounds()); onInsetsChanged(insetsState, false /* animate */); @@ -138,9 +156,11 @@ public class DividerView extends FrameLayout implements View.OnTouchListener { DIVIDER_HEIGHT_PROPERTY, mDividerBounds.height(), mTempRect.height()); animator.setInterpolator(InsetsController.RESIZE_INTERPOLATOR); animator.setDuration(InsetsController.ANIMATION_DURATION_RESIZE); + animator.addListener(mAnimatorListener); animator.start(); } else { DIVIDER_HEIGHT_PROPERTY.set(this, mTempRect.height()); + mSetTouchRegion = true; } mDividerBounds.set(mTempRect); } @@ -162,6 +182,17 @@ public class DividerView extends FrameLayout implements View.OnTouchListener { } @Override + protected void onLayout(boolean changed, int left, int top, int right, int bottom) { + super.onLayout(changed, left, top, right, bottom); + if (mSetTouchRegion) { + mTempRect.set(mHandle.getLeft(), mHandle.getTop(), mHandle.getRight(), + mHandle.getBottom()); + mSplitWindowManager.setTouchRegion(mTempRect); + mSetTouchRegion = false; + } + } + + @Override public boolean onTouch(View v, MotionEvent event) { if (mSplitLayout == null || !mInteractive) { return false; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java index a9ed64c525eb..625bcee673b5 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java @@ -40,8 +40,10 @@ import android.content.res.Configuration; import android.content.res.Resources; import android.graphics.Point; import android.graphics.Rect; +import android.view.Display; import android.view.InsetsSourceControl; import android.view.InsetsState; +import android.view.RoundedCorner; import android.view.SurfaceControl; import android.view.WindowInsets; import android.view.WindowManager; @@ -53,6 +55,7 @@ import androidx.annotation.Nullable; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.policy.DividerSnapAlgorithm; import com.android.internal.policy.DockedDividerUtils; +import com.android.wm.shell.R; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.animation.Interpolators; import com.android.wm.shell.common.DisplayImeController; @@ -133,17 +136,32 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange mDismissingEffectPolicy = new DismissingEffectPolicy(applyDismissingParallax); final Resources resources = context.getResources(); - mDividerWindowWidth = resources.getDimensionPixelSize( - com.android.internal.R.dimen.docked_stack_divider_thickness); - mDividerInsets = resources.getDimensionPixelSize( - com.android.internal.R.dimen.docked_stack_divider_insets); - mDividerSize = mDividerWindowWidth - mDividerInsets * 2; + mDividerSize = resources.getDimensionPixelSize(R.dimen.split_divider_bar_width); + mDividerInsets = getDividerInsets(resources, context.getDisplay()); + mDividerWindowWidth = mDividerSize + 2 * mDividerInsets; mRootBounds.set(configuration.windowConfiguration.getBounds()); mDividerSnapAlgorithm = getSnapAlgorithm(mContext, mRootBounds); resetDividerPosition(); } + private int getDividerInsets(Resources resources, Display display) { + final int dividerInset = resources.getDimensionPixelSize( + com.android.internal.R.dimen.docked_stack_divider_insets); + + int radius = 0; + RoundedCorner corner = display.getRoundedCorner(RoundedCorner.POSITION_TOP_LEFT); + radius = corner != null ? Math.max(radius, corner.getRadius()) : radius; + corner = display.getRoundedCorner(RoundedCorner.POSITION_TOP_RIGHT); + radius = corner != null ? Math.max(radius, corner.getRadius()) : radius; + corner = display.getRoundedCorner(RoundedCorner.POSITION_BOTTOM_RIGHT); + radius = corner != null ? Math.max(radius, corner.getRadius()) : radius; + corner = display.getRoundedCorner(RoundedCorner.POSITION_BOTTOM_LEFT); + radius = corner != null ? Math.max(radius, corner.getRadius()) : radius; + + return Math.max(dividerInset, radius); + } + /** Gets bounds of the primary split. */ public Rect getBounds1() { return new Rect(mBounds1); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitWindowManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitWindowManager.java index 08754d328772..4903f9d46dc7 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitWindowManager.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitWindowManager.java @@ -31,7 +31,6 @@ import android.graphics.PixelFormat; import android.graphics.Rect; import android.graphics.Region; import android.os.Binder; -import android.os.IBinder; import android.view.IWindow; import android.view.InsetsState; import android.view.LayoutInflater; @@ -41,6 +40,7 @@ import android.view.SurfaceSession; import android.view.WindowManager; import android.view.WindowlessWindowManager; +import androidx.annotation.NonNull; import androidx.annotation.Nullable; import com.android.wm.shell.R; @@ -71,9 +71,10 @@ public final class SplitWindowManager extends WindowlessWindowManager { mWindowName = windowName; } - @Override - public void setTouchRegion(IBinder window, Region region) { - super.setTouchRegion(window, region); + void setTouchRegion(@NonNull Rect region) { + if (mViewHost != null) { + setTouchRegion(mViewHost.getWindowToken().asBinder(), new Region(region)); + } } @Override @@ -122,7 +123,7 @@ public final class SplitWindowManager extends WindowlessWindowManager { lp.setTitle(mWindowName); lp.privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION | PRIVATE_FLAG_TRUSTED_OVERLAY; mViewHost.setView(mDividerView, lp); - mDividerView.setup(splitLayout, mViewHost, insetsState); + mDividerView.setup(splitLayout, this, mViewHost, insetsState); } /** diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt index c07f0eb11510..c4be785cff19 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt @@ -78,7 +78,7 @@ fun FlickerTestParameter.appPairsPrimaryBoundsIsVisibleAtEnd( assertLayersEnd { val dividerRegion = layer(APP_PAIR_SPLIT_DIVIDER_COMPONENT).visibleRegion.region visibleRegion(primaryComponent) - .coversExactly(getPrimaryRegion(dividerRegion, rotation)) + .overlaps(getPrimaryRegion(dividerRegion, rotation)) } } @@ -89,7 +89,7 @@ fun FlickerTestParameter.dockedStackPrimaryBoundsIsVisibleAtEnd( assertLayersEnd { val dividerRegion = layer(DOCKED_STACK_DIVIDER_COMPONENT).visibleRegion.region visibleRegion(primaryComponent) - .coversExactly(getPrimaryRegion(dividerRegion, rotation)) + .overlaps(getPrimaryRegion(dividerRegion, rotation)) } } @@ -100,7 +100,7 @@ fun FlickerTestParameter.appPairsSecondaryBoundsIsVisibleAtEnd( assertLayersEnd { val dividerRegion = layer(APP_PAIR_SPLIT_DIVIDER_COMPONENT).visibleRegion.region visibleRegion(secondaryComponent) - .coversExactly(getSecondaryRegion(dividerRegion, rotation)) + .overlaps(getSecondaryRegion(dividerRegion, rotation)) } } @@ -111,7 +111,7 @@ fun FlickerTestParameter.dockedStackSecondaryBoundsIsVisibleAtEnd( assertLayersEnd { val dividerRegion = layer(DOCKED_STACK_DIVIDER_COMPONENT).visibleRegion.region visibleRegion(secondaryComponent) - .coversExactly(getSecondaryRegion(dividerRegion, rotation)) + .overlaps(getSecondaryRegion(dividerRegion, rotation)) } } diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt index c2b36089d0a7..669a054eaa2a 100644 --- a/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt +++ b/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt @@ -21,11 +21,16 @@ import android.content.Context import android.graphics.Color import android.os.Looper import android.util.Log +import android.view.GhostView import android.view.Gravity import android.view.View import android.view.ViewGroup -import android.view.ViewTreeObserver +import android.view.ViewTreeObserver.OnPreDrawListener +import android.view.WindowInsets import android.view.WindowManager +import android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR +import android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN +import android.view.WindowManagerPolicyConstants import android.widget.FrameLayout private const val TAG = "DialogLaunchAnimator" @@ -221,10 +226,12 @@ private class DialogLaunchAnimation( private var isDismissing = false private var dismissRequested = false - private var drawHostDialog = false var ignoreNextCallToHide = false var exitAnimationDisabled = false + private var isTouchSurfaceGhostDrawn = false + private var isOriginalDialogViewLaidOut = false + fun start() { // Show the host (fullscreen) dialog, to which we will add the stolen dialog view. hostDialog.show() @@ -252,19 +259,76 @@ private class DialogLaunchAnimation( WindowManager.LayoutParams.MATCH_PARENT ) - // Prevent the host dialog from drawing until the animation starts. - hostDialogRoot.viewTreeObserver.addOnPreDrawListener( - object : ViewTreeObserver.OnPreDrawListener { - override fun onPreDraw(): Boolean { - if (drawHostDialog) { - hostDialogRoot.viewTreeObserver.removeOnPreDrawListener(this) - return true - } + // If we are using gesture navigation, then we can overlay the navigation/task bars with + // the host dialog. + val navigationMode = context.resources.getInteger( + com.android.internal.R.integer.config_navBarInteractionMode) + if (navigationMode == WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL) { + window.attributes.fitInsetsTypes = window.attributes.fitInsetsTypes and + WindowInsets.Type.navigationBars().inv() + window.addFlags(FLAG_LAYOUT_IN_SCREEN or FLAG_LAYOUT_INSET_DECOR) + window.setDecorFitsSystemWindows(false) + } - return false - } + // Disable the dim. We will enable it once we start the animation. + window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND) + + // Add a temporary touch surface ghost as soon as the window is ready to draw. This + // temporary ghost will be drawn together with the touch surface, but in the host dialog + // window. Once it is drawn, we will make the touch surface invisible, and then start the + // animation. We do all this synchronization to avoid flicker that would occur if we made + // the touch surface invisible too early (before its ghost is drawn), leading to one or more + // frames with a hole instead of the touch surface (or its ghost). + hostDialogRoot.viewTreeObserver.addOnPreDrawListener(object : OnPreDrawListener { + override fun onPreDraw(): Boolean { + hostDialogRoot.viewTreeObserver.removeOnPreDrawListener(this) + addTemporaryTouchSurfaceGhost() + return true } - ) + }) + hostDialogRoot.invalidate() + } + + private fun addTemporaryTouchSurfaceGhost() { + // Create a ghost of the touch surface (which will make the touch surface invisible) and add + // it to the host dialog. We will wait for this ghost to be drawn before starting the + // animation. + val ghost = GhostView.addGhost(touchSurface, hostDialogRoot) + + // The ghost of the touch surface was just created, so the touch surface was made invisible. + // We make it visible again until the ghost is actually drawn. + touchSurface.visibility = View.VISIBLE + + // Wait for the ghost to be drawn before continuing. + ghost.viewTreeObserver.addOnPreDrawListener(object : OnPreDrawListener { + override fun onPreDraw(): Boolean { + ghost.viewTreeObserver.removeOnPreDrawListener(this) + onTouchSurfaceGhostDrawn() + return true + } + }) + ghost.invalidate() + } + + private fun onTouchSurfaceGhostDrawn() { + // Make the touch surface invisible and make sure that it stays invisible as long as the + // dialog is shown or animating. + touchSurface.visibility = View.INVISIBLE + if (touchSurface is LaunchableView) { + touchSurface.setShouldBlockVisibilityChanges(true) + } + + // Add a pre draw listener to (maybe) start the animation once the touch surface is + // actually invisible. + touchSurface.viewTreeObserver.addOnPreDrawListener(object : OnPreDrawListener { + override fun onPreDraw(): Boolean { + touchSurface.viewTreeObserver.removeOnPreDrawListener(this) + isTouchSurfaceGhostDrawn = true + maybeStartLaunchAnimation() + return true + } + }) + touchSurface.invalidate() } /** Get the content view of [originalDialog] and pass it to [then]. */ @@ -276,7 +340,7 @@ private class DialogLaunchAnimation( ?: throw IllegalStateException("Dialog does not have any android.R.id.content view") androidContent.viewTreeObserver.addOnPreDrawListener( - object : ViewTreeObserver.OnPreDrawListener { + object : OnPreDrawListener { override fun onPreDraw(): Boolean { if (androidContent.childCount == 1) { androidContent.viewTreeObserver.removeOnPreDrawListener(this) @@ -354,32 +418,47 @@ private class DialogLaunchAnimation( oldBottom: Int ) { dialogView.removeOnLayoutChangeListener(this) - startAnimation( - isLaunching = true, - onLaunchAnimationStart = { drawHostDialog = true }, - onLaunchAnimationEnd = { - touchSurface.setTag(R.id.launch_animation_running, null) - - // We hide the touch surface when the dialog is showing. We will make this - // view visible again when dismissing the dialog. - // TODO(b/193634619): Provide an easy way for views to check if they should - // be hidden because of a dialog launch so that they don't override this - // visibility when updating/refreshing itself. - touchSurface.visibility = View.INVISIBLE - - isLaunching = false - - // dismiss was called during the animation, dismiss again now to actually - // dismiss. - if (dismissRequested) { - hostDialog.dismiss() - } - } - ) + + isOriginalDialogViewLaidOut = true + maybeStartLaunchAnimation() } }) } + private fun maybeStartLaunchAnimation() { + if (!isTouchSurfaceGhostDrawn || !isOriginalDialogViewLaidOut) { + return + } + + // Show the background dim. + hostDialog.window.addFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND) + + startAnimation( + isLaunching = true, + onLaunchAnimationStart = { + // Remove the temporary ghost. Another ghost (that ghosts only the touch surface + // content, and not its background) will be added right after this and will be + // animated. + GhostView.removeGhost(touchSurface) + }, + onLaunchAnimationEnd = { + touchSurface.setTag(R.id.launch_animation_running, null) + + // We hide the touch surface when the dialog is showing. We will make this + // view visible again when dismissing the dialog. + touchSurface.visibility = View.INVISIBLE + + isLaunching = false + + // dismiss was called during the animation, dismiss again now to actually + // dismiss. + if (dismissRequested) { + hostDialog.dismiss() + } + } + ) + } + private fun onHostDialogDismissed(actualDismiss: () -> Unit) { if (Looper.myLooper() != Looper.getMainLooper()) { context.mainExecutor.execute { onHostDialogDismissed(actualDismiss) } @@ -417,6 +496,11 @@ private class DialogLaunchAnimation( if (!shouldAnimateDialogIntoView()) { Log.i(TAG, "Skipping animation of dialog into the touch surface") + // Make sure we allow the touch surface to change its visibility again. + if (touchSurface is LaunchableView) { + touchSurface.setShouldBlockVisibilityChanges(false) + } + // If the view is invisible it's probably because of us, so we make it visible again. if (touchSurface.visibility == View.INVISIBLE) { touchSurface.visibility = View.VISIBLE @@ -434,10 +518,33 @@ private class DialogLaunchAnimation( hostDialog.window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND) }, onLaunchAnimationEnd = { + // Make sure we allow the touch surface to change its visibility again. + if (touchSurface is LaunchableView) { + touchSurface.setShouldBlockVisibilityChanges(false) + } + touchSurface.visibility = View.VISIBLE originalDialogView!!.visibility = View.INVISIBLE - dismissDialogs(true /* instantDismiss */) - onDialogDismissed(this@DialogLaunchAnimation) + + // The animated ghost was just removed. We create a temporary ghost that will be + // removed only once we draw the touch surface, to avoid flickering that would + // happen when removing the ghost too early (before the touch surface is drawn). + GhostView.addGhost(touchSurface, hostDialogRoot) + + touchSurface.viewTreeObserver.addOnPreDrawListener(object : OnPreDrawListener { + override fun onPreDraw(): Boolean { + touchSurface.viewTreeObserver.removeOnPreDrawListener(this) + + // Now that the touch surface was drawn, we can remove the temporary ghost + // and instantly dismiss the dialog. + GhostView.removeGhost(touchSurface) + dismissDialogs(true /* instantDismiss */) + onDialogDismissed(this@DialogLaunchAnimation) + + return true + } + }) + touchSurface.invalidate() } ) } @@ -472,10 +579,13 @@ private class DialogLaunchAnimation( } override fun onLaunchAnimationStart(isExpandingFullyAbove: Boolean) { + // During launch, onLaunchAnimationStart will be used to remove the temporary touch + // surface ghost so it is important to call this before calling + // onLaunchAnimationStart on the controller (which will create its own ghost). + onLaunchAnimationStart() + startViewController.onLaunchAnimationStart(isExpandingFullyAbove) endViewController.onLaunchAnimationStart(isExpandingFullyAbove) - - onLaunchAnimationStart() } override fun onLaunchAnimationEnd(isExpandingFullyAbove: Boolean) { diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/LaunchableView.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/LaunchableView.kt new file mode 100644 index 000000000000..80a3eb839940 --- /dev/null +++ b/packages/SystemUI/animation/src/com/android/systemui/animation/LaunchableView.kt @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2021 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.animation + +/** A view that can expand/launch into an app or a dialog. */ +interface LaunchableView { + /** + * Set whether this view should block/prevent all visibility changes. This ensures that this + * view remains invisible during the launch animation given that it is ghosted and already drawn + * somewhere else. + * + * Note that when this is set to true, both the [normal][android.view.View.setVisibility] and + * [transition][android.view.View.setTransitionVisibility] visibility changes must be blocked. + */ + fun setShouldBlockVisibilityChanges(block: Boolean) +}
\ No newline at end of file diff --git a/packages/SystemUI/res/drawable/internet_dialog_background.xml b/packages/SystemUI/res/drawable/internet_dialog_background.xml deleted file mode 100644 index 3ceb0f6ac06a..000000000000 --- a/packages/SystemUI/res/drawable/internet_dialog_background.xml +++ /dev/null @@ -1,23 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - ~ Copyright (C) 2020 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. - --> - -<inset xmlns:android="http://schemas.android.com/apk/res/android"> - <shape android:shape="rectangle"> - <corners android:radius="8dp" /> - <solid android:color="?android:attr/colorBackground" /> - </shape> -</inset> diff --git a/packages/SystemUI/res/drawable/internet_dialog_rounded_top_corner_background.xml b/packages/SystemUI/res/drawable/internet_dialog_rounded_top_corner_background.xml deleted file mode 100644 index 14672ef3dcfe..000000000000 --- a/packages/SystemUI/res/drawable/internet_dialog_rounded_top_corner_background.xml +++ /dev/null @@ -1,22 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2021 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. ---> -<inset xmlns:android="http://schemas.android.com/apk/res/android"> - <shape android:shape="rectangle"> - <corners - android:topLeftRadius="@dimen/internet_dialog_corner_radius" - android:topRightRadius="@dimen/internet_dialog_corner_radius" - android:bottomLeftRadius="@dimen/internet_dialog_corner_radius" - android:bottomRightRadius="@dimen/internet_dialog_corner_radius"/> - <solid android:color="?android:attr/colorBackground" /> - </shape> -</inset> diff --git a/packages/SystemUI/res/layout/internet_connectivity_dialog.xml b/packages/SystemUI/res/layout/internet_connectivity_dialog.xml index 79ac737ba304..f4faa62430db 100644 --- a/packages/SystemUI/res/layout/internet_connectivity_dialog.xml +++ b/packages/SystemUI/res/layout/internet_connectivity_dialog.xml @@ -20,8 +20,7 @@ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android" android:id="@+id/internet_connectivity_dialog" android:layout_width="@dimen/large_dialog_width" - android:layout_height="@dimen/internet_dialog_list_max_height" - android:background="@drawable/internet_dialog_rounded_top_corner_background" + android:layout_height="wrap_content" android:orientation="vertical"> <LinearLayout diff --git a/packages/SystemUI/res/values-land/styles.xml b/packages/SystemUI/res/values-land/styles.xml index 82cba58e48fa..f3d83645a8e0 100644 --- a/packages/SystemUI/res/values-land/styles.xml +++ b/packages/SystemUI/res/values-land/styles.xml @@ -25,12 +25,6 @@ <item name="android:layout_gravity">center_horizontal</item> </style> - <style name="DockedDividerHandle"> - <item name="android:layout_gravity">center_vertical</item> - <item name="android:layout_width">48dp</item> - <item name="android:layout_height">96dp</item> - </style> - <style name="DockedDividerMinimizedShadow"> <item name="android:layout_width">8dp</item> <item name="android:layout_height">match_parent</item> diff --git a/packages/SystemUI/res/values-sw600dp/dimens.xml b/packages/SystemUI/res/values-sw600dp/dimens.xml index 85f8f0957a08..4e578615f30f 100644 --- a/packages/SystemUI/res/values-sw600dp/dimens.xml +++ b/packages/SystemUI/res/values-sw600dp/dimens.xml @@ -105,5 +105,5 @@ <dimen name="qs_detail_margin_top">0dp</dimen> <!-- The width of large/content heavy dialogs (e.g. Internet, Media output, etc) --> - <dimen name="large_dialog_width">624dp</dimen> + <dimen name="large_dialog_width">504dp</dimen> </resources> diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index 7293f3148ae4..db6985d2b61f 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -1602,7 +1602,6 @@ <!-- Internet panel related dimensions --> <dimen name="internet_dialog_list_margin">12dp</dimen> - <dimen name="internet_dialog_list_max_height">662dp</dimen> <!-- The width of large/content heavy dialogs (e.g. Internet, Media output, etc) --> <dimen name="large_dialog_width">@dimen/match_parent</dimen> diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml index ff299eae8cf2..9bdd572a8eba 100644 --- a/packages/SystemUI/res/values/styles.xml +++ b/packages/SystemUI/res/values/styles.xml @@ -956,10 +956,6 @@ <item name="android:textColor">?android:attr/textColorPrimary</item> </style> - <style name="Theme.SystemUI.Dialog.Internet"> - <item name="android:windowBackground">@drawable/internet_dialog_background</item> - </style> - <style name="MainSwitch.Settingslib" parent="@android:style/Theme.DeviceDefault"> <item name="android:switchMinWidth">@dimen/settingslib_min_switch_width</item> </style> diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java index 6895ef10fd07..26ce645eefc5 100644 --- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java +++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java @@ -104,8 +104,6 @@ public abstract class MediaOutputBaseDialog extends SystemUIDialog implements lp.setFitInsetsIgnoringVisibility(true); window.setAttributes(lp); window.setContentView(mDialogView); - window.setLayout(mContext.getResources().getDimensionPixelSize(R.dimen.large_dialog_width), - ViewGroup.LayoutParams.WRAP_CONTENT); mHeaderTitle = mDialogView.requireViewById(R.id.header_title); mHeaderSubtitle = mDialogView.requireViewById(R.id.header_subtitle); diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/KeyButtonRipple.java b/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/KeyButtonRipple.java index e3e2367aeef8..00124ac01cc4 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/KeyButtonRipple.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/KeyButtonRipple.java @@ -37,6 +37,8 @@ import android.view.animation.Interpolator; import com.android.systemui.R; import com.android.systemui.animation.Interpolators; +import androidx.annotation.Keep; + import java.util.ArrayList; import java.util.HashSet; @@ -184,19 +186,27 @@ public class KeyButtonRipple extends Drawable { } } + /** Gets the glow alpha, used by {@link android.animation.ObjectAnimator} via reflection. */ + @Keep public float getGlowAlpha() { return mGlowAlpha; } + /** Sets the glow alpha, used by {@link android.animation.ObjectAnimator} via reflection. */ + @Keep public void setGlowAlpha(float x) { mGlowAlpha = x; invalidateSelf(); } + /** Gets the glow scale, used by {@link android.animation.ObjectAnimator} via reflection. */ + @Keep public float getGlowScale() { return mGlowScale; } + /** Sets the glow scale, used by {@link android.animation.ObjectAnimator} via reflection. */ + @Keep public void setGlowScale(float x) { mGlowScale = x; invalidateSelf(); diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialog.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialog.kt index 9e8f6b82c182..23482677038c 100644 --- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialog.kt +++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialog.kt @@ -24,7 +24,6 @@ import android.view.Gravity import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import android.view.ViewGroup.LayoutParams.WRAP_CONTENT import android.view.WindowInsets import android.widget.ImageView import android.widget.TextView @@ -65,7 +64,6 @@ class PrivacyDialog( window?.apply { attributes.fitInsetsTypes = attributes.fitInsetsTypes or WindowInsets.Type.statusBars() attributes.receiveInsetsIgnoringZOrder = true - setLayout(context.resources.getDimensionPixelSize(R.dimen.qs_panel_width), WRAP_CONTENT) setGravity(Gravity.TOP or Gravity.CENTER_HORIZONTAL) } diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java index 2665f3ad98b3..71eb4a2e6cbb 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java @@ -41,7 +41,7 @@ import com.android.internal.widget.RemeasuringLinearLayout; import com.android.systemui.R; import com.android.systemui.plugins.qs.DetailAdapter; import com.android.systemui.plugins.qs.QSTile; -import com.android.systemui.settings.brightness.BrightnessSlider; +import com.android.systemui.settings.brightness.BrightnessSliderController; import com.android.systemui.statusbar.policy.BrightnessMirrorController; import com.android.systemui.tuner.TunerService; import com.android.systemui.tuner.TunerService.Tunable; @@ -69,7 +69,7 @@ public class QSPanel extends LinearLayout implements Tunable { @Nullable protected View mBrightnessView; @Nullable - protected BrightnessSlider mToggleSliderController; + protected BrightnessSliderController mToggleSliderController; private final H mHandler = new H(); /** Whether or not the QS media player feature is enabled. */ diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java index 70892a7047c0..6794d5b0cee4 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java @@ -41,7 +41,7 @@ import com.android.systemui.qs.dagger.QSScope; import com.android.systemui.qs.logging.QSLogger; import com.android.systemui.settings.brightness.BrightnessController; import com.android.systemui.settings.brightness.BrightnessMirrorHandler; -import com.android.systemui.settings.brightness.BrightnessSlider; +import com.android.systemui.settings.brightness.BrightnessSliderController; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.policy.BrightnessMirrorController; import com.android.systemui.tuner.TunerService; @@ -63,7 +63,7 @@ public class QSPanelController extends QSPanelControllerBase<QSPanel> { private final FalsingManager mFalsingManager; private final CommandQueue mCommandQueue; private final BrightnessController mBrightnessController; - private final BrightnessSlider mBrightnessSlider; + private final BrightnessSliderController mBrightnessSliderController; private final BrightnessMirrorHandler mBrightnessMirrorHandler; private boolean mGridContentVisible = true; @@ -99,8 +99,8 @@ public class QSPanelController extends QSPanelControllerBase<QSPanel> { QSTileRevealController.Factory qsTileRevealControllerFactory, DumpManager dumpManager, MetricsLogger metricsLogger, UiEventLogger uiEventLogger, QSLogger qsLogger, BrightnessController.Factory brightnessControllerFactory, - BrightnessSlider.Factory brightnessSliderFactory, FalsingManager falsingManager, - CommandQueue commandQueue) { + BrightnessSliderController.Factory brightnessSliderFactory, + FalsingManager falsingManager, CommandQueue commandQueue) { super(view, qstileHost, qsCustomizerController, usingMediaPlayer, mediaHost, metricsLogger, uiEventLogger, qsLogger, dumpManager); mQsSecurityFooter = qsSecurityFooter; @@ -111,10 +111,10 @@ public class QSPanelController extends QSPanelControllerBase<QSPanel> { mCommandQueue = commandQueue; mQsSecurityFooter.setHostEnvironment(qstileHost); - mBrightnessSlider = brightnessSliderFactory.create(getContext(), mView); - mView.setBrightnessView(mBrightnessSlider.getRootView()); + mBrightnessSliderController = brightnessSliderFactory.create(getContext(), mView); + mView.setBrightnessView(mBrightnessSliderController.getRootView()); - mBrightnessController = brightnessControllerFactory.create(mBrightnessSlider); + mBrightnessController = brightnessControllerFactory.create(mBrightnessSliderController); mBrightnessMirrorHandler = new BrightnessMirrorHandler(mBrightnessController); } @@ -125,7 +125,7 @@ public class QSPanelController extends QSPanelControllerBase<QSPanel> { mMediaHost.setShowsOnlyActiveMedia(false); mMediaHost.init(MediaHierarchyManager.LOCATION_QS); mQsCustomizerController.init(); - mBrightnessSlider.init(); + mBrightnessSliderController.init(); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSBrightnessController.kt b/packages/SystemUI/src/com/android/systemui/qs/QuickQSBrightnessController.kt index 14374ffe9f89..65889d792769 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSBrightnessController.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSBrightnessController.kt @@ -18,7 +18,7 @@ package com.android.systemui.qs import androidx.annotation.VisibleForTesting import com.android.systemui.settings.brightness.BrightnessController -import com.android.systemui.settings.brightness.BrightnessSlider +import com.android.systemui.settings.brightness.BrightnessSliderController import com.android.systemui.settings.brightness.MirroredBrightnessController import com.android.systemui.statusbar.policy.BrightnessMirrorController import javax.inject.Inject @@ -33,10 +33,11 @@ class QuickQSBrightnessController @VisibleForTesting constructor( @Inject constructor( brightnessControllerFactory: BrightnessController.Factory, - brightnessSliderFactory: BrightnessSlider.Factory, + brightnessSliderControllerFactory: BrightnessSliderController.Factory, quickQSPanel: QuickQSPanel ) : this(brightnessControllerFactory = { - val slider = brightnessSliderFactory.create(quickQSPanel.context, quickQSPanel) + val slider = brightnessSliderControllerFactory.create(quickQSPanel.context, + quickQSPanel) slider.init() quickQSPanel.setBrightnessView(slider.rootView) brightnessControllerFactory.create(slider) diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt index 36101c9e2587..69be33261995 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt @@ -42,6 +42,7 @@ import androidx.annotation.VisibleForTesting import com.android.settingslib.Utils import com.android.systemui.FontSizeUtils import com.android.systemui.R +import com.android.systemui.animation.LaunchableView import com.android.systemui.plugins.qs.QSIconView import com.android.systemui.plugins.qs.QSTile import com.android.systemui.plugins.qs.QSTile.BooleanState @@ -54,7 +55,7 @@ open class QSTileViewImpl @JvmOverloads constructor( context: Context, private val _icon: QSIconView, private val collapsed: Boolean = false -) : QSTileView(context), HeightOverrideable { +) : QSTileView(context), HeightOverrideable, LaunchableView { companion object { private const val INVALID = -1 @@ -130,6 +131,8 @@ open class QSTileViewImpl @JvmOverloads constructor( private var lastStateDescription: CharSequence? = null private var tileState = false private var lastState = INVALID + private var blockVisibilityChanges = false + private var lastVisibility = View.VISIBLE private val locInScreen = IntArray(2) @@ -319,6 +322,36 @@ open class QSTileViewImpl @JvmOverloads constructor( return sideView } + override fun setShouldBlockVisibilityChanges(block: Boolean) { + blockVisibilityChanges = block + + if (block) { + lastVisibility = visibility + } else { + visibility = lastVisibility + } + } + + override fun setVisibility(visibility: Int) { + if (blockVisibilityChanges) { + lastVisibility = visibility + return + } + + super.setVisibility(visibility) + } + + override fun setTransitionVisibility(visibility: Int) { + if (blockVisibilityChanges) { + // View.setTransitionVisibility just sets the visibility flag, so we don't have to save + // the transition visibility separately from the normal visibility. + lastVisibility = visibility + return + } + + super.setTransitionVisibility(visibility) + } + // Accessibility override fun onInitializeAccessibilityEvent(event: AccessibilityEvent) { @@ -484,7 +517,7 @@ open class QSTileViewImpl @JvmOverloads constructor( } private fun setColor(color: Int) { - colorBackgroundDrawable.setTint(color) + colorBackgroundDrawable.mutate().setTint(color) paintColor = color } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java index 98d0a72685ba..23b2a7642e36 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java @@ -124,7 +124,7 @@ public class InternetTile extends QSTileImpl<SignalState> { protected void handleClick(@Nullable View view) { mHandler.post(() -> mInternetDialogFactory.create(true, mAccessPointController.canConfigMobileData(), - mAccessPointController.canConfigWifi())); + mAccessPointController.canConfigWifi(), view)); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java index 58e899285e3b..204dd46189d8 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java @@ -15,14 +15,10 @@ */ package com.android.systemui.qs.tiles.dialog; -import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS; - import static com.android.systemui.Prefs.Key.QS_HAS_TURNED_OFF_MOBILE_DATA; import android.app.AlertDialog; import android.content.Context; -import android.graphics.Color; -import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.net.Network; import android.net.NetworkCapabilities; @@ -41,10 +37,7 @@ import android.util.Log; import android.view.Gravity; import android.view.LayoutInflater; import android.view.View; -import android.view.ViewGroup; -import android.view.ViewTreeObserver; import android.view.Window; -import android.view.WindowInsets; import android.view.WindowManager; import android.widget.FrameLayout; import android.widget.ImageView; @@ -130,7 +123,6 @@ public class InternetDialog extends SystemUIDialog implements private Switch mWiFiToggle; private FrameLayout mDoneLayout; private Drawable mBackgroundOn; - private int mListMaxHeight; private int mDefaultDataSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID; private boolean mCanConfigMobileData; @@ -149,20 +141,11 @@ public class InternetDialog extends SystemUIDialog implements mInternetDialogSubTitle.setText(getSubtitleText()); }; - private final ViewTreeObserver.OnGlobalLayoutListener mInternetListLayoutListener = () -> { - // Set max height for list - if (mInternetDialogLayout.getHeight() > mListMaxHeight) { - ViewGroup.LayoutParams params = mInternetDialogLayout.getLayoutParams(); - params.height = mListMaxHeight; - mInternetDialogLayout.setLayoutParams(params); - } - }; - public InternetDialog(Context context, InternetDialogFactory internetDialogFactory, InternetDialogController internetDialogController, boolean canConfigMobileData, boolean canConfigWifi, boolean aboveStatusBar, UiEventLogger uiEventLogger, @Main Handler handler, @Background Executor executor) { - super(context, R.style.Theme_SystemUI_Dialog_Internet); + super(context); if (DEBUG) { Log.d(TAG, "Init InternetDialog"); } @@ -184,8 +167,6 @@ public class InternetDialog extends SystemUIDialog implements return false; } }; - mListMaxHeight = context.getResources().getDimensionPixelSize( - R.dimen.internet_dialog_list_max_height); mUiEventLogger = uiEventLogger; mAdapter = new InternetAdapter(mInternetDialogController); if (!aboveStatusBar) { @@ -203,21 +184,9 @@ public class InternetDialog extends SystemUIDialog implements mDialogView = LayoutInflater.from(mContext).inflate(R.layout.internet_connectivity_dialog, null); final Window window = getWindow(); - final WindowManager.LayoutParams layoutParams = window.getAttributes(); - layoutParams.gravity = Gravity.BOTTOM; - // Move down the dialog to overlay the navigation bar. - layoutParams.setFitInsetsTypes( - layoutParams.getFitInsetsTypes() & ~WindowInsets.Type.navigationBars()); - layoutParams.setFitInsetsSides(WindowInsets.Side.all()); - layoutParams.setFitInsetsIgnoringVisibility(true); - window.setAttributes(layoutParams); window.setContentView(mDialogView); - //Only fix the width for large screen or tablet. - window.setLayout(mContext.getResources().getDimensionPixelSize( - R.dimen.large_dialog_width), ViewGroup.LayoutParams.WRAP_CONTENT); + window.setWindowAnimations(R.style.Animation_InternetDialog); - window.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT)); - window.addFlags(FLAG_LAYOUT_NO_LIMITS); mInternetDialogLayout = mDialogView.requireViewById(R.id.internet_connectivity_dialog); mInternetDialogTitle = mDialogView.requireViewById(R.id.internet_dialog_title); @@ -244,8 +213,6 @@ public class InternetDialog extends SystemUIDialog implements mMobileDataToggle = mDialogView.requireViewById(R.id.mobile_toggle); mWiFiToggle = mDialogView.requireViewById(R.id.wifi_toggle); mBackgroundOn = mContext.getDrawable(R.drawable.settingslib_switch_bar_bg_on); - mInternetDialogLayout.getViewTreeObserver().addOnGlobalLayoutListener( - mInternetListLayoutListener); mInternetDialogTitle.setText(getDialogTitleText()); mInternetDialogTitle.setGravity(Gravity.START | Gravity.CENTER_VERTICAL); diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java index 40590a76536e..5673136e1828 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java @@ -71,6 +71,7 @@ import com.android.settingslib.mobile.TelephonyIcons; import com.android.settingslib.net.SignalStrengthUtil; import com.android.settingslib.wifi.WifiUtils; import com.android.systemui.R; +import com.android.systemui.animation.DialogLaunchAnimator; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.dagger.qualifiers.Main; @@ -152,6 +153,7 @@ public class InternetDialogController implements WifiEntry.DisconnectCallback, private ToastFactory mToastFactory; private SignalDrawable mSignalDrawable; private LocationController mLocationController; + private DialogLaunchAnimator mDialogLaunchAnimator; @VisibleForTesting static final float TOAST_PARAMS_HORIZONTAL_WEIGHT = 1.0f; @@ -202,7 +204,8 @@ public class InternetDialogController implements WifiEntry.DisconnectCallback, WindowManager windowManager, ToastFactory toastFactory, @Background Handler workerHandler, CarrierConfigTracker carrierConfigTracker, - LocationController locationController) { + LocationController locationController, + DialogLaunchAnimator dialogLaunchAnimator) { if (DEBUG) { Log.d(TAG, "Init InternetDialogController"); } @@ -231,6 +234,7 @@ public class InternetDialogController implements WifiEntry.DisconnectCallback, mToastFactory = toastFactory; mSignalDrawable = new SignalDrawable(mContext); mLocationController = locationController; + mDialogLaunchAnimator = dialogLaunchAnimator; } void onStart(@NonNull InternetDialogCallback callback, boolean canConfigWifi) { @@ -596,20 +600,32 @@ public class InternetDialogController implements WifiEntry.DisconnectCallback, } void launchNetworkSetting() { + // Dismissing a dialog into its touch surface and starting an activity at the same time + // looks bad, so let's make sure the dialog just fades out quickly. + mDialogLaunchAnimator.disableAllCurrentDialogsExitAnimations(); mCallback.dismissDialog(); + mActivityStarter.postStartActivityDismissingKeyguard(getSettingsIntent(), 0); } void launchWifiNetworkDetailsSetting(String key) { Intent intent = getWifiDetailsSettingsIntent(key); if (intent != null) { + // Dismissing a dialog into its touch surface and starting an activity at the same time + // looks bad, so let's make sure the dialog just fades out quickly. + mDialogLaunchAnimator.disableAllCurrentDialogsExitAnimations(); mCallback.dismissDialog(); + mActivityStarter.postStartActivityDismissingKeyguard(intent, 0); } } void launchWifiScanningSetting() { + // Dismissing a dialog into its touch surface and starting an activity at the same time + // looks bad, so let's make sure the dialog just fades out quickly. + mDialogLaunchAnimator.disableAllCurrentDialogsExitAnimations(); mCallback.dismissDialog(); + final Intent intent = new Intent(ACTION_WIFI_SCANNING_SETTINGS); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); mActivityStarter.postStartActivityDismissingKeyguard(intent, 0); diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogFactory.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogFactory.kt index ea5df17bca58..93828b3bcc99 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogFactory.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogFactory.kt @@ -18,9 +18,11 @@ package com.android.systemui.qs.tiles.dialog import android.content.Context import android.os.Handler import android.util.Log +import android.view.View import com.android.internal.logging.UiEventLogger +import com.android.systemui.animation.DialogLaunchAnimator import com.android.systemui.dagger.SysUISingleton -import com.android.systemui.dagger.qualifiers.Background; +import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.dagger.qualifiers.Main import java.util.concurrent.Executor import javax.inject.Inject @@ -37,14 +39,20 @@ class InternetDialogFactory @Inject constructor( @Background private val executor: Executor, private val internetDialogController: InternetDialogController, private val context: Context, - private val uiEventLogger: UiEventLogger + private val uiEventLogger: UiEventLogger, + private val dialogLaunchAnimator: DialogLaunchAnimator ) { companion object { var internetDialog: InternetDialog? = null } - /** Creates a [InternetDialog]. */ - fun create(aboveStatusBar: Boolean, canConfigMobileData: Boolean, canConfigWifi: Boolean) { + /** Creates a [InternetDialog]. The dialog will be animated from [view] if it is not null. */ + fun create( + aboveStatusBar: Boolean, + canConfigMobileData: Boolean, + canConfigWifi: Boolean, + view: View? + ) { if (internetDialog != null) { if (DEBUG) { Log.d(TAG, "InternetDialog is showing, do not create it twice.") @@ -54,7 +62,11 @@ class InternetDialogFactory @Inject constructor( internetDialog = InternetDialog(context, this, internetDialogController, canConfigMobileData, canConfigWifi, aboveStatusBar, uiEventLogger, handler, executor) - internetDialog?.show() + if (view != null) { + dialogLaunchAnimator.showFromView(internetDialog!!, view) + } else { + internetDialog?.show() + } } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/user/UserDialog.kt b/packages/SystemUI/src/com/android/systemui/qs/user/UserDialog.kt index 01afa56fc496..26d1bbde2a54 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/user/UserDialog.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/user/UserDialog.kt @@ -71,10 +71,6 @@ class UserDialog( setType(WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL) attributes.fitInsetsTypes = attributes.fitInsetsTypes or WindowInsets.Type.statusBars() attributes.receiveInsetsIgnoringZOrder = true - setLayout( - context.resources.getDimensionPixelSize(R.dimen.notification_panel_width), - ViewGroup.LayoutParams.WRAP_CONTENT - ) setGravity(Gravity.CENTER) } setContentView(R.layout.qs_user_dialog_content) diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessController.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessController.java index acc6ee130539..d7d1de00c82d 100644 --- a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessController.java +++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessController.java @@ -51,8 +51,6 @@ import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.settings.CurrentUserTracker; import com.android.systemui.statusbar.policy.BrightnessMirrorController; -import java.util.ArrayList; - import javax.inject.Inject; public class BrightnessController implements ToggleSlider.Listener, MirroredBrightnessController { @@ -92,13 +90,9 @@ public class BrightnessController implements ToggleSlider.Listener, MirroredBrig @Override public void onDisplayChanged(int displayId) { mBackgroundHandler.post(mUpdateSliderRunnable); - notifyCallbacks(); } }; - private ArrayList<BrightnessStateChangeCallback> mChangeCallbacks = - new ArrayList<BrightnessStateChangeCallback>(); - private volatile boolean mAutomatic; // Brightness adjusted automatically using ambient light. private volatile boolean mIsVrModeEnabled; private boolean mListening; @@ -114,11 +108,6 @@ public class BrightnessController implements ToggleSlider.Listener, MirroredBrig mControl.setMirrorControllerAndMirror(controller); } - public interface BrightnessStateChangeCallback { - /** Indicates that some of the brightness settings have changed */ - void onBrightnessLevelChanged(); - } - /** ContentObserver to watch brightness */ private class BrightnessObserver extends ContentObserver { @@ -139,7 +128,6 @@ public class BrightnessController implements ToggleSlider.Listener, MirroredBrig mBackgroundHandler.post(mUpdateModeRunnable); mBackgroundHandler.post(mUpdateSliderRunnable); } - notifyCallbacks(); } public void startObserving() { @@ -317,14 +305,6 @@ public class BrightnessController implements ToggleSlider.Listener, MirroredBrig Context.VR_SERVICE)); } - public void addStateChangedCallback(BrightnessStateChangeCallback cb) { - mChangeCallbacks.add(cb); - } - - public boolean removeStateChangedCallback(BrightnessStateChangeCallback cb) { - return mChangeCallbacks.remove(cb); - } - public void registerCallbacks() { mBackgroundHandler.post(mStartListeningRunnable); } @@ -375,10 +355,6 @@ public class BrightnessController implements ToggleSlider.Listener, MirroredBrig } }); } - - for (BrightnessStateChangeCallback cb : mChangeCallbacks) { - cb.onBrightnessLevelChanged(); - } } public void checkRestrictionAndSetEnabled() { @@ -435,8 +411,12 @@ public class BrightnessController implements ToggleSlider.Listener, MirroredBrig } private void animateSliderTo(int target) { - if (!mControlValueInitialized) { + if (!mControlValueInitialized || !mControl.isVisible()) { // Don't animate the first value since its default state isn't meaningful to users. + // We also don't want to animate slider if it's not visible - especially important when + // two sliders are active at the same time in split shade (one in QS and one in QQS), + // as this negatively affects transition between them and they share mirror slider - + // animating it from two different sources causes janky motion mControl.setValue(target); mControlValueInitialized = true; } @@ -455,13 +435,6 @@ public class BrightnessController implements ToggleSlider.Listener, MirroredBrig mSliderAnimator.start(); } - private void notifyCallbacks() { - final int size = mChangeCallbacks.size(); - for (int i = 0; i < size; i++) { - mChangeCallbacks.get(i).onBrightnessLevelChanged(); - } - } - /** Factory for creating a {@link BrightnessController}. */ public static class Factory { private final Context mContext; diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessDialog.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessDialog.java index 8fc831a7ce4d..c9c1a9b55c3f 100644 --- a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessDialog.java +++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessDialog.java @@ -41,14 +41,14 @@ import javax.inject.Inject; public class BrightnessDialog extends Activity { private BrightnessController mBrightnessController; - private final BrightnessSlider.Factory mToggleSliderFactory; + private final BrightnessSliderController.Factory mToggleSliderFactory; private final BroadcastDispatcher mBroadcastDispatcher; private final Handler mBackgroundHandler; @Inject public BrightnessDialog( BroadcastDispatcher broadcastDispatcher, - BrightnessSlider.Factory factory, + BrightnessSliderController.Factory factory, @Background Handler bgHandler) { mBroadcastDispatcher = broadcastDispatcher; mToggleSliderFactory = factory; @@ -77,7 +77,7 @@ public class BrightnessDialog extends Activity { // The brightness mirror container is INVISIBLE by default. frame.setVisibility(View.VISIBLE); - BrightnessSlider controller = mToggleSliderFactory.create(this, frame); + BrightnessSliderController controller = mToggleSliderFactory.create(this, frame); controller.init(); frame.addView(controller.getRootView(), MATCH_PARENT, WRAP_CONTENT); diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSlider.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderController.java index b0e320ad1e2f..6c8190af27f7 100644 --- a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSlider.java +++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderController.java @@ -44,7 +44,8 @@ import javax.inject.Inject; * * @see BrightnessMirrorController */ -public class BrightnessSlider extends ViewController<BrightnessSliderView> implements ToggleSlider { +public class BrightnessSliderController extends ViewController<BrightnessSliderView> implements + ToggleSlider { private Listener mListener; private ToggleSlider mMirror; @@ -69,7 +70,7 @@ public class BrightnessSlider extends ViewController<BrightnessSliderView> imple } }; - BrightnessSlider( + BrightnessSliderController( BrightnessSliderView brightnessSliderView, FalsingManager falsingManager) { super(brightnessSliderView); @@ -184,6 +185,15 @@ public class BrightnessSlider extends ViewController<BrightnessSliderView> imple mView.setVisibility(View.VISIBLE); } + @Override + public boolean isVisible() { + // this should be called rarely - once or twice per slider's value change, but not for + // every value change when user slides finger - only the final one. + // If view is not visible this call is quick (around 50 µs) as it sees parent is not visible + // otherwise it's slightly longer (70 µs) because there are more checks to be done + return mView.isVisibleToUser(); + } + private final SeekBar.OnSeekBarChangeListener mSeekListener = new SeekBar.OnSeekBarChangeListener() { @Override @@ -222,7 +232,7 @@ public class BrightnessSlider extends ViewController<BrightnessSliderView> imple }; /** - * Creates a {@link BrightnessSlider} with its associated view. + * Creates a {@link BrightnessSliderController} with its associated view. */ public static class Factory { @@ -240,11 +250,11 @@ public class BrightnessSlider extends ViewController<BrightnessSliderView> imple * @param viewRoot the {@link ViewGroup} that will contain the hierarchy. The inflated * hierarchy will not be attached */ - public BrightnessSlider create(Context context, @Nullable ViewGroup viewRoot) { + public BrightnessSliderController create(Context context, @Nullable ViewGroup viewRoot) { int layout = getLayout(); BrightnessSliderView root = (BrightnessSliderView) LayoutInflater.from(context) .inflate(layout, viewRoot, false); - return new BrightnessSlider(root, mFalsingManager); + return new BrightnessSliderController(root, mFalsingManager); } /** Get the layout to inflate based on what slider to use */ diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/ToggleSlider.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/ToggleSlider.java index 5de22d43a21b..648e33b1d228 100644 --- a/packages/SystemUI/src/com/android/systemui/settings/brightness/ToggleSlider.java +++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/ToggleSlider.java @@ -38,4 +38,5 @@ public interface ToggleSlider { void showView(); void hideView(); + boolean isVisible(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java index f72178f0c8b0..daae43f69d3b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java @@ -811,7 +811,8 @@ public class NetworkControllerImpl extends BroadcastReceiver break; case Settings.Panel.ACTION_INTERNET_CONNECTIVITY: mMainHandler.post(() -> mInternetDialogFactory.create(true, - mAccessPoints.canConfigMobileData(), mAccessPoints.canConfigWifi())); + mAccessPoints.canConfigMobileData(), mAccessPoints.canConfigWifi(), + null /* view */)); break; default: int subId = intent.getIntExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java index e368aad31ac8..4f3bbdbff030 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java @@ -195,12 +195,21 @@ public class KeyguardClockPositionAlgorithm { 1.0f /* panelExpansion */, 1.0f /* darkAmount */); result.clockAlpha = getClockAlpha(y); result.stackScrollerPadding = getStackScrollerPadding(y); - result.stackScrollerPaddingExpanded = mBypassEnabled ? mUnlockedStackScrollerPadding - : getClockY(1.0f, mDarkAmount) + mKeyguardStatusHeight; + result.stackScrollerPaddingExpanded = getStackScrollerPaddingExpanded(); result.clockX = (int) interpolate(0, burnInPreventionOffsetX(), mDarkAmount); result.clockScale = interpolate(getBurnInScale(), 1.0f, 1.0f - mDarkAmount); } + private int getStackScrollerPaddingExpanded() { + if (mBypassEnabled) { + return mUnlockedStackScrollerPadding; + } else if (mIsSplitShade) { + return getClockY(1.0f, mDarkAmount); + } else { + return getClockY(1.0f, mDarkAmount) + mKeyguardStatusHeight; + } + } + private int getStackScrollerPadding(int clockYPosition) { if (mBypassEnabled) { return (int) (mUnlockedStackScrollerPadding + mOverStretchAmount); @@ -212,8 +221,13 @@ public class KeyguardClockPositionAlgorithm { } public float getMinStackScrollerPadding() { - return mBypassEnabled ? mUnlockedStackScrollerPadding - : mMinTopMargin + mKeyguardStatusHeight; + if (mBypassEnabled) { + return mUnlockedStackScrollerPadding; + } else if (mIsSplitShade) { + return mMinTopMargin; + } else { + return mMinTopMargin + mKeyguardStatusHeight; + } } private int getExpandedPreferredClockY() { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java index bc50893d9a64..706dff4de36e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -174,7 +174,7 @@ import com.android.systemui.qs.QSFragment; import com.android.systemui.qs.QSPanelController; import com.android.systemui.recents.ScreenPinningRequest; import com.android.systemui.scrim.ScrimView; -import com.android.systemui.settings.brightness.BrightnessSlider; +import com.android.systemui.settings.brightness.BrightnessSliderController; import com.android.systemui.shared.plugins.PluginManager; import com.android.systemui.statusbar.AutoHideUiElement; import com.android.systemui.statusbar.BackDropView; @@ -541,7 +541,7 @@ public class StatusBar extends SystemUI implements private final NotificationViewHierarchyManager mViewHierarchyManager; private final KeyguardViewMediator mKeyguardViewMediator; protected final NotificationInterruptStateProvider mNotificationInterruptStateProvider; - private final BrightnessSlider.Factory mBrightnessSliderFactory; + private final BrightnessSliderController.Factory mBrightnessSliderFactory; private final FeatureFlags mFeatureFlags; private final UnfoldTransitionConfig mUnfoldTransitionConfig; private final Lazy<UnfoldLightRevealOverlayAnimation> mUnfoldLightRevealOverlayAnimation; @@ -781,7 +781,7 @@ public class StatusBar extends SystemUI implements Lazy<NotificationShadeDepthController> notificationShadeDepthControllerLazy, StatusBarTouchableRegionManager statusBarTouchableRegionManager, NotificationIconAreaController notificationIconAreaController, - BrightnessSlider.Factory brightnessSliderFactory, + BrightnessSliderController.Factory brightnessSliderFactory, UnfoldTransitionConfig unfoldTransitionConfig, Lazy<UnfoldLightRevealOverlayAnimation> unfoldLightRevealOverlayAnimation, Lazy<UnfoldTransitionWallpaperController> unfoldTransitionWallpaperController, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java index cd95cfd2a74f..cac66a3186ca 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java @@ -907,7 +907,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb @Override public boolean bouncerIsOrWillBeShowing() { - return mBouncer.isShowing() || mBouncer.getShowingSoon(); + return isBouncerShowing() || mBouncer.getShowingSoon(); } public boolean isFullscreenBouncer() { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java index 9415d5082d10..18aa6893e7bc 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java @@ -22,7 +22,11 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.os.Bundle; +import android.os.SystemProperties; import android.os.UserHandle; +import android.util.TypedValue; +import android.view.ViewGroup; import android.view.Window; import android.view.WindowInsets.Type; import android.view.WindowManager; @@ -45,6 +49,10 @@ import java.util.Set; * and dismisses itself when it receives the broadcast. */ public class SystemUIDialog extends AlertDialog implements ListenableDialog { + // TODO(b/203389579): Remove this once the dialog width on large screens has been agreed on. + private static final String FLAG_TABLET_DIALOG_WIDTH = + "persist.systemui.flag_tablet_dialog_width"; + private final Context mContext; private final DismissReceiver mDismissReceiver; private final Set<DialogListener> mDialogListeners = new LinkedHashSet<>(); @@ -66,6 +74,41 @@ public class SystemUIDialog extends AlertDialog implements ListenableDialog { } @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + // Set the dialog window size. + getWindow().setLayout(getDialogWidth(), ViewGroup.LayoutParams.WRAP_CONTENT); + } + + private int getDialogWidth() { + boolean isOnTablet = + mContext.getResources().getConfiguration().smallestScreenWidthDp >= 600; + if (!isOnTablet) { + return ViewGroup.LayoutParams.MATCH_PARENT; + } + + int flagValue = SystemProperties.getInt(FLAG_TABLET_DIALOG_WIDTH, 0); + if (flagValue == -1) { + // The width of bottom sheets (624dp). + return Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 624, + mContext.getResources().getDisplayMetrics())); + } else if (flagValue == -2) { + // The suggested small width for all dialogs (348dp) + return Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 348, + mContext.getResources().getDisplayMetrics())); + } else if (flagValue > 0) { + // Any given width. + return Math.round( + TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, flagValue, + mContext.getResources().getDisplayMetrics())); + } else { + // By default we use the same width as the notification shade in portrait mode (504dp). + return mContext.getResources().getDimensionPixelSize(R.dimen.large_dialog_width); + } + } + + @Override protected void onStart() { super.onStart(); mDismissReceiver.register(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java index c452a486cbe1..590e238af193 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java @@ -48,7 +48,7 @@ import com.android.systemui.navigationbar.NavigationBarController; import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.PluginDependencyProvider; import com.android.systemui.recents.ScreenPinningRequest; -import com.android.systemui.settings.brightness.BrightnessSlider; +import com.android.systemui.settings.brightness.BrightnessSliderController; import com.android.systemui.shared.plugins.PluginManager; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.KeyguardIndicationController; @@ -220,7 +220,7 @@ public interface StatusBarPhoneModule { Lazy<NotificationShadeDepthController> notificationShadeDepthController, StatusBarTouchableRegionManager statusBarTouchableRegionManager, NotificationIconAreaController notificationIconAreaController, - BrightnessSlider.Factory brightnessSliderFactory, + BrightnessSliderController.Factory brightnessSliderFactory, UnfoldTransitionConfig unfoldTransitionConfig, Lazy<UnfoldLightRevealOverlayAnimation> unfoldLightRevealOverlayAnimation, Lazy<NaturalRotationUnfoldProgressProvider> naturalRotationUnfoldProgressProvider, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BrightnessMirrorController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BrightnessMirrorController.java index 1e5251196379..5bd20ff2d090 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BrightnessMirrorController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BrightnessMirrorController.java @@ -26,7 +26,7 @@ import android.view.ViewGroup; import android.widget.FrameLayout; import com.android.systemui.R; -import com.android.systemui.settings.brightness.BrightnessSlider; +import com.android.systemui.settings.brightness.BrightnessSliderController; import com.android.systemui.settings.brightness.ToggleSlider; import com.android.systemui.statusbar.NotificationShadeDepthController; import com.android.systemui.statusbar.phone.NotificationPanelViewController; @@ -46,8 +46,8 @@ public class BrightnessMirrorController private final NotificationPanelViewController mNotificationPanel; private final NotificationShadeDepthController mDepthController; private final ArraySet<BrightnessMirrorListener> mBrightnessMirrorListeners = new ArraySet<>(); - private final BrightnessSlider.Factory mToggleSliderFactory; - private BrightnessSlider mToggleSliderController; + private final BrightnessSliderController.Factory mToggleSliderFactory; + private BrightnessSliderController mToggleSliderController; private final int[] mInt2Cache = new int[2]; private FrameLayout mBrightnessMirror; private int mBrightnessMirrorBackgroundPadding; @@ -56,7 +56,7 @@ public class BrightnessMirrorController public BrightnessMirrorController(NotificationShadeWindowView statusBarWindow, NotificationPanelViewController notificationPanelViewController, NotificationShadeDepthController notificationShadeDepthController, - BrightnessSlider.Factory factory, + BrightnessSliderController.Factory factory, @NonNull Consumer<Boolean> visibilityCallback) { mStatusBarWindow = statusBarWindow; mToggleSliderFactory = factory; @@ -135,9 +135,10 @@ public class BrightnessMirrorController reinflate(); } - private BrightnessSlider setMirrorLayout() { + private BrightnessSliderController setMirrorLayout() { Context context = mBrightnessMirror.getContext(); - BrightnessSlider controller = mToggleSliderFactory.create(context, mBrightnessMirror); + BrightnessSliderController controller = mToggleSliderFactory.create(context, + mBrightnessMirror); controller.init(); mBrightnessMirror.addView(controller.getRootView(), ViewGroup.LayoutParams.MATCH_PARENT, diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.java index 06a4ae096d44..3242adbcfad8 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.java @@ -42,7 +42,7 @@ import com.android.systemui.qs.customize.QSCustomizerController; import com.android.systemui.qs.logging.QSLogger; import com.android.systemui.qs.tileimpl.QSTileImpl; import com.android.systemui.settings.brightness.BrightnessController; -import com.android.systemui.settings.brightness.BrightnessSlider; +import com.android.systemui.settings.brightness.BrightnessSliderController; import com.android.systemui.settings.brightness.ToggleSlider; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.tuner.TunerService; @@ -88,9 +88,9 @@ public class QSPanelControllerTest extends SysuiTestCase { @Mock private BrightnessController mBrightnessController; @Mock - private BrightnessSlider.Factory mToggleSliderViewControllerFactory; + private BrightnessSliderController.Factory mToggleSliderViewControllerFactory; @Mock - private BrightnessSlider mBrightnessSlider; + private BrightnessSliderController mBrightnessSliderController; @Mock QSTileImpl mQSTile; @Mock @@ -120,7 +120,7 @@ public class QSPanelControllerTest extends SysuiTestCase { when(mQSTileHost.getTiles()).thenReturn(Collections.singleton(mQSTile)); when(mQSTileHost.createTileView(any(), eq(mQSTile), anyBoolean())).thenReturn(mQSTileView); when(mToggleSliderViewControllerFactory.create(any(), any())) - .thenReturn(mBrightnessSlider); + .thenReturn(mBrightnessSliderController); when(mBrightnessControllerFactory.create(any(ToggleSlider.class))) .thenReturn(mBrightnessController); when(mQSTileRevealControllerFactory.create(any(), any())) diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogControllerTest.java index 5cea7632192b..eb03b5ff2a6c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogControllerTest.java @@ -45,6 +45,7 @@ import com.android.keyguard.KeyguardUpdateMonitor; import com.android.settingslib.wifi.WifiUtils; import com.android.systemui.R; import com.android.systemui.SysuiTestCase; +import com.android.systemui.animation.DialogLaunchAnimator; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.plugins.ActivityStarter; @@ -138,6 +139,8 @@ public class InternetDialogControllerTest extends SysuiTestCase { private CarrierConfigTracker mCarrierConfigTracker; @Mock private LocationController mLocationController; + @Mock + private DialogLaunchAnimator mDialogLaunchAnimator; private TestableResources mTestableResources; private MockInternetDialogController mInternetDialogController; @@ -174,7 +177,7 @@ public class InternetDialogControllerTest extends SysuiTestCase { mock(ConnectivityManager.class), mHandler, mExecutor, mBroadcastDispatcher, mock(KeyguardUpdateMonitor.class), mGlobalSettings, mKeyguardStateController, mWindowManager, mToastFactory, mWorkerHandler, mCarrierConfigTracker, - mLocationController); + mLocationController, mDialogLaunchAnimator); mSubscriptionManager.addOnSubscriptionsChangedListener(mExecutor, mInternetDialogController.mOnSubscriptionsChangedListener); mInternetDialogController.onStart(mInternetDialogCallback, true); @@ -654,12 +657,13 @@ public class InternetDialogControllerTest extends SysuiTestCase { KeyguardStateController keyguardStateController, WindowManager windowManager, ToastFactory toastFactory, Handler workerHandler, CarrierConfigTracker carrierConfigTracker, - LocationController locationController) { + LocationController locationController, + DialogLaunchAnimator dialogLaunchAnimator) { super(context, uiEventLogger, starter, accessPointController, subscriptionManager, telephonyManager, wifiManager, connectivityManager, handler, mainExecutor, broadcastDispatcher, keyguardUpdateMonitor, globalSettings, keyguardStateController, windowManager, toastFactory, workerHandler, - carrierConfigTracker, locationController); + carrierConfigTracker, locationController, dialogLaunchAnimator); mGlobalSettings = globalSettings; } diff --git a/packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessSliderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessSliderControllerTest.kt index bceb92894609..2b39354d99e8 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessSliderTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessSliderControllerTest.kt @@ -45,7 +45,7 @@ import org.mockito.Mockito.`when` as whenever @SmallTest @RunWith(AndroidTestingRunner::class) -class BrightnessSliderTest : SysuiTestCase() { +class BrightnessSliderControllerTest : SysuiTestCase() { @Mock private lateinit var brightnessSliderView: BrightnessSliderView @@ -66,7 +66,7 @@ class BrightnessSliderTest : SysuiTestCase() { private lateinit var seekBar: SeekBar private var mFalsingManager: FalsingManagerFake = FalsingManagerFake() - private lateinit var mController: BrightnessSlider + private lateinit var mController: BrightnessSliderController @Before fun setUp() { @@ -75,7 +75,7 @@ class BrightnessSliderTest : SysuiTestCase() { whenever(mirrorController.toggleSlider).thenReturn(mirror) whenever(motionEvent.copy()).thenReturn(motionEvent) - mController = BrightnessSlider(brightnessSliderView, mFalsingManager) + mController = BrightnessSliderController(brightnessSliderView, mFalsingManager) mController.init() mController.setOnChangedListener(listener) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java index d098e1a0b8a5..624bedc30be9 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java @@ -58,7 +58,6 @@ public class KeyguardClockPositionAlgorithmTest extends SysuiTestCase { private KeyguardClockPositionAlgorithm.Result mClockPosition; private MockitoSession mStaticMockSession; - private int mNotificationStackHeight; private float mPanelExpansion; private int mKeyguardStatusBarHeaderHeight; @@ -264,6 +263,30 @@ public class KeyguardClockPositionAlgorithmTest extends SysuiTestCase { } @Test + public void notifPaddingExpandedAlignedWithClockInSplitShadeMode() { + givenLockScreen(); + mIsSplitShade = true; + mKeyguardStatusHeight = 200; + // WHEN the position algorithm is run + positionClock(); + // THEN the padding DOESN'T adjust for keyguard status height. + assertThat(mClockPosition.stackScrollerPaddingExpanded) + .isEqualTo(mClockPosition.clockYFullyDozing); + } + + @Test + public void notifMinPaddingAlignedWithClockInSplitShadeMode() { + givenLockScreen(); + mIsSplitShade = true; + mKeyguardStatusHeight = 200; + // WHEN the position algorithm is run + positionClock(); + // THEN the padding DOESN'T adjust for keyguard status height. + assertThat(mClockPositionAlgorithm.getMinStackScrollerPadding()) + .isEqualTo(mKeyguardStatusBarHeaderHeight); + } + + @Test public void notifPositionWithLargeClockOnLockScreen() { // GIVEN on lock screen and clock has a nonzero height givenLockScreen(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java index 35d15af6bd89..2d944aa707bf 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java @@ -16,6 +16,7 @@ package com.android.systemui.statusbar.phone; +import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyFloat; @@ -97,6 +98,8 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase { @Mock private UnlockedScreenOffAnimationController mUnlockedScreenOffAnimationController; @Mock + private StatusBarKeyguardViewManager.AlternateAuthInterceptor mAlternateAuthInterceptor; + @Mock private KeyguardMessageArea mKeyguardMessageArea; @Mock private Lazy<ShadeController> mShadeController; @@ -287,6 +290,24 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase { } @Test + public void testShowing_whenAlternateAuthShowing() { + mStatusBarKeyguardViewManager.setAlternateAuthInterceptor(mAlternateAuthInterceptor); + when(mBouncer.isShowing()).thenReturn(false); + when(mAlternateAuthInterceptor.isShowingAlternateAuthBouncer()).thenReturn(true); + assertTrue("Is showing not accurate when alternative auth showing", + mStatusBarKeyguardViewManager.isShowing()); + } + + @Test + public void testWillBeShowing_whenAlternateAuthShowing() { + mStatusBarKeyguardViewManager.setAlternateAuthInterceptor(mAlternateAuthInterceptor); + when(mBouncer.isShowing()).thenReturn(false); + when(mAlternateAuthInterceptor.isShowingAlternateAuthBouncer()).thenReturn(true); + assertTrue("Is or will be showing not accurate when alternative auth showing", + mStatusBarKeyguardViewManager.bouncerIsOrWillBeShowing()); + } + + @Test public void testUpdateResources_delegatesToBouncer() { mStatusBarKeyguardViewManager.updateResources(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java index 943d3c79984d..3e96bfde43c0 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java @@ -96,7 +96,7 @@ import com.android.systemui.plugins.ActivityStarter.OnDismissAction; import com.android.systemui.plugins.PluginDependencyProvider; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.recents.ScreenPinningRequest; -import com.android.systemui.settings.brightness.BrightnessSlider; +import com.android.systemui.settings.brightness.BrightnessSliderController; import com.android.systemui.shared.plugins.PluginManager; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.KeyguardIndicationController; @@ -257,7 +257,7 @@ public class StatusBarTest extends SysuiTestCase { @Mock private PhoneStatusBarPolicy mPhoneStatusBarPolicy; @Mock private DemoModeController mDemoModeController; @Mock private Lazy<NotificationShadeDepthController> mNotificationShadeDepthControllerLazy; - @Mock private BrightnessSlider.Factory mBrightnessSliderFactory; + @Mock private BrightnessSliderController.Factory mBrightnessSliderFactory; @Mock private UnfoldTransitionConfig mUnfoldTransitionConfig; @Mock private Lazy<UnfoldLightRevealOverlayAnimation> mUnfoldLightRevealOverlayAnimationLazy; @Mock private Lazy<NaturalRotationUnfoldProgressProvider> mNaturalRotationProgressProvider; diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintStateCallback.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintStateCallback.java index 0050a895034f..be0e6edb2a42 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintStateCallback.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintStateCallback.java @@ -23,7 +23,6 @@ import static android.hardware.fingerprint.FingerprintStateListener.STATE_IDLE; import static android.hardware.fingerprint.FingerprintStateListener.STATE_KEYGUARD_AUTH; import android.annotation.NonNull; -import android.content.Context; import android.hardware.fingerprint.FingerprintStateListener; import android.hardware.fingerprint.IFingerprintStateListener; import android.os.RemoteException; @@ -34,8 +33,6 @@ import com.android.server.biometrics.sensors.AuthenticationClient; import com.android.server.biometrics.sensors.BaseClientMonitor; import com.android.server.biometrics.sensors.EnrollClient; import com.android.server.biometrics.sensors.EnrollmentModifier; -import com.android.server.biometrics.sensors.RemovalConsumer; -import com.android.server.biometrics.sensors.fingerprint.hidl.FingerprintEnrollClient; import java.util.concurrent.CopyOnWriteArrayList; @@ -70,7 +67,7 @@ public class FingerprintStateCallback implements BaseClientMonitor.Callback { } else { mFingerprintState = STATE_AUTH_OTHER; } - } else if (client instanceof FingerprintEnrollClient) { + } else if (client instanceof EnrollClient) { mFingerprintState = STATE_ENROLLING; } else { Slog.w(FingerprintService.TAG, @@ -143,6 +140,7 @@ public class FingerprintStateCallback implements BaseClientMonitor.Callback { /** * Enables clients to register a FingerprintStateListener. Used by FingerprintService to forward * updates in fingerprint sensor state to the SideFpNsEventHandler + * * @param listener */ public void registerFingerprintStateListener(@NonNull IFingerprintStateListener listener) { diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 84e62d44d61b..8ace5e4aff87 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -3081,9 +3081,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A mAtmService.deferWindowLayout(); try { - final Transition newTransition = (!mTransitionController.isCollecting() - && mTransitionController.getTransitionPlayer() != null) - ? mTransitionController.createTransition(TRANSIT_CLOSE) : null; mTaskSupervisor.mNoHistoryActivities.remove(this); makeFinishingLocked(); // Make a local reference to its task since this.task could be set to null once this @@ -3115,10 +3112,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A final boolean endTask = task.getTopNonFinishingActivity() == null && !task.isClearingToReuseTask(); - if (newTransition != null) { - mTransitionController.requestStartTransition(newTransition, - endTask ? task : null, null /* remote */); - } + mTransitionController.requestCloseTransitionIfNeeded(endTask ? task : this); if (isState(RESUMED)) { if (endTask) { mAtmService.getTaskChangeNotificationController().notifyTaskRemovalStarted( @@ -3544,13 +3538,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A if (stopped) { abortAndClearOptionsAnimation(); } - if (mTransitionController.isCollecting()) { - // We don't want the finishing to change the transition ready state since there will not - // be corresponding setReady for finishing. - mTransitionController.collectExistenceChange(this); - } else { - mTransitionController.requestTransitionIfNeeded(TRANSIT_CLOSE, this); - } } /** @@ -3732,6 +3719,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // to the restarted activity. nowVisible = mVisibleRequested; } + mTransitionController.requestCloseTransitionIfNeeded(this); cleanUp(true /* cleanServices */, true /* setState */); if (remove) { if (mStartingData != null && mVisible && task != null) { diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java index ba305929d808..7c5f059fb89a 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java +++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java @@ -44,7 +44,6 @@ import static android.os.PowerManager.PARTIAL_WAKE_LOCK; import static android.os.Process.INVALID_UID; import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER; import static android.view.Display.DEFAULT_DISPLAY; -import static android.view.WindowManager.TRANSIT_CLOSE; import static android.view.WindowManager.TRANSIT_TO_FRONT; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_STATES; @@ -1562,19 +1561,7 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { // Prevent recursion. return; } - if (task.isVisible()) { - if (task.mTransitionController.isCollecting()) { - // We don't want the finishing to change the transition ready state since there will - // not be corresponding setReady for finishing. - task.mTransitionController.collectExistenceChange(task); - } else { - task.mTransitionController.requestTransitionIfNeeded(TRANSIT_CLOSE, task); - } - } else { - // Removing a non-visible task doesn't require a transition, but if there is one - // collecting, this should be a member just in case. - task.mTransitionController.collect(task); - } + task.mTransitionController.requestCloseTransitionIfNeeded(task); task.mInRemoveTask = true; try { task.performClearTask(reason); diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java index 275ed0ee28a9..f5e7967de410 100644 --- a/services/core/java/com/android/server/wm/TaskDisplayArea.java +++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java @@ -864,12 +864,9 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> { int layer = 0; // Place root home tasks to the bottom. layer = adjustRootTaskLayer(t, mTmpHomeChildren, layer); - adjustRootTaskLayer(t, mTmpNormalChildren, layer); - - // Always on top tasks layer should higher than split divider layer so set it as start. - t.setLayer(mSplitScreenDividerAnchor, SPLIT_DIVIDER_LAYER); - layer = SPLIT_DIVIDER_LAYER + 1; + layer = adjustRootTaskLayer(t, mTmpNormalChildren, layer); adjustRootTaskLayer(t, mTmpAlwaysOnTopChildren, layer); + t.setLayer(mSplitScreenDividerAnchor, SPLIT_DIVIDER_LAYER); } /** @@ -884,19 +881,33 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> { ArrayList<WindowContainer> children, int startLayer) { mTmpNeedsZBoostIndexes.clear(); final int childCount = children.size(); + boolean hasAdjacentTask = false; for (int i = 0; i < childCount; i++) { final WindowContainer child = children.get(i); final TaskDisplayArea childTda = child.asTaskDisplayArea(); - - boolean childNeedsZBoost = childTda != null + final boolean childNeedsZBoost = childTda != null ? childTda.childrenNeedZBoost() : child.needsZBoost(); - if (!childNeedsZBoost) { - child.assignLayer(t, startLayer++); - } else { + if (childNeedsZBoost) { mTmpNeedsZBoostIndexes.add(i); + continue; } + + final Task childTask = child.asTask(); + final boolean inAdjacentTask = childTask != null + && child.inMultiWindowMode() + && childTask.getRootTask().getAdjacentTaskFragment() != null; + + if (inAdjacentTask || child.inSplitScreenWindowingMode()) { + hasAdjacentTask = true; + } else if (hasAdjacentTask && startLayer < SPLIT_DIVIDER_LAYER) { + // Task on top of adjacent tasks should be higher than split divider layer so + // set it as start. + startLayer = SPLIT_DIVIDER_LAYER + 1; + } + + child.assignLayer(t, startLayer++); } final int zBoostSize = mTmpNeedsZBoostIndexes.size(); diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java index 5d82553ad284..4db8ef49a11a 100644 --- a/services/core/java/com/android/server/wm/Transition.java +++ b/services/core/java/com/android/server/wm/Transition.java @@ -338,6 +338,11 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe applyReady(); } + @VisibleForTesting + boolean allReady() { + return mReadyTracker.allReady(); + } + /** * Build a transaction that "resets" all the re-parenting and layer changes. This is * intended to be applied at the end of the transition but before the finish callback. This diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java index 91825ccf98e7..a21e4f2fee0d 100644 --- a/services/core/java/com/android/server/wm/TransitionController.java +++ b/services/core/java/com/android/server/wm/TransitionController.java @@ -35,7 +35,6 @@ import android.util.ArrayMap; import android.util.Slog; import android.util.proto.ProtoOutputStream; import android.view.WindowManager; -import android.window.IRemoteTransition; import android.window.ITransitionMetricsReporter; import android.window.ITransitionPlayer; import android.window.RemoteTransition; @@ -226,7 +225,7 @@ class TransitionController { } /** - * @see #requestTransitionIfNeeded(int, int, WindowContainer, IRemoteTransition) + * @see #requestTransitionIfNeeded(int, int, WindowContainer, WindowContainer, RemoteTransition) */ @Nullable Transition requestTransitionIfNeeded(@WindowManager.TransitionType int type, @@ -235,7 +234,7 @@ class TransitionController { } /** - * @see #requestTransitionIfNeeded(int, int, WindowContainer, IRemoteTransition) + * @see #requestTransitionIfNeeded(int, int, WindowContainer, WindowContainer, RemoteTransition) */ @Nullable Transition requestTransitionIfNeeded(@WindowManager.TransitionType int type, @@ -306,6 +305,22 @@ class TransitionController { return transition; } + /** Requests transition for a window container which will be removed or invisible. */ + void requestCloseTransitionIfNeeded(@NonNull WindowContainer<?> wc) { + if (mTransitionPlayer == null) return; + if (wc.isVisibleRequested()) { + if (!isCollecting()) { + requestStartTransition(createTransition(TRANSIT_CLOSE, 0 /* flags */), + wc.asTask(), null /* remoteTransition */); + } + collectExistenceChange(wc); + } else { + // Removing a non-visible window doesn't require a transition, but if there is one + // collecting, this should be a member just in case. + collect(wc); + } + } + /** @see Transition#collect */ void collect(@NonNull WindowContainer wc) { if (mCollectingTransition == null) return; diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java index 51ecce0ec9ec..a68b09e2e307 100644 --- a/services/core/java/com/android/server/wm/WindowContainer.java +++ b/services/core/java/com/android/server/wm/WindowContainer.java @@ -32,10 +32,6 @@ import static android.os.UserHandle.USER_NULL; import static android.view.SurfaceControl.Transaction; import static android.view.WindowManager.LayoutParams.INVALID_WINDOW_TYPE; import static android.view.WindowManager.TRANSIT_CHANGE; -import static android.view.WindowManager.TRANSIT_OLD_TASK_CLOSE; -import static android.view.WindowManager.TRANSIT_OLD_TASK_OPEN; -import static android.view.WindowManager.TRANSIT_OLD_TASK_TO_BACK; -import static android.view.WindowManager.TRANSIT_OLD_TASK_TO_FRONT; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS_ANIM; @@ -43,6 +39,7 @@ import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ORIENTATION; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_SYNC_ENGINE; import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER; import static com.android.server.wm.AppTransition.MAX_APP_TRANSITION_DURATION; +import static com.android.server.wm.AppTransition.isTaskTransitOld; import static com.android.server.wm.DisplayContent.IME_TARGET_LAYERING; import static com.android.server.wm.IdentifierProto.HASH_CODE; import static com.android.server.wm.IdentifierProto.TITLE; @@ -70,7 +67,6 @@ import android.annotation.ColorInt; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; -import android.app.ActivityThread; import android.content.Context; import android.content.pm.ActivityInfo; import android.content.res.Configuration; @@ -91,6 +87,7 @@ import android.view.RemoteAnimationTarget; import android.view.SurfaceControl; import android.view.SurfaceControl.Builder; import android.view.SurfaceSession; +import android.view.TaskTransitionSpec; import android.view.WindowManager; import android.view.WindowManager.TransitionOldType; import android.view.animation.Animation; @@ -2825,33 +2822,15 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< mSurfaceAnimationSources.addAll(sources); } - TaskDisplayArea taskDisplayArea = getTaskDisplayArea(); - boolean isSettingBackgroundColor = taskDisplayArea != null - && isTransitionWithBackgroundColor(transit); - - if (isSettingBackgroundColor) { - Context uiContext = ActivityThread.currentActivityThread().getSystemUiContext(); - @ColorInt int backgroundColor = uiContext.getColor(R.color.overview_background); + AnimationRunnerBuilder animationRunnerBuilder = new AnimationRunnerBuilder(); - taskDisplayArea.setBackgroundColor(backgroundColor); + if (isTaskTransitOld(transit)) { + animationRunnerBuilder.setTaskBackgroundColor(getTaskAnimationBackgroundColor()); } - // Atomic counter to make sure the clearColor callback is only called one. - // It will be called twice in the case we cancel the animation without restart - // (in that case it will run as the cancel and finished callbacks). - final AtomicInteger callbackCounter = new AtomicInteger(0); - final Runnable clearBackgroundColorHandler = () -> { - if (callbackCounter.getAndIncrement() == 0) { - taskDisplayArea.clearBackgroundColor(); - } - }; - - final Runnable cleanUpCallback = isSettingBackgroundColor - ? clearBackgroundColorHandler : () -> {}; - - startAnimation(getPendingTransaction(), adapter, !isVisible(), - ANIMATION_TYPE_APP_TRANSITION, (type, anim) -> cleanUpCallback.run(), - cleanUpCallback, thumbnailAdapter); + animationRunnerBuilder.build() + .startAnimation(getPendingTransaction(), adapter, !isVisible(), + ANIMATION_TYPE_APP_TRANSITION, thumbnailAdapter); if (adapter.getShowWallpaper()) { getDisplayContent().pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER; @@ -2859,11 +2838,16 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< } } - private boolean isTransitionWithBackgroundColor(@TransitionOldType int transit) { - return transit == TRANSIT_OLD_TASK_OPEN - || transit == TRANSIT_OLD_TASK_CLOSE - || transit == TRANSIT_OLD_TASK_TO_FRONT - || transit == TRANSIT_OLD_TASK_TO_BACK; + private @ColorInt int getTaskAnimationBackgroundColor() { + Context uiContext = mDisplayContent.getDisplayPolicy().getSystemUiContext(); + TaskTransitionSpec customSpec = mWmService.mTaskTransitionSpec; + @ColorInt int defaultFallbackColor = uiContext.getColor(R.color.overview_background); + + if (customSpec != null && customSpec.backgroundColor != 0) { + return customSpec.backgroundColor; + } + + return defaultFallbackColor; } final SurfaceAnimationRunner getSurfaceAnimationRunner() { @@ -3551,4 +3535,53 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< getPendingTransaction().setSecure(mSurfaceControl, !canScreenshot); return true; } + + private class AnimationRunnerBuilder { + /** + * Runs when the surface stops animating + */ + private final List<Runnable> mOnAnimationFinished = new LinkedList<>(); + /** + * Runs when the animation is cancelled but the surface is still animating + */ + private final List<Runnable> mOnAnimationCancelled = new LinkedList<>(); + + private void setTaskBackgroundColor(@ColorInt int backgroundColor) { + TaskDisplayArea taskDisplayArea = getTaskDisplayArea(); + + if (taskDisplayArea != null) { + taskDisplayArea.setBackgroundColor(backgroundColor); + + // Atomic counter to make sure the clearColor callback is only called one. + // It will be called twice in the case we cancel the animation without restart + // (in that case it will run as the cancel and finished callbacks). + final AtomicInteger callbackCounter = new AtomicInteger(0); + final Runnable clearBackgroundColorHandler = () -> { + if (callbackCounter.getAndIncrement() == 0) { + taskDisplayArea.clearBackgroundColor(); + } + }; + + // We want to make sure this is called both when the surface stops animating and + // also when an animation is cancelled (i.e. animation is replaced by another + // animation but and so the surface is still animating) + mOnAnimationFinished.add(clearBackgroundColorHandler); + mOnAnimationCancelled.add(clearBackgroundColorHandler); + } + } + + private IAnimationStarter build() { + return (Transaction t, AnimationAdapter adapter, boolean hidden, + @AnimationType int type, @Nullable AnimationAdapter snapshotAnim) -> { + startAnimation(getPendingTransaction(), adapter, !isVisible(), type, + (animType, anim) -> mOnAnimationFinished.forEach(Runnable::run), + () -> mOnAnimationCancelled.forEach(Runnable::run), snapshotAnim); + }; + } + } + + private interface IAnimationStarter { + void startAnimation(Transaction t, AnimationAdapter anim, boolean hidden, + @AnimationType int type, @Nullable AnimationAdapter snapshotAnim); + } } diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java index 44cff33d6146..128bfa8a28f3 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java @@ -166,6 +166,8 @@ public class ActivityRecordTests extends WindowTestsBase { @Before public void setUp() throws Exception { setBooted(mAtm); + // Because the booted state is set, avoid starting real home if there is no task. + doReturn(false).when(mRootWindowContainer).resumeHomeActivity(any(), anyString(), any()); } private TestStartingWindowOrganizer registerTestStartingWindowOrganizer() { @@ -1083,6 +1085,7 @@ public class ActivityRecordTests extends WindowTestsBase { */ @Test public void testFinishActivityIfPossible_nonVisibleNoAppTransition() { + registerTestTransitionPlayer(); final ActivityRecord activity = createActivityWithTask(); // Put an activity on top of test activity to make it invisible and prevent us from // accidentally resuming the topmost one again. @@ -1093,6 +1096,7 @@ public class ActivityRecordTests extends WindowTestsBase { activity.finishIfPossible("test", false /* oomAdj */); verify(activity.mDisplayContent, never()).prepareAppTransition(eq(TRANSIT_CLOSE)); + assertFalse(activity.inTransition()); } /** @@ -1101,11 +1105,7 @@ public class ActivityRecordTests extends WindowTestsBase { */ @Test public void testFinishActivityIfPossible_lastInTaskRequestsTransitionWithTrigger() { - // Set-up mock shell transitions - final TestTransitionPlayer testPlayer = new TestTransitionPlayer( - mAtm.getTransitionController(), mAtm.mWindowOrganizerController); - mAtm.getTransitionController().registerTransitionPlayer(testPlayer); - + final TestTransitionPlayer testPlayer = registerTestTransitionPlayer(); final ActivityRecord activity = createActivityWithTask(); activity.finishing = false; activity.mVisibleRequested = true; @@ -1117,6 +1117,29 @@ public class ActivityRecordTests extends WindowTestsBase { } /** + * Verify that when collecting activity to the existing close transition, it should not affect + * ready state. + */ + @Test + public void testFinishActivityIfPossible_collectToExistingTransition() { + final TestTransitionPlayer testPlayer = registerTestTransitionPlayer(); + final ActivityRecord activity = createActivityWithTask(); + activity.setState(PAUSED, "test"); + activity.finishIfPossible("test", false /* oomAdj */); + final Transition lastTransition = testPlayer.mLastTransit; + assertTrue(lastTransition.allReady()); + assertTrue(activity.inTransition()); + + // Collect another activity to the existing transition without changing ready state. + final ActivityRecord activity2 = createActivityRecord(activity.getTask()); + activity2.setState(PAUSING, "test"); + activity2.finishIfPossible("test", false /* oomAdj */); + assertTrue(activity2.inTransition()); + assertEquals(lastTransition, testPlayer.mLastTransit); + assertTrue(lastTransition.allReady()); + } + + /** * Verify that complete finish request for non-finishing activity is invalid. */ @Test(expected = IllegalArgumentException.class) diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java index 24bbf4682157..f3c1ec5b200e 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java @@ -1674,11 +1674,7 @@ public class DisplayContentTests extends WindowTestsBase { public void testShellTransitRotation() { DisplayContent dc = createNewDisplay(); - // Set-up mock shell transitions - final TestTransitionPlayer testPlayer = new TestTransitionPlayer( - mAtm.getTransitionController(), mAtm.mWindowOrganizerController); - mAtm.getTransitionController().registerTransitionPlayer(testPlayer); - + final TestTransitionPlayer testPlayer = registerTestTransitionPlayer(); final DisplayRotation dr = dc.getDisplayRotation(); doCallRealMethod().when(dr).updateRotationUnchecked(anyBoolean()); // Rotate 180 degree so the display doesn't have configuration change. This condition is diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java index ac61bb15ab06..81b00eae0021 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java @@ -798,6 +798,14 @@ class WindowTestsBase extends SystemServiceTestsBase { }; } + /** Sets up a simple implementation of transition player for shell transitions. */ + TestTransitionPlayer registerTestTransitionPlayer() { + final TestTransitionPlayer testPlayer = new TestTransitionPlayer( + mAtm.getTransitionController(), mAtm.mWindowOrganizerController); + testPlayer.mController.registerTransitionPlayer(testPlayer); + return testPlayer; + } + /** * Avoids rotating screen disturbed by some conditions. It is usually used for the default * display that is not the instance of {@link TestDisplayContent} (it bypasses the conditions). @@ -1606,7 +1614,7 @@ class WindowTestsBase extends SystemServiceTestsBase { } } - class TestTransitionPlayer extends ITransitionPlayer.Stub { + static class TestTransitionPlayer extends ITransitionPlayer.Stub { final TransitionController mController; final WindowOrganizerController mOrganizer; Transition mLastTransit = null; diff --git a/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java b/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java index 22ea3d5be71b..72b05c08661b 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java @@ -23,7 +23,6 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW; import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; -import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_ABOVE_SUB_PANEL; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA; @@ -442,24 +441,42 @@ public class ZOrderingTests extends WindowTestsBase { @Test public void testDockedDividerPosition() { - final WindowState pinnedStackWindow = createWindow(null, WINDOWING_MODE_PINNED, - ACTIVITY_TYPE_STANDARD, TYPE_BASE_APPLICATION, mDisplayContent, - "pinnedStackWindow"); - final WindowState splitScreenWindow = createWindow(null, - WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, TYPE_BASE_APPLICATION, - mDisplayContent, "splitScreenWindow"); - final WindowState splitScreenSecondaryWindow = createWindow(null, - WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, ACTIVITY_TYPE_STANDARD, - TYPE_BASE_APPLICATION, mDisplayContent, "splitScreenSecondaryWindow"); - final WindowState assistantStackWindow = createWindow(null, - WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_ASSISTANT, TYPE_BASE_APPLICATION, - mDisplayContent, "assistantStackWindow"); + final Task pinnedTask = + createTask(mDisplayContent, WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD); + final WindowState pinnedWindow = + createAppWindow(pinnedTask, ACTIVITY_TYPE_STANDARD, "pinnedWindow"); + + final Task belowTask = + createTask(mDisplayContent, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD); + final WindowState belowTaskWindow = + createAppWindow(belowTask, ACTIVITY_TYPE_STANDARD, "belowTaskWindow"); + + final Task splitScreenTask1 = + createTask(mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD); + final WindowState splitWindow1 = + createAppWindow(splitScreenTask1, ACTIVITY_TYPE_STANDARD, "splitWindow1"); + final Task splitScreenTask2 = + createTask(mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD); + final WindowState splitWindow2 = + createAppWindow(splitScreenTask2, ACTIVITY_TYPE_STANDARD, "splitWindow2"); + splitScreenTask1.setAdjacentTaskFragment(splitScreenTask2); + splitScreenTask2.setAdjacentTaskFragment(splitScreenTask1); + + final Task aboveTask = + createTask(mDisplayContent, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD); + final WindowState aboveTaskWindow = + createAppWindow(aboveTask, ACTIVITY_TYPE_STANDARD, "aboveTaskWindow"); mDisplayContent.assignChildLayers(mTransaction); - assertWindowHigher(mDockedDividerWindow, splitScreenWindow); - assertWindowHigher(mDockedDividerWindow, splitScreenSecondaryWindow); - assertWindowHigher(pinnedStackWindow, mDockedDividerWindow); + assertWindowHigher(splitWindow1, belowTaskWindow); + assertWindowHigher(splitWindow1, belowTaskWindow); + assertWindowHigher(splitWindow2, belowTaskWindow); + assertWindowHigher(splitWindow2, belowTaskWindow); + assertWindowHigher(mDockedDividerWindow, splitWindow1); + assertWindowHigher(mDockedDividerWindow, splitWindow2); + assertWindowHigher(aboveTaskWindow, mDockedDividerWindow); + assertWindowHigher(pinnedWindow, aboveTaskWindow); } @Test |