Compare commits
2 Commits
fix/180-de
...
02f697faf7
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
02f697faf7 | ||
| 2dd81f1327 |
@@ -1,7 +1,6 @@
|
|||||||
package de.jeanlucmakiola.calendula.data.calendar
|
package de.jeanlucmakiola.calendula.data.calendar
|
||||||
|
|
||||||
import de.jeanlucmakiola.floret.time.toKotlinInstantFromEpochMillis
|
import de.jeanlucmakiola.floret.time.toKotlinInstantFromEpochMillis
|
||||||
import android.provider.CalendarContract
|
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import de.jeanlucmakiola.calendula.domain.EventInstance
|
import de.jeanlucmakiola.calendula.domain.EventInstance
|
||||||
|
|
||||||
@@ -39,7 +38,5 @@ internal fun ColumnReader.toEventInstance(): EventInstance? {
|
|||||||
isAllDay = getInt(InstanceProjection.IDX_ALL_DAY) != 0,
|
isAllDay = getInt(InstanceProjection.IDX_ALL_DAY) != 0,
|
||||||
color = color,
|
color = color,
|
||||||
location = getString(InstanceProjection.IDX_LOCATION),
|
location = getString(InstanceProjection.IDX_LOCATION),
|
||||||
isDeclined = getInt(InstanceProjection.IDX_SELF_ATTENDEE_STATUS) ==
|
|
||||||
CalendarContract.Attendees.ATTENDEE_STATUS_DECLINED,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -52,7 +52,6 @@ internal object InstanceProjection {
|
|||||||
CalendarContract.Instances.EVENT_COLOR,
|
CalendarContract.Instances.EVENT_COLOR,
|
||||||
CalendarContract.Instances.CALENDAR_COLOR,
|
CalendarContract.Instances.CALENDAR_COLOR,
|
||||||
CalendarContract.Instances.EVENT_LOCATION,
|
CalendarContract.Instances.EVENT_LOCATION,
|
||||||
CalendarContract.Instances.SELF_ATTENDEE_STATUS,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const val IDX_INSTANCE_ID = 0
|
const val IDX_INSTANCE_ID = 0
|
||||||
@@ -65,7 +64,6 @@ internal object InstanceProjection {
|
|||||||
const val IDX_EVENT_COLOR = 7
|
const val IDX_EVENT_COLOR = 7
|
||||||
const val IDX_CALENDAR_COLOR = 8
|
const val IDX_CALENDAR_COLOR = 8
|
||||||
const val IDX_LOCATION = 9
|
const val IDX_LOCATION = 9
|
||||||
const val IDX_SELF_ATTENDEE_STATUS = 10
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal object EventDetailProjection {
|
internal object EventDetailProjection {
|
||||||
@@ -184,7 +182,6 @@ internal object SearchProjection {
|
|||||||
CalendarContract.Events.RDATE,
|
CalendarContract.Events.RDATE,
|
||||||
// Excerpted, not just filtered on: a hit has to show what it matched.
|
// Excerpted, not just filtered on: a hit has to show what it matched.
|
||||||
CalendarContract.Events.DESCRIPTION,
|
CalendarContract.Events.DESCRIPTION,
|
||||||
CalendarContract.Events.SELF_ATTENDEE_STATUS,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const val IDX_ID = 0
|
const val IDX_ID = 0
|
||||||
@@ -200,7 +197,6 @@ internal object SearchProjection {
|
|||||||
const val IDX_RRULE = 10
|
const val IDX_RRULE = 10
|
||||||
const val IDX_RDATE = 11
|
const val IDX_RDATE = 11
|
||||||
const val IDX_DESCRIPTION = 12
|
const val IDX_DESCRIPTION = 12
|
||||||
const val IDX_SELF_ATTENDEE_STATUS = 13
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
package de.jeanlucmakiola.calendula.data.calendar
|
package de.jeanlucmakiola.calendula.data.calendar
|
||||||
|
|
||||||
import android.provider.CalendarContract
|
|
||||||
import de.jeanlucmakiola.floret.time.toKotlinInstantFromEpochMillis
|
import de.jeanlucmakiola.floret.time.toKotlinInstantFromEpochMillis
|
||||||
import de.jeanlucmakiola.calendula.domain.EventInstance
|
import de.jeanlucmakiola.calendula.domain.EventInstance
|
||||||
import de.jeanlucmakiola.calendula.domain.ics.parseRfc2445DurationMillis
|
import de.jeanlucmakiola.calendula.domain.ics.parseRfc2445DurationMillis
|
||||||
@@ -43,7 +42,5 @@ internal fun ColumnReader.toSearchResult(): EventInstance? {
|
|||||||
location = getString(SearchProjection.IDX_LOCATION),
|
location = getString(SearchProjection.IDX_LOCATION),
|
||||||
isRecurring = !getString(SearchProjection.IDX_RRULE).isNullOrEmpty() ||
|
isRecurring = !getString(SearchProjection.IDX_RRULE).isNullOrEmpty() ||
|
||||||
!getString(SearchProjection.IDX_RDATE).isNullOrEmpty(),
|
!getString(SearchProjection.IDX_RDATE).isNullOrEmpty(),
|
||||||
isDeclined = getInt(SearchProjection.IDX_SELF_ATTENDEE_STATUS) ==
|
|
||||||
CalendarContract.Attendees.ATTENDEE_STATUS_DECLINED,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,16 +44,9 @@ class ProviderReminderInstanceSource @Inject constructor(
|
|||||||
// `visible` is the flag the app's one visibility model writes (#75).
|
// `visible` is the flag the app's one visibility model writes (#75).
|
||||||
// The status clause mirrors CalendarDataSource.instances: NULL means
|
// The status clause mirrors CalendarDataSource.instances: NULL means
|
||||||
// "normal", so a bare `!= CANCELED` would drop every ordinary event.
|
// "normal", so a bare `!= CANCELED` would drop every ordinary event.
|
||||||
// An invitation the user declined is answered — it stays on the calendar
|
|
||||||
// struck through, but it plans nothing (#180). NULL again means "no
|
|
||||||
// answer recorded", which is not a "no".
|
|
||||||
val selection = "${CalendarContract.Calendars.VISIBLE} = 1 AND " +
|
val selection = "${CalendarContract.Calendars.VISIBLE} = 1 AND " +
|
||||||
"(${CalendarContract.Instances.STATUS} IS NULL OR " +
|
"(${CalendarContract.Instances.STATUS} IS NULL OR " +
|
||||||
"${CalendarContract.Instances.STATUS} != " +
|
"${CalendarContract.Instances.STATUS} != ${CalendarContract.Events.STATUS_CANCELED})"
|
||||||
"${CalendarContract.Events.STATUS_CANCELED}) AND " +
|
|
||||||
"(${CalendarContract.Instances.SELF_ATTENDEE_STATUS} IS NULL OR " +
|
|
||||||
"${CalendarContract.Instances.SELF_ATTENDEE_STATUS} != " +
|
|
||||||
"${CalendarContract.Attendees.ATTENDEE_STATUS_DECLINED})"
|
|
||||||
return context.contentResolver.query(
|
return context.contentResolver.query(
|
||||||
uri, OCCURRENCE_PROJECTION, selection, null, null,
|
uri, OCCURRENCE_PROJECTION, selection, null, null,
|
||||||
)?.use { c ->
|
)?.use { c ->
|
||||||
|
|||||||
@@ -67,13 +67,6 @@ data class EventInstance(
|
|||||||
* Instances query already yields one row per occurrence.
|
* Instances query already yields one row per occurrence.
|
||||||
*/
|
*/
|
||||||
val isRecurring: Boolean = false,
|
val isRecurring: Boolean = false,
|
||||||
/**
|
|
||||||
* This device user answered "no" to the invitation
|
|
||||||
* (`Events.SELF_ATTENDEE_STATUS`). The event stays on the calendar — it is
|
|
||||||
* still an appointment someone expects an answer about — but every surface
|
|
||||||
* strikes it through, and it plans no reminders (#180).
|
|
||||||
*/
|
|
||||||
val isDeclined: Boolean = false,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -24,13 +24,11 @@ import androidx.compose.ui.Modifier
|
|||||||
import androidx.compose.ui.draw.alpha
|
import androidx.compose.ui.draw.alpha
|
||||||
import androidx.compose.ui.draw.clip
|
import androidx.compose.ui.draw.clip
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.text.AnnotatedString
|
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import de.jeanlucmakiola.calendula.R
|
import de.jeanlucmakiola.calendula.R
|
||||||
import de.jeanlucmakiola.calendula.domain.EventInstance
|
import de.jeanlucmakiola.calendula.domain.EventInstance
|
||||||
import de.jeanlucmakiola.calendula.ui.common.EventDimAlpha
|
import de.jeanlucmakiola.calendula.ui.common.EventDimAlpha
|
||||||
import de.jeanlucmakiola.calendula.ui.common.declinedTitle
|
|
||||||
import de.jeanlucmakiola.calendula.ui.common.LocalSoftenColors
|
import de.jeanlucmakiola.calendula.ui.common.LocalSoftenColors
|
||||||
import de.jeanlucmakiola.calendula.ui.common.LocalUse24HourFormat
|
import de.jeanlucmakiola.calendula.ui.common.LocalUse24HourFormat
|
||||||
import de.jeanlucmakiola.calendula.ui.common.eventAccent
|
import de.jeanlucmakiola.calendula.ui.common.eventAccent
|
||||||
@@ -131,8 +129,8 @@ internal fun AgendaEventRow(
|
|||||||
val title = event.title.ifBlank { stringResource(R.string.event_untitled) }
|
val title = event.title.ifBlank { stringResource(R.string.event_untitled) }
|
||||||
GroupedRow(
|
GroupedRow(
|
||||||
modifier = if (dimmed) modifier.alpha(EventDimAlpha) else modifier,
|
modifier = if (dimmed) modifier.alpha(EventDimAlpha) else modifier,
|
||||||
title = declinedTitle(title, event.isDeclined),
|
title = title,
|
||||||
summary = AnnotatedString(agendaTimeSummary(event, day, zone)),
|
summary = agendaTimeSummary(event, day, zone),
|
||||||
position = position,
|
position = position,
|
||||||
minHeight = 64.dp,
|
minHeight = 64.dp,
|
||||||
leading = {
|
leading = {
|
||||||
|
|||||||
@@ -1,10 +1,6 @@
|
|||||||
package de.jeanlucmakiola.calendula.ui.common
|
package de.jeanlucmakiola.calendula.ui.common
|
||||||
|
|
||||||
import androidx.compose.runtime.compositionLocalOf
|
import androidx.compose.runtime.compositionLocalOf
|
||||||
import androidx.compose.ui.text.AnnotatedString
|
|
||||||
import androidx.compose.ui.text.SpanStyle
|
|
||||||
import androidx.compose.ui.text.buildAnnotatedString
|
|
||||||
import androidx.compose.ui.text.style.TextDecoration
|
|
||||||
import kotlin.time.Instant
|
import kotlin.time.Instant
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -18,30 +14,3 @@ val LocalDimCutoff = compositionLocalOf<Instant?> { null }
|
|||||||
|
|
||||||
/** Opacity applied to a completed/past event chip when it is dimmed. */
|
/** Opacity applied to a completed/past event chip when it is dimmed. */
|
||||||
const val EventDimAlpha = 0.4f
|
const val EventDimAlpha = 0.4f
|
||||||
|
|
||||||
/**
|
|
||||||
* How a declined invitation's title is struck through (#180) — the mark every
|
|
||||||
* surface uses to say "you answered no", chosen over hiding the event because it
|
|
||||||
* is still something the organiser expects you at. Null for everything else, so
|
|
||||||
* it drops straight into a `Text`'s `textDecoration`.
|
|
||||||
*/
|
|
||||||
fun declinedDecoration(isDeclined: Boolean): TextDecoration? =
|
|
||||||
if (isDeclined) TextDecoration.LineThrough else null
|
|
||||||
|
|
||||||
/** [declinedDecoration] for the rows that take styled text rather than a `String`. */
|
|
||||||
fun declinedTitle(title: String, isDeclined: Boolean): AnnotatedString = if (isDeclined) {
|
|
||||||
AnnotatedString(title, SpanStyle(textDecoration = TextDecoration.LineThrough))
|
|
||||||
} else {
|
|
||||||
AnnotatedString(title)
|
|
||||||
}
|
|
||||||
|
|
||||||
/** [declinedTitle] over already-styled text, e.g. a search hit's marked runs. */
|
|
||||||
fun declinedTitle(title: AnnotatedString, isDeclined: Boolean): AnnotatedString =
|
|
||||||
if (!isDeclined) {
|
|
||||||
title
|
|
||||||
} else {
|
|
||||||
buildAnnotatedString {
|
|
||||||
append(title)
|
|
||||||
addStyle(SpanStyle(textDecoration = TextDecoration.LineThrough), 0, title.length)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -106,7 +106,6 @@ import de.jeanlucmakiola.calendula.ui.common.next
|
|||||||
import de.jeanlucmakiola.calendula.ui.common.LocalSoftenColors
|
import de.jeanlucmakiola.calendula.ui.common.LocalSoftenColors
|
||||||
import de.jeanlucmakiola.calendula.ui.common.eventFill
|
import de.jeanlucmakiola.calendula.ui.common.eventFill
|
||||||
import de.jeanlucmakiola.calendula.ui.common.eventInk
|
import de.jeanlucmakiola.calendula.ui.common.eventInk
|
||||||
import de.jeanlucmakiola.calendula.ui.common.declinedDecoration
|
|
||||||
import de.jeanlucmakiola.calendula.ui.common.rememberCalendarSlideSpec
|
import de.jeanlucmakiola.calendula.ui.common.rememberCalendarSlideSpec
|
||||||
import de.jeanlucmakiola.floret.locale.currentLocale
|
import de.jeanlucmakiola.floret.locale.currentLocale
|
||||||
import de.jeanlucmakiola.calendula.ui.common.LocalUse24HourFormat
|
import de.jeanlucmakiola.calendula.ui.common.LocalUse24HourFormat
|
||||||
@@ -526,7 +525,6 @@ private fun AllDayBar(
|
|||||||
maxLines = 1,
|
maxLines = 1,
|
||||||
overflow = TextOverflow.Ellipsis,
|
overflow = TextOverflow.Ellipsis,
|
||||||
color = eventInk(fill),
|
color = eventInk(fill),
|
||||||
textDecoration = declinedDecoration(event.isDeclined),
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -774,7 +772,6 @@ private fun EventBlock(
|
|||||||
maxLines = if (showTime) 1 else 2,
|
maxLines = if (showTime) 1 else 2,
|
||||||
overflow = TextOverflow.Ellipsis,
|
overflow = TextOverflow.Ellipsis,
|
||||||
color = eventInk(fill, alpha = 0.85f),
|
color = eventInk(fill, alpha = 0.85f),
|
||||||
textDecoration = declinedDecoration(block.event.isDeclined),
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
if (showTime) {
|
if (showTime) {
|
||||||
|
|||||||
@@ -138,7 +138,6 @@ import de.jeanlucmakiola.calendula.ui.common.CALENDAR_SWIPE_THRESHOLD
|
|||||||
import de.jeanlucmakiola.calendula.ui.common.CalendarView
|
import de.jeanlucmakiola.calendula.ui.common.CalendarView
|
||||||
import de.jeanlucmakiola.calendula.ui.common.IMPLEMENTED_VIEWS
|
import de.jeanlucmakiola.calendula.ui.common.IMPLEMENTED_VIEWS
|
||||||
import de.jeanlucmakiola.calendula.ui.common.EventDimAlpha
|
import de.jeanlucmakiola.calendula.ui.common.EventDimAlpha
|
||||||
import de.jeanlucmakiola.calendula.ui.common.declinedDecoration
|
|
||||||
import de.jeanlucmakiola.calendula.ui.common.LocalDimCutoff
|
import de.jeanlucmakiola.calendula.ui.common.LocalDimCutoff
|
||||||
import de.jeanlucmakiola.calendula.ui.common.LocalSoftenColors
|
import de.jeanlucmakiola.calendula.ui.common.LocalSoftenColors
|
||||||
import de.jeanlucmakiola.calendula.ui.common.eventAccent
|
import de.jeanlucmakiola.calendula.ui.common.eventAccent
|
||||||
@@ -2359,7 +2358,6 @@ private fun MonthBar(
|
|||||||
maxLines = 1,
|
maxLines = 1,
|
||||||
overflow = TextOverflow.Ellipsis,
|
overflow = TextOverflow.Ellipsis,
|
||||||
color = eventInk(fill),
|
color = eventInk(fill),
|
||||||
textDecoration = declinedDecoration(event.isDeclined),
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -85,7 +85,6 @@ import de.jeanlucmakiola.calendula.domain.SearchMonth
|
|||||||
import de.jeanlucmakiola.floret.identity.animateItemMotion
|
import de.jeanlucmakiola.floret.identity.animateItemMotion
|
||||||
import de.jeanlucmakiola.floret.identity.fadeThrough
|
import de.jeanlucmakiola.floret.identity.fadeThrough
|
||||||
import de.jeanlucmakiola.floret.identity.predictiveBack
|
import de.jeanlucmakiola.floret.identity.predictiveBack
|
||||||
import de.jeanlucmakiola.calendula.ui.common.declinedTitle
|
|
||||||
import de.jeanlucmakiola.floret.components.GroupedRow
|
import de.jeanlucmakiola.floret.components.GroupedRow
|
||||||
import de.jeanlucmakiola.floret.components.InlineTextField
|
import de.jeanlucmakiola.floret.components.InlineTextField
|
||||||
import de.jeanlucmakiola.floret.components.Position
|
import de.jeanlucmakiola.floret.components.Position
|
||||||
@@ -520,7 +519,7 @@ private fun SearchResultRow(
|
|||||||
// Faded like a past event anywhere else in the app — search reaches back
|
// Faded like a past event anywhere else in the app — search reaches back
|
||||||
// through the whole history.
|
// through the whole history.
|
||||||
modifier = if (hit.isPast) modifier.alpha(EventDimAlpha) else modifier,
|
modifier = if (hit.isPast) modifier.alpha(EventDimAlpha) else modifier,
|
||||||
title = declinedTitle(marked(event.title, hit.titleSpans, highlight), event.isDeclined),
|
title = marked(event.title, hit.titleSpans, highlight),
|
||||||
summary = searchSummary(hit, highlight),
|
summary = searchSummary(hit, highlight),
|
||||||
position = position,
|
position = position,
|
||||||
minHeight = 64.dp,
|
minHeight = 64.dp,
|
||||||
|
|||||||
@@ -90,7 +90,6 @@ import de.jeanlucmakiola.calendula.ui.common.CalendarFailure
|
|||||||
import de.jeanlucmakiola.calendula.ui.common.CalendarView
|
import de.jeanlucmakiola.calendula.ui.common.CalendarView
|
||||||
import de.jeanlucmakiola.calendula.ui.common.IMPLEMENTED_VIEWS
|
import de.jeanlucmakiola.calendula.ui.common.IMPLEMENTED_VIEWS
|
||||||
import de.jeanlucmakiola.calendula.ui.common.EventDimAlpha
|
import de.jeanlucmakiola.calendula.ui.common.EventDimAlpha
|
||||||
import de.jeanlucmakiola.calendula.ui.common.declinedDecoration
|
|
||||||
import de.jeanlucmakiola.calendula.ui.common.BlockTimeLabel
|
import de.jeanlucmakiola.calendula.ui.common.BlockTimeLabel
|
||||||
import de.jeanlucmakiola.calendula.ui.common.animatedBlockPlacement
|
import de.jeanlucmakiola.calendula.ui.common.animatedBlockPlacement
|
||||||
import de.jeanlucmakiola.calendula.ui.common.ghostAlpha
|
import de.jeanlucmakiola.calendula.ui.common.ghostAlpha
|
||||||
@@ -663,7 +662,6 @@ private fun AllDayBar(
|
|||||||
maxLines = 1,
|
maxLines = 1,
|
||||||
overflow = TextOverflow.Ellipsis,
|
overflow = TextOverflow.Ellipsis,
|
||||||
color = eventInk(fill),
|
color = eventInk(fill),
|
||||||
textDecoration = declinedDecoration(event.isDeclined),
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -942,7 +940,6 @@ private fun EventBlock(
|
|||||||
maxLines = titleMaxLines,
|
maxLines = titleMaxLines,
|
||||||
overflow = TextOverflow.Ellipsis,
|
overflow = TextOverflow.Ellipsis,
|
||||||
color = eventInk(fill, alpha = 0.85f),
|
color = eventInk(fill, alpha = 0.85f),
|
||||||
textDecoration = declinedDecoration(block.event.isDeclined),
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
if (showTime) {
|
if (showTime) {
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import android.os.Build
|
|||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.glance.GlanceTheme
|
import androidx.glance.GlanceTheme
|
||||||
import androidx.glance.material3.ColorProviders
|
import androidx.glance.material3.ColorProviders
|
||||||
import androidx.glance.text.TextDecoration
|
|
||||||
import de.jeanlucmakiola.calendula.ui.theme.CalendulaDarkFallback
|
import de.jeanlucmakiola.calendula.ui.theme.CalendulaDarkFallback
|
||||||
import de.jeanlucmakiola.calendula.ui.theme.CalendulaLightFallback
|
import de.jeanlucmakiola.calendula.ui.theme.CalendulaLightFallback
|
||||||
|
|
||||||
@@ -35,10 +34,3 @@ fun CalendulaGlanceTheme(content: @Composable () -> Unit) {
|
|||||||
}
|
}
|
||||||
GlanceTheme(colors = colors, content = content)
|
GlanceTheme(colors = colors, content = content)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Glance's counterpart to the app's `declinedDecoration`: a declined invitation
|
|
||||||
* reads the same on the home screen as it does inside the app (#180).
|
|
||||||
*/
|
|
||||||
fun glanceDeclinedDecoration(isDeclined: Boolean): TextDecoration =
|
|
||||||
if (isDeclined) TextDecoration.LineThrough else TextDecoration.None
|
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
package de.jeanlucmakiola.calendula.widget.agenda
|
package de.jeanlucmakiola.calendula.widget.agenda
|
||||||
|
|
||||||
import de.jeanlucmakiola.calendula.widget.glanceDeclinedDecoration
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.res.Configuration
|
import android.content.res.Configuration
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
@@ -25,7 +24,6 @@ import androidx.glance.appwidget.cornerRadius
|
|||||||
import androidx.glance.appwidget.lazy.LazyColumn
|
import androidx.glance.appwidget.lazy.LazyColumn
|
||||||
import androidx.glance.appwidget.lazy.items
|
import androidx.glance.appwidget.lazy.items
|
||||||
import androidx.glance.appwidget.provideContent
|
import androidx.glance.appwidget.provideContent
|
||||||
import androidx.glance.appwidget.updateAll
|
|
||||||
import androidx.glance.background
|
import androidx.glance.background
|
||||||
import androidx.glance.currentState
|
import androidx.glance.currentState
|
||||||
import androidx.glance.state.PreferencesGlanceStateDefinition
|
import androidx.glance.state.PreferencesGlanceStateDefinition
|
||||||
@@ -136,10 +134,14 @@ class AgendaWidget : GlanceAppWidget() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Re-reads the calendar and redraws the widget (header refresh button). */
|
/**
|
||||||
|
* Re-reads the calendar and redraws the widget (header refresh button). Targets
|
||||||
|
* the tapped widget's own id rather than `updateAll`, whose provider-name lookup
|
||||||
|
* is empty in a process a tap woke from cold — see `ShiftMonthAction` (#18).
|
||||||
|
*/
|
||||||
class RefreshAgendaAction : ActionCallback {
|
class RefreshAgendaAction : ActionCallback {
|
||||||
override suspend fun onAction(context: Context, glanceId: GlanceId, parameters: ActionParameters) {
|
override suspend fun onAction(context: Context, glanceId: GlanceId, parameters: ActionParameters) {
|
||||||
AgendaWidget().updateAll(context.applicationContext)
|
AgendaWidget().update(context.applicationContext, glanceId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -417,11 +419,7 @@ private fun EventRow(
|
|||||||
Text(
|
Text(
|
||||||
text = title,
|
text = title,
|
||||||
maxLines = 1,
|
maxLines = 1,
|
||||||
style = TextStyle(
|
style = TextStyle(color = titleColor, fontSize = metrics.eventTitle),
|
||||||
color = titleColor,
|
|
||||||
fontSize = metrics.eventTitle,
|
|
||||||
textDecoration = glanceDeclinedDecoration(event.isDeclined),
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
Text(
|
Text(
|
||||||
text = eventTimeSummary(context, event, day, is24Hour),
|
text = eventTimeSummary(context, event, day, is24Hour),
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
package de.jeanlucmakiola.calendula.widget.month
|
package de.jeanlucmakiola.calendula.widget.month
|
||||||
|
|
||||||
import de.jeanlucmakiola.calendula.widget.glanceDeclinedDecoration
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.res.Configuration
|
import android.content.res.Configuration
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
@@ -26,7 +25,6 @@ import androidx.glance.appwidget.action.actionStartActivity
|
|||||||
import androidx.glance.appwidget.cornerRadius
|
import androidx.glance.appwidget.cornerRadius
|
||||||
import androidx.glance.appwidget.provideContent
|
import androidx.glance.appwidget.provideContent
|
||||||
import androidx.glance.appwidget.state.updateAppWidgetState
|
import androidx.glance.appwidget.state.updateAppWidgetState
|
||||||
import androidx.glance.appwidget.updateAll
|
|
||||||
import androidx.glance.background
|
import androidx.glance.background
|
||||||
import androidx.glance.currentState
|
import androidx.glance.currentState
|
||||||
import androidx.glance.layout.Alignment
|
import androidx.glance.layout.Alignment
|
||||||
@@ -119,7 +117,17 @@ class MonthWidget : GlanceAppWidget() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Step the displayed month by the `delta` action parameter (±1). */
|
/**
|
||||||
|
* Step the displayed month by the `delta` action parameter (±1).
|
||||||
|
*
|
||||||
|
* Redrawn through [GlanceAppWidget.update] with the id the callback was handed,
|
||||||
|
* never `updateAll`. `updateAll` resolves its targets through the provider-name
|
||||||
|
* -> app-widget-id map Glance persists for the *receiver*, and in a process that
|
||||||
|
* has done nothing else yet — the one a tap wakes after a reboot — that lookup
|
||||||
|
* comes back empty and the redraw is dropped: the state write lands, nothing
|
||||||
|
* recomposes, and the arrows read as dead until something else opens the app and
|
||||||
|
* starts a session (#18). The tapped widget's own id needs no lookup.
|
||||||
|
*/
|
||||||
class ShiftMonthAction : ActionCallback {
|
class ShiftMonthAction : ActionCallback {
|
||||||
override suspend fun onAction(context: Context, glanceId: GlanceId, parameters: ActionParameters) {
|
override suspend fun onAction(context: Context, glanceId: GlanceId, parameters: ActionParameters) {
|
||||||
val delta = parameters[deltaKey] ?: 0
|
val delta = parameters[deltaKey] ?: 0
|
||||||
@@ -127,7 +135,7 @@ class ShiftMonthAction : ActionCallback {
|
|||||||
val cur = prefs[MONTH_INDEX_KEY] ?: currentMonthIndex(systemZone())
|
val cur = prefs[MONTH_INDEX_KEY] ?: currentMonthIndex(systemZone())
|
||||||
prefs[MONTH_INDEX_KEY] = cur + delta
|
prefs[MONTH_INDEX_KEY] = cur + delta
|
||||||
}
|
}
|
||||||
MonthWidget().updateAll(context.applicationContext)
|
MonthWidget().update(context.applicationContext, glanceId)
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
@@ -135,11 +143,11 @@ class ShiftMonthAction : ActionCallback {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Jump the displayed month back to the current month. */
|
/** Jump the displayed month back to the current month. See [ShiftMonthAction]. */
|
||||||
class ResetMonthAction : ActionCallback {
|
class ResetMonthAction : ActionCallback {
|
||||||
override suspend fun onAction(context: Context, glanceId: GlanceId, parameters: ActionParameters) {
|
override suspend fun onAction(context: Context, glanceId: GlanceId, parameters: ActionParameters) {
|
||||||
updateAppWidgetState(context, glanceId) { prefs -> prefs.remove(MONTH_INDEX_KEY) }
|
updateAppWidgetState(context, glanceId) { prefs -> prefs.remove(MONTH_INDEX_KEY) }
|
||||||
MonthWidget().updateAll(context.applicationContext)
|
MonthWidget().update(context.applicationContext, glanceId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -415,11 +423,7 @@ private fun SpanBar(event: EventInstance, dark: Boolean, soften: Boolean, width:
|
|||||||
Text(
|
Text(
|
||||||
text = event.title.ifBlank { context.getString(R.string.event_untitled) },
|
text = event.title.ifBlank { context.getString(R.string.event_untitled) },
|
||||||
maxLines = 1,
|
maxLines = 1,
|
||||||
style = TextStyle(
|
style = TextStyle(color = ColorProvider(eventInk(fill)), fontSize = 9.sp),
|
||||||
color = ColorProvider(eventInk(fill)),
|
|
||||||
fontSize = 9.sp,
|
|
||||||
textDecoration = glanceDeclinedDecoration(event.isDeclined),
|
|
||||||
),
|
|
||||||
modifier = GlanceModifier.padding(horizontal = 3.dp),
|
modifier = GlanceModifier.padding(horizontal = 3.dp),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
package de.jeanlucmakiola.calendula.data.calendar
|
package de.jeanlucmakiola.calendula.data.calendar
|
||||||
|
|
||||||
import android.provider.CalendarContract
|
|
||||||
import com.google.common.truth.Truth.assertThat
|
import com.google.common.truth.Truth.assertThat
|
||||||
import kotlin.time.Instant
|
import kotlin.time.Instant
|
||||||
import org.junit.jupiter.api.Test
|
import org.junit.jupiter.api.Test
|
||||||
@@ -18,7 +17,6 @@ class InstanceMapperTest {
|
|||||||
eventColor: Any? = null,
|
eventColor: Any? = null,
|
||||||
calendarColor: Int = 0xFFAABBCC.toInt(),
|
calendarColor: Int = 0xFFAABBCC.toInt(),
|
||||||
location: String? = null,
|
location: String? = null,
|
||||||
selfAttendeeStatus: Int = CalendarContract.Attendees.ATTENDEE_STATUS_NONE,
|
|
||||||
): MapColumnReader = MapColumnReader(
|
): MapColumnReader = MapColumnReader(
|
||||||
InstanceProjection.IDX_INSTANCE_ID to instanceId,
|
InstanceProjection.IDX_INSTANCE_ID to instanceId,
|
||||||
InstanceProjection.IDX_EVENT_ID to eventId,
|
InstanceProjection.IDX_EVENT_ID to eventId,
|
||||||
@@ -30,7 +28,6 @@ class InstanceMapperTest {
|
|||||||
InstanceProjection.IDX_EVENT_COLOR to eventColor,
|
InstanceProjection.IDX_EVENT_COLOR to eventColor,
|
||||||
InstanceProjection.IDX_CALENDAR_COLOR to calendarColor,
|
InstanceProjection.IDX_CALENDAR_COLOR to calendarColor,
|
||||||
InstanceProjection.IDX_LOCATION to location,
|
InstanceProjection.IDX_LOCATION to location,
|
||||||
InstanceProjection.IDX_SELF_ATTENDEE_STATUS to selfAttendeeStatus,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -93,20 +90,4 @@ class InstanceMapperTest {
|
|||||||
val inst = reader(location = "Berlin").toEventInstance()
|
val inst = reader(location = "Berlin").toEventInstance()
|
||||||
assertThat(inst!!.location).isEqualTo("Berlin")
|
assertThat(inst!!.location).isEqualTo("Berlin")
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `a declined invitation is marked, any other answer is not`() {
|
|
||||||
assertThat(reader().toEventInstance()!!.isDeclined).isFalse()
|
|
||||||
assertThat(
|
|
||||||
reader(selfAttendeeStatus = CalendarContract.Attendees.ATTENDEE_STATUS_DECLINED)
|
|
||||||
.toEventInstance()!!.isDeclined,
|
|
||||||
).isTrue()
|
|
||||||
listOf(
|
|
||||||
CalendarContract.Attendees.ATTENDEE_STATUS_ACCEPTED,
|
|
||||||
CalendarContract.Attendees.ATTENDEE_STATUS_TENTATIVE,
|
|
||||||
CalendarContract.Attendees.ATTENDEE_STATUS_INVITED,
|
|
||||||
).forEach { status ->
|
|
||||||
assertThat(reader(selfAttendeeStatus = status).toEventInstance()!!.isDeclined).isFalse()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user