diff --git a/i18n/src/main/res/values-de/strings.xml b/i18n/src/main/res/values-de/strings.xml
index 1769c4c4..e197dc7d 100644
--- a/i18n/src/main/res/values-de/strings.xml
+++ b/i18n/src/main/res/values-de/strings.xml
@@ -404,7 +404,6 @@
Lizenziert unter der GNU General Public License 3.0
Diese Funktion ist in dieser Version von %1$s nicht verfügbar.
Uhr
- Uhrenstil
- +%1$d laufender Termin aus vergangenen Tagen
- +%1$d laufende Termine aus vergangenen Tagen
@@ -432,6 +431,13 @@
Wetter
Musik
+ Uhr
+ Layout
+ Vertikal
+ Horizontal
+ Stil
+ Uhr auswählen
+
Crash-Reporter
Fehler- und Absturzberichte
diff --git a/i18n/src/main/res/values/strings.xml b/i18n/src/main/res/values/strings.xml
index 4a28e84b..2f2d3c9e 100644
--- a/i18n/src/main/res/values/strings.xml
+++ b/i18n/src/main/res/values/strings.xml
@@ -451,7 +451,6 @@
Licensed under the GNU General Public License 3.0
This feature is not available in this version of %1$s
Clock
- Clock style
Grid
Number of columns
@@ -470,6 +469,13 @@
Weather
Music
+ Clock
+ Layout
+ Vertical
+ Horizontal
+ Style
+ Select a clock
+
Crash reporter
Error and crash reports
diff --git a/preferences/src/main/java/de/mm20/launcher2/preferences/Defaults.kt b/preferences/src/main/java/de/mm20/launcher2/preferences/Defaults.kt
index 2b09b9ca..905bfe16 100644
--- a/preferences/src/main/java/de/mm20/launcher2/preferences/Defaults.kt
+++ b/preferences/src/main/java/de/mm20/launcher2/preferences/Defaults.kt
@@ -21,5 +21,11 @@ fun createFactorySettings(context: Context): Settings {
.setFilterSources(true)
.build()
)
+ .setClockWidget(Settings.ClockWidgetSettings
+ .newBuilder()
+ .setLayout(Settings.ClockWidgetSettings.ClockWidgetLayout.Vertical)
+ .setClockStyle(Settings.ClockWidgetSettings.ClockStyle.DigitalClock1)
+ .build()
+ )
.build()
}
\ No newline at end of file
diff --git a/preferences/src/main/proto/settings.proto b/preferences/src/main/proto/settings.proto
index 169f294f..b503cf14 100644
--- a/preferences/src/main/proto/settings.proto
+++ b/preferences/src/main/proto/settings.proto
@@ -37,4 +37,19 @@ message Settings {
}
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;
+
}
\ No newline at end of file
diff --git a/settings.gradle.kts b/settings.gradle.kts
index 99087197..8701ead3 100644
--- a/settings.gradle.kts
+++ b/settings.gradle.kts
@@ -137,6 +137,9 @@ dependencyResolutionManagement {
alias("accompanist.pager")
.to("com.google.accompanist", "accompanist-pager")
.versionRef("accompanist")
+ alias("accompanist.pagerindicators")
+ .to("com.google.accompanist", "accompanist-pager-indicators")
+ .versionRef("accompanist")
alias("accompanist.flowlayout")
.to("com.google.accompanist", "accompanist-flowlayout")
.versionRef("accompanist")
diff --git a/ui/build.gradle.kts b/ui/build.gradle.kts
index 38cc9d1b..e9dfaf3e 100644
--- a/ui/build.gradle.kts
+++ b/ui/build.gradle.kts
@@ -84,6 +84,7 @@ dependencies {
implementation(libs.accompanist.insets)
implementation(libs.accompanist.systemuicontroller)
implementation(libs.accompanist.pager)
+ implementation(libs.accompanist.pagerindicators)
implementation(libs.accompanist.flowlayout)
implementation(libs.accompanist.navigationanimation)
diff --git a/ui/src/main/java/de/mm20/launcher2/ui/component/Clocks.kt b/ui/src/main/java/de/mm20/launcher2/ui/component/Clocks.kt
deleted file mode 100644
index bb539ed5..00000000
--- a/ui/src/main/java/de/mm20/launcher2/ui/component/Clocks.kt
+++ /dev/null
@@ -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
-}
\ No newline at end of file
diff --git a/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/clock/ClockWidget.kt b/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/clock/ClockWidget.kt
index 1904c3bd..c40ef0d7 100644
--- a/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/clock/ClockWidget.kt
+++ b/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/clock/ClockWidget.kt
@@ -1,87 +1,138 @@
package de.mm20.launcher2.ui
-import android.content.BroadcastReceiver
-import android.content.Context
-import android.content.Intent
-import android.content.IntentFilter
-import android.provider.AlarmClock
+import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
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.runtime.*
+import androidx.compose.runtime.livedata.observeAsState
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.unit.dp
-import de.mm20.launcher2.ui.component.DigitalClock
-import de.mm20.launcher2.ui.widget.parts.DatePart
+import androidx.lifecycle.viewmodel.compose.viewModel
+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
fun ClockWidget(
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(
modifier = Modifier
.fillMaxWidth(),
contentAlignment = Alignment.BottomCenter
) {
- CompositionLocalProvider(LocalContentColor provides Color.White) {
- Column(
- 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() }
+ CompositionLocalProvider(
+ LocalContentColor provides Color.White
) {
- context.startActivity(Intent(AlarmClock.ACTION_SHOW_ALARMS).apply {
- flags = Intent.FLAG_ACTIVITY_NEW_TASK
- })
+ if (layout == ClockWidgetLayout.Vertical) {
+ 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
-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(
- modifier = Modifier.padding(bottom = 16.dp)
+ modifier = modifier
) {
- DatePart()
+ DatePart(time, layout)
}
}
\ No newline at end of file
diff --git a/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/clock/ClockWidgetVM.kt b/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/clock/ClockWidgetVM.kt
index 187e0efd..413c759f 100644
--- a/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/clock/ClockWidgetVM.kt
+++ b/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/clock/ClockWidgetVM.kt
@@ -1,6 +1,49 @@
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.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 = 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
+ })
+ }
}
\ No newline at end of file
diff --git a/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/clock/clocks/BinaryClock.kt b/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/clock/clocks/BinaryClock.kt
new file mode 100644
index 00000000..2ae7c767
--- /dev/null
+++ b/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/clock/clocks/BinaryClock.kt
@@ -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
+ )
+ )
+ )
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/clock/clocks/DigitalClock1.kt b/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/clock/clocks/DigitalClock1.kt
new file mode 100644
index 00000000..1437045d
--- /dev/null
+++ b/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/clock/clocks/DigitalClock1.kt
@@ -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,
+ )
+ )
+}
\ No newline at end of file
diff --git a/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/clock/clocks/DigitalClock2.kt b/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/clock/clocks/DigitalClock2.kt
new file mode 100644
index 00000000..da736928
--- /dev/null
+++ b/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/clock/clocks/DigitalClock2.kt
@@ -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
+ )
+}
\ No newline at end of file
diff --git a/ui/src/main/java/de/mm20/launcher2/ui/legacy/activity/LauncherActivity.kt b/ui/src/main/java/de/mm20/launcher2/ui/legacy/activity/LauncherActivity.kt
index 6f00569a..2186931c 100644
--- a/ui/src/main/java/de/mm20/launcher2/ui/legacy/activity/LauncherActivity.kt
+++ b/ui/src/main/java/de/mm20/launcher2/ui/legacy/activity/LauncherActivity.kt
@@ -108,7 +108,6 @@ class LauncherActivity : BaseActivity() {
set(value) {
field = value
if (value) {
- binding.widgetSpacer.visibility = View.GONE
binding.clockWidget.visibility = View.GONE
binding.searchBar.setRightIcon(R.drawable.ic_done)
binding.scrollView.setOnTouchListener(null)
@@ -154,7 +153,6 @@ class LauncherActivity : BaseActivity() {
.start()
} else {
widgetViewModel.saveWidgets(widgets)
- binding.widgetSpacer.visibility = View.VISIBLE
binding.widgetList.layoutTransition = ChangingLayoutTransition()
binding.widgetContainer.layoutTransition = ChangingLayoutTransition()
binding.scrollContainer.layoutTransition = ChangingLayoutTransition()
@@ -226,9 +224,9 @@ class LauncherActivity : BaseActivity() {
binding.searchContainer.layoutTransition.enableTransitionType(LayoutTransition.CHANGING)
binding.widgetContainer.layoutTransition.enableTransitionType(LayoutTransition.CHANGING)
- val params = binding.widgetSpacer.layoutParams as LinearLayout.LayoutParams
- params.topMargin = Point().also { windowManager.defaultDisplay.getSize(it) }.y
- binding.widgetSpacer.layoutParams = params
+ val params = binding.clockWidget.layoutParams
+ params.height = Point().also { windowManager.defaultDisplay.getSize(it) }.y
+ binding.clockWidget.layoutParams = params
binding.container.doOnLayout {
adjustWidgetSpace()
}
@@ -237,9 +235,7 @@ class LauncherActivity : BaseActivity() {
binding.scrollView.setOnScrollChangeListener { _: NestedScrollView?, _: Int, scrollY: Int, _: Int, oldScrollY: Int ->
when {
/* Hide searchbar*/
- scrollY > oldScrollY && ((searchVisibility && scrollY > binding.searchBar.height) || widgetEditMode ||
- scrollY > binding.widgetSpacer.height + binding.searchBar.height
- + (binding.widgetSpacer.layoutParams as LinearLayout.LayoutParams).topMargin) -> {
+ scrollY > oldScrollY && ((scrollY > binding.searchBar.height) || widgetEditMode) -> {
var newTransY = binding.searchBar.translationY - scrollY + oldScrollY
if (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
}
}
- if (scrollY > 0 && (searchVisibility || widgetEditMode ||
- scrollY > binding.widgetSpacer.height
- + (binding.widgetSpacer.layoutParams as LinearLayout.LayoutParams).topMargin)
+ if (scrollY > 0 && (searchVisibility || widgetEditMode)
) {
binding.searchBar.raise()
} else binding.searchBar.drop()
@@ -455,13 +449,10 @@ class LauncherActivity : BaseActivity() {
}
private fun adjustWidgetSpace() {
- val firstWidget = binding.clockWidget
- val m = binding.scrollContainer.paddingTop +
- (firstWidget.layoutParams as LinearLayout.LayoutParams).run { topMargin + bottomMargin }
- 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
+ val height = binding.scrollView.height - binding.searchBar.height - 8 * dp
+ binding.clockWidget.layoutParams = binding.clockWidget.layoutParams.also {
+ it.height = height.toInt()
+ }
}
diff --git a/ui/src/main/java/de/mm20/launcher2/ui/legacy/widget/ClockWidget.kt b/ui/src/main/java/de/mm20/launcher2/ui/legacy/widget/ClockWidget.kt
index d378801f..36b1896b 100644
--- a/ui/src/main/java/de/mm20/launcher2/ui/legacy/widget/ClockWidget.kt
+++ b/ui/src/main/java/de/mm20/launcher2/ui/legacy/widget/ClockWidget.kt
@@ -30,7 +30,7 @@ class ClockWidget : FrameLayout {
addView(composeView)
composeView.layoutParams =
- LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT)
+ LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)
composeView.setContent {
diff --git a/ui/src/main/java/de/mm20/launcher2/ui/settings/SettingsActivity.kt b/ui/src/main/java/de/mm20/launcher2/ui/settings/SettingsActivity.kt
index e23ad411..0ac04017 100644
--- a/ui/src/main/java/de/mm20/launcher2/ui/settings/SettingsActivity.kt
+++ b/ui/src/main/java/de/mm20/launcher2/ui/settings/SettingsActivity.kt
@@ -23,6 +23,7 @@ import de.mm20.launcher2.ui.base.BaseActivity
import de.mm20.launcher2.ui.locals.LocalNavController
import de.mm20.launcher2.ui.settings.about.AboutSettingsScreen
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.license.LicenseScreen
import de.mm20.launcher2.ui.settings.main.MainSettingsScreen
@@ -93,6 +94,9 @@ class SettingsActivity : BaseActivity() {
composable("settings/widgets/music") {
MusicWidgetSettingsScreen()
}
+ composable("settings/widgets/clock") {
+ ClockWidgetSettingsScreen()
+ }
composable("settings/about") {
AboutSettingsScreen()
}
diff --git a/ui/src/main/java/de/mm20/launcher2/ui/settings/clockwidget/ClockWidgetSettingsScreen.kt b/ui/src/main/java/de/mm20/launcher2/ui/settings/clockwidget/ClockWidgetSettingsScreen.kt
new file mode 100644
index 00000000..a755d97c
--- /dev/null
+++ b/ui/src/main/java/de/mm20/launcher2/ui/settings/clockwidget/ClockWidgetSettingsScreen.kt
@@ -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)
+ }
+ }
+ )
+ }
+}
\ No newline at end of file
diff --git a/ui/src/main/java/de/mm20/launcher2/ui/settings/clockwidget/ClockWidgetSettingsScreenVM.kt b/ui/src/main/java/de/mm20/launcher2/ui/settings/clockwidget/ClockWidgetSettingsScreenVM.kt
new file mode 100644
index 00000000..304d143a
--- /dev/null
+++ b/ui/src/main/java/de/mm20/launcher2/ui/settings/clockwidget/ClockWidgetSettingsScreenVM.kt
@@ -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()
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/ui/src/main/java/de/mm20/launcher2/ui/settings/clockwidget/parts/DatePart.kt b/ui/src/main/java/de/mm20/launcher2/ui/settings/clockwidget/parts/DatePart.kt
new file mode 100644
index 00000000..75c5bf03
--- /dev/null
+++ b/ui/src/main/java/de/mm20/launcher2/ui/settings/clockwidget/parts/DatePart.kt
@@ -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
+ )
+ }
+ }
+}
\ No newline at end of file
diff --git a/ui/src/main/java/de/mm20/launcher2/ui/settings/widgets/WidgetsSettingsScreen.kt b/ui/src/main/java/de/mm20/launcher2/ui/settings/widgets/WidgetsSettingsScreen.kt
index bcc42009..22d4df47 100644
--- a/ui/src/main/java/de/mm20/launcher2/ui/settings/widgets/WidgetsSettingsScreen.kt
+++ b/ui/src/main/java/de/mm20/launcher2/ui/settings/widgets/WidgetsSettingsScreen.kt
@@ -3,6 +3,7 @@ package de.mm20.launcher2.ui.settings.widgets
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.rounded.Audiotrack
import androidx.compose.material.icons.rounded.LightMode
+import androidx.compose.material.icons.rounded.Schedule
import androidx.compose.material.icons.rounded.Today
import androidx.compose.runtime.Composable
import androidx.compose.ui.res.stringResource
@@ -16,6 +17,13 @@ fun WidgetsSettingsScreen() {
val navController = LocalNavController.current
PreferenceScreen(title = stringResource(R.string.preference_screen_widgets)) {
item {
+ Preference(
+ title = stringResource(R.string.preference_screen_clockwidget),
+ icon = Icons.Rounded.Schedule,
+ onClick = {
+ navController?.navigate("settings/widgets/clock")
+ }
+ )
Preference(
title = stringResource(R.string.preference_screen_weatherwidget),
icon = Icons.Rounded.LightMode,
diff --git a/ui/src/main/java/de/mm20/launcher2/ui/widget/parts/DatePart.kt b/ui/src/main/java/de/mm20/launcher2/ui/widget/parts/DatePart.kt
deleted file mode 100644
index 86cd04cf..00000000
--- a/ui/src/main/java/de/mm20/launcher2/ui/widget/parts/DatePart.kt
+++ /dev/null
@@ -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
- )
- }
-}
\ No newline at end of file
diff --git a/ui/src/main/res/layout/activity_launcher.xml b/ui/src/main/res/layout/activity_launcher.xml
index 612cfe85..d624488c 100644
--- a/ui/src/main/res/layout/activity_launcher.xml
+++ b/ui/src/main/res/layout/activity_launcher.xml
@@ -129,16 +129,10 @@
android:paddingTop="8dp"
android:orientation="vertical">
-
-
+ android:layout_height="match_parent" />