Loading libs/WindowManager/Jetpack/src/androidx/window/common/layout/CommonFoldingFeature.java +3 −1 Original line number Diff line number Diff line Loading @@ -25,6 +25,7 @@ import android.hardware.devicestate.DeviceStateManager; import android.util.Log; import androidx.annotation.NonNull; import androidx.annotation.VisibleForTesting; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; Loading Loading @@ -212,7 +213,8 @@ public final class CommonFoldingFeature { @NonNull private final Rect mRect; CommonFoldingFeature(int type, @State int state, @NonNull Rect rect) { @VisibleForTesting public CommonFoldingFeature(int type, @State int state, @NonNull Rect rect) { assertReportableState(state); this.mType = type; this.mState = state; Loading libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsImpl.java +5 −0 Original line number Diff line number Diff line Loading @@ -57,6 +57,8 @@ class WindowExtensionsImpl implements WindowExtensions { */ private static final int NO_LEVEL_OVERRIDE = -1; private static final int EXTENSIONS_VERSION_V9 = 9; private static final int EXTENSIONS_VERSION_V8 = 8; private static final int EXTENSIONS_VERSION_V7 = 7; Loading @@ -80,6 +82,9 @@ class WindowExtensionsImpl implements WindowExtensions { */ @VisibleForTesting static int getExtensionsVersionCurrentPlatform() { if (Flags.wlinfoOncreate()) { return EXTENSIONS_VERSION_V9; } if (Flags.aeBackStackRestore()) { return EXTENSIONS_VERSION_V8; } Loading libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java +15 −0 Original line number Diff line number Diff line Loading @@ -74,6 +74,12 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent { @GuardedBy("mLock") private final DeviceStateManagerFoldingFeatureProducer mFoldingFeatureProducer; /** * The last reported folding features from the device. This is initialized in the constructor * because the data change callback added to {@link #mFoldingFeatureProducer} is immediately * called. This is due to current device state from the device state manager already being * available in the {@link DeviceStateManagerFoldingFeatureProducer}. */ @GuardedBy("mLock") private final List<CommonFoldingFeature> mLastReportedFoldingFeatures = new ArrayList<>(); Loading Loading @@ -308,6 +314,7 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent { * @param context a proxy for the {@link android.view.Window} that contains the * {@link DisplayFeature}. */ @NonNull private WindowLayoutInfo getWindowLayoutInfo(@NonNull @UiContext Context context, List<CommonFoldingFeature> storedFeatures) { List<DisplayFeature> displayFeatureList = getDisplayFeatures(context, storedFeatures); Loading @@ -329,6 +336,14 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent { } } @Override @NonNull public WindowLayoutInfo getCurrentWindowLayoutInfo(@NonNull @UiContext Context context) { synchronized (mLock) { return getWindowLayoutInfo(context, mLastReportedFoldingFeatures); } } /** * Returns the {@link SupportedWindowFeatures} for the device. This list does not change over * time. Loading libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/layout/WindowLayoutComponentImplTest.java +76 −8 Original line number Diff line number Diff line Loading @@ -16,22 +16,30 @@ package androidx.window.extensions.layout; import static com.google.common.truth.Truth.assertThat; import static org.mockito.Mockito.mock; import android.app.WindowConfiguration; import android.content.Context; import android.content.ContextWrapper; import android.graphics.Rect; import android.platform.test.annotations.Presubmit; import android.view.Display; import androidx.annotation.NonNull; 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.common.layout.CommonFoldingFeature; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import java.util.Collections; import java.util.List; /** * Test class for {@link WindowLayoutComponentImpl}. Loading @@ -44,31 +52,91 @@ import java.util.Collections; @RunWith(AndroidJUnit4.class) public class WindowLayoutComponentImplTest { private final Context mAppContext = ApplicationProvider.getApplicationContext(); @NonNull private WindowLayoutComponentImpl mWindowLayoutComponent; @Before public void setUp() { mWindowLayoutComponent = new WindowLayoutComponentImpl( ApplicationProvider.getApplicationContext(), mWindowLayoutComponent = new WindowLayoutComponentImpl(mAppContext, mock(DeviceStateManagerFoldingFeatureProducer.class)); } @Test public void testAddWindowLayoutListenerOnFakeUiContext_noCrash() { final Context fakeUiContext = createTestContext(); public void testAddWindowLayoutListener_onFakeUiContext_noCrash() { final Context fakeUiContext = new FakeUiContext(mAppContext); mWindowLayoutComponent.addWindowLayoutInfoListener(fakeUiContext, info -> {}); mWindowLayoutComponent.onDisplayFeaturesChanged(Collections.emptyList()); } private static Context createTestContext() { return new FakeUiContext(ApplicationProvider.getApplicationContext()); @Test public void testGetCurrentWindowLayoutInfo_noFoldingFeature_returnsEmptyList() { final Context testUiContext = new TestUiContext(mAppContext); final WindowLayoutInfo layoutInfo = mWindowLayoutComponent.getCurrentWindowLayoutInfo(testUiContext); assertThat(layoutInfo.getDisplayFeatures()).isEmpty(); } @Test public void testGetCurrentWindowLayoutInfo_hasFoldingFeature_returnsWindowLayoutInfo() { final Context testUiContext = new TestUiContext(mAppContext); final WindowConfiguration windowConfiguration = testUiContext.getResources().getConfiguration().windowConfiguration; final Rect featureRect = windowConfiguration.getBounds(); final CommonFoldingFeature foldingFeature = new CommonFoldingFeature( CommonFoldingFeature.COMMON_TYPE_HINGE, CommonFoldingFeature.COMMON_STATE_FLAT, featureRect ); mWindowLayoutComponent.onDisplayFeaturesChanged(List.of(foldingFeature)); final WindowLayoutInfo layoutInfo = mWindowLayoutComponent.getCurrentWindowLayoutInfo(testUiContext); assertThat(layoutInfo.getDisplayFeatures()).containsExactly(new FoldingFeature( featureRect, FoldingFeature.TYPE_HINGE, FoldingFeature.STATE_FLAT)); } @Test public void testGetCurrentWindowLayoutInfo_nonUiContext_returnsEmptyList() { final WindowLayoutInfo layoutInfo = mWindowLayoutComponent.getCurrentWindowLayoutInfo(mAppContext); assertThat(layoutInfo.getDisplayFeatures()).isEmpty(); } /** * A {@link Context} that simulates a UI context specifically for testing purposes. * This class overrides {@link Context#getAssociatedDisplayId()} to return * {@link Display#DEFAULT_DISPLAY}, ensuring the context is tied to the default display, * and {@link Context#isUiContext()} to always return {@code true}, simulating a UI context. */ private static class TestUiContext extends ContextWrapper { TestUiContext(Context base) { super(base); } @Override public int getAssociatedDisplayId() { return Display.DEFAULT_DISPLAY; } @Override public boolean isUiContext() { return true; } } /** * A {@link android.content.Context} overrides {@link android.content.Context#isUiContext} to * {@code true}. * A {@link Context} that cheats by overriding {@link Context#isUiContext} to always * return {@code true}. This is useful for scenarios where a UI context is needed, * but the underlying context isn't actually a UI one. */ private static class FakeUiContext extends ContextWrapper { Loading Loading
libs/WindowManager/Jetpack/src/androidx/window/common/layout/CommonFoldingFeature.java +3 −1 Original line number Diff line number Diff line Loading @@ -25,6 +25,7 @@ import android.hardware.devicestate.DeviceStateManager; import android.util.Log; import androidx.annotation.NonNull; import androidx.annotation.VisibleForTesting; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; Loading Loading @@ -212,7 +213,8 @@ public final class CommonFoldingFeature { @NonNull private final Rect mRect; CommonFoldingFeature(int type, @State int state, @NonNull Rect rect) { @VisibleForTesting public CommonFoldingFeature(int type, @State int state, @NonNull Rect rect) { assertReportableState(state); this.mType = type; this.mState = state; Loading
libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsImpl.java +5 −0 Original line number Diff line number Diff line Loading @@ -57,6 +57,8 @@ class WindowExtensionsImpl implements WindowExtensions { */ private static final int NO_LEVEL_OVERRIDE = -1; private static final int EXTENSIONS_VERSION_V9 = 9; private static final int EXTENSIONS_VERSION_V8 = 8; private static final int EXTENSIONS_VERSION_V7 = 7; Loading @@ -80,6 +82,9 @@ class WindowExtensionsImpl implements WindowExtensions { */ @VisibleForTesting static int getExtensionsVersionCurrentPlatform() { if (Flags.wlinfoOncreate()) { return EXTENSIONS_VERSION_V9; } if (Flags.aeBackStackRestore()) { return EXTENSIONS_VERSION_V8; } Loading
libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java +15 −0 Original line number Diff line number Diff line Loading @@ -74,6 +74,12 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent { @GuardedBy("mLock") private final DeviceStateManagerFoldingFeatureProducer mFoldingFeatureProducer; /** * The last reported folding features from the device. This is initialized in the constructor * because the data change callback added to {@link #mFoldingFeatureProducer} is immediately * called. This is due to current device state from the device state manager already being * available in the {@link DeviceStateManagerFoldingFeatureProducer}. */ @GuardedBy("mLock") private final List<CommonFoldingFeature> mLastReportedFoldingFeatures = new ArrayList<>(); Loading Loading @@ -308,6 +314,7 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent { * @param context a proxy for the {@link android.view.Window} that contains the * {@link DisplayFeature}. */ @NonNull private WindowLayoutInfo getWindowLayoutInfo(@NonNull @UiContext Context context, List<CommonFoldingFeature> storedFeatures) { List<DisplayFeature> displayFeatureList = getDisplayFeatures(context, storedFeatures); Loading @@ -329,6 +336,14 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent { } } @Override @NonNull public WindowLayoutInfo getCurrentWindowLayoutInfo(@NonNull @UiContext Context context) { synchronized (mLock) { return getWindowLayoutInfo(context, mLastReportedFoldingFeatures); } } /** * Returns the {@link SupportedWindowFeatures} for the device. This list does not change over * time. Loading
libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/layout/WindowLayoutComponentImplTest.java +76 −8 Original line number Diff line number Diff line Loading @@ -16,22 +16,30 @@ package androidx.window.extensions.layout; import static com.google.common.truth.Truth.assertThat; import static org.mockito.Mockito.mock; import android.app.WindowConfiguration; import android.content.Context; import android.content.ContextWrapper; import android.graphics.Rect; import android.platform.test.annotations.Presubmit; import android.view.Display; import androidx.annotation.NonNull; 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.common.layout.CommonFoldingFeature; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import java.util.Collections; import java.util.List; /** * Test class for {@link WindowLayoutComponentImpl}. Loading @@ -44,31 +52,91 @@ import java.util.Collections; @RunWith(AndroidJUnit4.class) public class WindowLayoutComponentImplTest { private final Context mAppContext = ApplicationProvider.getApplicationContext(); @NonNull private WindowLayoutComponentImpl mWindowLayoutComponent; @Before public void setUp() { mWindowLayoutComponent = new WindowLayoutComponentImpl( ApplicationProvider.getApplicationContext(), mWindowLayoutComponent = new WindowLayoutComponentImpl(mAppContext, mock(DeviceStateManagerFoldingFeatureProducer.class)); } @Test public void testAddWindowLayoutListenerOnFakeUiContext_noCrash() { final Context fakeUiContext = createTestContext(); public void testAddWindowLayoutListener_onFakeUiContext_noCrash() { final Context fakeUiContext = new FakeUiContext(mAppContext); mWindowLayoutComponent.addWindowLayoutInfoListener(fakeUiContext, info -> {}); mWindowLayoutComponent.onDisplayFeaturesChanged(Collections.emptyList()); } private static Context createTestContext() { return new FakeUiContext(ApplicationProvider.getApplicationContext()); @Test public void testGetCurrentWindowLayoutInfo_noFoldingFeature_returnsEmptyList() { final Context testUiContext = new TestUiContext(mAppContext); final WindowLayoutInfo layoutInfo = mWindowLayoutComponent.getCurrentWindowLayoutInfo(testUiContext); assertThat(layoutInfo.getDisplayFeatures()).isEmpty(); } @Test public void testGetCurrentWindowLayoutInfo_hasFoldingFeature_returnsWindowLayoutInfo() { final Context testUiContext = new TestUiContext(mAppContext); final WindowConfiguration windowConfiguration = testUiContext.getResources().getConfiguration().windowConfiguration; final Rect featureRect = windowConfiguration.getBounds(); final CommonFoldingFeature foldingFeature = new CommonFoldingFeature( CommonFoldingFeature.COMMON_TYPE_HINGE, CommonFoldingFeature.COMMON_STATE_FLAT, featureRect ); mWindowLayoutComponent.onDisplayFeaturesChanged(List.of(foldingFeature)); final WindowLayoutInfo layoutInfo = mWindowLayoutComponent.getCurrentWindowLayoutInfo(testUiContext); assertThat(layoutInfo.getDisplayFeatures()).containsExactly(new FoldingFeature( featureRect, FoldingFeature.TYPE_HINGE, FoldingFeature.STATE_FLAT)); } @Test public void testGetCurrentWindowLayoutInfo_nonUiContext_returnsEmptyList() { final WindowLayoutInfo layoutInfo = mWindowLayoutComponent.getCurrentWindowLayoutInfo(mAppContext); assertThat(layoutInfo.getDisplayFeatures()).isEmpty(); } /** * A {@link Context} that simulates a UI context specifically for testing purposes. * This class overrides {@link Context#getAssociatedDisplayId()} to return * {@link Display#DEFAULT_DISPLAY}, ensuring the context is tied to the default display, * and {@link Context#isUiContext()} to always return {@code true}, simulating a UI context. */ private static class TestUiContext extends ContextWrapper { TestUiContext(Context base) { super(base); } @Override public int getAssociatedDisplayId() { return Display.DEFAULT_DISPLAY; } @Override public boolean isUiContext() { return true; } } /** * A {@link android.content.Context} overrides {@link android.content.Context#isUiContext} to * {@code true}. * A {@link Context} that cheats by overriding {@link Context#isUiContext} to always * return {@code true}. This is useful for scenarios where a UI context is needed, * but the underlying context isn't actually a UI one. */ private static class FakeUiContext extends ContextWrapper { Loading