Skip to content

Commit 47caba5

Browse files
committed
feature: introduce TimeSpan class and avoid issue drawing background.
1 parent 57522b1 commit 47caba5

File tree

9 files changed

+127
-45
lines changed

9 files changed

+127
-45
lines changed

app/src/main/java/de/tobiasschuerg/weekview/sample/EventCreator.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package de.tobiasschuerg.weekview.sample
33
import android.graphics.Color
44
import de.tobiasschuerg.weekview.data.Event
55
import de.tobiasschuerg.weekview.data.WeekData
6+
import de.tobiasschuerg.weekview.util.TimeSpan
67
import org.threeten.bp.DayOfWeek
78
import org.threeten.bp.LocalDate
89
import org.threeten.bp.LocalTime
@@ -52,8 +53,7 @@ object EventCreator {
5253
title = name,
5354
shortTitle = name,
5455
subTitle = subTitle,
55-
startTime = startTime,
56-
endTime = endTime,
56+
timeSpan = TimeSpan(startTime, endTime),
5757
textColor = Color.WHITE,
5858
backgroundColor = randomColor()
5959
)

app/src/main/java/de/tobiasschuerg/weekview/sample/SampleActivity.kt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,10 @@ import androidx.appcompat.app.AppCompatActivity
1212
import com.jakewharton.threetenabp.AndroidThreeTen
1313
import de.tobiasschuerg.weekview.data.Event
1414
import de.tobiasschuerg.weekview.data.EventConfig
15+
import de.tobiasschuerg.weekview.util.TimeSpan
1516
import de.tobiasschuerg.weekview.view.EventView
1617
import de.tobiasschuerg.weekview.view.WeekView
18+
import org.threeten.bp.Duration
1719
import org.threeten.bp.LocalDate
1820
import org.threeten.bp.LocalTime
1921
import org.threeten.bp.temporal.ChronoUnit
@@ -38,8 +40,7 @@ class SampleActivity : AppCompatActivity() {
3840
date = LocalDate.now(),
3941
title = "Current hour",
4042
shortTitle = "Now",
41-
startTime = LocalTime.now().truncatedTo(ChronoUnit.HOURS),
42-
endTime = LocalTime.now().truncatedTo(ChronoUnit.HOURS).plusMinutes(59),
43+
timeSpan = TimeSpan.of(LocalTime.now().truncatedTo(ChronoUnit.HOURS), Duration.ofHours(1)),
4344
backgroundColor = Color.RED,
4445
textColor = Color.WHITE
4546
)

library/src/main/java/de/tobiasschuerg/weekview/data/Event.kt

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
package de.tobiasschuerg.weekview.data
22

3+
import de.tobiasschuerg.weekview.util.TimeSpan
34
import org.threeten.bp.Duration
45
import org.threeten.bp.LocalDate
5-
import org.threeten.bp.LocalTime
66

77
sealed class Event {
88

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

21-
val startTime: LocalTime,
22-
val endTime: LocalTime,
21+
val timeSpan: TimeSpan,
2322

2423
val upperText: String? = null,
2524
val lowerText: String? = null,
2625

2726
val textColor: Int,
2827
val backgroundColor: Int
2928
) : Event() {
30-
val duration: Duration = Duration.between(startTime, endTime)
29+
val duration: Duration = timeSpan.duration
3130
}
3231

3332
data class AllDay(

library/src/main/java/de/tobiasschuerg/weekview/data/WeekData.kt

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,22 @@
11
package de.tobiasschuerg.weekview.data
22

3+
import de.tobiasschuerg.weekview.util.TimeSpan
34
import org.threeten.bp.LocalTime
45

56
class WeekData {
67

78
private val singleEvents: MutableList<Event.Single> = mutableListOf()
8-
99
private val allDays: MutableList<Event.AllDay> = mutableListOf()
10-
11-
var earliestStart: LocalTime = LocalTime.MAX
12-
13-
var latestEnd: LocalTime = LocalTime.MIN
10+
private var earliestStart: LocalTime? = null
11+
private var latestEnd: LocalTime? = null
12+
13+
fun getTimeSpan(): TimeSpan? {
14+
return if (earliestStart != null && latestEnd != null) {
15+
TimeSpan(earliestStart!!, latestEnd!!)
16+
} else {
17+
null
18+
}
19+
}
1420

1521
fun add(item: Event.AllDay) {
1622
allDays.add(item)
@@ -19,12 +25,12 @@ class WeekData {
1925
fun add(item: Event.Single) {
2026
singleEvents.add(item)
2127

22-
if (item.startTime.isBefore(earliestStart)) {
23-
earliestStart = item.startTime
28+
if (earliestStart == null || item.timeSpan.start.isBefore(earliestStart)) {
29+
earliestStart = item.timeSpan.start
2430
}
2531

26-
if (item.endTime.isAfter(latestEnd)) {
27-
latestEnd = item.endTime
32+
if (latestEnd == null || item.timeSpan.endExclusive.isAfter(latestEnd)) {
33+
latestEnd = item.timeSpan.endExclusive
2834
}
2935
}
3036

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package de.tobiasschuerg.weekview.util
2+
3+
import org.threeten.bp.Duration
4+
import org.threeten.bp.LocalTime
5+
6+
/**
7+
* Holds a duration of time.
8+
*/
9+
data class TimeSpan(
10+
val start: LocalTime,
11+
val endExclusive: LocalTime
12+
) {
13+
14+
init {
15+
require(start.isBefore(endExclusive)) {
16+
"Start time $start must be before end time $endExclusive!"
17+
}
18+
}
19+
20+
val duration: Duration by lazy { Duration.between(start, endExclusive) }
21+
22+
companion object {
23+
fun of(start: LocalTime, duration: Duration): TimeSpan {
24+
return TimeSpan(start, start.plus(duration))
25+
}
26+
}
27+
}

library/src/main/java/de/tobiasschuerg/weekview/view/EventView.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,14 +100,14 @@ class EventView(
100100

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

108108
// end time
109109
if (config.showTimeEnd) {
110-
val endText = event.endTime.toLocalString()
110+
val endText = event.timeSpan.endExclusive.toLocalString()
111111
textPaint.getTextBounds(endText, 0, endText.length, textBounds)
112112
canvas.drawText(endText, (width - (textBounds.right + paddingRight)).toFloat(), (height - paddingBottom).toFloat(), textPaint)
113113
}

library/src/main/java/de/tobiasschuerg/weekview/view/WeekBackgroundView.kt

Lines changed: 33 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import android.graphics.Rect
88
import android.util.Log
99
import android.view.View
1010
import de.tobiasschuerg.weekview.util.DayOfWeekUtil
11+
import de.tobiasschuerg.weekview.util.TimeSpan
1112
import de.tobiasschuerg.weekview.util.dipToPixelF
1213
import de.tobiasschuerg.weekview.util.dipToPixelI
1314
import de.tobiasschuerg.weekview.util.toLocalString
@@ -113,7 +114,13 @@ internal class WeekBackgroundView constructor(context: Context) : View(context)
113114

114115
// final String timeString = localTime.format(DateTimeFormatter.ofLocalizedTime(FormatStyle.SHORT));
115116
val timeString = localTime.toLocalString()
116-
drawMultiLineText(this, timeString, context.dipToPixelF(25f), y + context.dipToPixelF(20f), mPaintLabels)
117+
drawMultiLineText(
118+
this,
119+
timeString,
120+
context.dipToPixelF(25f),
121+
y + context.dipToPixelF(20f),
122+
mPaintLabels
123+
)
117124

118125
last = localTime
119126
localTime = localTime.plusHours(1)
@@ -151,10 +158,21 @@ internal class WeekBackgroundView constructor(context: Context) : View(context)
151158
private fun Canvas.drawWeekDayName(day: DayOfWeek, column: Int) {
152159
val shortName = day.getDisplayName(TextStyle.SHORT, Locale.getDefault())
153160
val xLabel = (getColumnStart(column, false) + getColumnEnd(column, false)) / 2
154-
drawText(shortName, xLabel.toFloat(), topOffsetPx / 2 + mPaintLabels.descent(), mPaintLabels)
155-
}
156-
157-
private fun drawMultiLineText(canvas: Canvas, text: String, initialX: Float, initialY: Float, paint: Paint) {
161+
drawText(
162+
shortName,
163+
xLabel.toFloat(),
164+
topOffsetPx / 2 + mPaintLabels.descent(),
165+
mPaintLabels
166+
)
167+
}
168+
169+
private fun drawMultiLineText(
170+
canvas: Canvas,
171+
text: String,
172+
initialX: Float,
173+
initialY: Float,
174+
paint: Paint
175+
) {
158176
var currentY = initialY
159177
text.split(" ")
160178
.dropLastWhile(String::isEmpty)
@@ -190,27 +208,26 @@ internal class WeekBackgroundView constructor(context: Context) : View(context)
190208
}
191209

192210
override fun onMeasure(widthMeasureSpec: Int, hms: Int) {
193-
val height = topOffsetPx + context.dipToPixelF(getDurationMinutes() * scalingFactor) + paddingBottom
194-
val heightMeasureSpec2 = MeasureSpec.makeMeasureSpec(height.roundToInt(), MeasureSpec.EXACTLY)
211+
val height =
212+
topOffsetPx + context.dipToPixelF(getDurationMinutes() * scalingFactor) + paddingBottom
213+
val heightMeasureSpec2 =
214+
MeasureSpec.makeMeasureSpec(height.roundToInt(), MeasureSpec.EXACTLY)
195215
super.onMeasure(widthMeasureSpec, heightMeasureSpec2)
196216
}
197217

198218
fun setScreenshotMode(screenshotMode: Boolean) {
199219
isInScreenshotMode = screenshotMode
200220
}
201221

202-
fun updateTimes(startTime: LocalTime, endTime: LocalTime) {
203-
if (startTime.isAfter(endTime)) {
204-
throw IllegalArgumentException("Start time $startTime must be before end time $endTime")
205-
}
222+
fun updateTimes(timeSpan: TimeSpan) {
206223
var timesHaveChanged = false
207-
if (startTime.isBefore(this.startTime)) {
208-
this.startTime = startTime.truncatedTo(ChronoUnit.HOURS)
224+
if (timeSpan.start.isBefore(startTime)) {
225+
startTime = timeSpan.start.truncatedTo(ChronoUnit.HOURS)
209226
timesHaveChanged = true
210227
}
211-
if (endTime.isAfter(this.endTime)) {
212-
if (endTime.isBefore(LocalTime.of(23, 0))) {
213-
this.endTime = endTime.truncatedTo(ChronoUnit.HOURS).plusHours(1)
228+
if (timeSpan.endExclusive.isAfter(endTime)) {
229+
if (timeSpan.endExclusive.isBefore(LocalTime.of(23, 0))) {
230+
this.endTime = timeSpan.endExclusive.truncatedTo(ChronoUnit.HOURS).plusHours(1)
214231
} else {
215232
this.endTime = LocalTime.MAX
216233
}

library/src/main/java/de/tobiasschuerg/weekview/view/WeekView.kt

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,9 @@ class WeekView(context: Context, attributeSet: AttributeSet) :
123123
fun addEvents(weekData: WeekData) {
124124
Log.d(TAG, "Adding ${weekData.getSingleEvents().size} weekData to week view")
125125

126-
backgroundView.updateTimes(weekData.earliestStart, weekData.latestEnd)
126+
weekData.getTimeSpan()?.let {
127+
backgroundView.updateTimes(it)
128+
}
127129

128130
for (event in weekData.getSingleEvents()) {
129131
addEvent(event)
@@ -158,12 +160,12 @@ class WeekView(context: Context, attributeSet: AttributeSet) :
158160
}
159161

160162
val lv = EventView(context, event, eventConfig, weekViewConfig.scalingFactor)
161-
backgroundView.updateTimes(event.startTime, event.endTime)
163+
backgroundView.updateTimes(event.timeSpan)
162164

163165
// mark active event
164166
val now = LocalTime.now()
165167
if (LocalDate.now().dayOfWeek == event.date.dayOfWeek && // this day
166-
event.startTime < now && event.endTime > now
168+
event.timeSpan.start < now && event.timeSpan.endExclusive > now
167169
) {
168170
lv.animation = Animation.createBlinkAnimation()
169171
}
@@ -246,7 +248,7 @@ class WeekView(context: Context, attributeSet: AttributeSet) :
246248

247249
eventView.scalingFactor = weekViewConfig.scalingFactor
248250
val startTime = backgroundView.startTime
249-
val lessonStart = eventView.event.startTime
251+
val lessonStart = eventView.event.timeSpan.start
250252
val offset = Duration.between(startTime, lessonStart)
251253

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

264266
private fun overlaps(left: EventView, right: EventView): Boolean {
265-
val rightStartsAfterLeftStarts = right.event.startTime >= left.event.startTime
266-
val rightStartsBeforeLeftEnds = right.event.startTime < left.event.endTime
267+
val rightStartsAfterLeftStarts = right.event.timeSpan.start >= left.event.timeSpan.start
268+
val rightStartsBeforeLeftEnds = right.event.timeSpan.start < left.event.timeSpan.endExclusive
267269
val lessonStartsWithing = rightStartsAfterLeftStarts && rightStartsBeforeLeftEnds
268270

269-
val leftStartsBeforeRightEnds = left.event.startTime < right.event.endTime
270-
val rightEndsBeforeOrWithLeftEnds = right.event.endTime <= left.event.endTime
271+
val leftStartsBeforeRightEnds = left.event.timeSpan.start < right.event.timeSpan.endExclusive
272+
val rightEndsBeforeOrWithLeftEnds = right.event.timeSpan.endExclusive <= left.event.timeSpan.endExclusive
271273
val lessonEndsWithing = leftStartsBeforeRightEnds && rightEndsBeforeOrWithLeftEnds
272274

273-
val leftStartsAfterRightStarts = left.event.startTime > right.event.startTime
274-
val rightEndsAfterLeftEnds = right.event.endTime > left.event.endTime
275+
val leftStartsAfterRightStarts = left.event.timeSpan.start > right.event.timeSpan.start
276+
val rightEndsAfterLeftEnds = right.event.timeSpan.start > left.event.timeSpan.endExclusive
275277
val lessonWithin = leftStartsAfterRightStarts && rightEndsAfterLeftEnds
276278

277279
return lessonStartsWithing || lessonEndsWithing || lessonWithin
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package de.tobiasschuerg.weekview.util
2+
3+
import org.junit.Assert.assertEquals
4+
import org.junit.Test
5+
import org.threeten.bp.Duration
6+
import org.threeten.bp.LocalTime
7+
8+
/**
9+
* Created by Tobias Schrg on 11.02.2022.
10+
*/
11+
class TimeSpanTest {
12+
13+
@Test
14+
fun testDuration() {
15+
val timeSpan = TimeSpan(
16+
start = LocalTime.of(10, 15),
17+
endExclusive = LocalTime.of(10, 45)
18+
)
19+
assertEquals(Duration.ofMinutes(30), timeSpan.duration)
20+
}
21+
22+
@Test
23+
fun testDuration2() {
24+
val timeSpan = TimeSpan.of(
25+
start = LocalTime.of(10, 15),
26+
duration = Duration.ofMinutes(30)
27+
)
28+
assertEquals(Duration.ofMinutes(30), timeSpan.duration)
29+
}
30+
}

0 commit comments

Comments
 (0)