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

Commit a0e7b8e7 authored by Chris Li's avatar Chris Li Committed by Automerger Merge Worker
Browse files

Merge "Don't start app transition when there is empty TaskFragment" into...

Merge "Don't start app transition when there is empty TaskFragment" into sc-v2-dev am: abdb320b am: 79ff79b8

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/15878941

Change-Id: Iede240bd2df03a7951672682256edd056ed17a5c
parents 896712d3 79ff79b8
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -553,6 +553,12 @@
      "group": "WM_SHOW_TRANSACTIONS",
      "at": "com\/android\/server\/wm\/RootWindowContainer.java"
    },
    "-1501564055": {
      "message": "Organized TaskFragment is not ready= %s",
      "level": "VERBOSE",
      "group": "WM_DEBUG_APP_TRANSITIONS",
      "at": "com\/android\/server\/wm\/AppTransitionController.java"
    },
    "-1499134947": {
      "message": "Removing starting %s from %s",
      "level": "VERBOSE",
+100 −56
Original line number Diff line number Diff line
@@ -165,8 +165,8 @@ public class AppTransitionController {
    void handleAppTransitionReady() {
        mTempTransitionReasons.clear();
        if (!transitionGoodToGo(mDisplayContent.mOpeningApps, mTempTransitionReasons)
                || !transitionGoodToGo(mDisplayContent.mChangingContainers,
                        mTempTransitionReasons)) {
                || !transitionGoodToGo(mDisplayContent.mChangingContainers, mTempTransitionReasons)
                || !transitionGoodToGoForTaskFragments()) {
            return;
        }
        Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "AppTransitionReady");
@@ -599,6 +599,13 @@ public class AppTransitionController {
        }
    }

    @Nullable
    static Task findRootTaskFromContainer(WindowContainer wc) {
        return wc.asTaskFragment() != null ? wc.asTaskFragment().getRootTask()
                : wc.asActivityRecord().getRootTask();
    }

    @Nullable
    static ActivityRecord getAppFromContainer(WindowContainer wc) {
        return wc.asTaskFragment() != null ? wc.asTaskFragment().getTopNonFinishingActivity()
                : wc.asActivityRecord();
@@ -972,11 +979,12 @@ public class AppTransitionController {
        ProtoLog.v(WM_DEBUG_APP_TRANSITIONS,
                "Checking %d opening apps (frozen=%b timeout=%b)...", apps.size(),
                mService.mDisplayFrozen, mDisplayContent.mAppTransition.isTimeout());

        if (mDisplayContent.mAppTransition.isTimeout()) {
            return true;
        }
        final ScreenRotationAnimation screenRotationAnimation = mService.mRoot.getDisplayContent(
                Display.DEFAULT_DISPLAY).getRotationAnimation();

        if (!mDisplayContent.mAppTransition.isTimeout()) {
        // Imagine the case where we are changing orientation due to an app transition, but a
        // previous orientation change is still in progress. We won't process the orientation
        // change for our transition because we need to wait for the rotation animation to
@@ -1003,7 +1011,6 @@ public class AppTransitionController {
                    activity.startingMoved, activity.isRelaunching(),
                    activity.mStartingWindow);


            final boolean allDrawn = activity.allDrawn && !activity.isRelaunching();
            if (!allDrawn && !activity.startingDisplayed && !activity.startingMoved) {
                return false;
@@ -1031,13 +1038,50 @@ public class AppTransitionController {
        }

        // If the wallpaper is visible, we need to check it's ready too.
            boolean wallpaperReady = !mWallpaperControllerLocked.isWallpaperVisible() ||
                    mWallpaperControllerLocked.wallpaperTransitionReady();
            if (wallpaperReady) {
        return !mWallpaperControllerLocked.isWallpaperVisible()
                || mWallpaperControllerLocked.wallpaperTransitionReady();
    }

    private boolean transitionGoodToGoForTaskFragments() {
        if (mDisplayContent.mAppTransition.isTimeout()) {
            return true;
        }

        // Check all Tasks in this transition. This is needed because new TaskFragment created for
        // launching activity may not be in the tracking lists, but we still want to wait for the
        // activity launch to start the transition.
        final ArraySet<Task> rootTasks = new ArraySet<>();
        for (int i = mDisplayContent.mOpeningApps.size() - 1; i >= 0; i--) {
            rootTasks.add(mDisplayContent.mOpeningApps.valueAt(i).getRootTask());
        }
        for (int i = mDisplayContent.mClosingApps.size() - 1; i >= 0; i--) {
            rootTasks.add(mDisplayContent.mClosingApps.valueAt(i).getRootTask());
        }
        for (int i = mDisplayContent.mChangingContainers.size() - 1; i >= 0; i--) {
            rootTasks.add(
                    findRootTaskFromContainer(mDisplayContent.mChangingContainers.valueAt(i)));
        }

        // Organized TaskFragment can be empty for two situations:
        // 1. New created and is waiting for Activity launch. In this case, we want to wait for
        //    the Activity launch to trigger the transition.
        // 2. Last Activity is just removed. In this case, we want to wait for organizer to
        //    remove the TaskFragment because it may also want to change other TaskFragments in
        //    the same transition.
        for (int i = rootTasks.size() - 1; i >= 0; i--) {
            final Task rootTask = rootTasks.valueAt(i);
            final boolean notReady = rootTask.forAllLeafTaskFragments(taskFragment -> {
                if (!taskFragment.isReadyToTransit()) {
                    ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, "Organized TaskFragment is not ready= %s",
                            taskFragment);
                    return true;
                }
                return false;
            });
            if (notReady) {
                return false;
            }
        }
        return true;
    }

+0 −5
Original line number Diff line number Diff line
@@ -2347,11 +2347,6 @@ class Task extends TaskFragment {
        return getRootTask().mTaskId;
    }

    @Nullable
    Task getRootTask() {
        return getRootTaskFragment().asTask();
    }

    /** @return the first organized task. */
    @Nullable
    Task getOrganizedTask() {
+12 −0
Original line number Diff line number Diff line
@@ -459,6 +459,11 @@ class TaskFragment extends WindowContainer<WindowContainer> {
        return parentTaskFragment == null ? this : parentTaskFragment.getRootTaskFragment();
    }

    @Nullable
    Task getRootTask() {
        return getRootTaskFragment().asTask();
    }

    @Override
    TaskFragment asTaskFragment() {
        return this;
@@ -2153,6 +2158,13 @@ class TaskFragment extends WindowContainer<WindowContainer> {
        return mTaskFragmentOrganizer != null;
    }

    boolean isReadyToTransit() {
        // We don't want to start the transition if the organized TaskFragment is empty, unless
        // it is requested to be removed.
        return !isOrganizedTaskFragment() || getTopNonFinishingActivity() != null
                || mIsRemovalRequested;
    }

    /** Clear {@link #mLastPausedActivity} for all {@link TaskFragment} children */
    void clearLastPausedActivity() {
        forAllTaskFragments(taskFragment -> taskFragment.mLastPausedActivity = null);
+38 −0
Original line number Diff line number Diff line
@@ -31,13 +31,17 @@ import static android.view.WindowManager.TRANSIT_OLD_TASK_OPEN;
import static android.view.WindowManager.TRANSIT_OPEN;
import static android.view.WindowManager.TRANSIT_TO_FRONT;

import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.doCallRealMethod;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;

import android.os.Binder;
@@ -791,4 +795,38 @@ public class AppTransitionControllerTest extends WindowTestsBase {
        verify(mDisplayContent.mAppTransition)
                .overridePendingAppTransitionRemote(adapter, false /* sync */);
    }

    @Test
    public void testTransitionGoodToGoForTaskFragments() {
        final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run);
        final Task task = createTask(mDisplayContent);
        final TaskFragment changeTaskFragment = new TaskFragmentBuilder(mAtm)
                .setParentTask(task)
                .createActivityCount(1)
                .setOrganizer(organizer)
                .build();
        final TaskFragment emptyTaskFragment = new TaskFragmentBuilder(mAtm)
                .setParentTask(task)
                .setOrganizer(organizer)
                .build();
        changeTaskFragment.getTopMostActivity().allDrawn = true;
        mDisplayContent.mAppTransition.prepareAppTransition(TRANSIT_CHANGE, 0);
        mDisplayContent.mChangingContainers.add(changeTaskFragment);
        spyOn(mDisplayContent.mAppTransition);
        spyOn(emptyTaskFragment);

        mDisplayContent.mAppTransitionController.handleAppTransitionReady();

        // Transition not ready because there is an empty non-finishing TaskFragment.
        verify(mDisplayContent.mAppTransition, never()).goodToGo(anyInt(), any());

        doReturn(true).when(emptyTaskFragment).hasChild();
        emptyTaskFragment.remove(false /* withTransition */, "test");

        mDisplayContent.mAppTransitionController.handleAppTransitionReady();

        // Transition ready because the empty (no running activity) TaskFragment is requested to be
        // removed.
        verify(mDisplayContent.mAppTransition).goodToGo(anyInt(), any());
    }
}
 No newline at end of file