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