diff --git a/app/src/main/java/net/waterfox/android/compose/preference/RadioGroupPreference.kt b/app/src/main/java/net/waterfox/android/compose/preference/RadioGroupPreference.kt
index 1cfbda465..f609f8869 100644
--- a/app/src/main/java/net/waterfox/android/compose/preference/RadioGroupPreference.kt
+++ b/app/src/main/java/net/waterfox/android/compose/preference/RadioGroupPreference.kt
@@ -5,29 +5,56 @@
package net.waterfox.android.compose.preference
import android.content.res.Configuration
+import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.selection.toggleable
+import androidx.compose.foundation.text.BasicTextField
+import androidx.compose.foundation.text.KeyboardActions
+import androidx.compose.foundation.text.KeyboardOptions
+import androidx.compose.foundation.text.selection.LocalTextSelectionColors
+import androidx.compose.foundation.text.selection.TextSelectionColors
+import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.material.RadioButton
import androidx.compose.material.RadioButtonDefaults
import androidx.compose.material.Text
+import androidx.compose.material.TextFieldDefaults
+import androidx.compose.material.TextFieldDefaults.indicatorLine
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.CompositionLocalProvider
+import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
+import androidx.compose.ui.focus.FocusRequester
+import androidx.compose.ui.focus.focusRequester
+import androidx.compose.ui.graphics.Color.Companion.Green
+import androidx.compose.ui.graphics.Color.Companion.Red
+import androidx.compose.ui.graphics.SolidColor
import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.platform.LocalFocusManager
+import androidx.compose.ui.platform.LocalSoftwareKeyboardController
+import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.Role
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.semantics.testTag
import androidx.compose.ui.semantics.testTagsAsResourceId
+import androidx.compose.ui.text.TextStyle
+import androidx.compose.ui.text.input.ImeAction
+import androidx.compose.ui.text.input.KeyboardCapitalization
+import androidx.compose.ui.text.input.VisualTransformation
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import net.waterfox.android.ext.readBooleanPreference
import net.waterfox.android.ext.writeBooleanPreference
import net.waterfox.android.theme.Theme
import net.waterfox.android.theme.WaterfoxTheme
+import net.waterfox.android.R
+import net.waterfox.android.ext.readStringPreference
+import net.waterfox.android.ext.writeStringPreference
@Composable
fun RadioGroupPreference(
@@ -41,17 +68,33 @@ fun RadioGroupPreference(
}
return Column {
+ val onValueChange: (Boolean, RadioGroupItem) -> Unit = { _, item ->
+ items.forEach {
+ context.writeBooleanPreference(
+ it.key,
+ it.key == item.key,
+ )
+ }
+ item.onClick?.invoke()
+ setSelected(item)
+ }
items.forEach { item ->
if (item.visible) {
- RadioButtonPreference(
- title = item.title,
- selected = selected?.key == item.key,
- onValueChange = {
- items.forEach { context.writeBooleanPreference(it.key, it.key == item.key) }
- item.onClick?.invoke()
- setSelected(item)
- },
- )
+ if (item.editable) {
+ val key = stringResource(R.string.pref_key_new_tab_web_address_value)
+ RadioButtonWithInputPreference(
+ value = context.readStringPreference(key, "")!!,
+ selected = selected?.key == item.key,
+ onValueChange = { onValueChange(it, item) },
+ onInputValueChange = { context.writeStringPreference(key, it) },
+ )
+ } else {
+ RadioButtonPreference(
+ title = item.title,
+ selected = selected?.key == item.key,
+ onValueChange = { onValueChange(it, item) },
+ )
+ }
}
}
}
@@ -62,6 +105,7 @@ data class RadioGroupItem(
val key: String,
val defaultValue: Boolean,
val visible: Boolean = true,
+ val editable: Boolean = false,
val onClick: (() -> Unit)? = null,
)
@@ -140,3 +184,162 @@ private fun RadioButtonPreferenceOffPreview() {
)
}
}
+
+
+@OptIn(ExperimentalComposeUiApi::class)
+@Composable
+fun RadioButtonWithInputPreference(
+ value: String,
+ selected: Boolean,
+ onValueChange: (Boolean) -> Unit,
+ onInputValueChange: (String) -> Unit,
+ enabled: Boolean = true,
+) {
+ val focusManager = LocalFocusManager.current
+ val keyboardController = LocalSoftwareKeyboardController.current
+ return Row(
+ modifier = Modifier
+ .fillMaxWidth()
+ .toggleable(
+ value = selected,
+ onValueChange = { newValue ->
+ if (enabled) {
+ onValueChange(newValue)
+ }
+ },
+ role = Role.RadioButton,
+ )
+ .alpha(if (enabled) 1f else 0.5f)
+ .semantics {
+ testTagsAsResourceId = true
+ testTag = "radio.button.preference"
+ },
+ ) {
+ RadioButton(
+ selected = selected,
+ onClick = null,
+ modifier = Modifier
+ .size(48.dp)
+ .padding(start = 16.dp),
+ colors = RadioButtonDefaults.colors(
+ selectedColor = WaterfoxTheme.colors.formSelected,
+ unselectedColor = WaterfoxTheme.colors.formDefault,
+ ),
+ )
+
+ TextField(
+ text = value,
+ onValueChange = {
+ onInputValueChange(it)
+ keyboardController?.hide()
+ focusManager.clearFocus()
+ },
+ selected = selected,
+ modifier = Modifier
+ .align(Alignment.CenterVertically),
+ )
+ }
+}
+
+@OptIn(ExperimentalMaterialApi::class)
+@Composable
+private fun TextField(
+ text: String,
+ onValueChange: (String) -> Unit,
+ selected: Boolean,
+ modifier: Modifier = Modifier,
+) {
+ var value by remember { mutableStateOf(text) }
+ val interactionSource = remember { MutableInteractionSource() }
+ val customTextSelectionColors = TextSelectionColors(
+ handleColor = WaterfoxTheme.colors.formSelected,
+ backgroundColor = WaterfoxTheme.colors.formSelected.copy(alpha = 0.4f),
+ )
+ CompositionLocalProvider(LocalTextSelectionColors provides customTextSelectionColors) {
+ BasicTextField(
+ value = value,
+ onValueChange = { value = it },
+ modifier = modifier
+ .fillMaxWidth()
+ .padding(
+ start = 24.dp,
+ end = 16.dp,
+ )
+ .indicatorLine(
+ enabled = selected,
+ false,
+ interactionSource,
+ TextFieldDefaults.textFieldColors(
+ unfocusedIndicatorColor = WaterfoxTheme.colors.formDisabled,
+ focusedIndicatorColor = WaterfoxTheme.colors.formSelected,
+ ),
+ ),
+ singleLine = true,
+ enabled = selected,
+ textStyle = WaterfoxTheme.typography.subtitle1.merge(
+ TextStyle(color = WaterfoxTheme.colors.textPrimary),
+ ),
+ cursorBrush = SolidColor(WaterfoxTheme.colors.formSelected),
+ keyboardOptions = KeyboardOptions(
+ imeAction = ImeAction.Done,
+ capitalization = KeyboardCapitalization.None,
+ ),
+ keyboardActions = KeyboardActions(onDone = { onValueChange(value) }),
+ ) { innerTextField ->
+ TextFieldDefaults.TextFieldDecorationBox(
+ value = value,
+ visualTransformation = VisualTransformation.None,
+ innerTextField = innerTextField,
+ placeholder = {
+ Text(
+ text = stringResource(id = R.string.preferences_open_new_tab_web_address),
+ modifier = Modifier.fillMaxWidth(),
+ color = WaterfoxTheme.colors.textSecondary,
+ style = WaterfoxTheme.typography.subtitle1,
+ )
+ },
+ singleLine = true,
+ enabled = selected,
+ interactionSource = interactionSource,
+ contentPadding = TextFieldDefaults.textFieldWithoutLabelPadding(
+ start = 0.dp, end = 0.dp,
+ ),
+ colors = TextFieldDefaults.textFieldColors(
+ textColor = WaterfoxTheme.colors.textPrimary,
+ cursorColor = WaterfoxTheme.colors.formSelected,
+ ),
+ )
+ }
+ }
+}
+
+
+@Preview(uiMode = Configuration.UI_MODE_NIGHT_NO, showBackground = true)
+@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES, showBackground = true)
+@Composable
+private fun RadioButtonWithInputPreferenceOnPreview() {
+ WaterfoxTheme(theme = Theme.getTheme()) {
+ RadioButtonWithInputPreference(
+ value = "example.com",
+ selected = true,
+ onValueChange = {},
+ onInputValueChange = {},
+ enabled = true,
+ )
+ }
+}
+
+@Preview(uiMode = Configuration.UI_MODE_NIGHT_NO, showBackground = true)
+@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES, showBackground = true)
+@Composable
+private fun RadioButtonWithInputPreferenceOffPreview() {
+ WaterfoxTheme(theme = Theme.getTheme()) {
+ RadioButtonWithInputPreference(
+ value = "example.com",
+ selected = false,
+ onValueChange = {},
+ onInputValueChange = {},
+ enabled = true,
+ )
+ }
+}
diff --git a/app/src/main/java/net/waterfox/android/ext/Context.kt b/app/src/main/java/net/waterfox/android/ext/Context.kt
index eff33aec6..3cf679187 100644
--- a/app/src/main/java/net/waterfox/android/ext/Context.kt
+++ b/app/src/main/java/net/waterfox/android/ext/Context.kt
@@ -51,6 +51,12 @@ fun Context.readFloatPreference(key: String, defaultValue: Float) =
fun Context.writeFloatPreference(key: String, value: Float) =
settings().preferences.edit().putFloat(key, value).apply()
+fun Context.readStringPreference(key: String, defaultValue: String) =
+ settings().preferences.getString(key, defaultValue)
+
+fun Context.writeStringPreference(key: String, value: String) =
+ settings().preferences.edit().putString(key, value).apply()
+
/**
* Gets the Root View with an activity context
*
diff --git a/app/src/main/java/net/waterfox/android/settings/TabsSettingsComposeView.kt b/app/src/main/java/net/waterfox/android/settings/TabsSettingsComposeView.kt
index 511106f60..e752df4ac 100644
--- a/app/src/main/java/net/waterfox/android/settings/TabsSettingsComposeView.kt
+++ b/app/src/main/java/net/waterfox/android/settings/TabsSettingsComposeView.kt
@@ -7,10 +7,15 @@ package net.waterfox.android.settings
import android.content.Context
import android.util.AttributeSet
import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.imePadding
+import androidx.compose.foundation.rememberScrollState
+import androidx.compose.foundation.verticalScroll
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
+import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.AbstractComposeView
import androidx.compose.ui.res.stringResource
import net.waterfox.android.R
@@ -35,7 +40,12 @@ class TabsSettingsComposeView @JvmOverloads constructor(
@Composable
override fun Content() {
WaterfoxTheme {
- Column {
+ Column(
+ modifier = Modifier
+ .fillMaxSize()
+ .verticalScroll(rememberScrollState())
+ .imePadding(),
+ ) {
PreferenceCategory(
title = stringResource(R.string.preferences_tab_view),
allowDividerAbove = false,
@@ -98,6 +108,34 @@ class TabsSettingsComposeView @JvmOverloads constructor(
enabled = inactiveTabsCategoryEnabled,
)
}
+
+ PreferenceCategory(
+ title = stringResource(id = R.string.preferences_open_new_tab),
+ ) {
+ RadioGroupPreference(
+ items = listOf(
+ RadioGroupItem(
+ title = stringResource(id = R.string.preferences_open_new_tab_show_home),
+ key = stringResource(R.string.pref_key_new_tab_show_home),
+ defaultValue = true,
+ onClick = {},
+ ),
+ RadioGroupItem(
+ title = stringResource(id = R.string.preferences_open_new_tab_blank_tab),
+ key = stringResource(R.string.pref_key_new_tab_blank),
+ defaultValue = false,
+ onClick = {},
+ ),
+ RadioGroupItem(
+ title = "",
+ key = stringResource(R.string.pref_key_new_tab_web_address),
+ defaultValue = false,
+ editable = true,
+ onClick = {},
+ ),
+ ),
+ )
+ }
}
}
}
diff --git a/app/src/main/java/net/waterfox/android/tabstray/TabsTrayController.kt b/app/src/main/java/net/waterfox/android/tabstray/TabsTrayController.kt
index b0ee076fc..feea9fc24 100644
--- a/app/src/main/java/net/waterfox/android/tabstray/TabsTrayController.kt
+++ b/app/src/main/java/net/waterfox/android/tabstray/TabsTrayController.kt
@@ -36,6 +36,7 @@ import net.waterfox.android.components.appstate.AppAction
import net.waterfox.android.components.bookmarks.BookmarksUseCase
import net.waterfox.android.ext.DEFAULT_ACTIVE_DAYS
import net.waterfox.android.ext.potentialInactiveTabs
+import net.waterfox.android.ext.settings
import net.waterfox.android.home.HomeFragment
import net.waterfox.android.library.bookmarks.BookmarksSharedViewModel
import net.waterfox.android.tabstray.browser.InactiveTabsController
@@ -240,10 +241,35 @@ class DefaultTabsTrayController(
private fun openNewTab(isPrivate: Boolean) {
val startTime = profiler?.getProfilerTime()
browsingModeManager.mode = BrowsingMode.fromBoolean(isPrivate)
- navController.navigate(
- TabsTrayFragmentDirections.actionGlobalHome(focusOnAddressBar = true),
- )
- navigationInteractor.onTabTrayDismissed()
+
+ val settings = navController.context.settings()
+ if (settings.openTabShowHome) {
+ navController.navigate(
+ TabsTrayFragmentDirections.actionGlobalHome(focusOnAddressBar = true),
+ )
+ navigationInteractor.onTabTrayDismissed()
+ } else {
+ val url = if (settings.openTabShowBlank) {
+ "about:blank"
+ } else {
+ val address = settings.openTabShowWebAddressValue
+ if (address.startsWith("http")) {
+ address
+ } else {
+ "https://$address"
+ }
+ }
+ val tab = tabsUseCases.addTab(
+ url,
+ selectTab = false,
+ startLoading = true,
+ parentId = null,
+ contextId = null,
+ )
+ tabsUseCases.selectTab(tab)
+ handleNavigateToBrowser()
+ }
+
profiler?.addMarker(
"DefaultTabTrayController.onNewTabTapped",
startTime,
diff --git a/app/src/main/java/net/waterfox/android/utils/Settings.kt b/app/src/main/java/net/waterfox/android/utils/Settings.kt
index 3b9b8c837..81211eef2 100644
--- a/app/src/main/java/net/waterfox/android/utils/Settings.kt
+++ b/app/src/main/java/net/waterfox/android/utils/Settings.kt
@@ -278,6 +278,26 @@ class Settings(private val appContext: Context) : PreferencesHolder {
default = false
)
+ var openTabShowHome by booleanPreference(
+ appContext.getPreferenceKey(R.string.pref_key_new_tab_show_home),
+ default = true
+ )
+
+ var openTabShowBlank by booleanPreference(
+ appContext.getPreferenceKey(R.string.pref_key_new_tab_blank),
+ default = false
+ )
+
+ var openTabShowWebAddress by booleanPreference(
+ appContext.getPreferenceKey(R.string.pref_key_new_tab_web_address),
+ default = true
+ )
+
+ var openTabShowWebAddressValue by stringPreference(
+ appContext.getPreferenceKey(R.string.pref_key_new_tab_web_address_value),
+ default = ""
+ )
+
var allowThirdPartyRootCerts by booleanPreference(
appContext.getPreferenceKey(R.string.pref_key_allow_third_party_root_certs),
default = false
diff --git a/app/src/main/res/values/preference_keys.xml b/app/src/main/res/values/preference_keys.xml
index 2523e9360..d1af9a45c 100644
--- a/app/src/main/res/values/preference_keys.xml
+++ b/app/src/main/res/values/preference_keys.xml
@@ -263,6 +263,10 @@
pref_key_camera_permissions_needed
pref_key_inactive_tabs_category
pref_key_inactive_tabs
+ pref_key_new_tab_show_home
+ pref_key_new_tab_blank
+ pref_key_new_tab_web_address
+ pref_key_new_tab_web_address_value
pref_key_return_to_browser
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 795be3084..7803dad1a 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -662,6 +662,13 @@
Tabs you haven’t viewed for two weeks get moved to the inactive section.
+
+
+ Open new tab
+ Show home
+ Blank tab
+ Enter web address
+
Open tabs