1 /*
2  * Copyright (C) 2022 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.systemui.animation
18 
19 import android.util.Log
20 import android.view.GhostView
21 import android.view.View
22 import android.view.ViewGroup
23 import android.view.ViewRootImpl
24 import com.android.internal.jank.InteractionJankMonitor
25 
26 private const val TAG = "ViewDialogTransitionAnimatorController"
27 
28 /** A [DialogTransitionAnimator.Controller] that can animate a [View] from/to a dialog. */
29 class ViewDialogTransitionAnimatorController
30 internal constructor(
31     private val source: View,
32     override val cuj: DialogCuj?,
33 ) : DialogTransitionAnimator.Controller {
34     override val viewRoot: ViewRootImpl?
35         get() = source.viewRootImpl
36 
37     override val sourceIdentity: Any = source
38 
startDrawingInOverlayOfnull39     override fun startDrawingInOverlayOf(viewGroup: ViewGroup) {
40         // Delay the calls to `source.setVisibility()` during the animation. This must be called
41         // before `GhostView.addGhost()` is called because the latter will change the *transition*
42         // visibility, which won't be blocked and will affect the normal View visibility that is
43         // saved by `setShouldBlockVisibilityChanges()` for a later restoration.
44         (source as? LaunchableView)?.setShouldBlockVisibilityChanges(true)
45 
46         // Create a temporary ghost of the source (which will make it invisible) and add it
47         // to the host dialog.
48         if (source.parent !is ViewGroup) {
49             // This should usually not happen, but let's make sure we don't call GhostView.addGhost
50             // and crash if the view was detached right before we started the animation.
51             Log.w(TAG, "source was detached right before drawing was moved to overlay")
52         } else {
53             GhostView.addGhost(source, viewGroup)
54         }
55     }
56 
stopDrawingInOverlaynull57     override fun stopDrawingInOverlay() {
58         // Note: here we should remove the ghost from the overlay, but in practice this is
59         // already done by the transition controller created below.
60 
61         if (source is LaunchableView) {
62             // Make sure we allow the source to change its visibility again and restore its previous
63             // value.
64             source.setShouldBlockVisibilityChanges(false)
65         } else {
66             // We made the source invisible earlier, so let's make it visible again.
67             source.visibility = View.VISIBLE
68         }
69     }
70 
createTransitionControllernull71     override fun createTransitionController(): TransitionAnimator.Controller {
72         val delegate = GhostedViewTransitionAnimatorController(source)
73         return object : TransitionAnimator.Controller by delegate {
74             override fun onTransitionAnimationStart(isExpandingFullyAbove: Boolean) {
75                 // Remove the temporary ghost added by [startDrawingInOverlayOf]. Another
76                 // ghost (that ghosts only the source content, and not its background) will
77                 // be added right after this by the delegate and will be animated.
78                 GhostView.removeGhost(source)
79                 delegate.onTransitionAnimationStart(isExpandingFullyAbove)
80             }
81 
82             override fun onTransitionAnimationEnd(isExpandingFullyAbove: Boolean) {
83                 delegate.onTransitionAnimationEnd(isExpandingFullyAbove)
84 
85                 // At this point the view visibility is restored by the delegate, so we delay the
86                 // visibility changes again and make it invisible while the dialog is shown.
87                 if (source is LaunchableView) {
88                     source.setShouldBlockVisibilityChanges(true)
89                     source.setTransitionVisibility(View.INVISIBLE)
90                 } else {
91                     source.visibility = View.INVISIBLE
92                 }
93             }
94         }
95     }
96 
createExitControllernull97     override fun createExitController(): TransitionAnimator.Controller {
98         return GhostedViewTransitionAnimatorController(source)
99     }
100 
shouldAnimateExitnull101     override fun shouldAnimateExit(): Boolean {
102         // The source should be invisible by now, if it's not then something else changed
103         // its visibility and we probably don't want to run the animation.
104         if (source.visibility != View.INVISIBLE) {
105             return false
106         }
107 
108         return source.isAttachedToWindow && ((source.parent as? View)?.isShown ?: true)
109     }
110 
onExitAnimationCancellednull111     override fun onExitAnimationCancelled() {
112         if (source is LaunchableView) {
113             // Make sure we allow the source to change its visibility again.
114             source.setShouldBlockVisibilityChanges(false)
115         } else {
116             // If the view is invisible it's probably because of us, so we make it visible
117             // again.
118             if (source.visibility == View.INVISIBLE) {
119                 source.visibility = View.VISIBLE
120             }
121         }
122     }
123 
jankConfigurationBuildernull124     override fun jankConfigurationBuilder(): InteractionJankMonitor.Configuration.Builder? {
125         val type = cuj?.cujType ?: return null
126         return InteractionJankMonitor.Configuration.Builder.withView(type, source)
127     }
128 }
129