Fix black flash when splitting task

- Draw the thumbnail view and align with the thumbnail bounds instead of
  the whole task bounds with the icon
- Defer animating the task list until after the animation completes

Bug: 73118672
Test: Enter split screen
Change-Id: Ie10c079cb22ae82f3c5974296462abae335ef5a8
This commit is contained in:
Winson Chung
2018-03-15 17:59:04 -07:00
parent 0b0847b272
commit 8a968fab72
4 changed files with 91 additions and 19 deletions
@@ -198,8 +198,8 @@ public class OverviewSwipeController extends AnimatorListenerAdapter
}
} else {
if (goingUp) {
mPendingAnimation = mRecentsView
.createTaskDismissAnimation(mTaskBeingDragged, maxDuration);
mPendingAnimation = mRecentsView.createTaskDismissAnimation(mTaskBeingDragged,
true /* animateTaskView */, true /* removeTask */, maxDuration);
mCurrentAnimation = AnimatorPlaybackController
.wrap(mPendingAnimation.anim, maxDuration);
mEndDisplacement = -mTaskBeingDragged.getHeight();
@@ -16,6 +16,8 @@
package com.android.quickstep;
import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
import android.content.ComponentName;
import android.content.Intent;
import android.graphics.Bitmap;
@@ -31,12 +33,15 @@ import android.view.ViewTreeObserver.OnPreDrawListener;
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.R;
import com.android.launcher3.ShortcutInfo;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.popup.SystemShortcut;
import com.android.launcher3.util.InstantAppResolver;
import com.android.quickstep.views.RecentsView;
import com.android.quickstep.views.TaskThumbnailView;
import com.android.quickstep.views.TaskView;
import com.android.systemui.shared.recents.ISystemUiProxy;
import com.android.systemui.shared.recents.model.Task;
@@ -57,6 +62,7 @@ import java.util.function.Consumer;
public class TaskSystemShortcut<T extends SystemShortcut> extends SystemShortcut {
private static final String TAG = "TaskSystemShortcut";
private static final int DISMISS_TASK_DURATION = 300;
protected T mSystemShortcut;
@@ -99,10 +105,13 @@ public class TaskSystemShortcut<T extends SystemShortcut> extends SystemShortcut
}
}
public static class SplitScreen extends TaskSystemShortcut implements OnPreDrawListener {
public static class SplitScreen extends TaskSystemShortcut implements OnPreDrawListener,
DeviceProfile.OnDeviceProfileChangeListener, View.OnLayoutChangeListener {
private Handler mHandler;
private RecentsView mRecentsView;
private TaskView mTaskView;
private BaseDraggingActivity mActivity;
public SplitScreen() {
super(R.drawable.ic_split_screen, R.string.recent_task_option_split_screen);
@@ -116,15 +125,19 @@ public class TaskSystemShortcut<T extends SystemShortcut> extends SystemShortcut
return null;
}
final Task task = taskView.getTask();
final int taskId = task.key.id;
if (!task.isDockable) {
return null;
}
mActivity = activity;
mRecentsView = activity.getOverviewPanel();
mTaskView = taskView;
final TaskThumbnailView thumbnailView = taskView.getThumbnail();
return (v -> {
AbstractFloatingView.closeOpenViews(activity, true,
AbstractFloatingView.TYPE_ALL & ~AbstractFloatingView.TYPE_REBIND_SAFE);
if (ActivityManagerWrapper.getInstance().startActivityFromRecents(task.key.id,
if (ActivityManagerWrapper.getInstance().startActivityFromRecents(taskId,
ActivityOptionsCompat.makeSplitScreenOptions(true))) {
ISystemUiProxy sysUiProxy = RecentsModel.getInstance(activity).getSystemUiProxy();
try {
@@ -134,26 +147,35 @@ public class TaskSystemShortcut<T extends SystemShortcut> extends SystemShortcut
return;
}
// Add a device profile change listener to kick off animating the side tasks
// once we enter multiwindow mode and relayout
activity.addOnDeviceProfileChangeListener(this);
final Runnable animStartedListener = () -> {
// Hide the task view and wait for the window to be resized
// TODO: Consider animating in launcher and do an in-place start activity
// afterwards
mRecentsView.addIgnoreResetTask(mTaskView);
mTaskView.setAlpha(0f);
mTaskView.getViewTreeObserver().addOnPreDrawListener(SplitScreen.this);
activity.<RecentsView>getOverviewPanel().removeView(taskView);
};
final int[] position = new int[2];
taskView.getLocationOnScreen(position);
final int width = (int) (taskView.getWidth() * taskView.getScaleX());
final int height = (int) (taskView.getHeight() * taskView.getScaleY());
thumbnailView.getLocationOnScreen(position);
final int width = (int) (thumbnailView.getWidth() * taskView.getScaleX());
final int height = (int) (thumbnailView.getHeight() * taskView.getScaleY());
final Rect taskBounds = new Rect(position[0], position[1],
position[0] + width, position[1] + height);
Bitmap thumbnail = RecentsTransition.drawViewIntoHardwareBitmap(
taskBounds.width(), taskBounds.height(), taskView, 1f, Color.BLACK);
taskBounds.width(), taskBounds.height(), thumbnailView, 1f,
Color.BLACK);
AppTransitionAnimationSpecsFuture future =
new AppTransitionAnimationSpecsFuture(mHandler) {
@Override
public List<AppTransitionAnimationSpecCompat> composeSpecs() {
return Collections.singletonList(new AppTransitionAnimationSpecCompat(
task.key.id, thumbnail, taskBounds));
taskId, thumbnail, taskBounds));
}
};
WindowManagerWrapper.getInstance().overridePendingAppTransitionMultiThumbFuture(
@@ -168,6 +190,31 @@ public class TaskSystemShortcut<T extends SystemShortcut> extends SystemShortcut
WindowManagerWrapper.getInstance().endProlongedAnimations();
return true;
}
@Override
public void onDeviceProfileChanged(DeviceProfile dp) {
mActivity.removeOnDeviceProfileChangeListener(this);
if (dp.isMultiWindowMode) {
mTaskView.getRootView().addOnLayoutChangeListener(this);
}
}
@Override
public void onLayoutChange(View v, int l, int t, int r, int b,
int oldL, int oldT, int oldR, int oldB) {
mTaskView.getRootView().removeOnLayoutChangeListener(this);
mRecentsView.removeIgnoreResetTask(mTaskView);
// Start animating in the side pages once launcher has been resized
PendingAnimation pendingAnim = mRecentsView.createTaskDismissAnimation(mTaskView,
false, false, DISMISS_TASK_DURATION);
AnimatorPlaybackController controller = AnimatorPlaybackController.wrap(
pendingAnim.anim, DISMISS_TASK_DURATION);
controller.dispatchOnStart();
controller.setEndAction(() -> pendingAnim.finish(true));
controller.getAnimationPlayer().setInterpolator(FAST_OUT_SLOW_IN);
controller.start();
}
}
public static class Pin extends TaskSystemShortcut {
@@ -30,6 +30,7 @@ import android.content.SharedPreferences;
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
import android.graphics.Rect;
import android.os.Build;
import android.util.ArraySet;
import android.util.AttributeSet;
import android.util.SparseBooleanArray;
import android.view.LayoutInflater;
@@ -104,6 +105,9 @@ public abstract class RecentsView<T extends BaseActivity>
private PendingAnimation mPendingAnimation;
// Keeps track of task views whose visual state should not be reset
private ArraySet<TaskView> mIgnoreResetTaskViews = new ArraySet<>();
public RecentsView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
setPageSpacing(getResources().getDimensionPixelSize(R.dimen.recents_page_spacing));
@@ -261,7 +265,10 @@ public abstract class RecentsView<T extends BaseActivity>
public void resetTaskVisuals() {
for (int i = getChildCount() - 1; i >= 0; i--) {
((TaskView) getChildAt(i)).resetVisualProperties();
TaskView taskView = (TaskView) getChildAt(i);
if (!mIgnoreResetTaskViews.contains(taskView)) {
taskView.resetVisualProperties();
}
}
updateCurveProperties();
@@ -512,7 +519,16 @@ public abstract class RecentsView<T extends BaseActivity>
public float linearInterpolation;
}
public PendingAnimation createTaskDismissAnimation(TaskView taskView, long duration) {
public void addIgnoreResetTask(TaskView taskView) {
mIgnoreResetTaskViews.add(taskView);
}
public void removeIgnoreResetTask(TaskView taskView) {
mIgnoreResetTaskViews.remove(taskView);
}
public PendingAnimation createTaskDismissAnimation(TaskView taskView, boolean animateTaskView,
boolean removeTask, long duration) {
if (FeatureFlags.IS_DOGFOOD_BUILD && mPendingAnimation != null) {
throw new IllegalStateException("Another pending animation is still running");
}
@@ -543,9 +559,11 @@ public abstract class RecentsView<T extends BaseActivity>
for (int i = 0; i < count; i++) {
View child = getChildAt(i);
if (child == taskView) {
addAnim(ObjectAnimator.ofFloat(taskView, ALPHA, 0), duration, ACCEL_2, anim);
addAnim(ObjectAnimator.ofFloat(taskView, TRANSLATION_Y, -taskView.getHeight()),
duration, LINEAR, anim);
if (animateTaskView) {
addAnim(ObjectAnimator.ofFloat(taskView, ALPHA, 0), duration, ACCEL_2, anim);
addAnim(ObjectAnimator.ofFloat(taskView, TRANSLATION_Y, -taskView.getHeight()),
duration, LINEAR, anim);
}
} else {
int scrollDiff = newScroll[i] - oldScroll[i] + maxScrollDiff;
if (scrollDiff != 0) {
@@ -563,12 +581,16 @@ public abstract class RecentsView<T extends BaseActivity>
}
// Add a tiny bit of translation Z, so that it draws on top of other views
taskView.setTranslationZ(0.1f);
if (animateTaskView) {
taskView.setTranslationZ(0.1f);
}
mPendingAnimation = pendingAnimation;
mPendingAnimation.addEndListener((isSuccess) -> {
if (isSuccess) {
ActivityManagerWrapper.getInstance().removeTask(taskView.getTask().key.id);
if (removeTask) {
ActivityManagerWrapper.getInstance().removeTask(taskView.getTask().key.id);
}
removeView(taskView);
if (getChildCount() == 0) {
onAllTasksRemoved();
+5 -2
View File
@@ -98,9 +98,12 @@ public abstract class BaseActivity extends Activity {
mDPChangeListeners.add(listener);
}
public void removeOnDeviceProfileChangeListener(OnDeviceProfileChangeListener listener) {
mDPChangeListeners.remove(listener);
}
protected void dispatchDeviceProfileChanged() {
int count = mDPChangeListeners.size();
for (int i = 0; i < count; i++) {
for (int i = mDPChangeListeners.size() - 1; i >= 0; i--) {
mDPChangeListeners.get(i).onDeviceProfileChanged(mDeviceProfile);
}
}