[go: nahoru, domu]

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