Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 49a61e54 authored by Jordan Demeulenaere's avatar Jordan Demeulenaere Committed by Android (Google) Code Review
Browse files

Merge "Ensure that launched Views implement LaunchableView" into tm-qpr-dev

parents 0f1270b1 859ccdbd
Loading
Loading
Loading
Loading
+13 −1
Original line number Diff line number Diff line
@@ -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,
+27 −23
Original line number Diff line number Diff line
@@ -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.
+12 −1
Original line number Diff line number Diff line
@@ -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) {
+53 −0
Original line number Diff line number Diff line
/*
 * 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)
    }
}
+1 −1
Original line number Diff line number Diff line
@@ -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?
Loading