TaskView.java revision 88f3db9e2180be5e2d3c524b9f1dd99e05465041
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.ObjectAnimator; 21import android.animation.ValueAnimator; 22import android.content.Context; 23import android.graphics.*; 24import android.util.AttributeSet; 25import android.view.View; 26import android.view.ViewOutlineProvider; 27import android.view.animation.AccelerateInterpolator; 28import android.widget.FrameLayout; 29import com.android.systemui.R; 30import com.android.systemui.recents.Constants; 31import com.android.systemui.recents.RecentsConfiguration; 32import com.android.systemui.recents.misc.Utilities; 33import com.android.systemui.recents.model.Task; 34import com.android.systemui.statusbar.phone.PhoneStatusBar; 35 36/* A task view */ 37public class TaskView extends FrameLayout implements Task.TaskCallbacks, 38 View.OnClickListener, View.OnLongClickListener { 39 40 /** The TaskView callbacks */ 41 interface TaskViewCallbacks { 42 public void onTaskViewAppIconClicked(TaskView tv); 43 public void onTaskViewAppInfoClicked(TaskView tv); 44 public void onTaskViewClicked(TaskView tv, Task task, boolean lockToTask); 45 public void onTaskViewDismissed(TaskView tv); 46 public void onTaskViewClipStateChanged(TaskView tv); 47 public void onTaskViewFocusChanged(TaskView tv, boolean focused); 48 49 public void onTaskResize(TaskView tv); 50 } 51 52 RecentsConfiguration mConfig; 53 54 float mTaskProgress; 55 ObjectAnimator mTaskProgressAnimator; 56 float mMaxDimScale; 57 int mDimAlpha; 58 AccelerateInterpolator mDimInterpolator = new AccelerateInterpolator(1f); 59 PorterDuffColorFilter mDimColorFilter = new PorterDuffColorFilter(0, PorterDuff.Mode.SRC_ATOP); 60 Paint mDimLayerPaint = new Paint(); 61 float mActionButtonTranslationZ; 62 63 Task mTask; 64 boolean mTaskDataLoaded; 65 boolean mIsFocused; 66 boolean mFocusAnimationsEnabled; 67 boolean mClipViewInStack; 68 AnimateableViewBounds mViewBounds; 69 70 View mContent; 71 TaskViewThumbnail mThumbnailView; 72 TaskViewHeader mHeaderView; 73 View mActionButtonView; 74 TaskViewCallbacks mCb; 75 76 // Optimizations 77 ValueAnimator.AnimatorUpdateListener mUpdateDimListener = 78 new ValueAnimator.AnimatorUpdateListener() { 79 @Override 80 public void onAnimationUpdate(ValueAnimator animation) { 81 setTaskProgress((Float) animation.getAnimatedValue()); 82 } 83 }; 84 85 86 public TaskView(Context context) { 87 this(context, null); 88 } 89 90 public TaskView(Context context, AttributeSet attrs) { 91 this(context, attrs, 0); 92 } 93 94 public TaskView(Context context, AttributeSet attrs, int defStyleAttr) { 95 this(context, attrs, defStyleAttr, 0); 96 } 97 98 public TaskView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { 99 super(context, attrs, defStyleAttr, defStyleRes); 100 mConfig = RecentsConfiguration.getInstance(); 101 mMaxDimScale = mConfig.taskStackMaxDim / 255f; 102 mClipViewInStack = true; 103 mViewBounds = new AnimateableViewBounds(this, mConfig.taskViewRoundedCornerRadiusPx); 104 setTaskProgress(getTaskProgress()); 105 setDim(getDim()); 106 if (mConfig.fakeShadows) { 107 setBackground(new FakeShadowDrawable(context.getResources(), mConfig)); 108 } 109 setOutlineProvider(mViewBounds); 110 } 111 112 /** Set callback */ 113 void setCallbacks(TaskViewCallbacks cb) { 114 mCb = cb; 115 } 116 117 /** Resets this TaskView for reuse. */ 118 void reset() { 119 resetViewProperties(); 120 resetNoUserInteractionState(); 121 setClipViewInStack(false); 122 setCallbacks(null); 123 } 124 125 /** Gets the task */ 126 Task getTask() { 127 return mTask; 128 } 129 130 /** Returns the view bounds. */ 131 AnimateableViewBounds getViewBounds() { 132 return mViewBounds; 133 } 134 135 @Override 136 protected void onFinishInflate() { 137 // Bind the views 138 mContent = findViewById(R.id.task_view_content); 139 mHeaderView = (TaskViewHeader) findViewById(R.id.task_view_bar); 140 mThumbnailView = (TaskViewThumbnail) findViewById(R.id.task_view_thumbnail); 141 mThumbnailView.updateClipToTaskBar(mHeaderView); 142 mActionButtonView = findViewById(R.id.lock_to_app_fab); 143 mActionButtonView.setOutlineProvider(new ViewOutlineProvider() { 144 @Override 145 public void getOutline(View view, Outline outline) { 146 // Set the outline to match the FAB background 147 outline.setOval(0, 0, mActionButtonView.getWidth(), mActionButtonView.getHeight()); 148 } 149 }); 150 mActionButtonTranslationZ = mActionButtonView.getTranslationZ(); 151 } 152 153 @Override 154 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 155 int width = MeasureSpec.getSize(widthMeasureSpec); 156 int height = MeasureSpec.getSize(heightMeasureSpec); 157 158 int widthWithoutPadding = width - mPaddingLeft - mPaddingRight; 159 int heightWithoutPadding = height - mPaddingTop - mPaddingBottom; 160 161 // Measure the content 162 mContent.measure(MeasureSpec.makeMeasureSpec(widthWithoutPadding, MeasureSpec.EXACTLY), 163 MeasureSpec.makeMeasureSpec(widthWithoutPadding, MeasureSpec.EXACTLY)); 164 165 // Measure the bar view, and action button 166 mHeaderView.measure(MeasureSpec.makeMeasureSpec(widthWithoutPadding, MeasureSpec.EXACTLY), 167 MeasureSpec.makeMeasureSpec(mConfig.taskBarHeight, MeasureSpec.EXACTLY)); 168 mActionButtonView.measure( 169 MeasureSpec.makeMeasureSpec(widthWithoutPadding, MeasureSpec.AT_MOST), 170 MeasureSpec.makeMeasureSpec(heightWithoutPadding, MeasureSpec.AT_MOST)); 171 // Measure the thumbnail to be square 172 mThumbnailView.measure( 173 MeasureSpec.makeMeasureSpec(widthWithoutPadding, MeasureSpec.EXACTLY), 174 MeasureSpec.makeMeasureSpec(widthWithoutPadding, MeasureSpec.EXACTLY)); 175 setMeasuredDimension(width, height); 176 invalidateOutline(); 177 } 178 179 /** Synchronizes this view's properties with the task's transform */ 180 void updateViewPropertiesToTaskTransform(TaskViewTransform toTransform, int duration) { 181 updateViewPropertiesToTaskTransform(toTransform, duration, null); 182 } 183 184 void updateViewPropertiesToTaskTransform(TaskViewTransform toTransform, int duration, 185 ValueAnimator.AnimatorUpdateListener updateCallback) { 186 // Apply the transform 187 toTransform.applyToTaskView(this, duration, mConfig.fastOutSlowInInterpolator, false, 188 !mConfig.fakeShadows, updateCallback); 189 190 // Update the task progress 191 Utilities.cancelAnimationWithoutCallbacks(mTaskProgressAnimator); 192 if (duration <= 0) { 193 setTaskProgress(toTransform.p); 194 } else { 195 mTaskProgressAnimator = ObjectAnimator.ofFloat(this, "taskProgress", toTransform.p); 196 mTaskProgressAnimator.setDuration(duration); 197 mTaskProgressAnimator.addUpdateListener(mUpdateDimListener); 198 mTaskProgressAnimator.start(); 199 } 200 } 201 202 /** Resets this view's properties */ 203 void resetViewProperties() { 204 setDim(0); 205 setLayerType(View.LAYER_TYPE_NONE, null); 206 TaskViewTransform.reset(this); 207 if (mActionButtonView != null) { 208 mActionButtonView.setScaleX(1f); 209 mActionButtonView.setScaleY(1f); 210 mActionButtonView.setAlpha(1f); 211 mActionButtonView.setTranslationZ(mActionButtonTranslationZ); 212 } 213 } 214 215 /** 216 * When we are un/filtering, this method will set up the transform that we are animating to, 217 * in order to hide the task. 218 */ 219 void prepareTaskTransformForFilterTaskHidden(TaskViewTransform toTransform) { 220 // Fade the view out and slide it away 221 toTransform.alpha = 0f; 222 toTransform.translationY += 200; 223 toTransform.translationZ = 0; 224 } 225 226 /** 227 * When we are un/filtering, this method will setup the transform that we are animating from, 228 * in order to show the task. 229 */ 230 void prepareTaskTransformForFilterTaskVisible(TaskViewTransform fromTransform) { 231 // Fade the view in 232 fromTransform.alpha = 0f; 233 } 234 235 /** Prepares this task view for the enter-recents animations. This is called earlier in the 236 * first layout because the actual animation into recents may take a long time. */ 237 void prepareEnterRecentsAnimation(boolean isTaskViewLaunchTargetTask, 238 boolean occludesLaunchTarget, int offscreenY) { 239 int initialDim = getDim(); 240 if (mConfig.launchedHasConfigurationChanged) { 241 // Just load the views as-is 242 } else if (mConfig.launchedFromAppWithThumbnail) { 243 if (isTaskViewLaunchTargetTask) { 244 // Set the dim to 0 so we can animate it in 245 initialDim = 0; 246 // Hide the action button 247 mActionButtonView.setAlpha(0f); 248 } else if (occludesLaunchTarget) { 249 // Move the task view off screen (below) so we can animate it in 250 setTranslationY(offscreenY); 251 } 252 253 } else if (mConfig.launchedFromHome) { 254 // Move the task view off screen (below) so we can animate it in 255 setTranslationY(offscreenY); 256 setTranslationZ(0); 257 setScaleX(1f); 258 setScaleY(1f); 259 } 260 // Apply the current dim 261 setDim(initialDim); 262 // Prepare the thumbnail view alpha 263 mThumbnailView.prepareEnterRecentsAnimation(isTaskViewLaunchTargetTask); 264 } 265 266 /** Animates this task view as it enters recents */ 267 void startEnterRecentsAnimation(final ViewAnimation.TaskViewEnterContext ctx) { 268 final TaskViewTransform transform = ctx.currentTaskTransform; 269 int startDelay = 0; 270 271 if (mConfig.launchedFromAppWithThumbnail) { 272 if (mTask.isLaunchTarget) { 273 // Animate the dim/overlay 274 if (Constants.DebugFlags.App.EnableThumbnailAlphaOnFrontmost) { 275 // Animate the thumbnail alpha before the dim animation (to prevent updating the 276 // hardware layer) 277 mThumbnailView.startEnterRecentsAnimation(mConfig.transitionEnterFromAppDelay, 278 new Runnable() { 279 @Override 280 public void run() { 281 animateDimToProgress(0, mConfig.taskViewEnterFromAppDuration, 282 ctx.postAnimationTrigger.decrementOnAnimationEnd()); 283 } 284 }); 285 } else { 286 // Immediately start the dim animation 287 animateDimToProgress(mConfig.transitionEnterFromAppDelay, 288 mConfig.taskViewEnterFromAppDuration, 289 ctx.postAnimationTrigger.decrementOnAnimationEnd()); 290 } 291 ctx.postAnimationTrigger.increment(); 292 293 // Animate the action button in 294 fadeInActionButton(mConfig.transitionEnterFromAppDelay, 295 mConfig.taskViewEnterFromAppDuration); 296 } else { 297 // Animate the task up if it was occluding the launch target 298 if (ctx.currentTaskOccludesLaunchTarget) { 299 setTranslationY(transform.translationY + mConfig.taskViewAffiliateGroupEnterOffsetPx); 300 setAlpha(0f); 301 animate().alpha(1f) 302 .translationY(transform.translationY) 303 .setStartDelay(mConfig.transitionEnterFromAppDelay) 304 .setUpdateListener(null) 305 .setInterpolator(mConfig.fastOutSlowInInterpolator) 306 .setDuration(mConfig.taskViewEnterFromHomeDuration) 307 .withEndAction(new Runnable() { 308 @Override 309 public void run() { 310 // Decrement the post animation trigger 311 ctx.postAnimationTrigger.decrement(); 312 } 313 }) 314 .start(); 315 ctx.postAnimationTrigger.increment(); 316 } 317 } 318 startDelay = mConfig.transitionEnterFromAppDelay; 319 320 } else if (mConfig.launchedFromHome) { 321 // Animate the tasks up 322 int frontIndex = (ctx.currentStackViewCount - ctx.currentStackViewIndex - 1); 323 int delay = mConfig.transitionEnterFromHomeDelay + 324 frontIndex * mConfig.taskViewEnterFromHomeStaggerDelay; 325 326 setScaleX(transform.scale); 327 setScaleY(transform.scale); 328 if (!mConfig.fakeShadows) { 329 animate().translationZ(transform.translationZ); 330 } 331 animate() 332 .translationY(transform.translationY) 333 .setStartDelay(delay) 334 .setUpdateListener(ctx.updateListener) 335 .setInterpolator(mConfig.quintOutInterpolator) 336 .setDuration(mConfig.taskViewEnterFromHomeDuration + 337 frontIndex * mConfig.taskViewEnterFromHomeStaggerDelay) 338 .withEndAction(new Runnable() { 339 @Override 340 public void run() { 341 // Decrement the post animation trigger 342 ctx.postAnimationTrigger.decrement(); 343 } 344 }) 345 .start(); 346 ctx.postAnimationTrigger.increment(); 347 startDelay = delay; 348 } 349 350 // Enable the focus animations from this point onwards so that they aren't affected by the 351 // window transitions 352 postDelayed(new Runnable() { 353 @Override 354 public void run() { 355 enableFocusAnimations(); 356 } 357 }, startDelay); 358 } 359 360 public void fadeInActionButton(int delay, int duration) { 361 // Hide the action button 362 mActionButtonView.setAlpha(0f); 363 364 // Animate the action button in 365 mActionButtonView.animate().alpha(1f) 366 .setStartDelay(delay) 367 .setDuration(duration) 368 .setInterpolator(PhoneStatusBar.ALPHA_IN) 369 .start(); 370 } 371 372 /** Animates this task view as it leaves recents by pressing home. */ 373 void startExitToHomeAnimation(ViewAnimation.TaskViewExitContext ctx) { 374 animate() 375 .translationY(ctx.offscreenTranslationY) 376 .setStartDelay(0) 377 .setUpdateListener(null) 378 .setInterpolator(mConfig.fastOutLinearInInterpolator) 379 .setDuration(mConfig.taskViewExitToHomeDuration) 380 .withEndAction(ctx.postAnimationTrigger.decrementAsRunnable()) 381 .start(); 382 ctx.postAnimationTrigger.increment(); 383 } 384 385 /** Animates this task view away when dismissing all tasks. */ 386 void startDismissAllAnimation() { 387 dismissTask(); 388 } 389 390 /** Animates this task view as it exits recents */ 391 void startLaunchTaskAnimation(final Runnable postAnimRunnable, boolean isLaunchingTask, 392 boolean occludesLaunchTarget, boolean lockToTask) { 393 if (isLaunchingTask) { 394 // Animate the thumbnail alpha back into full opacity for the window animation out 395 mThumbnailView.startLaunchTaskAnimation(postAnimRunnable); 396 397 // Animate the dim 398 if (mDimAlpha > 0) { 399 ObjectAnimator anim = ObjectAnimator.ofInt(this, "dim", 0); 400 anim.setDuration(mConfig.taskViewExitToAppDuration); 401 anim.setInterpolator(mConfig.fastOutLinearInInterpolator); 402 anim.start(); 403 } 404 405 // Animate the action button away 406 if (!lockToTask) { 407 float toScale = 0.9f; 408 mActionButtonView.animate() 409 .scaleX(toScale) 410 .scaleY(toScale); 411 } 412 mActionButtonView.animate() 413 .alpha(0f) 414 .setStartDelay(0) 415 .setDuration(mConfig.taskViewExitToAppDuration) 416 .setInterpolator(mConfig.fastOutLinearInInterpolator) 417 .start(); 418 } else { 419 // Hide the dismiss button 420 mHeaderView.startLaunchTaskDismissAnimation(); 421 // If this is another view in the task grouping and is in front of the launch task, 422 // animate it away first 423 if (occludesLaunchTarget) { 424 animate().alpha(0f) 425 .translationY(getTranslationY() + mConfig.taskViewAffiliateGroupEnterOffsetPx) 426 .setStartDelay(0) 427 .setUpdateListener(null) 428 .setInterpolator(mConfig.fastOutLinearInInterpolator) 429 .setDuration(mConfig.taskViewExitToAppDuration) 430 .start(); 431 } 432 } 433 } 434 435 /** Animates the deletion of this task view */ 436 void startDeleteTaskAnimation(final Runnable r, int delay) { 437 // Disabling clipping with the stack while the view is animating away 438 setClipViewInStack(false); 439 440 animate().translationX(mConfig.taskViewRemoveAnimTranslationXPx) 441 .alpha(0f) 442 .setStartDelay(delay) 443 .setUpdateListener(null) 444 .setInterpolator(mConfig.fastOutSlowInInterpolator) 445 .setDuration(mConfig.taskViewRemoveAnimDuration) 446 .withEndAction(new Runnable() { 447 @Override 448 public void run() { 449 if (r != null) { 450 r.run(); 451 } 452 453 // Re-enable clipping with the stack (we will reuse this view) 454 setClipViewInStack(true); 455 } 456 }) 457 .start(); 458 } 459 460 /** Enables/disables handling touch on this task view. */ 461 void setTouchEnabled(boolean enabled) { 462 setOnClickListener(enabled ? this : null); 463 } 464 465 /** Animates this task view if the user does not interact with the stack after a certain time. */ 466 void startNoUserInteractionAnimation() { 467 mHeaderView.startNoUserInteractionAnimation(); 468 } 469 470 /** Mark this task view that the user does has not interacted with the stack after a certain time. */ 471 void setNoUserInteractionState() { 472 mHeaderView.setNoUserInteractionState(); 473 } 474 475 /** Resets the state tracking that the user has not interacted with the stack after a certain time. */ 476 void resetNoUserInteractionState() { 477 mHeaderView.resetNoUserInteractionState(); 478 } 479 480 /** Dismisses this task. */ 481 void dismissTask() { 482 // Animate out the view and call the callback 483 final TaskView tv = this; 484 startDeleteTaskAnimation(new Runnable() { 485 @Override 486 public void run() { 487 if (mCb != null) { 488 mCb.onTaskViewDismissed(tv); 489 } 490 } 491 }, 0); 492 } 493 494 /** 495 * Returns whether this view should be clipped, or any views below should clip against this 496 * view. 497 */ 498 boolean shouldClipViewInStack() { 499 return mClipViewInStack && (getVisibility() == View.VISIBLE); 500 } 501 502 /** Sets whether this view should be clipped, or clipped against. */ 503 void setClipViewInStack(boolean clip) { 504 if (clip != mClipViewInStack) { 505 mClipViewInStack = clip; 506 if (mCb != null) { 507 mCb.onTaskViewClipStateChanged(this); 508 } 509 } 510 } 511 512 /** Sets the current task progress. */ 513 public void setTaskProgress(float p) { 514 mTaskProgress = p; 515 mViewBounds.setAlpha(p); 516 updateDimFromTaskProgress(); 517 } 518 519 /** Returns the current task progress. */ 520 public float getTaskProgress() { 521 return mTaskProgress; 522 } 523 524 /** Returns the current dim. */ 525 public void setDim(int dim) { 526 mDimAlpha = dim; 527 if (mConfig.useHardwareLayers) { 528 // Defer setting hardware layers if we have not yet measured, or there is no dim to draw 529 if (getMeasuredWidth() > 0 && getMeasuredHeight() > 0) { 530 mDimColorFilter.setColor(Color.argb(mDimAlpha, 0, 0, 0)); 531 mDimLayerPaint.setColorFilter(mDimColorFilter); 532 mContent.setLayerType(LAYER_TYPE_HARDWARE, mDimLayerPaint); 533 } 534 } else { 535 float dimAlpha = mDimAlpha / 255.0f; 536 if (mThumbnailView != null) { 537 mThumbnailView.setDimAlpha(dimAlpha); 538 } 539 if (mHeaderView != null) { 540 mHeaderView.setDimAlpha(dim); 541 } 542 } 543 } 544 545 /** Returns the current dim. */ 546 public int getDim() { 547 return mDimAlpha; 548 } 549 550 /** Animates the dim to the task progress. */ 551 void animateDimToProgress(int delay, int duration, Animator.AnimatorListener postAnimRunnable) { 552 // Animate the dim into view as well 553 int toDim = getDimFromTaskProgress(); 554 if (toDim != getDim()) { 555 ObjectAnimator anim = ObjectAnimator.ofInt(TaskView.this, "dim", toDim); 556 anim.setStartDelay(delay); 557 anim.setDuration(duration); 558 if (postAnimRunnable != null) { 559 anim.addListener(postAnimRunnable); 560 } 561 anim.start(); 562 } 563 } 564 565 /** Compute the dim as a function of the scale of this view. */ 566 int getDimFromTaskProgress() { 567 float dim = mMaxDimScale * mDimInterpolator.getInterpolation(1f - mTaskProgress); 568 return (int) (dim * 255); 569 } 570 571 /** Update the dim as a function of the scale of this view. */ 572 void updateDimFromTaskProgress() { 573 setDim(getDimFromTaskProgress()); 574 } 575 576 /**** View focus state ****/ 577 578 /** 579 * Sets the focused task explicitly. We need a separate flag because requestFocus() won't happen 580 * if the view is not currently visible, or we are in touch state (where we still want to keep 581 * track of focus). 582 */ 583 public void setFocusedTask(boolean animateFocusedState) { 584 mIsFocused = true; 585 if (mFocusAnimationsEnabled) { 586 // Focus the header bar 587 mHeaderView.onTaskViewFocusChanged(true, animateFocusedState); 588 } 589 // Update the thumbnail alpha with the focus 590 mThumbnailView.onFocusChanged(true); 591 // Call the callback 592 if (mCb != null) { 593 mCb.onTaskViewFocusChanged(this, true); 594 } 595 // Workaround, we don't always want it focusable in touch mode, but we want the first task 596 // to be focused after the enter-recents animation, which can be triggered from either touch 597 // or keyboard 598 setFocusableInTouchMode(true); 599 requestFocus(); 600 setFocusableInTouchMode(false); 601 invalidate(); 602 } 603 604 /** 605 * Unsets the focused task explicitly. 606 */ 607 void unsetFocusedTask() { 608 mIsFocused = false; 609 if (mFocusAnimationsEnabled) { 610 // Un-focus the header bar 611 mHeaderView.onTaskViewFocusChanged(false, true); 612 } 613 614 // Update the thumbnail alpha with the focus 615 mThumbnailView.onFocusChanged(false); 616 // Call the callback 617 if (mCb != null) { 618 mCb.onTaskViewFocusChanged(this, false); 619 } 620 invalidate(); 621 } 622 623 /** 624 * Updates the explicitly focused state when the view focus changes. 625 */ 626 @Override 627 protected void onFocusChanged(boolean gainFocus, int direction, Rect previouslyFocusedRect) { 628 super.onFocusChanged(gainFocus, direction, previouslyFocusedRect); 629 if (!gainFocus) { 630 unsetFocusedTask(); 631 } 632 } 633 634 /** 635 * Returns whether we have explicitly been focused. 636 */ 637 public boolean isFocusedTask() { 638 return mIsFocused || isFocused(); 639 } 640 641 /** Enables all focus animations. */ 642 void enableFocusAnimations() { 643 boolean wasFocusAnimationsEnabled = mFocusAnimationsEnabled; 644 mFocusAnimationsEnabled = true; 645 if (mIsFocused && !wasFocusAnimationsEnabled) { 646 // Re-notify the header if we were focused and animations were not previously enabled 647 mHeaderView.onTaskViewFocusChanged(true, true); 648 } 649 } 650 651 public void disableLayersForOneFrame() { 652 mHeaderView.disableLayersForOneFrame(); 653 } 654 655 /**** TaskCallbacks Implementation ****/ 656 657 /** Binds this task view to the task */ 658 public void onTaskBound(Task t) { 659 mTask = t; 660 mTask.setCallbacks(this); 661 662 // Hide the action button if lock to app is disabled for this view 663 int lockButtonVisibility = (!t.lockToTaskEnabled || !t.lockToThisTask) ? GONE : VISIBLE; 664 if (mActionButtonView.getVisibility() != lockButtonVisibility) { 665 mActionButtonView.setVisibility(lockButtonVisibility); 666 requestLayout(); 667 } 668 } 669 670 @Override 671 public void onTaskDataLoaded() { 672 if (mThumbnailView != null && mHeaderView != null) { 673 // Bind each of the views to the new task data 674 mThumbnailView.rebindToTask(mTask); 675 mHeaderView.rebindToTask(mTask); 676 // Rebind any listeners 677 mHeaderView.mApplicationIcon.setOnClickListener(this); 678 mHeaderView.mDismissButton.setOnClickListener(this); 679 if (mConfig.multiStackEnabled) { 680 mHeaderView.mMoveTaskButton.setOnClickListener(this); 681 } 682 mActionButtonView.setOnClickListener(this); 683 mHeaderView.mApplicationIcon.setOnLongClickListener(this); 684 } 685 mTaskDataLoaded = true; 686 } 687 688 @Override 689 public void onTaskDataUnloaded() { 690 if (mThumbnailView != null && mHeaderView != null) { 691 // Unbind each of the views from the task data and remove the task callback 692 mTask.setCallbacks(null); 693 mThumbnailView.unbindFromTask(); 694 mHeaderView.unbindFromTask(); 695 // Unbind any listeners 696 mHeaderView.mApplicationIcon.setOnClickListener(null); 697 mHeaderView.mDismissButton.setOnClickListener(null); 698 if (mConfig.multiStackEnabled) { 699 mHeaderView.mMoveTaskButton.setOnClickListener(null); 700 } 701 mActionButtonView.setOnClickListener(null); 702 mHeaderView.mApplicationIcon.setOnLongClickListener(null); 703 } 704 mTaskDataLoaded = false; 705 } 706 707 @Override 708 public void onMultiStackDebugTaskStackIdChanged() { 709 mHeaderView.rebindToTask(mTask); 710 } 711 712 /**** View.OnClickListener Implementation ****/ 713 714 @Override 715 public void onClick(final View v) { 716 final TaskView tv = this; 717 final boolean delayViewClick = (v != this) && (v != mActionButtonView); 718 if (delayViewClick) { 719 // We purposely post the handler delayed to allow for the touch feedback to draw 720 postDelayed(new Runnable() { 721 @Override 722 public void run() { 723 if (Constants.DebugFlags.App.EnableTaskFiltering && v == mHeaderView.mApplicationIcon) { 724 if (mCb != null) { 725 mCb.onTaskViewAppIconClicked(tv); 726 } 727 } else if (v == mHeaderView.mDismissButton) { 728 dismissTask(); 729 } else if (v == mHeaderView.mMoveTaskButton) { 730 if (mCb != null) { 731 mCb.onTaskResize(tv); 732 } 733 } 734 } 735 }, 125); 736 } else { 737 if (v == mActionButtonView) { 738 // Reset the translation of the action button before we animate it out 739 mActionButtonView.setTranslationZ(0f); 740 } 741 if (mCb != null) { 742 mCb.onTaskViewClicked(tv, tv.getTask(), (v == mActionButtonView)); 743 } 744 } 745 } 746 747 /**** View.OnLongClickListener Implementation ****/ 748 749 @Override 750 public boolean onLongClick(View v) { 751 if (v == mHeaderView.mApplicationIcon) { 752 if (mCb != null) { 753 mCb.onTaskViewAppInfoClicked(this); 754 return true; 755 } 756 } 757 return false; 758 } 759} 760