diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index d534f814..77eeaac0 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -183,6 +183,11 @@
android:launchMode="singleTop"
android:screenOrientation="portrait" />
+
+
+ if (isChecked && this.model.selectedTimeUnit != TimeUnit.DAY) {
+ this.model.selectedTimeUnit = TimeUnit.DAY
+ this.launch()
+ }
+ }
+
+ this.binding.monthUnit.setOnCheckedChangeListener { _, isChecked ->
+ if (isChecked && this.model.selectedTimeUnit != TimeUnit.MONTH) {
+ this.model.selectedTimeUnit = TimeUnit.MONTH
+ this.launch()
+ }
+ }
+
+ }
+
+ private fun initData() {
+ this.launch()
+ }
+
+ private fun launch() {
+ this.model.buildDataStructure()
+ this.dataAdapter.notifyDataSetChanged()
}
}
\ No newline at end of file
diff --git a/app/src/main/java/net/pokeranalytics/android/ui/modules/calendar/GridCalendarViewModel.kt b/app/src/main/java/net/pokeranalytics/android/ui/modules/calendar/GridCalendarViewModel.kt
new file mode 100644
index 00000000..f123087e
--- /dev/null
+++ b/app/src/main/java/net/pokeranalytics/android/ui/modules/calendar/GridCalendarViewModel.kt
@@ -0,0 +1,223 @@
+package net.pokeranalytics.android.ui.modules.calendar
+
+import androidx.lifecycle.ViewModel
+import io.realm.Realm
+import net.pokeranalytics.android.R
+import net.pokeranalytics.android.exceptions.PAIllegalStateException
+import net.pokeranalytics.android.model.filter.Query
+import net.pokeranalytics.android.model.filter.QueryCondition
+import net.pokeranalytics.android.model.realm.Session
+import net.pokeranalytics.android.ui.adapter.RowRepresentableDataSource
+import net.pokeranalytics.android.ui.view.RowRepresentable
+import net.pokeranalytics.android.ui.view.RowViewType
+import net.pokeranalytics.android.util.extensions.*
+import java.util.*
+import kotlin.collections.HashMap
+
+class GridCalendarViewModel : ViewModel(), RowRepresentableDataSource {
+
+ var selectedTimeUnit: TimeUnit = TimeUnit.DAY
+
+ private var groups: TreeSet = TreeSet()
+
+ private var results: HashMap = HashMap()
+
+ private fun addGroup(date: Date) {
+ this.groups.add(date)
+ }
+
+ private fun result(index: Int): CalendarItemCell {
+ val group = this.groups.elementAt(index)
+ return this.results[group] ?: throw PAIllegalStateException("item not found")
+ }
+
+ private fun addResult(group: Date, result: CellResult, unit: TimeUnit) {
+ this.addGroup(group)
+ this.results[group]?.add(result) ?: run {
+ this.results[group] = CalendarItemCell(group, result, unit)
+ }
+
+ }
+
+ private fun clear() {
+ this.groups.clear()
+ this.results.clear()
+ }
+
+ fun buildDataStructure() {
+
+// Timber.d(">>> Start buildDataStructure: ${this.selectedTimeUnit}")
+
+ this.clear()
+
+ val realm = Realm.getDefaultInstance()
+ val query = Query(QueryCondition.DateNotNull, QueryCondition.EndDateNotNull)
+ val sessions = realm.findAll(query, "startDate")
+
+ val groupedSessions = when (this.selectedTimeUnit) {
+ TimeUnit.DAY -> sessions.groupBy { it.startDate!!.startOfDay() }
+ TimeUnit.MONTH -> sessions.groupBy { it.startDate!!.startOfMonth() }
+ }
+
+ val firstDate = sessions.firstOrNull()?.startDate?.startOf(this.selectedTimeUnit.groupingUnit)
+ val lastDate = sessions.lastOrNull()?.startDate?.endOf(this.selectedTimeUnit.groupingUnit)
+
+// Timber.d("f = $firstDate, e = $lastDate")
+
+ if (firstDate != null && lastDate != null) {
+ var tmpDate: Date = firstDate.startOfDay()
+
+ val calendar = Calendar.getInstance()
+ while (tmpDate.time <= lastDate.time) {
+
+ val result = groupedSessions[tmpDate]?.let { bucket ->
+ if (bucket.sumByDouble {
+ it.result?.net ?: 0.0
+ } > 0.0) CellResult.POSITIVE else CellResult.NEGATIVE
+ } ?: run {
+ CellResult.EMPTY
+ }
+
+ val groupingDate = tmpDate.startOf(this.selectedTimeUnit.groupingUnit)
+ this.addResult(groupingDate, result, this.selectedTimeUnit)
+
+ // change tmp date
+ calendar.time = tmpDate
+ calendar.add(selectedTimeUnit.calendarUnit, 1)
+ tmpDate = calendar.time
+ }
+
+ }
+
+ }
+
+ // RowRepresentableDataSource
+
+ override fun adapterRows(): List {
+ return this.groups.descendingSet().map { this.results[it]!! }
+ }
+
+ override fun rowRepresentableForPosition(position: Int): RowRepresentable {
+ return this.result(position)
+ }
+
+ override fun numberOfRows(): Int {
+ return this.results.size
+ }
+
+ override fun viewTypeForPosition(position: Int): Int {
+ return RowViewType.CALENDAR_GRID_CELL.ordinal
+ }
+
+}
+
+class CalendarItemCell(var date: Date, result: CellResult, var unit: TimeUnit) : RowRepresentable, RowRepresentableDataSource {
+
+ private var results: MutableList = mutableListOf(result)
+ private set
+
+ fun add(result: CellResult) {
+ this.results.add(result)
+ }
+
+ val size: Int = this.results.size
+
+ override val viewType: Int = RowViewType.CALENDAR_GRID_CELL.ordinal
+
+ val title: String
+ get() {
+ return when (unit) {
+ TimeUnit.DAY -> date.getMonthAndYear()
+ TimeUnit.MONTH -> date.getDateYear()
+ }
+ }
+
+ // RowRepresentableDataSource
+
+ override fun adapterRows(): List {
+ return this.results
+ }
+
+ override fun rowRepresentableForPosition(position: Int): RowRepresentable {
+ return this.results[position]
+ }
+
+ override fun numberOfRows(): Int {
+ return this.results.size
+ }
+
+ override fun viewTypeForPosition(position: Int): Int {
+ return RowViewType.CALENDAR_TIME_UNIT_CELL.ordinal
+ }
+
+}
+
+enum class TimeUnit {
+ DAY,
+ MONTH;
+
+ val calendarUnit: Int
+ get() {
+ return when (this) {
+ DAY -> Calendar.DAY_OF_MONTH
+ MONTH -> Calendar.MONTH
+ }
+ }
+
+ val groupingUnit: Int
+ get() {
+ return when (this) {
+ DAY -> Calendar.MONTH
+ MONTH -> Calendar.YEAR
+ }
+ }
+
+ val spanCount: Int
+ get() {
+ return when (this) {
+ DAY -> 7
+ MONTH -> 4
+ }
+ }
+
+}
+
+enum class CellResult : RowRepresentable {
+ POSITIVE,
+ NEGATIVE,
+ EMPTY;
+
+ val background: Int
+ get() {
+ return when (this) {
+ POSITIVE -> R.drawable.rounded_green_rect
+ NEGATIVE -> R.drawable.rounded_red_rect
+ EMPTY -> R.drawable.rounded_grey_rect
+ }
+ }
+
+ override val viewType: Int = RowViewType.CALENDAR_TIME_UNIT_CELL.ordinal
+
+}
+
+private fun Date.startOf(unit: Int): Date {
+
+ return when (unit) {
+ Calendar.DAY_OF_MONTH -> this.startOfDay()
+ Calendar.MONTH -> this.startOfMonth()
+ Calendar.YEAR -> this.startOfYear()
+ else -> throw PAIllegalStateException("Un-managed case: $unit")
+ }
+
+}
+
+private fun Date.endOf(unit: Int): Date {
+
+ return when (unit) {
+ Calendar.DAY_OF_MONTH -> this.endOfDay()
+ Calendar.MONTH -> this.endOfMonth()
+ Calendar.YEAR -> this.endOfYear()
+ else -> throw PAIllegalStateException("Un-managed case: $unit")
+ }
+
+}
diff --git a/app/src/main/java/net/pokeranalytics/android/ui/modules/handhistory/views/KeyboardActionView.kt b/app/src/main/java/net/pokeranalytics/android/ui/modules/handhistory/views/KeyboardActionView.kt
index 6ea9db84..447ab4cd 100644
--- a/app/src/main/java/net/pokeranalytics/android/ui/modules/handhistory/views/KeyboardActionView.kt
+++ b/app/src/main/java/net/pokeranalytics/android/ui/modules/handhistory/views/KeyboardActionView.kt
@@ -41,12 +41,12 @@ class KeyboardActionView(context: Context) : AbstractKeyboardView(context),
// Action recycler
val spanCount = 3
- val viewManager = GridLayoutManager(context, spanCount)
this.dataAdapter = RowRepresentableAdapter(this, this)
val spacing = 2.px
val includeEdge = false
+ val viewManager = GridLayoutManager(context, spanCount)
actionRecyclerView.apply {
setHasFixedSize(true)
layoutManager = viewManager
diff --git a/app/src/main/java/net/pokeranalytics/android/ui/view/RowRepresentable.kt b/app/src/main/java/net/pokeranalytics/android/ui/view/RowRepresentable.kt
index 43886371..29d3fa71 100644
--- a/app/src/main/java/net/pokeranalytics/android/ui/view/RowRepresentable.kt
+++ b/app/src/main/java/net/pokeranalytics/android/ui/view/RowRepresentable.kt
@@ -2,7 +2,6 @@ package net.pokeranalytics.android.ui.view
import android.content.Context
import net.pokeranalytics.android.model.LiveData
-import net.pokeranalytics.android.model.interfaces.Identifiable
import net.pokeranalytics.android.ui.fragment.components.bottomsheet.BottomSheetType
import net.pokeranalytics.android.util.NULL_TEXT
@@ -14,6 +13,7 @@ interface RowRepresentable : Displayable, EditDataSource, ImageDecorator {
fun getDisplayName(context: Context): String {
return NULL_TEXT
}
+
}
interface EditDataSource {
diff --git a/app/src/main/java/net/pokeranalytics/android/ui/view/RowViewType.kt b/app/src/main/java/net/pokeranalytics/android/ui/view/RowViewType.kt
index d357fcb1..5bbd4ca1 100644
--- a/app/src/main/java/net/pokeranalytics/android/ui/view/RowViewType.kt
+++ b/app/src/main/java/net/pokeranalytics/android/ui/view/RowViewType.kt
@@ -7,14 +7,18 @@ import android.widget.FrameLayout
import androidx.appcompat.widget.AppCompatImageView
import androidx.appcompat.widget.AppCompatTextView
import androidx.constraintlayout.widget.ConstraintLayout
+import androidx.core.content.ContextCompat
import androidx.core.view.isVisible
import androidx.core.widget.ContentLoadingProgressBar
+import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.github.mikephil.charting.charts.BarChart
import com.github.mikephil.charting.charts.LineChart
import com.github.mikephil.charting.data.*
import com.google.android.material.chip.Chip
import com.google.android.material.chip.ChipGroup
+import kotlinx.android.synthetic.main.cell_calendar_time_unit.view.*
+import kotlinx.android.synthetic.main.row_recycler.view.*
import net.pokeranalytics.android.R
import net.pokeranalytics.android.calculus.ComputedStat
import net.pokeranalytics.android.calculus.Stat
@@ -27,12 +31,16 @@ import net.pokeranalytics.android.model.realm.Session
import net.pokeranalytics.android.model.realm.Transaction
import net.pokeranalytics.android.ui.adapter.BindableHolder
import net.pokeranalytics.android.ui.adapter.RecyclerAdapter
+import net.pokeranalytics.android.ui.adapter.RowRepresentableAdapter
import net.pokeranalytics.android.ui.extensions.ChipGroupExtension
+import net.pokeranalytics.android.ui.extensions.dp
import net.pokeranalytics.android.ui.extensions.px
import net.pokeranalytics.android.ui.extensions.setTextFormat
import net.pokeranalytics.android.ui.graph.Graph
import net.pokeranalytics.android.ui.modules.bankroll.BankrollRowRepresentable
import net.pokeranalytics.android.ui.graph.setStyle
+import net.pokeranalytics.android.ui.modules.calendar.CalendarItemCell
+import net.pokeranalytics.android.ui.modules.calendar.CellResult
import net.pokeranalytics.android.ui.modules.handhistory.views.RowHandHistoryViewHolder
import net.pokeranalytics.android.ui.view.holder.RowViewHolder
import net.pokeranalytics.android.ui.view.rows.*
@@ -86,6 +94,8 @@ enum class RowViewType(private var layoutRes: Int) : ViewIdentifier {
ROW_PLAYER(R.layout.row_player),
ROW_PLAYER_IMAGE(R.layout.row_player_image),
HAND_HISTORY(R.layout.row_hand_history_view),
+ CALENDAR_GRID_CELL(R.layout.cell_calendar_grid),
+ CALENDAR_TIME_UNIT_CELL(R.layout.cell_calendar_time_unit),
// ROW_HAND_ACTION(R.layout.row_hand_action),
// ROW_HAND_STREET(R.layout.row_hand_cards),
@@ -147,6 +157,9 @@ enum class RowViewType(private var layoutRes: Int) : ViewIdentifier {
// ROW_HAND_ACTION -> RowHandAction(layout)
// ROW_HAND_STREET -> RowHandStreet(layout)
+ CALENDAR_GRID_CELL -> CalendarGridCellHolder(layout)
+ CALENDAR_TIME_UNIT_CELL -> CalendarTimeUnitCellHolder(layout)
+
// Separator
SEPARATOR -> SeparatorViewHolder(layout)
@@ -526,7 +539,6 @@ enum class RowViewType(private var layoutRes: Int) : ViewIdentifier {
}
}
-
/**
* Display a player image view
*/
@@ -569,7 +581,6 @@ enum class RowViewType(private var layoutRes: Int) : ViewIdentifier {
}
}
-
/**
* Display a separator
*/
@@ -579,4 +590,94 @@ enum class RowViewType(private var layoutRes: Int) : ViewIdentifier {
}
}
+ inner class CalendarGridCellHolder(itemView: View) : RecyclerView.ViewHolder(itemView),
+ BindableHolder {
+
+ private var spanCount = 7
+
+ init {
+
+ itemView.findViewById(R.id.recyclerView)?.let { recyclerView ->
+
+// val spanCount = row.unit.spanCount
+ val spacing = 4.px
+ val includeEdge = true
+
+ val viewManager = object : GridLayoutManager(itemView.context, spanCount) {
+ override fun checkLayoutParams(lp: RecyclerView.LayoutParams?): Boolean {
+ val side = width / this.spanCount - 4.px
+ lp?.let { params ->
+ params.width = side
+ params.height = side
+ }
+ return true
+ }
+ }
+
+ recyclerView.apply {
+ setHasFixedSize(true)
+ layoutManager = viewManager
+ addItemDecoration(GridSpacingItemDecoration(spanCount, spacing, includeEdge))
+ }
+
+ }
+ }
+
+ private fun setLayoutManager(spanCount: Int) {
+ itemView.findViewById(R.id.recyclerView)?.let { recyclerView ->
+
+ val viewManager = object : GridLayoutManager(itemView.context, spanCount) {
+ override fun checkLayoutParams(lp: RecyclerView.LayoutParams?): Boolean {
+ val side = width / spanCount - 4.px
+ lp?.let { params ->
+ params.width = side
+ params.height = side
+ }
+ return true
+ }
+ }
+ recyclerView.layoutManager = viewManager
+ }
+ }
+
+ override fun onBind(position: Int, row: RowRepresentable, adapter: RecyclerAdapter) {
+
+ if (row is CalendarItemCell) {
+ val sc = row.unit.spanCount
+
+ if (this.spanCount != sc) {
+ setLayoutManager(sc)
+ this.spanCount = sc
+ }
+
+ itemView.findViewById(R.id.recyclerView)?.let { recyclerView ->
+ if (recyclerView.adapter == null) {
+ recyclerView.adapter = RowRepresentableAdapter(row)
+ } else {
+ (recyclerView.adapter as RowRepresentableAdapter).changeDataSource(row)
+ }
+ recyclerView.adapter?.notifyDataSetChanged()
+ }
+
+ itemView.findViewById(R.id.title)?.let { textView ->
+ textView.text = row.title
+ }
+
+ }
+ }
+
+ }
+
+ inner class CalendarTimeUnitCellHolder(itemView: View) : RecyclerView.ViewHolder(itemView),
+ BindableHolder {
+
+ override fun onBind(position: Int, row: RowRepresentable, adapter: RecyclerAdapter) {
+
+ if (row is CellResult) {
+ itemView.timeUnit.background = ContextCompat.getDrawable(itemView.context, row.background)
+ }
+
+ }
+ }
+
}
\ No newline at end of file
diff --git a/app/src/main/java/net/pokeranalytics/android/util/csv/PACSVDescriptor.kt b/app/src/main/java/net/pokeranalytics/android/util/csv/PACSVDescriptor.kt
index e0befb16..312ac9f3 100644
--- a/app/src/main/java/net/pokeranalytics/android/util/csv/PACSVDescriptor.kt
+++ b/app/src/main/java/net/pokeranalytics/android/util/csv/PACSVDescriptor.kt
@@ -121,6 +121,7 @@ abstract class PACSVDescriptor(source: DataSource,
is SessionField.Addon -> additionalBuyins += field.parse(value) ?: 0.0
is SessionField.Rebuy -> additionalBuyins += field.parse(value) ?: 0.0
is SessionField.Tips -> session.result?.tips = field.parse(value)
+ is SessionField.HandsCount -> session.handsCount = field.parse(value)
is SessionField.Break -> {
field.parse(value)?.let {
session.breakDuration = it.toLong()
diff --git a/app/src/main/java/net/pokeranalytics/android/util/csv/ProductCSVDescriptors.kt b/app/src/main/java/net/pokeranalytics/android/util/csv/ProductCSVDescriptors.kt
index 3a137468..7f17162d 100644
--- a/app/src/main/java/net/pokeranalytics/android/util/csv/ProductCSVDescriptors.kt
+++ b/app/src/main/java/net/pokeranalytics/android/util/csv/ProductCSVDescriptors.kt
@@ -220,6 +220,7 @@ class ProductCSVDescriptors {
SessionField.CashedOut("Cashed Out"),
SessionField.NetResult("Online Net"),
SessionField.Tips("Tips"),
+ SessionField.HandsCount("Hands Count"),
SessionField.LimitType("Limit"),
SessionField.Game("Game"),
SessionField.TableSize("Table Size"),
diff --git a/app/src/main/java/net/pokeranalytics/android/util/csv/SessionCSVDescriptor.kt b/app/src/main/java/net/pokeranalytics/android/util/csv/SessionCSVDescriptor.kt
index 3e4b49fd..52af5f00 100644
--- a/app/src/main/java/net/pokeranalytics/android/util/csv/SessionCSVDescriptor.kt
+++ b/app/src/main/java/net/pokeranalytics/android/util/csv/SessionCSVDescriptor.kt
@@ -51,6 +51,7 @@ class SessionCSVDescriptor(source: DataSource, isTournament: Boolean?, vararg el
is SessionField.CashedOut -> field.format(data.result?.cashout)
is SessionField.NetResult -> field.format(data.result?.netResult)
is SessionField.Tips -> field.format(data.result?.tips)
+ is SessionField.HandsCount -> field.format(data.handsCount)
is SessionField.LimitType -> {
data.limit?.let { limit ->
Limit.values()[limit].longName
diff --git a/app/src/main/java/net/pokeranalytics/android/util/csv/SessionField.kt b/app/src/main/java/net/pokeranalytics/android/util/csv/SessionField.kt
index 842c92c4..c306c4d0 100644
--- a/app/src/main/java/net/pokeranalytics/android/util/csv/SessionField.kt
+++ b/app/src/main/java/net/pokeranalytics/android/util/csv/SessionField.kt
@@ -121,6 +121,11 @@ sealed class SessionField {
override var callback: ((String) -> Double?)? = null
) : NumberCSVField
+ data class HandsCount(
+ override var header: String,
+ override var callback: ((String) -> Int?)? = null
+ ) : IntCSVField
+
data class SmallBlind(
override var header: String,
override var callback: ((String) -> Double?)? = null
diff --git a/app/src/main/java/net/pokeranalytics/android/util/extensions/DateExtension.kt b/app/src/main/java/net/pokeranalytics/android/util/extensions/DateExtension.kt
index 490b750a..c80cc2ef 100644
--- a/app/src/main/java/net/pokeranalytics/android/util/extensions/DateExtension.kt
+++ b/app/src/main/java/net/pokeranalytics/android/util/extensions/DateExtension.kt
@@ -165,6 +165,16 @@ fun Date.startOfMonth(): Date {
return calendar.time
}
+// Return the date of the end of the current date
+fun Date.endOfMonth(): Date {
+ val calendar = Calendar.getInstance()
+ calendar.time = this
+ calendar.add(Calendar.MONTH, 1)
+ calendar.set(Calendar.DAY_OF_MONTH, 1)
+ calendar.add(Calendar.DATE, -1)
+ return calendar.time
+}
+
// Return the date of the beginning of the current year
fun Date.startOfYear(): Date {
val calendar = Calendar.getInstance()
@@ -173,6 +183,15 @@ fun Date.startOfYear(): Date {
return calendar.time
}
+// Return the date of the end of the current date
+fun Date.endOfYear(): Date {
+ val calendar = Calendar.getInstance()
+ calendar.time = this
+ calendar.set(Calendar.MONTH, 11)
+ calendar.set(Calendar.DAY_OF_MONTH, 31)
+ return calendar.time
+}
+
// Return the number of seconds until the next minute
fun Date.getNextMinuteInseconds(): Int {
return (getNextMinuteInMilliseconds() / 1000).toInt()
diff --git a/app/src/main/java/net/pokeranalytics/android/util/extensions/RealmExtensions.kt b/app/src/main/java/net/pokeranalytics/android/util/extensions/RealmExtensions.kt
index 8bf53edf..0ebf60fd 100644
--- a/app/src/main/java/net/pokeranalytics/android/util/extensions/RealmExtensions.kt
+++ b/app/src/main/java/net/pokeranalytics/android/util/extensions/RealmExtensions.kt
@@ -1,6 +1,8 @@
package net.pokeranalytics.android.util.extensions
import io.realm.*
+import net.pokeranalytics.android.model.filter.Filterable
+import net.pokeranalytics.android.model.filter.Query
import net.pokeranalytics.android.model.interfaces.UsageCountable
import net.pokeranalytics.android.model.interfaces.Identifiable
import net.pokeranalytics.android.model.interfaces.NameManageable
@@ -119,4 +121,8 @@ fun < T : RealmModel> Realm.find(clazz: Class, searchContent: String?) : Real
val sortField = arrayOf("name")
val resultSort = arrayOf(Sort.ASCENDING)
return items.sort(sortField, resultSort)
-}
\ No newline at end of file
+}
+
+inline fun Realm.findAll(query: Query, sortField: String? = null): RealmResults {
+ return Filter.queryOn(this, query, sortField)
+}
diff --git a/app/src/main/res/drawable/rounded_grey_rect.xml b/app/src/main/res/drawable/rounded_grey_rect.xml
new file mode 100644
index 00000000..67e39ad4
--- /dev/null
+++ b/app/src/main/res/drawable/rounded_grey_rect.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout-sw320dp/fragment_calendar.xml b/app/src/main/res/layout-sw320dp/fragment_calendar.xml
index 96386cec..b4ff7686 100644
--- a/app/src/main/res/layout-sw320dp/fragment_calendar.xml
+++ b/app/src/main/res/layout-sw320dp/fragment_calendar.xml
@@ -123,4 +123,20 @@
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/appBar" />
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout-sw400dp/fragment_calendar.xml b/app/src/main/res/layout-sw400dp/fragment_calendar.xml
index 96386cec..b4ff7686 100644
--- a/app/src/main/res/layout-sw400dp/fragment_calendar.xml
+++ b/app/src/main/res/layout-sw400dp/fragment_calendar.xml
@@ -123,4 +123,20 @@
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/appBar" />
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_grid_calendar.xml b/app/src/main/res/layout/activity_grid_calendar.xml
index d4c76fd8..8a3d48dc 100644
--- a/app/src/main/res/layout/activity_grid_calendar.xml
+++ b/app/src/main/res/layout/activity_grid_calendar.xml
@@ -6,7 +6,7 @@
android:orientation="vertical">
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/cell_calendar_time_unit.xml b/app/src/main/res/layout/cell_calendar_time_unit.xml
new file mode 100644
index 00000000..5ac88985
--- /dev/null
+++ b/app/src/main/res/layout/cell_calendar_time_unit.xml
@@ -0,0 +1,8 @@
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_calendar.xml b/app/src/main/res/layout/fragment_calendar.xml
index a7bbd39b..9096ec72 100644
--- a/app/src/main/res/layout/fragment_calendar.xml
+++ b/app/src/main/res/layout/fragment_calendar.xml
@@ -127,4 +127,20 @@
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/appBar" />
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_grid_calendar.xml b/app/src/main/res/layout/fragment_grid_calendar.xml
index 61a4490a..95b6a1f4 100644
--- a/app/src/main/res/layout/fragment_grid_calendar.xml
+++ b/app/src/main/res/layout/fragment_grid_calendar.xml
@@ -1,6 +1,77 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file