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