mirror of
https://github.com/LawnchairLauncher/lawnchair.git
synced 2026-03-01 08:16:49 +00:00
671 lines
27 KiB
Kotlin
671 lines
27 KiB
Kotlin
/*
|
|
* 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 android.content.Context
|
|
import android.content.Intent
|
|
import android.content.pm.ShortcutInfo
|
|
import android.content.res.Resources
|
|
import android.graphics.Insets
|
|
import android.graphics.PointF
|
|
import android.graphics.Rect
|
|
import android.os.UserHandle
|
|
import android.view.WindowManager
|
|
import androidx.test.core.app.ApplicationProvider
|
|
import androidx.test.ext.junit.runners.AndroidJUnit4
|
|
import androidx.test.filters.SmallTest
|
|
import com.android.internal.protolog.common.ProtoLog
|
|
import com.android.wm.shell.R
|
|
import com.android.wm.shell.bubbles.BubblePositioner.MAX_HEIGHT
|
|
import com.android.wm.shell.common.bubbles.BubbleBarLocation
|
|
import com.google.common.truth.Truth.assertThat
|
|
import com.google.common.util.concurrent.MoreExecutors.directExecutor
|
|
import org.junit.Before
|
|
import org.junit.Test
|
|
import org.junit.runner.RunWith
|
|
|
|
/** Tests operations and the resulting state managed by [BubblePositioner]. */
|
|
@SmallTest
|
|
@RunWith(AndroidJUnit4::class)
|
|
class BubblePositionerTest {
|
|
|
|
private lateinit var positioner: BubblePositioner
|
|
private val context = ApplicationProvider.getApplicationContext<Context>()
|
|
private val resources: Resources
|
|
get() = context.resources
|
|
|
|
private val defaultDeviceConfig =
|
|
DeviceConfig(
|
|
windowBounds = Rect(0, 0, 1000, 2000),
|
|
isLargeScreen = false,
|
|
isSmallTablet = false,
|
|
isLandscape = false,
|
|
isRtl = false,
|
|
insets = Insets.of(0, 0, 0, 0)
|
|
)
|
|
|
|
@Before
|
|
fun setUp() {
|
|
ProtoLog.REQUIRE_PROTOLOGTOOL = false
|
|
val windowManager = context.getSystemService(WindowManager::class.java)
|
|
positioner = BubblePositioner(context, windowManager)
|
|
}
|
|
|
|
@Test
|
|
fun testUpdate() {
|
|
val insets = Insets.of(10, 20, 5, 15)
|
|
val screenBounds = Rect(0, 0, 1000, 1200)
|
|
val availableRect = Rect(screenBounds)
|
|
availableRect.inset(insets)
|
|
positioner.update(defaultDeviceConfig.copy(insets = insets, windowBounds = screenBounds))
|
|
assertThat(positioner.availableRect).isEqualTo(availableRect)
|
|
assertThat(positioner.isLandscape).isFalse()
|
|
assertThat(positioner.isLargeScreen).isFalse()
|
|
assertThat(positioner.insets).isEqualTo(insets)
|
|
}
|
|
|
|
@Test
|
|
fun testShowBubblesVertically_phonePortrait() {
|
|
positioner.update(defaultDeviceConfig)
|
|
assertThat(positioner.showBubblesVertically()).isFalse()
|
|
}
|
|
|
|
@Test
|
|
fun testShowBubblesVertically_phoneLandscape() {
|
|
positioner.update(defaultDeviceConfig.copy(isLandscape = true))
|
|
assertThat(positioner.isLandscape).isTrue()
|
|
assertThat(positioner.showBubblesVertically()).isTrue()
|
|
}
|
|
|
|
@Test
|
|
fun testShowBubblesVertically_tablet() {
|
|
positioner.update(defaultDeviceConfig.copy(isLargeScreen = true))
|
|
assertThat(positioner.showBubblesVertically()).isTrue()
|
|
}
|
|
|
|
/** If a resting position hasn't been set, calling it will return the default position. */
|
|
@Test
|
|
fun testGetRestingPosition_returnsDefaultPosition() {
|
|
positioner.update(defaultDeviceConfig)
|
|
val restingPosition = positioner.getRestingPosition()
|
|
val defaultPosition = positioner.defaultStartPosition
|
|
assertThat(restingPosition).isEqualTo(defaultPosition)
|
|
}
|
|
|
|
/** If a resting position has been set, it'll return that instead of the default position. */
|
|
@Test
|
|
fun testGetRestingPosition_returnsRestingPosition() {
|
|
positioner.update(defaultDeviceConfig)
|
|
val restingPosition = PointF(100f, 100f)
|
|
positioner.restingPosition = restingPosition
|
|
assertThat(positioner.getRestingPosition()).isEqualTo(restingPosition)
|
|
}
|
|
|
|
/** Test that the default resting position on phone is in upper left. */
|
|
@Test
|
|
fun testGetRestingPosition_bubble_onPhone() {
|
|
positioner.update(defaultDeviceConfig)
|
|
val allowableStackRegion = positioner.getAllowableStackPositionRegion(1 /* bubbleCount */)
|
|
val restingPosition = positioner.getRestingPosition()
|
|
assertThat(restingPosition.x).isEqualTo(allowableStackRegion.left)
|
|
assertThat(restingPosition.y).isEqualTo(defaultYPosition)
|
|
}
|
|
|
|
@Test
|
|
fun testGetRestingPosition_bubble_onPhone_RTL() {
|
|
positioner.update(defaultDeviceConfig.copy(isRtl = true))
|
|
val allowableStackRegion = positioner.getAllowableStackPositionRegion(1 /* bubbleCount */)
|
|
val restingPosition = positioner.getRestingPosition()
|
|
assertThat(restingPosition.x).isEqualTo(allowableStackRegion.right)
|
|
assertThat(restingPosition.y).isEqualTo(defaultYPosition)
|
|
}
|
|
|
|
/** Test that the default resting position on tablet is middle left. */
|
|
@Test
|
|
fun testGetRestingPosition_chatBubble_onTablet() {
|
|
positioner.update(defaultDeviceConfig.copy(isLargeScreen = true))
|
|
val allowableStackRegion = positioner.getAllowableStackPositionRegion(1 /* bubbleCount */)
|
|
val restingPosition = positioner.getRestingPosition()
|
|
assertThat(restingPosition.x).isEqualTo(allowableStackRegion.left)
|
|
assertThat(restingPosition.y).isEqualTo(defaultYPosition)
|
|
}
|
|
|
|
@Test
|
|
fun testGetRestingPosition_chatBubble_onTablet_RTL() {
|
|
positioner.update(defaultDeviceConfig.copy(isLargeScreen = true, isRtl = true))
|
|
val allowableStackRegion = positioner.getAllowableStackPositionRegion(1 /* bubbleCount */)
|
|
val restingPosition = positioner.getRestingPosition()
|
|
assertThat(restingPosition.x).isEqualTo(allowableStackRegion.right)
|
|
assertThat(restingPosition.y).isEqualTo(defaultYPosition)
|
|
}
|
|
|
|
/** Test that the default resting position on tablet is middle right. */
|
|
@Test
|
|
fun testGetDefaultPosition_appBubble_onTablet() {
|
|
positioner.update(defaultDeviceConfig.copy(isLargeScreen = true))
|
|
val allowableStackRegion = positioner.getAllowableStackPositionRegion(1 /* bubbleCount */)
|
|
val startPosition = positioner.getDefaultStartPosition(true /* isAppBubble */)
|
|
assertThat(startPosition.x).isEqualTo(allowableStackRegion.right)
|
|
assertThat(startPosition.y).isEqualTo(defaultYPosition)
|
|
}
|
|
|
|
@Test
|
|
fun testGetRestingPosition_appBubble_onTablet_RTL() {
|
|
positioner.update(defaultDeviceConfig.copy(isLargeScreen = true, isRtl = true))
|
|
val allowableStackRegion = positioner.getAllowableStackPositionRegion(1 /* bubbleCount */)
|
|
val startPosition = positioner.getDefaultStartPosition(true /* isAppBubble */)
|
|
assertThat(startPosition.x).isEqualTo(allowableStackRegion.left)
|
|
assertThat(startPosition.y).isEqualTo(defaultYPosition)
|
|
}
|
|
|
|
@Test
|
|
fun testGetRestingPosition_afterBoundsChange() {
|
|
positioner.update(
|
|
defaultDeviceConfig.copy(isLargeScreen = true, windowBounds = Rect(0, 0, 2000, 1600))
|
|
)
|
|
|
|
// Set the resting position to the right side
|
|
var allowableStackRegion = positioner.getAllowableStackPositionRegion(1 /* bubbleCount */)
|
|
val restingPosition = PointF(allowableStackRegion.right, allowableStackRegion.centerY())
|
|
positioner.restingPosition = restingPosition
|
|
|
|
// Now make the device smaller
|
|
positioner.update(
|
|
defaultDeviceConfig.copy(isLargeScreen = false, windowBounds = Rect(0, 0, 1000, 1600))
|
|
)
|
|
|
|
// Check the resting position is on the correct side
|
|
allowableStackRegion = positioner.getAllowableStackPositionRegion(1 /* bubbleCount */)
|
|
assertThat(positioner.restingPosition.x).isEqualTo(allowableStackRegion.right)
|
|
}
|
|
|
|
@Test
|
|
fun testHasUserModifiedDefaultPosition_false() {
|
|
positioner.update(defaultDeviceConfig.copy(isLargeScreen = true, isRtl = true))
|
|
assertThat(positioner.hasUserModifiedDefaultPosition()).isFalse()
|
|
positioner.restingPosition = positioner.defaultStartPosition
|
|
assertThat(positioner.hasUserModifiedDefaultPosition()).isFalse()
|
|
}
|
|
|
|
@Test
|
|
fun testHasUserModifiedDefaultPosition_true() {
|
|
positioner.update(defaultDeviceConfig.copy(isLargeScreen = true, isRtl = true))
|
|
assertThat(positioner.hasUserModifiedDefaultPosition()).isFalse()
|
|
positioner.restingPosition = PointF(0f, 100f)
|
|
assertThat(positioner.hasUserModifiedDefaultPosition()).isTrue()
|
|
}
|
|
|
|
@Test
|
|
fun testBubbleBarExpandedViewHeightAndWidth() {
|
|
val deviceConfig =
|
|
defaultDeviceConfig.copy(
|
|
// portrait orientation
|
|
isLandscape = false,
|
|
isLargeScreen = true,
|
|
insets = Insets.of(10, 20, 5, 15),
|
|
windowBounds = Rect(0, 0, 1800, 2600)
|
|
)
|
|
|
|
positioner.setShowingInBubbleBar(true)
|
|
positioner.update(deviceConfig)
|
|
positioner.bubbleBarTopOnScreen = 2500
|
|
|
|
val spaceBetweenTopInsetAndBubbleBarInLandscape = 1680
|
|
val expandedViewVerticalSpacing =
|
|
resources.getDimensionPixelSize(R.dimen.bubble_expanded_view_padding)
|
|
val expectedHeight =
|
|
spaceBetweenTopInsetAndBubbleBarInLandscape - 2 * expandedViewVerticalSpacing
|
|
val expectedWidth = resources.getDimensionPixelSize(R.dimen.bubble_bar_expanded_view_width)
|
|
|
|
assertThat(positioner.getExpandedViewWidthForBubbleBar(false)).isEqualTo(expectedWidth)
|
|
assertThat(positioner.getExpandedViewHeightForBubbleBar(false)).isEqualTo(expectedHeight)
|
|
}
|
|
|
|
@Test
|
|
fun testBubbleBarExpandedViewHeightAndWidth_screenWidthTooSmall() {
|
|
val screenWidth = 300
|
|
val deviceConfig =
|
|
defaultDeviceConfig.copy(
|
|
// portrait orientation
|
|
isLandscape = false,
|
|
isLargeScreen = true,
|
|
insets = Insets.of(10, 20, 5, 15),
|
|
windowBounds = Rect(0, 0, screenWidth, 2600)
|
|
)
|
|
positioner.setShowingInBubbleBar(true)
|
|
positioner.update(deviceConfig)
|
|
positioner.bubbleBarTopOnScreen = 2500
|
|
|
|
val spaceBetweenTopInsetAndBubbleBarInLandscape = 180
|
|
val expandedViewSpacing =
|
|
resources.getDimensionPixelSize(R.dimen.bubble_expanded_view_padding)
|
|
val expectedHeight = spaceBetweenTopInsetAndBubbleBarInLandscape - 2 * expandedViewSpacing
|
|
val expectedWidth = screenWidth - 15 /* horizontal insets */ - 2 * expandedViewSpacing
|
|
assertThat(positioner.getExpandedViewWidthForBubbleBar(false)).isEqualTo(expectedWidth)
|
|
assertThat(positioner.getExpandedViewHeightForBubbleBar(false)).isEqualTo(expectedHeight)
|
|
}
|
|
|
|
@Test
|
|
fun testGetExpandedViewHeight_max() {
|
|
val deviceConfig =
|
|
defaultDeviceConfig.copy(
|
|
isLargeScreen = true,
|
|
insets = Insets.of(10, 20, 5, 15),
|
|
windowBounds = Rect(0, 0, 1800, 2600)
|
|
)
|
|
positioner.update(deviceConfig)
|
|
val intent = Intent(Intent.ACTION_VIEW).setPackage(context.packageName)
|
|
val bubble = Bubble.createAppBubble(intent, UserHandle(1), null, directExecutor())
|
|
|
|
assertThat(positioner.getExpandedViewHeight(bubble)).isEqualTo(MAX_HEIGHT)
|
|
}
|
|
|
|
@Test
|
|
fun testGetExpandedViewHeight_customHeight_valid() {
|
|
val deviceConfig =
|
|
defaultDeviceConfig.copy(
|
|
isLargeScreen = true,
|
|
insets = Insets.of(10, 20, 5, 15),
|
|
windowBounds = Rect(0, 0, 1800, 2600)
|
|
)
|
|
positioner.update(deviceConfig)
|
|
val minHeight =
|
|
context.resources.getDimensionPixelSize(R.dimen.bubble_expanded_default_height)
|
|
val bubble =
|
|
Bubble(
|
|
"key",
|
|
ShortcutInfo.Builder(context, "id").build(),
|
|
minHeight + 100 /* desiredHeight */,
|
|
0 /* desiredHeightResId */,
|
|
"title",
|
|
0 /* taskId */,
|
|
null /* locus */,
|
|
true /* isDismissable */,
|
|
directExecutor()
|
|
) {}
|
|
|
|
// Ensure the height is the same as the desired value
|
|
assertThat(positioner.getExpandedViewHeight(bubble))
|
|
.isEqualTo(bubble.getDesiredHeight(context))
|
|
}
|
|
|
|
@Test
|
|
fun testGetExpandedViewHeight_customHeight_tooSmall() {
|
|
val deviceConfig =
|
|
defaultDeviceConfig.copy(
|
|
isLargeScreen = true,
|
|
insets = Insets.of(10, 20, 5, 15),
|
|
windowBounds = Rect(0, 0, 1800, 2600)
|
|
)
|
|
positioner.update(deviceConfig)
|
|
|
|
val bubble =
|
|
Bubble(
|
|
"key",
|
|
ShortcutInfo.Builder(context, "id").build(),
|
|
10 /* desiredHeight */,
|
|
0 /* desiredHeightResId */,
|
|
"title",
|
|
0 /* taskId */,
|
|
null /* locus */,
|
|
true /* isDismissable */,
|
|
directExecutor()
|
|
) {}
|
|
|
|
// Ensure the height is the same as the desired value
|
|
val minHeight =
|
|
context.resources.getDimensionPixelSize(R.dimen.bubble_expanded_default_height)
|
|
assertThat(positioner.getExpandedViewHeight(bubble)).isEqualTo(minHeight)
|
|
}
|
|
|
|
@Test
|
|
fun testGetMaxExpandedViewHeight_onLargeTablet() {
|
|
val deviceConfig =
|
|
defaultDeviceConfig.copy(
|
|
isLargeScreen = true,
|
|
insets = Insets.of(10, 20, 5, 15),
|
|
windowBounds = Rect(0, 0, 1800, 2600)
|
|
)
|
|
positioner.update(deviceConfig)
|
|
|
|
val manageButtonHeight =
|
|
context.resources.getDimensionPixelSize(R.dimen.bubble_manage_button_height)
|
|
val pointerWidth = context.resources.getDimensionPixelSize(R.dimen.bubble_pointer_width)
|
|
val expandedViewPadding =
|
|
context.resources.getDimensionPixelSize(R.dimen.bubble_expanded_view_padding)
|
|
val expectedHeight =
|
|
1800 - 2 * 20 - manageButtonHeight - pointerWidth - expandedViewPadding * 2
|
|
assertThat(positioner.getMaxExpandedViewHeight(false /* isOverflow */))
|
|
.isEqualTo(expectedHeight)
|
|
}
|
|
|
|
@Test
|
|
fun testAreBubblesBottomAligned_largeScreen_true() {
|
|
val deviceConfig =
|
|
defaultDeviceConfig.copy(
|
|
isLargeScreen = true,
|
|
insets = Insets.of(10, 20, 5, 15),
|
|
windowBounds = Rect(0, 0, 1800, 2600)
|
|
)
|
|
positioner.update(deviceConfig)
|
|
|
|
assertThat(positioner.areBubblesBottomAligned()).isTrue()
|
|
}
|
|
|
|
@Test
|
|
fun testAreBubblesBottomAligned_largeScreen_landscape_false() {
|
|
val deviceConfig =
|
|
defaultDeviceConfig.copy(
|
|
isLargeScreen = true,
|
|
isLandscape = true,
|
|
insets = Insets.of(10, 20, 5, 15),
|
|
windowBounds = Rect(0, 0, 1800, 2600)
|
|
)
|
|
positioner.update(deviceConfig)
|
|
|
|
assertThat(positioner.areBubblesBottomAligned()).isFalse()
|
|
}
|
|
|
|
@Test
|
|
fun testAreBubblesBottomAligned_smallTablet_false() {
|
|
val deviceConfig =
|
|
defaultDeviceConfig.copy(
|
|
isLargeScreen = true,
|
|
isSmallTablet = true,
|
|
insets = Insets.of(10, 20, 5, 15),
|
|
windowBounds = Rect(0, 0, 1800, 2600)
|
|
)
|
|
positioner.update(deviceConfig)
|
|
|
|
assertThat(positioner.areBubblesBottomAligned()).isFalse()
|
|
}
|
|
|
|
@Test
|
|
fun testAreBubblesBottomAligned_phone_false() {
|
|
val deviceConfig =
|
|
defaultDeviceConfig.copy(
|
|
insets = Insets.of(10, 20, 5, 15),
|
|
windowBounds = Rect(0, 0, 1800, 2600)
|
|
)
|
|
positioner.update(deviceConfig)
|
|
|
|
assertThat(positioner.areBubblesBottomAligned()).isFalse()
|
|
}
|
|
|
|
@Test
|
|
fun testExpandedViewY_phoneLandscape() {
|
|
val deviceConfig =
|
|
defaultDeviceConfig.copy(
|
|
isLandscape = true,
|
|
insets = Insets.of(10, 20, 5, 15),
|
|
windowBounds = Rect(0, 0, 1800, 2600)
|
|
)
|
|
positioner.update(deviceConfig)
|
|
|
|
val intent = Intent(Intent.ACTION_VIEW).setPackage(context.packageName)
|
|
val bubble = Bubble.createAppBubble(intent, UserHandle(1), null, directExecutor())
|
|
|
|
// This bubble will have max height so it'll always be top aligned
|
|
assertThat(positioner.getExpandedViewY(bubble, 0f /* bubblePosition */))
|
|
.isEqualTo(positioner.getExpandedViewYTopAligned())
|
|
}
|
|
|
|
@Test
|
|
fun testExpandedViewY_phonePortrait() {
|
|
val deviceConfig =
|
|
defaultDeviceConfig.copy(
|
|
insets = Insets.of(10, 20, 5, 15),
|
|
windowBounds = Rect(0, 0, 1800, 2600)
|
|
)
|
|
positioner.update(deviceConfig)
|
|
|
|
val intent = Intent(Intent.ACTION_VIEW).setPackage(context.packageName)
|
|
val bubble = Bubble.createAppBubble(intent, UserHandle(1), null, directExecutor())
|
|
|
|
// Always top aligned in phone portrait
|
|
assertThat(positioner.getExpandedViewY(bubble, 0f /* bubblePosition */))
|
|
.isEqualTo(positioner.getExpandedViewYTopAligned())
|
|
}
|
|
|
|
@Test
|
|
fun testExpandedViewY_smallTabletLandscape() {
|
|
val deviceConfig =
|
|
defaultDeviceConfig.copy(
|
|
isSmallTablet = true,
|
|
isLandscape = true,
|
|
insets = Insets.of(10, 20, 5, 15),
|
|
windowBounds = Rect(0, 0, 1800, 2600)
|
|
)
|
|
positioner.update(deviceConfig)
|
|
|
|
val intent = Intent(Intent.ACTION_VIEW).setPackage(context.packageName)
|
|
val bubble = Bubble.createAppBubble(intent, UserHandle(1), null, directExecutor())
|
|
|
|
// This bubble will have max height which is always top aligned on small tablets
|
|
assertThat(positioner.getExpandedViewY(bubble, 0f /* bubblePosition */))
|
|
.isEqualTo(positioner.getExpandedViewYTopAligned())
|
|
}
|
|
|
|
@Test
|
|
fun testExpandedViewY_smallTabletPortrait() {
|
|
val deviceConfig =
|
|
defaultDeviceConfig.copy(
|
|
isSmallTablet = true,
|
|
insets = Insets.of(10, 20, 5, 15),
|
|
windowBounds = Rect(0, 0, 1800, 2600)
|
|
)
|
|
positioner.update(deviceConfig)
|
|
|
|
val intent = Intent(Intent.ACTION_VIEW).setPackage(context.packageName)
|
|
val bubble = Bubble.createAppBubble(intent, UserHandle(1), null, directExecutor())
|
|
|
|
// This bubble will have max height which is always top aligned on small tablets
|
|
assertThat(positioner.getExpandedViewY(bubble, 0f /* bubblePosition */))
|
|
.isEqualTo(positioner.getExpandedViewYTopAligned())
|
|
}
|
|
|
|
@Test
|
|
fun testExpandedViewY_largeScreenLandscape() {
|
|
val deviceConfig =
|
|
defaultDeviceConfig.copy(
|
|
isLargeScreen = true,
|
|
isLandscape = true,
|
|
insets = Insets.of(10, 20, 5, 15),
|
|
windowBounds = Rect(0, 0, 1800, 2600)
|
|
)
|
|
positioner.update(deviceConfig)
|
|
|
|
val intent = Intent(Intent.ACTION_VIEW).setPackage(context.packageName)
|
|
val bubble = Bubble.createAppBubble(intent, UserHandle(1), null, directExecutor())
|
|
|
|
// This bubble will have max height which is always top aligned on landscape, large tablet
|
|
assertThat(positioner.getExpandedViewY(bubble, 0f /* bubblePosition */))
|
|
.isEqualTo(positioner.getExpandedViewYTopAligned())
|
|
}
|
|
|
|
@Test
|
|
fun testExpandedViewY_largeScreenPortrait() {
|
|
val deviceConfig =
|
|
defaultDeviceConfig.copy(
|
|
isLargeScreen = true,
|
|
insets = Insets.of(10, 20, 5, 15),
|
|
windowBounds = Rect(0, 0, 1800, 2600)
|
|
)
|
|
positioner.update(deviceConfig)
|
|
|
|
val intent = Intent(Intent.ACTION_VIEW).setPackage(context.packageName)
|
|
val bubble = Bubble.createAppBubble(intent, UserHandle(1), null, directExecutor())
|
|
|
|
val manageButtonHeight =
|
|
context.resources.getDimensionPixelSize(R.dimen.bubble_manage_button_height)
|
|
val manageButtonPlusMargin =
|
|
manageButtonHeight +
|
|
2 * context.resources.getDimensionPixelSize(R.dimen.bubble_manage_button_margin)
|
|
val pointerWidth = context.resources.getDimensionPixelSize(R.dimen.bubble_pointer_width)
|
|
|
|
val expectedExpandedViewY =
|
|
positioner.availableRect.bottom -
|
|
manageButtonPlusMargin -
|
|
positioner.getExpandedViewHeightForLargeScreen() -
|
|
pointerWidth
|
|
|
|
// Bubbles are bottom aligned on portrait, large tablet
|
|
assertThat(positioner.getExpandedViewY(bubble, 0f /* bubblePosition */))
|
|
.isEqualTo(expectedExpandedViewY)
|
|
}
|
|
|
|
@Test
|
|
fun testGetTaskViewContentWidth_onLeft() {
|
|
positioner.update(defaultDeviceConfig.copy(insets = Insets.of(100, 0, 200, 0)))
|
|
val taskViewWidth = positioner.getTaskViewContentWidth(true /* onLeft */)
|
|
val paddings =
|
|
positioner.getExpandedViewContainerPadding(true /* onLeft */, false /* isOverflow */)
|
|
assertThat(taskViewWidth)
|
|
.isEqualTo(positioner.screenRect.width() - paddings[0] - paddings[2])
|
|
}
|
|
|
|
@Test
|
|
fun testGetTaskViewContentWidth_onRight() {
|
|
positioner.update(defaultDeviceConfig.copy(insets = Insets.of(100, 0, 200, 0)))
|
|
val taskViewWidth = positioner.getTaskViewContentWidth(false /* onLeft */)
|
|
val paddings =
|
|
positioner.getExpandedViewContainerPadding(false /* onLeft */, false /* isOverflow */)
|
|
assertThat(taskViewWidth)
|
|
.isEqualTo(positioner.screenRect.width() - paddings[0] - paddings[2])
|
|
}
|
|
|
|
@Test
|
|
fun testIsBubbleBarOnLeft_defaultsToRight() {
|
|
positioner.bubbleBarLocation = BubbleBarLocation.DEFAULT
|
|
assertThat(positioner.isBubbleBarOnLeft).isFalse()
|
|
|
|
// Check that left and right return expected position
|
|
positioner.bubbleBarLocation = BubbleBarLocation.LEFT
|
|
assertThat(positioner.isBubbleBarOnLeft).isTrue()
|
|
positioner.bubbleBarLocation = BubbleBarLocation.RIGHT
|
|
assertThat(positioner.isBubbleBarOnLeft).isFalse()
|
|
}
|
|
|
|
@Test
|
|
fun testIsBubbleBarOnLeft_rtlEnabled_defaultsToLeft() {
|
|
positioner.update(defaultDeviceConfig.copy(isRtl = true))
|
|
|
|
positioner.bubbleBarLocation = BubbleBarLocation.DEFAULT
|
|
assertThat(positioner.isBubbleBarOnLeft).isTrue()
|
|
|
|
// Check that left and right return expected position
|
|
positioner.bubbleBarLocation = BubbleBarLocation.LEFT
|
|
assertThat(positioner.isBubbleBarOnLeft).isTrue()
|
|
positioner.bubbleBarLocation = BubbleBarLocation.RIGHT
|
|
assertThat(positioner.isBubbleBarOnLeft).isFalse()
|
|
}
|
|
|
|
@Test
|
|
fun testGetBubbleBarExpandedViewBounds_onLeft() {
|
|
testGetBubbleBarExpandedViewBounds(onLeft = true, isOverflow = false)
|
|
}
|
|
|
|
@Test
|
|
fun testGetBubbleBarExpandedViewBounds_onRight() {
|
|
testGetBubbleBarExpandedViewBounds(onLeft = false, isOverflow = false)
|
|
}
|
|
|
|
@Test
|
|
fun testGetBubbleBarExpandedViewBounds_isOverflow_onLeft() {
|
|
testGetBubbleBarExpandedViewBounds(onLeft = true, isOverflow = true)
|
|
}
|
|
|
|
@Test
|
|
fun testGetBubbleBarExpandedViewBounds_isOverflow_onRight() {
|
|
testGetBubbleBarExpandedViewBounds(onLeft = false, isOverflow = true)
|
|
}
|
|
|
|
private fun testGetBubbleBarExpandedViewBounds(onLeft: Boolean, isOverflow: Boolean) {
|
|
positioner.setShowingInBubbleBar(true)
|
|
val windowBounds = Rect(0, 0, 2000, 2600)
|
|
val insets = Insets.of(10, 20, 5, 15)
|
|
val deviceConfig =
|
|
defaultDeviceConfig.copy(
|
|
isLargeScreen = true,
|
|
isLandscape = true,
|
|
insets = insets,
|
|
windowBounds = windowBounds
|
|
)
|
|
positioner.update(deviceConfig)
|
|
|
|
val bubbleBarHeight = 100
|
|
positioner.bubbleBarTopOnScreen = windowBounds.bottom - insets.bottom - bubbleBarHeight
|
|
|
|
val expandedViewPadding =
|
|
context.resources.getDimensionPixelSize(R.dimen.bubble_expanded_view_padding)
|
|
|
|
val left: Int
|
|
val right: Int
|
|
if (onLeft) {
|
|
// Pin to the left, calculate right
|
|
left = deviceConfig.insets.left + expandedViewPadding
|
|
right = left + positioner.getExpandedViewWidthForBubbleBar(isOverflow)
|
|
} else {
|
|
// Pin to the right, calculate left
|
|
right =
|
|
deviceConfig.windowBounds.right - deviceConfig.insets.right - expandedViewPadding
|
|
left = right - positioner.getExpandedViewWidthForBubbleBar(isOverflow)
|
|
}
|
|
// Above the bubble bar
|
|
val bottom = positioner.bubbleBarTopOnScreen - expandedViewPadding
|
|
// Calculate right and top based on size
|
|
val top = bottom - positioner.getExpandedViewHeightForBubbleBar(isOverflow)
|
|
val expectedBounds = Rect(left, top, right, bottom)
|
|
|
|
val bounds = Rect()
|
|
positioner.getBubbleBarExpandedViewBounds(onLeft, isOverflow, bounds)
|
|
|
|
assertThat(bounds).isEqualTo(expectedBounds)
|
|
}
|
|
|
|
private val defaultYPosition: Float
|
|
/**
|
|
* Calculates the Y position bubbles should be placed based on the config. Based on the
|
|
* calculations in [BubblePositioner.getDefaultStartPosition] and
|
|
* [BubbleStackView.RelativeStackPosition].
|
|
*/
|
|
get() {
|
|
val isTablet = positioner.isLargeScreen
|
|
|
|
// On tablet the position is centered, on phone it is an offset from the top.
|
|
val desiredY =
|
|
if (isTablet) {
|
|
positioner.screenRect.height() / 2f - positioner.bubbleSize / 2f
|
|
} else {
|
|
context.resources
|
|
.getDimensionPixelOffset(R.dimen.bubble_stack_starting_offset_y)
|
|
.toFloat()
|
|
}
|
|
// Since we're visually centering the bubbles on tablet, use total screen height rather
|
|
// than the available height.
|
|
val height =
|
|
if (isTablet) {
|
|
positioner.screenRect.height()
|
|
} else {
|
|
positioner.availableRect.height()
|
|
}
|
|
val offsetPercent = (desiredY / height).coerceIn(0f, 1f)
|
|
val allowableStackRegion =
|
|
positioner.getAllowableStackPositionRegion(1 /* bubbleCount */)
|
|
return allowableStackRegion.top + allowableStackRegion.height() * offsetPercent
|
|
}
|
|
}
|