blob: a2f61c267f0304a0b1b54b257e2ebbaf44254a5a [file] [log] [blame]
Winson Chung37c8d8e2014-03-24 14:53:07 -07001/*
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 *
Peter Schillerb124d562015-12-11 21:31:17 -08008 * http://www.apache.org/licenses/LICENSE-2.0
Winson Chung37c8d8e2014-03-24 14:53:07 -07009 *
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
Winsonc5ef63f2016-01-21 14:39:23 -080019import android.animation.Animator;
20import android.animation.AnimatorListenerAdapter;
Winson94a0e892016-01-08 16:06:50 -080021import android.annotation.Nullable;
Winsonc5ef63f2016-01-21 14:39:23 -080022import android.content.ComponentName;
Winson Chung37c8d8e2014-03-24 14:53:07 -070023import android.content.Context;
Winsonc5ef63f2016-01-21 14:39:23 -080024import android.content.pm.ActivityInfo;
Winson94a0e892016-01-08 16:06:50 -080025import android.content.res.Resources;
Winson Chungcd23c842014-05-29 18:30:51 -070026import android.graphics.Canvas;
Winson Chunga0e88b52014-08-11 19:25:42 -070027import android.graphics.Color;
Winson94a0e892016-01-08 16:06:50 -080028import android.graphics.ColorFilter;
Winson Chungcd23c842014-05-29 18:30:51 -070029import android.graphics.Paint;
Winson94a0e892016-01-08 16:06:50 -080030import android.graphics.PixelFormat;
Winsonc5ef63f2016-01-21 14:39:23 -080031import android.graphics.PorterDuff;
Skuhne8aa7d162015-03-20 13:40:53 -070032import android.graphics.Rect;
Winson35f30502015-09-28 11:24:36 -070033import android.graphics.drawable.Drawable;
Peter Schillerb124d562015-12-11 21:31:17 -080034import android.os.CountDownTimer;
Winsonc5ef63f2016-01-21 14:39:23 -080035import android.support.v4.graphics.ColorUtils;
Winson Chung37c8d8e2014-03-24 14:53:07 -070036import android.util.AttributeSet;
Winson21700932016-03-24 17:26:23 -070037import android.view.Gravity;
Winson Chungd42a6cf2014-06-03 16:24:04 -070038import android.view.View;
Winsonc5ef63f2016-01-21 14:39:23 -080039import android.view.ViewAnimationUtils;
Winson231bc9c2016-02-09 12:31:00 -080040import android.view.ViewDebug;
Winson21700932016-03-24 17:26:23 -070041import android.view.ViewGroup;
Winsonc5ef63f2016-01-21 14:39:23 -080042import android.view.ViewStub;
Winson Chung37c8d8e2014-03-24 14:53:07 -070043import android.widget.FrameLayout;
44import android.widget.ImageView;
Peter Schillerb124d562015-12-11 21:31:17 -080045import android.widget.ProgressBar;
Winson Chung37c8d8e2014-03-24 14:53:07 -070046import android.widget.TextView;
Winsonc0d70582016-01-29 10:24:39 -080047
Winson2536c7e2015-10-01 15:49:31 -070048import com.android.internal.logging.MetricsLogger;
Winsonc0d70582016-01-29 10:24:39 -080049import com.android.systemui.Interpolators;
Winson Chung37c8d8e2014-03-24 14:53:07 -070050import com.android.systemui.R;
Winson Chungf5e22e72014-05-02 18:35:35 -070051import com.android.systemui.recents.Constants;
Winsone7f138c2015-10-22 16:15:21 -070052import com.android.systemui.recents.Recents;
Winson2536c7e2015-10-01 15:49:31 -070053import com.android.systemui.recents.events.EventBus;
Winson Chung48f2cda2015-12-11 13:20:12 -050054import com.android.systemui.recents.events.activity.LaunchTaskEvent;
Winson2536c7e2015-10-01 15:49:31 -070055import com.android.systemui.recents.events.ui.ShowApplicationInfoEvent;
Skuhne8aa7d162015-03-20 13:40:53 -070056import com.android.systemui.recents.misc.SystemServicesProxy;
Winson Chunga0e88b52014-08-11 19:25:42 -070057import com.android.systemui.recents.misc.Utilities;
Winson Chung37c8d8e2014-03-24 14:53:07 -070058import com.android.systemui.recents.model.Task;
59
Winson Chung48f2cda2015-12-11 13:20:12 -050060import 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
Winson Chung37c8d8e2014-03-24 14:53:07 -070064/* The task bar view */
Winson2536c7e2015-10-01 15:49:31 -070065public class TaskViewHeader extends FrameLayout
66 implements View.OnClickListener, View.OnLongClickListener {
Winson Chungd42a6cf2014-06-03 16:24:04 -070067
Winson12858a62016-02-24 11:06:01 -080068 private static final float HIGHLIGHT_LIGHTNESS_INCREMENT = 0.075f;
Winsonc5ef63f2016-01-21 14:39:23 -080069 private static final float OVERLAY_LIGHTNESS_INCREMENT = -0.0625f;
70 private static final int OVERLAY_REVEAL_DURATION = 250;
Peter Schillerb124d562015-12-11 21:31:17 -080071 private static final long FOCUS_INDICATOR_INTERVAL_MS = 30;
Winson94a0e892016-01-08 16:06:50 -080072
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();
Winson8be16342016-02-09 11:53:27 -080080 private int mColor;
81 private float mDimAlpha;
Winson94a0e892016-01-08 16:06:50 -080082
Winson94a0e892016-01-08 16:06:50 -080083 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) {
Winson8be16342016-02-09 11:53:27 -080091 if (mColor != color || Float.compare(mDimAlpha, dimAlpha) != 0) {
92 mColor = color;
93 mDimAlpha = dimAlpha;
94 mBackgroundPaint.setColor(color);
Winson94a0e892016-01-08 16:06:50 -080095
Winson8be16342016-02-09 11:53:27 -080096 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));
Winson94a0e892016-01-08 16:06:50 -0800101
Winson8be16342016-02-09 11:53:27 -0800102 invalidateSelf();
103 }
Winson94a0e892016-01-08 16:06:50 -0800104 }
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 }
Winson8be16342016-02-09 11:53:27 -0800133
134 public int getColor() {
135 return mColor;
136 }
Winson94a0e892016-01-08 16:06:50 -0800137 }
138
Winson2536c7e2015-10-01 15:49:31 -0700139 Task mTask;
Winson Chungd42a6cf2014-06-03 16:24:04 -0700140
Winson Chung353c0b92014-10-16 17:43:23 -0700141 // Header views
Winson Chung296278a2015-12-17 12:09:02 -0500142 ImageView mIconView;
143 TextView mTitleView;
Winson931845f2016-02-24 19:38:41 -0800144 TextView mSubTitleView;
Winsonc5ef63f2016-01-21 14:39:23 -0800145 ImageView mMoveTaskButton;
146 ImageView mDismissButton;
147 ViewStub mAppOverlayViewStub;
148 FrameLayout mAppOverlayView;
149 ImageView mAppIconView;
150 ImageView mAppInfoView;
151 TextView mAppTitleView;
152 ViewStub mFocusTimerIndicatorStub;
Peter Schillerb124d562015-12-11 21:31:17 -0800153 ProgressBar mFocusTimerIndicator;
Winson Chung37c8d8e2014-03-24 14:53:07 -0700154
Winson Chung353c0b92014-10-16 17:43:23 -0700155 // Header drawables
Winson231bc9c2016-02-09 12:31:00 -0800156 @ViewDebug.ExportedProperty(category="recents")
Winsonde0591a2015-12-04 17:24:35 -0800157 Rect mTaskViewRect = new Rect();
Winson21700932016-03-24 17:26:23 -0700158 int mHeaderBarHeight;
159 int mHeaderButtonPadding;
Winson35f30502015-09-28 11:24:36 -0700160 int mCornerRadius;
161 int mHighlightHeight;
Winson231bc9c2016-02-09 12:31:00 -0800162 @ViewDebug.ExportedProperty(category="recents")
Winson94a0e892016-01-08 16:06:50 -0800163 float mDimAlpha;
Winson Chung54e297a2014-05-09 17:15:32 -0700164 Drawable mLightDismissDrawable;
165 Drawable mDarkDismissDrawable;
Winson3e874742016-01-07 10:08:17 -0800166 Drawable mLightFreeformIcon;
167 Drawable mDarkFreeformIcon;
168 Drawable mLightFullscreenIcon;
169 Drawable mDarkFullscreenIcon;
Winsonc5ef63f2016-01-21 14:39:23 -0800170 Drawable mLightInfoIcon;
171 Drawable mDarkInfoIcon;
Winson94a0e892016-01-08 16:06:50 -0800172 int mTaskBarViewLightTextColor;
173 int mTaskBarViewDarkTextColor;
Winson8be16342016-02-09 11:53:27 -0800174 int mDisabledTaskBarBackgroundColor;
Winsonc5ef63f2016-01-21 14:39:23 -0800175 int mMoveTaskTargetStackId = INVALID_STACK_ID;
Winson Chung54e297a2014-05-09 17:15:32 -0700176
Winson94a0e892016-01-08 16:06:50 -0800177 // Header background
178 private HighlightColorDrawable mBackground;
Winsonc5ef63f2016-01-21 14:39:23 -0800179 private HighlightColorDrawable mOverlayBackground;
180 private float[] mTmpHSL = new float[3];
Winson Chung353c0b92014-10-16 17:43:23 -0700181
182 // Header dim, which is only used when task view hardware layers are not used
Winson94a0e892016-01-08 16:06:50 -0800183 private Paint mDimLayerPaint = new Paint();
Winson Chungcd23c842014-05-29 18:30:51 -0700184
Peter Schillerb124d562015-12-11 21:31:17 -0800185 private CountDownTimer mFocusTimerCountDown;
Peter Schillerb124d562015-12-11 21:31:17 -0800186
Winson Chungec396d62014-08-06 17:08:00 -0700187 public TaskViewHeader(Context context) {
Winson Chung37c8d8e2014-03-24 14:53:07 -0700188 this(context, null);
189 }
190
Winson Chungec396d62014-08-06 17:08:00 -0700191 public TaskViewHeader(Context context, AttributeSet attrs) {
Winson Chung37c8d8e2014-03-24 14:53:07 -0700192 this(context, attrs, 0);
193 }
194
Winson Chungec396d62014-08-06 17:08:00 -0700195 public TaskViewHeader(Context context, AttributeSet attrs, int defStyleAttr) {
Winson Chung37c8d8e2014-03-24 14:53:07 -0700196 this(context, attrs, defStyleAttr, 0);
197 }
198
Winson Chungec396d62014-08-06 17:08:00 -0700199 public TaskViewHeader(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
Winson Chung37c8d8e2014-03-24 14:53:07 -0700200 super(context, attrs, defStyleAttr, defStyleRes);
Winson Chungcd23c842014-05-29 18:30:51 -0700201 setWillNotDraw(false);
202
203 // Load the dismiss resources
Winson94a0e892016-01-08 16:06:50 -0800204 Resources res = context.getResources();
Winson Chung911f4002015-02-13 13:49:36 -0800205 mLightDismissDrawable = context.getDrawable(R.drawable.recents_dismiss_light);
206 mDarkDismissDrawable = context.getDrawable(R.drawable.recents_dismiss_dark);
Winson94a0e892016-01-08 16:06:50 -0800207 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);
Winson3e874742016-01-07 10:08:17 -0800211 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);
Winsonc5ef63f2016-01-21 14:39:23 -0800215 mLightInfoIcon = context.getDrawable(R.drawable.recents_info_light);
216 mDarkInfoIcon = context.getDrawable(R.drawable.recents_info_dark);
Winson8be16342016-02-09 11:53:27 -0800217 mDisabledTaskBarBackgroundColor =
218 context.getColor(R.color.recents_task_bar_disabled_background_color);
Winson3e874742016-01-07 10:08:17 -0800219
Winson94a0e892016-01-08 16:06:50 -0800220 // Configure the background and dim
221 mBackground = new HighlightColorDrawable();
222 mBackground.setColorAndDim(Color.argb(255, 0, 0, 0), 0f);
223 setBackground(mBackground);
Winsonc5ef63f2016-01-21 14:39:23 -0800224 mOverlayBackground = new HighlightColorDrawable();
Winson94a0e892016-01-08 16:06:50 -0800225 mDimLayerPaint.setColor(Color.argb(255, 0, 0, 0));
226 mDimLayerPaint.setAntiAlias(true);
Winson Chung37c8d8e2014-03-24 14:53:07 -0700227 }
228
Winsonc5ef63f2016-01-21 14:39:23 -0800229 /**
230 * Resets this header along with the TaskView.
231 */
232 public void reset() {
233 hideAppOverlay(true /* immediate */);
234 }
235
Winson Chung37c8d8e2014-03-24 14:53:07 -0700236 @Override
237 protected void onFinishInflate() {
Winson Chungbbb3d3d2016-01-30 01:09:20 +0000238 SystemServicesProxy ssp = Recents.getSystemServices();
239
Winson Chung37c8d8e2014-03-24 14:53:07 -0700240 // Initialize the icon and description views
Winson Chung296278a2015-12-17 12:09:02 -0500241 mIconView = (ImageView) findViewById(R.id.icon);
Winsonc5ef63f2016-01-21 14:39:23 -0800242 mIconView.setClickable(false);
Winson Chung296278a2015-12-17 12:09:02 -0500243 mIconView.setOnLongClickListener(this);
244 mTitleView = (TextView) findViewById(R.id.title);
Winson931845f2016-02-24 19:38:41 -0800245 mSubTitleView = (TextView) findViewById(R.id.sub_title);
Winson Chung54e297a2014-05-09 17:15:32 -0700246 mDismissButton = (ImageView) findViewById(R.id.dismiss_task);
Winson Chungbbb3d3d2016-01-30 01:09:20 +0000247 if (ssp.hasFreeformWorkspaceSupport()) {
248 mMoveTaskButton = (ImageView) findViewById(R.id.move_task);
249 }
Winsonc5ef63f2016-01-21 14:39:23 -0800250 mFocusTimerIndicatorStub = (ViewStub) findViewById(R.id.focus_timer_indicator_stub);
251 mAppOverlayViewStub = (ViewStub) findViewById(R.id.app_overlay_stub);
Winson21700932016-03-24 17:26:23 -0700252
Winson619e40c2016-03-25 16:12:35 -0700253 onConfigurationChanged();
Winson21700932016-03-24 17:26:23 -0700254 }
255
256 /**
257 * Programmatically sets the layout params for a header bar layout. This is necessary because
258 * we can't get resources based on the current configuration, but instead need to get them
259 * based on the device configuration.
260 */
261 private void updateLayoutParams(View icon, View title, View secondaryButton, View button) {
262 FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(
263 ViewGroup.LayoutParams.MATCH_PARENT, mHeaderBarHeight, Gravity.TOP);
264 setLayoutParams(lp);
265 lp = new FrameLayout.LayoutParams(mHeaderBarHeight, mHeaderBarHeight, Gravity.START);
266 icon.setLayoutParams(lp);
267 lp = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
268 ViewGroup.LayoutParams.WRAP_CONTENT, Gravity.START | Gravity.CENTER_VERTICAL);
Winsona1ededd2016-03-25 12:23:12 -0700269 lp.setMarginStart(mHeaderBarHeight);
Winson19852372016-03-30 18:26:49 -0700270 lp.setMarginEnd(mMoveTaskButton != null
Winson21700932016-03-24 17:26:23 -0700271 ? 2 * mHeaderBarHeight
Winson19852372016-03-30 18:26:49 -0700272 : mHeaderBarHeight);
Winson21700932016-03-24 17:26:23 -0700273 title.setLayoutParams(lp);
274 if (secondaryButton != null) {
275 lp = new FrameLayout.LayoutParams(mHeaderBarHeight, mHeaderBarHeight, Gravity.END);
Winsona1ededd2016-03-25 12:23:12 -0700276 lp.setMarginEnd(mHeaderBarHeight);
Winson21700932016-03-24 17:26:23 -0700277 secondaryButton.setLayoutParams(lp);
278 secondaryButton.setPadding(mHeaderButtonPadding, mHeaderButtonPadding,
279 mHeaderButtonPadding, mHeaderButtonPadding);
280 }
281 lp = new FrameLayout.LayoutParams(mHeaderBarHeight, mHeaderBarHeight, Gravity.END);
282 button.setLayoutParams(lp);
283 button.setPadding(mHeaderButtonPadding, mHeaderButtonPadding, mHeaderButtonPadding,
284 mHeaderButtonPadding);
Winson Chung54e297a2014-05-09 17:15:32 -0700285 }
286
Winson619e40c2016-03-25 16:12:35 -0700287 /**
288 * Update the header view when the configuration changes.
289 */
290 void onConfigurationChanged() {
291 // Update the dimensions of everything in the header. We do this because we need to use
292 // resources for the display, and not the current configuration.
293 Resources res = getResources();
294 mHeaderBarHeight = TaskStackLayoutAlgorithm.getDimensionForDevice(res,
295 R.dimen.recents_task_view_header_height,
296 R.dimen.recents_task_view_header_height,
297 R.dimen.recents_task_view_header_height,
298 R.dimen.recents_task_view_header_height_tablet_land,
299 R.dimen.recents_task_view_header_height,
300 R.dimen.recents_task_view_header_height_tablet_land);
301 mHeaderButtonPadding = TaskStackLayoutAlgorithm.getDimensionForDevice(res,
302 R.dimen.recents_task_view_header_button_padding,
303 R.dimen.recents_task_view_header_button_padding,
304 R.dimen.recents_task_view_header_button_padding,
305 R.dimen.recents_task_view_header_button_padding_tablet_land,
306 R.dimen.recents_task_view_header_button_padding,
307 R.dimen.recents_task_view_header_button_padding_tablet_land);
308 updateLayoutParams(mIconView, findViewById(R.id.title_container), mMoveTaskButton,
309 mDismissButton);
310 if (mAppOverlayView != null) {
311 updateLayoutParams(mAppIconView, mAppTitleView, null, mAppInfoView);
312 }
313 }
314
Winson Chungbbb3d3d2016-01-30 01:09:20 +0000315 @Override
316 protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
317 super.onLayout(changed, left, top, right, bottom);
318
319 // Since we update the position of children based on the width of the parent and this view
320 // recompute these changes with the new view size
321 onTaskViewSizeChanged(mTaskViewRect.width(), mTaskViewRect.height());
322 }
323
Winsonde0591a2015-12-04 17:24:35 -0800324 /**
325 * Called when the task view frame changes, allowing us to move the contents of the header
326 * to match the frame changes.
327 */
328 public void onTaskViewSizeChanged(int width, int height) {
329 mTaskViewRect.set(0, 0, width, height);
Winson Chung48f2cda2015-12-11 13:20:12 -0500330
Winson Chungbbb3d3d2016-01-30 01:09:20 +0000331 boolean showTitle = true;
332 boolean showMoveIcon = true;
333 boolean showDismissIcon = true;
334 int rightInset = width - getMeasuredWidth();
335
336 if (mTask != null && mTask.isFreeformTask()) {
337 // For freeform tasks, we always show the app icon, and only show the title, move-task
338 // icon, and the dismiss icon if there is room
339 int appIconWidth = mIconView.getMeasuredWidth();
340 int titleWidth = (int) mTitleView.getPaint().measureText(mTask.title);
341 int dismissWidth = mDismissButton.getMeasuredWidth();
342 int moveTaskWidth = mMoveTaskButton != null
343 ? mMoveTaskButton.getMeasuredWidth()
344 : 0;
345 showTitle = width >= (appIconWidth + dismissWidth + moveTaskWidth + titleWidth);
346 showMoveIcon = width >= (appIconWidth + dismissWidth + moveTaskWidth);
347 showDismissIcon = width >= (appIconWidth + dismissWidth);
Winsonde0591a2015-12-04 17:24:35 -0800348 }
Winson Chungbbb3d3d2016-01-30 01:09:20 +0000349
350 mTitleView.setVisibility(showTitle ? View.VISIBLE : View.INVISIBLE);
351 if (mMoveTaskButton != null) {
352 mMoveTaskButton.setVisibility(showMoveIcon ? View.VISIBLE : View.INVISIBLE);
353 mMoveTaskButton.setTranslationX(rightInset);
Winson Chung48f2cda2015-12-11 13:20:12 -0500354 }
Winson Chungbbb3d3d2016-01-30 01:09:20 +0000355 mDismissButton.setVisibility(showDismissIcon ? View.VISIBLE : View.INVISIBLE);
356 mDismissButton.setTranslationX(rightInset);
Winsonde0591a2015-12-04 17:24:35 -0800357 }
358
Winson Chungcd23c842014-05-29 18:30:51 -0700359 @Override
Winsonbe8e6962016-02-01 14:27:52 -0800360 public void onDrawForeground(Canvas canvas) {
361 super.onDrawForeground(canvas);
Winson94a0e892016-01-08 16:06:50 -0800362
363 // Draw the dim layer with the rounded corners
364 canvas.drawRoundRect(0, 0, mTaskViewRect.width(), getHeight() + mCornerRadius,
365 mCornerRadius, mCornerRadius, mDimLayerPaint);
Winson Chungcd23c842014-05-29 18:30:51 -0700366 }
367
Peter Schillerb124d562015-12-11 21:31:17 -0800368 /** Starts the focus timer. */
Winson4b9cded2016-01-26 16:26:47 -0800369 public void startFocusTimerIndicator(int duration) {
Winsonc5ef63f2016-01-21 14:39:23 -0800370 if (mFocusTimerIndicator == null) {
371 return;
372 }
373
Peter Schillerb124d562015-12-11 21:31:17 -0800374 mFocusTimerIndicator.setVisibility(View.VISIBLE);
Winson4b9cded2016-01-26 16:26:47 -0800375 mFocusTimerIndicator.setMax(duration);
376 mFocusTimerIndicator.setProgress(duration);
377 if (mFocusTimerCountDown != null) {
378 mFocusTimerCountDown.cancel();
Peter Schillerb124d562015-12-11 21:31:17 -0800379 }
Winson4b9cded2016-01-26 16:26:47 -0800380 mFocusTimerCountDown = new CountDownTimer(duration,
381 FOCUS_INDICATOR_INTERVAL_MS) {
382 public void onTick(long millisUntilFinished) {
383 mFocusTimerIndicator.setProgress((int) millisUntilFinished);
384 }
385
386 public void onFinish() {
387 // Do nothing
388 }
389 }.start();
Peter Schillerb124d562015-12-11 21:31:17 -0800390 }
391
392 /** Cancels the focus timer. */
393 public void cancelFocusTimerIndicator() {
Winsonc5ef63f2016-01-21 14:39:23 -0800394 if (mFocusTimerIndicator == null) {
395 return;
396 }
397
398 if (mFocusTimerCountDown != null) {
Peter Schillerb124d562015-12-11 21:31:17 -0800399 mFocusTimerCountDown.cancel();
400 mFocusTimerIndicator.setProgress(0);
401 mFocusTimerIndicator.setVisibility(View.INVISIBLE);
402 }
403 }
404
Winson22574af2016-03-23 19:00:28 -0700405 /** Only exposed for the workaround for b/27815919. */
406 public ImageView getIconView() {
407 return mIconView;
408 }
409
Peter Schillerb124d562015-12-11 21:31:17 -0800410 /** Returns the secondary color for a primary color. */
411 int getSecondaryColor(int primaryColor, boolean useLightOverlayColor) {
412 int overlayColor = useLightOverlayColor ? Color.WHITE : Color.BLACK;
413 return Utilities.getColorWithOverlay(primaryColor, overlayColor, 0.8f);
414 }
415
Winson Chung353c0b92014-10-16 17:43:23 -0700416 /**
417 * Sets the dim alpha, only used when we are not using hardware layers.
418 * (see RecentsConfiguration.useHardwareLayers)
419 */
Winsone693aaf2016-03-01 12:05:59 -0800420 public void setDimAlpha(float dimAlpha) {
421 if (Float.compare(mDimAlpha, dimAlpha) != 0) {
422 mDimAlpha = dimAlpha;
Winson59924fe2016-03-17 14:13:18 -0700423 mTitleView.setAlpha(1f - dimAlpha);
Winsone693aaf2016-03-01 12:05:59 -0800424 updateBackgroundColor(mBackground.getColor(), dimAlpha);
425 }
Winson Chung353c0b92014-10-16 17:43:23 -0700426 }
427
Winson94a0e892016-01-08 16:06:50 -0800428 /**
429 * Updates the background and highlight colors for this header.
430 */
Winson8be16342016-02-09 11:53:27 -0800431 private void updateBackgroundColor(int color, float dimAlpha) {
Winson94a0e892016-01-08 16:06:50 -0800432 if (mTask != null) {
Winson8be16342016-02-09 11:53:27 -0800433 mBackground.setColorAndDim(color, dimAlpha);
Winsonc5ef63f2016-01-21 14:39:23 -0800434 // TODO: Consider using the saturation of the color to adjust the lightness as well
Winson8be16342016-02-09 11:53:27 -0800435 ColorUtils.colorToHSL(color, mTmpHSL);
Winsonc5ef63f2016-01-21 14:39:23 -0800436 mTmpHSL[2] = Math.min(1f, mTmpHSL[2] + OVERLAY_LIGHTNESS_INCREMENT * (1.0f - dimAlpha));
437 mOverlayBackground.setColorAndDim(ColorUtils.HSLToColor(mTmpHSL), dimAlpha);
Winson94a0e892016-01-08 16:06:50 -0800438 mDimLayerPaint.setAlpha((int) (dimAlpha * 255));
Winson Chungbbb3d3d2016-01-30 01:09:20 +0000439 invalidate();
Winson94a0e892016-01-08 16:06:50 -0800440 }
441 }
442
Winson Chung37c8d8e2014-03-24 14:53:07 -0700443 /** Binds the bar view to the task */
Winson8be16342016-02-09 11:53:27 -0800444 public void rebindToTask(Task t, boolean touchExplorationEnabled, boolean disabledInSafeMode) {
Winson931845f2016-02-24 19:38:41 -0800445 SystemServicesProxy ssp = Recents.getSystemServices();
Winson2536c7e2015-10-01 15:49:31 -0700446 mTask = t;
447
Winson Chung11e41ba2014-04-21 12:39:20 -0700448 // If an activity icon is defined, then we use that as the primary icon to show in the bar,
449 // otherwise, we fall back to the application icon
Winson8be16342016-02-09 11:53:27 -0800450 int primaryColor = disabledInSafeMode
451 ? mDisabledTaskBarBackgroundColor
452 : t.colorPrimary;
Winsone693aaf2016-03-01 12:05:59 -0800453 if (mBackground.getColor() != primaryColor) {
454 updateBackgroundColor(primaryColor, mDimAlpha);
455 }
Winson Chung296278a2015-12-17 12:09:02 -0500456 if (t.icon != null) {
457 mIconView.setImageDrawable(t.icon);
Winson Chung11e41ba2014-04-21 12:39:20 -0700458 }
Winson Chung296278a2015-12-17 12:09:02 -0500459 if (!mTitleView.getText().toString().equals(t.title)) {
460 mTitleView.setText(t.title);
Winson Chungbdbb87d2014-07-09 14:29:13 -0700461 }
Winson Chung296278a2015-12-17 12:09:02 -0500462 mTitleView.setContentDescription(t.contentDescription);
Winson Chung296278a2015-12-17 12:09:02 -0500463 mTitleView.setTextColor(t.useLightOnPrimaryColor ?
Winson94a0e892016-01-08 16:06:50 -0800464 mTaskBarViewLightTextColor : mTaskBarViewDarkTextColor);
Winson931845f2016-02-24 19:38:41 -0800465 if (!t.isDockable && ssp.hasDockedTask()) {
466 mSubTitleView.setVisibility(View.VISIBLE);
467 mSubTitleView.setTextColor(t.useLightOnPrimaryColor ?
468 mTaskBarViewLightTextColor : mTaskBarViewDarkTextColor);
469 } else {
470 mSubTitleView.setVisibility(View.GONE);
471 }
Winson Chung93748a12014-07-13 17:43:31 -0700472 mDismissButton.setImageDrawable(t.useLightOnPrimaryColor ?
473 mLightDismissDrawable : mDarkDismissDrawable);
Winson55003902016-01-12 12:00:37 -0800474 mDismissButton.setContentDescription(t.dismissDescription);
Winson Chung48f2cda2015-12-11 13:20:12 -0500475
476 // When freeform workspaces are enabled, then update the move-task button depending on the
477 // current task
Winson Chungbbb3d3d2016-01-30 01:09:20 +0000478 if (mMoveTaskButton != null) {
Winson Chung48f2cda2015-12-11 13:20:12 -0500479 if (t.isFreeformTask()) {
480 mMoveTaskTargetStackId = FULLSCREEN_WORKSPACE_STACK_ID;
Winson3e874742016-01-07 10:08:17 -0800481 mMoveTaskButton.setImageDrawable(t.useLightOnPrimaryColor
482 ? mLightFullscreenIcon
483 : mDarkFullscreenIcon);
Winson Chung48f2cda2015-12-11 13:20:12 -0500484 } else {
485 mMoveTaskTargetStackId = FREEFORM_WORKSPACE_STACK_ID;
Winson3e874742016-01-07 10:08:17 -0800486 mMoveTaskButton.setImageDrawable(t.useLightOnPrimaryColor
487 ? mLightFreeformIcon
488 : mDarkFreeformIcon);
Winson Chung48f2cda2015-12-11 13:20:12 -0500489 }
Winson Chung48f2cda2015-12-11 13:20:12 -0500490 }
Winson2536c7e2015-10-01 15:49:31 -0700491
Winson4b9cded2016-01-26 16:26:47 -0800492 if (Recents.getDebugFlags().isFastToggleRecentsEnabled()) {
Winsonc5ef63f2016-01-21 14:39:23 -0800493 if (mFocusTimerIndicator == null) {
494 mFocusTimerIndicator = (ProgressBar) mFocusTimerIndicatorStub.inflate();
495 }
496 mFocusTimerIndicator.getProgressDrawable()
497 .setColorFilter(
498 getSecondaryColor(t.colorPrimary, t.useLightOnPrimaryColor),
499 PorterDuff.Mode.SRC_IN);
500 }
Peter Schillerb124d562015-12-11 21:31:17 -0800501
Winson2536c7e2015-10-01 15:49:31 -0700502 // In accessibility, a single click on the focused app info button will show it
Winson Chungbbb3d3d2016-01-30 01:09:20 +0000503 if (touchExplorationEnabled) {
Winson Chung296278a2015-12-17 12:09:02 -0500504 mIconView.setOnClickListener(this);
Winson2536c7e2015-10-01 15:49:31 -0700505 }
506 }
507
508 /** Unbinds the bar view from the task */
Winson Chungbbb3d3d2016-01-30 01:09:20 +0000509 void unbindFromTask(boolean touchExplorationEnabled) {
Winson2536c7e2015-10-01 15:49:31 -0700510 mTask = null;
Winson Chung296278a2015-12-17 12:09:02 -0500511 mIconView.setImageDrawable(null);
Winson Chungbbb3d3d2016-01-30 01:09:20 +0000512 if (touchExplorationEnabled) {
513 mIconView.setOnClickListener(null);
514 }
Winson Chung37c8d8e2014-03-24 14:53:07 -0700515 }
516
Winson Chunga26fb782014-06-12 17:52:39 -0700517 /** Animates this task bar if the user does not interact with the stack after a certain time. */
Winson Chunga0e88b52014-08-11 19:25:42 -0700518 void startNoUserInteractionAnimation() {
Winson Chungbbb3d3d2016-01-30 01:09:20 +0000519 int duration = getResources().getInteger(R.integer.recents_task_enter_from_app_duration);
520 mDismissButton.setOnClickListener(this);
521 mDismissButton.setVisibility(View.VISIBLE);
522 if (mDismissButton.getVisibility() == VISIBLE) {
Winson Chung133ad442016-01-30 01:07:34 +0000523 mDismissButton.animate()
524 .alpha(1f)
Winson Chung133ad442016-01-30 01:07:34 +0000525 .setInterpolator(Interpolators.FAST_OUT_LINEAR_IN)
Winson Chungbbb3d3d2016-01-30 01:09:20 +0000526 .setDuration(duration)
Winson Chung133ad442016-01-30 01:07:34 +0000527 .start();
Winson Chungbbb3d3d2016-01-30 01:09:20 +0000528 } else {
529 mDismissButton.setAlpha(1f);
530 }
531 if (mMoveTaskButton != null) {
532 if (mMoveTaskButton.getVisibility() == VISIBLE) {
533 mMoveTaskButton.setOnClickListener(this);
534 mMoveTaskButton.setVisibility(View.VISIBLE);
535 mMoveTaskButton.animate()
536 .alpha(1f)
537 .setInterpolator(Interpolators.FAST_OUT_LINEAR_IN)
538 .setDuration(duration)
539 .start();
540 } else {
541 mMoveTaskButton.setAlpha(1f);
542 }
Winson Chung133ad442016-01-30 01:07:34 +0000543 }
Winson Chunga26fb782014-06-12 17:52:39 -0700544 }
545
Peter Schillerb124d562015-12-11 21:31:17 -0800546 /**
547 * Mark this task view that the user does has not interacted with the stack after a certain
548 * time.
549 */
Winson Chunga0e88b52014-08-11 19:25:42 -0700550 void setNoUserInteractionState() {
Winson Chungbbb3d3d2016-01-30 01:09:20 +0000551 mDismissButton.setVisibility(View.VISIBLE);
552 mDismissButton.animate().cancel();
553 mDismissButton.setAlpha(1f);
554 mDismissButton.setOnClickListener(this);
555 if (mMoveTaskButton != null) {
556 mMoveTaskButton.setVisibility(View.VISIBLE);
557 mMoveTaskButton.animate().cancel();
558 mMoveTaskButton.setAlpha(1f);
559 mMoveTaskButton.setOnClickListener(this);
Winson Chung133ad442016-01-30 01:07:34 +0000560 }
Winson Chunga26fb782014-06-12 17:52:39 -0700561 }
Winson Chunga0e88b52014-08-11 19:25:42 -0700562
Peter Schillerb124d562015-12-11 21:31:17 -0800563 /**
564 * Resets the state tracking that the user has not interacted with the stack after a certain
565 * time.
566 */
Winson Chungb0a28ea2014-10-28 15:21:35 -0700567 void resetNoUserInteractionState() {
568 mDismissButton.setVisibility(View.INVISIBLE);
Winson Chungbbb3d3d2016-01-30 01:09:20 +0000569 mDismissButton.setAlpha(0f);
570 mDismissButton.setOnClickListener(null);
571 if (mMoveTaskButton != null) {
572 mMoveTaskButton.setVisibility(View.INVISIBLE);
573 mMoveTaskButton.setAlpha(0f);
574 mMoveTaskButton.setOnClickListener(null);
575 }
Winson Chungb0a28ea2014-10-28 15:21:35 -0700576 }
577
Winson Chungbf5dbf12014-09-16 00:58:25 +0200578 @Override
579 protected int[] onCreateDrawableState(int extraSpace) {
580
581 // Don't forward our state to the drawable - we do it manually in onTaskViewFocusChanged.
582 // This is to prevent layer trashing when the view is pressed.
583 return new int[] {};
584 }
585
Jorim Jaggi900fb482015-06-02 15:07:33 -0700586 @Override
Winson2536c7e2015-10-01 15:49:31 -0700587 public void onClick(View v) {
Winson Chung296278a2015-12-17 12:09:02 -0500588 if (v == mIconView) {
Winson Chungbbb3d3d2016-01-30 01:09:20 +0000589 // In accessibility, a single click on the focused app info button will show it
590 EventBus.getDefault().send(new ShowApplicationInfoEvent(mTask));
Winson2536c7e2015-10-01 15:49:31 -0700591 } else if (v == mDismissButton) {
Winsonbe7607a2015-10-01 17:24:51 -0700592 TaskView tv = Utilities.findParent(this, TaskView.class);
Winson2536c7e2015-10-01 15:49:31 -0700593 tv.dismissTask();
594
595 // Keep track of deletions by the dismiss button
596 MetricsLogger.histogram(getContext(), "overview_task_dismissed_source",
597 Constants.Metrics.DismissSourceHeaderButton);
598 } else if (v == mMoveTaskButton) {
Winson Chung48f2cda2015-12-11 13:20:12 -0500599 TaskView tv = Utilities.findParent(this, TaskView.class);
600 Rect bounds = mMoveTaskTargetStackId == FREEFORM_WORKSPACE_STACK_ID
601 ? new Rect(mTaskViewRect)
602 : new Rect();
603 EventBus.getDefault().send(new LaunchTaskEvent(tv, mTask, bounds,
604 mMoveTaskTargetStackId, false));
Winsonc5ef63f2016-01-21 14:39:23 -0800605 } else if (v == mAppInfoView) {
606 EventBus.getDefault().send(new ShowApplicationInfoEvent(mTask));
607 } else if (v == mAppIconView) {
608 hideAppOverlay(false /* immediate */);
Winson2536c7e2015-10-01 15:49:31 -0700609 }
610 }
611
612 @Override
613 public boolean onLongClick(View v) {
Winson Chung296278a2015-12-17 12:09:02 -0500614 if (v == mIconView) {
Winsonc5ef63f2016-01-21 14:39:23 -0800615 showAppOverlay();
616 return true;
617 } else if (v == mAppIconView) {
618 hideAppOverlay(false /* immediate */);
Winson2536c7e2015-10-01 15:49:31 -0700619 return true;
620 }
621 return false;
622 }
Winsonc5ef63f2016-01-21 14:39:23 -0800623
624 /**
625 * Shows the application overlay.
626 */
627 private void showAppOverlay() {
628 // Skip early if the task is invalid
629 SystemServicesProxy ssp = Recents.getSystemServices();
630 ComponentName cn = mTask.key.getComponent();
631 int userId = mTask.key.userId;
632 ActivityInfo activityInfo = ssp.getActivityInfo(cn, userId);
633 if (activityInfo == null) {
634 return;
635 }
636
637 // Inflate the overlay if necessary
638 if (mAppOverlayView == null) {
639 mAppOverlayView = (FrameLayout) mAppOverlayViewStub.inflate();
640 mAppOverlayView.setBackground(mOverlayBackground);
641 mAppIconView = (ImageView) mAppOverlayView.findViewById(R.id.app_icon);
642 mAppIconView.setOnClickListener(this);
643 mAppIconView.setOnLongClickListener(this);
644 mAppInfoView = (ImageView) mAppOverlayView.findViewById(R.id.app_info);
645 mAppInfoView.setOnClickListener(this);
646 mAppTitleView = (TextView) mAppOverlayView.findViewById(R.id.app_title);
Winson21700932016-03-24 17:26:23 -0700647 updateLayoutParams(mAppIconView, mAppTitleView, null, mAppInfoView);
Winsonc5ef63f2016-01-21 14:39:23 -0800648 }
649
650 // Update the overlay contents for the current app
651 mAppTitleView.setText(ssp.getBadgedApplicationLabel(activityInfo.applicationInfo, userId));
Winson4c84a8c2016-01-21 18:14:12 -0800652 mAppTitleView.setTextColor(mTask.useLightOnPrimaryColor ?
653 mTaskBarViewLightTextColor : mTaskBarViewDarkTextColor);
654 mAppIconView.setImageDrawable(ssp.getBadgedApplicationIcon(activityInfo.applicationInfo,
655 userId));
Winsonc5ef63f2016-01-21 14:39:23 -0800656 mAppInfoView.setImageDrawable(mTask.useLightOnPrimaryColor
657 ? mLightInfoIcon
658 : mDarkInfoIcon);
659 mAppOverlayView.setVisibility(View.VISIBLE);
660
661 int x = mIconView.getLeft() + mIconView.getWidth() / 2;
662 int y = mIconView.getTop() + mIconView.getHeight() / 2;
663 Animator revealAnim = ViewAnimationUtils.createCircularReveal(mAppOverlayView, x, y, 0,
664 getWidth());
665 revealAnim.setDuration(OVERLAY_REVEAL_DURATION);
Winsonc0d70582016-01-29 10:24:39 -0800666 revealAnim.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
Winsonc5ef63f2016-01-21 14:39:23 -0800667 revealAnim.start();
668 }
669
670 /**
671 * Hide the application overlay.
672 */
673 private void hideAppOverlay(boolean immediate) {
674 // Skip if we haven't even loaded the overlay yet
675 if (mAppOverlayView == null) {
676 return;
677 }
678
679 if (immediate) {
680 mAppOverlayView.setVisibility(View.GONE);
681 } else {
682 int x = mIconView.getLeft() + mIconView.getWidth() / 2;
683 int y = mIconView.getTop() + mIconView.getHeight() / 2;
684 Animator revealAnim = ViewAnimationUtils.createCircularReveal(mAppOverlayView, x, y,
685 getWidth(), 0);
686 revealAnim.setDuration(OVERLAY_REVEAL_DURATION);
Winsonc0d70582016-01-29 10:24:39 -0800687 revealAnim.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
Winsonc5ef63f2016-01-21 14:39:23 -0800688 revealAnim.addListener(new AnimatorListenerAdapter() {
689 @Override
690 public void onAnimationEnd(Animator animation) {
691 mAppOverlayView.setVisibility(View.GONE);
692 }
693 });
694 revealAnim.start();
695 }
696 }
Winson Chung37c8d8e2014-03-24 14:53:07 -0700697}