• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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