Settings facelift
This commit is contained in:
parent
fcc111b048
commit
b6d533466b
@ -10,7 +10,7 @@ import androidx.compose.ui.graphics.vector.ImageVector
|
|||||||
fun CheckboxPreference(
|
fun CheckboxPreference(
|
||||||
title: String,
|
title: String,
|
||||||
icon: ImageVector? = null,
|
icon: ImageVector? = null,
|
||||||
iconPadding: Boolean = true,
|
iconPadding: Boolean = icon != null,
|
||||||
summary: String? = null,
|
summary: String? = null,
|
||||||
value: Boolean,
|
value: Boolean,
|
||||||
onValueChanged: (Boolean) -> Unit,
|
onValueChanged: (Boolean) -> Unit,
|
||||||
|
|||||||
@ -0,0 +1,54 @@
|
|||||||
|
package de.mm20.launcher2.ui.component.preferences
|
||||||
|
|
||||||
|
import androidx.compose.foundation.background
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.rounded.Lock
|
||||||
|
import androidx.compose.material3.Button
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.draw.clip
|
||||||
|
import androidx.compose.ui.graphics.vector.ImageVector
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import de.mm20.launcher2.ui.R
|
||||||
|
import de.mm20.launcher2.ui.component.Banner
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun GuardedPreference(
|
||||||
|
locked: Boolean,
|
||||||
|
onUnlock: (() -> Unit)? = null,
|
||||||
|
description: String,
|
||||||
|
icon: ImageVector = Icons.Rounded.Lock,
|
||||||
|
unlockLabel: String = stringResource(R.string.grant_permission),
|
||||||
|
preference: @Composable () -> Unit,
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.clip(MaterialTheme.shapes.extraSmall)
|
||||||
|
.background(MaterialTheme.colorScheme.surface)
|
||||||
|
) {
|
||||||
|
if (locked) {
|
||||||
|
Banner(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(top = 16.dp, start = 16.dp, end = 16.dp, bottom = 8.dp),
|
||||||
|
icon = icon,
|
||||||
|
text = description,
|
||||||
|
primaryAction = if (onUnlock != null) {
|
||||||
|
{
|
||||||
|
Button(onClick = onUnlock) {
|
||||||
|
Text(text = unlockLabel)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else null
|
||||||
|
)
|
||||||
|
}
|
||||||
|
preference()
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -17,7 +17,7 @@ import androidx.compose.ui.window.Dialog
|
|||||||
fun <T> ListPreference(
|
fun <T> ListPreference(
|
||||||
title: String,
|
title: String,
|
||||||
icon: ImageVector? = null,
|
icon: ImageVector? = null,
|
||||||
iconPadding: Boolean = true,
|
iconPadding: Boolean = icon != null,
|
||||||
items: List<ListPreferenceItem<T>>,
|
items: List<ListPreferenceItem<T>>,
|
||||||
value: T,
|
value: T,
|
||||||
summary: String? = items.firstOrNull { value == it.value }?.label,
|
summary: String? = items.firstOrNull { value == it.value }?.label,
|
||||||
|
|||||||
@ -1,15 +1,21 @@
|
|||||||
package de.mm20.launcher2.ui.component.preferences
|
package de.mm20.launcher2.ui.component.preferences
|
||||||
|
|
||||||
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.layout.*
|
import androidx.compose.foundation.layout.*
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.material3.LocalContentColor
|
||||||
|
import androidx.compose.material3.LocalTextStyle
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.ProvideTextStyle
|
import androidx.compose.material3.ProvideTextStyle
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.CompositionLocalProvider
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.draw.alpha
|
import androidx.compose.ui.draw.alpha
|
||||||
|
import androidx.compose.ui.draw.clip
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.graphics.vector.ImageVector
|
import androidx.compose.ui.graphics.vector.ImageVector
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
|
|
||||||
@ -20,22 +26,28 @@ fun Preference(
|
|||||||
icon: @Composable (() -> Unit)? = null,
|
icon: @Composable (() -> Unit)? = null,
|
||||||
onClick: () -> Unit = {},
|
onClick: () -> Unit = {},
|
||||||
controls: @Composable (() -> Unit)? = null,
|
controls: @Composable (() -> Unit)? = null,
|
||||||
enabled: Boolean = true
|
enabled: Boolean = true,
|
||||||
|
containerColor: Color = MaterialTheme.colorScheme.surface,
|
||||||
) {
|
) {
|
||||||
Row(
|
Row(
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
|
.clip(MaterialTheme.shapes.extraSmall)
|
||||||
.clickable(enabled = enabled, onClick = onClick)
|
.clickable(enabled = enabled, onClick = onClick)
|
||||||
.padding(horizontal = 16.dp)
|
.background(containerColor)
|
||||||
|
.padding(
|
||||||
|
start = if (icon != null) 8.dp else 16.dp,
|
||||||
|
end = 16.dp,
|
||||||
|
)
|
||||||
.alpha(if (enabled) 1f else 0.38f),
|
.alpha(if (enabled) 1f else 0.38f),
|
||||||
) {
|
) {
|
||||||
if (icon != null) {
|
if (icon != null) {
|
||||||
Box(
|
Box(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.width(56.dp)
|
.width(56.dp)
|
||||||
.padding(start = 4.dp),
|
.padding(end = 8.dp),
|
||||||
contentAlignment = Alignment.CenterStart
|
contentAlignment = Alignment.Center
|
||||||
) {
|
) {
|
||||||
icon()
|
icon()
|
||||||
}
|
}
|
||||||
@ -45,14 +57,16 @@ fun Preference(
|
|||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.weight(1f)
|
.weight(1f)
|
||||||
.padding(vertical = 16.dp)
|
.padding(vertical = 16.dp),
|
||||||
) {
|
) {
|
||||||
ProvideTextStyle(value = MaterialTheme.typography.titleMedium) {
|
ProvideTextStyle(value = MaterialTheme.typography.titleMedium) {
|
||||||
title()
|
title()
|
||||||
}
|
}
|
||||||
if (summary != null) {
|
if (summary != null) {
|
||||||
Spacer(modifier = Modifier.height(2.dp))
|
CompositionLocalProvider(
|
||||||
ProvideTextStyle(value = MaterialTheme.typography.bodyMedium) {
|
LocalTextStyle provides MaterialTheme.typography.bodyMedium,
|
||||||
|
LocalContentColor provides MaterialTheme.colorScheme.onSurfaceVariant,
|
||||||
|
) {
|
||||||
summary()
|
summary()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -75,7 +89,8 @@ fun Preference(
|
|||||||
summary: String? = null,
|
summary: String? = null,
|
||||||
onClick: () -> Unit = {},
|
onClick: () -> Unit = {},
|
||||||
controls: @Composable (() -> Unit)? = null,
|
controls: @Composable (() -> Unit)? = null,
|
||||||
enabled: Boolean = true
|
enabled: Boolean = true,
|
||||||
|
containerColor: Color = MaterialTheme.colorScheme.surface,
|
||||||
) {
|
) {
|
||||||
Preference(
|
Preference(
|
||||||
title = {
|
title = {
|
||||||
@ -89,7 +104,8 @@ fun Preference(
|
|||||||
icon = if (iconPadding) icon else null,
|
icon = if (iconPadding) icon else null,
|
||||||
onClick = onClick,
|
onClick = onClick,
|
||||||
controls = controls,
|
controls = controls,
|
||||||
enabled = enabled
|
enabled = enabled,
|
||||||
|
containerColor = containerColor,
|
||||||
)
|
)
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -98,11 +114,12 @@ fun Preference(
|
|||||||
fun Preference(
|
fun Preference(
|
||||||
title: String,
|
title: String,
|
||||||
icon: ImageVector? = null,
|
icon: ImageVector? = null,
|
||||||
iconPadding: Boolean = true,
|
iconPadding: Boolean = icon != null,
|
||||||
summary: String? = null,
|
summary: String? = null,
|
||||||
onClick: () -> Unit = {},
|
onClick: () -> Unit = {},
|
||||||
controls: @Composable (() -> Unit)? = null,
|
controls: @Composable (() -> Unit)? = null,
|
||||||
enabled: Boolean = true
|
enabled: Boolean = true,
|
||||||
|
containerColor: Color = MaterialTheme.colorScheme.surface,
|
||||||
) {
|
) {
|
||||||
Preference(
|
Preference(
|
||||||
title,
|
title,
|
||||||
@ -114,6 +131,6 @@ fun Preference(
|
|||||||
tint = MaterialTheme.colorScheme.primary,
|
tint = MaterialTheme.colorScheme.primary,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}, iconPadding, summary, onClick, controls, enabled
|
}, iconPadding, summary, onClick, controls, enabled, containerColor,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -6,6 +6,7 @@ import androidx.compose.material3.MaterialTheme
|
|||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.draw.clip
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
|
|
||||||
|
|
||||||
@ -15,25 +16,27 @@ fun PreferenceCategory(
|
|||||||
iconPadding: Boolean = true,
|
iconPadding: Boolean = true,
|
||||||
content: @Composable ColumnScope.() -> Unit
|
content: @Composable ColumnScope.() -> Unit
|
||||||
) {
|
) {
|
||||||
Column {
|
Column(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
) {
|
||||||
if (title != null) {
|
if (title != null) {
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.padding(start = 16.dp, top = 16.dp, end = 16.dp)
|
.padding(start = 16.dp, top = 16.dp, end = 16.dp, bottom = 8.dp)
|
||||||
) {
|
) {
|
||||||
Text(
|
Text(
|
||||||
modifier = Modifier.padding(start = if (iconPadding) 56.dp else 0.dp),
|
|
||||||
text = title,
|
text = title,
|
||||||
style = MaterialTheme.typography.titleSmall,
|
style = MaterialTheme.typography.titleSmall,
|
||||||
color = MaterialTheme.colorScheme.primary
|
color = MaterialTheme.colorScheme.secondary
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
content()
|
Column(
|
||||||
Box(
|
modifier = Modifier
|
||||||
modifier = Modifier.fillMaxWidth().height(0.5.dp).background(
|
.clip(MaterialTheme.shapes.medium),
|
||||||
MaterialTheme.colorScheme.onSurface.copy(alpha = 0.12f)
|
verticalArrangement = Arrangement.spacedBy(2.dp)
|
||||||
)
|
) {
|
||||||
)
|
content()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,12 +1,13 @@
|
|||||||
package de.mm20.launcher2.ui.component.preferences
|
package de.mm20.launcher2.ui.component.preferences
|
||||||
|
|
||||||
import android.net.Uri
|
import androidx.activity.compose.LocalActivity
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
|
||||||
import androidx.browser.customtabs.CustomTabColorSchemeParams
|
import androidx.browser.customtabs.CustomTabColorSchemeParams
|
||||||
import androidx.browser.customtabs.CustomTabsIntent
|
import androidx.browser.customtabs.CustomTabsIntent
|
||||||
import androidx.compose.animation.AnimatedVisibility
|
import androidx.compose.animation.AnimatedVisibility
|
||||||
import androidx.compose.animation.scaleIn
|
import androidx.compose.animation.scaleIn
|
||||||
import androidx.compose.animation.scaleOut
|
import androidx.compose.animation.scaleOut
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
import androidx.compose.foundation.layout.RowScope
|
import androidx.compose.foundation.layout.RowScope
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
@ -16,14 +17,15 @@ import androidx.compose.foundation.lazy.LazyListState
|
|||||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.automirrored.rounded.ArrowBack
|
import androidx.compose.material.icons.automirrored.rounded.ArrowBack
|
||||||
import androidx.compose.material.icons.rounded.ArrowBack
|
|
||||||
import androidx.compose.material.icons.rounded.HelpOutline
|
import androidx.compose.material.icons.rounded.HelpOutline
|
||||||
import androidx.compose.material3.CenterAlignedTopAppBar
|
import androidx.compose.material3.CenterAlignedTopAppBar
|
||||||
|
import androidx.compose.material3.FilledTonalIconButton
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.IconButton
|
import androidx.compose.material3.IconButton
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Scaffold
|
import androidx.compose.material3.Scaffold
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.material3.TopAppBarDefaults
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
@ -31,18 +33,18 @@ import androidx.compose.runtime.remember
|
|||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.geometry.Offset
|
import androidx.compose.ui.geometry.Offset
|
||||||
import androidx.compose.ui.graphics.Color
|
|
||||||
import androidx.compose.ui.graphics.toArgb
|
import androidx.compose.ui.graphics.toArgb
|
||||||
import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
|
import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
|
||||||
import androidx.compose.ui.input.nestedscroll.NestedScrollSource
|
import androidx.compose.ui.input.nestedscroll.NestedScrollSource
|
||||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.platform.LocalViewConfiguration
|
import androidx.compose.ui.platform.LocalViewConfiguration
|
||||||
import androidx.compose.ui.platform.ViewConfiguration
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.text.style.TextOverflow
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import com.google.accompanist.systemuicontroller.rememberSystemUiController
|
import androidx.core.net.toUri
|
||||||
import de.mm20.launcher2.ui.locals.LocalNavController
|
import de.mm20.launcher2.ui.locals.LocalNavController
|
||||||
|
import de.mm20.launcher2.ui.R
|
||||||
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
@ -89,7 +91,7 @@ fun PreferenceScreen(
|
|||||||
val touchSlop = LocalViewConfiguration.current.touchSlop
|
val touchSlop = LocalViewConfiguration.current.touchSlop
|
||||||
var fabVisible by remember { mutableStateOf(true) }
|
var fabVisible by remember { mutableStateOf(true) }
|
||||||
val nestedScrollConnection = remember {
|
val nestedScrollConnection = remember {
|
||||||
object: NestedScrollConnection {
|
object : NestedScrollConnection {
|
||||||
override fun onPostScroll(
|
override fun onPostScroll(
|
||||||
consumed: Offset,
|
consumed: Offset,
|
||||||
available: Offset,
|
available: Offset,
|
||||||
@ -102,7 +104,9 @@ fun PreferenceScreen(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val activity = LocalContext.current as? AppCompatActivity
|
val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior()
|
||||||
|
|
||||||
|
val activity = LocalActivity.current
|
||||||
Scaffold(
|
Scaffold(
|
||||||
floatingActionButton = {
|
floatingActionButton = {
|
||||||
AnimatedVisibility(
|
AnimatedVisibility(
|
||||||
@ -116,13 +120,20 @@ fun PreferenceScreen(
|
|||||||
topBar = {
|
topBar = {
|
||||||
CenterAlignedTopAppBar(
|
CenterAlignedTopAppBar(
|
||||||
title = title,
|
title = title,
|
||||||
|
colors = TopAppBarDefaults.topAppBarColors(
|
||||||
|
containerColor = MaterialTheme.colorScheme.surfaceContainer,
|
||||||
|
scrolledContainerColor = MaterialTheme.colorScheme.surfaceContainerHigh,
|
||||||
|
),
|
||||||
navigationIcon = {
|
navigationIcon = {
|
||||||
IconButton(onClick = {
|
IconButton(onClick = {
|
||||||
if (navController?.navigateUp() != true) {
|
if (navController?.navigateUp() != true) {
|
||||||
activity?.onBackPressed()
|
activity?.onBackPressed()
|
||||||
}
|
}
|
||||||
}) {
|
}) {
|
||||||
Icon(imageVector = Icons.AutoMirrored.Rounded.ArrowBack, contentDescription = "Back")
|
Icon(
|
||||||
|
imageVector = Icons.AutoMirrored.Rounded.ArrowBack,
|
||||||
|
contentDescription = "Back"
|
||||||
|
)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
actions = {
|
actions = {
|
||||||
@ -135,25 +146,32 @@ fun PreferenceScreen(
|
|||||||
.setSecondaryToolbarColor(colorScheme.secondaryContainer.toArgb())
|
.setSecondaryToolbarColor(colorScheme.secondaryContainer.toArgb())
|
||||||
.build()
|
.build()
|
||||||
)
|
)
|
||||||
.build().launchUrl(context, Uri.parse(helpUrl))
|
.build().launchUrl(context, helpUrl.toUri())
|
||||||
}) {
|
}) {
|
||||||
Icon(
|
Icon(
|
||||||
imageVector = Icons.Rounded.HelpOutline,
|
imageVector = Icons.Rounded.HelpOutline,
|
||||||
contentDescription = "Help"
|
contentDescription = stringResource(R.string.help)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
topBarActions()
|
topBarActions()
|
||||||
}
|
},
|
||||||
|
scrollBehavior = scrollBehavior,
|
||||||
)
|
)
|
||||||
}) {
|
},
|
||||||
|
containerColor = MaterialTheme.colorScheme.surfaceContainer
|
||||||
|
) {
|
||||||
LazyColumn(
|
LazyColumn(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
.nestedScroll(nestedScrollConnection)
|
.nestedScroll(scrollBehavior.nestedScrollConnection)
|
||||||
.padding(it),
|
.padding(it),
|
||||||
state = lazyColumnState,
|
state = lazyColumnState,
|
||||||
content = content,
|
content = content,
|
||||||
|
verticalArrangement = Arrangement.spacedBy(12.dp),
|
||||||
|
contentPadding = PaddingValues(
|
||||||
|
12.dp
|
||||||
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -3,6 +3,7 @@ package de.mm20.launcher2.ui.component.preferences
|
|||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.layout.*
|
import androidx.compose.foundation.layout.*
|
||||||
import androidx.compose.material3.LocalContentColor
|
import androidx.compose.material3.LocalContentColor
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Switch
|
import androidx.compose.material3.Switch
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
@ -20,10 +21,11 @@ fun PreferenceWithSwitch(
|
|||||||
onClick: () -> Unit = {},
|
onClick: () -> Unit = {},
|
||||||
switchValue: Boolean,
|
switchValue: Boolean,
|
||||||
onSwitchChanged: (Boolean) -> Unit,
|
onSwitchChanged: (Boolean) -> Unit,
|
||||||
iconPadding: Boolean = true
|
iconPadding: Boolean = icon != null
|
||||||
) {
|
) {
|
||||||
Row(
|
Row(
|
||||||
verticalAlignment = (Alignment.CenterVertically)
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
modifier = Modifier.background(MaterialTheme.colorScheme.surface, MaterialTheme.shapes.extraSmall)
|
||||||
) {
|
) {
|
||||||
Box(
|
Box(
|
||||||
modifier = Modifier.weight(1f)
|
modifier = Modifier.weight(1f)
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
package de.mm20.launcher2.ui.component.preferences
|
package de.mm20.launcher2.ui.component.preferences
|
||||||
|
|
||||||
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.layout.*
|
import androidx.compose.foundation.layout.*
|
||||||
import androidx.compose.material3.Slider
|
import androidx.compose.material3.Slider
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
@ -10,6 +11,7 @@ import androidx.compose.ui.Alignment
|
|||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.draw.alpha
|
import androidx.compose.ui.draw.alpha
|
||||||
import androidx.compose.ui.graphics.vector.ImageVector
|
import androidx.compose.ui.graphics.vector.ImageVector
|
||||||
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import java.text.DecimalFormat
|
import java.text.DecimalFormat
|
||||||
import kotlin.math.floor
|
import kotlin.math.floor
|
||||||
@ -20,6 +22,7 @@ import kotlin.math.roundToInt
|
|||||||
fun SliderPreference(
|
fun SliderPreference(
|
||||||
title: String,
|
title: String,
|
||||||
icon: ImageVector? = null,
|
icon: ImageVector? = null,
|
||||||
|
iconPadding: Boolean = icon != null,
|
||||||
value: Float,
|
value: Float,
|
||||||
min: Float = 0f,
|
min: Float = 0f,
|
||||||
max: Float = 1f,
|
max: Float = 1f,
|
||||||
@ -33,25 +36,32 @@ fun SliderPreference(
|
|||||||
verticalAlignment = Alignment.CenterVertically,
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.padding(horizontal = 16.dp, vertical = 16.dp)
|
.background(MaterialTheme.colorScheme.surface, MaterialTheme.shapes.extraSmall)
|
||||||
|
.padding(
|
||||||
|
start = if (icon != null || iconPadding) 8.dp else 16.dp,
|
||||||
|
end = 16.dp,
|
||||||
|
)
|
||||||
.alpha(if (enabled) 1f else 0.38f),
|
.alpha(if (enabled) 1f else 0.38f),
|
||||||
) {
|
) {
|
||||||
Box(
|
if (icon != null || iconPadding) {
|
||||||
modifier = Modifier
|
Box(
|
||||||
.width(54.dp)
|
modifier = Modifier
|
||||||
.padding(start = 4.dp),
|
.width(56.dp)
|
||||||
contentAlignment = Alignment.CenterStart
|
.padding(end = 8.dp),
|
||||||
) {
|
contentAlignment = Alignment.Center
|
||||||
if (icon != null) {
|
) {
|
||||||
Icon(
|
if (icon != null) {
|
||||||
imageVector = icon,
|
Icon(
|
||||||
contentDescription = null,
|
imageVector = icon,
|
||||||
tint = MaterialTheme.colorScheme.primary,
|
contentDescription = null,
|
||||||
)
|
tint = MaterialTheme.colorScheme.primary,
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier.weight(1f)
|
modifier = Modifier.weight(1f)
|
||||||
|
.padding(vertical = 16.dp)
|
||||||
) {
|
) {
|
||||||
Text(
|
Text(
|
||||||
modifier = Modifier.padding(start = 2.dp),
|
modifier = Modifier.padding(start = 2.dp),
|
||||||
@ -71,7 +81,7 @@ fun SliderPreference(
|
|||||||
steps = step?.let { ((max - min) / it).toInt() - 1 } ?: 0,
|
steps = step?.let { ((max - min) / it).toInt() - 1 } ?: 0,
|
||||||
onValueChangeFinished = {
|
onValueChangeFinished = {
|
||||||
onValueChanged(sliderValue)
|
onValueChanged(sliderValue)
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
if (label != null) {
|
if (label != null) {
|
||||||
label(sliderValue)
|
label(sliderValue)
|
||||||
@ -84,7 +94,8 @@ fun SliderPreference(
|
|||||||
Text(
|
Text(
|
||||||
modifier = Modifier.width(56.dp).padding(start = 24.dp),
|
modifier = Modifier.width(56.dp).padding(start = 24.dp),
|
||||||
text = format.format(sliderValue),
|
text = format.format(sliderValue),
|
||||||
style = MaterialTheme.typography.titleSmall
|
style = MaterialTheme.typography.titleSmall,
|
||||||
|
textAlign = TextAlign.Center,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,18 +1,21 @@
|
|||||||
package de.mm20.launcher2.ui.component.preferences
|
package de.mm20.launcher2.ui.component.preferences
|
||||||
|
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Switch
|
import androidx.compose.material3.Switch
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.graphics.vector.ImageVector
|
import androidx.compose.ui.graphics.vector.ImageVector
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun SwitchPreference(
|
fun SwitchPreference(
|
||||||
title: String,
|
title: String,
|
||||||
icon: ImageVector? = null,
|
icon: ImageVector? = null,
|
||||||
iconPadding: Boolean = true,
|
iconPadding: Boolean = icon != null,
|
||||||
summary: String? = null,
|
summary: String? = null,
|
||||||
value: Boolean,
|
value: Boolean,
|
||||||
onValueChanged: (Boolean) -> Unit,
|
onValueChanged: (Boolean) -> Unit,
|
||||||
enabled: Boolean = true
|
enabled: Boolean = true,
|
||||||
|
containerColor: Color = MaterialTheme.colorScheme.surface,
|
||||||
) {
|
) {
|
||||||
Preference(
|
Preference(
|
||||||
title = title,
|
title = title,
|
||||||
@ -27,6 +30,7 @@ fun SwitchPreference(
|
|||||||
Switch(
|
Switch(
|
||||||
enabled = enabled, checked = value, onCheckedChange = onValueChanged,
|
enabled = enabled, checked = value, onCheckedChange = onValueChanged,
|
||||||
)
|
)
|
||||||
}
|
},
|
||||||
|
containerColor = containerColor,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -11,6 +11,7 @@ import androidx.compose.animation.scaleIn
|
|||||||
import androidx.compose.animation.scaleOut
|
import androidx.compose.animation.scaleOut
|
||||||
import androidx.compose.animation.slideInHorizontally
|
import androidx.compose.animation.slideInHorizontally
|
||||||
import androidx.compose.animation.slideOutHorizontally
|
import androidx.compose.animation.slideOutHorizontally
|
||||||
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.runtime.CompositionLocalProvider
|
import androidx.compose.runtime.CompositionLocalProvider
|
||||||
@ -132,7 +133,9 @@ class SettingsActivity : BaseActivity() {
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
OverlayHost {
|
OverlayHost(
|
||||||
|
modifier = Modifier.fillMaxSize().background(MaterialTheme.colorScheme.surfaceContainer)
|
||||||
|
) {
|
||||||
NavHost(
|
NavHost(
|
||||||
modifier = Modifier.fillMaxSize(),
|
modifier = Modifier.fillMaxSize(),
|
||||||
navController = navController,
|
navController = navController,
|
||||||
|
|||||||
@ -21,30 +21,32 @@ fun BuildInfoSettingsScreen() {
|
|||||||
val buildFeatures by viewModel.buildFeatures.collectAsState(emptyMap())
|
val buildFeatures by viewModel.buildFeatures.collectAsState(emptyMap())
|
||||||
PreferenceScreen(title = stringResource(R.string.preference_screen_buildinfo)) {
|
PreferenceScreen(title = stringResource(R.string.preference_screen_buildinfo)) {
|
||||||
item {
|
item {
|
||||||
Preference(title = "Build type", summary = BuildConfig.BUILD_TYPE)
|
PreferenceCategory {
|
||||||
var buildSignature by remember { mutableStateOf<String?>(null) }
|
Preference(title = "Build type", summary = BuildConfig.BUILD_TYPE)
|
||||||
LaunchedEffect(null) {
|
var buildSignature by remember { mutableStateOf<String?>(null) }
|
||||||
val signature = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
LaunchedEffect(null) {
|
||||||
val pi = context.packageManager.getPackageInfo(
|
val signature = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
||||||
context.packageName,
|
val pi = context.packageManager.getPackageInfo(
|
||||||
PackageManager.GET_SIGNING_CERTIFICATES
|
context.packageName,
|
||||||
)
|
PackageManager.GET_SIGNING_CERTIFICATES
|
||||||
pi.signingInfo?.apkContentsSigners?.firstOrNull()
|
)
|
||||||
} else {
|
pi.signingInfo?.apkContentsSigners?.firstOrNull()
|
||||||
val pi = context.packageManager.getPackageInfo(
|
} else {
|
||||||
context.packageName,
|
val pi = context.packageManager.getPackageInfo(
|
||||||
PackageManager.GET_SIGNATURES
|
context.packageName,
|
||||||
)
|
PackageManager.GET_SIGNATURES
|
||||||
pi.signatures?.firstOrNull()
|
)
|
||||||
|
pi.signatures?.firstOrNull()
|
||||||
|
}
|
||||||
|
val signatureHash = if (signature != null) {
|
||||||
|
val digest = MessageDigest.getInstance("SHA")
|
||||||
|
digest.update(signature.toByteArray())
|
||||||
|
Base64.encodeToString(digest.digest(), Base64.NO_WRAP)
|
||||||
|
} else "null"
|
||||||
|
buildSignature = signatureHash
|
||||||
}
|
}
|
||||||
val signatureHash = if (signature != null) {
|
Preference(title = "Signature hash", summary = buildSignature)
|
||||||
val digest = MessageDigest.getInstance("SHA")
|
|
||||||
digest.update(signature.toByteArray())
|
|
||||||
Base64.encodeToString(digest.digest(), Base64.NO_WRAP)
|
|
||||||
} else "null"
|
|
||||||
buildSignature = signatureHash
|
|
||||||
}
|
}
|
||||||
Preference(title = "Signature hash", summary = buildSignature)
|
|
||||||
}
|
}
|
||||||
item {
|
item {
|
||||||
PreferenceCategory(title = "Features") {
|
PreferenceCategory(title = "Features") {
|
||||||
|
|||||||
@ -6,8 +6,6 @@ import androidx.compose.animation.AnimatedVisibility
|
|||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.rounded.ErrorOutline
|
import androidx.compose.material.icons.rounded.ErrorOutline
|
||||||
import androidx.compose.material3.Text
|
|
||||||
import androidx.compose.material3.TextButton
|
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.collectAsState
|
import androidx.compose.runtime.collectAsState
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
@ -22,8 +20,8 @@ import de.mm20.launcher2.crashreporter.CrashReporter
|
|||||||
import de.mm20.launcher2.ktx.sendWithBackgroundPermission
|
import de.mm20.launcher2.ktx.sendWithBackgroundPermission
|
||||||
import de.mm20.launcher2.plugin.PluginState
|
import de.mm20.launcher2.plugin.PluginState
|
||||||
import de.mm20.launcher2.ui.R
|
import de.mm20.launcher2.ui.R
|
||||||
import de.mm20.launcher2.ui.component.Banner
|
|
||||||
import de.mm20.launcher2.ui.component.MissingPermissionBanner
|
import de.mm20.launcher2.ui.component.MissingPermissionBanner
|
||||||
|
import de.mm20.launcher2.ui.component.preferences.GuardedPreference
|
||||||
import de.mm20.launcher2.ui.component.preferences.PreferenceCategory
|
import de.mm20.launcher2.ui.component.preferences.PreferenceCategory
|
||||||
import de.mm20.launcher2.ui.component.preferences.PreferenceScreen
|
import de.mm20.launcher2.ui.component.preferences.PreferenceScreen
|
||||||
import de.mm20.launcher2.ui.component.preferences.PreferenceWithSwitch
|
import de.mm20.launcher2.ui.component.preferences.PreferenceWithSwitch
|
||||||
@ -47,85 +45,81 @@ fun CalendarSearchSettingsScreen() {
|
|||||||
PreferenceScreen(title = stringResource(R.string.preference_search_calendar)) {
|
PreferenceScreen(title = stringResource(R.string.preference_search_calendar)) {
|
||||||
item {
|
item {
|
||||||
PreferenceCategory {
|
PreferenceCategory {
|
||||||
AnimatedVisibility(hasCalendarPermission == false) {
|
GuardedPreference(
|
||||||
MissingPermissionBanner(
|
locked = hasCalendarPermission == false,
|
||||||
text = stringResource(R.string.missing_permission_calendar_search_settings),
|
onUnlock = {
|
||||||
onClick = {
|
viewModel.requestCalendarPermission(context as AppCompatActivity)
|
||||||
viewModel.requestCalendarPermission(context as AppCompatActivity)
|
|
||||||
},
|
|
||||||
modifier = Modifier.padding(16.dp)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
PreferenceWithSwitch(
|
|
||||||
title = stringResource(R.string.preference_search_calendar),
|
|
||||||
summary = stringResource(R.string.preference_search_local_calendar_summary),
|
|
||||||
switchValue = enabledProviders.contains("local") && hasCalendarPermission == true,
|
|
||||||
onSwitchChanged = {
|
|
||||||
viewModel.setProviderEnabled("local", it)
|
|
||||||
},
|
},
|
||||||
enabled = hasCalendarPermission == true,
|
description = stringResource(R.string.missing_permission_calendar_search_settings),
|
||||||
onClick = {
|
) {
|
||||||
navController?.navigate("settings/search/calendar/local")
|
|
||||||
}
|
|
||||||
)
|
|
||||||
if (isTasksAppInstalled) {
|
|
||||||
AnimatedVisibility(hasTasksPermission == false) {
|
|
||||||
MissingPermissionBanner(
|
|
||||||
text = stringResource(R.string.missing_permission_tasks_search_settings),
|
|
||||||
onClick = {
|
|
||||||
viewModel.requestTasksPermission(context as AppCompatActivity)
|
|
||||||
},
|
|
||||||
modifier = Modifier.padding(16.dp)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
PreferenceWithSwitch(
|
PreferenceWithSwitch(
|
||||||
title = stringResource(R.string.preference_search_tasks),
|
title = stringResource(R.string.preference_search_calendar),
|
||||||
summary = stringResource(R.string.preference_search_tasks_summary),
|
summary = stringResource(R.string.preference_search_local_calendar_summary),
|
||||||
switchValue = enabledProviders.contains("tasks.org") && hasTasksPermission == true,
|
switchValue = enabledProviders.contains("local") && hasCalendarPermission == true,
|
||||||
onSwitchChanged = {
|
onSwitchChanged = {
|
||||||
viewModel.setProviderEnabled("tasks.org", it)
|
viewModel.setProviderEnabled("local", it)
|
||||||
},
|
},
|
||||||
enabled = hasTasksPermission == true,
|
enabled = hasCalendarPermission == true,
|
||||||
onClick = {
|
onClick = {
|
||||||
navController?.navigate("settings/search/calendar/tasks.org")
|
navController?.navigate("settings/search/calendar/local")
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
for (plugin in plugins) {
|
if (isTasksAppInstalled) {
|
||||||
val state = plugin.state
|
GuardedPreference(
|
||||||
if (state is PluginState.SetupRequired) {
|
locked = hasTasksPermission == false,
|
||||||
Banner(
|
onUnlock = {
|
||||||
modifier = Modifier.padding(16.dp),
|
viewModel.requestTasksPermission(context as AppCompatActivity)
|
||||||
text = state.message
|
},
|
||||||
?: stringResource(id = R.string.plugin_state_setup_required),
|
description = stringResource(R.string.missing_permission_tasks_search_settings),
|
||||||
icon = Icons.Rounded.ErrorOutline,
|
) {
|
||||||
primaryAction = {
|
PreferenceWithSwitch(
|
||||||
TextButton(onClick = {
|
title = stringResource(R.string.preference_search_tasks),
|
||||||
try {
|
summary = stringResource(R.string.preference_search_tasks_summary),
|
||||||
state.setupActivity.sendWithBackgroundPermission(context)
|
switchValue = enabledProviders.contains("tasks.org") && hasTasksPermission == true,
|
||||||
} catch (e: PendingIntent.CanceledException) {
|
onSwitchChanged = {
|
||||||
CrashReporter.logException(e)
|
viewModel.setProviderEnabled("tasks.org", it)
|
||||||
}
|
},
|
||||||
}) {
|
enabled = hasTasksPermission == true,
|
||||||
Text(stringResource(id = R.string.plugin_action_setup))
|
onClick = {
|
||||||
}
|
navController?.navigate("settings/search/calendar/tasks.org")
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
PreferenceWithSwitch(
|
}
|
||||||
title = plugin.plugin.label,
|
for (plugin in plugins) {
|
||||||
enabled = state is PluginState.Ready,
|
val state = plugin.state
|
||||||
summary = (state as? PluginState.SetupRequired)?.message
|
GuardedPreference(
|
||||||
?: (state as? PluginState.Ready)?.text
|
locked = state is PluginState.SetupRequired,
|
||||||
?: plugin.plugin.description,
|
onUnlock = {
|
||||||
switchValue = enabledProviders.contains(plugin.plugin.authority) && state is PluginState.Ready,
|
try {
|
||||||
onSwitchChanged = {
|
(state as PluginState.SetupRequired).setupActivity.sendWithBackgroundPermission(
|
||||||
viewModel.setProviderEnabled(plugin.plugin.authority, it)
|
context
|
||||||
|
)
|
||||||
|
} catch (e: PendingIntent.CanceledException) {
|
||||||
|
CrashReporter.logException(e)
|
||||||
|
}
|
||||||
},
|
},
|
||||||
onClick = {
|
description = (state as? PluginState.SetupRequired)?.message
|
||||||
navController?.navigate("settings/search/calendar/${plugin.plugin.authority}")
|
?: stringResource(id = R.string.plugin_state_setup_required),
|
||||||
}
|
icon = Icons.Rounded.ErrorOutline,
|
||||||
)
|
unlockLabel = stringResource(id = R.string.plugin_action_setup),
|
||||||
|
) {
|
||||||
|
PreferenceWithSwitch(
|
||||||
|
title = plugin.plugin.label,
|
||||||
|
enabled = state is PluginState.Ready,
|
||||||
|
summary = (state as? PluginState.SetupRequired)?.message
|
||||||
|
?: (state as? PluginState.Ready)?.text
|
||||||
|
?: plugin.plugin.description,
|
||||||
|
switchValue = enabledProviders.contains(plugin.plugin.authority) && state is PluginState.Ready,
|
||||||
|
onSwitchChanged = {
|
||||||
|
viewModel.setProviderEnabled(plugin.plugin.authority, it)
|
||||||
|
},
|
||||||
|
onClick = {
|
||||||
|
navController?.navigate("settings/search/calendar/${plugin.plugin.authority}")
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,8 @@
|
|||||||
package de.mm20.launcher2.ui.settings.colorscheme
|
package de.mm20.launcher2.ui.settings.colorscheme
|
||||||
|
|
||||||
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.horizontalScroll
|
import androidx.compose.foundation.horizontalScroll
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
import androidx.compose.foundation.layout.RowScope
|
import androidx.compose.foundation.layout.RowScope
|
||||||
@ -10,10 +12,7 @@ import androidx.compose.foundation.rememberScrollState
|
|||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.rounded.DarkMode
|
import androidx.compose.material.icons.rounded.DarkMode
|
||||||
import androidx.compose.material.icons.rounded.LightMode
|
import androidx.compose.material.icons.rounded.LightMode
|
||||||
import androidx.compose.material3.Card
|
|
||||||
import androidx.compose.material3.CardDefaults
|
|
||||||
import androidx.compose.material3.ColorScheme
|
import androidx.compose.material3.ColorScheme
|
||||||
import androidx.compose.material3.HorizontalDivider
|
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.SegmentedButton
|
import androidx.compose.material3.SegmentedButton
|
||||||
@ -23,6 +22,7 @@ import androidx.compose.material3.Text
|
|||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.draw.clip
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
@ -35,12 +35,13 @@ fun ColorSchemePreferenceCategory(
|
|||||||
preview: @Composable RowScope.() -> Unit
|
preview: @Composable RowScope.() -> Unit
|
||||||
) {
|
) {
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier.padding(top = 16.dp)
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(top = 16.dp)
|
||||||
) {
|
) {
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.padding(start = 16.dp, bottom = 8.dp),
|
||||||
.padding(horizontal = 16.dp),
|
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
) {
|
) {
|
||||||
Text(
|
Text(
|
||||||
@ -66,21 +67,22 @@ fun ColorSchemePreferenceCategory(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
MaterialTheme(
|
|
||||||
colorScheme = previewColorScheme
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.clip(MaterialTheme.shapes.medium),
|
||||||
|
verticalArrangement = Arrangement.spacedBy(2.dp)
|
||||||
) {
|
) {
|
||||||
Card(
|
|
||||||
modifier = Modifier
|
MaterialTheme(
|
||||||
.fillMaxWidth()
|
colorScheme = previewColorScheme
|
||||||
.padding(16.dp),
|
|
||||||
colors = CardDefaults.cardColors(
|
|
||||||
containerColor = MaterialTheme.colorScheme.surfaceContainerLow
|
|
||||||
)
|
|
||||||
) {
|
) {
|
||||||
|
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
|
.background(MaterialTheme.colorScheme.surfaceContainerLowest, MaterialTheme.shapes.extraSmall)
|
||||||
.horizontalScroll(rememberScrollState())
|
.horizontalScroll(rememberScrollState())
|
||||||
.padding(16.dp),
|
.padding(16.dp),
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
@ -88,10 +90,8 @@ fun ColorSchemePreferenceCategory(
|
|||||||
preview()
|
preview()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
colorPreferences()
|
||||||
}
|
}
|
||||||
|
|
||||||
colorPreferences()
|
|
||||||
HorizontalDivider(modifier = Modifier.padding(top = 8.dp))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -66,6 +66,7 @@ import de.mm20.launcher2.themes.merge
|
|||||||
import de.mm20.launcher2.ui.R
|
import de.mm20.launcher2.ui.R
|
||||||
import de.mm20.launcher2.ui.component.Banner
|
import de.mm20.launcher2.ui.component.Banner
|
||||||
import de.mm20.launcher2.ui.component.ShapedLauncherIcon
|
import de.mm20.launcher2.ui.component.ShapedLauncherIcon
|
||||||
|
import de.mm20.launcher2.ui.component.preferences.PreferenceCategory
|
||||||
import de.mm20.launcher2.ui.component.preferences.PreferenceScreen
|
import de.mm20.launcher2.ui.component.preferences.PreferenceScreen
|
||||||
import de.mm20.launcher2.ui.locals.LocalDarkTheme
|
import de.mm20.launcher2.ui.locals.LocalDarkTheme
|
||||||
import de.mm20.launcher2.ui.theme.colorscheme.darkColorSchemeOf
|
import de.mm20.launcher2.ui.theme.colorscheme.darkColorSchemeOf
|
||||||
@ -142,17 +143,9 @@ fun ColorSchemeSettingsScreen(themeId: UUID) {
|
|||||||
if (previewDarkTheme) DefaultDarkColorScheme else DefaultLightColorScheme
|
if (previewDarkTheme) DefaultDarkColorScheme else DefaultLightColorScheme
|
||||||
item {
|
item {
|
||||||
|
|
||||||
Column(
|
PreferenceCategory(
|
||||||
modifier = Modifier
|
title = stringResource(R.string.preference_custom_colors_corepalette),
|
||||||
) {
|
) {
|
||||||
Text(
|
|
||||||
stringResource(R.string.preference_custom_colors_corepalette),
|
|
||||||
style = MaterialTheme.typography.titleSmall,
|
|
||||||
color = MaterialTheme.colorScheme.secondary,
|
|
||||||
modifier = Modifier.padding(bottom = 16.dp, start = 16.dp, end = 16.dp),
|
|
||||||
maxLines = 1,
|
|
||||||
overflow = TextOverflow.Ellipsis,
|
|
||||||
)
|
|
||||||
CorePaletteColorPreference(
|
CorePaletteColorPreference(
|
||||||
title = "Primary",
|
title = "Primary",
|
||||||
value = theme?.corePalette?.primary,
|
value = theme?.corePalette?.primary,
|
||||||
@ -267,7 +260,6 @@ fun ColorSchemeSettingsScreen(themeId: UUID) {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
HorizontalDivider(modifier = Modifier.padding(top = 8.dp))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
item {
|
item {
|
||||||
@ -681,7 +673,6 @@ fun ColorSchemeSettingsScreen(themeId: UUID) {
|
|||||||
)
|
)
|
||||||
},
|
},
|
||||||
defaultValue = selectedDefaultScheme.surfaceDim,
|
defaultValue = selectedDefaultScheme.surfaceDim,
|
||||||
modifier = Modifier.padding(end = 12.dp),
|
|
||||||
)
|
)
|
||||||
ThemeColorPreference(
|
ThemeColorPreference(
|
||||||
title = "Surface",
|
title = "Surface",
|
||||||
|
|||||||
@ -3,6 +3,7 @@ package de.mm20.launcher2.ui.settings.colorscheme
|
|||||||
import androidx.compose.animation.AnimatedVisibility
|
import androidx.compose.animation.AnimatedVisibility
|
||||||
import androidx.compose.animation.expandVertically
|
import androidx.compose.animation.expandVertically
|
||||||
import androidx.compose.animation.shrinkVertically
|
import androidx.compose.animation.shrinkVertically
|
||||||
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.combinedClickable
|
import androidx.compose.foundation.combinedClickable
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
@ -28,6 +29,7 @@ import androidx.compose.runtime.remember
|
|||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.draw.clip
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.graphics.toArgb
|
import androidx.compose.ui.graphics.toArgb
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
@ -56,10 +58,12 @@ fun CorePaletteColorPreference(
|
|||||||
|
|
||||||
Row(
|
Row(
|
||||||
modifier = modifier.fillMaxWidth()
|
modifier = modifier.fillMaxWidth()
|
||||||
|
.clip(MaterialTheme.shapes.extraSmall)
|
||||||
.clickable(
|
.clickable(
|
||||||
onClick = { showDialog = true },
|
onClick = { showDialog = true },
|
||||||
)
|
)
|
||||||
.padding(horizontal = 16.dp, vertical = 8.dp),
|
.background(MaterialTheme.colorScheme.surface)
|
||||||
|
.padding(16.dp),
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
) {
|
) {
|
||||||
ColorSwatch(
|
ColorSwatch(
|
||||||
|
|||||||
@ -80,10 +80,12 @@ fun ThemeColorPreference(
|
|||||||
|
|
||||||
Row(
|
Row(
|
||||||
modifier = modifier.fillMaxWidth()
|
modifier = modifier.fillMaxWidth()
|
||||||
|
.clip(MaterialTheme.shapes.extraSmall)
|
||||||
.clickable(
|
.clickable(
|
||||||
onClick = { showDialog = true },
|
onClick = { showDialog = true },
|
||||||
)
|
)
|
||||||
.padding(horizontal = 16.dp, vertical = 8.dp),
|
.background(MaterialTheme.colorScheme.surface)
|
||||||
|
.padding(16.dp),
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
) {
|
) {
|
||||||
ColorSwatch(
|
ColorSwatch(
|
||||||
|
|||||||
@ -26,9 +26,9 @@ import de.mm20.launcher2.plugin.PluginState
|
|||||||
import de.mm20.launcher2.ui.R
|
import de.mm20.launcher2.ui.R
|
||||||
import de.mm20.launcher2.ui.component.Banner
|
import de.mm20.launcher2.ui.component.Banner
|
||||||
import de.mm20.launcher2.ui.component.MissingPermissionBanner
|
import de.mm20.launcher2.ui.component.MissingPermissionBanner
|
||||||
|
import de.mm20.launcher2.ui.component.preferences.GuardedPreference
|
||||||
import de.mm20.launcher2.ui.component.preferences.PreferenceCategory
|
import de.mm20.launcher2.ui.component.preferences.PreferenceCategory
|
||||||
import de.mm20.launcher2.ui.component.preferences.PreferenceScreen
|
import de.mm20.launcher2.ui.component.preferences.PreferenceScreen
|
||||||
import de.mm20.launcher2.ui.component.preferences.PreferenceWithSwitch
|
|
||||||
import de.mm20.launcher2.ui.component.preferences.SwitchPreference
|
import de.mm20.launcher2.ui.component.preferences.SwitchPreference
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
@ -38,7 +38,10 @@ fun ContactsSettingsScreen() {
|
|||||||
|
|
||||||
val hasContactsPermission by viewModel.hasContactsPermission.collectAsStateWithLifecycle(null)
|
val hasContactsPermission by viewModel.hasContactsPermission.collectAsStateWithLifecycle(null)
|
||||||
val hasCallPermission by viewModel.hasCallPermission.collectAsStateWithLifecycle(null)
|
val hasCallPermission by viewModel.hasCallPermission.collectAsStateWithLifecycle(null)
|
||||||
val plugins by viewModel.availablePlugins.collectAsStateWithLifecycle(emptyList(), minActiveState = Lifecycle.State.RESUMED)
|
val plugins by viewModel.availablePlugins.collectAsStateWithLifecycle(
|
||||||
|
emptyList(),
|
||||||
|
minActiveState = Lifecycle.State.RESUMED
|
||||||
|
)
|
||||||
val enabledProviders by viewModel.enabledProviders.collectAsState(emptySet())
|
val enabledProviders by viewModel.enabledProviders.collectAsState(emptySet())
|
||||||
val callOnTap by viewModel.callOnTap.collectAsStateWithLifecycle(null)
|
val callOnTap by viewModel.callOnTap.collectAsStateWithLifecycle(null)
|
||||||
|
|
||||||
@ -56,69 +59,76 @@ fun ContactsSettingsScreen() {
|
|||||||
modifier = Modifier.padding(16.dp)
|
modifier = Modifier.padding(16.dp)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
SwitchPreference(
|
GuardedPreference(
|
||||||
title = stringResource(R.string.preference_search_contacts),
|
locked = hasContactsPermission == false,
|
||||||
summary = stringResource(R.string.preference_search_contacts_summary),
|
onUnlock = {
|
||||||
icon = Icons.Rounded.Person,
|
viewModel.requestContactsPermission(context as AppCompatActivity)
|
||||||
value = enabledProviders.contains("local"),
|
},
|
||||||
onValueChanged = {
|
description = stringResource(R.string.missing_permission_contact_search_settings),
|
||||||
viewModel.setProviderEnabled("local", it)
|
) {
|
||||||
}
|
SwitchPreference(
|
||||||
)
|
title = stringResource(R.string.preference_search_contacts),
|
||||||
|
summary = stringResource(R.string.preference_search_contacts_summary),
|
||||||
|
icon = Icons.Rounded.Person,
|
||||||
|
value = enabledProviders.contains("local"),
|
||||||
|
onValueChanged = {
|
||||||
|
viewModel.setProviderEnabled("local", it)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
for (plugin in plugins) {
|
for (plugin in plugins) {
|
||||||
val state = plugin.state
|
val state = plugin.state
|
||||||
if (state is PluginState.SetupRequired) {
|
GuardedPreference(
|
||||||
Banner(
|
locked = state is PluginState.SetupRequired,
|
||||||
modifier = Modifier.padding(16.dp),
|
onUnlock = {
|
||||||
text = state.message
|
try {
|
||||||
?: stringResource(id = R.string.plugin_state_setup_required),
|
(state as PluginState.SetupRequired).setupActivity.sendWithBackgroundPermission(
|
||||||
icon = Icons.Rounded.ErrorOutline,
|
context
|
||||||
primaryAction = {
|
)
|
||||||
TextButton(onClick = {
|
} catch (e: PendingIntent.CanceledException) {
|
||||||
try {
|
CrashReporter.logException(e)
|
||||||
state.setupActivity.sendWithBackgroundPermission(context)
|
|
||||||
} catch (e: PendingIntent.CanceledException) {
|
|
||||||
CrashReporter.logException(e)
|
|
||||||
}
|
|
||||||
}) {
|
|
||||||
Text(stringResource(id = R.string.plugin_action_setup))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
description = (state as? PluginState.SetupRequired)?.message
|
||||||
|
?: stringResource(id = R.string.plugin_state_setup_required),
|
||||||
|
icon = Icons.Rounded.ErrorOutline,
|
||||||
|
unlockLabel = stringResource(id = R.string.plugin_action_setup),
|
||||||
|
) {
|
||||||
|
SwitchPreference(
|
||||||
|
title = plugin.plugin.label,
|
||||||
|
enabled = state is PluginState.Ready,
|
||||||
|
summary = (state as? PluginState.SetupRequired)?.message
|
||||||
|
?: (state as? PluginState.Ready)?.text
|
||||||
|
?: plugin.plugin.description,
|
||||||
|
value = enabledProviders.contains(plugin.plugin.authority) && state is PluginState.Ready,
|
||||||
|
onValueChanged = {
|
||||||
|
viewModel.setProviderEnabled(plugin.plugin.authority, it)
|
||||||
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
SwitchPreference(
|
|
||||||
title = plugin.plugin.label,
|
|
||||||
enabled = state is PluginState.Ready,
|
|
||||||
summary = (state as? PluginState.SetupRequired)?.message
|
|
||||||
?: (state as? PluginState.Ready)?.text
|
|
||||||
?: plugin.plugin.description,
|
|
||||||
value = enabledProviders.contains(plugin.plugin.authority) && state is PluginState.Ready,
|
|
||||||
onValueChanged = {
|
|
||||||
viewModel.setProviderEnabled(plugin.plugin.authority, it)
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
item {
|
||||||
PreferenceCategory {
|
PreferenceCategory {
|
||||||
AnimatedVisibility(hasCallPermission == false) {
|
GuardedPreference(
|
||||||
MissingPermissionBanner(
|
locked = hasCallPermission == false,
|
||||||
text = stringResource(R.string.missing_permission_call_contacts_settings),
|
onUnlock = {
|
||||||
onClick = {
|
viewModel.requestCallPermission(context as AppCompatActivity)
|
||||||
viewModel.requestCallPermission(context as AppCompatActivity)
|
},
|
||||||
|
description = stringResource(R.string.missing_permission_call_contacts_settings),
|
||||||
|
) {
|
||||||
|
SwitchPreference(
|
||||||
|
title = stringResource(R.string.preference_contacts_call_on_tap),
|
||||||
|
summary = stringResource(R.string.preference_contacts_call_on_tap_summary),
|
||||||
|
icon = Icons.Rounded.Call,
|
||||||
|
value = callOnTap == true && hasCallPermission == true,
|
||||||
|
onValueChanged = {
|
||||||
|
viewModel.setCallOnTap(it)
|
||||||
},
|
},
|
||||||
modifier = Modifier.padding(16.dp)
|
enabled = hasCallPermission == true
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
SwitchPreference(
|
|
||||||
title = stringResource(R.string.preference_contacts_call_on_tap),
|
|
||||||
summary = stringResource(R.string.preference_contacts_call_on_tap_summary),
|
|
||||||
icon = Icons.Rounded.Call,
|
|
||||||
value = callOnTap == true && hasCallPermission == true,
|
|
||||||
onValueChanged = {
|
|
||||||
viewModel.setCallOnTap(it)
|
|
||||||
},
|
|
||||||
enabled = hasCallPermission == true
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,7 +2,6 @@ package de.mm20.launcher2.ui.settings.filesearch
|
|||||||
|
|
||||||
import android.app.PendingIntent
|
import android.app.PendingIntent
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.compose.animation.AnimatedVisibility
|
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
@ -31,7 +30,7 @@ import de.mm20.launcher2.ktx.sendWithBackgroundPermission
|
|||||||
import de.mm20.launcher2.plugin.PluginState
|
import de.mm20.launcher2.plugin.PluginState
|
||||||
import de.mm20.launcher2.ui.R
|
import de.mm20.launcher2.ui.R
|
||||||
import de.mm20.launcher2.ui.component.Banner
|
import de.mm20.launcher2.ui.component.Banner
|
||||||
import de.mm20.launcher2.ui.component.MissingPermissionBanner
|
import de.mm20.launcher2.ui.component.preferences.GuardedPreference
|
||||||
import de.mm20.launcher2.ui.component.preferences.PreferenceCategory
|
import de.mm20.launcher2.ui.component.preferences.PreferenceCategory
|
||||||
import de.mm20.launcher2.ui.component.preferences.PreferenceScreen
|
import de.mm20.launcher2.ui.component.preferences.PreferenceScreen
|
||||||
import de.mm20.launcher2.ui.component.preferences.SwitchPreference
|
import de.mm20.launcher2.ui.component.preferences.SwitchPreference
|
||||||
@ -67,123 +66,104 @@ fun FileSearchSettingsScreen() {
|
|||||||
PreferenceCategory {
|
PreferenceCategory {
|
||||||
val localFiles by viewModel.localFiles.collectAsState()
|
val localFiles by viewModel.localFiles.collectAsState()
|
||||||
val hasFilePermission by viewModel.hasFilePermission.collectAsState()
|
val hasFilePermission by viewModel.hasFilePermission.collectAsState()
|
||||||
AnimatedVisibility(hasFilePermission == false) {
|
GuardedPreference(
|
||||||
MissingPermissionBanner(
|
locked = hasFilePermission == false,
|
||||||
text = stringResource(
|
onUnlock = {
|
||||||
if (isAtLeastApiLevel(29)) R.string.missing_permission_file_search_settings_android10 else R.string.missing_permission_file_search_settings
|
viewModel.requestFilePermission(context as AppCompatActivity)
|
||||||
), onClick = {
|
},
|
||||||
viewModel.requestFilePermission(context as AppCompatActivity)
|
description = stringResource(
|
||||||
|
if (isAtLeastApiLevel(29)) R.string.missing_permission_file_search_settings_android10 else R.string.missing_permission_file_search_settings
|
||||||
|
),
|
||||||
|
) {
|
||||||
|
SwitchPreference(
|
||||||
|
title = stringResource(R.string.preference_search_localfiles),
|
||||||
|
summary = stringResource(R.string.preference_search_localfiles_summary),
|
||||||
|
value = localFiles == true && hasFilePermission == true,
|
||||||
|
onValueChanged = {
|
||||||
|
viewModel.setLocalFiles(it)
|
||||||
},
|
},
|
||||||
modifier = Modifier.padding(16.dp)
|
enabled = hasFilePermission == true
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
SwitchPreference(
|
|
||||||
title = stringResource(R.string.preference_search_localfiles),
|
|
||||||
summary = stringResource(R.string.preference_search_localfiles_summary),
|
|
||||||
value = localFiles == true && hasFilePermission == true,
|
|
||||||
onValueChanged = {
|
|
||||||
viewModel.setLocalFiles(it)
|
|
||||||
},
|
|
||||||
enabled = hasFilePermission == true
|
|
||||||
)
|
|
||||||
|
|
||||||
val nextcloud by viewModel.nextcloud.collectAsState()
|
val nextcloud by viewModel.nextcloud.collectAsState()
|
||||||
val nextcloudAccount by viewModel.nextcloudAccount
|
val nextcloudAccount by viewModel.nextcloudAccount
|
||||||
AnimatedVisibility(nextcloudAccount == null) {
|
GuardedPreference(
|
||||||
Banner(
|
locked = nextcloudAccount == null,
|
||||||
text = stringResource(R.string.no_account_nextcloud),
|
onUnlock = {
|
||||||
icon = Icons.Rounded.AccountBox,
|
viewModel.login(context as AppCompatActivity, AccountType.Nextcloud)
|
||||||
primaryAction = {
|
},
|
||||||
TextButton(onClick = {
|
icon = Icons.Rounded.AccountBox,
|
||||||
viewModel.login(
|
description = stringResource(R.string.no_account_nextcloud),
|
||||||
context as AppCompatActivity,
|
unlockLabel = stringResource(R.string.connect_account),
|
||||||
AccountType.Nextcloud
|
) {
|
||||||
)
|
SwitchPreference(
|
||||||
}) {
|
title = stringResource(R.string.preference_search_nextcloud),
|
||||||
Text(
|
summary = nextcloudAccount?.let {
|
||||||
stringResource(R.string.connect_account),
|
stringResource(R.string.preference_search_cloud_summary, it.userName)
|
||||||
)
|
} ?: stringResource(R.string.preference_summary_not_logged_in),
|
||||||
}
|
value = nextcloud == true && nextcloudAccount != null,
|
||||||
|
onValueChanged = {
|
||||||
|
viewModel.setNextcloud(it)
|
||||||
},
|
},
|
||||||
modifier = Modifier.padding(16.dp)
|
enabled = nextcloudAccount != null
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
SwitchPreference(
|
|
||||||
title = stringResource(R.string.preference_search_nextcloud),
|
|
||||||
summary = nextcloudAccount?.let {
|
|
||||||
stringResource(R.string.preference_search_cloud_summary, it.userName)
|
|
||||||
} ?: stringResource(R.string.preference_summary_not_logged_in),
|
|
||||||
value = nextcloud == true && nextcloudAccount != null,
|
|
||||||
onValueChanged = {
|
|
||||||
viewModel.setNextcloud(it)
|
|
||||||
},
|
|
||||||
enabled = nextcloudAccount != null
|
|
||||||
)
|
|
||||||
|
|
||||||
val owncloud by viewModel.owncloud.collectAsState()
|
val owncloud by viewModel.owncloud.collectAsState()
|
||||||
val owncloudAccount by viewModel.owncloudAccount
|
val owncloudAccount by viewModel.owncloudAccount
|
||||||
AnimatedVisibility(owncloudAccount == null) {
|
GuardedPreference(
|
||||||
Banner(
|
locked = owncloudAccount == null,
|
||||||
text = stringResource(R.string.no_account_owncloud),
|
onUnlock = {
|
||||||
icon = Icons.Rounded.AccountBox,
|
viewModel.login(context as AppCompatActivity, AccountType.Owncloud)
|
||||||
primaryAction = {
|
},
|
||||||
TextButton(onClick = {
|
icon = Icons.Rounded.AccountBox,
|
||||||
viewModel.login(
|
description = stringResource(R.string.no_account_owncloud),
|
||||||
context as AppCompatActivity,
|
unlockLabel = stringResource(R.string.connect_account),
|
||||||
AccountType.Owncloud
|
) {
|
||||||
)
|
SwitchPreference(
|
||||||
}) {
|
title = stringResource(R.string.preference_search_owncloud),
|
||||||
Text(
|
summary = owncloudAccount?.let {
|
||||||
stringResource(R.string.connect_account),
|
stringResource(R.string.preference_search_cloud_summary, it.userName)
|
||||||
)
|
} ?: stringResource(R.string.preference_summary_not_logged_in),
|
||||||
}
|
value = owncloud == true && owncloudAccount != null,
|
||||||
|
onValueChanged = {
|
||||||
|
viewModel.setOwncloud(it)
|
||||||
},
|
},
|
||||||
modifier = Modifier.padding(16.dp)
|
enabled = owncloudAccount != null
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
SwitchPreference(
|
|
||||||
title = stringResource(R.string.preference_search_owncloud),
|
|
||||||
summary = owncloudAccount?.let {
|
|
||||||
stringResource(R.string.preference_search_cloud_summary, it.userName)
|
|
||||||
} ?: stringResource(R.string.preference_summary_not_logged_in),
|
|
||||||
value = owncloud == true && owncloudAccount != null,
|
|
||||||
onValueChanged = {
|
|
||||||
viewModel.setOwncloud(it)
|
|
||||||
},
|
|
||||||
enabled = owncloudAccount != null
|
|
||||||
)
|
|
||||||
|
|
||||||
for (plugin in plugins) {
|
for (plugin in plugins) {
|
||||||
val state = plugin.state
|
val state = plugin.state
|
||||||
if (state is PluginState.SetupRequired) {
|
GuardedPreference(
|
||||||
Banner(
|
locked = state is PluginState.SetupRequired,
|
||||||
modifier = Modifier.padding(16.dp),
|
onUnlock = {
|
||||||
text = state.message ?: stringResource(id = R.string.plugin_state_setup_required),
|
try {
|
||||||
icon = Icons.Rounded.ErrorOutline,
|
(state as PluginState.SetupRequired).setupActivity.sendWithBackgroundPermission(
|
||||||
primaryAction = {
|
context
|
||||||
TextButton(onClick = {
|
)
|
||||||
try {
|
} catch (e: PendingIntent.CanceledException) {
|
||||||
state.setupActivity.sendWithBackgroundPermission(context)
|
CrashReporter.logException(e)
|
||||||
} catch (e: PendingIntent.CanceledException) {
|
|
||||||
CrashReporter.logException(e)
|
|
||||||
}
|
|
||||||
}) {
|
|
||||||
Text(stringResource(id = R.string.plugin_action_setup))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
description = (state as? PluginState.SetupRequired)?.message
|
||||||
|
?: stringResource(id = R.string.plugin_state_setup_required),
|
||||||
|
icon = Icons.Rounded.ErrorOutline,
|
||||||
|
unlockLabel = stringResource(id = R.string.plugin_action_setup),
|
||||||
|
) {
|
||||||
|
SwitchPreference(
|
||||||
|
title = plugin.plugin.label,
|
||||||
|
enabled = enabledPlugins != null && state is PluginState.Ready,
|
||||||
|
summary = (state as? PluginState.Ready)?.text
|
||||||
|
?: (state as? PluginState.SetupRequired)?.message
|
||||||
|
?: plugin.plugin.description,
|
||||||
|
value = enabledPlugins?.contains(plugin.plugin.authority) == true && state is PluginState.Ready,
|
||||||
|
onValueChanged = {
|
||||||
|
viewModel.setPluginEnabled(plugin.plugin.authority, it)
|
||||||
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
SwitchPreference(
|
|
||||||
title = plugin.plugin.label,
|
|
||||||
enabled = enabledPlugins != null && state is PluginState.Ready,
|
|
||||||
summary = (state as? PluginState.Ready)?.text
|
|
||||||
?: (state as? PluginState.SetupRequired)?.message
|
|
||||||
?: plugin.plugin.description,
|
|
||||||
value = enabledPlugins?.contains(plugin.plugin.authority) == true && state is PluginState.Ready,
|
|
||||||
onValueChanged = {
|
|
||||||
viewModel.setPluginEnabled(plugin.plugin.authority, it)
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -41,6 +41,7 @@ import de.mm20.launcher2.ui.R
|
|||||||
import de.mm20.launcher2.ui.common.SearchablePicker
|
import de.mm20.launcher2.ui.common.SearchablePicker
|
||||||
import de.mm20.launcher2.ui.component.MissingPermissionBanner
|
import de.mm20.launcher2.ui.component.MissingPermissionBanner
|
||||||
import de.mm20.launcher2.ui.component.ShapedLauncherIcon
|
import de.mm20.launcher2.ui.component.ShapedLauncherIcon
|
||||||
|
import de.mm20.launcher2.ui.component.preferences.GuardedPreference
|
||||||
import de.mm20.launcher2.ui.component.preferences.ListPreference
|
import de.mm20.launcher2.ui.component.preferences.ListPreference
|
||||||
import de.mm20.launcher2.ui.component.preferences.PreferenceCategory
|
import de.mm20.launcher2.ui.component.preferences.PreferenceCategory
|
||||||
import de.mm20.launcher2.ui.component.preferences.PreferenceScreen
|
import de.mm20.launcher2.ui.component.preferences.PreferenceScreen
|
||||||
@ -72,142 +73,137 @@ fun GestureSettingsScreen() {
|
|||||||
|
|
||||||
|
|
||||||
val swipeDown by viewModel.swipeDown.collectAsStateWithLifecycle(null)
|
val swipeDown by viewModel.swipeDown.collectAsStateWithLifecycle(null)
|
||||||
AnimatedVisibility(hasPermission == false && requiresAccessibilityService(swipeDown)) {
|
|
||||||
MissingPermissionBanner(
|
|
||||||
modifier = Modifier.padding(16.dp),
|
|
||||||
text = stringResource(R.string.missing_permission_accessibility_gesture_settings),
|
|
||||||
onClick = { viewModel.requestPermission(context as AppCompatActivity) }
|
|
||||||
)
|
|
||||||
}
|
|
||||||
val swipeDownApp by viewModel.swipeDownApp.collectAsState(null)
|
val swipeDownApp by viewModel.swipeDownApp.collectAsState(null)
|
||||||
val swipeDownAppIcon by remember(swipeDownApp?.key) {
|
val swipeDownAppIcon by remember(swipeDownApp?.key) {
|
||||||
viewModel.getIcon(swipeDownApp, appIconSize.toInt())
|
viewModel.getIcon(swipeDownApp, appIconSize.toInt())
|
||||||
}.collectAsState(null)
|
}.collectAsState(null)
|
||||||
GesturePreference(
|
|
||||||
title = stringResource(R.string.preference_gesture_swipe_down),
|
|
||||||
icon = Icons.Rounded.SwipeDownAlt,
|
|
||||||
value = swipeDown,
|
|
||||||
onValueChanged = { viewModel.setSwipeDown(it) },
|
|
||||||
options = options,
|
|
||||||
app = swipeDownApp,
|
|
||||||
appIcon = swipeDownAppIcon,
|
|
||||||
onAppChanged = { viewModel.setSwipeDownApp(it) }
|
|
||||||
)
|
|
||||||
|
|
||||||
val swipeLeft by viewModel.swipeLeft.collectAsStateWithLifecycle(null)
|
GuardedPreference(
|
||||||
AnimatedVisibility(hasPermission == false && requiresAccessibilityService(swipeLeft)) {
|
locked = hasPermission == false && requiresAccessibilityService(swipeDown),
|
||||||
MissingPermissionBanner(
|
description = stringResource(R.string.missing_permission_accessibility_gesture_settings),
|
||||||
modifier = Modifier.padding(16.dp),
|
onUnlock = { viewModel.requestPermission(context as AppCompatActivity) },
|
||||||
text = stringResource(R.string.missing_permission_accessibility_gesture_settings),
|
) {
|
||||||
onClick = { viewModel.requestPermission(context as AppCompatActivity) }
|
GesturePreference(
|
||||||
|
title = stringResource(R.string.preference_gesture_swipe_down),
|
||||||
|
icon = Icons.Rounded.SwipeDownAlt,
|
||||||
|
value = swipeDown,
|
||||||
|
onValueChanged = { viewModel.setSwipeDown(it) },
|
||||||
|
options = options,
|
||||||
|
app = swipeDownApp,
|
||||||
|
appIcon = swipeDownAppIcon,
|
||||||
|
onAppChanged = { viewModel.setSwipeDownApp(it) }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val swipeLeft by viewModel.swipeLeft.collectAsStateWithLifecycle(null)
|
||||||
val swipeLeftApp by viewModel.swipeLeftApp.collectAsState(null)
|
val swipeLeftApp by viewModel.swipeLeftApp.collectAsState(null)
|
||||||
val swipeLeftAppIcon by remember(swipeLeftApp?.key) {
|
val swipeLeftAppIcon by remember(swipeLeftApp?.key) {
|
||||||
viewModel.getIcon(swipeLeftApp, appIconSize.toInt())
|
viewModel.getIcon(swipeLeftApp, appIconSize.toInt())
|
||||||
}.collectAsState(null)
|
}.collectAsState(null)
|
||||||
GesturePreference(
|
GuardedPreference(
|
||||||
title = stringResource(R.string.preference_gesture_swipe_left),
|
locked = hasPermission == false && requiresAccessibilityService(swipeLeft),
|
||||||
icon = Icons.Rounded.SwipeLeftAlt,
|
description = stringResource(R.string.missing_permission_accessibility_gesture_settings),
|
||||||
value = swipeLeft,
|
onUnlock = { viewModel.requestPermission(context as AppCompatActivity) },
|
||||||
onValueChanged = { viewModel.setSwipeLeft(it) },
|
) {
|
||||||
options = options,
|
GesturePreference(
|
||||||
app = swipeLeftApp,
|
title = stringResource(R.string.preference_gesture_swipe_left),
|
||||||
appIcon = swipeLeftAppIcon,
|
icon = Icons.Rounded.SwipeLeftAlt,
|
||||||
onAppChanged = { viewModel.setSwipeLeftApp(it) }
|
value = swipeLeft,
|
||||||
)
|
onValueChanged = { viewModel.setSwipeLeft(it) },
|
||||||
|
options = options,
|
||||||
val swipeRight by viewModel.swipeRight.collectAsStateWithLifecycle(null)
|
app = swipeLeftApp,
|
||||||
AnimatedVisibility(hasPermission == false && requiresAccessibilityService(swipeRight)) {
|
appIcon = swipeLeftAppIcon,
|
||||||
MissingPermissionBanner(
|
onAppChanged = { viewModel.setSwipeLeftApp(it) }
|
||||||
modifier = Modifier.padding(16.dp),
|
|
||||||
text = stringResource(R.string.missing_permission_accessibility_gesture_settings),
|
|
||||||
onClick = { viewModel.requestPermission(context as AppCompatActivity) }
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val swipeRight by viewModel.swipeRight.collectAsStateWithLifecycle(null)
|
||||||
val swipeRightApp by viewModel.swipeRightApp.collectAsState(null)
|
val swipeRightApp by viewModel.swipeRightApp.collectAsState(null)
|
||||||
val swipeRightAppIcon by remember(swipeRightApp?.key) {
|
val swipeRightAppIcon by remember(swipeRightApp?.key) {
|
||||||
viewModel.getIcon(swipeRightApp, appIconSize.toInt())
|
viewModel.getIcon(swipeRightApp, appIconSize.toInt())
|
||||||
}.collectAsState(null)
|
}.collectAsState(null)
|
||||||
GesturePreference(
|
GuardedPreference(
|
||||||
title = stringResource(R.string.preference_gesture_swipe_right),
|
locked = hasPermission == false && requiresAccessibilityService(swipeRight),
|
||||||
icon = Icons.Rounded.SwipeRightAlt,
|
description = stringResource(R.string.missing_permission_accessibility_gesture_settings),
|
||||||
value = swipeRight,
|
onUnlock = { viewModel.requestPermission(context as AppCompatActivity) },
|
||||||
onValueChanged = { viewModel.setSwipeRight(it) },
|
) {
|
||||||
options = options,
|
GesturePreference(
|
||||||
app = swipeRightApp,
|
title = stringResource(R.string.preference_gesture_swipe_right),
|
||||||
appIcon = swipeRightAppIcon,
|
icon = Icons.Rounded.SwipeRightAlt,
|
||||||
onAppChanged = { viewModel.setSwipeRightApp(it) }
|
value = swipeRight,
|
||||||
)
|
onValueChanged = { viewModel.setSwipeRight(it) },
|
||||||
|
options = options,
|
||||||
val swipeUp by viewModel.swipeUp.collectAsStateWithLifecycle(null)
|
app = swipeRightApp,
|
||||||
AnimatedVisibility(hasPermission == false && requiresAccessibilityService(swipeUp)) {
|
appIcon = swipeRightAppIcon,
|
||||||
MissingPermissionBanner(
|
onAppChanged = { viewModel.setSwipeRightApp(it) }
|
||||||
modifier = Modifier.padding(16.dp),
|
|
||||||
text = stringResource(R.string.missing_permission_accessibility_gesture_settings),
|
|
||||||
onClick = { viewModel.requestPermission(context as AppCompatActivity) }
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val swipeUp by viewModel.swipeUp.collectAsStateWithLifecycle(null)
|
||||||
val swipeUpApp by viewModel.swipeUpApp.collectAsState(null)
|
val swipeUpApp by viewModel.swipeUpApp.collectAsState(null)
|
||||||
val swipeUpAppIcon by remember(swipeUpApp?.key) {
|
val swipeUpAppIcon by remember(swipeUpApp?.key) {
|
||||||
viewModel.getIcon(swipeUpApp, appIconSize.toInt())
|
viewModel.getIcon(swipeUpApp, appIconSize.toInt())
|
||||||
}.collectAsState(null)
|
}.collectAsState(null)
|
||||||
GesturePreference(
|
GuardedPreference(
|
||||||
title = stringResource(R.string.preference_gesture_swipe_up),
|
locked = hasPermission == false && requiresAccessibilityService(swipeUp),
|
||||||
icon = Icons.Rounded.SwipeUpAlt,
|
description = stringResource(R.string.missing_permission_accessibility_gesture_settings),
|
||||||
value = swipeUp,
|
onUnlock = { viewModel.requestPermission(context as AppCompatActivity) },
|
||||||
onValueChanged = { viewModel.setSwipeUp(it) },
|
) {
|
||||||
options = options,
|
GesturePreference(
|
||||||
app = swipeUpApp,
|
title = stringResource(R.string.preference_gesture_swipe_up),
|
||||||
appIcon = swipeUpAppIcon,
|
icon = Icons.Rounded.SwipeUpAlt,
|
||||||
onAppChanged = { viewModel.setSwipeUpApp(it) }
|
value = swipeUp,
|
||||||
)
|
onValueChanged = { viewModel.setSwipeUp(it) },
|
||||||
|
options = options,
|
||||||
val doubleTap by viewModel.doubleTap.collectAsStateWithLifecycle(null)
|
app = swipeUpApp,
|
||||||
AnimatedVisibility(hasPermission == false && requiresAccessibilityService(doubleTap)) {
|
appIcon = swipeUpAppIcon,
|
||||||
MissingPermissionBanner(
|
onAppChanged = { viewModel.setSwipeUpApp(it) }
|
||||||
modifier = Modifier.padding(16.dp),
|
|
||||||
text = stringResource(R.string.missing_permission_accessibility_gesture_settings),
|
|
||||||
onClick = { viewModel.requestPermission(context as AppCompatActivity) }
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val doubleTap by viewModel.doubleTap.collectAsStateWithLifecycle(null)
|
||||||
val doubleTapApp by viewModel.doubleTapApp.collectAsState(null)
|
val doubleTapApp by viewModel.doubleTapApp.collectAsState(null)
|
||||||
val doubleTapAppIcon by remember(doubleTapApp?.key) {
|
val doubleTapAppIcon by remember(doubleTapApp?.key) {
|
||||||
viewModel.getIcon(doubleTapApp, appIconSize.toInt())
|
viewModel.getIcon(doubleTapApp, appIconSize.toInt())
|
||||||
}.collectAsState(null)
|
}.collectAsState(null)
|
||||||
GesturePreference(
|
GuardedPreference(
|
||||||
title = stringResource(R.string.preference_gesture_double_tap),
|
locked = hasPermission == false && requiresAccessibilityService(doubleTap),
|
||||||
icon = Icons.Rounded.Adjust,
|
description = stringResource(R.string.missing_permission_accessibility_gesture_settings),
|
||||||
value = doubleTap,
|
onUnlock = { viewModel.requestPermission(context as AppCompatActivity) },
|
||||||
onValueChanged = { viewModel.setDoubleTap(it) },
|
) {
|
||||||
options = options,
|
GesturePreference(
|
||||||
app = doubleTapApp,
|
title = stringResource(R.string.preference_gesture_double_tap),
|
||||||
appIcon = doubleTapAppIcon,
|
icon = Icons.Rounded.Adjust,
|
||||||
onAppChanged = { viewModel.setDoubleTapApp(it) }
|
value = doubleTap,
|
||||||
)
|
onValueChanged = { viewModel.setDoubleTap(it) },
|
||||||
|
options = options,
|
||||||
val longPress by viewModel.longPress.collectAsStateWithLifecycle(null)
|
app = doubleTapApp,
|
||||||
AnimatedVisibility(hasPermission == false && requiresAccessibilityService(longPress)) {
|
appIcon = doubleTapAppIcon,
|
||||||
MissingPermissionBanner(
|
onAppChanged = { viewModel.setDoubleTapApp(it) }
|
||||||
modifier = Modifier.padding(16.dp),
|
|
||||||
text = stringResource(R.string.missing_permission_accessibility_gesture_settings),
|
|
||||||
onClick = { viewModel.requestPermission(context as AppCompatActivity) }
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val longPress by viewModel.longPress.collectAsStateWithLifecycle(null)
|
||||||
val longPressApp by viewModel.longPressApp.collectAsState(null)
|
val longPressApp by viewModel.longPressApp.collectAsState(null)
|
||||||
val longPressAppIcon by remember(longPressApp?.key) {
|
val longPressAppIcon by remember(longPressApp?.key) {
|
||||||
viewModel.getIcon(longPressApp, appIconSize.toInt())
|
viewModel.getIcon(longPressApp, appIconSize.toInt())
|
||||||
}.collectAsState(null)
|
}.collectAsState(null)
|
||||||
GesturePreference(
|
GuardedPreference(
|
||||||
title = stringResource(R.string.preference_gesture_long_press),
|
locked = hasPermission == false && requiresAccessibilityService(longPress),
|
||||||
icon = Icons.Rounded.Circle,
|
description = stringResource(R.string.missing_permission_accessibility_gesture_settings),
|
||||||
value = longPress,
|
onUnlock = { viewModel.requestPermission(context as AppCompatActivity) },
|
||||||
onValueChanged = { viewModel.setLongPress(it) },
|
) {
|
||||||
options = options,
|
GesturePreference(
|
||||||
app = longPressApp,
|
title = stringResource(R.string.preference_gesture_long_press),
|
||||||
appIcon = longPressAppIcon,
|
icon = Icons.Rounded.Circle,
|
||||||
onAppChanged = { viewModel.setLongPressApp(it) }
|
value = longPress,
|
||||||
)
|
onValueChanged = { viewModel.setLongPress(it) },
|
||||||
|
options = options,
|
||||||
|
app = longPressApp,
|
||||||
|
appIcon = longPressAppIcon,
|
||||||
|
onAppChanged = { viewModel.setLongPressApp(it) }
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,7 +2,6 @@ package de.mm20.launcher2.ui.settings.icons
|
|||||||
|
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.compose.animation.AnimatedVisibility
|
import androidx.compose.animation.AnimatedVisibility
|
||||||
import androidx.compose.foundation.BorderStroke
|
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
@ -144,24 +143,20 @@ fun IconsSettingsScreen() {
|
|||||||
item {
|
item {
|
||||||
PreferenceCategory(stringResource(R.string.preference_category_icons)) {
|
PreferenceCategory(stringResource(R.string.preference_category_icons)) {
|
||||||
if (previewIcons.value.isNotEmpty()) {
|
if (previewIcons.value.isNotEmpty()) {
|
||||||
Surface(
|
Row(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.background(
|
||||||
.padding(16.dp),
|
MaterialTheme.colorScheme.surfaceContainerLowest,
|
||||||
shape = MaterialTheme.shapes.medium,
|
MaterialTheme.shapes.extraSmall
|
||||||
color = MaterialTheme.colorScheme.surfaceContainer,
|
)
|
||||||
border = BorderStroke(1.dp, MaterialTheme.colorScheme.outlineVariant)
|
.padding(vertical = 24.dp, horizontal = 8.dp)
|
||||||
) {
|
) {
|
||||||
Row(
|
for (icon in previewIcons.value) {
|
||||||
modifier = Modifier.padding(vertical = 24.dp, horizontal = 8.dp)
|
Box(
|
||||||
) {
|
modifier = Modifier.weight(1f),
|
||||||
for (icon in previewIcons.value) {
|
contentAlignment = Alignment.Center
|
||||||
Box(
|
) {
|
||||||
modifier = Modifier.weight(1f),
|
ShapedLauncherIcon(size = grid.iconSize.dp, icon = { icon })
|
||||||
contentAlignment = Alignment.Center
|
|
||||||
) {
|
|
||||||
ShapedLauncherIcon(size = grid.iconSize.dp, icon = { icon })
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -445,7 +440,8 @@ fun IconShapePreference(
|
|||||||
horizontalAlignment = Alignment.CenterHorizontally
|
horizontalAlignment = Alignment.CenterHorizontally
|
||||||
) {
|
) {
|
||||||
Box(
|
Box(
|
||||||
modifier = Modifier.clip(getShape(it))
|
modifier = Modifier
|
||||||
|
.clip(getShape(it))
|
||||||
.size(48.dp)
|
.size(48.dp)
|
||||||
.background(MaterialTheme.colorScheme.primary)
|
.background(MaterialTheme.colorScheme.primary)
|
||||||
.clickable {
|
.clickable {
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
package de.mm20.launcher2.ui.settings.nextcloud
|
package de.mm20.launcher2.ui.settings.nextcloud
|
||||||
|
|
||||||
|
import androidx.activity.compose.LocalActivity
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.border
|
import androidx.compose.foundation.border
|
||||||
@ -14,7 +15,6 @@ import androidx.compose.material.icons.automirrored.rounded.Login
|
|||||||
import androidx.compose.material.icons.automirrored.rounded.Logout
|
import androidx.compose.material.icons.automirrored.rounded.Logout
|
||||||
import androidx.compose.material3.Button
|
import androidx.compose.material3.Button
|
||||||
import androidx.compose.material3.ButtonDefaults
|
import androidx.compose.material3.ButtonDefaults
|
||||||
import androidx.compose.material3.HorizontalDivider
|
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
@ -24,7 +24,6 @@ import androidx.compose.runtime.collectAsState
|
|||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.platform.LocalContext
|
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.lifecycle.Lifecycle
|
import androidx.lifecycle.Lifecycle
|
||||||
@ -33,6 +32,7 @@ import androidx.lifecycle.repeatOnLifecycle
|
|||||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||||
import de.mm20.launcher2.ui.R
|
import de.mm20.launcher2.ui.R
|
||||||
import de.mm20.launcher2.ui.component.preferences.Preference
|
import de.mm20.launcher2.ui.component.preferences.Preference
|
||||||
|
import de.mm20.launcher2.ui.component.preferences.PreferenceCategory
|
||||||
import de.mm20.launcher2.ui.component.preferences.PreferenceScreen
|
import de.mm20.launcher2.ui.component.preferences.PreferenceScreen
|
||||||
import de.mm20.launcher2.ui.component.preferences.SwitchPreference
|
import de.mm20.launcher2.ui.component.preferences.SwitchPreference
|
||||||
import de.mm20.launcher2.ui.locals.LocalNavController
|
import de.mm20.launcher2.ui.locals.LocalNavController
|
||||||
@ -60,10 +60,12 @@ fun NextcloudSettingsScreen() {
|
|||||||
item {
|
item {
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.background(MaterialTheme.colorScheme.secondaryContainer)
|
.background(
|
||||||
|
MaterialTheme.colorScheme.secondaryContainer,
|
||||||
|
MaterialTheme.shapes.medium
|
||||||
|
)
|
||||||
.fillParentMaxWidth()
|
.fillParentMaxWidth()
|
||||||
.padding(vertical = 64.dp)
|
.padding(vertical = 64.dp),
|
||||||
,
|
|
||||||
horizontalAlignment = Alignment.CenterHorizontally,
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
verticalArrangement = Arrangement.Center
|
verticalArrangement = Arrangement.Center
|
||||||
) {
|
) {
|
||||||
@ -72,7 +74,11 @@ fun NextcloudSettingsScreen() {
|
|||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.size(72.dp)
|
.size(72.dp)
|
||||||
.background(MaterialTheme.colorScheme.secondary, CircleShape)
|
.background(MaterialTheme.colorScheme.secondary, CircleShape)
|
||||||
.border(2.dp, MaterialTheme.colorScheme.onSecondaryContainer, CircleShape),
|
.border(
|
||||||
|
2.dp,
|
||||||
|
MaterialTheme.colorScheme.onSecondaryContainer,
|
||||||
|
CircleShape
|
||||||
|
),
|
||||||
) {
|
) {
|
||||||
Text(
|
Text(
|
||||||
text = nextcloudUser!!.userName.split(" ")
|
text = nextcloudUser!!.userName.split(" ")
|
||||||
@ -80,15 +86,17 @@ fun NextcloudSettingsScreen() {
|
|||||||
.joinToString("").let {
|
.joinToString("").let {
|
||||||
if (it.length >= 2) it.first().toString() + it.last().toString()
|
if (it.length >= 2) it.first().toString() + it.last().toString()
|
||||||
else it.first().toString()
|
else it.first().toString()
|
||||||
}
|
},
|
||||||
,
|
|
||||||
color = MaterialTheme.colorScheme.onSecondary,
|
color = MaterialTheme.colorScheme.onSecondary,
|
||||||
style = MaterialTheme.typography.headlineMedium,
|
style = MaterialTheme.typography.headlineMedium,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
Text(
|
Text(
|
||||||
modifier = Modifier.padding(top = 24.dp),
|
modifier = Modifier.padding(top = 24.dp),
|
||||||
text = stringResource(R.string.preference_signin_user, nextcloudUser!!.userName),
|
text = stringResource(
|
||||||
|
R.string.preference_signin_user,
|
||||||
|
nextcloudUser!!.userName
|
||||||
|
),
|
||||||
color = MaterialTheme.colorScheme.onSecondaryContainer,
|
color = MaterialTheme.colorScheme.onSecondaryContainer,
|
||||||
style = MaterialTheme.typography.bodyLarge,
|
style = MaterialTheme.typography.bodyLarge,
|
||||||
)
|
)
|
||||||
@ -104,40 +112,42 @@ fun NextcloudSettingsScreen() {
|
|||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.padding(end = ButtonDefaults.IconSpacing)
|
.padding(end = ButtonDefaults.IconSpacing)
|
||||||
.size(ButtonDefaults.IconSize),
|
.size(ButtonDefaults.IconSize),
|
||||||
contentDescription = null)
|
contentDescription = null
|
||||||
|
)
|
||||||
Text(text = stringResource(R.string.preference_signout))
|
Text(text = stringResource(R.string.preference_signout))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
item {
|
|
||||||
HorizontalDivider()
|
|
||||||
}
|
|
||||||
|
|
||||||
item {
|
item {
|
||||||
SwitchPreference(
|
PreferenceCategory {
|
||||||
title = stringResource(R.string.plugin_type_filesearch),
|
SwitchPreference(
|
||||||
summary = stringResource(R.string.preference_search_cloud_summary, nextcloudUser!!.userName),
|
title = stringResource(R.string.plugin_type_filesearch),
|
||||||
value = searchFiles == true,
|
summary = stringResource(
|
||||||
onValueChanged = {
|
R.string.preference_search_cloud_summary,
|
||||||
viewModel.setSearchFiles(it)
|
nextcloudUser!!.userName
|
||||||
},
|
),
|
||||||
iconPadding = false,
|
value = searchFiles == true,
|
||||||
)
|
onValueChanged = {
|
||||||
|
viewModel.setSearchFiles(it)
|
||||||
|
},
|
||||||
|
iconPadding = false,
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
item {
|
item {
|
||||||
val activity = LocalContext.current as AppCompatActivity
|
val activity = LocalActivity.current as AppCompatActivity
|
||||||
Preference(
|
PreferenceCategory {
|
||||||
title = stringResource(R.string.preference_nextcloud_signin),
|
Preference(
|
||||||
summary = stringResource(R.string.preference_nextcloud_signin_summary),
|
title = stringResource(R.string.preference_nextcloud_signin),
|
||||||
icon = Icons.AutoMirrored.Rounded.Login,
|
summary = stringResource(R.string.preference_nextcloud_signin_summary),
|
||||||
onClick = {
|
icon = Icons.AutoMirrored.Rounded.Login,
|
||||||
viewModel.signIn(activity)
|
onClick = {
|
||||||
}
|
viewModel.signIn(activity)
|
||||||
)
|
}
|
||||||
}
|
)
|
||||||
item {
|
}
|
||||||
HorizontalDivider()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
package de.mm20.launcher2.ui.settings.owncloud
|
package de.mm20.launcher2.ui.settings.owncloud
|
||||||
|
|
||||||
|
import androidx.activity.compose.LocalActivity
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.border
|
import androidx.compose.foundation.border
|
||||||
@ -33,6 +34,7 @@ import androidx.lifecycle.repeatOnLifecycle
|
|||||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||||
import de.mm20.launcher2.ui.R
|
import de.mm20.launcher2.ui.R
|
||||||
import de.mm20.launcher2.ui.component.preferences.Preference
|
import de.mm20.launcher2.ui.component.preferences.Preference
|
||||||
|
import de.mm20.launcher2.ui.component.preferences.PreferenceCategory
|
||||||
import de.mm20.launcher2.ui.component.preferences.PreferenceScreen
|
import de.mm20.launcher2.ui.component.preferences.PreferenceScreen
|
||||||
import de.mm20.launcher2.ui.component.preferences.SwitchPreference
|
import de.mm20.launcher2.ui.component.preferences.SwitchPreference
|
||||||
import de.mm20.launcher2.ui.locals.LocalNavController
|
import de.mm20.launcher2.ui.locals.LocalNavController
|
||||||
@ -60,7 +62,7 @@ fun OwncloudSettingsScreen() {
|
|||||||
item {
|
item {
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.background(MaterialTheme.colorScheme.secondaryContainer)
|
.background(MaterialTheme.colorScheme.secondaryContainer, MaterialTheme.shapes.medium)
|
||||||
.fillParentMaxWidth()
|
.fillParentMaxWidth()
|
||||||
.padding(vertical = 64.dp)
|
.padding(vertical = 64.dp)
|
||||||
,
|
,
|
||||||
@ -109,35 +111,36 @@ fun OwncloudSettingsScreen() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
item {
|
|
||||||
HorizontalDivider()
|
|
||||||
}
|
|
||||||
|
|
||||||
item {
|
item {
|
||||||
SwitchPreference(
|
PreferenceCategory {
|
||||||
title = stringResource(R.string.plugin_type_filesearch),
|
SwitchPreference(
|
||||||
summary = stringResource(R.string.preference_search_cloud_summary, owncloudUser!!.userName),
|
title = stringResource(R.string.plugin_type_filesearch),
|
||||||
value = searchFiles == true,
|
summary = stringResource(
|
||||||
onValueChanged = {
|
R.string.preference_search_cloud_summary,
|
||||||
viewModel.setSearchFiles(it)
|
owncloudUser!!.userName
|
||||||
},
|
),
|
||||||
iconPadding = false,
|
value = searchFiles == true,
|
||||||
)
|
onValueChanged = {
|
||||||
|
viewModel.setSearchFiles(it)
|
||||||
|
},
|
||||||
|
iconPadding = false,
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
item {
|
item {
|
||||||
val activity = LocalContext.current as AppCompatActivity
|
val activity = LocalActivity.current as AppCompatActivity
|
||||||
Preference(
|
PreferenceCategory {
|
||||||
title = stringResource(R.string.preference_owncloud_signin),
|
Preference(
|
||||||
summary = stringResource(R.string.preference_owncloud_signin_summary),
|
title = stringResource(R.string.preference_owncloud_signin),
|
||||||
icon = Icons.AutoMirrored.Rounded.Login,
|
summary = stringResource(R.string.preference_owncloud_signin_summary),
|
||||||
onClick = {
|
icon = Icons.AutoMirrored.Rounded.Login,
|
||||||
viewModel.signIn(activity)
|
onClick = {
|
||||||
}
|
viewModel.signIn(activity)
|
||||||
)
|
}
|
||||||
}
|
)
|
||||||
item {
|
}
|
||||||
HorizontalDivider()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,7 +7,7 @@ import androidx.activity.compose.LocalActivity
|
|||||||
import androidx.activity.compose.rememberLauncherForActivityResult
|
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||||
import androidx.activity.result.contract.ActivityResultContracts
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
import androidx.compose.animation.AnimatedVisibility
|
import androidx.compose.animation.AnimatedVisibility
|
||||||
import androidx.compose.animation.animateColorAsState
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
@ -34,8 +34,8 @@ import androidx.compose.material3.ModalBottomSheet
|
|||||||
import androidx.compose.material3.Scaffold
|
import androidx.compose.material3.Scaffold
|
||||||
import androidx.compose.material3.Surface
|
import androidx.compose.material3.Surface
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.material3.TextButton
|
|
||||||
import androidx.compose.material3.TopAppBar
|
import androidx.compose.material3.TopAppBar
|
||||||
|
import androidx.compose.material3.TopAppBarDefaults
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.collectAsState
|
import androidx.compose.runtime.collectAsState
|
||||||
@ -59,8 +59,8 @@ import de.mm20.launcher2.ktx.sendWithBackgroundPermission
|
|||||||
import de.mm20.launcher2.plugin.PluginState
|
import de.mm20.launcher2.plugin.PluginState
|
||||||
import de.mm20.launcher2.themes.atTone
|
import de.mm20.launcher2.themes.atTone
|
||||||
import de.mm20.launcher2.ui.R
|
import de.mm20.launcher2.ui.R
|
||||||
import de.mm20.launcher2.ui.component.Banner
|
|
||||||
import de.mm20.launcher2.ui.component.preferences.CheckboxPreference
|
import de.mm20.launcher2.ui.component.preferences.CheckboxPreference
|
||||||
|
import de.mm20.launcher2.ui.component.preferences.GuardedPreference
|
||||||
import de.mm20.launcher2.ui.component.preferences.Preference
|
import de.mm20.launcher2.ui.component.preferences.Preference
|
||||||
import de.mm20.launcher2.ui.component.preferences.PreferenceCategory
|
import de.mm20.launcher2.ui.component.preferences.PreferenceCategory
|
||||||
import de.mm20.launcher2.ui.component.preferences.PreferenceWithSwitch
|
import de.mm20.launcher2.ui.component.preferences.PreferenceWithSwitch
|
||||||
@ -142,6 +142,10 @@ fun PluginSettingsScreen(pluginId: String) {
|
|||||||
topBar = {
|
topBar = {
|
||||||
TopAppBar(
|
TopAppBar(
|
||||||
title = {},
|
title = {},
|
||||||
|
colors = TopAppBarDefaults.topAppBarColors(
|
||||||
|
containerColor = MaterialTheme.colorScheme.surfaceContainer,
|
||||||
|
scrolledContainerColor = MaterialTheme.colorScheme.surfaceContainerHigh,
|
||||||
|
),
|
||||||
navigationIcon = {
|
navigationIcon = {
|
||||||
IconButton(onClick = {
|
IconButton(onClick = {
|
||||||
if (navController?.navigateUp() != true) {
|
if (navController?.navigateUp() != true) {
|
||||||
@ -186,7 +190,8 @@ fun PluginSettingsScreen(pluginId: String) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
},
|
||||||
|
containerColor = MaterialTheme.colorScheme.surfaceContainer,
|
||||||
) {
|
) {
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
@ -194,10 +199,14 @@ fun PluginSettingsScreen(pluginId: String) {
|
|||||||
.padding(it)
|
.padding(it)
|
||||||
) {
|
) {
|
||||||
Surface(
|
Surface(
|
||||||
modifier = Modifier.fillMaxWidth()
|
modifier = Modifier
|
||||||
.padding(bottom = 16.dp)
|
.fillMaxWidth()
|
||||||
|
.padding(bottom = 16.dp),
|
||||||
|
color = MaterialTheme.colorScheme.surfaceContainer,
|
||||||
) {
|
) {
|
||||||
Column {
|
Column(
|
||||||
|
modifier = Modifier.padding(bottom = 16.dp)
|
||||||
|
) {
|
||||||
Row(
|
Row(
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
modifier = Modifier.padding(vertical = 8.dp, horizontal = 12.dp)
|
modifier = Modifier.padding(vertical = 8.dp, horizontal = 12.dp)
|
||||||
@ -253,327 +262,60 @@ fun PluginSettingsScreen(pluginId: String) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
val surfaceColor by animateColorAsState(
|
Column(
|
||||||
if (pluginPackage?.enabled == true) {
|
modifier = Modifier.padding(horizontal = 12.dp),
|
||||||
MaterialTheme.colorScheme.secondaryContainer
|
verticalArrangement = Arrangement.spacedBy(12.dp),
|
||||||
} else {
|
|
||||||
MaterialTheme.colorScheme.surfaceContainer
|
|
||||||
}
|
|
||||||
)
|
|
||||||
Surface(
|
|
||||||
modifier = Modifier
|
|
||||||
.fillMaxWidth(),
|
|
||||||
color = surfaceColor,
|
|
||||||
) {
|
) {
|
||||||
SwitchPreference(
|
PreferenceCategory {
|
||||||
enabled = pluginPackage != null && hasPermission != null,
|
SwitchPreference(
|
||||||
iconPadding = false,
|
enabled = pluginPackage != null && hasPermission != null,
|
||||||
title = stringResource(R.string.preference_plugin_enable),
|
iconPadding = false,
|
||||||
value = pluginPackage?.enabled == true && hasPermission == true,
|
title = stringResource(R.string.preference_plugin_enable),
|
||||||
onValueChanged = {
|
value = pluginPackage?.enabled == true && hasPermission == true,
|
||||||
if (hasPermission == true) {
|
onValueChanged = {
|
||||||
viewModel.setPluginEnabled(it)
|
if (hasPermission == true) {
|
||||||
} else {
|
viewModel.setPluginEnabled(it)
|
||||||
requestPermissionStarter.launch(
|
} else {
|
||||||
Intent().apply {
|
requestPermissionStarter.launch(
|
||||||
`package` = pluginPackage?.packageName
|
Intent().apply {
|
||||||
action = "de.mm20.launcher2.plugin.REQUEST_PERMISSION"
|
`package` = pluginPackage?.packageName
|
||||||
}
|
action = "de.mm20.launcher2.plugin.REQUEST_PERMISSION"
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
AnimatedVisibility(pluginPackage?.enabled == true && hasPermission == true) {
|
|
||||||
Column(
|
|
||||||
modifier = Modifier.verticalScroll(rememberScrollState())
|
|
||||||
) {
|
|
||||||
if (filePlugins.isNotEmpty()) {
|
|
||||||
PreferenceCategory(
|
|
||||||
stringResource(R.string.plugin_type_filesearch),
|
|
||||||
iconPadding = false,
|
|
||||||
) {
|
|
||||||
for (plugin in filePlugins) {
|
|
||||||
val state = plugin.state
|
|
||||||
if (state is PluginState.SetupRequired) {
|
|
||||||
Banner(
|
|
||||||
modifier = Modifier.padding(16.dp),
|
|
||||||
text = state.message
|
|
||||||
?: stringResource(R.string.plugin_state_setup_required),
|
|
||||||
icon = Icons.Rounded.Info,
|
|
||||||
primaryAction = {
|
|
||||||
TextButton(onClick = {
|
|
||||||
try {
|
|
||||||
state.setupActivity.sendWithBackgroundPermission(
|
|
||||||
context
|
|
||||||
)
|
|
||||||
} catch (e: PendingIntent.CanceledException) {
|
|
||||||
CrashReporter.logException(e)
|
|
||||||
}
|
|
||||||
}) {
|
|
||||||
Text(stringResource(R.string.plugin_action_setup))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
} else if (state is PluginState.Error) {
|
|
||||||
Banner(
|
|
||||||
modifier = Modifier.padding(16.dp),
|
|
||||||
text = stringResource(R.string.plugin_state_error),
|
|
||||||
icon = Icons.Rounded.Error,
|
|
||||||
color = MaterialTheme.colorScheme.errorContainer,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
SwitchPreference(
|
|
||||||
title = plugin.plugin.label,
|
|
||||||
enabled = enabledFileSearchPlugins != null && state is PluginState.Ready,
|
|
||||||
summary = (state as? PluginState.Ready)?.text
|
|
||||||
?: (state as? PluginState.SetupRequired)?.message
|
|
||||||
?: plugin.plugin.description,
|
|
||||||
value = enabledFileSearchPlugins?.contains(plugin.plugin.authority) == true && state is PluginState.Ready,
|
|
||||||
onValueChanged = {
|
|
||||||
viewModel.setFileSearchPluginEnabled(
|
|
||||||
plugin.plugin.authority,
|
|
||||||
it
|
|
||||||
)
|
|
||||||
},
|
|
||||||
iconPadding = false,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (contactPlugins.isNotEmpty()) {
|
|
||||||
PreferenceCategory(
|
|
||||||
stringResource(R.string.plugin_type_contacts),
|
|
||||||
iconPadding = false,
|
|
||||||
) {
|
|
||||||
for (plugin in contactPlugins) {
|
|
||||||
val state = plugin.state
|
|
||||||
if (state is PluginState.SetupRequired) {
|
|
||||||
Banner(
|
|
||||||
modifier = Modifier.padding(16.dp),
|
|
||||||
text = state.message
|
|
||||||
?: stringResource(R.string.plugin_state_setup_required),
|
|
||||||
icon = Icons.Rounded.Info,
|
|
||||||
primaryAction = {
|
|
||||||
TextButton(onClick = {
|
|
||||||
try {
|
|
||||||
state.setupActivity.sendWithBackgroundPermission(
|
|
||||||
context
|
|
||||||
)
|
|
||||||
} catch (e: PendingIntent.CanceledException) {
|
|
||||||
CrashReporter.logException(e)
|
|
||||||
}
|
|
||||||
}) {
|
|
||||||
Text(stringResource(R.string.plugin_action_setup))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
} else if (state is PluginState.Error) {
|
|
||||||
Banner(
|
|
||||||
modifier = Modifier.padding(16.dp),
|
|
||||||
text = stringResource(R.string.plugin_state_error),
|
|
||||||
icon = Icons.Rounded.Error,
|
|
||||||
color = MaterialTheme.colorScheme.errorContainer,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
SwitchPreference(
|
|
||||||
title = plugin.plugin.label,
|
|
||||||
enabled = enabledContactPlugins != null && state is PluginState.Ready,
|
|
||||||
summary = (state as? PluginState.Ready)?.text
|
|
||||||
?: (state as? PluginState.SetupRequired)?.message
|
|
||||||
?: plugin.plugin.description,
|
|
||||||
value = enabledContactPlugins?.contains(plugin.plugin.authority) == true && state is PluginState.Ready,
|
|
||||||
onValueChanged = {
|
|
||||||
viewModel.setContactPluginEnabled(
|
|
||||||
plugin.plugin.authority,
|
|
||||||
it
|
|
||||||
)
|
|
||||||
},
|
|
||||||
iconPadding = false,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (locationPlugins.isNotEmpty()) {
|
|
||||||
PreferenceCategory(
|
|
||||||
stringResource(R.string.plugin_type_locationsearch),
|
|
||||||
iconPadding = false,
|
|
||||||
) {
|
|
||||||
for (plugin in locationPlugins) {
|
|
||||||
val state = plugin.state
|
|
||||||
if (state is PluginState.SetupRequired) {
|
|
||||||
Banner(
|
|
||||||
modifier = Modifier.padding(16.dp),
|
|
||||||
text = state.message
|
|
||||||
?: stringResource(R.string.plugin_state_setup_required),
|
|
||||||
icon = Icons.Rounded.Info,
|
|
||||||
primaryAction = {
|
|
||||||
TextButton(onClick = {
|
|
||||||
try {
|
|
||||||
state.setupActivity.sendWithBackgroundPermission(
|
|
||||||
context
|
|
||||||
)
|
|
||||||
} catch (e: PendingIntent.CanceledException) {
|
|
||||||
CrashReporter.logException(e)
|
|
||||||
}
|
|
||||||
}) {
|
|
||||||
Text(stringResource(R.string.plugin_action_setup))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
} else if (state is PluginState.Error) {
|
|
||||||
Banner(
|
|
||||||
modifier = Modifier.padding(16.dp),
|
|
||||||
text = stringResource(R.string.plugin_state_error),
|
|
||||||
icon = Icons.Rounded.Error,
|
|
||||||
color = MaterialTheme.colorScheme.errorContainer,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
SwitchPreference(
|
|
||||||
title = plugin.plugin.label,
|
|
||||||
enabled = enabledLocationSearchPlugins != null && state is PluginState.Ready,
|
|
||||||
summary = (state as? PluginState.Ready)?.text
|
|
||||||
?: (state as? PluginState.SetupRequired)?.message
|
|
||||||
?: plugin.plugin.description,
|
|
||||||
value = enabledLocationSearchPlugins?.contains(plugin.plugin.authority) == true && state is PluginState.Ready,
|
|
||||||
onValueChanged = {
|
|
||||||
viewModel.setLocationSearchPluginEnabled(
|
|
||||||
plugin.plugin.authority,
|
|
||||||
it
|
|
||||||
)
|
|
||||||
},
|
|
||||||
iconPadding = false,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (calendarPlugins.isNotEmpty()) {
|
|
||||||
PreferenceCategory(
|
|
||||||
stringResource(R.string.plugin_type_calendar),
|
|
||||||
iconPadding = false,
|
|
||||||
) {
|
|
||||||
val excludedCalendars by viewModel.excludedCalendars.collectAsState(
|
|
||||||
emptySet()
|
|
||||||
)
|
|
||||||
for (plugin in calendarPlugins) {
|
|
||||||
val state = plugin.state
|
|
||||||
if (state is PluginState.SetupRequired) {
|
|
||||||
Banner(
|
|
||||||
modifier = Modifier.padding(16.dp),
|
|
||||||
text = state.message
|
|
||||||
?: stringResource(R.string.plugin_state_setup_required),
|
|
||||||
icon = Icons.Rounded.Info,
|
|
||||||
primaryAction = {
|
|
||||||
TextButton(onClick = {
|
|
||||||
try {
|
|
||||||
state.setupActivity.sendWithBackgroundPermission(
|
|
||||||
context
|
|
||||||
)
|
|
||||||
} catch (e: PendingIntent.CanceledException) {
|
|
||||||
CrashReporter.logException(e)
|
|
||||||
}
|
|
||||||
}) {
|
|
||||||
Text(stringResource(R.string.plugin_action_setup))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
} else if (state is PluginState.Error) {
|
|
||||||
Banner(
|
|
||||||
modifier = Modifier.padding(16.dp),
|
|
||||||
text = stringResource(R.string.plugin_state_error),
|
|
||||||
icon = Icons.Rounded.Error,
|
|
||||||
color = MaterialTheme.colorScheme.errorContainer,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
val calendarLists by remember(plugin.state, plugin.plugin) {
|
|
||||||
viewModel.getCalendarLists(plugin.plugin)
|
|
||||||
}.collectAsStateWithLifecycle(
|
|
||||||
null,
|
|
||||||
minActiveState = Lifecycle.State.RESUMED
|
|
||||||
)
|
|
||||||
var showDialog by remember { mutableStateOf(false) }
|
|
||||||
val selectedCalendars = remember(excludedCalendars, calendarLists) {
|
|
||||||
calendarLists?.size?.minus(excludedCalendars.count {
|
|
||||||
it.startsWith(
|
|
||||||
plugin.plugin.authority
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
PreferenceWithSwitch(
|
|
||||||
title = plugin.plugin.label,
|
|
||||||
enabled = enabledCalendarSearchPlugins != null && state is PluginState.Ready,
|
|
||||||
summary = (state as? PluginState.SetupRequired)?.message
|
|
||||||
?: if (selectedCalendars != null && calendarLists != null) {
|
|
||||||
pluralStringResource(
|
|
||||||
R.plurals.calendar_search_enabled_lists,
|
|
||||||
selectedCalendars,
|
|
||||||
selectedCalendars
|
|
||||||
)
|
|
||||||
}
|
|
||||||
else (state as? PluginState.Ready)?.text
|
|
||||||
?: plugin.plugin.description,
|
|
||||||
switchValue = enabledCalendarSearchPlugins?.contains(plugin.plugin.authority) == true && state is PluginState.Ready,
|
|
||||||
onSwitchChanged = {
|
|
||||||
viewModel.setCalendarSearchPluginEnabled(
|
|
||||||
plugin.plugin.authority,
|
|
||||||
it
|
|
||||||
)
|
|
||||||
},
|
|
||||||
iconPadding = false,
|
|
||||||
onClick = {
|
|
||||||
showDialog = true
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
if (showDialog && calendarLists?.isNotEmpty() == true) {
|
}
|
||||||
ModalBottomSheet(
|
},
|
||||||
onDismissRequest = {
|
containerColor = MaterialTheme.colorScheme.secondaryContainer
|
||||||
showDialog = false
|
)
|
||||||
},
|
}
|
||||||
) {
|
AnimatedVisibility(pluginPackage?.enabled == true && hasPermission == true) {
|
||||||
LazyColumn {
|
Column(
|
||||||
items(calendarLists!!) {
|
modifier = Modifier.verticalScroll(rememberScrollState())
|
||||||
CheckboxPreference(
|
) {
|
||||||
title = it.name,
|
if (filePlugins.isNotEmpty()) {
|
||||||
summary = it.owner,
|
PreferenceCategory(
|
||||||
iconPadding = false,
|
stringResource(R.string.plugin_type_filesearch),
|
||||||
value = it.id !in excludedCalendars,
|
iconPadding = false,
|
||||||
onValueChanged = { value ->
|
) {
|
||||||
viewModel.setCalendarExcluded(it.id, !value)
|
for (plugin in filePlugins) {
|
||||||
},
|
val state = plugin.state
|
||||||
|
GuardedPreference(
|
||||||
|
locked = state is PluginState.Error || state is PluginState.SetupRequired,
|
||||||
|
icon = if (state is PluginState.Error) Icons.Rounded.Error else Icons.Rounded.Info,
|
||||||
|
description = when (state) {
|
||||||
|
is PluginState.Error -> {
|
||||||
|
stringResource(R.string.plugin_state_error)
|
||||||
|
}
|
||||||
|
|
||||||
checkboxColors = CheckboxDefaults.colors(
|
is PluginState.SetupRequired -> {
|
||||||
checkedColor = if (it.color == 0) MaterialTheme.colorScheme.primary
|
state.message
|
||||||
else Color(
|
?: stringResource(R.string.plugin_state_setup_required)
|
||||||
it.color.atTone(if (LocalDarkTheme.current) 80 else 40)
|
|
||||||
),
|
|
||||||
checkmarkColor = if (it.color == 0) MaterialTheme.colorScheme.onPrimary
|
|
||||||
else Color(
|
|
||||||
it.color.atTone(if (LocalDarkTheme.current) 20 else 100)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
else -> ""
|
||||||
}
|
},
|
||||||
}
|
onUnlock = if (state is PluginState.SetupRequired) {
|
||||||
}
|
{
|
||||||
}
|
|
||||||
if (weatherPlugins.isNotEmpty()) {
|
|
||||||
PreferenceCategory(
|
|
||||||
stringResource(R.string.plugin_type_weather),
|
|
||||||
iconPadding = false,
|
|
||||||
) {
|
|
||||||
for (plugin in weatherPlugins) {
|
|
||||||
val state = plugin.state
|
|
||||||
if (state is PluginState.SetupRequired) {
|
|
||||||
Banner(
|
|
||||||
modifier = Modifier.padding(16.dp),
|
|
||||||
text = state.message
|
|
||||||
?: stringResource(R.string.plugin_state_setup_required),
|
|
||||||
icon = Icons.Rounded.Info,
|
|
||||||
primaryAction = {
|
|
||||||
TextButton(onClick = {
|
|
||||||
try {
|
try {
|
||||||
state.setupActivity.sendWithBackgroundPermission(
|
state.setupActivity.sendWithBackgroundPermission(
|
||||||
context
|
context
|
||||||
@ -581,40 +323,278 @@ fun PluginSettingsScreen(pluginId: String) {
|
|||||||
} catch (e: PendingIntent.CanceledException) {
|
} catch (e: PendingIntent.CanceledException) {
|
||||||
CrashReporter.logException(e)
|
CrashReporter.logException(e)
|
||||||
}
|
}
|
||||||
}) {
|
|
||||||
Text(stringResource(R.string.plugin_action_setup))
|
|
||||||
}
|
}
|
||||||
|
} else null
|
||||||
|
) {
|
||||||
|
SwitchPreference(
|
||||||
|
title = plugin.plugin.label,
|
||||||
|
enabled = enabledFileSearchPlugins != null && state is PluginState.Ready,
|
||||||
|
summary = (state as? PluginState.Ready)?.text
|
||||||
|
?: (state as? PluginState.SetupRequired)?.message
|
||||||
|
?: plugin.plugin.description,
|
||||||
|
value = enabledFileSearchPlugins?.contains(plugin.plugin.authority) == true && state is PluginState.Ready,
|
||||||
|
onValueChanged = {
|
||||||
|
viewModel.setFileSearchPluginEnabled(
|
||||||
|
plugin.plugin.authority,
|
||||||
|
it
|
||||||
|
)
|
||||||
|
},
|
||||||
|
iconPadding = false,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (contactPlugins.isNotEmpty()) {
|
||||||
|
PreferenceCategory(
|
||||||
|
stringResource(R.string.plugin_type_contacts),
|
||||||
|
iconPadding = false,
|
||||||
|
) {
|
||||||
|
for (plugin in contactPlugins) {
|
||||||
|
val state = plugin.state
|
||||||
|
GuardedPreference(
|
||||||
|
locked = state is PluginState.Error || state is PluginState.SetupRequired,
|
||||||
|
icon = if (state is PluginState.Error) Icons.Rounded.Error else Icons.Rounded.Info,
|
||||||
|
description = when (state) {
|
||||||
|
is PluginState.Error -> {
|
||||||
|
stringResource(R.string.plugin_state_error)
|
||||||
|
}
|
||||||
|
|
||||||
|
is PluginState.SetupRequired -> {
|
||||||
|
state.message
|
||||||
|
?: stringResource(R.string.plugin_state_setup_required)
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> ""
|
||||||
|
},
|
||||||
|
onUnlock = if (state is PluginState.SetupRequired) {
|
||||||
|
{
|
||||||
|
|
||||||
|
try {
|
||||||
|
state.setupActivity.sendWithBackgroundPermission(
|
||||||
|
context
|
||||||
|
)
|
||||||
|
} catch (e: PendingIntent.CanceledException) {
|
||||||
|
CrashReporter.logException(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else null
|
||||||
|
) {
|
||||||
|
SwitchPreference(
|
||||||
|
title = plugin.plugin.label,
|
||||||
|
enabled = enabledContactPlugins != null && state is PluginState.Ready,
|
||||||
|
summary = (state as? PluginState.Ready)?.text
|
||||||
|
?: (state as? PluginState.SetupRequired)?.message
|
||||||
|
?: plugin.plugin.description,
|
||||||
|
value = enabledContactPlugins?.contains(plugin.plugin.authority) == true && state is PluginState.Ready,
|
||||||
|
onValueChanged = {
|
||||||
|
viewModel.setContactPluginEnabled(
|
||||||
|
plugin.plugin.authority,
|
||||||
|
it
|
||||||
|
)
|
||||||
|
},
|
||||||
|
iconPadding = false,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (locationPlugins.isNotEmpty()) {
|
||||||
|
PreferenceCategory(
|
||||||
|
stringResource(R.string.plugin_type_locationsearch),
|
||||||
|
iconPadding = false,
|
||||||
|
) {
|
||||||
|
for (plugin in locationPlugins) {
|
||||||
|
val state = plugin.state
|
||||||
|
GuardedPreference(
|
||||||
|
locked = state is PluginState.Error || state is PluginState.SetupRequired,
|
||||||
|
icon = if (state is PluginState.Error) Icons.Rounded.Error else Icons.Rounded.Info,
|
||||||
|
description = when (state) {
|
||||||
|
is PluginState.Error -> {
|
||||||
|
stringResource(R.string.plugin_state_error)
|
||||||
|
}
|
||||||
|
|
||||||
|
is PluginState.SetupRequired -> {
|
||||||
|
state.message
|
||||||
|
?: stringResource(R.string.plugin_state_setup_required)
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> ""
|
||||||
|
},
|
||||||
|
onUnlock = if (state is PluginState.SetupRequired) {
|
||||||
|
{
|
||||||
|
|
||||||
|
try {
|
||||||
|
state.setupActivity.sendWithBackgroundPermission(
|
||||||
|
context
|
||||||
|
)
|
||||||
|
} catch (e: PendingIntent.CanceledException) {
|
||||||
|
CrashReporter.logException(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else null
|
||||||
|
) {
|
||||||
|
SwitchPreference(
|
||||||
|
title = plugin.plugin.label,
|
||||||
|
enabled = enabledLocationSearchPlugins != null && state is PluginState.Ready,
|
||||||
|
summary = (state as? PluginState.Ready)?.text
|
||||||
|
?: (state as? PluginState.SetupRequired)?.message
|
||||||
|
?: plugin.plugin.description,
|
||||||
|
value = enabledLocationSearchPlugins?.contains(plugin.plugin.authority) == true && state is PluginState.Ready,
|
||||||
|
onValueChanged = {
|
||||||
|
viewModel.setLocationSearchPluginEnabled(
|
||||||
|
plugin.plugin.authority,
|
||||||
|
it
|
||||||
|
)
|
||||||
|
},
|
||||||
|
iconPadding = false,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (calendarPlugins.isNotEmpty()) {
|
||||||
|
PreferenceCategory(
|
||||||
|
stringResource(R.string.plugin_type_calendar),
|
||||||
|
iconPadding = false,
|
||||||
|
) {
|
||||||
|
val excludedCalendars by viewModel.excludedCalendars.collectAsState(
|
||||||
|
emptySet()
|
||||||
|
)
|
||||||
|
for (plugin in calendarPlugins) {
|
||||||
|
val state = plugin.state
|
||||||
|
|
||||||
|
val calendarLists by remember(plugin.state, plugin.plugin) {
|
||||||
|
viewModel.getCalendarLists(plugin.plugin)
|
||||||
|
}.collectAsStateWithLifecycle(
|
||||||
|
null,
|
||||||
|
minActiveState = Lifecycle.State.RESUMED
|
||||||
|
)
|
||||||
|
val selectedCalendars =
|
||||||
|
remember(excludedCalendars, calendarLists) {
|
||||||
|
calendarLists?.size?.minus(excludedCalendars.count {
|
||||||
|
it.startsWith(
|
||||||
|
plugin.plugin.authority
|
||||||
|
)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
)
|
GuardedPreference(
|
||||||
} else if (state is PluginState.Error) {
|
locked = state is PluginState.Error || state is PluginState.SetupRequired,
|
||||||
Banner(
|
icon = if (state is PluginState.Error) Icons.Rounded.Error else Icons.Rounded.Info,
|
||||||
modifier = Modifier.padding(16.dp),
|
description = when (state) {
|
||||||
text = stringResource(R.string.plugin_state_error),
|
is PluginState.Error -> {
|
||||||
icon = Icons.Rounded.Error,
|
stringResource(R.string.plugin_state_error)
|
||||||
color = MaterialTheme.colorScheme.errorContainer,
|
}
|
||||||
)
|
|
||||||
|
is PluginState.SetupRequired -> {
|
||||||
|
state.message
|
||||||
|
?: stringResource(R.string.plugin_state_setup_required)
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> ""
|
||||||
|
},
|
||||||
|
onUnlock = if (state is PluginState.SetupRequired) {
|
||||||
|
{
|
||||||
|
|
||||||
|
try {
|
||||||
|
state.setupActivity.sendWithBackgroundPermission(
|
||||||
|
context
|
||||||
|
)
|
||||||
|
} catch (e: PendingIntent.CanceledException) {
|
||||||
|
CrashReporter.logException(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else null
|
||||||
|
) {
|
||||||
|
PreferenceWithSwitch(
|
||||||
|
title = plugin.plugin.label,
|
||||||
|
enabled = enabledCalendarSearchPlugins != null && state is PluginState.Ready,
|
||||||
|
summary = (state as? PluginState.SetupRequired)?.message
|
||||||
|
?: if (selectedCalendars != null && calendarLists != null) {
|
||||||
|
pluralStringResource(
|
||||||
|
R.plurals.calendar_search_enabled_lists,
|
||||||
|
selectedCalendars,
|
||||||
|
selectedCalendars
|
||||||
|
)
|
||||||
|
} else (state as? PluginState.Ready)?.text
|
||||||
|
?: plugin.plugin.description,
|
||||||
|
switchValue = enabledCalendarSearchPlugins?.contains(
|
||||||
|
plugin.plugin.authority
|
||||||
|
) == true && state is PluginState.Ready,
|
||||||
|
onSwitchChanged = {
|
||||||
|
viewModel.setCalendarSearchPluginEnabled(
|
||||||
|
plugin.plugin.authority,
|
||||||
|
it
|
||||||
|
)
|
||||||
|
},
|
||||||
|
iconPadding = false,
|
||||||
|
onClick = {
|
||||||
|
navController?.navigate("settings/search/calendar/${plugin.plugin.authority}")
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (weatherPlugins.isNotEmpty()) {
|
||||||
|
PreferenceCategory(
|
||||||
|
stringResource(R.string.plugin_type_weather),
|
||||||
|
iconPadding = false,
|
||||||
|
) {
|
||||||
|
for (plugin in weatherPlugins) {
|
||||||
|
val state = plugin.state
|
||||||
|
GuardedPreference(
|
||||||
|
locked = state is PluginState.Error || state is PluginState.SetupRequired,
|
||||||
|
icon = if (state is PluginState.Error) Icons.Rounded.Error else Icons.Rounded.Info,
|
||||||
|
description = when (state) {
|
||||||
|
is PluginState.Error -> {
|
||||||
|
stringResource(R.string.plugin_state_error)
|
||||||
|
}
|
||||||
|
|
||||||
|
is PluginState.SetupRequired -> {
|
||||||
|
state.message
|
||||||
|
?: stringResource(R.string.plugin_state_setup_required)
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> ""
|
||||||
|
},
|
||||||
|
onUnlock = if (state is PluginState.SetupRequired) {
|
||||||
|
{
|
||||||
|
|
||||||
|
try {
|
||||||
|
state.setupActivity.sendWithBackgroundPermission(
|
||||||
|
context
|
||||||
|
)
|
||||||
|
} catch (e: PendingIntent.CanceledException) {
|
||||||
|
CrashReporter.logException(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else null
|
||||||
|
) {
|
||||||
|
Preference(
|
||||||
|
title = plugin.plugin.label,
|
||||||
|
enabled = state is PluginState.Ready && weatherProviderId != plugin.plugin.authority,
|
||||||
|
iconPadding = false,
|
||||||
|
summary = if (weatherProviderId != plugin.plugin.authority) {
|
||||||
|
stringResource(R.string.plugin_weather_provider_enable)
|
||||||
|
} else {
|
||||||
|
stringResource(R.string.plugin_weather_provider_enabled)
|
||||||
|
},
|
||||||
|
onClick = {
|
||||||
|
viewModel.setWeatherProvider(plugin.plugin.authority)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Preference(
|
Preference(
|
||||||
title = plugin.plugin.label,
|
title = stringResource(R.string.widget_config_weather_integration_settings),
|
||||||
enabled = state is PluginState.Ready && weatherProviderId != plugin.plugin.authority,
|
icon = Icons.AutoMirrored.Rounded.OpenInNew,
|
||||||
iconPadding = false,
|
|
||||||
summary = if (weatherProviderId != plugin.plugin.authority) {
|
|
||||||
stringResource(R.string.plugin_weather_provider_enable)
|
|
||||||
} else {
|
|
||||||
stringResource(R.string.plugin_weather_provider_enabled)
|
|
||||||
},
|
|
||||||
onClick = {
|
onClick = {
|
||||||
viewModel.setWeatherProvider(plugin.plugin.authority)
|
navController?.navigate("settings/integrations/weather")
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
Preference(
|
|
||||||
title = stringResource(R.string.widget_config_weather_integration_settings),
|
|
||||||
icon = Icons.AutoMirrored.Rounded.OpenInNew,
|
|
||||||
onClick = {
|
|
||||||
navController?.navigate("settings/integrations/weather")
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -31,7 +31,6 @@ import androidx.compose.ui.Modifier
|
|||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.lifecycle.compose.LocalLifecycleOwner
|
|
||||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||||
import de.mm20.launcher2.icons.Wikipedia
|
import de.mm20.launcher2.icons.Wikipedia
|
||||||
@ -40,6 +39,7 @@ import de.mm20.launcher2.ui.R
|
|||||||
import de.mm20.launcher2.ui.component.BottomSheetDialog
|
import de.mm20.launcher2.ui.component.BottomSheetDialog
|
||||||
import de.mm20.launcher2.ui.component.MissingPermissionBanner
|
import de.mm20.launcher2.ui.component.MissingPermissionBanner
|
||||||
import de.mm20.launcher2.ui.component.SmallMessage
|
import de.mm20.launcher2.ui.component.SmallMessage
|
||||||
|
import de.mm20.launcher2.ui.component.preferences.GuardedPreference
|
||||||
import de.mm20.launcher2.ui.component.preferences.ListPreference
|
import de.mm20.launcher2.ui.component.preferences.ListPreference
|
||||||
import de.mm20.launcher2.ui.component.preferences.Preference
|
import de.mm20.launcher2.ui.component.preferences.Preference
|
||||||
import de.mm20.launcher2.ui.component.preferences.PreferenceCategory
|
import de.mm20.launcher2.ui.component.preferences.PreferenceCategory
|
||||||
@ -65,7 +65,9 @@ fun SearchSettingsScreen() {
|
|||||||
val hasContactPlugins by remember { derivedStateOf { plugins?.any { it.plugin.type == PluginType.ContactSearch } } }
|
val hasContactPlugins by remember { derivedStateOf { plugins?.any { it.plugin.type == PluginType.ContactSearch } } }
|
||||||
val isTasksAppInstalled by viewModel.isTasksAppInstalled.collectAsStateWithLifecycle()
|
val isTasksAppInstalled by viewModel.isTasksAppInstalled.collectAsStateWithLifecycle()
|
||||||
|
|
||||||
val hasAppShortcutsPermission by viewModel.hasAppShortcutPermission.collectAsStateWithLifecycle(null)
|
val hasAppShortcutsPermission by viewModel.hasAppShortcutPermission.collectAsStateWithLifecycle(
|
||||||
|
null
|
||||||
|
)
|
||||||
val hasContactsPermission by viewModel.hasContactsPermission.collectAsStateWithLifecycle(null)
|
val hasContactsPermission by viewModel.hasContactsPermission.collectAsStateWithLifecycle(null)
|
||||||
val hasCalendarPermission by viewModel.hasCalendarPermission.collectAsStateWithLifecycle(null)
|
val hasCalendarPermission by viewModel.hasCalendarPermission.collectAsStateWithLifecycle(null)
|
||||||
val hasLocationPermission by viewModel.hasLocationPermission.collectAsStateWithLifecycle(null)
|
val hasLocationPermission by viewModel.hasLocationPermission.collectAsStateWithLifecycle(null)
|
||||||
@ -121,28 +123,27 @@ fun SearchSettingsScreen() {
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
AnimatedVisibility(hasContactsPermission == false) {
|
GuardedPreference(
|
||||||
MissingPermissionBanner(
|
locked = hasContactsPermission == false,
|
||||||
text = stringResource(R.string.missing_permission_contact_search_settings),
|
onUnlock = {
|
||||||
onClick = {
|
viewModel.requestContactsPermission(context as AppCompatActivity)
|
||||||
viewModel.requestContactsPermission(context as AppCompatActivity)
|
},
|
||||||
|
description = stringResource(R.string.missing_permission_contact_search_settings),
|
||||||
|
) {
|
||||||
|
PreferenceWithSwitch(
|
||||||
|
title = stringResource(R.string.preference_search_contacts),
|
||||||
|
summary = stringResource(R.string.preference_search_contacts_summary),
|
||||||
|
icon = Icons.Rounded.Person,
|
||||||
|
switchValue = contacts == true && hasContactsPermission == true,
|
||||||
|
onSwitchChanged = {
|
||||||
|
viewModel.setContacts(it)
|
||||||
},
|
},
|
||||||
modifier = Modifier.padding(16.dp)
|
onClick = {
|
||||||
|
navController?.navigate("settings/search/contacts")
|
||||||
|
},
|
||||||
|
enabled = hasContactsPermission == true
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
PreferenceWithSwitch(
|
|
||||||
title = stringResource(R.string.preference_search_contacts),
|
|
||||||
summary = stringResource(R.string.preference_search_contacts_summary),
|
|
||||||
icon = Icons.Rounded.Person,
|
|
||||||
switchValue = contacts == true && hasContactsPermission == true,
|
|
||||||
onSwitchChanged = {
|
|
||||||
viewModel.setContacts(it)
|
|
||||||
},
|
|
||||||
onClick = {
|
|
||||||
navController?.navigate("settings/search/contacts")
|
|
||||||
},
|
|
||||||
enabled = hasContactsPermission == true
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hasCalendarPlugins != false || isTasksAppInstalled != false) {
|
if (hasCalendarPlugins != false || isTasksAppInstalled != false) {
|
||||||
@ -155,51 +156,50 @@ fun SearchSettingsScreen() {
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
AnimatedVisibility(hasCalendarPermission == false) {
|
|
||||||
MissingPermissionBanner(
|
GuardedPreference(
|
||||||
text = stringResource(R.string.missing_permission_calendar_search_settings),
|
locked = hasCalendarPermission == false,
|
||||||
onClick = {
|
onUnlock = {
|
||||||
viewModel.requestCalendarPermission(context as AppCompatActivity)
|
viewModel.requestCalendarPermission(context as AppCompatActivity)
|
||||||
|
},
|
||||||
|
description = stringResource(R.string.missing_permission_calendar_search_settings),
|
||||||
|
) {
|
||||||
|
PreferenceWithSwitch(
|
||||||
|
title = stringResource(R.string.preference_search_calendar),
|
||||||
|
summary = stringResource(R.string.preference_search_calendar_summary),
|
||||||
|
switchValue = calendar == true,
|
||||||
|
onSwitchChanged = {
|
||||||
|
viewModel.setCalendarSearch(it)
|
||||||
},
|
},
|
||||||
modifier = Modifier.padding(16.dp)
|
icon = Icons.Rounded.Today,
|
||||||
|
enabled = hasCalendarPermission == true,
|
||||||
|
onClick = {
|
||||||
|
navController?.navigate("settings/search/calendar/local")
|
||||||
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
PreferenceWithSwitch(
|
|
||||||
title = stringResource(R.string.preference_search_calendar),
|
|
||||||
summary = stringResource(R.string.preference_search_calendar_summary),
|
|
||||||
switchValue = calendar == true,
|
|
||||||
onSwitchChanged = {
|
|
||||||
viewModel.setCalendarSearch(it)
|
|
||||||
},
|
|
||||||
icon = Icons.Rounded.Today,
|
|
||||||
enabled = hasCalendarPermission == true,
|
|
||||||
onClick = {
|
|
||||||
navController?.navigate("settings/search/calendar/local")
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
AnimatedVisibility(hasAppShortcutsPermission == false) {
|
GuardedPreference(
|
||||||
MissingPermissionBanner(
|
locked = hasAppShortcutsPermission == false,
|
||||||
text = stringResource(
|
onUnlock = {
|
||||||
R.string.missing_permission_appshortcuts_search_settings,
|
viewModel.requestAppShortcutsPermission(context as AppCompatActivity)
|
||||||
stringResource(R.string.app_name)
|
|
||||||
),
|
|
||||||
onClick = {
|
|
||||||
viewModel.requestAppShortcutsPermission(context as AppCompatActivity)
|
|
||||||
},
|
|
||||||
modifier = Modifier.padding(16.dp)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
SwitchPreference(
|
|
||||||
title = stringResource(R.string.preference_search_appshortcuts),
|
|
||||||
summary = stringResource(R.string.preference_search_appshortcuts_summary),
|
|
||||||
icon = Icons.Rounded.AppShortcut,
|
|
||||||
value = appShortcuts == true && hasAppShortcutsPermission == true,
|
|
||||||
onValueChanged = {
|
|
||||||
viewModel.setAppShortcuts(it)
|
|
||||||
},
|
},
|
||||||
enabled = hasAppShortcutsPermission == true
|
description = stringResource(
|
||||||
)
|
R.string.missing_permission_appshortcuts_search_settings,
|
||||||
|
stringResource(R.string.app_name),
|
||||||
|
),
|
||||||
|
) {
|
||||||
|
SwitchPreference(
|
||||||
|
title = stringResource(R.string.preference_search_appshortcuts),
|
||||||
|
summary = stringResource(R.string.preference_search_appshortcuts_summary),
|
||||||
|
icon = Icons.Rounded.AppShortcut,
|
||||||
|
value = appShortcuts == true && hasAppShortcutsPermission == true,
|
||||||
|
onValueChanged = {
|
||||||
|
viewModel.setAppShortcuts(it)
|
||||||
|
},
|
||||||
|
enabled = hasAppShortcutsPermission == true
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
SwitchPreference(
|
SwitchPreference(
|
||||||
title = stringResource(R.string.preference_search_calculator),
|
title = stringResource(R.string.preference_search_calculator),
|
||||||
@ -246,43 +246,38 @@ fun SearchSettingsScreen() {
|
|||||||
viewModel.setWebsites(it)
|
viewModel.setWebsites(it)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
GuardedPreference(
|
||||||
AnimatedVisibility(hasLocationPermission == false) {
|
locked = hasLocationPermission == false,
|
||||||
MissingPermissionBanner(
|
onUnlock = {
|
||||||
text = stringResource(
|
viewModel.requestLocationPermission(context as AppCompatActivity)
|
||||||
R.string.missing_permission_location_search,
|
},
|
||||||
),
|
description = stringResource(R.string.missing_permission_location_search),
|
||||||
onClick = {
|
) {
|
||||||
viewModel.requestLocationPermission(context as AppCompatActivity)
|
if (hasLocationPlugins != false) {
|
||||||
},
|
Preference(
|
||||||
modifier = Modifier.padding(16.dp)
|
title = stringResource(R.string.preference_search_locations),
|
||||||
)
|
summary = stringResource(R.string.preference_search_locations_summary),
|
||||||
}
|
icon = Icons.Rounded.Place,
|
||||||
|
enabled = hasLocationPermission == true,
|
||||||
if (hasLocationPlugins != false) {
|
onClick = {
|
||||||
Preference(
|
navController?.navigate("settings/search/locations")
|
||||||
title = stringResource(R.string.preference_search_locations),
|
}
|
||||||
summary = stringResource(R.string.preference_search_locations_summary),
|
)
|
||||||
icon = Icons.Rounded.Place,
|
} else {
|
||||||
enabled = hasLocationPermission == true,
|
PreferenceWithSwitch(
|
||||||
onClick = {
|
title = stringResource(R.string.preference_search_locations),
|
||||||
navController?.navigate("settings/search/locations")
|
summary = stringResource(R.string.preference_search_locations_summary),
|
||||||
}
|
icon = Icons.Rounded.Place,
|
||||||
)
|
onClick = {
|
||||||
} else {
|
navController?.navigate("settings/search/locations")
|
||||||
PreferenceWithSwitch(
|
},
|
||||||
title = stringResource(R.string.preference_search_locations),
|
switchValue = places == true,
|
||||||
summary = stringResource(R.string.preference_search_locations_summary),
|
onSwitchChanged = {
|
||||||
icon = Icons.Rounded.Place,
|
viewModel.setPlacesSearch(it)
|
||||||
onClick = {
|
},
|
||||||
navController?.navigate("settings/search/locations")
|
enabled = hasLocationPermission == true,
|
||||||
},
|
)
|
||||||
switchValue = places == true,
|
}
|
||||||
onSwitchChanged = {
|
|
||||||
viewModel.setPlacesSearch(it)
|
|
||||||
},
|
|
||||||
enabled = hasLocationPermission == true,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Preference(
|
Preference(
|
||||||
@ -327,6 +322,7 @@ fun SearchSettingsScreen() {
|
|||||||
)
|
)
|
||||||
SwitchPreference(
|
SwitchPreference(
|
||||||
title = stringResource(R.string.preference_filter_bar),
|
title = stringResource(R.string.preference_filter_bar),
|
||||||
|
iconPadding = true,
|
||||||
summary = stringResource(R.string.preference_filter_bar_summary),
|
summary = stringResource(R.string.preference_filter_bar_summary),
|
||||||
value = filterBar == true,
|
value = filterBar == true,
|
||||||
onValueChanged = {
|
onValueChanged = {
|
||||||
@ -336,6 +332,7 @@ fun SearchSettingsScreen() {
|
|||||||
AnimatedVisibility(filterBar == true) {
|
AnimatedVisibility(filterBar == true) {
|
||||||
Preference(
|
Preference(
|
||||||
title = stringResource(R.string.preference_customize_filter_bar),
|
title = stringResource(R.string.preference_customize_filter_bar),
|
||||||
|
iconPadding = true,
|
||||||
summary = stringResource(R.string.preference_customize_filter_bar_summary),
|
summary = stringResource(R.string.preference_customize_filter_bar_summary),
|
||||||
onClick = {
|
onClick = {
|
||||||
navController?.navigate("settings/search/filterbar")
|
navController?.navigate("settings/search/filterbar")
|
||||||
@ -357,6 +354,7 @@ fun SearchSettingsScreen() {
|
|||||||
)
|
)
|
||||||
SwitchPreference(
|
SwitchPreference(
|
||||||
title = stringResource(R.string.preference_search_bar_launch_on_enter),
|
title = stringResource(R.string.preference_search_bar_launch_on_enter),
|
||||||
|
iconPadding = true,
|
||||||
summary = stringResource(R.string.preference_search_bar_launch_on_enter_summary),
|
summary = stringResource(R.string.preference_search_bar_launch_on_enter_summary),
|
||||||
value = launchOnEnter == true,
|
value = launchOnEnter == true,
|
||||||
onValueChanged = {
|
onValueChanged = {
|
||||||
|
|||||||
@ -29,8 +29,6 @@ import androidx.compose.material3.AlertDialog
|
|||||||
import androidx.compose.material3.BottomSheetDefaults
|
import androidx.compose.material3.BottomSheetDefaults
|
||||||
import androidx.compose.material3.Button
|
import androidx.compose.material3.Button
|
||||||
import androidx.compose.material3.ButtonDefaults
|
import androidx.compose.material3.ButtonDefaults
|
||||||
import androidx.compose.material3.Card
|
|
||||||
import androidx.compose.material3.CardDefaults
|
|
||||||
import androidx.compose.material3.DropdownMenuItem
|
import androidx.compose.material3.DropdownMenuItem
|
||||||
import androidx.compose.material3.FilterChip
|
import androidx.compose.material3.FilterChip
|
||||||
import androidx.compose.material3.FloatingActionButton
|
import androidx.compose.material3.FloatingActionButton
|
||||||
@ -53,6 +51,7 @@ import androidx.compose.runtime.remember
|
|||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.draw.clip
|
||||||
import androidx.compose.ui.draw.rotate
|
import androidx.compose.ui.draw.rotate
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
@ -222,6 +221,8 @@ fun ShapeSchemeSettingsScreen(themeId: UUID) {
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
item {
|
||||||
PreferenceCategory {
|
PreferenceCategory {
|
||||||
ShapePreview(
|
ShapePreview(
|
||||||
previewShapes = previewShapes,
|
previewShapes = previewShapes,
|
||||||
@ -246,6 +247,8 @@ fun ShapeSchemeSettingsScreen(themeId: UUID) {
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
item {
|
||||||
PreferenceCategory {
|
PreferenceCategory {
|
||||||
ShapePreview(
|
ShapePreview(
|
||||||
previewShapes = previewShapes,
|
previewShapes = previewShapes,
|
||||||
@ -279,7 +282,9 @@ fun ShapeSchemeSettingsScreen(themeId: UUID) {
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
PreferenceCategory {
|
}
|
||||||
|
item {
|
||||||
|
PreferenceCategory(title = "Large") {
|
||||||
ShapePreview(
|
ShapePreview(
|
||||||
previewShapes = previewShapes,
|
previewShapes = previewShapes,
|
||||||
) {
|
) {
|
||||||
@ -299,6 +304,8 @@ fun ShapeSchemeSettingsScreen(themeId: UUID) {
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
item {
|
||||||
PreferenceCategory {
|
PreferenceCategory {
|
||||||
ShapePreference(
|
ShapePreference(
|
||||||
title = "Large increased",
|
title = "Large increased",
|
||||||
@ -312,6 +319,8 @@ fun ShapeSchemeSettingsScreen(themeId: UUID) {
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
item {
|
||||||
PreferenceCategory {
|
PreferenceCategory {
|
||||||
ShapePreview(
|
ShapePreview(
|
||||||
previewShapes = previewShapes,
|
previewShapes = previewShapes,
|
||||||
@ -344,6 +353,8 @@ fun ShapeSchemeSettingsScreen(themeId: UUID) {
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
item {
|
||||||
PreferenceCategory {
|
PreferenceCategory {
|
||||||
ShapePreference(
|
ShapePreference(
|
||||||
title = "Extra large increased",
|
title = "Extra large increased",
|
||||||
@ -357,6 +368,8 @@ fun ShapeSchemeSettingsScreen(themeId: UUID) {
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
item {
|
||||||
PreferenceCategory {
|
PreferenceCategory {
|
||||||
ShapePreference(
|
ShapePreference(
|
||||||
title = "Extra extra large",
|
title = "Extra extra large",
|
||||||
@ -398,11 +411,12 @@ fun ShapePreference(
|
|||||||
Row(
|
Row(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.padding(bottom = 8.dp)
|
.clip(MaterialTheme.shapes.extraSmall)
|
||||||
|
.background(MaterialTheme.colorScheme.surface)
|
||||||
.clickable(
|
.clickable(
|
||||||
onClick = { showDialog = true },
|
onClick = { showDialog = true },
|
||||||
)
|
)
|
||||||
.padding(horizontal = 16.dp, vertical = 8.dp),
|
.padding(16.dp),
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
) {
|
) {
|
||||||
Box(
|
Box(
|
||||||
@ -706,28 +720,20 @@ private fun ShapePreview(
|
|||||||
previewShapes: Shapes,
|
previewShapes: Shapes,
|
||||||
content: @Composable () -> Unit,
|
content: @Composable () -> Unit,
|
||||||
) {
|
) {
|
||||||
MaterialTheme(
|
Row(
|
||||||
shapes = previewShapes
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.clip(MaterialTheme.shapes.extraSmall)
|
||||||
|
.background(MaterialTheme.colorScheme.surfaceContainerLowest)
|
||||||
|
.horizontalScroll(rememberScrollState())
|
||||||
|
.padding(16.dp),
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(16.dp)
|
||||||
) {
|
) {
|
||||||
Card(
|
MaterialTheme(
|
||||||
modifier = Modifier
|
shapes = previewShapes
|
||||||
.fillMaxWidth()
|
|
||||||
.padding(16.dp),
|
|
||||||
colors = CardDefaults.cardColors(
|
|
||||||
containerColor = MaterialTheme.colorScheme.surfaceContainer
|
|
||||||
)
|
|
||||||
) {
|
) {
|
||||||
|
content()
|
||||||
Row(
|
|
||||||
modifier = Modifier
|
|
||||||
.fillMaxWidth()
|
|
||||||
.horizontalScroll(rememberScrollState())
|
|
||||||
.padding(16.dp),
|
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
|
||||||
horizontalArrangement = Arrangement.spacedBy(16.dp)
|
|
||||||
) {
|
|
||||||
content()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2,6 +2,7 @@ package de.mm20.launcher2.ui.settings.tasks
|
|||||||
|
|
||||||
import androidx.activity.compose.LocalActivity
|
import androidx.activity.compose.LocalActivity
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.automirrored.rounded.OpenInNew
|
import androidx.compose.material.icons.automirrored.rounded.OpenInNew
|
||||||
@ -9,6 +10,7 @@ import androidx.compose.material.icons.rounded.CheckCircle
|
|||||||
import androidx.compose.material.icons.rounded.Info
|
import androidx.compose.material.icons.rounded.Info
|
||||||
import androidx.compose.material.icons.rounded.TaskAlt
|
import androidx.compose.material.icons.rounded.TaskAlt
|
||||||
import androidx.compose.material3.Button
|
import androidx.compose.material3.Button
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
@ -19,7 +21,7 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
|||||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||||
import de.mm20.launcher2.ui.R
|
import de.mm20.launcher2.ui.R
|
||||||
import de.mm20.launcher2.ui.component.Banner
|
import de.mm20.launcher2.ui.component.Banner
|
||||||
import de.mm20.launcher2.ui.component.MissingPermissionBanner
|
import de.mm20.launcher2.ui.component.preferences.GuardedPreference
|
||||||
import de.mm20.launcher2.ui.component.preferences.Preference
|
import de.mm20.launcher2.ui.component.preferences.Preference
|
||||||
import de.mm20.launcher2.ui.component.preferences.PreferenceCategory
|
import de.mm20.launcher2.ui.component.preferences.PreferenceCategory
|
||||||
import de.mm20.launcher2.ui.component.preferences.PreferenceScreen
|
import de.mm20.launcher2.ui.component.preferences.PreferenceScreen
|
||||||
@ -41,55 +43,63 @@ fun TasksIntegrationSettingsScreen() {
|
|||||||
) {
|
) {
|
||||||
if (isTasksInstalled == false) {
|
if (isTasksInstalled == false) {
|
||||||
item {
|
item {
|
||||||
Banner(
|
PreferenceCategory {
|
||||||
text = stringResource(
|
Banner(
|
||||||
R.string.preference_tasks_integration_description,
|
text = stringResource(
|
||||||
stringResource(R.string.app_name)
|
R.string.preference_tasks_integration_description,
|
||||||
),
|
stringResource(R.string.app_name)
|
||||||
icon = Icons.Rounded.Info,
|
),
|
||||||
modifier = Modifier.padding(16.dp),
|
icon = Icons.Rounded.Info,
|
||||||
primaryAction = {
|
modifier = Modifier
|
||||||
Button(onClick = {
|
.background(MaterialTheme.colorScheme.surface)
|
||||||
viewModel.downloadTasksApp(activity as AppCompatActivity)
|
.padding(16.dp),
|
||||||
}) {
|
primaryAction = {
|
||||||
Text(stringResource(R.string.action_install))
|
Button(onClick = {
|
||||||
|
viewModel.downloadTasksApp(activity as AppCompatActivity)
|
||||||
|
}) {
|
||||||
|
Text(stringResource(R.string.action_install))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
)
|
||||||
)
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (isTasksInstalled == true) {
|
if (isTasksInstalled == true) {
|
||||||
item {
|
item {
|
||||||
PreferenceCategory {
|
PreferenceCategory {
|
||||||
if (hasTasksPermission == false) {
|
|
||||||
MissingPermissionBanner(
|
|
||||||
modifier = Modifier.padding(16.dp),
|
|
||||||
text = stringResource(R.string.missing_permission_tasks_integration),
|
|
||||||
onClick = {
|
|
||||||
viewModel.requestTasksPermission(activity as AppCompatActivity)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
if (hasTasksPermission == true) {
|
if (hasTasksPermission == true) {
|
||||||
Banner(
|
Banner(
|
||||||
text = stringResource(R.string.preference_tasks_integration_ready),
|
text = stringResource(R.string.preference_tasks_integration_ready),
|
||||||
icon = Icons.Rounded.CheckCircle,
|
icon = Icons.Rounded.CheckCircle,
|
||||||
modifier = Modifier.padding(16.dp),
|
modifier = Modifier
|
||||||
|
.background(
|
||||||
|
MaterialTheme.colorScheme.surface,
|
||||||
|
MaterialTheme.shapes.extraSmall
|
||||||
|
)
|
||||||
|
.padding(16.dp),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
PreferenceWithSwitch(
|
GuardedPreference(
|
||||||
icon = Icons.Rounded.TaskAlt,
|
locked = hasTasksPermission == false,
|
||||||
title = stringResource(R.string.preference_search_tasks),
|
onUnlock = {
|
||||||
summary = stringResource(R.string.preference_search_tasks_summary),
|
viewModel.requestTasksPermission(activity as AppCompatActivity)
|
||||||
switchValue = isTasksSearchEnabled == true && hasTasksPermission == true,
|
|
||||||
onSwitchChanged = {
|
|
||||||
viewModel.setTasksSearchEnabled(it)
|
|
||||||
},
|
},
|
||||||
enabled = hasTasksPermission == true,
|
description = stringResource(R.string.missing_permission_tasks_integration),
|
||||||
onClick = {
|
) {
|
||||||
navController?.navigate("settings/search/calendar/tasks.org")
|
PreferenceWithSwitch(
|
||||||
}
|
icon = Icons.Rounded.TaskAlt,
|
||||||
)
|
title = stringResource(R.string.preference_search_tasks),
|
||||||
|
summary = stringResource(R.string.preference_search_tasks_summary),
|
||||||
|
switchValue = isTasksSearchEnabled == true && hasTasksPermission == true,
|
||||||
|
onSwitchChanged = {
|
||||||
|
viewModel.setTasksSearchEnabled(it)
|
||||||
|
},
|
||||||
|
enabled = hasTasksPermission == true,
|
||||||
|
onClick = {
|
||||||
|
navController?.navigate("settings/search/calendar/tasks.org")
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
Preference(
|
Preference(
|
||||||
title = stringResource(R.string.preference_launch_tasks_app),
|
title = stringResource(R.string.preference_launch_tasks_app),
|
||||||
icon = Icons.AutoMirrored.Rounded.OpenInNew,
|
icon = Icons.AutoMirrored.Rounded.OpenInNew,
|
||||||
|
|||||||
@ -45,6 +45,8 @@ fun UnitConverterSettingsScreen() {
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
item {
|
||||||
PreferenceCategory {
|
PreferenceCategory {
|
||||||
Preference(
|
Preference(
|
||||||
title = stringResource(R.string.preference_search_supportedunits),
|
title = stringResource(R.string.preference_search_supportedunits),
|
||||||
|
|||||||
@ -10,19 +10,34 @@ fun makeTypography(
|
|||||||
val baseTypography = Typography()
|
val baseTypography = Typography()
|
||||||
return Typography(
|
return Typography(
|
||||||
displayLarge = baseTypography.displayLarge.copy(fontFamily = headlineFamily, fontWeight = FontWeight.Medium),
|
displayLarge = baseTypography.displayLarge.copy(fontFamily = headlineFamily, fontWeight = FontWeight.Medium),
|
||||||
|
displayLargeEmphasized = baseTypography.displayLarge.copy(fontFamily = headlineFamily, fontWeight = FontWeight.SemiBold),
|
||||||
displayMedium = baseTypography.displayMedium.copy(fontFamily = headlineFamily, fontWeight = FontWeight.Medium),
|
displayMedium = baseTypography.displayMedium.copy(fontFamily = headlineFamily, fontWeight = FontWeight.Medium),
|
||||||
|
displayMediumEmphasized = baseTypography.displayMedium.copy(fontFamily = headlineFamily, fontWeight = FontWeight.SemiBold),
|
||||||
displaySmall = baseTypography.displaySmall.copy(fontFamily = headlineFamily, fontWeight = FontWeight.Medium),
|
displaySmall = baseTypography.displaySmall.copy(fontFamily = headlineFamily, fontWeight = FontWeight.Medium),
|
||||||
|
displaySmallEmphasized = baseTypography.displaySmall.copy(fontFamily = headlineFamily, fontWeight = FontWeight.SemiBold),
|
||||||
headlineLarge = baseTypography.headlineLarge.copy(fontFamily = headlineFamily, fontWeight = FontWeight.SemiBold),
|
headlineLarge = baseTypography.headlineLarge.copy(fontFamily = headlineFamily, fontWeight = FontWeight.SemiBold),
|
||||||
|
headlineLargeEmphasized = baseTypography.headlineLarge.copy(fontFamily = headlineFamily, fontWeight = FontWeight.Bold),
|
||||||
headlineMedium = baseTypography.headlineMedium.copy(fontFamily = headlineFamily, fontWeight = FontWeight.SemiBold),
|
headlineMedium = baseTypography.headlineMedium.copy(fontFamily = headlineFamily, fontWeight = FontWeight.SemiBold),
|
||||||
|
headlineMediumEmphasized = baseTypography.headlineMedium.copy(fontFamily = headlineFamily, fontWeight = FontWeight.Bold),
|
||||||
headlineSmall = baseTypography.headlineSmall.copy(fontFamily = headlineFamily, fontWeight = FontWeight.SemiBold),
|
headlineSmall = baseTypography.headlineSmall.copy(fontFamily = headlineFamily, fontWeight = FontWeight.SemiBold),
|
||||||
|
headlineSmallEmphasized = baseTypography.headlineSmall.copy(fontFamily = headlineFamily, fontWeight = FontWeight.Bold),
|
||||||
titleLarge = baseTypography.titleLarge.copy(fontFamily = headlineFamily, fontWeight = FontWeight.SemiBold),
|
titleLarge = baseTypography.titleLarge.copy(fontFamily = headlineFamily, fontWeight = FontWeight.SemiBold),
|
||||||
titleMedium = baseTypography.titleMedium.copy(fontFamily = headlineFamily, fontWeight = FontWeight.SemiBold),
|
titleLargeEmphasized = baseTypography.titleLargeEmphasized.copy(fontFamily = headlineFamily, fontWeight = FontWeight.Bold),
|
||||||
|
titleMedium = baseTypography.titleMedium.copy(fontFamily = headlineFamily),
|
||||||
|
titleMediumEmphasized = baseTypography.titleMediumEmphasized.copy(fontFamily = headlineFamily),
|
||||||
titleSmall = baseTypography.titleSmall.copy(fontFamily = headlineFamily, fontWeight = FontWeight.SemiBold),
|
titleSmall = baseTypography.titleSmall.copy(fontFamily = headlineFamily, fontWeight = FontWeight.SemiBold),
|
||||||
|
titleSmallEmphasized = baseTypography.titleSmallEmphasized.copy(fontFamily = headlineFamily, fontWeight = FontWeight.Bold),
|
||||||
bodyLarge = baseTypography.bodyLarge.copy(fontFamily = bodyFamily),
|
bodyLarge = baseTypography.bodyLarge.copy(fontFamily = bodyFamily),
|
||||||
|
bodyLargeEmphasized = baseTypography.bodyLargeEmphasized.copy(fontFamily = bodyFamily, fontWeight = FontWeight.Medium),
|
||||||
bodyMedium = baseTypography.bodyMedium.copy(fontFamily = bodyFamily),
|
bodyMedium = baseTypography.bodyMedium.copy(fontFamily = bodyFamily),
|
||||||
|
bodyMediumEmphasized = baseTypography.bodyMediumEmphasized.copy(fontFamily = bodyFamily, fontWeight = FontWeight.Medium),
|
||||||
bodySmall = baseTypography.bodySmall.copy(fontFamily = bodyFamily),
|
bodySmall = baseTypography.bodySmall.copy(fontFamily = bodyFamily),
|
||||||
labelLarge = baseTypography.labelLarge.copy(fontFamily = headlineFamily, fontWeight = FontWeight.SemiBold),
|
bodySmallEmphasized = baseTypography.bodySmallEmphasized.copy(fontFamily = bodyFamily, fontWeight = FontWeight.Medium),
|
||||||
labelMedium = baseTypography.labelMedium.copy(fontFamily = headlineFamily, fontWeight = FontWeight.SemiBold),
|
labelLarge = baseTypography.labelLarge.copy(fontFamily = headlineFamily, fontWeight = FontWeight.Medium),
|
||||||
labelSmall = baseTypography.labelSmall.copy(fontFamily = headlineFamily, fontWeight = FontWeight.SemiBold)
|
labelLargeEmphasized = baseTypography.labelLargeEmphasized.copy(fontFamily = headlineFamily, fontWeight = FontWeight.SemiBold),
|
||||||
|
labelMedium = baseTypography.labelMedium.copy(fontFamily = headlineFamily, fontWeight = FontWeight.Medium),
|
||||||
|
labelMediumEmphasized = baseTypography.labelMediumEmphasized.copy(fontFamily = headlineFamily, fontWeight = FontWeight.SemiBold),
|
||||||
|
labelSmall = baseTypography.labelSmall.copy(fontFamily = headlineFamily, fontWeight = FontWeight.Medium),
|
||||||
|
labelSmallEmphasized = baseTypography.labelSmallEmphasized.copy(fontFamily = headlineFamily, fontWeight = FontWeight.SemiBold),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -11,6 +11,7 @@ val Outfit = FontFamily(
|
|||||||
Font(R.font.outfit_200, weight = FontWeight.ExtraLight, style = FontStyle.Normal),
|
Font(R.font.outfit_200, weight = FontWeight.ExtraLight, style = FontStyle.Normal),
|
||||||
Font(R.font.outfit_300, weight = FontWeight.Light, style = FontStyle.Normal),
|
Font(R.font.outfit_300, weight = FontWeight.Light, style = FontStyle.Normal),
|
||||||
Font(R.font.outfit_400, weight = FontWeight.Normal, style = FontStyle.Normal),
|
Font(R.font.outfit_400, weight = FontWeight.Normal, style = FontStyle.Normal),
|
||||||
|
Font(R.font.outfit_500, weight = FontWeight.Medium, style = FontStyle.Normal),
|
||||||
Font(R.font.outfit_600, weight = FontWeight.SemiBold, style = FontStyle.Normal),
|
Font(R.font.outfit_600, weight = FontWeight.SemiBold, style = FontStyle.Normal),
|
||||||
Font(R.font.outfit_700, weight = FontWeight.Bold, style = FontStyle.Normal),
|
Font(R.font.outfit_700, weight = FontWeight.Bold, style = FontStyle.Normal),
|
||||||
Font(R.font.outfit_800, weight = FontWeight.ExtraBold, style = FontStyle.Normal),
|
Font(R.font.outfit_800, weight = FontWeight.ExtraBold, style = FontStyle.Normal),
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user