Add alarm part
This commit is contained in:
parent
3e2ab857c9
commit
c1b0c49cc2
@ -14,7 +14,10 @@ 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.platform.LocalLifecycleOwner
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.repeatOnLifecycle
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import de.mm20.launcher2.preferences.Settings.ClockWidgetSettings.ClockStyle
|
||||
import de.mm20.launcher2.preferences.Settings.ClockWidgetSettings.ClockWidgetLayout
|
||||
@ -28,11 +31,18 @@ fun ClockWidget(
|
||||
) {
|
||||
val viewModel: ClockWidgetVM = viewModel()
|
||||
val context = LocalContext.current
|
||||
val time by viewModel.getTime(context).collectAsState(System.currentTimeMillis())
|
||||
val lifecycleOwner = LocalLifecycleOwner.current
|
||||
val time by viewModel.time.collectAsState(System.currentTimeMillis())
|
||||
val layout by viewModel.layout.observeAsState()
|
||||
val clockStyle by viewModel.clockStyle.observeAsState()
|
||||
|
||||
val partProvider by viewModel.getActivePart().collectAsState(null)
|
||||
LaunchedEffect(null) {
|
||||
lifecycleOwner.repeatOnLifecycle(Lifecycle.State.RESUMED) {
|
||||
viewModel.onActive(context)
|
||||
}
|
||||
}
|
||||
|
||||
val partProvider by viewModel.getActivePart(LocalContext.current).collectAsState(null)
|
||||
|
||||
Box(
|
||||
modifier = Modifier
|
||||
@ -68,7 +78,9 @@ fun ClockWidget(
|
||||
}
|
||||
if (layout == ClockWidgetLayout.Horizontal) {
|
||||
Column(
|
||||
modifier = Modifier.fillMaxWidth().padding(bottom = 16.dp),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(bottom = 16.dp),
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
Icon(imageVector = Icons.Rounded.ExpandLess, contentDescription = "")
|
||||
|
||||
@ -10,13 +10,13 @@ import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.asLiveData
|
||||
import de.mm20.launcher2.ktx.tryStartActivity
|
||||
import de.mm20.launcher2.preferences.LauncherDataStore
|
||||
import de.mm20.launcher2.ui.launcher.widgets.clock.parts.AlarmPartProvider
|
||||
import de.mm20.launcher2.ui.launcher.widgets.clock.parts.DatePartProvider
|
||||
import de.mm20.launcher2.ui.launcher.widgets.clock.parts.MusicPartProvider
|
||||
import de.mm20.launcher2.ui.launcher.widgets.clock.parts.PartProvider
|
||||
import kotlinx.coroutines.channels.awaitClose
|
||||
import kotlinx.coroutines.channels.trySendBlocking
|
||||
import kotlinx.coroutines.flow.*
|
||||
import kotlinx.coroutines.launch
|
||||
import org.koin.core.component.KoinComponent
|
||||
import org.koin.core.component.inject
|
||||
|
||||
@ -26,16 +26,22 @@ class ClockWidgetVM : ViewModel(), KoinComponent {
|
||||
private val partProviders = MutableStateFlow<List<PartProvider>>(emptyList())
|
||||
|
||||
init {
|
||||
partProviders.value = listOf(DatePartProvider(), MusicPartProvider())
|
||||
partProviders.value = listOf(
|
||||
DatePartProvider(),
|
||||
MusicPartProvider(),
|
||||
AlarmPartProvider()
|
||||
)
|
||||
}
|
||||
|
||||
fun getActivePart(): Flow<PartProvider?> = channelFlow {
|
||||
val time = MutableStateFlow(System.currentTimeMillis())
|
||||
|
||||
fun getActivePart(context: Context): Flow<PartProvider?> = channelFlow {
|
||||
partProviders.collectLatest { providers ->
|
||||
if (providers.isEmpty()) {
|
||||
send(null)
|
||||
return@collectLatest
|
||||
}
|
||||
val rankings = providers.map { it.getRanking() }
|
||||
val rankings = providers.map { it.getRanking(context) }
|
||||
combine(rankings) { r ->
|
||||
var prov = providers[0]
|
||||
for (i in 1 until providers.size) {
|
||||
@ -43,7 +49,6 @@ class ClockWidgetVM : ViewModel(), KoinComponent {
|
||||
prov = providers[i]
|
||||
}
|
||||
}
|
||||
Log.d("MM20", prov.toString())
|
||||
return@combine prov
|
||||
}.collectLatest {
|
||||
send(it)
|
||||
@ -55,7 +60,7 @@ class ClockWidgetVM : ViewModel(), KoinComponent {
|
||||
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 {
|
||||
private fun getTime(context: Context): Flow<Long> = callbackFlow {
|
||||
trySendBlocking(System.currentTimeMillis())
|
||||
val receiver = object : BroadcastReceiver() {
|
||||
override fun onReceive(context: Context?, intent: Intent?) {
|
||||
@ -71,9 +76,20 @@ class ClockWidgetVM : ViewModel(), KoinComponent {
|
||||
}
|
||||
}
|
||||
|
||||
private fun updatePartsTime(time: Long) {
|
||||
partProviders.value.forEach { it.setTime(time) }
|
||||
}
|
||||
|
||||
fun launchClockApp(context: Context) {
|
||||
context.tryStartActivity(Intent(AlarmClock.ACTION_SHOW_ALARMS).apply {
|
||||
flags = Intent.FLAG_ACTIVITY_NEW_TASK
|
||||
})
|
||||
}
|
||||
|
||||
suspend fun onActive(context: Context) {
|
||||
getTime(context).collectLatest {
|
||||
time.value = it
|
||||
updatePartsTime(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,101 @@
|
||||
package de.mm20.launcher2.ui.launcher.widgets.clock.parts
|
||||
|
||||
import android.app.AlarmManager
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.IntentFilter
|
||||
import android.provider.AlarmClock
|
||||
import android.text.format.DateUtils
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.rounded.Alarm
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.livedata.observeAsState
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.core.content.getSystemService
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import de.mm20.launcher2.ktx.tryStartActivity
|
||||
import de.mm20.launcher2.preferences.Settings.ClockWidgetSettings.ClockWidgetLayout
|
||||
import kotlinx.coroutines.channels.awaitClose
|
||||
import kotlinx.coroutines.channels.trySendBlocking
|
||||
import kotlinx.coroutines.flow.*
|
||||
|
||||
class AlarmPartProvider : PartProvider {
|
||||
|
||||
private val nextAlarmTime = MutableLiveData<Long?>(null)
|
||||
|
||||
private val time = MutableStateFlow(System.currentTimeMillis())
|
||||
|
||||
override fun setTime(time: Long) {
|
||||
this.time.value = time
|
||||
}
|
||||
|
||||
override fun getRanking(context: Context): Flow<Int> = channelFlow {
|
||||
val nextAlarm = getNextAlarmTime(context)
|
||||
nextAlarm.collectLatest { alarm ->
|
||||
nextAlarmTime.value = alarm
|
||||
if (alarm == null) {
|
||||
send(0)
|
||||
} else {
|
||||
time.collectLatest {
|
||||
if (alarm > it + 15 * 60 * 1000) {
|
||||
send(0)
|
||||
} else {
|
||||
send(60)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getNextAlarmTime(context: Context): Flow<Long?> = callbackFlow {
|
||||
val alarmManager: AlarmManager = context.getSystemService() ?: return@callbackFlow
|
||||
trySendBlocking(alarmManager.nextAlarmClock?.triggerTime)
|
||||
val receiver = object : BroadcastReceiver() {
|
||||
override fun onReceive(context: Context?, intent: Intent?) {
|
||||
trySendBlocking(alarmManager.nextAlarmClock?.triggerTime)
|
||||
}
|
||||
}
|
||||
context.registerReceiver(receiver, IntentFilter().apply {
|
||||
addAction(AlarmManager.ACTION_NEXT_ALARM_CLOCK_CHANGED)
|
||||
})
|
||||
awaitClose {
|
||||
context.unregisterReceiver(receiver)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
override fun Component(layout: ClockWidgetLayout) {
|
||||
val context = LocalContext.current
|
||||
|
||||
val alarmTime by nextAlarmTime.observeAsState(null)
|
||||
val time by this.time.collectAsState(System.currentTimeMillis())
|
||||
|
||||
alarmTime?.let {
|
||||
TextButton(onClick = {
|
||||
context.tryStartActivity(Intent(AlarmClock.ACTION_SHOW_ALARMS))
|
||||
},
|
||||
colors = ButtonDefaults.textButtonColors(
|
||||
contentColor = LocalContentColor.current
|
||||
)
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Rounded.Alarm,
|
||||
contentDescription = null
|
||||
)
|
||||
Text(
|
||||
modifier = Modifier.padding(start = 12.dp),
|
||||
text = DateUtils.getRelativeTimeSpanString(it, time, DateUtils.MINUTE_IN_MILLIS)
|
||||
.toString(),
|
||||
style = MaterialTheme.typography.labelLarge
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,6 +1,7 @@
|
||||
package de.mm20.launcher2.ui.launcher.widgets.clock.parts
|
||||
|
||||
import android.content.ContentUris
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.provider.CalendarContract
|
||||
import android.text.format.DateFormat
|
||||
@ -20,19 +21,25 @@ import de.mm20.launcher2.preferences.Settings
|
||||
import de.mm20.launcher2.ui.launcher.widgets.clock.ClockWidgetVM
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.flow
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.*
|
||||
|
||||
class DatePartProvider: PartProvider {
|
||||
override fun getRanking(): Flow<Int> = flow {
|
||||
override fun getRanking(context: Context): Flow<Int> = flow {
|
||||
emit(1)
|
||||
}
|
||||
|
||||
private val time = MutableStateFlow(System.currentTimeMillis())
|
||||
|
||||
override fun setTime(time: Long) {
|
||||
this.time.value = time
|
||||
}
|
||||
|
||||
@Composable
|
||||
override fun Component(layout: Settings.ClockWidgetSettings.ClockWidgetLayout) {
|
||||
val viewModel: ClockWidgetVM = viewModel()
|
||||
val time by viewModel.getTime(LocalContext.current).collectAsState(System.currentTimeMillis())
|
||||
val time by this.time.collectAsState(System.currentTimeMillis())
|
||||
val verticalLayout = layout == Settings.ClockWidgetSettings.ClockWidgetLayout.Vertical
|
||||
val context = LocalContext.current
|
||||
TextButton(onClick = {
|
||||
|
||||
@ -1,10 +1,12 @@
|
||||
package de.mm20.launcher2.ui.launcher.widgets.clock.parts
|
||||
|
||||
import android.app.PendingIntent
|
||||
import android.content.Context
|
||||
import androidx.compose.animation.graphics.ExperimentalAnimationGraphicsApi
|
||||
import androidx.compose.animation.graphics.res.animatedVectorResource
|
||||
import androidx.compose.animation.graphics.res.rememberAnimatedVectorPainter
|
||||
import androidx.compose.animation.graphics.vector.AnimatedImageVector
|
||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||
import androidx.compose.foundation.combinedClickable
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
@ -39,7 +41,7 @@ class MusicPartProvider : PartProvider, KoinComponent {
|
||||
|
||||
private val musicRepository: MusicRepository by inject()
|
||||
|
||||
override fun getRanking(): Flow<Int> = channelFlow {
|
||||
override fun getRanking(context: Context): Flow<Int> = channelFlow {
|
||||
musicRepository.playbackState.collectLatest {
|
||||
if (it == PlaybackState.Stopped) send(0)
|
||||
else send(50)
|
||||
@ -48,7 +50,7 @@ class MusicPartProvider : PartProvider, KoinComponent {
|
||||
|
||||
@OptIn(
|
||||
ExperimentalAnimationGraphicsApi::class,
|
||||
androidx.compose.foundation.ExperimentalFoundationApi::class
|
||||
ExperimentalFoundationApi::class
|
||||
)
|
||||
@Composable
|
||||
override fun Component(layout: ClockWidgetLayout) {
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
package de.mm20.launcher2.ui.launcher.widgets.clock.parts
|
||||
|
||||
import android.content.Context
|
||||
import androidx.compose.runtime.Composable
|
||||
import de.mm20.launcher2.preferences.Settings
|
||||
import de.mm20.launcher2.preferences.Settings.ClockWidgetSettings.ClockWidgetLayout
|
||||
@ -7,7 +8,9 @@ import kotlinx.coroutines.flow.Flow
|
||||
|
||||
interface PartProvider {
|
||||
|
||||
fun getRanking(): Flow<Int>
|
||||
fun getRanking(context: Context): Flow<Int>
|
||||
|
||||
fun setTime(time: Long) {}
|
||||
|
||||
@Composable
|
||||
fun Component(layout: ClockWidgetLayout)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user