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