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