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

Commit 893296d6 authored by Android Build Coastguard Worker's avatar Android Build Coastguard Worker
Browse files

Merge cherrypicks of ['ag/20941605', 'ag/20946885'] into tm-qpr2-release.

Change-Id: I2f86b8635384aa2a07f47149f3257eb36ef98b9e
parents fd6c648b c262d577
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -791,13 +791,13 @@ private class AnimatedDialog(
        // Move the drawing of the source in the overlay of this dialog, then animate. We trigger a
        // one-off synchronization to make sure that this is done in sync between the two different
        // windows.
        controller.startDrawingInOverlayOf(decorView)
        synchronizeNextDraw(
            then = {
                isSourceDrawnInDialog = true
                maybeStartLaunchAnimation()
            }
        )
        controller.startDrawingInOverlayOf(decorView)
    }

    /**
+17 −10
Original line number Diff line number Diff line
@@ -195,14 +195,16 @@ open class GhostedViewLaunchAnimatorController @JvmOverloads constructor(
        backgroundDrawable = WrappedDrawable(background)
        backgroundView?.background = backgroundDrawable

        // Delay the calls to `ghostedView.setVisibility()` during the animation. This must be
        // called before `GhostView.addGhost()` is called because the latter will change the
        // *transition* visibility, which won't be blocked and will affect the normal View
        // visibility that is saved by `setShouldBlockVisibilityChanges()` for a later restoration.
        (ghostedView as? LaunchableView)?.setShouldBlockVisibilityChanges(true)

        // Create a ghost of the view that will be moving and fading out. This allows to fade out
        // the content before fading out the background.
        ghostView = GhostView.addGhost(ghostedView, launchContainer)

        // The ghost was just created, so ghostedView is currently invisible. We need to make sure
        // that it stays invisible as long as we are animating.
        (ghostedView as? LaunchableView)?.setShouldBlockVisibilityChanges(true)

        val matrix = ghostView?.animationMatrix ?: Matrix.IDENTITY_MATRIX
        matrix.getValues(initialGhostViewMatrixValues)

@@ -297,15 +299,20 @@ open class GhostedViewLaunchAnimatorController @JvmOverloads constructor(
        backgroundDrawable?.wrapped?.alpha = startBackgroundAlpha

        GhostView.removeGhost(ghostedView)
        (ghostedView as? LaunchableView)?.setShouldBlockVisibilityChanges(false)
        launchContainerOverlay.remove(backgroundView)

        // Make sure that the view is considered VISIBLE by accessibility by first making it
        // INVISIBLE then VISIBLE (see b/204944038#comment17 for more info).
        if (ghostedView is LaunchableView) {
            // Restore the ghosted view visibility.
            ghostedView.setShouldBlockVisibilityChanges(false)
        } else {
            // Make the ghosted view visible. We ensure that the view is considered VISIBLE by
            // accessibility by first making it INVISIBLE then VISIBLE (see b/204944038#comment17
            // for more info).
            ghostedView.visibility = View.INVISIBLE
            ghostedView.visibility = View.VISIBLE
            ghostedView.invalidate()
        }
    }

    companion object {
        private const val CORNER_RADIUS_TOP_INDEX = 0
+21 −25
Original line number Diff line number Diff line
@@ -21,15 +21,19 @@ import android.view.View
/** A view that can expand/launch into an app or a dialog. */
interface LaunchableView {
    /**
     * Set whether this view should block/postpone all visibility changes. This ensures that this
     * view:
     * Set whether this view should block/postpone all calls to [View.setVisibility]. This ensures
     * that this view:
     * - remains invisible during the launch animation given that it is ghosted and already drawn
     * somewhere else.
     * - remains invisible as long as a dialog expanded from it is shown.
     * - restores its expected visibility once the dialog expanded from it is dismissed.
     *
     * 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.
     * When `setShouldBlockVisibilityChanges(false)` is called, then visibility of the View should
     * be restored to its expected value, i.e. it should have the visibility of the last call to
     * `View.setVisibility()` that was made after `setShouldBlockVisibilityChanges(true)`, if any,
     * or the original view visibility otherwise.
     *
     * Note that calls to [View.setTransitionVisibility] shouldn't be blocked.
     *
     * @param block whether we should block/postpone all calls to `setVisibility` and
     * `setTransitionVisibility`.
@@ -46,29 +50,33 @@ class LaunchableViewDelegate(
     * super.setVisibility(visibility).
     */
    private val superSetVisibility: (Int) -> Unit,

    /**
     * The lambda that should set the actual transition visibility of [view], usually by calling
     * super.setTransitionVisibility(visibility).
     */
    private val superSetTransitionVisibility: (Int) -> Unit,
) {
) : LaunchableView {
    private var blockVisibilityChanges = false
    private var lastVisibility = view.visibility

    /** Call this when [LaunchableView.setShouldBlockVisibilityChanges] is called. */
    fun setShouldBlockVisibilityChanges(block: Boolean) {
    override fun setShouldBlockVisibilityChanges(block: Boolean) {
        if (block == blockVisibilityChanges) {
            return
        }

        blockVisibilityChanges = block
        if (block) {
            // Save the current visibility for later.
            lastVisibility = view.visibility
        } else {
            // Restore the visibility. To avoid accessibility issues, we change the visibility twice
            // which makes sure that we trigger a visibility flag change (see b/204944038#comment17
            // for more info).
            if (lastVisibility == View.VISIBLE) {
                superSetVisibility(View.INVISIBLE)
                superSetVisibility(View.VISIBLE)
            } else {
                superSetVisibility(View.VISIBLE)
                superSetVisibility(lastVisibility)
            }
        }
    }

    /** Call this when [View.setVisibility] is called. */
    fun setVisibility(visibility: Int) {
@@ -79,16 +87,4 @@ class LaunchableViewDelegate(

        superSetVisibility(visibility)
    }

    /** Call this when [View.setTransitionVisibility] is called. */
    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
        }

        superSetTransitionVisibility(visibility)
    }
}
+33 −21
Original line number Diff line number Diff line
@@ -34,24 +34,30 @@ internal constructor(
    override val sourceIdentity: Any = source

    override fun startDrawingInOverlayOf(viewGroup: ViewGroup) {
        // Delay the calls to `source.setVisibility()` during the animation. This must be called
        // before `GhostView.addGhost()` is called because the latter will change the *transition*
        // visibility, which won't be blocked and will affect the normal View visibility that is
        // saved by `setShouldBlockVisibilityChanges()` for a later restoration.
        (source as? LaunchableView)?.setShouldBlockVisibilityChanges(true)

        // Create a temporary ghost of the source (which will make it invisible) and add it
        // to the host dialog.
        GhostView.addGhost(source, viewGroup)

        // The ghost of the source was just created, so the source is currently invisible.
        // We need to make sure that it stays invisible as long as the dialog is shown or
        // animating.
        (source as? LaunchableView)?.setShouldBlockVisibilityChanges(true)
    }

    override fun stopDrawingInOverlay() {
        // Note: here we should remove the ghost from the overlay, but in practice this is
        // already done by the launch controllers created below.

        // Make sure we allow the source to change its visibility again.
        (source as? LaunchableView)?.setShouldBlockVisibilityChanges(false)
        // already done by the launch controller created below.

        if (source is LaunchableView) {
            // Make sure we allow the source to change its visibility again and restore its previous
            // value.
            source.setShouldBlockVisibilityChanges(false)
        } else {
            // We made the source invisible earlier, so let's make it visible again.
            source.visibility = View.VISIBLE
        }
    }

    override fun createLaunchController(): LaunchAnimator.Controller {
        val delegate = GhostedViewLaunchAnimatorController(source)
@@ -67,13 +73,17 @@ internal constructor(
            override fun onLaunchAnimationEnd(isExpandingFullyAbove: Boolean) {
                delegate.onLaunchAnimationEnd(isExpandingFullyAbove)

                // We hide the source when the dialog is showing. We will make this view
                // visible again when dismissing the dialog. This does nothing if the source
                // implements [LaunchableView], as it's already INVISIBLE in that case.
                // At this point the view visibility is restored by the delegate, so we delay the
                // visibility changes again and make it invisible while the dialog is shown.
                if (source is LaunchableView) {
                    source.setShouldBlockVisibilityChanges(true)
                    source.setTransitionVisibility(View.INVISIBLE)
                } else {
                    source.visibility = View.INVISIBLE
                }
            }
        }
    }

    override fun createExitController(): LaunchAnimator.Controller {
        return GhostedViewLaunchAnimatorController(source)
@@ -90,15 +100,17 @@ internal constructor(
    }

    override fun onExitAnimationCancelled() {
        if (source is LaunchableView) {
            // Make sure we allow the source to change its visibility again.
        (source as? LaunchableView)?.setShouldBlockVisibilityChanges(false)

            source.setShouldBlockVisibilityChanges(false)
        } else {
            // If the view is invisible it's probably because of us, so we make it visible
            // again.
            if (source.visibility == View.INVISIBLE) {
                source.visibility = View.VISIBLE
            }
        }
    }

    override fun jankConfigurationBuilder(): InteractionJankMonitor.Configuration.Builder? {
        val type = cuj?.cujType ?: return null
+0 −5
Original line number Diff line number Diff line
@@ -28,7 +28,6 @@ class LaunchableImageView : ImageView, LaunchableView {
        LaunchableViewDelegate(
            this,
            superSetVisibility = { super.setVisibility(it) },
            superSetTransitionVisibility = { super.setTransitionVisibility(it) },
        )

    constructor(context: Context?) : super(context)
@@ -53,8 +52,4 @@ class LaunchableImageView : ImageView, LaunchableView {
    override fun setVisibility(visibility: Int) {
        delegate.setVisibility(visibility)
    }

    override fun setTransitionVisibility(visibility: Int) {
        delegate.setTransitionVisibility(visibility)
    }
}
Loading