Skip to content

Commit 8ebc646

Browse files
Restored prefetching mechanism, fixing this issue:
Quivr#86 Quivr#89
1 parent ef94b8d commit 8ebc646

File tree

8 files changed

+131
-40
lines changed

8 files changed

+131
-40
lines changed

build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ buildscript {
77
google()
88
}
99
dependencies {
10-
classpath 'com.android.tools.build:gradle:3.2.0-alpha14'
10+
classpath 'com.android.tools.build:gradle:3.2.0-alpha15'
1111
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
1212

1313
// NOTE: Do not place your application dependencies here; they belong
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package com.alamkanak.weekview
2+
3+
import android.support.annotation.IntRange
4+
import java.util.*
5+
6+
/**
7+
* <h1>PrefetchingWeekViewLoader</h1>
8+
* This class provides prefetching data loading behaviour.
9+
* By setting a specific period of N, data is retrieved for the current period,
10+
* the next N periods and the previous N periods.
11+
*/
12+
/**
13+
* @param weekViewLoader An instance of the WeekViewLoader class
14+
* @param prefetchingPeriod The amount of periods to be fetched before and after the
15+
* current period. Must be 1 or greater.
16+
*/
17+
class PrefetchingWeekViewLoader(weekViewLoader: WeekViewLoader, @IntRange(from = 1L) val prefetchingPeriod: Int = 1) : WeekViewLoader {
18+
private var mWeekViewLoader: WeekViewLoader? = weekViewLoader
19+
20+
init {
21+
if (prefetchingPeriod < 1)
22+
throw IllegalArgumentException("Must specify prefetching period of at least 1!")
23+
}
24+
25+
override fun onLoad(periodIndex: Int): MutableList<WeekViewEvent>? {
26+
// fetch the current period
27+
val events = ArrayList(mWeekViewLoader!!.onLoad(periodIndex)!!)
28+
// fetch periods before/after
29+
for (i in 1..this.prefetchingPeriod) {
30+
events.addAll(mWeekViewLoader!!.onLoad(periodIndex - i)!!)
31+
events.addAll(mWeekViewLoader!!.onLoad(periodIndex + i)!!)
32+
}
33+
// return list of all events together
34+
return events
35+
}
36+
37+
override fun toWeekViewPeriodIndex(instance: Calendar) = mWeekViewLoader!!.toWeekViewPeriodIndex(instance)
38+
39+
}

library/src/main/java/com/alamkanak/weekview/WeekView.kt

Lines changed: 53 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import android.util.AttributeSet
1717
import android.util.TypedValue
1818
import android.view.*
1919
import android.widget.OverScroller
20+
import com.alamkanak.weekview.MonthLoader.MonthChangeListener
2021
import com.alamkanak.weekview.WeekViewUtil.daysBetween
2122
import com.alamkanak.weekview.WeekViewUtil.getPassedMinutesInDay
2223
import com.alamkanak.weekview.WeekViewUtil.isSameDay
@@ -224,7 +225,11 @@ class WeekView @JvmOverloads constructor(context: Context, attrs: AttributeSet?
224225
* are loaded in week view. For a MonthLoader events are loaded for every month. You can define
225226
* your custom event loader by extending WeekViewLoader.
226227
*/
227-
var weekViewLoader: WeekViewLoader? = null
228+
val weekViewLoader: WeekViewLoader = PrefetchingWeekViewLoader(MonthLoader(object : MonthChangeListener {
229+
override fun onMonthChange(newYear: Int, newMonth: Int): MutableList<WeekViewEvent>? {
230+
return monthChangeListener?.onMonthChange(newYear, newMonth)
231+
}
232+
}))
228233
var emptyViewClickListener: EmptyViewClickListener? = null
229234
var emptyViewLongPressListener: EmptyViewLongPressListener? = null
230235
var scrollListener: ScrollListener? = null
@@ -359,7 +364,7 @@ class WeekView @JvmOverloads constructor(context: Context, attrs: AttributeSet?
359364
// If the tap was on an event then trigger the callback.
360365
if (mEventRects != null && eventClickListener != null) {
361366
val reversedEventRects = mEventRects
362-
Collections.reverse(reversedEventRects!!)
367+
reversedEventRects!!.reverse()
363368
for (eventRect in reversedEventRects) {
364369
if (newEventIdentifier != eventRect.event.identifier && eventRect.rectF != null && e.x > eventRect.rectF!!.left && e.x < eventRect.rectF!!.right && e.y > eventRect.rectF!!.top && e.y < eventRect.rectF!!.bottom) {
365370
eventClickListener!!.onEventClick(eventRect.originalEvent, eventRect.rectF!!)
@@ -455,7 +460,7 @@ class WeekView @JvmOverloads constructor(context: Context, attrs: AttributeSet?
455460

456461
if (eventLongPressListener != null && mEventRects != null) {
457462
val reversedEventRects = mEventRects
458-
Collections.reverse(reversedEventRects!!)
463+
reversedEventRects!!.reverse()
459464
for (event in reversedEventRects) {
460465
if (event.rectF != null && e.x > event.rectF!!.left && e.x < event.rectF!!.right && e.y > event.rectF!!.top && e.y < event.rectF!!.bottom) {
461466
eventLongPressListener!!.onEventLongPress(event.originalEvent, event.rectF!!)
@@ -491,16 +496,16 @@ class WeekView @JvmOverloads constructor(context: Context, attrs: AttributeSet?
491496

492497
private val xMinLimit: Float
493498
get() {
494-
if (maxDate == null) {
495-
return Integer.MIN_VALUE.toFloat()
499+
return if (maxDate == null) {
500+
Integer.MIN_VALUE.toFloat()
496501
} else {
497502
val date = maxDate!!.clone() as Calendar
498503
date.add(Calendar.DATE, 1 - realNumberOfVisibleDays)
499504
while (date.before(minDate)) {
500505
date.add(Calendar.DATE, 1)
501506
}
502507

503-
return getXOriginForDate(date)
508+
getXOriginForDate(date)
504509
}
505510
}
506511

@@ -525,11 +530,7 @@ class WeekView @JvmOverloads constructor(context: Context, attrs: AttributeSet?
525530
get() = mCurrentOrigin.x + (mWidthPerDay + columnGap) * leftDaysWithGaps +
526531
mHeaderColumnWidth
527532

528-
var monthChangeListener: MonthLoader.MonthChangeListener?
529-
get() = if (weekViewLoader is MonthLoader) (weekViewLoader as MonthLoader).onMonthChangeListener else null
530-
set(value) {
531-
this.weekViewLoader = MonthLoader(value)
532-
}
533+
var monthChangeListener: MonthChangeListener? = null
533534

534535
/**
535536
* the interpreter which provides the text to show in the header column and the header row.
@@ -1404,11 +1405,35 @@ class WeekView @JvmOverloads constructor(context: Context, attrs: AttributeSet?
14041405
continue
14051406
}
14061407

1407-
// Get more events if necessary. We want to store the events 3 months beforehand. Get
1408-
// events only when it is the first iteration of the loop.
1409-
if (mEventRects == null || mRefreshEvents ||
1410-
dayNumber == leftDaysWithGaps + 1 && mFetchedPeriod != weekViewLoader!!.toWeekViewPeriodIndex(day).toInt() &&
1411-
Math.abs(mFetchedPeriod - weekViewLoader!!.toWeekViewPeriodIndex(day)) > 0.5) {
1408+
// Get more events if necessary.
1409+
1410+
// mFetchedPeriod: currently fetched period index
1411+
// mWeekViewLoader.toWeekViewPeriodIndex(day): index for the day we want to display
1412+
// fetchIndex = 1.0: end of period in the future reached
1413+
// fetchIndex = 0.0: end of period in the past reached
1414+
val fetchIndex = this.weekViewLoader.toWeekViewPeriodIndex(day) - mFetchedPeriod
1415+
1416+
// if we are using the PrefetchingWeekViewLoader class, we need to adjust the bounds
1417+
// so that we wait to fetch new data until we really need it
1418+
var upperBound = 1.0
1419+
var lowerBound = 0.0
1420+
1421+
if (this.weekViewLoader is PrefetchingWeekViewLoader) {
1422+
// the offset causes the onMonthChangeListener to be trigger when half of the
1423+
// last fetched period is passed
1424+
1425+
// example:
1426+
// if the prefetching period = 1, we load the current period, the next and the previous
1427+
// when half of the next/previous period is passed, the listener is triggered to fetch new data
1428+
val boundOffset = this.weekViewLoader.prefetchingPeriod - 0.5
1429+
1430+
upperBound = 1.0 + boundOffset
1431+
lowerBound = 0.0 - boundOffset
1432+
}
1433+
1434+
if ((mEventRects == null || mRefreshEvents ||
1435+
dayNumber == leftDaysWithGaps + 1 && mFetchedPeriod != this.weekViewLoader.toWeekViewPeriodIndex(day).toInt() &&
1436+
(fetchIndex >= upperBound || fetchIndex <= lowerBound))) {
14121437
getMoreEvents(day)
14131438
mRefreshEvents = false
14141439
}
@@ -1824,7 +1849,7 @@ class WeekView @JvmOverloads constructor(context: Context, attrs: AttributeSet?
18241849
if (mEvents == null)
18251850
mEvents = ArrayList()
18261851

1827-
if (weekViewLoader == null && !isInEditMode)
1852+
if (this.weekViewLoader == null && !isInEditMode)
18281853
throw IllegalStateException("You must provide a MonthChangeListener")
18291854

18301855
// If a refresh was requested then reset some variables.
@@ -1833,18 +1858,16 @@ class WeekView @JvmOverloads constructor(context: Context, attrs: AttributeSet?
18331858
mFetchedPeriod = -1
18341859
}
18351860

1836-
if (weekViewLoader != null) {
1837-
val periodToFetch = weekViewLoader!!.toWeekViewPeriodIndex(day).toInt()
1838-
if (!isInEditMode && (mFetchedPeriod < 0 || mFetchedPeriod != periodToFetch || mRefreshEvents)) {
1839-
val newEvents = weekViewLoader!!.onLoad(periodToFetch)
1861+
val periodToFetch = this.weekViewLoader.toWeekViewPeriodIndex(day).toInt()
1862+
if (!isInEditMode && (mFetchedPeriod < 0 || mFetchedPeriod != periodToFetch || mRefreshEvents)) {
1863+
val newEvents = this.weekViewLoader.onLoad(periodToFetch)
18401864

1841-
// Clear events.
1842-
this.clearEvents()
1843-
cacheAndSortEvents(newEvents)
1844-
calculateHeaderHeight()
1865+
// Clear events.
1866+
this.clearEvents()
1867+
cacheAndSortEvents(newEvents)
1868+
calculateHeaderHeight()
18451869

1846-
mFetchedPeriod = periodToFetch
1847-
}
1870+
mFetchedPeriod = periodToFetch
18481871
}
18491872

18501873
// Prepare to calculate positions of each events.
@@ -2427,12 +2450,12 @@ class WeekView @JvmOverloads constructor(context: Context, attrs: AttributeSet?
24272450
goToNearestOrigin()
24282451

24292452
// Calculate focused point for scale action
2430-
if (isZoomFocusPointEnabled) {
2453+
mFocusedPointY = if (isZoomFocusPointEnabled) {
24312454
// Use fractional focus, percentage of height
2432-
mFocusedPointY = (height.toFloat() - mHeaderHeight - (weekDaysHeaderRowPadding * 2).toFloat() - spaceBelowAllDayEvents) * zoomFocusPoint
2455+
(height.toFloat() - mHeaderHeight - (weekDaysHeaderRowPadding * 2).toFloat() - spaceBelowAllDayEvents) * zoomFocusPoint
24332456
} else {
24342457
// Grab focus
2435-
mFocusedPointY = detector.focusY
2458+
detector.focusY
24362459
}
24372460

24382461
return true

library/src/main/java/com/alamkanak/weekview/WeekViewEvent.kt

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ class WeekViewEvent {
7878
* @param allDay Is the event an all day event.
7979
* @param shader the Shader of the event rectangle
8080
*/
81-
@JvmOverloads constructor(id: String, name: String?, location: String?, startTime: Calendar, endTime: Calendar, allDay: Boolean = false, shader: Shader? = null) {
81+
@JvmOverloads constructor(id: String?, name: String?, location: String?, startTime: Calendar, endTime: Calendar, allDay: Boolean = false, shader: Shader? = null) {
8282
this.identifier = id
8383
this.name = name
8484
this.location = location
@@ -96,7 +96,7 @@ class WeekViewEvent {
9696
* @param startTime The time when the event starts.
9797
* @param endTime The time when the event ends.
9898
*/
99-
constructor(id: String, name: String, startTime: Calendar, endTime: Calendar) : this(id, name, null, startTime, endTime) {}
99+
constructor(id: String?, name: String, startTime: Calendar, endTime: Calendar) : this(id, name, null, startTime, endTime)
100100

101101
override fun equals(other: Any?): Boolean {
102102
if (this === other) return true
@@ -111,6 +111,7 @@ class WeekViewEvent {
111111
return identifier!!.hashCode()
112112
}
113113

114+
114115
fun splitWeekViewEvents(): MutableList<WeekViewEvent> {
115116
//This function splits the WeekViewEvent in WeekViewEvents by day
116117
val events = ArrayList<WeekViewEvent>()
@@ -156,4 +157,16 @@ class WeekViewEvent {
156157

157158
return events
158159
}
160+
161+
override fun toString(): String {
162+
if (isAllDay)
163+
return "WeekViewEvent(identifier=$identifier, time=${WeekViewUtil.calendarToString(startTime, false)}, name=$name, location=$location, color=$color, isAllDay=$isAllDay, shader=$shader)"
164+
val startTimeText = if (startTime == null) null
165+
else startTime!!.get(Calendar.YEAR).toString() + "-" + (startTime!!.get(Calendar.MONTH) + 1).toString() + "-" + startTime!!.get(Calendar.DAY_OF_MONTH).toString() + " " +
166+
startTime!!.get(Calendar.HOUR_OF_DAY).toString() + ":" + startTime!!.get(Calendar.MINUTE) + ":" + startTime!!.get(Calendar.SECOND) + "." + startTime!!.get(Calendar.MILLISECOND)
167+
val endTimeText = if (endTime == null) null
168+
else endTime!!.get(Calendar.YEAR).toString() + "-" + (endTime!!.get(Calendar.MONTH) + 1).toString() + "-" + endTime!!.get(Calendar.DAY_OF_MONTH).toString() + " " +
169+
endTime!!.get(Calendar.HOUR_OF_DAY).toString() + ":" + endTime!!.get(Calendar.MINUTE) + ":" + endTime!!.get(Calendar.SECOND) + "." + endTime!!.get(Calendar.MILLISECOND)
170+
return "WeekViewEvent(identifier=$identifier, startTime=$startTimeText, endTime=$endTimeText, name=$name, location=$location, color=$color, isAllDay=$isAllDay, shader=$shader)"
171+
}
159172
}

library/src/main/java/com/alamkanak/weekview/WeekViewLoader.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package com.alamkanak.weekview
33
import java.util.*
44

55
interface WeekViewLoader {
6+
67
/**
78
* Convert a date into a double that will be used to reference when you're loading data.
89
*

library/src/main/java/com/alamkanak/weekview/WeekViewUtil.kt

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,4 +130,19 @@ object WeekViewUtil {
130130
return SimpleDateFormat(defaultDateFormatPattern, Locale.getDefault())
131131
}
132132
}
133+
134+
@JvmStatic
135+
fun calendarToString(cal: Calendar?, includeTime: Boolean = true): String {
136+
if (cal == null)
137+
return ""
138+
val sb = StringBuilder()
139+
with(cal) {
140+
sb.append(get(Calendar.YEAR).toString()).append('-').append((get(Calendar.MONTH) + 1).toString())
141+
.append('-').append(get(Calendar.DAY_OF_MONTH).toString())
142+
if (includeTime)
143+
sb.append(get(Calendar.HOUR_OF_DAY).toString()).append(':').append(get(Calendar.MINUTE).toString()).append(':')
144+
.append(get(Calendar.SECOND).toString()).append('.').append(get(Calendar.MILLISECOND).toString())
145+
}
146+
return sb.toString()
147+
}
133148
}

sample/src/main/java/com/alamkanak/weekview/sample/BaseActivity.kt

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -202,10 +202,10 @@ abstract class BaseActivity : AppCompatActivity(), WeekView.EventClickListener,
202202

203203
protected fun getEventTitle(cal: Calendar, endCal: Calendar? = null): String {
204204
val date = cal.time
205-
if (endCal == null)
206-
return "${shortDateFormat.format(date)} ${timeFormat.format(date)}"
205+
return if (endCal == null)
206+
"${shortDateFormat.format(date)} ${timeFormat.format(date)}"
207207
else
208-
return "${shortDateFormat.format(date)} ${timeFormat.format(date)}..${timeFormat.format(endCal.time)}"
208+
"${shortDateFormat.format(date)} ${timeFormat.format(date)}..${timeFormat.format(endCal.time)}"
209209
// return String.format("%02d:%02d %s/%d", time.get(Calendar.HOUR_OF_DAY), time.get(Calendar.MINUTE), time.get(Calendar.MONTH) + 1, time.get(Calendar.DAY_OF_MONTH))
210210
}
211211

@@ -238,8 +238,8 @@ abstract class BaseActivity : AppCompatActivity(), WeekView.EventClickListener,
238238
}
239239

240240
companion object {
241-
val TYPE_DAY_VIEW = 1
242-
val TYPE_THREE_DAY_VIEW = 2
243-
val TYPE_WEEK_VIEW = 3
241+
const val TYPE_DAY_VIEW = 1
242+
const val TYPE_THREE_DAY_VIEW = 2
243+
const val TYPE_WEEK_VIEW = 3
244244
}
245245
}

sample/src/main/res/layout/activity_main.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<LinearLayout
22
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools"
3-
android:layout_width="match_parent" android:layout_height="match_parent" android:background="#fff"
3+
android:layout_width="match_parent" android:layout_height="match_parent"
44
android:orientation="vertical" tools:context=".MainActivity">
55

66
<Button

0 commit comments

Comments
 (0)