• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 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;
18 
19 import static com.android.launcher3.model.WidgetsModel.GO_DISABLE_WIDGETS;
20 import static com.android.launcher3.util.SystemUiController.UI_STATE_FULLSCREEN_TASK;
21 
22 import static java.lang.annotation.RetentionPolicy.SOURCE;
23 
24 import android.app.Activity;
25 import android.content.Context;
26 import android.content.ContextWrapper;
27 import android.content.Intent;
28 import android.content.pm.LauncherApps;
29 import android.content.res.Configuration;
30 import android.graphics.Rect;
31 import android.os.Bundle;
32 import android.os.UserHandle;
33 import android.util.Log;
34 
35 import androidx.annotation.IntDef;
36 
37 import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener;
38 import com.android.launcher3.logging.StatsLogManager;
39 import com.android.launcher3.util.SystemUiController;
40 import com.android.launcher3.util.ViewCache;
41 import com.android.launcher3.views.ActivityContext;
42 import com.android.launcher3.views.ScrimView;
43 
44 import java.io.PrintWriter;
45 import java.lang.annotation.Retention;
46 import java.util.ArrayList;
47 
48 /**
49  * Launcher BaseActivity
50  */
51 public abstract class BaseActivity extends Activity implements ActivityContext {
52 
53     private static final String TAG = "BaseActivity";
54 
55     public static final int INVISIBLE_BY_STATE_HANDLER = 1 << 0;
56     public static final int INVISIBLE_BY_APP_TRANSITIONS = 1 << 1;
57     public static final int INVISIBLE_BY_PENDING_FLAGS = 1 << 2;
58 
59     // This is not treated as invisibility flag, but adds as a hint for an incomplete transition.
60     // When the wallpaper animation runs, it replaces this flag with a proper invisibility
61     // flag, INVISIBLE_BY_PENDING_FLAGS only for the duration of that animation.
62     public static final int PENDING_INVISIBLE_BY_WALLPAPER_ANIMATION = 1 << 3;
63 
64     private static final int INVISIBLE_FLAGS =
65             INVISIBLE_BY_STATE_HANDLER | INVISIBLE_BY_APP_TRANSITIONS | INVISIBLE_BY_PENDING_FLAGS;
66     public static final int STATE_HANDLER_INVISIBILITY_FLAGS =
67             INVISIBLE_BY_STATE_HANDLER | PENDING_INVISIBLE_BY_WALLPAPER_ANIMATION;
68     public static final int INVISIBLE_ALL =
69             INVISIBLE_FLAGS | PENDING_INVISIBLE_BY_WALLPAPER_ANIMATION;
70 
71     @Retention(SOURCE)
72     @IntDef(
73             flag = true,
74             value = {INVISIBLE_BY_STATE_HANDLER, INVISIBLE_BY_APP_TRANSITIONS,
75                     INVISIBLE_BY_PENDING_FLAGS, PENDING_INVISIBLE_BY_WALLPAPER_ANIMATION})
76     public @interface InvisibilityFlags{}
77 
78     private final ArrayList<OnDeviceProfileChangeListener> mDPChangeListeners = new ArrayList<>();
79     private final ArrayList<MultiWindowModeChangedListener> mMultiWindowModeChangedListeners =
80             new ArrayList<>();
81 
82     protected DeviceProfile mDeviceProfile;
83     protected StatsLogManager mStatsLogManager;
84     protected SystemUiController mSystemUiController;
85 
86 
87     public static final int ACTIVITY_STATE_STARTED = 1 << 0;
88     public static final int ACTIVITY_STATE_RESUMED = 1 << 1;
89 
90     /**
91      * State flags indicating that the activity has received one frame after resume, and was
92      * not immediately paused.
93      */
94     public static final int ACTIVITY_STATE_DEFERRED_RESUMED = 1 << 2;
95 
96     public static final int ACTIVITY_STATE_WINDOW_FOCUSED = 1 << 3;
97 
98     /**
99      * State flag indicating if the user is active or the activity when to background as a result
100      * of user action.
101      * @see #isUserActive()
102      */
103     public static final int ACTIVITY_STATE_USER_ACTIVE = 1 << 4;
104 
105     /**
106      * State flag indicating if the user will be active shortly.
107      */
108     public static final int ACTIVITY_STATE_USER_WILL_BE_ACTIVE = 1 << 5;
109 
110     /**
111      * State flag indicating that a state transition is in progress
112      */
113     public static final int ACTIVITY_STATE_TRANSITION_ACTIVE = 1 << 6;
114 
115     @Retention(SOURCE)
116     @IntDef(
117             flag = true,
118             value = {ACTIVITY_STATE_STARTED,
119                     ACTIVITY_STATE_RESUMED,
120                     ACTIVITY_STATE_DEFERRED_RESUMED,
121                     ACTIVITY_STATE_WINDOW_FOCUSED,
122                     ACTIVITY_STATE_USER_ACTIVE,
123                     ACTIVITY_STATE_TRANSITION_ACTIVE})
124     public @interface ActivityFlags{}
125 
126     @ActivityFlags
127     private int mActivityFlags;
128 
129     // When the recents animation is running, the visibility of the Launcher is managed by the
130     // animation
131     @InvisibilityFlags private int mForceInvisible;
132 
133     private final ViewCache mViewCache = new ViewCache();
134 
135     @Override
getViewCache()136     public ViewCache getViewCache() {
137         return mViewCache;
138     }
139 
140     @Override
getDeviceProfile()141     public DeviceProfile getDeviceProfile() {
142         return mDeviceProfile;
143     }
144 
145     /**
146      * Returns {@link StatsLogManager} for user event logging.
147      */
getStatsLogManager()148     public StatsLogManager getStatsLogManager() {
149         if (mStatsLogManager == null) {
150             mStatsLogManager = StatsLogManager.newInstance(this);
151         }
152         return mStatsLogManager;
153     }
154 
getSystemUiController()155     public SystemUiController getSystemUiController() {
156         if (mSystemUiController == null) {
157             mSystemUiController = new SystemUiController(getWindow());
158         }
159         return mSystemUiController;
160     }
161 
getScrimView()162     public ScrimView getScrimView() {
163         return null;
164     }
165 
166     @Override
onActivityResult(int requestCode, int resultCode, Intent data)167     public void onActivityResult(int requestCode, int resultCode, Intent data) {
168         super.onActivityResult(requestCode, resultCode, data);
169     }
170 
171     @Override
onStart()172     protected void onStart() {
173         addActivityFlags(ACTIVITY_STATE_STARTED);
174         super.onStart();
175     }
176 
177     @Override
onResume()178     protected void onResume() {
179         addActivityFlags(ACTIVITY_STATE_RESUMED | ACTIVITY_STATE_USER_ACTIVE);
180         removeActivityFlags(ACTIVITY_STATE_USER_WILL_BE_ACTIVE);
181         super.onResume();
182     }
183 
184     @Override
onUserLeaveHint()185     protected void onUserLeaveHint() {
186         removeActivityFlags(ACTIVITY_STATE_USER_ACTIVE);
187         super.onUserLeaveHint();
188     }
189 
190     @Override
onMultiWindowModeChanged(boolean isInMultiWindowMode, Configuration newConfig)191     public void onMultiWindowModeChanged(boolean isInMultiWindowMode, Configuration newConfig) {
192         super.onMultiWindowModeChanged(isInMultiWindowMode, newConfig);
193         for (int i = mMultiWindowModeChangedListeners.size() - 1; i >= 0; i--) {
194             mMultiWindowModeChangedListeners.get(i).onMultiWindowModeChanged(isInMultiWindowMode);
195         }
196     }
197 
198     @Override
onStop()199     protected void onStop() {
200         removeActivityFlags(ACTIVITY_STATE_STARTED | ACTIVITY_STATE_USER_ACTIVE);
201         mForceInvisible = 0;
202         super.onStop();
203 
204         // Reset the overridden sysui flags used for the task-swipe launch animation, this is a
205         // catch all for if we do not get resumed (and therefore not paused below)
206         getSystemUiController().updateUiState(UI_STATE_FULLSCREEN_TASK, 0);
207     }
208 
209     @Override
onPause()210     protected void onPause() {
211         removeActivityFlags(ACTIVITY_STATE_RESUMED | ACTIVITY_STATE_DEFERRED_RESUMED);
212         super.onPause();
213 
214         // Reset the overridden sysui flags used for the task-swipe launch animation, we do this
215         // here instead of at the end of the animation because the start of the new activity does
216         // not happen immediately, which would cause us to reset to launcher's sysui flags and then
217         // back to the new app (causing a flash)
218         getSystemUiController().updateUiState(UI_STATE_FULLSCREEN_TASK, 0);
219     }
220 
221     @Override
onWindowFocusChanged(boolean hasFocus)222     public void onWindowFocusChanged(boolean hasFocus) {
223         super.onWindowFocusChanged(hasFocus);
224         if (hasFocus) {
225             addActivityFlags(ACTIVITY_STATE_WINDOW_FOCUSED);
226         } else {
227             removeActivityFlags(ACTIVITY_STATE_WINDOW_FOCUSED);
228         }
229 
230     }
231 
isStarted()232     public boolean isStarted() {
233         return (mActivityFlags & ACTIVITY_STATE_STARTED) != 0;
234     }
235 
236     /**
237      * isResumed in already defined as a hidden final method in Activity.java
238      */
hasBeenResumed()239     public boolean hasBeenResumed() {
240         return (mActivityFlags & ACTIVITY_STATE_RESUMED) != 0;
241     }
242 
isUserActive()243     public boolean isUserActive() {
244         return (mActivityFlags & ACTIVITY_STATE_USER_ACTIVE) != 0;
245     }
246 
getActivityFlags()247     public int getActivityFlags() {
248         return mActivityFlags;
249     }
250 
addActivityFlags(int flags)251     protected void addActivityFlags(int flags) {
252         mActivityFlags |= flags;
253         onActivityFlagsChanged(flags);
254     }
255 
removeActivityFlags(int flags)256     protected void removeActivityFlags(int flags) {
257         mActivityFlags &= ~flags;
258         onActivityFlagsChanged(flags);
259     }
260 
onActivityFlagsChanged(int changeBits)261     protected void onActivityFlagsChanged(int changeBits) { }
262 
addOnDeviceProfileChangeListener(OnDeviceProfileChangeListener listener)263     public void addOnDeviceProfileChangeListener(OnDeviceProfileChangeListener listener) {
264         mDPChangeListeners.add(listener);
265     }
266 
removeOnDeviceProfileChangeListener(OnDeviceProfileChangeListener listener)267     public void removeOnDeviceProfileChangeListener(OnDeviceProfileChangeListener listener) {
268         mDPChangeListeners.remove(listener);
269     }
270 
dispatchDeviceProfileChanged()271     protected void dispatchDeviceProfileChanged() {
272         for (int i = mDPChangeListeners.size() - 1; i >= 0; i--) {
273             mDPChangeListeners.get(i).onDeviceProfileChanged(mDeviceProfile);
274         }
275     }
276 
addMultiWindowModeChangedListener(MultiWindowModeChangedListener listener)277     public void addMultiWindowModeChangedListener(MultiWindowModeChangedListener listener) {
278         mMultiWindowModeChangedListeners.add(listener);
279     }
280 
removeMultiWindowModeChangedListener(MultiWindowModeChangedListener listener)281     public void removeMultiWindowModeChangedListener(MultiWindowModeChangedListener listener) {
282         mMultiWindowModeChangedListeners.remove(listener);
283     }
284 
285     /**
286      * Used to set the override visibility state, used only to handle the transition home with the
287      * recents animation.
288      * @see QuickstepTransitionManager#createWallpaperOpenRunner
289      */
addForceInvisibleFlag(@nvisibilityFlags int flag)290     public void addForceInvisibleFlag(@InvisibilityFlags int flag) {
291         mForceInvisible |= flag;
292     }
293 
clearForceInvisibleFlag(@nvisibilityFlags int flag)294     public void clearForceInvisibleFlag(@InvisibilityFlags int flag) {
295         mForceInvisible &= ~flag;
296     }
297 
298     /**
299      * @return Wether this activity should be considered invisible regardless of actual visibility.
300      */
isForceInvisible()301     public boolean isForceInvisible() {
302         return hasSomeInvisibleFlag(INVISIBLE_FLAGS);
303     }
304 
hasSomeInvisibleFlag(int mask)305     public boolean hasSomeInvisibleFlag(int mask) {
306         return (mForceInvisible & mask) != 0;
307     }
308 
309     public interface MultiWindowModeChangedListener {
onMultiWindowModeChanged(boolean isInMultiWindowMode)310         void onMultiWindowModeChanged(boolean isInMultiWindowMode);
311     }
312 
dumpMisc(String prefix, PrintWriter writer)313     protected void dumpMisc(String prefix, PrintWriter writer) {
314         writer.println(prefix + "deviceProfile isTransposed="
315                 + getDeviceProfile().isVerticalBarLayout());
316         writer.println(prefix + "orientation=" + getResources().getConfiguration().orientation);
317         writer.println(prefix + "mSystemUiController: " + mSystemUiController);
318         writer.println(prefix + "mActivityFlags: " + mActivityFlags);
319         writer.println(prefix + "mForceInvisible: " + mForceInvisible);
320     }
321 
322     /**
323      * A wrapper around the platform method with Launcher specific checks
324      */
startShortcut(String packageName, String id, Rect sourceBounds, Bundle startActivityOptions, UserHandle user)325     public void startShortcut(String packageName, String id, Rect sourceBounds,
326             Bundle startActivityOptions, UserHandle user) {
327         if (GO_DISABLE_WIDGETS) {
328             return;
329         }
330         try {
331             getSystemService(LauncherApps.class).startShortcut(packageName, id, sourceBounds,
332                     startActivityOptions, user);
333         } catch (SecurityException | IllegalStateException e) {
334             Log.e(TAG, "Failed to start shortcut", e);
335         }
336     }
337 
fromContext(Context context)338     public static <T extends BaseActivity> T fromContext(Context context) {
339         if (context instanceof BaseActivity) {
340             return (T) context;
341         } else if (context instanceof ContextWrapper) {
342             return fromContext(((ContextWrapper) context).getBaseContext());
343         } else {
344             throw new IllegalArgumentException("Cannot find BaseActivity in parent tree");
345         }
346     }
347 }
348