/** ** TaskStackView.TaskStackCallbacks Implementation *** */ @Override public void onTaskViewClicked( final TaskStackView stackView, final TaskView tv, final TaskStack stack, final Task task, final boolean lockToTask) { // Notify any callbacks of the launching of a new task if (mCb != null) { mCb.onTaskViewClicked(); } // Skip if we are already launching tasks if (mAlreadyLaunchingTask) { return; } mAlreadyLaunchingTask = true; // Upfront the processing of the thumbnail TaskViewTransform transform = new TaskViewTransform(); View sourceView; int offsetX = 0; int offsetY = 0; float stackScroll = stackView.getScroller().getStackScroll(); if (tv == null) { // If there is no actual task view, then use the stack view as the source view // and then offset to the expected transform rect, but bound this to just // outside the display rect (to ensure we don't animate from too far away) sourceView = stackView; transform = stackView.getStackAlgorithm().getStackTransform(task, stackScroll, transform, null); offsetX = transform.rect.left; offsetY = mConfig.displayRect.height(); } else { sourceView = tv.mThumbnailView; transform = stackView.getStackAlgorithm().getStackTransform(task, stackScroll, transform, null); } // Compute the thumbnail to scale up from final SystemServicesProxy ssp = RecentsTaskLoader.getInstance().getSystemServicesProxy(); ActivityOptions opts = null; if (task.thumbnail != null && task.thumbnail.getWidth() > 0 && task.thumbnail.getHeight() > 0) { Bitmap b; if (tv != null) { // Disable any focused state before we draw the header if (tv.isFocusedTask()) { tv.unsetFocusedTask(); } float scale = tv.getScaleX(); int fromHeaderWidth = (int) (tv.mHeaderView.getMeasuredWidth() * scale); int fromHeaderHeight = (int) (tv.mHeaderView.getMeasuredHeight() * scale); b = Bitmap.createBitmap(fromHeaderWidth, fromHeaderHeight, Bitmap.Config.ARGB_8888); if (Constants.DebugFlags.App.EnableTransitionThumbnailDebugMode) { b.eraseColor(0xFFff0000); } else { Canvas c = new Canvas(b); c.scale(tv.getScaleX(), tv.getScaleY()); tv.mHeaderView.draw(c); c.setBitmap(null); } } else { // Notify the system to skip the thumbnail layer by using an ALPHA_8 bitmap b = Bitmap.createBitmap(1, 1, Bitmap.Config.ALPHA_8); } ActivityOptions.OnAnimationStartedListener animStartedListener = null; if (lockToTask) { animStartedListener = new ActivityOptions.OnAnimationStartedListener() { boolean mTriggered = false; @Override public void onAnimationStarted() { if (!mTriggered) { postDelayed( new Runnable() { @Override public void run() { ssp.lockCurrentTask(); } }, 350); mTriggered = true; } } }; } opts = ActivityOptions.makeThumbnailAspectScaleUpAnimation( sourceView, b, offsetX, offsetY, transform.rect.width(), transform.rect.height(), animStartedListener); } final ActivityOptions launchOpts = opts; final Runnable launchRunnable = new Runnable() { @Override public void run() { if (task.isActive) { // Bring an active task to the foreground ssp.moveTaskToFront(task.key.id, launchOpts); } else { if (ssp.startActivityFromRecents( getContext(), task.key.id, task.activityLabel, launchOpts)) { if (launchOpts == null && lockToTask) { ssp.lockCurrentTask(); } } else { // Dismiss the task and return the user to home if we fail to // launch the task onTaskViewDismissed(task); if (mCb != null) { mCb.onTaskLaunchFailed(); } } } } }; // Launch the app right away if there is no task view, otherwise, animate the icon out first if (tv == null) { post(launchRunnable); } else { if (!task.group.isFrontMostTask(task)) { // For affiliated tasks that are behind other tasks, we must animate the front cards // out of view before starting the task transition stackView.startLaunchTaskAnimation(tv, launchRunnable, lockToTask); } else { // Otherwise, we can start the task transition immediately stackView.startLaunchTaskAnimation(tv, null, lockToTask); postDelayed(launchRunnable, 17); } } }
/** ** TaskStackView.TaskStackCallbacks Implementation *** */ @Override public void onTaskViewClicked( final TaskStackView stackView, final TaskView tv, final TaskStack stack, final Task task, final boolean lockToTask) { Log.d(TAG, "onTaskViewClicked: "); // Notify any callbacks of the launching of a new task if (mCb != null) { mCb.onTaskViewClicked(); } // Upfront the processing of the thumbnail TaskViewTransform transform = new TaskViewTransform(); View sourceView; int offsetX = 0; int offsetY = 0; float stackScroll = stackView.getScroller().getStackScroll(); if (tv == null) { // If there is no actual task view, then use the stack view as the source view // and then offset to the expected transform rect, but bound this to just // outside the display rect (to ensure we don't animate from too far away) sourceView = stackView; transform = stackView.getStackAlgorithm().getStackTransform(task, stackScroll, transform, null); offsetX = transform.rect.left; offsetY = mConfig.displayRect.height(); } else { sourceView = tv.mThumbnailView; transform = stackView.getStackAlgorithm().getStackTransform(task, stackScroll, transform, null); } // Compute the thumbnail to scale up from final SystemServicesProxy ssp = RecentsTaskLoader.getInstance().getSystemServicesProxy(); ActivityOptions opts = null; if (task.thumbnail != null && task.thumbnail.getWidth() > 0 && task.thumbnail.getHeight() > 0) { ActivityOptions.OnAnimationStartedListener animStartedListener = null; if (lockToTask) { animStartedListener = new ActivityOptions.OnAnimationStartedListener() { boolean mTriggered = false; @Override public void onAnimationStarted() { if (!mTriggered) { postDelayed( new Runnable() { @Override public void run() { mCb.onScreenPinningRequest(); } }, 350); mTriggered = true; } } }; } if (tv != null) { postDrawHeaderThumbnailTransitionRunnable( tv, offsetX, offsetY, transform, animStartedListener); } if (mConfig.multiStackEnabled) { opts = ActivityOptions.makeCustomAnimation( sourceView.getContext(), R.anim.recents_from_unknown_enter, R.anim.recents_from_unknown_exit, sourceView.getHandler(), animStartedListener); } else { opts = ActivityOptions.makeThumbnailAspectScaleUpAnimation( sourceView, Bitmap.createBitmap(1, 1, Bitmap.Config.ALPHA_8).createAshmemBitmap(), offsetX, offsetY, transform.rect.width(), transform.rect.height(), sourceView.getHandler(), animStartedListener); } } final ActivityOptions launchOpts = opts; final Runnable launchRunnable = new Runnable() { @Override public void run() { if (task.isActive) { // Bring an active task to the foreground ssp.moveTaskToFront(task.key.id, launchOpts); } else { if (ssp.startActivityFromRecents( getContext(), task.key.id, task.activityLabel, launchOpts)) { if (launchOpts == null && lockToTask) { mCb.onScreenPinningRequest(); } } else { // Dismiss the task and return the user to home if we fail to // launch the task onTaskViewDismissed(task); if (mCb != null) { mCb.onTaskLaunchFailed(); } // Keep track of failed launches MetricsLogger.count(getContext(), "overview_task_launch_failed", 1); } } } }; // Keep track of the index of the task launch int taskIndexFromFront = 0; int taskIndex = stack.indexOfTask(task); if (taskIndex > -1) { taskIndexFromFront = stack.getTaskCount() - taskIndex - 1; } MetricsLogger.histogram(getContext(), "overview_task_launch_index", taskIndexFromFront); // Launch the app right away if there is no task view, otherwise, animate the icon out first if (tv == null) { launchRunnable.run(); } else { if (task.group != null && !task.group.isFrontMostTask(task)) { // For affiliated tasks that are behind other tasks, we must animate the front cards // out of view before starting the task transition stackView.startLaunchTaskAnimation(tv, launchRunnable, lockToTask); } else { // Otherwise, we can start the task transition immediately stackView.startLaunchTaskAnimation(tv, null, lockToTask); launchRunnable.run(); } } }