Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 8a968fab authored by Winson Chung's avatar Winson Chung
Browse files

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
parent 0b0847b2
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -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();
+55 −8
Original line number Diff line number Diff line
@@ -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 {
+29 −7
Original line number Diff line number Diff line
@@ -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) {
                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
        if (animateTaskView) {
            taskView.setTranslationZ(0.1f);
        }

        mPendingAnimation = pendingAnimation;
        mPendingAnimation.addEndListener((isSuccess) -> {
           if (isSuccess) {
               if (removeTask) {
                   ActivityManagerWrapper.getInstance().removeTask(taskView.getTask().key.id);
               }
               removeView(taskView);
               if (getChildCount() == 0) {
                   onAllTasksRemoved();
+5 −2
Original line number Diff line number Diff line
@@ -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);
        }
    }