• 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 android.content.pm.ActivityInfo.CONFIG_ORIENTATION;
19 import static android.content.pm.ActivityInfo.CONFIG_SCREEN_SIZE;
20 
21 import static com.android.launcher3.LauncherState.FLAG_NON_INTERACTIVE;
22 
23 import android.content.res.Configuration;
24 import android.os.Bundle;
25 import android.os.Handler;
26 import android.os.Trace;
27 import android.view.LayoutInflater;
28 import android.view.View;
29 
30 import androidx.annotation.CallSuper;
31 
32 import com.android.launcher3.BaseActivity;
33 import com.android.launcher3.LauncherRootView;
34 import com.android.launcher3.Utilities;
35 import com.android.launcher3.statemanager.StateManager.StateHandler;
36 import com.android.launcher3.util.window.WindowManagerProxy;
37 import com.android.launcher3.views.BaseDragLayer;
38 
39 import java.util.List;
40 
41 /**
42  * Abstract activity with state management
43  * @param <STATE_TYPE> Type of state object
44  */
45 public abstract class StatefulActivity<STATE_TYPE extends BaseState<STATE_TYPE>>
46         extends BaseActivity implements StatefulContainer<STATE_TYPE> {
47 
48     public final Handler mHandler = new Handler();
49     private final Runnable mHandleDeferredResume = this::handleDeferredResume;
50     private boolean mDeferredResumePending;
51 
52     private LauncherRootView mRootView;
53 
54     protected Configuration mOldConfig;
55     private int mOldRotation;
56 
57     @Override
onCreate(Bundle savedInstanceState)58     protected void onCreate(Bundle savedInstanceState) {
59         super.onCreate(savedInstanceState);
60 
61         mOldConfig = new Configuration(getResources().getConfiguration());
62         mOldRotation = WindowManagerProxy.INSTANCE.get(this).getRotation(this);
63     }
64 
65     /**
66      * Create handlers to control the property changes for this activity
67      */
68 
69     @Override
collectStateHandlers(List<StateHandler<STATE_TYPE>> out)70     public abstract void collectStateHandlers(List<StateHandler<STATE_TYPE>> out);
71 
inflateRootView(int layoutId)72     protected void inflateRootView(int layoutId) {
73         mRootView = (LauncherRootView) LayoutInflater.from(this).inflate(layoutId, null);
74         mRootView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
75                 | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
76                 | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
77     }
78 
79     @Override
getRootView()80     public final LauncherRootView getRootView() {
81         return mRootView;
82     }
83 
84     @Override
findViewById(int id)85     public <T extends View> T findViewById(int id) {
86         return mRootView.findViewById(id);
87     }
88 
89     /**
90      * Called when transition to the state starts
91      */
92     @CallSuper
onStateSetStart(STATE_TYPE state)93     public void onStateSetStart(STATE_TYPE state) {
94         if (mDeferredResumePending) {
95             handleDeferredResume();
96         }
97         StatefulContainer.super.onStateSetStart(state);
98     }
99 
100     @Override
shouldAnimateStateChange()101     public boolean shouldAnimateStateChange() {
102         return !isForceInvisible() && isStarted();
103     }
104 
105     @Override
reapplyUi()106     public void reapplyUi() {
107         getRootView().dispatchInsets();
108         getStateManager().reapplyState(true /* cancelCurrentAnimation */);
109     }
110 
111     @Override
onStop()112     protected void onStop() {
113         BaseDragLayer dragLayer = getDragLayer();
114         final boolean wasActive = isUserActive();
115         final STATE_TYPE origState = getStateManager().getState();
116         final int origDragLayerChildCount = dragLayer.getChildCount();
117         super.onStop();
118 
119         if (!isChangingConfigurations()) {
120             getStateManager().moveToRestState();
121         }
122 
123         // Workaround for b/78520668, explicitly trim memory once UI is hidden
124         onTrimMemory(TRIM_MEMORY_UI_HIDDEN);
125 
126         if (wasActive) {
127             // The expected condition is that this activity is stopped because the device goes to
128             // sleep and the UI may have noticeable changes.
129             dragLayer.post(() -> {
130                 if ((!getStateManager().isInStableState(origState)
131                         // The drag layer may be animating (e.g. dismissing QSB).
132                         || dragLayer.getAlpha() < 1
133                         // Maybe an ArrowPopup is closed.
134                         || dragLayer.getChildCount() != origDragLayerChildCount)) {
135                     onUiChangedWhileSleeping();
136                 }
137             });
138         }
139     }
140 
141     /**
142      * Called if the Activity UI changed while the activity was not visible
143      */
onUiChangedWhileSleeping()144     public void onUiChangedWhileSleeping() { }
145 
handleDeferredResume()146     private void handleDeferredResume() {
147         if (hasBeenResumed() && !getStateManager().getState().hasFlag(FLAG_NON_INTERACTIVE)) {
148             addActivityFlags(ACTIVITY_STATE_DEFERRED_RESUMED);
149             onDeferredResumed();
150 
151             mDeferredResumePending = false;
152         } else {
153             mDeferredResumePending = true;
154         }
155     }
156 
157     /**
158      * Called want the activity has stayed resumed for 1 frame.
159      */
onDeferredResumed()160     protected void onDeferredResumed() { }
161 
162     @Override
onResume()163     protected void onResume() {
164         super.onResume();
165 
166         mHandler.removeCallbacks(mHandleDeferredResume);
167         Utilities.postAsyncCallback(mHandler, mHandleDeferredResume);
168     }
169 
170     /**
171      * Runs the given {@param r} runnable when this activity binds to the touch interaction service.
172      */
runOnBindToTouchInteractionService(Runnable r)173     public void runOnBindToTouchInteractionService(Runnable r) {
174         r.run();
175     }
176 
177     @Override
onConfigurationChanged(Configuration newConfig)178     public void onConfigurationChanged(Configuration newConfig) {
179         Trace.beginSection("statefulActivity#onConfigurationChanged");
180         handleConfigurationChanged(newConfig);
181         super.onConfigurationChanged(newConfig);
182         Trace.endSection();
183     }
184 
185     /**
186      * Handles configuration change when system calls {@link #onConfigurationChanged}, or on other
187      * situations that configuration might change.
188      */
handleConfigurationChanged(Configuration newConfig)189     public void handleConfigurationChanged(Configuration newConfig) {
190         int diff = newConfig.diff(mOldConfig);
191         int rotation = WindowManagerProxy.INSTANCE.get(this).getRotation(this);
192         if ((diff & (CONFIG_ORIENTATION | CONFIG_SCREEN_SIZE)) != 0
193                 || rotation != mOldRotation) {
194             onHandleConfigurationChanged();
195         }
196 
197         mOldConfig.setTo(newConfig);
198         mOldRotation = rotation;
199     }
200 
201     /**
202      * Logic for when device configuration changes (rotation, screen size change, multi-window,
203      * etc.)
204      */
onHandleConfigurationChanged()205     protected abstract void onHandleConfigurationChanged();
206 
207 }
208