Merge "Update split controller to latest extension interface" into sc-v2-dev

This commit is contained in:
Andrii Kulian
2021-08-18 01:06:10 +00:00
committed by Android (Google) Code Review
8 changed files with 193 additions and 164 deletions

View File

@@ -749,7 +749,8 @@ public class Instrumentation {
* @see #onStartActivity(Intent) * @see #onStartActivity(Intent)
* @hide * @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); return onStartActivity(intent);
} }

View File

@@ -18,6 +18,10 @@ package androidx.window.extensions;
import android.content.Context; 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 * 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. * vendor library, and the vendor implementation must match the signature of this class.
@@ -31,6 +35,12 @@ public class ExtensionProvider {
return new SampleExtensionImpl(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. * The support library will use this method to check API version compatibility.
* @return API version string in MAJOR.MINOR.PATCH-description format. * @return API version string in MAJOR.MINOR.PATCH-description format.

View File

@@ -23,19 +23,15 @@ import static androidx.window.util.ExtensionHelper.transformToWindowSpaceRect;
import android.app.Activity; import android.app.Activity;
import android.content.Context; import android.content.Context;
import android.content.Intent;
import android.graphics.Rect; import android.graphics.Rect;
import android.os.Bundle;
import android.util.Log; import android.util.Log;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.window.common.DeviceStateManagerPostureProducer; import androidx.window.common.DeviceStateManagerPostureProducer;
import androidx.window.common.DisplayFeature; import androidx.window.common.DisplayFeature;
import androidx.window.common.ResourceConfigDisplayFeatureProducer; import androidx.window.common.ResourceConfigDisplayFeatureProducer;
import androidx.window.common.SettingsDevicePostureProducer; import androidx.window.common.SettingsDevicePostureProducer;
import androidx.window.common.SettingsDisplayFeatureProducer; import androidx.window.common.SettingsDisplayFeatureProducer;
import androidx.window.extensions.organizer.SplitController;
import androidx.window.util.DataProducer; import androidx.window.util.DataProducer;
import androidx.window.util.PriorityDataProducer; import androidx.window.util.PriorityDataProducer;
@@ -60,8 +56,6 @@ class SampleExtensionImpl extends StubExtension {
private final SettingsDisplayFeatureProducer mSettingsDisplayFeatureProducer; private final SettingsDisplayFeatureProducer mSettingsDisplayFeatureProducer;
private final DataProducer<List<DisplayFeature>> mDisplayFeatureProducer; private final DataProducer<List<DisplayFeature>> mDisplayFeatureProducer;
private final SplitController mSplitController;
SampleExtensionImpl(Context context) { SampleExtensionImpl(Context context) {
mSettingsDevicePostureProducer = new SettingsDevicePostureProducer(context); mSettingsDevicePostureProducer = new SettingsDevicePostureProducer(context);
mDevicePostureProducer = new PriorityDataProducer<>(List.of( mDevicePostureProducer = new PriorityDataProducer<>(List.of(
@@ -77,8 +71,6 @@ class SampleExtensionImpl extends StubExtension {
mDevicePostureProducer.addDataChangedCallback(this::onDisplayFeaturesChanged); mDevicePostureProducer.addDataChangedCallback(this::onDisplayFeaturesChanged);
mDisplayFeatureProducer.addDataChangedCallback(this::onDisplayFeaturesChanged); mDisplayFeatureProducer.addDataChangedCallback(this::onDisplayFeaturesChanged);
mSplitController = new SplitController();
} }
private int getFeatureState(DisplayFeature feature) { private int getFeatureState(DisplayFeature feature) {
@@ -142,28 +134,4 @@ class SampleExtensionImpl extends StubExtension {
onDisplayFeaturesChanged(); onDisplayFeaturesChanged();
} }
@Override
public void setSplitRules(@NonNull List<ExtensionSplitRule> splitRules) {
mSplitController.setSplitRules(splitRules);
}
@Override
@NonNull
public List<ExtensionSplitRule> 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);
}
} }

View File

@@ -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<EmbeddingRule> rules) {
mSplitController.setEmbeddingRules(rules);
}
@Override
public void setEmbeddingCallback(@NonNull Consumer<List<SplitInfo>> consumer) {
mSplitController.setEmbeddingCallback(consumer);
}
}

View File

@@ -19,7 +19,9 @@ package androidx.window.extensions.organizer;
import android.annotation.NonNull; import android.annotation.NonNull;
import android.app.Activity; 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. * Client-side descriptor of a split that holds two containers.
@@ -27,20 +29,26 @@ import androidx.window.extensions.ExtensionSplitPairRule;
class SplitContainer { class SplitContainer {
private final TaskFragmentContainer mPrimaryContainer; private final TaskFragmentContainer mPrimaryContainer;
private final TaskFragmentContainer mSecondaryContainer; private final TaskFragmentContainer mSecondaryContainer;
private final ExtensionSplitPairRule mSplitPairRule; private final SplitRule mSplitRule;
SplitContainer(@NonNull TaskFragmentContainer primaryContainer, SplitContainer(@NonNull TaskFragmentContainer primaryContainer,
@NonNull Activity primaryActivity, @NonNull Activity primaryActivity,
@NonNull TaskFragmentContainer secondaryContainer, @NonNull TaskFragmentContainer secondaryContainer,
@NonNull ExtensionSplitPairRule splitPairRule) { @NonNull SplitRule splitRule) {
mPrimaryContainer = primaryContainer; mPrimaryContainer = primaryContainer;
mSecondaryContainer = secondaryContainer; 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); mSecondaryContainer.addActivityToFinishOnExit(primaryActivity);
} }
if (mSplitPairRule.finishSecondaryWithPrimary || mSplitPairRule.useAsPlaceholder) { if (shouldFinishSecondaryWithPrimary || isPlaceholderContainer) {
mPrimaryContainer.addContainerToFinishOnExit(mSecondaryContainer); mPrimaryContainer.addContainerToFinishOnExit(mSecondaryContainer);
} }
} }
@@ -56,7 +64,11 @@ class SplitContainer {
} }
@NonNull @NonNull
ExtensionSplitPairRule getSplitPairRule() { SplitRule getSplitRule() {
return mSplitPairRule; return mSplitRule;
}
boolean isPlaceholderContainer() {
return (mSplitRule instanceof SplitPlaceholderRule);
} }
} }

View File

@@ -24,7 +24,6 @@ import android.app.ActivityOptions;
import android.app.ActivityThread; import android.app.ActivityThread;
import android.app.Application.ActivityLifecycleCallbacks; import android.app.Application.ActivityLifecycleCallbacks;
import android.app.Instrumentation; import android.app.Instrumentation;
import android.content.ComponentName;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.res.Configuration; import android.content.res.Configuration;
@@ -32,20 +31,24 @@ 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.ExtensionInterface.SplitOrganizerCallback; import androidx.window.extensions.embedding.ActivityRule;
import androidx.window.extensions.ExtensionSplitActivityRule; import androidx.window.extensions.embedding.EmbeddingRule;
import androidx.window.extensions.ExtensionSplitInfo; import androidx.window.extensions.embedding.SplitInfo;
import androidx.window.extensions.ExtensionSplitPairRule; import androidx.window.extensions.embedding.SplitPairRule;
import androidx.window.extensions.ExtensionSplitRule; import androidx.window.extensions.embedding.SplitPlaceholderRule;
import androidx.window.extensions.ExtensionTaskFragment; 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.concurrent.Executor; import java.util.concurrent.Executor;
import java.util.function.Consumer;
/** /**
* Main controller class that manages split states and presentation. * Main controller class that manages split states and presentation.
@@ -55,12 +58,12 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
private final SplitPresenter mPresenter; private final SplitPresenter mPresenter;
// Currently applied split configuration. // Currently applied split configuration.
private final List<ExtensionSplitRule> mSplitRules = new ArrayList<>(); private final List<EmbeddingRule> mSplitRules = new ArrayList<>();
private final List<TaskFragmentContainer> mContainers = new ArrayList<>(); private final List<TaskFragmentContainer> mContainers = new ArrayList<>();
private final List<SplitContainer> mSplitContainers = new ArrayList<>(); private final List<SplitContainer> mSplitContainers = new ArrayList<>();
// Callback to Jetpack to notify about changes to split states. // Callback to Jetpack to notify about changes to split states.
private SplitOrganizerCallback mSplitOrganizerCallback; private @NonNull Consumer<List<SplitInfo>> mEmbeddingCallback;
public SplitController() { public SplitController() {
mPresenter = new SplitPresenter(new MainThreadExecutor(), this); mPresenter = new SplitPresenter(new MainThreadExecutor(), this);
@@ -73,13 +76,14 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
instrumentation.addMonitor(new ActivityStartMonitor()); instrumentation.addMonitor(new ActivityStartMonitor());
} }
public void setSplitRules(@NonNull List<ExtensionSplitRule> splitRules) { /** Updates the embedding rules applied to future activity launches. */
public void setEmbeddingRules(@NonNull Set<EmbeddingRule> rules) {
mSplitRules.clear(); mSplitRules.clear();
mSplitRules.addAll(splitRules); mSplitRules.addAll(rules);
} }
@NonNull @NonNull
public List<ExtensionSplitRule> getSplitRules() { public List<EmbeddingRule> getSplitRules() {
return mSplitRules; 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. * Starts an activity to side of the launchingActivity with the provided split config.
*/ */
public void startActivityToSide(@NonNull Activity launchingActivity, @NonNull Intent intent, public void startActivityToSide(@NonNull Activity launchingActivity, @NonNull Intent intent,
@Nullable Bundle options, @NonNull ExtensionSplitPairRule splitPairRule, @Nullable Bundle options, @NonNull SplitRule sideRule,
int startRequestId) { @NonNull Consumer<Exception> failureCallback) {
try { try {
mPresenter.startActivityToSide(launchingActivity, intent, options, splitPairRule); mPresenter.startActivityToSide(launchingActivity, intent, options, sideRule);
} catch (Exception e) { } catch (Exception e) {
if (mSplitOrganizerCallback != null && startRequestId != -1) { failureCallback.accept(e);
mSplitOrganizerCallback.onActivityFailedToStartInContainer(startRequestId, e);
}
} }
} }
/** /**
* 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 setSplitOrganizerCallback(@NonNull SplitOrganizerCallback callback) { public void setEmbeddingCallback(@NonNull Consumer<List<SplitInfo>> callback) {
mSplitOrganizerCallback = callback; mEmbeddingCallback = callback;
updateCallbackIfNecessary(); updateCallbackIfNecessary();
} }
@@ -160,14 +162,12 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
*/ */
// TODO(b/190433398): Break down into smaller functions. // TODO(b/190433398): Break down into smaller functions.
void onActivityCreated(@NonNull Activity launchedActivity) { void onActivityCreated(@NonNull Activity launchedActivity) {
final ComponentName componentName = launchedActivity.getComponentName(); final List<EmbeddingRule> splitRules = getSplitRules();
final List<ExtensionSplitRule> 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(componentName, splitRules)) { if (shouldExpand(launchedActivity, 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());
@@ -224,8 +224,8 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
} }
} }
final ExtensionSplitPairRule splitPairRule = getSplitRule( final SplitPairRule splitPairRule = getSplitRule(activityBelow, launchedActivity,
activityBelow.getComponentName(), componentName, splitRules); splitRules);
if (splitPairRule == null) { if (splitPairRule == null) {
return; return;
} }
@@ -302,11 +302,12 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
void registerSplit(@NonNull WindowContainerTransaction wct, void registerSplit(@NonNull WindowContainerTransaction wct,
@NonNull TaskFragmentContainer primaryContainer, @NonNull Activity primaryActivity, @NonNull TaskFragmentContainer primaryContainer, @NonNull Activity primaryActivity,
@NonNull TaskFragmentContainer secondaryContainer, @NonNull TaskFragmentContainer secondaryContainer,
@NonNull ExtensionSplitPairRule splitPairRule) { @NonNull SplitRule splitRule) {
removeExistingSecondaryContainers(wct, primaryContainer); if (splitRule instanceof SplitPairRule && ((SplitPairRule) splitRule).shouldClearTop()) {
removeExistingSecondaryContainers(wct, primaryContainer);
}
SplitContainer splitContainer = new SplitContainer(primaryContainer, primaryActivity, SplitContainer splitContainer = new SplitContainer(primaryContainer, primaryActivity,
secondaryContainer, splitPairRule); secondaryContainer, splitRule);
mSplitContainers.add(splitContainer); mSplitContainers.add(splitContainer);
} }
@@ -456,22 +457,20 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
} }
// Check if there is enough space for launch // Check if there is enough space for launch
final ExtensionSplitPairRule placeholderRule = getPlaceholderRule( final SplitPlaceholderRule placeholderRule = getPlaceholderRule(activity);
activity.getComponentName());
if (placeholderRule == null || !mPresenter.shouldShowSideBySide( if (placeholderRule == null || !mPresenter.shouldShowSideBySide(
mPresenter.getParentContainerBounds(activity), placeholderRule)) { mPresenter.getParentContainerBounds(activity), placeholderRule)) {
return false; return false;
} }
Intent placeholderIntent = new Intent();
placeholderIntent.setComponent(placeholderRule.secondaryActivityName);
// TODO(b/190433398): Handle failed request // TODO(b/190433398): Handle failed request
startActivityToSide(activity, placeholderIntent, null, placeholderRule, -1); startActivityToSide(activity, placeholderRule.getPlaceholderIntent(), null,
placeholderRule, null);
return true; return true;
} }
private boolean dismissPlaceholderIfNecessary(@NonNull SplitContainer splitContainer) { private boolean dismissPlaceholderIfNecessary(@NonNull SplitContainer splitContainer) {
if (!splitContainer.getSplitPairRule().useAsPlaceholder) { if (!splitContainer.isPlaceholderContainer()) {
return false; 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 * Returns the rule to launch a placeholder for the activity with the provided component name
* if it is configured in the split config. * if it is configured in the split config.
*/ */
private ExtensionSplitPairRule getPlaceholderRule(@NonNull ComponentName componentName) { private SplitPlaceholderRule getPlaceholderRule(@NonNull Activity activity) {
for (ExtensionSplitRule rule : mSplitRules) { for (EmbeddingRule rule : mSplitRules) {
if (!(rule instanceof ExtensionSplitPairRule)) { if (!(rule instanceof SplitPlaceholderRule)) {
continue; continue;
} }
ExtensionSplitPairRule pairRule = (ExtensionSplitPairRule) rule; SplitPlaceholderRule placeholderRule = (SplitPlaceholderRule) rule;
if (componentName.equals(pairRule.primaryActivityName) if (placeholderRule.getActivityPredicate().test(activity)) {
&& pairRule.useAsPlaceholder) { return placeholderRule;
return pairRule;
} }
} }
return null; return null;
@@ -506,27 +504,27 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
* Notifies listeners about changes to split states if necessary. * Notifies listeners about changes to split states if necessary.
*/ */
private void updateCallbackIfNecessary() { private void updateCallbackIfNecessary() {
if (mSplitOrganizerCallback == null) { if (mEmbeddingCallback == null) {
return; return;
} }
// TODO(b/190433398): Check if something actually changed // TODO(b/190433398): Check if something actually changed
mSplitOrganizerCallback.onSplitInfoChanged(getActiveSplitStates()); mEmbeddingCallback.accept(getActiveSplitStates());
} }
/** /**
* Returns a list of descriptors for currently active split states. * Returns a list of descriptors for currently active split states.
*/ */
private List<ExtensionSplitInfo> getActiveSplitStates() { private List<SplitInfo> getActiveSplitStates() {
List<ExtensionSplitInfo> splitStates = new ArrayList<>(); List<SplitInfo> splitStates = new ArrayList<>();
for (SplitContainer container : mSplitContainers) { for (SplitContainer container : mSplitContainers) {
ExtensionTaskFragment primaryContainer = TaskFragment primaryContainer =
new ExtensionTaskFragment( new TaskFragment(
container.getPrimaryContainer().collectActivities()); container.getPrimaryContainer().collectActivities());
ExtensionTaskFragment secondaryContainer = TaskFragment secondaryContainer =
new ExtensionTaskFragment( new TaskFragment(
container.getSecondaryContainer().collectActivities()); container.getSecondaryContainer().collectActivities());
ExtensionSplitInfo splitState = new ExtensionSplitInfo(primaryContainer, SplitInfo splitState = new SplitInfo(primaryContainer,
secondaryContainer, container.getSplitPairRule().splitRatio); secondaryContainer, container.getSplitRule().getSplitRatio());
splitStates.add(splitState); splitStates.add(splitState);
} }
return splitStates; 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 @Nullable
private static ExtensionSplitPairRule getSplitRule(@NonNull ComponentName primaryActivityName, private static SplitPairRule getSplitRule(@NonNull Activity primaryActivity,
@NonNull ComponentName secondaryActivityName, @NonNull Intent secondaryActivityIntent, @NonNull List<EmbeddingRule> splitRules) {
@NonNull List<ExtensionSplitRule> splitRules) { for (EmbeddingRule rule : splitRules) {
if (splitRules == null || primaryActivityName == null || secondaryActivityName == null) { if (!(rule instanceof SplitPairRule)) {
return null;
}
for (ExtensionSplitRule rule : splitRules) {
if (!(rule instanceof ExtensionSplitPairRule)) {
continue; continue;
} }
ExtensionSplitPairRule pairRule = (ExtensionSplitPairRule) rule; SplitPairRule pairRule = (SplitPairRule) rule;
if (match(secondaryActivityName, pairRule.secondaryActivityName) if (pairRule.getActivityIntentPredicate().test(
&& match(primaryActivityName, pairRule.primaryActivityName)) { 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<EmbeddingRule> 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; 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 * 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 ComponentName componentName, private static boolean shouldExpand(@NonNull Activity activity,
List<ExtensionSplitRule> splitRules) { List<EmbeddingRule> splitRules) {
if (splitRules == null) { if (splitRules == null) {
return false; return false;
} }
for (ExtensionSplitRule rule : splitRules) { for (EmbeddingRule rule : splitRules) {
if (!(rule instanceof ExtensionSplitActivityRule)) { if (!(rule instanceof ActivityRule)) {
continue; continue;
} }
ExtensionSplitActivityRule activityRule = (ExtensionSplitActivityRule) rule; ActivityRule activityRule = (ActivityRule) rule;
if (match(componentName, activityRule.activityName) if (!activityRule.shouldAlwaysExpand()) {
&& activityRule.alwaysExpand) { continue;
}
if (activityRule.getActivityPredicate().test(activity)) {
return true; return true;
} }
} }
return false; 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 { private final class LifecycleCallbacks implements ActivityLifecycleCallbacks {
@Override @Override
@@ -698,8 +685,8 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
private class ActivityStartMonitor extends Instrumentation.ActivityMonitor { private class ActivityStartMonitor extends Instrumentation.ActivityMonitor {
@Override @Override
public Instrumentation.ActivityResult onStartActivity(Context who, Intent intent, public Instrumentation.ActivityResult onStartActivity(@NonNull Context who,
Bundle options) { @NonNull Intent intent, @NonNull Bundle options) {
// TODO(b/190433398): Check if the activity is configured to always be expanded. // 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. // 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, private boolean setLaunchingToSideContainer(Activity launchingActivity, Intent intent,
Bundle options) { Bundle options) {
final ExtensionSplitPairRule splitPairRule = getSplitRule( final SplitPairRule splitPairRule = getSplitRule(launchingActivity, intent,
launchingActivity.getComponentName(), intent.getComponent(), getSplitRules()); getSplitRules());
if (splitPairRule == null) { if (splitPairRule == null) {
return false; return false;
} }
@@ -770,8 +757,8 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
return; return;
} }
final ExtensionSplitPairRule splitPairRule = getSplitRule( final SplitPairRule splitPairRule = getSplitRule(primaryActivity, intent,
primaryActivity.getComponentName(), intent.getComponent(), getSplitRules()); getSplitRules());
if (splitPairRule == null) { if (splitPairRule == null) {
return; return;
} }

View File

@@ -24,13 +24,16 @@ 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.view.WindowInsets;
import android.view.WindowMetrics;
import android.window.TaskFragmentCreationParams; import android.window.TaskFragmentCreationParams;
import android.window.WindowContainerTransaction; 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.ExtensionSplitPairRule; import androidx.window.extensions.embedding.SplitPairRule;
import androidx.window.extensions.embedding.SplitRule;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
@@ -89,7 +92,7 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
* @return The newly created secondary container. * @return The newly created secondary container.
*/ */
TaskFragmentContainer createNewSplitWithEmptySideContainer(@NonNull Activity primaryActivity, TaskFragmentContainer createNewSplitWithEmptySideContainer(@NonNull Activity primaryActivity,
@NonNull ExtensionSplitPairRule rule) { @NonNull SplitPairRule rule) {
final WindowContainerTransaction wct = new WindowContainerTransaction(); final WindowContainerTransaction wct = new WindowContainerTransaction();
final Rect parentBounds = getParentContainerBounds(primaryActivity); final Rect parentBounds = getParentContainerBounds(primaryActivity);
@@ -128,7 +131,7 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
* @param rule The split rule to be applied to the container. * @param rule The split rule to be applied to the container.
*/ */
void createNewSplitContainer(@NonNull Activity primaryActivity, void createNewSplitContainer(@NonNull Activity primaryActivity,
@NonNull Activity secondaryActivity, @NonNull ExtensionSplitPairRule rule) { @NonNull Activity secondaryActivity, @NonNull SplitPairRule rule) {
final WindowContainerTransaction wct = new WindowContainerTransaction(); final WindowContainerTransaction wct = new WindowContainerTransaction();
final Rect parentBounds = getParentContainerBounds(primaryActivity); final Rect parentBounds = getParentContainerBounds(primaryActivity);
@@ -192,7 +195,7 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
* @param rule The split rule to be applied to the container. * @param rule The split rule to be applied to the container.
*/ */
void startActivityToSide(@NonNull Activity launchingActivity, @NonNull Intent activityIntent, 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 parentBounds = getParentContainerBounds(launchingActivity);
final Rect primaryRectBounds = getBoundsForPosition(POSITION_LEFT, parentBounds, rule); final Rect primaryRectBounds = getBoundsForPosition(POSITION_LEFT, parentBounds, rule);
final Rect secondaryRectBounds = getBoundsForPosition(POSITION_RIGHT, parentBounds, rule); final Rect secondaryRectBounds = getBoundsForPosition(POSITION_RIGHT, parentBounds, rule);
@@ -227,7 +230,7 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
@NonNull WindowContainerTransaction wct) { @NonNull WindowContainerTransaction wct) {
// 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 ExtensionSplitPairRule rule = splitContainer.getSplitPairRule(); final SplitRule rule = splitContainer.getSplitRule();
final Rect primaryRectBounds = getBoundsForPosition(POSITION_LEFT, parentBounds, rule); final Rect primaryRectBounds = getBoundsForPosition(POSITION_LEFT, parentBounds, rule);
final Rect secondaryRectBounds = getBoundsForPosition(POSITION_RIGHT, parentBounds, rule); final Rect secondaryRectBounds = getBoundsForPosition(POSITION_RIGHT, parentBounds, rule);
@@ -273,24 +276,24 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
boolean shouldShowSideBySide(@NonNull SplitContainer splitContainer) { boolean shouldShowSideBySide(@NonNull SplitContainer splitContainer) {
final Rect parentBounds = getParentContainerBounds(splitContainer.getPrimaryContainer()); final Rect parentBounds = getParentContainerBounds(splitContainer.getPrimaryContainer());
return shouldShowSideBySide(parentBounds, splitContainer.getSplitPairRule()); return shouldShowSideBySide(parentBounds, splitContainer.getSplitRule());
} }
boolean shouldShowSideBySide(@Nullable Rect parentBounds, boolean shouldShowSideBySide(@Nullable Rect parentBounds, @NonNull SplitRule rule) {
@NonNull ExtensionSplitPairRule rule) { // TODO(b/190433398): Supply correct insets.
return parentBounds != null && parentBounds.width() >= rule.minWidth final WindowMetrics parentMetrics = new WindowMetrics(parentBounds,
// TODO(b/190433398): Consider proper smallest width computation. new WindowInsets(new Rect()));
&& Math.min(parentBounds.width(), parentBounds.height()) >= rule.minSmallestWidth; return rule.getParentWindowMetricsPredicate().test(parentMetrics);
} }
@NonNull @NonNull
private Rect getBoundsForPosition(@Position int position, @NonNull Rect parentBounds, private Rect getBoundsForPosition(@Position int position, @NonNull Rect parentBounds,
@NonNull ExtensionSplitPairRule rule) { @NonNull SplitRule rule) {
if (!shouldShowSideBySide(parentBounds, rule)) { if (!shouldShowSideBySide(parentBounds, rule)) {
return new Rect(); return new Rect();
} }
float splitRatio = rule.splitRatio; float splitRatio = rule.getSplitRatio();
switch (position) { switch (position) {
case POSITION_LEFT: case POSITION_LEFT:
return new Rect( return new Rect(