For #19135 - Refactor the behavior code to support more functionality later
Setup a new TabSheetBehaviorManager with all the dependencies it needs to set the initial tray's behavior. This same manager will later be called to update behavior's properties.upstream-sync
parent
c3001fff41
commit
d7544337b8
@ -0,0 +1,59 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
package org.mozilla.fenix.tabstray
|
||||
|
||||
import android.view.View
|
||||
import androidx.annotation.VisibleForTesting
|
||||
import androidx.constraintlayout.widget.ConstraintLayout
|
||||
import com.google.android.material.bottomsheet.BottomSheetBehavior
|
||||
import com.google.android.material.bottomsheet.BottomSheetBehavior.STATE_HIDDEN
|
||||
|
||||
/**
|
||||
* Helper class for updating how the tray looks and behaves depending on app state / internal tray state.
|
||||
*
|
||||
* @param behavior [BottomSheetBehavior] that will actually control the tray.
|
||||
* @param isLandscape whether the device is currently is portrait or landscape.
|
||||
* @param maxNumberOfTabs highest number of tabs in each tray page.
|
||||
* @param numberForExpandingTray limit depending on which the tray should be collapsed or expanded.
|
||||
* @param navigationInteractor [NavigationInteractor] used for tray updates / navigation.
|
||||
*/
|
||||
internal class TabSheetBehaviorManager(
|
||||
behavior: BottomSheetBehavior<ConstraintLayout>,
|
||||
isLandscape: Boolean,
|
||||
maxNumberOfTabs: Int,
|
||||
numberForExpandingTray: Int,
|
||||
navigationInteractor: NavigationInteractor
|
||||
) {
|
||||
init {
|
||||
behavior.addBottomSheetCallback(
|
||||
TraySheetBehaviorCallback(behavior, navigationInteractor)
|
||||
)
|
||||
|
||||
behavior.state = if (isLandscape || maxNumberOfTabs >= numberForExpandingTray) {
|
||||
BottomSheetBehavior.STATE_EXPANDED
|
||||
} else {
|
||||
BottomSheetBehavior.STATE_COLLAPSED
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
internal class TraySheetBehaviorCallback(
|
||||
@VisibleForTesting internal val behavior: BottomSheetBehavior<ConstraintLayout>,
|
||||
@VisibleForTesting internal val trayInteractor: NavigationInteractor
|
||||
) : BottomSheetBehavior.BottomSheetCallback() {
|
||||
|
||||
override fun onStateChanged(bottomSheet: View, newState: Int) {
|
||||
if (newState == STATE_HIDDEN) {
|
||||
trayInteractor.onTabTrayDismissed()
|
||||
} else if (newState == BottomSheetBehavior.STATE_HALF_EXPANDED) {
|
||||
// We only support expanded and collapsed states.
|
||||
// But why??
|
||||
behavior.state = STATE_HIDDEN
|
||||
}
|
||||
}
|
||||
|
||||
override fun onSlide(bottomSheet: View, slideOffset: Float) = Unit
|
||||
}
|
@ -1,44 +0,0 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
package org.mozilla.fenix.tabstray
|
||||
|
||||
import android.view.View
|
||||
import androidx.constraintlayout.widget.ConstraintLayout
|
||||
import com.google.android.material.bottomsheet.BottomSheetBehavior
|
||||
import com.google.android.material.bottomsheet.BottomSheetBehavior.STATE_HIDDEN
|
||||
|
||||
class TraySheetBehaviorCallback(
|
||||
private val behavior: BottomSheetBehavior<ConstraintLayout>,
|
||||
private val trayInteractor: NavigationInteractor
|
||||
) : BottomSheetBehavior.BottomSheetCallback() {
|
||||
|
||||
override fun onStateChanged(bottomSheet: View, newState: Int) {
|
||||
if (newState == STATE_HIDDEN) {
|
||||
trayInteractor.onTabTrayDismissed()
|
||||
} else if (newState == BottomSheetBehavior.STATE_HALF_EXPANDED) {
|
||||
// We only support expanded and collapsed states.
|
||||
// But why??
|
||||
behavior.state = STATE_HIDDEN
|
||||
}
|
||||
}
|
||||
|
||||
override fun onSlide(bottomSheet: View, slideOffset: Float) = Unit
|
||||
}
|
||||
|
||||
fun BottomSheetBehavior<ConstraintLayout>.setUpTrayBehavior(
|
||||
isLandscape: Boolean,
|
||||
maxNumberOfTabs: Int,
|
||||
numberForExpandingTray: Int,
|
||||
navigationInteractor: DefaultNavigationInteractor
|
||||
) {
|
||||
addBottomSheetCallback(
|
||||
TraySheetBehaviorCallback(this, navigationInteractor)
|
||||
)
|
||||
state = if (isLandscape || maxNumberOfTabs >= numberForExpandingTray) {
|
||||
BottomSheetBehavior.STATE_EXPANDED
|
||||
} else {
|
||||
BottomSheetBehavior.STATE_COLLAPSED
|
||||
}
|
||||
}
|
@ -0,0 +1,126 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
package org.mozilla.fenix.tabstray
|
||||
|
||||
import androidx.constraintlayout.widget.ConstraintLayout
|
||||
import com.google.android.material.bottomsheet.BottomSheetBehavior
|
||||
import com.google.android.material.bottomsheet.BottomSheetBehavior.STATE_COLLAPSED
|
||||
import com.google.android.material.bottomsheet.BottomSheetBehavior.STATE_DRAGGING
|
||||
import com.google.android.material.bottomsheet.BottomSheetBehavior.STATE_EXPANDED
|
||||
import com.google.android.material.bottomsheet.BottomSheetBehavior.STATE_HALF_EXPANDED
|
||||
import com.google.android.material.bottomsheet.BottomSheetBehavior.STATE_HIDDEN
|
||||
import com.google.android.material.bottomsheet.BottomSheetBehavior.STATE_SETTLING
|
||||
import io.mockk.Called
|
||||
import io.mockk.mockk
|
||||
import io.mockk.slot
|
||||
import io.mockk.verify
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertSame
|
||||
import org.junit.Test
|
||||
|
||||
class TabSheetBehaviorManagerTest {
|
||||
|
||||
@Test
|
||||
fun `WHEN state is hidden THEN invoke interactor`() {
|
||||
val interactor = mockk<NavigationInteractor>(relaxed = true)
|
||||
val callback = TraySheetBehaviorCallback(mockk(), interactor)
|
||||
|
||||
callback.onStateChanged(mockk(), STATE_HIDDEN)
|
||||
|
||||
verify { interactor.onTabTrayDismissed() }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `WHEN state is half-expanded THEN close the tray`() {
|
||||
val behavior = mockk<BottomSheetBehavior<ConstraintLayout>>(relaxed = true)
|
||||
val callback = TraySheetBehaviorCallback(behavior, mockk())
|
||||
|
||||
callback.onStateChanged(mockk(), STATE_HALF_EXPANDED)
|
||||
|
||||
verify { behavior.state = STATE_HIDDEN }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `WHEN other states are invoked THEN do nothing`() {
|
||||
val behavior = mockk<BottomSheetBehavior<ConstraintLayout>>(relaxed = true)
|
||||
val interactor = mockk<NavigationInteractor>(relaxed = true)
|
||||
val callback = TraySheetBehaviorCallback(behavior, interactor)
|
||||
|
||||
callback.onStateChanged(mockk(), STATE_COLLAPSED)
|
||||
callback.onStateChanged(mockk(), STATE_DRAGGING)
|
||||
callback.onStateChanged(mockk(), STATE_SETTLING)
|
||||
callback.onStateChanged(mockk(), STATE_EXPANDED)
|
||||
|
||||
verify { behavior wasNot Called }
|
||||
verify { interactor wasNot Called }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN a behavior WHEN TabSheetBehaviorManager is initialized THEN it sets a TraySheetBehaviorCallback on that behavior`() {
|
||||
val behavior: BottomSheetBehavior<ConstraintLayout> = mockk(relaxed = true)
|
||||
val navigationInteractor: NavigationInteractor = mockk()
|
||||
val callbackCaptor = slot<TraySheetBehaviorCallback>()
|
||||
|
||||
TabSheetBehaviorManager(behavior, true, 2, 2, navigationInteractor)
|
||||
|
||||
verify { behavior.addBottomSheetCallback(capture(callbackCaptor)) }
|
||||
assertSame(behavior, callbackCaptor.captured.behavior)
|
||||
assertSame(navigationInteractor, callbackCaptor.captured.trayInteractor)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN more tabs opened than the expanding limit and portrait orientation WHEN TabSheetBehaviorManager is initialized THEN the behavior is set as expanded`() {
|
||||
val behavior = BottomSheetBehavior<ConstraintLayout>()
|
||||
|
||||
TabSheetBehaviorManager(behavior, false, 5, 4, mockk())
|
||||
|
||||
assertEquals(STATE_EXPANDED, behavior.state)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN the number of tabs opened is exactly the expanding limit and portrait orientation WHEN TabSheetBehaviorManager is initialized THEN the behavior is set as expanded`() {
|
||||
val behavior = BottomSheetBehavior<ConstraintLayout>()
|
||||
|
||||
TabSheetBehaviorManager(behavior, false, 5, 5, mockk())
|
||||
|
||||
assertEquals(STATE_EXPANDED, behavior.state)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN fewer tabs opened than the expanding limit and portrait orientation WHEN TabSheetBehaviorManager is initialized THEN the behavior is set as collapsed`() {
|
||||
val behavior = BottomSheetBehavior<ConstraintLayout>()
|
||||
|
||||
TabSheetBehaviorManager(behavior, false, 4, 5, mockk())
|
||||
|
||||
assertEquals(STATE_COLLAPSED, behavior.state)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN more tabs opened than the expanding limit and landscape orientation WHEN TabSheetBehaviorManager is initialized THEN the behavior is set as expanded`() {
|
||||
val behavior = BottomSheetBehavior<ConstraintLayout>()
|
||||
|
||||
TabSheetBehaviorManager(behavior, true, 5, 4, mockk())
|
||||
|
||||
assertEquals(STATE_EXPANDED, behavior.state)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN the number of tabs opened is exactly the expanding limit and landscape orientation WHEN TabSheetBehaviorManager is initialized THEN the behavior is set as expanded`() {
|
||||
val behavior = BottomSheetBehavior<ConstraintLayout>()
|
||||
|
||||
TabSheetBehaviorManager(behavior, true, 5, 5, mockk())
|
||||
|
||||
assertEquals(STATE_EXPANDED, behavior.state)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN fewer tabs opened than the expanding limit and landscape orientation WHEN TabSheetBehaviorManager is initialized THEN the behavior is set as expanded`() {
|
||||
val behavior = BottomSheetBehavior<ConstraintLayout>()
|
||||
|
||||
TabSheetBehaviorManager(behavior, true, 4, 5, mockk())
|
||||
|
||||
assertEquals(STATE_EXPANDED, behavior.state)
|
||||
}
|
||||
}
|
@ -1,93 +0,0 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
package org.mozilla.fenix.tabstray
|
||||
|
||||
import androidx.constraintlayout.widget.ConstraintLayout
|
||||
import com.google.android.material.bottomsheet.BottomSheetBehavior
|
||||
import com.google.android.material.bottomsheet.BottomSheetBehavior.STATE_HALF_EXPANDED
|
||||
import com.google.android.material.bottomsheet.BottomSheetBehavior.STATE_HIDDEN
|
||||
import com.google.android.material.bottomsheet.BottomSheetBehavior.STATE_COLLAPSED
|
||||
import com.google.android.material.bottomsheet.BottomSheetBehavior.STATE_DRAGGING
|
||||
import com.google.android.material.bottomsheet.BottomSheetBehavior.STATE_SETTLING
|
||||
import com.google.android.material.bottomsheet.BottomSheetBehavior.STATE_EXPANDED
|
||||
import io.mockk.Called
|
||||
import io.mockk.mockk
|
||||
import io.mockk.spyk
|
||||
import io.mockk.verify
|
||||
import org.junit.Test
|
||||
|
||||
class TraySheetBehaviorCallbackTest {
|
||||
|
||||
@Test
|
||||
fun `WHEN state is hidden THEN invoke interactor`() {
|
||||
val interactor = mockk<NavigationInteractor>(relaxed = true)
|
||||
val callback = TraySheetBehaviorCallback(mockk(), interactor)
|
||||
|
||||
callback.onStateChanged(mockk(), STATE_HIDDEN)
|
||||
|
||||
verify { interactor.onTabTrayDismissed() }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `WHEN state is half-expanded THEN close the tray`() {
|
||||
val behavior = mockk<BottomSheetBehavior<ConstraintLayout>>(relaxed = true)
|
||||
val callback = TraySheetBehaviorCallback(behavior, mockk())
|
||||
|
||||
callback.onStateChanged(mockk(), STATE_HALF_EXPANDED)
|
||||
|
||||
verify { behavior.state = STATE_HIDDEN }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `WHEN other states are invoked THEN do nothing`() {
|
||||
val behavior = mockk<BottomSheetBehavior<ConstraintLayout>>(relaxed = true)
|
||||
val interactor = mockk<NavigationInteractor>(relaxed = true)
|
||||
val callback = TraySheetBehaviorCallback(behavior, interactor)
|
||||
|
||||
callback.onStateChanged(mockk(), STATE_COLLAPSED)
|
||||
callback.onStateChanged(mockk(), STATE_DRAGGING)
|
||||
callback.onStateChanged(mockk(), STATE_SETTLING)
|
||||
callback.onStateChanged(mockk(), STATE_EXPANDED)
|
||||
|
||||
verify { behavior wasNot Called }
|
||||
verify { interactor wasNot Called }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN portraitMode and 5 tabs WHEN setUpTrayBehavior THEN add TraySheetBehaviorCallback and STATE_COLLAPSED`() {
|
||||
// given
|
||||
val behavior = spyk(BottomSheetBehavior<ConstraintLayout>())
|
||||
val interactor = mockk<DefaultNavigationInteractor>(relaxed = true)
|
||||
|
||||
// when
|
||||
behavior.setUpTrayBehavior(
|
||||
isLandscape = false,
|
||||
maxNumberOfTabs = 5,
|
||||
numberForExpandingTray = TabsTrayFragment.EXPAND_AT_LIST_SIZE,
|
||||
navigationInteractor = interactor
|
||||
)
|
||||
|
||||
// then
|
||||
assert(behavior.state == STATE_EXPANDED)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN portraitMode and 2 tabs WHEN setUpTrayBehavior THEN add TraySheetBehaviorCallback and STATE_COLLAPSED`() {
|
||||
// given
|
||||
val behavior = spyk(BottomSheetBehavior<ConstraintLayout>())
|
||||
val interactor = mockk<DefaultNavigationInteractor>(relaxed = true)
|
||||
|
||||
// when
|
||||
behavior.setUpTrayBehavior(
|
||||
isLandscape = false,
|
||||
maxNumberOfTabs = 2,
|
||||
numberForExpandingTray = TabsTrayFragment.EXPAND_AT_LIST_SIZE,
|
||||
navigationInteractor = interactor
|
||||
)
|
||||
|
||||
// then
|
||||
assert(behavior.state == STATE_COLLAPSED)
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue