Merge pull request #1722 from Naveen3Singh/fix_event_offset

Properly handle repeating events
This commit is contained in:
Tibor Kaputa 2022-05-21 17:09:11 +02:00 committed by GitHub
commit 99f0e0f21e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 44 additions and 31 deletions

View File

@ -2,4 +2,4 @@ package com.simplemobiletools.calendar.pro.extensions
import android.util.Range
fun Range<Int>.touch(other: Range<Int>) = (upper >= other.lower && lower <= other.upper) || (other.upper >= lower && other.lower <= upper)
fun Range<Int>.intersects(other: Range<Int>) = (upper >= other.lower && lower <= other.upper) || (other.upper >= lower && other.lower <= upper)

View File

@ -103,7 +103,7 @@ class MonthFragment : Fragment(), MonthlyCalendar {
contentDescription = text
if (activity != null) {
setTextColor(activity!!.getProperTextColor())
setTextColor(requireActivity().getProperTextColor())
}
}
updateDays(days)

View File

@ -35,6 +35,9 @@ import kotlinx.android.synthetic.main.week_event_marker.view.*
import org.joda.time.DateTime
import org.joda.time.Days
import java.util.*
import kotlin.math.max
import kotlin.math.min
import kotlin.math.roundToInt
class WeekFragment : Fragment(), WeeklyCalendar {
private val WEEKLY_EVENT_ID_LABEL = "event_id_label"
@ -132,7 +135,7 @@ class WeekFragment : Fragment(), WeeklyCalendar {
}
val initialScrollY = (rowHeight * config.startWeeklyAt).toInt()
updateScrollY(Math.max(listener?.getCurrScrollY() ?: 0, initialScrollY))
updateScrollY(max(listener?.getCurrScrollY() ?: 0, initialScrollY))
}
wasFragmentInit = true
@ -361,7 +364,7 @@ class WeekFragment : Fragment(), WeeklyCalendar {
prevScaleSpanY = detector.currentSpanY
val wantedFactor = config.weeklyViewItemHeightMultiplier - (SCALE_RANGE * percent)
var newFactor = Math.max(Math.min(wantedFactor, MAX_SCALE_FACTOR), MIN_SCALE_FACTOR)
var newFactor = max(min(wantedFactor, MAX_SCALE_FACTOR), MIN_SCALE_FACTOR)
if (scrollView.height > defaultRowHeight * newFactor * 24) {
newFactor = scrollView.height / 24f / defaultRowHeight
}
@ -422,7 +425,7 @@ class WeekFragment : Fragment(), WeeklyCalendar {
rowHeight = context?.getWeeklyViewItemHeight() ?: return
val oneDp = res.getDimension(R.dimen.one_dp).toInt()
val fullHeight = Math.max(rowHeight.toInt() * 24, scrollView.height + oneDp)
val fullHeight = max(rowHeight.toInt() * 24, scrollView.height + oneDp)
scrollView.layoutParams.height = fullHeight - oneDp
mView.week_horizontal_grid_holder.layoutParams.height = fullHeight
mView.week_events_columns_holder.layoutParams.height = fullHeight
@ -440,7 +443,7 @@ class WeekFragment : Fragment(), WeeklyCalendar {
val minuteHeight = rowHeight / 60
val minimalHeight = res.getDimension(R.dimen.weekly_view_minimal_event_height).toInt()
val density = Math.round(res.displayMetrics.density)
val density = res.displayMetrics.density.roundToInt()
for (event in events) {
val startDateTime = Formatter.getDateTimeFromTS(event.startTS)
@ -493,7 +496,7 @@ class WeekFragment : Fragment(), WeeklyCalendar {
eventsCollisionChecked.add(eventId)
val eventWeeklyViewsToCheck = eventDayList.filter { !eventsCollisionChecked.contains(it.key) }
for ((toCheckId, eventWeeklyViewToCheck) in eventWeeklyViewsToCheck) {
val areTouching = eventWeeklyView.range.touch(eventWeeklyViewToCheck.range)
val areTouching = eventWeeklyView.range.intersects(eventWeeklyViewToCheck.range)
val doHaveCommonMinutes = if (areTouching) {
eventWeeklyView.range.upper > eventWeeklyViewToCheck.range.lower || (eventWeeklyView.range.lower == eventWeeklyView.range.upper &&
eventWeeklyView.range.upper == eventWeeklyViewToCheck.range.lower)
@ -507,7 +510,7 @@ class WeekFragment : Fragment(), WeeklyCalendar {
val slotRange = Array(eventWeeklyView.slot_max) { it + 1 }
val collisionEventWeeklyViews = eventDayList.filter { eventWeeklyView.collisions.contains(it.key) }
for ((_, collisionEventWeeklyView) in collisionEventWeeklyViews) {
if (collisionEventWeeklyView.range.touch(eventWeeklyViewToCheck.range)) {
if (collisionEventWeeklyView.range.intersects(eventWeeklyViewToCheck.range)) {
slotRange[collisionEventWeeklyView.slot - 1] = nextSlot
}
}
@ -723,8 +726,8 @@ class WeekFragment : Fragment(), WeeklyCalendar {
val startDateTime = Formatter.getDateTimeFromTS(event.startTS)
val endDateTime = Formatter.getDateTimeFromTS(event.endTS)
val minTS = Math.max(startDateTime.seconds(), weekTimestamp)
val maxTS = Math.min(endDateTime.seconds(), weekTimestamp + 2 * WEEK_SECONDS)
val minTS = max(startDateTime.seconds(), weekTimestamp)
val maxTS = min(endDateTime.seconds(), weekTimestamp + 2 * WEEK_SECONDS)
// fix a visual glitch with all-day events or events lasting multiple days starting at midnight on monday, being shown the previous week too
if (minTS == maxTS && (minTS - weekTimestamp == WEEK_SECONDS.toLong())) {

View File

@ -8,8 +8,7 @@ import com.simplemobiletools.calendar.pro.interfaces.MonthlyCalendar
import com.simplemobiletools.calendar.pro.models.DayMonthly
import com.simplemobiletools.calendar.pro.models.Event
import org.joda.time.DateTime
import java.util.*
import kotlin.collections.ArrayList
import kotlin.math.min
class MonthlyCalendarImpl(val callback: MonthlyCalendar, val context: Context) {
private val DAYS_CNT = 42
@ -83,25 +82,25 @@ class MonthlyCalendarImpl(val callback: MonthlyCalendar, val context: Context) {
}
}
// it works more often than not, dont touch
// it works more often than not, don't touch
private fun markDaysWithEvents(days: ArrayList<DayMonthly>) {
val dayEvents = HashMap<String, ArrayList<Event>>()
mEvents.forEach {
val startDateTime = Formatter.getDateTimeFromTS(it.startTS)
val endDateTime = Formatter.getDateTimeFromTS(it.endTS)
mEvents.forEach { event ->
val startDateTime = Formatter.getDateTimeFromTS(event.startTS)
val endDateTime = Formatter.getDateTimeFromTS(event.endTS)
val endCode = Formatter.getDayCodeFromDateTime(endDateTime)
var currDay = startDateTime
var dayCode = Formatter.getDayCodeFromDateTime(currDay)
var currDayEvents = dayEvents[dayCode] ?: ArrayList()
currDayEvents.add(it)
currDayEvents.add(event)
dayEvents[dayCode] = currDayEvents
while (Formatter.getDayCodeFromDateTime(currDay) != endCode) {
currDay = currDay.plusDays(1)
dayCode = Formatter.getDayCodeFromDateTime(currDay)
currDayEvents = dayEvents[dayCode] ?: ArrayList()
currDayEvents.add(it)
currDayEvents.add(event)
dayEvents[dayCode] = currDayEvents
}
}
@ -114,7 +113,7 @@ class MonthlyCalendarImpl(val callback: MonthlyCalendar, val context: Context) {
private fun isToday(targetDate: DateTime, curDayInMonth: Int): Boolean {
val targetMonthDays = targetDate.dayOfMonth().maximumValue
return targetDate.withDayOfMonth(Math.min(curDayInMonth, targetMonthDays)).toString(Formatter.DAYCODE_PATTERN) == mToday
return targetDate.withDayOfMonth(min(curDayInMonth, targetMonthDays)).toString(Formatter.DAYCODE_PATTERN) == mToday
}
private val monthName: String

View File

@ -23,6 +23,8 @@ import com.simplemobiletools.commons.helpers.LOWER_ALPHA
import com.simplemobiletools.commons.helpers.MEDIUM_ALPHA
import org.joda.time.DateTime
import org.joda.time.Days
import kotlin.math.max
import kotlin.math.min
// used in the Monthly view fragment, 1 view per screen
class MonthView(context: Context, attrs: AttributeSet, defStyle: Int) : View(context, attrs, defStyle) {
@ -113,16 +115,15 @@ class MonthView(context: Context, attrs: AttributeSet, defStyle: Int) : View(con
}
private fun groupAllEvents() {
days.forEach {
val day = it
day.dayEvents.forEach {
val event = it
days.forEach { day ->
day.dayEvents.forEach { event ->
// make sure we properly handle events lasting multiple days and repeating ones
val lastEvent = allEvents.lastOrNull { it.id == event.id }
val daysCnt = getEventLastingDaysCount(event)
val validDayEvent = isDayValid(event, day.code)
if ((lastEvent == null || lastEvent.startDayIndex + daysCnt <= day.indexOnMonthView) && !validDayEvent) {
if ((lastEvent == null || lastEvent.startDayIndex + daysCnt <= findLastDay(event).indexOnMonthView) && !validDayEvent) {
val monthViewEvent = MonthViewEvent(
event.id!!, event.title, event.startTS, event.endTS, event.color, day.indexOnMonthView,
daysCnt, day.indexOnMonthView, event.getIsAllDay(), event.isPastEvent, event.isTask(), event.isTaskCompleted()
@ -133,8 +134,9 @@ class MonthView(context: Context, attrs: AttributeSet, defStyle: Int) : View(con
}
allEvents =
allEvents.asSequence().sortedWith(compareBy({ -it.daysCnt }, { !it.isAllDay }, { it.startTS }, { it.endTS }, { it.startDayIndex }, { it.title }))
.toMutableList() as ArrayList<MonthViewEvent>
allEvents.asSequence().sortedWith(
compareBy({ -it.daysCnt }, { !it.isAllDay }, { it.startTS }, { it.endTS }, { it.startDayIndex }, { it.title })
).toMutableList() as ArrayList<MonthViewEvent>
}
override fun onDraw(canvas: Canvas) {
@ -255,8 +257,8 @@ class MonthView(context: Context, attrs: AttributeSet, defStyle: Int) : View(con
private fun drawEvent(event: MonthViewEvent, canvas: Canvas) {
var verticalOffset = 0
for (i in 0 until Math.min(event.daysCnt, 7 - event.startDayIndex % 7)) {
verticalOffset = Math.max(verticalOffset, dayVerticalOffsets[event.startDayIndex + i])
for (i in 0 until min(event.daysCnt, 7 - event.startDayIndex % 7)) {
verticalOffset = max(verticalOffset, dayVerticalOffsets[event.startDayIndex + i])
}
val xPos = event.startDayIndex % 7 * dayWidth + horizontalOffset
val yPos = (event.startDayIndex / 7) * dayHeight
@ -285,7 +287,7 @@ class MonthView(context: Context, attrs: AttributeSet, defStyle: Int) : View(con
}
val startDayIndex = days[event.originalStartDayIndex]
val endDayIndex = days[Math.min(event.startDayIndex + event.daysCnt - 1, 41)]
val endDayIndex = days[min(event.startDayIndex + event.daysCnt - 1, 41)]
bgRectF.set(bgLeft, bgTop, bgRight, bgBottom)
canvas.drawRoundRect(bgRectF, BG_CORNER_RADIUS, BG_CORNER_RADIUS, getEventBackgroundColor(event, startDayIndex, endDayIndex))
@ -301,7 +303,7 @@ class MonthView(context: Context, attrs: AttributeSet, defStyle: Int) : View(con
drawEventTitle(event, canvas, xPos + taskIconWidth, yPos + verticalOffset, bgRight - bgLeft - smallPadding - taskIconWidth, specificEventTitlePaint)
for (i in 0 until Math.min(event.daysCnt, 7 - event.startDayIndex % 7)) {
for (i in 0 until min(event.daysCnt, 7 - event.startDayIndex % 7)) {
dayVerticalOffsets.put(event.startDayIndex + i, verticalOffset + eventTitleHeight + smallPadding * 2)
}
}
@ -429,6 +431,12 @@ class MonthView(context: Context, attrs: AttributeSet, defStyle: Int) : View(con
return event.startTS != event.endTS && Formatter.getDateTimeFromTS(event.endTS) == Formatter.getDateTimeFromTS(date.seconds()).withTimeAtStartOfDay()
}
private fun findLastDay(event: Event): DayMonthly {
return days.last { day ->
day.dayEvents.find { it.id == event.id } != null
}
}
fun togglePrintMode() {
isPrintVersion = !isPrintVersion
textColor = if (isPrintVersion) {

View File

@ -59,7 +59,10 @@ class MonthViewWrapper(context: Context, attrs: AttributeSet, defStyle: Int) : F
continue
}
child.measure(MeasureSpec.makeMeasureSpec(dayWidth.toInt(), MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(dayHeight.toInt(), MeasureSpec.EXACTLY))
child.measure(
MeasureSpec.makeMeasureSpec(dayWidth.toInt(), MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(dayHeight.toInt(), MeasureSpec.EXACTLY)
)
val childLeft = x * dayWidth + horizontalOffset - child.translationX
val childTop = y * dayHeight + weekDaysLetterHeight - child.translationY