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.view.View; 33 import android.window.OnBackInvokedDispatcher; 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.testing.TestLogging; 40 import com.android.launcher3.testing.shared.TestProtocol; 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 @Override getViewCache()157 public ViewCache getViewCache() { 158 return mViewCache; 159 } 160 161 @Override getDeviceProfile()162 public DeviceProfile getDeviceProfile() { 163 return mDeviceProfile; 164 } 165 166 @Override getOnDeviceProfileChangeListeners()167 public List<OnDeviceProfileChangeListener> getOnDeviceProfileChangeListeners() { 168 return mDPChangeListeners; 169 } 170 171 /** 172 * Returns {@link StatsLogManager} for user event logging. 173 */ 174 @Override getStatsLogManager()175 public StatsLogManager getStatsLogManager() { 176 if (mStatsLogManager == null) { 177 mStatsLogManager = StatsLogManager.newInstance(this); 178 } 179 return mStatsLogManager; 180 } 181 getSystemUiController()182 public SystemUiController getSystemUiController() { 183 if (mSystemUiController == null) { 184 mSystemUiController = new SystemUiController(getWindow()); 185 } 186 return mSystemUiController; 187 } 188 getScrimView()189 public ScrimView getScrimView() { 190 return null; 191 } 192 193 @Override onActivityResult(int requestCode, int resultCode, Intent data)194 public void onActivityResult(int requestCode, int resultCode, Intent data) { 195 super.onActivityResult(requestCode, resultCode, data); 196 } 197 198 @Override onCreate(Bundle savedInstanceState)199 protected void onCreate(Bundle savedInstanceState) { 200 super.onCreate(savedInstanceState); 201 registerBackDispatcher(); 202 } 203 204 @Override onStart()205 protected void onStart() { 206 addActivityFlags(ACTIVITY_STATE_STARTED); 207 super.onStart(); 208 } 209 210 @Override onResume()211 protected void onResume() { 212 setResumed(); 213 super.onResume(); 214 } 215 216 @Override onUserLeaveHint()217 protected void onUserLeaveHint() { 218 removeActivityFlags(ACTIVITY_STATE_USER_ACTIVE); 219 super.onUserLeaveHint(); 220 } 221 222 @Override onMultiWindowModeChanged(boolean isInMultiWindowMode, Configuration newConfig)223 public void onMultiWindowModeChanged(boolean isInMultiWindowMode, Configuration newConfig) { 224 super.onMultiWindowModeChanged(isInMultiWindowMode, newConfig); 225 for (int i = mMultiWindowModeChangedListeners.size() - 1; i >= 0; i--) { 226 mMultiWindowModeChangedListeners.get(i).onMultiWindowModeChanged(isInMultiWindowMode); 227 } 228 } 229 230 @Override onStop()231 protected void onStop() { 232 removeActivityFlags(ACTIVITY_STATE_STARTED | ACTIVITY_STATE_USER_ACTIVE); 233 mForceInvisible = 0; 234 super.onStop(); 235 236 // Reset the overridden sysui flags used for the task-swipe launch animation, this is a 237 // catch all for if we do not get resumed (and therefore not paused below) 238 getSystemUiController().updateUiState(UI_STATE_FULLSCREEN_TASK, 0); 239 } 240 241 @Override onPause()242 protected void onPause() { 243 setPaused(); 244 super.onPause(); 245 246 // Reset the overridden sysui flags used for the task-swipe launch animation, we do this 247 // here instead of at the end of the animation because the start of the new activity does 248 // not happen immediately, which would cause us to reset to launcher's sysui flags and then 249 // back to the new app (causing a flash) 250 getSystemUiController().updateUiState(UI_STATE_FULLSCREEN_TASK, 0); 251 } 252 253 @Override onWindowFocusChanged(boolean hasFocus)254 public void onWindowFocusChanged(boolean hasFocus) { 255 super.onWindowFocusChanged(hasFocus); 256 if (hasFocus) { 257 addActivityFlags(ACTIVITY_STATE_WINDOW_FOCUSED); 258 } else { 259 removeActivityFlags(ACTIVITY_STATE_WINDOW_FOCUSED); 260 } 261 262 } 263 registerBackDispatcher()264 protected void registerBackDispatcher() { 265 if (Utilities.ATLEAST_T) { 266 getOnBackInvokedDispatcher().registerOnBackInvokedCallback( 267 OnBackInvokedDispatcher.PRIORITY_DEFAULT, 268 () -> { 269 onBackPressed(); 270 TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "onBackInvoked"); 271 }); 272 } 273 } 274 isStarted()275 public boolean isStarted() { 276 return (mActivityFlags & ACTIVITY_STATE_STARTED) != 0; 277 } 278 279 /** 280 * isResumed in already defined as a hidden final method in Activity.java 281 */ hasBeenResumed()282 public boolean hasBeenResumed() { 283 return (mActivityFlags & ACTIVITY_STATE_RESUMED) != 0; 284 } 285 286 /** 287 * Sets the activity to appear as paused. 288 */ setPaused()289 public void setPaused() { 290 removeActivityFlags(ACTIVITY_STATE_RESUMED | ACTIVITY_STATE_DEFERRED_RESUMED); 291 } 292 293 /** 294 * Sets the activity to appear as resumed. 295 */ setResumed()296 public void setResumed() { 297 addActivityFlags(ACTIVITY_STATE_RESUMED | ACTIVITY_STATE_USER_ACTIVE); 298 removeActivityFlags(ACTIVITY_STATE_USER_WILL_BE_ACTIVE); 299 } 300 isUserActive()301 public boolean isUserActive() { 302 return (mActivityFlags & ACTIVITY_STATE_USER_ACTIVE) != 0; 303 } 304 getActivityFlags()305 public int getActivityFlags() { 306 return mActivityFlags; 307 } 308 addActivityFlags(int toAdd)309 protected void addActivityFlags(int toAdd) { 310 final int oldFlags = mActivityFlags; 311 mActivityFlags |= toAdd; 312 if (DEBUG) { 313 Log.d(TAG, "Launcher flags updated: " + formatFlagChange(mActivityFlags, oldFlags, 314 BaseActivity::getActivityStateString)); 315 } 316 onActivityFlagsChanged(toAdd); 317 } 318 removeActivityFlags(int toRemove)319 protected void removeActivityFlags(int toRemove) { 320 final int oldFlags = mActivityFlags; 321 mActivityFlags &= ~toRemove; 322 if (DEBUG) { 323 Log.d(TAG, "Launcher flags updated: " + formatFlagChange(mActivityFlags, oldFlags, 324 BaseActivity::getActivityStateString)); 325 } 326 327 onActivityFlagsChanged(toRemove); 328 } 329 onActivityFlagsChanged(int changeBits)330 protected void onActivityFlagsChanged(int changeBits) { 331 } 332 addMultiWindowModeChangedListener(MultiWindowModeChangedListener listener)333 public void addMultiWindowModeChangedListener(MultiWindowModeChangedListener listener) { 334 mMultiWindowModeChangedListeners.add(listener); 335 } 336 removeMultiWindowModeChangedListener(MultiWindowModeChangedListener listener)337 public void removeMultiWindowModeChangedListener(MultiWindowModeChangedListener listener) { 338 mMultiWindowModeChangedListeners.remove(listener); 339 } 340 341 /** 342 * Used to set the override visibility state, used only to handle the transition home with the 343 * recents animation. 344 * 345 * @see QuickstepTransitionManager#createWallpaperOpenRunner 346 */ addForceInvisibleFlag(@nvisibilityFlags int flag)347 public void addForceInvisibleFlag(@InvisibilityFlags int flag) { 348 mForceInvisible |= flag; 349 } 350 clearForceInvisibleFlag(@nvisibilityFlags int flag)351 public void clearForceInvisibleFlag(@InvisibilityFlags int flag) { 352 mForceInvisible &= ~flag; 353 } 354 355 /** 356 * @return Wether this activity should be considered invisible regardless of actual visibility. 357 */ isForceInvisible()358 public boolean isForceInvisible() { 359 return hasSomeInvisibleFlag(INVISIBLE_FLAGS); 360 } 361 hasSomeInvisibleFlag(int mask)362 public boolean hasSomeInvisibleFlag(int mask) { 363 return (mForceInvisible & mask) != 0; 364 } 365 366 /** 367 * Attempts to clear accessibility focus on {@param view}. 368 */ tryClearAccessibilityFocus(View view)369 public void tryClearAccessibilityFocus(View view) { 370 } 371 372 public interface MultiWindowModeChangedListener { onMultiWindowModeChanged(boolean isInMultiWindowMode)373 void onMultiWindowModeChanged(boolean isInMultiWindowMode); 374 } 375 dumpMisc(String prefix, PrintWriter writer)376 protected void dumpMisc(String prefix, PrintWriter writer) { 377 writer.println(prefix + "deviceProfile isTransposed=" 378 + getDeviceProfile().isVerticalBarLayout()); 379 writer.println(prefix + "orientation=" + getResources().getConfiguration().orientation); 380 writer.println(prefix + "mSystemUiController: " + mSystemUiController); 381 writer.println(prefix + "mActivityFlags: " + getActivityStateString(mActivityFlags)); 382 writer.println(prefix + "mForceInvisible: " + mForceInvisible); 383 } 384 fromContext(Context context)385 public static <T extends BaseActivity> T fromContext(Context context) { 386 if (context instanceof BaseActivity) { 387 return (T) context; 388 } else if (context instanceof ContextWrapper) { 389 return fromContext(((ContextWrapper) context).getBaseContext()); 390 } else { 391 throw new IllegalArgumentException("Cannot find BaseActivity in parent tree"); 392 } 393 } 394 } 395