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