FileDocCategorySizeDatePackage
TaskView.javaAPI DocAndroid 5.1 API29239Thu Mar 12 22:22:42 GMT 2015com.android.systemui.recents.views

TaskView

public class TaskView extends android.widget.FrameLayout implements Task.TaskCallbacks, View.OnClickListener, View.OnLongClickListener

Fields Summary
com.android.systemui.recents.RecentsConfiguration
mConfig
float
mTaskProgress
android.animation.ObjectAnimator
mTaskProgressAnimator
float
mMaxDimScale
int
mDimAlpha
android.view.animation.AccelerateInterpolator
mDimInterpolator
PorterDuffColorFilter
mDimColorFilter
Paint
mDimLayerPaint
float
mActionButtonTranslationZ
com.android.systemui.recents.model.Task
mTask
boolean
mTaskDataLoaded
boolean
mIsFocused
boolean
mFocusAnimationsEnabled
boolean
mClipViewInStack
AnimateableViewBounds
mViewBounds
android.view.View
mContent
TaskViewThumbnail
mThumbnailView
TaskViewHeader
mHeaderView
android.view.View
mActionButtonView
TaskViewCallbacks
mCb
ValueAnimator.AnimatorUpdateListener
mUpdateDimListener
Constructors Summary
public TaskView(android.content.Context context)



       
        this(context, null);
    
public TaskView(android.content.Context context, android.util.AttributeSet attrs)

        this(context, attrs, 0);
    
public TaskView(android.content.Context context, android.util.AttributeSet attrs, int defStyleAttr)

        this(context, attrs, defStyleAttr, 0);
    
public TaskView(android.content.Context context, android.util.AttributeSet attrs, int defStyleAttr, int defStyleRes)

        super(context, attrs, defStyleAttr, defStyleRes);
        mConfig = RecentsConfiguration.getInstance();
        mMaxDimScale = mConfig.taskStackMaxDim / 255f;
        mClipViewInStack = true;
        mViewBounds = new AnimateableViewBounds(this, mConfig.taskViewRoundedCornerRadiusPx);
        setTaskProgress(getTaskProgress());
        setDim(getDim());
        if (mConfig.fakeShadows) {
            setBackground(new FakeShadowDrawable(context.getResources(), mConfig));
        }
        setOutlineProvider(mViewBounds);
    
Methods Summary
voidanimateDimToProgress(int delay, int duration, Animator.AnimatorListener postAnimRunnable)
Animates the dim to the task progress.

        // Animate the dim into view as well
        int toDim = getDimFromTaskProgress();
        if (toDim != getDim()) {
            ObjectAnimator anim = ObjectAnimator.ofInt(TaskView.this, "dim", toDim);
            anim.setStartDelay(delay);
            anim.setDuration(duration);
            if (postAnimRunnable != null) {
                anim.addListener(postAnimRunnable);
            }
            anim.start();
        }
    
voiddismissTask()
Dismisses this task.

        // Animate out the view and call the callback
        final TaskView tv = this;
        startDeleteTaskAnimation(new Runnable() {
            @Override
            public void run() {
                if (mCb != null) {
                    mCb.onTaskViewDismissed(tv);
                }
            }
        });
    
voidenableFocusAnimations()
Enables all focus animations.

        boolean wasFocusAnimationsEnabled = mFocusAnimationsEnabled;
        mFocusAnimationsEnabled = true;
        if (mIsFocused && !wasFocusAnimationsEnabled) {
            // Re-notify the header if we were focused and animations were not previously enabled
            mHeaderView.onTaskViewFocusChanged(true, true);
        }
    
public voidfadeInActionButton(int delay, int duration)

        // Hide the action button
        mActionButtonView.setAlpha(0f);

        // Animate the action button in
        mActionButtonView.animate().alpha(1f)
                .setStartDelay(delay)
                .setDuration(duration)
                .setInterpolator(PhoneStatusBar.ALPHA_IN)
                .withLayer()
                .start();
    
public intgetDim()
Returns the current dim.

        return mDimAlpha;
    
intgetDimFromTaskProgress()
Compute the dim as a function of the scale of this view.

        float dim = mMaxDimScale * mDimInterpolator.getInterpolation(1f - mTaskProgress);
        return (int) (dim * 255);
    
com.android.systemui.recents.model.TaskgetTask()
Gets the task

        return mTask;
    
public floatgetTaskProgress()
Returns the current task progress.

        return mTaskProgress;
    
AnimateableViewBoundsgetViewBounds()
Returns the view bounds.

        return mViewBounds;
    
public booleanisFocusedTask()
Returns whether we have explicitly been focused.

        return mIsFocused || isFocused();
    
public voidonClick(android.view.View v)
View.OnClickListener Implementation

        final TaskView tv = this;
        final boolean delayViewClick = (v != this) && (v != mActionButtonView);
        if (delayViewClick) {
            // We purposely post the handler delayed to allow for the touch feedback to draw
            postDelayed(new Runnable() {
                @Override
                public void run() {
                    if (Constants.DebugFlags.App.EnableTaskFiltering && v == mHeaderView.mApplicationIcon) {
                        if (mCb != null) {
                            mCb.onTaskViewAppIconClicked(tv);
                        }
                    } else if (v == mHeaderView.mDismissButton) {
                        dismissTask();
                    }
                }
            }, 125);
        } else {
            if (v == mActionButtonView) {
                // Reset the translation of the action button before we animate it out
                mActionButtonView.setTranslationZ(0f);
            }
            if (mCb != null) {
                mCb.onTaskViewClicked(tv, tv.getTask(), (v == mActionButtonView));
            }
        }
    
protected voidonFinishInflate()

        // Bind the views
        mContent = findViewById(R.id.task_view_content);
        mHeaderView = (TaskViewHeader) findViewById(R.id.task_view_bar);
        mThumbnailView = (TaskViewThumbnail) findViewById(R.id.task_view_thumbnail);
        mThumbnailView.updateClipToTaskBar(mHeaderView);
        mActionButtonView = findViewById(R.id.lock_to_app_fab);
        mActionButtonView.setOutlineProvider(new ViewOutlineProvider() {
            @Override
            public void getOutline(View view, Outline outline) {
                // Set the outline to match the FAB background
                outline.setOval(0, 0, mActionButtonView.getWidth(), mActionButtonView.getHeight());
            }
        });
        mActionButtonTranslationZ = mActionButtonView.getTranslationZ();
    
protected voidonFocusChanged(boolean gainFocus, int direction, Rect previouslyFocusedRect)
Updates the explicitly focused state when the view focus changes.

        super.onFocusChanged(gainFocus, direction, previouslyFocusedRect);
        if (!gainFocus) {
            unsetFocusedTask();
        }
    
public booleanonLongClick(android.view.View v)
View.OnLongClickListener Implementation

        if (v == mHeaderView.mApplicationIcon) {
            if (mCb != null) {
                mCb.onTaskViewAppInfoClicked(this);
                return true;
            }
        }
        return false;
    
protected voidonMeasure(int widthMeasureSpec, int heightMeasureSpec)

        int width = MeasureSpec.getSize(widthMeasureSpec);
        int height = MeasureSpec.getSize(heightMeasureSpec);

        int widthWithoutPadding = width - mPaddingLeft - mPaddingRight;
        int heightWithoutPadding = height - mPaddingTop - mPaddingBottom;

        // Measure the content
        mContent.measure(MeasureSpec.makeMeasureSpec(widthWithoutPadding, MeasureSpec.EXACTLY),
                MeasureSpec.makeMeasureSpec(widthWithoutPadding, MeasureSpec.EXACTLY));

        // Measure the bar view, and action button
        mHeaderView.measure(MeasureSpec.makeMeasureSpec(widthWithoutPadding, MeasureSpec.EXACTLY),
                MeasureSpec.makeMeasureSpec(mConfig.taskBarHeight, MeasureSpec.EXACTLY));
        mActionButtonView.measure(
                MeasureSpec.makeMeasureSpec(widthWithoutPadding, MeasureSpec.AT_MOST),
                MeasureSpec.makeMeasureSpec(heightWithoutPadding, MeasureSpec.AT_MOST));
        // Measure the thumbnail to be square
        mThumbnailView.measure(
                MeasureSpec.makeMeasureSpec(widthWithoutPadding, MeasureSpec.EXACTLY),
                MeasureSpec.makeMeasureSpec(widthWithoutPadding, MeasureSpec.EXACTLY));
        setMeasuredDimension(width, height);
        invalidateOutline();
    
public voidonTaskBound(com.android.systemui.recents.model.Task t)
Binds this task view to the task

        mTask = t;
        mTask.setCallbacks(this);

        // Hide the action button if lock to app is disabled for this view
        int lockButtonVisibility = (!t.lockToTaskEnabled || !t.lockToThisTask) ? GONE : VISIBLE;
        if (mActionButtonView.getVisibility() != lockButtonVisibility) {
            mActionButtonView.setVisibility(lockButtonVisibility);
            requestLayout();
        }
    
public voidonTaskDataLoaded()

        if (mThumbnailView != null && mHeaderView != null) {
            // Bind each of the views to the new task data
            mThumbnailView.rebindToTask(mTask);
            mHeaderView.rebindToTask(mTask);
            // Rebind any listeners
            mHeaderView.mApplicationIcon.setOnClickListener(this);
            mHeaderView.mDismissButton.setOnClickListener(this);
            mActionButtonView.setOnClickListener(this);
            if (Constants.DebugFlags.App.EnableDevAppInfoOnLongPress) {
                if (mConfig.developerOptionsEnabled) {
                    mHeaderView.mApplicationIcon.setOnLongClickListener(this);
                }
            }
        }
        mTaskDataLoaded = true;
    
public voidonTaskDataUnloaded()

        if (mThumbnailView != null && mHeaderView != null) {
            // Unbind each of the views from the task data and remove the task callback
            mTask.setCallbacks(null);
            mThumbnailView.unbindFromTask();
            mHeaderView.unbindFromTask();
            // Unbind any listeners
            mHeaderView.mApplicationIcon.setOnClickListener(null);
            mHeaderView.mDismissButton.setOnClickListener(null);
            mActionButtonView.setOnClickListener(null);
            if (Constants.DebugFlags.App.EnableDevAppInfoOnLongPress) {
                mHeaderView.mApplicationIcon.setOnLongClickListener(null);
            }
        }
        mTaskDataLoaded = false;
    
voidprepareEnterRecentsAnimation(boolean isTaskViewLaunchTargetTask, boolean occludesLaunchTarget, int offscreenY)
Prepares this task view for the enter-recents animations. This is called earlier in the first layout because the actual animation into recents may take a long time.

        int initialDim = getDim();
        if (mConfig.launchedHasConfigurationChanged) {
            // Just load the views as-is
        } else if (mConfig.launchedFromAppWithThumbnail) {
            if (isTaskViewLaunchTargetTask) {
                // Set the dim to 0 so we can animate it in
                initialDim = 0;
                // Hide the action button
                mActionButtonView.setAlpha(0f);
            } else if (occludesLaunchTarget) {
                // Move the task view off screen (below) so we can animate it in
                setTranslationY(offscreenY);
            }

        } else if (mConfig.launchedFromHome) {
            // Move the task view off screen (below) so we can animate it in
            setTranslationY(offscreenY);
            setTranslationZ(0);
            setScaleX(1f);
            setScaleY(1f);
        }
        // Apply the current dim
        setDim(initialDim);
        // Prepare the thumbnail view alpha
        mThumbnailView.prepareEnterRecentsAnimation(isTaskViewLaunchTargetTask);
    
voidprepareTaskTransformForFilterTaskHidden(TaskViewTransform toTransform)
When we are un/filtering, this method will set up the transform that we are animating to, in order to hide the task.

        // Fade the view out and slide it away
        toTransform.alpha = 0f;
        toTransform.translationY += 200;
        toTransform.translationZ = 0;
    
voidprepareTaskTransformForFilterTaskVisible(TaskViewTransform fromTransform)
When we are un/filtering, this method will setup the transform that we are animating from, in order to show the task.

        // Fade the view in
        fromTransform.alpha = 0f;
    
voidreset()
Resets this TaskView for reuse.

        resetViewProperties();
        resetNoUserInteractionState();
        setClipViewInStack(false);
        setCallbacks(null);
    
voidresetNoUserInteractionState()
Resets the state tracking that the user has not interacted with the stack after a certain time.

        mHeaderView.resetNoUserInteractionState();
    
voidresetViewProperties()
Resets this view's properties

        setDim(0);
        setLayerType(View.LAYER_TYPE_NONE, null);
        TaskViewTransform.reset(this);
        if (mActionButtonView != null) {
            mActionButtonView.setScaleX(1f);
            mActionButtonView.setScaleY(1f);
            mActionButtonView.setAlpha(1f);
            mActionButtonView.setTranslationZ(mActionButtonTranslationZ);
        }
    
voidsetCallbacks(com.android.systemui.recents.views.TaskView$TaskViewCallbacks cb)
Set callback

        mCb = cb;
    
voidsetClipViewInStack(boolean clip)
Sets whether this view should be clipped, or clipped against.

        if (clip != mClipViewInStack) {
            mClipViewInStack = clip;
            if (mCb != null) {
                mCb.onTaskViewClipStateChanged(this);
            }
        }
    
public voidsetDim(int dim)
Returns the current dim.

        mDimAlpha = dim;
        if (mConfig.useHardwareLayers) {
            // Defer setting hardware layers if we have not yet measured, or there is no dim to draw
            if (getMeasuredWidth() > 0 && getMeasuredHeight() > 0) {
                mDimColorFilter.setColor(Color.argb(mDimAlpha, 0, 0, 0));
                mDimLayerPaint.setColorFilter(mDimColorFilter);
                mContent.setLayerType(LAYER_TYPE_HARDWARE, mDimLayerPaint);
            }
        } else {
            float dimAlpha = mDimAlpha / 255.0f;
            if (mThumbnailView != null) {
                mThumbnailView.setDimAlpha(dimAlpha);
            }
            if (mHeaderView != null) {
                mHeaderView.setDimAlpha(dim);
            }
        }
    
public voidsetFocusedTask(boolean animateFocusedState)
Sets the focused task explicitly. We need a separate flag because requestFocus() won't happen if the view is not currently visible, or we are in touch state (where we still want to keep track of focus).

        mIsFocused = true;
        if (mFocusAnimationsEnabled) {
            // Focus the header bar
            mHeaderView.onTaskViewFocusChanged(true, animateFocusedState);
        }
        // Update the thumbnail alpha with the focus
        mThumbnailView.onFocusChanged(true);
        // Call the callback
        if (mCb != null) {
            mCb.onTaskViewFocusChanged(this, true);
        }
        // Workaround, we don't always want it focusable in touch mode, but we want the first task
        // to be focused after the enter-recents animation, which can be triggered from either touch
        // or keyboard
        setFocusableInTouchMode(true);
        requestFocus();
        setFocusableInTouchMode(false);
        invalidate();
    
voidsetNoUserInteractionState()
Mark this task view that the user does has not interacted with the stack after a certain time.

        mHeaderView.setNoUserInteractionState();
    
public voidsetTaskProgress(float p)
Sets the current task progress.

        mTaskProgress = p;
        mViewBounds.setAlpha(p);
        updateDimFromTaskProgress();
    
voidsetTouchEnabled(boolean enabled)
Enables/disables handling touch on this task view.

        setOnClickListener(enabled ? this : null);
    
booleanshouldClipViewInStack()
Returns whether this view should be clipped, or any views below should clip against this view.

        return mClipViewInStack && (getVisibility() == View.VISIBLE);
    
voidstartDeleteTaskAnimation(java.lang.Runnable r)
Animates the deletion of this task view

        // Disabling clipping with the stack while the view is animating away
        setClipViewInStack(false);

        animate().translationX(mConfig.taskViewRemoveAnimTranslationXPx)
            .alpha(0f)
            .setStartDelay(0)
            .setUpdateListener(null)
            .setInterpolator(mConfig.fastOutSlowInInterpolator)
            .setDuration(mConfig.taskViewRemoveAnimDuration)
            .withEndAction(new Runnable() {
                @Override
                public void run() {
                    // We just throw this into a runnable because starting a view property
                    // animation using layers can cause inconsisten results if we try and
                    // update the layers while the animation is running.  In some cases,
                    // the runnabled passed in may start an animation which also uses layers
                    // so we defer all this by posting this.
                    r.run();

                    // Re-enable clipping with the stack (we will reuse this view)
                    setClipViewInStack(true);
                }
            })
            .start();
    
voidstartEnterRecentsAnimation(ViewAnimation.TaskViewEnterContext ctx)
Animates this task view as it enters recents

        final TaskViewTransform transform = ctx.currentTaskTransform;
        int startDelay = 0;

        if (mConfig.launchedFromAppWithThumbnail) {
            if (mTask.isLaunchTarget) {
                // Animate the dim/overlay
                if (Constants.DebugFlags.App.EnableThumbnailAlphaOnFrontmost) {
                    // Animate the thumbnail alpha before the dim animation (to prevent updating the
                    // hardware layer)
                    mThumbnailView.startEnterRecentsAnimation(mConfig.transitionEnterFromAppDelay,
                            new Runnable() {
                                @Override
                                public void run() {
                                    animateDimToProgress(0, mConfig.taskViewEnterFromAppDuration,
                                            ctx.postAnimationTrigger.decrementOnAnimationEnd());
                                }
                            });
                } else {
                    // Immediately start the dim animation
                    animateDimToProgress(mConfig.transitionEnterFromAppDelay,
                            mConfig.taskViewEnterFromAppDuration,
                            ctx.postAnimationTrigger.decrementOnAnimationEnd());
                }
                ctx.postAnimationTrigger.increment();

                // Animate the action button in
                fadeInActionButton(mConfig.transitionEnterFromAppDelay,
                        mConfig.taskViewEnterFromAppDuration);
            } else {
                // Animate the task up if it was occluding the launch target
                if (ctx.currentTaskOccludesLaunchTarget) {
                    setTranslationY(transform.translationY + mConfig.taskViewAffiliateGroupEnterOffsetPx);
                    setAlpha(0f);
                    animate().alpha(1f)
                            .translationY(transform.translationY)
                            .setStartDelay(mConfig.transitionEnterFromAppDelay)
                            .setUpdateListener(null)
                            .setInterpolator(mConfig.fastOutSlowInInterpolator)
                            .setDuration(mConfig.taskViewEnterFromHomeDuration)
                            .withEndAction(new Runnable() {
                                @Override
                                public void run() {
                                    // Decrement the post animation trigger
                                    ctx.postAnimationTrigger.decrement();
                                }
                            })
                            .start();
                    ctx.postAnimationTrigger.increment();
                }
            }
            startDelay = mConfig.transitionEnterFromAppDelay;

        } else if (mConfig.launchedFromHome) {
            // Animate the tasks up
            int frontIndex = (ctx.currentStackViewCount - ctx.currentStackViewIndex - 1);
            int delay = mConfig.transitionEnterFromHomeDelay +
                    frontIndex * mConfig.taskViewEnterFromHomeStaggerDelay;

            setScaleX(transform.scale);
            setScaleY(transform.scale);
            if (!mConfig.fakeShadows) {
                animate().translationZ(transform.translationZ);
            }
            animate()
                    .translationY(transform.translationY)
                    .setStartDelay(delay)
                    .setUpdateListener(ctx.updateListener)
                    .setInterpolator(mConfig.quintOutInterpolator)
                    .setDuration(mConfig.taskViewEnterFromHomeDuration +
                            frontIndex * mConfig.taskViewEnterFromHomeStaggerDelay)
                    .withEndAction(new Runnable() {
                        @Override
                        public void run() {
                            // Decrement the post animation trigger
                            ctx.postAnimationTrigger.decrement();
                        }
                    })
                    .start();
            ctx.postAnimationTrigger.increment();
            startDelay = delay;
        }

        // Enable the focus animations from this point onwards so that they aren't affected by the
        // window transitions
        postDelayed(new Runnable() {
            @Override
            public void run() {
                enableFocusAnimations();
            }
        }, startDelay);
    
voidstartExitToHomeAnimation(ViewAnimation.TaskViewExitContext ctx)
Animates this task view as it leaves recents by pressing home.

        animate()
                .translationY(ctx.offscreenTranslationY)
                .setStartDelay(0)
                .setUpdateListener(null)
                .setInterpolator(mConfig.fastOutLinearInInterpolator)
                .setDuration(mConfig.taskViewExitToHomeDuration)
                .withEndAction(ctx.postAnimationTrigger.decrementAsRunnable())
                .start();
        ctx.postAnimationTrigger.increment();
    
voidstartLaunchTaskAnimation(java.lang.Runnable postAnimRunnable, boolean isLaunchingTask, boolean occludesLaunchTarget, boolean lockToTask)
Animates this task view as it exits recents

        if (isLaunchingTask) {
            // Animate the thumbnail alpha back into full opacity for the window animation out
            mThumbnailView.startLaunchTaskAnimation(postAnimRunnable);

            // Animate the dim
            if (mDimAlpha > 0) {
                ObjectAnimator anim = ObjectAnimator.ofInt(this, "dim", 0);
                anim.setDuration(mConfig.taskViewExitToAppDuration);
                anim.setInterpolator(mConfig.fastOutLinearInInterpolator);
                anim.start();
            }

            // Animate the action button away
            if (!lockToTask) {
                float toScale = 0.9f;
                mActionButtonView.animate()
                        .scaleX(toScale)
                        .scaleY(toScale);
            }
            mActionButtonView.animate()
                    .alpha(0f)
                    .setStartDelay(0)
                    .setDuration(mConfig.taskViewExitToAppDuration)
                    .setInterpolator(mConfig.fastOutLinearInInterpolator)
                    .withLayer()
                    .start();
        } else {
            // Hide the dismiss button
            mHeaderView.startLaunchTaskDismissAnimation();
            // If this is another view in the task grouping and is in front of the launch task,
            // animate it away first
            if (occludesLaunchTarget) {
                animate().alpha(0f)
                    .translationY(getTranslationY() + mConfig.taskViewAffiliateGroupEnterOffsetPx)
                    .setStartDelay(0)
                    .setUpdateListener(null)
                    .setInterpolator(mConfig.fastOutLinearInInterpolator)
                    .setDuration(mConfig.taskViewExitToAppDuration)
                    .start();
            }
        }
    
voidstartNoUserInteractionAnimation()
Animates this task view if the user does not interact with the stack after a certain time.

        mHeaderView.startNoUserInteractionAnimation();
    
voidunsetFocusedTask()
Unsets the focused task explicitly.

        mIsFocused = false;
        if (mFocusAnimationsEnabled) {
            // Un-focus the header bar
            mHeaderView.onTaskViewFocusChanged(false, true);
        }

        // Update the thumbnail alpha with the focus
        mThumbnailView.onFocusChanged(false);
        // Call the callback
        if (mCb != null) {
            mCb.onTaskViewFocusChanged(this, false);
        }
        invalidate();
    
voidupdateDimFromTaskProgress()
Update the dim as a function of the scale of this view.

        setDim(getDimFromTaskProgress());
    
voidupdateViewPropertiesToTaskTransform(TaskViewTransform toTransform, int duration)
Synchronizes this view's properties with the task's transform

        updateViewPropertiesToTaskTransform(toTransform, duration, null);
    
voidupdateViewPropertiesToTaskTransform(TaskViewTransform toTransform, int duration, ValueAnimator.AnimatorUpdateListener updateCallback)

        // Apply the transform
        toTransform.applyToTaskView(this, duration, mConfig.fastOutSlowInInterpolator, false,
                !mConfig.fakeShadows, updateCallback);

        // Update the task progress
        Utilities.cancelAnimationWithoutCallbacks(mTaskProgressAnimator);
        if (duration <= 0) {
            setTaskProgress(toTransform.p);
        } else {
            mTaskProgressAnimator = ObjectAnimator.ofFloat(this, "taskProgress", toTransform.p);
            mTaskProgressAnimator.setDuration(duration);
            mTaskProgressAnimator.addUpdateListener(mUpdateDimListener);
            mTaskProgressAnimator.start();
        }