OSDN Git Service

am d5a114cd: am d3cc11a1: docs: Remove reference to handheld app for bitmap resource...
[android-x86/frameworks-base.git] / packages / SystemUI / src / com / android / systemui / recents / views / TaskViewHeader.java
1 /*
2  * Copyright (C) 2014 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 package com.android.systemui.recents.views;
18
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;
51
52
53 /* The task bar view */
54 public class TaskViewHeader extends FrameLayout {
55
56     RecentsConfiguration mConfig;
57
58     // Header views
59     ImageView mDismissButton;
60     ImageView mApplicationIcon;
61     TextView mActivityDescription;
62
63     // Header drawables
64     boolean mCurrentPrimaryColorIsDark;
65     int mCurrentPrimaryColor;
66     int mBackgroundColor;
67     Drawable mLightDismissDrawable;
68     Drawable mDarkDismissDrawable;
69     RippleDrawable mBackground;
70     GradientDrawable mBackgroundColorDrawable;
71     AnimatorSet mFocusAnimator;
72     String mDismissContentDescription;
73
74     // Static highlight that we draw at the top of each view
75     static Paint sHighlightPaint;
76
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);
80
81     public TaskViewHeader(Context context) {
82         this(context, null);
83     }
84
85     public TaskViewHeader(Context context, AttributeSet attrs) {
86         this(context, attrs, 0);
87     }
88
89     public TaskViewHeader(Context context, AttributeSet attrs, int defStyleAttr) {
90         this(context, attrs, defStyleAttr, 0);
91     }
92
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() {
99             @Override
100             public void getOutline(View view, Outline outline) {
101                 outline.setRect(0, 0, getMeasuredWidth(), getMeasuredHeight());
102             }
103         });
104
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);
111
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);
120         }
121     }
122
123     @Override
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;
127
128         return super.onTouchEvent(event);
129     }
130
131     @Override
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);
137
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);
142             }
143         }
144
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);
154     }
155
156     @Override
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);
166     }
167
168     @Override
169     public boolean hasOverlappingRendering() {
170         return false;
171     }
172
173     /**
174      * Sets the dim alpha, only used when we are not using hardware layers.
175      * (see RecentsConfiguration.useHardwareLayers)
176      */
177     void setDimAlpha(int alpha) {
178         mDimColorFilter.setColor(Color.argb(alpha, 0, 0, 0));
179         mDimLayerPaint.setColorFilter(mDimColorFilter);
180         setLayerType(LAYER_TYPE_HARDWARE, mDimLayerPaint);
181     }
182
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);
187     }
188
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);
197         }
198         mApplicationIcon.setContentDescription(t.activityLabel);
199         if (!mActivityDescription.getText().toString().equals(t.activityLabel)) {
200             mActivityDescription.setText(t.activityLabel);
201         }
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;
208         }
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,
216                 t.activityLabel));
217     }
218
219     /** Unbinds the bar view from the task */
220     void unbindFromTask() {
221         mApplicationIcon.setImageDrawable(null);
222     }
223
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()
229                     .alpha(0f)
230                     .setStartDelay(0)
231                     .setInterpolator(mConfig.fastOutSlowInInterpolator)
232                     .setDuration(mConfig.taskViewExitToAppDuration)
233                     .withLayer()
234                     .start();
235         }
236     }
237
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()
244                     .alpha(1f)
245                     .setStartDelay(0)
246                     .setInterpolator(mConfig.fastOutLinearInInterpolator)
247                     .setDuration(mConfig.taskViewEnterFromAppDuration)
248                     .withLayer()
249                     .start();
250         }
251     }
252
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);
259         }
260     }
261
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);
265     }
266
267     @Override
268     protected int[] onCreateDrawableState(int extraSpace) {
269
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.
272         return new int[] {};
273     }
274
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;
279
280         boolean isRunning = false;
281         if (mFocusAnimator != null) {
282             isRunning = mFocusAnimator.isRunning();
283             Utilities.cancelAnimationWithoutCallbacks(mFocusAnimator);
284         }
285
286         if (focused) {
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 }
291             };
292             int[] newStates = new int[]{
293                     android.R.attr.state_enabled,
294                     android.R.attr.state_pressed
295             };
296             int[] colors = new int[] {
297                     secondaryColor,
298                     secondaryColor
299             };
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() {
308                 @Override
309                 public void onAnimationStart(Animator animation) {
310                     mBackground.setState(new int[]{});
311                 }
312             });
313             backgroundColor.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
314                 @Override
315                 public void onAnimationUpdate(ValueAnimator animation) {
316                     int color = (int) animation.getAnimatedValue();
317                     mBackgroundColorDrawable.setColor(color);
318                     mBackgroundColor = color;
319                 }
320             });
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);
327
328             mFocusAnimator = new AnimatorSet();
329             mFocusAnimator.playTogether(backgroundColor, translation);
330             mFocusAnimator.setStartDelay(750);
331             mFocusAnimator.setDuration(750);
332             mFocusAnimator.start();
333         } else {
334             if (isRunning) {
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() {
340                     @Override
341                     public void onAnimationUpdate(ValueAnimator animation) {
342                         int color = (int) animation.getAnimatedValue();
343                         mBackgroundColorDrawable.setColor(color);
344                         mBackgroundColor = color;
345                     }
346                 });
347                 // Restore the translation
348                 ObjectAnimator translation = ObjectAnimator.ofFloat(this, "translationZ", 0f);
349
350                 mFocusAnimator = new AnimatorSet();
351                 mFocusAnimator.playTogether(backgroundColor, translation);
352                 mFocusAnimator.setDuration(150);
353                 mFocusAnimator.start();
354             } else {
355                 mBackground.setState(new int[] {});
356                 setTranslationZ(0f);
357             }
358         }
359     }
360 }