• 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 static com.android.launcher3.Flags.enableOverviewBackgroundWallpaperBlur;
19 import static com.android.launcher3.Flags.enableScalingRevealHomeAnimation;
20 
21 import android.app.WallpaperManager;
22 import android.os.IBinder;
23 import android.util.FloatProperty;
24 import android.util.Log;
25 import android.view.AttachedSurfaceControl;
26 import android.view.SurfaceControl;
27 
28 import androidx.annotation.Nullable;
29 
30 import com.android.launcher3.Flags;
31 import com.android.launcher3.Launcher;
32 import com.android.launcher3.R;
33 import com.android.launcher3.Utilities;
34 import com.android.launcher3.util.MultiPropertyFactory;
35 import com.android.launcher3.util.MultiPropertyFactory.MultiProperty;
36 import com.android.systemui.shared.system.BlurUtils;
37 
38 /**
39  * Utility class for applying depth effect
40  */
41 public class BaseDepthController {
42     public static final float DEPTH_0_PERCENT = 0f;
43     public static final float DEPTH_60_PERCENT = 0.6f;
44     public static final float DEPTH_70_PERCENT = 0.7f;
45 
46     private static final FloatProperty<BaseDepthController> DEPTH =
47             new FloatProperty<BaseDepthController>("depth") {
48                 @Override
49                 public void setValue(BaseDepthController depthController, float depth) {
50                     depthController.setDepth(depth);
51                 }
52 
53                 @Override
54                 public Float get(BaseDepthController depthController) {
55                     return depthController.mDepth;
56                 }
57             };
58 
59     private static final int DEPTH_INDEX_STATE_TRANSITION = 0;
60     private static final int DEPTH_INDEX_WIDGET = 1;
61     private static final int DEPTH_INDEX_COUNT = 2;
62 
63     // b/291401432
64     private static final String TAG = "BaseDepthController";
65 
66     protected final Launcher mLauncher;
67     /** Property to set the depth for state transition. */
68     public final MultiProperty stateDepth;
69     /** Property to set the depth for widget picker. */
70     public final MultiProperty widgetDepth;
71 
72     /**
73      * Blur radius when completely zoomed out, in pixels.
74      */
75     protected final int mMaxBlurRadius;
76     protected final WallpaperManager mWallpaperManager;
77     protected boolean mCrossWindowBlursEnabled;
78 
79     /**
80      * Ratio from 0 to 1, where 0 is fully zoomed out, and 1 is zoomed in.
81      *
82      * @see android.service.wallpaper.WallpaperService.Engine#onZoomChanged(float)
83      */
84     private float mDepth;
85 
86     protected SurfaceControl mBaseSurface;
87 
88     protected SurfaceControl mBaseSurfaceOverride;
89 
90     // Hints that there is potentially content behind Launcher and that we shouldn't optimize by
91     // marking the launcher surface as opaque.  Only used in certain Launcher states.
92     private boolean mHasContentBehindLauncher;
93 
94     /** Pause blur but allow transparent, can be used when launch something behind the Launcher. */
95     protected boolean mPauseBlurs;
96 
97     /**
98      * Last blur value, in pixels, that was applied.
99      * For debugging purposes.
100      */
101     protected int mCurrentBlur;
102     /**
103      * If we requested early wake-up offsets to SurfaceFlinger.
104      */
105     protected boolean mInEarlyWakeUp;
106 
107     protected boolean mWaitingOnSurfaceValidity;
108 
109     private SurfaceControl mBlurSurface = null;
110 
BaseDepthController(Launcher activity)111     public BaseDepthController(Launcher activity) {
112         mLauncher = activity;
113         if (Flags.allAppsBlur()) {
114             mMaxBlurRadius = activity.getResources().getDimensionPixelSize(
115                     R.dimen.max_depth_blur_radius_enhanced);
116         } else {
117             mMaxBlurRadius = activity.getResources().getInteger(R.integer.max_depth_blur_radius);
118         }
119         mWallpaperManager = activity.getSystemService(WallpaperManager.class);
120 
121         MultiPropertyFactory<BaseDepthController> depthProperty =
122                 new MultiPropertyFactory<>(this, DEPTH, DEPTH_INDEX_COUNT, Float::max);
123         stateDepth = depthProperty.get(DEPTH_INDEX_STATE_TRANSITION);
124         widgetDepth = depthProperty.get(DEPTH_INDEX_WIDGET);
125         if (enableOverviewBackgroundWallpaperBlur()) {
126             mBlurSurface = new SurfaceControl.Builder()
127                     .setName("Overview Blur")
128                     .setHidden(false)
129                     .build();
130         }
131 
132     }
133 
setCrossWindowBlursEnabled(boolean isEnabled)134     protected void setCrossWindowBlursEnabled(boolean isEnabled) {
135         mCrossWindowBlursEnabled = isEnabled;
136         applyDepthAndBlur();
137     }
138 
setHasContentBehindLauncher(boolean hasContentBehindLauncher)139     public void setHasContentBehindLauncher(boolean hasContentBehindLauncher) {
140         mHasContentBehindLauncher = hasContentBehindLauncher;
141     }
142 
pauseBlursOnWindows(boolean pause)143     public void pauseBlursOnWindows(boolean pause) {
144         if (pause != mPauseBlurs) {
145             mPauseBlurs = pause;
146             applyDepthAndBlur();
147         }
148     }
149 
onInvalidSurface()150     protected void onInvalidSurface() { }
151 
applyDepthAndBlur()152     protected void applyDepthAndBlur() {
153         float depth = mDepth;
154         IBinder windowToken = mLauncher.getRootView().getWindowToken();
155         if (windowToken != null) {
156             if (enableScalingRevealHomeAnimation()) {
157                 mWallpaperManager.setWallpaperZoomOut(windowToken, depth);
158             } else {
159                 // The API's full zoom-out is three times larger than the zoom-out we apply to the
160                 // icons. To keep the two consistent throughout the animation while keeping
161                 // Launcher's concept of full depth unchanged, we divide the depth by 3 here.
162                 mWallpaperManager.setWallpaperZoomOut(windowToken, depth / 3);
163             }
164         }
165 
166         if (!BlurUtils.supportsBlursOnWindows()) {
167             return;
168         }
169         if (mBaseSurface == null) {
170             Log.d(TAG, "mSurface is null and mCurrentBlur is: " + mCurrentBlur);
171             return;
172         }
173         if (!mBaseSurface.isValid()) {
174             Log.d(TAG, "mSurface is not valid");
175             mWaitingOnSurfaceValidity = true;
176             onInvalidSurface();
177             return;
178         }
179         mWaitingOnSurfaceValidity = false;
180         boolean hasOpaqueBg = mLauncher.getScrimView().isFullyOpaque();
181         boolean isSurfaceOpaque = !mHasContentBehindLauncher && hasOpaqueBg && !mPauseBlurs;
182 
183         float blurAmount;
184         if (enableScalingRevealHomeAnimation()) {
185             blurAmount = mapDepthToBlur(depth);
186         } else {
187             blurAmount = depth;
188         }
189         mCurrentBlur = !mCrossWindowBlursEnabled || hasOpaqueBg || mPauseBlurs
190                 ? 0 : (int) (blurAmount * mMaxBlurRadius);
191 
192         SurfaceControl.Transaction transaction = new SurfaceControl.Transaction();
193         if (enableOverviewBackgroundWallpaperBlur() && mBlurSurface != null) {
194             // Reparent to launcher for full screen blur.
195             transaction.setBackgroundBlurRadius(mBlurSurface, mCurrentBlur)
196                     .reparent(mBlurSurface, mBaseSurface);
197             // Set mBlurSurface to be 1 layer behind mBaseSurface or mBaseSurfaceOverride.
198             if (mBaseSurfaceOverride != null && mBaseSurfaceOverride.isValid()) {
199                 transaction.setRelativeLayer(mBlurSurface, mBaseSurfaceOverride, -1);
200             } else {
201                 transaction.setRelativeLayer(mBlurSurface, mBaseSurface, -1);
202             }
203         } else {
204             transaction.setBackgroundBlurRadius(mBaseSurface, mCurrentBlur);
205         }
206         transaction.setOpaque(mBaseSurface, isSurfaceOpaque);
207         // Set early wake-up flags when we know we're executing an expensive operation, this way
208         // SurfaceFlinger will adjust its internal offsets to avoid jank.
209         boolean wantsEarlyWakeUp = depth > 0 && depth < 1;
210         if (wantsEarlyWakeUp && !mInEarlyWakeUp) {
211             transaction.setEarlyWakeupStart();
212             mInEarlyWakeUp = true;
213         } else if (!wantsEarlyWakeUp && mInEarlyWakeUp) {
214             transaction.setEarlyWakeupEnd();
215             mInEarlyWakeUp = false;
216         }
217 
218         AttachedSurfaceControl rootSurfaceControl =
219                 mLauncher.getRootView().getRootSurfaceControl();
220         if (rootSurfaceControl != null) {
221             rootSurfaceControl.applyTransactionOnDraw(transaction);
222         }
223     }
224 
225     private void setDepth(float depth) {
226         depth = Utilities.boundToRange(depth, 0, 1);
227         // Round out the depth to dedupe frequent, non-perceptable updates
228         int depthI = (int) (depth * 256);
229         float depthF = depthI / 256f;
230         if (Float.compare(mDepth, depthF) == 0) {
231             return;
232         }
233         mDepth = depthF;
234         applyDepthAndBlur();
235     }
236 
237     /**
238      * Sets the lowest surface that should not be blurred.
239      * <p>
240      * Blur is applied to below {@link #mBaseSurfaceOverride}. When set to {@code null}, blur is
241      * applied
242      * to below {@link #mBaseSurface}.
243      * </p>
244      */
245     public void setBaseSurfaceOverride(@Nullable SurfaceControl baseSurfaceOverride) {
246         this.mBaseSurfaceOverride = baseSurfaceOverride;
247         applyDepthAndBlur();
248     }
249 
250     /**
251      * Sets the specified app target surface to apply the blur to.
252      */
253     protected void setBaseSurface(SurfaceControl baseSurface) {
254         if (mBaseSurface != baseSurface || mWaitingOnSurfaceValidity) {
255             mBaseSurface = baseSurface;
256             Log.d(TAG, "setSurface:\n\tmWaitingOnSurfaceValidity: " + mWaitingOnSurfaceValidity
257                     + "\n\tmBaseSurface: " + mBaseSurface);
258             applyDepthAndBlur();
259         }
260     }
261 
262     /**
263      * Maps depth values to blur amounts as a percentage of the max blur.
264      * The blur percentage grows linearly with depth, and maxes out at 30% depth.
265      */
266     private static float mapDepthToBlur(float depth) {
267         return Math.min(3 * depth, 1f);
268     }
269 }
270