1 /* 2 * Copyright (C) 2019 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 package com.android.launcher3; 17 18 import static com.android.launcher3.AbstractFloatingView.TYPE_ALL; 19 import static com.android.launcher3.AbstractFloatingView.TYPE_HIDE_BACK_BUTTON; 20 import static com.android.launcher3.LauncherState.FLAG_HIDE_BACK_BUTTON; 21 import static com.android.launcher3.LauncherState.NORMAL; 22 import static com.android.quickstep.SysUINavigationMode.removeShelfFromOverview; 23 import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_HOME_KEY; 24 25 import android.animation.AnimatorSet; 26 import android.animation.ValueAnimator; 27 import android.content.Intent; 28 import android.content.IntentSender; 29 import android.content.SharedPreferences; 30 import android.os.Bundle; 31 import android.os.CancellationSignal; 32 33 import com.android.launcher3.config.FeatureFlags; 34 import com.android.launcher3.hybridhotseat.HotseatPredictionController; 35 import com.android.launcher3.model.WellbeingModel; 36 import com.android.launcher3.popup.SystemShortcut; 37 import com.android.launcher3.proxy.ProxyActivityStarter; 38 import com.android.launcher3.proxy.StartActivityParams; 39 import com.android.launcher3.statehandlers.BackButtonAlphaHandler; 40 import com.android.launcher3.statehandlers.DepthController; 41 import com.android.launcher3.statemanager.StateManager.StateHandler; 42 import com.android.launcher3.uioverrides.RecentsViewStateController; 43 import com.android.launcher3.util.OnboardingPrefs; 44 import com.android.launcher3.util.UiThreadHelper; 45 import com.android.quickstep.RecentsModel; 46 import com.android.quickstep.SysUINavigationMode; 47 import com.android.quickstep.SysUINavigationMode.Mode; 48 import com.android.quickstep.SysUINavigationMode.NavigationModeChangeListener; 49 import com.android.quickstep.SystemUiProxy; 50 import com.android.quickstep.util.QuickstepOnboardingPrefs; 51 import com.android.quickstep.util.RemoteAnimationProvider; 52 import com.android.quickstep.util.RemoteFadeOutAnimationListener; 53 import com.android.quickstep.util.ShelfPeekAnim; 54 import com.android.quickstep.views.OverviewActionsView; 55 import com.android.quickstep.views.RecentsView; 56 import com.android.systemui.shared.system.ActivityManagerWrapper; 57 import com.android.systemui.shared.system.RemoteAnimationTargetCompat; 58 59 import java.util.stream.Stream; 60 61 /** 62 * Extension of Launcher activity to provide quickstep specific functionality 63 */ 64 public abstract class BaseQuickstepLauncher extends Launcher 65 implements NavigationModeChangeListener { 66 67 private DepthController mDepthController = new DepthController(this); 68 69 /** 70 * Reusable command for applying the back button alpha on the background thread. 71 */ 72 public static final UiThreadHelper.AsyncCommand SET_BACK_BUTTON_ALPHA = 73 (context, arg1, arg2) -> SystemUiProxy.INSTANCE.get(context).setBackButtonAlpha( 74 Float.intBitsToFloat(arg1), arg2 != 0); 75 76 private final ShelfPeekAnim mShelfPeekAnim = new ShelfPeekAnim(this); 77 78 private OverviewActionsView mActionsView; 79 protected HotseatPredictionController mHotseatPredictionController; 80 81 @Override onCreate(Bundle savedInstanceState)82 protected void onCreate(Bundle savedInstanceState) { 83 super.onCreate(savedInstanceState); 84 85 SysUINavigationMode.INSTANCE.get(this).addModeChangeListener(this); 86 addMultiWindowModeChangedListener(mDepthController); 87 } 88 89 @Override onDestroy()90 public void onDestroy() { 91 SysUINavigationMode.INSTANCE.get(this).removeModeChangeListener(this); 92 super.onDestroy(); 93 } 94 95 @Override onNavigationModeChanged(Mode newMode)96 public void onNavigationModeChanged(Mode newMode) { 97 getDragLayer().recreateControllers(); 98 if (mActionsView != null && isOverviewActionsEnabled()) { 99 mActionsView.updateVerticalMargin(newMode); 100 } 101 } 102 103 @Override onEnterAnimationComplete()104 public void onEnterAnimationComplete() { 105 super.onEnterAnimationComplete(); 106 // After the transition to home, enable the high-res thumbnail loader if it wasn't enabled 107 // as a part of quickstep, so that high-res thumbnails can load the next time we enter 108 // overview 109 RecentsModel.INSTANCE.get(this).getThumbnailCache() 110 .getHighResLoadingState().setVisible(true); 111 } 112 113 @Override onTrimMemory(int level)114 public void onTrimMemory(int level) { 115 super.onTrimMemory(level); 116 RecentsModel.INSTANCE.get(this).onTrimMemory(level); 117 } 118 119 @Override onUiChangedWhileSleeping()120 protected void onUiChangedWhileSleeping() { 121 // Remove the snapshot because the content view may have obvious changes. 122 ActivityManagerWrapper.getInstance().invalidateHomeTaskSnapshot(this); 123 } 124 125 @Override startIntentSenderForResult(IntentSender intent, int requestCode, Intent fillInIntent, int flagsMask, int flagsValues, int extraFlags, Bundle options)126 public void startIntentSenderForResult(IntentSender intent, int requestCode, 127 Intent fillInIntent, int flagsMask, int flagsValues, int extraFlags, Bundle options) { 128 if (requestCode != -1) { 129 mPendingActivityRequestCode = requestCode; 130 StartActivityParams params = new StartActivityParams(this, requestCode); 131 params.intentSender = intent; 132 params.fillInIntent = fillInIntent; 133 params.flagsMask = flagsMask; 134 params.flagsValues = flagsValues; 135 params.extraFlags = extraFlags; 136 params.options = options; 137 startActivity(ProxyActivityStarter.getLaunchIntent(this, params)); 138 } else { 139 super.startIntentSenderForResult(intent, requestCode, fillInIntent, flagsMask, 140 flagsValues, extraFlags, options); 141 } 142 } 143 144 @Override startActivityForResult(Intent intent, int requestCode, Bundle options)145 public void startActivityForResult(Intent intent, int requestCode, Bundle options) { 146 if (requestCode != -1) { 147 mPendingActivityRequestCode = requestCode; 148 StartActivityParams params = new StartActivityParams(this, requestCode); 149 params.intent = intent; 150 params.options = options; 151 startActivity(ProxyActivityStarter.getLaunchIntent(this, params)); 152 } else { 153 super.startActivityForResult(intent, requestCode, options); 154 } 155 } 156 157 @Override onDeferredResumed()158 protected void onDeferredResumed() { 159 super.onDeferredResumed(); 160 if (mPendingActivityRequestCode != -1 && isInState(NORMAL)) { 161 // Remove any active ProxyActivityStarter task and send RESULT_CANCELED to Launcher. 162 onActivityResult(mPendingActivityRequestCode, RESULT_CANCELED, null); 163 // ProxyActivityStarter is started with clear task to reset the task after which it 164 // removes the task itself. 165 startActivity(ProxyActivityStarter.getLaunchIntent(this, null)); 166 } 167 } 168 169 @Override setupViews()170 protected void setupViews() { 171 super.setupViews(); 172 173 SysUINavigationMode.INSTANCE.get(this).updateMode(); 174 mActionsView = findViewById(R.id.overview_actions_view); 175 ((RecentsView) getOverviewPanel()).init(mActionsView); 176 177 if (isOverviewActionsEnabled()) { 178 // Overview is above all other launcher elements, including qsb, so move it to the top. 179 getOverviewPanel().bringToFront(); 180 mActionsView.bringToFront(); 181 mActionsView.updateVerticalMargin(SysUINavigationMode.getMode(this)); 182 } 183 } 184 isOverviewActionsEnabled()185 private boolean isOverviewActionsEnabled() { 186 return FeatureFlags.ENABLE_OVERVIEW_ACTIONS.get() && removeShelfFromOverview(this); 187 } 188 getActionsView()189 public <T extends OverviewActionsView> T getActionsView() { 190 return (T) mActionsView; 191 } 192 193 @Override closeOpenViews(boolean animate)194 protected void closeOpenViews(boolean animate) { 195 super.closeOpenViews(animate); 196 ActivityManagerWrapper.getInstance() 197 .closeSystemWindows(CLOSE_SYSTEM_WINDOWS_REASON_HOME_KEY); 198 } 199 200 @Override createStateHandlers()201 protected StateHandler<LauncherState>[] createStateHandlers() { 202 return new StateHandler[] { 203 getAllAppsController(), 204 getWorkspace(), 205 getDepthController(), 206 new RecentsViewStateController(this), 207 new BackButtonAlphaHandler(this)}; 208 } 209 getDepthController()210 public DepthController getDepthController() { 211 return mDepthController; 212 } 213 214 @Override createOnboardingPrefs(SharedPreferences sharedPrefs)215 protected OnboardingPrefs createOnboardingPrefs(SharedPreferences sharedPrefs) { 216 return new QuickstepOnboardingPrefs(this, sharedPrefs); 217 } 218 219 @Override useFadeOutAnimationForLauncherStart(CancellationSignal signal)220 public void useFadeOutAnimationForLauncherStart(CancellationSignal signal) { 221 QuickstepAppTransitionManagerImpl appTransitionManager = 222 (QuickstepAppTransitionManagerImpl) getAppTransitionManager(); 223 appTransitionManager.setRemoteAnimationProvider(new RemoteAnimationProvider() { 224 @Override 225 public AnimatorSet createWindowAnimation(RemoteAnimationTargetCompat[] appTargets, 226 RemoteAnimationTargetCompat[] wallpaperTargets) { 227 228 // On the first call clear the reference. 229 signal.cancel(); 230 231 ValueAnimator fadeAnimation = ValueAnimator.ofFloat(1, 0); 232 fadeAnimation.addUpdateListener(new RemoteFadeOutAnimationListener(appTargets, 233 wallpaperTargets)); 234 AnimatorSet anim = new AnimatorSet(); 235 anim.play(fadeAnimation); 236 return anim; 237 } 238 }, signal); 239 } 240 241 @Override getNormalOverviewScaleAndOffset()242 public float[] getNormalOverviewScaleAndOffset() { 243 return SysUINavigationMode.getMode(this) == Mode.NO_BUTTON 244 ? new float[] {1, 1} : new float[] {1.1f, 0}; 245 } 246 247 @Override onDragLayerHierarchyChanged()248 public void onDragLayerHierarchyChanged() { 249 onLauncherStateOrFocusChanged(); 250 } 251 252 @Override onActivityFlagsChanged(int changeBits)253 protected void onActivityFlagsChanged(int changeBits) { 254 if ((changeBits 255 & (ACTIVITY_STATE_WINDOW_FOCUSED | ACTIVITY_STATE_TRANSITION_ACTIVE)) != 0) { 256 onLauncherStateOrFocusChanged(); 257 } 258 259 if ((changeBits & ACTIVITY_STATE_STARTED) != 0) { 260 mDepthController.setActivityStarted(isStarted()); 261 } 262 263 super.onActivityFlagsChanged(changeBits); 264 } 265 shouldBackButtonBeHidden(LauncherState toState)266 public boolean shouldBackButtonBeHidden(LauncherState toState) { 267 Mode mode = SysUINavigationMode.getMode(this); 268 boolean shouldBackButtonBeHidden = mode.hasGestures 269 && toState.hasFlag(FLAG_HIDE_BACK_BUTTON) 270 && hasWindowFocus() 271 && (getActivityFlags() & ACTIVITY_STATE_TRANSITION_ACTIVE) == 0; 272 if (shouldBackButtonBeHidden) { 273 // Show the back button if there is a floating view visible. 274 shouldBackButtonBeHidden = AbstractFloatingView.getTopOpenViewWithType(this, 275 TYPE_ALL & ~TYPE_HIDE_BACK_BUTTON) == null; 276 } 277 return shouldBackButtonBeHidden; 278 } 279 280 /** 281 * Sets the back button visibility based on the current state/window focus. 282 */ onLauncherStateOrFocusChanged()283 private void onLauncherStateOrFocusChanged() { 284 boolean shouldBackButtonBeHidden = shouldBackButtonBeHidden(getStateManager().getState()); 285 UiThreadHelper.setBackButtonAlphaAsync(this, SET_BACK_BUTTON_ALPHA, 286 shouldBackButtonBeHidden ? 0f : 1f, true /* animate */); 287 if (getDragLayer() != null) { 288 getRootView().setDisallowBackGesture(shouldBackButtonBeHidden); 289 } 290 } 291 292 @Override finishBindingItems(int pageBoundFirst)293 public void finishBindingItems(int pageBoundFirst) { 294 super.finishBindingItems(pageBoundFirst); 295 // Instantiate and initialize WellbeingModel now that its loading won't interfere with 296 // populating workspace. 297 // TODO: Find a better place for this 298 WellbeingModel.INSTANCE.get(this); 299 } 300 301 @Override getSupportedShortcuts()302 public Stream<SystemShortcut.Factory> getSupportedShortcuts() { 303 return Stream.concat(super.getSupportedShortcuts(), 304 Stream.of(WellbeingModel.SHORTCUT_FACTORY)); 305 } 306 getShelfPeekAnim()307 public ShelfPeekAnim getShelfPeekAnim() { 308 return mShelfPeekAnim; 309 } 310 311 /** 312 * Returns Prediction controller for hybrid hotseat 313 */ getHotseatPredictionController()314 public HotseatPredictionController getHotseatPredictionController() { 315 return mHotseatPredictionController; 316 } 317 setHintUserWillBeActive()318 public void setHintUserWillBeActive() { 319 addActivityFlags(ACTIVITY_STATE_USER_WILL_BE_ACTIVE); 320 } 321 } 322