1 /*
2  * Copyright (C) 2021 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.graphics.Rect
20 import android.view.View
21 
22 /** A view that can expand/launch into an app or a dialog. */
23 interface LaunchableView {
24     /**
25      * Set whether this view should block/postpone all calls to [View.setVisibility]. This ensures
26      * that this view:
27      * - remains invisible during the launch animation given that it is ghosted and already drawn
28      *   somewhere else.
29      * - remains invisible as long as a dialog expanded from it is shown.
30      * - restores its expected visibility once the dialog expanded from it is dismissed.
31      *
32      * When `setShouldBlockVisibilityChanges(false)` is called, then visibility of the View should
33      * be restored to its expected value, i.e. it should have the visibility of the last call to
34      * `View.setVisibility()` that was made after `setShouldBlockVisibilityChanges(true)`, if any,
35      * or the original view visibility otherwise.
36      *
37      * Note that calls to [View.setTransitionVisibility] shouldn't be blocked.
38      *
39      * @param block whether we should block/postpone all calls to `setVisibility`.
40      */
setShouldBlockVisibilityChangesnull41     fun setShouldBlockVisibilityChanges(block: Boolean)
42 
43     /** Perform an action when the activity launch animation ends */
44     fun onActivityLaunchAnimationEnd() {}
45 
46     /** Provide an optional correction applied to the visible area during a launch animation */
getPaddingForLaunchAnimationnull47     fun getPaddingForLaunchAnimation(): Rect = Rect()
48 }
49 
50 /** A delegate that can be used by views to make the implementation of [LaunchableView] easier. */
51 class LaunchableViewDelegate(
52     private val view: View,
53 
54     /**
55      * The lambda that should set the actual visibility of [view], usually by calling
56      * super.setVisibility(visibility).
57      */
58     private val superSetVisibility: (Int) -> Unit,
59 ) : LaunchableView {
60     private var blockVisibilityChanges = false
61     private var lastVisibility = view.visibility
62 
63     /** Call this when [LaunchableView.setShouldBlockVisibilityChanges] is called. */
64     override fun setShouldBlockVisibilityChanges(block: Boolean) {
65         if (block == blockVisibilityChanges) {
66             return
67         }
68 
69         blockVisibilityChanges = block
70         if (block) {
71             // Save the current visibility for later.
72             lastVisibility = view.visibility
73         } else {
74             // Restore the visibility. To avoid accessibility issues, we change the visibility twice
75             // which makes sure that we trigger a visibility flag change (see b/204944038#comment17
76             // for more info).
77             if (lastVisibility == View.VISIBLE) {
78                 superSetVisibility(View.INVISIBLE)
79                 superSetVisibility(View.VISIBLE)
80             } else {
81                 superSetVisibility(View.VISIBLE)
82                 superSetVisibility(lastVisibility)
83             }
84         }
85     }
86 
87     /** Call this when [View.setVisibility] is called. */
88     fun setVisibility(visibility: Int) {
89         if (blockVisibilityChanges) {
90             lastVisibility = visibility
91             return
92         }
93 
94         superSetVisibility(visibility)
95     }
96 }
97