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

Commit dacefc40 authored by Bryce Lee's avatar Bryce Lee
Browse files

Initial implementation of LaunchingBoundsController.

This changelist introduces the LaunchingBoundsController, a central
location for calculating launch bounds. It also defines a
positioner interface that can be registered with LaunchingBoundsController
to participate in bounds calculation.

Test: bit FrameworksServicesTests:com.android.server.am.LaunchingTaskPositionerTests
Test: bit FrameworksServicesTests:com.android.server.am.LaunchingActivityPositionerTests
Test: bit FrameworksServicesTests:com.android.server.am.LaunchingBoundsControllerTests
Bug: 64144308
Change-Id: I35eaf095e2edd562375403413050ce82618c44f2
parent 1eb19a3b
Loading
Loading
Loading
Loading
+2 −14
Original line number Diff line number Diff line
@@ -107,7 +107,6 @@ import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.res.Configuration;
import android.graphics.Point;
import android.graphics.Rect;
import android.net.Uri;
import android.os.Binder;
@@ -346,7 +345,6 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
    private final SparseArray<Rect> mTmpBounds = new SparseArray<>();
    private final SparseArray<Rect> mTmpInsetBounds = new SparseArray<>();
    private final Rect mTmpRect2 = new Rect();
    private final Point mTmpSize = new Point();

    /** Run all ActivityStacks through this */
    protected final ActivityStackSupervisor mStackSupervisor;
@@ -5123,24 +5121,14 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
        addTask(task, toTop, "createTaskRecord");
        final boolean isLockscreenShown = mService.mStackSupervisor.mKeyguardController
                .isKeyguardShowing(mDisplayId != INVALID_DISPLAY ? mDisplayId : DEFAULT_DISPLAY);
        if (!layoutTaskInStack(task, info.windowLayout) && mBounds != null && task.isResizeable()
                && !isLockscreenShown) {
        if (!mStackSupervisor.getLaunchingBoundsController().layoutTask(task, info.windowLayout)
                && mBounds != null && task.isResizeable() && !isLockscreenShown) {
            task.updateOverrideConfiguration(mBounds);
        }
        task.createWindowContainer(toTop, (info.flags & FLAG_SHOW_FOR_ALL_USERS) != 0);
        return task;
    }

    boolean layoutTaskInStack(TaskRecord task, ActivityInfo.WindowLayout windowLayout) {
        if (!task.inFreeformWindowingMode()) {
            return false;
        }
        mStackSupervisor.getLaunchingTaskPositioner()
                .updateDefaultBounds(task, mTaskHistory, windowLayout);

        return true;
    }

    ArrayList<TaskRecord> getAllTasks() {
        return new ArrayList<>(mTaskHistory);
    }
+6 −3
Original line number Diff line number Diff line
@@ -295,7 +295,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
    WindowManagerService mWindowManager;
    DisplayManager mDisplayManager;

    LaunchingTaskPositioner mTaskPositioner = new LaunchingTaskPositioner();
    private final LaunchingBoundsController mLaunchingBoundsController;

    /** Counter for next free stack ID to use for dynamic activity stacks. */
    private int mNextFreeStackId = 0;
@@ -575,6 +575,9 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
        mHandler = new ActivityStackSupervisorHandler(looper);
        mActivityMetricsLogger = new ActivityMetricsLogger(this, mService.mContext);
        mKeyguardController = new KeyguardController(service, this);

        mLaunchingBoundsController = new LaunchingBoundsController();
        mLaunchingBoundsController.registerDefaultPositioners(this);
    }

    void setRecentTasks(RecentTasks recentTasks) {
@@ -2161,8 +2164,8 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
                || mService.mSupportsFreeformWindowManagement;
    }

    LaunchingTaskPositioner getLaunchingTaskPositioner() {
        return mTaskPositioner;
    LaunchingBoundsController getLaunchingBoundsController() {
        return mLaunchingBoundsController;
    }

    protected <T extends ActivityStack> T getStack(int stackId) {
+13 −12
Original line number Diff line number Diff line
@@ -151,7 +151,7 @@ class ActivityStarter {
    private boolean mLaunchTaskBehind;
    private int mLaunchFlags;

    private Rect mLaunchBounds;
    private Rect mLaunchBounds = new Rect();

    private ActivityRecord mNotTop;
    private boolean mDoResume;
@@ -210,7 +210,7 @@ class ActivityStarter {
        mLaunchFlags = 0;
        mLaunchMode = INVALID_LAUNCH_MODE;

        mLaunchBounds = null;
        mLaunchBounds.setEmpty();

        mNotTop = null;
        mDoResume = false;
@@ -1254,7 +1254,10 @@ class ActivityStarter {

        mPreferredDisplayId = getPreferedDisplayId(mSourceRecord, mStartActivity, options);

        mLaunchBounds = getOverrideBounds(r, options, inTask);
        mLaunchBounds.setEmpty();

        mSupervisor.getLaunchingBoundsController().calculateBounds(inTask, null /*layout*/, r,
                options, mLaunchBounds);

        mLaunchMode = r.launchMode;

@@ -1725,7 +1728,7 @@ class ActivityStarter {
                    // Target stack got cleared when we all activities were removed above.
                    // Go ahead and reset it.
                    mTargetStack = computeStackFocus(mSourceRecord, false /* newTask */,
                            null /* bounds */, mLaunchFlags, mOptions);
                            mLaunchFlags, mOptions);
                    mTargetStack.addTask(task,
                            !mLaunchTaskBehind /* toTop */, "startActivityUnchecked");
                }
@@ -1776,8 +1779,7 @@ class ActivityStarter {

    private int setTaskFromReuseOrCreateNewTask(
            TaskRecord taskToAffiliate, ActivityStack topStack) {
        mTargetStack = computeStackFocus(
                mStartActivity, true, mLaunchBounds, mLaunchFlags, mOptions);
        mTargetStack = computeStackFocus(mStartActivity, true, mLaunchFlags, mOptions);

        // Do no move the target stack to front yet, as we might bail if
        // isLockTaskModeViolation fails below.
@@ -1962,7 +1964,7 @@ class ActivityStarter {
            return START_TASK_TO_FRONT;
        }

        if (mLaunchBounds != null) {
        if (!mLaunchBounds.isEmpty()) {
            // TODO: Shouldn't we already know what stack to use by the time we get here?
            ActivityStack stack = mSupervisor.getLaunchStack(null, null, mInTask, ON_TOP);
            if (stack != mInTask.getStack()) {
@@ -1985,7 +1987,7 @@ class ActivityStarter {
    }

    void updateBounds(TaskRecord task, Rect bounds) {
        if (bounds == null) {
        if (bounds.isEmpty()) {
            return;
        }

@@ -1998,8 +2000,7 @@ class ActivityStarter {
    }

    private void setTaskToCurrentTopOrCreateNewTask() {
        mTargetStack = computeStackFocus(mStartActivity, false, null /* bounds */, mLaunchFlags,
                mOptions);
        mTargetStack = computeStackFocus(mStartActivity, false, mLaunchFlags, mOptions);
        if (mDoResume) {
            mTargetStack.moveToFront("addingToTopTask");
        }
@@ -2062,8 +2063,8 @@ class ActivityStarter {
        }
    }

    private ActivityStack computeStackFocus(ActivityRecord r, boolean newTask, Rect bounds,
            int launchFlags, ActivityOptions aOptions) {
    private ActivityStack computeStackFocus(ActivityRecord r, boolean newTask, int launchFlags,
            ActivityOptions aOptions) {
        final TaskRecord task = r.getTask();
        ActivityStack stack = getLaunchStack(r, launchFlags, task, aOptions);
        if (stack != null) {
+62 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2017 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License
 */

package com.android.server.am;

import android.app.ActivityOptions;
import android.content.pm.ActivityInfo;
import android.graphics.Rect;
import com.android.server.am.LaunchingBoundsController.LaunchingBoundsPositioner;

/**
 * An implementation of {@link LaunchingBoundsPositioner}, which applies the launch bounds specified
 * inside {@link ActivityOptions#getLaunchBounds()}.
 */
public class LaunchingActivityPositioner implements LaunchingBoundsPositioner {
    private final ActivityStackSupervisor mSupervisor;

    LaunchingActivityPositioner(ActivityStackSupervisor activityStackSupervisor) {
        mSupervisor = activityStackSupervisor;
    }

    @Override
    public int onCalculateBounds(TaskRecord task, ActivityInfo.WindowLayout layout,
            ActivityRecord activity, ActivityOptions options,  Rect current, Rect result) {
        // We only care about figuring out bounds for activities.
        if (activity == null) {
            return RESULT_SKIP;
        }

        // Activity must be resizeable in the specified task.
        if (!(mSupervisor.canUseActivityOptionsLaunchBounds(options)
            && (activity.isResizeable() || (task != null && task.isResizeable())))) {
            return RESULT_SKIP;
        }

        final Rect bounds = TaskRecord.validateBounds(options.getLaunchBounds());

        // Bounds weren't valid.
        if (bounds == null) {
            return RESULT_SKIP;
        }

        result.set(bounds);

        // When this is the most explicit position specification so we should not allow further
        // modification of the position.
        return RESULT_DONE;
    }
}
+160 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2017 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License
 */

package com.android.server.am;

import android.annotation.IntDef;
import android.app.ActivityOptions;
import android.content.pm.ActivityInfo.WindowLayout;
import android.graphics.Rect;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.List;

import static com.android.server.am.LaunchingBoundsController.LaunchingBoundsPositioner.RESULT_CONTINUE;
import static com.android.server.am.LaunchingBoundsController.LaunchingBoundsPositioner.RESULT_DONE;
import static com.android.server.am.LaunchingBoundsController.LaunchingBoundsPositioner.RESULT_SKIP;

/**
 * {@link LaunchingBoundsController} calculates the launch bounds by coordinating between registered
 * {@link LaunchingBoundsPositioner}.
 */
class LaunchingBoundsController {
    private final List<LaunchingBoundsPositioner> mPositioners = new ArrayList<>();

    // Temporary {@link Rect} for calculations. This is kept separate from {@code mTmpCurrent} and
    // {@code mTmpResult} to prevent clobbering values.
    private final Rect mTmpRect = new Rect();

    private final Rect mTmpCurrent = new Rect();
    private final Rect mTmpResult = new Rect();

    /**
     * Creates a {@link LaunchingBoundsController} with default registered
     * {@link LaunchingBoundsPositioner}s.
     */
    void registerDefaultPositioners(ActivityStackSupervisor supervisor) {
        // {@link LaunchingTaskPositioner} handles window layout preferences.
        registerPositioner(new LaunchingTaskPositioner());

        // {@link LaunchingActivityPositioner} is the most specific positioner and thus should be
        // registered last (applied first) out of the defaults.
        registerPositioner(new LaunchingActivityPositioner(supervisor));
    }

    /**
     * Returns the position calculated by the registered positioners
     * @param task      The {@link TaskRecord} currently being positioned.
     * @param layout    The specified {@link WindowLayout}.
     * @param activity  The {@link ActivityRecord} currently being positioned.
     * @param options   The {@link ActivityOptions} specified for the activity.
     * @param result    The resulting bounds. If no bounds are set, {@link Rect#isEmpty()} will be
     *                  true.
     */
    void calculateBounds(TaskRecord task, WindowLayout layout, ActivityRecord activity,
            ActivityOptions options, Rect result) {
        result.setEmpty();

        // We start at the last registered {@link LaunchingBoundsPositioner} as this represents
        // The positioner closest to the product level. Moving back through the list moves closer to
        // the platform logic.
        for (int i = mPositioners.size() - 1; i >= 0; --i) {
            mTmpResult.setEmpty();
            mTmpCurrent.set(result);
            final LaunchingBoundsPositioner positioner = mPositioners.get(i);

            switch(positioner.onCalculateBounds(task, layout, activity, options, mTmpCurrent,
                    mTmpResult)) {
                case RESULT_SKIP:
                    // Do not apply any results when we are told to skip
                    continue;
                case RESULT_DONE:
                    // Set result and return immediately.
                    result.set(mTmpResult);
                    return;
                case RESULT_CONTINUE:
                    // Set result and continue
                    result.set(mTmpResult);
                    break;
            }
        }
    }

    /**
     * A convenience method for laying out a task.
     * @return {@code true} if bounds were set on the task. {@code false} otherwise.
     */
    boolean layoutTask(TaskRecord task, WindowLayout layout) {
        calculateBounds(task, layout, null /*activity*/, null /*options*/, mTmpRect);

        if (mTmpRect.isEmpty()) {
            return false;
        }

        task.updateOverrideConfiguration(mTmpRect);

        return true;
    }

    /**
     * Adds a positioner to participate in future bounds calculation. Note that the last registered
     * {@link LaunchingBoundsPositioner} will be the first to calculate the bounds.
     */
    void registerPositioner(LaunchingBoundsPositioner positioner) {
        if (mPositioners.contains(positioner)) {
            return;
        }

        mPositioners.add(positioner);
    }

    /**
     * An interface implemented by those wanting to participate in bounds calculation.
     */
    interface LaunchingBoundsPositioner {
        @Retention(RetentionPolicy.SOURCE)
        @IntDef({RESULT_SKIP, RESULT_DONE, RESULT_CONTINUE})
        @interface Result {}

        // Returned when the positioner does not want to influence the bounds calculation
        int RESULT_SKIP = 0;
        // Returned when the positioner has changed the bounds and would like its results to be the
        // final bounds applied.
        int RESULT_DONE = 1;
        // Returned when the positioner has changed the bounds but is okay with other positioners
        // influencing the bounds.
        int RESULT_CONTINUE = 2;

        /**
         * Called when asked to calculate bounds.
         * @param task      The {@link TaskRecord} currently being positioned.
         * @param layout    The specified {@link WindowLayout}.
         * @param activity  The {@link ActivityRecord} currently being positioned.
         * @param options   The {@link ActivityOptions} specified for the activity.
         * @param current   The current bounds. This can differ from the initial bounds as it
         *                  represents the modified bounds up to this point.
         * @param result    The {@link Rect} which the positioner should return its modified bounds.
         *                  Any merging of the current bounds should be already applied to this
         *                  value as well before returning.
         * @return A {@link Result} representing the result of the bounds calculation.
         */
        @Result
        int onCalculateBounds(TaskRecord task, WindowLayout layout, ActivityRecord activity,
                ActivityOptions options, Rect current, Rect result);
    }
}
Loading