diff options
2 files changed, 95 insertions, 3 deletions
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java index 070fa5bcfae4..859bc2cc40f3 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java @@ -17,6 +17,7 @@ package androidx.window.extensions.layout; import static android.view.Display.DEFAULT_DISPLAY; +import static android.view.Display.INVALID_DISPLAY; import static androidx.window.common.CommonFoldingFeature.COMMON_STATE_FLAT; import static androidx.window.common.CommonFoldingFeature.COMMON_STATE_HALF_OPENED; @@ -41,6 +42,7 @@ import androidx.annotation.GuardedBy; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.UiContext; +import androidx.annotation.VisibleForTesting; import androidx.window.common.CommonFoldingFeature; import androidx.window.common.DeviceStateManagerFoldingFeatureProducer; import androidx.window.common.EmptyLifecycleCallbacksAdapter; @@ -138,6 +140,10 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent { throw new IllegalArgumentException("Context must be a UI Context, which should be" + " an Activity, WindowContext or InputMethodService"); } + if (context.getAssociatedDisplayId() == INVALID_DISPLAY) { + Log.w(TAG, "The registered Context is a UI Context but not associated with any" + + " display. This Context may not receive any WindowLayoutInfo update"); + } mFoldingFeatureProducer.getData((features) -> { WindowLayoutInfo newWindowLayout = getWindowLayoutInfo(context, features); consumer.accept(newWindowLayout); @@ -257,7 +263,8 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent { } } - private void onDisplayFeaturesChanged(List<CommonFoldingFeature> storedFeatures) { + @VisibleForTesting + void onDisplayFeaturesChanged(List<CommonFoldingFeature> storedFeatures) { synchronized (mLock) { mLastReportedFoldingFeatures.clear(); mLastReportedFoldingFeatures.addAll(storedFeatures); @@ -409,9 +416,10 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent { * @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(); + int displayId = context.getAssociatedDisplayId(); if (displayId != DEFAULT_DISPLAY) { - // Display features are not supported on secondary displays. + // Display features are not supported on secondary displays or the context is not + // associated with any display. return false; } diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/layout/WindowLayoutComponentImplTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/layout/WindowLayoutComponentImplTest.java new file mode 100644 index 000000000000..ff0a82fe05d6 --- /dev/null +++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/layout/WindowLayoutComponentImplTest.java @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2024 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.layout; + +import static org.mockito.Mockito.mock; + +import android.content.Context; +import android.content.ContextWrapper; +import android.platform.test.annotations.Presubmit; + +import androidx.test.core.app.ApplicationProvider; +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.filters.SmallTest; +import androidx.window.common.DeviceStateManagerFoldingFeatureProducer; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.Collections; + +/** + * Test class for {@link WindowLayoutComponentImpl}. + * + * Build/Install/Run: + * atest WMJetpackUnitTests:WindowLayoutComponentImplTest + */ +@Presubmit +@SmallTest +@RunWith(AndroidJUnit4.class) +public class WindowLayoutComponentImplTest { + + private WindowLayoutComponentImpl mWindowLayoutComponent; + + @Before + public void setUp() { + mWindowLayoutComponent = new WindowLayoutComponentImpl( + ApplicationProvider.getApplicationContext(), + mock(DeviceStateManagerFoldingFeatureProducer.class)); + } + + @Test + public void testAddWindowLayoutListenerOnFakeUiContext_noCrash() { + final Context fakeUiContext = createTestContext(); + + mWindowLayoutComponent.addWindowLayoutInfoListener(fakeUiContext, info -> {}); + + mWindowLayoutComponent.onDisplayFeaturesChanged(Collections.emptyList()); + } + + private static Context createTestContext() { + return new FakeUiContext(ApplicationProvider.getApplicationContext()); + } + + /** + * A {@link android.content.Context} overrides {@link android.content.Context#isUiContext} to + * {@code true}. + */ + private static class FakeUiContext extends ContextWrapper { + + FakeUiContext(Context base) { + super(base); + } + + @Override + public boolean isUiContext() { + return true; + } + } +} |