TaskViewHeader.java revision 3c107166fc1f719f3b94a706fdf52a23b1820761
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.AnimatorListenerAdapter; 21import android.annotation.Nullable; 22import android.content.ComponentName; 23import android.content.Context; 24import android.content.pm.ActivityInfo; 25import android.content.res.Resources; 26import android.graphics.Canvas; 27import android.graphics.Color; 28import android.graphics.ColorFilter; 29import android.graphics.Paint; 30import android.graphics.PixelFormat; 31import android.graphics.PorterDuff; 32import android.graphics.Rect; 33import android.graphics.drawable.Drawable; 34import android.os.CountDownTimer; 35import android.support.v4.graphics.ColorUtils; 36import android.util.AttributeSet; 37import android.view.View; 38import android.view.ViewAnimationUtils; 39import android.view.ViewStub; 40import android.view.animation.AnimationUtils; 41import android.view.animation.Interpolator; 42import android.widget.FrameLayout; 43import android.widget.ImageView; 44import android.widget.ProgressBar; 45import android.widget.TextView; 46import com.android.internal.logging.MetricsLogger; 47import com.android.systemui.R; 48import com.android.systemui.recents.Constants; 49import com.android.systemui.recents.Recents; 50import com.android.systemui.recents.RecentsDebugFlags; 51import com.android.systemui.recents.events.EventBus; 52import com.android.systemui.recents.events.activity.LaunchTaskEvent; 53import com.android.systemui.recents.events.ui.ShowApplicationInfoEvent; 54import com.android.systemui.recents.misc.SystemServicesProxy; 55import com.android.systemui.recents.misc.Utilities; 56import com.android.systemui.recents.model.Task; 57 58import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID; 59import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID; 60import static android.app.ActivityManager.StackId.INVALID_STACK_ID; 61 62/* The task bar view */ 63public class TaskViewHeader extends FrameLayout 64 implements View.OnClickListener, View.OnLongClickListener { 65 66 private static final float HIGHLIGHT_LIGHTNESS_INCREMENT = 0.125f; 67 private static final float OVERLAY_LIGHTNESS_INCREMENT = -0.0625f; 68 private static final int OVERLAY_REVEAL_DURATION = 250; 69 private static final long FOCUS_INDICATOR_INTERVAL_MS = 30; 70 71 /** 72 * A color drawable that draws a slight highlight at the top to help it stand out. 73 */ 74 private class HighlightColorDrawable extends Drawable { 75 76 private Paint mHighlightPaint = new Paint(); 77 private Paint mBackgroundPaint = new Paint(); 78 79 public HighlightColorDrawable() { 80 mBackgroundPaint.setColor(Color.argb(255, 0, 0, 0)); 81 mBackgroundPaint.setAntiAlias(true); 82 mHighlightPaint.setColor(Color.argb(255, 255, 255, 255)); 83 mHighlightPaint.setAntiAlias(true); 84 } 85 86 public void setColorAndDim(int color, float dimAlpha) { 87 mBackgroundPaint.setColor(color); 88 89 ColorUtils.colorToHSL(color, mTmpHSL); 90 // TODO: Consider using the saturation of the color to adjust the lightness as well 91 mTmpHSL[2] = Math.min(1f, 92 mTmpHSL[2] + HIGHLIGHT_LIGHTNESS_INCREMENT * (1.0f - dimAlpha)); 93 mHighlightPaint.setColor(ColorUtils.HSLToColor(mTmpHSL)); 94 95 invalidateSelf(); 96 } 97 98 @Override 99 public void setColorFilter(@Nullable ColorFilter colorFilter) { 100 // Do nothing 101 } 102 103 @Override 104 public void setAlpha(int alpha) { 105 // Do nothing 106 } 107 108 @Override 109 public void draw(Canvas canvas) { 110 // Draw the highlight at the top edge (but put the bottom edge just out of view) 111 canvas.drawRoundRect(0, 0, mTaskViewRect.width(), 112 2 * Math.max(mHighlightHeight, mCornerRadius), 113 mCornerRadius, mCornerRadius, mHighlightPaint); 114 115 // Draw the background with the rounded corners 116 canvas.drawRoundRect(0, mHighlightHeight, mTaskViewRect.width(), 117 getHeight() + mCornerRadius, 118 mCornerRadius, mCornerRadius, mBackgroundPaint); 119 } 120 121 @Override 122 public int getOpacity() { 123 return PixelFormat.OPAQUE; 124 } 125 } 126 127 Task mTask; 128 129 // Header views 130 ImageView mIconView; 131 TextView mTitleView; 132 ImageView mMoveTaskButton; 133 ImageView mDismissButton; 134 ViewStub mAppOverlayViewStub; 135 FrameLayout mAppOverlayView; 136 ImageView mAppIconView; 137 ImageView mAppInfoView; 138 TextView mAppTitleView; 139 ViewStub mFocusTimerIndicatorStub; 140 ProgressBar mFocusTimerIndicator; 141 142 // Header drawables 143 Rect mTaskViewRect = new Rect(); 144 int mCornerRadius; 145 int mHighlightHeight; 146 float mDimAlpha; 147 Drawable mLightDismissDrawable; 148 Drawable mDarkDismissDrawable; 149 Drawable mLightFreeformIcon; 150 Drawable mDarkFreeformIcon; 151 Drawable mLightFullscreenIcon; 152 Drawable mDarkFullscreenIcon; 153 Drawable mLightInfoIcon; 154 Drawable mDarkInfoIcon; 155 int mTaskBarViewLightTextColor; 156 int mTaskBarViewDarkTextColor; 157 int mMoveTaskTargetStackId = INVALID_STACK_ID; 158 159 // Header background 160 private HighlightColorDrawable mBackground; 161 private HighlightColorDrawable mOverlayBackground; 162 private float[] mTmpHSL = new float[3]; 163 164 // Header dim, which is only used when task view hardware layers are not used 165 private Paint mDimLayerPaint = new Paint(); 166 167 Interpolator mFastOutSlowInInterpolator; 168 Interpolator mFastOutLinearInInterpolator; 169 Interpolator mLinearOutSlowInInterpolator; 170 171 private CountDownTimer mFocusTimerCountDown; 172 private long mFocusTimerDuration; 173 174 public TaskViewHeader(Context context) { 175 this(context, null); 176 } 177 178 public TaskViewHeader(Context context, AttributeSet attrs) { 179 this(context, attrs, 0); 180 } 181 182 public TaskViewHeader(Context context, AttributeSet attrs, int defStyleAttr) { 183 this(context, attrs, defStyleAttr, 0); 184 } 185 186 public TaskViewHeader(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { 187 super(context, attrs, defStyleAttr, defStyleRes); 188 setWillNotDraw(false); 189 190 // Load the dismiss resources 191 Resources res = context.getResources(); 192 mLightDismissDrawable = context.getDrawable(R.drawable.recents_dismiss_light); 193 mDarkDismissDrawable = context.getDrawable(R.drawable.recents_dismiss_dark); 194 mCornerRadius = res.getDimensionPixelSize(R.dimen.recents_task_view_rounded_corners_radius); 195 mHighlightHeight = res.getDimensionPixelSize(R.dimen.recents_task_view_highlight); 196 mTaskBarViewLightTextColor = context.getColor(R.color.recents_task_bar_light_text_color); 197 mTaskBarViewDarkTextColor = context.getColor(R.color.recents_task_bar_dark_text_color); 198 mLightFreeformIcon = context.getDrawable(R.drawable.recents_move_task_freeform_light); 199 mDarkFreeformIcon = context.getDrawable(R.drawable.recents_move_task_freeform_dark); 200 mLightFullscreenIcon = context.getDrawable(R.drawable.recents_move_task_fullscreen_light); 201 mDarkFullscreenIcon = context.getDrawable(R.drawable.recents_move_task_fullscreen_dark); 202 mLightInfoIcon = context.getDrawable(R.drawable.recents_info_light); 203 mDarkInfoIcon = context.getDrawable(R.drawable.recents_info_dark); 204 205 mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(context, 206 com.android.internal.R.interpolator.fast_out_slow_in); 207 mFastOutLinearInInterpolator = AnimationUtils.loadInterpolator(context, 208 com.android.internal.R.interpolator.fast_out_linear_in); 209 mLinearOutSlowInInterpolator = AnimationUtils.loadInterpolator(context, 210 com.android.internal.R.interpolator.linear_out_slow_in); 211 212 // Configure the background and dim 213 mBackground = new HighlightColorDrawable(); 214 mBackground.setColorAndDim(Color.argb(255, 0, 0, 0), 0f); 215 setBackground(mBackground); 216 mOverlayBackground = new HighlightColorDrawable(); 217 mDimLayerPaint.setColor(Color.argb(255, 0, 0, 0)); 218 mDimLayerPaint.setAntiAlias(true); 219 mFocusTimerDuration = res.getInteger(R.integer.recents_auto_advance_duration); 220 } 221 222 /** 223 * Resets this header along with the TaskView. 224 */ 225 public void reset() { 226 hideAppOverlay(true /* immediate */); 227 } 228 229 @Override 230 protected void onFinishInflate() { 231 // Initialize the icon and description views 232 mIconView = (ImageView) findViewById(R.id.icon); 233 mIconView.setClickable(false); 234 mIconView.setOnLongClickListener(this); 235 mTitleView = (TextView) findViewById(R.id.title); 236 mDismissButton = (ImageView) findViewById(R.id.dismiss_task); 237 mDismissButton.setOnClickListener(this); 238 mMoveTaskButton = (ImageView) findViewById(R.id.move_task); 239 mFocusTimerIndicatorStub = (ViewStub) findViewById(R.id.focus_timer_indicator_stub); 240 mAppOverlayViewStub = (ViewStub) findViewById(R.id.app_overlay_stub); 241 } 242 243 /** 244 * Called when the task view frame changes, allowing us to move the contents of the header 245 * to match the frame changes. 246 */ 247 public void onTaskViewSizeChanged(int width, int height) { 248 // TODO: Optimize this path 249 mTaskViewRect.set(0, 0, width, height); 250 boolean updateMoveTaskButton = mMoveTaskButton.getVisibility() != View.GONE; 251 boolean isFreeformTask = (mTask != null) && mTask.isFreeformTask(); 252 int appIconWidth = mIconView.getMeasuredWidth(); 253 int activityDescWidth = (mTask != null) 254 ? (int) mTitleView.getPaint().measureText(mTask.title) 255 : mTitleView.getMeasuredWidth(); 256 int dismissIconWidth = mDismissButton.getMeasuredWidth(); 257 int moveTaskIconWidth = mMoveTaskButton.getVisibility() == View.VISIBLE 258 ? mMoveTaskButton.getMeasuredWidth() 259 : 0; 260 261 // Priority-wise, we show the activity icon first, the dismiss icon if there is room, the 262 // move-task icon if there is room, and then finally, the activity label if there is room 263 if (isFreeformTask && width < (appIconWidth + dismissIconWidth)) { 264 mTitleView.setVisibility(View.INVISIBLE); 265 if (updateMoveTaskButton) { 266 mMoveTaskButton.setVisibility(View.INVISIBLE); 267 } 268 mDismissButton.setVisibility(View.INVISIBLE); 269 } else if (isFreeformTask && width < (appIconWidth + dismissIconWidth + 270 moveTaskIconWidth)) { 271 mTitleView.setVisibility(View.INVISIBLE); 272 if (updateMoveTaskButton) { 273 mMoveTaskButton.setVisibility(View.INVISIBLE); 274 } 275 mDismissButton.setVisibility(View.VISIBLE); 276 } else if (isFreeformTask && width < (appIconWidth + dismissIconWidth + moveTaskIconWidth + 277 activityDescWidth)) { 278 mTitleView.setVisibility(View.INVISIBLE); 279 if (updateMoveTaskButton) { 280 mMoveTaskButton.setVisibility(View.VISIBLE); 281 } 282 mDismissButton.setVisibility(View.VISIBLE); 283 } else { 284 mTitleView.setVisibility(View.VISIBLE); 285 if (updateMoveTaskButton) { 286 mMoveTaskButton.setVisibility(View.VISIBLE); 287 } 288 mDismissButton.setVisibility(View.VISIBLE); 289 } 290 if (updateMoveTaskButton) { 291 mMoveTaskButton.setTranslationX(width - getMeasuredWidth()); 292 } 293 mDismissButton.setTranslationX(width - getMeasuredWidth()); 294 invalidate(); 295 } 296 297 @Override 298 protected void dispatchDraw(Canvas canvas) { 299 super.dispatchDraw(canvas); 300 301 // Draw the dim layer with the rounded corners 302 canvas.drawRoundRect(0, 0, mTaskViewRect.width(), getHeight() + mCornerRadius, 303 mCornerRadius, mCornerRadius, mDimLayerPaint); 304 } 305 306 /** Starts the focus timer. */ 307 public void startFocusTimerIndicator() { 308 if (mFocusTimerIndicator == null) { 309 return; 310 } 311 312 mFocusTimerIndicator.setVisibility(View.VISIBLE); 313 mFocusTimerIndicator.setMax((int) mFocusTimerDuration); 314 if (mFocusTimerCountDown == null) { 315 mFocusTimerCountDown = new CountDownTimer(mFocusTimerDuration, 316 FOCUS_INDICATOR_INTERVAL_MS) { 317 public void onTick(long millisUntilFinished) { 318 mFocusTimerIndicator.setProgress((int) millisUntilFinished); 319 } 320 321 public void onFinish() { 322 mFocusTimerIndicator.setProgress((int) mFocusTimerDuration); 323 } 324 }.start(); 325 } else { 326 mFocusTimerCountDown.start(); 327 } 328 } 329 330 /** Cancels the focus timer. */ 331 public void cancelFocusTimerIndicator() { 332 if (mFocusTimerIndicator == null) { 333 return; 334 } 335 336 if (mFocusTimerCountDown != null) { 337 mFocusTimerCountDown.cancel(); 338 mFocusTimerIndicator.setProgress(0); 339 mFocusTimerIndicator.setVisibility(View.INVISIBLE); 340 } 341 } 342 343 /** Returns the secondary color for a primary color. */ 344 int getSecondaryColor(int primaryColor, boolean useLightOverlayColor) { 345 int overlayColor = useLightOverlayColor ? Color.WHITE : Color.BLACK; 346 return Utilities.getColorWithOverlay(primaryColor, overlayColor, 0.8f); 347 } 348 349 /** 350 * Sets the dim alpha, only used when we are not using hardware layers. 351 * (see RecentsConfiguration.useHardwareLayers) 352 */ 353 void setDimAlpha(float dimAlpha) { 354 mDimAlpha = dimAlpha; 355 updateBackgroundColor(dimAlpha); 356 invalidate(); 357 } 358 359 /** 360 * Updates the background and highlight colors for this header. 361 */ 362 private void updateBackgroundColor(float dimAlpha) { 363 if (mTask != null) { 364 mBackground.setColorAndDim(mTask.colorPrimary, dimAlpha); 365 // TODO: Consider using the saturation of the color to adjust the lightness as well 366 ColorUtils.colorToHSL(mTask.colorPrimary, mTmpHSL); 367 mTmpHSL[2] = Math.min(1f, mTmpHSL[2] + OVERLAY_LIGHTNESS_INCREMENT * (1.0f - dimAlpha)); 368 mOverlayBackground.setColorAndDim(ColorUtils.HSLToColor(mTmpHSL), dimAlpha); 369 mDimLayerPaint.setAlpha((int) (dimAlpha * 255)); 370 } 371 } 372 373 /** Binds the bar view to the task */ 374 public void rebindToTask(Task t) { 375 SystemServicesProxy ssp = Recents.getSystemServices(); 376 mTask = t; 377 378 // If an activity icon is defined, then we use that as the primary icon to show in the bar, 379 // otherwise, we fall back to the application icon 380 updateBackgroundColor(mDimAlpha); 381 if (t.icon != null) { 382 mIconView.setImageDrawable(t.icon); 383 } 384 if (!mTitleView.getText().toString().equals(t.title)) { 385 mTitleView.setText(t.title); 386 } 387 mTitleView.setContentDescription(t.contentDescription); 388 mTitleView.setTextColor(t.useLightOnPrimaryColor ? 389 mTaskBarViewLightTextColor : mTaskBarViewDarkTextColor); 390 mDismissButton.setImageDrawable(t.useLightOnPrimaryColor ? 391 mLightDismissDrawable : mDarkDismissDrawable); 392 mDismissButton.setContentDescription(t.dismissDescription); 393 394 // When freeform workspaces are enabled, then update the move-task button depending on the 395 // current task 396 if (ssp.hasFreeformWorkspaceSupport()) { 397 if (t.isFreeformTask()) { 398 mMoveTaskTargetStackId = FULLSCREEN_WORKSPACE_STACK_ID; 399 mMoveTaskButton.setImageDrawable(t.useLightOnPrimaryColor 400 ? mLightFullscreenIcon 401 : mDarkFullscreenIcon); 402 } else { 403 mMoveTaskTargetStackId = FREEFORM_WORKSPACE_STACK_ID; 404 mMoveTaskButton.setImageDrawable(t.useLightOnPrimaryColor 405 ? mLightFreeformIcon 406 : mDarkFreeformIcon); 407 } 408 if (mMoveTaskButton.getVisibility() != View.VISIBLE) { 409 mMoveTaskButton.setVisibility(View.VISIBLE); 410 } 411 mMoveTaskButton.setOnClickListener(this); 412 } 413 414 if (Recents.getDebugFlags().isFastToggleIndicatorEnabled()) { 415 if (mFocusTimerIndicator == null) { 416 mFocusTimerIndicator = (ProgressBar) mFocusTimerIndicatorStub.inflate(); 417 } 418 mFocusTimerIndicator.getProgressDrawable() 419 .setColorFilter( 420 getSecondaryColor(t.colorPrimary, t.useLightOnPrimaryColor), 421 PorterDuff.Mode.SRC_IN); 422 } 423 424 // In accessibility, a single click on the focused app info button will show it 425 if (ssp.isTouchExplorationEnabled()) { 426 mIconView.setOnClickListener(this); 427 } 428 } 429 430 /** Unbinds the bar view from the task */ 431 void unbindFromTask() { 432 mTask = null; 433 mIconView.setImageDrawable(null); 434 mIconView.setOnClickListener(null); 435 mMoveTaskButton.setOnClickListener(null); 436 } 437 438 /** Animates this task bar if the user does not interact with the stack after a certain time. */ 439 void startNoUserInteractionAnimation() { 440 if (mDismissButton.getVisibility() != View.VISIBLE) { 441 mDismissButton.setVisibility(View.VISIBLE); 442 mDismissButton.setAlpha(0f); 443 mDismissButton.animate() 444 .alpha(1f) 445 .setStartDelay(0) 446 .setInterpolator(mFastOutLinearInInterpolator) 447 .setDuration(getResources().getInteger( 448 R.integer.recents_task_enter_from_app_duration)) 449 .start(); 450 } 451 } 452 453 /** 454 * Mark this task view that the user does has not interacted with the stack after a certain 455 * time. 456 */ 457 void setNoUserInteractionState() { 458 if (mDismissButton.getVisibility() != View.VISIBLE) { 459 mDismissButton.animate().cancel(); 460 mDismissButton.setVisibility(View.VISIBLE); 461 mDismissButton.setAlpha(1f); 462 } 463 } 464 465 /** 466 * Resets the state tracking that the user has not interacted with the stack after a certain 467 * time. 468 */ 469 void resetNoUserInteractionState() { 470 mDismissButton.setVisibility(View.INVISIBLE); 471 } 472 473 @Override 474 protected int[] onCreateDrawableState(int extraSpace) { 475 476 // Don't forward our state to the drawable - we do it manually in onTaskViewFocusChanged. 477 // This is to prevent layer trashing when the view is pressed. 478 return new int[] {}; 479 } 480 481 @Override 482 public void onClick(View v) { 483 if (v == mIconView) { 484 SystemServicesProxy ssp = Recents.getSystemServices(); 485 if (ssp.isTouchExplorationEnabled()) { 486 // In accessibility, a single click on the focused app info button will show it 487 EventBus.getDefault().send(new ShowApplicationInfoEvent(mTask)); 488 } 489 } else if (v == mDismissButton) { 490 TaskView tv = Utilities.findParent(this, TaskView.class); 491 tv.dismissTask(); 492 493 // Keep track of deletions by the dismiss button 494 MetricsLogger.histogram(getContext(), "overview_task_dismissed_source", 495 Constants.Metrics.DismissSourceHeaderButton); 496 } else if (v == mMoveTaskButton) { 497 TaskView tv = Utilities.findParent(this, TaskView.class); 498 Rect bounds = mMoveTaskTargetStackId == FREEFORM_WORKSPACE_STACK_ID 499 ? new Rect(mTaskViewRect) 500 : new Rect(); 501 EventBus.getDefault().send(new LaunchTaskEvent(tv, mTask, bounds, 502 mMoveTaskTargetStackId, false)); 503 } else if (v == mAppInfoView) { 504 EventBus.getDefault().send(new ShowApplicationInfoEvent(mTask)); 505 } else if (v == mAppIconView) { 506 hideAppOverlay(false /* immediate */); 507 } 508 } 509 510 @Override 511 public boolean onLongClick(View v) { 512 if (v == mIconView) { 513 showAppOverlay(); 514 return true; 515 } else if (v == mAppIconView) { 516 hideAppOverlay(false /* immediate */); 517 return true; 518 } 519 return false; 520 } 521 522 /** 523 * Shows the application overlay. 524 */ 525 private void showAppOverlay() { 526 // Skip early if the task is invalid 527 SystemServicesProxy ssp = Recents.getSystemServices(); 528 ComponentName cn = mTask.key.getComponent(); 529 int userId = mTask.key.userId; 530 ActivityInfo activityInfo = ssp.getActivityInfo(cn, userId); 531 if (activityInfo == null) { 532 return; 533 } 534 535 // Inflate the overlay if necessary 536 if (mAppOverlayView == null) { 537 mAppOverlayView = (FrameLayout) mAppOverlayViewStub.inflate(); 538 mAppOverlayView.setBackground(mOverlayBackground); 539 mAppIconView = (ImageView) mAppOverlayView.findViewById(R.id.app_icon); 540 mAppIconView.setOnClickListener(this); 541 mAppIconView.setOnLongClickListener(this); 542 mAppInfoView = (ImageView) mAppOverlayView.findViewById(R.id.app_info); 543 mAppInfoView.setOnClickListener(this); 544 mAppTitleView = (TextView) mAppOverlayView.findViewById(R.id.app_title); 545 } 546 547 // Update the overlay contents for the current app 548 mAppTitleView.setText(ssp.getBadgedApplicationLabel(activityInfo.applicationInfo, userId)); 549 mAppTitleView.setTextColor(mTask.useLightOnPrimaryColor ? 550 mTaskBarViewLightTextColor : mTaskBarViewDarkTextColor); 551 mAppIconView.setImageDrawable(ssp.getBadgedApplicationIcon(activityInfo.applicationInfo, 552 userId)); 553 mAppInfoView.setImageDrawable(mTask.useLightOnPrimaryColor 554 ? mLightInfoIcon 555 : mDarkInfoIcon); 556 mAppOverlayView.setVisibility(View.VISIBLE); 557 558 int x = mIconView.getLeft() + mIconView.getWidth() / 2; 559 int y = mIconView.getTop() + mIconView.getHeight() / 2; 560 Animator revealAnim = ViewAnimationUtils.createCircularReveal(mAppOverlayView, x, y, 0, 561 getWidth()); 562 revealAnim.setDuration(OVERLAY_REVEAL_DURATION); 563 revealAnim.setInterpolator(mLinearOutSlowInInterpolator); 564 revealAnim.start(); 565 } 566 567 /** 568 * Hide the application overlay. 569 */ 570 private void hideAppOverlay(boolean immediate) { 571 // Skip if we haven't even loaded the overlay yet 572 if (mAppOverlayView == null) { 573 return; 574 } 575 576 if (immediate) { 577 mAppOverlayView.setVisibility(View.GONE); 578 } else { 579 int x = mIconView.getLeft() + mIconView.getWidth() / 2; 580 int y = mIconView.getTop() + mIconView.getHeight() / 2; 581 Animator revealAnim = ViewAnimationUtils.createCircularReveal(mAppOverlayView, x, y, 582 getWidth(), 0); 583 revealAnim.setDuration(OVERLAY_REVEAL_DURATION); 584 revealAnim.setInterpolator(mLinearOutSlowInInterpolator); 585 revealAnim.addListener(new AnimatorListenerAdapter() { 586 @Override 587 public void onAnimationEnd(Animator animation) { 588 mAppOverlayView.setVisibility(View.GONE); 589 } 590 }); 591 revealAnim.start(); 592 } 593 } 594} 595