• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 package com.android.quickstep.util;
17 
18 import android.app.WallpaperManager;
19 import android.os.IBinder;
20 import android.util.FloatProperty;
21 import android.util.Log;
22 import android.view.AttachedSurfaceControl;
23 import android.view.SurfaceControl;
24 
25 import com.android.launcher3.Launcher;
26 import com.android.launcher3.R;
27 import com.android.launcher3.Utilities;
28 import com.android.launcher3.util.MultiPropertyFactory;
29 import com.android.launcher3.util.MultiPropertyFactory.MultiProperty;
30 import com.android.systemui.shared.system.BlurUtils;
31 
32 /**
33  * Utility class for applying depth effect
34  */
35 public class BaseDepthController {
36 
37     private static final FloatProperty<BaseDepthController> DEPTH =
38             new FloatProperty<BaseDepthController>("depth") {
39                 @Override
40                 public void setValue(BaseDepthController depthController, float depth) {
41                     depthController.setDepth(depth);
42                 }
43 
44                 @Override
45                 public Float get(BaseDepthController depthController) {
46                     return depthController.mDepth;
47                 }
48             };
49 
50     private static final int DEPTH_INDEX_STATE_TRANSITION = 0;
51     private static final int DEPTH_INDEX_WIDGET = 1;
52     private static final int DEPTH_INDEX_COUNT = 2;
53 
54     // b/291401432
55     private static final String TAG = "BaseDepthController";
56 
57     protected final Launcher mLauncher;
58     /** Property to set the depth for state transition. */
59     public final MultiProperty stateDepth;
60     /** Property to set the depth for widget picker. */
61     public final MultiProperty widgetDepth;
62 
63     /**
64      * Blur radius when completely zoomed out, in pixels.
65      */
66     protected final int mMaxBlurRadius;
67     protected final WallpaperManager mWallpaperManager;
68     protected boolean mCrossWindowBlursEnabled;
69 
70     /**
71      * Ratio from 0 to 1, where 0 is fully zoomed out, and 1 is zoomed in.
72      * @see android.service.wallpaper.WallpaperService.Engine#onZoomChanged(float)
73      */
74     private float mDepth;
75 
76     protected SurfaceControl mSurface;
77 
78     // Hints that there is potentially content behind Launcher and that we shouldn't optimize by
79     // marking the launcher surface as opaque.  Only used in certain Launcher states.
80     private boolean mHasContentBehindLauncher;
81 
82     /** Pause blur but allow transparent, can be used when launch something behind the Launcher. */
83     protected boolean mPauseBlurs;
84 
85     /**
86      * Last blur value, in pixels, that was applied.
87      * For debugging purposes.
88      */
89     protected int mCurrentBlur;
90     /**
91      * If we requested early wake-up offsets to SurfaceFlinger.
92      */
93     protected boolean mInEarlyWakeUp;
94 
95     protected boolean mWaitingOnSurfaceValidity;
96 
BaseDepthController(Launcher activity)97     public BaseDepthController(Launcher activity) {
98         mLauncher = activity;
99         mMaxBlurRadius = activity.getResources().getInteger(R.integer.max_depth_blur_radius);
100         mWallpaperManager = activity.getSystemService(WallpaperManager.class);
101 
102         MultiPropertyFactory<BaseDepthController> depthProperty =
103                 new MultiPropertyFactory<>(this, DEPTH, DEPTH_INDEX_COUNT, Float::max);
104         stateDepth = depthProperty.get(DEPTH_INDEX_STATE_TRANSITION);
105         widgetDepth = depthProperty.get(DEPTH_INDEX_WIDGET);
106     }
107 
setCrossWindowBlursEnabled(boolean isEnabled)108     protected void setCrossWindowBlursEnabled(boolean isEnabled) {
109         mCrossWindowBlursEnabled = isEnabled;
110         applyDepthAndBlur();
111     }
112 
setHasContentBehindLauncher(boolean hasContentBehindLauncher)113     public void setHasContentBehindLauncher(boolean hasContentBehindLauncher) {
114         mHasContentBehindLauncher = hasContentBehindLauncher;
115     }
116 
pauseBlursOnWindows(boolean pause)117     public void pauseBlursOnWindows(boolean pause) {
118         if (pause != mPauseBlurs) {
119             mPauseBlurs = pause;
120             applyDepthAndBlur();
121         }
122     }
123 
onInvalidSurface()124     protected void onInvalidSurface() { }
125 
applyDepthAndBlur()126     protected void applyDepthAndBlur() {
127         float depth = mDepth;
128         IBinder windowToken = mLauncher.getRootView().getWindowToken();
129         if (windowToken != null) {
130             // The API's full zoom-out is three times larger than the zoom-out we apply to the
131             // icons. To keep the two consistent throughout the animation while keeping Launcher's
132             // concept of full depth unchanged, we divide the depth by 3 here.
133             mWallpaperManager.setWallpaperZoomOut(windowToken, depth / 3);
134         }
135 
136         if (!BlurUtils.supportsBlursOnWindows()) {
137             return;
138         }
139         if (mSurface == null) {
140             Log.d(TAG, "mSurface is null and mCurrentBlur is: " + mCurrentBlur);
141             return;
142         }
143         if (!mSurface.isValid()) {
144             Log.d(TAG, "mSurface is not valid");
145             mWaitingOnSurfaceValidity = true;
146             onInvalidSurface();
147             return;
148         }
149         mWaitingOnSurfaceValidity = false;
150         boolean hasOpaqueBg = mLauncher.getScrimView().isFullyOpaque();
151         boolean isSurfaceOpaque = !mHasContentBehindLauncher && hasOpaqueBg && !mPauseBlurs;
152 
153         mCurrentBlur = !mCrossWindowBlursEnabled || hasOpaqueBg || mPauseBlurs
154                 ? 0 : (int) (depth * mMaxBlurRadius);
155         SurfaceControl.Transaction transaction = new SurfaceControl.Transaction()
156                 .setBackgroundBlurRadius(mSurface, mCurrentBlur)
157                 .setOpaque(mSurface, isSurfaceOpaque);
158 
159         // Set early wake-up flags when we know we're executing an expensive operation, this way
160         // SurfaceFlinger will adjust its internal offsets to avoid jank.
161         boolean wantsEarlyWakeUp = depth > 0 && depth < 1;
162         if (wantsEarlyWakeUp && !mInEarlyWakeUp) {
163             transaction.setEarlyWakeupStart();
164             mInEarlyWakeUp = true;
165         } else if (!wantsEarlyWakeUp && mInEarlyWakeUp) {
166             transaction.setEarlyWakeupEnd();
167             mInEarlyWakeUp = false;
168         }
169 
170         AttachedSurfaceControl rootSurfaceControl =
171                 mLauncher.getRootView().getRootSurfaceControl();
172         if (rootSurfaceControl != null) {
173             rootSurfaceControl.applyTransactionOnDraw(transaction);
174         }
175     }
176 
177     private void setDepth(float depth) {
178         depth = Utilities.boundToRange(depth, 0, 1);
179         // Round out the depth to dedupe frequent, non-perceptable updates
180         int depthI = (int) (depth * 256);
181         float depthF = depthI / 256f;
182         if (Float.compare(mDepth, depthF) == 0) {
183             return;
184         }
185         mDepth = depthF;
186         applyDepthAndBlur();
187     }
188 
189     /**
190      * Sets the specified app target surface to apply the blur to.
191      */
192     protected void setSurface(SurfaceControl surface) {
193         if (mSurface != surface || mWaitingOnSurfaceValidity) {
194             mSurface = surface;
195             Log.d(TAG, "setSurface:\n\tmWaitingOnSurfaceValidity: " + mWaitingOnSurfaceValidity
196                     + "\n\tmSurface: " + mSurface);
197             applyDepthAndBlur();
198         }
199     }
200 }
201