Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2ca4335e09 |
No files matched your search
@@ -18,7 +18,6 @@ import androidx.core.content.edit
|
||||
import de.jeanlucmakiola.agendula.MainActivity
|
||||
import de.jeanlucmakiola.agendula.R
|
||||
import de.jeanlucmakiola.agendula.data.prefs.Settings
|
||||
import de.jeanlucmakiola.agendula.domain.readableDescription
|
||||
import de.jeanlucmakiola.agendula.data.prefs.is24Hour
|
||||
import de.jeanlucmakiola.agendula.data.di.ApplicationScope
|
||||
import de.jeanlucmakiola.agendula.data.tasks.ProviderResolver
|
||||
@@ -85,7 +84,6 @@ class TaskNotifier @Inject constructor(
|
||||
),
|
||||
)
|
||||
}
|
||||
val description = task.description?.readableDescription()?.trim()?.takeIf { it.isNotBlank() }
|
||||
val tapIntent = PendingIntent.getActivity(
|
||||
context,
|
||||
task.taskId.toInt(),
|
||||
@@ -95,10 +93,7 @@ class TaskNotifier @Inject constructor(
|
||||
val notification = NotificationCompat.Builder(context, channelId)
|
||||
.setSmallIcon(R.drawable.ic_notification)
|
||||
.setContentTitle(title)
|
||||
.apply { (text ?: description)?.let(::setContentText) }
|
||||
.apply {
|
||||
expandedText(text, description)?.let { setStyle(NotificationCompat.BigTextStyle().bigText(it)) }
|
||||
}
|
||||
.apply { if (text != null) setContentText(text) }
|
||||
.setCategory(NotificationCompat.CATEGORY_REMINDER)
|
||||
.setPriority(NotificationCompat.PRIORITY_HIGH)
|
||||
.setAutoCancel(true)
|
||||
@@ -245,10 +240,3 @@ private fun relativeDay(date: LocalDate, today: LocalDate, words: RelativeDays,
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
/** The expanded body of a due reminder: the due line, then the task's description. */
|
||||
internal fun expandedText(dueLine: String?, description: String?): String? = when {
|
||||
description == null -> null
|
||||
dueLine == null -> description
|
||||
else -> "$dueLine\n\n$description"
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
package de.jeanlucmakiola.agendula.ui.common
|
||||
|
||||
import androidx.compose.foundation.gestures.awaitEachGesture
|
||||
import androidx.compose.foundation.gestures.awaitFirstDown
|
||||
import androidx.compose.foundation.layout.WindowInsets
|
||||
import androidx.compose.foundation.layout.systemGestures
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableFloatStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.input.pointer.PointerEventPass
|
||||
import androidx.compose.ui.input.pointer.pointerInput
|
||||
import androidx.compose.ui.layout.onGloballyPositioned
|
||||
import androidx.compose.ui.layout.positionInWindow
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.platform.LocalLayoutDirection
|
||||
import androidx.compose.ui.platform.LocalWindowInfo
|
||||
import androidx.compose.ui.unit.dp
|
||||
import kotlin.math.abs
|
||||
|
||||
/** How far past the system back-gesture zone a horizontal swipe is still ignored. */
|
||||
private val EDGE_MARGIN = 24.dp
|
||||
|
||||
/**
|
||||
* Swallows horizontal swipes that start near the screen's left or right edge,
|
||||
* before a swipe box below it sees them (#90). A back gesture started just
|
||||
* inside the system's gesture zone otherwise reaches the app as a row swipe.
|
||||
* Taps and vertical scrolls are left alone.
|
||||
*/
|
||||
@Composable
|
||||
fun Modifier.ignoringEdgeSwipes(): Modifier {
|
||||
val density = LocalDensity.current
|
||||
val layoutDirection = LocalLayoutDirection.current
|
||||
val windowWidth = LocalWindowInfo.current.containerSize.width
|
||||
val insets = WindowInsets.systemGestures
|
||||
val margin = with(density) { EDGE_MARGIN.roundToPx() }
|
||||
val leftEdge = insets.getLeft(density, layoutDirection) + margin
|
||||
val rightEdge = windowWidth - insets.getRight(density, layoutDirection) - margin
|
||||
var offsetX by remember { mutableFloatStateOf(0f) }
|
||||
return this
|
||||
.onGloballyPositioned { offsetX = it.positionInWindow().x }
|
||||
.pointerInput(leftEdge, rightEdge) {
|
||||
val slop = viewConfiguration.touchSlop
|
||||
awaitEachGesture {
|
||||
val down = awaitFirstDown(requireUnconsumed = false, pass = PointerEventPass.Initial)
|
||||
val x = offsetX + down.position.x
|
||||
if (x > leftEdge && x < rightEdge) return@awaitEachGesture
|
||||
var horizontal = false
|
||||
while (true) {
|
||||
val change = awaitPointerEvent(PointerEventPass.Initial).changes
|
||||
.firstOrNull { it.id == down.id } ?: break
|
||||
if (!horizontal) {
|
||||
val delta = change.position - down.position
|
||||
when {
|
||||
abs(delta.x) > slop -> horizontal = true
|
||||
abs(delta.y) > slop -> break
|
||||
}
|
||||
}
|
||||
if (horizontal) change.consume()
|
||||
if (!change.pressed) break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -112,6 +112,7 @@ import de.jeanlucmakiola.agendula.domain.TaskFilter
|
||||
import de.jeanlucmakiola.agendula.domain.TaskSection
|
||||
import de.jeanlucmakiola.agendula.domain.TaskSections
|
||||
import de.jeanlucmakiola.agendula.ui.common.LocalUse24HourFormat
|
||||
import de.jeanlucmakiola.agendula.ui.common.ignoringEdgeSwipes
|
||||
import de.jeanlucmakiola.agendula.ui.common.priorityAccent
|
||||
import de.jeanlucmakiola.agendula.ui.lists.ListEditorSheet
|
||||
import de.jeanlucmakiola.agendula.ui.lists.listWriteFailureMessage
|
||||
@@ -655,7 +656,7 @@ private fun TaskRow(
|
||||
|
||||
SwipeToDismissBox(
|
||||
state = dismissState,
|
||||
modifier = modifier.fillMaxWidth().padding(horizontal = 16.dp).then(gap),
|
||||
modifier = modifier.ignoringEdgeSwipes().fillMaxWidth().padding(horizontal = 16.dp).then(gap),
|
||||
// dismissDirection (not targetValue) tracks the live drag, so the colour +
|
||||
// icon reveal as you swipe rather than only once the threshold is crossed.
|
||||
backgroundContent = { SwipeBackground(dismissState.dismissDirection, task.isCompleted) },
|
||||
@@ -1037,7 +1038,7 @@ private fun SubtaskRow(
|
||||
val gap = if (lastInRun) Modifier else Modifier.padding(bottom = 2.dp)
|
||||
SwipeToDismissBox(
|
||||
state = dismissState,
|
||||
modifier = modifier.fillMaxWidth().padding(horizontal = 16.dp).then(gap),
|
||||
modifier = modifier.ignoringEdgeSwipes().fillMaxWidth().padding(horizontal = 16.dp).then(gap),
|
||||
backgroundContent = { SwipeBackground(dismissState.dismissDirection, task.isCompleted) },
|
||||
) {
|
||||
Surface(
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
package de.jeanlucmakiola.agendula.data.reminders
|
||||
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
class ExpandedTextTest {
|
||||
|
||||
@Test
|
||||
fun `no description means no expanded body`() {
|
||||
assertThat(expandedText("Due today", null)).isNull()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `description alone when there is no due line`() {
|
||||
assertThat(expandedText(null, "Buy oat milk")).isEqualTo("Buy oat milk")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `due line comes first, separated by a blank line`() {
|
||||
assertThat(expandedText("Due today", "Buy oat milk")).isEqualTo("Due today\n\nBuy oat milk")
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user