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