diff options
21 files changed, 242 insertions, 93 deletions
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt index c729b09628af..17a94b8639d0 100644 --- a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt +++ b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt @@ -41,6 +41,7 @@ import androidx.annotation.BinderThread import androidx.annotation.UiThread import com.android.internal.annotations.VisibleForTesting import com.android.internal.policy.ScreenDecorationsUtils +import java.lang.IllegalArgumentException import kotlin.math.roundToInt private const val TAG = "ActivityLaunchAnimator" @@ -338,13 +339,24 @@ class ActivityLaunchAnimator( * Return a [Controller] that will animate and expand [view] into the opening window. * * Important: The view must be attached to a [ViewGroup] when calling this function and - * during the animation. For safety, this method will return null when it is not. + * during the animation. For safety, this method will return null when it is not. The + * view must also implement [LaunchableView], otherwise this method will throw. * * Note: The background of [view] should be a (rounded) rectangle so that it can be * properly animated. */ @JvmStatic fun fromView(view: View, cujType: Int? = null): Controller? { + // Make sure the View we launch from implements LaunchableView to avoid visibility + // issues. + if (view !is LaunchableView) { + throw IllegalArgumentException( + "An ActivityLaunchAnimator.Controller was created from a View that does " + + "not implement LaunchableView. This can lead to subtle bugs where the" + + " visibility of the View we are launching from is not what we expected." + ) + } + if (view.parent !is ViewGroup) { Log.e( TAG, 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 e91a6710ace5..b8d78fb208f4 100644 --- a/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt +++ b/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt @@ -40,6 +40,7 @@ import com.android.systemui.animation.back.BackAnimationSpec import com.android.systemui.animation.back.applyTo import com.android.systemui.animation.back.floatingSystemSurfacesForSysUi import com.android.systemui.animation.back.onBackAnimationCallbackFrom +import java.lang.IllegalArgumentException import kotlin.math.roundToInt private const val TAG = "DialogLaunchAnimator" @@ -157,12 +158,23 @@ constructor( * Create a [Controller] that can animate [source] to and from a dialog. * * Important: The view must be attached to a [ViewGroup] when calling this function and - * during the animation. For safety, this method will return null when it is not. + * during the animation. For safety, this method will return null when it is not. The + * view must also implement [LaunchableView], otherwise this method will throw. * * Note: The background of [view] should be a (rounded) rectangle so that it can be * properly animated. */ fun fromView(source: View, cuj: DialogCuj? = null): Controller? { + // Make sure the View we launch from implements LaunchableView to avoid visibility + // issues. + if (source !is LaunchableView) { + throw IllegalArgumentException( + "A DialogLaunchAnimator.Controller was created from a View that does not " + + "implement LaunchableView. This can lead to subtle bugs where the " + + "visibility of the View we are launching from is not what we expected." + ) + } + if (source.parent !is ViewGroup) { Log.e( TAG, @@ -249,23 +261,6 @@ constructor( } ?: controller - if ( - animatedParent == null && - controller is ViewDialogLaunchAnimatorController && - controller.source !is LaunchableView - ) { - // Make sure the View we launch from implements LaunchableView to avoid visibility - // issues. Given that we don't own dialog decorViews so we can't enforce it for launches - // from a dialog. - // TODO(b/243636422): Throw instead of logging to enforce this. - Log.w( - TAG, - "A dialog was launched from a View that does not implement LaunchableView. This " + - "can lead to subtle bugs where the visibility of the View we are " + - "launching from is not what we expected." - ) - } - // Make sure we don't run the launch animation from the same source twice at the same time. if (openedDialogs.any { it.controller.sourceIdentity == controller.sourceIdentity }) { Log.e( @@ -613,10 +608,16 @@ private class AnimatedDialog( } // Animate that view with the background. Throw if we didn't find one, because - // otherwise - // it's not clear what we should animate. + // otherwise it's not clear what we should animate. + if (viewGroupWithBackground == null) { + error("Unable to find ViewGroup with background") + } + + if (viewGroupWithBackground !is LaunchableView) { + error("The animated ViewGroup with background must implement LaunchableView") + } + viewGroupWithBackground - ?: throw IllegalStateException("Unable to find ViewGroup with background") } else { // We will make the dialog window (and therefore its DecorView) fullscreen to make // it possible to animate outside its bounds. @@ -639,7 +640,7 @@ private class AnimatedDialog( FrameLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT) ) - val dialogContentWithBackground = FrameLayout(dialog.context) + val dialogContentWithBackground = LaunchableFrameLayout(dialog.context) dialogContentWithBackground.background = decorView.background // Make the window background transparent. Note that setting the window (or @@ -720,7 +721,10 @@ private class AnimatedDialog( // Make the background view invisible until we start the animation. We use the transition // visibility like GhostView does so that we don't mess up with the accessibility tree (see - // b/204944038#comment17). + // b/204944038#comment17). Given that this background implements LaunchableView, we call + // setShouldBlockVisibilityChanges() early so that the current visibility (VISIBLE) is + // restored at the end of the animation. + dialogContentWithBackground.setShouldBlockVisibilityChanges(true) dialogContentWithBackground.setTransitionVisibility(View.INVISIBLE) // Make sure the dialog is visible instantly and does not do any window animation. diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt index 26aa0e85cbd2..23e3a01c2686 100644 --- a/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt +++ b/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt @@ -34,6 +34,7 @@ import android.view.ViewGroup import android.view.ViewGroupOverlay import android.widget.FrameLayout import com.android.internal.jank.InteractionJankMonitor +import java.lang.IllegalArgumentException import java.util.LinkedList import kotlin.math.min import kotlin.math.roundToInt @@ -46,7 +47,8 @@ private const val TAG = "GhostedViewLaunchAnimatorController" * of the ghosted view. * * Important: [ghostedView] must be attached to a [ViewGroup] when calling this function and during - * the animation. + * the animation. It must also implement [LaunchableView], otherwise an exception will be thrown + * during this controller instantiation. * * Note: Avoid instantiating this directly and call [ActivityLaunchAnimator.Controller.fromView] * whenever possible instead. @@ -101,6 +103,15 @@ constructor( private val background: Drawable? init { + // Make sure the View we launch from implements LaunchableView to avoid visibility issues. + if (ghostedView !is LaunchableView) { + throw IllegalArgumentException( + "A GhostedViewLaunchAnimatorController was created from a View that does not " + + "implement LaunchableView. This can lead to subtle bugs where the visibility " + + "of the View we are launching from is not what we expected." + ) + } + /** Find the first view with a background in [view] and its children. */ fun findBackground(view: View): Drawable? { if (view.background != null) { diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/LaunchableFrameLayout.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/LaunchableFrameLayout.kt new file mode 100644 index 000000000000..2eb503b43cc5 --- /dev/null +++ b/packages/SystemUI/animation/src/com/android/systemui/animation/LaunchableFrameLayout.kt @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2022 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 + +import android.content.Context +import android.util.AttributeSet +import android.widget.FrameLayout + +/** A [FrameLayout] that also implements [LaunchableView]. */ +open class LaunchableFrameLayout : FrameLayout, LaunchableView { + private val delegate = + LaunchableViewDelegate( + this, + superSetVisibility = { super.setVisibility(it) }, + ) + + constructor(context: Context) : super(context) + constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) + constructor( + context: Context, + attrs: AttributeSet?, + defStyleAttr: Int + ) : super(context, attrs, defStyleAttr) + + constructor( + context: Context, + attrs: AttributeSet?, + defStyleAttr: Int, + defStyleRes: Int + ) : super(context, attrs, defStyleAttr, defStyleRes) + + override fun setShouldBlockVisibilityChanges(block: Boolean) { + delegate.setShouldBlockVisibilityChanges(block) + } + + override fun setVisibility(visibility: Int) { + delegate.setVisibility(visibility) + } +} diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/ViewDialogLaunchAnimatorController.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/ViewDialogLaunchAnimatorController.kt index 9257f99efe96..46d5a5c0af8c 100644 --- a/packages/SystemUI/animation/src/com/android/systemui/animation/ViewDialogLaunchAnimatorController.kt +++ b/packages/SystemUI/animation/src/com/android/systemui/animation/ViewDialogLaunchAnimatorController.kt @@ -25,7 +25,7 @@ import com.android.internal.jank.InteractionJankMonitor /** A [DialogLaunchAnimator.Controller] that can animate a [View] from/to a dialog. */ class ViewDialogLaunchAnimatorController internal constructor( - internal val source: View, + private val source: View, override val cuj: DialogCuj?, ) : DialogLaunchAnimator.Controller { override val viewRoot: ViewRootImpl? diff --git a/packages/SystemUI/res/layout/dream_overlay_home_controls_chip.xml b/packages/SystemUI/res/layout/dream_overlay_home_controls_chip.xml index de96e9765668..446bb0122630 100644 --- a/packages/SystemUI/res/layout/dream_overlay_home_controls_chip.xml +++ b/packages/SystemUI/res/layout/dream_overlay_home_controls_chip.xml @@ -20,7 +20,7 @@ android:layout_width="wrap_content" android:paddingVertical="@dimen/dream_overlay_complication_home_controls_padding"> - <ImageView + <com.android.systemui.common.ui.view.LaunchableImageView android:id="@+id/home_controls_chip" android:layout_height="@dimen/keyguard_affordance_fixed_height" android:layout_width="@dimen/keyguard_affordance_fixed_width" diff --git a/packages/SystemUI/res/layout/global_actions_grid_lite.xml b/packages/SystemUI/res/layout/global_actions_grid_lite.xml index 5588fd391681..a64c9aebec3e 100644 --- a/packages/SystemUI/res/layout/global_actions_grid_lite.xml +++ b/packages/SystemUI/res/layout/global_actions_grid_lite.xml @@ -33,7 +33,7 @@ app:layout_constraintStart_toStartOf="parent" app:layout_constraintEnd_toEndOf="parent" android:layout_weight="1"> - <androidx.constraintlayout.widget.ConstraintLayout + <com.android.systemui.common.ui.view.LaunchableConstraintLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@android:id/list" @@ -55,6 +55,6 @@ app:flow_horizontalGap="@dimen/global_actions_lite_padding" app:flow_verticalGap="@dimen/global_actions_lite_padding" app:flow_horizontalStyle="packed"/> - </androidx.constraintlayout.widget.ConstraintLayout> + </com.android.systemui.common.ui.view.LaunchableConstraintLayout> </com.android.systemui.globalactions.GlobalActionsLayoutLite> </androidx.constraintlayout.widget.ConstraintLayout> diff --git a/packages/SystemUI/res/layout/keyguard_qs_user_switch.xml b/packages/SystemUI/res/layout/keyguard_qs_user_switch.xml index 6f3362308484..07c428b5dd7a 100644 --- a/packages/SystemUI/res/layout/keyguard_qs_user_switch.xml +++ b/packages/SystemUI/res/layout/keyguard_qs_user_switch.xml @@ -24,7 +24,7 @@ android:layout_gravity="end"> <!-- We add a background behind the UserAvatarView with the same color and with a circular shape so that this view can be expanded into a Dialog or an Activity. --> - <FrameLayout + <com.android.systemui.animation.LaunchableFrameLayout android:id="@+id/kg_multi_user_avatar_with_background" android:layout_width="wrap_content" android:layout_height="wrap_content" @@ -42,5 +42,5 @@ systemui:framePadding="0dp" systemui:frameWidth="0dp"> </com.android.systemui.statusbar.phone.UserAvatarView> - </FrameLayout> + </com.android.systemui.animation.LaunchableFrameLayout> </FrameLayout>
\ No newline at end of file diff --git a/packages/SystemUI/res/layout/media_session_view.xml b/packages/SystemUI/res/layout/media_session_view.xml index abc83379950a..f2e114b511bc 100644 --- a/packages/SystemUI/res/layout/media_session_view.xml +++ b/packages/SystemUI/res/layout/media_session_view.xml @@ -106,7 +106,7 @@ app:layout_constrainedWidth="true" app:layout_constraintWidth_min="@dimen/min_clickable_item_size" app:layout_constraintHeight_min="@dimen/min_clickable_item_size"> - <LinearLayout + <com.android.systemui.common.ui.view.LaunchableLinearLayout android:id="@+id/media_seamless_button" android:layout_width="wrap_content" android:layout_height="wrap_content" @@ -135,7 +135,7 @@ android:textDirection="locale" android:textSize="12sp" android:lineHeight="16sp" /> - </LinearLayout> + </com.android.systemui.common.ui.view.LaunchableLinearLayout> </LinearLayout> <!-- Song name --> diff --git a/packages/SystemUI/res/layout/ongoing_call_chip.xml b/packages/SystemUI/res/layout/ongoing_call_chip.xml index c949ba0db171..18d231c5d32f 100644 --- a/packages/SystemUI/res/layout/ongoing_call_chip.xml +++ b/packages/SystemUI/res/layout/ongoing_call_chip.xml @@ -23,7 +23,7 @@ android:layout_gravity="center_vertical|start" android:layout_marginStart="5dp" > - <LinearLayout + <com.android.systemui.common.ui.view.LaunchableLinearLayout android:id="@+id/ongoing_call_chip_background" android:layout_width="wrap_content" android:layout_height="@dimen/ongoing_appops_chip_height" @@ -55,5 +55,5 @@ android:textColor="?android:attr/colorPrimary" /> - </LinearLayout> + </com.android.systemui.common.ui.view.LaunchableLinearLayout> </FrameLayout> diff --git a/packages/SystemUI/src/com/android/systemui/common/ui/view/LaunchableConstraintLayout.kt b/packages/SystemUI/src/com/android/systemui/common/ui/view/LaunchableConstraintLayout.kt new file mode 100644 index 000000000000..9763665c5b7c --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/common/ui/view/LaunchableConstraintLayout.kt @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2022 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.common.ui.view + +import android.content.Context +import android.util.AttributeSet +import androidx.constraintlayout.widget.ConstraintLayout +import com.android.systemui.animation.LaunchableView +import com.android.systemui.animation.LaunchableViewDelegate + +/** A [ConstraintLayout] that also implements [LaunchableView]. */ +open class LaunchableConstraintLayout : ConstraintLayout, LaunchableView { + private val delegate = + LaunchableViewDelegate( + this, + superSetVisibility = { super.setVisibility(it) }, + ) + + constructor(context: Context) : super(context) + constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) + constructor( + context: Context, + attrs: AttributeSet?, + defStyleAttr: Int + ) : super(context, attrs, defStyleAttr) + + constructor( + context: Context, + attrs: AttributeSet?, + defStyleAttr: Int, + defStyleRes: Int + ) : super(context, attrs, defStyleAttr, defStyleRes) + + override fun setShouldBlockVisibilityChanges(block: Boolean) { + delegate.setShouldBlockVisibilityChanges(block) + } + + override fun setVisibility(visibility: Int) { + delegate.setVisibility(visibility) + } +} diff --git a/packages/SystemUI/src/com/android/systemui/common/ui/view/LaunchableLinearLayout.kt b/packages/SystemUI/src/com/android/systemui/common/ui/view/LaunchableLinearLayout.kt index ddde6280f3a2..2edac528b037 100644 --- a/packages/SystemUI/src/com/android/systemui/common/ui/view/LaunchableLinearLayout.kt +++ b/packages/SystemUI/src/com/android/systemui/common/ui/view/LaunchableLinearLayout.kt @@ -23,7 +23,7 @@ import com.android.systemui.animation.LaunchableView import com.android.systemui.animation.LaunchableViewDelegate /** A [LinearLayout] that also implements [LaunchableView]. */ -class LaunchableLinearLayout : LinearLayout, LaunchableView { +open class LaunchableLinearLayout : LinearLayout, LaunchableView { private val delegate = LaunchableViewDelegate( this, diff --git a/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt b/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt index 2a6ca1acb38e..8ad2f867a073 100644 --- a/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt +++ b/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt @@ -17,11 +17,11 @@ package com.android.systemui.privacy import android.content.Context import android.util.AttributeSet import android.view.ViewGroup -import android.widget.FrameLayout import android.widget.ImageView import android.widget.LinearLayout import com.android.settingslib.Utils import com.android.systemui.R +import com.android.systemui.animation.LaunchableFrameLayout import com.android.systemui.statusbar.events.BackgroundAnimatableView class OngoingPrivacyChip @JvmOverloads constructor( @@ -29,7 +29,7 @@ class OngoingPrivacyChip @JvmOverloads constructor( attrs: AttributeSet? = null, defStyleAttrs: Int = 0, defStyleRes: Int = 0 -) : FrameLayout(context, attrs, defStyleAttrs, defStyleRes), BackgroundAnimatableView { +) : LaunchableFrameLayout(context, attrs, defStyleAttrs, defStyleRes), BackgroundAnimatableView { private var iconMargin = 0 private var iconSize = 0 diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/AlphaOptimizedButton.java b/packages/SystemUI/src/com/android/systemui/statusbar/AlphaOptimizedButton.java index 87c12c25a0a5..6577cf696b61 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/AlphaOptimizedButton.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/AlphaOptimizedButton.java @@ -20,10 +20,21 @@ import android.content.Context; import android.util.AttributeSet; import android.widget.Button; +import com.android.systemui.animation.LaunchableView; +import com.android.systemui.animation.LaunchableViewDelegate; + +import kotlin.Unit; + /** * A Button which doesn't have overlapping drawing commands */ -public class AlphaOptimizedButton extends Button { +public class AlphaOptimizedButton extends Button implements LaunchableView { + private LaunchableViewDelegate mDelegate = new LaunchableViewDelegate(this, + (visibility) -> { + super.setVisibility(visibility); + return Unit.INSTANCE; + }); + public AlphaOptimizedButton(Context context) { super(context); } @@ -45,4 +56,14 @@ public class AlphaOptimizedButton extends Button { public boolean hasOverlappingRendering() { return false; } + + @Override + public void setShouldBlockVisibilityChanges(boolean block) { + mDelegate.setShouldBlockVisibilityChanges(block); + } + + @Override + public void setVisibility(int visibility) { + mDelegate.setVisibility(visibility); + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/userswitcher/StatusBarUserSwitcherContainer.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/userswitcher/StatusBarUserSwitcherContainer.kt index 2c8677dee4d9..2d80edb8d2f4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/userswitcher/StatusBarUserSwitcherContainer.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/userswitcher/StatusBarUserSwitcherContainer.kt @@ -19,14 +19,14 @@ package com.android.systemui.statusbar.phone.userswitcher import android.content.Context import android.util.AttributeSet import android.widget.ImageView -import android.widget.LinearLayout import android.widget.TextView import com.android.systemui.R +import com.android.systemui.common.ui.view.LaunchableLinearLayout class StatusBarUserSwitcherContainer( context: Context?, attrs: AttributeSet? -) : LinearLayout(context, attrs) { +) : LaunchableLinearLayout(context, attrs) { lateinit var text: TextView private set lateinit var avatar: ImageView diff --git a/packages/SystemUI/src/com/android/systemui/util/animation/TransitionLayout.kt b/packages/SystemUI/src/com/android/systemui/util/animation/TransitionLayout.kt index 08ee0af17fb0..56c5d3b433ff 100644 --- a/packages/SystemUI/src/com/android/systemui/util/animation/TransitionLayout.kt +++ b/packages/SystemUI/src/com/android/systemui/util/animation/TransitionLayout.kt @@ -27,6 +27,8 @@ import android.view.ViewTreeObserver import android.widget.TextView import androidx.constraintlayout.widget.ConstraintLayout import androidx.constraintlayout.widget.ConstraintSet +import com.android.systemui.animation.LaunchableView +import com.android.systemui.animation.LaunchableViewDelegate import com.android.systemui.statusbar.CrossFadeHelper /** @@ -38,7 +40,7 @@ class TransitionLayout @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0 -) : ConstraintLayout(context, attrs, defStyleAttr) { +) : ConstraintLayout(context, attrs, defStyleAttr), LaunchableView { private val boundsRect = Rect() private val originalGoneChildrenSet: MutableSet<Int> = mutableSetOf() @@ -50,7 +52,11 @@ class TransitionLayout @JvmOverloads constructor( private var desiredMeasureWidth = 0 private var desiredMeasureHeight = 0 - private var transitionVisibility = View.VISIBLE + private val delegate = + LaunchableViewDelegate( + this, + superSetVisibility = { super.setVisibility(it) }, + ) /** * The measured state of this view which is the one we will lay ourselves out with. This @@ -83,11 +89,12 @@ class TransitionLayout @JvmOverloads constructor( } } - override fun setTransitionVisibility(visibility: Int) { - // We store the last transition visibility assigned to this view to restore it later if - // necessary. - super.setTransitionVisibility(visibility) - transitionVisibility = visibility + override fun setShouldBlockVisibilityChanges(block: Boolean) { + delegate.setShouldBlockVisibilityChanges(block) + } + + override fun setVisibility(visibility: Int) { + delegate.setVisibility(visibility) } override fun onFinishInflate() { @@ -173,14 +180,6 @@ class TransitionLayout @JvmOverloads constructor( translationY = currentState.translation.y CrossFadeHelper.fadeIn(this, currentState.alpha) - - // CrossFadeHelper#fadeIn will change this view visibility, which overrides the transition - // visibility. We set the transition visibility again to make sure that this view plays well - // with GhostView, which sets the transition visibility and is used for activity launch - // animations. - if (transitionVisibility != View.VISIBLE) { - setTransitionVisibility(transitionVisibility) - } } private fun applyCurrentStateOnPredraw() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityLaunchAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityLaunchAnimatorTest.kt index a61cd23b60fc..578e1d4d02ce 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityLaunchAnimatorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityLaunchAnimatorTest.kt @@ -15,6 +15,7 @@ import android.view.RemoteAnimationAdapter import android.view.RemoteAnimationTarget import android.view.SurfaceControl import android.view.ViewGroup +import android.widget.FrameLayout import android.widget.LinearLayout import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase @@ -26,6 +27,7 @@ import junit.framework.Assert.assertTrue import junit.framework.AssertionFailedError import kotlin.concurrent.thread import org.junit.After +import org.junit.Assert.assertThrows import org.junit.Before import org.junit.Rule import org.junit.Test @@ -195,6 +197,13 @@ class ActivityLaunchAnimatorTest : SysuiTestCase() { verify(controller).onLaunchAnimationStart(anyBoolean()) } + @Test + fun creatingControllerFromNormalViewThrows() { + assertThrows(IllegalArgumentException::class.java) { + ActivityLaunchAnimator.Controller.fromView(FrameLayout(mContext)) + } + } + private fun fakeWindow(): RemoteAnimationTarget { val bounds = Rect(10 /* left */, 20 /* top */, 30 /* right */, 40 /* bottom */) val taskInfo = ActivityManager.RunningTaskInfo() diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/DialogLaunchAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/DialogLaunchAnimatorTest.kt index cac4a0e5432c..1e62fd2332c3 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/animation/DialogLaunchAnimatorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/animation/DialogLaunchAnimatorTest.kt @@ -26,6 +26,7 @@ import junit.framework.Assert.assertNull import junit.framework.Assert.assertTrue import org.junit.After import org.junit.Assert.assertNotEquals +import org.junit.Assert.assertThrows import org.junit.Before import org.junit.Rule import org.junit.Test @@ -260,6 +261,13 @@ class DialogLaunchAnimatorTest : SysuiTestCase() { assertThat(touchSurface.visibility).isEqualTo(View.GONE) } + @Test + fun creatingControllerFromNormalViewThrows() { + assertThrows(IllegalArgumentException::class.java) { + DialogLaunchAnimator.Controller.fromView(FrameLayout(mContext)) + } + } + private fun createAndShowDialog( animator: DialogLaunchAnimator = dialogLaunchAnimator, ): TestDialog { diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/GhostedViewLaunchAnimatorControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/GhostedViewLaunchAnimatorControllerTest.kt index 3696ec540baf..0798d73cc2f2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/animation/GhostedViewLaunchAnimatorControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/animation/GhostedViewLaunchAnimatorControllerTest.kt @@ -16,58 +16,34 @@ package com.android.systemui.animation -import android.graphics.drawable.Drawable import android.testing.AndroidTestingRunner import android.testing.TestableLooper -import android.view.View -import android.view.ViewGroup -import android.view.ViewParent +import android.widget.FrameLayout import androidx.test.filters.SmallTest -import com.android.internal.jank.InteractionJankMonitor import com.android.systemui.SysuiTestCase -import org.junit.Before +import org.junit.Assert.assertThrows import org.junit.Test import org.junit.runner.RunWith -import org.mockito.ArgumentMatchers.any -import org.mockito.ArgumentMatchers.anyInt -import org.mockito.Mock -import org.mockito.Mockito.`when` as whenever -import org.mockito.MockitoAnnotations @SmallTest @RunWith(AndroidTestingRunner::class) @TestableLooper.RunWithLooper class GhostedViewLaunchAnimatorControllerTest : SysuiTestCase() { - @Mock lateinit var interactionJankMonitor: InteractionJankMonitor - @Mock lateinit var view: View - @Mock lateinit var rootView: ViewGroup - @Mock lateinit var viewParent: ViewParent - @Mock lateinit var drawable: Drawable - lateinit var controller: GhostedViewLaunchAnimatorController - - @Before - fun setup() { - MockitoAnnotations.initMocks(this) - whenever(view.rootView).thenReturn(rootView) - whenever(view.background).thenReturn(drawable) - whenever(view.height).thenReturn(0) - whenever(view.width).thenReturn(0) - whenever(view.parent).thenReturn(viewParent) - whenever(view.visibility).thenReturn(View.VISIBLE) - whenever(view.invalidate()).then { /* NO-OP */ } - whenever(view.getLocationOnScreen(any())).then { /* NO-OP */ } - whenever(interactionJankMonitor.begin(any(), anyInt())).thenReturn(true) - whenever(interactionJankMonitor.end(anyInt())).thenReturn(true) - controller = GhostedViewLaunchAnimatorController(view, 0, interactionJankMonitor) - } - @Test fun animatingOrphanViewDoesNotCrash() { val state = LaunchAnimator.State(top = 0, bottom = 0, left = 0, right = 0) + val controller = GhostedViewLaunchAnimatorController(LaunchableFrameLayout(mContext)) controller.onIntentStarted(willAnimate = true) controller.onLaunchAnimationStart(isExpandingFullyAbove = true) controller.onLaunchAnimationProgress(state, progress = 0f, linearProgress = 0f) controller.onLaunchAnimationEnd(isExpandingFullyAbove = true) } + + @Test + fun creatingControllerFromNormalViewThrows() { + assertThrows(IllegalArgumentException::class.java) { + GhostedViewLaunchAnimatorController(FrameLayout(mContext)) + } + } }
\ No newline at end of file diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/DreamHomeControlsComplicationTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/DreamHomeControlsComplicationTest.java index 89c728082cc5..a4cf15c3fafa 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/DreamHomeControlsComplicationTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/DreamHomeControlsComplicationTest.java @@ -31,13 +31,13 @@ import android.content.ComponentName; import android.content.Context; import android.testing.AndroidTestingRunner; import android.view.View; -import android.widget.ImageView; import androidx.test.filters.SmallTest; import com.android.internal.logging.UiEventLogger; import com.android.systemui.R; import com.android.systemui.SysuiTestCase; +import com.android.systemui.common.ui.view.LaunchableImageView; import com.android.systemui.controls.ControlsServiceInfo; import com.android.systemui.controls.controller.ControlsController; import com.android.systemui.controls.controller.StructureInfo; @@ -90,7 +90,7 @@ public class DreamHomeControlsComplicationTest extends SysuiTestCase { private View mView; @Mock - private ImageView mHomeControlsView; + private LaunchableImageView mHomeControlsView; @Mock private ActivityStarter mActivityStarter; diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt index 2bd068a674ae..4e9590231a19 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt @@ -35,6 +35,7 @@ import android.view.View import com.android.internal.logging.MetricsLogger import com.android.systemui.SysuiTestCase import com.android.systemui.animation.ActivityLaunchAnimator +import com.android.systemui.animation.LaunchableFrameLayout import com.android.systemui.classifier.FalsingManagerFake import com.android.systemui.plugins.ActivityStarter import com.android.systemui.plugins.qs.QSTile @@ -339,7 +340,7 @@ class CustomTileTest : SysuiTestCase() { val tile = CustomTile.create(customTileBuilder, TILE_SPEC, mContext) tile.qsTile.activityLaunchForClick = pi - tile.handleClick(mock(View::class.java)) + tile.handleClick(mock(LaunchableFrameLayout::class.java)) testableLooper.processAllMessages() |