parent
5f2ffdb306
commit
4044cee176
@ -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<Date> = TreeSet<Date>() |
||||
|
||||
private var results: HashMap<Date, CalendarItemCell> = 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<Session>(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<RowRepresentable> { |
||||
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<CellResult> = 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<RowRepresentable> { |
||||
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") |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,6 @@ |
||||
<?xml version="1.0" encoding="UTF-8"?> |
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"> |
||||
<solid android:color="@color/grey"/> |
||||
<!-- <stroke android:width="3dp" android:color="#B1BCBE" />--> |
||||
<corners android:radius="8dp"/> |
||||
</shape> |
||||
@ -0,0 +1,29 @@ |
||||
<?xml version="1.0" encoding="utf-8"?> |
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" |
||||
android:layout_width="match_parent" |
||||
android:layout_height="wrap_content" |
||||
xmlns:app="http://schemas.android.com/apk/res-auto"> |
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView |
||||
android:id="@+id/title" |
||||
android:layout_width="0dp" |
||||
android:layout_height="wrap_content" |
||||
android:layout_marginTop="16dp" |
||||
android:layout_marginBottom="16dp" |
||||
style="@style/PokerAnalyticsTheme.TextView.Header" |
||||
app:layout_constraintTop_toTopOf="parent" |
||||
app:layout_constraintStart_toStartOf="parent" |
||||
app:layout_constraintEnd_toEndOf="parent" |
||||
/> |
||||
|
||||
<androidx.recyclerview.widget.RecyclerView |
||||
android:id="@+id/recyclerView" |
||||
android:layout_width="match_parent" |
||||
android:layout_height="wrap_content" |
||||
app:layout_constraintTop_toBottomOf="@id/title" |
||||
app:layout_constraintStart_toStartOf="parent" |
||||
app:layout_constraintEnd_toEndOf="parent" |
||||
app:layout_constraintBottom_toBottomOf="parent" |
||||
/> |
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout> |
||||
@ -0,0 +1,8 @@ |
||||
<?xml version="1.0" encoding="utf-8"?> |
||||
<androidx.constraintlayout.widget.ConstraintLayout |
||||
xmlns:android="http://schemas.android.com/apk/res/android" |
||||
android:id="@+id/timeUnit" |
||||
android:layout_width="16dp" |
||||
android:layout_height="16dp"> |
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout> |
||||
@ -1,6 +1,77 @@ |
||||
<?xml version="1.0" encoding="utf-8"?> |
||||
<androidx.constraintlayout.widget.ConstraintLayout |
||||
xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" |
||||
xmlns:android="http://schemas.android.com/apk/res/android" |
||||
xmlns:app="http://schemas.android.com/apk/res-auto" |
||||
android:layout_width="match_parent" |
||||
android:layout_height="match_parent"> |
||||
|
||||
<com.google.android.material.appbar.AppBarLayout |
||||
android:id="@+id/appBar" |
||||
android:layout_width="match_parent" |
||||
android:layout_height="128dp" |
||||
android:theme="@style/PokerAnalyticsTheme.Toolbar.Session" |
||||
app:layout_constraintEnd_toEndOf="parent" |
||||
app:layout_constraintStart_toStartOf="parent" |
||||
app:layout_constraintTop_toTopOf="parent"> |
||||
|
||||
<com.google.android.material.appbar.CollapsingToolbarLayout |
||||
android:id="@+id/collapsingToolbar" |
||||
android:layout_width="match_parent" |
||||
android:layout_height="match_parent" |
||||
app:collapsedTitleTextAppearance="@style/PokerAnalyticsTheme.Toolbar.CollapsedTitleAppearance" |
||||
app:contentScrim="?attr/colorPrimary" |
||||
app:expandedTitleGravity="bottom" |
||||
app:expandedTitleMarginStart="72dp" |
||||
app:expandedTitleTextAppearance="@style/PokerAnalyticsTheme.Toolbar.ExpandedTitleAppearance" |
||||
app:layout_scrollFlags="scroll|exitUntilCollapsed|snap"> |
||||
|
||||
<androidx.appcompat.widget.Toolbar |
||||
android:id="@+id/toolbar" |
||||
android:layout_width="match_parent" |
||||
android:layout_height="?attr/actionBarSize" |
||||
app:layout_collapseMode="pin" |
||||
app:title="@string/calendar" |
||||
app:titleTextColor="@color/white" /> |
||||
|
||||
</com.google.android.material.appbar.CollapsingToolbarLayout> |
||||
|
||||
</com.google.android.material.appbar.AppBarLayout> |
||||
|
||||
<com.google.android.material.chip.ChipGroup |
||||
android:id="@+id/timeUnits" |
||||
android:layout_width="wrap_content" |
||||
android:layout_height="wrap_content" |
||||
app:layout_constraintTop_toBottomOf="@id/appBar" |
||||
app:layout_constraintStart_toStartOf="parent" |
||||
app:layout_constraintEnd_toEndOf="parent" |
||||
app:singleSelection="true" |
||||
app:selectionRequired="true" |
||||
app:chipSpacing="8dp"> |
||||
|
||||
<com.google.android.material.chip.Chip |
||||
android:id="@+id/dayUnit" |
||||
android:layout_width="wrap_content" |
||||
android:layout_height="wrap_content" |
||||
android:text="@string/day" |
||||
android:checked="true" /> |
||||
|
||||
<com.google.android.material.chip.Chip |
||||
android:id="@+id/monthUnit" |
||||
android:layout_width="wrap_content" |
||||
android:layout_height="wrap_content" |
||||
android:text="@string/month" /> |
||||
|
||||
</com.google.android.material.chip.ChipGroup> |
||||
|
||||
<androidx.recyclerview.widget.RecyclerView |
||||
android:id="@+id/recyclerView" |
||||
android:layout_width="match_parent" |
||||
android:layout_height="0dp" |
||||
android:padding="16dp" |
||||
app:layout_constraintTop_toBottomOf="@id/timeUnits" |
||||
app:layout_constraintStart_toStartOf="parent" |
||||
app:layout_constraintEnd_toEndOf="parent" |
||||
app:layout_constraintBottom_toBottomOf="parent" |
||||
/> |
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout> |
||||
Loading…
Reference in new issue