Merge changes from topic "embedding_interface_0922" into sc-v2-dev
* changes:
Update Reference implementation for window-extensions.
Add RTL support in split activity organizer
Deduplicate SplitInfo states from sidecar
Update extensions embedding component package
Update reference WM layout component interface
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 2e22b92..80554d7 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -3616,6 +3616,11 @@
}
activity.mLaunchedFromBubble = r.mLaunchedFromBubble;
activity.mCalled = false;
+ // Assigning the activity to the record before calling onCreate() allows
+ // ActivityThread#getActivity() lookup for the callbacks triggered from
+ // ActivityLifecycleCallbacks#onActivityCreated() or
+ // ActivityLifecycleCallback#onActivityPostCreated().
+ r.activity = activity;
if (r.isPersistable()) {
mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
} else {
@@ -3626,7 +3631,6 @@
"Activity " + r.intent.getComponent().toShortString() +
" did not call through to super.onCreate()");
}
- r.activity = activity;
mLastReportedWindowingMode.put(activity.getActivityToken(),
config.windowConfiguration.getWindowingMode());
}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/ExtensionProvider.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/ExtensionProvider.java
deleted file mode 100644
index ce4e103..0000000
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/ExtensionProvider.java
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright (C) 2021 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 androidx.window.extensions;
-
-import android.content.Context;
-
-import androidx.annotation.NonNull;
-import androidx.window.extensions.embedding.ActivityEmbeddingComponent;
-import androidx.window.extensions.organizer.EmbeddingExtensionImpl;
-
-/**
- * Provider class that will instantiate the library implementation. It must be included in the
- * vendor library, and the vendor implementation must match the signature of this class.
- */
-public class ExtensionProvider {
- /**
- * Provides a simple implementation of {@link ExtensionInterface} that can be replaced by
- * an OEM by overriding this method.
- */
- public static ExtensionInterface getExtensionImpl(Context context) {
- return new SampleExtensionImpl(context);
- }
-
- /** Provides a reference implementation of {@link ActivityEmbeddingComponent}. */
- public static ActivityEmbeddingComponent getActivityEmbeddingExtensionImpl(
- @NonNull Context context) {
- return new EmbeddingExtensionImpl();
- }
-
- /**
- * The support library will use this method to check API version compatibility.
- * @return API version string in MAJOR.MINOR.PATCH-description format.
- */
- public static String getApiVersion() {
- return "1.0.0-settings_sample";
- }
-}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/StubExtension.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/StubExtension.java
deleted file mode 100644
index 6a53efe..0000000
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/StubExtension.java
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Copyright (C) 2021 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 androidx.window.extensions;
-
-import android.app.Activity;
-
-import androidx.annotation.NonNull;
-
-import java.util.HashSet;
-import java.util.Set;
-
-/**
- * Basic implementation of the {@link ExtensionInterface}. An OEM can choose to use it as the base
- * class for their implementation.
- */
-abstract class StubExtension implements ExtensionInterface {
-
- private ExtensionCallback mExtensionCallback;
- private final Set<Activity> mWindowLayoutChangeListenerActivities = new HashSet<>();
-
- StubExtension() {
- }
-
- @Override
- public void setExtensionCallback(@NonNull ExtensionCallback extensionCallback) {
- this.mExtensionCallback = extensionCallback;
- }
-
- @Override
- public void onWindowLayoutChangeListenerAdded(@NonNull Activity activity) {
- this.mWindowLayoutChangeListenerActivities.add(activity);
- this.onListenersChanged();
- }
-
- @Override
- public void onWindowLayoutChangeListenerRemoved(@NonNull Activity activity) {
- this.mWindowLayoutChangeListenerActivities.remove(activity);
- this.onListenersChanged();
- }
-
- void updateWindowLayout(@NonNull Activity activity,
- @NonNull ExtensionWindowLayoutInfo newLayout) {
- if (this.mExtensionCallback != null) {
- mExtensionCallback.onWindowLayoutChanged(activity, newLayout);
- }
- }
-
- @NonNull
- Set<Activity> getActivitiesListeningForLayoutChanges() {
- return mWindowLayoutChangeListenerActivities;
- }
-
- protected boolean hasListeners() {
- return !mWindowLayoutChangeListenerActivities.isEmpty();
- }
-
- protected abstract void onListenersChanged();
-}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsImpl.java
new file mode 100644
index 0000000..990d7b6
--- /dev/null
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsImpl.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2021 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 androidx.window.extensions;
+
+import android.app.ActivityThread;
+import android.content.Context;
+
+import androidx.annotation.NonNull;
+import androidx.window.extensions.embedding.ActivityEmbeddingComponent;
+import androidx.window.extensions.embedding.SplitController;
+import androidx.window.extensions.layout.WindowLayoutComponent;
+import androidx.window.extensions.layout.WindowLayoutComponentImpl;
+
+/**
+ * The reference implementation of {@link WindowExtensions} that implements the initial API version.
+ */
+public class WindowExtensionsImpl implements WindowExtensions {
+
+ private final Object mLock = new Object();
+ private volatile WindowLayoutComponent mWindowLayoutComponent;
+ private volatile SplitController mSplitController;
+
+ @Override
+ public int getVendorApiLevel() {
+ return 1;
+ }
+
+ @Override
+ public boolean isWindowLayoutComponentAvailable() {
+ return true;
+ }
+
+ @Override
+ public WindowLayoutComponent getWindowLayoutComponent() {
+ if (mWindowLayoutComponent == null) {
+ synchronized (mLock) {
+ if (mWindowLayoutComponent == null) {
+ Context context = ActivityThread.currentApplication();
+ mWindowLayoutComponent = new WindowLayoutComponentImpl(context);
+ }
+ }
+ }
+ return mWindowLayoutComponent;
+ }
+
+ /**
+ * Returns {@code true} if {@link ActivityEmbeddingComponent} is present on the device,
+ * {@code false} otherwise. If the component is not available the developer will receive a
+ * single callback with empty data or default values where possible.
+ */
+ @Override
+ public boolean isEmbeddingComponentAvailable() {
+ return true;
+ }
+
+ /**
+ * Returns the OEM implementation of {@link ActivityEmbeddingComponent} if it is supported on
+ * the device. The implementation must match the API level reported in
+ * {@link androidx.window.extensions.WindowExtensions}. An
+ * {@link UnsupportedOperationException} will be thrown if the device does not support
+ * Activity Embedding. Use
+ * {@link WindowExtensions#isEmbeddingComponentAvailable()} to determine if
+ * {@link ActivityEmbeddingComponent} is present.
+ * @return the OEM implementation of {@link ActivityEmbeddingComponent}
+ */
+ @NonNull
+ public ActivityEmbeddingComponent getActivityEmbeddingComponent() {
+ if (mSplitController == null) {
+ synchronized (mLock) {
+ if (mSplitController == null) {
+ mSplitController = new SplitController();
+ }
+ }
+ }
+ return mSplitController;
+ }
+}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsProvider.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsProvider.java
new file mode 100644
index 0000000..f9e1f07
--- /dev/null
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsProvider.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2021 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 androidx.window.extensions;
+
+import android.annotation.NonNull;
+
+/**
+ * Provides the OEM implementation of {@link WindowExtensions}.
+ */
+public class WindowExtensionsProvider {
+
+ private static final WindowExtensions sWindowExtensions = new WindowExtensionsImpl();
+
+ /**
+ * Returns the OEM implementation of {@link WindowExtensions}. This method is implemented in
+ * the library provided on the device and overwrites one in the Jetpack library included in
+ * apps.
+ * @return the OEM implementation of {@link WindowExtensions}
+ */
+ @NonNull
+ public static WindowExtensions getWindowExtensions() {
+ return sWindowExtensions;
+ }
+}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/JetpackTaskFragmentOrganizer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java
similarity index 98%
rename from libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/JetpackTaskFragmentOrganizer.java
rename to libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java
index 46c8ffe..85ef270 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/JetpackTaskFragmentOrganizer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package androidx.window.extensions.organizer;
+package androidx.window.extensions.embedding;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
@@ -36,7 +36,6 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-import androidx.window.extensions.embedding.SplitRule;
import java.util.Map;
import java.util.concurrent.Executor;
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/SplitContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitContainer.java
similarity index 93%
rename from libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/SplitContainer.java
rename to libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitContainer.java
index a41557d..06e7d14 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/SplitContainer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitContainer.java
@@ -14,15 +14,11 @@
* limitations under the License.
*/
-package androidx.window.extensions.organizer;
+package androidx.window.extensions.embedding;
import android.annotation.NonNull;
import android.app.Activity;
-import androidx.window.extensions.embedding.SplitPairRule;
-import androidx.window.extensions.embedding.SplitPlaceholderRule;
-import androidx.window.extensions.embedding.SplitRule;
-
/**
* Client-side descriptor of a split that holds two containers.
*/
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/SplitController.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
similarity index 88%
rename from libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/SplitController.java
rename to libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
index a783fcd..e1c8b11 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/SplitController.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package androidx.window.extensions.organizer;
+package androidx.window.extensions.embedding;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -31,19 +31,10 @@
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
-import android.util.Pair;
import android.window.TaskFragmentAppearedInfo;
import android.window.TaskFragmentInfo;
import android.window.WindowContainerTransaction;
-import androidx.window.extensions.embedding.ActivityRule;
-import androidx.window.extensions.embedding.EmbeddingRule;
-import androidx.window.extensions.embedding.SplitInfo;
-import androidx.window.extensions.embedding.SplitPairRule;
-import androidx.window.extensions.embedding.SplitPlaceholderRule;
-import androidx.window.extensions.embedding.SplitRule;
-import androidx.window.extensions.embedding.TaskFragment;
-
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
@@ -53,7 +44,8 @@
/**
* Main controller class that manages split states and presentation.
*/
-public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmentCallback {
+public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmentCallback,
+ ActivityEmbeddingComponent {
private final SplitPresenter mPresenter;
@@ -64,6 +56,7 @@
// Callback to Jetpack to notify about changes to split states.
private @NonNull Consumer<List<SplitInfo>> mEmbeddingCallback;
+ private final List<SplitInfo> mLastReportedSplitStates = new ArrayList<>();
public SplitController() {
mPresenter = new SplitPresenter(new MainThreadExecutor(), this);
@@ -77,6 +70,7 @@
}
/** Updates the embedding rules applied to future activity launches. */
+ @Override
public void setEmbeddingRules(@NonNull Set<EmbeddingRule> rules) {
mSplitRules.clear();
mSplitRules.addAll(rules);
@@ -103,7 +97,8 @@
/**
* Registers the split organizer callback to notify about changes to active splits.
*/
- public void setEmbeddingCallback(@NonNull Consumer<List<SplitInfo>> callback) {
+ @Override
+ public void setSplitInfoCallback(@NonNull Consumer<List<SplitInfo>> callback) {
mEmbeddingCallback = callback;
updateCallbackIfNecessary();
}
@@ -119,8 +114,8 @@
container.setInfo(taskFragmentAppearedInfo.getTaskFragmentInfo());
if (container.isFinished()) {
mPresenter.cleanupContainer(container, false /* shouldFinishDependent */);
- updateCallbackIfNecessary();
}
+ updateCallbackIfNecessary();
}
@Override
@@ -139,8 +134,8 @@
final boolean shouldFinishDependent =
!taskFragmentInfo.isTaskClearedForReuse();
mPresenter.cleanupContainer(container, shouldFinishDependent);
- updateCallbackIfNecessary();
}
+ updateCallbackIfNecessary();
}
@Override
@@ -164,18 +159,23 @@
}
}
+ void onActivityCreated(@NonNull Activity launchedActivity) {
+ handleActivityCreated(launchedActivity);
+ updateCallbackIfNecessary();
+ }
+
/**
* Checks if the activity start should be routed to a particular container. It can create a new
* container for the activity and a new split container if necessary.
*/
// TODO(b/190433398): Break down into smaller functions.
- void onActivityCreated(@NonNull Activity launchedActivity) {
+ void handleActivityCreated(@NonNull Activity launchedActivity) {
final List<EmbeddingRule> splitRules = getSplitRules();
final TaskFragmentContainer currentContainer = getContainerWithActivity(
launchedActivity.getActivityToken(), launchedActivity);
// Check if the activity is configured to always be expanded.
- if (shouldExpand(launchedActivity, splitRules)) {
+ if (shouldExpand(launchedActivity, null, splitRules)) {
if (shouldContainerBeExpanded(currentContainer)) {
// Make sure that the existing container is expanded
mPresenter.expandTaskFragment(currentContainer.getTaskFragmentToken());
@@ -240,8 +240,6 @@
mPresenter.createNewSplitContainer(activityBelow, launchedActivity,
splitPairRule);
-
- updateCallbackIfNecessary();
}
private void onActivityConfigurationChanged(@NonNull Activity activity) {
@@ -501,7 +499,7 @@
continue;
}
SplitPlaceholderRule placeholderRule = (SplitPlaceholderRule) rule;
- if (placeholderRule.getActivityPredicate().test(activity)) {
+ if (placeholderRule.matchesActivity(activity)) {
return placeholderRule;
}
}
@@ -515,8 +513,16 @@
if (mEmbeddingCallback == null) {
return;
}
- // TODO(b/190433398): Check if something actually changed
- mEmbeddingCallback.accept(getActiveSplitStates());
+ if (!allActivitiesCreated()) {
+ return;
+ }
+ List<SplitInfo> currentSplitStates = getActiveSplitStates();
+ if (mLastReportedSplitStates.equals(currentSplitStates)) {
+ return;
+ }
+ mLastReportedSplitStates.clear();
+ mLastReportedSplitStates.addAll(currentSplitStates);
+ mEmbeddingCallback.accept(currentSplitStates);
}
/**
@@ -525,20 +531,46 @@
private List<SplitInfo> getActiveSplitStates() {
List<SplitInfo> splitStates = new ArrayList<>();
for (SplitContainer container : mSplitContainers) {
- TaskFragment primaryContainer =
- new TaskFragment(
+ if (container.getPrimaryContainer().isEmpty()
+ || container.getSecondaryContainer().isEmpty()) {
+ // Skipping containers that do not have any activities to report.
+ continue;
+ }
+ ActivityStack primaryContainer =
+ new ActivityStack(
container.getPrimaryContainer().collectActivities());
- TaskFragment secondaryContainer =
- new TaskFragment(
+ ActivityStack secondaryContainer =
+ new ActivityStack(
container.getSecondaryContainer().collectActivities());
SplitInfo splitState = new SplitInfo(primaryContainer,
- secondaryContainer, container.getSplitRule().getSplitRatio());
+ secondaryContainer,
+ // Splits that are not showing side-by-side are reported as having 0 split
+ // ratio, since by definition in the API the primary container occupies no
+ // width of the split when covered by the secondary.
+ mPresenter.shouldShowSideBySide(container)
+ ? container.getSplitRule().getSplitRatio()
+ : 0.0f);
splitStates.add(splitState);
}
return splitStates;
}
/**
+ * Checks if all activities that are registered with the containers have already appeared in
+ * the client.
+ */
+ private boolean allActivitiesCreated() {
+ for (TaskFragmentContainer container : mContainers) {
+ if (container.getInfo() == null
+ || container.getInfo().getActivities().size()
+ != container.collectActivities().size()) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
* Returns {@code true} if the container is expanded to occupy full task size.
* Returns {@code false} if the container is included in an active split.
*/
@@ -567,8 +599,7 @@
continue;
}
SplitPairRule pairRule = (SplitPairRule) rule;
- if (pairRule.getActivityIntentPredicate().test(
- new Pair(primaryActivity, secondaryActivityIntent))) {
+ if (pairRule.matchesActivityIntentPair(primaryActivity, secondaryActivityIntent)) {
return pairRule;
}
}
@@ -587,10 +618,9 @@
}
SplitPairRule pairRule = (SplitPairRule) rule;
final Intent intent = secondaryActivity.getIntent();
- if (pairRule.getActivityPairPredicate().test(
- new Pair(primaryActivity, secondaryActivity))
- && (intent == null || pairRule.getActivityIntentPredicate().test(
- new Pair(primaryActivity, intent)))) {
+ if (pairRule.matchesActivityPair(primaryActivity, secondaryActivity)
+ && (intent == null
+ || pairRule.matchesActivityIntentPair(primaryActivity, intent))) {
return pairRule;
}
}
@@ -611,7 +641,7 @@
* Returns {@code true} if an Activity with the provided component name should always be
* expanded to occupy full task bounds. Such activity must not be put in a split.
*/
- private static boolean shouldExpand(@NonNull Activity activity,
+ private static boolean shouldExpand(@Nullable Activity activity, @Nullable Intent intent,
List<EmbeddingRule> splitRules) {
if (splitRules == null) {
return false;
@@ -624,7 +654,9 @@
if (!activityRule.shouldAlwaysExpand()) {
continue;
}
- if (activityRule.getActivityPredicate().test(activity)) {
+ if (activity != null && activityRule.matchesActivity(activity)) {
+ return true;
+ } else if (intent != null && activityRule.matchesIntent(intent)) {
return true;
}
}
@@ -678,11 +710,11 @@
/** Executor that posts on the main application thread. */
private static class MainThreadExecutor implements Executor {
- private final Handler handler = new Handler(Looper.getMainLooper());
+ private final Handler mHandler = new Handler(Looper.getMainLooper());
@Override
public void execute(Runnable r) {
- handler.post(r);
+ mHandler.post(r);
}
}
@@ -703,13 +735,25 @@
}
final Activity launchingActivity = (Activity) who;
- if (!setLaunchingToSideContainer(launchingActivity, intent, options)) {
+ if (shouldExpand(null, intent, getSplitRules())) {
+ setLaunchingInExpandedContainer(launchingActivity, options);
+ } else if (!setLaunchingToSideContainer(launchingActivity, intent, options)) {
setLaunchingInSameContainer(launchingActivity, intent, options);
}
return super.onStartActivity(who, intent, options);
}
+ private void setLaunchingInExpandedContainer(Activity launchingActivity, Bundle options) {
+ TaskFragmentContainer newContainer = mPresenter.createNewExpandedContainer(
+ launchingActivity);
+
+ // Amend the request to let the WM know that the activity should be placed in the
+ // dedicated container.
+ options.putBinder(ActivityOptions.KEY_LAUNCH_TASK_FRAGMENT_TOKEN,
+ newContainer.getTaskFragmentToken());
+ }
+
/**
* Returns {@code true} if the activity that is going to be started via the
* {@code intent} should be paired with the {@code launchingActivity} and is set to be
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/SplitPresenter.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
similarity index 80%
rename from libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/SplitPresenter.java
rename to libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
index ac85ac8..25292b9 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/SplitPresenter.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
@@ -14,16 +14,19 @@
* limitations under the License.
*/
-package androidx.window.extensions.organizer;
+package androidx.window.extensions.embedding;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import android.app.Activity;
+import android.content.Context;
import android.content.Intent;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.os.Bundle;
import android.os.IBinder;
+import android.util.LayoutDirection;
+import android.view.View;
import android.view.WindowInsets;
import android.view.WindowMetrics;
import android.window.TaskFragmentCreationParams;
@@ -32,8 +35,6 @@
import androidx.annotation.IntDef;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-import androidx.window.extensions.embedding.SplitPairRule;
-import androidx.window.extensions.embedding.SplitRule;
import java.util.concurrent.Executor;
@@ -42,13 +43,13 @@
* {@link SplitController}.
*/
class SplitPresenter extends JetpackTaskFragmentOrganizer {
- private static final int POSITION_LEFT = 0;
- private static final int POSITION_RIGHT = 1;
+ private static final int POSITION_START = 0;
+ private static final int POSITION_END = 1;
private static final int POSITION_FILL = 2;
@IntDef(value = {
- POSITION_LEFT,
- POSITION_RIGHT,
+ POSITION_START,
+ POSITION_END,
POSITION_FILL,
})
private @interface Position {}
@@ -96,13 +97,15 @@
final WindowContainerTransaction wct = new WindowContainerTransaction();
final Rect parentBounds = getParentContainerBounds(primaryActivity);
- final Rect primaryRectBounds = getBoundsForPosition(POSITION_LEFT, parentBounds, rule);
+ final Rect primaryRectBounds = getBoundsForPosition(POSITION_START, parentBounds, rule,
+ isLtr(primaryActivity, rule));
final TaskFragmentContainer primaryContainer = prepareContainerForActivity(wct,
primaryActivity, primaryRectBounds, null);
// Create new empty task fragment
- TaskFragmentContainer secondaryContainer = mController.newContainer(null);
- final Rect secondaryRectBounds = getBoundsForPosition(POSITION_RIGHT, parentBounds, rule);
+ final TaskFragmentContainer secondaryContainer = mController.newContainer(null);
+ final Rect secondaryRectBounds = getBoundsForPosition(POSITION_END, parentBounds,
+ rule, isLtr(primaryActivity, rule));
createTaskFragment(wct, secondaryContainer.getTaskFragmentToken(),
primaryActivity.getActivityToken(), secondaryRectBounds,
WINDOWING_MODE_MULTI_WINDOW);
@@ -135,11 +138,13 @@
final WindowContainerTransaction wct = new WindowContainerTransaction();
final Rect parentBounds = getParentContainerBounds(primaryActivity);
- final Rect primaryRectBounds = getBoundsForPosition(POSITION_LEFT, parentBounds, rule);
+ final Rect primaryRectBounds = getBoundsForPosition(POSITION_START, parentBounds, rule,
+ isLtr(primaryActivity, rule));
final TaskFragmentContainer primaryContainer = prepareContainerForActivity(wct,
primaryActivity, primaryRectBounds, null);
- final Rect secondaryRectBounds = getBoundsForPosition(POSITION_RIGHT, parentBounds, rule);
+ final Rect secondaryRectBounds = getBoundsForPosition(POSITION_END, parentBounds, rule,
+ isLtr(primaryActivity, rule));
final TaskFragmentContainer secondaryContainer = prepareContainerForActivity(wct,
secondaryActivity, secondaryRectBounds, primaryContainer);
@@ -153,6 +158,20 @@
}
/**
+ * Creates a new expanded container.
+ */
+ TaskFragmentContainer createNewExpandedContainer(@NonNull Activity launchingActivity) {
+ final TaskFragmentContainer newContainer = mController.newContainer(null);
+
+ final WindowContainerTransaction wct = new WindowContainerTransaction();
+ createTaskFragment(wct, newContainer.getTaskFragmentToken(),
+ launchingActivity.getActivityToken(), new Rect(), WINDOWING_MODE_MULTI_WINDOW);
+
+ applyTransaction(wct);
+ return newContainer;
+ }
+
+ /**
* Creates a new container or resizes an existing container for activity to the provided bounds.
* @param activity The activity to be re-parented to the container if necessary.
* @param containerToAvoid Re-parent from this container if an activity is already in it.
@@ -197,8 +216,10 @@
void startActivityToSide(@NonNull Activity launchingActivity, @NonNull Intent activityIntent,
@Nullable Bundle activityOptions, @NonNull SplitRule rule) {
final Rect parentBounds = getParentContainerBounds(launchingActivity);
- final Rect primaryRectBounds = getBoundsForPosition(POSITION_LEFT, parentBounds, rule);
- final Rect secondaryRectBounds = getBoundsForPosition(POSITION_RIGHT, parentBounds, rule);
+ final Rect primaryRectBounds = getBoundsForPosition(POSITION_START, parentBounds, rule,
+ isLtr(launchingActivity, rule));
+ final Rect secondaryRectBounds = getBoundsForPosition(POSITION_END, parentBounds, rule,
+ isLtr(launchingActivity, rule));
TaskFragmentContainer primaryContainer = mController.getContainerWithActivity(
launchingActivity.getActivityToken());
@@ -231,8 +252,15 @@
// Getting the parent bounds using the updated container - it will have the recent value.
final Rect parentBounds = getParentContainerBounds(updatedContainer);
final SplitRule rule = splitContainer.getSplitRule();
- final Rect primaryRectBounds = getBoundsForPosition(POSITION_LEFT, parentBounds, rule);
- final Rect secondaryRectBounds = getBoundsForPosition(POSITION_RIGHT, parentBounds, rule);
+ final Activity activity = splitContainer.getPrimaryContainer().getTopNonFinishingActivity();
+ if (activity == null) {
+ return;
+ }
+ final boolean isLtr = isLtr(activity, rule);
+ final Rect primaryRectBounds = getBoundsForPosition(POSITION_START, parentBounds, rule,
+ isLtr);
+ final Rect secondaryRectBounds = getBoundsForPosition(POSITION_END, parentBounds, rule,
+ isLtr);
// If the task fragments are not registered yet, the positions will be updated after they
// are created again.
@@ -283,36 +311,64 @@
// TODO(b/190433398): Supply correct insets.
final WindowMetrics parentMetrics = new WindowMetrics(parentBounds,
new WindowInsets(new Rect()));
- return rule.getParentWindowMetricsPredicate().test(parentMetrics);
+ return rule.checkParentMetrics(parentMetrics);
}
@NonNull
private Rect getBoundsForPosition(@Position int position, @NonNull Rect parentBounds,
- @NonNull SplitRule rule) {
+ @NonNull SplitRule rule, boolean isLtr) {
if (!shouldShowSideBySide(parentBounds, rule)) {
return new Rect();
}
- float splitRatio = rule.getSplitRatio();
+ final float splitRatio = rule.getSplitRatio();
+ final float rtlSplitRatio = 1 - splitRatio;
switch (position) {
- case POSITION_LEFT:
- return new Rect(
- parentBounds.left,
- parentBounds.top,
- (int) (parentBounds.left + parentBounds.width() * splitRatio),
- parentBounds.bottom);
- case POSITION_RIGHT:
- return new Rect(
- (int) (parentBounds.left + parentBounds.width() * splitRatio),
- parentBounds.top,
- parentBounds.right,
- parentBounds.bottom);
+ case POSITION_START:
+ return isLtr ? getLeftContainerBounds(parentBounds, splitRatio)
+ : getRightContainerBounds(parentBounds, rtlSplitRatio);
+ case POSITION_END:
+ return isLtr ? getRightContainerBounds(parentBounds, splitRatio)
+ : getLeftContainerBounds(parentBounds, rtlSplitRatio);
case POSITION_FILL:
return parentBounds;
}
return parentBounds;
}
+ private Rect getLeftContainerBounds(@NonNull Rect parentBounds, float splitRatio) {
+ return new Rect(
+ parentBounds.left,
+ parentBounds.top,
+ (int) (parentBounds.left + parentBounds.width() * splitRatio),
+ parentBounds.bottom);
+ }
+
+ private Rect getRightContainerBounds(@NonNull Rect parentBounds, float splitRatio) {
+ return new Rect(
+ (int) (parentBounds.left + parentBounds.width() * splitRatio),
+ parentBounds.top,
+ parentBounds.right,
+ parentBounds.bottom);
+ }
+
+ /**
+ * Checks if a split with the provided rule should be displays in left-to-right layout
+ * direction, either always or with the current configuration.
+ */
+ private boolean isLtr(@NonNull Context context, @NonNull SplitRule rule) {
+ switch (rule.getLayoutDirection()) {
+ case LayoutDirection.LOCALE:
+ return context.getResources().getConfiguration().getLayoutDirection()
+ == View.LAYOUT_DIRECTION_LTR;
+ case LayoutDirection.RTL:
+ return false;
+ case LayoutDirection.LTR:
+ default:
+ return true;
+ }
+ }
+
@NonNull
Rect getParentContainerBounds(@NonNull TaskFragmentContainer container) {
final Configuration parentConfig = mFragmentParentConfigs.get(
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/TaskFragmentAnimationAdapter.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationAdapter.java
similarity index 98%
rename from libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/TaskFragmentAnimationAdapter.java
rename to libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationAdapter.java
index 155c649..65bd9f3 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/TaskFragmentAnimationAdapter.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationAdapter.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package androidx.window.extensions.organizer;
+package androidx.window.extensions.embedding;
import android.graphics.Rect;
import android.view.Choreographer;
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/TaskFragmentAnimationController.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationController.java
similarity index 97%
rename from libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/TaskFragmentAnimationController.java
rename to libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationController.java
index 6631243..6579766 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/TaskFragmentAnimationController.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationController.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package androidx.window.extensions.organizer;
+package androidx.window.extensions.embedding;
import static android.view.WindowManager.TRANSIT_OLD_TASK_FRAGMENT_CHANGE;
import static android.view.WindowManager.TRANSIT_OLD_TASK_FRAGMENT_CLOSE;
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/TaskFragmentAnimationRunner.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationRunner.java
similarity index 99%
rename from libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/TaskFragmentAnimationRunner.java
rename to libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationRunner.java
index da3d116..bb37fff 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/TaskFragmentAnimationRunner.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationRunner.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package androidx.window.extensions.organizer;
+package androidx.window.extensions.embedding;
import static android.view.RemoteAnimationTarget.MODE_CLOSING;
import static android.view.WindowManager.TRANSIT_OLD_TASK_FRAGMENT_CHANGE;
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/TaskFragmentAnimationSpec.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationSpec.java
similarity index 99%
rename from libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/TaskFragmentAnimationSpec.java
rename to libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationSpec.java
index ddcb27d..7129590 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/TaskFragmentAnimationSpec.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationSpec.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package androidx.window.extensions.organizer;
+package androidx.window.extensions.embedding;
import static android.view.RemoteAnimationTarget.MODE_CLOSING;
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/TaskFragmentContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java
similarity index 98%
rename from libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/TaskFragmentContainer.java
rename to libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java
index 8503b9f..54e44a7 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/TaskFragmentContainer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package androidx.window.extensions.organizer;
+package androidx.window.extensions.embedding;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -212,7 +212,9 @@
@NonNull WindowContainerTransaction wct, @NonNull SplitController controller) {
// Finish own activities
for (Activity activity : collectActivities()) {
- activity.finish();
+ if (!activity.isFinishing()) {
+ activity.finish();
+ }
}
if (!shouldFinishDependent) {
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/SampleExtensionImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java
similarity index 63%
rename from libs/WindowManager/Jetpack/src/androidx/window/extensions/SampleExtensionImpl.java
rename to libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java
index a0d5b00..383d91d 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/SampleExtensionImpl.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package androidx.window.extensions;
+package androidx.window.extensions.layout;
import static android.view.Display.DEFAULT_DISPLAY;
@@ -36,19 +36,27 @@
import androidx.window.util.PriorityDataProducer;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
import java.util.Optional;
+import java.util.Set;
+import java.util.function.Consumer;
/**
- * Reference implementation of androidx.window.extensions OEM interface for use with
+ * Reference implementation of androidx.window.extensions.layout OEM interface for use with
* WindowManager Jetpack.
*
* NOTE: This version is a work in progress and under active development. It MUST NOT be used in
* production builds since the interface can still change before reaching stable version.
* Please refer to {@link androidx.window.sidecar.SampleSidecarImpl} instead.
*/
-class SampleExtensionImpl extends StubExtension {
+public class WindowLayoutComponentImpl implements WindowLayoutComponent {
private static final String TAG = "SampleExtension";
+ private static WindowLayoutComponent sInstance;
+
+ private final Map<Activity, Consumer<WindowLayoutInfo>> mWindowLayoutChangeListeners =
+ new HashMap<>();
private final SettingsDevicePostureProducer mSettingsDevicePostureProducer;
private final DataProducer<Integer> mDevicePostureProducer;
@@ -56,7 +64,7 @@
private final SettingsDisplayFeatureProducer mSettingsDisplayFeatureProducer;
private final DataProducer<List<DisplayFeature>> mDisplayFeatureProducer;
- SampleExtensionImpl(Context context) {
+ public WindowLayoutComponentImpl(Context context) {
mSettingsDevicePostureProducer = new SettingsDevicePostureProducer(context);
mDevicePostureProducer = new PriorityDataProducer<>(List.of(
mSettingsDevicePostureProducer,
@@ -73,28 +81,68 @@
mDisplayFeatureProducer.addDataChangedCallback(this::onDisplayFeaturesChanged);
}
+ /**
+ * Adds a listener interested in receiving updates to {@link WindowLayoutInfo}
+ * @param activity hosting a {@link android.view.Window}
+ * @param consumer interested in receiving updates to {@link WindowLayoutInfo}
+ */
+ public void addWindowLayoutInfoListener(@NonNull Activity activity,
+ @NonNull Consumer<WindowLayoutInfo> consumer) {
+ mWindowLayoutChangeListeners.put(activity, consumer);
+ updateRegistrations();
+ }
+
+ /**
+ * Removes a listener no longer interested in receiving updates.
+ * @param consumer no longer interested in receiving updates to {@link WindowLayoutInfo}
+ */
+ public void removeWindowLayoutInfoListener(
+ @NonNull Consumer<WindowLayoutInfo> consumer) {
+ mWindowLayoutChangeListeners.values().remove(consumer);
+ updateRegistrations();
+ }
+
+ void updateWindowLayout(@NonNull Activity activity,
+ @NonNull WindowLayoutInfo newLayout) {
+ Consumer<WindowLayoutInfo> consumer = mWindowLayoutChangeListeners.get(activity);
+ if (consumer != null) {
+ consumer.accept(newLayout);
+ }
+ }
+
+ @NonNull
+ Set<Activity> getActivitiesListeningForLayoutChanges() {
+ return mWindowLayoutChangeListeners.keySet();
+ }
+
+ protected boolean hasListeners() {
+ return !mWindowLayoutChangeListeners.isEmpty();
+ }
+
private int getFeatureState(DisplayFeature feature) {
Integer featureState = feature.getState();
Optional<Integer> posture = mDevicePostureProducer.getData();
- int fallbackPosture = posture.orElse(ExtensionFoldingFeature.STATE_FLAT);
+ int fallbackPosture = posture.orElse(FoldingFeature.STATE_FLAT);
return featureState == null ? fallbackPosture : featureState;
}
private void onDisplayFeaturesChanged() {
for (Activity activity : getActivitiesListeningForLayoutChanges()) {
- ExtensionWindowLayoutInfo newLayout = getWindowLayoutInfo(activity);
+ WindowLayoutInfo newLayout = getWindowLayoutInfo(activity);
updateWindowLayout(activity, newLayout);
}
}
@NonNull
- private ExtensionWindowLayoutInfo getWindowLayoutInfo(@NonNull Activity activity) {
- List<ExtensionDisplayFeature> displayFeatures = getDisplayFeatures(activity);
- return new ExtensionWindowLayoutInfo(displayFeatures);
+ private WindowLayoutInfo getWindowLayoutInfo(@NonNull Activity activity) {
+ List<androidx.window.extensions.layout.DisplayFeature> displayFeatures =
+ getDisplayFeatures(activity);
+ return new WindowLayoutInfo(displayFeatures);
}
- private List<ExtensionDisplayFeature> getDisplayFeatures(@NonNull Activity activity) {
- List<ExtensionDisplayFeature> features = new ArrayList<>();
+ private List<androidx.window.extensions.layout.DisplayFeature> getDisplayFeatures(
+ @NonNull Activity activity) {
+ List<androidx.window.extensions.layout.DisplayFeature> features = new ArrayList<>();
int displayId = activity.getDisplay().getDisplayId();
if (displayId != DEFAULT_DISPLAY) {
Log.w(TAG, "This sample doesn't support display features on secondary displays");
@@ -115,15 +163,14 @@
rotateRectToDisplayRotation(displayId, featureRect);
transformToWindowSpaceRect(activity, featureRect);
- features.add(new ExtensionFoldingFeature(featureRect, baseFeature.getType(),
+ features.add(new FoldingFeature(featureRect, baseFeature.getType(),
getFeatureState(baseFeature)));
}
}
return features;
}
- @Override
- protected void onListenersChanged() {
+ private void updateRegistrations() {
if (hasListeners()) {
mSettingsDevicePostureProducer.registerObserversIfNeeded();
mSettingsDisplayFeatureProducer.registerObserversIfNeeded();
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/EmbeddingExtensionImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/EmbeddingExtensionImpl.java
deleted file mode 100644
index 9a8961f..0000000
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/EmbeddingExtensionImpl.java
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright (C) 2021 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 androidx.window.extensions.organizer;
-
-import androidx.annotation.NonNull;
-import androidx.window.extensions.embedding.ActivityEmbeddingComponent;
-import androidx.window.extensions.embedding.EmbeddingRule;
-import androidx.window.extensions.embedding.SplitInfo;
-
-import java.util.List;
-import java.util.Set;
-import java.util.function.Consumer;
-
-/**
- * Reference implementation of the activity embedding interface defined in WM Jetpack.
- */
-public class EmbeddingExtensionImpl implements ActivityEmbeddingComponent {
-
- private final SplitController mSplitController;
-
- public EmbeddingExtensionImpl() {
- mSplitController = new SplitController();
- }
-
- @Override
- public void setEmbeddingRules(@NonNull Set<EmbeddingRule> rules) {
- mSplitController.setEmbeddingRules(rules);
- }
-
- @Override
- public void setEmbeddingCallback(@NonNull Consumer<List<SplitInfo>> consumer) {
- mSplitController.setEmbeddingCallback(consumer);
- }
-}
diff --git a/libs/WindowManager/Jetpack/window-extensions-release.aar b/libs/WindowManager/Jetpack/window-extensions-release.aar
index 097febf..42e829e 100644
--- a/libs/WindowManager/Jetpack/window-extensions-release.aar
+++ b/libs/WindowManager/Jetpack/window-extensions-release.aar
Binary files differ