blob: 0777163293d931660a570ae072db2e1de38cf90d [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;
Winson18227e42016-06-08 12:57:40 -070034import android.graphics.drawable.RippleDrawable;
Peter Schillerb124d562015-12-11 21:31:17 -080035import android.os.CountDownTimer;
Winsonc5ef63f2016-01-21 14:39:23 -080036import android.support.v4.graphics.ColorUtils;
Winson Chung37c8d8e2014-03-24 14:53:07 -070037import android.util.AttributeSet;
Winson21700932016-03-24 17:26:23 -070038import android.view.Gravity;
Winson Chungd42a6cf2014-06-03 16:24:04 -070039import android.view.View;
Winsonc5ef63f2016-01-21 14:39:23 -080040import android.view.ViewAnimationUtils;
Winson231bc9c2016-02-09 12:31:00 -080041import android.view.ViewDebug;
Winson21700932016-03-24 17:26:23 -070042import android.view.ViewGroup;
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;
Winsonc5ef63f2016-01-21 14:39:23 -0800144 ImageView mMoveTaskButton;
145 ImageView mDismissButton;
Winsonc5ef63f2016-01-21 14:39:23 -0800146 FrameLayout mAppOverlayView;
147 ImageView mAppIconView;
148 ImageView mAppInfoView;
149 TextView mAppTitleView;
Peter Schillerb124d562015-12-11 21:31:17 -0800150 ProgressBar mFocusTimerIndicator;
Winson Chung37c8d8e2014-03-24 14:53:07 -0700151
Winson Chung353c0b92014-10-16 17:43:23 -0700152 // Header drawables
Winson231bc9c2016-02-09 12:31:00 -0800153 @ViewDebug.ExportedProperty(category="recents")
Winsonde0591a2015-12-04 17:24:35 -0800154 Rect mTaskViewRect = new Rect();
Winson21700932016-03-24 17:26:23 -0700155 int mHeaderBarHeight;
156 int mHeaderButtonPadding;
Winson35f30502015-09-28 11:24:36 -0700157 int mCornerRadius;
158 int mHighlightHeight;
Winson231bc9c2016-02-09 12:31:00 -0800159 @ViewDebug.ExportedProperty(category="recents")
Winson94a0e892016-01-08 16:06:50 -0800160 float mDimAlpha;
Winson Chung54e297a2014-05-09 17:15:32 -0700161 Drawable mLightDismissDrawable;
162 Drawable mDarkDismissDrawable;
Winson3e874742016-01-07 10:08:17 -0800163 Drawable mLightFreeformIcon;
164 Drawable mDarkFreeformIcon;
165 Drawable mLightFullscreenIcon;
166 Drawable mDarkFullscreenIcon;
Winsonc5ef63f2016-01-21 14:39:23 -0800167 Drawable mLightInfoIcon;
168 Drawable mDarkInfoIcon;
Winson94a0e892016-01-08 16:06:50 -0800169 int mTaskBarViewLightTextColor;
170 int mTaskBarViewDarkTextColor;
Winson8be16342016-02-09 11:53:27 -0800171 int mDisabledTaskBarBackgroundColor;
Winsonc5ef63f2016-01-21 14:39:23 -0800172 int mMoveTaskTargetStackId = INVALID_STACK_ID;
Winson Chung54e297a2014-05-09 17:15:32 -0700173
Winson94a0e892016-01-08 16:06:50 -0800174 // Header background
175 private HighlightColorDrawable mBackground;
Winsonc5ef63f2016-01-21 14:39:23 -0800176 private HighlightColorDrawable mOverlayBackground;
177 private float[] mTmpHSL = new float[3];
Winson Chung353c0b92014-10-16 17:43:23 -0700178
179 // Header dim, which is only used when task view hardware layers are not used
Winson94a0e892016-01-08 16:06:50 -0800180 private Paint mDimLayerPaint = new Paint();
Winson Chungcd23c842014-05-29 18:30:51 -0700181
Peter Schillerb124d562015-12-11 21:31:17 -0800182 private CountDownTimer mFocusTimerCountDown;
Peter Schillerb124d562015-12-11 21:31:17 -0800183
Winson Chungec396d62014-08-06 17:08:00 -0700184 public TaskViewHeader(Context context) {
Winson Chung37c8d8e2014-03-24 14:53:07 -0700185 this(context, null);
186 }
187
Winson Chungec396d62014-08-06 17:08:00 -0700188 public TaskViewHeader(Context context, AttributeSet attrs) {
Winson Chung37c8d8e2014-03-24 14:53:07 -0700189 this(context, attrs, 0);
190 }
191
Winson Chungec396d62014-08-06 17:08:00 -0700192 public TaskViewHeader(Context context, AttributeSet attrs, int defStyleAttr) {
Winson Chung37c8d8e2014-03-24 14:53:07 -0700193 this(context, attrs, defStyleAttr, 0);
194 }
195
Winson Chungec396d62014-08-06 17:08:00 -0700196 public TaskViewHeader(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
Winson Chung37c8d8e2014-03-24 14:53:07 -0700197 super(context, attrs, defStyleAttr, defStyleRes);
Winson Chungcd23c842014-05-29 18:30:51 -0700198 setWillNotDraw(false);
199
200 // Load the dismiss resources
Winson94a0e892016-01-08 16:06:50 -0800201 Resources res = context.getResources();
Winson Chung911f4002015-02-13 13:49:36 -0800202 mLightDismissDrawable = context.getDrawable(R.drawable.recents_dismiss_light);
203 mDarkDismissDrawable = context.getDrawable(R.drawable.recents_dismiss_dark);
Jiaquan He90f4f522017-01-09 15:50:35 -0800204 mCornerRadius = Recents.getConfiguration().isGridEnabled ?
205 res.getDimensionPixelSize(R.dimen.recents_grid_task_view_rounded_corners_radius) :
206 res.getDimensionPixelSize(R.dimen.recents_task_view_rounded_corners_radius);
Winson94a0e892016-01-08 16:06:50 -0800207 mHighlightHeight = res.getDimensionPixelSize(R.dimen.recents_task_view_highlight);
208 mTaskBarViewLightTextColor = context.getColor(R.color.recents_task_bar_light_text_color);
209 mTaskBarViewDarkTextColor = context.getColor(R.color.recents_task_bar_dark_text_color);
Winson3e874742016-01-07 10:08:17 -0800210 mLightFreeformIcon = context.getDrawable(R.drawable.recents_move_task_freeform_light);
211 mDarkFreeformIcon = context.getDrawable(R.drawable.recents_move_task_freeform_dark);
212 mLightFullscreenIcon = context.getDrawable(R.drawable.recents_move_task_fullscreen_light);
213 mDarkFullscreenIcon = context.getDrawable(R.drawable.recents_move_task_fullscreen_dark);
Winsonc5ef63f2016-01-21 14:39:23 -0800214 mLightInfoIcon = context.getDrawable(R.drawable.recents_info_light);
215 mDarkInfoIcon = context.getDrawable(R.drawable.recents_info_dark);
Winson8be16342016-02-09 11:53:27 -0800216 mDisabledTaskBarBackgroundColor =
217 context.getColor(R.color.recents_task_bar_disabled_background_color);
Winson3e874742016-01-07 10:08:17 -0800218
Winson94a0e892016-01-08 16:06:50 -0800219 // Configure the background and dim
220 mBackground = new HighlightColorDrawable();
221 mBackground.setColorAndDim(Color.argb(255, 0, 0, 0), 0f);
222 setBackground(mBackground);
Winsonc5ef63f2016-01-21 14:39:23 -0800223 mOverlayBackground = new HighlightColorDrawable();
Winson94a0e892016-01-08 16:06:50 -0800224 mDimLayerPaint.setColor(Color.argb(255, 0, 0, 0));
225 mDimLayerPaint.setAntiAlias(true);
Winson Chung37c8d8e2014-03-24 14:53:07 -0700226 }
227
Winsonc5ef63f2016-01-21 14:39:23 -0800228 /**
229 * Resets this header along with the TaskView.
230 */
231 public void reset() {
232 hideAppOverlay(true /* immediate */);
233 }
234
Winson Chung37c8d8e2014-03-24 14:53:07 -0700235 @Override
236 protected void onFinishInflate() {
Winson Chungbbb3d3d2016-01-30 01:09:20 +0000237 SystemServicesProxy ssp = Recents.getSystemServices();
238
Winson Chung37c8d8e2014-03-24 14:53:07 -0700239 // Initialize the icon and description views
Winson Chung296278a2015-12-17 12:09:02 -0500240 mIconView = (ImageView) findViewById(R.id.icon);
241 mIconView.setOnLongClickListener(this);
242 mTitleView = (TextView) findViewById(R.id.title);
Winson Chung54e297a2014-05-09 17:15:32 -0700243 mDismissButton = (ImageView) findViewById(R.id.dismiss_task);
Winson Chungbbb3d3d2016-01-30 01:09:20 +0000244 if (ssp.hasFreeformWorkspaceSupport()) {
245 mMoveTaskButton = (ImageView) findViewById(R.id.move_task);
246 }
Winson21700932016-03-24 17:26:23 -0700247
Winson619e40c2016-03-25 16:12:35 -0700248 onConfigurationChanged();
Winson21700932016-03-24 17:26:23 -0700249 }
250
251 /**
252 * Programmatically sets the layout params for a header bar layout. This is necessary because
253 * we can't get resources based on the current configuration, but instead need to get them
254 * based on the device configuration.
255 */
256 private void updateLayoutParams(View icon, View title, View secondaryButton, View button) {
257 FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(
258 ViewGroup.LayoutParams.MATCH_PARENT, mHeaderBarHeight, Gravity.TOP);
259 setLayoutParams(lp);
260 lp = new FrameLayout.LayoutParams(mHeaderBarHeight, mHeaderBarHeight, Gravity.START);
261 icon.setLayoutParams(lp);
262 lp = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
263 ViewGroup.LayoutParams.WRAP_CONTENT, Gravity.START | Gravity.CENTER_VERTICAL);
Winsona1ededd2016-03-25 12:23:12 -0700264 lp.setMarginStart(mHeaderBarHeight);
Winson19852372016-03-30 18:26:49 -0700265 lp.setMarginEnd(mMoveTaskButton != null
Winson21700932016-03-24 17:26:23 -0700266 ? 2 * mHeaderBarHeight
Winson19852372016-03-30 18:26:49 -0700267 : mHeaderBarHeight);
Winson21700932016-03-24 17:26:23 -0700268 title.setLayoutParams(lp);
269 if (secondaryButton != null) {
270 lp = new FrameLayout.LayoutParams(mHeaderBarHeight, mHeaderBarHeight, Gravity.END);
Winsona1ededd2016-03-25 12:23:12 -0700271 lp.setMarginEnd(mHeaderBarHeight);
Winson21700932016-03-24 17:26:23 -0700272 secondaryButton.setLayoutParams(lp);
273 secondaryButton.setPadding(mHeaderButtonPadding, mHeaderButtonPadding,
274 mHeaderButtonPadding, mHeaderButtonPadding);
275 }
276 lp = new FrameLayout.LayoutParams(mHeaderBarHeight, mHeaderBarHeight, Gravity.END);
277 button.setLayoutParams(lp);
278 button.setPadding(mHeaderButtonPadding, mHeaderButtonPadding, mHeaderButtonPadding,
279 mHeaderButtonPadding);
Winson Chung54e297a2014-05-09 17:15:32 -0700280 }
281
Winson619e40c2016-03-25 16:12:35 -0700282 /**
283 * Update the header view when the configuration changes.
284 */
Winson670ea712016-04-12 17:02:26 -0700285 public void onConfigurationChanged() {
Winson619e40c2016-03-25 16:12:35 -0700286 // Update the dimensions of everything in the header. We do this because we need to use
287 // resources for the display, and not the current configuration.
288 Resources res = getResources();
Jorim Jaggi25160db2016-04-18 16:03:36 -0700289 int headerBarHeight = TaskStackLayoutAlgorithm.getDimensionForDevice(getContext(),
Winson619e40c2016-03-25 16:12:35 -0700290 R.dimen.recents_task_view_header_height,
291 R.dimen.recents_task_view_header_height,
292 R.dimen.recents_task_view_header_height,
293 R.dimen.recents_task_view_header_height_tablet_land,
294 R.dimen.recents_task_view_header_height,
Manu Cornetbf8e2902016-12-20 08:29:33 -0800295 R.dimen.recents_task_view_header_height_tablet_land,
296 R.dimen.recents_grid_task_view_header_height);
Jorim Jaggi25160db2016-04-18 16:03:36 -0700297 int headerButtonPadding = TaskStackLayoutAlgorithm.getDimensionForDevice(getContext(),
Winson619e40c2016-03-25 16:12:35 -0700298 R.dimen.recents_task_view_header_button_padding,
299 R.dimen.recents_task_view_header_button_padding,
300 R.dimen.recents_task_view_header_button_padding,
301 R.dimen.recents_task_view_header_button_padding_tablet_land,
302 R.dimen.recents_task_view_header_button_padding,
Manu Cornetbf8e2902016-12-20 08:29:33 -0800303 R.dimen.recents_task_view_header_button_padding_tablet_land,
304 R.dimen.recents_grid_task_view_header_button_padding);
Winson670ea712016-04-12 17:02:26 -0700305 if (headerBarHeight != mHeaderBarHeight || headerButtonPadding != mHeaderButtonPadding) {
306 mHeaderBarHeight = headerBarHeight;
307 mHeaderButtonPadding = headerButtonPadding;
308 updateLayoutParams(mIconView, mTitleView, mMoveTaskButton, mDismissButton);
309 if (mAppOverlayView != null) {
310 updateLayoutParams(mAppIconView, mAppTitleView, null, mAppInfoView);
311 }
Winson619e40c2016-03-25 16:12:35 -0700312 }
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);
Winson67c79572016-04-13 14:02:18 -0700357
358 setLeftTopRightBottom(0, 0, width, getMeasuredHeight());
Winsonde0591a2015-12-04 17:24:35 -0800359 }
360
Winson Chungcd23c842014-05-29 18:30:51 -0700361 @Override
Winsonbe8e6962016-02-01 14:27:52 -0800362 public void onDrawForeground(Canvas canvas) {
363 super.onDrawForeground(canvas);
Winson94a0e892016-01-08 16:06:50 -0800364
365 // Draw the dim layer with the rounded corners
366 canvas.drawRoundRect(0, 0, mTaskViewRect.width(), getHeight() + mCornerRadius,
367 mCornerRadius, mCornerRadius, mDimLayerPaint);
Winson Chungcd23c842014-05-29 18:30:51 -0700368 }
369
Peter Schillerb124d562015-12-11 21:31:17 -0800370 /** Starts the focus timer. */
Winson4b9cded2016-01-26 16:26:47 -0800371 public void startFocusTimerIndicator(int duration) {
Winsonc5ef63f2016-01-21 14:39:23 -0800372 if (mFocusTimerIndicator == null) {
373 return;
374 }
375
Peter Schillerb124d562015-12-11 21:31:17 -0800376 mFocusTimerIndicator.setVisibility(View.VISIBLE);
Winson4b9cded2016-01-26 16:26:47 -0800377 mFocusTimerIndicator.setMax(duration);
378 mFocusTimerIndicator.setProgress(duration);
379 if (mFocusTimerCountDown != null) {
380 mFocusTimerCountDown.cancel();
Peter Schillerb124d562015-12-11 21:31:17 -0800381 }
Winson4b9cded2016-01-26 16:26:47 -0800382 mFocusTimerCountDown = new CountDownTimer(duration,
383 FOCUS_INDICATOR_INTERVAL_MS) {
384 public void onTick(long millisUntilFinished) {
385 mFocusTimerIndicator.setProgress((int) millisUntilFinished);
386 }
387
388 public void onFinish() {
389 // Do nothing
390 }
391 }.start();
Peter Schillerb124d562015-12-11 21:31:17 -0800392 }
393
394 /** Cancels the focus timer. */
395 public void cancelFocusTimerIndicator() {
Winsonc5ef63f2016-01-21 14:39:23 -0800396 if (mFocusTimerIndicator == null) {
397 return;
398 }
399
400 if (mFocusTimerCountDown != null) {
Peter Schillerb124d562015-12-11 21:31:17 -0800401 mFocusTimerCountDown.cancel();
402 mFocusTimerIndicator.setProgress(0);
403 mFocusTimerIndicator.setVisibility(View.INVISIBLE);
404 }
405 }
406
Winson22574af2016-03-23 19:00:28 -0700407 /** Only exposed for the workaround for b/27815919. */
408 public ImageView getIconView() {
409 return mIconView;
410 }
411
Peter Schillerb124d562015-12-11 21:31:17 -0800412 /** Returns the secondary color for a primary color. */
413 int getSecondaryColor(int primaryColor, boolean useLightOverlayColor) {
414 int overlayColor = useLightOverlayColor ? Color.WHITE : Color.BLACK;
415 return Utilities.getColorWithOverlay(primaryColor, overlayColor, 0.8f);
416 }
417
Winson Chung353c0b92014-10-16 17:43:23 -0700418 /**
419 * Sets the dim alpha, only used when we are not using hardware layers.
420 * (see RecentsConfiguration.useHardwareLayers)
421 */
Winsone693aaf2016-03-01 12:05:59 -0800422 public void setDimAlpha(float dimAlpha) {
423 if (Float.compare(mDimAlpha, dimAlpha) != 0) {
424 mDimAlpha = dimAlpha;
Winson59924fe2016-03-17 14:13:18 -0700425 mTitleView.setAlpha(1f - dimAlpha);
Winsone693aaf2016-03-01 12:05:59 -0800426 updateBackgroundColor(mBackground.getColor(), dimAlpha);
427 }
Winson Chung353c0b92014-10-16 17:43:23 -0700428 }
429
Winson94a0e892016-01-08 16:06:50 -0800430 /**
431 * Updates the background and highlight colors for this header.
432 */
Winson8be16342016-02-09 11:53:27 -0800433 private void updateBackgroundColor(int color, float dimAlpha) {
Winson94a0e892016-01-08 16:06:50 -0800434 if (mTask != null) {
Winson8be16342016-02-09 11:53:27 -0800435 mBackground.setColorAndDim(color, dimAlpha);
Winsonc5ef63f2016-01-21 14:39:23 -0800436 // TODO: Consider using the saturation of the color to adjust the lightness as well
Winson8be16342016-02-09 11:53:27 -0800437 ColorUtils.colorToHSL(color, mTmpHSL);
Winsonc5ef63f2016-01-21 14:39:23 -0800438 mTmpHSL[2] = Math.min(1f, mTmpHSL[2] + OVERLAY_LIGHTNESS_INCREMENT * (1.0f - dimAlpha));
439 mOverlayBackground.setColorAndDim(ColorUtils.HSLToColor(mTmpHSL), dimAlpha);
Winson94a0e892016-01-08 16:06:50 -0800440 mDimLayerPaint.setAlpha((int) (dimAlpha * 255));
Winson Chungbbb3d3d2016-01-30 01:09:20 +0000441 invalidate();
Winson94a0e892016-01-08 16:06:50 -0800442 }
443 }
444
Winsond2a03062016-04-15 11:19:07 -0700445 /**
446 * Binds the bar view to the task.
447 */
448 public void bindToTask(Task t, boolean touchExplorationEnabled, boolean disabledInSafeMode) {
Winson2536c7e2015-10-01 15:49:31 -0700449 mTask = t;
450
Winson8be16342016-02-09 11:53:27 -0800451 int primaryColor = disabledInSafeMode
452 ? mDisabledTaskBarBackgroundColor
453 : t.colorPrimary;
Winsone693aaf2016-03-01 12:05:59 -0800454 if (mBackground.getColor() != primaryColor) {
455 updateBackgroundColor(primaryColor, mDimAlpha);
456 }
Winson Chung296278a2015-12-17 12:09:02 -0500457 if (!mTitleView.getText().toString().equals(t.title)) {
458 mTitleView.setText(t.title);
Winson Chungbdbb87d2014-07-09 14:29:13 -0700459 }
Winsonb92dd632016-04-04 11:02:11 -0700460 mTitleView.setContentDescription(t.titleDescription);
Winson Chung296278a2015-12-17 12:09:02 -0500461 mTitleView.setTextColor(t.useLightOnPrimaryColor ?
Winson94a0e892016-01-08 16:06:50 -0800462 mTaskBarViewLightTextColor : mTaskBarViewDarkTextColor);
Winson Chung93748a12014-07-13 17:43:31 -0700463 mDismissButton.setImageDrawable(t.useLightOnPrimaryColor ?
464 mLightDismissDrawable : mDarkDismissDrawable);
Winson55003902016-01-12 12:00:37 -0800465 mDismissButton.setContentDescription(t.dismissDescription);
Winson3e5f0af2016-04-26 10:22:47 -0700466 mDismissButton.setOnClickListener(this);
467 mDismissButton.setClickable(false);
Winson18227e42016-06-08 12:57:40 -0700468 ((RippleDrawable) mDismissButton.getBackground()).setForceSoftware(true);
Winson Chung48f2cda2015-12-11 13:20:12 -0500469
470 // When freeform workspaces are enabled, then update the move-task button depending on the
471 // current task
Winson Chungbbb3d3d2016-01-30 01:09:20 +0000472 if (mMoveTaskButton != null) {
Winson Chung48f2cda2015-12-11 13:20:12 -0500473 if (t.isFreeformTask()) {
474 mMoveTaskTargetStackId = FULLSCREEN_WORKSPACE_STACK_ID;
Winson3e874742016-01-07 10:08:17 -0800475 mMoveTaskButton.setImageDrawable(t.useLightOnPrimaryColor
476 ? mLightFullscreenIcon
477 : mDarkFullscreenIcon);
Winson Chung48f2cda2015-12-11 13:20:12 -0500478 } else {
479 mMoveTaskTargetStackId = FREEFORM_WORKSPACE_STACK_ID;
Winson3e874742016-01-07 10:08:17 -0800480 mMoveTaskButton.setImageDrawable(t.useLightOnPrimaryColor
481 ? mLightFreeformIcon
482 : mDarkFreeformIcon);
Winson Chung48f2cda2015-12-11 13:20:12 -0500483 }
Winson3e5f0af2016-04-26 10:22:47 -0700484 mMoveTaskButton.setOnClickListener(this);
485 mMoveTaskButton.setClickable(false);
Winson18227e42016-06-08 12:57:40 -0700486 ((RippleDrawable) mMoveTaskButton.getBackground()).setForceSoftware(true);
Winson Chung48f2cda2015-12-11 13:20:12 -0500487 }
Winson2536c7e2015-10-01 15:49:31 -0700488
Winson4b9cded2016-01-26 16:26:47 -0800489 if (Recents.getDebugFlags().isFastToggleRecentsEnabled()) {
Winsonc5ef63f2016-01-21 14:39:23 -0800490 if (mFocusTimerIndicator == null) {
Winson9b001392016-04-08 14:54:02 -0700491 mFocusTimerIndicator = (ProgressBar) Utilities.findViewStubById(this,
492 R.id.focus_timer_indicator_stub).inflate();
Winsonc5ef63f2016-01-21 14:39:23 -0800493 }
494 mFocusTimerIndicator.getProgressDrawable()
495 .setColorFilter(
496 getSecondaryColor(t.colorPrimary, t.useLightOnPrimaryColor),
497 PorterDuff.Mode.SRC_IN);
498 }
Peter Schillerb124d562015-12-11 21:31:17 -0800499
Winson2536c7e2015-10-01 15:49:31 -0700500 // In accessibility, a single click on the focused app info button will show it
Winson Chungbbb3d3d2016-01-30 01:09:20 +0000501 if (touchExplorationEnabled) {
Winsonb92dd632016-04-04 11:02:11 -0700502 mIconView.setContentDescription(t.appInfoDescription);
Winson Chung296278a2015-12-17 12:09:02 -0500503 mIconView.setOnClickListener(this);
Winson3e5f0af2016-04-26 10:22:47 -0700504 mIconView.setClickable(true);
Winson2536c7e2015-10-01 15:49:31 -0700505 }
506 }
507
Winsond2a03062016-04-15 11:19:07 -0700508 /**
509 * Called when the bound task's data has loaded and this view should update to reflect the
510 * changes.
511 */
512 public void onTaskDataLoaded() {
513 if (mTask.icon != null) {
514 mIconView.setImageDrawable(mTask.icon);
515 }
516 }
517
Winson2536c7e2015-10-01 15:49:31 -0700518 /** Unbinds the bar view from the task */
Winson Chungbbb3d3d2016-01-30 01:09:20 +0000519 void unbindFromTask(boolean touchExplorationEnabled) {
Winson2536c7e2015-10-01 15:49:31 -0700520 mTask = null;
Winson Chung296278a2015-12-17 12:09:02 -0500521 mIconView.setImageDrawable(null);
Winson Chungbbb3d3d2016-01-30 01:09:20 +0000522 if (touchExplorationEnabled) {
Winson3e5f0af2016-04-26 10:22:47 -0700523 mIconView.setClickable(false);
Winson Chungbbb3d3d2016-01-30 01:09:20 +0000524 }
Winson Chung37c8d8e2014-03-24 14:53:07 -0700525 }
526
Winson Chunga26fb782014-06-12 17:52:39 -0700527 /** Animates this task bar if the user does not interact with the stack after a certain time. */
Winson Chunga0e88b52014-08-11 19:25:42 -0700528 void startNoUserInteractionAnimation() {
Winson Chungbbb3d3d2016-01-30 01:09:20 +0000529 int duration = getResources().getInteger(R.integer.recents_task_enter_from_app_duration);
Winson Chungbbb3d3d2016-01-30 01:09:20 +0000530 mDismissButton.setVisibility(View.VISIBLE);
Winson3e5f0af2016-04-26 10:22:47 -0700531 mDismissButton.setClickable(true);
Winson Chungbbb3d3d2016-01-30 01:09:20 +0000532 if (mDismissButton.getVisibility() == VISIBLE) {
Winson Chung133ad442016-01-30 01:07:34 +0000533 mDismissButton.animate()
534 .alpha(1f)
Winson Chung133ad442016-01-30 01:07:34 +0000535 .setInterpolator(Interpolators.FAST_OUT_LINEAR_IN)
Winson Chungbbb3d3d2016-01-30 01:09:20 +0000536 .setDuration(duration)
Winson Chung133ad442016-01-30 01:07:34 +0000537 .start();
Winson Chungbbb3d3d2016-01-30 01:09:20 +0000538 } else {
539 mDismissButton.setAlpha(1f);
540 }
541 if (mMoveTaskButton != null) {
542 if (mMoveTaskButton.getVisibility() == VISIBLE) {
Winson Chungbbb3d3d2016-01-30 01:09:20 +0000543 mMoveTaskButton.setVisibility(View.VISIBLE);
Winson3e5f0af2016-04-26 10:22:47 -0700544 mMoveTaskButton.setClickable(true);
Winson Chungbbb3d3d2016-01-30 01:09:20 +0000545 mMoveTaskButton.animate()
546 .alpha(1f)
547 .setInterpolator(Interpolators.FAST_OUT_LINEAR_IN)
548 .setDuration(duration)
549 .start();
550 } else {
551 mMoveTaskButton.setAlpha(1f);
552 }
Winson Chung133ad442016-01-30 01:07:34 +0000553 }
Winson Chunga26fb782014-06-12 17:52:39 -0700554 }
555
Peter Schillerb124d562015-12-11 21:31:17 -0800556 /**
557 * Mark this task view that the user does has not interacted with the stack after a certain
558 * time.
559 */
Winson Chunga0e88b52014-08-11 19:25:42 -0700560 void setNoUserInteractionState() {
Winson Chungbbb3d3d2016-01-30 01:09:20 +0000561 mDismissButton.setVisibility(View.VISIBLE);
562 mDismissButton.animate().cancel();
563 mDismissButton.setAlpha(1f);
Winson3e5f0af2016-04-26 10:22:47 -0700564 mDismissButton.setClickable(true);
Winson Chungbbb3d3d2016-01-30 01:09:20 +0000565 if (mMoveTaskButton != null) {
566 mMoveTaskButton.setVisibility(View.VISIBLE);
567 mMoveTaskButton.animate().cancel();
568 mMoveTaskButton.setAlpha(1f);
Winson3e5f0af2016-04-26 10:22:47 -0700569 mMoveTaskButton.setClickable(true);
Winson Chung133ad442016-01-30 01:07:34 +0000570 }
Winson Chunga26fb782014-06-12 17:52:39 -0700571 }
Winson Chunga0e88b52014-08-11 19:25:42 -0700572
Peter Schillerb124d562015-12-11 21:31:17 -0800573 /**
574 * Resets the state tracking that the user has not interacted with the stack after a certain
575 * time.
576 */
Winson Chungb0a28ea2014-10-28 15:21:35 -0700577 void resetNoUserInteractionState() {
578 mDismissButton.setVisibility(View.INVISIBLE);
Winson Chungbbb3d3d2016-01-30 01:09:20 +0000579 mDismissButton.setAlpha(0f);
Winson3e5f0af2016-04-26 10:22:47 -0700580 mDismissButton.setClickable(false);
Winson Chungbbb3d3d2016-01-30 01:09:20 +0000581 if (mMoveTaskButton != null) {
582 mMoveTaskButton.setVisibility(View.INVISIBLE);
583 mMoveTaskButton.setAlpha(0f);
Winson3e5f0af2016-04-26 10:22:47 -0700584 mMoveTaskButton.setClickable(false);
Winson Chungbbb3d3d2016-01-30 01:09:20 +0000585 }
Winson Chungb0a28ea2014-10-28 15:21:35 -0700586 }
587
Winson Chungbf5dbf12014-09-16 00:58:25 +0200588 @Override
589 protected int[] onCreateDrawableState(int extraSpace) {
590
591 // Don't forward our state to the drawable - we do it manually in onTaskViewFocusChanged.
592 // This is to prevent layer trashing when the view is pressed.
593 return new int[] {};
594 }
595
Jorim Jaggi900fb482015-06-02 15:07:33 -0700596 @Override
Winson2536c7e2015-10-01 15:49:31 -0700597 public void onClick(View v) {
Winson Chung296278a2015-12-17 12:09:02 -0500598 if (v == mIconView) {
Winson Chungbbb3d3d2016-01-30 01:09:20 +0000599 // In accessibility, a single click on the focused app info button will show it
600 EventBus.getDefault().send(new ShowApplicationInfoEvent(mTask));
Winson2536c7e2015-10-01 15:49:31 -0700601 } else if (v == mDismissButton) {
Winsonbe7607a2015-10-01 17:24:51 -0700602 TaskView tv = Utilities.findParent(this, TaskView.class);
Winson2536c7e2015-10-01 15:49:31 -0700603 tv.dismissTask();
604
605 // Keep track of deletions by the dismiss button
606 MetricsLogger.histogram(getContext(), "overview_task_dismissed_source",
607 Constants.Metrics.DismissSourceHeaderButton);
608 } else if (v == mMoveTaskButton) {
Winson Chung48f2cda2015-12-11 13:20:12 -0500609 TaskView tv = Utilities.findParent(this, TaskView.class);
Manu Cornetbf8e2902016-12-20 08:29:33 -0800610 EventBus.getDefault().send(new LaunchTaskEvent(tv, mTask, null,
Winson Chung48f2cda2015-12-11 13:20:12 -0500611 mMoveTaskTargetStackId, false));
Winsonc5ef63f2016-01-21 14:39:23 -0800612 } else if (v == mAppInfoView) {
613 EventBus.getDefault().send(new ShowApplicationInfoEvent(mTask));
614 } else if (v == mAppIconView) {
615 hideAppOverlay(false /* immediate */);
Winson2536c7e2015-10-01 15:49:31 -0700616 }
617 }
618
619 @Override
620 public boolean onLongClick(View v) {
Winson Chung296278a2015-12-17 12:09:02 -0500621 if (v == mIconView) {
Winsonc5ef63f2016-01-21 14:39:23 -0800622 showAppOverlay();
623 return true;
624 } else if (v == mAppIconView) {
625 hideAppOverlay(false /* immediate */);
Winson2536c7e2015-10-01 15:49:31 -0700626 return true;
627 }
628 return false;
629 }
Winsonc5ef63f2016-01-21 14:39:23 -0800630
631 /**
632 * Shows the application overlay.
633 */
634 private void showAppOverlay() {
635 // Skip early if the task is invalid
636 SystemServicesProxy ssp = Recents.getSystemServices();
637 ComponentName cn = mTask.key.getComponent();
638 int userId = mTask.key.userId;
639 ActivityInfo activityInfo = ssp.getActivityInfo(cn, userId);
640 if (activityInfo == null) {
641 return;
642 }
643
644 // Inflate the overlay if necessary
645 if (mAppOverlayView == null) {
Winson9b001392016-04-08 14:54:02 -0700646 mAppOverlayView = (FrameLayout) Utilities.findViewStubById(this,
647 R.id.app_overlay_stub).inflate();
Winsonc5ef63f2016-01-21 14:39:23 -0800648 mAppOverlayView.setBackground(mOverlayBackground);
649 mAppIconView = (ImageView) mAppOverlayView.findViewById(R.id.app_icon);
650 mAppIconView.setOnClickListener(this);
651 mAppIconView.setOnLongClickListener(this);
652 mAppInfoView = (ImageView) mAppOverlayView.findViewById(R.id.app_info);
653 mAppInfoView.setOnClickListener(this);
654 mAppTitleView = (TextView) mAppOverlayView.findViewById(R.id.app_title);
Winson21700932016-03-24 17:26:23 -0700655 updateLayoutParams(mAppIconView, mAppTitleView, null, mAppInfoView);
Winsonc5ef63f2016-01-21 14:39:23 -0800656 }
657
658 // Update the overlay contents for the current app
659 mAppTitleView.setText(ssp.getBadgedApplicationLabel(activityInfo.applicationInfo, userId));
Winson4c84a8c2016-01-21 18:14:12 -0800660 mAppTitleView.setTextColor(mTask.useLightOnPrimaryColor ?
661 mTaskBarViewLightTextColor : mTaskBarViewDarkTextColor);
662 mAppIconView.setImageDrawable(ssp.getBadgedApplicationIcon(activityInfo.applicationInfo,
663 userId));
Winsonc5ef63f2016-01-21 14:39:23 -0800664 mAppInfoView.setImageDrawable(mTask.useLightOnPrimaryColor
665 ? mLightInfoIcon
666 : mDarkInfoIcon);
667 mAppOverlayView.setVisibility(View.VISIBLE);
668
669 int x = mIconView.getLeft() + mIconView.getWidth() / 2;
670 int y = mIconView.getTop() + mIconView.getHeight() / 2;
671 Animator revealAnim = ViewAnimationUtils.createCircularReveal(mAppOverlayView, x, y, 0,
672 getWidth());
673 revealAnim.setDuration(OVERLAY_REVEAL_DURATION);
Winsonc0d70582016-01-29 10:24:39 -0800674 revealAnim.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
Winsonc5ef63f2016-01-21 14:39:23 -0800675 revealAnim.start();
676 }
677
678 /**
679 * Hide the application overlay.
680 */
681 private void hideAppOverlay(boolean immediate) {
682 // Skip if we haven't even loaded the overlay yet
683 if (mAppOverlayView == null) {
684 return;
685 }
686
687 if (immediate) {
688 mAppOverlayView.setVisibility(View.GONE);
689 } else {
690 int x = mIconView.getLeft() + mIconView.getWidth() / 2;
691 int y = mIconView.getTop() + mIconView.getHeight() / 2;
692 Animator revealAnim = ViewAnimationUtils.createCircularReveal(mAppOverlayView, x, y,
693 getWidth(), 0);
694 revealAnim.setDuration(OVERLAY_REVEAL_DURATION);
Winsonc0d70582016-01-29 10:24:39 -0800695 revealAnim.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
Winsonc5ef63f2016-01-21 14:39:23 -0800696 revealAnim.addListener(new AnimatorListenerAdapter() {
697 @Override
698 public void onAnimationEnd(Animator animation) {
699 mAppOverlayView.setVisibility(View.GONE);
700 }
701 });
702 revealAnim.start();
703 }
704 }
Winson Chung37c8d8e2014-03-24 14:53:07 -0700705}