Skip to content

Commit

Permalink
feature: introduce TimeSpan class and avoid issue drawing background.
Browse files Browse the repository at this point in the history
  • Loading branch information
tobiasschuerg committed Feb 11, 2022
1 parent 57522b1 commit 47caba5
Show file tree
Hide file tree
Showing 9 changed files with 127 additions and 45 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package de.tobiasschuerg.weekview.sample
import android.graphics.Color
import de.tobiasschuerg.weekview.data.Event
import de.tobiasschuerg.weekview.data.WeekData
import de.tobiasschuerg.weekview.util.TimeSpan
import org.threeten.bp.DayOfWeek
import org.threeten.bp.LocalDate
import org.threeten.bp.LocalTime
Expand Down Expand Up @@ -52,8 +53,7 @@ object EventCreator {
title = name,
shortTitle = name,
subTitle = subTitle,
startTime = startTime,
endTime = endTime,
timeSpan = TimeSpan(startTime, endTime),
textColor = Color.WHITE,
backgroundColor = randomColor()
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,10 @@ import androidx.appcompat.app.AppCompatActivity
import com.jakewharton.threetenabp.AndroidThreeTen
import de.tobiasschuerg.weekview.data.Event
import de.tobiasschuerg.weekview.data.EventConfig
import de.tobiasschuerg.weekview.util.TimeSpan
import de.tobiasschuerg.weekview.view.EventView
import de.tobiasschuerg.weekview.view.WeekView
import org.threeten.bp.Duration
import org.threeten.bp.LocalDate
import org.threeten.bp.LocalTime
import org.threeten.bp.temporal.ChronoUnit
Expand All @@ -38,8 +40,7 @@ class SampleActivity : AppCompatActivity() {
date = LocalDate.now(),
title = "Current hour",
shortTitle = "Now",
startTime = LocalTime.now().truncatedTo(ChronoUnit.HOURS),
endTime = LocalTime.now().truncatedTo(ChronoUnit.HOURS).plusMinutes(59),
timeSpan = TimeSpan.of(LocalTime.now().truncatedTo(ChronoUnit.HOURS), Duration.ofHours(1)),
backgroundColor = Color.RED,
textColor = Color.WHITE
)
Expand Down
7 changes: 3 additions & 4 deletions library/src/main/java/de/tobiasschuerg/weekview/data/Event.kt
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package de.tobiasschuerg.weekview.data

import de.tobiasschuerg.weekview.util.TimeSpan
import org.threeten.bp.Duration
import org.threeten.bp.LocalDate
import org.threeten.bp.LocalTime

sealed class Event {

Expand All @@ -18,16 +18,15 @@ sealed class Event {
override val shortTitle: String,
val subTitle: String? = null,

val startTime: LocalTime,
val endTime: LocalTime,
val timeSpan: TimeSpan,

val upperText: String? = null,
val lowerText: String? = null,

val textColor: Int,
val backgroundColor: Int
) : Event() {
val duration: Duration = Duration.between(startTime, endTime)
val duration: Duration = timeSpan.duration
}

data class AllDay(
Expand Down
24 changes: 15 additions & 9 deletions library/src/main/java/de/tobiasschuerg/weekview/data/WeekData.kt
Original file line number Diff line number Diff line change
@@ -1,16 +1,22 @@
package de.tobiasschuerg.weekview.data

import de.tobiasschuerg.weekview.util.TimeSpan
import org.threeten.bp.LocalTime

class WeekData {

private val singleEvents: MutableList<Event.Single> = mutableListOf()

private val allDays: MutableList<Event.AllDay> = mutableListOf()

var earliestStart: LocalTime = LocalTime.MAX

var latestEnd: LocalTime = LocalTime.MIN
private var earliestStart: LocalTime? = null
private var latestEnd: LocalTime? = null

fun getTimeSpan(): TimeSpan? {
return if (earliestStart != null && latestEnd != null) {
TimeSpan(earliestStart!!, latestEnd!!)
} else {
null
}
}

fun add(item: Event.AllDay) {
allDays.add(item)
Expand All @@ -19,12 +25,12 @@ class WeekData {
fun add(item: Event.Single) {
singleEvents.add(item)

if (item.startTime.isBefore(earliestStart)) {
earliestStart = item.startTime
if (earliestStart == null || item.timeSpan.start.isBefore(earliestStart)) {
earliestStart = item.timeSpan.start
}

if (item.endTime.isAfter(latestEnd)) {
latestEnd = item.endTime
if (latestEnd == null || item.timeSpan.endExclusive.isAfter(latestEnd)) {
latestEnd = item.timeSpan.endExclusive
}
}

Expand Down
27 changes: 27 additions & 0 deletions library/src/main/java/de/tobiasschuerg/weekview/util/TimeSpan.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package de.tobiasschuerg.weekview.util

import org.threeten.bp.Duration
import org.threeten.bp.LocalTime

/**
* Holds a duration of time.
*/
data class TimeSpan(
val start: LocalTime,
val endExclusive: LocalTime
) {

init {
require(start.isBefore(endExclusive)) {
"Start time $start must be before end time $endExclusive!"
}
}

val duration: Duration by lazy { Duration.between(start, endExclusive) }

companion object {
fun of(start: LocalTime, duration: Duration): TimeSpan {
return TimeSpan(start, start.plus(duration))
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -100,14 +100,14 @@ class EventView(

// start time
if (config.showTimeStart) {
val startText = event.startTime.toLocalString()
val startText = event.timeSpan.start.toLocalString()
textPaint.getTextBounds(startText, 0, startText.length, textBounds)
canvas.drawText(startText, (textBounds.left + paddingLeft).toFloat(), (textBounds.height() + paddingTop).toFloat(), textPaint)
}

// end time
if (config.showTimeEnd) {
val endText = event.endTime.toLocalString()
val endText = event.timeSpan.endExclusive.toLocalString()
textPaint.getTextBounds(endText, 0, endText.length, textBounds)
canvas.drawText(endText, (width - (textBounds.right + paddingRight)).toFloat(), (height - paddingBottom).toFloat(), textPaint)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import android.graphics.Rect
import android.util.Log
import android.view.View
import de.tobiasschuerg.weekview.util.DayOfWeekUtil
import de.tobiasschuerg.weekview.util.TimeSpan
import de.tobiasschuerg.weekview.util.dipToPixelF
import de.tobiasschuerg.weekview.util.dipToPixelI
import de.tobiasschuerg.weekview.util.toLocalString
Expand Down Expand Up @@ -113,7 +114,13 @@ internal class WeekBackgroundView constructor(context: Context) : View(context)

// final String timeString = localTime.format(DateTimeFormatter.ofLocalizedTime(FormatStyle.SHORT));
val timeString = localTime.toLocalString()
drawMultiLineText(this, timeString, context.dipToPixelF(25f), y + context.dipToPixelF(20f), mPaintLabels)
drawMultiLineText(
this,
timeString,
context.dipToPixelF(25f),
y + context.dipToPixelF(20f),
mPaintLabels
)

last = localTime
localTime = localTime.plusHours(1)
Expand Down Expand Up @@ -151,10 +158,21 @@ internal class WeekBackgroundView constructor(context: Context) : View(context)
private fun Canvas.drawWeekDayName(day: DayOfWeek, column: Int) {
val shortName = day.getDisplayName(TextStyle.SHORT, Locale.getDefault())
val xLabel = (getColumnStart(column, false) + getColumnEnd(column, false)) / 2
drawText(shortName, xLabel.toFloat(), topOffsetPx / 2 + mPaintLabels.descent(), mPaintLabels)
}

private fun drawMultiLineText(canvas: Canvas, text: String, initialX: Float, initialY: Float, paint: Paint) {
drawText(
shortName,
xLabel.toFloat(),
topOffsetPx / 2 + mPaintLabels.descent(),
mPaintLabels
)
}

private fun drawMultiLineText(
canvas: Canvas,
text: String,
initialX: Float,
initialY: Float,
paint: Paint
) {
var currentY = initialY
text.split(" ")
.dropLastWhile(String::isEmpty)
Expand Down Expand Up @@ -190,27 +208,26 @@ internal class WeekBackgroundView constructor(context: Context) : View(context)
}

override fun onMeasure(widthMeasureSpec: Int, hms: Int) {
val height = topOffsetPx + context.dipToPixelF(getDurationMinutes() * scalingFactor) + paddingBottom
val heightMeasureSpec2 = MeasureSpec.makeMeasureSpec(height.roundToInt(), MeasureSpec.EXACTLY)
val height =
topOffsetPx + context.dipToPixelF(getDurationMinutes() * scalingFactor) + paddingBottom
val heightMeasureSpec2 =
MeasureSpec.makeMeasureSpec(height.roundToInt(), MeasureSpec.EXACTLY)
super.onMeasure(widthMeasureSpec, heightMeasureSpec2)
}

fun setScreenshotMode(screenshotMode: Boolean) {
isInScreenshotMode = screenshotMode
}

fun updateTimes(startTime: LocalTime, endTime: LocalTime) {
if (startTime.isAfter(endTime)) {
throw IllegalArgumentException("Start time $startTime must be before end time $endTime")
}
fun updateTimes(timeSpan: TimeSpan) {
var timesHaveChanged = false
if (startTime.isBefore(this.startTime)) {
this.startTime = startTime.truncatedTo(ChronoUnit.HOURS)
if (timeSpan.start.isBefore(startTime)) {
startTime = timeSpan.start.truncatedTo(ChronoUnit.HOURS)
timesHaveChanged = true
}
if (endTime.isAfter(this.endTime)) {
if (endTime.isBefore(LocalTime.of(23, 0))) {
this.endTime = endTime.truncatedTo(ChronoUnit.HOURS).plusHours(1)
if (timeSpan.endExclusive.isAfter(endTime)) {
if (timeSpan.endExclusive.isBefore(LocalTime.of(23, 0))) {
this.endTime = timeSpan.endExclusive.truncatedTo(ChronoUnit.HOURS).plusHours(1)
} else {
this.endTime = LocalTime.MAX
}
Expand Down
22 changes: 12 additions & 10 deletions library/src/main/java/de/tobiasschuerg/weekview/view/WeekView.kt
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,9 @@ class WeekView(context: Context, attributeSet: AttributeSet) :
fun addEvents(weekData: WeekData) {
Log.d(TAG, "Adding ${weekData.getSingleEvents().size} weekData to week view")

backgroundView.updateTimes(weekData.earliestStart, weekData.latestEnd)
weekData.getTimeSpan()?.let {
backgroundView.updateTimes(it)
}

for (event in weekData.getSingleEvents()) {
addEvent(event)
Expand Down Expand Up @@ -158,12 +160,12 @@ class WeekView(context: Context, attributeSet: AttributeSet) :
}

val lv = EventView(context, event, eventConfig, weekViewConfig.scalingFactor)
backgroundView.updateTimes(event.startTime, event.endTime)
backgroundView.updateTimes(event.timeSpan)

// mark active event
val now = LocalTime.now()
if (LocalDate.now().dayOfWeek == event.date.dayOfWeek && // this day
event.startTime < now && event.endTime > now
event.timeSpan.start < now && event.timeSpan.endExclusive > now
) {
lv.animation = Animation.createBlinkAnimation()
}
Expand Down Expand Up @@ -246,7 +248,7 @@ class WeekView(context: Context, attributeSet: AttributeSet) :

eventView.scalingFactor = weekViewConfig.scalingFactor
val startTime = backgroundView.startTime
val lessonStart = eventView.event.startTime
val lessonStart = eventView.event.timeSpan.start
val offset = Duration.between(startTime, lessonStart)

val yOffset = offset.toMinutes() * weekViewConfig.scalingFactor
Expand All @@ -262,16 +264,16 @@ class WeekView(context: Context, attributeSet: AttributeSet) :
}

private fun overlaps(left: EventView, right: EventView): Boolean {
val rightStartsAfterLeftStarts = right.event.startTime >= left.event.startTime
val rightStartsBeforeLeftEnds = right.event.startTime < left.event.endTime
val rightStartsAfterLeftStarts = right.event.timeSpan.start >= left.event.timeSpan.start
val rightStartsBeforeLeftEnds = right.event.timeSpan.start < left.event.timeSpan.endExclusive
val lessonStartsWithing = rightStartsAfterLeftStarts && rightStartsBeforeLeftEnds

val leftStartsBeforeRightEnds = left.event.startTime < right.event.endTime
val rightEndsBeforeOrWithLeftEnds = right.event.endTime <= left.event.endTime
val leftStartsBeforeRightEnds = left.event.timeSpan.start < right.event.timeSpan.endExclusive
val rightEndsBeforeOrWithLeftEnds = right.event.timeSpan.endExclusive <= left.event.timeSpan.endExclusive
val lessonEndsWithing = leftStartsBeforeRightEnds && rightEndsBeforeOrWithLeftEnds

val leftStartsAfterRightStarts = left.event.startTime > right.event.startTime
val rightEndsAfterLeftEnds = right.event.endTime > left.event.endTime
val leftStartsAfterRightStarts = left.event.timeSpan.start > right.event.timeSpan.start
val rightEndsAfterLeftEnds = right.event.timeSpan.start > left.event.timeSpan.endExclusive
val lessonWithin = leftStartsAfterRightStarts && rightEndsAfterLeftEnds

return lessonStartsWithing || lessonEndsWithing || lessonWithin
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package de.tobiasschuerg.weekview.util

import org.junit.Assert.assertEquals
import org.junit.Test
import org.threeten.bp.Duration
import org.threeten.bp.LocalTime

/**
* Created by Tobias Schrg on 11.02.2022.
*/
class TimeSpanTest {

@Test
fun testDuration() {
val timeSpan = TimeSpan(
start = LocalTime.of(10, 15),
endExclusive = LocalTime.of(10, 45)
)
assertEquals(Duration.ofMinutes(30), timeSpan.duration)
}

@Test
fun testDuration2() {
val timeSpan = TimeSpan.of(
start = LocalTime.of(10, 15),
duration = Duration.ofMinutes(30)
)
assertEquals(Duration.ofMinutes(30), timeSpan.duration)
}
}

0 comments on commit 47caba5

Please sign in to comment.