• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
<lambda>null2  * 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.shared.system.smartspace
18 
19 import android.graphics.Rect
20 import android.view.View
21 import com.android.systemui.shared.system.ActivityManagerWrapper
22 import com.android.systemui.shared.system.QuickStepContract
23 import kotlin.math.min
24 
25 /**
26  * Controller that keeps track of SmartSpace instances in remote processes (such as Launcher),
27  * allowing System UI to query or update their state during shared-element transitions.
28  */
29 class SmartspaceTransitionController {
30 
31     /**
32      * Implementation of [ISmartspaceTransitionController] that we provide to Launcher, allowing it
33      * to provide us with a callback to query and update the state of its Smartspace.
34      */
35     private val ISmartspaceTransitionController = object : ISmartspaceTransitionController.Stub() {
36         override fun setSmartspace(callback: ISmartspaceCallback?) {
37             this@SmartspaceTransitionController.launcherSmartspace = callback
38             updateLauncherSmartSpaceState()
39         }
40     }
41 
42     /**
43      * Callback provided by Launcher to allow us to query and update the state of its SmartSpace.
44      */
45     public var launcherSmartspace: ISmartspaceCallback? = null
46 
47     public var lockscreenSmartspace: View? = null
48 
49     /**
50      * Cached state of the Launcher SmartSpace. Retrieving the state is an IPC, so we should avoid
51      * unnecessary
52      */
53     public var mLauncherSmartspaceState: SmartspaceState? = null
54 
55     /**
56      * The bounds of our SmartSpace when the shared element transition began. We'll interpolate
57      * between this and [smartspaceDestinationBounds] as the dismiss amount changes.
58      */
59     private val smartspaceOriginBounds = Rect()
60 
61     /** The bounds of the Launcher's SmartSpace, which is where we are animating our SmartSpace. */
62 
63     private val smartspaceDestinationBounds = Rect()
64 
65     fun createExternalInterface(): ISmartspaceTransitionController {
66         return ISmartspaceTransitionController
67     }
68 
69     /**
70      * Updates [mLauncherSmartspaceState] and returns it. This will trigger a binder call, so use the
71      * cached [mLauncherSmartspaceState] if possible.
72      */
73     fun updateLauncherSmartSpaceState(): SmartspaceState? {
74         return launcherSmartspace?.smartspaceState.also {
75             mLauncherSmartspaceState = it
76         }
77     }
78 
79     fun prepareForUnlockTransition() {
80         updateLauncherSmartSpaceState().also { state ->
81             if (state?.boundsOnScreen != null && lockscreenSmartspace != null) {
82                 lockscreenSmartspace!!.getBoundsOnScreen(smartspaceOriginBounds)
83                 with(smartspaceDestinationBounds) {
84                     set(state.boundsOnScreen)
85                     offset(-lockscreenSmartspace!!.paddingLeft,
86                             -lockscreenSmartspace!!.paddingTop)
87                 }
88             }
89         }
90     }
91 
92     fun setProgressToDestinationBounds(progress: Float) {
93         if (!isSmartspaceTransitionPossible()) {
94             return
95         }
96 
97         val progressClamped = min(1f, progress)
98 
99         // Calculate the distance (relative to the origin) that we need to be for the current
100         // progress value.
101         val progressX =
102                 (smartspaceDestinationBounds.left - smartspaceOriginBounds.left) * progressClamped
103         val progressY =
104                 (smartspaceDestinationBounds.top - smartspaceOriginBounds.top) * progressClamped
105 
106         val lockscreenSmartspaceCurrentBounds = Rect().also {
107             lockscreenSmartspace!!.getBoundsOnScreen(it)
108         }
109 
110         // Figure out how far that is from our present location on the screen. This approach
111         // compensates for the fact that our parent container is also translating to animate out.
112         val dx = smartspaceOriginBounds.left + progressX -
113                 lockscreenSmartspaceCurrentBounds.left
114         var dy = smartspaceOriginBounds.top + progressY -
115                 lockscreenSmartspaceCurrentBounds.top
116 
117         with(lockscreenSmartspace!!) {
118             translationX = translationX + dx
119             translationY = translationY + dy
120         }
121     }
122 
123     /**
124      * Whether we're capable of performing the Smartspace shared element transition when we unlock.
125      * This is true if:
126      *
127      * - The Launcher registered a Smartspace with us, it's reporting non-empty bounds on screen.
128      * - Launcher is behind the keyguard, and the Smartspace is visible on the currently selected
129      *   page.
130      */
131     public fun isSmartspaceTransitionPossible(): Boolean {
132         val smartSpaceNullOrBoundsEmpty = mLauncherSmartspaceState?.boundsOnScreen?.isEmpty ?: true
133         return isLauncherUnderneath() && !smartSpaceNullOrBoundsEmpty
134     }
135 
136     companion object {
137         fun isLauncherUnderneath(): Boolean {
138             return ActivityManagerWrapper.getInstance()
139                     .runningTask?.topActivity?.className?.equals(
140                             QuickStepContract.LAUNCHER_ACTIVITY_CLASS_NAME) ?: false
141         }
142     }
143 }