blob: 842899b62635e371b149ba78f7fe27612dc06112 [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
Wale Ogunwale0568aed2017-09-08 13:29:37 -070019import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
Wale Ogunwale0568aed2017-09-08 13:29:37 -070020import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
21
Winsonc5ef63f2016-01-21 14:39:23 -080022import android.animation.Animator;
23import android.animation.AnimatorListenerAdapter;
Winson94a0e892016-01-08 16:06:50 -080024import android.annotation.Nullable;
Sunny Goyalaf42c842018-02-23 11:46:18 -080025import android.app.AppGlobals;
Winsonc5ef63f2016-01-21 14:39:23 -080026import android.content.ComponentName;
Winson Chung37c8d8e2014-03-24 14:53:07 -070027import android.content.Context;
Winsonc5ef63f2016-01-21 14:39:23 -080028import android.content.pm.ActivityInfo;
Winson94a0e892016-01-08 16:06:50 -080029import android.content.res.Resources;
Winson Chungcd23c842014-05-29 18:30:51 -070030import android.graphics.Canvas;
Winson Chunga0e88b52014-08-11 19:25:42 -070031import android.graphics.Color;
Winson94a0e892016-01-08 16:06:50 -080032import android.graphics.ColorFilter;
Winson Chungcd23c842014-05-29 18:30:51 -070033import android.graphics.Paint;
Winson94a0e892016-01-08 16:06:50 -080034import android.graphics.PixelFormat;
Skuhne8aa7d162015-03-20 13:40:53 -070035import android.graphics.Rect;
Winson35f30502015-09-28 11:24:36 -070036import android.graphics.drawable.Drawable;
Winson18227e42016-06-08 12:57:40 -070037import android.graphics.drawable.RippleDrawable;
Peter Schillerb124d562015-12-11 21:31:17 -080038import android.os.CountDownTimer;
Winsonc5ef63f2016-01-21 14:39:23 -080039import android.support.v4.graphics.ColorUtils;
Winson Chung37c8d8e2014-03-24 14:53:07 -070040import android.util.AttributeSet;
Sunny Goyalaf42c842018-02-23 11:46:18 -080041import android.util.IconDrawableFactory;
Winson21700932016-03-24 17:26:23 -070042import android.view.Gravity;
Winson Chungd42a6cf2014-06-03 16:24:04 -070043import android.view.View;
Winsonc5ef63f2016-01-21 14:39:23 -080044import android.view.ViewAnimationUtils;
Winson231bc9c2016-02-09 12:31:00 -080045import android.view.ViewDebug;
Winson21700932016-03-24 17:26:23 -070046import android.view.ViewGroup;
Winson Chung37c8d8e2014-03-24 14:53:07 -070047import android.widget.FrameLayout;
48import android.widget.ImageView;
Peter Schillerb124d562015-12-11 21:31:17 -080049import android.widget.ProgressBar;
Winson Chung37c8d8e2014-03-24 14:53:07 -070050import android.widget.TextView;
Winsonc0d70582016-01-29 10:24:39 -080051
Winson2536c7e2015-10-01 15:49:31 -070052import com.android.internal.logging.MetricsLogger;
Winsonc0d70582016-01-29 10:24:39 -080053import com.android.systemui.Interpolators;
Winson Chung37c8d8e2014-03-24 14:53:07 -070054import com.android.systemui.R;
Winson Chungf5e22e72014-05-02 18:35:35 -070055import com.android.systemui.recents.Constants;
Winsone7f138c2015-10-22 16:15:21 -070056import com.android.systemui.recents.Recents;
Winson2536c7e2015-10-01 15:49:31 -070057import com.android.systemui.recents.events.EventBus;
Winson Chung48f2cda2015-12-11 13:20:12 -050058import com.android.systemui.recents.events.activity.LaunchTaskEvent;
Winson2536c7e2015-10-01 15:49:31 -070059import com.android.systemui.recents.events.ui.ShowApplicationInfoEvent;
Skuhne8aa7d162015-03-20 13:40:53 -070060import com.android.systemui.recents.misc.SystemServicesProxy;
Winson Chung6519c1b2017-10-13 17:12:56 -070061import com.android.systemui.shared.system.ActivityManagerWrapper;
62import com.android.systemui.shared.system.PackageManagerWrapper;
63import com.android.systemui.shared.recents.utilities.Utilities;
64import com.android.systemui.shared.recents.model.Task;
Winson Chung37c8d8e2014-03-24 14:53:07 -070065
Winson Chung37c8d8e2014-03-24 14:53:07 -070066/* The task bar view */
Winson2536c7e2015-10-01 15:49:31 -070067public class TaskViewHeader extends FrameLayout
68 implements View.OnClickListener, View.OnLongClickListener {
Winson Chungd42a6cf2014-06-03 16:24:04 -070069
Sunny Goyalaf42c842018-02-23 11:46:18 -080070 private static IconDrawableFactory sDrawableFactory;
71
Winson12858a62016-02-24 11:06:01 -080072 private static final float HIGHLIGHT_LIGHTNESS_INCREMENT = 0.075f;
Winsonc5ef63f2016-01-21 14:39:23 -080073 private static final float OVERLAY_LIGHTNESS_INCREMENT = -0.0625f;
74 private static final int OVERLAY_REVEAL_DURATION = 250;
Peter Schillerb124d562015-12-11 21:31:17 -080075 private static final long FOCUS_INDICATOR_INTERVAL_MS = 30;
Winson94a0e892016-01-08 16:06:50 -080076
77 /**
78 * A color drawable that draws a slight highlight at the top to help it stand out.
79 */
80 private class HighlightColorDrawable extends Drawable {
81
82 private Paint mHighlightPaint = new Paint();
83 private Paint mBackgroundPaint = new Paint();
Winson8be16342016-02-09 11:53:27 -080084 private int mColor;
85 private float mDimAlpha;
Winson94a0e892016-01-08 16:06:50 -080086
Winson94a0e892016-01-08 16:06:50 -080087 public HighlightColorDrawable() {
88 mBackgroundPaint.setColor(Color.argb(255, 0, 0, 0));
89 mBackgroundPaint.setAntiAlias(true);
90 mHighlightPaint.setColor(Color.argb(255, 255, 255, 255));
91 mHighlightPaint.setAntiAlias(true);
92 }
93
94 public void setColorAndDim(int color, float dimAlpha) {
Winson8be16342016-02-09 11:53:27 -080095 if (mColor != color || Float.compare(mDimAlpha, dimAlpha) != 0) {
96 mColor = color;
97 mDimAlpha = dimAlpha;
Manu Cornet017a3ac2017-02-06 17:47:42 -080098 if (mShouldDarkenBackgroundColor) {
99 color = getSecondaryColor(color, false /* useLightOverlayColor */);
100 }
Winson8be16342016-02-09 11:53:27 -0800101 mBackgroundPaint.setColor(color);
Winson94a0e892016-01-08 16:06:50 -0800102
Winson8be16342016-02-09 11:53:27 -0800103 ColorUtils.colorToHSL(color, mTmpHSL);
104 // TODO: Consider using the saturation of the color to adjust the lightness as well
105 mTmpHSL[2] = Math.min(1f,
106 mTmpHSL[2] + HIGHLIGHT_LIGHTNESS_INCREMENT * (1.0f - dimAlpha));
107 mHighlightPaint.setColor(ColorUtils.HSLToColor(mTmpHSL));
Winson94a0e892016-01-08 16:06:50 -0800108
Winson8be16342016-02-09 11:53:27 -0800109 invalidateSelf();
110 }
Winson94a0e892016-01-08 16:06:50 -0800111 }
112
113 @Override
114 public void setColorFilter(@Nullable ColorFilter colorFilter) {
115 // Do nothing
116 }
117
118 @Override
119 public void setAlpha(int alpha) {
120 // Do nothing
121 }
122
123 @Override
124 public void draw(Canvas canvas) {
125 // Draw the highlight at the top edge (but put the bottom edge just out of view)
126 canvas.drawRoundRect(0, 0, mTaskViewRect.width(),
127 2 * Math.max(mHighlightHeight, mCornerRadius),
128 mCornerRadius, mCornerRadius, mHighlightPaint);
129
130 // Draw the background with the rounded corners
131 canvas.drawRoundRect(0, mHighlightHeight, mTaskViewRect.width(),
132 getHeight() + mCornerRadius,
133 mCornerRadius, mCornerRadius, mBackgroundPaint);
134 }
135
136 @Override
137 public int getOpacity() {
138 return PixelFormat.OPAQUE;
139 }
Winson8be16342016-02-09 11:53:27 -0800140
141 public int getColor() {
142 return mColor;
143 }
Winson94a0e892016-01-08 16:06:50 -0800144 }
145
Winson2536c7e2015-10-01 15:49:31 -0700146 Task mTask;
Winson Chungd42a6cf2014-06-03 16:24:04 -0700147
Winson Chung353c0b92014-10-16 17:43:23 -0700148 // Header views
Winson Chung296278a2015-12-17 12:09:02 -0500149 ImageView mIconView;
150 TextView mTitleView;
Winsonc5ef63f2016-01-21 14:39:23 -0800151 ImageView mMoveTaskButton;
152 ImageView mDismissButton;
Winsonc5ef63f2016-01-21 14:39:23 -0800153 FrameLayout mAppOverlayView;
154 ImageView mAppIconView;
155 ImageView mAppInfoView;
156 TextView mAppTitleView;
Peter Schillerb124d562015-12-11 21:31:17 -0800157 ProgressBar mFocusTimerIndicator;
Winson Chung37c8d8e2014-03-24 14:53:07 -0700158
Winson Chung353c0b92014-10-16 17:43:23 -0700159 // Header drawables
Winson231bc9c2016-02-09 12:31:00 -0800160 @ViewDebug.ExportedProperty(category="recents")
Winsonde0591a2015-12-04 17:24:35 -0800161 Rect mTaskViewRect = new Rect();
Winson21700932016-03-24 17:26:23 -0700162 int mHeaderBarHeight;
163 int mHeaderButtonPadding;
Winson35f30502015-09-28 11:24:36 -0700164 int mCornerRadius;
165 int mHighlightHeight;
Winson231bc9c2016-02-09 12:31:00 -0800166 @ViewDebug.ExportedProperty(category="recents")
Winson94a0e892016-01-08 16:06:50 -0800167 float mDimAlpha;
Winson Chung54e297a2014-05-09 17:15:32 -0700168 Drawable mLightDismissDrawable;
169 Drawable mDarkDismissDrawable;
Winson3e874742016-01-07 10:08:17 -0800170 Drawable mLightFullscreenIcon;
171 Drawable mDarkFullscreenIcon;
Winsonc5ef63f2016-01-21 14:39:23 -0800172 Drawable mLightInfoIcon;
173 Drawable mDarkInfoIcon;
Winson94a0e892016-01-08 16:06:50 -0800174 int mTaskBarViewLightTextColor;
175 int mTaskBarViewDarkTextColor;
Winson8be16342016-02-09 11:53:27 -0800176 int mDisabledTaskBarBackgroundColor;
Winson Chung6519c1b2017-10-13 17:12:56 -0700177 String mDismissDescFormat;
178 String mAppInfoDescFormat;
Wale Ogunwale0568aed2017-09-08 13:29:37 -0700179 int mTaskWindowingMode = WINDOWING_MODE_UNDEFINED;
Winson Chung54e297a2014-05-09 17:15:32 -0700180
Winson94a0e892016-01-08 16:06:50 -0800181 // Header background
182 private HighlightColorDrawable mBackground;
Winsonc5ef63f2016-01-21 14:39:23 -0800183 private HighlightColorDrawable mOverlayBackground;
184 private float[] mTmpHSL = new float[3];
Winson Chung353c0b92014-10-16 17:43:23 -0700185
186 // Header dim, which is only used when task view hardware layers are not used
Winson94a0e892016-01-08 16:06:50 -0800187 private Paint mDimLayerPaint = new Paint();
Winson Chungcd23c842014-05-29 18:30:51 -0700188
Manu Cornet017a3ac2017-02-06 17:47:42 -0800189 // Whether the background color should be darkened to differentiate from the primary color.
190 // Used in grid layout.
191 private boolean mShouldDarkenBackgroundColor = false;
192
Peter Schillerb124d562015-12-11 21:31:17 -0800193 private CountDownTimer mFocusTimerCountDown;
Peter Schillerb124d562015-12-11 21:31:17 -0800194
Winson Chungec396d62014-08-06 17:08:00 -0700195 public TaskViewHeader(Context context) {
Winson Chung37c8d8e2014-03-24 14:53:07 -0700196 this(context, null);
197 }
198
Winson Chungec396d62014-08-06 17:08:00 -0700199 public TaskViewHeader(Context context, AttributeSet attrs) {
Winson Chung37c8d8e2014-03-24 14:53:07 -0700200 this(context, attrs, 0);
201 }
202
Winson Chungec396d62014-08-06 17:08:00 -0700203 public TaskViewHeader(Context context, AttributeSet attrs, int defStyleAttr) {
Winson Chung37c8d8e2014-03-24 14:53:07 -0700204 this(context, attrs, defStyleAttr, 0);
205 }
206
Winson Chungec396d62014-08-06 17:08:00 -0700207 public TaskViewHeader(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
Winson Chung37c8d8e2014-03-24 14:53:07 -0700208 super(context, attrs, defStyleAttr, defStyleRes);
Winson Chungcd23c842014-05-29 18:30:51 -0700209 setWillNotDraw(false);
210
211 // Load the dismiss resources
Winson94a0e892016-01-08 16:06:50 -0800212 Resources res = context.getResources();
Winson Chung911f4002015-02-13 13:49:36 -0800213 mLightDismissDrawable = context.getDrawable(R.drawable.recents_dismiss_light);
214 mDarkDismissDrawable = context.getDrawable(R.drawable.recents_dismiss_dark);
Jiaquan He90f4f522017-01-09 15:50:35 -0800215 mCornerRadius = Recents.getConfiguration().isGridEnabled ?
216 res.getDimensionPixelSize(R.dimen.recents_grid_task_view_rounded_corners_radius) :
217 res.getDimensionPixelSize(R.dimen.recents_task_view_rounded_corners_radius);
Winson94a0e892016-01-08 16:06:50 -0800218 mHighlightHeight = res.getDimensionPixelSize(R.dimen.recents_task_view_highlight);
219 mTaskBarViewLightTextColor = context.getColor(R.color.recents_task_bar_light_text_color);
220 mTaskBarViewDarkTextColor = context.getColor(R.color.recents_task_bar_dark_text_color);
Winson3e874742016-01-07 10:08:17 -0800221 mLightFullscreenIcon = context.getDrawable(R.drawable.recents_move_task_fullscreen_light);
222 mDarkFullscreenIcon = context.getDrawable(R.drawable.recents_move_task_fullscreen_dark);
Winsonc5ef63f2016-01-21 14:39:23 -0800223 mLightInfoIcon = context.getDrawable(R.drawable.recents_info_light);
224 mDarkInfoIcon = context.getDrawable(R.drawable.recents_info_dark);
Winson8be16342016-02-09 11:53:27 -0800225 mDisabledTaskBarBackgroundColor =
226 context.getColor(R.color.recents_task_bar_disabled_background_color);
Winson Chung6519c1b2017-10-13 17:12:56 -0700227 mDismissDescFormat = mContext.getString(
228 R.string.accessibility_recents_item_will_be_dismissed);
229 mAppInfoDescFormat = mContext.getString(R.string.accessibility_recents_item_open_app_info);
Winson3e874742016-01-07 10:08:17 -0800230
Winson94a0e892016-01-08 16:06:50 -0800231 // Configure the background and dim
232 mBackground = new HighlightColorDrawable();
233 mBackground.setColorAndDim(Color.argb(255, 0, 0, 0), 0f);
234 setBackground(mBackground);
Winsonc5ef63f2016-01-21 14:39:23 -0800235 mOverlayBackground = new HighlightColorDrawable();
Winson94a0e892016-01-08 16:06:50 -0800236 mDimLayerPaint.setColor(Color.argb(255, 0, 0, 0));
237 mDimLayerPaint.setAntiAlias(true);
Winson Chung37c8d8e2014-03-24 14:53:07 -0700238 }
239
Winsonc5ef63f2016-01-21 14:39:23 -0800240 /**
241 * Resets this header along with the TaskView.
242 */
243 public void reset() {
244 hideAppOverlay(true /* immediate */);
245 }
246
Winson Chung37c8d8e2014-03-24 14:53:07 -0700247 @Override
248 protected void onFinishInflate() {
Winson Chungbbb3d3d2016-01-30 01:09:20 +0000249 SystemServicesProxy ssp = Recents.getSystemServices();
250
Winson Chung37c8d8e2014-03-24 14:53:07 -0700251 // Initialize the icon and description views
Alan Viverette51efddb2017-04-05 10:00:01 -0400252 mIconView = findViewById(R.id.icon);
Winson Chung296278a2015-12-17 12:09:02 -0500253 mIconView.setOnLongClickListener(this);
Alan Viverette51efddb2017-04-05 10:00:01 -0400254 mTitleView = findViewById(R.id.title);
255 mDismissButton = findViewById(R.id.dismiss_task);
Winson21700932016-03-24 17:26:23 -0700256
Winson619e40c2016-03-25 16:12:35 -0700257 onConfigurationChanged();
Winson21700932016-03-24 17:26:23 -0700258 }
259
260 /**
261 * Programmatically sets the layout params for a header bar layout. This is necessary because
262 * we can't get resources based on the current configuration, but instead need to get them
263 * based on the device configuration.
264 */
265 private void updateLayoutParams(View icon, View title, View secondaryButton, View button) {
266 FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(
267 ViewGroup.LayoutParams.MATCH_PARENT, mHeaderBarHeight, Gravity.TOP);
268 setLayoutParams(lp);
269 lp = new FrameLayout.LayoutParams(mHeaderBarHeight, mHeaderBarHeight, Gravity.START);
270 icon.setLayoutParams(lp);
271 lp = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
272 ViewGroup.LayoutParams.WRAP_CONTENT, Gravity.START | Gravity.CENTER_VERTICAL);
Winsona1ededd2016-03-25 12:23:12 -0700273 lp.setMarginStart(mHeaderBarHeight);
Winson19852372016-03-30 18:26:49 -0700274 lp.setMarginEnd(mMoveTaskButton != null
Winson21700932016-03-24 17:26:23 -0700275 ? 2 * mHeaderBarHeight
Winson19852372016-03-30 18:26:49 -0700276 : mHeaderBarHeight);
Winson21700932016-03-24 17:26:23 -0700277 title.setLayoutParams(lp);
278 if (secondaryButton != null) {
279 lp = new FrameLayout.LayoutParams(mHeaderBarHeight, mHeaderBarHeight, Gravity.END);
Winsona1ededd2016-03-25 12:23:12 -0700280 lp.setMarginEnd(mHeaderBarHeight);
Winson21700932016-03-24 17:26:23 -0700281 secondaryButton.setLayoutParams(lp);
282 secondaryButton.setPadding(mHeaderButtonPadding, mHeaderButtonPadding,
283 mHeaderButtonPadding, mHeaderButtonPadding);
284 }
285 lp = new FrameLayout.LayoutParams(mHeaderBarHeight, mHeaderBarHeight, Gravity.END);
286 button.setLayoutParams(lp);
287 button.setPadding(mHeaderButtonPadding, mHeaderButtonPadding, mHeaderButtonPadding,
288 mHeaderButtonPadding);
Winson Chung54e297a2014-05-09 17:15:32 -0700289 }
290
Winson619e40c2016-03-25 16:12:35 -0700291 /**
292 * Update the header view when the configuration changes.
293 */
Winson670ea712016-04-12 17:02:26 -0700294 public void onConfigurationChanged() {
Winson619e40c2016-03-25 16:12:35 -0700295 // Update the dimensions of everything in the header. We do this because we need to use
296 // resources for the display, and not the current configuration.
297 Resources res = getResources();
Jorim Jaggi25160db2016-04-18 16:03:36 -0700298 int headerBarHeight = TaskStackLayoutAlgorithm.getDimensionForDevice(getContext(),
Winson619e40c2016-03-25 16:12:35 -0700299 R.dimen.recents_task_view_header_height,
300 R.dimen.recents_task_view_header_height,
301 R.dimen.recents_task_view_header_height,
302 R.dimen.recents_task_view_header_height_tablet_land,
303 R.dimen.recents_task_view_header_height,
Manu Cornetbf8e2902016-12-20 08:29:33 -0800304 R.dimen.recents_task_view_header_height_tablet_land,
305 R.dimen.recents_grid_task_view_header_height);
Jorim Jaggi25160db2016-04-18 16:03:36 -0700306 int headerButtonPadding = TaskStackLayoutAlgorithm.getDimensionForDevice(getContext(),
Winson619e40c2016-03-25 16:12:35 -0700307 R.dimen.recents_task_view_header_button_padding,
308 R.dimen.recents_task_view_header_button_padding,
309 R.dimen.recents_task_view_header_button_padding,
310 R.dimen.recents_task_view_header_button_padding_tablet_land,
311 R.dimen.recents_task_view_header_button_padding,
Manu Cornetbf8e2902016-12-20 08:29:33 -0800312 R.dimen.recents_task_view_header_button_padding_tablet_land,
313 R.dimen.recents_grid_task_view_header_button_padding);
Winson670ea712016-04-12 17:02:26 -0700314 if (headerBarHeight != mHeaderBarHeight || headerButtonPadding != mHeaderButtonPadding) {
315 mHeaderBarHeight = headerBarHeight;
316 mHeaderButtonPadding = headerButtonPadding;
317 updateLayoutParams(mIconView, mTitleView, mMoveTaskButton, mDismissButton);
318 if (mAppOverlayView != null) {
319 updateLayoutParams(mAppIconView, mAppTitleView, null, mAppInfoView);
320 }
Winson619e40c2016-03-25 16:12:35 -0700321 }
322 }
323
Winson Chungbbb3d3d2016-01-30 01:09:20 +0000324 @Override
325 protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
326 super.onLayout(changed, left, top, right, bottom);
327
328 // Since we update the position of children based on the width of the parent and this view
329 // recompute these changes with the new view size
330 onTaskViewSizeChanged(mTaskViewRect.width(), mTaskViewRect.height());
331 }
332
Winsonde0591a2015-12-04 17:24:35 -0800333 /**
334 * Called when the task view frame changes, allowing us to move the contents of the header
335 * to match the frame changes.
336 */
337 public void onTaskViewSizeChanged(int width, int height) {
338 mTaskViewRect.set(0, 0, width, height);
Winson Chung48f2cda2015-12-11 13:20:12 -0500339
Winson Chungbbb3d3d2016-01-30 01:09:20 +0000340 boolean showTitle = true;
341 boolean showMoveIcon = true;
342 boolean showDismissIcon = true;
343 int rightInset = width - getMeasuredWidth();
344
Winson Chungbbb3d3d2016-01-30 01:09:20 +0000345 mTitleView.setVisibility(showTitle ? View.VISIBLE : View.INVISIBLE);
346 if (mMoveTaskButton != null) {
347 mMoveTaskButton.setVisibility(showMoveIcon ? View.VISIBLE : View.INVISIBLE);
348 mMoveTaskButton.setTranslationX(rightInset);
Winson Chung48f2cda2015-12-11 13:20:12 -0500349 }
Winson Chungbbb3d3d2016-01-30 01:09:20 +0000350 mDismissButton.setVisibility(showDismissIcon ? View.VISIBLE : View.INVISIBLE);
351 mDismissButton.setTranslationX(rightInset);
Winson67c79572016-04-13 14:02:18 -0700352
353 setLeftTopRightBottom(0, 0, width, getMeasuredHeight());
Winsonde0591a2015-12-04 17:24:35 -0800354 }
355
Winson Chungcd23c842014-05-29 18:30:51 -0700356 @Override
Winsonbe8e6962016-02-01 14:27:52 -0800357 public void onDrawForeground(Canvas canvas) {
358 super.onDrawForeground(canvas);
Winson94a0e892016-01-08 16:06:50 -0800359
360 // Draw the dim layer with the rounded corners
361 canvas.drawRoundRect(0, 0, mTaskViewRect.width(), getHeight() + mCornerRadius,
362 mCornerRadius, mCornerRadius, mDimLayerPaint);
Winson Chungcd23c842014-05-29 18:30:51 -0700363 }
364
Peter Schillerb124d562015-12-11 21:31:17 -0800365 /** Starts the focus timer. */
Winson4b9cded2016-01-26 16:26:47 -0800366 public void startFocusTimerIndicator(int duration) {
Winsonc5ef63f2016-01-21 14:39:23 -0800367 if (mFocusTimerIndicator == null) {
368 return;
369 }
370
Peter Schillerb124d562015-12-11 21:31:17 -0800371 mFocusTimerIndicator.setVisibility(View.VISIBLE);
Winson4b9cded2016-01-26 16:26:47 -0800372 mFocusTimerIndicator.setMax(duration);
373 mFocusTimerIndicator.setProgress(duration);
374 if (mFocusTimerCountDown != null) {
375 mFocusTimerCountDown.cancel();
Peter Schillerb124d562015-12-11 21:31:17 -0800376 }
Winson4b9cded2016-01-26 16:26:47 -0800377 mFocusTimerCountDown = new CountDownTimer(duration,
378 FOCUS_INDICATOR_INTERVAL_MS) {
379 public void onTick(long millisUntilFinished) {
380 mFocusTimerIndicator.setProgress((int) millisUntilFinished);
381 }
382
383 public void onFinish() {
384 // Do nothing
385 }
386 }.start();
Peter Schillerb124d562015-12-11 21:31:17 -0800387 }
388
389 /** Cancels the focus timer. */
390 public void cancelFocusTimerIndicator() {
Winsonc5ef63f2016-01-21 14:39:23 -0800391 if (mFocusTimerIndicator == null) {
392 return;
393 }
394
395 if (mFocusTimerCountDown != null) {
Peter Schillerb124d562015-12-11 21:31:17 -0800396 mFocusTimerCountDown.cancel();
397 mFocusTimerIndicator.setProgress(0);
398 mFocusTimerIndicator.setVisibility(View.INVISIBLE);
399 }
400 }
401
Winson22574af2016-03-23 19:00:28 -0700402 /** Only exposed for the workaround for b/27815919. */
403 public ImageView getIconView() {
404 return mIconView;
405 }
406
Peter Schillerb124d562015-12-11 21:31:17 -0800407 /** Returns the secondary color for a primary color. */
408 int getSecondaryColor(int primaryColor, boolean useLightOverlayColor) {
409 int overlayColor = useLightOverlayColor ? Color.WHITE : Color.BLACK;
410 return Utilities.getColorWithOverlay(primaryColor, overlayColor, 0.8f);
411 }
412
Winson Chung353c0b92014-10-16 17:43:23 -0700413 /**
414 * Sets the dim alpha, only used when we are not using hardware layers.
415 * (see RecentsConfiguration.useHardwareLayers)
416 */
Winsone693aaf2016-03-01 12:05:59 -0800417 public void setDimAlpha(float dimAlpha) {
418 if (Float.compare(mDimAlpha, dimAlpha) != 0) {
419 mDimAlpha = dimAlpha;
Winson59924fe2016-03-17 14:13:18 -0700420 mTitleView.setAlpha(1f - dimAlpha);
Winsone693aaf2016-03-01 12:05:59 -0800421 updateBackgroundColor(mBackground.getColor(), dimAlpha);
422 }
Winson Chung353c0b92014-10-16 17:43:23 -0700423 }
424
Winson94a0e892016-01-08 16:06:50 -0800425 /**
426 * Updates the background and highlight colors for this header.
427 */
Winson8be16342016-02-09 11:53:27 -0800428 private void updateBackgroundColor(int color, float dimAlpha) {
Winson94a0e892016-01-08 16:06:50 -0800429 if (mTask != null) {
Winson8be16342016-02-09 11:53:27 -0800430 mBackground.setColorAndDim(color, dimAlpha);
Winsonc5ef63f2016-01-21 14:39:23 -0800431 // TODO: Consider using the saturation of the color to adjust the lightness as well
Winson8be16342016-02-09 11:53:27 -0800432 ColorUtils.colorToHSL(color, mTmpHSL);
Winsonc5ef63f2016-01-21 14:39:23 -0800433 mTmpHSL[2] = Math.min(1f, mTmpHSL[2] + OVERLAY_LIGHTNESS_INCREMENT * (1.0f - dimAlpha));
434 mOverlayBackground.setColorAndDim(ColorUtils.HSLToColor(mTmpHSL), dimAlpha);
Winson94a0e892016-01-08 16:06:50 -0800435 mDimLayerPaint.setAlpha((int) (dimAlpha * 255));
Winson Chungbbb3d3d2016-01-30 01:09:20 +0000436 invalidate();
Winson94a0e892016-01-08 16:06:50 -0800437 }
438 }
439
Winsond2a03062016-04-15 11:19:07 -0700440 /**
Manu Cornet017a3ac2017-02-06 17:47:42 -0800441 * Sets whether the background color should be darkened to differentiate from the primary color.
442 */
443 public void setShouldDarkenBackgroundColor(boolean flag) {
444 mShouldDarkenBackgroundColor = flag;
445 }
446
447 /**
Winsond2a03062016-04-15 11:19:07 -0700448 * Binds the bar view to the task.
449 */
450 public void bindToTask(Task t, boolean touchExplorationEnabled, boolean disabledInSafeMode) {
Winson2536c7e2015-10-01 15:49:31 -0700451 mTask = t;
452
Winson8be16342016-02-09 11:53:27 -0800453 int primaryColor = disabledInSafeMode
454 ? mDisabledTaskBarBackgroundColor
455 : t.colorPrimary;
Winsone693aaf2016-03-01 12:05:59 -0800456 if (mBackground.getColor() != primaryColor) {
457 updateBackgroundColor(primaryColor, mDimAlpha);
458 }
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 }
Winsonb92dd632016-04-04 11:02:11 -0700462 mTitleView.setContentDescription(t.titleDescription);
Winson Chung296278a2015-12-17 12:09:02 -0500463 mTitleView.setTextColor(t.useLightOnPrimaryColor ?
Winson94a0e892016-01-08 16:06:50 -0800464 mTaskBarViewLightTextColor : mTaskBarViewDarkTextColor);
Winson Chung93748a12014-07-13 17:43:31 -0700465 mDismissButton.setImageDrawable(t.useLightOnPrimaryColor ?
466 mLightDismissDrawable : mDarkDismissDrawable);
Winson Chung6519c1b2017-10-13 17:12:56 -0700467 mDismissButton.setContentDescription(String.format(mDismissDescFormat, t.titleDescription));
Winson3e5f0af2016-04-26 10:22:47 -0700468 mDismissButton.setOnClickListener(this);
469 mDismissButton.setClickable(false);
Winson18227e42016-06-08 12:57:40 -0700470 ((RippleDrawable) mDismissButton.getBackground()).setForceSoftware(true);
Winson Chung48f2cda2015-12-11 13:20:12 -0500471
Winson2536c7e2015-10-01 15:49:31 -0700472 // In accessibility, a single click on the focused app info button will show it
Winson Chungbbb3d3d2016-01-30 01:09:20 +0000473 if (touchExplorationEnabled) {
Winson Chung6519c1b2017-10-13 17:12:56 -0700474 mIconView.setContentDescription(String.format(mAppInfoDescFormat, t.titleDescription));
Winson Chung296278a2015-12-17 12:09:02 -0500475 mIconView.setOnClickListener(this);
Winson3e5f0af2016-04-26 10:22:47 -0700476 mIconView.setClickable(true);
Winson2536c7e2015-10-01 15:49:31 -0700477 }
478 }
479
Winsond2a03062016-04-15 11:19:07 -0700480 /**
481 * Called when the bound task's data has loaded and this view should update to reflect the
482 * changes.
483 */
484 public void onTaskDataLoaded() {
Jorim Jaggi2390ec22017-04-04 14:06:30 +0200485 if (mTask != null && mTask.icon != null) {
Winsond2a03062016-04-15 11:19:07 -0700486 mIconView.setImageDrawable(mTask.icon);
487 }
488 }
489
Winson2536c7e2015-10-01 15:49:31 -0700490 /** Unbinds the bar view from the task */
Winson Chungbbb3d3d2016-01-30 01:09:20 +0000491 void unbindFromTask(boolean touchExplorationEnabled) {
Winson2536c7e2015-10-01 15:49:31 -0700492 mTask = null;
Winson Chung296278a2015-12-17 12:09:02 -0500493 mIconView.setImageDrawable(null);
Winson Chungbbb3d3d2016-01-30 01:09:20 +0000494 if (touchExplorationEnabled) {
Winson3e5f0af2016-04-26 10:22:47 -0700495 mIconView.setClickable(false);
Winson Chungbbb3d3d2016-01-30 01:09:20 +0000496 }
Winson Chung37c8d8e2014-03-24 14:53:07 -0700497 }
498
Winson Chunga26fb782014-06-12 17:52:39 -0700499 /** Animates this task bar if the user does not interact with the stack after a certain time. */
Winson Chunga0e88b52014-08-11 19:25:42 -0700500 void startNoUserInteractionAnimation() {
Winson Chungbbb3d3d2016-01-30 01:09:20 +0000501 int duration = getResources().getInteger(R.integer.recents_task_enter_from_app_duration);
Winson Chungbbb3d3d2016-01-30 01:09:20 +0000502 mDismissButton.setVisibility(View.VISIBLE);
Winson3e5f0af2016-04-26 10:22:47 -0700503 mDismissButton.setClickable(true);
Winson Chungbbb3d3d2016-01-30 01:09:20 +0000504 if (mDismissButton.getVisibility() == VISIBLE) {
Winson Chung133ad442016-01-30 01:07:34 +0000505 mDismissButton.animate()
506 .alpha(1f)
Winson Chung133ad442016-01-30 01:07:34 +0000507 .setInterpolator(Interpolators.FAST_OUT_LINEAR_IN)
Winson Chungbbb3d3d2016-01-30 01:09:20 +0000508 .setDuration(duration)
Winson Chung133ad442016-01-30 01:07:34 +0000509 .start();
Winson Chungbbb3d3d2016-01-30 01:09:20 +0000510 } else {
511 mDismissButton.setAlpha(1f);
512 }
513 if (mMoveTaskButton != null) {
514 if (mMoveTaskButton.getVisibility() == VISIBLE) {
Winson Chungbbb3d3d2016-01-30 01:09:20 +0000515 mMoveTaskButton.setVisibility(View.VISIBLE);
Winson3e5f0af2016-04-26 10:22:47 -0700516 mMoveTaskButton.setClickable(true);
Winson Chungbbb3d3d2016-01-30 01:09:20 +0000517 mMoveTaskButton.animate()
518 .alpha(1f)
519 .setInterpolator(Interpolators.FAST_OUT_LINEAR_IN)
520 .setDuration(duration)
521 .start();
522 } else {
523 mMoveTaskButton.setAlpha(1f);
524 }
Winson Chung133ad442016-01-30 01:07:34 +0000525 }
Winson Chunga26fb782014-06-12 17:52:39 -0700526 }
527
Peter Schillerb124d562015-12-11 21:31:17 -0800528 /**
529 * Mark this task view that the user does has not interacted with the stack after a certain
530 * time.
531 */
Manu Cornet017a3ac2017-02-06 17:47:42 -0800532 public void setNoUserInteractionState() {
Winson Chungbbb3d3d2016-01-30 01:09:20 +0000533 mDismissButton.setVisibility(View.VISIBLE);
534 mDismissButton.animate().cancel();
535 mDismissButton.setAlpha(1f);
Winson3e5f0af2016-04-26 10:22:47 -0700536 mDismissButton.setClickable(true);
Winson Chungbbb3d3d2016-01-30 01:09:20 +0000537 if (mMoveTaskButton != null) {
538 mMoveTaskButton.setVisibility(View.VISIBLE);
539 mMoveTaskButton.animate().cancel();
540 mMoveTaskButton.setAlpha(1f);
Winson3e5f0af2016-04-26 10:22:47 -0700541 mMoveTaskButton.setClickable(true);
Winson Chung133ad442016-01-30 01:07:34 +0000542 }
Winson Chunga26fb782014-06-12 17:52:39 -0700543 }
Winson Chunga0e88b52014-08-11 19:25:42 -0700544
Peter Schillerb124d562015-12-11 21:31:17 -0800545 /**
546 * Resets the state tracking that the user has not interacted with the stack after a certain
547 * time.
548 */
Winson Chungb0a28ea2014-10-28 15:21:35 -0700549 void resetNoUserInteractionState() {
550 mDismissButton.setVisibility(View.INVISIBLE);
Winson Chungbbb3d3d2016-01-30 01:09:20 +0000551 mDismissButton.setAlpha(0f);
Winson3e5f0af2016-04-26 10:22:47 -0700552 mDismissButton.setClickable(false);
Winson Chungbbb3d3d2016-01-30 01:09:20 +0000553 if (mMoveTaskButton != null) {
554 mMoveTaskButton.setVisibility(View.INVISIBLE);
555 mMoveTaskButton.setAlpha(0f);
Winson3e5f0af2016-04-26 10:22:47 -0700556 mMoveTaskButton.setClickable(false);
Winson Chungbbb3d3d2016-01-30 01:09:20 +0000557 }
Winson Chungb0a28ea2014-10-28 15:21:35 -0700558 }
559
Winson Chungbf5dbf12014-09-16 00:58:25 +0200560 @Override
561 protected int[] onCreateDrawableState(int extraSpace) {
562
563 // Don't forward our state to the drawable - we do it manually in onTaskViewFocusChanged.
564 // This is to prevent layer trashing when the view is pressed.
565 return new int[] {};
566 }
567
Jorim Jaggi900fb482015-06-02 15:07:33 -0700568 @Override
Winson2536c7e2015-10-01 15:49:31 -0700569 public void onClick(View v) {
Winson Chung296278a2015-12-17 12:09:02 -0500570 if (v == mIconView) {
Winson Chungbbb3d3d2016-01-30 01:09:20 +0000571 // In accessibility, a single click on the focused app info button will show it
572 EventBus.getDefault().send(new ShowApplicationInfoEvent(mTask));
Winson2536c7e2015-10-01 15:49:31 -0700573 } else if (v == mDismissButton) {
Winsonbe7607a2015-10-01 17:24:51 -0700574 TaskView tv = Utilities.findParent(this, TaskView.class);
Winson2536c7e2015-10-01 15:49:31 -0700575 tv.dismissTask();
576
577 // Keep track of deletions by the dismiss button
578 MetricsLogger.histogram(getContext(), "overview_task_dismissed_source",
579 Constants.Metrics.DismissSourceHeaderButton);
580 } else if (v == mMoveTaskButton) {
Winson Chung48f2cda2015-12-11 13:20:12 -0500581 TaskView tv = Utilities.findParent(this, TaskView.class);
Wale Ogunwale0568aed2017-09-08 13:29:37 -0700582 EventBus.getDefault().send(new LaunchTaskEvent(tv, mTask, null, false,
583 mTaskWindowingMode, ACTIVITY_TYPE_UNDEFINED));
Winsonc5ef63f2016-01-21 14:39:23 -0800584 } else if (v == mAppInfoView) {
585 EventBus.getDefault().send(new ShowApplicationInfoEvent(mTask));
586 } else if (v == mAppIconView) {
587 hideAppOverlay(false /* immediate */);
Winson2536c7e2015-10-01 15:49:31 -0700588 }
589 }
590
591 @Override
592 public boolean onLongClick(View v) {
Winson Chung296278a2015-12-17 12:09:02 -0500593 if (v == mIconView) {
Winsonc5ef63f2016-01-21 14:39:23 -0800594 showAppOverlay();
595 return true;
596 } else if (v == mAppIconView) {
597 hideAppOverlay(false /* immediate */);
Winson2536c7e2015-10-01 15:49:31 -0700598 return true;
599 }
600 return false;
601 }
Winsonc5ef63f2016-01-21 14:39:23 -0800602
603 /**
604 * Shows the application overlay.
605 */
606 private void showAppOverlay() {
607 // Skip early if the task is invalid
608 SystemServicesProxy ssp = Recents.getSystemServices();
609 ComponentName cn = mTask.key.getComponent();
610 int userId = mTask.key.userId;
Winson Chung6519c1b2017-10-13 17:12:56 -0700611 ActivityInfo activityInfo = PackageManagerWrapper.getInstance().getActivityInfo(cn, userId);
Winsonc5ef63f2016-01-21 14:39:23 -0800612 if (activityInfo == null) {
613 return;
614 }
615
616 // Inflate the overlay if necessary
617 if (mAppOverlayView == null) {
Winson9b001392016-04-08 14:54:02 -0700618 mAppOverlayView = (FrameLayout) Utilities.findViewStubById(this,
619 R.id.app_overlay_stub).inflate();
Winsonc5ef63f2016-01-21 14:39:23 -0800620 mAppOverlayView.setBackground(mOverlayBackground);
621 mAppIconView = (ImageView) mAppOverlayView.findViewById(R.id.app_icon);
622 mAppIconView.setOnClickListener(this);
623 mAppIconView.setOnLongClickListener(this);
624 mAppInfoView = (ImageView) mAppOverlayView.findViewById(R.id.app_info);
625 mAppInfoView.setOnClickListener(this);
626 mAppTitleView = (TextView) mAppOverlayView.findViewById(R.id.app_title);
Winson21700932016-03-24 17:26:23 -0700627 updateLayoutParams(mAppIconView, mAppTitleView, null, mAppInfoView);
Winsonc5ef63f2016-01-21 14:39:23 -0800628 }
629
630 // Update the overlay contents for the current app
Winson Chung6519c1b2017-10-13 17:12:56 -0700631 mAppTitleView.setText(ActivityManagerWrapper.getInstance().getBadgedApplicationLabel(
632 activityInfo.applicationInfo, userId));
Winson4c84a8c2016-01-21 18:14:12 -0800633 mAppTitleView.setTextColor(mTask.useLightOnPrimaryColor ?
634 mTaskBarViewLightTextColor : mTaskBarViewDarkTextColor);
Sunny Goyalaf42c842018-02-23 11:46:18 -0800635 mAppIconView.setImageDrawable(getIconDrawableFactory().getBadgedIcon(
Winson Chung6519c1b2017-10-13 17:12:56 -0700636 activityInfo.applicationInfo, userId));
Winsonc5ef63f2016-01-21 14:39:23 -0800637 mAppInfoView.setImageDrawable(mTask.useLightOnPrimaryColor
638 ? mLightInfoIcon
639 : mDarkInfoIcon);
640 mAppOverlayView.setVisibility(View.VISIBLE);
641
642 int x = mIconView.getLeft() + mIconView.getWidth() / 2;
643 int y = mIconView.getTop() + mIconView.getHeight() / 2;
644 Animator revealAnim = ViewAnimationUtils.createCircularReveal(mAppOverlayView, x, y, 0,
645 getWidth());
646 revealAnim.setDuration(OVERLAY_REVEAL_DURATION);
Winsonc0d70582016-01-29 10:24:39 -0800647 revealAnim.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
Winsonc5ef63f2016-01-21 14:39:23 -0800648 revealAnim.start();
649 }
650
651 /**
652 * Hide the application overlay.
653 */
654 private void hideAppOverlay(boolean immediate) {
655 // Skip if we haven't even loaded the overlay yet
656 if (mAppOverlayView == null) {
657 return;
658 }
659
660 if (immediate) {
661 mAppOverlayView.setVisibility(View.GONE);
662 } else {
663 int x = mIconView.getLeft() + mIconView.getWidth() / 2;
664 int y = mIconView.getTop() + mIconView.getHeight() / 2;
665 Animator revealAnim = ViewAnimationUtils.createCircularReveal(mAppOverlayView, x, y,
666 getWidth(), 0);
667 revealAnim.setDuration(OVERLAY_REVEAL_DURATION);
Winsonc0d70582016-01-29 10:24:39 -0800668 revealAnim.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
Winsonc5ef63f2016-01-21 14:39:23 -0800669 revealAnim.addListener(new AnimatorListenerAdapter() {
670 @Override
671 public void onAnimationEnd(Animator animation) {
672 mAppOverlayView.setVisibility(View.GONE);
673 }
674 });
675 revealAnim.start();
676 }
677 }
Sunny Goyalaf42c842018-02-23 11:46:18 -0800678
679 private static IconDrawableFactory getIconDrawableFactory() {
680 if (sDrawableFactory == null) {
681 sDrawableFactory = IconDrawableFactory.newInstance(AppGlobals.getInitialApplication());
682 }
683 return sDrawableFactory;
684 }
Winson Chung37c8d8e2014-03-24 14:53:07 -0700685}