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