• 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.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