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.SystemUiController.UI_STATE_OVERVIEW; 20 21 import static java.lang.annotation.RetentionPolicy.SOURCE; 22 23 import android.app.Activity; 24 import android.content.Context; 25 import android.content.ContextWrapper; 26 import android.content.Intent; 27 import android.content.res.Configuration; 28 import android.view.ContextThemeWrapper; 29 30 import androidx.annotation.IntDef; 31 32 import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener; 33 import com.android.launcher3.logging.StatsLogManager; 34 import com.android.launcher3.logging.StatsLogUtils; 35 import com.android.launcher3.logging.StatsLogUtils.LogStateProvider; 36 import com.android.launcher3.logging.UserEventDispatcher; 37 import com.android.launcher3.logging.UserEventDispatcher.UserEventDelegate; 38 import com.android.launcher3.uioverrides.UiFactory; 39 import com.android.launcher3.userevent.nano.LauncherLogProto; 40 import com.android.launcher3.util.SystemUiController; 41 import com.android.launcher3.util.ViewCache; 42 import com.android.launcher3.views.ActivityContext; 43 44 import java.io.FileDescriptor; 45 import java.io.PrintWriter; 46 import java.lang.annotation.Retention; 47 import java.util.ArrayList; 48 49 public abstract class BaseActivity extends Activity 50 implements UserEventDelegate, LogStateProvider, ActivityContext { 51 52 public static final int INVISIBLE_BY_STATE_HANDLER = 1 << 0; 53 public static final int INVISIBLE_BY_APP_TRANSITIONS = 1 << 1; 54 public static final int INVISIBLE_BY_PENDING_FLAGS = 1 << 2; 55 56 // This is not treated as invisibility flag, but adds as a hint for an incomplete transition. 57 // When the wallpaper animation runs, it replaces this flag with a proper invisibility 58 // flag, INVISIBLE_BY_PENDING_FLAGS only for the duration of that animation. 59 public static final int PENDING_INVISIBLE_BY_WALLPAPER_ANIMATION = 1 << 3; 60 61 private static final int INVISIBLE_FLAGS = 62 INVISIBLE_BY_STATE_HANDLER | INVISIBLE_BY_APP_TRANSITIONS | INVISIBLE_BY_PENDING_FLAGS; 63 public static final int STATE_HANDLER_INVISIBILITY_FLAGS = 64 INVISIBLE_BY_STATE_HANDLER | PENDING_INVISIBLE_BY_WALLPAPER_ANIMATION; 65 public static final int INVISIBLE_ALL = 66 INVISIBLE_FLAGS | PENDING_INVISIBLE_BY_WALLPAPER_ANIMATION; 67 68 @Retention(SOURCE) 69 @IntDef( 70 flag = true, 71 value = {INVISIBLE_BY_STATE_HANDLER, INVISIBLE_BY_APP_TRANSITIONS, 72 INVISIBLE_BY_PENDING_FLAGS, PENDING_INVISIBLE_BY_WALLPAPER_ANIMATION}) 73 public @interface InvisibilityFlags{} 74 75 private final ArrayList<OnDeviceProfileChangeListener> mDPChangeListeners = new ArrayList<>(); 76 private final ArrayList<MultiWindowModeChangedListener> mMultiWindowModeChangedListeners = 77 new ArrayList<>(); 78 79 protected DeviceProfile mDeviceProfile; 80 protected UserEventDispatcher mUserEventDispatcher; 81 protected StatsLogManager mStatsLogManager; 82 protected SystemUiController mSystemUiController; 83 84 private static final int ACTIVITY_STATE_STARTED = 1 << 0; 85 private static final int ACTIVITY_STATE_RESUMED = 1 << 1; 86 /** 87 * State flag indicating if the user is active or the actitvity when to background as a result 88 * of user action. 89 * @see #isUserActive() 90 */ 91 private static final int ACTIVITY_STATE_USER_ACTIVE = 1 << 2; 92 93 @Retention(SOURCE) 94 @IntDef( 95 flag = true, 96 value = {ACTIVITY_STATE_STARTED, ACTIVITY_STATE_RESUMED, ACTIVITY_STATE_USER_ACTIVE}) 97 public @interface ActivityFlags{} 98 99 @ActivityFlags 100 private int mActivityFlags; 101 102 // When the recents animation is running, the visibility of the Launcher is managed by the 103 // animation 104 @InvisibilityFlags private int mForceInvisible; 105 106 private final ViewCache mViewCache = new ViewCache(); 107 getViewCache()108 public ViewCache getViewCache() { 109 return mViewCache; 110 } 111 112 @Override getDeviceProfile()113 public DeviceProfile getDeviceProfile() { 114 return mDeviceProfile; 115 } 116 getCurrentState()117 public int getCurrentState() { return StatsLogUtils.LAUNCHER_STATE_BACKGROUND; } 118 modifyUserEvent(LauncherLogProto.LauncherEvent event)119 public void modifyUserEvent(LauncherLogProto.LauncherEvent event) {} 120 getStatsLogManager()121 public final StatsLogManager getStatsLogManager() { 122 if (mStatsLogManager == null) { 123 mStatsLogManager = StatsLogManager.newInstance(this, this); 124 } 125 return mStatsLogManager; 126 } 127 getUserEventDispatcher()128 public final UserEventDispatcher getUserEventDispatcher() { 129 if (mUserEventDispatcher == null) { 130 mUserEventDispatcher = UserEventDispatcher.newInstance(this, this); 131 } 132 return mUserEventDispatcher; 133 } 134 getSystemUiController()135 public SystemUiController getSystemUiController() { 136 if (mSystemUiController == null) { 137 mSystemUiController = new SystemUiController(getWindow()); 138 } 139 return mSystemUiController; 140 } 141 142 @Override onActivityResult(int requestCode, int resultCode, Intent data)143 public void onActivityResult(int requestCode, int resultCode, Intent data) { 144 super.onActivityResult(requestCode, resultCode, data); 145 } 146 147 @Override onStart()148 protected void onStart() { 149 mActivityFlags |= ACTIVITY_STATE_STARTED; 150 super.onStart(); 151 } 152 153 @Override onResume()154 protected void onResume() { 155 mActivityFlags |= ACTIVITY_STATE_RESUMED | ACTIVITY_STATE_USER_ACTIVE; 156 super.onResume(); 157 } 158 159 @Override onUserLeaveHint()160 protected void onUserLeaveHint() { 161 mActivityFlags &= ~ACTIVITY_STATE_USER_ACTIVE; 162 super.onUserLeaveHint(); 163 } 164 165 @Override onMultiWindowModeChanged(boolean isInMultiWindowMode, Configuration newConfig)166 public void onMultiWindowModeChanged(boolean isInMultiWindowMode, Configuration newConfig) { 167 super.onMultiWindowModeChanged(isInMultiWindowMode, newConfig); 168 for (int i = mMultiWindowModeChangedListeners.size() - 1; i >= 0; i--) { 169 mMultiWindowModeChangedListeners.get(i).onMultiWindowModeChanged(isInMultiWindowMode); 170 } 171 } 172 173 @Override onStop()174 protected void onStop() { 175 mActivityFlags &= ~ACTIVITY_STATE_STARTED & ~ACTIVITY_STATE_USER_ACTIVE; 176 mForceInvisible = 0; 177 super.onStop(); 178 179 // Reset the overridden sysui flags used for the task-swipe launch animation, this is a 180 // catch all for if we do not get resumed (and therefore not paused below) 181 getSystemUiController().updateUiState(UI_STATE_OVERVIEW, 0); 182 } 183 184 @Override onPause()185 protected void onPause() { 186 mActivityFlags &= ~ACTIVITY_STATE_RESUMED; 187 super.onPause(); 188 189 // Reset the overridden sysui flags used for the task-swipe launch animation, we do this 190 // here instead of at the end of the animation because the start of the new activity does 191 // not happen immediately, which would cause us to reset to launcher's sysui flags and then 192 // back to the new app (causing a flash) 193 getSystemUiController().updateUiState(UI_STATE_OVERVIEW, 0); 194 } 195 isStarted()196 public boolean isStarted() { 197 return (mActivityFlags & ACTIVITY_STATE_STARTED) != 0; 198 } 199 200 /** 201 * isResumed in already defined as a hidden final method in Activity.java 202 */ hasBeenResumed()203 public boolean hasBeenResumed() { 204 return (mActivityFlags & ACTIVITY_STATE_RESUMED) != 0; 205 } 206 isUserActive()207 public boolean isUserActive() { 208 return (mActivityFlags & ACTIVITY_STATE_USER_ACTIVE) != 0; 209 } 210 addOnDeviceProfileChangeListener(OnDeviceProfileChangeListener listener)211 public void addOnDeviceProfileChangeListener(OnDeviceProfileChangeListener listener) { 212 mDPChangeListeners.add(listener); 213 } 214 removeOnDeviceProfileChangeListener(OnDeviceProfileChangeListener listener)215 public void removeOnDeviceProfileChangeListener(OnDeviceProfileChangeListener listener) { 216 mDPChangeListeners.remove(listener); 217 } 218 dispatchDeviceProfileChanged()219 protected void dispatchDeviceProfileChanged() { 220 for (int i = mDPChangeListeners.size() - 1; i >= 0; i--) { 221 mDPChangeListeners.get(i).onDeviceProfileChanged(mDeviceProfile); 222 } 223 } 224 addMultiWindowModeChangedListener(MultiWindowModeChangedListener listener)225 public void addMultiWindowModeChangedListener(MultiWindowModeChangedListener listener) { 226 mMultiWindowModeChangedListeners.add(listener); 227 } 228 removeMultiWindowModeChangedListener(MultiWindowModeChangedListener listener)229 public void removeMultiWindowModeChangedListener(MultiWindowModeChangedListener listener) { 230 mMultiWindowModeChangedListeners.remove(listener); 231 } 232 233 /** 234 * Used to set the override visibility state, used only to handle the transition home with the 235 * recents animation. 236 * @see QuickstepAppTransitionManagerImpl#getWallpaperOpenRunner() 237 */ addForceInvisibleFlag(@nvisibilityFlags int flag)238 public void addForceInvisibleFlag(@InvisibilityFlags int flag) { 239 mForceInvisible |= flag; 240 } 241 clearForceInvisibleFlag(@nvisibilityFlags int flag)242 public void clearForceInvisibleFlag(@InvisibilityFlags int flag) { 243 mForceInvisible &= ~flag; 244 } 245 246 /** 247 * @return Wether this activity should be considered invisible regardless of actual visibility. 248 */ isForceInvisible()249 public boolean isForceInvisible() { 250 return hasSomeInvisibleFlag(INVISIBLE_FLAGS); 251 } 252 hasSomeInvisibleFlag(int mask)253 public boolean hasSomeInvisibleFlag(int mask) { 254 return (mForceInvisible & mask) != 0; 255 } 256 257 public interface MultiWindowModeChangedListener { onMultiWindowModeChanged(boolean isInMultiWindowMode)258 void onMultiWindowModeChanged(boolean isInMultiWindowMode); 259 } 260 261 @Override dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args)262 public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) { 263 if (!UiFactory.dumpActivity(this, writer)) { 264 super.dump(prefix, fd, writer, args); 265 } 266 } 267 dumpMisc(String prefix, PrintWriter writer)268 protected void dumpMisc(String prefix, PrintWriter writer) { 269 writer.println(prefix + "deviceProfile isTransposed=" 270 + getDeviceProfile().isVerticalBarLayout()); 271 writer.println(prefix + "orientation=" + getResources().getConfiguration().orientation); 272 writer.println(prefix + "mSystemUiController: " + mSystemUiController); 273 writer.println(prefix + "mActivityFlags: " + mActivityFlags); 274 writer.println(prefix + "mForceInvisible: " + mForceInvisible); 275 } 276 fromContext(Context context)277 public static <T extends BaseActivity> T fromContext(Context context) { 278 if (context instanceof BaseActivity) { 279 return (T) context; 280 } else if (context instanceof ContextThemeWrapper) { 281 return fromContext(((ContextWrapper) context).getBaseContext()); 282 } else { 283 throw new IllegalArgumentException("Cannot find BaseActivity in parent tree"); 284 } 285 } 286 } 287