mirror of https://github.com/tasks/tasks
You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
176 lines
7.0 KiB
Kotlin
176 lines
7.0 KiB
Kotlin
/*
|
|
* Copyright (c) 2012 Todoroo Inc
|
|
*
|
|
* See the file "LICENSE" for the full license governing this code.
|
|
*/
|
|
package com.todoroo.astrid.gcal
|
|
|
|
import android.content.ContentResolver
|
|
import android.content.ContentValues
|
|
import android.content.Context
|
|
import android.net.Uri
|
|
import android.provider.CalendarContract
|
|
import android.text.format.Time
|
|
import com.todoroo.andlib.utility.DateUtilities
|
|
import com.todoroo.astrid.data.Task
|
|
import dagger.hilt.android.qualifiers.ApplicationContext
|
|
import org.tasks.R
|
|
import org.tasks.Strings.isNullOrEmpty
|
|
import org.tasks.calendars.CalendarEventProvider
|
|
import org.tasks.data.TaskDao
|
|
import org.tasks.preferences.PermissionChecker
|
|
import org.tasks.preferences.Preferences
|
|
import timber.log.Timber
|
|
import java.util.*
|
|
import javax.inject.Inject
|
|
|
|
class GCalHelper @Inject constructor(
|
|
@ApplicationContext private val context: Context,
|
|
private val taskDao: TaskDao,
|
|
private val preferences: Preferences,
|
|
private val permissionChecker: PermissionChecker,
|
|
private val calendarEventProvider: CalendarEventProvider) {
|
|
|
|
private val cr: ContentResolver = context.contentResolver
|
|
|
|
private suspend fun getTaskEventUri(task: Task) =
|
|
if (!task.calendarURI.isNullOrBlank()) {
|
|
task.calendarURI
|
|
} else {
|
|
taskDao.fetch(task.id)
|
|
?.calendarURI
|
|
}
|
|
|
|
suspend fun createTaskEventIfEnabled(t: Task) {
|
|
if (!t.hasDueDate()) {
|
|
return
|
|
}
|
|
createTaskEventIfEnabled(t, true)
|
|
}
|
|
|
|
private suspend fun createTaskEventIfEnabled(t: Task, deleteEventIfExists: Boolean) {
|
|
if (preferences.isDefaultCalendarSet) {
|
|
val calendarUri = createTaskEvent(t, ContentValues(), deleteEventIfExists)
|
|
if (calendarUri != null) {
|
|
t.calendarURI = calendarUri.toString()
|
|
}
|
|
}
|
|
}
|
|
|
|
suspend fun createTaskEvent(task: Task, calendarId: String?): Uri? {
|
|
val values = ContentValues()
|
|
values.put(CalendarContract.Events.CALENDAR_ID, calendarId)
|
|
return createTaskEvent(task, values, true)
|
|
}
|
|
|
|
private suspend fun createTaskEvent(task: Task, values: ContentValues, deleteEventIfExists: Boolean): Uri? {
|
|
if (!permissionChecker.canAccessCalendars()) {
|
|
return null
|
|
}
|
|
val eventuri = getTaskEventUri(task)
|
|
if (!isNullOrEmpty(eventuri) && deleteEventIfExists) {
|
|
calendarEventProvider.deleteEvent(task)
|
|
}
|
|
try {
|
|
values.put(CalendarContract.Events.TITLE, task.title)
|
|
values.put(CalendarContract.Events.DESCRIPTION, task.notes)
|
|
values.put(CalendarContract.Events.HAS_ALARM, 0)
|
|
val valuesContainCalendarId = (values.containsKey(CalendarContract.Events.CALENDAR_ID)
|
|
&& !isNullOrEmpty(values.getAsString(CalendarContract.Events.CALENDAR_ID)))
|
|
if (!valuesContainCalendarId) {
|
|
val calendarId = preferences.defaultCalendar
|
|
if (!isNullOrEmpty(calendarId)) {
|
|
values.put(CalendarContract.Events.CALENDAR_ID, calendarId)
|
|
}
|
|
}
|
|
createStartAndEndDate(task, values)
|
|
val eventUri = cr.insert(CalendarContract.Events.CONTENT_URI, values)
|
|
cr.notifyChange(eventUri!!, null)
|
|
return eventUri
|
|
} catch (e: Exception) {
|
|
// won't work on emulator
|
|
Timber.e(e)
|
|
}
|
|
return null
|
|
}
|
|
|
|
fun updateEvent(task: Task) {
|
|
val uri = task.calendarURI?.takeIf { it.isNotBlank() } ?: return
|
|
if (!permissionChecker.canAccessCalendars()) {
|
|
return
|
|
}
|
|
try {
|
|
val updateValues = ContentValues()
|
|
updateValues.put(CalendarContract.Events.TITLE, if (task.isCompleted) {
|
|
context.getString(R.string.gcal_completed_title, task.title)
|
|
} else {
|
|
task.title
|
|
})
|
|
updateValues.put(CalendarContract.Events.DESCRIPTION, task.notes)
|
|
createStartAndEndDate(task, updateValues)
|
|
cr.update(Uri.parse(uri), updateValues, null, null)
|
|
} catch (e: Exception) {
|
|
Timber.e(e, "Failed to update calendar: %s [%s]", uri, task)
|
|
}
|
|
}
|
|
|
|
suspend fun rescheduleRepeatingTask(task: Task) {
|
|
val taskUri = getTaskEventUri(task)
|
|
if (isNullOrEmpty(taskUri)) {
|
|
return
|
|
}
|
|
val eventUri = Uri.parse(taskUri)
|
|
val event = calendarEventProvider.getEvent(eventUri)
|
|
if (event == null) {
|
|
task.calendarURI = ""
|
|
return
|
|
}
|
|
val cv = ContentValues()
|
|
cv.put(CalendarContract.Events.CALENDAR_ID, event.calendarId)
|
|
val uri = createTaskEvent(task, cv, false)
|
|
task.calendarURI = uri.toString()
|
|
}
|
|
|
|
private fun createStartAndEndDate(task: Task, values: ContentValues) {
|
|
val dueDate = task.dueDate
|
|
val tzCorrectedDueDate = dueDate + TimeZone.getDefault().getOffset(dueDate)
|
|
val tzCorrectedDueDateNow = DateUtilities.now() + TimeZone.getDefault().getOffset(DateUtilities.now())
|
|
// FIXME: doesn't respect timezones, see story 17443653
|
|
if (task.hasDueDate()) {
|
|
if (task.hasDueTime()) {
|
|
var estimatedTime = task.estimatedSeconds * 1000.toLong()
|
|
if (estimatedTime <= 0) {
|
|
estimatedTime = DEFAULT_CAL_TIME
|
|
}
|
|
if (preferences.getBoolean(R.string.p_end_at_deadline, true)) {
|
|
values.put(CalendarContract.Events.DTSTART, dueDate)
|
|
values.put(CalendarContract.Events.DTEND, dueDate + estimatedTime)
|
|
} else {
|
|
values.put(CalendarContract.Events.DTSTART, dueDate - estimatedTime)
|
|
values.put(CalendarContract.Events.DTEND, dueDate)
|
|
}
|
|
// setting a duetime to a previously timeless event requires explicitly setting allDay=0
|
|
values.put(CalendarContract.Events.ALL_DAY, "0")
|
|
values.put(CalendarContract.Events.EVENT_TIMEZONE, TimeZone.getDefault().id)
|
|
} else {
|
|
values.put(CalendarContract.Events.DTSTART, tzCorrectedDueDate)
|
|
values.put(CalendarContract.Events.DTEND, tzCorrectedDueDate)
|
|
values.put(CalendarContract.Events.ALL_DAY, "1")
|
|
}
|
|
} else {
|
|
values.put(CalendarContract.Events.DTSTART, tzCorrectedDueDateNow)
|
|
values.put(CalendarContract.Events.DTEND, tzCorrectedDueDateNow)
|
|
values.put(CalendarContract.Events.ALL_DAY, "1")
|
|
}
|
|
if ("1" == values[CalendarContract.Events.ALL_DAY]) {
|
|
values.put(CalendarContract.Events.EVENT_TIMEZONE, Time.TIMEZONE_UTC)
|
|
} else {
|
|
values.put(CalendarContract.Events.EVENT_TIMEZONE, TimeZone.getDefault().id)
|
|
}
|
|
}
|
|
|
|
companion object {
|
|
/** If task has no estimated time, how early to set a task in calendar (seconds) */
|
|
private const val DEFAULT_CAL_TIME = DateUtilities.ONE_HOUR
|
|
}
|
|
} |