/** * 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. */ void prepareEnterRecentsAnimation( boolean isTaskViewLaunchTargetTask, boolean occludesLaunchTarget, int offscreenY) { 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); }
@Override protected void onMeasure(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(); }
/** Unsets the focused task explicitly. */ void unsetFocusedTask() { 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(); }
/** Animates this task view as it exits recents */ void startLaunchTaskAnimation( final Runnable postAnimRunnable, boolean isLaunchingTask, boolean occludesLaunchTarget, boolean lockToTask) { 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(); } } }
@Override public void onTaskDataUnloaded() { 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; }
@Override public void onTaskDataLoaded() { 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; }
@Override protected void onFinishInflate() { // 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(); }
/** Returns the current dim. */ public void setDim(int 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); } } }
/** * 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). */ public void setFocusedTask(boolean animateFocusedState) { 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(); }
/** Animates this task view as it enters recents */ void startEnterRecentsAnimation(final ViewAnimation.TaskViewEnterContext ctx) { 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); }