TaskView.java revision 231bc9c54a48921f9c6a1ae187969c9bfe9d121f
1/* 2 * Copyright (C) 2014 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 17package com.android.systemui.recents.views; 18 19import android.animation.Animator; 20import android.animation.AnimatorSet; 21import android.animation.ObjectAnimator; 22import android.animation.ValueAnimator; 23import android.content.Context; 24import android.content.res.Resources; 25import android.graphics.Color; 26import android.graphics.Outline; 27import android.graphics.Paint; 28import android.graphics.Point; 29import android.graphics.PorterDuff; 30import android.graphics.PorterDuffColorFilter; 31import android.graphics.Rect; 32import android.util.AttributeSet; 33import android.util.FloatProperty; 34import android.util.IntProperty; 35import android.util.Property; 36import android.view.MotionEvent; 37import android.view.View; 38import android.view.ViewDebug; 39import android.view.ViewOutlineProvider; 40import android.view.animation.AccelerateInterpolator; 41import android.widget.Toast; 42 43import com.android.internal.logging.MetricsLogger; 44import com.android.internal.logging.MetricsProto.MetricsEvent; 45import com.android.systemui.Interpolators; 46import com.android.systemui.R; 47import com.android.systemui.recents.Recents; 48import com.android.systemui.recents.RecentsActivity; 49import com.android.systemui.recents.RecentsConfiguration; 50import com.android.systemui.recents.events.EventBus; 51import com.android.systemui.recents.events.activity.LaunchTaskEvent; 52import com.android.systemui.recents.events.ui.DismissTaskViewEvent; 53import com.android.systemui.recents.events.ui.TaskViewDismissedEvent; 54import com.android.systemui.recents.events.ui.dragndrop.DragEndEvent; 55import com.android.systemui.recents.events.ui.dragndrop.DragStartEvent; 56import com.android.systemui.recents.misc.ReferenceCountedTrigger; 57import com.android.systemui.recents.misc.SystemServicesProxy; 58import com.android.systemui.recents.misc.Utilities; 59import com.android.systemui.recents.model.Task; 60import com.android.systemui.recents.model.TaskStack; 61 62import java.util.ArrayList; 63 64import static android.app.ActivityManager.StackId.INVALID_STACK_ID; 65 66/** 67 * A {@link TaskView} represents a fixed view of a task. Because the TaskView's layout is directed 68 * solely by the {@link TaskStackView}, we make it a fixed size layout which allows relayouts down 69 * the view hierarchy, but not upwards from any of its children (the TaskView will relayout itself 70 * with the previous bounds if any child requests layout). 71 */ 72public class TaskView extends FixedSizeFrameLayout implements Task.TaskCallbacks, 73 TaskStackAnimationHelper.Callbacks, View.OnClickListener, View.OnLongClickListener { 74 75 /** The TaskView callbacks */ 76 interface TaskViewCallbacks { 77 void onTaskViewClipStateChanged(TaskView tv); 78 } 79 80 /** 81 * The dim overlay is generally calculated from the task progress, but occasionally (like when 82 * launching) needs to be animated independently of the task progress. 83 */ 84 public static final Property<TaskView, Integer> DIM = 85 new IntProperty<TaskView>("dim") { 86 @Override 87 public void setValue(TaskView tv, int dim) { 88 tv.setDim(dim); 89 } 90 91 @Override 92 public Integer get(TaskView tv) { 93 return tv.getDim(); 94 } 95 }; 96 97 public static final Property<TaskView, Float> TASK_PROGRESS = 98 new FloatProperty<TaskView>("taskProgress") { 99 @Override 100 public void setValue(TaskView tv, float p) { 101 tv.setTaskProgress(p); 102 } 103 104 @Override 105 public Float get(TaskView tv) { 106 return tv.getTaskProgress(); 107 } 108 }; 109 110 @ViewDebug.ExportedProperty(category="recents") 111 float mTaskProgress; 112 @ViewDebug.ExportedProperty(category="recents") 113 float mMaxDimScale; 114 @ViewDebug.ExportedProperty(category="recents") 115 int mDimAlpha; 116 AccelerateInterpolator mDimInterpolator = new AccelerateInterpolator(3f); 117 PorterDuffColorFilter mDimColorFilter = new PorterDuffColorFilter(0, PorterDuff.Mode.SRC_ATOP); 118 Paint mDimLayerPaint = new Paint(); 119 float mActionButtonTranslationZ; 120 121 @ViewDebug.ExportedProperty(deepExport=true, prefix="task_") 122 Task mTask; 123 @ViewDebug.ExportedProperty(category="recents") 124 boolean mTaskDataLoaded; 125 @ViewDebug.ExportedProperty(category="recents") 126 boolean mClipViewInStack = true; 127 @ViewDebug.ExportedProperty(category="recents") 128 boolean mTouchExplorationEnabled; 129 @ViewDebug.ExportedProperty(category="recents") 130 boolean mIsDisabledInSafeMode; 131 @ViewDebug.ExportedProperty(deepExport=true, prefix="view_bounds_") 132 AnimateableViewBounds mViewBounds; 133 134 private AnimatorSet mTransformAnimation; 135 private ArrayList<Animator> mTmpAnimators = new ArrayList<>(); 136 137 View mContent; 138 @ViewDebug.ExportedProperty(deepExport=true, prefix="thumbnail_") 139 TaskViewThumbnail mThumbnailView; 140 @ViewDebug.ExportedProperty(deepExport=true, prefix="header_") 141 TaskViewHeader mHeaderView; 142 View mActionButtonView; 143 TaskViewCallbacks mCb; 144 145 @ViewDebug.ExportedProperty(category="recents") 146 Point mDownTouchPos = new Point(); 147 148 private Toast mDisabledAppToast; 149 150 public TaskView(Context context) { 151 this(context, null); 152 } 153 154 public TaskView(Context context, AttributeSet attrs) { 155 this(context, attrs, 0); 156 } 157 158 public TaskView(Context context, AttributeSet attrs, int defStyleAttr) { 159 this(context, attrs, defStyleAttr, 0); 160 } 161 162 public TaskView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { 163 super(context, attrs, defStyleAttr, defStyleRes); 164 RecentsConfiguration config = Recents.getConfiguration(); 165 Resources res = context.getResources(); 166 mMaxDimScale = res.getInteger(R.integer.recents_max_task_stack_view_dim) / 255f; 167 mViewBounds = new AnimateableViewBounds(this, res.getDimensionPixelSize( 168 R.dimen.recents_task_view_rounded_corners_radius)); 169 if (config.fakeShadows) { 170 setBackground(new FakeShadowDrawable(res, config)); 171 } 172 setOutlineProvider(mViewBounds); 173 setOnLongClickListener(this); 174 } 175 176 /** Set callback */ 177 void setCallbacks(TaskViewCallbacks cb) { 178 mCb = cb; 179 } 180 181 /** Resets this TaskView for reuse. */ 182 void reset() { 183 resetViewProperties(); 184 resetNoUserInteractionState(); 185 readSystemFlags(); 186 setClipViewInStack(false); 187 setCallbacks(null); 188 } 189 190 /** Gets the task */ 191 public Task getTask() { 192 return mTask; 193 } 194 195 /** Returns the view bounds. */ 196 AnimateableViewBounds getViewBounds() { 197 return mViewBounds; 198 } 199 200 @Override 201 protected void onAttachedToWindow() { 202 super.onAttachedToWindow(); 203 readSystemFlags(); 204 } 205 206 @Override 207 protected void onFinishInflate() { 208 // Bind the views 209 mContent = findViewById(R.id.task_view_content); 210 mHeaderView = (TaskViewHeader) findViewById(R.id.task_view_bar); 211 mThumbnailView = (TaskViewThumbnail) findViewById(R.id.task_view_thumbnail); 212 mActionButtonView = findViewById(R.id.lock_to_app_fab); 213 mActionButtonView.setOutlineProvider(new ViewOutlineProvider() { 214 @Override 215 public void getOutline(View view, Outline outline) { 216 // Set the outline to match the FAB background 217 outline.setOval(0, 0, mActionButtonView.getWidth(), mActionButtonView.getHeight()); 218 outline.setAlpha(0.35f); 219 } 220 }); 221 mActionButtonView.setOnClickListener(this); 222 mActionButtonTranslationZ = mActionButtonView.getTranslationZ(); 223 } 224 225 @Override 226 protected void onSizeChanged(int w, int h, int oldw, int oldh) { 227 super.onSizeChanged(w, h, oldw, oldh); 228 if (w > 0 && h > 0) { 229 mHeaderView.onTaskViewSizeChanged(w, h); 230 mThumbnailView.onTaskViewSizeChanged(w, h); 231 } 232 } 233 234 @Override 235 public boolean hasOverlappingRendering() { 236 return false; 237 } 238 239 @Override 240 public boolean onInterceptTouchEvent(MotionEvent ev) { 241 if (ev.getAction() == MotionEvent.ACTION_DOWN) { 242 mDownTouchPos.set((int) (ev.getX() * getScaleX()), (int) (ev.getY() * getScaleY())); 243 } 244 return super.onInterceptTouchEvent(ev); 245 } 246 247 248 @Override 249 protected void measureContents(int width, int height) { 250 int widthWithoutPadding = width - mPaddingLeft - mPaddingRight; 251 int heightWithoutPadding = height - mPaddingTop - mPaddingBottom; 252 253 // Measure the content 254 mContent.measure(MeasureSpec.makeMeasureSpec(widthWithoutPadding, MeasureSpec.EXACTLY), 255 MeasureSpec.makeMeasureSpec(heightWithoutPadding, MeasureSpec.EXACTLY)); 256 257 // Optimization: Prevent overdraw of the thumbnail under the header view 258 mThumbnailView.updateClipToTaskBar(mHeaderView); 259 260 setMeasuredDimension(width, height); 261 } 262 263 void updateViewPropertiesToTaskTransform(TaskViewTransform toTransform, 264 AnimationProps toAnimation, ValueAnimator.AnimatorUpdateListener updateCallback) { 265 RecentsConfiguration config = Recents.getConfiguration(); 266 cancelTransformAnimation(); 267 268 // Compose the animations for the transform 269 mTmpAnimators.clear(); 270 toTransform.applyToTaskView(this, mTmpAnimators, toAnimation, !config.fakeShadows); 271 if (toAnimation.isImmediate()) { 272 if (Float.compare(getTaskProgress(), toTransform.p) != 0) { 273 setTaskProgress(toTransform.p); 274 } 275 // Manually call back to the animator listener and update callback 276 if (toAnimation.getListener() != null) { 277 toAnimation.getListener().onAnimationEnd(null); 278 } 279 if (updateCallback != null) { 280 updateCallback.onAnimationUpdate(null); 281 } 282 } else { 283 // Both the progress and the update are a function of the bounds movement of the task 284 if (Float.compare(getTaskProgress(), toTransform.p) != 0) { 285 ObjectAnimator anim = ObjectAnimator.ofFloat(this, TASK_PROGRESS, getTaskProgress(), 286 toTransform.p); 287 mTmpAnimators.add(toAnimation.apply(AnimationProps.BOUNDS, anim)); 288 } 289 if (updateCallback != null) { 290 ValueAnimator updateCallbackAnim = ValueAnimator.ofInt(0, 1); 291 updateCallbackAnim.addUpdateListener(updateCallback); 292 mTmpAnimators.add(toAnimation.apply(AnimationProps.BOUNDS, updateCallbackAnim)); 293 } 294 295 // Create the animator 296 mTransformAnimation = toAnimation.createAnimator(mTmpAnimators); 297 mTransformAnimation.start(); 298 } 299 } 300 301 /** Resets this view's properties */ 302 void resetViewProperties() { 303 cancelTransformAnimation(); 304 setDim(0); 305 setVisibility(View.VISIBLE); 306 getViewBounds().reset(); 307 getHeaderView().reset(); 308 TaskViewTransform.reset(this); 309 310 mActionButtonView.setScaleX(1f); 311 mActionButtonView.setScaleY(1f); 312 mActionButtonView.setAlpha(0f); 313 mActionButtonView.setTranslationZ(mActionButtonTranslationZ); 314 } 315 316 /** 317 * Cancels any current transform animations. 318 */ 319 public void cancelTransformAnimation() { 320 Utilities.cancelAnimationWithoutCallbacks(mTransformAnimation); 321 } 322 323 /** Enables/disables handling touch on this task view. */ 324 void setTouchEnabled(boolean enabled) { 325 setOnClickListener(enabled ? this : null); 326 } 327 328 /** Animates this task view if the user does not interact with the stack after a certain time. */ 329 void startNoUserInteractionAnimation() { 330 mHeaderView.startNoUserInteractionAnimation(); 331 } 332 333 /** Mark this task view that the user does has not interacted with the stack after a certain time. */ 334 void setNoUserInteractionState() { 335 mHeaderView.setNoUserInteractionState(); 336 } 337 338 /** Resets the state tracking that the user has not interacted with the stack after a certain time. */ 339 void resetNoUserInteractionState() { 340 mHeaderView.resetNoUserInteractionState(); 341 } 342 343 /** Dismisses this task. */ 344 void dismissTask() { 345 // Animate out the view and call the callback 346 final TaskView tv = this; 347 DismissTaskViewEvent dismissEvent = new DismissTaskViewEvent(tv, mTask); 348 dismissEvent.addPostAnimationCallback(new Runnable() { 349 @Override 350 public void run() { 351 EventBus.getDefault().send(new TaskViewDismissedEvent(mTask, tv)); 352 } 353 }); 354 EventBus.getDefault().send(dismissEvent); 355 } 356 357 /** 358 * Returns whether this view should be clipped, or any views below should clip against this 359 * view. 360 */ 361 boolean shouldClipViewInStack() { 362 // Never clip for freeform tasks or if invisible 363 if (mTask.isFreeformTask() || getVisibility() != View.VISIBLE) { 364 return false; 365 } 366 return mClipViewInStack; 367 } 368 369 /** Sets whether this view should be clipped, or clipped against. */ 370 void setClipViewInStack(boolean clip) { 371 if (clip != mClipViewInStack) { 372 mClipViewInStack = clip; 373 if (mCb != null) { 374 mCb.onTaskViewClipStateChanged(this); 375 } 376 } 377 } 378 379 /** Sets the current task progress. */ 380 public void setTaskProgress(float p) { 381 mTaskProgress = p; 382 mViewBounds.setAlpha(p); 383 updateDimFromTaskProgress(); 384 } 385 386 public TaskViewHeader getHeaderView() { 387 return mHeaderView; 388 } 389 390 /** Returns the current task progress. */ 391 public float getTaskProgress() { 392 return mTaskProgress; 393 } 394 395 /** Returns the current dim. */ 396 public void setDim(int dim) { 397 RecentsConfiguration config = Recents.getConfiguration(); 398 399 mDimAlpha = dim; 400 if (config.useHardwareLayers) { 401 // Defer setting hardware layers if we have not yet measured, or there is no dim to draw 402 if (getMeasuredWidth() > 0 && getMeasuredHeight() > 0) { 403 mDimColorFilter.setColor(Color.argb(mDimAlpha, 0, 0, 0)); 404 mDimLayerPaint.setColorFilter(mDimColorFilter); 405 mContent.setLayerType(LAYER_TYPE_HARDWARE, mDimLayerPaint); 406 } 407 } else { 408 float dimAlpha = mDimAlpha / 255.0f; 409 mThumbnailView.setDimAlpha(dimAlpha); 410 mHeaderView.setDimAlpha(dimAlpha); 411 } 412 } 413 414 /** Returns the current dim. */ 415 public int getDim() { 416 return mDimAlpha; 417 } 418 419 /** Animates the dim to the task progress. */ 420 void animateDimToProgress(int duration, Animator.AnimatorListener animListener) { 421 // Animate the dim into view as well 422 int toDim = getDimFromTaskProgress(); 423 if (toDim != getDim()) { 424 ObjectAnimator anim = ObjectAnimator.ofInt(this, DIM, getDim(), toDim); 425 anim.setDuration(duration); 426 if (animListener != null) { 427 anim.addListener(animListener); 428 } 429 anim.start(); 430 } else { 431 animListener.onAnimationEnd(null); 432 } 433 } 434 435 /** Compute the dim as a function of the scale of this view. */ 436 int getDimFromTaskProgress() { 437 float x = mTaskProgress < 0 438 ? 1f 439 : mDimInterpolator.getInterpolation(1f - mTaskProgress); 440 float dim = mMaxDimScale * x; 441 return (int) (dim * 255); 442 } 443 444 /** Update the dim as a function of the scale of this view. */ 445 void updateDimFromTaskProgress() { 446 setDim(getDimFromTaskProgress()); 447 } 448 449 /** 450 * Explicitly sets the focused state of this task. 451 */ 452 public void setFocusedState(boolean isFocused, boolean requestViewFocus) { 453 SystemServicesProxy ssp = Recents.getSystemServices(); 454 if (isFocused) { 455 if (requestViewFocus && !isFocused()) { 456 requestFocus(); 457 } 458 if (requestViewFocus && !isAccessibilityFocused() && ssp.isTouchExplorationEnabled()) { 459 requestAccessibilityFocus(); 460 } 461 } else { 462 if (isAccessibilityFocused() && ssp.isTouchExplorationEnabled()) { 463 clearAccessibilityFocus(); 464 } 465 } 466 } 467 468 /** 469 * Shows the action button. 470 * @param fadeIn whether or not to animate the action button in. 471 * @param fadeInDuration the duration of the action button animation, only used if 472 * {@param fadeIn} is true. 473 */ 474 public void showActionButton(boolean fadeIn, int fadeInDuration) { 475 mActionButtonView.setVisibility(View.VISIBLE); 476 477 if (fadeIn && mActionButtonView.getAlpha() < 1f) { 478 mActionButtonView.animate() 479 .alpha(1f) 480 .scaleX(1f) 481 .scaleY(1f) 482 .setDuration(fadeInDuration) 483 .setInterpolator(Interpolators.ALPHA_IN) 484 .start(); 485 } else { 486 mActionButtonView.setScaleX(1f); 487 mActionButtonView.setScaleY(1f); 488 mActionButtonView.setAlpha(1f); 489 mActionButtonView.setTranslationZ(mActionButtonTranslationZ); 490 } 491 } 492 493 /** 494 * Immediately hides the action button. 495 * 496 * @param fadeOut whether or not to animate the action button out. 497 */ 498 public void hideActionButton(boolean fadeOut, int fadeOutDuration, boolean scaleDown, 499 final Animator.AnimatorListener animListener) { 500 if (fadeOut && mActionButtonView.getAlpha() > 0f) { 501 if (scaleDown) { 502 float toScale = 0.9f; 503 mActionButtonView.animate() 504 .scaleX(toScale) 505 .scaleY(toScale); 506 } 507 mActionButtonView.animate() 508 .alpha(0f) 509 .setDuration(fadeOutDuration) 510 .setInterpolator(Interpolators.ALPHA_OUT) 511 .withEndAction(new Runnable() { 512 @Override 513 public void run() { 514 if (animListener != null) { 515 animListener.onAnimationEnd(null); 516 } 517 mActionButtonView.setVisibility(View.INVISIBLE); 518 } 519 }) 520 .start(); 521 } else { 522 mActionButtonView.setAlpha(0f); 523 mActionButtonView.setVisibility(View.INVISIBLE); 524 if (animListener != null) { 525 animListener.onAnimationEnd(null); 526 } 527 } 528 } 529 530 /**** TaskStackAnimationHelper.Callbacks Implementation ****/ 531 532 @Override 533 public void onPrepareLaunchTargetForEnterAnimation() { 534 // These values will be animated in when onStartLaunchTargetEnterAnimation() is called 535 setDim(0); 536 mActionButtonView.setAlpha(0f); 537 } 538 539 @Override 540 public void onStartLaunchTargetEnterAnimation(int duration, boolean screenPinningEnabled, 541 ReferenceCountedTrigger postAnimationTrigger) { 542 postAnimationTrigger.increment(); 543 animateDimToProgress(duration, postAnimationTrigger.decrementOnAnimationEnd()); 544 545 if (screenPinningEnabled) { 546 showActionButton(true /* fadeIn */, duration /* fadeInDuration */); 547 } 548 } 549 550 @Override 551 public void onStartLaunchTargetLaunchAnimation(int duration, boolean screenPinningRequested, 552 ReferenceCountedTrigger postAnimationTrigger) { 553 if (mDimAlpha > 0) { 554 ObjectAnimator anim = ObjectAnimator.ofInt(this, DIM, getDim(), 0); 555 anim.setDuration(duration); 556 anim.setInterpolator(Interpolators.ALPHA_OUT); 557 anim.start(); 558 } 559 560 postAnimationTrigger.increment(); 561 hideActionButton(true /* fadeOut */, duration, 562 !screenPinningRequested /* scaleDown */, 563 postAnimationTrigger.decrementOnAnimationEnd()); 564 } 565 566 /**** TaskCallbacks Implementation ****/ 567 568 public void onTaskBound(Task t) { 569 SystemServicesProxy ssp = Recents.getSystemServices(); 570 mTask = t; 571 mTask.addCallback(this); 572 mIsDisabledInSafeMode = !mTask.isSystemApp && ssp.isInSafeMode(); 573 } 574 575 @Override 576 public void onTaskDataLoaded(Task task) { 577 // Bind each of the views to the new task data 578 mThumbnailView.rebindToTask(mTask, mIsDisabledInSafeMode); 579 mHeaderView.rebindToTask(mTask, mTouchExplorationEnabled, mIsDisabledInSafeMode); 580 mTaskDataLoaded = true; 581 } 582 583 @Override 584 public void onTaskDataUnloaded() { 585 // Unbind each of the views from the task data and remove the task callback 586 mTask.removeCallback(this); 587 mThumbnailView.unbindFromTask(); 588 mHeaderView.unbindFromTask(mTouchExplorationEnabled); 589 mTaskDataLoaded = false; 590 } 591 592 @Override 593 public void onTaskStackIdChanged() { 594 mHeaderView.rebindToTask(mTask, mTouchExplorationEnabled, mIsDisabledInSafeMode); 595 } 596 597 /**** View.OnClickListener Implementation ****/ 598 599 @Override 600 public void onClick(final View v) { 601 if (mIsDisabledInSafeMode) { 602 Context context = getContext(); 603 String msg = context.getString(R.string.recents_launch_disabled_message, mTask.title); 604 if (mDisabledAppToast != null) { 605 mDisabledAppToast.cancel(); 606 } 607 mDisabledAppToast = Toast.makeText(context, msg, Toast.LENGTH_SHORT); 608 mDisabledAppToast.show(); 609 return; 610 } 611 612 boolean screenPinningRequested = false; 613 if (v == mActionButtonView) { 614 // Reset the translation of the action button before we animate it out 615 mActionButtonView.setTranslationZ(0f); 616 screenPinningRequested = true; 617 } 618 EventBus.getDefault().send(new LaunchTaskEvent(this, mTask, null, INVALID_STACK_ID, 619 screenPinningRequested)); 620 621 MetricsLogger.action(v.getContext(), MetricsEvent.OVERVIEW_SELECT, 622 mTask.key.getComponent().toString()); 623 } 624 625 /**** View.OnLongClickListener Implementation ****/ 626 627 @Override 628 public boolean onLongClick(View v) { 629 SystemServicesProxy ssp = Recents.getSystemServices(); 630 // Since we are clipping the view to the bounds, manually do the hit test 631 Rect clipBounds = new Rect(mViewBounds.mClipBounds); 632 clipBounds.scale(getScaleX()); 633 boolean inBounds = clipBounds.contains(mDownTouchPos.x, mDownTouchPos.y); 634 if (v == this && inBounds && !ssp.hasDockedTask()) { 635 // Start listening for drag events 636 setClipViewInStack(false); 637 638 mDownTouchPos.x += ((1f - getScaleX()) * getWidth()) / 2; 639 mDownTouchPos.y += ((1f - getScaleY()) * getHeight()) / 2; 640 641 EventBus.getDefault().register(this, RecentsActivity.EVENT_BUS_PRIORITY + 1); 642 EventBus.getDefault().send(new DragStartEvent(mTask, this, mDownTouchPos)); 643 return true; 644 } 645 return false; 646 } 647 648 /**** Events ****/ 649 650 public final void onBusEvent(DragEndEvent event) { 651 if (!(event.dropTarget instanceof TaskStack.DockState)) { 652 event.addPostAnimationCallback(new Runnable() { 653 @Override 654 public void run() { 655 // Animate the drag view back from where it is, to the view location, then after 656 // it returns, update the clip state 657 setClipViewInStack(true); 658 } 659 }); 660 } 661 EventBus.getDefault().unregister(this); 662 } 663 664 /** 665 * Reads current system flags related to accessibility and screen pinning. 666 */ 667 private void readSystemFlags() { 668 SystemServicesProxy ssp = Recents.getSystemServices(); 669 mTouchExplorationEnabled = ssp.isTouchExplorationEnabled(); 670 } 671} 672