Merge "Create LetterboxAppearanceCalculator for SysUI" into tm-qpr-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
4c08e92742
@@ -24,6 +24,7 @@ import com.android.systemui.statusbar.phone.PhoneStatusBarViewController
|
||||
import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent
|
||||
import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent.CentralSurfacesScope
|
||||
import com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragment
|
||||
import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentComponent
|
||||
import com.android.systemui.statusbar.window.StatusBarWindowController
|
||||
import java.lang.IllegalStateException
|
||||
import javax.inject.Inject
|
||||
@@ -34,7 +35,8 @@ import javax.inject.Inject
|
||||
*/
|
||||
@CentralSurfacesScope
|
||||
class StatusBarInitializer @Inject constructor(
|
||||
private val windowController: StatusBarWindowController
|
||||
private val windowController: StatusBarWindowController,
|
||||
private val creationListeners: Set<@JvmSuppressWildcards OnStatusBarViewInitializedListener>,
|
||||
) {
|
||||
|
||||
var statusBarViewUpdatedListener: OnStatusBarViewUpdatedListener? = null
|
||||
@@ -56,6 +58,9 @@ class StatusBarInitializer @Inject constructor(
|
||||
statusBarFragmentComponent.phoneStatusBarViewController,
|
||||
statusBarFragmentComponent.phoneStatusBarTransitions
|
||||
)
|
||||
creationListeners.forEach { listener ->
|
||||
listener.onStatusBarViewInitialized(statusBarFragmentComponent)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onFragmentViewDestroyed(tag: String?, fragment: Fragment?) {
|
||||
@@ -69,6 +74,17 @@ class StatusBarInitializer @Inject constructor(
|
||||
.commit()
|
||||
}
|
||||
|
||||
interface OnStatusBarViewInitializedListener {
|
||||
|
||||
/**
|
||||
* The status bar view has been initialized.
|
||||
*
|
||||
* @param component Dagger component that is created when the status bar view is created.
|
||||
* Can be used to retrieve dependencies from that scope, including the status bar root view.
|
||||
*/
|
||||
fun onStatusBarViewInitialized(component: StatusBarFragmentComponent)
|
||||
}
|
||||
|
||||
interface OnStatusBarViewUpdatedListener {
|
||||
fun onStatusBarViewUpdated(
|
||||
statusBarView: PhoneStatusBarView,
|
||||
|
||||
@@ -0,0 +1,227 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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.statusbar.phone
|
||||
|
||||
import android.annotation.ColorInt
|
||||
import android.graphics.Color
|
||||
import android.graphics.Rect
|
||||
import android.view.InsetsFlags
|
||||
import android.view.ViewDebug
|
||||
import android.view.WindowInsetsController
|
||||
import android.view.WindowInsetsController.APPEARANCE_SEMI_TRANSPARENT_STATUS_BARS
|
||||
import android.view.WindowInsetsController.Appearance
|
||||
import com.android.internal.statusbar.LetterboxDetails
|
||||
import com.android.internal.util.ContrastColorUtil
|
||||
import com.android.internal.view.AppearanceRegion
|
||||
import com.android.systemui.dump.DumpManager
|
||||
import com.android.systemui.statusbar.core.StatusBarInitializer.OnStatusBarViewInitializedListener
|
||||
import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent
|
||||
import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent.CentralSurfacesScope
|
||||
import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentComponent
|
||||
import java.io.PrintWriter
|
||||
import java.util.Arrays
|
||||
import javax.inject.Inject
|
||||
|
||||
class LetterboxAppearance(
|
||||
@Appearance val appearance: Int,
|
||||
val appearanceRegions: Array<AppearanceRegion>
|
||||
)
|
||||
|
||||
/**
|
||||
* Responsible for calculating the [Appearance] and [AppearanceRegion] for the status bar when apps
|
||||
* are letterboxed.
|
||||
*/
|
||||
@CentralSurfacesScope
|
||||
class LetterboxAppearanceCalculator
|
||||
@Inject
|
||||
constructor(
|
||||
private val lightBarController: LightBarController,
|
||||
private val dumpManager: DumpManager,
|
||||
) : OnStatusBarViewInitializedListener, CentralSurfacesComponent.Startable {
|
||||
|
||||
private var statusBarBoundsProvider: StatusBarBoundsProvider? = null
|
||||
|
||||
override fun start() {
|
||||
dumpManager.registerDumpable(javaClass.simpleName) { printWriter, _ -> dump(printWriter) }
|
||||
}
|
||||
|
||||
override fun stop() {
|
||||
dumpManager.unregisterDumpable(javaClass.simpleName)
|
||||
}
|
||||
|
||||
private var lastAppearance: Int? = null
|
||||
private var lastAppearanceRegions: Array<AppearanceRegion>? = null
|
||||
private var lastLetterboxes: Array<LetterboxDetails>? = null
|
||||
private var lastLetterboxAppearance: LetterboxAppearance? = null
|
||||
|
||||
fun getLetterboxAppearance(
|
||||
@Appearance originalAppearance: Int,
|
||||
originalAppearanceRegions: Array<AppearanceRegion>,
|
||||
letterboxes: Array<LetterboxDetails>
|
||||
): LetterboxAppearance {
|
||||
lastAppearance = originalAppearance
|
||||
lastAppearanceRegions = originalAppearanceRegions
|
||||
lastLetterboxes = letterboxes
|
||||
return getLetterboxAppearanceInternal(
|
||||
letterboxes, originalAppearance, originalAppearanceRegions)
|
||||
.also { lastLetterboxAppearance = it }
|
||||
}
|
||||
|
||||
private fun getLetterboxAppearanceInternal(
|
||||
letterboxes: Array<LetterboxDetails>,
|
||||
originalAppearance: Int,
|
||||
originalAppearanceRegions: Array<AppearanceRegion>
|
||||
): LetterboxAppearance {
|
||||
if (isScrimNeeded(letterboxes)) {
|
||||
return originalAppearanceWithScrim(originalAppearance, originalAppearanceRegions)
|
||||
}
|
||||
val appearance = appearanceWithoutScrim(originalAppearance)
|
||||
val appearanceRegions = getAppearanceRegions(originalAppearanceRegions, letterboxes)
|
||||
return LetterboxAppearance(appearance, appearanceRegions.toTypedArray())
|
||||
}
|
||||
|
||||
private fun isScrimNeeded(letterboxes: Array<LetterboxDetails>): Boolean {
|
||||
if (isOuterLetterboxMultiColored()) {
|
||||
return true
|
||||
}
|
||||
return letterboxes.any { letterbox ->
|
||||
letterbox.letterboxInnerBounds.overlapsWith(getStartSideIconBounds()) ||
|
||||
letterbox.letterboxInnerBounds.overlapsWith(getEndSideIconsBounds())
|
||||
}
|
||||
}
|
||||
|
||||
private fun getAppearanceRegions(
|
||||
originalAppearanceRegions: Array<AppearanceRegion>,
|
||||
letterboxes: Array<LetterboxDetails>
|
||||
): List<AppearanceRegion> {
|
||||
return sanitizeAppearanceRegions(originalAppearanceRegions, letterboxes) +
|
||||
getAllOuterAppearanceRegions(letterboxes)
|
||||
}
|
||||
|
||||
private fun sanitizeAppearanceRegions(
|
||||
originalAppearanceRegions: Array<AppearanceRegion>,
|
||||
letterboxes: Array<LetterboxDetails>
|
||||
): List<AppearanceRegion> =
|
||||
originalAppearanceRegions.map { appearanceRegion ->
|
||||
val matchingLetterbox =
|
||||
letterboxes.find { it.letterboxFullBounds == appearanceRegion.bounds }
|
||||
if (matchingLetterbox == null) {
|
||||
appearanceRegion
|
||||
} else {
|
||||
// When WindowManager sends appearance regions for an app, it sends them for the
|
||||
// full bounds of its window.
|
||||
// Here we want the bounds to be only for the inner bounds of the letterboxed app.
|
||||
AppearanceRegion(
|
||||
appearanceRegion.appearance, matchingLetterbox.letterboxInnerBounds)
|
||||
}
|
||||
}
|
||||
|
||||
private fun originalAppearanceWithScrim(
|
||||
@Appearance originalAppearance: Int,
|
||||
originalAppearanceRegions: Array<AppearanceRegion>
|
||||
): LetterboxAppearance {
|
||||
return LetterboxAppearance(
|
||||
originalAppearance or APPEARANCE_SEMI_TRANSPARENT_STATUS_BARS,
|
||||
originalAppearanceRegions)
|
||||
}
|
||||
|
||||
@Appearance
|
||||
private fun appearanceWithoutScrim(@Appearance originalAppearance: Int): Int =
|
||||
originalAppearance and APPEARANCE_SEMI_TRANSPARENT_STATUS_BARS.inv()
|
||||
|
||||
private fun getAllOuterAppearanceRegions(
|
||||
letterboxes: Array<LetterboxDetails>
|
||||
): List<AppearanceRegion> = letterboxes.map(this::getOuterAppearanceRegions).flatten()
|
||||
|
||||
private fun getOuterAppearanceRegions(
|
||||
letterboxDetails: LetterboxDetails
|
||||
): List<AppearanceRegion> {
|
||||
@Appearance val outerAppearance = getOuterAppearance()
|
||||
return getVisibleOuterBounds(letterboxDetails).map { bounds ->
|
||||
AppearanceRegion(outerAppearance, bounds)
|
||||
}
|
||||
}
|
||||
|
||||
private fun getVisibleOuterBounds(letterboxDetails: LetterboxDetails): List<Rect> {
|
||||
val inner = letterboxDetails.letterboxInnerBounds
|
||||
val outer = letterboxDetails.letterboxFullBounds
|
||||
val top = Rect(outer.left, outer.top, outer.right, inner.top)
|
||||
val left = Rect(outer.left, outer.top, inner.left, outer.bottom)
|
||||
val right = Rect(inner.right, outer.top, outer.right, outer.bottom)
|
||||
val bottom = Rect(outer.left, inner.bottom, outer.right, outer.bottom)
|
||||
return listOf(left, top, right, bottom).filter { !it.isEmpty }
|
||||
}
|
||||
|
||||
@Appearance
|
||||
private fun getOuterAppearance(): Int {
|
||||
val backgroundColor = outerLetterboxBackgroundColor()
|
||||
val darkAppearanceContrast =
|
||||
ContrastColorUtil.calculateContrast(
|
||||
lightBarController.darkAppearanceIconColor, backgroundColor)
|
||||
val lightAppearanceContrast =
|
||||
ContrastColorUtil.calculateContrast(
|
||||
lightBarController.lightAppearanceIconColor, backgroundColor)
|
||||
return if (lightAppearanceContrast > darkAppearanceContrast) {
|
||||
WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS
|
||||
} else {
|
||||
0 // APPEARANCE_DEFAULT
|
||||
}
|
||||
}
|
||||
|
||||
@ColorInt
|
||||
private fun outerLetterboxBackgroundColor(): Int {
|
||||
// TODO(b/238607453): retrieve this information from WindowManager.
|
||||
return Color.BLACK
|
||||
}
|
||||
|
||||
private fun isOuterLetterboxMultiColored(): Boolean {
|
||||
// TODO(b/238607453): retrieve this information from WindowManager.
|
||||
return false
|
||||
}
|
||||
|
||||
private fun getEndSideIconsBounds(): Rect {
|
||||
return statusBarBoundsProvider?.visibleEndSideBounds ?: Rect()
|
||||
}
|
||||
|
||||
private fun getStartSideIconBounds(): Rect {
|
||||
return statusBarBoundsProvider?.visibleStartSideBounds ?: Rect()
|
||||
}
|
||||
|
||||
override fun onStatusBarViewInitialized(component: StatusBarFragmentComponent) {
|
||||
statusBarBoundsProvider = component.boundsProvider
|
||||
}
|
||||
|
||||
private fun Rect.overlapsWith(other: Rect): Boolean {
|
||||
if (this.contains(other) || other.contains(this)) {
|
||||
return false
|
||||
}
|
||||
return this.intersect(other)
|
||||
}
|
||||
|
||||
private fun dump(printWriter: PrintWriter) {
|
||||
printWriter.println(
|
||||
"""
|
||||
lastAppearance: ${lastAppearance?.toAppearanceString()}
|
||||
lastAppearanceRegion: ${Arrays.toString(lastAppearanceRegions)},
|
||||
lastLetterboxes: ${Arrays.toString(lastLetterboxes)},
|
||||
lastLetterboxAppearance: $lastLetterboxAppearance
|
||||
""".trimIndent())
|
||||
}
|
||||
}
|
||||
|
||||
private fun Int.toAppearanceString(): String =
|
||||
ViewDebug.flagsToString(InsetsFlags::class.java, "appearance", this)
|
||||
@@ -23,8 +23,8 @@ import static android.view.WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS;
|
||||
import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT_TRANSPARENT;
|
||||
import static com.android.systemui.statusbar.phone.BarTransitions.MODE_TRANSPARENT;
|
||||
|
||||
import android.annotation.ColorInt;
|
||||
import android.content.Context;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Rect;
|
||||
import android.view.InsetsFlags;
|
||||
import android.view.ViewDebug;
|
||||
@@ -63,7 +63,8 @@ public class LightBarController implements BatteryController.BatteryStateChangeC
|
||||
private int mStatusBarMode;
|
||||
private int mNavigationBarMode;
|
||||
private int mNavigationMode;
|
||||
private final Color mDarkModeColor;
|
||||
private final int mDarkIconColor;
|
||||
private final int mLightIconColor;
|
||||
|
||||
/**
|
||||
* Whether the navigation bar should be light factoring in already how much alpha the scrim has
|
||||
@@ -94,7 +95,8 @@ public class LightBarController implements BatteryController.BatteryStateChangeC
|
||||
BatteryController batteryController,
|
||||
NavigationModeController navModeController,
|
||||
DumpManager dumpManager) {
|
||||
mDarkModeColor = Color.valueOf(ctx.getColor(R.color.dark_mode_icon_color_single_tone));
|
||||
mDarkIconColor = ctx.getColor(R.color.dark_mode_icon_color_single_tone);
|
||||
mLightIconColor = ctx.getColor(R.color.light_mode_icon_color_single_tone);
|
||||
mStatusBarIconController = (SysuiDarkIconDispatcher) darkIconDispatcher;
|
||||
mBatteryController = batteryController;
|
||||
mBatteryController.addCallback(this);
|
||||
@@ -107,6 +109,16 @@ public class LightBarController implements BatteryController.BatteryStateChangeC
|
||||
}
|
||||
}
|
||||
|
||||
@ColorInt
|
||||
int getLightAppearanceIconColor() {
|
||||
return mDarkIconColor;
|
||||
}
|
||||
|
||||
@ColorInt
|
||||
int getDarkAppearanceIconColor() {
|
||||
return mLightIconColor;
|
||||
}
|
||||
|
||||
public void setNavigationBar(LightBarTransitionsController navigationBar) {
|
||||
mNavigationBarController = navigationBar;
|
||||
updateNavigation();
|
||||
|
||||
@@ -16,13 +16,22 @@
|
||||
|
||||
package com.android.systemui.statusbar.phone.dagger;
|
||||
|
||||
import com.android.systemui.statusbar.phone.LetterboxAppearanceCalculator;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import dagger.Binds;
|
||||
import dagger.Module;
|
||||
import dagger.multibindings.IntoSet;
|
||||
import dagger.multibindings.Multibinds;
|
||||
|
||||
@Module
|
||||
interface CentralSurfacesStartableModule {
|
||||
@Multibinds
|
||||
Set<CentralSurfacesComponent.Startable> multibindStartables();
|
||||
|
||||
@Binds
|
||||
@IntoSet
|
||||
CentralSurfacesComponent.Startable letterboxAppearanceCalculator(
|
||||
LetterboxAppearanceCalculator letterboxAppearanceCalculator);
|
||||
}
|
||||
|
||||
@@ -46,10 +46,12 @@ import com.android.systemui.statusbar.NotificationShelf;
|
||||
import com.android.systemui.statusbar.NotificationShelfController;
|
||||
import com.android.systemui.statusbar.OperatorNameViewController;
|
||||
import com.android.systemui.statusbar.connectivity.NetworkController;
|
||||
import com.android.systemui.statusbar.core.StatusBarInitializer.OnStatusBarViewInitializedListener;
|
||||
import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler;
|
||||
import com.android.systemui.statusbar.notification.row.dagger.NotificationShelfComponent;
|
||||
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
|
||||
import com.android.systemui.statusbar.phone.KeyguardBottomAreaView;
|
||||
import com.android.systemui.statusbar.phone.LetterboxAppearanceCalculator;
|
||||
import com.android.systemui.statusbar.phone.NotificationIconAreaController;
|
||||
import com.android.systemui.statusbar.phone.StatusBarHideIconsForBouncerManager;
|
||||
import com.android.systemui.statusbar.phone.StatusBarIconController;
|
||||
@@ -72,8 +74,10 @@ import java.util.concurrent.Executor;
|
||||
|
||||
import javax.inject.Named;
|
||||
|
||||
import dagger.Binds;
|
||||
import dagger.Module;
|
||||
import dagger.Provides;
|
||||
import dagger.multibindings.IntoSet;
|
||||
|
||||
@Module(subcomponents = StatusBarFragmentComponent.class)
|
||||
public abstract class StatusBarViewModule {
|
||||
@@ -248,6 +252,12 @@ public abstract class StatusBarViewModule {
|
||||
return notificationShadeWindowView.findViewById(R.id.notification_container_parent);
|
||||
}
|
||||
|
||||
@Binds
|
||||
@IntoSet
|
||||
abstract OnStatusBarViewInitializedListener statusBarInitializedListener(
|
||||
LetterboxAppearanceCalculator letterboxAppearanceCalculator
|
||||
);
|
||||
|
||||
/**
|
||||
* Creates a new {@link CollapsedStatusBarFragment}.
|
||||
*
|
||||
|
||||
@@ -0,0 +1,205 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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.statusbar.phone
|
||||
|
||||
import android.graphics.Rect
|
||||
import android.testing.AndroidTestingRunner
|
||||
import android.view.WindowInsetsController
|
||||
import android.view.WindowInsetsController.APPEARANCE_SEMI_TRANSPARENT_STATUS_BARS
|
||||
import androidx.test.filters.SmallTest
|
||||
import com.android.internal.statusbar.LetterboxDetails
|
||||
import com.android.internal.view.AppearanceRegion
|
||||
import com.android.systemui.SysuiTestCase
|
||||
import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentComponent
|
||||
import com.google.common.truth.Expect
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mockito.Mock
|
||||
import org.mockito.Mockito.`when` as whenever
|
||||
import com.android.systemui.dump.DumpManager
|
||||
import org.mockito.MockitoAnnotations
|
||||
|
||||
@RunWith(AndroidTestingRunner::class)
|
||||
@SmallTest
|
||||
class LetterboxAppearanceCalculatorTest : SysuiTestCase() {
|
||||
|
||||
companion object {
|
||||
private const val DEFAULT_APPEARANCE = 0
|
||||
private const val TEST_APPEARANCE = WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS
|
||||
private val TEST_APPEARANCE_REGION_BOUNDS = Rect(0, 0, 20, 100)
|
||||
private val TEST_APPEARANCE_REGION =
|
||||
AppearanceRegion(TEST_APPEARANCE, TEST_APPEARANCE_REGION_BOUNDS)
|
||||
private val TEST_APPEARANCE_REGIONS = arrayOf(TEST_APPEARANCE_REGION)
|
||||
private val TEST_WINDOW_BOUNDS = Rect(0, 0, 500, 500)
|
||||
}
|
||||
|
||||
@get:Rule var expect = Expect.create()
|
||||
|
||||
@Mock private lateinit var lightBarController: LightBarController
|
||||
@Mock private lateinit var statusBarBoundsProvider: StatusBarBoundsProvider
|
||||
@Mock private lateinit var statusBarFragmentComponent: StatusBarFragmentComponent
|
||||
@Mock private lateinit var dumpManager: DumpManager
|
||||
|
||||
private lateinit var calculator: LetterboxAppearanceCalculator
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
MockitoAnnotations.initMocks(this)
|
||||
whenever(statusBarFragmentComponent.boundsProvider).thenReturn(statusBarBoundsProvider)
|
||||
calculator = LetterboxAppearanceCalculator(lightBarController, dumpManager)
|
||||
calculator.onStatusBarViewInitialized(statusBarFragmentComponent)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun getLetterboxAppearance_overlapStartSide_returnsOriginalWithScrim() {
|
||||
whenever(statusBarBoundsProvider.visibleStartSideBounds).thenReturn(Rect(0, 0, 100, 100))
|
||||
whenever(statusBarBoundsProvider.visibleEndSideBounds).thenReturn(Rect(200, 0, 300, 100))
|
||||
val letterbox = letterboxWithInnerBounds(Rect(50, 50, 150, 150))
|
||||
|
||||
val letterboxAppearance =
|
||||
calculator.getLetterboxAppearance(
|
||||
TEST_APPEARANCE, TEST_APPEARANCE_REGIONS, arrayOf(letterbox))
|
||||
|
||||
expect
|
||||
.that(letterboxAppearance.appearance)
|
||||
.isEqualTo(TEST_APPEARANCE or APPEARANCE_SEMI_TRANSPARENT_STATUS_BARS)
|
||||
expect.that(letterboxAppearance.appearanceRegions).isEqualTo(TEST_APPEARANCE_REGIONS)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun getLetterboxAppearance_overlapEndSide_returnsOriginalWithScrim() {
|
||||
whenever(statusBarBoundsProvider.visibleStartSideBounds).thenReturn(Rect(0, 0, 100, 100))
|
||||
whenever(statusBarBoundsProvider.visibleEndSideBounds).thenReturn(Rect(200, 0, 300, 100))
|
||||
val letterbox = letterboxWithInnerBounds(Rect(150, 50, 250, 150))
|
||||
|
||||
val letterboxAppearance =
|
||||
calculator.getLetterboxAppearance(
|
||||
TEST_APPEARANCE, TEST_APPEARANCE_REGIONS, arrayOf(letterbox))
|
||||
|
||||
expect
|
||||
.that(letterboxAppearance.appearance)
|
||||
.isEqualTo(TEST_APPEARANCE or APPEARANCE_SEMI_TRANSPARENT_STATUS_BARS)
|
||||
expect.that(letterboxAppearance.appearanceRegions).isEqualTo(TEST_APPEARANCE_REGIONS)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun getLetterboxAppearance_noOverlap_returnsAppearanceWithoutScrim() {
|
||||
whenever(statusBarBoundsProvider.visibleStartSideBounds).thenReturn(Rect(0, 0, 100, 100))
|
||||
whenever(statusBarBoundsProvider.visibleEndSideBounds).thenReturn(Rect(200, 0, 300, 100))
|
||||
val letterbox = letterboxWithInnerBounds(Rect(101, 0, 199, 100))
|
||||
|
||||
val letterboxAppearance =
|
||||
calculator.getLetterboxAppearance(
|
||||
TEST_APPEARANCE, TEST_APPEARANCE_REGIONS, arrayOf(letterbox))
|
||||
|
||||
assertThat(letterboxAppearance.appearance).isEqualTo(TEST_APPEARANCE)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun getLetterboxAppearance_letterboxContainsStartSide_returnsAppearanceWithoutScrim() {
|
||||
whenever(statusBarBoundsProvider.visibleStartSideBounds).thenReturn(Rect(0, 0, 100, 100))
|
||||
whenever(statusBarBoundsProvider.visibleEndSideBounds).thenReturn(Rect(200, 0, 300, 100))
|
||||
val letterbox = letterboxWithInnerBounds(Rect(0, 0, 101, 101))
|
||||
|
||||
val letterboxAppearance =
|
||||
calculator.getLetterboxAppearance(
|
||||
TEST_APPEARANCE, TEST_APPEARANCE_REGIONS, arrayOf(letterbox))
|
||||
|
||||
assertThat(letterboxAppearance.appearance).isEqualTo(TEST_APPEARANCE)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun getLetterboxAppearance_letterboxContainsEndSide_returnsAppearanceWithoutScrim() {
|
||||
whenever(statusBarBoundsProvider.visibleStartSideBounds).thenReturn(Rect(0, 0, 100, 100))
|
||||
whenever(statusBarBoundsProvider.visibleEndSideBounds).thenReturn(Rect(200, 0, 300, 100))
|
||||
val letterbox = letterboxWithInnerBounds(Rect(199, 0, 301, 101))
|
||||
|
||||
val letterboxAppearance =
|
||||
calculator.getLetterboxAppearance(
|
||||
TEST_APPEARANCE, TEST_APPEARANCE_REGIONS, arrayOf(letterbox))
|
||||
|
||||
assertThat(letterboxAppearance.appearance).isEqualTo(TEST_APPEARANCE)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun getLetterboxAppearance_letterboxContainsEntireStatusBar_returnsAppearanceWithoutScrim() {
|
||||
whenever(statusBarBoundsProvider.visibleStartSideBounds).thenReturn(Rect(0, 0, 100, 100))
|
||||
whenever(statusBarBoundsProvider.visibleEndSideBounds).thenReturn(Rect(200, 0, 300, 100))
|
||||
val letterbox = letterboxWithInnerBounds(Rect(0, 0, 300, 100))
|
||||
|
||||
val letterboxAppearance =
|
||||
calculator.getLetterboxAppearance(
|
||||
TEST_APPEARANCE, TEST_APPEARANCE_REGIONS, arrayOf(letterbox))
|
||||
|
||||
assertThat(letterboxAppearance.appearance).isEqualTo(TEST_APPEARANCE)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun getLetterboxAppearance_returnsAdaptedAppearanceRegions_basedOnLetterboxInnerBounds() {
|
||||
whenever(statusBarBoundsProvider.visibleStartSideBounds).thenReturn(Rect(0, 0, 0, 0))
|
||||
whenever(statusBarBoundsProvider.visibleEndSideBounds).thenReturn(Rect(0, 0, 0, 0))
|
||||
val letterbox = letterboxWithInnerBounds(Rect(150, 0, 300, 800))
|
||||
val letterboxRegion = TEST_APPEARANCE_REGION.copy(bounds = letterbox.letterboxFullBounds)
|
||||
|
||||
val letterboxAppearance =
|
||||
calculator.getLetterboxAppearance(
|
||||
TEST_APPEARANCE, arrayOf(letterboxRegion), arrayOf(letterbox))
|
||||
|
||||
val letterboxAdaptedRegion = letterboxRegion.copy(bounds = letterbox.letterboxInnerBounds)
|
||||
assertThat(letterboxAppearance.appearanceRegions.toList()).contains(letterboxAdaptedRegion)
|
||||
assertThat(letterboxAppearance.appearanceRegions.toList()).doesNotContain(letterboxRegion)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun getLetterboxAppearance_returnsDefaultAppearanceRegions_basedOnLetterboxOuterBounds() {
|
||||
whenever(statusBarBoundsProvider.visibleStartSideBounds).thenReturn(Rect(0, 0, 0, 0))
|
||||
whenever(statusBarBoundsProvider.visibleEndSideBounds).thenReturn(Rect(0, 0, 0, 0))
|
||||
val letterbox =
|
||||
letterboxWithBounds(
|
||||
innerBounds = Rect(left = 25, top = 0, right = 75, bottom = 100),
|
||||
fullBounds = Rect(left = 0, top = 0, right = 100, bottom = 100))
|
||||
val letterboxRegion = TEST_APPEARANCE_REGION.copy(bounds = letterbox.letterboxFullBounds)
|
||||
|
||||
val letterboxAppearance =
|
||||
calculator.getLetterboxAppearance(
|
||||
TEST_APPEARANCE, arrayOf(letterboxRegion), arrayOf(letterbox))
|
||||
|
||||
val outerRegions =
|
||||
listOf(
|
||||
AppearanceRegion(
|
||||
DEFAULT_APPEARANCE, Rect(left = 0, top = 0, right = 25, bottom = 100)),
|
||||
AppearanceRegion(
|
||||
DEFAULT_APPEARANCE, Rect(left = 75, top = 0, right = 100, bottom = 100)),
|
||||
)
|
||||
assertThat(letterboxAppearance.appearanceRegions.toList())
|
||||
.containsAtLeastElementsIn(outerRegions)
|
||||
}
|
||||
|
||||
private fun letterboxWithBounds(innerBounds: Rect, fullBounds: Rect) =
|
||||
LetterboxDetails(innerBounds, fullBounds, TEST_APPEARANCE)
|
||||
|
||||
private fun letterboxWithInnerBounds(innerBounds: Rect) =
|
||||
letterboxWithBounds(innerBounds, fullBounds = TEST_WINDOW_BOUNDS)
|
||||
}
|
||||
|
||||
private fun AppearanceRegion.copy(appearance: Int = this.appearance, bounds: Rect = this.bounds) =
|
||||
AppearanceRegion(appearance, bounds)
|
||||
|
||||
private fun Rect(left: Int, top: Int, right: Int, bottom: Int) = Rect(left, top, right, bottom)
|
||||
Reference in New Issue
Block a user