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

Commit 0ffc4fc6 authored by cocod's avatar cocod
Browse files

Adjust dream overlay margins for mobile

Introduce smaller margins for the dream overlay in portrait
mode on mobile.
Defined a util with window size bps for dream overlay and
communal hub.
Collect config change in complication hostView controller,
so when screen size changes, we'll update the layout engine
to re-calculate margins.

Bug: b/378163332
Test: atest ComplicationLayoutEngineTest
Test: atest ComplicationHostViewControllerTest
Test: manually on foldable and tablet
Flag: com.android.systemui.glanceable_hub_v2
Change-Id: I7af85f1fc6dfc5cce1710ea9106a2c1627d088dc
parent e9c2327c
Loading
Loading
Loading
Loading
+6 −3
Original line number Diff line number Diff line
@@ -48,6 +48,9 @@ import androidx.compose.ui.unit.coerceAtMost
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.times
import androidx.window.layout.WindowMetricsCalculator
import com.android.systemui.communal.util.WindowSizeUtils.COMPACT_HEIGHT
import com.android.systemui.communal.util.WindowSizeUtils.COMPACT_WIDTH
import com.android.systemui.communal.util.WindowSizeUtils.MEDIUM_WIDTH

/**
 * Renders a responsive [LazyHorizontalGrid] with dynamic columns and rows. Each cell will maintain
@@ -266,14 +269,14 @@ fun calculateWindowSize(): DpSize {
private fun calculateNumCellsWidth(width: Dp) =
    // See https://developer.android.com/develop/ui/views/layout/use-window-size-classes
    when {
        width >= 840.dp -> 3
        width >= 600.dp -> 2
        width >= MEDIUM_WIDTH -> 3
        width >= COMPACT_WIDTH -> 2
        else -> 1
    }

private fun calculateNumCellsHeight(height: Dp) =
    when {
        height >= 1000.dp -> 3
        height >= 480.dp -> 2
        height >= COMPACT_HEIGHT -> 2
        else -> 1
    }
+38 −3
Original line number Diff line number Diff line
@@ -22,8 +22,12 @@ import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import android.content.res.Configuration;
import android.graphics.Rect;
import android.os.UserHandle;
import android.provider.Settings;
import android.testing.TestableLooper;
import android.testing.ViewUtils;
import android.view.View;

import androidx.constraintlayout.widget.ConstraintLayout;
@@ -35,6 +39,7 @@ import androidx.test.filters.SmallTest;

import com.android.systemui.SysuiTestCase;
import com.android.systemui.dreams.DreamOverlayStateController;
import com.android.systemui.kosmos.KosmosJavaAdapter;
import com.android.systemui.util.settings.FakeSettings;
import com.android.systemui.util.settings.SecureSettings;

@@ -52,8 +57,8 @@ import java.util.HashSet;

@SmallTest
@RunWith(AndroidJUnit4.class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
public class ComplicationHostViewControllerTest extends SysuiTestCase {
    @Mock
    ConstraintLayout mComplicationHostView;

    @Mock
@@ -99,6 +104,10 @@ public class ComplicationHostViewControllerTest extends SysuiTestCase {

    private SecureSettings mSecureSettings;

    private KosmosJavaAdapter mKosmos;

    private TestableLooper mLooper;

    private static final int CURRENT_USER_ID = UserHandle.USER_SYSTEM;

    @Before
@@ -113,7 +122,10 @@ public class ComplicationHostViewControllerTest extends SysuiTestCase {
        when(mViewHolder.getLayoutParams()).thenReturn(mComplicationLayoutParams);
        when(mComplicationView.getParent()).thenReturn(mComplicationHostView);

        mLooper = TestableLooper.get(this);
        mKosmos = new KosmosJavaAdapter(this);
        mSecureSettings = new FakeSettings();
        mComplicationHostView = new ConstraintLayout(getContext());
        mSecureSettings.putFloatForUser(
                Settings.Global.ANIMATOR_DURATION_SCALE, 1.0f, CURRENT_USER_ID);

@@ -123,11 +135,34 @@ public class ComplicationHostViewControllerTest extends SysuiTestCase {
                mDreamOverlayStateController,
                mLifecycleOwner,
                mViewModel,
                mSecureSettings);
                mSecureSettings,
                mKosmos.getConfigurationInteractor(),
                mKosmos.getTestDispatcher()
        );

        mController.init();
    }

    /**
     * Ensures layout engine update is called on configuration change.
     */
    @Test
    public void testUpdateLayoutEngineOnConfigurationChange() {
        mController.onViewAttached();
        // Attach the complication host view so flows collecting on it start running.
        ViewUtils.attachView(mComplicationHostView);
        mLooper.processAllMessages();

        // emit configuration change
        Rect bounds = new Rect(0, 0, 2000, 2000);
        Configuration config = new Configuration();
        config.windowConfiguration.setMaxBounds(bounds);
        mKosmos.getConfigurationRepository().onConfigurationChange(config);
        mKosmos.getTestScope().getTestScheduler().runCurrent();

        verify(mLayoutEngine).updateLayoutEngine(bounds);
    }

    /**
     * Ensures the lifecycle of complications is properly handled.
     */
+63 −24
Original line number Diff line number Diff line
@@ -18,10 +18,12 @@ package com.android.systemui.complication;
import static com.google.common.truth.Truth.assertThat;

import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import android.graphics.Rect;
import android.view.View;

import androidx.constraintlayout.widget.ConstraintLayout;
@@ -56,12 +58,14 @@ public class ComplicationLayoutEngineTest extends SysuiTestCase {
    @Mock
    TouchInsetManager.TouchInsetSession mTouchSession;

    Margins mMargins = new Margins(0, 0, 0, 0);

    ComplicationLayoutEngine createComplicationLayoutEngine() {
        return createComplicationLayoutEngine(0);
    }

    ComplicationLayoutEngine createComplicationLayoutEngine(int spacing) {
        return new ComplicationLayoutEngine(mLayout, spacing, 0, 0, 0, 0, mTouchSession, 0, 0);
        return new ComplicationLayoutEngine(mLayout, spacing, () -> mMargins, mTouchSession, 0, 0);
    }

    @Before
@@ -84,7 +88,7 @@ public class ComplicationLayoutEngineTest extends SysuiTestCase {
                ConstraintLayout layout) {
            this.lp = params;
            this.category = category;
            this.view = Mockito.mock(View.class);
            this.view = mock(View.class);
            this.id = sFactory.getNextId();
            when(view.getId()).thenReturn(sNextId++);
            when(view.getParent()).thenReturn(layout);
@@ -131,15 +135,10 @@ public class ComplicationLayoutEngineTest extends SysuiTestCase {
    @Test
    public void testComplicationMarginPosition() {
        final Random rand = new Random();
        final int startMargin = rand.nextInt();
        final int topMargin = rand.nextInt();
        final int endMargin = rand.nextInt();
        final int bottomMargin = rand.nextInt();
        final int spacing = rand.nextInt();

        final ComplicationLayoutEngine engine = new ComplicationLayoutEngine(mLayout, spacing,
                startMargin, topMargin, endMargin, bottomMargin, mTouchSession, 0, 0);

        mMargins = new Margins(rand.nextInt(), rand.nextInt(), rand.nextInt(),
                rand.nextInt());
        final ComplicationLayoutEngine engine = createComplicationLayoutEngine(spacing);
        final ViewInfo firstViewInfo = new ViewInfo(
                new ComplicationLayoutParams(
                        100,
@@ -167,17 +166,67 @@ public class ComplicationLayoutEngineTest extends SysuiTestCase {

        addComplication(engine, secondViewInfo);


        // The first added view should have margins from both directions from the corner position.
        verifyChange(firstViewInfo, false, lp -> {
            assertThat(lp.topMargin).isEqualTo(topMargin);
            assertThat(lp.getMarginEnd()).isEqualTo(endMargin);
            assertThat(lp.topMargin).isEqualTo(mMargins.top);
            assertThat(lp.getMarginEnd()).isEqualTo(mMargins.end);
        });

        // The second view should be spaced below the first view and have the side end margin.
        verifyChange(secondViewInfo, false, lp -> {
            assertThat(lp.topMargin).isEqualTo(spacing);
            assertThat(lp.getMarginEnd()).isEqualTo(endMargin);
            assertThat(lp.getMarginEnd()).isEqualTo(mMargins.end);
        });
    }

    @Test
    public void testComplicationMarginsOnScreenSizeChange() {
        final Random rand = new Random();
        final int spacing = rand.nextInt();
        final ComplicationLayoutEngine engine = createComplicationLayoutEngine(spacing);
        final ViewInfo firstViewInfo = new ViewInfo(
                new ComplicationLayoutParams(
                        100,
                        100,
                        ComplicationLayoutParams.POSITION_TOP
                                | ComplicationLayoutParams.POSITION_END,
                        ComplicationLayoutParams.DIRECTION_DOWN,
                        0),
                Complication.CATEGORY_SYSTEM,
                mLayout);

        addComplication(engine, firstViewInfo);

        final ViewInfo secondViewInfo = new ViewInfo(
                new ComplicationLayoutParams(
                        100,
                        100,
                        ComplicationLayoutParams.POSITION_TOP
                                | ComplicationLayoutParams.POSITION_END,
                        ComplicationLayoutParams.DIRECTION_DOWN,
                        0),
                Complication.CATEGORY_STANDARD,
                mLayout);

        addComplication(engine, secondViewInfo);

        firstViewInfo.clearInvocations();
        secondViewInfo.clearInvocations();

        // Triggers an update to the layout engine with new margins.
        final int newTopMargin = rand.nextInt();
        final int newEndMargin = rand.nextInt();
        mMargins = new Margins(0, newTopMargin, newEndMargin, 0);
        engine.updateLayoutEngine(new Rect(0, 0, 800, 1000));

        // Ensure complication view have new margins
        verifyChange(firstViewInfo, false, lp -> {
            assertThat(lp.topMargin).isEqualTo(newTopMargin);
            assertThat(lp.getMarginEnd()).isEqualTo(newEndMargin);
        });
        verifyChange(secondViewInfo, false, lp -> {
            assertThat(lp.topMargin).isEqualTo(spacing);
            assertThat(lp.getMarginEnd()).isEqualTo(newEndMargin);
        });
    }

@@ -241,7 +290,6 @@ public class ComplicationLayoutEngineTest extends SysuiTestCase {
    @Test
    public void testDirectionLayout() {
        final ComplicationLayoutEngine engine = createComplicationLayoutEngine();

        final ViewInfo firstViewInfo = new ViewInfo(
                new ComplicationLayoutParams(
                        100,
@@ -289,7 +337,6 @@ public class ComplicationLayoutEngineTest extends SysuiTestCase {
    @Test
    public void testPositionLayout() {
        final ComplicationLayoutEngine engine = createComplicationLayoutEngine();

        final ViewInfo firstViewInfo = new ViewInfo(
                new ComplicationLayoutParams(
                        100,
@@ -376,7 +423,6 @@ public class ComplicationLayoutEngineTest extends SysuiTestCase {
    public void testDefaultMargin() {
        final int margin = 5;
        final ComplicationLayoutEngine engine = createComplicationLayoutEngine(margin);

        final ViewInfo firstViewInfo = new ViewInfo(
                new ComplicationLayoutParams(
                        100,
@@ -452,7 +498,6 @@ public class ComplicationLayoutEngineTest extends SysuiTestCase {
        final int defaultMargin = 5;
        final int complicationMargin = 10;
        final ComplicationLayoutEngine engine = createComplicationLayoutEngine(defaultMargin);

        final ViewInfo firstViewInfo = new ViewInfo(
                new ComplicationLayoutParams(
                        100,
@@ -518,7 +563,6 @@ public class ComplicationLayoutEngineTest extends SysuiTestCase {
    public void testWidthConstraint() {
        final int maxWidth = 20;
        final ComplicationLayoutEngine engine = createComplicationLayoutEngine();

        final ViewInfo viewStartDirection = new ViewInfo(
                new ComplicationLayoutParams(
                        100,
@@ -566,7 +610,6 @@ public class ComplicationLayoutEngineTest extends SysuiTestCase {
    public void testHeightConstraint() {
        final int maxHeight = 20;
        final ComplicationLayoutEngine engine = createComplicationLayoutEngine();

        final ViewInfo viewUpDirection = new ViewInfo(
                new ComplicationLayoutParams(
                        100,
@@ -613,7 +656,6 @@ public class ComplicationLayoutEngineTest extends SysuiTestCase {
    @Test
    public void testConstraintNotSetWhenNotSpecified() {
        final ComplicationLayoutEngine engine = createComplicationLayoutEngine();

        final ViewInfo view = new ViewInfo(
                new ComplicationLayoutParams(
                        100,
@@ -641,7 +683,6 @@ public class ComplicationLayoutEngineTest extends SysuiTestCase {
    @Test
    public void testRemoval() {
        final ComplicationLayoutEngine engine = createComplicationLayoutEngine();

        final ViewInfo firstViewInfo = new ViewInfo(
                new ComplicationLayoutParams(
                        100,
@@ -687,7 +728,6 @@ public class ComplicationLayoutEngineTest extends SysuiTestCase {
    @Test
    public void testDoubleRemoval() {
        final ComplicationLayoutEngine engine = createComplicationLayoutEngine();

        final ViewInfo firstViewInfo = new ViewInfo(
                new ComplicationLayoutParams(
                        100,
@@ -716,7 +756,6 @@ public class ComplicationLayoutEngineTest extends SysuiTestCase {
    @Test
    public void testGetViews() {
        final ComplicationLayoutEngine engine = createComplicationLayoutEngine();

        final ViewInfo topEndView = new ViewInfo(
                new ComplicationLayoutParams(
                        100,
+7 −3
Original line number Diff line number Diff line
@@ -1990,10 +1990,14 @@
    </item>

    <!-- The padding applied to the dream overlay container -->
    <dimen name="dream_overlay_container_padding_start">0dp</dimen>
    <dimen name="dream_overlay_container_padding_end">0dp</dimen>
    <dimen name="dream_overlay_container_padding_start">40dp</dimen>
    <dimen name="dream_overlay_container_padding_end">40dp</dimen>
    <dimen name="dream_overlay_container_padding_top">0dp</dimen>
    <dimen name="dream_overlay_container_padding_bottom">0dp</dimen>
    <dimen name="dream_overlay_container_padding_bottom">40dp</dimen>
    <dimen name="dream_overlay_container_small_padding_start">32dp</dimen>
    <dimen name="dream_overlay_container_small_padding_end">32dp</dimen>
    <dimen name="dream_overlay_container_small_padding_top">0dp</dimen>
    <dimen name="dream_overlay_container_small_padding_bottom">32dp</dimen>

    <!-- The margin applied between complications -->
    <dimen name="dream_overlay_complication_margin">0dp</dimen>
+45 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2025 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 com.android.systemui.communal.util

import android.content.Context
import androidx.compose.ui.unit.dp
import androidx.window.layout.WindowMetricsCalculator

/**
 * [WindowSizeUtils] defines viewport breakpoints that helps create responsive mobile layout.
 *
 * @see https://developer.android.com/develop/ui/views/layout/use-window-size-classes
 */
object WindowSizeUtils {
    /** Compact screen width breakpoint. */
    val COMPACT_WIDTH = 600.dp
    /** Medium screen width breakpoint. */
    val MEDIUM_WIDTH = 840.dp
    /** Compact screen height breakpoint. */
    val COMPACT_HEIGHT = 480.dp
    /** Expanded screen height breakpoint. */
    val EXPANDED_HEIGHT = 900.dp

    /** Whether the window size is compact, which reflects most mobile sizes in portrait. */
    @JvmStatic
    fun isCompactWindowSize(context: Context): Boolean {
        val metrics = WindowMetricsCalculator.getOrCreate().computeCurrentWindowMetrics(context)
        val width = metrics.bounds.width()
        return width / metrics.density < COMPACT_WIDTH.value
    }
}
Loading