Merge remote-tracking branch 'origin/fenix/123.0' into iceraven
commit
d2da775dfd
@ -0,0 +1,54 @@
|
|||||||
|
/* 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.ui
|
||||||
|
|
||||||
|
import androidx.core.net.toUri
|
||||||
|
import org.junit.Rule
|
||||||
|
import org.junit.Test
|
||||||
|
import org.mozilla.fenix.customannotations.SmokeTest
|
||||||
|
import org.mozilla.fenix.ext.settings
|
||||||
|
import org.mozilla.fenix.helpers.AppAndSystemHelper.runWithCondition
|
||||||
|
import org.mozilla.fenix.helpers.HomeActivityIntentTestRule
|
||||||
|
import org.mozilla.fenix.helpers.TestHelper.appContext
|
||||||
|
import org.mozilla.fenix.ui.robots.homeScreen
|
||||||
|
import org.mozilla.fenix.ui.robots.navigationToolbar
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for verifying the new Cookie banner blocker option and functionality.
|
||||||
|
*/
|
||||||
|
class CookieBannerBlockerTest {
|
||||||
|
@get:Rule
|
||||||
|
val activityTestRule = HomeActivityIntentTestRule.withDefaultSettingsOverrides(skipOnboarding = true)
|
||||||
|
|
||||||
|
// TestRail link: https://testrail.stage.mozaws.net/index.php?/cases/view/2419260
|
||||||
|
@SmokeTest
|
||||||
|
@Test
|
||||||
|
fun verifyCookieBannerBlockerSettingsOptionTest() {
|
||||||
|
runWithCondition(appContext.settings().shouldUseCookieBannerPrivateMode) {
|
||||||
|
homeScreen {
|
||||||
|
}.openThreeDotMenu {
|
||||||
|
}.openSettings {
|
||||||
|
verifyCookieBannerBlockerButton(enabled = true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestRail link: https://testrail.stage.mozaws.net/index.php?/cases/view/2419273
|
||||||
|
@SmokeTest
|
||||||
|
@Test
|
||||||
|
fun verifyCFRAfterBlockingTheCookieBanner() {
|
||||||
|
runWithCondition(appContext.settings().shouldUseCookieBannerPrivateMode) {
|
||||||
|
homeScreen {
|
||||||
|
}.togglePrivateBrowsingMode()
|
||||||
|
|
||||||
|
navigationToolbar {
|
||||||
|
}.enterURLAndEnterToBrowser("voetbal24.be".toUri()) {
|
||||||
|
waitForPageToLoad()
|
||||||
|
verifyCookieBannerExists(exists = false)
|
||||||
|
verifyCookieBannerBlockerCFRExists(exists = true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,49 @@
|
|||||||
|
/* 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.addons
|
||||||
|
|
||||||
|
import android.os.Handler
|
||||||
|
import android.os.Looper
|
||||||
|
import mozilla.components.browser.state.store.BrowserStore
|
||||||
|
import mozilla.components.support.webextensions.ExtensionsProcessDisabledPromptObserver
|
||||||
|
import org.mozilla.fenix.components.AppStore
|
||||||
|
import kotlin.system.exitProcess
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Controller for handling extensions process spawning disabled events. This is for when the app is
|
||||||
|
* in background, the app is killed to prevent extensions from being disabled and network requests
|
||||||
|
* continuing.
|
||||||
|
*
|
||||||
|
* @param browserStore The [BrowserStore] which holds the state for showing the dialog.
|
||||||
|
* @param appStore The [AppStore] containing the application state.
|
||||||
|
* @param onExtensionsProcessDisabled Invoked when the app is in background and extensions process
|
||||||
|
* is disabled.
|
||||||
|
*/
|
||||||
|
class ExtensionsProcessDisabledBackgroundController(
|
||||||
|
browserStore: BrowserStore,
|
||||||
|
appStore: AppStore,
|
||||||
|
onExtensionsProcessDisabled: () -> Unit = { killApp() },
|
||||||
|
) : ExtensionsProcessDisabledPromptObserver(
|
||||||
|
store = browserStore,
|
||||||
|
shouldCancelOnStop = false,
|
||||||
|
onShowExtensionsProcessDisabledPrompt = {
|
||||||
|
if (!appStore.state.isForeground) {
|
||||||
|
onExtensionsProcessDisabled()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
) {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
/**
|
||||||
|
* When a dialog can't be shown because the app is in the background, instead the app will
|
||||||
|
* be killed to prevent leaking network data without extensions enabled.
|
||||||
|
*/
|
||||||
|
private fun killApp() {
|
||||||
|
Handler(Looper.getMainLooper()).post {
|
||||||
|
exitProcess(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,24 @@
|
|||||||
|
/* 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.debugsettings.navigation
|
||||||
|
|
||||||
|
import androidx.annotation.StringRes
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A navigation destination for screens within the Debug Drawer.
|
||||||
|
*
|
||||||
|
* @property route The unique route used to navigate to the destination. This string can also contain
|
||||||
|
* optional parameters for arguments or deep linking.
|
||||||
|
* @property title The string ID of the destination's title.
|
||||||
|
* @property onClick Invoked when the destination is clicked to be navigated to.
|
||||||
|
* @property content The destination's [Composable].
|
||||||
|
*/
|
||||||
|
data class DebugDrawerDestination(
|
||||||
|
val route: String,
|
||||||
|
@StringRes val title: Int,
|
||||||
|
val onClick: () -> Unit,
|
||||||
|
val content: @Composable () -> Unit,
|
||||||
|
)
|
@ -0,0 +1,70 @@
|
|||||||
|
/* 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.debugsettings.navigation
|
||||||
|
|
||||||
|
import androidx.annotation.StringRes
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import mozilla.components.browser.state.store.BrowserStore
|
||||||
|
import org.mozilla.fenix.R
|
||||||
|
import org.mozilla.fenix.debugsettings.store.DebugDrawerAction
|
||||||
|
import org.mozilla.fenix.debugsettings.store.DebugDrawerStore
|
||||||
|
import org.mozilla.fenix.debugsettings.tabs.TabTools as TabToolsScreen
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The navigation routes for screens within the Debug Drawer.
|
||||||
|
*
|
||||||
|
* @property route The unique route used to navigate to the destination. This string can also contain
|
||||||
|
* optional parameters for arguments or deep linking.
|
||||||
|
* @property title The string ID of the destination's title.
|
||||||
|
*/
|
||||||
|
enum class DebugDrawerRoute(val route: String, @StringRes val title: Int) {
|
||||||
|
/**
|
||||||
|
* The navigation route for [TabToolsScreen].
|
||||||
|
*/
|
||||||
|
TabTools(
|
||||||
|
route = "tab_tools",
|
||||||
|
title = R.string.debug_drawer_tab_tools_title,
|
||||||
|
),
|
||||||
|
;
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
/**
|
||||||
|
* Transforms the values of [DebugDrawerRoute] into a list of [DebugDrawerDestination]s.
|
||||||
|
*
|
||||||
|
* @param debugDrawerStore [DebugDrawerStore] used to dispatch navigation actions.
|
||||||
|
* @param browserStore [BrowserStore] used to add tabs in [TabToolsScreen].
|
||||||
|
* @param inactiveTabsEnabled Whether the inactive tabs feature is enabled.
|
||||||
|
*/
|
||||||
|
fun generateDebugDrawerDestinations(
|
||||||
|
debugDrawerStore: DebugDrawerStore,
|
||||||
|
browserStore: BrowserStore,
|
||||||
|
inactiveTabsEnabled: Boolean,
|
||||||
|
): List<DebugDrawerDestination> =
|
||||||
|
DebugDrawerRoute.values().map { debugDrawerRoute ->
|
||||||
|
val onClick: () -> Unit
|
||||||
|
val content: @Composable () -> Unit
|
||||||
|
when (debugDrawerRoute) {
|
||||||
|
TabTools -> {
|
||||||
|
onClick = {
|
||||||
|
debugDrawerStore.dispatch(DebugDrawerAction.NavigateTo.TabTools)
|
||||||
|
}
|
||||||
|
content = {
|
||||||
|
TabToolsScreen(
|
||||||
|
store = browserStore,
|
||||||
|
inactiveTabsEnabled = inactiveTabsEnabled,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DebugDrawerDestination(
|
||||||
|
route = debugDrawerRoute.route,
|
||||||
|
title = debugDrawerRoute.title,
|
||||||
|
onClick = onClick,
|
||||||
|
content = content,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,46 @@
|
|||||||
|
/* 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.debugsettings.store
|
||||||
|
|
||||||
|
import mozilla.components.lib.state.Action
|
||||||
|
import org.mozilla.fenix.debugsettings.ui.DebugDrawerHome
|
||||||
|
import org.mozilla.fenix.debugsettings.tabs.TabTools as TabToolsScreen
|
||||||
|
|
||||||
|
/**
|
||||||
|
* [Action] implementation related to [DebugDrawerStore].
|
||||||
|
*/
|
||||||
|
sealed class DebugDrawerAction : Action {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* [DebugDrawerAction] fired when the user opens the drawer.
|
||||||
|
*/
|
||||||
|
object DrawerOpened : DebugDrawerAction()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* [DebugDrawerAction] fired when the user closes the drawer.
|
||||||
|
*/
|
||||||
|
object DrawerClosed : DebugDrawerAction()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* [DebugDrawerAction] fired when a navigation event occurs for a specific destination.
|
||||||
|
*/
|
||||||
|
sealed class NavigateTo : DebugDrawerAction() {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* [NavigateTo] action fired when the debug drawer needs to navigate to [DebugDrawerHome].
|
||||||
|
*/
|
||||||
|
object Home : NavigateTo()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* [NavigateTo] action fired when the debug drawer needs to navigate to [TabToolsScreen].
|
||||||
|
*/
|
||||||
|
object TabTools : NavigateTo()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* [DebugDrawerAction] fired when a back navigation event occurs.
|
||||||
|
*/
|
||||||
|
object OnBackPressed : DebugDrawerAction()
|
||||||
|
}
|
@ -0,0 +1,45 @@
|
|||||||
|
/* 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.debugsettings.store
|
||||||
|
|
||||||
|
import androidx.navigation.NavHostController
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import mozilla.components.lib.state.Middleware
|
||||||
|
import mozilla.components.lib.state.MiddlewareContext
|
||||||
|
import org.mozilla.fenix.debugsettings.navigation.DebugDrawerRoute
|
||||||
|
import org.mozilla.fenix.debugsettings.ui.DEBUG_DRAWER_HOME_ROUTE
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Middleware that handles navigation events for the Debug Drawer feature.
|
||||||
|
*
|
||||||
|
* @param navController [NavHostController] used to execute any navigation actions on the UI.
|
||||||
|
* @param scope [CoroutineScope] used to make calls to the main thread.
|
||||||
|
*/
|
||||||
|
class DebugDrawerNavigationMiddleware(
|
||||||
|
private val navController: NavHostController,
|
||||||
|
private val scope: CoroutineScope,
|
||||||
|
) : Middleware<DebugDrawerState, DebugDrawerAction> {
|
||||||
|
|
||||||
|
override fun invoke(
|
||||||
|
context: MiddlewareContext<DebugDrawerState, DebugDrawerAction>,
|
||||||
|
next: (DebugDrawerAction) -> Unit,
|
||||||
|
action: DebugDrawerAction,
|
||||||
|
) {
|
||||||
|
next(action)
|
||||||
|
scope.launch {
|
||||||
|
when (action) {
|
||||||
|
is DebugDrawerAction.NavigateTo.Home -> navController.popBackStack(
|
||||||
|
route = DEBUG_DRAWER_HOME_ROUTE,
|
||||||
|
inclusive = false,
|
||||||
|
)
|
||||||
|
is DebugDrawerAction.NavigateTo.TabTools ->
|
||||||
|
navController.navigate(route = DebugDrawerRoute.TabTools.route)
|
||||||
|
is DebugDrawerAction.OnBackPressed -> navController.popBackStack()
|
||||||
|
is DebugDrawerAction.DrawerOpened, DebugDrawerAction.DrawerClosed -> Unit // no-op
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,16 @@
|
|||||||
|
/* 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.debugsettings.store
|
||||||
|
|
||||||
|
import mozilla.components.lib.state.State
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UI state of the debug drawer feature.
|
||||||
|
*
|
||||||
|
* @property drawerStatus The [DrawerStatus] indicating the physical state of the drawer.
|
||||||
|
*/
|
||||||
|
data class DebugDrawerState(
|
||||||
|
val drawerStatus: DrawerStatus = DrawerStatus.Closed,
|
||||||
|
) : State
|
@ -0,0 +1,27 @@
|
|||||||
|
/* 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.debugsettings.store
|
||||||
|
|
||||||
|
import mozilla.components.lib.state.Middleware
|
||||||
|
import mozilla.components.lib.state.Store
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A [Store] that holds the [DebugDrawerState] for the Debug Drawer and reduces [DebugDrawerAction]s
|
||||||
|
* dispatched to the store.
|
||||||
|
*/
|
||||||
|
class DebugDrawerStore(
|
||||||
|
initialState: DebugDrawerState = DebugDrawerState(),
|
||||||
|
middlewares: List<Middleware<DebugDrawerState, DebugDrawerAction>> = emptyList(),
|
||||||
|
) : Store<DebugDrawerState, DebugDrawerAction>(
|
||||||
|
initialState,
|
||||||
|
::reduce,
|
||||||
|
middlewares,
|
||||||
|
)
|
||||||
|
|
||||||
|
private fun reduce(state: DebugDrawerState, action: DebugDrawerAction): DebugDrawerState = when (action) {
|
||||||
|
is DebugDrawerAction.DrawerOpened -> state.copy(drawerStatus = DrawerStatus.Open)
|
||||||
|
is DebugDrawerAction.DrawerClosed -> state.copy(drawerStatus = DrawerStatus.Closed)
|
||||||
|
is DebugDrawerAction.NavigateTo, DebugDrawerAction.OnBackPressed -> state // handled by [DebugDrawerNavigationMiddleware]
|
||||||
|
}
|
@ -0,0 +1,20 @@
|
|||||||
|
/* 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.debugsettings.store
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Possible values of the debug drawer's physical state.
|
||||||
|
*/
|
||||||
|
enum class DrawerStatus {
|
||||||
|
/**
|
||||||
|
* The state of the drawer when it is closed.
|
||||||
|
*/
|
||||||
|
Closed,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The state of the drawer when it is open.
|
||||||
|
*/
|
||||||
|
Open,
|
||||||
|
}
|
@ -0,0 +1,328 @@
|
|||||||
|
/* 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.debugsettings.tabs
|
||||||
|
|
||||||
|
import androidx.compose.foundation.background
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Box
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.Spacer
|
||||||
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.height
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.text.KeyboardActions
|
||||||
|
import androidx.compose.foundation.text.KeyboardOptions
|
||||||
|
import androidx.compose.material.Text
|
||||||
|
import androidx.compose.material.TextField
|
||||||
|
import androidx.compose.material.TextFieldDefaults
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.saveable.rememberSaveable
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
|
import androidx.compose.ui.ExperimentalComposeUiApi
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.text.input.KeyboardType
|
||||||
|
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||||
|
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.core.text.isDigitsOnly
|
||||||
|
import mozilla.components.browser.state.action.TabListAction
|
||||||
|
import mozilla.components.browser.state.state.createTab
|
||||||
|
import mozilla.components.browser.state.store.BrowserStore
|
||||||
|
import mozilla.components.lib.state.ext.observeAsState
|
||||||
|
import org.mozilla.fenix.R
|
||||||
|
import org.mozilla.fenix.compose.Divider
|
||||||
|
import org.mozilla.fenix.compose.annotation.LightDarkPreview
|
||||||
|
import org.mozilla.fenix.compose.button.PrimaryButton
|
||||||
|
import org.mozilla.fenix.debugsettings.ui.DebugDrawer
|
||||||
|
import org.mozilla.fenix.ext.maxActiveTime
|
||||||
|
import org.mozilla.fenix.tabstray.ext.isNormalTabInactive
|
||||||
|
import org.mozilla.fenix.theme.FirefoxTheme
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tab Tools UI for [DebugDrawer] that displays the tab counts and allows easy bulk-opening of tabs.
|
||||||
|
*
|
||||||
|
* @param store [BrowserStore] used to obtain the tab counts and fire any tab creation actions.
|
||||||
|
* @param inactiveTabsEnabled Whether the inactive tabs feature is enabled.
|
||||||
|
*/
|
||||||
|
@Composable
|
||||||
|
fun TabTools(
|
||||||
|
store: BrowserStore,
|
||||||
|
inactiveTabsEnabled: Boolean,
|
||||||
|
) {
|
||||||
|
val tabs by store.observeAsState(initialValue = emptyList()) { state -> state.tabs }
|
||||||
|
val totalTabCount = remember(tabs) { tabs.size }
|
||||||
|
val privateTabCount = remember(tabs) { tabs.filter { it.content.private }.size }
|
||||||
|
val inactiveTabCount = remember(tabs) {
|
||||||
|
if (inactiveTabsEnabled) {
|
||||||
|
tabs.filter { it.isNormalTabInactive(maxActiveTime) }.size
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val activeTabCount = remember(tabs) { totalTabCount - privateTabCount - inactiveTabCount }
|
||||||
|
|
||||||
|
TabToolsContent(
|
||||||
|
activeTabCount = activeTabCount,
|
||||||
|
inactiveTabCount = inactiveTabCount,
|
||||||
|
privateTabCount = privateTabCount,
|
||||||
|
totalTabCount = totalTabCount,
|
||||||
|
inactiveTabsEnabled = inactiveTabsEnabled,
|
||||||
|
onCreateTabsClick = { quantity, isInactive, isPrivate ->
|
||||||
|
store.dispatch(
|
||||||
|
TabListAction.AddMultipleTabsAction(
|
||||||
|
tabs = generateTabList(
|
||||||
|
quantity = quantity,
|
||||||
|
isInactive = isInactive,
|
||||||
|
isPrivate = isPrivate,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun generateTabList(
|
||||||
|
quantity: Int,
|
||||||
|
isInactive: Boolean = false,
|
||||||
|
isPrivate: Boolean = false,
|
||||||
|
) = List(quantity) {
|
||||||
|
createTab(
|
||||||
|
url = "www.example.com",
|
||||||
|
private = isPrivate,
|
||||||
|
createdAt = if (isInactive) 0L else System.currentTimeMillis(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun TabToolsContent(
|
||||||
|
activeTabCount: Int,
|
||||||
|
inactiveTabCount: Int,
|
||||||
|
privateTabCount: Int,
|
||||||
|
totalTabCount: Int,
|
||||||
|
inactiveTabsEnabled: Boolean,
|
||||||
|
onCreateTabsClick: ((quantity: Int, isInactive: Boolean, isPrivate: Boolean) -> Unit),
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.padding(all = 16.dp),
|
||||||
|
verticalArrangement = Arrangement.spacedBy(16.dp),
|
||||||
|
) {
|
||||||
|
TabCounter(
|
||||||
|
activeTabCount = activeTabCount,
|
||||||
|
inactiveTabCount = inactiveTabCount,
|
||||||
|
privateTabCount = privateTabCount,
|
||||||
|
totalTabCount = totalTabCount,
|
||||||
|
inactiveTabsEnabled = inactiveTabsEnabled,
|
||||||
|
)
|
||||||
|
|
||||||
|
TabCreationTool(
|
||||||
|
inactiveTabsEnabled = inactiveTabsEnabled,
|
||||||
|
onCreateTabsClick = onCreateTabsClick,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun TabCounter(
|
||||||
|
activeTabCount: Int,
|
||||||
|
inactiveTabCount: Int,
|
||||||
|
privateTabCount: Int,
|
||||||
|
totalTabCount: Int,
|
||||||
|
inactiveTabsEnabled: Boolean,
|
||||||
|
) {
|
||||||
|
Column {
|
||||||
|
Text(
|
||||||
|
text = stringResource(R.string.debug_drawer_tab_tools_tab_count_title),
|
||||||
|
color = FirefoxTheme.colors.textPrimary,
|
||||||
|
style = FirefoxTheme.typography.headline5,
|
||||||
|
)
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
|
|
||||||
|
TabCountRow(
|
||||||
|
tabType = stringResource(R.string.debug_drawer_tab_tools_tab_count_normal),
|
||||||
|
count = activeTabCount.toString(),
|
||||||
|
)
|
||||||
|
|
||||||
|
if (inactiveTabsEnabled) {
|
||||||
|
TabCountRow(
|
||||||
|
tabType = stringResource(R.string.debug_drawer_tab_tools_tab_count_inactive),
|
||||||
|
count = inactiveTabCount.toString(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
TabCountRow(
|
||||||
|
tabType = stringResource(R.string.debug_drawer_tab_tools_tab_count_private),
|
||||||
|
count = privateTabCount.toString(),
|
||||||
|
)
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(8.dp))
|
||||||
|
|
||||||
|
Divider()
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(8.dp))
|
||||||
|
|
||||||
|
TabCountRow(
|
||||||
|
tabType = stringResource(R.string.debug_drawer_tab_tools_tab_count_total),
|
||||||
|
count = totalTabCount.toString(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun TabCountRow(
|
||||||
|
tabType: String,
|
||||||
|
count: String,
|
||||||
|
) {
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(start = 16.dp),
|
||||||
|
horizontalArrangement = Arrangement.SpaceBetween,
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = tabType,
|
||||||
|
color = FirefoxTheme.colors.textSecondary,
|
||||||
|
style = FirefoxTheme.typography.headline6,
|
||||||
|
)
|
||||||
|
|
||||||
|
Text(
|
||||||
|
text = count,
|
||||||
|
color = FirefoxTheme.colors.textSecondary,
|
||||||
|
style = FirefoxTheme.typography.headline6,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private const val DEFAULT_TABS_TO_ADD = "1"
|
||||||
|
|
||||||
|
@OptIn(ExperimentalComposeUiApi::class)
|
||||||
|
@Composable
|
||||||
|
private fun TabCreationTool(
|
||||||
|
inactiveTabsEnabled: Boolean,
|
||||||
|
onCreateTabsClick: ((quantity: Int, isInactive: Boolean, isPrivate: Boolean) -> Unit),
|
||||||
|
) {
|
||||||
|
var tabQuantityToCreate by rememberSaveable { mutableStateOf(DEFAULT_TABS_TO_ADD) }
|
||||||
|
var hasError by rememberSaveable { mutableStateOf(false) }
|
||||||
|
val keyboardController = LocalSoftwareKeyboardController.current
|
||||||
|
|
||||||
|
Column {
|
||||||
|
Text(
|
||||||
|
text = stringResource(R.string.debug_drawer_tab_tools_tab_creation_tool_title),
|
||||||
|
color = FirefoxTheme.colors.textPrimary,
|
||||||
|
style = FirefoxTheme.typography.headline5,
|
||||||
|
)
|
||||||
|
|
||||||
|
TextField(
|
||||||
|
value = tabQuantityToCreate,
|
||||||
|
onValueChange = {
|
||||||
|
tabQuantityToCreate = it
|
||||||
|
hasError = it.isEmpty() || !it.isDigitsOnly() || it.toInt() == 0
|
||||||
|
},
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
textStyle = FirefoxTheme.typography.subtitle1,
|
||||||
|
label = {
|
||||||
|
Text(
|
||||||
|
text = stringResource(R.string.debug_drawer_tab_tools_tab_creation_tool_text_field_label),
|
||||||
|
color = FirefoxTheme.colors.textPrimary,
|
||||||
|
style = FirefoxTheme.typography.caption,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
isError = hasError,
|
||||||
|
keyboardOptions = KeyboardOptions(
|
||||||
|
keyboardType = KeyboardType.Number,
|
||||||
|
),
|
||||||
|
keyboardActions = KeyboardActions(
|
||||||
|
onDone = {
|
||||||
|
keyboardController?.hide()
|
||||||
|
},
|
||||||
|
),
|
||||||
|
singleLine = true,
|
||||||
|
colors = TextFieldDefaults.textFieldColors(
|
||||||
|
textColor = FirefoxTheme.colors.textPrimary,
|
||||||
|
backgroundColor = Color.Transparent,
|
||||||
|
cursorColor = FirefoxTheme.colors.borderFormDefault,
|
||||||
|
errorCursorColor = FirefoxTheme.colors.borderWarning,
|
||||||
|
focusedIndicatorColor = FirefoxTheme.colors.borderPrimary,
|
||||||
|
unfocusedIndicatorColor = FirefoxTheme.colors.borderPrimary,
|
||||||
|
errorIndicatorColor = FirefoxTheme.colors.borderWarning,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(8.dp))
|
||||||
|
|
||||||
|
PrimaryButton(
|
||||||
|
text = stringResource(id = R.string.debug_drawer_tab_tools_tab_creation_tool_button_text_active),
|
||||||
|
enabled = !hasError,
|
||||||
|
onClick = {
|
||||||
|
onCreateTabsClick(tabQuantityToCreate.toInt(), false, false)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(8.dp))
|
||||||
|
|
||||||
|
if (inactiveTabsEnabled) {
|
||||||
|
PrimaryButton(
|
||||||
|
text = stringResource(id = R.string.debug_drawer_tab_tools_tab_creation_tool_button_text_inactive),
|
||||||
|
enabled = !hasError,
|
||||||
|
onClick = {
|
||||||
|
onCreateTabsClick(tabQuantityToCreate.toInt(), true, false)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(8.dp))
|
||||||
|
}
|
||||||
|
|
||||||
|
PrimaryButton(
|
||||||
|
text = stringResource(id = R.string.debug_drawer_tab_tools_tab_creation_tool_button_text_private),
|
||||||
|
enabled = !hasError,
|
||||||
|
onClick = {
|
||||||
|
onCreateTabsClick(tabQuantityToCreate.toInt(), false, true)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private data class TabToolsPreviewModel(
|
||||||
|
val inactiveTabsEnabled: Boolean = true,
|
||||||
|
)
|
||||||
|
|
||||||
|
private class TabToolsPreviewParameterProvider : PreviewParameterProvider<TabToolsPreviewModel> {
|
||||||
|
override val values: Sequence<TabToolsPreviewModel>
|
||||||
|
get() = sequenceOf(
|
||||||
|
TabToolsPreviewModel(
|
||||||
|
inactiveTabsEnabled = true,
|
||||||
|
),
|
||||||
|
TabToolsPreviewModel(
|
||||||
|
inactiveTabsEnabled = false,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
@LightDarkPreview
|
||||||
|
private fun TabToolsPreview(
|
||||||
|
@PreviewParameter(TabToolsPreviewParameterProvider::class) model: TabToolsPreviewModel,
|
||||||
|
) {
|
||||||
|
FirefoxTheme {
|
||||||
|
Box(
|
||||||
|
modifier = Modifier.background(color = FirefoxTheme.colors.layer1),
|
||||||
|
) {
|
||||||
|
TabTools(
|
||||||
|
store = BrowserStore(),
|
||||||
|
inactiveTabsEnabled = model.inactiveTabsEnabled,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,138 @@
|
|||||||
|
/* 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.debugsettings.ui
|
||||||
|
|
||||||
|
import androidx.compose.foundation.background
|
||||||
|
import androidx.compose.foundation.layout.Box
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
|
import androidx.compose.material.Icon
|
||||||
|
import androidx.compose.material.IconButton
|
||||||
|
import androidx.compose.material.Text
|
||||||
|
import androidx.compose.material.TopAppBar
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.res.painterResource
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.navigation.NavHostController
|
||||||
|
import androidx.navigation.compose.NavHost
|
||||||
|
import androidx.navigation.compose.composable
|
||||||
|
import androidx.navigation.compose.rememberNavController
|
||||||
|
import org.mozilla.fenix.R
|
||||||
|
import org.mozilla.fenix.compose.annotation.LightDarkPreview
|
||||||
|
import org.mozilla.fenix.debugsettings.navigation.DebugDrawerDestination
|
||||||
|
import org.mozilla.fenix.theme.FirefoxTheme
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The debug drawer UI.
|
||||||
|
*
|
||||||
|
* @param navController [NavHostController] used to perform navigation actions on the [NavHost].
|
||||||
|
* @param destinations The list of [DebugDrawerDestination]s (excluding home) used to populate
|
||||||
|
* the [NavHost] with screens.
|
||||||
|
* @param onBackButtonClick Invoked when the user taps on the back button in the app bar.
|
||||||
|
*/
|
||||||
|
@Composable
|
||||||
|
fun DebugDrawer(
|
||||||
|
navController: NavHostController,
|
||||||
|
destinations: List<DebugDrawerDestination>,
|
||||||
|
onBackButtonClick: () -> Unit,
|
||||||
|
) {
|
||||||
|
var backButtonVisible by remember { mutableStateOf(false) }
|
||||||
|
var toolbarTitle by remember { mutableStateOf("") }
|
||||||
|
|
||||||
|
Column(modifier = Modifier.fillMaxSize()) {
|
||||||
|
TopAppBar(
|
||||||
|
title = {
|
||||||
|
Text(
|
||||||
|
text = toolbarTitle,
|
||||||
|
color = FirefoxTheme.colors.textPrimary,
|
||||||
|
style = FirefoxTheme.typography.headline6,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
navigationIcon = if (backButtonVisible) {
|
||||||
|
topBarBackButton(onClick = onBackButtonClick)
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
},
|
||||||
|
backgroundColor = FirefoxTheme.colors.layer1,
|
||||||
|
elevation = 5.dp,
|
||||||
|
)
|
||||||
|
|
||||||
|
NavHost(
|
||||||
|
navController = navController,
|
||||||
|
startDestination = DEBUG_DRAWER_HOME_ROUTE,
|
||||||
|
modifier = Modifier.fillMaxSize(),
|
||||||
|
) {
|
||||||
|
composable(route = DEBUG_DRAWER_HOME_ROUTE) {
|
||||||
|
toolbarTitle = stringResource(id = R.string.debug_drawer_title)
|
||||||
|
backButtonVisible = false
|
||||||
|
DebugDrawerHome(destinations = destinations)
|
||||||
|
}
|
||||||
|
|
||||||
|
destinations.forEach { destination ->
|
||||||
|
composable(route = destination.route) {
|
||||||
|
toolbarTitle = stringResource(id = destination.title)
|
||||||
|
backButtonVisible = true
|
||||||
|
|
||||||
|
destination.content()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun topBarBackButton(onClick: () -> Unit): @Composable () -> Unit = {
|
||||||
|
IconButton(
|
||||||
|
onClick = onClick,
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
painter = painterResource(R.drawable.mozac_ic_back_24),
|
||||||
|
contentDescription = stringResource(R.string.debug_drawer_back_button_content_description),
|
||||||
|
tint = FirefoxTheme.colors.iconPrimary,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
@LightDarkPreview
|
||||||
|
private fun DebugDrawerPreview() {
|
||||||
|
val navController = rememberNavController()
|
||||||
|
val destinations = remember {
|
||||||
|
List(size = 15) { index ->
|
||||||
|
DebugDrawerDestination(
|
||||||
|
route = "screen_$index",
|
||||||
|
title = R.string.debug_drawer_title,
|
||||||
|
onClick = {
|
||||||
|
navController.navigate(route = "screen_$index")
|
||||||
|
},
|
||||||
|
content = {
|
||||||
|
Text(
|
||||||
|
text = "Tool $index",
|
||||||
|
color = FirefoxTheme.colors.textPrimary,
|
||||||
|
style = FirefoxTheme.typography.headline6,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FirefoxTheme {
|
||||||
|
Box(modifier = Modifier.background(color = FirefoxTheme.colors.layer1)) {
|
||||||
|
DebugDrawer(
|
||||||
|
navController = navController,
|
||||||
|
destinations = destinations,
|
||||||
|
onBackButtonClick = {
|
||||||
|
navController.popBackStack()
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,144 @@
|
|||||||
|
/* 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.debugsettings.ui
|
||||||
|
|
||||||
|
import androidx.compose.foundation.background
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Box
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
|
import androidx.compose.foundation.lazy.items
|
||||||
|
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||||
|
import androidx.compose.material.Snackbar
|
||||||
|
import androidx.compose.material.SnackbarHost
|
||||||
|
import androidx.compose.material.SnackbarHostState
|
||||||
|
import androidx.compose.material.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import mozilla.components.support.ktx.android.content.appName
|
||||||
|
import mozilla.components.support.ktx.android.content.appVersionName
|
||||||
|
import org.mozilla.fenix.R
|
||||||
|
import org.mozilla.fenix.compose.Divider
|
||||||
|
import org.mozilla.fenix.compose.annotation.LightDarkPreview
|
||||||
|
import org.mozilla.fenix.compose.inComposePreview
|
||||||
|
import org.mozilla.fenix.compose.list.TextListItem
|
||||||
|
import org.mozilla.fenix.debugsettings.navigation.DebugDrawerDestination
|
||||||
|
import org.mozilla.fenix.theme.FirefoxTheme
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The navigation route for [DebugDrawerHome].
|
||||||
|
*/
|
||||||
|
const val DEBUG_DRAWER_HOME_ROUTE = "debug_drawer_home"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The home screen of the [DebugDrawer].
|
||||||
|
*
|
||||||
|
* @param destinations The list of [DebugDrawerDestination]s to display.
|
||||||
|
*/
|
||||||
|
@Composable
|
||||||
|
fun DebugDrawerHome(
|
||||||
|
destinations: List<DebugDrawerDestination>,
|
||||||
|
) {
|
||||||
|
val lazyListState = rememberLazyListState()
|
||||||
|
|
||||||
|
val appName: String
|
||||||
|
val appVersion: String
|
||||||
|
if (inComposePreview) {
|
||||||
|
appName = "App Name Preview"
|
||||||
|
appVersion = "100.00.000"
|
||||||
|
} else {
|
||||||
|
appName = LocalContext.current.appName
|
||||||
|
appVersion = LocalContext.current.appVersionName
|
||||||
|
}
|
||||||
|
|
||||||
|
LazyColumn(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.background(color = FirefoxTheme.colors.layer1),
|
||||||
|
state = lazyListState,
|
||||||
|
) {
|
||||||
|
item(key = "home_header") {
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(all = 16.dp)
|
||||||
|
.fillMaxWidth(),
|
||||||
|
horizontalArrangement = Arrangement.SpaceBetween,
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = appName,
|
||||||
|
color = FirefoxTheme.colors.textPrimary,
|
||||||
|
style = FirefoxTheme.typography.headline5,
|
||||||
|
)
|
||||||
|
|
||||||
|
Text(
|
||||||
|
text = appVersion,
|
||||||
|
color = FirefoxTheme.colors.textSecondary,
|
||||||
|
style = FirefoxTheme.typography.headline5,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
Divider()
|
||||||
|
}
|
||||||
|
|
||||||
|
items(
|
||||||
|
items = destinations,
|
||||||
|
key = { destination ->
|
||||||
|
destination.route
|
||||||
|
},
|
||||||
|
) { destination ->
|
||||||
|
TextListItem(
|
||||||
|
label = stringResource(id = destination.title),
|
||||||
|
onClick = destination.onClick,
|
||||||
|
)
|
||||||
|
|
||||||
|
Divider()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
@LightDarkPreview
|
||||||
|
private fun DebugDrawerHomePreview() {
|
||||||
|
val scope = rememberCoroutineScope()
|
||||||
|
val snackbarState = remember { SnackbarHostState() }
|
||||||
|
|
||||||
|
FirefoxTheme {
|
||||||
|
Box {
|
||||||
|
DebugDrawerHome(
|
||||||
|
destinations = List(size = 30) {
|
||||||
|
DebugDrawerDestination(
|
||||||
|
route = "screen_$it",
|
||||||
|
title = R.string.debug_drawer_title,
|
||||||
|
onClick = {
|
||||||
|
scope.launch {
|
||||||
|
snackbarState.showSnackbar("item $it clicked")
|
||||||
|
}
|
||||||
|
},
|
||||||
|
content = {},
|
||||||
|
)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
SnackbarHost(
|
||||||
|
hostState = snackbarState,
|
||||||
|
modifier = Modifier.align(Alignment.BottomCenter),
|
||||||
|
) { snackbarData ->
|
||||||
|
Snackbar(
|
||||||
|
snackbarData = snackbarData,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,83 @@
|
|||||||
|
/* 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.debugsettings.ui
|
||||||
|
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
|
import androidx.navigation.compose.rememberNavController
|
||||||
|
import mozilla.components.browser.state.store.BrowserStore
|
||||||
|
import mozilla.components.lib.state.ext.observeAsState
|
||||||
|
import org.mozilla.fenix.compose.annotation.LightDarkPreview
|
||||||
|
import org.mozilla.fenix.debugsettings.navigation.DebugDrawerRoute
|
||||||
|
import org.mozilla.fenix.debugsettings.store.DebugDrawerAction
|
||||||
|
import org.mozilla.fenix.debugsettings.store.DebugDrawerNavigationMiddleware
|
||||||
|
import org.mozilla.fenix.debugsettings.store.DebugDrawerStore
|
||||||
|
import org.mozilla.fenix.debugsettings.store.DrawerStatus
|
||||||
|
import org.mozilla.fenix.debugsettings.tabs.TabTools
|
||||||
|
import org.mozilla.fenix.theme.FirefoxTheme
|
||||||
|
import org.mozilla.fenix.theme.Theme
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Overlay for presenting Fenix-wide debugging content.
|
||||||
|
*
|
||||||
|
* @param browserStore [BrowserStore] used to access tab data for [TabTools].
|
||||||
|
* @param inactiveTabsEnabled Whether the inactive tabs feature is enabled.
|
||||||
|
*/
|
||||||
|
@Composable
|
||||||
|
fun FenixOverlay(
|
||||||
|
browserStore: BrowserStore,
|
||||||
|
inactiveTabsEnabled: Boolean,
|
||||||
|
) {
|
||||||
|
val navController = rememberNavController()
|
||||||
|
val coroutineScope = rememberCoroutineScope()
|
||||||
|
val debugDrawerStore = remember {
|
||||||
|
DebugDrawerStore(
|
||||||
|
middlewares = listOf(
|
||||||
|
DebugDrawerNavigationMiddleware(
|
||||||
|
navController = navController,
|
||||||
|
scope = coroutineScope,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
val debugDrawerDestinations = remember {
|
||||||
|
DebugDrawerRoute.generateDebugDrawerDestinations(
|
||||||
|
debugDrawerStore = debugDrawerStore,
|
||||||
|
browserStore = browserStore,
|
||||||
|
inactiveTabsEnabled = inactiveTabsEnabled,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
val drawerStatus by debugDrawerStore.observeAsState(initialValue = DrawerStatus.Closed) { state ->
|
||||||
|
state.drawerStatus
|
||||||
|
}
|
||||||
|
|
||||||
|
FirefoxTheme(theme = Theme.getTheme(allowPrivateTheme = false)) {
|
||||||
|
DebugOverlay(
|
||||||
|
navController = navController,
|
||||||
|
drawerStatus = drawerStatus,
|
||||||
|
debugDrawerDestinations = debugDrawerDestinations,
|
||||||
|
onDrawerOpen = {
|
||||||
|
debugDrawerStore.dispatch(DebugDrawerAction.DrawerOpened)
|
||||||
|
},
|
||||||
|
onDrawerClose = {
|
||||||
|
debugDrawerStore.dispatch(DebugDrawerAction.DrawerClosed)
|
||||||
|
},
|
||||||
|
onDrawerBackButtonClick = {
|
||||||
|
debugDrawerStore.dispatch(DebugDrawerAction.OnBackPressed)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@LightDarkPreview
|
||||||
|
@Composable
|
||||||
|
private fun FenixOverlayPreview() {
|
||||||
|
FenixOverlay(
|
||||||
|
browserStore = BrowserStore(),
|
||||||
|
inactiveTabsEnabled = true,
|
||||||
|
)
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue