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

Commit 9d72f3b2 authored by Mady Mellor's avatar Mady Mellor Committed by Android (Google) Code Review
Browse files

Merge "Add some basic tests for BubblePositioner" into udc-qpr-dev

parents 4a0d255a b738e935
Loading
Loading
Loading
Loading
+271 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2023 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.wm.shell.bubbles;

import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
import static android.view.View.LAYOUT_DIRECTION_LTR;
import static android.view.View.LAYOUT_DIRECTION_RTL;

import static com.google.common.truth.Truth.assertThat;

import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;

import android.content.res.Configuration;
import android.graphics.Insets;
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.RectF;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.testing.TestableResources;
import android.view.WindowInsets;
import android.view.WindowManager;
import android.view.WindowMetrics;

import androidx.test.filters.SmallTest;

import com.android.wm.shell.R;
import com.android.wm.shell.ShellTestCase;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;

/**
 * Tests operations and the resulting state managed by {@link BubblePositioner}.
 */
@SmallTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
public class BubblePositionerTest extends ShellTestCase {

    private static final int MIN_WIDTH_FOR_TABLET = 600;

    private BubblePositioner mPositioner;
    private Configuration mConfiguration;

    @Mock
    private WindowManager mWindowManager;
    @Mock
    private WindowMetrics mWindowMetrics;

    @Before
    public void setUp() {
        MockitoAnnotations.initMocks(this);

        mConfiguration = spy(new Configuration());
        TestableResources testableResources = mContext.getOrCreateTestableResources();
        testableResources.overrideConfiguration(mConfiguration);

        mPositioner = new BubblePositioner(mContext, mWindowManager);
    }

    @Test
    public void testUpdate() {
        Insets insets = Insets.of(10, 20, 5, 15);
        Rect screenBounds = new Rect(0, 0, 1000, 1200);
        Rect availableRect = new Rect(screenBounds);
        availableRect.inset(insets);

        new WindowManagerConfig()
                .setInsets(insets)
                .setScreenBounds(screenBounds)
                .setUpConfig();
        mPositioner.update();

        assertThat(mPositioner.getAvailableRect()).isEqualTo(availableRect);
        assertThat(mPositioner.isLandscape()).isFalse();
        assertThat(mPositioner.isLargeScreen()).isFalse();
        assertThat(mPositioner.getInsets()).isEqualTo(insets);
    }

    @Test
    public void testShowBubblesVertically_phonePortrait() {
        new WindowManagerConfig().setOrientation(ORIENTATION_PORTRAIT).setUpConfig();
        mPositioner.update();

        assertThat(mPositioner.showBubblesVertically()).isFalse();
    }

    @Test
    public void testShowBubblesVertically_phoneLandscape() {
        new WindowManagerConfig().setOrientation(ORIENTATION_LANDSCAPE).setUpConfig();
        mPositioner.update();

        assertThat(mPositioner.isLandscape()).isTrue();
        assertThat(mPositioner.showBubblesVertically()).isTrue();
    }

    @Test
    public void testShowBubblesVertically_tablet() {
        new WindowManagerConfig().setLargeScreen().setUpConfig();
        mPositioner.update();

        assertThat(mPositioner.showBubblesVertically()).isTrue();
    }

    /** If a resting position hasn't been set, calling it will return the default position. */
    @Test
    public void testGetRestingPosition_returnsDefaultPosition() {
        new WindowManagerConfig().setUpConfig();
        mPositioner.update();

        PointF restingPosition = mPositioner.getRestingPosition();
        PointF defaultPosition = mPositioner.getDefaultStartPosition();

        assertThat(restingPosition).isEqualTo(defaultPosition);
    }

    /** If a resting position has been set, it'll return that instead of the default position. */
    @Test
    public void testGetRestingPosition_returnsRestingPosition() {
        new WindowManagerConfig().setUpConfig();
        mPositioner.update();

        PointF restingPosition = new PointF(100, 100);
        mPositioner.setRestingPosition(restingPosition);

        assertThat(mPositioner.getRestingPosition()).isEqualTo(restingPosition);
    }

    /** Test that the default resting position on phone is in upper left. */
    @Test
    public void testGetRestingPosition_bubble_onPhone() {
        new WindowManagerConfig().setUpConfig();
        mPositioner.update();

        RectF allowableStackRegion =
                mPositioner.getAllowableStackPositionRegion(1 /* bubbleCount */);
        PointF restingPosition = mPositioner.getRestingPosition();

        assertThat(restingPosition.x).isEqualTo(allowableStackRegion.left);
        assertThat(restingPosition.y).isEqualTo(getDefaultYPosition());
    }

    @Test
    public void testGetRestingPosition_bubble_onPhone_RTL() {
        new WindowManagerConfig().setLayoutDirection(LAYOUT_DIRECTION_RTL).setUpConfig();
        mPositioner.update();

        RectF allowableStackRegion =
                mPositioner.getAllowableStackPositionRegion(1 /* bubbleCount */);
        PointF restingPosition = mPositioner.getRestingPosition();

        assertThat(restingPosition.x).isEqualTo(allowableStackRegion.right);
        assertThat(restingPosition.y).isEqualTo(getDefaultYPosition());
    }

    /** Test that the default resting position on tablet is middle left. */
    @Test
    public void testGetRestingPosition_chatBubble_onTablet() {
        new WindowManagerConfig().setLargeScreen().setUpConfig();
        mPositioner.update();

        RectF allowableStackRegion =
                mPositioner.getAllowableStackPositionRegion(1 /* bubbleCount */);
        PointF restingPosition = mPositioner.getRestingPosition();

        assertThat(restingPosition.x).isEqualTo(allowableStackRegion.left);
        assertThat(restingPosition.y).isEqualTo(getDefaultYPosition());
    }

    @Test
    public void testGetRestingPosition_chatBubble_onTablet_RTL() {
        new WindowManagerConfig().setLargeScreen().setLayoutDirection(
                LAYOUT_DIRECTION_RTL).setUpConfig();
        mPositioner.update();

        RectF allowableStackRegion =
                mPositioner.getAllowableStackPositionRegion(1 /* bubbleCount */);
        PointF restingPosition = mPositioner.getRestingPosition();

        assertThat(restingPosition.x).isEqualTo(allowableStackRegion.right);
        assertThat(restingPosition.y).isEqualTo(getDefaultYPosition());
    }

    /**
     * Calculates the Y position bubbles should be placed based on the config. Based on
     * the calculations in {@link BubblePositioner#getDefaultStartPosition()} and
     * {@link BubbleStackView.RelativeStackPosition}.
     */
    private float getDefaultYPosition() {
        final float desiredY = mContext.getResources().getDimensionPixelOffset(
                        R.dimen.bubble_stack_starting_offset_y);
        float offsetPercent = desiredY / mPositioner.getAvailableRect().height();
        offsetPercent = Math.max(0f, Math.min(1f, offsetPercent));
        final RectF allowableStackRegion =
                mPositioner.getAllowableStackPositionRegion(1 /* bubbleCount */);
        return allowableStackRegion.top + allowableStackRegion.height() * offsetPercent;
    }

    /**
     * Sets up window manager to return config values based on what you need for the test.
     * By default it sets up a portrait phone without any insets.
     */
    private class WindowManagerConfig {
        private Rect mScreenBounds = new Rect(0, 0, 1000, 2000);
        private boolean mIsLargeScreen = false;
        private int mOrientation = ORIENTATION_PORTRAIT;
        private int mLayoutDirection = LAYOUT_DIRECTION_LTR;
        private Insets mInsets = Insets.of(0, 0, 0, 0);

        public WindowManagerConfig setScreenBounds(Rect screenBounds) {
            mScreenBounds = screenBounds;
            return this;
        }

        public WindowManagerConfig setLargeScreen() {
            mIsLargeScreen = true;
            return this;
        }

        public WindowManagerConfig setOrientation(int orientation) {
            mOrientation = orientation;
            return this;
        }

        public WindowManagerConfig setLayoutDirection(int layoutDirection) {
            mLayoutDirection = layoutDirection;
            return this;
        }

        public WindowManagerConfig setInsets(Insets insets) {
            mInsets = insets;
            return this;
        }

        public void setUpConfig() {
            mConfiguration.smallestScreenWidthDp = mIsLargeScreen
                    ? MIN_WIDTH_FOR_TABLET
                    : MIN_WIDTH_FOR_TABLET - 1;
            mConfiguration.orientation = mOrientation;

            when(mConfiguration.getLayoutDirection()).thenReturn(mLayoutDirection);
            WindowInsets windowInsets = mock(WindowInsets.class);
            when(windowInsets.getInsetsIgnoringVisibility(anyInt())).thenReturn(mInsets);
            when(mWindowMetrics.getWindowInsets()).thenReturn(windowInsets);
            when(mWindowMetrics.getBounds()).thenReturn(mScreenBounds);
            when(mWindowManager.getCurrentWindowMetrics()).thenReturn(mWindowMetrics);
        }
    }
}