Loading libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsImpl.java +56 −11 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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. Loading @@ -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; Loading @@ -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 Loading @@ -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(); } /** Loading @@ -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() ); } } } Loading libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java +5 −13 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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(); Loading @@ -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>> { Loading libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java +8 −3 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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) { Loading Loading @@ -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( Loading Loading @@ -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()) { Loading libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java +44 −13 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading Loading @@ -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 */ Loading Loading @@ -230,6 +235,7 @@ 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}. */ Loading Loading @@ -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) { Loading Loading @@ -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(); Loading @@ -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. Loading Loading @@ -390,6 +420,7 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent { } @Override public void onLowMemory() {} public void onLowMemory() { } } } libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java +4 −1 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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 -> { Loading Loading
libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsImpl.java +56 −11 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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. Loading @@ -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; Loading @@ -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 Loading @@ -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(); } /** Loading @@ -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() ); } } } Loading
libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java +5 −13 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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(); Loading @@ -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>> { Loading
libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java +8 −3 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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) { Loading Loading @@ -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( Loading Loading @@ -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()) { Loading
libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java +44 −13 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading Loading @@ -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 */ Loading Loading @@ -230,6 +235,7 @@ 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}. */ Loading Loading @@ -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) { Loading Loading @@ -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(); Loading @@ -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. Loading Loading @@ -390,6 +420,7 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent { } @Override public void onLowMemory() {} public void onLowMemory() { } } }
libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java +4 −1 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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 -> { Loading