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

Commit e5390e73 authored by Filip Gruszczynski's avatar Filip Gruszczynski
Browse files

Non colliding positioning of entering resizeable tasks.

When a new task enters freeform work space, it becomes full screen. This
CL makes it non fullscreen upon entry and also tries to position the
task in a way, that won't collide with existing tasks (so it's clear to
the user, that they are still there).

Change-Id: Ia04fdcadd0e85b268c323358cd4b3e04affa7939
parent 16ac7c3b
Loading
Loading
Loading
Loading
+36 −4
Original line number Diff line number Diff line
@@ -248,6 +248,8 @@ final class ActivityStack {
    /** Run all ActivityStacks through this */
    final ActivityStackSupervisor mStackSupervisor;

    private final LaunchingTaskPositioner mTaskPositioner;

    static final int PAUSE_TIMEOUT_MSG = ActivityManagerService.FIRST_ACTIVITY_STACK_MSG + 1;
    static final int DESTROY_TIMEOUT_MSG = ActivityManagerService.FIRST_ACTIVITY_STACK_MSG + 2;
    static final int LAUNCH_TICK_MSG = ActivityManagerService.FIRST_ACTIVITY_STACK_MSG + 3;
@@ -363,6 +365,33 @@ final class ActivityStack {
        mStackId = activityContainer.mStackId;
        mCurrentUser = mService.mCurrentUserId;
        mRecentTasks = recentTasks;
        mTaskPositioner = mStackId == FREEFORM_WORKSPACE_STACK_ID
                ? new LaunchingTaskPositioner() : null;
    }

    void attachDisplay(ActivityStackSupervisor.ActivityDisplay activityDisplay, boolean onTop) {
        mDisplayId = activityDisplay.mDisplayId;
        mStacks = activityDisplay.mStacks;
        mBounds = mWindowManager.attachStack(mStackId, activityDisplay.mDisplayId, onTop);
        mFullscreen = mBounds == null;
        if (mTaskPositioner != null) {
            mTaskPositioner.setDisplay(activityDisplay.mDisplay);
            mTaskPositioner.configure(mBounds);
        }
    }

    void detachDisplay() {
        mDisplayId = Display.INVALID_DISPLAY;
        mStacks = null;
        if (mTaskPositioner != null) {
            mTaskPositioner.reset();
        }
        mWindowManager.detachStack(mStackId);
    }

    void setBounds(Rect bounds) {
        mBounds = mFullscreen ? null : new Rect(bounds);
        mTaskPositioner.configure(bounds);
    }

    boolean okToShowLocked(ActivityRecord r) {
@@ -2223,7 +2252,7 @@ final class ActivityStack {
                                + task, new RuntimeException("here").fillInStackTrace());
                        task.addActivityToTop(r);
                        r.putInHistory();
                        addAppToken(r, task);
                        addConfigOverride(r, task);
                        if (VALIDATE_TOKENS) {
                            validateAppTokensLocked();
                        }
@@ -2283,7 +2312,7 @@ final class ActivityStack {
                        : AppTransition.TRANSIT_ACTIVITY_OPEN, keepCurTransition);
                mNoAnimActivities.remove(r);
            }
            addAppToken(r, task);
            addConfigOverride(r, task);
            boolean doShow = true;
            if (newTask) {
                // Even though this activity is starting fresh, we still need
@@ -2332,7 +2361,7 @@ final class ActivityStack {
        } else {
            // If this is the first activity, don't do any fancy animations,
            // because there is nothing for it to animate on top of.
            addAppToken(r, task);
            addConfigOverride(r, task);
            ActivityOptions.abort(options);
            options = null;
        }
@@ -4513,6 +4542,9 @@ final class ActivityStack {
            boolean toTop) {
        TaskRecord task = new TaskRecord(mService, taskId, info, intent, voiceSession,
                voiceInteractor);
        if (mTaskPositioner != null) {
            mTaskPositioner.updateDefaultBounds(task, mTaskHistory);
        }
        addTask(task, toTop, false);
        return task;
    }
@@ -4548,7 +4580,7 @@ final class ActivityStack {
        }
    }

    void addAppToken(ActivityRecord r, TaskRecord task) {
    void addConfigOverride(ActivityRecord r, TaskRecord task) {
        final Rect bounds = task.getLaunchBounds();
        final Configuration config =
                mWindowManager.addAppToken(task.mActivities.indexOf(r), r.appToken,
+4 −11
Original line number Diff line number Diff line
@@ -2977,7 +2977,7 @@ public final class ActivityStackSupervisor implements DisplayListener {

            }
        }
        stack.mBounds = stack.mFullscreen ? null : new Rect(bounds);
        stack.setBounds(bounds);

        if (r != null) {
            final boolean updated = stack.ensureActivityConfigurationLocked(r, 0);
@@ -3112,7 +3112,7 @@ public final class ActivityStackSupervisor implements DisplayListener {
                "Added restored task=" + task + " to stack=" + stack);
        final ArrayList<ActivityRecord> activities = task.mActivities;
        for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
            stack.addAppToken(activities.get(activityNdx), task);
            stack.addConfigOverride(activities.get(activityNdx), task);
        }
        return true;
    }
@@ -4387,13 +4387,8 @@ public final class ActivityStackSupervisor implements DisplayListener {
            if (DEBUG_STACK) Slog.d(TAG_STACK, "attachToDisplayLocked: " + this
                    + " to display=" + activityDisplay + " onTop=" + onTop);
            mActivityDisplay = activityDisplay;
            mStack.mDisplayId = activityDisplay.mDisplayId;
            mStack.mStacks = activityDisplay.mStacks;

            mStack.attachDisplay(activityDisplay, onTop);
            activityDisplay.attachActivities(mStack, onTop);
            mStack.mBounds =
                    mWindowManager.attachStack(mStackId, activityDisplay.mDisplayId, onTop);
            mStack.mFullscreen = mStack.mBounds == null;
        }

        @Override
@@ -4465,9 +4460,7 @@ public final class ActivityStackSupervisor implements DisplayListener {
            if (mActivityDisplay != null) {
                mActivityDisplay.detachActivitiesLocked(mStack);
                mActivityDisplay = null;
                mStack.mDisplayId = -1;
                mStack.mStacks = null;
                mWindowManager.detachStack(mStackId);
                mStack.detachDisplay();
            }
        }

+142 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2015 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.graphics.Point;
import android.graphics.Rect;
import android.view.Display;

import java.util.ArrayList;

/**
 * Determines where a launching task should be positioned and sized on the display.
 */
class LaunchingTaskPositioner {
    // Determines how close window frames/corners have to be to call them colliding.
    private static final int BOUNDS_CONFLICT_MIN_DISTANCE = 4;

    // Task will receive dimensions based on available dimensions divided by this.
    private static final int WINDOW_SIZE_DENOMINATOR = 2;

    // Task will receive margins based on available dimensions divided by this.
    private static final int MARGIN_SIZE_DENOMINATOR = 4;

    // If task bounds collide with some other, we will step and try again until we find a good
    // position. The step will be determined by using dimensions and dividing it by this.
    private static final int STEP_DENOMINATOR = 16;

    // We always want to step by at least this.
    private static final int MINIMAL_STEP = 1;

    private boolean mDefaultStartBoundsConfigurationSet = false;
    private final Rect mAvailableRect = new Rect();
    private int mDefaultFreeformStartX;
    private int mDefaultFreeformStartY;
    private int mDefaultFreeformWidth;
    private int mDefaultFreeformHeight;
    private int mDefaultFreeformStepHorizontal;
    private int mDefaultFreeformStepVertical;
    private int mDisplayWidth;
    private int mDisplayHeight;

    void setDisplay(Display display) {
        Point size = new Point();
        display.getSize(size);
        mDisplayWidth = size.x;
        mDisplayHeight = size.y;
    }

    void configure(Rect stackBounds) {
        if (stackBounds == null) {
            mAvailableRect.set(0, 0, mDisplayWidth, mDisplayHeight);
        } else {
            mAvailableRect.set(stackBounds);
        }
        int width = mAvailableRect.width();
        int height = mAvailableRect.height();
        mDefaultFreeformStartX = mAvailableRect.left + width / MARGIN_SIZE_DENOMINATOR;
        mDefaultFreeformStartY = mAvailableRect.top + height / MARGIN_SIZE_DENOMINATOR;
        mDefaultFreeformWidth = width / WINDOW_SIZE_DENOMINATOR;
        mDefaultFreeformHeight = height / WINDOW_SIZE_DENOMINATOR;
        mDefaultFreeformStepHorizontal = Math.max(width / STEP_DENOMINATOR, MINIMAL_STEP);
        mDefaultFreeformStepVertical = Math.max(height / STEP_DENOMINATOR, MINIMAL_STEP);
        mDefaultStartBoundsConfigurationSet = true;
    }

    /**
     * Tries to set task's bound in a way that it won't collide with any other task. By colliding
     * we mean that two tasks have left-top corner very close to each other, so one might get
     * obfuscated by the other one.
     *
     * @param task Task for which we want to find bounds that won't collide with other.
     * @param tasks Existing tasks with which we don't want to collide.
     */
    void updateDefaultBounds(TaskRecord task, ArrayList<TaskRecord> tasks) {
        if (!mDefaultStartBoundsConfigurationSet) {
            return;
        }
        int startX = mDefaultFreeformStartX;
        int startY = mDefaultFreeformStartY;
        final int right = mAvailableRect.right;
        final int bottom = mAvailableRect.bottom;
        boolean restarted = false;
        while (boundsConflict(startX, startY, tasks)) {
            // Unfortunately there is already a task at that spot, so we need to look for some
            // other place.
            startX += mDefaultFreeformStepHorizontal;
            startY += mDefaultFreeformStepVertical;
            if (startX + mDefaultFreeformWidth > right
                    || startY + mDefaultFreeformHeight > bottom) {
                // We don't want the task to go outside of the display, because it won't look
                // nice. Let's restart from the top instead, because there should be some space
                // there.
                startX = mAvailableRect.left;
                startY = mAvailableRect.top;
                restarted = true;
            }
            if (restarted
                    && (startX > mDefaultFreeformStartX || startY > mDefaultFreeformStartY)) {
                // If we restarted and crossed the initial position, let's not struggle anymore.
                // The user already must have ton of tasks visible, we can just smack the new
                // one in the center.
                startX = mDefaultFreeformStartX;
                startY = mDefaultFreeformStartY;
                break;
            }
        }
        task.setInitialBounds(startX, startY, startX + mDefaultFreeformWidth,
                startY + mDefaultFreeformHeight);
    }

    private boolean boundsConflict(int startX, int startY, ArrayList<TaskRecord> tasks) {
        for (int i = tasks.size() - 1; i >= 0; i--) {
            TaskRecord task = tasks.get(i);
            if (!task.mActivities.isEmpty()) {
                Rect bounds = task.mBounds;
                if (bounds != null && (Math.abs(bounds.left - startX) < BOUNDS_CONFLICT_MIN_DISTANCE
                        || Math.abs(bounds.top - startY) < BOUNDS_CONFLICT_MIN_DISTANCE)) {
                    return true;
                }
            }
        }
        return false;
    }

    void reset() {
        mDefaultStartBoundsConfigurationSet = false;
    }
}
+8 −0
Original line number Diff line number Diff line
@@ -1217,6 +1217,14 @@ final class TaskRecord {
        return mLastNonFullscreenBounds;
    }

    void setInitialBounds(int left, int top, int right, int bottom) {
        if (mBounds == null) {
            mBounds = new Rect();
        }
        mBounds.set(left, top, right, bottom);
        mLastNonFullscreenBounds = mBounds;
    }

    void dump(PrintWriter pw, String prefix) {
        pw.print(prefix); pw.print("userId="); pw.print(userId);
                pw.print(" effectiveUid="); UserHandle.formatUid(pw, effectiveUid);