ZonedDateTime Implemented

This commit is contained in:
Hafiz 2022-04-17 22:28:07 -04:00
parent 441dfa5498
commit 018991e467
9 changed files with 198 additions and 71 deletions

View file

@ -55,6 +55,8 @@ dependencies {
implementation 'androidx.recyclerview:recyclerview:1.2.1'
implementation "androidx.preference:preference-ktx:1.2.0"
//Desugaring
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.0.9'
// Markdown
implementation "io.noties.markwon:core:4.6.2"
@ -267,6 +269,9 @@ android {
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
// Flag to enable support for the new language APIs
coreLibraryDesugaringEnabled true
}
kotlinOptions {

View file

@ -17,8 +17,14 @@ import com.habitrpg.android.habitica.receivers.TaskReceiver
import com.habitrpg.shared.habitica.HLogger
import com.habitrpg.shared.habitica.LogLevel
import io.reactivex.rxjava3.core.Flowable
import java.util.Calendar
import java.util.Date
import java.text.SimpleDateFormat
import java.time.LocalDateTime
import java.time.ZoneId
import java.time.ZonedDateTime
import java.time.format.DateTimeFormatter
import java.time.format.DateTimeFormatterBuilder
import java.time.temporal.TemporalAccessor
import java.util.*
class TaskAlarmManager(
private var context: Context,
@ -76,30 +82,42 @@ class TaskAlarmManager(
private fun setTimeForDailyReminder(remindersItem: RemindersItem?, task: Task): RemindersItem? {
val oldTime = remindersItem?.time
val newTime = task.getNextReminderOccurence(oldTime) ?: return null
val calendar = Calendar.getInstance()
calendar.time = newTime
@Suppress("DEPRECATION")
calendar.set(
calendar.get(Calendar.YEAR),
calendar.get(Calendar.MONTH),
calendar.get(Calendar.DATE),
oldTime?.hours ?: 0,
oldTime?.minutes ?: 0,
0
)
remindersItem?.time = calendar.time
val newTime = (task.getNextReminderOccurence(oldTime) ?: return null)
remindersItem?.time = newTime.withZoneSameLocal(ZoneId.systemDefault()).format(DateTimeFormatter.ISO_LOCAL_DATE_TIME)
return remindersItem
}
fun formatter(): DateTimeFormatter =
DateTimeFormatterBuilder().append(DateTimeFormatter.ISO_LOCAL_DATE)
.appendPattern("['T'][' ']")
.append(DateTimeFormatter.ISO_LOCAL_TIME)
.appendPattern("[XX]")
.toFormatter()
fun parse(dateTime: String?): ZonedDateTime? {
val parsed: TemporalAccessor = formatter().parseBest(
dateTime,
ZonedDateTime::from, LocalDateTime::from
)
return if (parsed is ZonedDateTime) {
parsed
} else {
val defaultZone: ZoneId = ZoneId.of("UTC")
(parsed as LocalDateTime).atZone(defaultZone)
}
}
private fun setAlarmForRemindersItem(reminderItemTask: Task, remindersItem: RemindersItem?) {
val now = Date()
if (remindersItem == null || remindersItem.time?.before(now) == true) {
val now = ZonedDateTime.now().withZoneSameLocal(ZoneId.systemDefault())?.toInstant()
if (remindersItem == null || parse(remindersItem.time)?.withZoneSameLocal(ZoneId.systemDefault())?.toInstant()?.isBefore(now) == true) {
return
}
val time = Date.from(parse(remindersItem.time)?.withZoneSameLocal(ZoneId.systemDefault())?.toInstant())
val cal = Calendar.getInstance()
cal.time = remindersItem.time
cal.time = time
val intent = Intent(context, TaskReceiver::class.java)
intent.action = remindersItem.id
@ -127,6 +145,7 @@ class TaskAlarmManager(
)
setAlarm(context, cal.timeInMillis, sender)
}
private fun removeAlarmForRemindersItem(remindersItem: RemindersItem) {

View file

@ -4,13 +4,19 @@ import android.os.Parcel
import android.os.Parcelable
import io.realm.RealmObject
import io.realm.annotations.PrimaryKey
import java.time.LocalDateTime
import java.time.ZoneId
import java.time.ZonedDateTime
import java.time.format.DateTimeFormatter
import java.time.format.DateTimeFormatterBuilder
import java.time.temporal.TemporalAccessor
import java.util.Date
open class RemindersItem : RealmObject, Parcelable {
@PrimaryKey
var id: String? = null
var startDate: Date? = null
var time: Date? = null
var startDate: String? = null
var time: String? = null
// Use to store task type before a task is created
var type: String? = null
@ -21,8 +27,8 @@ open class RemindersItem : RealmObject, Parcelable {
override fun writeToParcel(dest: Parcel, flags: Int) {
dest.writeString(id)
dest.writeLong(this.startDate?.time ?: -1)
dest.writeLong(this.time?.time ?: -1)
dest.writeString(startDate)
dest.writeString(time)
}
companion object CREATOR : Parcelable.Creator<RemindersItem> {
@ -33,8 +39,8 @@ open class RemindersItem : RealmObject, Parcelable {
constructor(source: Parcel) {
id = source.readString()
startDate = Date(source.readLong())
time = Date(source.readLong())
startDate = source.readString()
time = source.readString()
}
constructor()
@ -48,4 +54,24 @@ open class RemindersItem : RealmObject, Parcelable {
override fun hashCode(): Int {
return id?.hashCode() ?: 0
}
fun formatter(): DateTimeFormatter =
DateTimeFormatterBuilder().append(DateTimeFormatter.ISO_LOCAL_DATE)
.appendPattern("['T'][' ']")
.append(DateTimeFormatter.ISO_LOCAL_TIME)
.appendPattern("[XX]")
.toFormatter()
fun parse(dateTime: String): ZonedDateTime? {
val parsed: TemporalAccessor = formatter().parseBest(
dateTime,
ZonedDateTime::from, LocalDateTime::from
)
return if (parsed is ZonedDateTime) {
parsed
} else {
val defaultZone: ZoneId = ZoneId.of("UTC")
(parsed as LocalDateTime).atZone(defaultZone)
}
}
}

View file

@ -18,6 +18,12 @@ import java.util.Date
import java.util.GregorianCalendar
import org.json.JSONArray
import org.json.JSONException
import java.time.LocalDateTime
import java.time.ZoneId
import java.time.ZonedDateTime
import java.time.format.DateTimeFormatter
import java.time.format.DateTimeFormatterBuilder
import java.time.temporal.TemporalAccessor
open class Task : RealmObject, BaseMainObject, Parcelable {
@ -181,32 +187,46 @@ open class Task : RealmObject, BaseMainObject, Parcelable {
fun checkIfDue(): Boolean = isDue == true
fun getNextReminderOccurence(oldTime: Date?): Date? {
fun getNextReminderOccurence(oldTime: String?): ZonedDateTime? {
if (oldTime == null) {
return null
}
val today = Calendar.getInstance()
val newTime = GregorianCalendar()
newTime.time = oldTime
newTime.set(today.get(Calendar.YEAR), today.get(Calendar.MONTH), today.get(Calendar.DAY_OF_MONTH))
if (today.before(newTime)) {
today.add(Calendar.DAY_OF_MONTH, -1)
}
val nextDate = nextDue?.firstOrNull()
return if (nextDate != null && !isDisplayedActive) {
val nextDueCalendar = GregorianCalendar()
nextDueCalendar.time = nextDate
newTime.set(nextDueCalendar.get(Calendar.YEAR), nextDueCalendar.get(Calendar.MONTH), nextDueCalendar.get(Calendar.DAY_OF_MONTH))
newTime.time
parse(oldTime)
?.withYear(nextDueCalendar.get(Calendar.YEAR))
?.withMonth(nextDueCalendar.get(Calendar.MONTH))
?.withDayOfMonth(nextDueCalendar.get(Calendar.DAY_OF_MONTH))
} else if (isDisplayedActive) {
newTime.time
parse(oldTime)
} else {
null
}
}
fun formatter(): DateTimeFormatter =
DateTimeFormatterBuilder().append(DateTimeFormatter.ISO_LOCAL_DATE)
.appendPattern("['T'][' ']")
.append(DateTimeFormatter.ISO_LOCAL_TIME)
.appendPattern("[XX]")
.toFormatter()
fun parse(dateTime: String): ZonedDateTime? {
val parsed: TemporalAccessor = formatter().parseBest(
dateTime,
ZonedDateTime::from, LocalDateTime::from
)
return if (parsed is ZonedDateTime) {
parsed
} else {
val defaultZone: ZoneId = ZoneId.of("UTC")
(parsed as LocalDateTime).atZone(defaultZone)
}
}
fun parseMarkdown() {
parsedText = MarkdownParser.parseMarkdown(text)
parsedNotes = MarkdownParser.parseMarkdown(notes)

View file

@ -5,6 +5,12 @@ import com.habitrpg.android.habitica.models.responses.TaskDirection
import com.habitrpg.android.habitica.models.tasks.ChecklistItem
import com.habitrpg.android.habitica.models.tasks.Task
import java.text.DateFormat
import java.time.LocalDateTime
import java.time.ZoneId
import java.time.ZonedDateTime
import java.time.format.DateTimeFormatter
import java.time.format.DateTimeFormatterBuilder
import java.time.temporal.TemporalAccessor
import java.util.Calendar
import java.util.Date
@ -37,13 +43,19 @@ class DailyViewHolder(
val calendar = Calendar.getInstance()
val nextReminder = data.reminders?.firstOrNull {
calendar.time = now
calendar.set(calendar.get(Calendar.YEAR), calendar.get(Calendar.MONTH), calendar.get(Calendar.DATE), it.time?.hours ?: 0, it.time?.minutes ?: 0, 0)
calendar.set(calendar.get(Calendar.YEAR),
calendar.get(Calendar.MONTH),
calendar.get(Calendar.DATE),
parse(it.time)?.hour ?: 0,
parse(it.time)?.minute ?: 0,
0)
now < calendar.time
} ?: data.reminders?.first()
var reminderString = ""
if (nextReminder?.time != null) {
reminderString += formatter.format(nextReminder.time)
val time = Date.from(parse(nextReminder.time)?.withZoneSameLocal(ZoneId.systemDefault())?.toInstant())
reminderString += formatter.format(time)
}
if ((data.reminders?.size ?: 0) > 1) {
reminderString = "$reminderString (+${(data.reminders?.size ?: 0) - 1})"
@ -54,6 +66,26 @@ class DailyViewHolder(
super.bind(data, position, displayMode)
}
fun formatter(): DateTimeFormatter =
DateTimeFormatterBuilder().append(DateTimeFormatter.ISO_LOCAL_DATE)
.appendPattern("['T'][' ']")
.append(DateTimeFormatter.ISO_LOCAL_TIME)
.appendPattern("[XX]")
.toFormatter()
fun parse(dateTime: String?): ZonedDateTime? {
val parsed: TemporalAccessor = formatter().parseBest(
dateTime,
ZonedDateTime::from, LocalDateTime::from
)
return if (parsed is ZonedDateTime) {
parsed
} else {
val defaultZone: ZoneId = ZoneId.of("UTC")
(parsed as LocalDateTime).atZone(defaultZone)
}
}
override fun shouldDisplayAsActive(newTask: Task?): Boolean {
return newTask?.isDisplayedActive ?: false
}

View file

@ -22,6 +22,13 @@ import com.habitrpg.android.habitica.extensions.layoutInflater
import com.habitrpg.android.habitica.models.tasks.RemindersItem
import com.habitrpg.android.habitica.models.tasks.TaskType
import java.text.DateFormat
import java.text.SimpleDateFormat
import java.time.LocalDateTime
import java.time.ZoneId
import java.time.ZonedDateTime
import java.time.format.DateTimeFormatter
import java.time.format.DateTimeFormatterBuilder
import java.time.temporal.TemporalAccessor
import java.util.Calendar
import java.util.Date
import java.util.Locale
@ -33,13 +40,14 @@ class ReminderItemFormView @JvmOverloads constructor(
) : LinearLayout(context, attrs, defStyleAttr), TimePickerDialog.OnTimeSetListener, DatePickerDialog.OnDateSetListener {
private val formattedTime: CharSequence?
get() {
val time = item.time
return if (time != null) {
return if (item.time != null) {
val time = Date.from(parse(item.time)?.withZoneSameLocal(ZoneId.systemDefault())?.toInstant())
formatter.format(time)
} else {
""
}
}
private val binding = TaskFormReminderItemBinding.inflate(context.layoutInflater, this)
private val formatter: DateFormat
@ -103,22 +111,20 @@ class ReminderItemFormView @JvmOverloads constructor(
binding.button.drawable.mutate().setTint(tintColor)
binding.textView.setOnClickListener {
val calendar = Calendar.getInstance()
item.time?.let { calendar.time = it }
if (taskType == TaskType.DAILY) {
val timePickerDialog = TimePickerDialog(
context, this,
calendar.get(Calendar.HOUR_OF_DAY),
calendar.get(Calendar.MINUTE),
ZonedDateTime.now().hour,
ZonedDateTime.now().minute,
android.text.format.DateFormat.is24HourFormat(context)
)
timePickerDialog.show()
} else {
val timePickerDialog = DatePickerDialog(
context, this,
calendar.get(Calendar.YEAR),
calendar.get(Calendar.MONTH),
calendar.get(Calendar.DAY_OF_MONTH)
ZonedDateTime.now().year,
ZonedDateTime.now().monthValue,
ZonedDateTime.now().dayOfMonth
)
if ((firstDayOfWeek ?: -1) >= 0) {
timePickerDialog.datePicker.firstDayOfWeek = firstDayOfWeek ?: 0
@ -130,34 +136,53 @@ class ReminderItemFormView @JvmOverloads constructor(
}
override fun onTimeSet(view: TimePicker?, hourOfDay: Int, minute: Int) {
valueChangedListener?.let {
val calendar = Calendar.getInstance()
item.time?.let { calendar.time = it }
calendar.set(Calendar.HOUR_OF_DAY, hourOfDay)
calendar.set(Calendar.MINUTE, minute)
item.time = calendar.time
val calendar = ZonedDateTime.now()
.withHour(hourOfDay)
.withMinute(minute)
item.time = calendar.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME)
binding.textView.text = formattedTime
item.time?.let { date -> it(date) }
item.time?.let { date -> it(Date.from(parse(date)?.withZoneSameLocal(ZoneId.systemDefault())?.toInstant())) }
}
}
override fun onDateSet(view: DatePicker?, year: Int, month: Int, dayOfMonth: Int) {
valueChangedListener?.let {
val calendar = Calendar.getInstance()
item.time?.let { calendar.time = it }
calendar.set(Calendar.YEAR, year)
calendar.set(Calendar.MONTH, month)
calendar.set(Calendar.DAY_OF_MONTH, dayOfMonth)
item.time = calendar.time
// val calendar = Calendar.getInstance()
val calendar = ZonedDateTime.now()
.withYear(year)
.withMonth(month)
.withDayOfMonth(dayOfMonth)
item.time = calendar.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME)
binding.textView.text = formattedTime
item.time?.let { date -> it(date) }
item.time?.let { date -> Date.from(parse(date)?.withZoneSameLocal(ZoneId.systemDefault())?.toInstant()) }
val timePickerDialog = TimePickerDialog(
context, this,
calendar.get(Calendar.HOUR_OF_DAY),
calendar.get(Calendar.MINUTE),
ZonedDateTime.now().hour,
ZonedDateTime.now().minute,
android.text.format.DateFormat.is24HourFormat(context)
)
timePickerDialog.show()
}
}
fun formatter(): DateTimeFormatter =
DateTimeFormatterBuilder().append(DateTimeFormatter.ISO_LOCAL_DATE)
.appendPattern("['T'][' ']")
.append(DateTimeFormatter.ISO_LOCAL_TIME)
.appendPattern("[XX]")
.toFormatter()
fun parse(dateTime: String?): ZonedDateTime? {
val parsed: TemporalAccessor = formatter().parseBest(
dateTime,
ZonedDateTime::from, LocalDateTime::from
)
return if (parsed is ZonedDateTime) {
parsed
} else {
val defaultZone: ZoneId = ZoneId.of("UTC")
(parsed as LocalDateTime).atZone(defaultZone)
}
}
}

View file

@ -104,8 +104,8 @@ class TaskSerializer : JsonSerializer<Task>, JsonDeserializer<Task> {
val remindersObject = reminderElement.asJsonObject
val reminder = RemindersItem()
reminder.id = remindersObject.getAsString("id")
reminder.startDate = context.deserialize(remindersObject.get("startDate"), Date::class.java)
reminder.time = context.deserialize(remindersObject.get("time"), Date::class.java)
reminder.startDate = remindersObject.getAsString("startDate")
reminder.time = remindersObject.getAsString("time")
task.reminders?.add(reminder)
}
}
@ -207,9 +207,9 @@ class TaskSerializer : JsonSerializer<Task>, JsonDeserializer<Task> {
val jsonObject = JsonObject()
jsonObject.addProperty("id", item.id)
if (item.startDate != null) {
jsonObject.addProperty("startDate", item.startDate?.time)
jsonObject.addProperty("startDate", item.startDate)
}
jsonObject.addProperty("time", item.time?.time ?: Date().time)
jsonObject.addProperty("time", item.time)
jsonArray.add(jsonObject)
}
return jsonArray

View file

@ -8,7 +8,7 @@ buildscript {
maven { url "https://plugins.gradle.org/m2/" }
}
dependencies {
classpath 'com.android.tools.build:gradle:7.1.2'
classpath 'com.android.tools.build:gradle:7.0.2'
classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
classpath 'com.google.gms:google-services:4.3.10'
classpath 'com.google.firebase:firebase-crashlytics-gradle:2.8.1'

View file

@ -1,6 +1,6 @@
#Tue Oct 27 17:32:09 CET 2020
#Sun Apr 17 16:10:31 EDT 2022
distributionBase=GRADLE_USER_HOME
distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-bin.zip
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-all.zip
zipStoreBase=GRADLE_USER_HOME