[fenix] For https://github.com/mozilla-mobile/fenix/issues/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.pull/600/head
parent
2b5fbc7124
commit
515ae82e9b
@ -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