Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package org.wikipedia.games.db

import androidx.room.Entity
import androidx.room.Ignore
import androidx.room.PrimaryKey
import java.time.LocalDate

@Entity
data class DailyGameHistory(
Expand All @@ -14,4 +16,7 @@ data class DailyGameHistory(
var score: Int,
var playType: Int,
var gameData: String?
)
) {
@Ignore
val date: LocalDate = LocalDate.of(year, month, day)
}
63 changes: 11 additions & 52 deletions app/src/main/java/org/wikipedia/games/onthisday/DateDecorator.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,31 +2,18 @@ package org.wikipedia.games.onthisday

import android.content.Context
import android.content.res.ColorStateList
import android.os.Parcel
import android.os.Parcelable
import androidx.core.content.ContextCompat
import com.google.android.material.datepicker.DayViewDecorator
import kotlinx.parcelize.Parcelize
import org.wikipedia.R
import java.util.Calendar
import java.util.Date
import java.time.LocalDate

@Parcelize
class DateDecorator(
private val startDate: Date,
private val endDate: Date,
private val scoreData: Map<Long, Int>
private val startDate: LocalDate,
private val endDate: LocalDate,
private val scoreData: Map<LocalDate, Int>
) : DayViewDecorator() {

private val calendar = Calendar.getInstance()

private fun isDateInRange(year: Int, month: Int, day: Int): Boolean {
synchronized(calendar) {
calendar.set(year, month, day, 0, 0, 0)
calendar.set(Calendar.MILLISECOND, 0)

return !calendar.before(startDate) && !calendar.after(endDate)
}
}

override fun getBackgroundColor(
context: Context,
year: Int,
Expand All @@ -35,14 +22,12 @@ class DateDecorator(
valid: Boolean,
selected: Boolean
): ColorStateList? {
if (!isDateInRange(year, month, day)) {
val date = LocalDate.of(year, month + 1, day)
if (date !in startDate..endDate) {
return null
}

val dateKey = getDateKey(year, month + 1, day)
val score = scoreData[dateKey]

return when (score) {
return when (scoreData[date]) {
0, 1, 2 -> ColorStateList.valueOf(ContextCompat.getColor(context, R.color.yellow200))
3, 4 -> ColorStateList.valueOf(ContextCompat.getColor(context, R.color.orange200))
5 -> ColorStateList.valueOf(ContextCompat.getColor(context, R.color.green600))
Expand All @@ -58,36 +43,10 @@ class DateDecorator(
valid: Boolean,
selected: Boolean
): ColorStateList? {
val dateKey = getDateKey(year, month + 1, day)
val score = scoreData[dateKey]

return when (score) {
val date = LocalDate.of(year, month + 1, day)
return when (scoreData[date]) {
null -> super.getTextColor(context, year, month, day, valid, selected)
else -> ColorStateList.valueOf(ContextCompat.getColor(context, R.color.gray700))
}
}

constructor(parcel: Parcel) : this(
Date(),
Date(),
hashMapOf()
)

override fun describeContents(): Int { return 0 }

override fun writeToParcel(dest: Parcel, flags: Int) {}

companion object CREATOR : Parcelable.Creator<DateDecorator> {
override fun createFromParcel(parcel: Parcel): DateDecorator {
return DateDecorator(parcel)
}

override fun newArray(size: Int): Array<DateDecorator?> {
return arrayOfNulls(size)
}

fun getDateKey(year: Int, month: Int, day: Int): Long {
return (year * 10000 + month * 100 + day).toLong()
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,13 @@ import org.wikipedia.analytics.eventplatform.WikiGamesEvent
import org.wikipedia.games.onthisday.OnThisDayGameViewModel.Companion.LANG_CODES_SUPPORTED
import org.wikipedia.games.onthisday.OnThisDayGameViewModel.Companion.dateReleasedForLang
import org.wikipedia.util.log.L
import java.time.Instant
import java.time.LocalDate
import java.time.ZoneId
import java.time.ZoneOffset
import java.util.Calendar
import java.util.Date
import java.util.TimeZone
import java.time.temporal.ChronoUnit

abstract class OnThisDayGameBaseFragment : Fragment() {
private var scoreData: Map<Long, Int> = emptyMap()
private var scoreData = emptyMap<LocalDate, Int>()

private val fragmentLifecycleCallbacks = object : FragmentManager.FragmentLifecycleCallbacks() {
@SuppressLint("RestrictedApi")
Expand All @@ -38,7 +36,7 @@ abstract class OnThisDayGameBaseFragment : Fragment() {
(calendar as MaterialCalendar<Long>?)?.addOnSelectionChangedListener(object :
OnSelectionChangedListener<Long>() {
override fun onSelectionChanged(selection: Long) {
maybeShowToastForDate(selection, scoreData)
maybeShowToastForDate(selection)
}
})
}
Expand All @@ -58,90 +56,66 @@ abstract class OnThisDayGameBaseFragment : Fragment() {
protected fun prepareAndOpenArchiveCalendar(viewModel: OnThisDayGameViewModel) {
lifecycleScope.launch {
val startDateBasedOnLanguage = LANG_CODES_SUPPORTED.associateWith { dateReleasedForLang(it) }
val localDate = startDateBasedOnLanguage[viewModel.wikiSite.languageCode]
val startDate = Date.from(localDate?.atStartOfDay(ZoneId.systemDefault())?.toInstant())
val startDate = startDateBasedOnLanguage[viewModel.wikiSite.languageCode] ?: return@launch
scoreData = viewModel.getDataForArchiveCalendar(language = viewModel.wikiSite.languageCode)
showArchiveCalendar(
startDate,
Date(),
scoreData,
onDateSelected = { selectedDateInMillis ->
handleDateSelection(selectedDateInMillis)
}
)
showArchiveCalendar(startDate)
}
}

private fun showArchiveCalendar(startDate: Date, endDate: Date, scoreData: Map<Long, Int>, onDateSelected: (Long) -> Unit) {
val startTimeInMillis = startDate.time
val endTimeInMillis = endDate.time
private fun showArchiveCalendar(startDate: LocalDate) {
val startInstant = startDate.atStartOfDay(ZoneId.systemDefault()).toInstant()
val startTimeInMillis = startInstant.toEpochMilli()
val endInstant = Instant.now()
val endTimeInMillis = endInstant.toEpochMilli()
val oneDayBeforeStart = startInstant.minus(1, ChronoUnit.DAYS).toEpochMilli()
val calendarConstraints = CalendarConstraints.Builder()
.setStart(startDate.time)
.setStart(startTimeInMillis)
.setEnd(endTimeInMillis)
.setValidator(
CompositeDateValidator.allOf(
listOf(
DateValidatorPointForward.from(startTimeInMillis - (24 * 60 * 60 * 1000)),
DateValidatorPointForward.from(oneDayBeforeStart),
DateValidatorPointBackward.before(endTimeInMillis)
)
)
)
.build()

val endDate = LocalDate.ofInstant(endInstant, ZoneId.systemDefault())
val datePicker = MaterialDatePicker.Builder.datePicker()
.setTitleText(getString(R.string.on_this_day_game_archive_calendar_title))
.setTheme(R.style.MaterialDatePickerStyle)
.setDayViewDecorator(
DateDecorator(
startDate,
endDate,
scoreData
)
)
.setDayViewDecorator(DateDecorator(startDate, endDate, scoreData))
.setCalendarConstraints(calendarConstraints)
.setSelection(endTimeInMillis)
.build()
.apply {
addOnPositiveButtonClickListener { selectedDateInMillis ->
onDateSelected(selectedDateInMillis)
}
addOnPositiveButtonClickListener(::handleDateSelection)
}

datePicker.show(childFragmentManager, "datePicker")
}

private fun handleDateSelection(selectedDateInMillis: Long) {
val calendar = Calendar.getInstance(TimeZone.getTimeZone(ZoneOffset.UTC))
calendar.timeInMillis = selectedDateInMillis
val year = calendar.get(Calendar.YEAR)
val month = calendar.get(Calendar.MONTH) + 1
val day = calendar.get(Calendar.DAY_OF_MONTH)
val scoreDataKey = DateDecorator.getDateKey(year, month, day)
if (scoreData[scoreDataKey] != null) {
val localDate = LocalDate.ofInstant(Instant.ofEpochMilli(selectedDateInMillis),
ZoneId.systemDefault())
if (scoreData[localDate] != null) {
return
}
WikiGamesEvent.submit("date_select", "game_play", slideName = "archive_calendar")
onArchiveDateSelected(LocalDate.of(year, month, day))
onArchiveDateSelected(localDate)
}

abstract fun onArchiveDateSelected(date: LocalDate)

private fun maybeShowToastForDate(selectedDateInMillis: Long, scoreData: Map<Long, Int>) {
val calendar = Calendar.getInstance(TimeZone.getTimeZone(ZoneOffset.UTC))
calendar.timeInMillis = selectedDateInMillis
val year = calendar.get(Calendar.YEAR)
val month = calendar.get(Calendar.MONTH)
val day = calendar.get(Calendar.DAY_OF_MONTH)
val scoreDataKey = DateDecorator.getDateKey(year, month + 1, day)
if (scoreData[scoreDataKey] != null) {
Toast.makeText(
requireContext(),
getString(
R.string.on_this_day_game_score_toast_message,
scoreData[scoreDataKey],
OnThisDayGameViewModel.MAX_QUESTIONS
), Toast.LENGTH_SHORT
).show()
private fun maybeShowToastForDate(selectedDateInMillis: Long) {
val localDate = LocalDate.ofInstant(Instant.ofEpochMilli(selectedDateInMillis),
ZoneId.systemDefault())
val score = scoreData[localDate]
if (score != null) {
val message = getString(R.string.on_this_day_game_score_toast_message, score,
OnThisDayGameViewModel.MAX_QUESTIONS)
Toast.makeText(requireContext(), message, Toast.LENGTH_SHORT).show()
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -312,13 +312,12 @@ class OnThisDayGameViewModel(savedStateHandle: SavedStateHandle) : ViewModel() {
return event.pages.firstOrNull { !it.thumbnailUrl.isNullOrEmpty() }?.thumbnailUrl
}

suspend fun getDataForArchiveCalendar(gameName: Int = WikiGames.WHICH_CAME_FIRST.ordinal, language: String): Map<Long, Int> {
suspend fun getDataForArchiveCalendar(
gameName: Int = WikiGames.WHICH_CAME_FIRST.ordinal,
language: String
): Map<LocalDate, Int> {
val history = AppDatabase.instance.dailyGameHistoryDao().getGameHistory(gameName, language)
val map = history.associate {
val scoreKey = DateDecorator.getDateKey(it.year, it.month, it.day)
scoreKey to it.score
}
return map
return history.associate { it.date to it.score }
}

fun getCurrentGameState(): GameState {
Expand Down
Loading