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 */ 148 @Override getStatsLogManager()149 public StatsLogManager getStatsLogManager() { 150 if (mStatsLogManager == null) { 151 mStatsLogManager = StatsLogManager.newInstance(this); 152 } 153 return mStatsLogManager; 154 } 155 getSystemUiController()156 public SystemUiController getSystemUiController() { 157 if (mSystemUiController == null) { 158 mSystemUiController = new SystemUiController(getWindow()); 159 } 160 return mSystemUiController; 161 } 162 getScrimView()163 public ScrimView getScrimView() { 164 return null; 165 } 166 167 @Override onActivityResult(int requestCode, int resultCode, Intent data)168 public void onActivityResult(int requestCode, int resultCode, Intent data) { 169 super.onActivityResult(requestCode, resultCode, data); 170 } 171 172 @Override onStart()173 protected void onStart() { 174 addActivityFlags(ACTIVITY_STATE_STARTED); 175 super.onStart(); 176 } 177 178 @Override onResume()179 protected void onResume() { 180 addActivityFlags(ACTIVITY_STATE_RESUMED | ACTIVITY_STATE_USER_ACTIVE); 181 removeActivityFlags(ACTIVITY_STATE_USER_WILL_BE_ACTIVE); 182 super.onResume(); 183 } 184 185 @Override onUserLeaveHint()186 protected void onUserLeaveHint() { 187 removeActivityFlags(ACTIVITY_STATE_USER_ACTIVE); 188 super.onUserLeaveHint(); 189 } 190 191 @Override onMultiWindowModeChanged(boolean isInMultiWindowMode, Configuration newConfig)192 public void onMultiWindowModeChanged(boolean isInMultiWindowMode, Configuration newConfig) { 193 super.onMultiWindowModeChanged(isInMultiWindowMode, newConfig); 194 for (int i = mMultiWindowModeChangedListeners.size() - 1; i >= 0; i--) { 195 mMultiWindowModeChangedListeners.get(i).onMultiWindowModeChanged(isInMultiWindowMode); 196 } 197 } 198 199 @Override onStop()200 protected void onStop() { 201 removeActivityFlags(ACTIVITY_STATE_STARTED | ACTIVITY_STATE_USER_ACTIVE); 202 mForceInvisible = 0; 203 super.onStop(); 204 205 // Reset the overridden sysui flags used for the task-swipe launch animation, this is a 206 // catch all for if we do not get resumed (and therefore not paused below) 207 getSystemUiController().updateUiState(UI_STATE_FULLSCREEN_TASK, 0); 208 } 209 210 @Override onPause()211 protected void onPause() { 212 removeActivityFlags(ACTIVITY_STATE_RESUMED | ACTIVITY_STATE_DEFERRED_RESUMED); 213 super.onPause(); 214 215 // Reset the overridden sysui flags used for the task-swipe launch animation, we do this 216 // here instead of at the end of the animation because the start of the new activity does 217 // not happen immediately, which would cause us to reset to launcher's sysui flags and then 218 // back to the new app (causing a flash) 219 getSystemUiController().updateUiState(UI_STATE_FULLSCREEN_TASK, 0); 220 } 221 222 @Override onWindowFocusChanged(boolean hasFocus)223 public void onWindowFocusChanged(boolean hasFocus) { 224 super.onWindowFocusChanged(hasFocus); 225 if (hasFocus) { 226 addActivityFlags(ACTIVITY_STATE_WINDOW_FOCUSED); 227 } else { 228 removeActivityFlags(ACTIVITY_STATE_WINDOW_FOCUSED); 229 } 230 231 } 232 isStarted()233 public boolean isStarted() { 234 return (mActivityFlags & ACTIVITY_STATE_STARTED) != 0; 235 } 236 237 /** 238 * isResumed in already defined as a hidden final method in Activity.java 239 */ hasBeenResumed()240 public boolean hasBeenResumed() { 241 return (mActivityFlags & ACTIVITY_STATE_RESUMED) != 0; 242 } 243 isUserActive()244 public boolean isUserActive() { 245 return (mActivityFlags & ACTIVITY_STATE_USER_ACTIVE) != 0; 246 } 247 getActivityFlags()248 public int getActivityFlags() { 249 return mActivityFlags; 250 } 251 addActivityFlags(int flags)252 protected void addActivityFlags(int flags) { 253 mActivityFlags |= flags; 254 onActivityFlagsChanged(flags); 255 } 256 removeActivityFlags(int flags)257 protected void removeActivityFlags(int flags) { 258 mActivityFlags &= ~flags; 259 onActivityFlagsChanged(flags); 260 } 261 onActivityFlagsChanged(int changeBits)262 protected void onActivityFlagsChanged(int changeBits) { } 263 addOnDeviceProfileChangeListener(OnDeviceProfileChangeListener listener)264 public void addOnDeviceProfileChangeListener(OnDeviceProfileChangeListener listener) { 265 mDPChangeListeners.add(listener); 266 } 267 removeOnDeviceProfileChangeListener(OnDeviceProfileChangeListener listener)268 public void removeOnDeviceProfileChangeListener(OnDeviceProfileChangeListener listener) { 269 mDPChangeListeners.remove(listener); 270 } 271 dispatchDeviceProfileChanged()272 protected void dispatchDeviceProfileChanged() { 273 for (int i = mDPChangeListeners.size() - 1; i >= 0; i--) { 274 mDPChangeListeners.get(i).onDeviceProfileChanged(mDeviceProfile); 275 } 276 } 277 addMultiWindowModeChangedListener(MultiWindowModeChangedListener listener)278 public void addMultiWindowModeChangedListener(MultiWindowModeChangedListener listener) { 279 mMultiWindowModeChangedListeners.add(listener); 280 } 281 removeMultiWindowModeChangedListener(MultiWindowModeChangedListener listener)282 public void removeMultiWindowModeChangedListener(MultiWindowModeChangedListener listener) { 283 mMultiWindowModeChangedListeners.remove(listener); 284 } 285 286 /** 287 * Used to set the override visibility state, used only to handle the transition home with the 288 * recents animation. 289 * @see QuickstepTransitionManager#createWallpaperOpenRunner 290 */ addForceInvisibleFlag(@nvisibilityFlags int flag)291 public void addForceInvisibleFlag(@InvisibilityFlags int flag) { 292 mForceInvisible |= flag; 293 } 294 clearForceInvisibleFlag(@nvisibilityFlags int flag)295 public void clearForceInvisibleFlag(@InvisibilityFlags int flag) { 296 mForceInvisible &= ~flag; 297 } 298 299 /** 300 * @return Wether this activity should be considered invisible regardless of actual visibility. 301 */ isForceInvisible()302 public boolean isForceInvisible() { 303 return hasSomeInvisibleFlag(INVISIBLE_FLAGS); 304 } 305 hasSomeInvisibleFlag(int mask)306 public boolean hasSomeInvisibleFlag(int mask) { 307 return (mForceInvisible & mask) != 0; 308 } 309 310 public interface MultiWindowModeChangedListener { onMultiWindowModeChanged(boolean isInMultiWindowMode)311 void onMultiWindowModeChanged(boolean isInMultiWindowMode); 312 } 313 dumpMisc(String prefix, PrintWriter writer)314 protected void dumpMisc(String prefix, PrintWriter writer) { 315 writer.println(prefix + "deviceProfile isTransposed=" 316 + getDeviceProfile().isVerticalBarLayout()); 317 writer.println(prefix + "orientation=" + getResources().getConfiguration().orientation); 318 writer.println(prefix + "mSystemUiController: " + mSystemUiController); 319 writer.println(prefix + "mActivityFlags: " + mActivityFlags); 320 writer.println(prefix + "mForceInvisible: " + mForceInvisible); 321 } 322 323 /** 324 * A wrapper around the platform method with Launcher specific checks 325 */ startShortcut(String packageName, String id, Rect sourceBounds, Bundle startActivityOptions, UserHandle user)326 public void startShortcut(String packageName, String id, Rect sourceBounds, 327 Bundle startActivityOptions, UserHandle user) { 328 if (GO_DISABLE_WIDGETS) { 329 return; 330 } 331 try { 332 getSystemService(LauncherApps.class).startShortcut(packageName, id, sourceBounds, 333 startActivityOptions, user); 334 } catch (SecurityException | IllegalStateException e) { 335 Log.e(TAG, "Failed to start shortcut", e); 336 } 337 } 338 fromContext(Context context)339 public static <T extends BaseActivity> T fromContext(Context context) { 340 if (context instanceof BaseActivity) { 341 return (T) context; 342 } else if (context instanceof ContextWrapper) { 343 return fromContext(((ContextWrapper) context).getBaseContext()); 344 } else { 345 throw new IllegalArgumentException("Cannot find BaseActivity in parent tree"); 346 } 347 } 348 } 349