Merge changes from topic "embedding_interface_0922" into sc-v2-dev am: 731b8775b4
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/15881690 Change-Id: I9b6e6c68e788d54a4199c556e865669ebcc3a335
This commit is contained in:
@@ -3616,6 +3616,11 @@ public final class ActivityThread extends ClientTransactionHandler
|
|||||||
}
|
}
|
||||||
activity.mLaunchedFromBubble = r.mLaunchedFromBubble;
|
activity.mLaunchedFromBubble = r.mLaunchedFromBubble;
|
||||||
activity.mCalled = false;
|
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()) {
|
if (r.isPersistable()) {
|
||||||
mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
|
mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
|
||||||
} else {
|
} else {
|
||||||
@@ -3626,7 +3631,6 @@ public final class ActivityThread extends ClientTransactionHandler
|
|||||||
"Activity " + r.intent.getComponent().toShortString() +
|
"Activity " + r.intent.getComponent().toShortString() +
|
||||||
" did not call through to super.onCreate()");
|
" did not call through to super.onCreate()");
|
||||||
}
|
}
|
||||||
r.activity = activity;
|
|
||||||
mLastReportedWindowingMode.put(activity.getActivityToken(),
|
mLastReportedWindowingMode.put(activity.getActivityToken(),
|
||||||
config.windowConfiguration.getWindowingMode());
|
config.windowConfiguration.getWindowingMode());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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();
|
|
||||||
}
|
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -14,7 +14,7 @@
|
|||||||
* limitations under the License.
|
* 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_MULTI_WINDOW;
|
||||||
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
|
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
|
||||||
@@ -36,7 +36,6 @@ import android.window.WindowContainerTransaction;
|
|||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.window.extensions.embedding.SplitRule;
|
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
@@ -14,15 +14,11 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package androidx.window.extensions.organizer;
|
package androidx.window.extensions.embedding;
|
||||||
|
|
||||||
import android.annotation.NonNull;
|
import android.annotation.NonNull;
|
||||||
import android.app.Activity;
|
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.
|
* Client-side descriptor of a split that holds two containers.
|
||||||
*/
|
*/
|
||||||
@@ -14,7 +14,7 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package androidx.window.extensions.organizer;
|
package androidx.window.extensions.embedding;
|
||||||
|
|
||||||
import android.annotation.NonNull;
|
import android.annotation.NonNull;
|
||||||
import android.annotation.Nullable;
|
import android.annotation.Nullable;
|
||||||
@@ -31,19 +31,10 @@ import android.os.Bundle;
|
|||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
import android.os.Looper;
|
import android.os.Looper;
|
||||||
import android.util.Pair;
|
|
||||||
import android.window.TaskFragmentAppearedInfo;
|
import android.window.TaskFragmentAppearedInfo;
|
||||||
import android.window.TaskFragmentInfo;
|
import android.window.TaskFragmentInfo;
|
||||||
import android.window.WindowContainerTransaction;
|
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.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
@@ -53,7 +44,8 @@ import java.util.function.Consumer;
|
|||||||
/**
|
/**
|
||||||
* Main controller class that manages split states and presentation.
|
* 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;
|
private final SplitPresenter mPresenter;
|
||||||
|
|
||||||
@@ -64,6 +56,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
|
|||||||
|
|
||||||
// Callback to Jetpack to notify about changes to split states.
|
// Callback to Jetpack to notify about changes to split states.
|
||||||
private @NonNull Consumer<List<SplitInfo>> mEmbeddingCallback;
|
private @NonNull Consumer<List<SplitInfo>> mEmbeddingCallback;
|
||||||
|
private final List<SplitInfo> mLastReportedSplitStates = new ArrayList<>();
|
||||||
|
|
||||||
public SplitController() {
|
public SplitController() {
|
||||||
mPresenter = new SplitPresenter(new MainThreadExecutor(), this);
|
mPresenter = new SplitPresenter(new MainThreadExecutor(), this);
|
||||||
@@ -77,6 +70,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Updates the embedding rules applied to future activity launches. */
|
/** Updates the embedding rules applied to future activity launches. */
|
||||||
|
@Override
|
||||||
public void setEmbeddingRules(@NonNull Set<EmbeddingRule> rules) {
|
public void setEmbeddingRules(@NonNull Set<EmbeddingRule> rules) {
|
||||||
mSplitRules.clear();
|
mSplitRules.clear();
|
||||||
mSplitRules.addAll(rules);
|
mSplitRules.addAll(rules);
|
||||||
@@ -103,7 +97,8 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
|
|||||||
/**
|
/**
|
||||||
* Registers the split organizer callback to notify about changes to active splits.
|
* 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;
|
mEmbeddingCallback = callback;
|
||||||
updateCallbackIfNecessary();
|
updateCallbackIfNecessary();
|
||||||
}
|
}
|
||||||
@@ -119,8 +114,8 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
|
|||||||
container.setInfo(taskFragmentAppearedInfo.getTaskFragmentInfo());
|
container.setInfo(taskFragmentAppearedInfo.getTaskFragmentInfo());
|
||||||
if (container.isFinished()) {
|
if (container.isFinished()) {
|
||||||
mPresenter.cleanupContainer(container, false /* shouldFinishDependent */);
|
mPresenter.cleanupContainer(container, false /* shouldFinishDependent */);
|
||||||
updateCallbackIfNecessary();
|
|
||||||
}
|
}
|
||||||
|
updateCallbackIfNecessary();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -139,8 +134,8 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
|
|||||||
final boolean shouldFinishDependent =
|
final boolean shouldFinishDependent =
|
||||||
!taskFragmentInfo.isTaskClearedForReuse();
|
!taskFragmentInfo.isTaskClearedForReuse();
|
||||||
mPresenter.cleanupContainer(container, shouldFinishDependent);
|
mPresenter.cleanupContainer(container, shouldFinishDependent);
|
||||||
updateCallbackIfNecessary();
|
|
||||||
}
|
}
|
||||||
|
updateCallbackIfNecessary();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -164,18 +159,23 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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
|
* 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.
|
* container for the activity and a new split container if necessary.
|
||||||
*/
|
*/
|
||||||
// TODO(b/190433398): Break down into smaller functions.
|
// TODO(b/190433398): Break down into smaller functions.
|
||||||
void onActivityCreated(@NonNull Activity launchedActivity) {
|
void handleActivityCreated(@NonNull Activity launchedActivity) {
|
||||||
final List<EmbeddingRule> splitRules = getSplitRules();
|
final List<EmbeddingRule> splitRules = getSplitRules();
|
||||||
final TaskFragmentContainer currentContainer = getContainerWithActivity(
|
final TaskFragmentContainer currentContainer = getContainerWithActivity(
|
||||||
launchedActivity.getActivityToken(), launchedActivity);
|
launchedActivity.getActivityToken(), launchedActivity);
|
||||||
|
|
||||||
// Check if the activity is configured to always be expanded.
|
// Check if the activity is configured to always be expanded.
|
||||||
if (shouldExpand(launchedActivity, splitRules)) {
|
if (shouldExpand(launchedActivity, null, splitRules)) {
|
||||||
if (shouldContainerBeExpanded(currentContainer)) {
|
if (shouldContainerBeExpanded(currentContainer)) {
|
||||||
// Make sure that the existing container is expanded
|
// Make sure that the existing container is expanded
|
||||||
mPresenter.expandTaskFragment(currentContainer.getTaskFragmentToken());
|
mPresenter.expandTaskFragment(currentContainer.getTaskFragmentToken());
|
||||||
@@ -240,8 +240,6 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
|
|||||||
|
|
||||||
mPresenter.createNewSplitContainer(activityBelow, launchedActivity,
|
mPresenter.createNewSplitContainer(activityBelow, launchedActivity,
|
||||||
splitPairRule);
|
splitPairRule);
|
||||||
|
|
||||||
updateCallbackIfNecessary();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onActivityConfigurationChanged(@NonNull Activity activity) {
|
private void onActivityConfigurationChanged(@NonNull Activity activity) {
|
||||||
@@ -501,7 +499,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
SplitPlaceholderRule placeholderRule = (SplitPlaceholderRule) rule;
|
SplitPlaceholderRule placeholderRule = (SplitPlaceholderRule) rule;
|
||||||
if (placeholderRule.getActivityPredicate().test(activity)) {
|
if (placeholderRule.matchesActivity(activity)) {
|
||||||
return placeholderRule;
|
return placeholderRule;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -515,8 +513,16 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
|
|||||||
if (mEmbeddingCallback == null) {
|
if (mEmbeddingCallback == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// TODO(b/190433398): Check if something actually changed
|
if (!allActivitiesCreated()) {
|
||||||
mEmbeddingCallback.accept(getActiveSplitStates());
|
return;
|
||||||
|
}
|
||||||
|
List<SplitInfo> currentSplitStates = getActiveSplitStates();
|
||||||
|
if (mLastReportedSplitStates.equals(currentSplitStates)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
mLastReportedSplitStates.clear();
|
||||||
|
mLastReportedSplitStates.addAll(currentSplitStates);
|
||||||
|
mEmbeddingCallback.accept(currentSplitStates);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -525,19 +531,45 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
|
|||||||
private List<SplitInfo> getActiveSplitStates() {
|
private List<SplitInfo> getActiveSplitStates() {
|
||||||
List<SplitInfo> splitStates = new ArrayList<>();
|
List<SplitInfo> splitStates = new ArrayList<>();
|
||||||
for (SplitContainer container : mSplitContainers) {
|
for (SplitContainer container : mSplitContainers) {
|
||||||
TaskFragment primaryContainer =
|
if (container.getPrimaryContainer().isEmpty()
|
||||||
new TaskFragment(
|
|| container.getSecondaryContainer().isEmpty()) {
|
||||||
|
// Skipping containers that do not have any activities to report.
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
ActivityStack primaryContainer =
|
||||||
|
new ActivityStack(
|
||||||
container.getPrimaryContainer().collectActivities());
|
container.getPrimaryContainer().collectActivities());
|
||||||
TaskFragment secondaryContainer =
|
ActivityStack secondaryContainer =
|
||||||
new TaskFragment(
|
new ActivityStack(
|
||||||
container.getSecondaryContainer().collectActivities());
|
container.getSecondaryContainer().collectActivities());
|
||||||
SplitInfo splitState = new SplitInfo(primaryContainer,
|
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);
|
splitStates.add(splitState);
|
||||||
}
|
}
|
||||||
return splitStates;
|
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 true} if the container is expanded to occupy full task size.
|
||||||
* Returns {@code false} if the container is included in an active split.
|
* Returns {@code false} if the container is included in an active split.
|
||||||
@@ -567,8 +599,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
SplitPairRule pairRule = (SplitPairRule) rule;
|
SplitPairRule pairRule = (SplitPairRule) rule;
|
||||||
if (pairRule.getActivityIntentPredicate().test(
|
if (pairRule.matchesActivityIntentPair(primaryActivity, secondaryActivityIntent)) {
|
||||||
new Pair(primaryActivity, secondaryActivityIntent))) {
|
|
||||||
return pairRule;
|
return pairRule;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -587,10 +618,9 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
|
|||||||
}
|
}
|
||||||
SplitPairRule pairRule = (SplitPairRule) rule;
|
SplitPairRule pairRule = (SplitPairRule) rule;
|
||||||
final Intent intent = secondaryActivity.getIntent();
|
final Intent intent = secondaryActivity.getIntent();
|
||||||
if (pairRule.getActivityPairPredicate().test(
|
if (pairRule.matchesActivityPair(primaryActivity, secondaryActivity)
|
||||||
new Pair(primaryActivity, secondaryActivity))
|
&& (intent == null
|
||||||
&& (intent == null || pairRule.getActivityIntentPredicate().test(
|
|| pairRule.matchesActivityIntentPair(primaryActivity, intent))) {
|
||||||
new Pair(primaryActivity, intent)))) {
|
|
||||||
return pairRule;
|
return pairRule;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -611,7 +641,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
|
|||||||
* Returns {@code true} if an Activity with the provided component name should always be
|
* 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.
|
* 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) {
|
List<EmbeddingRule> splitRules) {
|
||||||
if (splitRules == null) {
|
if (splitRules == null) {
|
||||||
return false;
|
return false;
|
||||||
@@ -624,7 +654,9 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
|
|||||||
if (!activityRule.shouldAlwaysExpand()) {
|
if (!activityRule.shouldAlwaysExpand()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (activityRule.getActivityPredicate().test(activity)) {
|
if (activity != null && activityRule.matchesActivity(activity)) {
|
||||||
|
return true;
|
||||||
|
} else if (intent != null && activityRule.matchesIntent(intent)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -678,11 +710,11 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
|
|||||||
|
|
||||||
/** Executor that posts on the main application thread. */
|
/** Executor that posts on the main application thread. */
|
||||||
private static class MainThreadExecutor implements Executor {
|
private static class MainThreadExecutor implements Executor {
|
||||||
private final Handler handler = new Handler(Looper.getMainLooper());
|
private final Handler mHandler = new Handler(Looper.getMainLooper());
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void execute(Runnable r) {
|
public void execute(Runnable r) {
|
||||||
handler.post(r);
|
mHandler.post(r);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -703,13 +735,25 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
|
|||||||
}
|
}
|
||||||
final Activity launchingActivity = (Activity) who;
|
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);
|
setLaunchingInSameContainer(launchingActivity, intent, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
return super.onStartActivity(who, 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
|
* 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
|
* {@code intent} should be paired with the {@code launchingActivity} and is set to be
|
||||||
@@ -14,16 +14,19 @@
|
|||||||
* limitations under the License.
|
* 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_MULTI_WINDOW;
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.res.Configuration;
|
import android.content.res.Configuration;
|
||||||
import android.graphics.Rect;
|
import android.graphics.Rect;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
|
import android.util.LayoutDirection;
|
||||||
|
import android.view.View;
|
||||||
import android.view.WindowInsets;
|
import android.view.WindowInsets;
|
||||||
import android.view.WindowMetrics;
|
import android.view.WindowMetrics;
|
||||||
import android.window.TaskFragmentCreationParams;
|
import android.window.TaskFragmentCreationParams;
|
||||||
@@ -32,8 +35,6 @@ import android.window.WindowContainerTransaction;
|
|||||||
import androidx.annotation.IntDef;
|
import androidx.annotation.IntDef;
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.window.extensions.embedding.SplitPairRule;
|
|
||||||
import androidx.window.extensions.embedding.SplitRule;
|
|
||||||
|
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
|
|
||||||
@@ -42,13 +43,13 @@ import java.util.concurrent.Executor;
|
|||||||
* {@link SplitController}.
|
* {@link SplitController}.
|
||||||
*/
|
*/
|
||||||
class SplitPresenter extends JetpackTaskFragmentOrganizer {
|
class SplitPresenter extends JetpackTaskFragmentOrganizer {
|
||||||
private static final int POSITION_LEFT = 0;
|
private static final int POSITION_START = 0;
|
||||||
private static final int POSITION_RIGHT = 1;
|
private static final int POSITION_END = 1;
|
||||||
private static final int POSITION_FILL = 2;
|
private static final int POSITION_FILL = 2;
|
||||||
|
|
||||||
@IntDef(value = {
|
@IntDef(value = {
|
||||||
POSITION_LEFT,
|
POSITION_START,
|
||||||
POSITION_RIGHT,
|
POSITION_END,
|
||||||
POSITION_FILL,
|
POSITION_FILL,
|
||||||
})
|
})
|
||||||
private @interface Position {}
|
private @interface Position {}
|
||||||
@@ -96,13 +97,15 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
|
|||||||
final WindowContainerTransaction wct = new WindowContainerTransaction();
|
final WindowContainerTransaction wct = new WindowContainerTransaction();
|
||||||
|
|
||||||
final Rect parentBounds = getParentContainerBounds(primaryActivity);
|
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,
|
final TaskFragmentContainer primaryContainer = prepareContainerForActivity(wct,
|
||||||
primaryActivity, primaryRectBounds, null);
|
primaryActivity, primaryRectBounds, null);
|
||||||
|
|
||||||
// Create new empty task fragment
|
// Create new empty task fragment
|
||||||
TaskFragmentContainer secondaryContainer = mController.newContainer(null);
|
final TaskFragmentContainer secondaryContainer = mController.newContainer(null);
|
||||||
final Rect secondaryRectBounds = getBoundsForPosition(POSITION_RIGHT, parentBounds, rule);
|
final Rect secondaryRectBounds = getBoundsForPosition(POSITION_END, parentBounds,
|
||||||
|
rule, isLtr(primaryActivity, rule));
|
||||||
createTaskFragment(wct, secondaryContainer.getTaskFragmentToken(),
|
createTaskFragment(wct, secondaryContainer.getTaskFragmentToken(),
|
||||||
primaryActivity.getActivityToken(), secondaryRectBounds,
|
primaryActivity.getActivityToken(), secondaryRectBounds,
|
||||||
WINDOWING_MODE_MULTI_WINDOW);
|
WINDOWING_MODE_MULTI_WINDOW);
|
||||||
@@ -135,11 +138,13 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
|
|||||||
final WindowContainerTransaction wct = new WindowContainerTransaction();
|
final WindowContainerTransaction wct = new WindowContainerTransaction();
|
||||||
|
|
||||||
final Rect parentBounds = getParentContainerBounds(primaryActivity);
|
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,
|
final TaskFragmentContainer primaryContainer = prepareContainerForActivity(wct,
|
||||||
primaryActivity, primaryRectBounds, null);
|
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,
|
final TaskFragmentContainer secondaryContainer = prepareContainerForActivity(wct,
|
||||||
secondaryActivity, secondaryRectBounds, primaryContainer);
|
secondaryActivity, secondaryRectBounds, primaryContainer);
|
||||||
|
|
||||||
@@ -152,6 +157,20 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
|
|||||||
applyTransaction(wct);
|
applyTransaction(wct);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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.
|
* 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 activity The activity to be re-parented to the container if necessary.
|
||||||
@@ -197,8 +216,10 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
|
|||||||
void startActivityToSide(@NonNull Activity launchingActivity, @NonNull Intent activityIntent,
|
void startActivityToSide(@NonNull Activity launchingActivity, @NonNull Intent activityIntent,
|
||||||
@Nullable Bundle activityOptions, @NonNull SplitRule rule) {
|
@Nullable Bundle activityOptions, @NonNull SplitRule rule) {
|
||||||
final Rect parentBounds = getParentContainerBounds(launchingActivity);
|
final Rect parentBounds = getParentContainerBounds(launchingActivity);
|
||||||
final Rect primaryRectBounds = getBoundsForPosition(POSITION_LEFT, parentBounds, rule);
|
final Rect primaryRectBounds = getBoundsForPosition(POSITION_START, parentBounds, rule,
|
||||||
final Rect secondaryRectBounds = getBoundsForPosition(POSITION_RIGHT, parentBounds, rule);
|
isLtr(launchingActivity, rule));
|
||||||
|
final Rect secondaryRectBounds = getBoundsForPosition(POSITION_END, parentBounds, rule,
|
||||||
|
isLtr(launchingActivity, rule));
|
||||||
|
|
||||||
TaskFragmentContainer primaryContainer = mController.getContainerWithActivity(
|
TaskFragmentContainer primaryContainer = mController.getContainerWithActivity(
|
||||||
launchingActivity.getActivityToken());
|
launchingActivity.getActivityToken());
|
||||||
@@ -231,8 +252,15 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
|
|||||||
// Getting the parent bounds using the updated container - it will have the recent value.
|
// Getting the parent bounds using the updated container - it will have the recent value.
|
||||||
final Rect parentBounds = getParentContainerBounds(updatedContainer);
|
final Rect parentBounds = getParentContainerBounds(updatedContainer);
|
||||||
final SplitRule rule = splitContainer.getSplitRule();
|
final SplitRule rule = splitContainer.getSplitRule();
|
||||||
final Rect primaryRectBounds = getBoundsForPosition(POSITION_LEFT, parentBounds, rule);
|
final Activity activity = splitContainer.getPrimaryContainer().getTopNonFinishingActivity();
|
||||||
final Rect secondaryRectBounds = getBoundsForPosition(POSITION_RIGHT, parentBounds, rule);
|
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
|
// If the task fragments are not registered yet, the positions will be updated after they
|
||||||
// are created again.
|
// are created again.
|
||||||
@@ -283,34 +311,62 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
|
|||||||
// TODO(b/190433398): Supply correct insets.
|
// TODO(b/190433398): Supply correct insets.
|
||||||
final WindowMetrics parentMetrics = new WindowMetrics(parentBounds,
|
final WindowMetrics parentMetrics = new WindowMetrics(parentBounds,
|
||||||
new WindowInsets(new Rect()));
|
new WindowInsets(new Rect()));
|
||||||
return rule.getParentWindowMetricsPredicate().test(parentMetrics);
|
return rule.checkParentMetrics(parentMetrics);
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
private Rect getBoundsForPosition(@Position int position, @NonNull Rect parentBounds,
|
private Rect getBoundsForPosition(@Position int position, @NonNull Rect parentBounds,
|
||||||
@NonNull SplitRule rule) {
|
@NonNull SplitRule rule, boolean isLtr) {
|
||||||
if (!shouldShowSideBySide(parentBounds, rule)) {
|
if (!shouldShowSideBySide(parentBounds, rule)) {
|
||||||
return new Rect();
|
return new Rect();
|
||||||
}
|
}
|
||||||
|
|
||||||
float splitRatio = rule.getSplitRatio();
|
final float splitRatio = rule.getSplitRatio();
|
||||||
|
final float rtlSplitRatio = 1 - splitRatio;
|
||||||
switch (position) {
|
switch (position) {
|
||||||
case POSITION_LEFT:
|
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(
|
return new Rect(
|
||||||
parentBounds.left,
|
parentBounds.left,
|
||||||
parentBounds.top,
|
parentBounds.top,
|
||||||
(int) (parentBounds.left + parentBounds.width() * splitRatio),
|
(int) (parentBounds.left + parentBounds.width() * splitRatio),
|
||||||
parentBounds.bottom);
|
parentBounds.bottom);
|
||||||
case POSITION_RIGHT:
|
}
|
||||||
|
|
||||||
|
private Rect getRightContainerBounds(@NonNull Rect parentBounds, float splitRatio) {
|
||||||
return new Rect(
|
return new Rect(
|
||||||
(int) (parentBounds.left + parentBounds.width() * splitRatio),
|
(int) (parentBounds.left + parentBounds.width() * splitRatio),
|
||||||
parentBounds.top,
|
parentBounds.top,
|
||||||
parentBounds.right,
|
parentBounds.right,
|
||||||
parentBounds.bottom);
|
parentBounds.bottom);
|
||||||
case POSITION_FILL:
|
|
||||||
return parentBounds;
|
|
||||||
}
|
}
|
||||||
return parentBounds;
|
|
||||||
|
/**
|
||||||
|
* 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
|
@NonNull
|
||||||
@@ -14,7 +14,7 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package androidx.window.extensions.organizer;
|
package androidx.window.extensions.embedding;
|
||||||
|
|
||||||
import android.graphics.Rect;
|
import android.graphics.Rect;
|
||||||
import android.view.Choreographer;
|
import android.view.Choreographer;
|
||||||
@@ -14,7 +14,7 @@
|
|||||||
* limitations under the License.
|
* 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_CHANGE;
|
||||||
import static android.view.WindowManager.TRANSIT_OLD_TASK_FRAGMENT_CLOSE;
|
import static android.view.WindowManager.TRANSIT_OLD_TASK_FRAGMENT_CLOSE;
|
||||||
@@ -14,7 +14,7 @@
|
|||||||
* limitations under the License.
|
* 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.RemoteAnimationTarget.MODE_CLOSING;
|
||||||
import static android.view.WindowManager.TRANSIT_OLD_TASK_FRAGMENT_CHANGE;
|
import static android.view.WindowManager.TRANSIT_OLD_TASK_FRAGMENT_CHANGE;
|
||||||
@@ -14,7 +14,7 @@
|
|||||||
* limitations under the License.
|
* 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.RemoteAnimationTarget.MODE_CLOSING;
|
||||||
|
|
||||||
@@ -14,7 +14,7 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package androidx.window.extensions.organizer;
|
package androidx.window.extensions.embedding;
|
||||||
|
|
||||||
import android.annotation.NonNull;
|
import android.annotation.NonNull;
|
||||||
import android.annotation.Nullable;
|
import android.annotation.Nullable;
|
||||||
@@ -212,8 +212,10 @@ class TaskFragmentContainer {
|
|||||||
@NonNull WindowContainerTransaction wct, @NonNull SplitController controller) {
|
@NonNull WindowContainerTransaction wct, @NonNull SplitController controller) {
|
||||||
// Finish own activities
|
// Finish own activities
|
||||||
for (Activity activity : collectActivities()) {
|
for (Activity activity : collectActivities()) {
|
||||||
|
if (!activity.isFinishing()) {
|
||||||
activity.finish();
|
activity.finish();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!shouldFinishDependent) {
|
if (!shouldFinishDependent) {
|
||||||
return;
|
return;
|
||||||
@@ -14,7 +14,7 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package androidx.window.extensions;
|
package androidx.window.extensions.layout;
|
||||||
|
|
||||||
import static android.view.Display.DEFAULT_DISPLAY;
|
import static android.view.Display.DEFAULT_DISPLAY;
|
||||||
|
|
||||||
@@ -36,19 +36,27 @@ import androidx.window.util.DataProducer;
|
|||||||
import androidx.window.util.PriorityDataProducer;
|
import androidx.window.util.PriorityDataProducer;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
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.
|
* WindowManager Jetpack.
|
||||||
*
|
*
|
||||||
* NOTE: This version is a work in progress and under active development. It MUST NOT be used in
|
* 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.
|
* production builds since the interface can still change before reaching stable version.
|
||||||
* Please refer to {@link androidx.window.sidecar.SampleSidecarImpl} instead.
|
* 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 final String TAG = "SampleExtension";
|
||||||
|
private static WindowLayoutComponent sInstance;
|
||||||
|
|
||||||
|
private final Map<Activity, Consumer<WindowLayoutInfo>> mWindowLayoutChangeListeners =
|
||||||
|
new HashMap<>();
|
||||||
|
|
||||||
private final SettingsDevicePostureProducer mSettingsDevicePostureProducer;
|
private final SettingsDevicePostureProducer mSettingsDevicePostureProducer;
|
||||||
private final DataProducer<Integer> mDevicePostureProducer;
|
private final DataProducer<Integer> mDevicePostureProducer;
|
||||||
@@ -56,7 +64,7 @@ class SampleExtensionImpl extends StubExtension {
|
|||||||
private final SettingsDisplayFeatureProducer mSettingsDisplayFeatureProducer;
|
private final SettingsDisplayFeatureProducer mSettingsDisplayFeatureProducer;
|
||||||
private final DataProducer<List<DisplayFeature>> mDisplayFeatureProducer;
|
private final DataProducer<List<DisplayFeature>> mDisplayFeatureProducer;
|
||||||
|
|
||||||
SampleExtensionImpl(Context context) {
|
public WindowLayoutComponentImpl(Context context) {
|
||||||
mSettingsDevicePostureProducer = new SettingsDevicePostureProducer(context);
|
mSettingsDevicePostureProducer = new SettingsDevicePostureProducer(context);
|
||||||
mDevicePostureProducer = new PriorityDataProducer<>(List.of(
|
mDevicePostureProducer = new PriorityDataProducer<>(List.of(
|
||||||
mSettingsDevicePostureProducer,
|
mSettingsDevicePostureProducer,
|
||||||
@@ -73,28 +81,68 @@ class SampleExtensionImpl extends StubExtension {
|
|||||||
mDisplayFeatureProducer.addDataChangedCallback(this::onDisplayFeaturesChanged);
|
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) {
|
private int getFeatureState(DisplayFeature feature) {
|
||||||
Integer featureState = feature.getState();
|
Integer featureState = feature.getState();
|
||||||
Optional<Integer> posture = mDevicePostureProducer.getData();
|
Optional<Integer> posture = mDevicePostureProducer.getData();
|
||||||
int fallbackPosture = posture.orElse(ExtensionFoldingFeature.STATE_FLAT);
|
int fallbackPosture = posture.orElse(FoldingFeature.STATE_FLAT);
|
||||||
return featureState == null ? fallbackPosture : featureState;
|
return featureState == null ? fallbackPosture : featureState;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onDisplayFeaturesChanged() {
|
private void onDisplayFeaturesChanged() {
|
||||||
for (Activity activity : getActivitiesListeningForLayoutChanges()) {
|
for (Activity activity : getActivitiesListeningForLayoutChanges()) {
|
||||||
ExtensionWindowLayoutInfo newLayout = getWindowLayoutInfo(activity);
|
WindowLayoutInfo newLayout = getWindowLayoutInfo(activity);
|
||||||
updateWindowLayout(activity, newLayout);
|
updateWindowLayout(activity, newLayout);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
private ExtensionWindowLayoutInfo getWindowLayoutInfo(@NonNull Activity activity) {
|
private WindowLayoutInfo getWindowLayoutInfo(@NonNull Activity activity) {
|
||||||
List<ExtensionDisplayFeature> displayFeatures = getDisplayFeatures(activity);
|
List<androidx.window.extensions.layout.DisplayFeature> displayFeatures =
|
||||||
return new ExtensionWindowLayoutInfo(displayFeatures);
|
getDisplayFeatures(activity);
|
||||||
|
return new WindowLayoutInfo(displayFeatures);
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<ExtensionDisplayFeature> getDisplayFeatures(@NonNull Activity activity) {
|
private List<androidx.window.extensions.layout.DisplayFeature> getDisplayFeatures(
|
||||||
List<ExtensionDisplayFeature> features = new ArrayList<>();
|
@NonNull Activity activity) {
|
||||||
|
List<androidx.window.extensions.layout.DisplayFeature> features = new ArrayList<>();
|
||||||
int displayId = activity.getDisplay().getDisplayId();
|
int displayId = activity.getDisplay().getDisplayId();
|
||||||
if (displayId != DEFAULT_DISPLAY) {
|
if (displayId != DEFAULT_DISPLAY) {
|
||||||
Log.w(TAG, "This sample doesn't support display features on secondary displays");
|
Log.w(TAG, "This sample doesn't support display features on secondary displays");
|
||||||
@@ -115,15 +163,14 @@ class SampleExtensionImpl extends StubExtension {
|
|||||||
rotateRectToDisplayRotation(displayId, featureRect);
|
rotateRectToDisplayRotation(displayId, featureRect);
|
||||||
transformToWindowSpaceRect(activity, featureRect);
|
transformToWindowSpaceRect(activity, featureRect);
|
||||||
|
|
||||||
features.add(new ExtensionFoldingFeature(featureRect, baseFeature.getType(),
|
features.add(new FoldingFeature(featureRect, baseFeature.getType(),
|
||||||
getFeatureState(baseFeature)));
|
getFeatureState(baseFeature)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return features;
|
return features;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private void updateRegistrations() {
|
||||||
protected void onListenersChanged() {
|
|
||||||
if (hasListeners()) {
|
if (hasListeners()) {
|
||||||
mSettingsDevicePostureProducer.registerObserversIfNeeded();
|
mSettingsDevicePostureProducer.registerObserversIfNeeded();
|
||||||
mSettingsDisplayFeatureProducer.registerObserversIfNeeded();
|
mSettingsDisplayFeatureProducer.registerObserversIfNeeded();
|
||||||
@@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Binary file not shown.
Reference in New Issue
Block a user