2 * Copyright (C) 2014 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package com.android.systemui.recents.views;
19 import android.animation.Animator;
20 import android.animation.AnimatorListenerAdapter;
21 import android.animation.AnimatorSet;
22 import android.animation.ArgbEvaluator;
23 import android.animation.ObjectAnimator;
24 import android.animation.ValueAnimator;
25 import android.content.Context;
26 import android.content.res.ColorStateList;
27 import android.content.res.Resources;
28 import android.graphics.Canvas;
29 import android.graphics.Color;
30 import android.graphics.Outline;
31 import android.graphics.Paint;
32 import android.graphics.PorterDuff;
33 import android.graphics.PorterDuffColorFilter;
34 import android.graphics.PorterDuffXfermode;
35 import android.graphics.drawable.ColorDrawable;
36 import android.graphics.drawable.Drawable;
37 import android.graphics.drawable.GradientDrawable;
38 import android.graphics.drawable.RippleDrawable;
39 import android.util.AttributeSet;
40 import android.view.MotionEvent;
41 import android.view.View;
42 import android.view.ViewOutlineProvider;
43 import android.widget.FrameLayout;
44 import android.widget.ImageView;
45 import android.widget.TextView;
46 import com.android.systemui.R;
47 import com.android.systemui.recents.Constants;
48 import com.android.systemui.recents.RecentsConfiguration;
49 import com.android.systemui.recents.misc.Utilities;
50 import com.android.systemui.recents.model.Task;
53 /* The task bar view */
54 public class TaskViewHeader extends FrameLayout {
56 RecentsConfiguration mConfig;
59 ImageView mDismissButton;
60 ImageView mApplicationIcon;
61 TextView mActivityDescription;
64 boolean mCurrentPrimaryColorIsDark;
65 int mCurrentPrimaryColor;
67 Drawable mLightDismissDrawable;
68 Drawable mDarkDismissDrawable;
69 RippleDrawable mBackground;
70 GradientDrawable mBackgroundColorDrawable;
71 AnimatorSet mFocusAnimator;
72 String mDismissContentDescription;
74 // Static highlight that we draw at the top of each view
75 static Paint sHighlightPaint;
77 // Header dim, which is only used when task view hardware layers are not used
78 Paint mDimLayerPaint = new Paint();
79 PorterDuffColorFilter mDimColorFilter = new PorterDuffColorFilter(0, PorterDuff.Mode.SRC_ATOP);
81 public TaskViewHeader(Context context) {
85 public TaskViewHeader(Context context, AttributeSet attrs) {
86 this(context, attrs, 0);
89 public TaskViewHeader(Context context, AttributeSet attrs, int defStyleAttr) {
90 this(context, attrs, defStyleAttr, 0);
93 public TaskViewHeader(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
94 super(context, attrs, defStyleAttr, defStyleRes);
95 mConfig = RecentsConfiguration.getInstance();
96 setWillNotDraw(false);
97 setClipToOutline(true);
98 setOutlineProvider(new ViewOutlineProvider() {
100 public void getOutline(View view, Outline outline) {
101 outline.setRect(0, 0, getMeasuredWidth(), getMeasuredHeight());
105 // Load the dismiss resources
106 Resources res = context.getResources();
107 mLightDismissDrawable = res.getDrawable(R.drawable.recents_dismiss_light);
108 mDarkDismissDrawable = res.getDrawable(R.drawable.recents_dismiss_dark);
109 mDismissContentDescription =
110 res.getString(R.string.accessibility_recents_item_will_be_dismissed);
112 // Configure the highlight paint
113 if (sHighlightPaint == null) {
114 sHighlightPaint = new Paint();
115 sHighlightPaint.setStyle(Paint.Style.STROKE);
116 sHighlightPaint.setStrokeWidth(mConfig.taskViewHighlightPx);
117 sHighlightPaint.setColor(mConfig.taskBarViewHighlightColor);
118 sHighlightPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.ADD));
119 sHighlightPaint.setAntiAlias(true);
124 public boolean onTouchEvent(MotionEvent event) {
125 // We ignore taps on the task bar except on the filter and dismiss buttons
126 if (!Constants.DebugFlags.App.EnableTaskBarTouchEvents) return true;
128 return super.onTouchEvent(event);
132 protected void onFinishInflate() {
133 // Initialize the icon and description views
134 mApplicationIcon = (ImageView) findViewById(R.id.application_icon);
135 mActivityDescription = (TextView) findViewById(R.id.activity_description);
136 mDismissButton = (ImageView) findViewById(R.id.dismiss_task);
138 // Hide the backgrounds if they are ripple drawables
139 if (!Constants.DebugFlags.App.EnableTaskFiltering) {
140 if (mApplicationIcon.getBackground() instanceof RippleDrawable) {
141 mApplicationIcon.setBackground(null);
145 mBackgroundColorDrawable = (GradientDrawable) getContext().getDrawable(R.drawable
146 .recents_task_view_header_bg_color);
147 // Copy the ripple drawable since we are going to be manipulating it
148 mBackground = (RippleDrawable)
149 getContext().getDrawable(R.drawable.recents_task_view_header_bg);
150 mBackground = (RippleDrawable) mBackground.mutate().getConstantState().newDrawable();
151 mBackground.setColor(ColorStateList.valueOf(0));
152 mBackground.setDrawableByLayerId(mBackground.getId(0), mBackgroundColorDrawable);
153 setBackground(mBackground);
157 protected void onDraw(Canvas canvas) {
158 // Draw the highlight at the top edge (but put the bottom edge just out of view)
159 float offset = (float) Math.ceil(mConfig.taskViewHighlightPx / 2f);
160 float radius = mConfig.taskViewRoundedCornerRadiusPx;
161 int count = canvas.save(Canvas.CLIP_SAVE_FLAG);
162 canvas.clipRect(0, 0, getMeasuredWidth(), getMeasuredHeight());
163 canvas.drawRoundRect(-offset, 0f, (float) getMeasuredWidth() + offset,
164 getMeasuredHeight() + radius, radius, radius, sHighlightPaint);
165 canvas.restoreToCount(count);
169 public boolean hasOverlappingRendering() {
174 * Sets the dim alpha, only used when we are not using hardware layers.
175 * (see RecentsConfiguration.useHardwareLayers)
177 void setDimAlpha(int alpha) {
178 mDimColorFilter.setColor(Color.argb(alpha, 0, 0, 0));
179 mDimLayerPaint.setColorFilter(mDimColorFilter);
180 setLayerType(LAYER_TYPE_HARDWARE, mDimLayerPaint);
183 /** Returns the secondary color for a primary color. */
184 int getSecondaryColor(int primaryColor, boolean useLightOverlayColor) {
185 int overlayColor = useLightOverlayColor ? Color.WHITE : Color.BLACK;
186 return Utilities.getColorWithOverlay(primaryColor, overlayColor, 0.8f);
189 /** Binds the bar view to the task */
190 public void rebindToTask(Task t) {
191 // If an activity icon is defined, then we use that as the primary icon to show in the bar,
192 // otherwise, we fall back to the application icon
193 if (t.activityIcon != null) {
194 mApplicationIcon.setImageDrawable(t.activityIcon);
195 } else if (t.applicationIcon != null) {
196 mApplicationIcon.setImageDrawable(t.applicationIcon);
198 mApplicationIcon.setContentDescription(t.activityLabel);
199 if (!mActivityDescription.getText().toString().equals(t.activityLabel)) {
200 mActivityDescription.setText(t.activityLabel);
202 // Try and apply the system ui tint
203 int existingBgColor = (getBackground() instanceof ColorDrawable) ?
204 ((ColorDrawable) getBackground()).getColor() : 0;
205 if (existingBgColor != t.colorPrimary) {
206 mBackgroundColorDrawable.setColor(t.colorPrimary);
207 mBackgroundColor = t.colorPrimary;
209 mCurrentPrimaryColor = t.colorPrimary;
210 mCurrentPrimaryColorIsDark = t.useLightOnPrimaryColor;
211 mActivityDescription.setTextColor(t.useLightOnPrimaryColor ?
212 mConfig.taskBarViewLightTextColor : mConfig.taskBarViewDarkTextColor);
213 mDismissButton.setImageDrawable(t.useLightOnPrimaryColor ?
214 mLightDismissDrawable : mDarkDismissDrawable);
215 mDismissButton.setContentDescription(String.format(mDismissContentDescription,
219 /** Unbinds the bar view from the task */
220 void unbindFromTask() {
221 mApplicationIcon.setImageDrawable(null);
224 /** Animates this task bar dismiss button when launching a task. */
225 void startLaunchTaskDismissAnimation() {
226 if (mDismissButton.getVisibility() == View.VISIBLE) {
227 mDismissButton.animate().cancel();
228 mDismissButton.animate()
231 .setInterpolator(mConfig.fastOutSlowInInterpolator)
232 .setDuration(mConfig.taskViewExitToAppDuration)
238 /** Animates this task bar if the user does not interact with the stack after a certain time. */
239 void startNoUserInteractionAnimation() {
240 if (mDismissButton.getVisibility() != View.VISIBLE) {
241 mDismissButton.setVisibility(View.VISIBLE);
242 mDismissButton.setAlpha(0f);
243 mDismissButton.animate()
246 .setInterpolator(mConfig.fastOutLinearInInterpolator)
247 .setDuration(mConfig.taskViewEnterFromAppDuration)
253 /** Mark this task view that the user does has not interacted with the stack after a certain time. */
254 void setNoUserInteractionState() {
255 if (mDismissButton.getVisibility() != View.VISIBLE) {
256 mDismissButton.animate().cancel();
257 mDismissButton.setVisibility(View.VISIBLE);
258 mDismissButton.setAlpha(1f);
262 /** Resets the state tracking that the user has not interacted with the stack after a certain time. */
263 void resetNoUserInteractionState() {
264 mDismissButton.setVisibility(View.INVISIBLE);
268 protected int[] onCreateDrawableState(int extraSpace) {
270 // Don't forward our state to the drawable - we do it manually in onTaskViewFocusChanged.
271 // This is to prevent layer trashing when the view is pressed.
275 /** Notifies the associated TaskView has been focused. */
276 void onTaskViewFocusChanged(boolean focused, boolean animateFocusedState) {
277 // If we are not animating the visible state, just return
278 if (!animateFocusedState) return;
280 boolean isRunning = false;
281 if (mFocusAnimator != null) {
282 isRunning = mFocusAnimator.isRunning();
283 Utilities.cancelAnimationWithoutCallbacks(mFocusAnimator);
287 int secondaryColor = getSecondaryColor(mCurrentPrimaryColor, mCurrentPrimaryColorIsDark);
288 int[][] states = new int[][] {
289 new int[] { android.R.attr.state_enabled },
290 new int[] { android.R.attr.state_pressed }
292 int[] newStates = new int[]{
293 android.R.attr.state_enabled,
294 android.R.attr.state_pressed
296 int[] colors = new int[] {
300 mBackground.setColor(new ColorStateList(states, colors));
301 mBackground.setState(newStates);
302 // Pulse the background color
303 int currentColor = mBackgroundColor;
304 int lightPrimaryColor = getSecondaryColor(mCurrentPrimaryColor, mCurrentPrimaryColorIsDark);
305 ValueAnimator backgroundColor = ValueAnimator.ofObject(new ArgbEvaluator(),
306 currentColor, lightPrimaryColor);
307 backgroundColor.addListener(new AnimatorListenerAdapter() {
309 public void onAnimationStart(Animator animation) {
310 mBackground.setState(new int[]{});
313 backgroundColor.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
315 public void onAnimationUpdate(ValueAnimator animation) {
316 int color = (int) animation.getAnimatedValue();
317 mBackgroundColorDrawable.setColor(color);
318 mBackgroundColor = color;
321 backgroundColor.setRepeatCount(ValueAnimator.INFINITE);
322 backgroundColor.setRepeatMode(ValueAnimator.REVERSE);
323 // Pulse the translation
324 ObjectAnimator translation = ObjectAnimator.ofFloat(this, "translationZ", 15f);
325 translation.setRepeatCount(ValueAnimator.INFINITE);
326 translation.setRepeatMode(ValueAnimator.REVERSE);
328 mFocusAnimator = new AnimatorSet();
329 mFocusAnimator.playTogether(backgroundColor, translation);
330 mFocusAnimator.setStartDelay(750);
331 mFocusAnimator.setDuration(750);
332 mFocusAnimator.start();
335 // Restore the background color
336 int currentColor = mBackgroundColor;
337 ValueAnimator backgroundColor = ValueAnimator.ofObject(new ArgbEvaluator(),
338 currentColor, mCurrentPrimaryColor);
339 backgroundColor.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
341 public void onAnimationUpdate(ValueAnimator animation) {
342 int color = (int) animation.getAnimatedValue();
343 mBackgroundColorDrawable.setColor(color);
344 mBackgroundColor = color;
347 // Restore the translation
348 ObjectAnimator translation = ObjectAnimator.ofFloat(this, "translationZ", 0f);
350 mFocusAnimator = new AnimatorSet();
351 mFocusAnimator.playTogether(backgroundColor, translation);
352 mFocusAnimator.setDuration(150);
353 mFocusAnimator.start();
355 mBackground.setState(new int[] {});