1 /* 2 * Copyright (C) 2020 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 17 package com.android.launcher3.statehandlers; 18 19 import static com.android.app.animation.Interpolators.LINEAR; 20 import static com.android.launcher3.states.StateAnimationConfig.ANIM_DEPTH; 21 import static com.android.launcher3.states.StateAnimationConfig.SKIP_DEPTH_CONTROLLER; 22 import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR; 23 import static com.android.launcher3.util.MultiPropertyFactory.MULTI_PROPERTY_VALUE; 24 25 import android.animation.Animator; 26 import android.animation.AnimatorListenerAdapter; 27 import android.animation.ObjectAnimator; 28 import android.view.CrossWindowBlurListeners; 29 import android.view.View; 30 import android.view.ViewRootImpl; 31 import android.view.ViewTreeObserver; 32 33 import androidx.annotation.VisibleForTesting; 34 35 import com.android.launcher3.BaseActivity; 36 import com.android.launcher3.Launcher; 37 import com.android.launcher3.LauncherState; 38 import com.android.launcher3.anim.PendingAnimation; 39 import com.android.launcher3.statemanager.StateManager.StateHandler; 40 import com.android.launcher3.states.StateAnimationConfig; 41 import com.android.quickstep.util.BaseDepthController; 42 43 import java.io.PrintWriter; 44 import java.util.function.Consumer; 45 46 /** 47 * Controls blur and wallpaper zoom, for the Launcher surface only. 48 */ 49 public class DepthController extends BaseDepthController implements StateHandler<LauncherState>, 50 BaseActivity.MultiWindowModeChangedListener { 51 @VisibleForTesting 52 final ViewTreeObserver.OnDrawListener mOnDrawListener = this::onLauncherDraw; 53 54 private final Consumer<Boolean> mCrossWindowBlurListener = this::setCrossWindowBlursEnabled; 55 56 private final Runnable mOpaquenessListener = this::applyDepthAndBlur; 57 58 // Workaround for animating the depth when multiwindow mode changes. 59 private boolean mIgnoreStateChangesDuringMultiWindowAnimation = false; 60 61 private View.OnAttachStateChangeListener mOnAttachListener; 62 63 // Ensure {@link mOnDrawListener} is added only once to avoid spamming DragLayer's mRunQueue 64 // via {@link View#post(Runnable)} 65 private boolean mIsOnDrawListenerAdded = false; 66 DepthController(Launcher l)67 public DepthController(Launcher l) { 68 super(l); 69 } 70 onLauncherDraw()71 private void onLauncherDraw() { 72 View view = mLauncher.getDragLayer(); 73 ViewRootImpl viewRootImpl = view.getViewRootImpl(); 74 setBaseSurface(viewRootImpl != null ? viewRootImpl.getSurfaceControl() : null); 75 view.post(this::removeOnDrawListener); 76 } 77 ensureDependencies()78 private void ensureDependencies() { 79 View rootView = mLauncher.getRootView(); 80 if (rootView == null) { 81 return; 82 } 83 if (mOnAttachListener != null) { 84 return; 85 } 86 mOnAttachListener = new View.OnAttachStateChangeListener() { 87 @Override 88 public void onViewAttachedToWindow(View view) { 89 UI_HELPER_EXECUTOR.execute(() -> 90 CrossWindowBlurListeners.getInstance().addListener( 91 mLauncher.getMainExecutor(), mCrossWindowBlurListener)); 92 mLauncher.getScrimView().addOpaquenessListener(mOpaquenessListener); 93 94 // To handle the case where window token is invalid during last setDepth call. 95 applyDepthAndBlur(); 96 } 97 98 @Override 99 public void onViewDetachedFromWindow(View view) { 100 removeSecondaryListeners(); 101 } 102 }; 103 rootView.addOnAttachStateChangeListener(mOnAttachListener); 104 if (rootView.isAttachedToWindow()) { 105 mOnAttachListener.onViewAttachedToWindow(rootView); 106 } 107 } 108 109 /** 110 * Cleans up after this controller so it can be garbage collected without leaving traces. 111 */ dispose()112 public void dispose() { 113 removeSecondaryListeners(); 114 115 if (mLauncher.getRootView() != null && mOnAttachListener != null) { 116 mLauncher.getRootView().removeOnAttachStateChangeListener(mOnAttachListener); 117 mOnAttachListener = null; 118 } 119 } 120 removeSecondaryListeners()121 private void removeSecondaryListeners() { 122 UI_HELPER_EXECUTOR.execute(() -> 123 CrossWindowBlurListeners.getInstance() 124 .removeListener(mCrossWindowBlurListener)); 125 if (mOpaquenessListener != null) { 126 mLauncher.getScrimView().removeOpaquenessListener(mOpaquenessListener); 127 } 128 } 129 130 /** 131 * Sets if the underlying activity is started or not 132 */ setActivityStarted(boolean isStarted)133 public void setActivityStarted(boolean isStarted) { 134 if (isStarted) { 135 addOnDrawListener(); 136 } else { 137 removeOnDrawListener(); 138 setBaseSurface(null); 139 } 140 } 141 142 @Override setState(LauncherState toState)143 public void setState(LauncherState toState) { 144 if (mIgnoreStateChangesDuringMultiWindowAnimation) { 145 return; 146 } 147 148 stateDepth.setValue(toState.getDepth(mLauncher)); 149 if (toState == LauncherState.BACKGROUND_APP) { 150 addOnDrawListener(); 151 } 152 } 153 154 @Override setStateWithAnimation(LauncherState toState, StateAnimationConfig config, PendingAnimation animation)155 public void setStateWithAnimation(LauncherState toState, StateAnimationConfig config, 156 PendingAnimation animation) { 157 if (config.hasAnimationFlag(SKIP_DEPTH_CONTROLLER) 158 || mIgnoreStateChangesDuringMultiWindowAnimation) { 159 return; 160 } 161 162 float toDepth = toState.getDepth(mLauncher); 163 animation.setFloat(stateDepth, MULTI_PROPERTY_VALUE, toDepth, 164 config.getInterpolator(ANIM_DEPTH, LINEAR)); 165 } 166 167 @Override applyDepthAndBlur()168 protected void applyDepthAndBlur() { 169 ensureDependencies(); 170 super.applyDepthAndBlur(); 171 } 172 173 @Override onInvalidSurface()174 protected void onInvalidSurface() { 175 // Lets wait for surface to become valid again 176 addOnDrawListener(); 177 } 178 addOnDrawListener()179 private void addOnDrawListener() { 180 if (mIsOnDrawListenerAdded) { 181 return; 182 } 183 mLauncher.getDragLayer().getViewTreeObserver().addOnDrawListener(mOnDrawListener); 184 mIsOnDrawListenerAdded = true; 185 } 186 removeOnDrawListener()187 private void removeOnDrawListener() { 188 if (!mIsOnDrawListenerAdded) { 189 return; 190 } 191 mLauncher.getDragLayer().getViewTreeObserver().removeOnDrawListener(mOnDrawListener); 192 mIsOnDrawListenerAdded = false; 193 } 194 195 @Override onMultiWindowModeChanged(boolean isInMultiWindowMode)196 public void onMultiWindowModeChanged(boolean isInMultiWindowMode) { 197 mIgnoreStateChangesDuringMultiWindowAnimation = true; 198 199 ObjectAnimator mwAnimation = ObjectAnimator.ofFloat(stateDepth, MULTI_PROPERTY_VALUE, 200 mLauncher.getStateManager().getState().getDepth(mLauncher, isInMultiWindowMode)) 201 .setDuration(300); 202 mwAnimation.addListener(new AnimatorListenerAdapter() { 203 @Override 204 public void onAnimationEnd(Animator animation) { 205 mIgnoreStateChangesDuringMultiWindowAnimation = false; 206 } 207 }); 208 mwAnimation.setAutoCancel(true); 209 mwAnimation.start(); 210 } 211 dump(String prefix, PrintWriter writer)212 public void dump(String prefix, PrintWriter writer) { 213 writer.println(prefix + "DepthController"); 214 writer.println(prefix + "\tmMaxBlurRadius=" + mMaxBlurRadius); 215 writer.println(prefix + "\tmCrossWindowBlursEnabled=" + mCrossWindowBlursEnabled); 216 writer.println(prefix + "\tmBaseSurface=" + mBaseSurface); 217 writer.println(prefix + "\tmBaseSurfaceOverride=" + mBaseSurfaceOverride); 218 writer.println(prefix + "\tmStateDepth=" + stateDepth.getValue()); 219 writer.println(prefix + "\tmWidgetDepth=" + widgetDepth.getValue()); 220 writer.println(prefix + "\tmCurrentBlur=" + mCurrentBlur); 221 writer.println(prefix + "\tmInEarlyWakeUp=" + mInEarlyWakeUp); 222 writer.println(prefix + "\tmIgnoreStateChangesDuringMultiWindowAnimation=" 223 + mIgnoreStateChangesDuringMultiWindowAnimation); 224 writer.println(prefix + "\tmPauseBlurs=" + mPauseBlurs); 225 writer.println(prefix + "\tmWaitingOnSurfaceValidity=" + mWaitingOnSurfaceValidity); 226 } 227 } 228