From ce4afed050379be7f6a09bad83ede5c115a68421 Mon Sep 17 00:00:00 2001 From: Andrii Kulian Date: Thu, 22 Jul 2021 18:58:25 -0700 Subject: [PATCH] Update split controller to latest extension interface Bug: 190433398 Bug: 190433972 Test: Manual Change-Id: I215bd5925f35c2a798b7f82ac8753d86cf9b594c --- core/java/android/app/Instrumentation.java | 3 +- .../window/extensions/ExtensionProvider.java | 10 + .../extensions/SampleExtensionImpl.java | 32 --- .../organizer/EmbeddingExtensionImpl.java | 48 ++++ .../extensions/organizer/SplitContainer.java | 28 ++- .../extensions/organizer/SplitController.java | 207 ++++++++---------- .../extensions/organizer/SplitPresenter.java | 29 +-- .../Jetpack/window-extensions-release.aar | Bin 11688 -> 17281 bytes 8 files changed, 193 insertions(+), 164 deletions(-) create mode 100644 libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/EmbeddingExtensionImpl.java diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java index 45db0f6ff3e29..fd6fa57b9e8d1 100644 --- a/core/java/android/app/Instrumentation.java +++ b/core/java/android/app/Instrumentation.java @@ -749,7 +749,8 @@ public class Instrumentation { * @see #onStartActivity(Intent) * @hide */ - public ActivityResult onStartActivity(Context who, Intent intent, Bundle options) { + public ActivityResult onStartActivity(@NonNull Context who, @NonNull Intent intent, + @NonNull Bundle options) { return onStartActivity(intent); } diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/ExtensionProvider.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/ExtensionProvider.java index b7a60392c5125..ce4e10364ba24 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/ExtensionProvider.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/ExtensionProvider.java @@ -18,6 +18,10 @@ 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. @@ -31,6 +35,12 @@ public class ExtensionProvider { 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. diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/SampleExtensionImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/SampleExtensionImpl.java index cafc2337a022c..a0d5b004ff1c0 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/SampleExtensionImpl.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/SampleExtensionImpl.java @@ -23,19 +23,15 @@ import static androidx.window.util.ExtensionHelper.transformToWindowSpaceRect; import android.app.Activity; import android.content.Context; -import android.content.Intent; import android.graphics.Rect; -import android.os.Bundle; import android.util.Log; import androidx.annotation.NonNull; -import androidx.annotation.Nullable; import androidx.window.common.DeviceStateManagerPostureProducer; import androidx.window.common.DisplayFeature; import androidx.window.common.ResourceConfigDisplayFeatureProducer; import androidx.window.common.SettingsDevicePostureProducer; import androidx.window.common.SettingsDisplayFeatureProducer; -import androidx.window.extensions.organizer.SplitController; import androidx.window.util.DataProducer; import androidx.window.util.PriorityDataProducer; @@ -60,8 +56,6 @@ class SampleExtensionImpl extends StubExtension { private final SettingsDisplayFeatureProducer mSettingsDisplayFeatureProducer; private final DataProducer> mDisplayFeatureProducer; - private final SplitController mSplitController; - SampleExtensionImpl(Context context) { mSettingsDevicePostureProducer = new SettingsDevicePostureProducer(context); mDevicePostureProducer = new PriorityDataProducer<>(List.of( @@ -77,8 +71,6 @@ class SampleExtensionImpl extends StubExtension { mDevicePostureProducer.addDataChangedCallback(this::onDisplayFeaturesChanged); mDisplayFeatureProducer.addDataChangedCallback(this::onDisplayFeaturesChanged); - - mSplitController = new SplitController(); } private int getFeatureState(DisplayFeature feature) { @@ -142,28 +134,4 @@ class SampleExtensionImpl extends StubExtension { onDisplayFeaturesChanged(); } - - @Override - public void setSplitRules(@NonNull List splitRules) { - mSplitController.setSplitRules(splitRules); - } - - @Override - @NonNull - public List getSplitRules() { - return new ArrayList<>(mSplitController.getSplitRules()); - } - - @Override - public void setSplitOrganizerCallback(@Nullable SplitOrganizerCallback callback) { - mSplitController.setSplitOrganizerCallback(callback); - } - - @Override - public void startActivityToSide(@NonNull Activity launchingActivity, @NonNull Intent intent, - @Nullable Bundle options, @NonNull ExtensionSplitPairRule splitPairRule, - int startRequestId) { - mSplitController.startActivityToSide(launchingActivity, intent, options, splitPairRule, - startRequestId); - } } diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/EmbeddingExtensionImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/EmbeddingExtensionImpl.java new file mode 100644 index 0000000000000..9a8961f1d460b --- /dev/null +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/EmbeddingExtensionImpl.java @@ -0,0 +1,48 @@ +/* + * 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 rules) { + mSplitController.setEmbeddingRules(rules); + } + + @Override + public void setEmbeddingCallback(@NonNull Consumer> consumer) { + mSplitController.setEmbeddingCallback(consumer); + } +} diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/SplitContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/SplitContainer.java index ade8573589877..8fd710a7986d3 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/SplitContainer.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/SplitContainer.java @@ -19,7 +19,9 @@ package androidx.window.extensions.organizer; import android.annotation.NonNull; import android.app.Activity; -import androidx.window.extensions.ExtensionSplitPairRule; +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. @@ -27,20 +29,26 @@ import androidx.window.extensions.ExtensionSplitPairRule; class SplitContainer { private final TaskFragmentContainer mPrimaryContainer; private final TaskFragmentContainer mSecondaryContainer; - private final ExtensionSplitPairRule mSplitPairRule; + private final SplitRule mSplitRule; SplitContainer(@NonNull TaskFragmentContainer primaryContainer, @NonNull Activity primaryActivity, @NonNull TaskFragmentContainer secondaryContainer, - @NonNull ExtensionSplitPairRule splitPairRule) { + @NonNull SplitRule splitRule) { mPrimaryContainer = primaryContainer; mSecondaryContainer = secondaryContainer; - mSplitPairRule = splitPairRule; + mSplitRule = splitRule; - if (mSplitPairRule.finishPrimaryWithSecondary || mSplitPairRule.useAsPlaceholder) { + final boolean isPlaceholderContainer = isPlaceholderContainer(); + final boolean shouldFinishPrimaryWithSecondary = (mSplitRule instanceof SplitPairRule) + && ((SplitPairRule) mSplitRule).shouldFinishPrimaryWithSecondary(); + final boolean shouldFinishSecondaryWithPrimary = (mSplitRule instanceof SplitPairRule) + && ((SplitPairRule) mSplitRule).shouldFinishSecondaryWithPrimary(); + + if (shouldFinishPrimaryWithSecondary || isPlaceholderContainer) { mSecondaryContainer.addActivityToFinishOnExit(primaryActivity); } - if (mSplitPairRule.finishSecondaryWithPrimary || mSplitPairRule.useAsPlaceholder) { + if (shouldFinishSecondaryWithPrimary || isPlaceholderContainer) { mPrimaryContainer.addContainerToFinishOnExit(mSecondaryContainer); } } @@ -56,7 +64,11 @@ class SplitContainer { } @NonNull - ExtensionSplitPairRule getSplitPairRule() { - return mSplitPairRule; + SplitRule getSplitRule() { + return mSplitRule; + } + + boolean isPlaceholderContainer() { + return (mSplitRule instanceof SplitPlaceholderRule); } } diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/SplitController.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/SplitController.java index c4ec4627f6cb8..05c6792a3fc75 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/SplitController.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/SplitController.java @@ -24,7 +24,6 @@ import android.app.ActivityOptions; import android.app.ActivityThread; import android.app.Application.ActivityLifecycleCallbacks; import android.app.Instrumentation; -import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.res.Configuration; @@ -32,20 +31,24 @@ import android.os.Bundle; 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.ExtensionInterface.SplitOrganizerCallback; -import androidx.window.extensions.ExtensionSplitActivityRule; -import androidx.window.extensions.ExtensionSplitInfo; -import androidx.window.extensions.ExtensionSplitPairRule; -import androidx.window.extensions.ExtensionSplitRule; -import androidx.window.extensions.ExtensionTaskFragment; +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; import java.util.concurrent.Executor; +import java.util.function.Consumer; /** * Main controller class that manages split states and presentation. @@ -55,12 +58,12 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen private final SplitPresenter mPresenter; // Currently applied split configuration. - private final List mSplitRules = new ArrayList<>(); + private final List mSplitRules = new ArrayList<>(); private final List mContainers = new ArrayList<>(); private final List mSplitContainers = new ArrayList<>(); // Callback to Jetpack to notify about changes to split states. - private SplitOrganizerCallback mSplitOrganizerCallback; + private @NonNull Consumer> mEmbeddingCallback; public SplitController() { mPresenter = new SplitPresenter(new MainThreadExecutor(), this); @@ -73,13 +76,14 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen instrumentation.addMonitor(new ActivityStartMonitor()); } - public void setSplitRules(@NonNull List splitRules) { + /** Updates the embedding rules applied to future activity launches. */ + public void setEmbeddingRules(@NonNull Set rules) { mSplitRules.clear(); - mSplitRules.addAll(splitRules); + mSplitRules.addAll(rules); } @NonNull - public List getSplitRules() { + public List getSplitRules() { return mSplitRules; } @@ -87,22 +91,20 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen * Starts an activity to side of the launchingActivity with the provided split config. */ public void startActivityToSide(@NonNull Activity launchingActivity, @NonNull Intent intent, - @Nullable Bundle options, @NonNull ExtensionSplitPairRule splitPairRule, - int startRequestId) { + @Nullable Bundle options, @NonNull SplitRule sideRule, + @NonNull Consumer failureCallback) { try { - mPresenter.startActivityToSide(launchingActivity, intent, options, splitPairRule); + mPresenter.startActivityToSide(launchingActivity, intent, options, sideRule); } catch (Exception e) { - if (mSplitOrganizerCallback != null && startRequestId != -1) { - mSplitOrganizerCallback.onActivityFailedToStartInContainer(startRequestId, e); - } + failureCallback.accept(e); } } /** * Registers the split organizer callback to notify about changes to active splits. */ - public void setSplitOrganizerCallback(@NonNull SplitOrganizerCallback callback) { - mSplitOrganizerCallback = callback; + public void setEmbeddingCallback(@NonNull Consumer> callback) { + mEmbeddingCallback = callback; updateCallbackIfNecessary(); } @@ -160,14 +162,12 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen */ // TODO(b/190433398): Break down into smaller functions. void onActivityCreated(@NonNull Activity launchedActivity) { - final ComponentName componentName = launchedActivity.getComponentName(); - - final List splitRules = getSplitRules(); + final List splitRules = getSplitRules(); final TaskFragmentContainer currentContainer = getContainerWithActivity( launchedActivity.getActivityToken(), launchedActivity); // Check if the activity is configured to always be expanded. - if (shouldExpand(componentName, splitRules)) { + if (shouldExpand(launchedActivity, splitRules)) { if (shouldContainerBeExpanded(currentContainer)) { // Make sure that the existing container is expanded mPresenter.expandTaskFragment(currentContainer.getTaskFragmentToken()); @@ -224,8 +224,8 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen } } - final ExtensionSplitPairRule splitPairRule = getSplitRule( - activityBelow.getComponentName(), componentName, splitRules); + final SplitPairRule splitPairRule = getSplitRule(activityBelow, launchedActivity, + splitRules); if (splitPairRule == null) { return; } @@ -302,11 +302,12 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen void registerSplit(@NonNull WindowContainerTransaction wct, @NonNull TaskFragmentContainer primaryContainer, @NonNull Activity primaryActivity, @NonNull TaskFragmentContainer secondaryContainer, - @NonNull ExtensionSplitPairRule splitPairRule) { - removeExistingSecondaryContainers(wct, primaryContainer); - + @NonNull SplitRule splitRule) { + if (splitRule instanceof SplitPairRule && ((SplitPairRule) splitRule).shouldClearTop()) { + removeExistingSecondaryContainers(wct, primaryContainer); + } SplitContainer splitContainer = new SplitContainer(primaryContainer, primaryActivity, - secondaryContainer, splitPairRule); + secondaryContainer, splitRule); mSplitContainers.add(splitContainer); } @@ -456,22 +457,20 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen } // Check if there is enough space for launch - final ExtensionSplitPairRule placeholderRule = getPlaceholderRule( - activity.getComponentName()); + final SplitPlaceholderRule placeholderRule = getPlaceholderRule(activity); if (placeholderRule == null || !mPresenter.shouldShowSideBySide( mPresenter.getParentContainerBounds(activity), placeholderRule)) { return false; } - Intent placeholderIntent = new Intent(); - placeholderIntent.setComponent(placeholderRule.secondaryActivityName); // TODO(b/190433398): Handle failed request - startActivityToSide(activity, placeholderIntent, null, placeholderRule, -1); + startActivityToSide(activity, placeholderRule.getPlaceholderIntent(), null, + placeholderRule, null); return true; } private boolean dismissPlaceholderIfNecessary(@NonNull SplitContainer splitContainer) { - if (!splitContainer.getSplitPairRule().useAsPlaceholder) { + if (!splitContainer.isPlaceholderContainer()) { return false; } @@ -488,15 +487,14 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen * Returns the rule to launch a placeholder for the activity with the provided component name * if it is configured in the split config. */ - private ExtensionSplitPairRule getPlaceholderRule(@NonNull ComponentName componentName) { - for (ExtensionSplitRule rule : mSplitRules) { - if (!(rule instanceof ExtensionSplitPairRule)) { + private SplitPlaceholderRule getPlaceholderRule(@NonNull Activity activity) { + for (EmbeddingRule rule : mSplitRules) { + if (!(rule instanceof SplitPlaceholderRule)) { continue; } - ExtensionSplitPairRule pairRule = (ExtensionSplitPairRule) rule; - if (componentName.equals(pairRule.primaryActivityName) - && pairRule.useAsPlaceholder) { - return pairRule; + SplitPlaceholderRule placeholderRule = (SplitPlaceholderRule) rule; + if (placeholderRule.getActivityPredicate().test(activity)) { + return placeholderRule; } } return null; @@ -506,27 +504,27 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen * Notifies listeners about changes to split states if necessary. */ private void updateCallbackIfNecessary() { - if (mSplitOrganizerCallback == null) { + if (mEmbeddingCallback == null) { return; } // TODO(b/190433398): Check if something actually changed - mSplitOrganizerCallback.onSplitInfoChanged(getActiveSplitStates()); + mEmbeddingCallback.accept(getActiveSplitStates()); } /** * Returns a list of descriptors for currently active split states. */ - private List getActiveSplitStates() { - List splitStates = new ArrayList<>(); + private List getActiveSplitStates() { + List splitStates = new ArrayList<>(); for (SplitContainer container : mSplitContainers) { - ExtensionTaskFragment primaryContainer = - new ExtensionTaskFragment( + TaskFragment primaryContainer = + new TaskFragment( container.getPrimaryContainer().collectActivities()); - ExtensionTaskFragment secondaryContainer = - new ExtensionTaskFragment( + TaskFragment secondaryContainer = + new TaskFragment( container.getSecondaryContainer().collectActivities()); - ExtensionSplitInfo splitState = new ExtensionSplitInfo(primaryContainer, - secondaryContainer, container.getSplitPairRule().splitRatio); + SplitInfo splitState = new SplitInfo(primaryContainer, + secondaryContainer, container.getSplitRule().getSplitRatio()); splitStates.add(splitState); } return splitStates; @@ -550,23 +548,41 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen } /** - * Returns a split rule for the provided pair of component names if available. + * Returns a split rule for the provided pair of primary activity and secondary activity intent + * if available. */ @Nullable - private static ExtensionSplitPairRule getSplitRule(@NonNull ComponentName primaryActivityName, - @NonNull ComponentName secondaryActivityName, - @NonNull List splitRules) { - if (splitRules == null || primaryActivityName == null || secondaryActivityName == null) { - return null; - } - - for (ExtensionSplitRule rule : splitRules) { - if (!(rule instanceof ExtensionSplitPairRule)) { + private static SplitPairRule getSplitRule(@NonNull Activity primaryActivity, + @NonNull Intent secondaryActivityIntent, @NonNull List splitRules) { + for (EmbeddingRule rule : splitRules) { + if (!(rule instanceof SplitPairRule)) { continue; } - ExtensionSplitPairRule pairRule = (ExtensionSplitPairRule) rule; - if (match(secondaryActivityName, pairRule.secondaryActivityName) - && match(primaryActivityName, pairRule.primaryActivityName)) { + SplitPairRule pairRule = (SplitPairRule) rule; + if (pairRule.getActivityIntentPredicate().test( + new Pair(primaryActivity, secondaryActivityIntent))) { + return pairRule; + } + } + return null; + } + + /** + * Returns a split rule for the provided pair of primary and secondary activities if available. + */ + @Nullable + private static SplitPairRule getSplitRule(@NonNull Activity primaryActivity, + @NonNull Activity secondaryActivity, @NonNull List splitRules) { + for (EmbeddingRule rule : splitRules) { + if (!(rule instanceof SplitPairRule)) { + continue; + } + 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)))) { return pairRule; } } @@ -587,55 +603,26 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen * 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 ComponentName componentName, - List splitRules) { + private static boolean shouldExpand(@NonNull Activity activity, + List splitRules) { if (splitRules == null) { return false; } - for (ExtensionSplitRule rule : splitRules) { - if (!(rule instanceof ExtensionSplitActivityRule)) { + for (EmbeddingRule rule : splitRules) { + if (!(rule instanceof ActivityRule)) { continue; } - ExtensionSplitActivityRule activityRule = (ExtensionSplitActivityRule) rule; - if (match(componentName, activityRule.activityName) - && activityRule.alwaysExpand) { + ActivityRule activityRule = (ActivityRule) rule; + if (!activityRule.shouldAlwaysExpand()) { + continue; + } + if (activityRule.getActivityPredicate().test(activity)) { return true; } } return false; } - /** Match check allowing wildcards for activity class name but not package name. */ - private static boolean match(@NonNull ComponentName activityComponent, - @NonNull ComponentName ruleComponent) { - if (activityComponent.toString().contains("*")) { - throw new IllegalArgumentException("Wildcard can only be part of the rule."); - } - final boolean packagesMatch = - activityComponent.getPackageName().equals(ruleComponent.getPackageName()); - final boolean classesMatch = - activityComponent.getClassName().equals(ruleComponent.getClassName()); - return packagesMatch && (classesMatch - || wildcardMatch(activityComponent.getClassName(), ruleComponent.getClassName())); - } - - /** - * Checks if the provided name matches the pattern. - */ - private static boolean wildcardMatch(@NonNull String name, @NonNull String pattern) { - if (!pattern.contains("*")) { - return false; - } - if (pattern.equals("*")) { - return true; - } - if (pattern.indexOf("*") != pattern.lastIndexOf("*") || !pattern.endsWith("*")) { - throw new IllegalArgumentException( - "Name pattern with a wildcard must only contain a single * in the end"); - } - return name.startsWith(pattern.substring(0, pattern.length() - 1)); - } - private final class LifecycleCallbacks implements ActivityLifecycleCallbacks { @Override @@ -698,8 +685,8 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen private class ActivityStartMonitor extends Instrumentation.ActivityMonitor { @Override - public Instrumentation.ActivityResult onStartActivity(Context who, Intent intent, - Bundle options) { + public Instrumentation.ActivityResult onStartActivity(@NonNull Context who, + @NonNull Intent intent, @NonNull Bundle options) { // TODO(b/190433398): Check if the activity is configured to always be expanded. // Check if activity should be put in a split with the activity that launched it. @@ -722,8 +709,8 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen */ private boolean setLaunchingToSideContainer(Activity launchingActivity, Intent intent, Bundle options) { - final ExtensionSplitPairRule splitPairRule = getSplitRule( - launchingActivity.getComponentName(), intent.getComponent(), getSplitRules()); + final SplitPairRule splitPairRule = getSplitRule(launchingActivity, intent, + getSplitRules()); if (splitPairRule == null) { return false; } @@ -770,8 +757,8 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen return; } - final ExtensionSplitPairRule splitPairRule = getSplitRule( - primaryActivity.getComponentName(), intent.getComponent(), getSplitRules()); + final SplitPairRule splitPairRule = getSplitRule(primaryActivity, intent, + getSplitRules()); if (splitPairRule == null) { return; } diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/SplitPresenter.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/SplitPresenter.java index 47a1519ee6052..a7bce20cae8b8 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/SplitPresenter.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/SplitPresenter.java @@ -24,13 +24,16 @@ import android.content.res.Configuration; import android.graphics.Rect; import android.os.Bundle; import android.os.IBinder; +import android.view.WindowInsets; +import android.view.WindowMetrics; import android.window.TaskFragmentCreationParams; import android.window.WindowContainerTransaction; import androidx.annotation.IntDef; import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import androidx.window.extensions.ExtensionSplitPairRule; +import androidx.window.extensions.embedding.SplitPairRule; +import androidx.window.extensions.embedding.SplitRule; import java.util.concurrent.Executor; @@ -89,7 +92,7 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer { * @return The newly created secondary container. */ TaskFragmentContainer createNewSplitWithEmptySideContainer(@NonNull Activity primaryActivity, - @NonNull ExtensionSplitPairRule rule) { + @NonNull SplitPairRule rule) { final WindowContainerTransaction wct = new WindowContainerTransaction(); final Rect parentBounds = getParentContainerBounds(primaryActivity); @@ -128,7 +131,7 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer { * @param rule The split rule to be applied to the container. */ void createNewSplitContainer(@NonNull Activity primaryActivity, - @NonNull Activity secondaryActivity, @NonNull ExtensionSplitPairRule rule) { + @NonNull Activity secondaryActivity, @NonNull SplitPairRule rule) { final WindowContainerTransaction wct = new WindowContainerTransaction(); final Rect parentBounds = getParentContainerBounds(primaryActivity); @@ -192,7 +195,7 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer { * @param rule The split rule to be applied to the container. */ void startActivityToSide(@NonNull Activity launchingActivity, @NonNull Intent activityIntent, - @Nullable Bundle activityOptions, @NonNull ExtensionSplitPairRule rule) { + @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); @@ -227,7 +230,7 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer { @NonNull WindowContainerTransaction wct) { // Getting the parent bounds using the updated container - it will have the recent value. final Rect parentBounds = getParentContainerBounds(updatedContainer); - final ExtensionSplitPairRule rule = splitContainer.getSplitPairRule(); + final SplitRule rule = splitContainer.getSplitRule(); final Rect primaryRectBounds = getBoundsForPosition(POSITION_LEFT, parentBounds, rule); final Rect secondaryRectBounds = getBoundsForPosition(POSITION_RIGHT, parentBounds, rule); @@ -273,24 +276,24 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer { boolean shouldShowSideBySide(@NonNull SplitContainer splitContainer) { final Rect parentBounds = getParentContainerBounds(splitContainer.getPrimaryContainer()); - return shouldShowSideBySide(parentBounds, splitContainer.getSplitPairRule()); + return shouldShowSideBySide(parentBounds, splitContainer.getSplitRule()); } - boolean shouldShowSideBySide(@Nullable Rect parentBounds, - @NonNull ExtensionSplitPairRule rule) { - return parentBounds != null && parentBounds.width() >= rule.minWidth - // TODO(b/190433398): Consider proper smallest width computation. - && Math.min(parentBounds.width(), parentBounds.height()) >= rule.minSmallestWidth; + boolean shouldShowSideBySide(@Nullable Rect parentBounds, @NonNull SplitRule rule) { + // TODO(b/190433398): Supply correct insets. + final WindowMetrics parentMetrics = new WindowMetrics(parentBounds, + new WindowInsets(new Rect())); + return rule.getParentWindowMetricsPredicate().test(parentMetrics); } @NonNull private Rect getBoundsForPosition(@Position int position, @NonNull Rect parentBounds, - @NonNull ExtensionSplitPairRule rule) { + @NonNull SplitRule rule) { if (!shouldShowSideBySide(parentBounds, rule)) { return new Rect(); } - float splitRatio = rule.splitRatio; + float splitRatio = rule.getSplitRatio(); switch (position) { case POSITION_LEFT: return new Rect( diff --git a/libs/WindowManager/Jetpack/window-extensions-release.aar b/libs/WindowManager/Jetpack/window-extensions-release.aar index fdbc5f61c45134289bc43ede508807cbd9d697b5..097febf9770ae1dfb51be10e3da87f64c70b867c 100644 GIT binary patch delta 16142 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 JzbgRX{{vh29ee-) delta 10492 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