Add task repetition (handle task completion)

This commit is contained in:
Naveen 2022-07-20 14:11:10 +05:30
parent afbe184fee
commit b46514ea52
11 changed files with 98 additions and 17 deletions

View file

@ -51,6 +51,7 @@ class TaskActivity : SimpleActivity() {
private var mRepeatRule = 0
private var mTaskOccurrenceTS = 0L
private var mOriginalStartTS = 0L
private var mTaskCompleted = false
private var mLastSavePromptTS = 0L
override fun onCreate(savedInstanceState: Bundle?) {
@ -206,6 +207,7 @@ class TaskActivity : SimpleActivity() {
if (task != null) {
mTask = task
mTaskOccurrenceTS = intent.getLongExtra(EVENT_OCCURRENCE_TS, 0L)
mTaskCompleted = intent.getBooleanExtra(IS_TASK_COMPLETED, false)
if (intent.getBooleanExtra(IS_DUPLICATE_INTENT, false)) {
mTask.id = null
updateActionBarTitle(getString(R.string.new_task))
@ -469,7 +471,18 @@ class TaskActivity : SimpleActivity() {
private fun setupMarkCompleteButton() {
toggle_mark_complete.setOnClickListener { toggleCompletion() }
toggle_mark_complete.beVisibleIf(mTask.id != null)
if (mTask.isTaskCompleted()) {
updateTaskCompleted()
ensureBackgroundThread {
// the stored value might be incorrect so update it (e.g. user completed the task via notification action before editing)
mTaskCompleted = isTaskCompleted(mTask.copy(startTS = mOriginalStartTS, endTS = mOriginalStartTS))
runOnUiThread {
updateTaskCompleted()
}
}
}
private fun updateTaskCompleted() {
if (mTaskCompleted) {
toggle_mark_complete.background = ContextCompat.getDrawable(this, R.drawable.button_background_stroke)
toggle_mark_complete.setText(R.string.mark_incomplete)
toggle_mark_complete.setTextColor(getProperTextColor())
@ -484,14 +497,9 @@ class TaskActivity : SimpleActivity() {
}
private fun toggleCompletion() {
if (mTask.isTaskCompleted()) {
mTask.flags = mTask.flags.removeBit(FLAG_TASK_COMPLETED)
} else {
mTask.flags = mTask.flags or FLAG_TASK_COMPLETED
}
ensureBackgroundThread {
eventsDB.updateTaskCompletion(mTask.id!!, mTask.flags)
val task = mTask.copy(startTS = mOriginalStartTS)
updateTaskCompletion(task, completed = !mTaskCompleted)
hideKeyboard()
finish()
}

View file

@ -13,14 +13,16 @@ import com.simplemobiletools.calendar.pro.helpers.Converters
import com.simplemobiletools.calendar.pro.helpers.REGULAR_EVENT_TYPE_ID
import com.simplemobiletools.calendar.pro.interfaces.EventTypesDao
import com.simplemobiletools.calendar.pro.interfaces.EventsDao
import com.simplemobiletools.calendar.pro.interfaces.TasksDao
import com.simplemobiletools.calendar.pro.interfaces.WidgetsDao
import com.simplemobiletools.calendar.pro.models.Event
import com.simplemobiletools.calendar.pro.models.EventType
import com.simplemobiletools.calendar.pro.models.Task
import com.simplemobiletools.calendar.pro.models.Widget
import com.simplemobiletools.commons.extensions.getProperPrimaryColor
import java.util.concurrent.Executors
@Database(entities = [Event::class, EventType::class, Widget::class], version = 6)
@Database(entities = [Event::class, EventType::class, Widget::class, Task::class], version = 7)
@TypeConverters(Converters::class)
abstract class EventsDatabase : RoomDatabase() {
@ -30,6 +32,8 @@ abstract class EventsDatabase : RoomDatabase() {
abstract fun WidgetsDao(): WidgetsDao
abstract fun TasksDao(): TasksDao
companion object {
private var db: EventsDatabase? = null
@ -49,6 +53,7 @@ abstract class EventsDatabase : RoomDatabase() {
.addMigrations(MIGRATION_3_4)
.addMigrations(MIGRATION_4_5)
.addMigrations(MIGRATION_5_6)
.addMigrations(MIGRATION_6_7)
.build()
db!!.openHelper.setWriteAheadLoggingEnabled(true)
}
@ -113,5 +118,14 @@ abstract class EventsDatabase : RoomDatabase() {
}
}
}
private val MIGRATION_6_7 = object : Migration(6, 7) {
override fun migrate(database: SupportSQLiteDatabase) {
database.apply {
execSQL("CREATE TABLE IF NOT EXISTS `tasks` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `task_id` INTEGER NOT NULL, `start_ts` INTEGER NOT NULL, `flags` INTEGER NOT NULL)")
execSQL("CREATE UNIQUE INDEX IF NOT EXISTS `index_tasks_id` ON `tasks` (`id`)")
}
}
}
}
}

View file

@ -33,6 +33,7 @@ import com.simplemobiletools.calendar.pro.helpers.*
import com.simplemobiletools.calendar.pro.helpers.Formatter
import com.simplemobiletools.calendar.pro.interfaces.EventTypesDao
import com.simplemobiletools.calendar.pro.interfaces.EventsDao
import com.simplemobiletools.calendar.pro.interfaces.TasksDao
import com.simplemobiletools.calendar.pro.interfaces.WidgetsDao
import com.simplemobiletools.calendar.pro.models.*
import com.simplemobiletools.calendar.pro.receivers.CalDAVSyncReceiver
@ -51,6 +52,7 @@ val Context.config: Config get() = Config.newInstance(applicationContext)
val Context.eventsDB: EventsDao get() = EventsDatabase.getInstance(applicationContext).EventsDao()
val Context.eventTypesDB: EventTypesDao get() = EventsDatabase.getInstance(applicationContext).EventTypesDao()
val Context.widgetsDB: WidgetsDao get() = EventsDatabase.getInstance(applicationContext).WidgetsDao()
val Context.completedTasksDB: TasksDao get() = EventsDatabase.getInstance(applicationContext).TasksDao()
val Context.eventsHelper: EventsHelper get() = EventsHelper(this)
val Context.calDAVHelper: CalDAVHelper get() = CalDAVHelper(this)
@ -237,6 +239,7 @@ fun Context.notifyEvent(originalEvent: Event) {
val descriptionOrLocation = if (config.replaceDescription) event.location else event.description
val content = "$displayedStartDate $timeRange $descriptionOrLocation".trim()
ensureBackgroundThread {
if (event.isTask()) eventsHelper.updateIsTaskCompleted(event)
val notification = getNotification(pendingIntent, event, content)
val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
try {
@ -653,3 +656,19 @@ fun Context.getDatesWeekDateTime(date: DateTime): String {
date.withZone(DateTimeZone.UTC).toString()
}
}
fun Context.isTaskCompleted(event: Event): Boolean {
val task = completedTasksDB.getTaskWithIdAndTs(event.id!!, event.startTS) ?: return false
return task.flags and FLAG_TASK_COMPLETED != 0
}
fun Context.updateTaskCompletion(event: Event, completed: Boolean) {
if (completed) {
event.flags = event.flags or FLAG_TASK_COMPLETED
val task = Task(null, event.id!!, event.startTS, event.flags)
completedTasksDB.insertOrUpdate(task)
} else {
event.flags = event.flags.removeBit(FLAG_TASK_COMPLETED)
completedTasksDB.deleteTaskWithIdAndTs(event.id!!, event.startTS)
}
}

View file

@ -17,13 +17,11 @@ import com.simplemobiletools.calendar.pro.extensions.eventsHelper
import com.simplemobiletools.calendar.pro.extensions.getViewBitmap
import com.simplemobiletools.calendar.pro.extensions.printBitmap
import com.simplemobiletools.calendar.pro.helpers.*
import com.simplemobiletools.calendar.pro.helpers.Formatter
import com.simplemobiletools.calendar.pro.interfaces.NavigationListener
import com.simplemobiletools.calendar.pro.models.Event
import com.simplemobiletools.commons.extensions.*
import kotlinx.android.synthetic.main.fragment_day.view.*
import kotlinx.android.synthetic.main.top_navigation.view.*
import java.util.*
class DayFragment : Fragment() {
var mListener: NavigationListener? = null
@ -129,6 +127,7 @@ class DayFragment : Fragment() {
Intent(context, getActivityToOpen(event.isTask())).apply {
putExtra(EVENT_ID, event.id)
putExtra(EVENT_OCCURRENCE_TS, event.startTS)
putExtra(IS_TASK_COMPLETED, event.isTaskCompleted())
startActivity(this)
}
}

View file

@ -610,6 +610,7 @@ class WeekFragment : Fragment(), WeeklyCalendar {
Intent(context, getActivityToOpen(event.isTask())).apply {
putExtra(EVENT_ID, event.id!!)
putExtra(EVENT_OCCURRENCE_TS, event.startTS)
putExtra(IS_TASK_COMPLETED, event.isTaskCompleted())
startActivity(this)
}
}
@ -813,6 +814,7 @@ class WeekFragment : Fragment(), WeeklyCalendar {
Intent(context, getActivityToOpen(event.isTask())).apply {
putExtra(EVENT_ID, event.id)
putExtra(EVENT_OCCURRENCE_TS, event.startTS)
putExtra(IS_TASK_COMPLETED, event.isTaskCompleted())
startActivity(this)
}
}

View file

@ -13,6 +13,7 @@ const val YEAR_LABEL = "year"
const val EVENT_ID = "event_id"
const val IS_DUPLICATE_INTENT = "is_duplicate_intent"
const val EVENT_OCCURRENCE_TS = "event_occurrence_ts"
const val IS_TASK_COMPLETED = "is_task_completed"
const val NEW_EVENT_START_TS = "new_event_start_ts"
const val WEEK_START_TIMESTAMP = "week_start_timestamp"
const val NEW_EVENT_SET_HOUR_DURATION = "new_event_set_hour_duration"

View file

@ -316,6 +316,7 @@ class EventsHelper(val context: Context) {
}
events.forEach {
if (it.isTask()) updateIsTaskCompleted(it)
it.updateIsPastEvent()
val originalEvent = eventsDB.getEventWithId(it.id!!)
if (originalEvent != null &&
@ -481,10 +482,18 @@ class EventsHelper(val context: Context) {
return events
}
fun updateIsTaskCompleted(event: Event) {
val task = context.completedTasksDB.getTaskWithIdAndTs(event.id!!, startTs = event.startTS)
event.flags = task?.flags ?: event.flags
}
fun getRunningEventsOrTasks(): List<Event> {
val ts = getNowSeconds()
val events = eventsDB.getOneTimeEventsOrTasksFromTo(ts, ts).toMutableList() as ArrayList<Event>
events.addAll(getRepeatableEventsFor(ts, ts))
events.forEach {
if (it.isTask()) updateIsTaskCompleted(it)
}
return events
}

View file

@ -112,9 +112,6 @@ interface EventsDao {
@Query("UPDATE events SET repetition_exceptions = :repetitionExceptions WHERE id = :id AND (type = $TYPE_EVENT OR type = $TYPE_TASK)")
fun updateEventRepetitionExceptions(repetitionExceptions: String, id: Long)
@Query("UPDATE events SET flags = :newFlags WHERE id = :id")
fun updateTaskCompletion(id: Long, newFlags: Int)
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun insertOrUpdate(event: Event): Long

View file

@ -0,0 +1,19 @@
package com.simplemobiletools.calendar.pro.interfaces
import androidx.room.Dao
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query
import com.simplemobiletools.calendar.pro.models.Task
@Dao
interface TasksDao {
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun insertOrUpdate(task: Task): Long
@Query("SELECT * FROM tasks WHERE task_id = :id AND start_ts = :startTs")
fun getTaskWithIdAndTs(id: Long, startTs: Long): Task?
@Query("DELETE FROM tasks WHERE task_id = :id AND start_ts = :startTs")
fun deleteTaskWithIdAndTs(id: Long, startTs: Long)
}

View file

@ -0,0 +1,14 @@
package com.simplemobiletools.calendar.pro.models
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.Index
import androidx.room.PrimaryKey
@Entity(tableName = "tasks", indices = [(Index(value = ["id"], unique = true))])
data class Task(
@PrimaryKey(autoGenerate = true) var id: Long?,
@ColumnInfo(name = "task_id") var task_id: Long,
@ColumnInfo(name = "start_ts") var startTS: Long = 0L,
@ColumnInfo(name = "flags") var flags: Int = 0,
)

View file

@ -5,9 +5,9 @@ import android.content.Intent
import com.simplemobiletools.calendar.pro.extensions.cancelNotification
import com.simplemobiletools.calendar.pro.extensions.cancelPendingIntent
import com.simplemobiletools.calendar.pro.extensions.eventsDB
import com.simplemobiletools.calendar.pro.extensions.updateTaskCompletion
import com.simplemobiletools.calendar.pro.helpers.ACTION_MARK_COMPLETED
import com.simplemobiletools.calendar.pro.helpers.EVENT_ID
import com.simplemobiletools.calendar.pro.helpers.FLAG_TASK_COMPLETED
class MarkCompletedService : IntentService("MarkCompleted") {
@ -17,8 +17,7 @@ class MarkCompletedService : IntentService("MarkCompleted") {
val taskId = intent.getLongExtra(EVENT_ID, 0L)
val task = eventsDB.getTaskWithId(taskId)
if (task != null) {
task.flags = task.flags or FLAG_TASK_COMPLETED
eventsDB.updateTaskCompletion(task.id!!, task.flags)
updateTaskCompletion(task, true)
cancelPendingIntent(task.id!!)
cancelNotification(task.id!!)
}