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