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

Commit ff290691 authored by Bryce Lee's avatar Bryce Lee Committed by Android (Google) Code Review
Browse files

Merge "Initial implementation of LaunchingBoundsController."

parents bee33f84 dacefc40
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;
@@ -5053,24 +5051,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
@@ -287,7 +287,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;
@@ -568,6 +568,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) {
@@ -2154,8 +2157,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