Skip to content

Commit fecf398

Browse files
committed
Merge branch 'feat/calendar' into 'master'
Datum a čas See merge request fmasa/wfrp-master!104
2 parents 15f6238 + 5f9d1cb commit fecf398

File tree

26 files changed

+1142
-5
lines changed

26 files changed

+1142
-5
lines changed

app/build.gradle

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,10 @@ dependencies {
104104
implementation "org.koin:koin-android:2.1.5"
105105
implementation "org.koin:koin-android-viewmodel:2.1.5"
106106
implementation "org.koin:koin-androidx-fragment:2.1.5"
107+
implementation ('com.wdullaer:materialdatetimepicker:4.2.3') {
108+
exclude group: 'androidx.appcompat'
109+
exclude group: 'androidx.recyclerview'
110+
}
107111

108112
// Firebase-related dependencies
109113
implementation 'com.google.firebase:firebase-analytics:17.4.4'

app/src/main/java/cz/muni/fi/rpg/model/domain/party/Party.kt

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package cz.muni.fi.rpg.model.domain.party
22
import android.os.Parcelable
33
import cz.muni.fi.rpg.model.domain.common.Ambitions
4+
import cz.muni.fi.rpg.model.domain.party.time.DateTime
5+
import cz.muni.fi.rpg.model.domain.party.time.ImperialDate
46
import cz.muni.fi.rpg.model.generateAccessCode
57
import kotlinx.android.parcel.Parcelize
68
import java.util.*
@@ -12,7 +14,11 @@ data class Party(
1214
val gameMasterId: String?,
1315
val users: Set<String>,
1416
private var archived: Boolean = false,
15-
private var ambitions: Ambitions = Ambitions("", "")
17+
private var ambitions: Ambitions = Ambitions("", ""),
18+
private var time: DateTime = DateTime(
19+
ImperialDate.of(ImperialDate.StandaloneDay.HEXENSTAG, 2512),
20+
DateTime.TimeOfDay(12, 0)
21+
)
1622
) : Parcelable {
1723
companion object {
1824
const val NAME_MAX_LENGTH = 50
@@ -64,5 +70,11 @@ data class Party(
6470

6571
fun getAmbitions() = ambitions
6672

73+
fun getTime() = time
74+
6775
fun getInvitation() = Invitation(id, name, accessCode)
76+
77+
fun changeTime(time: DateTime) {
78+
this.time = time
79+
}
6880
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
package cz.muni.fi.rpg.model.domain.party.time
2+
3+
import android.os.Parcelable
4+
import com.fasterxml.jackson.annotation.JsonCreator
5+
import com.fasterxml.jackson.annotation.JsonUnwrapped
6+
import kotlinx.android.parcel.Parcelize
7+
8+
@Parcelize
9+
data class DateTime(
10+
@field:JsonUnwrapped
11+
val date: ImperialDate,
12+
private val minutes: Int
13+
) : Parcelable {
14+
15+
companion object {
16+
private fun toMinutes(time: TimeOfDay) = time.hour * 60 + time.minute
17+
}
18+
19+
init {
20+
require(minutes in 0 until 60 * 24)
21+
}
22+
23+
val time: TimeOfDay
24+
get() = TimeOfDay(minutes / 60, minutes % 60)
25+
26+
@Parcelize
27+
data class TimeOfDay(val hour: Int, val minute: Int) : Parcelable {
28+
fun format() = hour.toString().padStart(2, '0') + ":" + minute.toString().padStart(2, '0')
29+
30+
init {
31+
require(hour in 0 until 24)
32+
require(minute in 0 until 60)
33+
}
34+
}
35+
36+
constructor(date: ImperialDate, time: TimeOfDay) : this(date, toMinutes(time))
37+
38+
/**
39+
* This is necessary for deserialization
40+
*/
41+
@JsonCreator
42+
constructor(imperialDate: Int, minutes: Int) : this(ImperialDate(imperialDate), minutes)
43+
44+
fun withTime(time: TimeOfDay): DateTime = copy(minutes = toMinutes(time))
45+
}
Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
package cz.muni.fi.rpg.model.domain.party.time
2+
3+
import android.os.Parcelable
4+
import androidx.annotation.IntRange
5+
import arrow.core.Either
6+
import arrow.core.Left
7+
import arrow.core.Right
8+
import kotlinx.android.parcel.Parcelize
9+
10+
@Parcelize
11+
data class ImperialDate(
12+
@IntRange(from = 0)
13+
private val imperialDay: Int
14+
) : Comparable<ImperialDate>, Parcelable {
15+
16+
init {
17+
require(imperialDay >= 0)
18+
}
19+
20+
enum class DayOfWeek(val readableName: String) {
21+
WELLENTAG("Wellentag"),
22+
AUBENTAG("Aubentag"),
23+
MARKTAG("Marktag"),
24+
BACKERTAG("Backertag"),
25+
BEZAHLTAG("Bezahltag"),
26+
KONISTAG("Konistag"),
27+
ANGESTAG("Angestag"),
28+
FESTAG("Festag"),
29+
}
30+
31+
enum class Month(
32+
val readableName: String,
33+
val standaloneDayAtTheBeginning: StandaloneDay? = null
34+
) {
35+
NACHEXEN("Nachexen", StandaloneDay.HEXENSTAG),
36+
JAHRDRUNG("Jahrdrung"),
37+
PFLUGZEIT("Pflugzeit", StandaloneDay.MITTERFRUHL),
38+
SIGMARZEIT("Sigmarzeit"),
39+
SOMMERZEIT("Sommerzeit"),
40+
VORGEHEIM("Vorgeheim", StandaloneDay.SONNSTILL),
41+
NACHGEHEIM("Nachgeheim", StandaloneDay.GEHEIMNISTAG),
42+
ERNTEZEIT("Erntezeit"),
43+
BRAUZEIT("Brauzeit", StandaloneDay.MITTHERBST),
44+
KALDEZEIT("Kaldezeit"),
45+
ULRICZEIT("Ulriczeit"),
46+
VORHEXEN("Vorhexen", StandaloneDay.MONSTILLE);
47+
48+
val numberOfDays: Int
49+
get() = when (this) {
50+
NACHEXEN, NACHGEHEIM -> 32
51+
else -> 33
52+
}
53+
}
54+
55+
enum class StandaloneDay(val readableName: String) {
56+
HEXENSTAG("Hexenstag"),
57+
MITTERFRUHL("Mitterfruhl"),
58+
SONNSTILL("Sonnstill"),
59+
GEHEIMNISTAG("Geheimnistag"),
60+
MITTHERBST("Mittherbst"),
61+
MONSTILLE("Mondstille"),
62+
}
63+
64+
companion object {
65+
const val DAYS_IN_YEAR = 400
66+
private const val DAYS_IN_WEEK = 8
67+
68+
fun of(day: StandaloneDay, year: Int): ImperialDate {
69+
require(year > 0) { "Year must be >= 1." }
70+
71+
return ImperialDate(
72+
(year - 1) * DAYS_IN_YEAR +
73+
Month.values()
74+
.takeWhile { it.standaloneDayAtTheBeginning != day }
75+
.map { it.numberOfDays + if (it.standaloneDayAtTheBeginning != null) 1 else 0 }
76+
.sum()
77+
)
78+
}
79+
80+
fun of(day: Int, month: Int, year: Int): ImperialDate {
81+
82+
require(month in 1..Month.values().size) { "Invalid month" }
83+
84+
val monthInYear = Month.values()[month - 1]
85+
86+
require(day in 1..monthInYear.numberOfDays) { "$day is not valid day number of $month" }
87+
require(year > 0) { "Year must be >= 1." }
88+
89+
val imperialDay = (year - 1) * DAYS_IN_YEAR +
90+
// Days in previous months
91+
Month.values().takeWhile { it != monthInYear }
92+
.map { it.numberOfDays + if (it.standaloneDayAtTheBeginning != null) 1 else 0 }
93+
.sum() +
94+
// Days in current month
95+
if (monthInYear.standaloneDayAtTheBeginning != null) day else day - 1
96+
97+
return ImperialDate(imperialDay)
98+
}
99+
100+
fun of(day: Int, month: Month, year: Int): ImperialDate = of(day, month.ordinal + 1, year)
101+
}
102+
103+
val dayOfWeek: DayOfWeek?
104+
get() {
105+
return day.fold(
106+
{ null },
107+
{ (day, month) ->
108+
val firstWeekDayOfYear =
109+
(year - 1) * (DAYS_IN_YEAR - StandaloneDay.values().size) + DayOfWeek.MARKTAG.ordinal
110+
111+
val firstWeekDayOfMonth = Month.values()
112+
.takeWhile { it < month }
113+
.map { it.numberOfDays }
114+
.sum() + firstWeekDayOfYear
115+
116+
DayOfWeek.values()[(firstWeekDayOfMonth + day - 1) % DayOfWeek.values().size]
117+
}
118+
)
119+
}
120+
121+
val dayOfYear: Int get() = imperialDay % DAYS_IN_YEAR + 1
122+
val year: Int get() = imperialDay / DAYS_IN_YEAR + if (imperialDay < 0) -1 else 1
123+
124+
fun addDay() = addDays(1)
125+
fun removeDay() = addDays(-1)
126+
fun addWeek() = addDays(DAYS_IN_WEEK)
127+
128+
override fun compareTo(other: ImperialDate) = imperialDay.compareTo(other.imperialDay)
129+
130+
private fun addDays(days: Int) = ImperialDate(imperialDay + days)
131+
132+
val day: Either<StandaloneDay, Pair<Int, Month>>
133+
get() {
134+
var day = dayOfYear
135+
136+
for (month in Month.values()) {
137+
month.standaloneDayAtTheBeginning?.let {
138+
if (day == 1) {
139+
return Left(it)
140+
}
141+
142+
day--
143+
}
144+
145+
if (day <= month.numberOfDays) {
146+
return Right(day to month)
147+
}
148+
149+
day -= month.numberOfDays
150+
}
151+
152+
error("Could not compute correct day")
153+
}
154+
155+
fun format(): String {
156+
return day.fold(
157+
{ "${it.readableName}, $year" },
158+
{ "${it.first} ${it.second.readableName}, ${dayOfWeek?.readableName}, $year" }
159+
)
160+
}
161+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package cz.muni.fi.rpg.model.domain.party.time
2+
3+
enum class MannsliebPhase(val readableName: String) {
4+
NEW_MOON("New moon"),
5+
FULL_MOON("Full moon"),
6+
WAXING("Waxing"),
7+
WANING("Waning");
8+
9+
companion object {
10+
private const val MANNSLIEB_WAXING_PERIOD = 13
11+
private const val MANNSLIEB_WANING_PERIOD = 12
12+
13+
fun at(date: ImperialDate): MannsliebPhase {
14+
return when((date.dayOfYear - 1) % (MANNSLIEB_WAXING_PERIOD + MANNSLIEB_WANING_PERIOD)) {
15+
0 -> FULL_MOON
16+
in 1..MANNSLIEB_WANING_PERIOD -> WANING
17+
MANNSLIEB_WANING_PERIOD + 1 -> NEW_MOON
18+
else -> WAXING
19+
}
20+
}
21+
}
22+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package cz.muni.fi.rpg.model.domain.party.time
2+
3+
enum class YearSeason(val readableName: String) {
4+
5+
SPRING("Spring"),
6+
SUMMER("Summer"),
7+
FALL("Fall"),
8+
WINTER("Winter");
9+
10+
companion object {
11+
private const val FIRST_DAY_OF_SPRING = 17
12+
private const val FIRST_DAY_OF_SUMMER = 117
13+
private const val FIRST_DAY_OF_FALL = 217
14+
private const val FIRST_DAY_OF_WINTER = 317
15+
16+
fun at(date: ImperialDate) = when (date.dayOfYear - 1) {
17+
in FIRST_DAY_OF_SPRING until FIRST_DAY_OF_SUMMER -> SPRING
18+
in FIRST_DAY_OF_SUMMER until FIRST_DAY_OF_FALL -> SUMMER
19+
in FIRST_DAY_OF_FALL until FIRST_DAY_OF_WINTER -> FALL
20+
else -> WINTER
21+
}
22+
}
23+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package cz.muni.fi.rpg.ui.common
2+
3+
import android.graphics.*
4+
import android.graphics.drawable.Drawable
5+
import androidx.annotation.ColorInt
6+
7+
8+
class ColorCircleDrawable(@ColorInt color: Int) : Drawable() {
9+
10+
private val paint: Paint = Paint(Paint.ANTI_ALIAS_FLAG)
11+
private var radius = 0
12+
13+
init {
14+
paint.color = color
15+
}
16+
17+
override fun draw(canvas: Canvas) {
18+
val bounds = bounds
19+
canvas.drawCircle(
20+
bounds.centerX().toFloat(),
21+
bounds.centerY().toFloat(),
22+
radius.toFloat(),
23+
paint
24+
)
25+
}
26+
27+
override fun onBoundsChange(bounds: Rect) {
28+
super.onBoundsChange(bounds)
29+
radius = Math.min(bounds.width(), bounds.height()) / 2
30+
}
31+
32+
override fun setAlpha(alpha: Int) {
33+
paint.alpha = alpha
34+
}
35+
36+
override fun setColorFilter(cf: ColorFilter?) {
37+
paint.colorFilter = cf
38+
}
39+
40+
override fun getOpacity(): Int {
41+
return PixelFormat.TRANSLUCENT
42+
}
43+
44+
}

0 commit comments

Comments
 (0)