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
diff --git a/services/core/java/com/android/server/am/LaunchingBoundsController.java b/services/core/java/com/android/server/am/LaunchingBoundsController.java
new file mode 100644
index 0000000..8345ba6
--- /dev/null
+++ b/services/core/java/com/android/server/am/LaunchingBoundsController.java
@@ -0,0 +1,160 @@
+/*
+ * 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);
+ }
+}