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

Commit 301ff2fe authored by Chris Li's avatar Chris Li Committed by Android (Google) Code Review
Browse files

Merge "Keep the focus on the primary when launch ActivityEmbedding placeholder" into tm-dev

parents 9871c51f c21254f4
Loading
Loading
Loading
Loading
+23 −0
Original line number Diff line number Diff line
@@ -649,6 +649,26 @@ public final class WindowContainerTransaction implements Parcelable {
        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
     * 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_ADD_RECT_INSETS_PROVIDER = 16;
        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:
        // 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:
                    return "{removeLocalInsetsProvider: container=" + mContainer
                            + " insetsType=" + Arrays.toString(mInsetsTypes) + "}";
                case HIERARCHY_OP_TYPE_REQUEST_FOCUS_ON_TASK_FRAGMENT:
                    return "{requestFocusOnTaskFragment: container=" + mContainer + "}";
                default:
                    return "{mType=" + mType + " container=" + mContainer + " reparent=" + mReparent
                            + " mToTop=" + mToTop
+5 −4
Original line number Diff line number Diff line
@@ -112,9 +112,10 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
     */
    public void startActivityToSide(@NonNull Activity launchingActivity, @NonNull Intent intent,
            @Nullable Bundle options, @NonNull SplitRule sideRule,
            @Nullable Consumer<Exception> failureCallback) {
            @Nullable Consumer<Exception> failureCallback, boolean isPlaceholder) {
        try {
            mPresenter.startActivityToSide(launchingActivity, intent, options, sideRule);
            mPresenter.startActivityToSide(launchingActivity, intent, options, sideRule,
                    isPlaceholder);
        } catch (Exception e) {
            if (failureCallback != null) {
                failureCallback.accept(e);
@@ -710,8 +711,8 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
        }

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

+18 −6
Original line number Diff line number Diff line
@@ -220,9 +220,10 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
     * @param activityIntent    The intent to start the new activity.
     * @param activityOptions   The options to apply to new activity start.
     * @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,
            @Nullable Bundle activityOptions, @NonNull SplitRule rule) {
            @Nullable Bundle activityOptions, @NonNull SplitRule rule, boolean isPlaceholder) {
        final Rect parentBounds = getParentContainerBounds(launchingActivity);
        final Rect primaryRectBounds = getBoundsForPosition(POSITION_START, parentBounds, rule,
                isLtr(launchingActivity, rule));
@@ -244,6 +245,10 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
        startActivityToSide(wct, primaryContainer.getTaskFragmentToken(), primaryRectBounds,
                launchingActivity, secondaryContainer.getTaskFragmentToken(), secondaryRectBounds,
                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);

        primaryContainer.setLastRequestedBounds(primaryRectBounds);
@@ -272,14 +277,21 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
                isLtr);
        final Rect secondaryRectBounds = getBoundsForPosition(POSITION_END, parentBounds, rule,
                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
        // are created again.
        resizeTaskFragmentIfRegistered(wct, primaryContainer, primaryRectBounds);
        final TaskFragmentContainer secondaryContainer = splitContainer.getSecondaryContainer();
        resizeTaskFragmentIfRegistered(wct, secondaryContainer, secondaryRectBounds);

        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,
+25 −0
Original line number 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_ACTIVITY_TO_TASK_FRAGMENT;
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_SET_ADJACENT_ROOTS;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_SET_ADJACENT_TASK_FRAGMENTS;
@@ -775,6 +776,29 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
                }
                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: {
                // 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
@@ -1318,6 +1342,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
                case HIERARCHY_OP_TYPE_START_ACTIVITY_IN_TASK_FRAGMENT:
                case HIERARCHY_OP_TYPE_REPARENT_ACTIVITY_TO_TASK_FRAGMENT:
                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
                    // 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
+49 −0
Original line number Diff line number Diff line
@@ -521,6 +521,55 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
        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
    public void testTaskFragmentInPip_startActivityInTaskFragment() {
        setupTaskFragmentInPip();