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