Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit af83423b authored by Eric Lin's avatar Eric Lin Committed by Android (Google) Code Review
Browse files

Merge "Implement getCurrentWindowLayoutInfo() API." into main

parents 7c22e7ca a21a24f1
Loading
Loading
Loading
Loading
+3 −1
Original line number Diff line number Diff line
@@ -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;
@@ -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;
+5 −0
Original line number Diff line number Diff line
@@ -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;
@@ -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;
        }
+15 −0
Original line number Diff line number Diff line
@@ -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<>();

@@ -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);
@@ -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.
+76 −8
Original line number Diff line number Diff line
@@ -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}.
@@ -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 {