Make clock widget configurable from home screen
This commit is contained in:
parent
6cf8747eee
commit
b518e1713c
@ -482,21 +482,13 @@ fun PagerScaffold(
|
|||||||
)
|
)
|
||||||
.padding(top = editModePadding)
|
.padding(top = editModePadding)
|
||||||
) {
|
) {
|
||||||
|
ClockWidget(
|
||||||
AnimatedVisibility(!isWidgetEditMode) {
|
modifier = Modifier
|
||||||
Box(
|
.fillMaxWidth()
|
||||||
modifier = Modifier
|
.then(clockHeight?.let { Modifier.height(it) } ?: Modifier)
|
||||||
.fillMaxWidth()
|
.padding(bottom = clockPadding),
|
||||||
.then(clockHeight?.let { Modifier.height(it) }
|
editMode = isWidgetEditMode,
|
||||||
?: Modifier)
|
)
|
||||||
.padding(bottom = clockPadding),
|
|
||||||
contentAlignment = Alignment.BottomCenter
|
|
||||||
) {
|
|
||||||
ClockWidget(
|
|
||||||
modifier = Modifier.fillMaxWidth()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
WidgetColumn(
|
WidgetColumn(
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
|||||||
@ -1,6 +1,5 @@
|
|||||||
package de.mm20.launcher2.ui.launcher
|
package de.mm20.launcher2.ui.launcher
|
||||||
|
|
||||||
import android.app.WallpaperManager
|
|
||||||
import android.view.HapticFeedbackConstants
|
import android.view.HapticFeedbackConstants
|
||||||
import androidx.activity.compose.BackHandler
|
import androidx.activity.compose.BackHandler
|
||||||
import androidx.compose.animation.AnimatedVisibility
|
import androidx.compose.animation.AnimatedVisibility
|
||||||
@ -50,15 +49,12 @@ import androidx.compose.runtime.remember
|
|||||||
import androidx.compose.runtime.rememberCoroutineScope
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
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.blur
|
|
||||||
import androidx.compose.ui.geometry.Offset
|
import androidx.compose.ui.geometry.Offset
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.graphics.TransformOrigin
|
import androidx.compose.ui.graphics.TransformOrigin
|
||||||
import androidx.compose.ui.graphics.graphicsLayer
|
import androidx.compose.ui.graphics.graphicsLayer
|
||||||
import androidx.compose.ui.hapticfeedback.HapticFeedbackType
|
|
||||||
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.pointer.pointerInput
|
import androidx.compose.ui.input.pointer.pointerInput
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.platform.LocalDensity
|
import androidx.compose.ui.platform.LocalDensity
|
||||||
@ -295,6 +291,7 @@ fun PullDownScaffold(
|
|||||||
}
|
}
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> false
|
else -> false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -469,20 +466,14 @@ fun PullDownScaffold(
|
|||||||
)
|
)
|
||||||
.padding(top = editModePadding)
|
.padding(top = editModePadding)
|
||||||
) {
|
) {
|
||||||
AnimatedVisibility(!isWidgetEditMode) {
|
ClockWidget(
|
||||||
Box(
|
modifier = Modifier
|
||||||
modifier = Modifier
|
.fillMaxWidth()
|
||||||
.fillMaxWidth()
|
.then(clockHeight?.let { Modifier.height(it) } ?: Modifier)
|
||||||
.then(clockHeight?.let { Modifier.height(it) }
|
.padding(bottom = clockPadding),
|
||||||
?: Modifier)
|
editMode = isWidgetEditMode,
|
||||||
.padding(bottom = clockPadding),
|
)
|
||||||
contentAlignment = Alignment.BottomCenter
|
|
||||||
) {
|
|
||||||
ClockWidget(
|
|
||||||
modifier = Modifier.fillMaxWidth()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
WidgetColumn(
|
WidgetColumn(
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.fillMaxWidth(),
|
||||||
editMode = isWidgetEditMode,
|
editMode = isWidgetEditMode,
|
||||||
@ -505,8 +496,14 @@ fun PullDownScaffold(
|
|||||||
pagerState.currentPage + pagerState.currentPageOffsetFraction
|
pagerState.currentPage + pagerState.currentPageOffsetFraction
|
||||||
transformOrigin = TransformOrigin.Center
|
transformOrigin = TransformOrigin.Center
|
||||||
alpha = min(progress, 1f - dragProgress * 0.1f)
|
alpha = min(progress, 1f - dragProgress * 0.1f)
|
||||||
scaleX = min(1f - (dragProgress * 0.05f), 1f - (1f - progress) * 0.1f)
|
scaleX = min(
|
||||||
scaleY = min(1f - (dragProgress * 0.05f),1f - (1f - progress) * 0.1f)
|
1f - (dragProgress * 0.05f),
|
||||||
|
1f - (1f - progress) * 0.1f
|
||||||
|
)
|
||||||
|
scaleY = min(
|
||||||
|
1f - (dragProgress * 0.05f),
|
||||||
|
1f - (1f - progress) * 0.1f
|
||||||
|
)
|
||||||
}
|
}
|
||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
.padding(
|
.padding(
|
||||||
|
|||||||
@ -1,5 +1,7 @@
|
|||||||
package de.mm20.launcher2.ui.launcher.widgets.clock
|
package de.mm20.launcher2.ui.launcher.widgets.clock
|
||||||
|
|
||||||
|
import androidx.compose.animation.AnimatedContent
|
||||||
|
import androidx.compose.animation.animateContentSize
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||||
@ -10,38 +12,82 @@ import androidx.compose.foundation.layout.Row
|
|||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.height
|
import androidx.compose.foundation.layout.height
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.layout.size
|
||||||
import androidx.compose.foundation.layout.width
|
import androidx.compose.foundation.layout.width
|
||||||
import androidx.compose.foundation.pager.HorizontalPager
|
import androidx.compose.foundation.pager.HorizontalPager
|
||||||
import androidx.compose.foundation.pager.rememberPagerState
|
import androidx.compose.foundation.pager.rememberPagerState
|
||||||
|
import androidx.compose.foundation.rememberScrollState
|
||||||
|
import androidx.compose.foundation.verticalScroll
|
||||||
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.rounded.Alarm
|
||||||
|
import androidx.compose.material.icons.rounded.AutoAwesome
|
||||||
|
import androidx.compose.material.icons.rounded.BatteryFull
|
||||||
|
import androidx.compose.material.icons.rounded.ChevronLeft
|
||||||
|
import androidx.compose.material.icons.rounded.ChevronRight
|
||||||
|
import androidx.compose.material.icons.rounded.DarkMode
|
||||||
|
import androidx.compose.material.icons.rounded.Height
|
||||||
|
import androidx.compose.material.icons.rounded.HorizontalSplit
|
||||||
|
import androidx.compose.material.icons.rounded.LightMode
|
||||||
|
import androidx.compose.material.icons.rounded.MusicNote
|
||||||
|
import androidx.compose.material.icons.rounded.Star
|
||||||
|
import androidx.compose.material.icons.rounded.Style
|
||||||
|
import androidx.compose.material.icons.rounded.Today
|
||||||
|
import androidx.compose.material.icons.rounded.Tune
|
||||||
|
import androidx.compose.material.icons.rounded.VerticalSplit
|
||||||
|
import androidx.compose.material3.ButtonDefaults
|
||||||
|
import androidx.compose.material3.HorizontalDivider
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.material3.IconButton
|
||||||
import androidx.compose.material3.LocalContentColor
|
import androidx.compose.material3.LocalContentColor
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.OutlinedCard
|
||||||
|
import androidx.compose.material3.SegmentedButton
|
||||||
|
import androidx.compose.material3.SegmentedButtonDefaults
|
||||||
|
import androidx.compose.material3.SingleChoiceSegmentedButtonRow
|
||||||
|
import androidx.compose.material3.Surface
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.material3.TextButton
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.CompositionLocalProvider
|
import androidx.compose.runtime.CompositionLocalProvider
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.collectAsState
|
import androidx.compose.runtime.collectAsState
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
|
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.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
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.preferences.Settings.ClockWidgetSettings.ClockStyle
|
import de.mm20.launcher2.preferences.Settings.ClockWidgetSettings.ClockStyle
|
||||||
import de.mm20.launcher2.preferences.Settings.ClockWidgetSettings.ClockWidgetColors
|
import de.mm20.launcher2.preferences.Settings.ClockWidgetSettings.ClockWidgetColors
|
||||||
import de.mm20.launcher2.preferences.Settings.ClockWidgetSettings.ClockWidgetLayout
|
import de.mm20.launcher2.preferences.Settings.ClockWidgetSettings.ClockWidgetLayout
|
||||||
|
import de.mm20.launcher2.ui.R
|
||||||
import de.mm20.launcher2.ui.base.LocalTime
|
import de.mm20.launcher2.ui.base.LocalTime
|
||||||
|
import de.mm20.launcher2.ui.component.BottomSheetDialog
|
||||||
|
import de.mm20.launcher2.ui.component.preferences.SwitchPreference
|
||||||
import de.mm20.launcher2.ui.launcher.widgets.clock.clocks.AnalogClock
|
import de.mm20.launcher2.ui.launcher.widgets.clock.clocks.AnalogClock
|
||||||
import de.mm20.launcher2.ui.launcher.widgets.clock.clocks.BinaryClock
|
import de.mm20.launcher2.ui.launcher.widgets.clock.clocks.BinaryClock
|
||||||
import de.mm20.launcher2.ui.launcher.widgets.clock.clocks.DigitalClock1
|
import de.mm20.launcher2.ui.launcher.widgets.clock.clocks.DigitalClock1
|
||||||
import de.mm20.launcher2.ui.launcher.widgets.clock.clocks.DigitalClock2
|
import de.mm20.launcher2.ui.launcher.widgets.clock.clocks.DigitalClock2
|
||||||
import de.mm20.launcher2.ui.launcher.widgets.clock.clocks.OrbitClock
|
import de.mm20.launcher2.ui.launcher.widgets.clock.clocks.OrbitClock
|
||||||
import de.mm20.launcher2.ui.launcher.widgets.clock.parts.PartProvider
|
import de.mm20.launcher2.ui.launcher.widgets.clock.parts.PartProvider
|
||||||
|
import de.mm20.launcher2.ui.locals.LocalDarkTheme
|
||||||
import de.mm20.launcher2.ui.locals.LocalPreferDarkContentOverWallpaper
|
import de.mm20.launcher2.ui.locals.LocalPreferDarkContentOverWallpaper
|
||||||
|
import de.mm20.launcher2.ui.settings.clockwidget.ClockWidgetSettingsScreenVM
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun ClockWidget(
|
fun ClockWidget(
|
||||||
modifier: Modifier = Modifier
|
modifier: Modifier = Modifier,
|
||||||
|
editMode: Boolean = false,
|
||||||
) {
|
) {
|
||||||
val viewModel: ClockWidgetVM = viewModel()
|
val viewModel: ClockWidgetVM = viewModel()
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
@ -50,6 +96,13 @@ fun ClockWidget(
|
|||||||
val color by viewModel.color.collectAsState()
|
val color by viewModel.color.collectAsState()
|
||||||
val time = LocalTime.current
|
val time = LocalTime.current
|
||||||
|
|
||||||
|
val contentColor =
|
||||||
|
if (color == ClockWidgetColors.Auto && LocalPreferDarkContentOverWallpaper.current || color == ClockWidgetColors.Dark) {
|
||||||
|
Color(0, 0, 0, 180)
|
||||||
|
} else {
|
||||||
|
Color.White
|
||||||
|
}
|
||||||
|
|
||||||
LaunchedEffect(time) {
|
LaunchedEffect(time) {
|
||||||
viewModel.updateTime(time)
|
viewModel.updateTime(time)
|
||||||
}
|
}
|
||||||
@ -58,95 +111,132 @@ fun ClockWidget(
|
|||||||
emptyList()
|
emptyList()
|
||||||
)
|
)
|
||||||
|
|
||||||
Box(
|
AnimatedContent(editMode, label = "ClockWidget") {
|
||||||
modifier = Modifier
|
if (it) {
|
||||||
.fillMaxWidth(),
|
var configure by remember { mutableStateOf(false) }
|
||||||
contentAlignment = Alignment.BottomCenter
|
Column {
|
||||||
) {
|
Surface(
|
||||||
|
|
||||||
val contentColor =
|
|
||||||
if (color == ClockWidgetColors.Auto && LocalPreferDarkContentOverWallpaper.current || color == ClockWidgetColors.Dark) {
|
|
||||||
Color(0, 0, 0, 180)
|
|
||||||
} else {
|
|
||||||
Color.White
|
|
||||||
}
|
|
||||||
|
|
||||||
CompositionLocalProvider(
|
|
||||||
LocalContentColor provides contentColor
|
|
||||||
) {
|
|
||||||
if (layout == ClockWidgetLayout.Vertical) {
|
|
||||||
Column(
|
|
||||||
horizontalAlignment = Alignment.CenterHorizontally,
|
|
||||||
) {
|
|
||||||
Box(
|
|
||||||
modifier = Modifier.clickable(
|
|
||||||
enabled = clockStyle != ClockStyle.EmptyClock,
|
|
||||||
indication = null,
|
|
||||||
interactionSource = remember { MutableInteractionSource() }
|
|
||||||
) {
|
|
||||||
viewModel.launchClockApp(context)
|
|
||||||
}
|
|
||||||
) {
|
|
||||||
Clock(clockStyle, ClockWidgetLayout.Vertical)
|
|
||||||
}
|
|
||||||
|
|
||||||
for (part in partProviders) {
|
|
||||||
DynamicZone(
|
|
||||||
modifier = Modifier.padding(bottom = 8.dp),
|
|
||||||
layout = ClockWidgetLayout.Vertical,
|
|
||||||
provider = part,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (layout == ClockWidgetLayout.Horizontal) {
|
|
||||||
Row(
|
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.padding(end = 8.dp, bottom = 16.dp),
|
.padding(vertical = 8.dp),
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
shape = MaterialTheme.shapes.medium,
|
||||||
horizontalArrangement = Arrangement.SpaceBetween
|
color = MaterialTheme.colorScheme.surfaceVariant,
|
||||||
|
shadowElevation = 2.dp,
|
||||||
|
tonalElevation = 2.dp,
|
||||||
) {
|
) {
|
||||||
if (partProviders.size > 1) {
|
Row(
|
||||||
HorizontalPager(
|
modifier = Modifier.padding(8.dp),
|
||||||
state = rememberPagerState { 2 },
|
verticalAlignment = Alignment.CenterVertically
|
||||||
beyondBoundsPageCount = 1,
|
) {
|
||||||
modifier = Modifier.weight(1f)
|
Box(modifier = Modifier.size(24.dp))
|
||||||
|
Text(
|
||||||
|
text = stringResource(id = R.string.preference_screen_clockwidget),
|
||||||
|
style = MaterialTheme.typography.titleMedium,
|
||||||
|
modifier = Modifier
|
||||||
|
.weight(1f)
|
||||||
|
.padding(horizontal = 8.dp),
|
||||||
|
overflow = TextOverflow.Ellipsis,
|
||||||
|
maxLines = 1
|
||||||
|
)
|
||||||
|
IconButton(onClick = {
|
||||||
|
configure = true
|
||||||
|
}) {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Rounded.Tune,
|
||||||
|
contentDescription = stringResource(R.string.settings)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
HorizontalDivider()
|
||||||
|
if (configure) {
|
||||||
|
ConfigureClockWidgetSheet(onDismiss = { configure = false })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Box(
|
||||||
|
modifier = modifier,
|
||||||
|
contentAlignment = Alignment.BottomCenter
|
||||||
|
) {
|
||||||
|
|
||||||
|
CompositionLocalProvider(
|
||||||
|
LocalContentColor provides contentColor
|
||||||
|
) {
|
||||||
|
if (layout == ClockWidgetLayout.Vertical) {
|
||||||
|
Column(
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
) {
|
) {
|
||||||
partProviders.getOrNull(it)?.let {
|
Box(
|
||||||
|
modifier = Modifier.clickable(
|
||||||
|
enabled = clockStyle != ClockStyle.EmptyClock,
|
||||||
|
indication = null,
|
||||||
|
interactionSource = remember { MutableInteractionSource() }
|
||||||
|
) {
|
||||||
|
viewModel.launchClockApp(context)
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
Clock(clockStyle, ClockWidgetLayout.Vertical)
|
||||||
|
}
|
||||||
|
|
||||||
|
for (part in partProviders) {
|
||||||
DynamicZone(
|
DynamicZone(
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.padding(bottom = 8.dp),
|
||||||
layout = ClockWidgetLayout.Horizontal,
|
layout = ClockWidgetLayout.Vertical,
|
||||||
provider = it,
|
provider = part,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (partProviders.isNotEmpty()) {
|
|
||||||
DynamicZone(
|
|
||||||
modifier = Modifier.weight(1f),
|
|
||||||
layout = ClockWidgetLayout.Horizontal,
|
|
||||||
provider = partProviders[0],
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
Box(
|
if (layout == ClockWidgetLayout.Horizontal) {
|
||||||
modifier = Modifier
|
Row(
|
||||||
.padding(horizontal = 16.dp)
|
modifier = Modifier
|
||||||
.height(56.dp)
|
.fillMaxWidth()
|
||||||
.width(2.dp)
|
.padding(end = 8.dp, bottom = 16.dp),
|
||||||
.background(
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
LocalContentColor.current
|
horizontalArrangement = Arrangement.SpaceBetween
|
||||||
),
|
|
||||||
)
|
|
||||||
Box(
|
|
||||||
modifier = Modifier.clickable(
|
|
||||||
enabled = clockStyle != ClockStyle.EmptyClock,
|
|
||||||
indication = null,
|
|
||||||
interactionSource = remember { MutableInteractionSource() }
|
|
||||||
) {
|
) {
|
||||||
viewModel.launchClockApp(context)
|
if (partProviders.size > 1) {
|
||||||
|
HorizontalPager(
|
||||||
|
state = rememberPagerState { 2 },
|
||||||
|
beyondBoundsPageCount = 1,
|
||||||
|
modifier = Modifier.weight(1f)
|
||||||
|
) {
|
||||||
|
partProviders.getOrNull(it)?.let {
|
||||||
|
DynamicZone(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
layout = ClockWidgetLayout.Horizontal,
|
||||||
|
provider = it,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (partProviders.isNotEmpty()) {
|
||||||
|
DynamicZone(
|
||||||
|
modifier = Modifier.weight(1f),
|
||||||
|
layout = ClockWidgetLayout.Horizontal,
|
||||||
|
provider = partProviders[0],
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(horizontal = 16.dp)
|
||||||
|
.height(56.dp)
|
||||||
|
.width(2.dp)
|
||||||
|
.background(
|
||||||
|
LocalContentColor.current
|
||||||
|
),
|
||||||
|
)
|
||||||
|
Box(
|
||||||
|
modifier = Modifier.clickable(
|
||||||
|
enabled = clockStyle != ClockStyle.EmptyClock,
|
||||||
|
indication = null,
|
||||||
|
interactionSource = remember { MutableInteractionSource() }
|
||||||
|
) {
|
||||||
|
viewModel.launchClockApp(context)
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
Clock(clockStyle, ClockWidgetLayout.Horizontal)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
) {
|
|
||||||
Clock(clockStyle, ClockWidgetLayout.Horizontal)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -182,4 +272,203 @@ fun DynamicZone(
|
|||||||
) {
|
) {
|
||||||
provider?.Component(layout)
|
provider?.Component(layout)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun ConfigureClockWidgetSheet(
|
||||||
|
onDismiss: () -> Unit,
|
||||||
|
) {
|
||||||
|
val viewModel: ClockWidgetSettingsScreenVM = viewModel()
|
||||||
|
val layout by viewModel.layout.collectAsState()
|
||||||
|
val color by viewModel.color.collectAsState()
|
||||||
|
val style by viewModel.clockStyle.collectAsState()
|
||||||
|
val fillHeight by viewModel.fillHeight.collectAsState()
|
||||||
|
|
||||||
|
val date by viewModel.datePart.collectAsState()
|
||||||
|
val favorites by viewModel.favoritesPart.collectAsState()
|
||||||
|
val media by viewModel.musicPart.collectAsState()
|
||||||
|
val alarm by viewModel.alarmPart.collectAsState()
|
||||||
|
val battery by viewModel.batteryPart.collectAsState()
|
||||||
|
|
||||||
|
|
||||||
|
BottomSheetDialog(onDismissRequest = onDismiss) {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.verticalScroll(rememberScrollState())
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(it)
|
||||||
|
) {
|
||||||
|
SingleChoiceSegmentedButtonRow(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
) {
|
||||||
|
SegmentedButton(
|
||||||
|
selected = layout == ClockWidgetLayout.Vertical,
|
||||||
|
onClick = {
|
||||||
|
viewModel.setLayout(ClockWidgetLayout.Vertical)
|
||||||
|
},
|
||||||
|
shape = SegmentedButtonDefaults.itemShape(index = 0, count = 2),
|
||||||
|
icon = {
|
||||||
|
SegmentedButtonDefaults.Icon(
|
||||||
|
active = layout == ClockWidgetLayout.Vertical,
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Rounded.HorizontalSplit,
|
||||||
|
contentDescription = null,
|
||||||
|
modifier = Modifier.size(SegmentedButtonDefaults.IconSize)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
Text(text = stringResource(R.string.preference_clockwidget_layout_vertical))
|
||||||
|
}
|
||||||
|
SegmentedButton(
|
||||||
|
selected = layout == ClockWidgetLayout.Horizontal,
|
||||||
|
onClick = {
|
||||||
|
viewModel.setLayout(ClockWidgetLayout.Horizontal)
|
||||||
|
},
|
||||||
|
shape = SegmentedButtonDefaults.itemShape(index = 1, count = 2),
|
||||||
|
icon = {
|
||||||
|
SegmentedButtonDefaults.Icon(
|
||||||
|
active = layout == ClockWidgetLayout.Horizontal,
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Rounded.VerticalSplit,
|
||||||
|
contentDescription = null,
|
||||||
|
modifier = Modifier.size(SegmentedButtonDefaults.IconSize)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
Text(text = stringResource(R.string.preference_clockwidget_layout_horizontal))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (color != null && layout != null) {
|
||||||
|
WatchFaceSelector(layout = layout!!, colors = color!!, selected = style, onSelect = {
|
||||||
|
viewModel.setClockStyle(it)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
SingleChoiceSegmentedButtonRow(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
) {
|
||||||
|
SegmentedButton(
|
||||||
|
selected = color == ClockWidgetColors.Auto,
|
||||||
|
onClick = {
|
||||||
|
viewModel.setColor(ClockWidgetColors.Auto)
|
||||||
|
},
|
||||||
|
shape = SegmentedButtonDefaults.itemShape(index = 0, count = 3),
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Rounded.AutoAwesome,
|
||||||
|
contentDescription = null,
|
||||||
|
modifier = Modifier.size(SegmentedButtonDefaults.IconSize)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
SegmentedButton(
|
||||||
|
selected = color == ClockWidgetColors.Dark,
|
||||||
|
onClick = {
|
||||||
|
viewModel.setColor(ClockWidgetColors.Dark)
|
||||||
|
},
|
||||||
|
shape = SegmentedButtonDefaults.itemShape(index = 1, count = 3),
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Rounded.LightMode,
|
||||||
|
contentDescription = null,
|
||||||
|
modifier = Modifier.size(SegmentedButtonDefaults.IconSize)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
SegmentedButton(
|
||||||
|
selected = color == ClockWidgetColors.Light,
|
||||||
|
onClick = {
|
||||||
|
viewModel.setColor(ClockWidgetColors.Light)
|
||||||
|
},
|
||||||
|
shape = SegmentedButtonDefaults.itemShape(index = 2, count = 3),
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Rounded.DarkMode,
|
||||||
|
contentDescription = null,
|
||||||
|
modifier = Modifier.size(SegmentedButtonDefaults.IconSize)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
OutlinedCard(
|
||||||
|
modifier = Modifier.padding(top = 16.dp),
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier.fillMaxWidth()
|
||||||
|
) {
|
||||||
|
SwitchPreference(
|
||||||
|
title = stringResource(R.string.preference_clock_widget_fill_height),
|
||||||
|
icon = Icons.Rounded.Height,
|
||||||
|
value = fillHeight == true,
|
||||||
|
onValueChanged = {
|
||||||
|
viewModel.setFillHeight(it)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
OutlinedCard(
|
||||||
|
modifier = Modifier.padding(top = 16.dp),
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier.fillMaxWidth()
|
||||||
|
) {
|
||||||
|
SwitchPreference(
|
||||||
|
title = stringResource(R.string.preference_clockwidget_date_part),
|
||||||
|
summary = stringResource(R.string.preference_clockwidget_date_part_summary),
|
||||||
|
icon = Icons.Rounded.Today,
|
||||||
|
value = date == true,
|
||||||
|
onValueChanged = {
|
||||||
|
viewModel.setDatePart(it)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
SwitchPreference(
|
||||||
|
title = stringResource(R.string.preference_clockwidget_favorites_part),
|
||||||
|
summary = stringResource(R.string.preference_clockwidget_favorites_part_summary),
|
||||||
|
icon = Icons.Rounded.Star,
|
||||||
|
value = favorites == true,
|
||||||
|
onValueChanged = {
|
||||||
|
viewModel.setFavoritesPart(it)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
OutlinedCard(
|
||||||
|
modifier = Modifier.padding(top = 16.dp),
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier.fillMaxWidth()
|
||||||
|
) {
|
||||||
|
SwitchPreference(
|
||||||
|
title = stringResource(R.string.preference_clockwidget_music_part),
|
||||||
|
summary = stringResource(R.string.preference_clockwidget_music_part_summary),
|
||||||
|
icon = Icons.Rounded.MusicNote,
|
||||||
|
value = media == true,
|
||||||
|
onValueChanged = {
|
||||||
|
viewModel.setMusicPart(it)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
SwitchPreference(
|
||||||
|
title = stringResource(R.string.preference_clockwidget_alarm_part),
|
||||||
|
summary = stringResource(R.string.preference_clockwidget_alarm_part_summary),
|
||||||
|
icon = Icons.Rounded.Alarm,
|
||||||
|
value = alarm == true,
|
||||||
|
onValueChanged = {
|
||||||
|
viewModel.setAlarmPart(it)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
SwitchPreference(
|
||||||
|
title = stringResource(R.string.preference_clockwidget_battery_part),
|
||||||
|
summary = stringResource(R.string.preference_clockwidget_battery_part_summary),
|
||||||
|
icon = Icons.Rounded.BatteryFull,
|
||||||
|
value = battery == true,
|
||||||
|
onValueChanged = {
|
||||||
|
viewModel.setBatteryPart(it)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -0,0 +1,276 @@
|
|||||||
|
package de.mm20.launcher2.ui.launcher.widgets.clock
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import androidx.compose.animation.animateContentSize
|
||||||
|
import androidx.compose.animation.scaleIn
|
||||||
|
import androidx.compose.animation.scaleOut
|
||||||
|
import androidx.compose.foundation.layout.Box
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.layout.size
|
||||||
|
import androidx.compose.foundation.pager.HorizontalPager
|
||||||
|
import androidx.compose.foundation.pager.rememberPagerState
|
||||||
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.rounded.ArrowDropDown
|
||||||
|
import androidx.compose.material.icons.rounded.ChevronLeft
|
||||||
|
import androidx.compose.material.icons.rounded.ChevronRight
|
||||||
|
import androidx.compose.material.icons.rounded.Style
|
||||||
|
import androidx.compose.material3.ButtonDefaults
|
||||||
|
import androidx.compose.material3.DropdownMenu
|
||||||
|
import androidx.compose.material3.DropdownMenuItem
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.material3.IconButton
|
||||||
|
import androidx.compose.material3.LocalContentColor
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.Surface
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.material3.TextButton
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.CompositionLocalProvider
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.compose.ui.zIndex
|
||||||
|
import de.mm20.launcher2.preferences.Settings.ClockWidgetSettings.ClockStyle
|
||||||
|
import de.mm20.launcher2.preferences.Settings.ClockWidgetSettings.ClockWidgetColors
|
||||||
|
import de.mm20.launcher2.preferences.Settings.ClockWidgetSettings.ClockWidgetLayout
|
||||||
|
import de.mm20.launcher2.ui.locals.LocalDarkTheme
|
||||||
|
import de.mm20.launcher2.ui.locals.LocalPreferDarkContentOverWallpaper
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun WatchFaceSelector(
|
||||||
|
layout: ClockWidgetLayout,
|
||||||
|
colors: ClockWidgetColors,
|
||||||
|
selected: ClockStyle?,
|
||||||
|
onSelect: (ClockStyle) -> Unit,
|
||||||
|
) {
|
||||||
|
val context = LocalContext.current
|
||||||
|
Surface(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(vertical = 16.dp, horizontal = 32.dp),
|
||||||
|
color = if (colors == ClockWidgetColors.Dark || colors == ClockWidgetColors.Auto && LocalPreferDarkContentOverWallpaper.current) {
|
||||||
|
if (LocalDarkTheme.current) MaterialTheme.colorScheme.inverseSurface else MaterialTheme.colorScheme.surfaceContainer
|
||||||
|
} else {
|
||||||
|
if (LocalDarkTheme.current) MaterialTheme.colorScheme.surfaceContainer else MaterialTheme.colorScheme.inverseSurface
|
||||||
|
},
|
||||||
|
shape = MaterialTheme.shapes.medium,
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier,
|
||||||
|
) {
|
||||||
|
val styles = remember {
|
||||||
|
sortedMapOf(
|
||||||
|
ClockStyle.DigitalClock1 to 0,
|
||||||
|
ClockStyle.DigitalClock2 to 1,
|
||||||
|
ClockStyle.AnalogClock to 2,
|
||||||
|
ClockStyle.OrbitClock to 3,
|
||||||
|
ClockStyle.BinaryClock to 4,
|
||||||
|
ClockStyle.EmptyClock to 5,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
val pagerState = rememberPagerState(
|
||||||
|
initialPage = styles.getOrDefault(selected, 0)
|
||||||
|
) {
|
||||||
|
styles.values.max() + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
LaunchedEffect(pagerState.currentPage) {
|
||||||
|
if (styles.getOrDefault(selected, 0) == pagerState.currentPage) {
|
||||||
|
return@LaunchedEffect
|
||||||
|
}
|
||||||
|
onSelect(styles.entries.first { it.value == pagerState.currentPage }.key)
|
||||||
|
}
|
||||||
|
|
||||||
|
val scope = rememberCoroutineScope()
|
||||||
|
|
||||||
|
Box {
|
||||||
|
androidx.compose.animation.AnimatedVisibility(
|
||||||
|
styles.entries.count { it.value == pagerState.currentPage } > 1,
|
||||||
|
modifier = Modifier
|
||||||
|
.align(Alignment.TopEnd)
|
||||||
|
.zIndex(1f),
|
||||||
|
enter = scaleIn(),
|
||||||
|
exit = scaleOut(),
|
||||||
|
) {
|
||||||
|
var showVariantDropdown by remember { mutableStateOf(false) }
|
||||||
|
IconButton(
|
||||||
|
onClick = { showVariantDropdown = true },
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(4.dp)
|
||||||
|
) {
|
||||||
|
Icon(Icons.Rounded.Style, null)
|
||||||
|
}
|
||||||
|
DropdownMenu(
|
||||||
|
expanded = showVariantDropdown,
|
||||||
|
onDismissRequest = { showVariantDropdown = false }) {
|
||||||
|
for (variant in styles.filter { it.value == pagerState.currentPage }) {
|
||||||
|
DropdownMenuItem(
|
||||||
|
onClick = {
|
||||||
|
onSelect(variant.key)
|
||||||
|
showVariantDropdown = false
|
||||||
|
},
|
||||||
|
text = {
|
||||||
|
Text(
|
||||||
|
text = getVariantName(context, variant.key),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CompositionLocalProvider(
|
||||||
|
LocalContentColor provides if (colors == ClockWidgetColors.Auto && LocalPreferDarkContentOverWallpaper.current || colors == ClockWidgetColors.Dark) {
|
||||||
|
Color(0, 0, 0, 180)
|
||||||
|
} else {
|
||||||
|
Color.White
|
||||||
|
},
|
||||||
|
) {
|
||||||
|
|
||||||
|
HorizontalPager(
|
||||||
|
modifier = Modifier.animateContentSize(),
|
||||||
|
state = pagerState,
|
||||||
|
verticalAlignment = Alignment.Top,
|
||||||
|
) { pageIndex ->
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(top = 24.dp, bottom = 8.dp),
|
||||||
|
contentAlignment = Alignment.TopCenter,
|
||||||
|
) {
|
||||||
|
val currentPageStyles = remember {
|
||||||
|
styles.filter { it.value == pageIndex }
|
||||||
|
}
|
||||||
|
if (currentPageStyles.containsKey(selected)) {
|
||||||
|
Clock(selected, layout)
|
||||||
|
} else {
|
||||||
|
Clock(currentPageStyles.keys.first(), layout)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Row(
|
||||||
|
verticalAlignment = Alignment.CenterVertically
|
||||||
|
) {
|
||||||
|
IconButton(
|
||||||
|
enabled = pagerState.currentPage > 0,
|
||||||
|
onClick = {
|
||||||
|
scope.launch {
|
||||||
|
pagerState.animateScrollToPage(
|
||||||
|
pagerState.currentPage - 1,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}) {
|
||||||
|
Icon(Icons.Rounded.ChevronLeft, null)
|
||||||
|
}
|
||||||
|
var showStyleDropdown by remember { mutableStateOf(false) }
|
||||||
|
TextButton(
|
||||||
|
modifier = Modifier
|
||||||
|
.weight(1f)
|
||||||
|
.padding(horizontal = 16.dp),
|
||||||
|
onClick = { showStyleDropdown = true },
|
||||||
|
contentPadding = PaddingValues(
|
||||||
|
top = 8.dp,
|
||||||
|
bottom = 8.dp,
|
||||||
|
start = 16.dp,
|
||||||
|
end = 12.dp,
|
||||||
|
),
|
||||||
|
colors = ButtonDefaults.textButtonColors(
|
||||||
|
contentColor = LocalContentColor.current,
|
||||||
|
),
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = getClockstyleName(
|
||||||
|
context,
|
||||||
|
styles.entries.first { (k, v) -> v == pagerState.currentPage }.key
|
||||||
|
),
|
||||||
|
textAlign = TextAlign.Center,
|
||||||
|
)
|
||||||
|
Icon(
|
||||||
|
Icons.Rounded.ArrowDropDown,
|
||||||
|
null,
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(ButtonDefaults.IconSpacing)
|
||||||
|
.size(ButtonDefaults.IconSize)
|
||||||
|
)
|
||||||
|
DropdownMenu(
|
||||||
|
expanded = showStyleDropdown,
|
||||||
|
onDismissRequest = { showStyleDropdown = false }
|
||||||
|
) {
|
||||||
|
for (style in styles.entries.distinctBy { it.value }) {
|
||||||
|
DropdownMenuItem(
|
||||||
|
onClick = {
|
||||||
|
scope.launch {
|
||||||
|
pagerState.animateScrollToPage(
|
||||||
|
style.value,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
showStyleDropdown = false
|
||||||
|
},
|
||||||
|
text = {
|
||||||
|
Text(
|
||||||
|
text = getClockstyleName(context, style.key),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
IconButton(
|
||||||
|
enabled = pagerState.currentPage < pagerState.pageCount - 1,
|
||||||
|
onClick = {
|
||||||
|
scope.launch {
|
||||||
|
pagerState.animateScrollToPage(
|
||||||
|
pagerState.currentPage + 1,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}) {
|
||||||
|
Icon(Icons.Rounded.ChevronRight, null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getClockstyleName(context: Context, style: ClockStyle): String {
|
||||||
|
return when (style) {
|
||||||
|
ClockStyle.DigitalClock1 -> "Bold"
|
||||||
|
ClockStyle.DigitalClock2 -> "Simple"
|
||||||
|
ClockStyle.OrbitClock -> "Orbit"
|
||||||
|
ClockStyle.BinaryClock -> "Binary"
|
||||||
|
ClockStyle.AnalogClock -> "Hands"
|
||||||
|
ClockStyle.EmptyClock -> "Empty"
|
||||||
|
ClockStyle.UNRECOGNIZED -> ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getVariantName(context: Context, style: ClockStyle): String {
|
||||||
|
return when (style) {
|
||||||
|
ClockStyle.DigitalClock1,
|
||||||
|
ClockStyle.DigitalClock2,
|
||||||
|
ClockStyle.OrbitClock,
|
||||||
|
ClockStyle.BinaryClock,
|
||||||
|
ClockStyle.AnalogClock,
|
||||||
|
ClockStyle.EmptyClock -> "Standard"
|
||||||
|
|
||||||
|
ClockStyle.UNRECOGNIZED -> ""
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user