Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add preference to configure new tab behavior #72

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand All @@ -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) },
)
}
}
}
}
Expand All @@ -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,
)

Expand Down Expand Up @@ -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,
)
}
}
6 changes: 6 additions & 0 deletions app/src/main/java/net/waterfox/android/ext/Context.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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,
Expand Down Expand Up @@ -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 = {},
),
),
)
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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,
Expand Down
Loading