No issue: Refactor readerview to use browser-state
parent
c427b0a70b
commit
0f1bff7402
|
@ -29,6 +29,7 @@ import mozilla.components.support.base.feature.ViewBoundFeatureWrapper
|
||||||
import org.mozilla.fenix.FeatureFlags
|
import org.mozilla.fenix.FeatureFlags
|
||||||
import org.mozilla.fenix.HomeActivity
|
import org.mozilla.fenix.HomeActivity
|
||||||
import org.mozilla.fenix.R
|
import org.mozilla.fenix.R
|
||||||
|
import org.mozilla.fenix.addons.runIfFragmentIsAttached
|
||||||
import org.mozilla.fenix.components.FenixSnackbar
|
import org.mozilla.fenix.components.FenixSnackbar
|
||||||
import org.mozilla.fenix.components.TabCollectionStorage
|
import org.mozilla.fenix.components.TabCollectionStorage
|
||||||
import org.mozilla.fenix.components.metrics.Event
|
import org.mozilla.fenix.components.metrics.Event
|
||||||
|
@ -62,7 +63,6 @@ class BrowserFragment : BaseBrowserFragment(), UserInteractionHandler {
|
||||||
|
|
||||||
override fun initializeUI(view: View): Session? {
|
override fun initializeUI(view: View): Session? {
|
||||||
val context = requireContext()
|
val context = requireContext()
|
||||||
val sessionManager = context.components.core.sessionManager
|
|
||||||
val components = context.components
|
val components = context.components
|
||||||
|
|
||||||
return super.initializeUI(view)?.also {
|
return super.initializeUI(view)?.also {
|
||||||
|
@ -70,12 +70,15 @@ class BrowserFragment : BaseBrowserFragment(), UserInteractionHandler {
|
||||||
feature = ReaderViewFeature(
|
feature = ReaderViewFeature(
|
||||||
context,
|
context,
|
||||||
components.core.engine,
|
components.core.engine,
|
||||||
sessionManager,
|
components.core.store,
|
||||||
view.readerViewControlsBar
|
view.readerViewControlsBar
|
||||||
) { available ->
|
) { available, _ ->
|
||||||
if (available) {
|
if (available) {
|
||||||
components.analytics.metrics.track(Event.ReaderModeAvailable)
|
components.analytics.metrics.track(Event.ReaderModeAvailable)
|
||||||
}
|
}
|
||||||
|
runIfFragmentIsAttached {
|
||||||
|
browserToolbarView.toolbarIntegration.invalidateMenu()
|
||||||
|
}
|
||||||
},
|
},
|
||||||
owner = this,
|
owner = this,
|
||||||
view = view
|
view = view
|
||||||
|
|
|
@ -31,6 +31,7 @@ import mozilla.components.feature.media.RecordingDevicesNotificationFeature
|
||||||
import mozilla.components.feature.media.middleware.MediaMiddleware
|
import mozilla.components.feature.media.middleware.MediaMiddleware
|
||||||
import mozilla.components.feature.pwa.ManifestStorage
|
import mozilla.components.feature.pwa.ManifestStorage
|
||||||
import mozilla.components.feature.pwa.WebAppShortcutManager
|
import mozilla.components.feature.pwa.WebAppShortcutManager
|
||||||
|
import mozilla.components.feature.readerview.ReaderViewMiddleware
|
||||||
import mozilla.components.feature.session.HistoryDelegate
|
import mozilla.components.feature.session.HistoryDelegate
|
||||||
import mozilla.components.feature.webcompat.WebCompatFeature
|
import mozilla.components.feature.webcompat.WebCompatFeature
|
||||||
import mozilla.components.feature.webnotifications.WebNotificationFeature
|
import mozilla.components.feature.webnotifications.WebNotificationFeature
|
||||||
|
@ -99,7 +100,8 @@ class Core(private val context: Context) {
|
||||||
val store by lazy {
|
val store by lazy {
|
||||||
BrowserStore(
|
BrowserStore(
|
||||||
middleware = listOf(
|
middleware = listOf(
|
||||||
MediaMiddleware(context, MediaService::class.java)
|
MediaMiddleware(context, MediaService::class.java),
|
||||||
|
ReaderViewMiddleware()
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,7 @@ import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import mozilla.components.browser.session.Session
|
import mozilla.components.browser.session.Session
|
||||||
import mozilla.components.browser.session.SessionManager
|
import mozilla.components.browser.session.SessionManager
|
||||||
|
import mozilla.components.browser.state.selector.findTab
|
||||||
import mozilla.components.concept.engine.EngineView
|
import mozilla.components.concept.engine.EngineView
|
||||||
import mozilla.components.concept.engine.prompt.ShareData
|
import mozilla.components.concept.engine.prompt.ShareData
|
||||||
import mozilla.components.support.ktx.kotlin.isUrl
|
import mozilla.components.support.ktx.kotlin.isUrl
|
||||||
|
@ -275,9 +276,9 @@ class DefaultBrowserToolbarController(
|
||||||
is ToolbarMenu.Item.ReaderMode -> {
|
is ToolbarMenu.Item.ReaderMode -> {
|
||||||
activity.settings().readerModeOpened = true
|
activity.settings().readerModeOpened = true
|
||||||
|
|
||||||
val enabled = currentSession?.readerMode
|
val enabled = currentSession?.let {
|
||||||
?: activity.components.core.sessionManager.selectedSession?.readerMode
|
activity.components.core.store.state.findTab(it.id)?.readerState?.active
|
||||||
?: false
|
} ?: false
|
||||||
|
|
||||||
if (enabled) {
|
if (enabled) {
|
||||||
readerModeController.hideReaderView()
|
readerModeController.hideReaderView()
|
||||||
|
|
|
@ -207,6 +207,7 @@ class BrowserToolbarView(
|
||||||
onItemTapped = { interactor.onBrowserToolbarMenuItemTapped(it) },
|
onItemTapped = { interactor.onBrowserToolbarMenuItemTapped(it) },
|
||||||
lifecycleOwner = lifecycleOwner,
|
lifecycleOwner = lifecycleOwner,
|
||||||
sessionManager = sessionManager,
|
sessionManager = sessionManager,
|
||||||
|
store = components.core.store,
|
||||||
bookmarksStorage = bookmarkStorage
|
bookmarksStorage = bookmarkStorage
|
||||||
)
|
)
|
||||||
view.display.setMenuDismissAction {
|
view.display.setMenuDismissAction {
|
||||||
|
|
|
@ -21,6 +21,8 @@ import mozilla.components.browser.menu.item.BrowserMenuImageText
|
||||||
import mozilla.components.browser.menu.item.BrowserMenuItemToolbar
|
import mozilla.components.browser.menu.item.BrowserMenuItemToolbar
|
||||||
import mozilla.components.browser.session.Session
|
import mozilla.components.browser.session.Session
|
||||||
import mozilla.components.browser.session.SessionManager
|
import mozilla.components.browser.session.SessionManager
|
||||||
|
import mozilla.components.browser.state.selector.findTab
|
||||||
|
import mozilla.components.browser.state.store.BrowserStore
|
||||||
import mozilla.components.concept.storage.BookmarksStorage
|
import mozilla.components.concept.storage.BookmarksStorage
|
||||||
import mozilla.components.support.ktx.android.content.getColorFromAttr
|
import mozilla.components.support.ktx.android.content.getColorFromAttr
|
||||||
import org.mozilla.fenix.Config
|
import org.mozilla.fenix.Config
|
||||||
|
@ -47,6 +49,7 @@ import org.mozilla.fenix.utils.Settings
|
||||||
class DefaultToolbarMenu(
|
class DefaultToolbarMenu(
|
||||||
private val context: Context,
|
private val context: Context,
|
||||||
private val sessionManager: SessionManager,
|
private val sessionManager: SessionManager,
|
||||||
|
private val store: BrowserStore,
|
||||||
hasAccountProblem: Boolean = false,
|
hasAccountProblem: Boolean = false,
|
||||||
shouldReverseItems: Boolean,
|
shouldReverseItems: Boolean,
|
||||||
private val onItemTapped: (ToolbarMenu.Item) -> Unit = {},
|
private val onItemTapped: (ToolbarMenu.Item) -> Unit = {},
|
||||||
|
@ -64,7 +67,7 @@ class DefaultToolbarMenu(
|
||||||
WebExtensionBrowserMenuBuilder(
|
WebExtensionBrowserMenuBuilder(
|
||||||
menuItems,
|
menuItems,
|
||||||
endOfMenuAlwaysVisible = !shouldReverseItems,
|
endOfMenuAlwaysVisible = !shouldReverseItems,
|
||||||
store = context.components.core.store,
|
store = store,
|
||||||
appendExtensionActionAtStart = !shouldReverseItems
|
appendExtensionActionAtStart = !shouldReverseItems
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -150,14 +153,18 @@ class DefaultToolbarMenu(
|
||||||
private fun shouldShowAddToHomescreen(): Boolean =
|
private fun shouldShowAddToHomescreen(): Boolean =
|
||||||
session != null && context.components.useCases.webAppUseCases.isPinningSupported()
|
session != null && context.components.useCases.webAppUseCases.isPinningSupported()
|
||||||
|
|
||||||
private fun shouldShowReaderMode(): Boolean = session?.readerable ?: false
|
private fun shouldShowReaderMode(): Boolean = session?.let {
|
||||||
|
store.state.findTab(it.id)?.readerState?.readerable
|
||||||
|
} ?: false
|
||||||
|
|
||||||
private fun shouldShowOpenInApp(): Boolean = session?.let { session ->
|
private fun shouldShowOpenInApp(): Boolean = session?.let { session ->
|
||||||
val appLink = context.components.useCases.appLinksUseCases.appLinkRedirect
|
val appLink = context.components.useCases.appLinksUseCases.appLinkRedirect
|
||||||
appLink(session.url).hasExternalApp()
|
appLink(session.url).hasExternalApp()
|
||||||
} ?: false
|
} ?: false
|
||||||
|
|
||||||
private fun shouldShowReaderAppearance(): Boolean = session?.readerMode ?: false
|
private fun shouldShowReaderAppearance(): Boolean = session?.let {
|
||||||
|
store.state.findTab(it.id)?.readerState?.active
|
||||||
|
} ?: false
|
||||||
// End of predicates //
|
// End of predicates //
|
||||||
|
|
||||||
private val menuItems by lazy {
|
private val menuItems by lazy {
|
||||||
|
@ -300,7 +307,9 @@ class DefaultToolbarMenu(
|
||||||
label = context.getString(R.string.browser_menu_read),
|
label = context.getString(R.string.browser_menu_read),
|
||||||
startImageResource = R.drawable.ic_readermode,
|
startImageResource = R.drawable.ic_readermode,
|
||||||
initialState = {
|
initialState = {
|
||||||
session?.readerMode ?: false
|
session?.let {
|
||||||
|
store.state.findTab(it.id)?.readerState?.active
|
||||||
|
} ?: false
|
||||||
},
|
},
|
||||||
highlight = BrowserMenuHighlight.LowPriority(
|
highlight = BrowserMenuHighlight.LowPriority(
|
||||||
label = context.getString(R.string.browser_menu_read),
|
label = context.getString(R.string.browser_menu_read),
|
||||||
|
|
|
@ -22,20 +22,20 @@ class MenuPresenter(
|
||||||
|
|
||||||
/** Redraw the refresh/stop button */
|
/** Redraw the refresh/stop button */
|
||||||
override fun onLoadingStateChanged(session: Session, loading: Boolean) {
|
override fun onLoadingStateChanged(session: Session, loading: Boolean) {
|
||||||
menuToolbar.invalidateActions()
|
invalidateActions()
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Redraw the back and forward buttons */
|
/** Redraw the back and forward buttons */
|
||||||
override fun onNavigationStateChanged(session: Session, canGoBack: Boolean, canGoForward: Boolean) {
|
override fun onNavigationStateChanged(session: Session, canGoBack: Boolean, canGoForward: Boolean) {
|
||||||
menuToolbar.invalidateActions()
|
invalidateActions()
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Redraw the install web app button */
|
/** Redraw the install web app button */
|
||||||
override fun onWebAppManifestChanged(session: Session, manifest: WebAppManifest?) {
|
override fun onWebAppManifestChanged(session: Session, manifest: WebAppManifest?) {
|
||||||
menuToolbar.invalidateActions()
|
invalidateActions()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onReaderableStateUpdated(session: Session, readerable: Boolean) {
|
fun invalidateActions() {
|
||||||
menuToolbar.invalidateActions()
|
menuToolbar.invalidateActions()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,7 +45,7 @@ abstract class ToolbarIntegration(
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
private var menuPresenter =
|
private val menuPresenter =
|
||||||
MenuPresenter(toolbar, context.components.core.sessionManager, sessionId)
|
MenuPresenter(toolbar, context.components.core.sessionManager, sessionId)
|
||||||
|
|
||||||
init {
|
init {
|
||||||
|
@ -62,6 +62,10 @@ abstract class ToolbarIntegration(
|
||||||
menuPresenter.stop()
|
menuPresenter.stop()
|
||||||
toolbarPresenter.stop()
|
toolbarPresenter.stop()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun invalidateMenu() {
|
||||||
|
menuPresenter.invalidateActions()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class DefaultToolbarIntegration(
|
class DefaultToolbarIntegration(
|
||||||
|
|
|
@ -27,6 +27,10 @@ import kotlinx.coroutines.test.runBlockingTest
|
||||||
import kotlinx.coroutines.test.setMain
|
import kotlinx.coroutines.test.setMain
|
||||||
import mozilla.components.browser.session.Session
|
import mozilla.components.browser.session.Session
|
||||||
import mozilla.components.browser.session.SessionManager
|
import mozilla.components.browser.session.SessionManager
|
||||||
|
import mozilla.components.browser.state.state.BrowserState
|
||||||
|
import mozilla.components.browser.state.state.ReaderState
|
||||||
|
import mozilla.components.browser.state.state.createTab
|
||||||
|
import mozilla.components.browser.state.store.BrowserStore
|
||||||
import mozilla.components.concept.engine.EngineView
|
import mozilla.components.concept.engine.EngineView
|
||||||
import mozilla.components.feature.search.SearchUseCases
|
import mozilla.components.feature.search.SearchUseCases
|
||||||
import mozilla.components.feature.session.SessionUseCases
|
import mozilla.components.feature.session.SessionUseCases
|
||||||
|
@ -43,6 +47,7 @@ import org.mozilla.fenix.browser.BrowserAnimator
|
||||||
import org.mozilla.fenix.browser.BrowserFragment
|
import org.mozilla.fenix.browser.BrowserFragment
|
||||||
import org.mozilla.fenix.browser.BrowserFragmentDirections
|
import org.mozilla.fenix.browser.BrowserFragmentDirections
|
||||||
import org.mozilla.fenix.browser.browsingmode.BrowsingModeManager
|
import org.mozilla.fenix.browser.browsingmode.BrowsingModeManager
|
||||||
|
import org.mozilla.fenix.browser.readermode.ReaderModeController
|
||||||
import org.mozilla.fenix.collections.SaveCollectionStep
|
import org.mozilla.fenix.collections.SaveCollectionStep
|
||||||
import org.mozilla.fenix.components.Analytics
|
import org.mozilla.fenix.components.Analytics
|
||||||
import org.mozilla.fenix.components.FenixSnackbar
|
import org.mozilla.fenix.components.FenixSnackbar
|
||||||
|
@ -82,6 +87,13 @@ class DefaultBrowserToolbarControllerTest {
|
||||||
private val snackbar = mockk<FenixSnackbar>(relaxed = true)
|
private val snackbar = mockk<FenixSnackbar>(relaxed = true)
|
||||||
private val tabCollectionStorage = mockk<TabCollectionStorage>(relaxed = true)
|
private val tabCollectionStorage = mockk<TabCollectionStorage>(relaxed = true)
|
||||||
private val topSiteStorage = mockk<TopSiteStorage>(relaxed = true)
|
private val topSiteStorage = mockk<TopSiteStorage>(relaxed = true)
|
||||||
|
private val readerModeController = mockk<ReaderModeController>(relaxed = true)
|
||||||
|
private val store: BrowserStore = BrowserStore(initialState = BrowserState(
|
||||||
|
listOf(
|
||||||
|
createTab("https://www.mozilla.org", id = "reader-inactive-tab"),
|
||||||
|
createTab("https://www.mozilla.org", id = "reader-active-tab", readerState = ReaderState(active = true))
|
||||||
|
))
|
||||||
|
)
|
||||||
|
|
||||||
private lateinit var controller: DefaultBrowserToolbarController
|
private lateinit var controller: DefaultBrowserToolbarController
|
||||||
|
|
||||||
|
@ -104,7 +116,7 @@ class DefaultBrowserToolbarControllerTest {
|
||||||
tabCollectionStorage = tabCollectionStorage,
|
tabCollectionStorage = tabCollectionStorage,
|
||||||
topSiteStorage = topSiteStorage,
|
topSiteStorage = topSiteStorage,
|
||||||
bookmarkTapped = mockk(),
|
bookmarkTapped = mockk(),
|
||||||
readerModeController = mockk(),
|
readerModeController = readerModeController,
|
||||||
sessionManager = mockk(),
|
sessionManager = mockk(),
|
||||||
store = mockk(),
|
store = mockk(),
|
||||||
sharedViewModel = mockk()
|
sharedViewModel = mockk()
|
||||||
|
@ -125,6 +137,7 @@ class DefaultBrowserToolbarControllerTest {
|
||||||
every { activity.components.useCases.sessionUseCases } returns sessionUseCases
|
every { activity.components.useCases.sessionUseCases } returns sessionUseCases
|
||||||
every { activity.components.useCases.searchUseCases } returns searchUseCases
|
every { activity.components.useCases.searchUseCases } returns searchUseCases
|
||||||
every { activity.components.core.sessionManager.selectedSession } returns currentSession
|
every { activity.components.core.sessionManager.selectedSession } returns currentSession
|
||||||
|
every { activity.components.core.store } returns store
|
||||||
|
|
||||||
val onComplete = slot<() -> Unit>()
|
val onComplete = slot<() -> Unit>()
|
||||||
every { browserAnimator.captureEngineViewAndDrawStatically(capture(onComplete)) } answers { onComplete.captured.invoke() }
|
every { browserAnimator.captureEngineViewAndDrawStatically(capture(onComplete)) } answers { onComplete.captured.invoke() }
|
||||||
|
@ -559,4 +572,17 @@ class DefaultBrowserToolbarControllerTest {
|
||||||
|
|
||||||
verify { deleteAndQuit(activity, testScope, null) }
|
verify { deleteAndQuit(activity, testScope, null) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun handleToolbarReaderModePress() {
|
||||||
|
val item = ToolbarMenu.Item.ReaderMode(false)
|
||||||
|
|
||||||
|
every { currentSession.id } returns "reader-inactive-tab"
|
||||||
|
controller.handleToolbarItemInteraction(item)
|
||||||
|
verify { readerModeController.showReaderView() }
|
||||||
|
|
||||||
|
every { currentSession.id } returns "reader-active-tab"
|
||||||
|
controller.handleToolbarItemInteraction(item)
|
||||||
|
verify { readerModeController.hideReaderView() }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,10 @@ import io.mockk.mockkStatic
|
||||||
import io.mockk.spyk
|
import io.mockk.spyk
|
||||||
import mozilla.components.browser.session.Session
|
import mozilla.components.browser.session.Session
|
||||||
import mozilla.components.browser.session.SessionManager
|
import mozilla.components.browser.session.SessionManager
|
||||||
|
import mozilla.components.browser.state.state.BrowserState
|
||||||
|
import mozilla.components.browser.state.state.ReaderState
|
||||||
|
import mozilla.components.browser.state.state.createTab
|
||||||
|
import mozilla.components.browser.state.store.BrowserStore
|
||||||
import mozilla.components.concept.storage.BookmarksStorage
|
import mozilla.components.concept.storage.BookmarksStorage
|
||||||
import mozilla.components.feature.app.links.AppLinkRedirect
|
import mozilla.components.feature.app.links.AppLinkRedirect
|
||||||
import mozilla.components.feature.app.links.AppLinksUseCases
|
import mozilla.components.feature.app.links.AppLinksUseCases
|
||||||
|
@ -28,12 +32,19 @@ class DefaultToolbarMenuTest {
|
||||||
private val lifecycleOwner: LifecycleOwner = mockk(relaxed = true)
|
private val lifecycleOwner: LifecycleOwner = mockk(relaxed = true)
|
||||||
private val bookmarksStorage: BookmarksStorage = mockk(relaxed = true)
|
private val bookmarksStorage: BookmarksStorage = mockk(relaxed = true)
|
||||||
|
|
||||||
|
private val store: BrowserStore = BrowserStore(initialState = BrowserState(
|
||||||
|
listOf(
|
||||||
|
createTab("https://www.mozilla.org", id = "readerable-tab", readerState = ReaderState(readerable = true))
|
||||||
|
)
|
||||||
|
))
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
fun setUp() {
|
fun setUp() {
|
||||||
defaultToolbarMenu = spyk(
|
defaultToolbarMenu = spyk(
|
||||||
DefaultToolbarMenu(
|
DefaultToolbarMenu(
|
||||||
context,
|
context,
|
||||||
sessionManager,
|
sessionManager,
|
||||||
|
store,
|
||||||
hasAccountProblem = false,
|
hasAccountProblem = false,
|
||||||
shouldReverseItems = false,
|
shouldReverseItems = false,
|
||||||
onItemTapped = onItemTapped,
|
onItemTapped = onItemTapped,
|
||||||
|
@ -59,7 +70,7 @@ class DefaultToolbarMenuTest {
|
||||||
every { getAppLinkRedirect(any()) } returns appLinkRedirect
|
every { getAppLinkRedirect(any()) } returns appLinkRedirect
|
||||||
|
|
||||||
val session: Session = mockk(relaxed = true)
|
val session: Session = mockk(relaxed = true)
|
||||||
every { session.readerable } returns true
|
every { session.id } returns "readerable-tab"
|
||||||
every { sessionManager.selectedSession } returns session
|
every { sessionManager.selectedSession } returns session
|
||||||
|
|
||||||
val list = defaultToolbarMenu.getLowPrioHighlightItems()
|
val list = defaultToolbarMenu.getLowPrioHighlightItems()
|
||||||
|
@ -84,7 +95,7 @@ class DefaultToolbarMenuTest {
|
||||||
every { getAppLinkRedirect(any()) } returns appLinkRedirect
|
every { getAppLinkRedirect(any()) } returns appLinkRedirect
|
||||||
|
|
||||||
val session: Session = mockk(relaxed = true)
|
val session: Session = mockk(relaxed = true)
|
||||||
every { session.readerable } returns true
|
every { session.id } returns "readerable-tab"
|
||||||
every { sessionManager.selectedSession } returns session
|
every { sessionManager.selectedSession } returns session
|
||||||
|
|
||||||
val list = defaultToolbarMenu.getLowPrioHighlightItems()
|
val list = defaultToolbarMenu.getLowPrioHighlightItems()
|
||||||
|
@ -114,7 +125,7 @@ class DefaultToolbarMenuTest {
|
||||||
every { context.components.useCases.webAppUseCases.isInstallable() } returns true
|
every { context.components.useCases.webAppUseCases.isInstallable() } returns true
|
||||||
|
|
||||||
val session: Session = mockk(relaxed = true)
|
val session: Session = mockk(relaxed = true)
|
||||||
every { session.readerable } returns true
|
every { session.id } returns "readerable-tab"
|
||||||
every { sessionManager.selectedSession } returns session
|
every { sessionManager.selectedSession } returns session
|
||||||
|
|
||||||
val getAppLinkRedirect: AppLinksUseCases.GetAppLinkRedirect = mockk(relaxed = true)
|
val getAppLinkRedirect: AppLinksUseCases.GetAppLinkRedirect = mockk(relaxed = true)
|
||||||
|
|
Loading…
Reference in New Issue