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

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

Merge "Keep the focus on the primary when launch ActivityEmbedding...

Merge "Keep the focus on the primary when launch ActivityEmbedding placeholder" into tm-dev am: 301ff2fe am: 37d3bc3d

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



Change-Id: I5eaf0513d313bbb3cf0b95ec8a22bce18bc9e0b8
Signed-off-by: default avatarAutomerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
parents 25763ec0 37d3bc3d
Loading
Loading
Loading
Loading
+23 −0
Original line number Original line Diff line number Diff line
@@ -649,6 +649,26 @@ public final class WindowContainerTransaction implements Parcelable {
        return this;
        return this;
    }
    }


    /**
     * Requests focus on the top running Activity in the given TaskFragment. This will only take
     * effect if there is no focus, or if the current focus is in the same Task as the requested
     * TaskFragment.
     * @param fragmentToken client assigned unique token to create TaskFragment with specified in
     *                      {@link TaskFragmentCreationParams#getFragmentToken()}.
     * @hide
     */
    @NonNull
    public WindowContainerTransaction requestFocusOnTaskFragment(@NonNull IBinder fragmentToken) {
        final HierarchyOp hierarchyOp =
                new HierarchyOp.Builder(
                        HierarchyOp.HIERARCHY_OP_TYPE_REQUEST_FOCUS_ON_TASK_FRAGMENT)
                        .setContainer(fragmentToken)
                        .build();
        mHierarchyOps.add(hierarchyOp);
        return this;

    }

    /**
    /**
     * When this {@link WindowContainerTransaction} failed to finish on the server side, it will
     * When this {@link WindowContainerTransaction} failed to finish on the server side, it will
     * trigger callback with this {@param errorCallbackToken}.
     * trigger callback with this {@param errorCallbackToken}.
@@ -1057,6 +1077,7 @@ public final class WindowContainerTransaction implements Parcelable {
        public static final int HIERARCHY_OP_TYPE_RESTORE_TRANSIENT_ORDER = 15;
        public static final int HIERARCHY_OP_TYPE_RESTORE_TRANSIENT_ORDER = 15;
        public static final int HIERARCHY_OP_TYPE_ADD_RECT_INSETS_PROVIDER = 16;
        public static final int HIERARCHY_OP_TYPE_ADD_RECT_INSETS_PROVIDER = 16;
        public static final int HIERARCHY_OP_TYPE_REMOVE_INSETS_PROVIDER = 17;
        public static final int HIERARCHY_OP_TYPE_REMOVE_INSETS_PROVIDER = 17;
        public static final int HIERARCHY_OP_TYPE_REQUEST_FOCUS_ON_TASK_FRAGMENT = 18;


        // The following key(s) are for use with mLaunchOptions:
        // The following key(s) are for use with mLaunchOptions:
        // When launching a task (eg. from recents), this is the taskId to be launched.
        // When launching a task (eg. from recents), this is the taskId to be launched.
@@ -1368,6 +1389,8 @@ public final class WindowContainerTransaction implements Parcelable {
                case HIERARCHY_OP_TYPE_REMOVE_INSETS_PROVIDER:
                case HIERARCHY_OP_TYPE_REMOVE_INSETS_PROVIDER:
                    return "{removeLocalInsetsProvider: container=" + mContainer
                    return "{removeLocalInsetsProvider: container=" + mContainer
                            + " insetsType=" + Arrays.toString(mInsetsTypes) + "}";
                            + " insetsType=" + Arrays.toString(mInsetsTypes) + "}";
                case HIERARCHY_OP_TYPE_REQUEST_FOCUS_ON_TASK_FRAGMENT:
                    return "{requestFocusOnTaskFragment: container=" + mContainer + "}";
                default:
                default:
                    return "{mType=" + mType + " container=" + mContainer + " reparent=" + mReparent
                    return "{mType=" + mType + " container=" + mContainer + " reparent=" + mReparent
                            + " mToTop=" + mToTop
                            + " mToTop=" + mToTop
+5 −4
Original line number Original line Diff line number Diff line
@@ -112,9 +112,10 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
     */
     */
    public void startActivityToSide(@NonNull Activity launchingActivity, @NonNull Intent intent,
    public void startActivityToSide(@NonNull Activity launchingActivity, @NonNull Intent intent,
            @Nullable Bundle options, @NonNull SplitRule sideRule,
            @Nullable Bundle options, @NonNull SplitRule sideRule,
            @Nullable Consumer<Exception> failureCallback) {
            @Nullable Consumer<Exception> failureCallback, boolean isPlaceholder) {
        try {
        try {
            mPresenter.startActivityToSide(launchingActivity, intent, options, sideRule);
            mPresenter.startActivityToSide(launchingActivity, intent, options, sideRule,
                    isPlaceholder);
        } catch (Exception e) {
        } catch (Exception e) {
            if (failureCallback != null) {
            if (failureCallback != null) {
                failureCallback.accept(e);
                failureCallback.accept(e);
@@ -710,8 +711,8 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
        }
        }


        // TODO(b/190433398): Handle failed request
        // TODO(b/190433398): Handle failed request
        startActivityToSide(activity, placeholderRule.getPlaceholderIntent(), null,
        startActivityToSide(activity, placeholderRule.getPlaceholderIntent(), null /* options */,
                placeholderRule, null);
                placeholderRule, null /* failureCallback */, true /* isPlaceholder */);
        return true;
        return true;
    }
    }


+18 −6
Original line number Original line Diff line number Diff line
@@ -220,9 +220,10 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
     * @param activityIntent    The intent to start the new activity.
     * @param activityIntent    The intent to start the new activity.
     * @param activityOptions   The options to apply to new activity start.
     * @param activityOptions   The options to apply to new activity start.
     * @param rule              The split rule to be applied to the container.
     * @param rule              The split rule to be applied to the container.
     * @param isPlaceholder     Whether the launch is a placeholder.
     */
     */
    void startActivityToSide(@NonNull Activity launchingActivity, @NonNull Intent activityIntent,
    void startActivityToSide(@NonNull Activity launchingActivity, @NonNull Intent activityIntent,
            @Nullable Bundle activityOptions, @NonNull SplitRule rule) {
            @Nullable Bundle activityOptions, @NonNull SplitRule rule, boolean isPlaceholder) {
        final Rect parentBounds = getParentContainerBounds(launchingActivity);
        final Rect parentBounds = getParentContainerBounds(launchingActivity);
        final Rect primaryRectBounds = getBoundsForPosition(POSITION_START, parentBounds, rule,
        final Rect primaryRectBounds = getBoundsForPosition(POSITION_START, parentBounds, rule,
                isLtr(launchingActivity, rule));
                isLtr(launchingActivity, rule));
@@ -244,6 +245,10 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
        startActivityToSide(wct, primaryContainer.getTaskFragmentToken(), primaryRectBounds,
        startActivityToSide(wct, primaryContainer.getTaskFragmentToken(), primaryRectBounds,
                launchingActivity, secondaryContainer.getTaskFragmentToken(), secondaryRectBounds,
                launchingActivity, secondaryContainer.getTaskFragmentToken(), secondaryRectBounds,
                activityIntent, activityOptions, rule);
                activityIntent, activityOptions, rule);
        if (isPlaceholder) {
            // When placeholder is launched in split, we should keep the focus on the primary.
            wct.requestFocusOnTaskFragment(primaryContainer.getTaskFragmentToken());
        }
        applyTransaction(wct);
        applyTransaction(wct);


        primaryContainer.setLastRequestedBounds(primaryRectBounds);
        primaryContainer.setLastRequestedBounds(primaryRectBounds);
@@ -272,14 +277,21 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
                isLtr);
                isLtr);
        final Rect secondaryRectBounds = getBoundsForPosition(POSITION_END, parentBounds, rule,
        final Rect secondaryRectBounds = getBoundsForPosition(POSITION_END, parentBounds, rule,
                isLtr);
                isLtr);
        final TaskFragmentContainer secondaryContainer = splitContainer.getSecondaryContainer();
        // Whether the placeholder is becoming side-by-side with the primary from fullscreen.
        final boolean isPlaceholderBecomingSplit = splitContainer.isPlaceholderContainer()
                && secondaryContainer.areLastRequestedBoundsEqual(null /* bounds */)
                && !secondaryRectBounds.isEmpty();


        // If the task fragments are not registered yet, the positions will be updated after they
        // If the task fragments are not registered yet, the positions will be updated after they
        // are created again.
        // are created again.
        resizeTaskFragmentIfRegistered(wct, primaryContainer, primaryRectBounds);
        resizeTaskFragmentIfRegistered(wct, primaryContainer, primaryRectBounds);
        final TaskFragmentContainer secondaryContainer = splitContainer.getSecondaryContainer();
        resizeTaskFragmentIfRegistered(wct, secondaryContainer, secondaryRectBounds);
        resizeTaskFragmentIfRegistered(wct, secondaryContainer, secondaryRectBounds);

        setAdjacentTaskFragments(wct, primaryContainer, secondaryContainer, rule);
        setAdjacentTaskFragments(wct, primaryContainer, secondaryContainer, rule);
        if (isPlaceholderBecomingSplit) {
            // When placeholder is shown in split, we should keep the focus on the primary.
            wct.requestFocusOnTaskFragment(primaryContainer.getTaskFragmentToken());
        }
    }
    }


    private void setAdjacentTaskFragments(@NonNull WindowContainerTransaction wct,
    private void setAdjacentTaskFragments(@NonNull WindowContainerTransaction wct,
+25 −0
Original line number Original line Diff line number Diff line
@@ -30,6 +30,7 @@ import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REPARENT;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REPARENT;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REPARENT_ACTIVITY_TO_TASK_FRAGMENT;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REPARENT_ACTIVITY_TO_TASK_FRAGMENT;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REPARENT_CHILDREN;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REPARENT_CHILDREN;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REQUEST_FOCUS_ON_TASK_FRAGMENT;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_RESTORE_TRANSIENT_ORDER;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_RESTORE_TRANSIENT_ORDER;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_SET_ADJACENT_ROOTS;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_SET_ADJACENT_ROOTS;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_SET_ADJACENT_TASK_FRAGMENTS;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_SET_ADJACENT_TASK_FRAGMENTS;
@@ -777,6 +778,29 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
                }
                }
                break;
                break;
            }
            }
            case HIERARCHY_OP_TYPE_REQUEST_FOCUS_ON_TASK_FRAGMENT: {
                final TaskFragment tf = mLaunchTaskFragments.get(hop.getContainer());
                if (tf == null || !tf.isAttached()) {
                    Slog.e(TAG, "Attempt to operate on detached container: " + tf);
                    break;
                }
                final ActivityRecord curFocus = tf.getDisplayContent().mFocusedApp;
                if (curFocus != null && curFocus.getTaskFragment() == tf) {
                    Slog.d(TAG, "The requested TaskFragment already has the focus.");
                    break;
                }
                if (curFocus != null && curFocus.getTask() != tf.getTask()) {
                    Slog.d(TAG, "The Task of the requested TaskFragment doesn't have focus.");
                    break;
                }
                final ActivityRecord targetFocus = tf.getTopResumedActivity();
                if (targetFocus == null) {
                    Slog.d(TAG, "There is no resumed activity in the requested TaskFragment.");
                    break;
                }
                tf.getDisplayContent().setFocusedApp(targetFocus);
                break;
            }
            default: {
            default: {
                // The other operations may change task order so they are skipped while in lock
                // The other operations may change task order so they are skipped while in lock
                // task mode. The above operations are still allowed because they don't move
                // task mode. The above operations are still allowed because they don't move
@@ -1320,6 +1344,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
                case HIERARCHY_OP_TYPE_START_ACTIVITY_IN_TASK_FRAGMENT:
                case HIERARCHY_OP_TYPE_START_ACTIVITY_IN_TASK_FRAGMENT:
                case HIERARCHY_OP_TYPE_REPARENT_ACTIVITY_TO_TASK_FRAGMENT:
                case HIERARCHY_OP_TYPE_REPARENT_ACTIVITY_TO_TASK_FRAGMENT:
                case HIERARCHY_OP_TYPE_SET_ADJACENT_TASK_FRAGMENTS:
                case HIERARCHY_OP_TYPE_SET_ADJACENT_TASK_FRAGMENTS:
                case HIERARCHY_OP_TYPE_REQUEST_FOCUS_ON_TASK_FRAGMENT:
                    // We are allowing organizer to start/reparent activity to a TaskFragment it
                    // We are allowing organizer to start/reparent activity to a TaskFragment it
                    // created, or set two TaskFragments adjacent to each other. Nothing to check
                    // created, or set two TaskFragments adjacent to each other. Nothing to check
                    // here because the TaskFragment may not be created yet, but will be created in
                    // here because the TaskFragment may not be created yet, but will be created in
+49 −0
Original line number Original line Diff line number Diff line
@@ -521,6 +521,55 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
        verify(mAtm.mRootWindowContainer).resumeFocusedTasksTopActivities();
        verify(mAtm.mRootWindowContainer).resumeFocusedTasksTopActivities();
    }
    }


    @Test
    public void testApplyTransaction_requestFocusOnTaskFragment() {
        mOrganizer.applyTransaction(mTransaction);
        mController.registerOrganizer(mIOrganizer);
        final Task task = createTask(mDisplayContent);
        final IBinder token0 = new Binder();
        final TaskFragment tf0 = new TaskFragmentBuilder(mAtm)
                .setParentTask(task)
                .setFragmentToken(token0)
                .setOrganizer(mOrganizer)
                .createActivityCount(1)
                .build();
        final IBinder token1 = new Binder();
        final TaskFragment tf1 = new TaskFragmentBuilder(mAtm)
                .setParentTask(task)
                .setFragmentToken(token1)
                .setOrganizer(mOrganizer)
                .createActivityCount(1)
                .build();
        mAtm.mWindowOrganizerController.mLaunchTaskFragments.put(token0, tf0);
        mAtm.mWindowOrganizerController.mLaunchTaskFragments.put(token1, tf1);
        final ActivityRecord activity0 = tf0.getTopMostActivity();
        final ActivityRecord activity1 = tf1.getTopMostActivity();

        // No effect if the current focus is in a different Task.
        final ActivityRecord activityInOtherTask = createActivityRecord(mDefaultDisplay);
        mDisplayContent.setFocusedApp(activityInOtherTask);
        mTransaction.requestFocusOnTaskFragment(token0);
        mAtm.mWindowOrganizerController.applyTransaction(mTransaction);

        assertEquals(activityInOtherTask, mDisplayContent.mFocusedApp);

        // No effect if there is no resumed activity in the request TaskFragment.
        activity0.setState(ActivityRecord.State.PAUSED, "test");
        activity1.setState(ActivityRecord.State.RESUMED, "test");
        mDisplayContent.setFocusedApp(activity1);
        mAtm.mWindowOrganizerController.applyTransaction(mTransaction);

        assertEquals(activity1, mDisplayContent.mFocusedApp);

        // Set focus to the request TaskFragment when the current focus is in the same Task, and it
        // has a resumed activity.
        activity0.setState(ActivityRecord.State.RESUMED, "test");
        mDisplayContent.setFocusedApp(activity1);
        mAtm.mWindowOrganizerController.applyTransaction(mTransaction);

        assertEquals(activity0, mDisplayContent.mFocusedApp);
    }

    @Test
    @Test
    public void testTaskFragmentInPip_startActivityInTaskFragment() {
    public void testTaskFragmentInPip_startActivityInTaskFragment() {
        setupTaskFragmentInPip();
        setupTaskFragmentInPip();