blob: ba85932e912b72f1bc0cb2f8a188c689e7f94438 [file] [log] [blame]
/*
* Copyright (C) 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.systemui.recents.views;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ArgbEvaluator;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.content.Context;
import android.content.res.ColorStateList;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.GradientDrawable;
import android.graphics.drawable.RippleDrawable;
import android.graphics.Outline;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffColorFilter;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewOutlineProvider;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.TextView;
import com.android.systemui.R;
import com.android.systemui.recents.Constants;
import com.android.systemui.recents.RecentsConfiguration;
import com.android.systemui.recents.misc.SystemServicesProxy;
import com.android.systemui.recents.misc.Utilities;
import com.android.systemui.recents.model.RecentsTaskLoader;
import com.android.systemui.recents.model.Task;
/* The task bar view */
public class TaskViewHeader extends FrameLayout {
RecentsConfiguration mConfig;
private SystemServicesProxy mSsp;
Task mTask;
boolean mIsFocused;
float mFocusProgress;
// Header views
ImageView mMoveTaskButton;
ImageView mDismissButton;
ImageView mApplicationIcon;
TextView mActivityDescription;
// Header drawables
boolean mCurrentPrimaryColorIsDark;
int mCurrentPrimaryColor;
int mBackgroundColor;
Drawable mLightDismissDrawable;
Drawable mDarkDismissDrawable;
RippleDrawable mBackground;
GradientDrawable mBackgroundColorDrawable;
ValueAnimator mFocusAnimator;
String mDismissContentDescription;
// Static highlight that we draw at the top of each view
static Paint sHighlightPaint;
// Header dim, which is only used when task view hardware layers are not used
Paint mDimLayerPaint = new Paint();
PorterDuffColorFilter mDimColorFilter = new PorterDuffColorFilter(0, PorterDuff.Mode.SRC_ATOP);
boolean mLayersDisabled;
public TaskViewHeader(Context context) {
this(context, null);
}
public TaskViewHeader(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public TaskViewHeader(Context context, AttributeSet attrs, int defStyleAttr) {
this(context, attrs, defStyleAttr, 0);
}
public TaskViewHeader(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
mConfig = RecentsConfiguration.getInstance();
mSsp = RecentsTaskLoader.getInstance().getSystemServicesProxy();
setWillNotDraw(false);
setClipToOutline(true);
setOutlineProvider(new ViewOutlineProvider() {
@Override
public void getOutline(View view, Outline outline) {
outline.setRect(0, 0, getMeasuredWidth(), getMeasuredHeight());
}
});
// Load the dismiss resources
mLightDismissDrawable = context.getDrawable(R.drawable.recents_dismiss_light);
mDarkDismissDrawable = context.getDrawable(R.drawable.recents_dismiss_dark);
mDismissContentDescription =
context.getString(R.string.accessibility_recents_item_will_be_dismissed);
// Configure the highlight paint
if (sHighlightPaint == null) {
sHighlightPaint = new Paint();
sHighlightPaint.setStyle(Paint.Style.STROKE);
sHighlightPaint.setStrokeWidth(mConfig.taskViewHighlightPx);
sHighlightPaint.setColor(mConfig.taskBarViewHighlightColor);
sHighlightPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.ADD));
sHighlightPaint.setAntiAlias(true);
}
mFocusAnimator = ObjectAnimator.ofFloat(this, "focusProgress", 1f);
}
@Override
protected void onFinishInflate() {
// Initialize the icon and description views
mApplicationIcon = (ImageView) findViewById(R.id.application_icon);
mActivityDescription = (TextView) findViewById(R.id.activity_description);
mDismissButton = (ImageView) findViewById(R.id.dismiss_task);
mMoveTaskButton = (ImageView) findViewById(R.id.move_task);
// Hide the backgrounds if they are ripple drawables
if (!Constants.DebugFlags.App.EnableTaskFiltering) {
if (mApplicationIcon.getBackground() instanceof RippleDrawable) {
mApplicationIcon.setBackground(null);
}
}
mBackgroundColorDrawable = (GradientDrawable) getContext().getDrawable(R.drawable
.recents_task_view_header_bg_color);
// Copy the ripple drawable since we are going to be manipulating it
mBackground = (RippleDrawable)
getContext().getDrawable(R.drawable.recents_task_view_header_bg);
mBackground = (RippleDrawable) mBackground.mutate().getConstantState().newDrawable();
mBackground.setColor(ColorStateList.valueOf(0));
mBackground.setDrawableByLayerId(mBackground.getId(0), mBackgroundColorDrawable);
setBackground(mBackground);
}
@Override
protected void onDraw(Canvas canvas) {
// Draw the highlight at the top edge (but put the bottom edge just out of view)
float offset = (float) Math.ceil(mConfig.taskViewHighlightPx / 2f);
float radius = mConfig.taskViewRoundedCornerRadiusPx;
int count = canvas.save(Canvas.CLIP_SAVE_FLAG);
canvas.clipRect(0, 0, getMeasuredWidth(), getMeasuredHeight());
canvas.drawRoundRect(-offset, 0f, (float) getMeasuredWidth() + offset,
getMeasuredHeight() + radius, radius, radius, sHighlightPaint);
canvas.restoreToCount(count);
}
@Override
public boolean hasOverlappingRendering() {
return false;
}
/**
* Sets the dim alpha, only used when we are not using hardware layers.
* (see RecentsConfiguration.useHardwareLayers)
*/
void setDimAlpha(int alpha) {
mDimColorFilter.setColor(Color.argb(alpha, 0, 0, 0));
mDimLayerPaint.setColorFilter(mDimColorFilter);
if (!mLayersDisabled) {
setLayerType(LAYER_TYPE_HARDWARE, mDimLayerPaint);
}
}
/** Binds the bar view to the task */
public void rebindToTask(Task t) {
mTask = t;
// If an activity icon is defined, then we use that as the primary icon to show in the bar,
// otherwise, we fall back to the application icon
if (t.activityIcon != null) {
mApplicationIcon.setImageDrawable(t.activityIcon);
} else if (t.applicationIcon != null) {
mApplicationIcon.setImageDrawable(t.applicationIcon);
}
if (!mActivityDescription.getText().toString().equals(t.activityLabel)) {
mActivityDescription.setText(t.activityLabel);
}
mActivityDescription.setContentDescription(t.contentDescription);
// Try and apply the system ui tint
int existingBgColor = (getBackground() instanceof ColorDrawable) ?
((ColorDrawable) getBackground()).getColor() : 0;
if (existingBgColor != t.colorPrimary) {
mBackgroundColorDrawable.setColor(t.colorPrimary);
mBackgroundColor = t.colorPrimary;
}
mCurrentPrimaryColor = t.colorPrimary;
mCurrentPrimaryColorIsDark = t.useLightOnPrimaryColor;
mActivityDescription.setTextColor(t.useLightOnPrimaryColor ?
mConfig.taskBarViewLightTextColor : mConfig.taskBarViewDarkTextColor);
mDismissButton.setImageDrawable(t.useLightOnPrimaryColor ?
mLightDismissDrawable : mDarkDismissDrawable);
mDismissButton.setContentDescription(String.format(mDismissContentDescription,
t.contentDescription));
mMoveTaskButton.setVisibility((mConfig.multiStackEnabled) ? View.VISIBLE : View.INVISIBLE);
if (mConfig.multiStackEnabled) {
updateResizeTaskBarIcon(t);
}
}
/** Updates the resize task bar button. */
void updateResizeTaskBarIcon(Task t) {
Rect display = mSsp.getWindowRect();
Rect taskRect = mSsp.getTaskBounds(t.key.stackId);
int resId = R.drawable.star;
if (display.equals(taskRect) || taskRect.isEmpty()) {
resId = R.drawable.vector_drawable_place_fullscreen;
} else {
boolean top = display.top == taskRect.top;
boolean bottom = display.bottom == taskRect.bottom;
boolean left = display.left == taskRect.left;
boolean right = display.right == taskRect.right;
if (top && bottom && left) {
resId = R.drawable.vector_drawable_place_left;
} else if (top && bottom && right) {
resId = R.drawable.vector_drawable_place_right;
} else if (top && left && right) {
resId = R.drawable.vector_drawable_place_top;
} else if (bottom && left && right) {
resId = R.drawable.vector_drawable_place_bottom;
} else if (top && right) {
resId = R.drawable.vector_drawable_place_top_right;
} else if (top && left) {
resId = R.drawable.vector_drawable_place_top_left;
} else if (bottom && right) {
resId = R.drawable.vector_drawable_place_bottom_right;
} else if (bottom && left) {
resId = R.drawable.vector_drawable_place_bottom_left;
}
}
mMoveTaskButton.setImageResource(resId);
}
/** Unbinds the bar view from the task */
void unbindFromTask() {
mBackground.jumpToCurrentState();
if (mFocusAnimator != null) {
mFocusAnimator.cancel();
}
mApplicationIcon.setImageDrawable(null);
}
@Override
protected int[] onCreateDrawableState(int extraSpace) {
// Don't forward our state to the drawable - we do it manually in onTaskViewFocusChanged.
// This is to prevent layer trashing when the view is pressed.
return new int[] {};
}
@Override
protected void dispatchDraw(Canvas canvas) {
super.dispatchDraw(canvas);
if (mLayersDisabled) {
mLayersDisabled = false;
postOnAnimation(new Runnable() {
@Override
public void run() {
mLayersDisabled = false;
setLayerType(LAYER_TYPE_HARDWARE, mDimLayerPaint);
}
});
}
}
public void disableLayersForOneFrame() {
mLayersDisabled = true;
// Disable layer for a frame so we can draw our first frame faster.
setLayerType(LAYER_TYPE_NONE, null);
}
/** Sets the focus animation progress. Used by the property animator. */
public void setFocusProgress(float progress) {
mFocusProgress = progress;
final ArgbEvaluator colorEvaluator = new ArgbEvaluator();
int desaturatedColor = calculateDesauratedColor(mBackgroundColor);
int color = (Integer) colorEvaluator.evaluate(progress, mBackgroundColor,
mIsFocused ? desaturatedColor : mTask.colorPrimary);
mBackgroundColorDrawable.setColor(color);
mBackgroundColor = color;
TaskViewHeader.this.setTranslationZ((mIsFocused ? progress : (1 - progress)) * 1);
}
/** Returns the current focus animation progress. */
public float getFocusProgress() {
return mFocusProgress;
}
/** Returns a desaturated color value for a given color. */
private int calculateDesauratedColor(int saturatedColor) {
final float red = 0.2126f * Color.red(saturatedColor);
final float green = 0.7152f * Color.green(saturatedColor);
final float blue = 0.0722f * Color.blue(saturatedColor);
final int sum = (int) (red + green + blue);
return Color.argb(255, sum, sum, sum);
}
/** Notifies the associated TaskView has been focused. */
void onTaskViewFocusChanged(final boolean focused) {
mIsFocused = focused;
mFocusAnimator.setDuration(
mIsFocused ?
TaskView.sFocusInDurationMs :
TaskView.sFocusOutDurationMs);
mFocusAnimator.setInterpolator(
mIsFocused ?
TaskView.sFocusInRadiusInterpolator :
TaskView.sFocusOutInterpolator);
mFocusAnimator.start();
}
}