1 /* 2 * Copyright (C) 2018 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.quickstep; 17 18 import static com.android.launcher3.LauncherAnimUtils.VIEW_BACKGROUND_COLOR; 19 import static com.android.launcher3.anim.Interpolators.ACCEL_2; 20 import static com.android.launcher3.anim.Interpolators.INSTANT; 21 import static com.android.launcher3.anim.Interpolators.LINEAR; 22 import static com.android.quickstep.AbsSwipeUpHandler.RECENTS_ATTACH_DURATION; 23 import static com.android.quickstep.GestureState.GestureEndTarget.RECENTS; 24 import static com.android.quickstep.SysUINavigationMode.getMode; 25 import static com.android.quickstep.util.RecentsAtomicAnimationFactory.INDEX_RECENTS_FADE_ANIM; 26 import static com.android.quickstep.util.RecentsAtomicAnimationFactory.INDEX_RECENTS_TRANSLATE_X_ANIM; 27 import static com.android.quickstep.views.RecentsView.ADJACENT_PAGE_HORIZONTAL_OFFSET; 28 import static com.android.quickstep.views.RecentsView.FULLSCREEN_PROGRESS; 29 import static com.android.quickstep.views.RecentsView.RECENTS_SCALE_PROPERTY; 30 import static com.android.quickstep.views.RecentsView.TASK_SECONDARY_TRANSLATION; 31 32 import android.animation.Animator; 33 import android.animation.AnimatorListenerAdapter; 34 import android.animation.ObjectAnimator; 35 import android.annotation.TargetApi; 36 import android.content.Context; 37 import android.content.res.Resources; 38 import android.graphics.Color; 39 import android.graphics.PointF; 40 import android.graphics.Rect; 41 import android.os.Build; 42 import android.view.Gravity; 43 import android.view.MotionEvent; 44 import android.view.View; 45 46 import androidx.annotation.Nullable; 47 import androidx.annotation.UiThread; 48 49 import com.android.launcher3.DeviceProfile; 50 import com.android.launcher3.R; 51 import com.android.launcher3.anim.AnimatorPlaybackController; 52 import com.android.launcher3.anim.PendingAnimation; 53 import com.android.launcher3.statehandlers.DepthController; 54 import com.android.launcher3.statemanager.BaseState; 55 import com.android.launcher3.statemanager.StatefulActivity; 56 import com.android.launcher3.taskbar.TaskbarUIController; 57 import com.android.launcher3.touch.PagedOrientationHandler; 58 import com.android.launcher3.util.WindowBounds; 59 import com.android.launcher3.views.ScrimView; 60 import com.android.quickstep.SysUINavigationMode.Mode; 61 import com.android.quickstep.util.ActivityInitListener; 62 import com.android.quickstep.util.AnimatorControllerWithResistance; 63 import com.android.quickstep.util.SplitScreenBounds; 64 import com.android.quickstep.views.OverviewActionsView; 65 import com.android.quickstep.views.RecentsView; 66 import com.android.quickstep.views.TaskView; 67 import com.android.systemui.shared.recents.model.ThumbnailData; 68 import com.android.systemui.shared.system.RemoteAnimationTargetCompat; 69 70 import java.util.HashMap; 71 import java.util.function.Consumer; 72 import java.util.function.Predicate; 73 74 /** 75 * Utility class which abstracts out the logical differences between Launcher and RecentsActivity. 76 */ 77 @TargetApi(Build.VERSION_CODES.P) 78 public abstract class BaseActivityInterface<STATE_TYPE extends BaseState<STATE_TYPE>, 79 ACTIVITY_TYPE extends StatefulActivity<STATE_TYPE>> { 80 81 public final boolean rotationSupportedByActivity; 82 83 private final STATE_TYPE mBackgroundState; 84 85 private STATE_TYPE mTargetState; 86 BaseActivityInterface(boolean rotationSupportedByActivity, STATE_TYPE overviewState, STATE_TYPE backgroundState)87 protected BaseActivityInterface(boolean rotationSupportedByActivity, 88 STATE_TYPE overviewState, STATE_TYPE backgroundState) { 89 this.rotationSupportedByActivity = rotationSupportedByActivity; 90 mTargetState = overviewState; 91 mBackgroundState = backgroundState; 92 } 93 94 /** 95 * Called when the current gesture transition is cancelled. 96 * @param activityVisible Whether the user can see the changes we make here, so try to animate. 97 * @param endTarget If the gesture ended before we got cancelled, where we were headed. 98 */ onTransitionCancelled(boolean activityVisible, @Nullable GestureState.GestureEndTarget endTarget)99 public void onTransitionCancelled(boolean activityVisible, 100 @Nullable GestureState.GestureEndTarget endTarget) { 101 ACTIVITY_TYPE activity = getCreatedActivity(); 102 if (activity == null) { 103 return; 104 } 105 STATE_TYPE startState = activity.getStateManager().getRestState(); 106 if (endTarget != null) { 107 // We were on our way to this state when we got canceled, end there instead. 108 startState = stateFromGestureEndTarget(endTarget); 109 } 110 activity.getStateManager().goToState(startState, activityVisible); 111 } 112 getSwipeUpDestinationAndLength( DeviceProfile dp, Context context, Rect outRect, PagedOrientationHandler orientationHandler)113 public abstract int getSwipeUpDestinationAndLength( 114 DeviceProfile dp, Context context, Rect outRect, 115 PagedOrientationHandler orientationHandler); 116 117 /** Called when the animation to home has fully settled. */ onSwipeUpToHomeComplete(RecentsAnimationDeviceState deviceState)118 public void onSwipeUpToHomeComplete(RecentsAnimationDeviceState deviceState) {} 119 onAssistantVisibilityChanged(float visibility)120 public abstract void onAssistantVisibilityChanged(float visibility); 121 122 /** Called when one handed mode activated or deactivated. */ onOneHandedModeStateChanged(boolean activated)123 public abstract void onOneHandedModeStateChanged(boolean activated); 124 prepareRecentsUI(RecentsAnimationDeviceState deviceState, boolean activityVisible, Consumer<AnimatorControllerWithResistance> callback)125 public abstract AnimationFactory prepareRecentsUI(RecentsAnimationDeviceState deviceState, 126 boolean activityVisible, Consumer<AnimatorControllerWithResistance> callback); 127 createActivityInitListener( Predicate<Boolean> onInitListener)128 public abstract ActivityInitListener createActivityInitListener( 129 Predicate<Boolean> onInitListener); 130 131 /** 132 * Sets a callback to be run when an activity launch happens while launcher is not yet resumed. 133 */ setOnDeferredActivityLaunchCallback(Runnable r)134 public void setOnDeferredActivityLaunchCallback(Runnable r) {} 135 136 @Nullable getCreatedActivity()137 public abstract ACTIVITY_TYPE getCreatedActivity(); 138 139 @Nullable getDepthController()140 public DepthController getDepthController() { 141 return null; 142 } 143 144 @Nullable getTaskbarController()145 public abstract TaskbarUIController getTaskbarController(); 146 isResumed()147 public final boolean isResumed() { 148 ACTIVITY_TYPE activity = getCreatedActivity(); 149 return activity != null && activity.hasBeenResumed(); 150 } 151 isStarted()152 public final boolean isStarted() { 153 ACTIVITY_TYPE activity = getCreatedActivity(); 154 return activity != null && activity.isStarted(); 155 } 156 157 @UiThread 158 @Nullable getVisibleRecentsView()159 public abstract <T extends RecentsView> T getVisibleRecentsView(); 160 161 @UiThread switchToRecentsIfVisible(Runnable onCompleteCallback)162 public abstract boolean switchToRecentsIfVisible(Runnable onCompleteCallback); 163 getOverviewWindowBounds( Rect homeBounds, RemoteAnimationTargetCompat target)164 public abstract Rect getOverviewWindowBounds( 165 Rect homeBounds, RemoteAnimationTargetCompat target); 166 allowMinimizeSplitScreen()167 public abstract boolean allowMinimizeSplitScreen(); 168 deferStartingActivity(RecentsAnimationDeviceState deviceState, MotionEvent ev)169 public boolean deferStartingActivity(RecentsAnimationDeviceState deviceState, MotionEvent ev) { 170 return deviceState.isInDeferredGestureRegion(ev); 171 } 172 173 /** 174 * @return Whether the gesture in progress should be cancelled. 175 */ shouldCancelCurrentGesture()176 public boolean shouldCancelCurrentGesture() { 177 return false; 178 } 179 onExitOverview(RotationTouchHelper deviceState, Runnable exitRunnable)180 public abstract void onExitOverview(RotationTouchHelper deviceState, 181 Runnable exitRunnable); 182 isInLiveTileMode()183 public abstract boolean isInLiveTileMode(); 184 onLaunchTaskFailed()185 public abstract void onLaunchTaskFailed(); 186 onLaunchTaskSuccess()187 public void onLaunchTaskSuccess() { 188 ACTIVITY_TYPE activity = getCreatedActivity(); 189 if (activity == null) { 190 return; 191 } 192 activity.getStateManager().moveToRestState(); 193 } 194 closeOverlay()195 public void closeOverlay() { } 196 switchRunningTaskViewToScreenshot(HashMap<Integer, ThumbnailData> thumbnailDatas, Runnable runnable)197 public void switchRunningTaskViewToScreenshot(HashMap<Integer, ThumbnailData> thumbnailDatas, 198 Runnable runnable) { 199 ACTIVITY_TYPE activity = getCreatedActivity(); 200 if (activity == null) { 201 return; 202 } 203 RecentsView recentsView = activity.getOverviewPanel(); 204 if (recentsView == null) { 205 if (runnable != null) { 206 runnable.run(); 207 } 208 return; 209 } 210 recentsView.switchToScreenshot(thumbnailDatas, runnable); 211 } 212 213 /** 214 * Calculates the taskView size for the provided device configuration. 215 */ calculateTaskSize(Context context, DeviceProfile dp, Rect outRect)216 public final void calculateTaskSize(Context context, DeviceProfile dp, Rect outRect) { 217 Resources res = context.getResources(); 218 if (dp.overviewShowAsGrid) { 219 Rect gridRect = new Rect(); 220 calculateGridSize(context, dp, gridRect); 221 222 PointF taskDimension = getTaskDimension(context, dp); 223 float scale = gridRect.height() / taskDimension.y; 224 scale = Math.min(scale, res.getFloat(R.dimen.overview_max_scale)); 225 int outWidth = Math.round(scale * taskDimension.x); 226 int outHeight = Math.round(scale * taskDimension.y); 227 228 int gravity = Gravity.CENTER; 229 Gravity.apply(gravity, outWidth, outHeight, gridRect, outRect); 230 } else { 231 int taskMargin = dp.overviewTaskMarginPx; 232 calculateTaskSizeInternal(context, dp, 233 dp.overviewTaskThumbnailTopMarginPx, 234 getOverviewActionsHeight(context, dp), 235 res.getDimensionPixelSize(R.dimen.overview_minimum_next_prev_size) + taskMargin, 236 outRect); 237 } 238 } 239 calculateTaskSizeInternal(Context context, DeviceProfile dp, int claimedSpaceAbove, int claimedSpaceBelow, int minimumHorizontalPadding, Rect outRect)240 private void calculateTaskSizeInternal(Context context, DeviceProfile dp, 241 int claimedSpaceAbove, int claimedSpaceBelow, int minimumHorizontalPadding, 242 Rect outRect) { 243 PointF taskDimension = getTaskDimension(context, dp); 244 Rect insets = dp.getInsets(); 245 246 Rect potentialTaskRect = new Rect(0, 0, dp.widthPx, dp.heightPx); 247 potentialTaskRect.inset(insets.left, insets.top, insets.right, insets.bottom); 248 potentialTaskRect.inset( 249 minimumHorizontalPadding, 250 claimedSpaceAbove, 251 minimumHorizontalPadding, 252 claimedSpaceBelow); 253 254 float scale = Math.min( 255 potentialTaskRect.width() / taskDimension.x, 256 potentialTaskRect.height() / taskDimension.y); 257 int outWidth = Math.round(scale * taskDimension.x); 258 int outHeight = Math.round(scale * taskDimension.y); 259 260 Gravity.apply(Gravity.CENTER, outWidth, outHeight, potentialTaskRect, outRect); 261 } 262 getTaskDimension(Context context, DeviceProfile dp)263 private static PointF getTaskDimension(Context context, DeviceProfile dp) { 264 PointF dimension = new PointF(); 265 getTaskDimension(context, dp, dimension); 266 return dimension; 267 } 268 269 /** 270 * Gets the dimension of the task in the current system state. 271 */ getTaskDimension(Context context, DeviceProfile dp, PointF out)272 public static void getTaskDimension(Context context, DeviceProfile dp, PointF out) { 273 if (dp.isMultiWindowMode) { 274 WindowBounds bounds = SplitScreenBounds.INSTANCE.getSecondaryWindowBounds(context); 275 out.x = bounds.availableSize.x; 276 out.y = bounds.availableSize.y; 277 if (!TaskView.clipLeft(dp)) { 278 out.x += bounds.insets.left; 279 } 280 if (!TaskView.clipRight(dp)) { 281 out.x += bounds.insets.right; 282 } 283 if (!TaskView.clipTop(dp)) { 284 out.y += bounds.insets.top; 285 } 286 if (!TaskView.clipBottom(dp)) { 287 out.y += bounds.insets.bottom; 288 } 289 } else { 290 out.x = dp.widthPx; 291 out.y = dp.heightPx; 292 if (TaskView.clipLeft(dp)) { 293 out.x -= dp.getInsets().left; 294 } 295 if (TaskView.clipRight(dp)) { 296 out.x -= dp.getInsets().right; 297 } 298 if (TaskView.clipTop(dp)) { 299 out.y -= dp.getInsets().top; 300 } 301 if (TaskView.clipBottom(dp)) { 302 out.y -= Math.max(dp.getInsets().bottom, dp.taskbarSize); 303 } 304 } 305 } 306 307 /** 308 * Calculates the overview grid size for the provided device configuration. 309 */ calculateGridSize(Context context, DeviceProfile dp, Rect outRect)310 public final void calculateGridSize(Context context, DeviceProfile dp, Rect outRect) { 311 Rect insets = dp.getInsets(); 312 int topMargin = dp.overviewTaskThumbnailTopMarginPx; 313 int bottomMargin = getOverviewActionsHeight(context, dp); 314 int sideMargin = dp.overviewGridSideMargin; 315 316 outRect.set(0, 0, dp.widthPx, dp.heightPx); 317 outRect.inset(Math.max(insets.left, sideMargin), insets.top + topMargin, 318 Math.max(insets.right, sideMargin), Math.max(insets.bottom, bottomMargin)); 319 } 320 321 /** 322 * Calculates the overview grid non-focused task size for the provided device configuration. 323 */ calculateGridTaskSize(Context context, DeviceProfile dp, Rect outRect, PagedOrientationHandler orientedState)324 public final void calculateGridTaskSize(Context context, DeviceProfile dp, Rect outRect, 325 PagedOrientationHandler orientedState) { 326 Resources res = context.getResources(); 327 Rect taskRect = new Rect(); 328 calculateTaskSize(context, dp, taskRect); 329 330 float rowHeight = 331 (taskRect.height() + dp.overviewTaskThumbnailTopMarginPx - dp.overviewRowSpacing) 332 / 2f; 333 334 PointF taskDimension = getTaskDimension(context, dp); 335 float scale = (rowHeight - dp.overviewTaskThumbnailTopMarginPx) / taskDimension.y; 336 int outWidth = Math.round(scale * taskDimension.x); 337 int outHeight = Math.round(scale * taskDimension.y); 338 339 int gravity = Gravity.TOP; 340 gravity |= orientedState.getRecentsRtlSetting(res) ? Gravity.RIGHT : Gravity.LEFT; 341 Gravity.apply(gravity, outWidth, outHeight, taskRect, outRect); 342 } 343 344 /** 345 * Calculates the modal taskView size for the provided device configuration 346 */ calculateModalTaskSize(Context context, DeviceProfile dp, Rect outRect)347 public final void calculateModalTaskSize(Context context, DeviceProfile dp, Rect outRect) { 348 calculateTaskSizeInternal( 349 context, dp, 350 dp.overviewTaskMarginPx, 351 getOverviewActionsHeight(context, dp), 352 dp.overviewTaskMarginPx, 353 outRect); 354 } 355 356 /** Gets the space that the overview actions will take, including bottom margin. */ getOverviewActionsHeight(Context context, DeviceProfile dp)357 private int getOverviewActionsHeight(Context context, DeviceProfile dp) { 358 Resources res = context.getResources(); 359 return OverviewActionsView.getOverviewActionsBottomMarginPx(getMode(context), dp) 360 + OverviewActionsView.getOverviewActionsTopMarginPx(getMode(context), dp) 361 + res.getDimensionPixelSize(R.dimen.overview_actions_height); 362 } 363 364 /** 365 * Called when the gesture ends and the animation starts towards the given target. Used to add 366 * an optional additional animation with the same duration. 367 */ getParallelAnimationToLauncher( GestureState.GestureEndTarget endTarget, long duration, RecentsAnimationCallbacks callbacks)368 public @Nullable Animator getParallelAnimationToLauncher( 369 GestureState.GestureEndTarget endTarget, long duration, 370 RecentsAnimationCallbacks callbacks) { 371 if (endTarget == RECENTS) { 372 ACTIVITY_TYPE activity = getCreatedActivity(); 373 if (activity == null) { 374 return null; 375 } 376 STATE_TYPE state = stateFromGestureEndTarget(endTarget); 377 ScrimView scrimView = activity.getScrimView(); 378 ObjectAnimator anim = ObjectAnimator.ofArgb(scrimView, VIEW_BACKGROUND_COLOR, 379 getOverviewScrimColorForState(activity, state)); 380 anim.setDuration(duration); 381 return anim; 382 } 383 return null; 384 } 385 386 /** 387 * Returns the color of the scrim behind overview when at rest in this state. 388 * Return {@link Color#TRANSPARENT} for no scrim. 389 */ getOverviewScrimColorForState(ACTIVITY_TYPE activity, STATE_TYPE state)390 protected abstract int getOverviewScrimColorForState(ACTIVITY_TYPE activity, STATE_TYPE state); 391 392 /** 393 * Returns the expected STATE_TYPE from the provided GestureEndTarget. 394 */ stateFromGestureEndTarget(GestureState.GestureEndTarget endTarget)395 public abstract STATE_TYPE stateFromGestureEndTarget(GestureState.GestureEndTarget endTarget); 396 397 /** 398 * Called when the animation to the target has finished, but right before updating the state. 399 * @return A View that needs to draw before ending the recents animation to LAST_TASK. 400 * (This is a hack to ensure Taskbar draws its background first to avoid flickering.) 401 */ onSettledOnEndTarget(GestureState.GestureEndTarget endTarget)402 public @Nullable View onSettledOnEndTarget(GestureState.GestureEndTarget endTarget) { 403 TaskbarUIController taskbarUIController = getTaskbarController(); 404 if (taskbarUIController != null) { 405 taskbarUIController.setSystemGestureInProgress(false); 406 return taskbarUIController.getRootView(); 407 } 408 return null; 409 } 410 411 public interface AnimationFactory { 412 createActivityInterface(long transitionLength)413 void createActivityInterface(long transitionLength); 414 415 /** 416 * @param attached Whether to show RecentsView alongside the app window. If false, recents 417 * will be hidden by some property we can animate, e.g. alpha. 418 * @param animate Whether to animate recents to/from its new attached state. 419 */ setRecentsAttachedToAppWindow(boolean attached, boolean animate)420 default void setRecentsAttachedToAppWindow(boolean attached, boolean animate) { } 421 isRecentsAttachedToAppWindow()422 default boolean isRecentsAttachedToAppWindow() { 423 return false; 424 } 425 hasRecentsEverAttachedToAppWindow()426 default boolean hasRecentsEverAttachedToAppWindow() { 427 return false; 428 } 429 430 /** Called when the gesture ends and we know what state it is going towards */ setEndTarget(GestureState.GestureEndTarget endTarget)431 default void setEndTarget(GestureState.GestureEndTarget endTarget) { } 432 } 433 434 class DefaultAnimationFactory implements AnimationFactory { 435 436 protected final ACTIVITY_TYPE mActivity; 437 private final STATE_TYPE mStartState; 438 private final Consumer<AnimatorControllerWithResistance> mCallback; 439 440 private boolean mIsAttachedToWindow; 441 private boolean mHasEverAttachedToWindow; 442 DefaultAnimationFactory(Consumer<AnimatorControllerWithResistance> callback)443 DefaultAnimationFactory(Consumer<AnimatorControllerWithResistance> callback) { 444 mCallback = callback; 445 446 mActivity = getCreatedActivity(); 447 mStartState = mActivity.getStateManager().getState(); 448 } 449 initUI()450 protected ACTIVITY_TYPE initUI() { 451 STATE_TYPE resetState = mStartState; 452 if (mStartState.shouldDisableRestore()) { 453 resetState = mActivity.getStateManager().getRestState(); 454 } 455 mActivity.getStateManager().setRestState(resetState); 456 mActivity.getStateManager().goToState(mBackgroundState, false); 457 return mActivity; 458 } 459 460 @Override createActivityInterface(long transitionLength)461 public void createActivityInterface(long transitionLength) { 462 PendingAnimation pa = new PendingAnimation(transitionLength * 2); 463 createBackgroundToOverviewAnim(mActivity, pa); 464 AnimatorPlaybackController controller = pa.createPlaybackController(); 465 mActivity.getStateManager().setCurrentUserControlledAnimation(controller); 466 467 // Since we are changing the start position of the UI, reapply the state, at the end 468 controller.setEndAction(() -> mActivity.getStateManager().goToState( 469 controller.getInterpolatedProgress() > 0.5 ? mTargetState : mBackgroundState, 470 false)); 471 472 RecentsView recentsView = mActivity.getOverviewPanel(); 473 AnimatorControllerWithResistance controllerWithResistance = 474 AnimatorControllerWithResistance.createForRecents(controller, mActivity, 475 recentsView.getPagedViewOrientedState(), mActivity.getDeviceProfile(), 476 recentsView, RECENTS_SCALE_PROPERTY, recentsView, 477 TASK_SECONDARY_TRANSLATION); 478 mCallback.accept(controllerWithResistance); 479 480 // Creating the activity controller animation sometimes reapplies the launcher state 481 // (because we set the animation as the current state animation), so we reapply the 482 // attached state here as well to ensure recents is shown/hidden appropriately. 483 if (SysUINavigationMode.getMode(mActivity) == Mode.NO_BUTTON) { 484 setRecentsAttachedToAppWindow(mIsAttachedToWindow, false); 485 } 486 } 487 488 @Override setRecentsAttachedToAppWindow(boolean attached, boolean animate)489 public void setRecentsAttachedToAppWindow(boolean attached, boolean animate) { 490 if (mIsAttachedToWindow == attached && animate) { 491 return; 492 } 493 mIsAttachedToWindow = attached; 494 RecentsView recentsView = mActivity.getOverviewPanel(); 495 if (attached) { 496 mHasEverAttachedToWindow = true; 497 } 498 Animator fadeAnim = mActivity.getStateManager() 499 .createStateElementAnimation(INDEX_RECENTS_FADE_ANIM, attached ? 1 : 0); 500 501 float fromTranslation = attached ? 1 : 0; 502 float toTranslation = attached ? 0 : 1; 503 mActivity.getStateManager() 504 .cancelStateElementAnimation(INDEX_RECENTS_TRANSLATE_X_ANIM); 505 if (!recentsView.isShown() && animate) { 506 ADJACENT_PAGE_HORIZONTAL_OFFSET.set(recentsView, fromTranslation); 507 } else { 508 fromTranslation = ADJACENT_PAGE_HORIZONTAL_OFFSET.get(recentsView); 509 } 510 if (!animate) { 511 ADJACENT_PAGE_HORIZONTAL_OFFSET.set(recentsView, toTranslation); 512 } else { 513 mActivity.getStateManager().createStateElementAnimation( 514 INDEX_RECENTS_TRANSLATE_X_ANIM, 515 fromTranslation, toTranslation).start(); 516 } 517 518 fadeAnim.setInterpolator(attached ? INSTANT : ACCEL_2); 519 fadeAnim.setDuration(animate ? RECENTS_ATTACH_DURATION : 0).start(); 520 } 521 522 @Override isRecentsAttachedToAppWindow()523 public boolean isRecentsAttachedToAppWindow() { 524 return mIsAttachedToWindow; 525 } 526 527 @Override hasRecentsEverAttachedToAppWindow()528 public boolean hasRecentsEverAttachedToAppWindow() { 529 return mHasEverAttachedToWindow; 530 } 531 532 @Override setEndTarget(GestureState.GestureEndTarget endTarget)533 public void setEndTarget(GestureState.GestureEndTarget endTarget) { 534 mTargetState = stateFromGestureEndTarget(endTarget); 535 } 536 createBackgroundToOverviewAnim(ACTIVITY_TYPE activity, PendingAnimation pa)537 protected void createBackgroundToOverviewAnim(ACTIVITY_TYPE activity, PendingAnimation pa) { 538 // Scale down recents from being full screen to being in overview. 539 RecentsView recentsView = activity.getOverviewPanel(); 540 pa.addFloat(recentsView, RECENTS_SCALE_PROPERTY, 541 recentsView.getMaxScaleForFullScreen(), 1, LINEAR); 542 pa.addFloat(recentsView, FULLSCREEN_PROGRESS, 1, 0, LINEAR); 543 544 pa.addListener(new AnimatorListenerAdapter() { 545 @Override 546 public void onAnimationStart(Animator animation) { 547 TaskbarUIController taskbarUIController = getTaskbarController(); 548 if (taskbarUIController != null) { 549 taskbarUIController.setSystemGestureInProgress(true); 550 } 551 } 552 }); 553 } 554 } 555 } 556