Add clock widget customizations
This commit is contained in:
parent
eebffa333e
commit
ed11330674
@ -404,7 +404,6 @@
|
|||||||
<string name="preference_about_license_summary">Lizenziert unter der GNU General Public License 3.0</string>
|
<string name="preference_about_license_summary">Lizenziert unter der GNU General Public License 3.0</string>
|
||||||
<string name="feature_not_available">Diese Funktion ist in dieser Version von %1$s nicht verfügbar.</string>
|
<string name="feature_not_available">Diese Funktion ist in dieser Version von %1$s nicht verfügbar.</string>
|
||||||
<string name="preference_category_clock_widget">Uhr</string>
|
<string name="preference_category_clock_widget">Uhr</string>
|
||||||
<string name="preference_clock_widget_style">Uhrenstil</string>
|
|
||||||
<plurals name="calendar_widget_running_events">
|
<plurals name="calendar_widget_running_events">
|
||||||
<item quantity="one">+%1$d laufender Termin aus vergangenen Tagen</item>
|
<item quantity="one">+%1$d laufender Termin aus vergangenen Tagen</item>
|
||||||
<item quantity="other">+%1$d laufende Termine aus vergangenen Tagen</item>
|
<item quantity="other">+%1$d laufende Termine aus vergangenen Tagen</item>
|
||||||
@ -432,6 +431,13 @@
|
|||||||
<string name="preference_screen_weatherwidget">Wetter</string>
|
<string name="preference_screen_weatherwidget">Wetter</string>
|
||||||
<string name="preference_screen_musicwidget">Musik</string>
|
<string name="preference_screen_musicwidget">Musik</string>
|
||||||
|
|
||||||
|
<string name="preference_screen_clockwidget">Uhr</string>
|
||||||
|
<string name="preference_clockwidget_layout">Layout</string>
|
||||||
|
<string name="preference_clockwidget_layout_vertical">Vertikal</string>
|
||||||
|
<string name="preference_clockwidget_layout_horizontal">Horizontal</string>
|
||||||
|
<string name="preference_clock_widget_style">Stil</string>
|
||||||
|
<string name="preference_clock_widget_style_summary">Uhr auswählen</string>
|
||||||
|
|
||||||
<string name="preference_crash_reporter">Crash-Reporter</string>
|
<string name="preference_crash_reporter">Crash-Reporter</string>
|
||||||
<string name="preference_crash_reporter_summary">Fehler- und Absturzberichte</string>
|
<string name="preference_crash_reporter_summary">Fehler- und Absturzberichte</string>
|
||||||
|
|
||||||
|
|||||||
@ -451,7 +451,6 @@
|
|||||||
<string name="preference_about_license_summary">Licensed under the GNU General Public License 3.0</string>
|
<string name="preference_about_license_summary">Licensed under the GNU General Public License 3.0</string>
|
||||||
<string name="feature_not_available">This feature is not available in this version of %1$s</string>
|
<string name="feature_not_available">This feature is not available in this version of %1$s</string>
|
||||||
<string name="preference_category_clock_widget">Clock</string>
|
<string name="preference_category_clock_widget">Clock</string>
|
||||||
<string name="preference_clock_widget_style">Clock style</string>
|
|
||||||
|
|
||||||
<string name="preference_category_grid">Grid</string>
|
<string name="preference_category_grid">Grid</string>
|
||||||
<string name="preference_grid_column_count">Number of columns</string>
|
<string name="preference_grid_column_count">Number of columns</string>
|
||||||
@ -470,6 +469,13 @@
|
|||||||
<string name="preference_screen_weatherwidget">Weather</string>
|
<string name="preference_screen_weatherwidget">Weather</string>
|
||||||
<string name="preference_screen_musicwidget">Music</string>
|
<string name="preference_screen_musicwidget">Music</string>
|
||||||
|
|
||||||
|
<string name="preference_screen_clockwidget">Clock</string>
|
||||||
|
<string name="preference_clockwidget_layout">Layout</string>
|
||||||
|
<string name="preference_clockwidget_layout_vertical">Vertical</string>
|
||||||
|
<string name="preference_clockwidget_layout_horizontal">Horizontal</string>
|
||||||
|
<string name="preference_clock_widget_style">Style</string>
|
||||||
|
<string name="preference_clock_widget_style_summary">Select a clock</string>
|
||||||
|
|
||||||
<string name="preference_crash_reporter">Crash reporter</string>
|
<string name="preference_crash_reporter">Crash reporter</string>
|
||||||
<string name="preference_crash_reporter_summary">Error and crash reports</string>
|
<string name="preference_crash_reporter_summary">Error and crash reports</string>
|
||||||
|
|
||||||
|
|||||||
@ -21,5 +21,11 @@ fun createFactorySettings(context: Context): Settings {
|
|||||||
.setFilterSources(true)
|
.setFilterSources(true)
|
||||||
.build()
|
.build()
|
||||||
)
|
)
|
||||||
|
.setClockWidget(Settings.ClockWidgetSettings
|
||||||
|
.newBuilder()
|
||||||
|
.setLayout(Settings.ClockWidgetSettings.ClockWidgetLayout.Vertical)
|
||||||
|
.setClockStyle(Settings.ClockWidgetSettings.ClockStyle.DigitalClock1)
|
||||||
|
.build()
|
||||||
|
)
|
||||||
.build()
|
.build()
|
||||||
}
|
}
|
||||||
@ -37,4 +37,19 @@ message Settings {
|
|||||||
}
|
}
|
||||||
MusicWidgetSettings music_widget = 6;
|
MusicWidgetSettings music_widget = 6;
|
||||||
|
|
||||||
|
message ClockWidgetSettings {
|
||||||
|
enum ClockWidgetLayout {
|
||||||
|
Vertical = 0;
|
||||||
|
Horizontal = 1;
|
||||||
|
}
|
||||||
|
ClockWidgetLayout layout = 1;
|
||||||
|
enum ClockStyle {
|
||||||
|
DigitalClock1 = 0;
|
||||||
|
DigitalClock2 = 1;
|
||||||
|
BinaryClock = 2;
|
||||||
|
}
|
||||||
|
ClockStyle clock_style = 2;
|
||||||
|
}
|
||||||
|
ClockWidgetSettings clock_widget = 7;
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -137,6 +137,9 @@ dependencyResolutionManagement {
|
|||||||
alias("accompanist.pager")
|
alias("accompanist.pager")
|
||||||
.to("com.google.accompanist", "accompanist-pager")
|
.to("com.google.accompanist", "accompanist-pager")
|
||||||
.versionRef("accompanist")
|
.versionRef("accompanist")
|
||||||
|
alias("accompanist.pagerindicators")
|
||||||
|
.to("com.google.accompanist", "accompanist-pager-indicators")
|
||||||
|
.versionRef("accompanist")
|
||||||
alias("accompanist.flowlayout")
|
alias("accompanist.flowlayout")
|
||||||
.to("com.google.accompanist", "accompanist-flowlayout")
|
.to("com.google.accompanist", "accompanist-flowlayout")
|
||||||
.versionRef("accompanist")
|
.versionRef("accompanist")
|
||||||
|
|||||||
@ -84,6 +84,7 @@ dependencies {
|
|||||||
implementation(libs.accompanist.insets)
|
implementation(libs.accompanist.insets)
|
||||||
implementation(libs.accompanist.systemuicontroller)
|
implementation(libs.accompanist.systemuicontroller)
|
||||||
implementation(libs.accompanist.pager)
|
implementation(libs.accompanist.pager)
|
||||||
|
implementation(libs.accompanist.pagerindicators)
|
||||||
implementation(libs.accompanist.flowlayout)
|
implementation(libs.accompanist.flowlayout)
|
||||||
implementation(libs.accompanist.navigationanimation)
|
implementation(libs.accompanist.navigationanimation)
|
||||||
|
|
||||||
|
|||||||
@ -1,187 +0,0 @@
|
|||||||
package de.mm20.launcher2.ui.component
|
|
||||||
|
|
||||||
import android.text.format.DateFormat
|
|
||||||
import androidx.compose.foundation.Canvas
|
|
||||||
import androidx.compose.foundation.background
|
|
||||||
import androidx.compose.foundation.border
|
|
||||||
import androidx.compose.foundation.layout.*
|
|
||||||
import androidx.compose.foundation.shape.CircleShape
|
|
||||||
import androidx.compose.material3.LocalContentColor
|
|
||||||
import androidx.compose.material3.MaterialTheme
|
|
||||||
import androidx.compose.material3.Surface
|
|
||||||
import androidx.compose.material3.Text
|
|
||||||
import androidx.compose.runtime.Composable
|
|
||||||
import androidx.compose.ui.Alignment
|
|
||||||
import androidx.compose.ui.Modifier
|
|
||||||
import androidx.compose.ui.geometry.Offset
|
|
||||||
import androidx.compose.ui.geometry.center
|
|
||||||
import androidx.compose.ui.graphics.Color
|
|
||||||
import androidx.compose.ui.graphics.PointMode
|
|
||||||
import androidx.compose.ui.graphics.StrokeCap
|
|
||||||
import androidx.compose.ui.graphics.drawscope.rotate
|
|
||||||
import androidx.compose.ui.platform.LocalContext
|
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
|
||||||
import androidx.compose.ui.unit.dp
|
|
||||||
import androidx.compose.ui.unit.em
|
|
||||||
import androidx.compose.ui.unit.sp
|
|
||||||
import de.mm20.launcher2.ui.locals.LocalColorScheme
|
|
||||||
import java.text.SimpleDateFormat
|
|
||||||
import java.util.*
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
fun DigitalClock(time: Long) {
|
|
||||||
val format = SimpleDateFormat(
|
|
||||||
if (DateFormat.is24HourFormat(LocalContext.current))
|
|
||||||
"HH\nmm" else "hh\nmm",
|
|
||||||
Locale.getDefault()
|
|
||||||
)
|
|
||||||
Text(
|
|
||||||
modifier = Modifier.offset(y = 16.dp),
|
|
||||||
text = format.format(time),
|
|
||||||
style = MaterialTheme.typography.displayLarge.copy(
|
|
||||||
fontSize = 100.sp,
|
|
||||||
fontWeight = FontWeight.Black,
|
|
||||||
textAlign = TextAlign.Center,
|
|
||||||
lineHeight = 0.8.em,
|
|
||||||
letterSpacing = -0.1.em,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
fun BinaryClock(time: Long) {
|
|
||||||
val date = Calendar.getInstance()
|
|
||||||
date.timeInMillis = time
|
|
||||||
val minute = date[Calendar.MINUTE]
|
|
||||||
var hour = date[Calendar.HOUR]
|
|
||||||
if (hour == 0) hour = 12
|
|
||||||
Row(
|
|
||||||
modifier = Modifier.padding(vertical = 24.dp)
|
|
||||||
) {
|
|
||||||
for (i in 0 until 10) {
|
|
||||||
val active = if (i < 4) {
|
|
||||||
hour and (1 shl (3 - i)) != 0
|
|
||||||
} else {
|
|
||||||
minute and (1 shl (9 - i)) != 0
|
|
||||||
}
|
|
||||||
Box(
|
|
||||||
modifier = Modifier
|
|
||||||
.padding(4.dp)
|
|
||||||
.size(12.dp)
|
|
||||||
.background(
|
|
||||||
LocalContentColor.current.copy(
|
|
||||||
if (active) 1f else 0.45f
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
if (i == 3) {
|
|
||||||
Box(Modifier.size(8.dp))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
fun AnalogClock(time: Long) {
|
|
||||||
val date = Calendar.getInstance()
|
|
||||||
date.timeInMillis = time
|
|
||||||
val minute = date[Calendar.MINUTE]
|
|
||||||
val hour = date[Calendar.HOUR]
|
|
||||||
val dark = true//!MaterialTheme.colors.isLight
|
|
||||||
val cs = LocalColorScheme.current
|
|
||||||
val bgColor = if (dark) cs.primary.shade20 else cs.primary.shade80
|
|
||||||
val hourColor = if (dark) cs.primary.shade70 else cs.primary.shade40
|
|
||||||
val minuteColor = if (dark) cs.primary.shade80 else cs.primary.shade30
|
|
||||||
val textColor = if (dark) cs.primary.shade50 else cs.primary.shade60
|
|
||||||
|
|
||||||
val hourAngle = 30f * hour + 0.5f * minute
|
|
||||||
val minuteAngle = 6f * minute
|
|
||||||
|
|
||||||
Surface(
|
|
||||||
modifier = Modifier
|
|
||||||
.padding(bottom = 24.dp)
|
|
||||||
.size(156.dp),
|
|
||||||
shape = CircleShape,
|
|
||||||
color = bgColor,
|
|
||||||
shadowElevation = 8.dp
|
|
||||||
) {
|
|
||||||
Box(
|
|
||||||
modifier = Modifier.fillMaxSize()
|
|
||||||
) {
|
|
||||||
|
|
||||||
Text(
|
|
||||||
text = "12",
|
|
||||||
style = MaterialTheme.typography.headlineMedium.copy(
|
|
||||||
fontSize = 32.sp,
|
|
||||||
lineHeight = 32.sp,
|
|
||||||
),
|
|
||||||
color = textColor,
|
|
||||||
modifier = Modifier
|
|
||||||
.padding(10.dp, 2.dp)
|
|
||||||
.align(Alignment.TopCenter),
|
|
||||||
)
|
|
||||||
Text(
|
|
||||||
text = "3",
|
|
||||||
style = MaterialTheme.typography.headlineMedium.copy(
|
|
||||||
fontSize = 32.sp
|
|
||||||
),
|
|
||||||
color = textColor,
|
|
||||||
modifier = Modifier
|
|
||||||
.padding(10.dp, 2.dp)
|
|
||||||
.align(Alignment.CenterEnd),
|
|
||||||
)
|
|
||||||
Text(
|
|
||||||
text = "6",
|
|
||||||
style = MaterialTheme.typography.headlineMedium.copy(
|
|
||||||
fontSize = 32.sp
|
|
||||||
),
|
|
||||||
color = textColor,
|
|
||||||
modifier = Modifier
|
|
||||||
.padding(10.dp, 2.dp)
|
|
||||||
.align(Alignment.BottomCenter),
|
|
||||||
)
|
|
||||||
Text(
|
|
||||||
text = "9",
|
|
||||||
style = MaterialTheme.typography.headlineMedium.copy(
|
|
||||||
fontSize = 32.sp
|
|
||||||
),
|
|
||||||
color = textColor,
|
|
||||||
modifier = Modifier
|
|
||||||
.padding(10.dp, 2.dp)
|
|
||||||
.align(Alignment.CenterStart),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
Canvas(
|
|
||||||
modifier = Modifier
|
|
||||||
.fillMaxSize()
|
|
||||||
) {
|
|
||||||
rotate(
|
|
||||||
degrees = hourAngle - 180f
|
|
||||||
) {
|
|
||||||
|
|
||||||
drawLine(
|
|
||||||
strokeWidth = 12.dp.toPx(),
|
|
||||||
start = size.center.plus(Offset(0f, size.width / 5f)),
|
|
||||||
end = size.center,
|
|
||||||
color = hourColor,
|
|
||||||
cap = StrokeCap.Round
|
|
||||||
)
|
|
||||||
}
|
|
||||||
rotate(
|
|
||||||
degrees = minuteAngle - 180f
|
|
||||||
) {
|
|
||||||
drawLine(
|
|
||||||
strokeWidth = 12.dp.toPx(),
|
|
||||||
start = size.center.plus(Offset(0f, size.width / 3f)),
|
|
||||||
end = size.center,
|
|
||||||
color = minuteColor,
|
|
||||||
cap = StrokeCap.Round
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
PointMode.Polygon
|
|
||||||
}
|
|
||||||
@ -1,87 +1,138 @@
|
|||||||
package de.mm20.launcher2.ui
|
package de.mm20.launcher2.ui
|
||||||
|
|
||||||
import android.content.BroadcastReceiver
|
import androidx.compose.foundation.background
|
||||||
import android.content.Context
|
|
||||||
import android.content.Intent
|
|
||||||
import android.content.IntentFilter
|
|
||||||
import android.provider.AlarmClock
|
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||||
import androidx.compose.foundation.layout.*
|
import androidx.compose.foundation.layout.*
|
||||||
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.rounded.ExpandLess
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.LocalContentColor
|
import androidx.compose.material3.LocalContentColor
|
||||||
import androidx.compose.runtime.*
|
import androidx.compose.runtime.*
|
||||||
|
import androidx.compose.runtime.livedata.observeAsState
|
||||||
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.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import de.mm20.launcher2.ui.component.DigitalClock
|
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||||
import de.mm20.launcher2.ui.widget.parts.DatePart
|
import de.mm20.launcher2.preferences.Settings.ClockWidgetSettings.ClockStyle
|
||||||
|
import de.mm20.launcher2.preferences.Settings.ClockWidgetSettings.ClockWidgetLayout
|
||||||
|
import de.mm20.launcher2.ui.launcher.widgets.clock.ClockWidgetVM
|
||||||
|
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.DigitalClock2
|
||||||
|
import de.mm20.launcher2.ui.settings.clockwidget.parts.DatePart
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun ClockWidget(
|
fun ClockWidget(
|
||||||
modifier: Modifier = Modifier
|
modifier: Modifier = Modifier
|
||||||
) {
|
) {
|
||||||
|
val viewModel: ClockWidgetVM = viewModel()
|
||||||
|
val context = LocalContext.current
|
||||||
|
val time by viewModel.getTime(context).collectAsState(System.currentTimeMillis())
|
||||||
|
val layout by viewModel.layout.observeAsState()
|
||||||
|
val clockStyle by viewModel.clockStyle.observeAsState()
|
||||||
Box(
|
Box(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth(),
|
.fillMaxWidth(),
|
||||||
contentAlignment = Alignment.BottomCenter
|
contentAlignment = Alignment.BottomCenter
|
||||||
) {
|
) {
|
||||||
|
|
||||||
CompositionLocalProvider(LocalContentColor provides Color.White) {
|
CompositionLocalProvider(
|
||||||
Column(
|
LocalContentColor provides Color.White
|
||||||
horizontalAlignment = Alignment.CenterHorizontally,
|
|
||||||
modifier = Modifier.height(IntrinsicSize.Min)
|
|
||||||
) {
|
|
||||||
Clock()
|
|
||||||
|
|
||||||
DynamicZone()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
fun Clock() {
|
|
||||||
var time by remember { mutableStateOf(System.currentTimeMillis()) }
|
|
||||||
val context = LocalContext.current
|
|
||||||
|
|
||||||
DisposableEffect(null) {
|
|
||||||
val receiver = object : BroadcastReceiver() {
|
|
||||||
override fun onReceive(context: Context?, intent: Intent?) {
|
|
||||||
time = System.currentTimeMillis()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
val filter = IntentFilter(Intent.ACTION_TIME_TICK).also {
|
|
||||||
it.addAction(Intent.ACTION_TIME_CHANGED)
|
|
||||||
it.addAction(Intent.ACTION_TIMEZONE_CHANGED)
|
|
||||||
}
|
|
||||||
context.registerReceiver(receiver, filter)
|
|
||||||
onDispose {
|
|
||||||
context.unregisterReceiver(receiver)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Box(
|
|
||||||
modifier = Modifier.clickable(
|
|
||||||
indication = null,
|
|
||||||
interactionSource = remember { MutableInteractionSource() }
|
|
||||||
) {
|
) {
|
||||||
context.startActivity(Intent(AlarmClock.ACTION_SHOW_ALARMS).apply {
|
if (layout == ClockWidgetLayout.Vertical) {
|
||||||
flags = Intent.FLAG_ACTIVITY_NEW_TASK
|
Column(
|
||||||
})
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
|
modifier = Modifier.height(IntrinsicSize.Min),
|
||||||
|
) {
|
||||||
|
Box(
|
||||||
|
modifier = Modifier.clickable(
|
||||||
|
indication = null,
|
||||||
|
interactionSource = remember { MutableInteractionSource() }
|
||||||
|
) {
|
||||||
|
viewModel.launchClockApp(context)
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
Clock(clockStyle, ClockWidgetLayout.Vertical, time)
|
||||||
|
}
|
||||||
|
|
||||||
|
DynamicZone(
|
||||||
|
modifier = Modifier.padding(bottom = 16.dp),
|
||||||
|
ClockWidgetLayout.Vertical,
|
||||||
|
time
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (layout == ClockWidgetLayout.Horizontal) {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally
|
||||||
|
) {
|
||||||
|
Icon(imageVector = Icons.Rounded.ExpandLess, contentDescription = "")
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(end = 16.dp),
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
horizontalArrangement = Arrangement.SpaceBetween
|
||||||
|
) {
|
||||||
|
DynamicZone(
|
||||||
|
modifier = Modifier.weight(1f),
|
||||||
|
ClockWidgetLayout.Horizontal,
|
||||||
|
time
|
||||||
|
)
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(horizontal = 16.dp)
|
||||||
|
.height(56.dp)
|
||||||
|
.width(2.dp)
|
||||||
|
.background(
|
||||||
|
LocalContentColor.current
|
||||||
|
),
|
||||||
|
)
|
||||||
|
Box(
|
||||||
|
modifier = Modifier.clickable(
|
||||||
|
indication = null,
|
||||||
|
interactionSource = remember { MutableInteractionSource() }
|
||||||
|
) {
|
||||||
|
viewModel.launchClockApp(context)
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
Clock(clockStyle, ClockWidgetLayout.Horizontal, time)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
) {
|
|
||||||
DigitalClock(time = time)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun DynamicZone() {
|
fun Clock(
|
||||||
|
style: ClockStyle?,
|
||||||
|
layout: ClockWidgetLayout,
|
||||||
|
time: Long
|
||||||
|
) {
|
||||||
|
when (style) {
|
||||||
|
ClockStyle.DigitalClock1 -> DigitalClock1(time, layout)
|
||||||
|
ClockStyle.DigitalClock2 -> DigitalClock2(time, layout)
|
||||||
|
ClockStyle.BinaryClock -> BinaryClock(time, layout)
|
||||||
|
else -> {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun DynamicZone(
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
layout: ClockWidgetLayout,
|
||||||
|
time: Long,
|
||||||
|
) {
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier.padding(bottom = 16.dp)
|
modifier = modifier
|
||||||
) {
|
) {
|
||||||
DatePart()
|
DatePart(time, layout)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,6 +1,49 @@
|
|||||||
package de.mm20.launcher2.ui.launcher.widgets.clock
|
package de.mm20.launcher2.ui.launcher.widgets.clock
|
||||||
|
|
||||||
|
import android.content.BroadcastReceiver
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.content.IntentFilter
|
||||||
|
import android.provider.AlarmClock
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
|
import androidx.lifecycle.asLiveData
|
||||||
|
import de.mm20.launcher2.ktx.tryStartActivity
|
||||||
|
import de.mm20.launcher2.lifecycle.BroadcastReceiverLiveData
|
||||||
|
import de.mm20.launcher2.preferences.LauncherDataStore
|
||||||
|
import kotlinx.coroutines.channels.awaitClose
|
||||||
|
import kotlinx.coroutines.channels.trySendBlocking
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kotlinx.coroutines.flow.callbackFlow
|
||||||
|
import kotlinx.coroutines.flow.flow
|
||||||
|
import kotlinx.coroutines.flow.map
|
||||||
|
import org.koin.core.component.KoinComponent
|
||||||
|
import org.koin.core.component.inject
|
||||||
|
|
||||||
class ClockWidgetVM: ViewModel() {
|
class ClockWidgetVM: ViewModel(), KoinComponent {
|
||||||
|
private val dataStore: LauncherDataStore by inject()
|
||||||
|
|
||||||
|
val layout = dataStore.data.map { it.clockWidget.layout }.asLiveData()
|
||||||
|
val clockStyle = dataStore.data.map { it.clockWidget.clockStyle }.asLiveData()
|
||||||
|
|
||||||
|
fun getTime(context: Context): Flow<Long> = callbackFlow {
|
||||||
|
trySendBlocking(System.currentTimeMillis())
|
||||||
|
val receiver = object : BroadcastReceiver() {
|
||||||
|
override fun onReceive(context: Context?, intent: Intent?) {
|
||||||
|
trySendBlocking(System.currentTimeMillis())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
context.registerReceiver(receiver, IntentFilter().apply {
|
||||||
|
addAction(Intent.ACTION_TIME_TICK)
|
||||||
|
addAction(Intent.ACTION_TIME_CHANGED)
|
||||||
|
})
|
||||||
|
awaitClose {
|
||||||
|
context.unregisterReceiver(receiver)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun launchClockApp(context: Context) {
|
||||||
|
context.tryStartActivity(Intent(AlarmClock.ACTION_SHOW_ALARMS).apply {
|
||||||
|
flags = Intent.FLAG_ACTIVITY_NEW_TASK
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@ -0,0 +1,85 @@
|
|||||||
|
package de.mm20.launcher2.ui.launcher.widgets.clock.clocks
|
||||||
|
|
||||||
|
import androidx.compose.foundation.background
|
||||||
|
import androidx.compose.foundation.layout.*
|
||||||
|
import androidx.compose.material3.LocalContentColor
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import de.mm20.launcher2.preferences.Settings
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun BinaryClock(
|
||||||
|
time: Long,
|
||||||
|
layout: Settings.ClockWidgetSettings.ClockWidgetLayout
|
||||||
|
) {
|
||||||
|
val verticalLayout = layout == Settings.ClockWidgetSettings.ClockWidgetLayout.Vertical
|
||||||
|
val date = Calendar.getInstance()
|
||||||
|
date.timeInMillis = time
|
||||||
|
val minute = date[Calendar.MINUTE]
|
||||||
|
var hour = date[Calendar.HOUR]
|
||||||
|
if (hour == 0) hour = 12
|
||||||
|
if (verticalLayout) {
|
||||||
|
Row(
|
||||||
|
modifier = Modifier.padding(vertical = 24.dp)
|
||||||
|
) {
|
||||||
|
for (i in 0 until 10) {
|
||||||
|
val active = if (i < 4) {
|
||||||
|
hour and (1 shl (3 - i)) != 0
|
||||||
|
} else {
|
||||||
|
minute and (1 shl (9 - i)) != 0
|
||||||
|
}
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(4.dp)
|
||||||
|
.size(12.dp)
|
||||||
|
.background(
|
||||||
|
LocalContentColor.current.copy(
|
||||||
|
if (active) 1f else 0.45f
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
if (i == 3) {
|
||||||
|
Box(Modifier.size(8.dp))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Column(
|
||||||
|
horizontalAlignment = Alignment.End
|
||||||
|
) {
|
||||||
|
Row {
|
||||||
|
for (i in 0 until 4) {
|
||||||
|
val active = hour and (1 shl (3 - i)) != 0
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.padding( 4.dp)
|
||||||
|
.size(12.dp)
|
||||||
|
.background(
|
||||||
|
LocalContentColor.current.copy(
|
||||||
|
if (active) 1f else 0.45f
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Row {
|
||||||
|
for (i in 4 until 10) {
|
||||||
|
val active = minute and (1 shl (9 - i)) != 0
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(4.dp)
|
||||||
|
.size(12.dp)
|
||||||
|
.background(
|
||||||
|
LocalContentColor.current.copy(
|
||||||
|
if (active) 1f else 0.45f
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,45 @@
|
|||||||
|
package de.mm20.launcher2.ui.launcher.widgets.clock.clocks
|
||||||
|
|
||||||
|
import android.text.format.DateFormat
|
||||||
|
import androidx.compose.foundation.layout.offset
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.compose.ui.unit.em
|
||||||
|
import androidx.compose.ui.unit.sp
|
||||||
|
import de.mm20.launcher2.preferences.Settings
|
||||||
|
import java.text.SimpleDateFormat
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun DigitalClock1(
|
||||||
|
time: Long,
|
||||||
|
layout: Settings.ClockWidgetSettings.ClockWidgetLayout
|
||||||
|
) {
|
||||||
|
val verticalLayout = layout == Settings.ClockWidgetSettings.ClockWidgetLayout.Vertical
|
||||||
|
val format = SimpleDateFormat(
|
||||||
|
if (verticalLayout) {
|
||||||
|
if (DateFormat.is24HourFormat(LocalContext.current)) "HH\nmm" else "hh\nmm"
|
||||||
|
} else {
|
||||||
|
if (DateFormat.is24HourFormat(LocalContext.current)) "HH mm" else "hh mm"
|
||||||
|
},
|
||||||
|
Locale.getDefault()
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
modifier = Modifier
|
||||||
|
.offset(y = if (verticalLayout) 16.dp else 0.dp),
|
||||||
|
text = format.format(time),
|
||||||
|
style = MaterialTheme.typography.displayLarge.copy(
|
||||||
|
fontSize = if (verticalLayout) 100.sp else 48.sp,
|
||||||
|
fontWeight = FontWeight.Black,
|
||||||
|
textAlign = TextAlign.Center,
|
||||||
|
lineHeight = 0.8.em,
|
||||||
|
letterSpacing = -0.1.em,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
@ -0,0 +1,20 @@
|
|||||||
|
package de.mm20.launcher2.ui.launcher.widgets.clock.clocks
|
||||||
|
|
||||||
|
import android.text.format.DateUtils
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import de.mm20.launcher2.preferences.Settings
|
||||||
|
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun DigitalClock2(
|
||||||
|
time: Long,
|
||||||
|
layout: Settings.ClockWidgetSettings.ClockWidgetLayout
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = DateUtils.formatDateTime(LocalContext.current, time, DateUtils.FORMAT_SHOW_TIME),
|
||||||
|
style = MaterialTheme.typography.displayLarge
|
||||||
|
)
|
||||||
|
}
|
||||||
@ -108,7 +108,6 @@ class LauncherActivity : BaseActivity() {
|
|||||||
set(value) {
|
set(value) {
|
||||||
field = value
|
field = value
|
||||||
if (value) {
|
if (value) {
|
||||||
binding.widgetSpacer.visibility = View.GONE
|
|
||||||
binding.clockWidget.visibility = View.GONE
|
binding.clockWidget.visibility = View.GONE
|
||||||
binding.searchBar.setRightIcon(R.drawable.ic_done)
|
binding.searchBar.setRightIcon(R.drawable.ic_done)
|
||||||
binding.scrollView.setOnTouchListener(null)
|
binding.scrollView.setOnTouchListener(null)
|
||||||
@ -154,7 +153,6 @@ class LauncherActivity : BaseActivity() {
|
|||||||
.start()
|
.start()
|
||||||
} else {
|
} else {
|
||||||
widgetViewModel.saveWidgets(widgets)
|
widgetViewModel.saveWidgets(widgets)
|
||||||
binding.widgetSpacer.visibility = View.VISIBLE
|
|
||||||
binding.widgetList.layoutTransition = ChangingLayoutTransition()
|
binding.widgetList.layoutTransition = ChangingLayoutTransition()
|
||||||
binding.widgetContainer.layoutTransition = ChangingLayoutTransition()
|
binding.widgetContainer.layoutTransition = ChangingLayoutTransition()
|
||||||
binding.scrollContainer.layoutTransition = ChangingLayoutTransition()
|
binding.scrollContainer.layoutTransition = ChangingLayoutTransition()
|
||||||
@ -226,9 +224,9 @@ class LauncherActivity : BaseActivity() {
|
|||||||
binding.searchContainer.layoutTransition.enableTransitionType(LayoutTransition.CHANGING)
|
binding.searchContainer.layoutTransition.enableTransitionType(LayoutTransition.CHANGING)
|
||||||
binding.widgetContainer.layoutTransition.enableTransitionType(LayoutTransition.CHANGING)
|
binding.widgetContainer.layoutTransition.enableTransitionType(LayoutTransition.CHANGING)
|
||||||
|
|
||||||
val params = binding.widgetSpacer.layoutParams as LinearLayout.LayoutParams
|
val params = binding.clockWidget.layoutParams
|
||||||
params.topMargin = Point().also { windowManager.defaultDisplay.getSize(it) }.y
|
params.height = Point().also { windowManager.defaultDisplay.getSize(it) }.y
|
||||||
binding.widgetSpacer.layoutParams = params
|
binding.clockWidget.layoutParams = params
|
||||||
binding.container.doOnLayout {
|
binding.container.doOnLayout {
|
||||||
adjustWidgetSpace()
|
adjustWidgetSpace()
|
||||||
}
|
}
|
||||||
@ -237,9 +235,7 @@ class LauncherActivity : BaseActivity() {
|
|||||||
binding.scrollView.setOnScrollChangeListener { _: NestedScrollView?, _: Int, scrollY: Int, _: Int, oldScrollY: Int ->
|
binding.scrollView.setOnScrollChangeListener { _: NestedScrollView?, _: Int, scrollY: Int, _: Int, oldScrollY: Int ->
|
||||||
when {
|
when {
|
||||||
/* Hide searchbar*/
|
/* Hide searchbar*/
|
||||||
scrollY > oldScrollY && ((searchVisibility && scrollY > binding.searchBar.height) || widgetEditMode ||
|
scrollY > oldScrollY && ((scrollY > binding.searchBar.height) || widgetEditMode) -> {
|
||||||
scrollY > binding.widgetSpacer.height + binding.searchBar.height
|
|
||||||
+ (binding.widgetSpacer.layoutParams as LinearLayout.LayoutParams).topMargin) -> {
|
|
||||||
var newTransY = binding.searchBar.translationY - scrollY + oldScrollY
|
var newTransY = binding.searchBar.translationY - scrollY + oldScrollY
|
||||||
if (newTransY < -binding.searchBar.height.toFloat() * 1.5f) {
|
if (newTransY < -binding.searchBar.height.toFloat() * 1.5f) {
|
||||||
newTransY = -binding.searchBar.height.toFloat() * 1.5f
|
newTransY = -binding.searchBar.height.toFloat() * 1.5f
|
||||||
@ -255,9 +251,7 @@ class LauncherActivity : BaseActivity() {
|
|||||||
binding.searchBar.translationY = newTransY
|
binding.searchBar.translationY = newTransY
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (scrollY > 0 && (searchVisibility || widgetEditMode ||
|
if (scrollY > 0 && (searchVisibility || widgetEditMode)
|
||||||
scrollY > binding.widgetSpacer.height
|
|
||||||
+ (binding.widgetSpacer.layoutParams as LinearLayout.LayoutParams).topMargin)
|
|
||||||
) {
|
) {
|
||||||
binding.searchBar.raise()
|
binding.searchBar.raise()
|
||||||
} else binding.searchBar.drop()
|
} else binding.searchBar.drop()
|
||||||
@ -455,13 +449,10 @@ class LauncherActivity : BaseActivity() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun adjustWidgetSpace() {
|
private fun adjustWidgetSpace() {
|
||||||
val firstWidget = binding.clockWidget
|
val height = binding.scrollView.height - binding.searchBar.height - 8 * dp
|
||||||
val m = binding.scrollContainer.paddingTop +
|
binding.clockWidget.layoutParams = binding.clockWidget.layoutParams.also {
|
||||||
(firstWidget.layoutParams as LinearLayout.LayoutParams).run { topMargin + bottomMargin }
|
it.height = height.toInt()
|
||||||
val params = binding.widgetSpacer.layoutParams as LinearLayout.LayoutParams
|
}
|
||||||
params.topMargin =
|
|
||||||
binding.scrollView.height - firstWidget.measuredHeight - m - binding.widgetContainer.paddingTop - binding.widgetSpacer.height
|
|
||||||
binding.widgetSpacer.layoutParams = params
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -30,7 +30,7 @@ class ClockWidget : FrameLayout {
|
|||||||
addView(composeView)
|
addView(composeView)
|
||||||
|
|
||||||
composeView.layoutParams =
|
composeView.layoutParams =
|
||||||
LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT)
|
LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)
|
||||||
|
|
||||||
|
|
||||||
composeView.setContent {
|
composeView.setContent {
|
||||||
|
|||||||
@ -23,6 +23,7 @@ import de.mm20.launcher2.ui.base.BaseActivity
|
|||||||
import de.mm20.launcher2.ui.locals.LocalNavController
|
import de.mm20.launcher2.ui.locals.LocalNavController
|
||||||
import de.mm20.launcher2.ui.settings.about.AboutSettingsScreen
|
import de.mm20.launcher2.ui.settings.about.AboutSettingsScreen
|
||||||
import de.mm20.launcher2.ui.settings.appearance.AppearanceSettingsScreen
|
import de.mm20.launcher2.ui.settings.appearance.AppearanceSettingsScreen
|
||||||
|
import de.mm20.launcher2.ui.settings.clockwidget.ClockWidgetSettingsScreen
|
||||||
import de.mm20.launcher2.ui.settings.debug.DebugSettingsScreen
|
import de.mm20.launcher2.ui.settings.debug.DebugSettingsScreen
|
||||||
import de.mm20.launcher2.ui.settings.license.LicenseScreen
|
import de.mm20.launcher2.ui.settings.license.LicenseScreen
|
||||||
import de.mm20.launcher2.ui.settings.main.MainSettingsScreen
|
import de.mm20.launcher2.ui.settings.main.MainSettingsScreen
|
||||||
@ -93,6 +94,9 @@ class SettingsActivity : BaseActivity() {
|
|||||||
composable("settings/widgets/music") {
|
composable("settings/widgets/music") {
|
||||||
MusicWidgetSettingsScreen()
|
MusicWidgetSettingsScreen()
|
||||||
}
|
}
|
||||||
|
composable("settings/widgets/clock") {
|
||||||
|
ClockWidgetSettingsScreen()
|
||||||
|
}
|
||||||
composable("settings/about") {
|
composable("settings/about") {
|
||||||
AboutSettingsScreen()
|
AboutSettingsScreen()
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,121 @@
|
|||||||
|
package de.mm20.launcher2.ui.settings.clockwidget
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.height
|
||||||
|
import androidx.compose.material3.AlertDialog
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.material3.TextButton
|
||||||
|
import androidx.compose.runtime.*
|
||||||
|
import androidx.compose.runtime.livedata.observeAsState
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||||
|
import com.google.accompanist.pager.ExperimentalPagerApi
|
||||||
|
import com.google.accompanist.pager.HorizontalPager
|
||||||
|
import com.google.accompanist.pager.HorizontalPagerIndicator
|
||||||
|
import com.google.accompanist.pager.rememberPagerState
|
||||||
|
import de.mm20.launcher2.preferences.Settings.ClockWidgetSettings.ClockStyle
|
||||||
|
import de.mm20.launcher2.preferences.Settings.ClockWidgetSettings.ClockWidgetLayout
|
||||||
|
import de.mm20.launcher2.ui.Clock
|
||||||
|
import de.mm20.launcher2.ui.R
|
||||||
|
import de.mm20.launcher2.ui.component.preferences.ListPreference
|
||||||
|
import de.mm20.launcher2.ui.component.preferences.Preference
|
||||||
|
import de.mm20.launcher2.ui.component.preferences.PreferenceCategory
|
||||||
|
import de.mm20.launcher2.ui.component.preferences.PreferenceScreen
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun ClockWidgetSettingsScreen() {
|
||||||
|
val viewModel: ClockWidgetSettingsScreenVM = viewModel()
|
||||||
|
PreferenceScreen(
|
||||||
|
title = stringResource(R.string.preference_screen_clockwidget)
|
||||||
|
) {
|
||||||
|
item {
|
||||||
|
PreferenceCategory {
|
||||||
|
val layout by viewModel.layout.observeAsState()
|
||||||
|
ListPreference(
|
||||||
|
title = stringResource(R.string.preference_clockwidget_layout),
|
||||||
|
value = layout,
|
||||||
|
items = listOf(
|
||||||
|
stringResource(R.string.preference_clockwidget_layout_vertical) to ClockWidgetLayout.Vertical,
|
||||||
|
stringResource(R.string.preference_clockwidget_layout_horizontal) to ClockWidgetLayout.Horizontal
|
||||||
|
),
|
||||||
|
onValueChanged = {
|
||||||
|
if (it != null) viewModel.setLayout(it)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
val clockStyle by viewModel.clockStyle.observeAsState()
|
||||||
|
ClockStylePreference(
|
||||||
|
layout = layout ?: ClockWidgetLayout.Vertical,
|
||||||
|
value = clockStyle,
|
||||||
|
onValueChanged = {
|
||||||
|
viewModel.setClockStyle(it)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalPagerApi::class)
|
||||||
|
@Composable
|
||||||
|
fun ClockStylePreference(
|
||||||
|
layout: ClockWidgetLayout,
|
||||||
|
value: ClockStyle?,
|
||||||
|
onValueChanged: (ClockStyle) -> Unit
|
||||||
|
) {
|
||||||
|
var showDialog by remember { mutableStateOf(false) }
|
||||||
|
Preference(
|
||||||
|
title = stringResource(R.string.preference_clock_widget_style),
|
||||||
|
summary = stringResource(R.string.preference_clock_widget_style_summary),
|
||||||
|
onClick = {
|
||||||
|
showDialog = true
|
||||||
|
}
|
||||||
|
)
|
||||||
|
if (showDialog && value != null) {
|
||||||
|
val styles = remember {
|
||||||
|
ClockStyle.values().filter { it != ClockStyle.UNRECOGNIZED }
|
||||||
|
}
|
||||||
|
val pagerState = rememberPagerState(styles.indexOf(value))
|
||||||
|
|
||||||
|
AlertDialog(
|
||||||
|
onDismissRequest = { showDialog = false },
|
||||||
|
confirmButton = {
|
||||||
|
TextButton(onClick = {
|
||||||
|
showDialog = false
|
||||||
|
onValueChanged(styles[pagerState.currentPage])
|
||||||
|
}) {
|
||||||
|
Text(
|
||||||
|
text = stringResource(android.R.string.ok),
|
||||||
|
style = MaterialTheme.typography.labelLarge
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
dismissButton = {
|
||||||
|
TextButton(onClick = { showDialog = false }) {
|
||||||
|
Text(
|
||||||
|
text = stringResource(android.R.string.cancel),
|
||||||
|
style = MaterialTheme.typography.labelLarge
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
text = {
|
||||||
|
Column(
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally
|
||||||
|
) {
|
||||||
|
HorizontalPager(
|
||||||
|
count = styles.size,
|
||||||
|
state = pagerState,
|
||||||
|
modifier = Modifier.height(300.dp)
|
||||||
|
) {
|
||||||
|
Clock(style = styles[it], layout = layout, time = System.currentTimeMillis())
|
||||||
|
}
|
||||||
|
HorizontalPagerIndicator(pagerState = pagerState)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,40 @@
|
|||||||
|
package de.mm20.launcher2.ui.settings.clockwidget
|
||||||
|
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
|
import androidx.lifecycle.asLiveData
|
||||||
|
import androidx.lifecycle.viewModelScope
|
||||||
|
import de.mm20.launcher2.preferences.LauncherDataStore
|
||||||
|
import de.mm20.launcher2.preferences.Settings.ClockWidgetSettings
|
||||||
|
import kotlinx.coroutines.flow.map
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import org.koin.core.component.KoinComponent
|
||||||
|
import org.koin.core.component.inject
|
||||||
|
|
||||||
|
class ClockWidgetSettingsScreenVM : ViewModel(), KoinComponent {
|
||||||
|
private val dataStore: LauncherDataStore by inject()
|
||||||
|
val layout = dataStore.data.map { it.clockWidget.layout }.asLiveData()
|
||||||
|
fun setLayout(layout: ClockWidgetSettings.ClockWidgetLayout) {
|
||||||
|
viewModelScope.launch {
|
||||||
|
dataStore.updateData {
|
||||||
|
it.toBuilder()
|
||||||
|
.setClockWidget(
|
||||||
|
it.clockWidget.toBuilder()
|
||||||
|
.setLayout(layout)
|
||||||
|
).build()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val clockStyle = dataStore.data.map { it.clockWidget.clockStyle }.asLiveData()
|
||||||
|
fun setClockStyle(clockStyle: ClockWidgetSettings.ClockStyle) {
|
||||||
|
viewModelScope.launch {
|
||||||
|
dataStore.updateData {
|
||||||
|
it.toBuilder()
|
||||||
|
.setClockWidget(
|
||||||
|
it.clockWidget.toBuilder()
|
||||||
|
.setClockStyle(clockStyle)
|
||||||
|
).build()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,62 @@
|
|||||||
|
package de.mm20.launcher2.ui.settings.clockwidget.parts
|
||||||
|
|
||||||
|
import android.content.ContentUris
|
||||||
|
import android.content.Intent
|
||||||
|
import android.provider.CalendarContract
|
||||||
|
import android.text.format.DateFormat
|
||||||
|
import android.text.format.DateUtils
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.material3.TextButton
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.unit.em
|
||||||
|
import de.mm20.launcher2.preferences.Settings.ClockWidgetSettings.ClockWidgetLayout
|
||||||
|
import de.mm20.launcher2.ui.R
|
||||||
|
import java.text.SimpleDateFormat
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun DatePart(
|
||||||
|
time: Long,
|
||||||
|
layout: ClockWidgetLayout
|
||||||
|
) {
|
||||||
|
val verticalLayout = layout == ClockWidgetLayout.Vertical
|
||||||
|
val context = LocalContext.current
|
||||||
|
TextButton(onClick = {
|
||||||
|
val startMillis = System.currentTimeMillis()
|
||||||
|
val builder = CalendarContract.CONTENT_URI.buildUpon()
|
||||||
|
builder.appendPath("time")
|
||||||
|
ContentUris.appendId(builder, startMillis)
|
||||||
|
val intent = Intent(Intent.ACTION_VIEW)
|
||||||
|
.setData(builder.build())
|
||||||
|
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||||
|
|
||||||
|
context.startActivity(intent)
|
||||||
|
}) {
|
||||||
|
if (verticalLayout) {
|
||||||
|
Text(
|
||||||
|
text = DateUtils.formatDateTime(
|
||||||
|
context,
|
||||||
|
time,
|
||||||
|
DateUtils.FORMAT_SHOW_WEEKDAY or DateUtils.FORMAT_SHOW_DATE or DateUtils.FORMAT_SHOW_YEAR
|
||||||
|
),
|
||||||
|
style = MaterialTheme.typography.titleMedium,
|
||||||
|
color = Color.White
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
val line1Format = DateFormat.getBestDateTimePattern(Locale.getDefault(), "EEEE")
|
||||||
|
val line2Format = DateFormat.getBestDateTimePattern(Locale.getDefault(), "MMMM dd yyyy")
|
||||||
|
val format = SimpleDateFormat("$line1Format\n$line2Format")
|
||||||
|
Text(
|
||||||
|
text = format.format(time),
|
||||||
|
lineHeight = 1.2.em,
|
||||||
|
style = MaterialTheme.typography.titleLarge,
|
||||||
|
color = Color.White
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -3,6 +3,7 @@ package de.mm20.launcher2.ui.settings.widgets
|
|||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.rounded.Audiotrack
|
import androidx.compose.material.icons.rounded.Audiotrack
|
||||||
import androidx.compose.material.icons.rounded.LightMode
|
import androidx.compose.material.icons.rounded.LightMode
|
||||||
|
import androidx.compose.material.icons.rounded.Schedule
|
||||||
import androidx.compose.material.icons.rounded.Today
|
import androidx.compose.material.icons.rounded.Today
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
@ -16,6 +17,13 @@ fun WidgetsSettingsScreen() {
|
|||||||
val navController = LocalNavController.current
|
val navController = LocalNavController.current
|
||||||
PreferenceScreen(title = stringResource(R.string.preference_screen_widgets)) {
|
PreferenceScreen(title = stringResource(R.string.preference_screen_widgets)) {
|
||||||
item {
|
item {
|
||||||
|
Preference(
|
||||||
|
title = stringResource(R.string.preference_screen_clockwidget),
|
||||||
|
icon = Icons.Rounded.Schedule,
|
||||||
|
onClick = {
|
||||||
|
navController?.navigate("settings/widgets/clock")
|
||||||
|
}
|
||||||
|
)
|
||||||
Preference(
|
Preference(
|
||||||
title = stringResource(R.string.preference_screen_weatherwidget),
|
title = stringResource(R.string.preference_screen_weatherwidget),
|
||||||
icon = Icons.Rounded.LightMode,
|
icon = Icons.Rounded.LightMode,
|
||||||
|
|||||||
@ -1,34 +0,0 @@
|
|||||||
package de.mm20.launcher2.ui.widget.parts
|
|
||||||
|
|
||||||
import android.content.ContentUris
|
|
||||||
import android.content.Intent
|
|
||||||
import android.provider.CalendarContract
|
|
||||||
import android.text.format.DateUtils
|
|
||||||
import androidx.compose.material3.MaterialTheme
|
|
||||||
import androidx.compose.material3.TextButton
|
|
||||||
import androidx.compose.runtime.Composable
|
|
||||||
import androidx.compose.ui.graphics.Color
|
|
||||||
import androidx.compose.ui.platform.LocalContext
|
|
||||||
import de.mm20.launcher2.ui.component.TextClock
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
fun DatePart() {
|
|
||||||
val context = LocalContext.current
|
|
||||||
TextButton(onClick = {
|
|
||||||
val startMillis = System.currentTimeMillis()
|
|
||||||
val builder = CalendarContract.CONTENT_URI.buildUpon()
|
|
||||||
builder.appendPath("time")
|
|
||||||
ContentUris.appendId(builder, startMillis)
|
|
||||||
val intent = Intent(Intent.ACTION_VIEW)
|
|
||||||
.setData(builder.build())
|
|
||||||
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
|
||||||
|
|
||||||
context.startActivity(intent)
|
|
||||||
}) {
|
|
||||||
TextClock(
|
|
||||||
formatFlags = DateUtils.FORMAT_SHOW_WEEKDAY or DateUtils.FORMAT_SHOW_DATE or DateUtils.FORMAT_SHOW_YEAR,
|
|
||||||
style = MaterialTheme.typography.titleMedium,
|
|
||||||
color = Color.White
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -129,16 +129,10 @@
|
|||||||
android:paddingTop="8dp"
|
android:paddingTop="8dp"
|
||||||
android:orientation="vertical">
|
android:orientation="vertical">
|
||||||
|
|
||||||
<Space
|
|
||||||
android:id="@+id/widgetSpacer"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content" />
|
|
||||||
|
|
||||||
<de.mm20.launcher2.ui.legacy.widget.ClockWidget
|
<de.mm20.launcher2.ui.legacy.widget.ClockWidget
|
||||||
android:id="@+id/clockWidget"
|
android:id="@+id/clockWidget"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="match_parent" />
|
||||||
android:layout_marginTop="8dp" />
|
|
||||||
|
|
||||||
<com.jmedeisis.draglinearlayout.DragLinearLayout
|
<com.jmedeisis.draglinearlayout.DragLinearLayout
|
||||||
android:animateLayoutChanges="true"
|
android:animateLayoutChanges="true"
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user