Skip to content
This repository has been archived by the owner on Jun 18, 2021. It is now read-only.

Commit

Permalink
Merge branch 'release/0.0.4'
Browse files Browse the repository at this point in the history
  • Loading branch information
JonasWanke committed Aug 14, 2018
2 parents 258858b + 330ff45 commit 3ef1f3a
Show file tree
Hide file tree
Showing 32 changed files with 830 additions and 352 deletions.
14 changes: 12 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,15 @@ Optional intro comment.



## [0.0.4] - 2018-xx-xx

Optional intro comment.

- Added: Support all-day events ([#41](https://github.com/JonasWanke/com.jonaswanke.calendar/pull/41)), fixes [#9](https://github.com/JonasWanke/com.jonaswanke.calendar/issues/9)
- Added: Support creating events ([#44](https://github.com/JonasWanke/com.jonaswanke.calendar/pull/44)), fixes [#37](https://github.com/JonasWanke/com.jonaswanke.calendar/issues/37)
- Changed: Use AndroidX ([#42](https://github.com/JonasWanke/com.jonaswanke.calendar/pull/42)), fixes [#39](https://github.com/JonasWanke/com.jonaswanke.calendar/issues/39)


## [0.0.3] - 2018-08-05

- Added: add default text for empty title ([#38](https://github.com/JonasWanke/com.jonaswanke.calendar/pull/38)), fixes [#36](https://github.com/JonasWanke/com.jonaswanke.calendar/issues/36)
Expand Down Expand Up @@ -54,6 +63,7 @@ Optional intro comment.
Initial release supporting week view.


[Unreleased]: https://github.com/JonasWanke/com.jonaswanke.calendar/compare/v0.0.3...dev
[0.0.3]: https://github.com/JonasWanke/com.jonaswanke.calendar/compare/v0.0.2...0.0.3
[Unreleased]: https://github.com/JonasWanke/com.jonaswanke.calendar/compare/v0.0.4...dev
[0.0.4]: https://github.com/JonasWanke/com.jonaswanke.calendar/compare/v0.0.3...v0.0.4
[0.0.3]: https://github.com/JonasWanke/com.jonaswanke.calendar/compare/v0.0.2...v0.0.3
[0.0.2]: https://github.com/JonasWanke/com.jonaswanke.calendar/compare/v0.0.1...v0.0.2
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ This library provides a Material Design CalendarView for Android (week view; mor
1. Import the library

```groovy
implementation 'com.jonaswanke.calendar:calendar:0.0.3'
implementation 'com.jonaswanke.calendar:calendar:0.0.4'
```

2. Add CalendarView in you layout
Expand Down
4 changes: 2 additions & 2 deletions build.gradle
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
buildscript {
ext.kotlin_version = '1.2.30'
ext.kotlin_version = '1.2.51'
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.1.3'
classpath 'com.android.tools.build:gradle:3.2.0-beta05'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"

classpath 'com.github.dcendents:android-maven-gradle-plugin:1.5'
Expand Down
23 changes: 12 additions & 11 deletions calendar/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@ apply plugin: 'kotlin-android-extensions'
kotlin.experimental.coroutines "enable"

android {
compileSdkVersion 27
compileSdkVersion 28
defaultConfig {
minSdkVersion 16
targetSdkVersion 27
versionCode 3
versionName "0.0.3"
targetSdkVersion 28
versionCode 4
versionName "0.0.4"

testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"

}

Expand All @@ -30,14 +30,15 @@ dependencies {
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:0.22.5"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:0.22.3"

// Android Support
implementation 'com.android.support:appcompat-v7:27.1.1'
implementation "android.arch.lifecycle:livedata:1.1.1"
// AndroidX
// architecture
implementation 'androidx.appcompat:appcompat:1.0.0-rc01'
implementation 'androidx.core:core-ktx:1.0.0-rc01'

// Testing
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
androidTestImplementation 'androidx.test:runner:1.1.0-alpha4'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.0-alpha4'
}

ext {
Expand All @@ -54,7 +55,7 @@ ext {
gitUrl = 'https://github.com/JonasWanke/com.jonaswanke.calendar.git'
githubRepository = 'JonasWanke/com.jonaswanke.calendar'

libraryVersion = '0.0.3'
libraryVersion = '0.0.4'

developerId = 'JonasWanke'
developerName = 'Jonas Wanke'
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package com.jonaswanke.calendar;

import android.content.Context;
import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;

import org.junit.Test;
import org.junit.runner.RunWith;
Expand Down
225 changes: 225 additions & 0 deletions calendar/src/main/java/com/jonaswanke/calendar/AllDayEventsView.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,225 @@
package com.jonaswanke.calendar

import android.content.Context
import androidx.annotation.AttrRes
import android.util.AttributeSet
import android.view.View
import android.view.ViewGroup
import androidx.core.content.withStyledAttributes
import androidx.core.view.children
import androidx.core.view.get
import kotlinx.coroutines.experimental.android.UI
import kotlinx.coroutines.experimental.launch
import java.util.*
import kotlin.properties.Delegates

/**
* TODO: document your custom view class.
*/
class AllDayEventsView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
@AttrRes private val defStyleAttr: Int = R.attr.allDayEventsViewStyle,
_start: Day? = null,
_end: Day? = null
) : ViewGroup(context, attrs, defStyleAttr) {

var onEventClickListener: ((Event) -> Unit)?
by Delegates.observable<((Event) -> Unit)?>(null) { _, _, new ->
updateListeners(new, onEventLongClickListener)
}
var onEventLongClickListener: ((Event) -> Unit)?
by Delegates.observable<((Event) -> Unit)?>(null) { _, _, new ->
updateListeners(onEventClickListener, new)
}

var start: Day = _start ?: Day()
private set
var end: Day = _end ?: start.nextDay
private set
private var days: Int = 0

private var events: List<Event> = emptyList()
private val eventData: MutableMap<Event, EventData> = mutableMapOf()
private var rows: Int = 0

private var spacing: Float = 0f

private lateinit var calStart: Calendar
private lateinit var calEnd: Calendar

init {
context.withStyledAttributes(attrs, R.styleable.AllDayEventsView, defStyleAttr, R.style.Calendar_AllDayEventsViewStyle) {
spacing = getDimension(R.styleable.AllDayEventsView_spacing, 0f)
}

onUpdateRange(start, end)
}

override fun addView(child: View?, index: Int, params: LayoutParams?) {
if (child !is EventView)
throw IllegalArgumentException("Only EventViews may be children of AllDayEventsView")
super.addView(child, index, params)
}

override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
val rowsHeight = (rows * (((getChildAt(0) as? EventView)?.minHeight ?: 0) + spacing)).toInt()
val height = paddingTop + paddingBottom + Math.max(suggestedMinimumHeight, rowsHeight)
setMeasuredDimension(View.getDefaultSize(suggestedMinimumWidth, widthMeasureSpec),
height)
}

override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
val left = paddingLeft
val top = paddingTop
val right = r - l - paddingRight
val bottom = b - t - paddingBottom
val height = bottom - top
val width = right - left

fun getX(index: Int) = left + width * index / days
fun getY(index: Int) = top + height * index / rows

for (view in children) {
val eventView = view as EventView
val event = eventView.event ?: continue
val data = eventData[event] ?: continue

eventView.layout((getX(data.start) + spacing).toInt(), getY(data.index),
getX(data.end + 1), (getY(data.index + 1) - spacing).toInt())
}
}

fun setRange(start: Day = this.start, end: Day = this.end, events: List<Event> = emptyList()) {
this.start = start
this.end = end
onUpdateRange(start, end)

setEvents(events)
}

fun setEvents(events: List<Event>) {
checkEvents(events)
eventData.clear()
for (event in events) {
val start = calStart.daysUntil(event.start).coerceAtLeast(0)
val end = calStart.daysUntil(event.end).coerceIn(start, 6)
eventData[event] = EventData(start, end)
}
this.events = events.sortedWith(compareBy({ eventData[it]?.start }, { eventData[it]?.length }))

launch(UI) {
@Suppress("NAME_SHADOWING")
val events = this@AllDayEventsView.events
positionEvents()

val existing = childCount
for (i in 0 until events.size) {
val event = events[i]

if (existing > i)
(this@AllDayEventsView[i] as EventView).event = event
else
addView(EventView(
this@AllDayEventsView.context,
defStyleAttr = R.attr.eventViewAllDayStyle,
defStyleRes = R.style.Calendar_EventViewStyle_AllDay).also {
it.event = event
})
}
if (events.size < existing)
removeViews(events.size, existing - events.size)
updateListeners(onEventClickListener, onEventLongClickListener)
requestLayout()
}
}

private fun positionEvents() {
var currentGroup = mutableListOf<Event>()
var currentEnd = 0
fun endGroup() {
when (currentGroup.size) {
0 -> return
1 -> eventData[currentGroup[0]]?.index = 0
else -> {
val ends = mutableListOf<Int>()
for (event in currentGroup) {
val data = eventData[event] ?: continue
val min = ends.filter { it < data.start }.min()
val index = ends.indexOf(min)

if (index < 0) {
data.index = ends.size
ends.add(data.end)
} else {
data.index = index
ends[index] = data.end
}
}
}
}
}
for (event in events) {
val data = eventData[event] ?: continue
if (data.start <= currentEnd) {
currentGroup.add(event)
currentEnd = Math.max(currentEnd, data.end)
} else {
endGroup()
currentGroup = mutableListOf(event)
currentEnd = data.end
}
}
endGroup()
rows = (eventData.maxBy { it.value.index }?.value?.index ?: -1) + 1
}

private fun checkEvents(events: List<Event>) {
if (events.any { event -> !event.allDay })
throw IllegalArgumentException("only all-day events can be shown inside AllDayEventsView")
if (events.any { event -> event.start >= end.start || event.end < start.start })
throw IllegalArgumentException("event must all partly be inside the set range")
}

private fun updateListeners(
onEventClickListener: ((Event) -> Unit)?,
onEventLongClickListener: ((Event) -> Unit)?
) {
for (view in children) {
val eventView = view as EventView
val event = eventView.event
if (event == null) {
eventView.setOnClickListener(null)
eventView.setOnLongClickListener(null)
continue
}

onEventClickListener?.let { listener ->
eventView.setOnClickListener {
listener(event)
}
} ?: eventView.setOnClickListener(null)
onEventLongClickListener?.let { listener ->
eventView.setOnLongClickListener {
listener(event)
true
}
} ?: eventView.setOnLongClickListener(null)
}
}

private fun onUpdateRange(start: Day, end: Day) {
calStart = start.start.asCalendar()
calEnd = end.start.asCalendar()
days = calStart.daysUntil(end.start)
requestLayout()
}

private data class EventData(
val start: Int,
val end: Int,
var index: Int = 0
) {
val length = end - start
}
}
Loading

0 comments on commit 3ef1f3a

Please sign in to comment.