Merge "Use bounds to proxy if an app is full screen." into tm-qpr-dev
This commit is contained in:
@@ -17,9 +17,13 @@
|
||||
package androidx.window.extensions;
|
||||
|
||||
import android.app.ActivityThread;
|
||||
import android.app.Application;
|
||||
import android.content.Context;
|
||||
import android.window.TaskFragmentOrganizer;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.window.common.DeviceStateManagerFoldingFeatureProducer;
|
||||
import androidx.window.common.RawFoldingFeatureProducer;
|
||||
import androidx.window.extensions.area.WindowAreaComponent;
|
||||
import androidx.window.extensions.area.WindowAreaComponentImpl;
|
||||
import androidx.window.extensions.embedding.ActivityEmbeddingComponent;
|
||||
@@ -27,6 +31,8 @@ import androidx.window.extensions.embedding.SplitController;
|
||||
import androidx.window.extensions.layout.WindowLayoutComponent;
|
||||
import androidx.window.extensions.layout.WindowLayoutComponentImpl;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
|
||||
/**
|
||||
* The reference implementation of {@link WindowExtensions} that implements the initial API version.
|
||||
@@ -34,7 +40,8 @@ import androidx.window.extensions.layout.WindowLayoutComponentImpl;
|
||||
public class WindowExtensionsImpl implements WindowExtensions {
|
||||
|
||||
private final Object mLock = new Object();
|
||||
private volatile WindowLayoutComponent mWindowLayoutComponent;
|
||||
private volatile DeviceStateManagerFoldingFeatureProducer mFoldingFeatureProducer;
|
||||
private volatile WindowLayoutComponentImpl mWindowLayoutComponent;
|
||||
private volatile SplitController mSplitController;
|
||||
private volatile WindowAreaComponent mWindowAreaComponent;
|
||||
|
||||
@@ -44,6 +51,49 @@ public class WindowExtensionsImpl implements WindowExtensions {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private Application getApplication() {
|
||||
return Objects.requireNonNull(ActivityThread.currentApplication());
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private DeviceStateManagerFoldingFeatureProducer getFoldingFeatureProducer() {
|
||||
if (mFoldingFeatureProducer == null) {
|
||||
synchronized (mLock) {
|
||||
if (mFoldingFeatureProducer == null) {
|
||||
Context context = getApplication();
|
||||
RawFoldingFeatureProducer foldingFeatureProducer =
|
||||
new RawFoldingFeatureProducer(context);
|
||||
mFoldingFeatureProducer =
|
||||
new DeviceStateManagerFoldingFeatureProducer(context,
|
||||
foldingFeatureProducer);
|
||||
}
|
||||
}
|
||||
}
|
||||
return mFoldingFeatureProducer;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private WindowLayoutComponentImpl getWindowLayoutComponentImpl() {
|
||||
if (mWindowLayoutComponent == null) {
|
||||
synchronized (mLock) {
|
||||
if (mWindowLayoutComponent == null) {
|
||||
Context context = getApplication();
|
||||
DeviceStateManagerFoldingFeatureProducer producer =
|
||||
getFoldingFeatureProducer();
|
||||
// TODO(b/263263909) Use the organizer to tell if an Activity is embededed.
|
||||
// Need to improve our Dependency Injection and centralize the logic.
|
||||
TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(command -> {
|
||||
throw new RuntimeException("Not allowed!");
|
||||
});
|
||||
mWindowLayoutComponent = new WindowLayoutComponentImpl(context, organizer,
|
||||
producer);
|
||||
}
|
||||
}
|
||||
}
|
||||
return mWindowLayoutComponent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a reference implementation of {@link WindowLayoutComponent} if available,
|
||||
* {@code null} otherwise. The implementation must match the API level reported in
|
||||
@@ -52,15 +102,7 @@ public class WindowExtensionsImpl implements WindowExtensions {
|
||||
*/
|
||||
@Override
|
||||
public WindowLayoutComponent getWindowLayoutComponent() {
|
||||
if (mWindowLayoutComponent == null) {
|
||||
synchronized (mLock) {
|
||||
if (mWindowLayoutComponent == null) {
|
||||
Context context = ActivityThread.currentApplication();
|
||||
mWindowLayoutComponent = new WindowLayoutComponentImpl(context);
|
||||
}
|
||||
}
|
||||
}
|
||||
return mWindowLayoutComponent;
|
||||
return getWindowLayoutComponentImpl();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -74,7 +116,10 @@ public class WindowExtensionsImpl implements WindowExtensions {
|
||||
if (mSplitController == null) {
|
||||
synchronized (mLock) {
|
||||
if (mSplitController == null) {
|
||||
mSplitController = new SplitController();
|
||||
mSplitController = new SplitController(
|
||||
getWindowLayoutComponentImpl(),
|
||||
getFoldingFeatureProducer()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,8 +75,8 @@ import androidx.annotation.GuardedBy;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.window.common.CommonFoldingFeature;
|
||||
import androidx.window.common.DeviceStateManagerFoldingFeatureProducer;
|
||||
import androidx.window.common.EmptyLifecycleCallbacksAdapter;
|
||||
import androidx.window.extensions.WindowExtensionsProvider;
|
||||
import androidx.window.extensions.embedding.TransactionManager.TransactionRecord;
|
||||
import androidx.window.extensions.layout.WindowLayoutComponentImpl;
|
||||
|
||||
@@ -145,19 +145,12 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
|
||||
private final Handler mHandler;
|
||||
final Object mLock = new Object();
|
||||
private final ActivityStartMonitor mActivityStartMonitor;
|
||||
@NonNull
|
||||
final WindowLayoutComponentImpl mWindowLayoutComponent;
|
||||
|
||||
public SplitController() {
|
||||
this((WindowLayoutComponentImpl) Objects.requireNonNull(WindowExtensionsProvider
|
||||
.getWindowExtensions().getWindowLayoutComponent()));
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
SplitController(@NonNull WindowLayoutComponentImpl windowLayoutComponent) {
|
||||
public SplitController(@NonNull WindowLayoutComponentImpl windowLayoutComponent,
|
||||
@NonNull DeviceStateManagerFoldingFeatureProducer foldingFeatureProducer) {
|
||||
final MainThreadExecutor executor = new MainThreadExecutor();
|
||||
mHandler = executor.mHandler;
|
||||
mPresenter = new SplitPresenter(executor, this);
|
||||
mPresenter = new SplitPresenter(executor, windowLayoutComponent, this);
|
||||
mTransactionManager = new TransactionManager(mPresenter);
|
||||
final ActivityThread activityThread = ActivityThread.currentActivityThread();
|
||||
final Application application = activityThread.getApplication();
|
||||
@@ -168,8 +161,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
|
||||
|
||||
mActivityStartMonitor = new ActivityStartMonitor();
|
||||
instrumentation.addMonitor(mActivityStartMonitor);
|
||||
mWindowLayoutComponent = windowLayoutComponent;
|
||||
mWindowLayoutComponent.addFoldingStateChangedCallback(new FoldingFeatureListener());
|
||||
foldingFeatureProducer.addDataChangedCallback(new FoldingFeatureListener());
|
||||
}
|
||||
|
||||
private class FoldingFeatureListener implements Consumer<List<CommonFoldingFeature>> {
|
||||
|
||||
@@ -51,6 +51,7 @@ import androidx.window.extensions.embedding.SplitAttributesCalculator.SplitAttri
|
||||
import androidx.window.extensions.embedding.TaskContainer.TaskProperties;
|
||||
import androidx.window.extensions.layout.DisplayFeature;
|
||||
import androidx.window.extensions.layout.FoldingFeature;
|
||||
import androidx.window.extensions.layout.WindowLayoutComponentImpl;
|
||||
import androidx.window.extensions.layout.WindowLayoutInfo;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
@@ -137,10 +138,14 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
|
||||
.setSplitType(new ExpandContainersSplitType())
|
||||
.build();
|
||||
|
||||
private final WindowLayoutComponentImpl mWindowLayoutComponent;
|
||||
private final SplitController mController;
|
||||
|
||||
SplitPresenter(@NonNull Executor executor, @NonNull SplitController controller) {
|
||||
SplitPresenter(@NonNull Executor executor,
|
||||
@NonNull WindowLayoutComponentImpl windowLayoutComponent,
|
||||
@NonNull SplitController controller) {
|
||||
super(executor, controller);
|
||||
mWindowLayoutComponent = windowLayoutComponent;
|
||||
mController = controller;
|
||||
registerOrganizer();
|
||||
if (!SplitController.ENABLE_SHELL_TRANSITIONS) {
|
||||
@@ -556,7 +561,7 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
|
||||
return sanitizeSplitAttributes(taskProperties, defaultSplitAttributes,
|
||||
minDimensionsPair);
|
||||
}
|
||||
final WindowLayoutInfo windowLayoutInfo = mController.mWindowLayoutComponent
|
||||
final WindowLayoutInfo windowLayoutInfo = mWindowLayoutComponent
|
||||
.getCurrentWindowLayoutInfo(taskProperties.getDisplayId(),
|
||||
taskConfiguration.windowConfiguration);
|
||||
final SplitAttributesCalculatorParams params = new SplitAttributesCalculatorParams(
|
||||
@@ -838,7 +843,7 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
|
||||
final int displayId = taskProperties.getDisplayId();
|
||||
final WindowConfiguration windowConfiguration = taskProperties.getConfiguration()
|
||||
.windowConfiguration;
|
||||
final WindowLayoutInfo info = mController.mWindowLayoutComponent
|
||||
final WindowLayoutInfo info = mWindowLayoutComponent
|
||||
.getCurrentWindowLayoutInfo(displayId, windowConfiguration);
|
||||
final List<DisplayFeature> displayFeatures = info.getDisplayFeatures();
|
||||
if (displayFeatures.isEmpty()) {
|
||||
|
||||
@@ -35,6 +35,8 @@ import android.graphics.Rect;
|
||||
import android.os.Bundle;
|
||||
import android.os.IBinder;
|
||||
import android.util.ArrayMap;
|
||||
import android.view.WindowManager;
|
||||
import android.window.TaskFragmentOrganizer;
|
||||
|
||||
import androidx.annotation.GuardedBy;
|
||||
import androidx.annotation.NonNull;
|
||||
@@ -43,13 +45,13 @@ import androidx.annotation.UiContext;
|
||||
import androidx.window.common.CommonFoldingFeature;
|
||||
import androidx.window.common.DeviceStateManagerFoldingFeatureProducer;
|
||||
import androidx.window.common.EmptyLifecycleCallbacksAdapter;
|
||||
import androidx.window.common.RawFoldingFeatureProducer;
|
||||
import androidx.window.util.DataProducer;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
@@ -80,13 +82,16 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent {
|
||||
private final Map<IBinder, ConfigurationChangeListener> mConfigurationChangeListeners =
|
||||
new ArrayMap<>();
|
||||
|
||||
public WindowLayoutComponentImpl(@NonNull Context context) {
|
||||
private final TaskFragmentOrganizer mTaskFragmentOrganizer;
|
||||
|
||||
public WindowLayoutComponentImpl(@NonNull Context context,
|
||||
@NonNull TaskFragmentOrganizer taskFragmentOrganizer,
|
||||
@NonNull DeviceStateManagerFoldingFeatureProducer foldingFeatureProducer) {
|
||||
((Application) context.getApplicationContext())
|
||||
.registerActivityLifecycleCallbacks(new NotifyOnConfigurationChanged());
|
||||
RawFoldingFeatureProducer foldingFeatureProducer = new RawFoldingFeatureProducer(context);
|
||||
mFoldingFeatureProducer = new DeviceStateManagerFoldingFeatureProducer(context,
|
||||
foldingFeatureProducer);
|
||||
mFoldingFeatureProducer = foldingFeatureProducer;
|
||||
mFoldingFeatureProducer.addDataChangedCallback(this::onDisplayFeaturesChanged);
|
||||
mTaskFragmentOrganizer = taskFragmentOrganizer;
|
||||
}
|
||||
|
||||
/** Registers to listen to {@link CommonFoldingFeature} changes */
|
||||
@@ -182,7 +187,7 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent {
|
||||
|
||||
@GuardedBy("mLock")
|
||||
private boolean isListeningForLayoutChanges(IBinder token) {
|
||||
for (Context context: getContextsListeningForLayoutChanges()) {
|
||||
for (Context context : getContextsListeningForLayoutChanges()) {
|
||||
if (token.equals(Context.getToken(context))) {
|
||||
return true;
|
||||
}
|
||||
@@ -230,8 +235,9 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent {
|
||||
/**
|
||||
* Translates the {@link DisplayFeature} into a {@link WindowLayoutInfo} when a
|
||||
* valid state is found.
|
||||
*
|
||||
* @param context a proxy for the {@link android.view.Window} that contains the
|
||||
* {@link DisplayFeature}.
|
||||
* {@link DisplayFeature}.
|
||||
*/
|
||||
private WindowLayoutInfo getWindowLayoutInfo(@NonNull @UiContext Context context,
|
||||
List<CommonFoldingFeature> storedFeatures) {
|
||||
@@ -243,7 +249,7 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent {
|
||||
* Gets the current {@link WindowLayoutInfo} computed with passed {@link WindowConfiguration}.
|
||||
*
|
||||
* @return current {@link WindowLayoutInfo} on the default display. Returns
|
||||
* empty {@link WindowLayoutInfo} on secondary displays.
|
||||
* empty {@link WindowLayoutInfo} on secondary displays.
|
||||
*/
|
||||
@NonNull
|
||||
public WindowLayoutInfo getCurrentWindowLayoutInfo(int displayId,
|
||||
@@ -254,7 +260,7 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent {
|
||||
}
|
||||
}
|
||||
|
||||
/** @see #getWindowLayoutInfo(Context, List) */
|
||||
/** @see #getWindowLayoutInfo(Context, List) */
|
||||
private WindowLayoutInfo getWindowLayoutInfo(int displayId,
|
||||
@NonNull WindowConfiguration windowConfiguration,
|
||||
List<CommonFoldingFeature> storedFeatures) {
|
||||
@@ -278,7 +284,8 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent {
|
||||
*
|
||||
* @param context a proxy for the {@link android.view.Window} that contains the
|
||||
* {@link DisplayFeature}.
|
||||
* are within the {@link android.view.Window} of the {@link Activity}
|
||||
* @return a {@link List} of {@link DisplayFeature}s that are within the
|
||||
* {@link android.view.Window} of the {@link Activity}
|
||||
*/
|
||||
private List<DisplayFeature> getDisplayFeatures(
|
||||
@NonNull @UiContext Context context, List<CommonFoldingFeature> storedFeatures) {
|
||||
@@ -317,8 +324,11 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent {
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether display features should be reported for the activity.
|
||||
* Calculates if the display features should be reported for the UI Context. The calculation
|
||||
* uses the task information because that is accurate for Activities in ActivityEmbedding mode.
|
||||
* TODO(b/238948678): Support reporting display features in all windowing modes.
|
||||
*
|
||||
* @return true if the display features should be reported for the UI Context, false otherwise.
|
||||
*/
|
||||
private boolean shouldReportDisplayFeatures(@NonNull @UiContext Context context) {
|
||||
int displayId = context.getDisplay().getDisplayId();
|
||||
@@ -337,7 +347,27 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent {
|
||||
// bounds in this case can't be computed correctly, so we should skip.
|
||||
return false;
|
||||
}
|
||||
windowingMode = taskConfig.windowConfiguration.getWindowingMode();
|
||||
final Rect taskBounds = taskConfig.windowConfiguration.getBounds();
|
||||
final WindowManager windowManager = Objects.requireNonNull(
|
||||
context.getSystemService(WindowManager.class));
|
||||
final Rect currentBounds = windowManager.getCurrentWindowMetrics().getBounds();
|
||||
final Rect maxBounds = windowManager.getMaximumWindowMetrics().getBounds();
|
||||
boolean isTaskExpanded = maxBounds.equals(taskBounds);
|
||||
boolean isActivityExpanded = maxBounds.equals(currentBounds);
|
||||
/*
|
||||
* We need to proxy being in full screen because when a user enters PiP and exits PiP
|
||||
* the task windowingMode will report multi-window/pinned until the transition is
|
||||
* finished in WM Shell.
|
||||
* maxBounds == taskWindowBounds is a proxy check to verify the window is full screen
|
||||
* For tasks that are letterboxed, we use currentBounds == maxBounds to filter these
|
||||
* out.
|
||||
*/
|
||||
// TODO(b/262900133) remove currentBounds check when letterboxed apps report bounds.
|
||||
// currently we don't want to report to letterboxed apps since they do not update the
|
||||
// window bounds when the Activity is moved. An inaccurate fold will be reported so
|
||||
// we skip.
|
||||
return isTaskExpanded && (isActivityExpanded
|
||||
|| mTaskFragmentOrganizer.isActivityEmbedded(activityToken));
|
||||
} else {
|
||||
// TODO(b/242674941): use task windowing mode for window context that associates with
|
||||
// activity.
|
||||
@@ -390,6 +420,7 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLowMemory() {}
|
||||
public void onLowMemory() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -90,6 +90,7 @@ import android.window.WindowContainerTransaction;
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
import androidx.test.filters.SmallTest;
|
||||
import androidx.window.common.DeviceStateManagerFoldingFeatureProducer;
|
||||
import androidx.window.extensions.layout.WindowLayoutComponentImpl;
|
||||
import androidx.window.extensions.layout.WindowLayoutInfo;
|
||||
|
||||
@@ -142,7 +143,9 @@ public class SplitControllerTest {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
doReturn(new WindowLayoutInfo(new ArrayList<>())).when(mWindowLayoutComponent)
|
||||
.getCurrentWindowLayoutInfo(anyInt(), any());
|
||||
mSplitController = new SplitController(mWindowLayoutComponent);
|
||||
DeviceStateManagerFoldingFeatureProducer producer =
|
||||
mock(DeviceStateManagerFoldingFeatureProducer.class);
|
||||
mSplitController = new SplitController(mWindowLayoutComponent, producer);
|
||||
mSplitPresenter = mSplitController.mPresenter;
|
||||
mSplitInfos = new ArrayList<>();
|
||||
mEmbeddingCallback = splitInfos -> {
|
||||
|
||||
@@ -75,6 +75,7 @@ import android.window.WindowContainerTransaction;
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
import androidx.test.filters.SmallTest;
|
||||
import androidx.window.common.DeviceStateManagerFoldingFeatureProducer;
|
||||
import androidx.window.extensions.layout.WindowLayoutComponentImpl;
|
||||
import androidx.window.extensions.layout.WindowLayoutInfo;
|
||||
|
||||
@@ -117,7 +118,9 @@ public class SplitPresenterTest {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
doReturn(new WindowLayoutInfo(new ArrayList<>())).when(mWindowLayoutComponent)
|
||||
.getCurrentWindowLayoutInfo(anyInt(), any());
|
||||
mController = new SplitController(mWindowLayoutComponent);
|
||||
DeviceStateManagerFoldingFeatureProducer producer =
|
||||
mock(DeviceStateManagerFoldingFeatureProducer.class);
|
||||
mController = new SplitController(mWindowLayoutComponent, producer);
|
||||
mPresenter = mController.mPresenter;
|
||||
spyOn(mController);
|
||||
spyOn(mPresenter);
|
||||
|
||||
@@ -45,6 +45,8 @@ import android.window.WindowContainerTransaction;
|
||||
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
import androidx.test.filters.SmallTest;
|
||||
import androidx.window.common.DeviceStateManagerFoldingFeatureProducer;
|
||||
import androidx.window.extensions.layout.WindowLayoutComponentImpl;
|
||||
|
||||
import com.google.android.collect.Lists;
|
||||
|
||||
@@ -82,7 +84,10 @@ public class TaskFragmentContainerTest {
|
||||
@Before
|
||||
public void setup() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
mController = new SplitController();
|
||||
DeviceStateManagerFoldingFeatureProducer producer =
|
||||
mock(DeviceStateManagerFoldingFeatureProducer.class);
|
||||
WindowLayoutComponentImpl component = mock(WindowLayoutComponentImpl.class);
|
||||
mController = new SplitController(component, producer);
|
||||
spyOn(mController);
|
||||
mActivity = createMockActivity();
|
||||
mIntent = new Intent();
|
||||
|
||||
Reference in New Issue
Block a user