mirror of https://github.com/tasks/tasks
Use SCHEDULE_EXACT_ALARM
parent
a4cd0829b0
commit
d8186e5fe4
@ -0,0 +1,31 @@
|
|||||||
|
package org.tasks.compose
|
||||||
|
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.DisposableEffect
|
||||||
|
import androidx.compose.runtime.State
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import androidx.compose.ui.platform.LocalLifecycleOwner
|
||||||
|
import androidx.lifecycle.Lifecycle
|
||||||
|
import androidx.lifecycle.LifecycleEventObserver
|
||||||
|
import org.tasks.extensions.Context.canScheduleExactAlarms
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun rememberReminderPermissionState(): State<Boolean> {
|
||||||
|
val context = LocalContext.current
|
||||||
|
val hasRemindersPermission = remember { mutableStateOf(true) }
|
||||||
|
val observer = remember {
|
||||||
|
LifecycleEventObserver { _, event ->
|
||||||
|
if (event == Lifecycle.Event.ON_RESUME) {
|
||||||
|
hasRemindersPermission.value = context.canScheduleExactAlarms()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val lifecycle = LocalLifecycleOwner.current.lifecycle
|
||||||
|
DisposableEffect(lifecycle, observer) {
|
||||||
|
lifecycle.addObserver(observer)
|
||||||
|
onDispose { lifecycle.removeObserver(observer) }
|
||||||
|
}
|
||||||
|
return hasRemindersPermission
|
||||||
|
}
|
@ -0,0 +1,27 @@
|
|||||||
|
package org.tasks.receivers
|
||||||
|
|
||||||
|
import android.app.AlarmManager.ACTION_SCHEDULE_EXACT_ALARM_PERMISSION_STATE_CHANGED
|
||||||
|
import android.content.BroadcastReceiver
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
|
import org.tasks.extensions.Context.canScheduleExactAlarms
|
||||||
|
import org.tasks.jobs.WorkManager
|
||||||
|
import org.tasks.scheduling.NotificationSchedulerIntentService
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@AndroidEntryPoint
|
||||||
|
class ScheduleExactAlarmsPermissionReceiver : BroadcastReceiver() {
|
||||||
|
|
||||||
|
@Inject lateinit var workManager: WorkManager
|
||||||
|
|
||||||
|
override fun onReceive(context: Context, intent: Intent?) {
|
||||||
|
if (intent?.action != ACTION_SCHEDULE_EXACT_ALARM_PERMISSION_STATE_CHANGED) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (context.canScheduleExactAlarms()) {
|
||||||
|
NotificationSchedulerIntentService.enqueueWork(context)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,98 @@
|
|||||||
|
package org.tasks.compose.components
|
||||||
|
|
||||||
|
import androidx.compose.animation.AnimatedVisibility
|
||||||
|
import androidx.compose.animation.ExperimentalAnimationApi
|
||||||
|
import androidx.compose.animation.expandVertically
|
||||||
|
import androidx.compose.animation.shrinkVertically
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.Spacer
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.height
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.material3.Card
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.material3.TextButton
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
|
||||||
|
@ExperimentalAnimationApi
|
||||||
|
@Composable
|
||||||
|
fun AnimatedBanner(
|
||||||
|
visible: Boolean,
|
||||||
|
title: String,
|
||||||
|
body: String,
|
||||||
|
dismissText: String,
|
||||||
|
onDismiss: () -> Unit,
|
||||||
|
action: String,
|
||||||
|
onAction: () -> Unit,
|
||||||
|
) {
|
||||||
|
AnimatedBanner(
|
||||||
|
visible = visible,
|
||||||
|
content = {
|
||||||
|
Text(
|
||||||
|
text = title,
|
||||||
|
style = MaterialTheme.typography.bodyLarge,
|
||||||
|
modifier = Modifier.padding(horizontal = 16.dp),
|
||||||
|
color = MaterialTheme.colorScheme.onSurface,
|
||||||
|
)
|
||||||
|
Spacer(modifier = Modifier.height(4.dp))
|
||||||
|
Text(
|
||||||
|
text = body,
|
||||||
|
style = MaterialTheme.typography.bodyMedium,
|
||||||
|
modifier = Modifier.padding(horizontal = 16.dp),
|
||||||
|
color = MaterialTheme.colorScheme.onSurface,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
buttons = {
|
||||||
|
BannerTextButton(text = dismissText, onDismiss)
|
||||||
|
BannerTextButton(text = action, onAction)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@ExperimentalAnimationApi
|
||||||
|
@Composable
|
||||||
|
private fun AnimatedBanner(
|
||||||
|
visible: Boolean,
|
||||||
|
content: @Composable () -> Unit,
|
||||||
|
buttons: @Composable () -> Unit,
|
||||||
|
) {
|
||||||
|
AnimatedVisibility(
|
||||||
|
visible = visible,
|
||||||
|
enter = expandVertically(),
|
||||||
|
exit = shrinkVertically(),
|
||||||
|
) {
|
||||||
|
Card(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(16.dp)
|
||||||
|
) {
|
||||||
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
|
content()
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(vertical = 8.dp, horizontal = 16.dp)
|
||||||
|
.align(Alignment.End),
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(8.dp)
|
||||||
|
) {
|
||||||
|
buttons()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun BannerTextButton(text: String, onClick: () -> Unit) {
|
||||||
|
TextButton(onClick = onClick) {
|
||||||
|
Text(
|
||||||
|
text = text,
|
||||||
|
style = MaterialTheme.typography.labelLarge.copy(
|
||||||
|
color = MaterialTheme.colorScheme.onSurface
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue