Merge pull request #1785 from Naveen3Singh/repeating_tasks

Add task repetition
This commit is contained in:
Tibor Kaputa 2022-07-23 12:36:43 +02:00 committed by GitHub
commit 492c5ce621
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
60 changed files with 877 additions and 166 deletions

View File

@ -77,6 +77,7 @@ class EventActivity : SimpleActivity() {
private var mOriginalTimeZone = DateTimeZone.getDefault().id
private var mOriginalStartTS = 0L
private var mOriginalEndTS = 0L
private var mIsNewEvent = true
private lateinit var mEventStartDateTime: DateTime
private lateinit var mEventEndDateTime: DateTime
@ -353,6 +354,9 @@ class EventActivity : SimpleActivity() {
putLong(EVENT_TYPE_ID, mEventTypeId)
putInt(EVENT_CALENDAR_ID, mEventCalendarId)
putBoolean(IS_NEW_EVENT, mIsNewEvent)
putLong(ORIGINAL_START_TS, mOriginalStartTS)
putLong(ORIGINAL_END_TS, mOriginalEndTS)
}
}
@ -389,6 +393,9 @@ class EventActivity : SimpleActivity() {
mEventTypeId = getLong(EVENT_TYPE_ID)
mEventCalendarId = getInt(EVENT_CALENDAR_ID)
mIsNewEvent = getBoolean(IS_NEW_EVENT)
mOriginalStartTS = getLong(ORIGINAL_START_TS)
mOriginalEndTS = getLong(ORIGINAL_END_TS)
}
checkRepeatTexts(mRepeatInterval)
@ -397,6 +404,7 @@ class EventActivity : SimpleActivity() {
updateEventType()
updateCalDAVCalendar()
checkAttendees()
updateActionBarTitle()
}
override fun onActivityResult(requestCode: Int, resultCode: Int, resultData: Intent?) {
@ -420,6 +428,7 @@ class EventActivity : SimpleActivity() {
}
private fun setupEditEvent() {
mIsNewEvent = false
val realStart = if (mEventOccurrenceTS == 0L) mEvent.startTS else mEventOccurrenceTS
val duration = mEvent.endTS - mEvent.startTS
mOriginalStartTS = realStart
@ -1105,15 +1114,15 @@ class EventActivity : SimpleActivity() {
val reminders = getReminders()
if (!event_all_day.isChecked) {
if (reminders.getOrNull(2)?.minutes ?: 0 < -1) {
if ((reminders.getOrNull(2)?.minutes ?: 0) < -1) {
reminders.removeAt(2)
}
if (reminders.getOrNull(1)?.minutes ?: 0 < -1) {
if ((reminders.getOrNull(1)?.minutes ?: 0) < -1) {
reminders.removeAt(1)
}
if (reminders.getOrNull(0)?.minutes ?: 0 < -1) {
if ((reminders.getOrNull(0)?.minutes ?: 0) < -1) {
reminders.removeAt(0)
}
}
@ -1701,4 +1710,12 @@ class EventActivity : SimpleActivity() {
it.applyColorFilter(textColor)
}
}
private fun updateActionBarTitle() {
if (mIsNewEvent) {
updateActionBarTitle(getString(R.string.new_event))
} else {
updateActionBarTitle(getString(R.string.edit_event))
}
}
}

View File

@ -11,25 +11,28 @@ import android.view.WindowManager
import androidx.core.app.NotificationManagerCompat
import androidx.core.content.ContextCompat
import com.simplemobiletools.calendar.pro.R
import com.simplemobiletools.calendar.pro.dialogs.ReminderWarningDialog
import com.simplemobiletools.calendar.pro.dialogs.SelectEventTypeDialog
import com.simplemobiletools.calendar.pro.dialogs.*
import com.simplemobiletools.calendar.pro.extensions.*
import com.simplemobiletools.calendar.pro.helpers.*
import com.simplemobiletools.calendar.pro.helpers.Formatter
import com.simplemobiletools.calendar.pro.models.Event
import com.simplemobiletools.calendar.pro.models.EventType
import com.simplemobiletools.calendar.pro.models.Reminder
import com.simplemobiletools.commons.dialogs.ConfirmationAdvancedDialog
import com.simplemobiletools.commons.dialogs.ConfirmationDialog
import com.simplemobiletools.commons.dialogs.RadioGroupDialog
import com.simplemobiletools.commons.extensions.*
import com.simplemobiletools.commons.helpers.ensureBackgroundThread
import com.simplemobiletools.commons.helpers.*
import com.simplemobiletools.commons.models.RadioItem
import kotlinx.android.synthetic.main.activity_task.*
import org.joda.time.DateTime
import java.util.*
import kotlin.math.pow
class TaskActivity : SimpleActivity() {
private var mEventTypeId = REGULAR_EVENT_TYPE_ID
private lateinit var mTaskDateTime: DateTime
private lateinit var mTask: Event
private var mIsAllDayEvent = false
private var mReminder1Minutes = REMINDER_OFF
private var mReminder2Minutes = REMINDER_OFF
@ -37,6 +40,14 @@ class TaskActivity : SimpleActivity() {
private var mReminder1Type = REMINDER_NOTIFICATION
private var mReminder2Type = REMINDER_NOTIFICATION
private var mReminder3Type = REMINDER_NOTIFICATION
private var mRepeatInterval = 0
private var mRepeatLimit = 0L
private var mRepeatRule = 0
private var mTaskOccurrenceTS = 0L
private var mOriginalStartTS = 0L
private var mTaskCompleted = false
private var mLastSavePromptTS = 0L
private var mIsNewTask = true
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@ -57,9 +68,11 @@ class TaskActivity : SimpleActivity() {
return@ensureBackgroundThread
}
val storedEventTypes = eventTypesDB.getEventTypes().toMutableList() as ArrayList<EventType>
val localEventType = storedEventTypes.firstOrNull { it.id == config.lastUsedLocalEventTypeId }
runOnUiThread {
if (!isDestroyed && !isFinishing) {
gotTask(savedInstanceState, task)
gotTask(savedInstanceState, localEventType, task)
}
}
}
@ -78,7 +91,7 @@ class TaskActivity : SimpleActivity() {
override fun onOptionsItemSelected(item: MenuItem): Boolean {
when (item.itemId) {
R.id.save -> saveTask()
R.id.save -> saveCurrentTask()
R.id.delete -> deleteTask()
R.id.duplicate -> duplicateTask()
else -> return super.onOptionsItemSelected(item)
@ -86,6 +99,49 @@ class TaskActivity : SimpleActivity() {
return true
}
private fun isTaskChanged(): Boolean {
if (!this::mTask.isInitialized) {
return false
}
val newStartTS: Long = mTaskDateTime.seconds()
val hasTimeChanged = if (mOriginalStartTS == 0L) {
mTask.startTS != newStartTS
} else {
mOriginalStartTS != newStartTS
}
val reminders = getReminders()
val originalReminders = mTask.getReminders()
if (task_title.text.toString() != mTask.title ||
task_description.text.toString() != mTask.description ||
reminders != originalReminders ||
mRepeatInterval != mTask.repeatInterval ||
mRepeatRule != mTask.repeatRule ||
mEventTypeId != mTask.eventType ||
hasTimeChanged
) {
return true
}
return false
}
override fun onBackPressed() {
if (System.currentTimeMillis() - mLastSavePromptTS > SAVE_DISCARD_PROMPT_INTERVAL && isTaskChanged()) {
mLastSavePromptTS = System.currentTimeMillis()
ConfirmationAdvancedDialog(this, "", R.string.save_before_closing, R.string.save, R.string.discard) {
if (it) {
saveCurrentTask()
} else {
super.onBackPressed()
}
}
} else {
super.onBackPressed()
}
}
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
if (!::mTask.isInitialized) {
@ -100,6 +156,14 @@ class TaskActivity : SimpleActivity() {
putInt(REMINDER_1_MINUTES, mReminder1Minutes)
putInt(REMINDER_2_MINUTES, mReminder2Minutes)
putInt(REMINDER_3_MINUTES, mReminder3Minutes)
putInt(REPEAT_INTERVAL, mRepeatInterval)
putInt(REPEAT_RULE, mRepeatRule)
putLong(REPEAT_LIMIT, mRepeatLimit)
putLong(EVENT_TYPE_ID, mEventTypeId)
putBoolean(IS_NEW_EVENT, mIsNewTask)
putLong(ORIGINAL_START_TS, mOriginalStartTS)
}
}
@ -119,19 +183,34 @@ class TaskActivity : SimpleActivity() {
mReminder1Minutes = getInt(REMINDER_1_MINUTES)
mReminder2Minutes = getInt(REMINDER_2_MINUTES)
mReminder3Minutes = getInt(REMINDER_3_MINUTES)
mRepeatInterval = getInt(REPEAT_INTERVAL)
mRepeatRule = getInt(REPEAT_RULE)
mRepeatLimit = getLong(REPEAT_LIMIT)
mEventTypeId = getLong(EVENT_TYPE_ID)
mIsNewTask = getBoolean(IS_NEW_EVENT)
mOriginalStartTS = getLong(ORIGINAL_START_TS)
}
updateEventType()
updateDateText()
updateTimeText()
updateTexts()
setupMarkCompleteButton()
checkRepeatTexts(mRepeatInterval)
checkRepeatRule()
updateActionBarTitle()
}
private fun gotTask(savedInstanceState: Bundle?, task: Event?) {
private fun gotTask(savedInstanceState: Bundle?, localEventType: EventType?, task: Event?) {
if (localEventType == null) {
config.lastUsedLocalEventTypeId = REGULAR_EVENT_TYPE_ID
}
mEventTypeId = if (config.defaultEventTypeId == -1L) config.lastUsedLocalEventTypeId else config.defaultEventTypeId
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))
@ -160,9 +239,12 @@ class TaskActivity : SimpleActivity() {
task_date.setOnClickListener { setupDate() }
task_time.setOnClickListener { setupTime() }
event_type_holder.setOnClickListener { showEventTypeDialog() }
task_type_holder.setOnClickListener { showEventTypeDialog() }
task_repetition.setOnClickListener { showRepeatIntervalDialog() }
task_repetition_rule_holder.setOnClickListener { showRepetitionRuleDialog() }
task_repetition_limit_holder.setOnClickListener { showRepetitionTypePicker() }
event_reminder_1.setOnClickListener {
task_reminder_1.setOnClickListener {
handleNotificationAvailability {
if (config.wasAlarmWarningShown) {
showReminder1Dialog()
@ -175,19 +257,20 @@ class TaskActivity : SimpleActivity() {
}
}
event_reminder_2.setOnClickListener { showReminder2Dialog() }
event_reminder_3.setOnClickListener { showReminder3Dialog() }
task_reminder_2.setOnClickListener { showReminder2Dialog() }
task_reminder_3.setOnClickListener { showReminder3Dialog() }
if (savedInstanceState == null) {
updateEventType()
updateDateText()
updateTimeText()
updateReminderTexts()
updateTexts()
}
}
private fun setupEditTask() {
mTaskDateTime = Formatter.getDateTimeFromTS(mTask.startTS)
mIsNewTask = false
val realStart = if (mTaskOccurrenceTS == 0L) mTask.startTS else mTaskOccurrenceTS
mOriginalStartTS = realStart
mTaskDateTime = Formatter.getDateTimeFromTS(realStart)
window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN)
updateActionBarTitle(getString(R.string.edit_task))
@ -198,12 +281,16 @@ class TaskActivity : SimpleActivity() {
mReminder1Type = mTask.reminder1Type
mReminder2Type = mTask.reminder2Type
mReminder3Type = mTask.reminder3Type
mRepeatInterval = mTask.repeatInterval
mRepeatLimit = mTask.repeatLimit
mRepeatRule = mTask.repeatRule
task_title.setText(mTask.title)
task_description.setText(mTask.description)
task_all_day.isChecked = mTask.getIsAllDay()
toggleAllDay(mTask.getIsAllDay())
setupMarkCompleteButton()
checkRepeatTexts(mRepeatInterval)
}
private fun setupNewTask() {
@ -214,6 +301,33 @@ class TaskActivity : SimpleActivity() {
window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE)
task_title.requestFocus()
updateActionBarTitle(getString(R.string.new_task))
mTask.apply {
this.startTS = mTaskDateTime.seconds()
this.endTS = mTaskDateTime.seconds()
reminder1Minutes = mReminder1Minutes
reminder1Type = mReminder1Type
reminder2Minutes = mReminder2Minutes
reminder2Type = mReminder2Type
reminder3Minutes = mReminder3Minutes
reminder3Type = mReminder3Type
eventType = mEventTypeId
}
}
private fun saveCurrentTask() {
if (config.wasAlarmWarningShown || (mReminder1Minutes == REMINDER_OFF && mReminder2Minutes == REMINDER_OFF && mReminder3Minutes == REMINDER_OFF)) {
ensureBackgroundThread {
saveTask()
}
} else {
ReminderWarningDialog(this) {
config.wasAlarmWarningShown = true
ensureBackgroundThread {
saveTask()
}
}
}
}
private fun saveTask() {
@ -226,6 +340,7 @@ class TaskActivity : SimpleActivity() {
return
}
val wasRepeatable = mTask.repeatInterval > 0
val reminders = getReminders()
if (!task_all_day.isChecked) {
@ -260,21 +375,37 @@ class TaskActivity : SimpleActivity() {
endTS = startTS
title = newTitle
description = task_description.value
// migrate completed task to the new completed tasks db
if (!wasRepeatable && mTask.isTaskCompleted()) {
mTask.flags = mTask.flags.removeBit(FLAG_TASK_COMPLETED)
ensureBackgroundThread {
updateTaskCompletion(copy(startTS = mOriginalStartTS), true)
}
}
flags = mTask.flags.addBitIf(task_all_day.isChecked, FLAG_ALL_DAY)
lastUpdated = System.currentTimeMillis()
eventType = mEventTypeId
type = TYPE_TASK
reminder1Minutes = mReminder1Minutes
reminder1Minutes = reminder1.minutes
reminder1Type = mReminder1Type
reminder2Minutes = mReminder2Minutes
reminder2Minutes = reminder2.minutes
reminder2Type = mReminder2Type
reminder3Minutes = mReminder3Minutes
reminder3Minutes = reminder3.minutes
reminder3Type = mReminder3Type
repeatInterval = mRepeatInterval
repeatLimit = if (repeatInterval == 0) 0 else mRepeatLimit
repeatRule = mRepeatRule
}
ensureBackgroundThread {
EventsHelper(this).insertTask(mTask, true) {
storeTask(wasRepeatable)
}
private fun storeTask(wasRepeatable: Boolean) {
if (mTask.id == null) {
eventsHelper.insertTask(mTask, true) {
hideKeyboard()
if (DateTime.now().isAfter(mTaskDateTime.millis)) {
@ -285,13 +416,76 @@ class TaskActivity : SimpleActivity() {
finish()
}
} else {
if (mRepeatInterval > 0 && wasRepeatable) {
runOnUiThread {
showEditRepeatingTaskDialog()
}
} else {
hideKeyboard()
eventsHelper.updateEvent(mTask, updateAtCalDAV = false, showToasts = true) {
finish()
}
}
}
}
private fun showEditRepeatingTaskDialog() {
EditRepeatingEventDialog(this, isTask = true) {
hideKeyboard()
when (it) {
0 -> {
ensureBackgroundThread {
eventsHelper.addEventRepetitionException(mTask.id!!, mTaskOccurrenceTS, true)
mTask.apply {
parentId = id!!.toLong()
id = null
repeatRule = 0
repeatInterval = 0
repeatLimit = 0
}
eventsHelper.insertTask(mTask, showToasts = true) {
finish()
}
}
}
1 -> {
ensureBackgroundThread {
eventsHelper.addEventRepeatLimit(mTask.id!!, mTaskOccurrenceTS)
mTask.apply {
id = null
}
eventsHelper.insertTask(mTask, showToasts = true) {
finish()
}
}
}
2 -> {
ensureBackgroundThread {
eventsHelper.addEventRepeatLimit(mTask.id!!, mTaskOccurrenceTS)
eventsHelper.updateEvent(mTask, updateAtCalDAV = false, showToasts = true) {
finish()
}
}
}
}
}
}
private fun deleteTask() {
ConfirmationDialog(this) {
if (mTask.id == null) {
return
}
DeleteEventDialog(this, arrayListOf(mTask.id!!), mTask.repeatInterval > 0, isTask = true) {
ensureBackgroundThread {
eventsHelper.deleteEvent(mTask.id!!, false)
when (it) {
DELETE_SELECTED_OCCURRENCE -> eventsHelper.addEventRepetitionException(mTask.id!!, mTaskOccurrenceTS, true)
DELETE_FUTURE_OCCURRENCES -> eventsHelper.addEventRepeatLimit(mTask.id!!, mTaskOccurrenceTS)
DELETE_ALL_OCCURRENCES -> eventsHelper.deleteEvent(mTask.id!!, true)
}
runOnUiThread {
hideKeyboard()
@ -340,6 +534,7 @@ class TaskActivity : SimpleActivity() {
private fun dateSet(year: Int, month: Int, day: Int) {
mTaskDateTime = mTaskDateTime.withDate(year, month + 1, day)
updateDateText()
checkRepeatRule()
}
private fun timeSet(hours: Int, minutes: Int) {
@ -347,6 +542,28 @@ class TaskActivity : SimpleActivity() {
updateTimeText()
}
private fun updateTexts() {
updateDateText()
updateTimeText()
updateReminderTexts()
updateRepetitionText()
}
private fun checkRepeatRule() {
if (mRepeatInterval.isXWeeklyRepetition()) {
val day = mRepeatRule
if (day == MONDAY_BIT || day == TUESDAY_BIT || day == WEDNESDAY_BIT || day == THURSDAY_BIT || day == FRIDAY_BIT || day == SATURDAY_BIT || day == SUNDAY_BIT) {
setRepeatRule(2.0.pow((mTaskDateTime.dayOfWeek - 1).toDouble()).toInt())
}
} else if (mRepeatInterval.isXMonthlyRepetition() || mRepeatInterval.isXYearlyRepetition()) {
if (mRepeatRule == REPEAT_LAST_DAY && !isLastDayOfTheMonth()) {
mRepeatRule = REPEAT_SAME_DAY
}
checkRepetitionRuleText()
}
}
private fun updateDateText() {
task_date.text = Formatter.getDate(this, mTaskDateTime)
}
@ -363,7 +580,18 @@ class TaskActivity : SimpleActivity() {
private fun setupMarkCompleteButton() {
toggle_mark_complete.setOnClickListener { toggleCompletion() }
toggle_mark_complete.beVisibleIf(mTask.id != null)
if (mTask.isTaskCompleted()) {
updateTaskCompletedButton()
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))
runOnUiThread {
updateTaskCompletedButton()
}
}
}
private fun updateTaskCompletedButton() {
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())
@ -378,14 +606,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()
}
@ -398,12 +621,12 @@ class TaskActivity : SimpleActivity() {
}
private fun updateReminder1Text() {
event_reminder_1.text = getFormattedMinutes(mReminder1Minutes)
task_reminder_1.text = getFormattedMinutes(mReminder1Minutes)
}
private fun updateReminder2Text() {
event_reminder_2.apply {
beGoneIf(event_reminder_2.isGone() && mReminder1Minutes == REMINDER_OFF)
task_reminder_2.apply {
beGoneIf(task_reminder_2.isGone() && mReminder1Minutes == REMINDER_OFF)
if (mReminder2Minutes == REMINDER_OFF) {
text = resources.getString(R.string.add_another_reminder)
alpha = 0.4f
@ -415,8 +638,8 @@ class TaskActivity : SimpleActivity() {
}
private fun updateReminder3Text() {
event_reminder_3.apply {
beGoneIf(event_reminder_3.isGone() && (mReminder2Minutes == REMINDER_OFF || mReminder1Minutes == REMINDER_OFF))
task_reminder_3.apply {
beGoneIf(task_reminder_3.isGone() && (mReminder2Minutes == REMINDER_OFF || mReminder1Minutes == REMINDER_OFF))
if (mReminder3Minutes == REMINDER_OFF) {
text = resources.getString(R.string.add_another_reminder)
alpha = 0.4f
@ -438,21 +661,21 @@ class TaskActivity : SimpleActivity() {
}
private fun showReminder1Dialog() {
showPickSecondsDialogHelper(mReminder1Minutes, showDuringDayOption = mIsAllDayEvent) {
showPickSecondsDialogHelper(mReminder1Minutes) {
mReminder1Minutes = if (it == -1 || it == 0) it else it / 60
updateReminderTexts()
}
}
private fun showReminder2Dialog() {
showPickSecondsDialogHelper(mReminder2Minutes, showDuringDayOption = mIsAllDayEvent) {
showPickSecondsDialogHelper(mReminder2Minutes) {
mReminder2Minutes = if (it == -1 || it == 0) it else it / 60
updateReminderTexts()
}
}
private fun showReminder3Dialog() {
showPickSecondsDialogHelper(mReminder3Minutes, showDuringDayOption = mIsAllDayEvent) {
showPickSecondsDialogHelper(mReminder3Minutes) {
mReminder3Minutes = if (it == -1 || it == 0) it else it / 60
updateReminderTexts()
}
@ -488,8 +711,8 @@ class TaskActivity : SimpleActivity() {
val eventType = eventTypesDB.getEventTypeWithId(mEventTypeId)
if (eventType != null) {
runOnUiThread {
event_type.text = eventType.title
event_type_color.setFillWithStroke(eventType.color, getProperBackgroundColor())
task_type.text = eventType.title
task_type_color.setFillWithStroke(eventType.color, getProperBackgroundColor())
}
}
}
@ -499,9 +722,234 @@ class TaskActivity : SimpleActivity() {
updateTextColors(task_scrollview)
val textColor = getProperTextColor()
arrayOf(
task_time_image, event_reminder_image, event_type_image
task_time_image, task_reminder_image, task_type_image
).forEach {
it.applyColorFilter(textColor)
}
}
private fun showRepeatIntervalDialog() {
showEventRepeatIntervalDialog(mRepeatInterval) {
setRepeatInterval(it)
}
}
private fun setRepeatInterval(interval: Int) {
mRepeatInterval = interval
updateRepetitionText()
checkRepeatTexts(interval)
when {
mRepeatInterval.isXWeeklyRepetition() -> setRepeatRule(2.0.pow((mTaskDateTime.dayOfWeek - 1).toDouble()).toInt())
mRepeatInterval.isXMonthlyRepetition() -> setRepeatRule(REPEAT_SAME_DAY)
mRepeatInterval.isXYearlyRepetition() -> setRepeatRule(REPEAT_SAME_DAY)
}
}
private fun checkRepeatTexts(limit: Int) {
task_repetition_limit_holder.beGoneIf(limit == 0)
checkRepetitionLimitText()
task_repetition_rule_holder.beVisibleIf(mRepeatInterval.isXWeeklyRepetition() || mRepeatInterval.isXMonthlyRepetition() || mRepeatInterval.isXYearlyRepetition())
checkRepetitionRuleText()
}
private fun showRepetitionTypePicker() {
hideKeyboard()
RepeatLimitTypePickerDialog(this, mRepeatLimit, mTaskDateTime.seconds()) {
setRepeatLimit(it)
}
}
private fun setRepeatLimit(limit: Long) {
mRepeatLimit = limit
checkRepetitionLimitText()
}
private fun checkRepetitionLimitText() {
task_repetition_limit.text = when {
mRepeatLimit == 0L -> {
task_repetition_limit_label.text = getString(R.string.repeat)
resources.getString(R.string.forever)
}
mRepeatLimit > 0 -> {
task_repetition_limit_label.text = getString(R.string.repeat_till)
val repeatLimitDateTime = Formatter.getDateTimeFromTS(mRepeatLimit)
Formatter.getFullDate(this, repeatLimitDateTime)
}
else -> {
task_repetition_limit_label.text = getString(R.string.repeat)
"${-mRepeatLimit} ${getString(R.string.times)}"
}
}
}
private fun showRepetitionRuleDialog() {
hideKeyboard()
when {
mRepeatInterval.isXWeeklyRepetition() -> RepeatRuleWeeklyDialog(this, mRepeatRule) {
setRepeatRule(it)
}
mRepeatInterval.isXMonthlyRepetition() -> {
val items = getAvailableMonthlyRepetitionRules()
RadioGroupDialog(this, items, mRepeatRule) {
setRepeatRule(it as Int)
}
}
mRepeatInterval.isXYearlyRepetition() -> {
val items = getAvailableYearlyRepetitionRules()
RadioGroupDialog(this, items, mRepeatRule) {
setRepeatRule(it as Int)
}
}
}
}
private fun getAvailableMonthlyRepetitionRules(): ArrayList<RadioItem> {
val items = arrayListOf(RadioItem(REPEAT_SAME_DAY, getString(R.string.repeat_on_the_same_day_monthly)))
items.add(RadioItem(REPEAT_ORDER_WEEKDAY, getRepeatXthDayString(true, REPEAT_ORDER_WEEKDAY)))
if (isLastWeekDayOfMonth()) {
items.add(RadioItem(REPEAT_ORDER_WEEKDAY_USE_LAST, getRepeatXthDayString(true, REPEAT_ORDER_WEEKDAY_USE_LAST)))
}
if (isLastDayOfTheMonth()) {
items.add(RadioItem(REPEAT_LAST_DAY, getString(R.string.repeat_on_the_last_day_monthly)))
}
return items
}
private fun getAvailableYearlyRepetitionRules(): ArrayList<RadioItem> {
val items = arrayListOf(RadioItem(REPEAT_SAME_DAY, getString(R.string.repeat_on_the_same_day_yearly)))
items.add(RadioItem(REPEAT_ORDER_WEEKDAY, getRepeatXthDayInMonthString(true, REPEAT_ORDER_WEEKDAY)))
if (isLastWeekDayOfMonth()) {
items.add(RadioItem(REPEAT_ORDER_WEEKDAY_USE_LAST, getRepeatXthDayInMonthString(true, REPEAT_ORDER_WEEKDAY_USE_LAST)))
}
return items
}
private fun isLastDayOfTheMonth() = mTaskDateTime.dayOfMonth == mTaskDateTime.dayOfMonth().withMaximumValue().dayOfMonth
private fun isLastWeekDayOfMonth() = mTaskDateTime.monthOfYear != mTaskDateTime.plusDays(7).monthOfYear
private fun getRepeatXthDayString(includeBase: Boolean, repeatRule: Int): String {
val dayOfWeek = mTaskDateTime.dayOfWeek
val base = getBaseString(dayOfWeek)
val order = getOrderString(repeatRule)
val dayString = getDayString(dayOfWeek)
return if (includeBase) {
"$base $order $dayString"
} else {
val everyString = getString(if (isMaleGender(mTaskDateTime.dayOfWeek)) R.string.every_m else R.string.every_f)
"$everyString $order $dayString"
}
}
private fun getBaseString(day: Int): String {
return getString(
if (isMaleGender(day)) {
R.string.repeat_every_m
} else {
R.string.repeat_every_f
}
)
}
private fun isMaleGender(day: Int) = day == 1 || day == 2 || day == 4 || day == 5
private fun getOrderString(repeatRule: Int): String {
val dayOfMonth = mTaskDateTime.dayOfMonth
var order = (dayOfMonth - 1) / 7 + 1
if (isLastWeekDayOfMonth() && repeatRule == REPEAT_ORDER_WEEKDAY_USE_LAST) {
order = -1
}
val isMale = isMaleGender(mTaskDateTime.dayOfWeek)
return getString(
when (order) {
1 -> if (isMale) R.string.first_m else R.string.first_f
2 -> if (isMale) R.string.second_m else R.string.second_f
3 -> if (isMale) R.string.third_m else R.string.third_f
4 -> if (isMale) R.string.fourth_m else R.string.fourth_f
5 -> if (isMale) R.string.fifth_m else R.string.fifth_f
else -> if (isMale) R.string.last_m else R.string.last_f
}
)
}
private fun getDayString(day: Int): String {
return getString(
when (day) {
1 -> R.string.monday_alt
2 -> R.string.tuesday_alt
3 -> R.string.wednesday_alt
4 -> R.string.thursday_alt
5 -> R.string.friday_alt
6 -> R.string.saturday_alt
else -> R.string.sunday_alt
}
)
}
private fun getRepeatXthDayInMonthString(includeBase: Boolean, repeatRule: Int): String {
val weekDayString = getRepeatXthDayString(includeBase, repeatRule)
val monthString = resources.getStringArray(R.array.in_months)[mTaskDateTime.monthOfYear - 1]
return "$weekDayString $monthString"
}
private fun setRepeatRule(rule: Int) {
mRepeatRule = rule
checkRepetitionRuleText()
if (rule == 0) {
setRepeatInterval(0)
}
}
private fun checkRepetitionRuleText() {
when {
mRepeatInterval.isXWeeklyRepetition() -> {
task_repetition_rule.text = if (mRepeatRule == EVERY_DAY_BIT) getString(R.string.every_day) else getSelectedDaysString(mRepeatRule)
}
mRepeatInterval.isXMonthlyRepetition() -> {
val repeatString = if (mRepeatRule == REPEAT_ORDER_WEEKDAY_USE_LAST || mRepeatRule == REPEAT_ORDER_WEEKDAY)
R.string.repeat else R.string.repeat_on
task_repetition_rule_label.text = getString(repeatString)
task_repetition_rule.text = getMonthlyRepetitionRuleText()
}
mRepeatInterval.isXYearlyRepetition() -> {
val repeatString = if (mRepeatRule == REPEAT_ORDER_WEEKDAY_USE_LAST || mRepeatRule == REPEAT_ORDER_WEEKDAY)
R.string.repeat else R.string.repeat_on
task_repetition_rule_label.text = getString(repeatString)
task_repetition_rule.text = getYearlyRepetitionRuleText()
}
}
}
private fun getMonthlyRepetitionRuleText() = when (mRepeatRule) {
REPEAT_SAME_DAY -> getString(R.string.the_same_day)
REPEAT_LAST_DAY -> getString(R.string.the_last_day)
else -> getRepeatXthDayString(false, mRepeatRule)
}
private fun getYearlyRepetitionRuleText() = when (mRepeatRule) {
REPEAT_SAME_DAY -> getString(R.string.the_same_day)
else -> getRepeatXthDayInMonthString(false, mRepeatRule)
}
private fun updateRepetitionText() {
task_repetition.text = getRepetitionText(mRepeatInterval)
}
private fun updateActionBarTitle() {
if (mIsNewTask) {
updateActionBarTitle(getString(R.string.new_task))
} else {
updateActionBarTitle(getString(R.string.edit_task))
}
}
}

View File

@ -191,27 +191,27 @@ class EventListWidgetAdapter(val context: Context, val intent: Intent) : RemoteV
context.eventsHelper.getEventsSync(fromTS, toTS, applyTypeFilter = true) {
val listItems = ArrayList<ListItem>(it.size)
val replaceDescription = context.config.replaceDescription
val sorted = it.sortedWith(compareBy<Event> {
if (it.getIsAllDay()) {
Formatter.getDayStartTS(Formatter.getDayCodeFromTS(it.startTS)) - 1
val sorted = it.sortedWith(compareBy<Event> { event ->
if (event.getIsAllDay()) {
Formatter.getDayStartTS(Formatter.getDayCodeFromTS(event.startTS)) - 1
} else {
it.startTS
event.startTS
}
}.thenBy {
if (it.getIsAllDay()) {
Formatter.getDayEndTS(Formatter.getDayCodeFromTS(it.endTS))
}.thenBy { event ->
if (event.getIsAllDay()) {
Formatter.getDayEndTS(Formatter.getDayCodeFromTS(event.endTS))
} else {
it.endTS
event.endTS
}
}.thenBy { it.title }.thenBy { if (replaceDescription) it.location else it.description })
}.thenBy { event -> event.title }.thenBy { event -> if (replaceDescription) event.location else event.description })
var prevCode = ""
var prevMonthLabel = ""
val now = getNowSeconds()
val today = Formatter.getDayTitle(context, Formatter.getDayCodeFromTS(now))
sorted.forEach {
val code = Formatter.getDayCodeFromTS(it.startTS)
sorted.forEach { event ->
val code = Formatter.getDayCodeFromTS(event.startTS)
val monthLabel = Formatter.getLongMonthYear(context, code)
if (monthLabel != prevMonthLabel) {
val listSectionMonth = ListSectionMonth(monthLabel)
@ -222,24 +222,24 @@ class EventListWidgetAdapter(val context: Context, val intent: Intent) : RemoteV
if (code != prevCode) {
val day = Formatter.getDateDayTitle(code)
val isToday = day == today
val listSection = ListSectionDay(day, code, isToday, !isToday && it.startTS < now)
val listSection = ListSectionDay(day, code, isToday, !isToday && event.startTS < now)
listItems.add(listSection)
prevCode = code
}
val listEvent = ListEvent(
it.id!!,
it.startTS,
it.endTS,
it.title,
it.description,
it.getIsAllDay(),
it.color,
it.location,
it.isPastEvent,
it.repeatInterval > 0,
it.isTask(),
it.isTaskCompleted()
event.id!!,
event.startTS,
event.endTS,
event.title,
event.description,
event.getIsAllDay(),
event.color,
event.location,
event.isPastEvent,
event.repeatInterval > 0,
event.isTask(),
event.isTaskCompleted()
)
listItems.add(listEvent)
}

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_task_id` ON `tasks` (`id`, `task_id`)")
}
}
}
}
}

View File

@ -11,7 +11,13 @@ import com.simplemobiletools.commons.extensions.beVisibleIf
import com.simplemobiletools.commons.extensions.setupDialogStuff
import kotlinx.android.synthetic.main.dialog_delete_event.view.*
class DeleteEventDialog(val activity: Activity, eventIds: List<Long>, hasRepeatableEvent: Boolean, val callback: (deleteRule: Int) -> Unit) {
class DeleteEventDialog(
val activity: Activity,
eventIds: List<Long>,
hasRepeatableEvent: Boolean,
isTask: Boolean = false,
val callback: (deleteRule: Int) -> Unit
) {
val dialog: AlertDialog?
init {
@ -23,16 +29,22 @@ class DeleteEventDialog(val activity: Activity, eventIds: List<Long>, hasRepeata
}
if (eventIds.size > 1) {
delete_event_repeat_description.text = resources.getString(R.string.selection_contains_repetition)
delete_event_repeat_description.setText(R.string.selection_contains_repetition)
}
if (isTask) {
delete_event_repeat_description.setText(R.string.task_is_repeatable)
} else {
delete_event_repeat_description.setText(R.string.event_is_repeatable)
}
}
dialog = AlertDialog.Builder(activity)
.setPositiveButton(R.string.yes) { dialog, which -> dialogConfirmed(view as ViewGroup) }
.setNegativeButton(R.string.no, null)
.create().apply {
activity.setupDialogStuff(view, this)
}
.setPositiveButton(R.string.yes) { dialog, which -> dialogConfirmed(view as ViewGroup) }
.setNegativeButton(R.string.no, null)
.create().apply {
activity.setupDialogStuff(view, this)
}
}
private fun dialogConfirmed(view: ViewGroup) {

View File

@ -8,22 +8,28 @@ import com.simplemobiletools.commons.extensions.hideKeyboard
import com.simplemobiletools.commons.extensions.setupDialogStuff
import kotlinx.android.synthetic.main.dialog_edit_repeating_event.view.*
class EditRepeatingEventDialog(val activity: SimpleActivity, val callback: (allOccurrences: Int) -> Unit) {
class EditRepeatingEventDialog(val activity: SimpleActivity, val isTask: Boolean = false, val callback: (allOccurrences: Int) -> Unit) {
var dialog: AlertDialog
init {
val view = (activity.layoutInflater.inflate(R.layout.dialog_edit_repeating_event, null) as ViewGroup).apply {
edit_repeating_event_one_only.setOnClickListener { sendResult(0) }
edit_repeating_event_this_and_future_occurences.setOnClickListener { sendResult(1)}
edit_repeating_event_this_and_future_occurences.setOnClickListener { sendResult(1) }
edit_repeating_event_all_occurrences.setOnClickListener { sendResult(2) }
if (isTask) {
edit_repeating_event_title.setText(R.string.task_is_repeatable)
} else {
edit_repeating_event_title.setText(R.string.event_is_repeatable)
}
}
dialog = AlertDialog.Builder(activity)
.create().apply {
activity.setupDialogStuff(view, this) {
hideKeyboard()
}
.create().apply {
activity.setupDialogStuff(view, this) {
hideKeyboard()
}
}
}
private fun sendResult(allOccurrences: Int) {

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 {
@ -634,6 +637,7 @@ fun Context.editEvent(event: ListEvent) {
Intent(this, getActivityToOpen(event.isTask)).apply {
putExtra(EVENT_ID, event.id)
putExtra(EVENT_OCCURRENCE_TS, event.startTS)
putExtra(IS_TASK_COMPLETED, event.isTaskCompleted)
startActivity(this)
}
}
@ -653,3 +657,23 @@ fun Context.getDatesWeekDateTime(date: DateTime): String {
date.withZone(DateTimeZone.UTC).toString()
}
}
fun Context.isTaskCompleted(event: Event): Boolean {
if (event.id == null) return false
val originalEvent = eventsDB.getTaskWithId(event.id!!)
val task = completedTasksDB.getTaskWithIdAndTs(event.id!!, event.startTS)
return originalEvent?.isTaskCompleted() == true || task?.isTaskCompleted() == true
}
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)
}
// mark event as "incomplete" in the main events db
eventsDB.updateTaskCompletion(event.id!!, event.flags.removeBit(FLAG_TASK_COMPLETED))
}

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"
@ -206,6 +207,8 @@ const val EVENT = "EVENT"
const val TASK = "TASK"
const val START_TS = "START_TS"
const val END_TS = "END_TS"
const val ORIGINAL_START_TS = "ORIGINAL_START_TS"
const val ORIGINAL_END_TS = "ORIGINAL_END_TS"
const val REMINDER_1_MINUTES = "REMINDER_1_MINUTES"
const val REMINDER_2_MINUTES = "REMINDER_2_MINUTES"
const val REMINDER_3_MINUTES = "REMINDER_3_MINUTES"
@ -219,6 +222,7 @@ const val ATTENDEES = "ATTENDEES"
const val AVAILABILITY = "AVAILABILITY"
const val EVENT_TYPE_ID = "EVENT_TYPE_ID"
const val EVENT_CALENDAR_ID = "EVENT_CALENDAR_ID"
const val IS_NEW_EVENT = "IS_NEW_EVENT"
// actions
const val ACTION_MARK_COMPLETED = "ACTION_MARK_COMPLETED"

View File

@ -252,7 +252,7 @@ class EventsHelper(val context: Context) {
fun addEventRepetitionException(parentEventId: Long, occurrenceTS: Long, addToCalDAV: Boolean) {
ensureBackgroundThread {
val parentEvent = eventsDB.getEventWithId(parentEventId) ?: return@ensureBackgroundThread
val parentEvent = eventsDB.getEventOrTaskWithId(parentEventId) ?: return@ensureBackgroundThread
var repetitionExceptions = parentEvent.repetitionExceptions
repetitionExceptions.add(Formatter.getDayCodeFromTS(occurrenceTS))
repetitionExceptions = repetitionExceptions.distinct().toMutableList() as ArrayList<String>
@ -285,7 +285,6 @@ class EventsHelper(val context: Context) {
} else {
try {
val typesList = context.config.getDisplayEventTypessAsList()
events.addAll(eventsDB.getTasksFromTo(fromTS, toTS, typesList))
events.addAll(eventsDB.getOneTimeEventsFromToWithTypes(toTS, fromTS, typesList).toMutableList() as ArrayList<Event>)
} catch (e: Exception) {
@ -317,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 &&
@ -364,13 +364,13 @@ class EventsHelper(val context: Context) {
if (displayEventTypes.isEmpty()) {
return ArrayList()
} else {
eventsDB.getRepeatableEventsFromToWithTypes(toTS, context.config.getDisplayEventTypessAsList()).toMutableList() as ArrayList<Event>
eventsDB.getRepeatableEventsOrTasksWithTypes(toTS, context.config.getDisplayEventTypessAsList()).toMutableList() as ArrayList<Event>
}
} else {
if (eventId == -1L) {
eventsDB.getRepeatableEventsFromToWithTypes(toTS).toMutableList() as ArrayList<Event>
eventsDB.getRepeatableEventsOrTasksWithTypes(toTS).toMutableList() as ArrayList<Event>
} else {
eventsDB.getRepeatableEventFromToWithId(eventId, toTS).toMutableList() as ArrayList<Event>
eventsDB.getRepeatableEventsOrTasksWithId(eventId, toTS).toMutableList() as ArrayList<Event>
}
}
@ -482,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

@ -42,14 +42,14 @@ interface EventsDao {
@Query("SELECT * FROM events WHERE end_ts > :toTS AND repeat_interval = 0 AND event_type IN (:eventTypeIds) AND type = $TYPE_EVENT")
fun getOneTimeFutureEventsWithTypes(toTS: Long, eventTypeIds: List<Long>): List<Event>
@Query("SELECT * FROM events WHERE start_ts <= :toTS AND repeat_interval != 0 AND type = $TYPE_EVENT")
fun getRepeatableEventsFromToWithTypes(toTS: Long): List<Event>
@Query("SELECT * FROM events WHERE start_ts <= :toTS AND repeat_interval != 0 AND (type = $TYPE_EVENT OR type = $TYPE_TASK)")
fun getRepeatableEventsOrTasksWithTypes(toTS: Long): List<Event>
@Query("SELECT * FROM events WHERE id = :id AND start_ts <= :toTS AND repeat_interval != 0 AND type = $TYPE_EVENT")
fun getRepeatableEventFromToWithId(id: Long, toTS: Long): List<Event>
@Query("SELECT * FROM events WHERE id = :id AND start_ts <= :toTS AND repeat_interval != 0 AND (type = $TYPE_EVENT OR type = $TYPE_TASK)")
fun getRepeatableEventsOrTasksWithId(id: Long, toTS: Long): List<Event>
@Query("SELECT * FROM events WHERE start_ts <= :toTS AND start_ts != 0 AND repeat_interval != 0 AND event_type IN (:eventTypeIds) AND type = $TYPE_EVENT")
fun getRepeatableEventsFromToWithTypes(toTS: Long, eventTypeIds: List<Long>): List<Event>
@Query("SELECT * FROM events WHERE start_ts <= :toTS AND start_ts != 0 AND repeat_interval != 0 AND event_type IN (:eventTypeIds) AND (type = $TYPE_EVENT OR type = $TYPE_TASK)")
fun getRepeatableEventsOrTasksWithTypes(toTS: Long, eventTypeIds: List<Long>): List<Event>
@Query("SELECT * FROM events WHERE repeat_interval != 0 AND (repeat_limit == 0 OR repeat_limit > :currTS) AND event_type IN (:eventTypeIds) AND type = $TYPE_EVENT")
fun getRepeatableFutureEventsWithTypes(currTS: Long, eventTypeIds: List<Long>): List<Event>
@ -106,12 +106,13 @@ interface EventsDao {
@Query("UPDATE events SET import_id = :importId, source = :source WHERE id = :id AND type = $TYPE_EVENT")
fun updateEventImportIdAndSource(importId: String, source: String, id: Long)
@Query("UPDATE events SET repeat_limit = :repeatLimit WHERE id = :id AND type = $TYPE_EVENT")
@Query("UPDATE events SET repeat_limit = :repeatLimit WHERE id = :id AND (type = $TYPE_EVENT OR type = $TYPE_TASK)")
fun updateEventRepetitionLimit(repeatLimit: Long, id: Long)
@Query("UPDATE events SET repetition_exceptions = :repetitionExceptions WHERE id = :id AND type = $TYPE_EVENT")
@Query("UPDATE events SET repetition_exceptions = :repetitionExceptions WHERE id = :id AND (type = $TYPE_EVENT OR type = $TYPE_TASK)")
fun updateEventRepetitionExceptions(repetitionExceptions: String, id: Long)
@Deprecated("Use Context.updateTaskCompletion() instead unless you know what you are doing.")
@Query("UPDATE events SET flags = :newFlags WHERE id = :id")
fun updateTaskCompletion(id: Long, newFlags: Int)

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,17 @@
package com.simplemobiletools.calendar.pro.models
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.Index
import androidx.room.PrimaryKey
import com.simplemobiletools.calendar.pro.helpers.FLAG_TASK_COMPLETED
@Entity(tableName = "tasks", indices = [(Index(value = ["id", "task_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,
) {
fun isTaskCompleted() = flags and FLAG_TASK_COMPLETED != 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!!)
}

View File

@ -114,7 +114,7 @@
tools:text="00:00" />
<ImageView
android:id="@+id/event_date_time_divider"
android:id="@+id/task_date_time_divider"
android:layout_width="match_parent"
android:layout_height="1px"
android:layout_below="@+id/task_date"
@ -124,23 +124,23 @@
android:importantForAccessibility="no" />
<ImageView
android:id="@+id/event_reminder_image"
android:id="@+id/task_reminder_image"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_below="@+id/event_date_time_divider"
android:layout_alignTop="@+id/event_reminder_1"
android:layout_alignBottom="@+id/event_reminder_1"
android:layout_below="@+id/task_date_time_divider"
android:layout_alignTop="@+id/task_reminder_1"
android:layout_alignBottom="@+id/task_reminder_1"
android:layout_marginStart="@dimen/normal_margin"
android:padding="@dimen/medium_margin"
android:src="@drawable/ic_bell_vector" />
<com.simplemobiletools.commons.views.MyTextView
android:id="@+id/event_reminder_1"
android:id="@+id/task_reminder_1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/event_date_time_divider"
android:layout_below="@+id/task_date_time_divider"
android:layout_marginStart="@dimen/small_margin"
android:layout_toEndOf="@+id/event_reminder_image"
android:layout_toEndOf="@+id/task_reminder_image"
android:background="?attr/selectableItemBackground"
android:ellipsize="end"
android:lines="1"
@ -152,11 +152,11 @@
tools:text="@string/add_another_reminder" />
<com.simplemobiletools.commons.views.MyTextView
android:id="@+id/event_reminder_2"
android:id="@+id/task_reminder_2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/event_reminder_1"
android:layout_alignStart="@+id/event_reminder_1"
android:layout_below="@+id/task_reminder_1"
android:layout_alignStart="@+id/task_reminder_1"
android:alpha="0.4"
android:background="?attr/selectableItemBackground"
android:ellipsize="end"
@ -171,11 +171,11 @@
tools:text="@string/add_another_reminder" />
<com.simplemobiletools.commons.views.MyTextView
android:id="@+id/event_reminder_3"
android:id="@+id/task_reminder_3"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/event_reminder_2"
android:layout_alignStart="@+id/event_reminder_1"
android:layout_below="@+id/task_reminder_2"
android:layout_alignStart="@+id/task_reminder_1"
android:alpha="0.4"
android:background="?attr/selectableItemBackground"
android:ellipsize="end"
@ -190,49 +190,146 @@
tools:text="@string/add_another_reminder" />
<ImageView
android:id="@+id/event_caldav_calendar_divider"
android:id="@+id/task_caldav_calendar_divider"
android:layout_width="match_parent"
android:layout_height="1px"
android:layout_below="@+id/event_reminder_3"
android:layout_below="@+id/task_reminder_3"
android:layout_marginTop="@dimen/medium_margin"
android:layout_marginBottom="@dimen/medium_margin"
android:background="@color/divider_grey"
android:importantForAccessibility="no" />
<ImageView
android:id="@+id/event_type_image"
android:id="@+id/task_repetition_image"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_below="@+id/event_caldav_calendar_divider"
android:layout_alignTop="@+id/event_type_holder"
android:layout_alignBottom="@+id/event_type_holder"
android:layout_below="@+id/task_caldav_calendar_divider"
android:layout_alignTop="@+id/task_repetition"
android:layout_alignBottom="@+id/task_repetition"
android:layout_marginStart="@dimen/normal_margin"
android:padding="@dimen/medium_margin"
android:src="@drawable/ic_repeat_vector" />
<com.simplemobiletools.commons.views.MyTextView
android:id="@+id/task_repetition"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/task_caldav_calendar_divider"
android:layout_marginStart="@dimen/small_margin"
android:layout_toEndOf="@+id/task_repetition_image"
android:background="?attr/selectableItemBackground"
android:paddingTop="@dimen/normal_margin"
android:paddingEnd="@dimen/activity_margin"
android:paddingBottom="@dimen/normal_margin"
android:textSize="@dimen/day_text_size"
tools:text="@string/no_repetition" />
<RelativeLayout
android:id="@+id/task_repetition_rule_holder"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/task_repetition"
android:layout_alignStart="@+id/task_repetition"
android:background="?attr/selectableItemBackground"
android:visibility="gone">
<com.simplemobiletools.commons.views.MyTextView
android:id="@+id/task_repetition_rule_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:clickable="false"
android:paddingTop="@dimen/activity_margin"
android:paddingBottom="@dimen/activity_margin"
android:text="@string/repeat_on"
android:textSize="@dimen/day_text_size" />
<com.simplemobiletools.commons.views.MyTextView
android:id="@+id/task_repetition_rule"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_toEndOf="@+id/task_repetition_rule_label"
android:clickable="false"
android:gravity="end"
android:padding="@dimen/activity_margin"
android:text="@string/every_day"
android:textSize="@dimen/day_text_size" />
</RelativeLayout>
<RelativeLayout
android:id="@+id/task_repetition_limit_holder"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/task_repetition_rule_holder"
android:layout_alignStart="@+id/task_repetition"
android:background="?attr/selectableItemBackground"
android:visibility="gone">
<com.simplemobiletools.commons.views.MyTextView
android:id="@+id/task_repetition_limit_label"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_toStartOf="@+id/task_repetition_limit"
android:clickable="false"
android:paddingTop="@dimen/activity_margin"
android:paddingBottom="@dimen/activity_margin"
android:text="@string/repeat_till"
android:textSize="@dimen/day_text_size" />
<com.simplemobiletools.commons.views.MyTextView
android:id="@+id/task_repetition_limit"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:clickable="false"
android:padding="@dimen/activity_margin"
android:text="@string/forever"
android:textSize="@dimen/day_text_size" />
</RelativeLayout>
<ImageView
android:id="@+id/task_repetition_divider"
android:layout_width="match_parent"
android:layout_height="1px"
android:layout_below="@+id/task_repetition_limit_holder"
android:layout_marginTop="@dimen/medium_margin"
android:background="@color/divider_grey"
android:importantForAccessibility="no" />
<ImageView
android:id="@+id/task_type_image"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_below="@+id/task_repetition_divider"
android:layout_alignTop="@+id/task_type_holder"
android:layout_alignBottom="@+id/task_type_holder"
android:layout_marginStart="@dimen/normal_margin"
android:padding="@dimen/medium_margin"
android:src="@drawable/ic_color_vector" />
<RelativeLayout
android:id="@+id/event_type_holder"
android:id="@+id/task_type_holder"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/event_caldav_calendar_divider"
android:layout_below="@+id/task_repetition_divider"
android:layout_marginTop="@dimen/medium_margin"
android:layout_marginBottom="@dimen/medium_margin"
android:layout_toEndOf="@+id/event_type_image"
android:layout_toEndOf="@+id/task_type_image"
android:background="?attr/selectableItemBackground">
<com.simplemobiletools.commons.views.MyTextView
android:id="@+id/event_type"
android:id="@+id/task_type"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/small_margin"
android:layout_marginEnd="@dimen/medium_margin"
android:layout_toStartOf="@+id/event_type_color"
android:layout_toStartOf="@+id/task_type_color"
android:paddingTop="@dimen/normal_margin"
android:paddingBottom="@dimen/normal_margin"
android:textSize="@dimen/day_text_size" />
<ImageView
android:id="@+id/event_type_color"
android:id="@+id/task_type_color"
android:layout_width="@dimen/color_sample_size"
android:layout_height="@dimen/color_sample_size"
android:layout_alignParentEnd="true"
@ -243,10 +340,10 @@
</RelativeLayout>
<ImageView
android:id="@+id/event_type_divider"
android:id="@+id/task_type_divider"
android:layout_width="match_parent"
android:layout_height="1px"
android:layout_below="@+id/event_type_holder"
android:layout_below="@+id/task_type_holder"
android:background="@color/divider_grey"
android:importantForAccessibility="no" />
@ -255,7 +352,7 @@
style="@style/ColoredButtonStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/event_type_divider"
android:layout_below="@+id/task_type_divider"
android:layout_centerHorizontal="true"
android:layout_marginTop="@dimen/activity_margin"
android:text="@string/mark_completed"

View File

@ -59,6 +59,7 @@
<string name="repeat_till">تكرار حتى</string>
<string name="forever">للأبد</string>
<string name="event_is_repeatable">الحدث متكرر</string>
<string name="task_is_repeatable">The task is repeatable</string>
<string name="selection_contains_repetition">الإختيار يحتوى على احداث مكررة</string>
<string name="delete_one_only">حذف النسخ المحددة فقط</string>
<string name="delete_future_occurrences">حذف هذه النسخة وكل النسخ المستقبلية</string>

View File

@ -59,6 +59,7 @@
<string name="repeat_till">Bu vaxta qədər təkrarla</string>
<string name="forever">Sonsuz</string>
<string name="event_is_repeatable">Bu hadisə təkrarlanabilər</string>
<string name="task_is_repeatable">The task is repeatable</string>
<string name="selection_contains_repetition">Seçim təkrarlanan hadisələr ehtiva edir</string>
<string name="delete_one_only">Yalnız seçilmiş hadisəni sil</string>
<string name="delete_future_occurrences">Bunu və bütün gələcək hadisələri sil</string>
@ -279,4 +280,4 @@
Haven't found some strings? There's more at
https://github.com/SimpleMobileTools/Simple-Commons/tree/master/commons/src/main/res
-->
</resources>
</resources>

View File

@ -59,6 +59,7 @@
<string name="repeat_till">Repeat till</string>
<string name="forever">Forever</string>
<string name="event_is_repeatable">The event is repeatable</string>
<string name="task_is_repeatable">The task is repeatable</string>
<string name="selection_contains_repetition">The selection contains repeating events</string>
<string name="delete_one_only">Delete the selected occurrence only</string>
<string name="delete_future_occurrences">Delete this and all future occurrences</string>
@ -285,4 +286,4 @@
Haven't found some strings? There's more at
https://github.com/SimpleMobileTools/Simple-Commons/tree/master/commons/src/main/res
-->
</resources>
</resources>

View File

@ -59,6 +59,7 @@
<string name="repeat_till">Повтаряй до</string>
<string name="forever">Завинаги</string>
<string name="event_is_repeatable">Събитието се повтаря</string>
<string name="task_is_repeatable">The task is repeatable</string>
<string name="selection_contains_repetition">Изборът съдържа само повтарящи се събития</string>
<string name="delete_one_only">Изтрийте само избраното събитие</string>
<string name="delete_future_occurrences">Изтрийте това събитие и всички бъдещи събития</string>
@ -279,4 +280,4 @@
Haven't found some strings? There's more at
https://github.com/SimpleMobileTools/Simple-Commons/tree/master/commons/src/main/res
-->
</resources>
</resources>

View File

@ -58,6 +58,7 @@
<string name="repeat_till">পুনরাবৃত্তি ততক্ষণ পর্যন্ত</string>
<string name="forever">চিরতরে</string>
<string name="event_is_repeatable">ঘটনাটি পুনরাবৃত্তিযোগ্য</string>
<string name="task_is_repeatable">The task is repeatable</string>
<string name="selection_contains_repetition">সিলেকশনটিতে পুনরাবৃত্তি ইভেন্টগুলি রয়েছে</string>
<string name="delete_one_only">কেবলমাত্র সিলেক্টেড ঘটনা মুছুন</string>
<string name="delete_future_occurrences">এটি এবং ভবিষ্যতের সমস্ত ঘটনা মুছুন</string>
@ -278,4 +279,4 @@
Haven't found some strings? There's more at
https://github.com/SimpleMobileTools/Simple-Commons/tree/master/commons/src/main/res
-->
</resources>
</resources>

View File

@ -59,6 +59,7 @@
<string name="repeat_till">addegouezhout betek</string>
<string name="forever">Da viken</string>
<string name="event_is_repeatable">An darvoud a c\'hall addegouezhout</string>
<string name="task_is_repeatable">The task is repeatable</string>
<string name="selection_contains_repetition">Darvoudoù a c\'hall addegouezhout a zo en diuzad</string>
<string name="delete_one_only">Dilemel an degouezh bremanel nemetken</string>
<string name="delete_future_occurrences">Delete this and all future occurrences</string>

View File

@ -59,6 +59,7 @@
<string name="repeat_till">Repeteix fins</string>
<string name="forever">Per sempre</string>
<string name="event_is_repeatable">L\'esdeveniment és repetible</string>
<string name="task_is_repeatable">The task is repeatable</string>
<string name="selection_contains_repetition">La selecció conté esdeveniments que es repeteixen</string>
<string name="delete_one_only">Suprimeix només l\'ocurrència seleccionada</string>
<string name="delete_future_occurrences">Suprimeix aquesta i totes les ocurrències futures</string>
@ -279,4 +280,4 @@
Haven't found some strings? There's more at
https://github.com/SimpleMobileTools/Simple-Commons/tree/master/commons/src/main/res
-->
</resources>
</resources>

View File

@ -59,6 +59,7 @@
<string name="repeat_till">Opakovat do</string>
<string name="forever">Navždy</string>
<string name="event_is_repeatable">Událost se opakuje</string>
<string name="task_is_repeatable">The task is repeatable</string>
<string name="selection_contains_repetition">Výběr zahrnuje opakující se události</string>
<string name="delete_one_only">Vymazat pouze vybrané výskyty</string>
<string name="delete_future_occurrences">Vymazat tento a jakékoliv budoucí výskyty</string>
@ -282,4 +283,4 @@
Haven't found some strings? There's more at
https://github.com/SimpleMobileTools/Simple-Commons/tree/master/commons/src/main/res
-->
</resources>
</resources>

View File

@ -59,6 +59,7 @@
<string name="repeat_till">Gentag indtil</string>
<string name="forever">Ingen slutdato</string>
<string name="event_is_repeatable">Begivenheden kan gentages</string>
<string name="task_is_repeatable">The task is repeatable</string>
<string name="selection_contains_repetition">Valget indeholder gentagne begivenheder</string>
<string name="delete_one_only">Slet kun denne forekomst</string>
<string name="delete_future_occurrences">Slet denne og alle fremtidige forekomster</string>
@ -279,4 +280,4 @@
Haven't found some strings? There's more at
https://github.com/SimpleMobileTools/Simple-Commons/tree/master/commons/src/main/res
-->
</resources>
</resources>

View File

@ -59,6 +59,7 @@
<string name="repeat_till">Wiederholen bis</string>
<string name="forever">unendlich</string>
<string name="event_is_repeatable">Termin ist wiederholbar</string>
<string name="task_is_repeatable">The task is repeatable</string>
<string name="selection_contains_repetition">Die Auswahl enthält wiederkehrende Termine</string>
<string name="delete_one_only">Nur die ausgewählte Wiederholung löschen</string>
<string name="delete_future_occurrences">Diese und zukünftige Wiederholungen löschen</string>
@ -279,4 +280,4 @@
Haven't found some strings? There's more at
https://github.com/SimpleMobileTools/Simple-Commons/tree/master/commons/src/main/res
-->
</resources>
</resources>

View File

@ -59,6 +59,7 @@
<string name="repeat_till">Επανάληψη μέχρι</string>
<string name="forever">Για πάντα</string>
<string name="event_is_repeatable">Η εκδήλωση είναι επαναλαμβανόμενη</string>
<string name="task_is_repeatable">The task is repeatable</string>
<string name="selection_contains_repetition">Η επιλογή περιέχει επαναλαμβανόμενες εκδηλώσεις</string>
<string name="delete_one_only">Διαγράψτε μόνο το επιλεγμένο περιστατικό</string>
<string name="delete_future_occurrences">Διαγράψτε αυτό και όλα τα μελλοντικά συμβάντα</string>
@ -279,4 +280,4 @@
Haven't found some strings? There's more at
https://github.com/SimpleMobileTools/Simple-Commons/tree/master/commons/src/main/res
-->
</resources>
</resources>

View File

@ -59,6 +59,7 @@
<string name="repeat_till">Ripeti ĝis</string>
<string name="forever">Forever</string>
<string name="event_is_repeatable">The event is repeatable</string>
<string name="task_is_repeatable">The task is repeatable</string>
<string name="selection_contains_repetition">The selection contains repeating events</string>
<string name="delete_one_only">Delete the selected occurrence only</string>
<string name="delete_future_occurrences">Delete this and all future occurrences</string>
@ -279,4 +280,4 @@
Haven't found some strings? There's more at
https://github.com/SimpleMobileTools/Simple-Commons/tree/master/commons/src/main/res
-->
</resources>
</resources>

View File

@ -59,6 +59,7 @@
<string name="repeat_till">Repetir hasta</string>
<string name="forever">Siempre</string>
<string name="event_is_repeatable">Este evento se repite</string>
<string name="task_is_repeatable">The task is repeatable</string>
<string name="selection_contains_repetition">La selección contiene repetición de eventos</string>
<string name="delete_one_only">Eliminar solo el evento seleccionado</string>
<string name="delete_future_occurrences">Eliminar el evento y repeticiones futuras</string>
@ -279,4 +280,4 @@
Haven't found some strings? There's more at
https://github.com/SimpleMobileTools/Simple-Commons/tree/master/commons/src/main/res
-->
</resources>
</resources>

View File

@ -59,6 +59,7 @@
<string name="repeat_till">Korda kuni</string>
<string name="forever">Igavesti</string>
<string name="event_is_repeatable">Sündmus on korratav</string>
<string name="task_is_repeatable">The task is repeatable</string>
<string name="selection_contains_repetition">Valik sisaldab korduvaid sündmusi</string>
<string name="delete_one_only">Delete the selected occurrence only</string>
<string name="delete_future_occurrences">Delete this and all future occurrences</string>
@ -279,4 +280,4 @@
Haven't found some strings? There's more at
https://github.com/SimpleMobileTools/Simple-Commons/tree/master/commons/src/main/res
-->
</resources>
</resources>

View File

@ -59,6 +59,7 @@
<string name="repeat_till">Noiz arte errepikatu</string>
<string name="forever">Betiko</string>
<string name="event_is_repeatable">Gertaera errepikagarria da</string>
<string name="task_is_repeatable">The task is repeatable</string>
<string name="selection_contains_repetition">Hautaketak errepikatzen diren gertaerak ditu</string>
<string name="delete_one_only">Ezabatu hautatutako gertaera soilik</string>
<string name="delete_future_occurrences">Ezabatu gertaera hau eta datozen guztiak</string>
@ -279,4 +280,4 @@
Haven't found some strings? There's more at
https://github.com/SimpleMobileTools/Simple-Commons/tree/master/commons/src/main/res
-->
</resources>
</resources>

View File

@ -59,6 +59,7 @@
<string name="repeat_till">Toista kunnes</string>
<string name="forever">Aina</string>
<string name="event_is_repeatable">Tapahtuma on toistettavissa</string>
<string name="task_is_repeatable">The task is repeatable</string>
<string name="selection_contains_repetition">Valinta sisältää toistettavia tapahtumia</string>
<string name="delete_one_only">Poista vain valittu esiintymä</string>
<string name="delete_future_occurrences">Poista tämä ja seuraavat esiintymät</string>
@ -279,4 +280,4 @@
Haven't found some strings? There's more at
https://github.com/SimpleMobileTools/Simple-Commons/tree/master/commons/src/main/res
-->
</resources>
</resources>

View File

@ -59,6 +59,7 @@
<string name="repeat_till">Répéter jusqu\'à</string>
<string name="forever">Éternellement</string>
<string name="event_is_repeatable">L\'évènement est périodique</string>
<string name="task_is_repeatable">The task is repeatable</string>
<string name="selection_contains_repetition">La sélection contient des évènements périodiques</string>
<string name="delete_one_only">Supprimer seulement l\'occurrence sélectionnée</string>
<string name="delete_future_occurrences">Supprimer cette occurrence et toutes les occurrences futures</string>
@ -279,4 +280,4 @@
Haven't found some strings? There's more at
https://github.com/SimpleMobileTools/Simple-Commons/tree/master/commons/src/main/res
-->
</resources>
</resources>

View File

@ -59,6 +59,7 @@
<string name="repeat_till">Repetir ata</string>
<string name="forever">Sempre</string>
<string name="event_is_repeatable">O evento é repetible</string>
<string name="task_is_repeatable">The task is repeatable</string>
<string name="selection_contains_repetition">A selección contén eventos recurrentes</string>
<string name="delete_one_only">Eliminar só o evento seleccionado</string>
<string name="delete_future_occurrences">Eliminar este e todos os eventos futuros</string>
@ -279,4 +280,4 @@
Haven't found some strings? There's more at
https://github.com/SimpleMobileTools/Simple-Commons/tree/master/commons/src/main/res
-->
</resources>
</resources>

View File

@ -59,6 +59,7 @@
<string name="repeat_till">Repeat till</string>
<string name="forever">Forever</string>
<string name="event_is_repeatable">The event is repeatable</string>
<string name="task_is_repeatable">The task is repeatable</string>
<string name="selection_contains_repetition">The selection contains repeating events</string>
<string name="delete_one_only">Delete the selected occurrence only</string>
<string name="delete_future_occurrences">Delete this and all future occurrences</string>
@ -279,4 +280,4 @@
Haven't found some strings? There's more at
https://github.com/SimpleMobileTools/Simple-Commons/tree/master/commons/src/main/res
-->
</resources>
</resources>

View File

@ -59,6 +59,7 @@
<string name="repeat_till">Ponavljaj do</string>
<string name="forever">Zauvijek</string>
<string name="event_is_repeatable">Događaj se ponavlja</string>
<string name="task_is_repeatable">The task is repeatable</string>
<string name="selection_contains_repetition">Odabir sadrži ponavljajuće događaje</string>
<string name="delete_one_only">Izbriši samo odabrano ponavljanje</string>
<string name="delete_future_occurrences">Izbriši ovo i sva buduća ponavljanja</string>
@ -282,4 +283,4 @@
Haven't found some strings? There's more at
https://github.com/SimpleMobileTools/Simple-Commons/tree/master/commons/src/main/res
-->
</resources>
</resources>

View File

@ -59,6 +59,7 @@
<string name="repeat_till">Ismétlés eddig:</string>
<string name="forever">Örökké</string>
<string name="event_is_repeatable">Az esemény ismétlődik</string>
<string name="task_is_repeatable">The task is repeatable</string>
<string name="selection_contains_repetition">A kiválasztás ismétlődő eseményeket tartalmaz</string>
<string name="delete_one_only">Csak a kiválasztott előfordulás törlése</string>
<string name="delete_future_occurrences">Ez és az összes jövőbeli előfordulás törlése</string>
@ -279,4 +280,4 @@
Haven't found some strings? There's more at
https://github.com/SimpleMobileTools/Simple-Commons/tree/master/commons/src/main/res
-->
</resources>
</resources>

View File

@ -59,6 +59,7 @@
<string name="repeat_till">Ulangi sampai</string>
<string name="forever">Selamanya</string>
<string name="event_is_repeatable">Acara berulang</string>
<string name="task_is_repeatable">The task is repeatable</string>
<string name="selection_contains_repetition">Acara yang dipilih berisi acara yang berulang</string>
<string name="delete_one_only">Hapus acara ini saja</string>
<string name="delete_future_occurrences">Hapus acara ini dan semua perulangannya</string>

View File

@ -59,6 +59,7 @@
<string name="repeat_till">Ripeti fino a</string>
<string name="forever">Per sempre</string>
<string name="event_is_repeatable">L\'evento è ripetibile</string>
<string name="task_is_repeatable">The task is repeatable</string>
<string name="selection_contains_repetition">La selezione contiene eventi ripetuti</string>
<string name="delete_one_only">Elimina solamente l\'occorenza selezionata</string>
<string name="delete_future_occurrences">Rimuovi questo e tutte le future occorrenze</string>
@ -279,4 +280,4 @@
Haven't found some strings? There's more at
https://github.com/SimpleMobileTools/Simple-Commons/tree/master/commons/src/main/res
-->
</resources>
</resources>

View File

@ -59,6 +59,7 @@
<string name="repeat_till">חזרה עד</string>
<string name="forever">לנצח</string>
<string name="event_is_repeatable">האירוע ניתן לחזרה</string>
<string name="task_is_repeatable">The task is repeatable</string>
<string name="selection_contains_repetition">הבחירה כוללת אירועים חוזרים</string>
<string name="delete_one_only">מחיקת אירוע בודד</string>
<string name="delete_future_occurrences">מחיקת כל האירועים העתידיים בסדרה</string>

View File

@ -60,6 +60,7 @@
<string name="repeat_till">Repeat till</string>
<string name="forever">Forever</string>
<string name="event_is_repeatable">The event is repeatable</string>
<string name="task_is_repeatable">The task is repeatable</string>
<string name="selection_contains_repetition">The selection contains repeating events</string>
<string name="delete_one_only">Delete the selected occurrence only</string>
<string name="delete_future_occurrences">Delete this and all future occurrences</string>
@ -277,4 +278,4 @@
Haven't found some strings? There's more at
https://github.com/SimpleMobileTools/Simple-Commons/tree/master/commons/src/main/res
-->
</resources>
</resources>

View File

@ -59,6 +59,7 @@
<string name="repeat_till">까지 반복</string>
<string name="forever">영원히</string>
<string name="event_is_repeatable">반복 일정입니다</string>
<string name="task_is_repeatable">The task is repeatable</string>
<string name="selection_contains_repetition">선택한 항목에 반복되는 일정들이 있습니다</string>
<string name="delete_one_only">선택한 항목만 삭제</string>
<string name="delete_future_occurrences">반복되는 일정까지 삭제</string>

View File

@ -59,6 +59,7 @@
<string name="repeat_till">Kartoti iki</string>
<string name="forever">Amžinai</string>
<string name="event_is_repeatable">Įvykis yra pasikartojantis</string>
<string name="task_is_repeatable">The task is repeatable</string>
<string name="selection_contains_repetition">Žymėjime yra pasikartojančių įvykių</string>
<string name="delete_one_only">Ištrinti tik pasirinktą pasikartojimą</string>
<string name="delete_future_occurrences">Ištrinti šį ir visus būsimus pasikartojimus</string>
@ -282,4 +283,4 @@
Haven't found some strings? There's more at
https://github.com/SimpleMobileTools/Simple-Commons/tree/master/commons/src/main/res
-->
</resources>
</resources>

View File

@ -59,6 +59,7 @@
<string name="repeat_till">Atkārtot līdz</string>
<string name="forever">Bezgalīgi</string>
<string name="event_is_repeatable">Notikums ir atkārtojošs</string>
<string name="task_is_repeatable">The task is repeatable</string>
<string name="selection_contains_repetition">Atlasītajos ir atkārtojoši/periodiski notikumi</string>
<string name="delete_one_only">Dzēst tikai šo atlasīto notikumu</string>
<string name="delete_future_occurrences">Dzēst šo un visus turpmākos notikumus</string>

View File

@ -59,6 +59,7 @@
<string name="repeat_till">Gjenta til</string>
<string name="forever">For alltid</string>
<string name="event_is_repeatable">Hendelsen er repeterbar</string>
<string name="task_is_repeatable">The task is repeatable</string>
<string name="selection_contains_repetition">Markeringen inneholder gjentagende hendelser</string>
<string name="delete_one_only">Slett bare den merkede forekomsten</string>
<string name="delete_future_occurrences">Slett denne og alle framtidige forekomster</string>
@ -279,4 +280,4 @@
Haven't found some strings? There's more at
https://github.com/SimpleMobileTools/Simple-Commons/tree/master/commons/src/main/res
-->
</resources>
</resources>

View File

@ -55,6 +55,7 @@
<string name="repeat_till">Herhalen tot</string>
<string name="forever">Blijven herhalen</string>
<string name="event_is_repeatable">De afspraak wordt herhaald</string>
<string name="task_is_repeatable">The task is repeatable</string>
<string name="selection_contains_repetition">De selectie bevat herhaalde afspraken</string>
<string name="delete_one_only">Alleen huidige afspraak verwijderen</string>
<string name="delete_future_occurrences">Deze afspraak en hierop volgende herhalingen verwijderen</string>
@ -275,4 +276,4 @@
Haven't found some strings? There's more at
https://github.com/SimpleMobileTools/Simple-Commons/tree/master/commons/src/main/res
-->
</resources>
</resources>

View File

@ -59,6 +59,7 @@
<string name="repeat_till">Powtarzaj do</string>
<string name="forever">Zawsze</string>
<string name="event_is_repeatable">Wydarzenie jest cykliczne</string>
<string name="task_is_repeatable">The task is repeatable</string>
<string name="selection_contains_repetition">Wybór zawiera powtarzające się wydarzenia</string>
<string name="delete_one_only">Usuń tylko wybrane wystąpienie</string>
<string name="delete_future_occurrences">Usuń to i wszystkie przyszłe wystąpienia</string>
@ -285,4 +286,4 @@
Haven't found some strings? There's more at
https://github.com/SimpleMobileTools/Simple-Commons/tree/master/commons/src/main/res
-->
</resources>
</resources>

View File

@ -59,6 +59,7 @@
<string name="repeat_till">Repetir até</string>
<string name="forever">Eternamente</string>
<string name="event_is_repeatable">O evento é repetitivo</string>
<string name="task_is_repeatable">The task is repeatable</string>
<string name="selection_contains_repetition">A seleção contém eventos recorrentes</string>
<string name="delete_one_only">Apagar a ocorrência selecionada</string>
<string name="delete_future_occurrences">Exclua essa e todas as ocorrências futuras</string>
@ -280,4 +281,4 @@
Não encontrou todas as cadeias a traduzir? Existem mais algumas em:
https://github.com/SimpleMobileTools/Simple-Commons/tree/master/commons/src/main/res
-->
</resources>
</resources>

View File

@ -59,6 +59,7 @@
<string name="repeat_till">Repetir até</string>
<string name="forever">Eternamente</string>
<string name="event_is_repeatable">O evento é recorrente</string>
<string name="task_is_repeatable">The task is repeatable</string>
<string name="selection_contains_repetition">A seleção contém eventos recorrentes</string>
<string name="delete_one_only">Apagar a ocorrência selecionada</string>
<string name="delete_future_occurrences">Apagar esta e todas as ocorrências futuras</string>
@ -279,4 +280,4 @@
Não encontrou todas as cadeias a traduzir? Existem mais algumas em:
https://github.com/SimpleMobileTools/Simple-Commons/tree/master/commons/src/main/res
-->
</resources>
</resources>

View File

@ -59,6 +59,7 @@
<string name="repeat_till">Repetă până pe</string>
<string name="forever">Pentru totdeauna</string>
<string name="event_is_repeatable">Evenimentul este repetabil</string>
<string name="task_is_repeatable">The task is repeatable</string>
<string name="selection_contains_repetition">Selecția conține evenimente repetitive</string>
<string name="delete_one_only">Șterge numai evenimentul repetitiv selectat</string>
<string name="delete_future_occurrences">Șterge acest și toate evenimentele repetitive viitoare</string>

View File

@ -59,6 +59,7 @@
<string name="repeat_till">Повторять до</string>
<string name="forever">Бесконечно</string>
<string name="event_is_repeatable">Это событие может повторяться</string>
<string name="task_is_repeatable">The task is repeatable</string>
<string name="selection_contains_repetition">В выбранном есть повторяющиеся события</string>
<string name="delete_one_only">Удалить только это событие</string>
<string name="delete_future_occurrences">Удалить это и все будущие события</string>
@ -285,4 +286,4 @@
Haven't found some strings? There's more at
https://github.com/SimpleMobileTools/Simple-Commons/tree/master/commons/src/main/res
-->
</resources>
</resources>

View File

@ -59,6 +59,7 @@
<string name="repeat_till">Opakovať do</string>
<string name="forever">Navždy</string>
<string name="event_is_repeatable">Udalosť je opakujúca sa</string>
<string name="task_is_repeatable">Úloha je opakujúca sa</string>
<string name="selection_contains_repetition">Výber obsahuje opakujúce sa udalosti</string>
<string name="delete_one_only">Vymazať iba označené opakovania</string>
<string name="delete_future_occurrences">Vymazať toto a všetky budúce opakovania</string>
@ -282,4 +283,4 @@
Haven't found some strings? There's more at
https://github.com/SimpleMobileTools/Simple-Commons/tree/master/commons/src/main/res
-->
</resources>
</resources>

View File

@ -59,6 +59,7 @@
<string name="repeat_till">Upprepa tills</string>
<string name="forever">Alltid</string>
<string name="event_is_repeatable">Händelsen är återkommande</string>
<string name="task_is_repeatable">The task is repeatable</string>
<string name="selection_contains_repetition">Markeringen innehåller återkommande händelser</string>
<string name="delete_one_only">Ta bara bort den valda förekomsten</string>
<string name="delete_future_occurrences">Ta bort denna och alla framtida förekomster</string>
@ -279,4 +280,4 @@
Haven't found some strings? There's more at
https://github.com/SimpleMobileTools/Simple-Commons/tree/master/commons/src/main/res
-->
</resources>
</resources>

View File

@ -59,6 +59,7 @@
<string name="repeat_till">Repeat till</string>
<string name="forever">Forever</string>
<string name="event_is_repeatable">The event is repeatable</string>
<string name="task_is_repeatable">The task is repeatable</string>
<string name="selection_contains_repetition">The selection contains repeating events</string>
<string name="delete_one_only">Delete the selected occurrence only</string>
<string name="delete_future_occurrences">Delete this and all future occurrences</string>

View File

@ -59,6 +59,7 @@
<string name="repeat_till">Şu kadar tekrarla:</string>
<string name="forever">Sonsuza kadar</string>
<string name="event_is_repeatable">Etkinlik tekrarlanabilir</string>
<string name="task_is_repeatable">The task is repeatable</string>
<string name="selection_contains_repetition">Seçim tekrarlanan etkinlikleri içeriyor</string>
<string name="delete_one_only">Yalnızca seçilen etkinlikleri sil</string>
<string name="delete_future_occurrences">Bu ve gelecekteki tüm etkinlikleri sil</string>
@ -279,4 +280,4 @@
Haven't found some strings? There's more at
https://github.com/SimpleMobileTools/Simple-Commons/tree/master/commons/src/main/res
-->
</resources>
</resources>

View File

@ -59,6 +59,7 @@
<string name="repeat_till">Повторювати до</string>
<string name="forever">Безкінечно</string>
<string name="event_is_repeatable">Ця подія є повторюваною</string>
<string name="task_is_repeatable">The task is repeatable</string>
<string name="selection_contains_repetition">Вибране містить повторювані події</string>
<string name="delete_one_only">Видалити лише обране повторення</string>
<string name="delete_future_occurrences">Видалити це і всі наступні повторення</string>
@ -285,4 +286,4 @@
Haven't found some strings? There's more at
https://github.com/SimpleMobileTools/Simple-Commons/tree/master/commons/src/main/res
-->
</resources>
</resources>

View File

@ -59,6 +59,7 @@
<string name="repeat_till">重复直到</string>
<string name="forever">永远</string>
<string name="event_is_repeatable">这是个重复活动</string>
<string name="task_is_repeatable">The task is repeatable</string>
<string name="selection_contains_repetition">选择的项目含有重复活动</string>
<string name="delete_one_only">只删除选择的事件</string>
<string name="delete_future_occurrences">删除这个及全部未来的事件</string>
@ -276,4 +277,4 @@
Haven't found some strings? There's more at
https://github.com/SimpleMobileTools/Simple-Commons/tree/master/commons/src/main/res
-->
</resources>
</resources>

View File

@ -59,6 +59,7 @@
<string name="repeat_till">重複直到</string>
<string name="forever">永遠</string>
<string name="event_is_repeatable">這是個重複活動</string>
<string name="task_is_repeatable">The task is repeatable</string>
<string name="selection_contains_repetition">選擇的項目含有重複活動</string>
<string name="delete_one_only">只刪除選擇的事件</string>
<string name="delete_future_occurrences">刪除這個及全部未來的事件</string>
@ -276,4 +277,4 @@
Haven't found some strings? There's more at
https://github.com/SimpleMobileTools/Simple-Commons/tree/master/commons/src/main/res
-->
</resources>
</resources>

View File

@ -59,6 +59,7 @@
<string name="repeat_till">重複直到</string>
<string name="forever">永遠</string>
<string name="event_is_repeatable">這是個重複活動</string>
<string name="task_is_repeatable">The task is repeatable</string>
<string name="selection_contains_repetition">選擇的項目含有重複活動</string>
<string name="delete_one_only">只刪除選擇的事件</string>
<string name="delete_future_occurrences">刪除這個及全部未來的事件</string>

View File

@ -57,6 +57,7 @@
<string name="repeat_till">Repeat till</string>
<string name="forever">Forever</string>
<string name="event_is_repeatable">The event is repeatable</string>
<string name="task_is_repeatable">The task is repeatable</string>
<string name="selection_contains_repetition">The selection contains repeating events</string>
<string name="delete_one_only">Delete the selected occurrence only</string>
<string name="delete_future_occurrences">Delete this and all future occurrences</string>