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.util.FlagDebugUtils.appendFlag; 20 import static com.android.launcher3.util.FlagDebugUtils.formatFlagChange; 21 import static com.android.launcher3.util.SystemUiController.UI_STATE_FULLSCREEN_TASK; 22 23 import static java.lang.annotation.RetentionPolicy.SOURCE; 24 25 import android.app.Activity; 26 import android.content.Context; 27 import android.content.ContextWrapper; 28 import android.content.Intent; 29 import android.content.res.Configuration; 30 import android.os.Bundle; 31 import android.util.Log; 32 import android.window.OnBackInvokedDispatcher; 33 34 import androidx.annotation.IntDef; 35 36 import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener; 37 import com.android.launcher3.logging.StatsLogManager; 38 import com.android.launcher3.testing.TestLogging; 39 import com.android.launcher3.testing.shared.TestProtocol; 40 import com.android.launcher3.util.RunnableList; 41 import com.android.launcher3.util.SystemUiController; 42 import com.android.launcher3.util.ViewCache; 43 import com.android.launcher3.views.ActivityContext; 44 import com.android.launcher3.views.ScrimView; 45 46 import java.io.PrintWriter; 47 import java.lang.annotation.Retention; 48 import java.util.ArrayList; 49 import java.util.List; 50 import java.util.StringJoiner; 51 52 /** 53 * Launcher BaseActivity 54 */ 55 public abstract class BaseActivity extends Activity implements ActivityContext { 56 57 private static final String TAG = "BaseActivity"; 58 static final boolean DEBUG = false; 59 60 public static final int INVISIBLE_BY_STATE_HANDLER = 1 << 0; 61 public static final int INVISIBLE_BY_APP_TRANSITIONS = 1 << 1; 62 public static final int INVISIBLE_BY_PENDING_FLAGS = 1 << 2; 63 64 // This is not treated as invisibility flag, but adds as a hint for an incomplete transition. 65 // When the wallpaper animation runs, it replaces this flag with a proper invisibility 66 // flag, INVISIBLE_BY_PENDING_FLAGS only for the duration of that animation. 67 public static final int PENDING_INVISIBLE_BY_WALLPAPER_ANIMATION = 1 << 3; 68 69 private static final int INVISIBLE_FLAGS = 70 INVISIBLE_BY_STATE_HANDLER | INVISIBLE_BY_APP_TRANSITIONS | INVISIBLE_BY_PENDING_FLAGS; 71 public static final int STATE_HANDLER_INVISIBILITY_FLAGS = 72 INVISIBLE_BY_STATE_HANDLER | PENDING_INVISIBLE_BY_WALLPAPER_ANIMATION; 73 public static final int INVISIBLE_ALL = 74 INVISIBLE_FLAGS | PENDING_INVISIBLE_BY_WALLPAPER_ANIMATION; 75 76 @Retention(SOURCE) 77 @IntDef( 78 flag = true, 79 value = {INVISIBLE_BY_STATE_HANDLER, INVISIBLE_BY_APP_TRANSITIONS, 80 INVISIBLE_BY_PENDING_FLAGS, PENDING_INVISIBLE_BY_WALLPAPER_ANIMATION}) 81 public @interface InvisibilityFlags { 82 } 83 84 private final ArrayList<OnDeviceProfileChangeListener> mDPChangeListeners = new ArrayList<>(); 85 private final ArrayList<MultiWindowModeChangedListener> mMultiWindowModeChangedListeners = 86 new ArrayList<>(); 87 88 protected DeviceProfile mDeviceProfile; 89 protected SystemUiController mSystemUiController; 90 private StatsLogManager mStatsLogManager; 91 92 93 public static final int ACTIVITY_STATE_STARTED = 1 << 0; 94 public static final int ACTIVITY_STATE_RESUMED = 1 << 1; 95 96 /** 97 * State flags indicating that the activity has received one frame after resume, and was 98 * not immediately paused. 99 */ 100 public static final int ACTIVITY_STATE_DEFERRED_RESUMED = 1 << 2; 101 102 public static final int ACTIVITY_STATE_WINDOW_FOCUSED = 1 << 3; 103 104 /** 105 * State flag indicating if the user is active or the activity when to background as a result 106 * of user action. 107 * 108 * @see #isUserActive() 109 */ 110 public static final int ACTIVITY_STATE_USER_ACTIVE = 1 << 4; 111 112 /** 113 * State flag indicating if the user will be active shortly. 114 */ 115 public static final int ACTIVITY_STATE_USER_WILL_BE_ACTIVE = 1 << 5; 116 117 /** 118 * State flag indicating that a state transition is in progress 119 */ 120 public static final int ACTIVITY_STATE_TRANSITION_ACTIVE = 1 << 6; 121 122 @Retention(SOURCE) 123 @IntDef( 124 flag = true, 125 value = {ACTIVITY_STATE_STARTED, 126 ACTIVITY_STATE_RESUMED, 127 ACTIVITY_STATE_DEFERRED_RESUMED, 128 ACTIVITY_STATE_WINDOW_FOCUSED, 129 ACTIVITY_STATE_USER_ACTIVE, 130 ACTIVITY_STATE_TRANSITION_ACTIVE}) 131 public @interface ActivityFlags { 132 } 133 134 /** Returns a human-readable string for the specified {@link ActivityFlags}. */ getActivityStateString(@ctivityFlags int flags)135 public static String getActivityStateString(@ActivityFlags int flags) { 136 StringJoiner result = new StringJoiner("|"); 137 appendFlag(result, flags, ACTIVITY_STATE_STARTED, "state_started"); 138 appendFlag(result, flags, ACTIVITY_STATE_RESUMED, "state_resumed"); 139 appendFlag(result, flags, ACTIVITY_STATE_DEFERRED_RESUMED, "state_deferred_resumed"); 140 appendFlag(result, flags, ACTIVITY_STATE_WINDOW_FOCUSED, "state_window_focused"); 141 appendFlag(result, flags, ACTIVITY_STATE_USER_ACTIVE, "state_user_active"); 142 appendFlag(result, flags, ACTIVITY_STATE_TRANSITION_ACTIVE, "state_transition_active"); 143 return result.toString(); 144 } 145 146 @ActivityFlags 147 private int mActivityFlags; 148 149 // When the recents animation is running, the visibility of the Launcher is managed by the 150 // animation 151 @InvisibilityFlags 152 private int mForceInvisible; 153 154 private final ViewCache mViewCache = new ViewCache(); 155 156 @Retention(SOURCE) 157 @IntDef({EVENT_STARTED, EVENT_RESUMED, EVENT_STOPPED, EVENT_DESTROYED}) 158 public @interface ActivityEvent { } 159 public static final int EVENT_STARTED = 0; 160 public static final int EVENT_RESUMED = 1; 161 public static final int EVENT_STOPPED = 2; 162 public static final int EVENT_DESTROYED = 3; 163 164 // Callback array that corresponds to events defined in @ActivityEvent 165 private final RunnableList[] mEventCallbacks = 166 {new RunnableList(), new RunnableList(), new RunnableList(), new RunnableList()}; 167 168 @Override getViewCache()169 public ViewCache getViewCache() { 170 return mViewCache; 171 } 172 173 @Override getDeviceProfile()174 public DeviceProfile getDeviceProfile() { 175 return mDeviceProfile; 176 } 177 178 @Override getOnDeviceProfileChangeListeners()179 public List<OnDeviceProfileChangeListener> getOnDeviceProfileChangeListeners() { 180 return mDPChangeListeners; 181 } 182 183 /** 184 * Returns {@link StatsLogManager} for user event logging. 185 */ 186 @Override getStatsLogManager()187 public StatsLogManager getStatsLogManager() { 188 if (mStatsLogManager == null) { 189 mStatsLogManager = StatsLogManager.newInstance(this); 190 } 191 return mStatsLogManager; 192 } 193 getSystemUiController()194 public SystemUiController getSystemUiController() { 195 if (mSystemUiController == null) { 196 mSystemUiController = new SystemUiController(getWindow()); 197 } 198 return mSystemUiController; 199 } 200 getScrimView()201 public ScrimView getScrimView() { 202 return null; 203 } 204 205 @Override onActivityResult(int requestCode, int resultCode, Intent data)206 public void onActivityResult(int requestCode, int resultCode, Intent data) { 207 super.onActivityResult(requestCode, resultCode, data); 208 } 209 210 @Override onCreate(Bundle savedInstanceState)211 protected void onCreate(Bundle savedInstanceState) { 212 super.onCreate(savedInstanceState); 213 registerBackDispatcher(); 214 } 215 216 @Override onStart()217 protected void onStart() { 218 addActivityFlags(ACTIVITY_STATE_STARTED); 219 super.onStart(); 220 mEventCallbacks[EVENT_STARTED].executeAllAndClear(); 221 } 222 223 @Override onResume()224 protected void onResume() { 225 setResumed(); 226 super.onResume(); 227 mEventCallbacks[EVENT_RESUMED].executeAllAndClear(); 228 } 229 230 @Override onUserLeaveHint()231 protected void onUserLeaveHint() { 232 removeActivityFlags(ACTIVITY_STATE_USER_ACTIVE); 233 super.onUserLeaveHint(); 234 } 235 236 @Override onMultiWindowModeChanged(boolean isInMultiWindowMode, Configuration newConfig)237 public void onMultiWindowModeChanged(boolean isInMultiWindowMode, Configuration newConfig) { 238 super.onMultiWindowModeChanged(isInMultiWindowMode, newConfig); 239 for (int i = mMultiWindowModeChangedListeners.size() - 1; i >= 0; i--) { 240 mMultiWindowModeChangedListeners.get(i).onMultiWindowModeChanged(isInMultiWindowMode); 241 } 242 } 243 244 @Override onStop()245 protected void onStop() { 246 removeActivityFlags(ACTIVITY_STATE_STARTED | ACTIVITY_STATE_USER_ACTIVE); 247 mForceInvisible = 0; 248 super.onStop(); 249 mEventCallbacks[EVENT_STOPPED].executeAllAndClear(); 250 251 252 // Reset the overridden sysui flags used for the task-swipe launch animation, this is a 253 // catch all for if we do not get resumed (and therefore not paused below) 254 getSystemUiController().updateUiState(UI_STATE_FULLSCREEN_TASK, 0); 255 } 256 257 @Override onDestroy()258 protected void onDestroy() { 259 super.onDestroy(); 260 mEventCallbacks[EVENT_DESTROYED].executeAllAndClear(); 261 } 262 263 @Override onPause()264 protected void onPause() { 265 setPaused(); 266 super.onPause(); 267 268 // Reset the overridden sysui flags used for the task-swipe launch animation, we do this 269 // here instead of at the end of the animation because the start of the new activity does 270 // not happen immediately, which would cause us to reset to launcher's sysui flags and then 271 // back to the new app (causing a flash) 272 getSystemUiController().updateUiState(UI_STATE_FULLSCREEN_TASK, 0); 273 } 274 275 @Override onWindowFocusChanged(boolean hasFocus)276 public void onWindowFocusChanged(boolean hasFocus) { 277 super.onWindowFocusChanged(hasFocus); 278 if (hasFocus) { 279 addActivityFlags(ACTIVITY_STATE_WINDOW_FOCUSED); 280 } else { 281 removeActivityFlags(ACTIVITY_STATE_WINDOW_FOCUSED); 282 } 283 } 284 registerBackDispatcher()285 protected void registerBackDispatcher() { 286 if (Utilities.ATLEAST_T) { 287 getOnBackInvokedDispatcher().registerOnBackInvokedCallback( 288 OnBackInvokedDispatcher.PRIORITY_DEFAULT, 289 () -> { 290 onBackPressed(); 291 TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "onBackInvoked"); 292 }); 293 } 294 } 295 isStarted()296 public boolean isStarted() { 297 return (mActivityFlags & ACTIVITY_STATE_STARTED) != 0; 298 } 299 300 /** 301 * isResumed in already defined as a hidden final method in Activity.java 302 */ hasBeenResumed()303 public boolean hasBeenResumed() { 304 return (mActivityFlags & ACTIVITY_STATE_RESUMED) != 0; 305 } 306 307 /** 308 * Sets the activity to appear as paused. 309 */ setPaused()310 public void setPaused() { 311 removeActivityFlags(ACTIVITY_STATE_RESUMED | ACTIVITY_STATE_DEFERRED_RESUMED); 312 } 313 314 /** 315 * Sets the activity to appear as resumed. 316 */ setResumed()317 public void setResumed() { 318 addActivityFlags(ACTIVITY_STATE_RESUMED | ACTIVITY_STATE_USER_ACTIVE); 319 removeActivityFlags(ACTIVITY_STATE_USER_WILL_BE_ACTIVE); 320 } 321 isUserActive()322 public boolean isUserActive() { 323 return (mActivityFlags & ACTIVITY_STATE_USER_ACTIVE) != 0; 324 } 325 getActivityFlags()326 public int getActivityFlags() { 327 return mActivityFlags; 328 } 329 addActivityFlags(int toAdd)330 protected void addActivityFlags(int toAdd) { 331 final int oldFlags = mActivityFlags; 332 mActivityFlags |= toAdd; 333 if (DEBUG) { 334 Log.d(TAG, "Launcher flags updated: " + formatFlagChange(mActivityFlags, oldFlags, 335 BaseActivity::getActivityStateString)); 336 } 337 onActivityFlagsChanged(toAdd); 338 } 339 removeActivityFlags(int toRemove)340 protected void removeActivityFlags(int toRemove) { 341 final int oldFlags = mActivityFlags; 342 mActivityFlags &= ~toRemove; 343 if (DEBUG) { 344 Log.d(TAG, "Launcher flags updated: " + formatFlagChange(mActivityFlags, oldFlags, 345 BaseActivity::getActivityStateString)); 346 } 347 348 onActivityFlagsChanged(toRemove); 349 } 350 onActivityFlagsChanged(int changeBits)351 protected void onActivityFlagsChanged(int changeBits) { 352 } 353 addMultiWindowModeChangedListener(MultiWindowModeChangedListener listener)354 public void addMultiWindowModeChangedListener(MultiWindowModeChangedListener listener) { 355 mMultiWindowModeChangedListeners.add(listener); 356 } 357 removeMultiWindowModeChangedListener(MultiWindowModeChangedListener listener)358 public void removeMultiWindowModeChangedListener(MultiWindowModeChangedListener listener) { 359 mMultiWindowModeChangedListeners.remove(listener); 360 } 361 362 /** 363 * Used to set the override visibility state, used only to handle the transition home with the 364 * recents animation. 365 * 366 * @see QuickstepTransitionManager#createWallpaperOpenRunner 367 */ addForceInvisibleFlag(@nvisibilityFlags int flag)368 public void addForceInvisibleFlag(@InvisibilityFlags int flag) { 369 mForceInvisible |= flag; 370 } 371 clearForceInvisibleFlag(@nvisibilityFlags int flag)372 public void clearForceInvisibleFlag(@InvisibilityFlags int flag) { 373 mForceInvisible &= ~flag; 374 } 375 376 /** 377 * @return Wether this activity should be considered invisible regardless of actual visibility. 378 */ isForceInvisible()379 public boolean isForceInvisible() { 380 return hasSomeInvisibleFlag(INVISIBLE_FLAGS); 381 } 382 hasSomeInvisibleFlag(int mask)383 public boolean hasSomeInvisibleFlag(int mask) { 384 return (mForceInvisible & mask) != 0; 385 } 386 387 /** 388 * Adds a callback for the provided activity event 389 */ addEventCallback(@ctivityEvent int event, Runnable callback)390 public void addEventCallback(@ActivityEvent int event, Runnable callback) { 391 mEventCallbacks[event].add(callback); 392 } 393 394 /** Removes a previously added callback */ removeEventCallback(@ctivityEvent int event, Runnable callback)395 public void removeEventCallback(@ActivityEvent int event, Runnable callback) { 396 mEventCallbacks[event].remove(callback); 397 } 398 399 public interface MultiWindowModeChangedListener { onMultiWindowModeChanged(boolean isInMultiWindowMode)400 void onMultiWindowModeChanged(boolean isInMultiWindowMode); 401 } 402 dumpMisc(String prefix, PrintWriter writer)403 protected void dumpMisc(String prefix, PrintWriter writer) { 404 writer.println(prefix + "deviceProfile isTransposed=" 405 + getDeviceProfile().isVerticalBarLayout()); 406 writer.println(prefix + "orientation=" + getResources().getConfiguration().orientation); 407 writer.println(prefix + "mSystemUiController: " + mSystemUiController); 408 writer.println(prefix + "mActivityFlags: " + getActivityStateString(mActivityFlags)); 409 writer.println(prefix + "mForceInvisible: " + mForceInvisible); 410 } 411 fromContext(Context context)412 public static <T extends BaseActivity> T fromContext(Context context) { 413 if (context instanceof BaseActivity) { 414 return (T) context; 415 } else if (context instanceof ActivityContextDelegate) { 416 return (T) ((ActivityContextDelegate) context).mDelegate; 417 } else if (context instanceof ContextWrapper) { 418 return fromContext(((ContextWrapper) context).getBaseContext()); 419 } else { 420 throw new IllegalArgumentException("Cannot find BaseActivity in parent tree"); 421 } 422 } 423 } 424