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