• 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 package com.android.launcher3.statemanager;
17 
18 import static com.android.launcher3.LauncherState.FLAG_NON_INTERACTIVE;
19 
20 import android.os.Handler;
21 import android.view.LayoutInflater;
22 import android.view.View;
23 
24 import androidx.annotation.CallSuper;
25 
26 import com.android.launcher3.BaseDraggingActivity;
27 import com.android.launcher3.LauncherRootView;
28 import com.android.launcher3.Utilities;
29 import com.android.launcher3.statemanager.StateManager.AtomicAnimationFactory;
30 import com.android.launcher3.statemanager.StateManager.StateHandler;
31 import com.android.launcher3.views.BaseDragLayer;
32 
33 import java.util.List;
34 
35 /**
36  * Abstract activity with state management
37  * @param <STATE_TYPE> Type of state object
38  */
39 public abstract class StatefulActivity<STATE_TYPE extends BaseState<STATE_TYPE>>
40         extends BaseDraggingActivity {
41 
42     public final Handler mHandler = new Handler();
43     private final Runnable mHandleDeferredResume = this::handleDeferredResume;
44     private boolean mDeferredResumePending;
45 
46     private LauncherRootView mRootView;
47 
48     /**
49      * Create handlers to control the property changes for this activity
50      */
collectStateHandlers(List<StateHandler> out)51     protected abstract void collectStateHandlers(List<StateHandler> out);
52 
53     /**
54      * Returns true if the activity is in the provided state
55      */
isInState(STATE_TYPE state)56     public boolean isInState(STATE_TYPE state) {
57         return getStateManager().getState() == state;
58     }
59 
60     /**
61      * Returns the state manager for this activity
62      */
getStateManager()63     public abstract StateManager<STATE_TYPE> getStateManager();
64 
inflateRootView(int layoutId)65     protected void inflateRootView(int layoutId) {
66         mRootView = (LauncherRootView) LayoutInflater.from(this).inflate(layoutId, null);
67         mRootView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
68                 | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
69                 | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
70     }
71 
72     @Override
getRootView()73     public final LauncherRootView getRootView() {
74         return mRootView;
75     }
76 
77     @Override
findViewById(int id)78     public <T extends View> T findViewById(int id) {
79         return mRootView.findViewById(id);
80     }
81 
82     /**
83      * Called when transition to the state starts
84      */
85     @CallSuper
onStateSetStart(STATE_TYPE state)86     public void onStateSetStart(STATE_TYPE state) {
87         if (mDeferredResumePending) {
88             handleDeferredResume();
89         }
90     }
91 
92     /**
93      * Called when transition to state ends
94      */
onStateSetEnd(STATE_TYPE state)95     public void onStateSetEnd(STATE_TYPE state) { }
96 
97     /**
98      * Creates a factory for atomic state animations
99      */
createAtomicAnimationFactory()100     public AtomicAnimationFactory<STATE_TYPE> createAtomicAnimationFactory() {
101         return new AtomicAnimationFactory(0);
102     }
103 
104     @Override
reapplyUi()105     public void reapplyUi() {
106         reapplyUi(true /* cancelCurrentAnimation */);
107     }
108 
109     /**
110      * Re-applies if any state transition is not running, optionally cancelling
111      * the transition if requested.
112      */
reapplyUi(boolean cancelCurrentAnimation)113     public void reapplyUi(boolean cancelCurrentAnimation) {
114         getRootView().dispatchInsets();
115         getStateManager().reapplyState(cancelCurrentAnimation);
116     }
117 
118     @Override
onStop()119     protected void onStop() {
120         BaseDragLayer dragLayer = getDragLayer();
121         final boolean wasActive = isUserActive();
122         final STATE_TYPE origState = getStateManager().getState();
123         final int origDragLayerChildCount = dragLayer.getChildCount();
124         super.onStop();
125 
126         if (!isChangingConfigurations()) {
127             getStateManager().moveToRestState();
128         }
129 
130         // Workaround for b/78520668, explicitly trim memory once UI is hidden
131         onTrimMemory(TRIM_MEMORY_UI_HIDDEN);
132 
133         if (wasActive) {
134             // The expected condition is that this activity is stopped because the device goes to
135             // sleep and the UI may have noticeable changes.
136             dragLayer.post(() -> {
137                 if ((!getStateManager().isInStableState(origState)
138                         // The drag layer may be animating (e.g. dismissing QSB).
139                         || dragLayer.getAlpha() < 1
140                         // Maybe an ArrowPopup is closed.
141                         || dragLayer.getChildCount() != origDragLayerChildCount)) {
142                     onUiChangedWhileSleeping();
143                 }
144             });
145         }
146     }
147 
148     /**
149      * Called if the Activity UI changed while the activity was not visible
150      */
onUiChangedWhileSleeping()151     protected void onUiChangedWhileSleeping() { }
152 
handleDeferredResume()153     private void handleDeferredResume() {
154         if (hasBeenResumed() && !getStateManager().getState().hasFlag(FLAG_NON_INTERACTIVE)) {
155             addActivityFlags(ACTIVITY_STATE_DEFERRED_RESUMED);
156             onDeferredResumed();
157 
158             mDeferredResumePending = false;
159         } else {
160             mDeferredResumePending = true;
161         }
162     }
163 
164     /**
165      * Called want the activity has stayed resumed for 1 frame.
166      */
onDeferredResumed()167     protected void onDeferredResumed() { }
168 
169     @Override
onResume()170     protected void onResume() {
171         super.onResume();
172 
173         mHandler.removeCallbacks(mHandleDeferredResume);
174         Utilities.postAsyncCallback(mHandler, mHandleDeferredResume);
175     }
176 }
177