Merge pull request #1937 from Naveen3Singh/feature_export_tasks

Add option to export/import tasks
This commit is contained in:
Tibor Kaputa 2023-01-15 20:33:46 +01:00 committed by GitHub
commit bf3cf47285
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 281 additions and 79 deletions

View file

@ -1115,7 +1115,7 @@ class EventActivity : SimpleActivity() {
val newImportId = if (mEvent.id != null) {
mEvent.importId
} else {
UUID.randomUUID().toString().replace("-", "") + System.currentTimeMillis().toString()
generateImportId()
}
val newEventType = if (!config.caldavSync || config.lastUsedCaldavCalendarId == 0 || mEventCalendarId == STORED_LOCALLY_ONLY) {

View file

@ -159,6 +159,10 @@ class MainActivity : SimpleActivity(), RefreshRecyclerViewListener {
ConfirmationDialog(this, "", R.string.upgraded_to_pro_calendar, R.string.ok, 0, false) {}
config.wasUpgradedFromFreeShown = true
}
addImportIdsToTasks {
refreshViewPager()
}
}
override fun onResume() {
@ -1129,8 +1133,8 @@ class MainActivity : SimpleActivity(), RefreshRecyclerViewListener {
}
}
} else {
handlePermission(PERMISSION_WRITE_STORAGE) {
if (it) {
handlePermission(PERMISSION_WRITE_STORAGE) { granted ->
if (granted) {
ExportEventsDialog(this, config.lastExportPath, false) { file, eventTypes ->
getFileOutputStream(file.toFileDirItem(this), true) {
exportEventsTo(eventTypes, it)
@ -1147,9 +1151,9 @@ class MainActivity : SimpleActivity(), RefreshRecyclerViewListener {
if (events.isEmpty()) {
toast(R.string.no_entries_for_exporting)
} else {
IcsExporter().exportEvents(this, outputStream, events, true) {
IcsExporter(this).exportEvents(outputStream, events, true) { result ->
toast(
when (it) {
when (result) {
ExportResult.EXPORT_OK -> R.string.exporting_successful
ExportResult.EXPORT_PARTIAL -> R.string.exporting_some_entries_failed
else -> R.string.exporting_failed

View file

@ -351,6 +351,11 @@ class TaskActivity : SimpleActivity() {
}
val wasRepeatable = mTask.repeatInterval > 0
val newImportId = if (mTask.id != null) {
mTask.importId
} else {
generateImportId()
}
val reminders = getReminders()
if (!task_all_day.isChecked) {
@ -393,6 +398,7 @@ class TaskActivity : SimpleActivity() {
updateTaskCompletion(copy(startTS = mOriginalStartTS), true)
}
}
importId = newImportId
flags = mTask.flags.addBitIf(task_all_day.isChecked, FLAG_ALL_DAY)
lastUpdated = System.currentTimeMillis()
eventType = mEventTypeId
@ -458,7 +464,7 @@ class TaskActivity : SimpleActivity() {
when (it) {
0 -> {
ensureBackgroundThread {
eventsHelper.addEventRepetitionException(mTask.id!!, mTaskOccurrenceTS, true)
eventsHelper.addEventRepetitionException(mTask.id!!, mTaskOccurrenceTS, addToCalDAV = false)
mTask.apply {
parentId = id!!.toLong()
id = null
@ -610,7 +616,6 @@ class TaskActivity : SimpleActivity() {
}
}
private fun updateDateText() {
task_date.text = Formatter.getDate(this, mTaskDateTime)
}
@ -766,7 +771,6 @@ class TaskActivity : SimpleActivity() {
}
}
private fun showRepeatIntervalDialog() {
showEventRepeatIntervalDialog(mRepeatInterval) {
setRepeatInterval(it)

View file

@ -17,14 +17,23 @@ class ExportEventsDialog(
val activity: SimpleActivity, val path: String, val hidePath: Boolean,
val callback: (file: File, eventTypes: ArrayList<Long>) -> Unit
) {
private var realPath = if (path.isEmpty()) activity.internalStoragePath else path
private var realPath = path.ifEmpty { activity.internalStoragePath }
private val config = activity.config
init {
val view = (activity.layoutInflater.inflate(R.layout.dialog_export_events, null) as ViewGroup).apply {
export_events_folder.setText(activity.humanizePath(realPath))
export_events_filename.setText("${activity.getString(R.string.events)}_${activity.getCurrentFormattedDateTime()}")
export_past_events_checkbox.isChecked = config.exportPastEvents
export_events_checkbox.isChecked = config.exportEvents
export_events_checkbox_holder.setOnClickListener {
export_events_checkbox.toggle()
}
export_tasks_checkbox.isChecked = config.exportTasks
export_tasks_checkbox_holder.setOnClickListener {
export_tasks_checkbox.toggle()
}
export_past_events_checkbox.isChecked = config.exportPastEntries
export_past_events_checkbox_holder.setOnClickListener {
export_past_events_checkbox.toggle()
}
@ -69,9 +78,20 @@ class ExportEventsDialog(
return@setOnClickListener
}
val exportEventsChecked = view.export_events_checkbox.isChecked
val exportTasksChecked = view.export_tasks_checkbox.isChecked
if (!exportEventsChecked && !exportTasksChecked) {
activity.toast(R.string.no_entries_for_exporting)
return@setOnClickListener
}
ensureBackgroundThread {
config.lastExportPath = file.absolutePath.getParentPath()
config.exportPastEvents = view.export_past_events_checkbox.isChecked
config.apply {
lastExportPath = file.absolutePath.getParentPath()
exportEvents = exportEventsChecked
exportTasks = exportTasksChecked
exportPastEntries = view.export_past_events_checkbox.isChecked
}
val eventTypes = (view.export_events_types_list.adapter as FilterEventTypeAdapter).getSelectedItemsList()
callback(file, eventTypes)

View file

@ -13,7 +13,6 @@ import com.simplemobiletools.commons.helpers.ensureBackgroundThread
import com.simplemobiletools.commons.models.RadioItem
import java.io.File
import java.util.*
import kotlin.collections.ArrayList
fun BaseSimpleActivity.shareEvents(ids: List<Long>) {
ensureBackgroundThread {
@ -29,8 +28,8 @@ fun BaseSimpleActivity.shareEvents(ids: List<Long>) {
}
getFileOutputStream(file.toFileDirItem(this), true) {
IcsExporter().exportEvents(this, it, events, false) {
if (it == IcsExporter.ExportResult.EXPORT_OK) {
IcsExporter(this).exportEvents(it, events, false) { result ->
if (result == IcsExporter.ExportResult.EXPORT_OK) {
sharePathIntent(file.absolutePath, BuildConfig.APPLICATION_ID)
}
}

View file

@ -708,3 +708,23 @@ inline fun Context.queryCursorInlined(
}
}
}
fun Context.addImportIdsToTasks(callback: () -> Unit) {
ensureBackgroundThread {
var count = 0
eventsDB.getAllTasks().forEach { task ->
if (task.importId.isEmpty()) {
eventsDB.updateTaskImportId(
importId = generateImportId(),
id = task.id!!
)
count += 1
}
}
if (count > 0) {
callback()
}
}
}

View file

@ -223,7 +223,15 @@ class Config(context: Context) : BaseConfig(context) {
get() = prefs.getString(LAST_EXPORT_PATH, "")!!
set(lastExportPath) = prefs.edit().putString(LAST_EXPORT_PATH, lastExportPath).apply()
var exportPastEvents: Boolean
var exportEvents: Boolean
get() = prefs.getBoolean(EXPORT_EVENTS, true)
set(exportEvents) = prefs.edit().putBoolean(EXPORT_EVENTS, exportEvents).apply()
var exportTasks: Boolean
get() = prefs.getBoolean(EXPORT_TASKS, true)
set(exportTasks) = prefs.edit().putBoolean(EXPORT_TASKS, exportTasks).apply()
var exportPastEntries: Boolean
get() = prefs.getBoolean(EXPORT_PAST_EVENTS, false)
set(exportPastEvents) = prefs.edit().putBoolean(EXPORT_PAST_EVENTS, exportPastEvents).apply()

View file

@ -3,6 +3,7 @@ package com.simplemobiletools.calendar.pro.helpers
import com.simplemobiletools.calendar.pro.activities.EventActivity
import com.simplemobiletools.calendar.pro.activities.TaskActivity
import com.simplemobiletools.commons.helpers.MONTH_SECONDS
import java.util.*
const val STORED_LOCALLY_ONLY = 0
const val ROW_COUNT = 6
@ -116,6 +117,8 @@ const val ADD_ANNIVERSARIES_AUTOMATICALLY = "add_anniversaries_automatically"
const val BIRTHDAY_REMINDERS = "birthday_reminders"
const val ANNIVERSARY_REMINDERS = "anniversary_reminders"
const val LAST_EXPORT_PATH = "last_export_path"
const val EXPORT_EVENTS = "export_events"
const val EXPORT_TASKS = "export_tasks"
const val EXPORT_PAST_EVENTS = "export_past_events"
const val WEEKLY_VIEW_ITEM_HEIGHT_MULTIPLIER = "weekly_view_item_height_multiplier"
const val WEEKLY_VIEW_DAYS = "weekly_view_days"
@ -144,6 +147,8 @@ const val CALENDAR_PRODID = "PRODID:-//Simple Mobile Tools//NONSGML Event Calend
const val CALENDAR_VERSION = "VERSION:2.0"
const val BEGIN_EVENT = "BEGIN:VEVENT"
const val END_EVENT = "END:VEVENT"
const val BEGIN_TASK = "BEGIN:VTODO"
const val END_TASK = "END:VTODO"
const val BEGIN_ALARM = "BEGIN:VALARM"
const val END_ALARM = "END:VALARM"
const val DTSTART = "DTSTART"
@ -182,6 +187,7 @@ const val UNTIL = "UNTIL"
const val COUNT = "COUNT"
const val INTERVAL = "INTERVAL"
const val CONFIRMED = "CONFIRMED"
const val COMPLETED = "COMPLETED"
const val VALUE = "VALUE"
const val DATE = "DATE"
@ -253,3 +259,7 @@ fun getActivityToOpen(isTask: Boolean) = if (isTask) {
} else {
EventActivity::class.java
}
fun generateImportId(): String {
return UUID.randomUUID().toString().replace("-", "") + System.currentTimeMillis().toString()
}

View file

@ -522,13 +522,28 @@ class EventsHelper(val context: Context) {
fun getEventsToExport(eventTypes: ArrayList<Long>): ArrayList<Event> {
val currTS = getNowSeconds()
var events = ArrayList<Event>()
if (config.exportPastEvents) {
events.addAll(eventsDB.getAllEventsWithTypes(eventTypes))
val tasks = ArrayList<Event>()
if (config.exportPastEntries) {
if (config.exportEvents) {
events.addAll(eventsDB.getAllEventsWithTypes(eventTypes))
}
if (config.exportTasks) {
tasks.addAll(eventsDB.getAllTasksWithTypes(eventTypes))
}
} else {
events.addAll(eventsDB.getOneTimeFutureEventsWithTypes(currTS, eventTypes))
events.addAll(eventsDB.getRepeatableFutureEventsWithTypes(currTS, eventTypes))
if (config.exportEvents) {
events.addAll(eventsDB.getAllFutureEventsWithTypes(currTS, eventTypes))
}
if (config.exportTasks) {
tasks.addAll(eventsDB.getAllFutureTasksWithTypes(currTS, eventTypes))
}
}
tasks.forEach {
updateIsTaskCompleted(it)
}
events.addAll(tasks)
events = events.distinctBy { it.id } as ArrayList<Event>
return events
}

View file

@ -17,7 +17,7 @@ import java.io.BufferedWriter
import java.io.OutputStream
import java.io.OutputStreamWriter
class IcsExporter {
class IcsExporter(private val activity: BaseSimpleActivity) {
enum class ExportResult {
EXPORT_FAIL, EXPORT_OK, EXPORT_PARTIAL
}
@ -26,9 +26,10 @@ class IcsExporter {
private var eventsExported = 0
private var eventsFailed = 0
private var calendars = ArrayList<CalDAVCalendar>()
private val reminderLabel = activity.getString(R.string.reminder)
private val exportTime = Formatter.getExportedTime(System.currentTimeMillis())
fun exportEvents(
activity: BaseSimpleActivity,
outputStream: OutputStream?,
events: ArrayList<Event>,
showExportingToast: Boolean,
@ -40,15 +41,11 @@ class IcsExporter {
}
ensureBackgroundThread {
val reminderLabel = activity.getString(R.string.reminder)
val exportTime = Formatter.getExportedTime(System.currentTimeMillis())
calendars = activity.calDAVHelper.getCalDAVCalendars("", false)
if (showExportingToast) {
activity.toast(R.string.exporting)
}
object : BufferedWriter(OutputStreamWriter(outputStream, Charsets.UTF_8)) {
val lineSeparator = "\r\n"
@ -66,34 +63,11 @@ class IcsExporter {
out.writeLn(CALENDAR_PRODID)
out.writeLn(CALENDAR_VERSION)
for (event in events) {
out.writeLn(BEGIN_EVENT)
event.title.replace("\n", "\\n").let { if (it.isNotEmpty()) out.writeLn("$SUMMARY:$it") }
event.importId.let { if (it.isNotEmpty()) out.writeLn("$UID$it") }
event.eventType.let { out.writeLn("$CATEGORY_COLOR${activity.eventTypesDB.getEventTypeWithId(it)?.color}") }
event.eventType.let { out.writeLn("$CATEGORIES${activity.eventTypesDB.getEventTypeWithId(it)?.title}") }
event.lastUpdated.let { out.writeLn("$LAST_MODIFIED:${Formatter.getExportedTime(it)}") }
event.location.let { if (it.isNotEmpty()) out.writeLn("$LOCATION:$it") }
event.availability.let { out.writeLn("$TRANSP${if (it == Events.AVAILABILITY_FREE) TRANSPARENT else OPAQUE}") }
if (event.getIsAllDay()) {
out.writeLn("$DTSTART;$VALUE=$DATE:${Formatter.getDayCodeFromTS(event.startTS)}")
out.writeLn("$DTEND;$VALUE=$DATE:${Formatter.getDayCodeFromTS(event.endTS + TWELVE_HOURS)}")
if (event.isTask()) {
writeTask(out, event)
} else {
event.startTS.let { out.writeLn("$DTSTART:${Formatter.getExportedTime(it * 1000L)}") }
event.endTS.let { out.writeLn("$DTEND:${Formatter.getExportedTime(it * 1000L)}") }
writeEvent(out, event)
}
event.hasMissingYear().let { out.writeLn("$MISSING_YEAR${if (it) 1 else 0}") }
out.writeLn("$DTSTAMP$exportTime")
out.writeLn("$STATUS$CONFIRMED")
Parser().getRepeatCode(event).let { if (it.isNotEmpty()) out.writeLn("$RRULE$it") }
fillDescription(event.description.replace("\n", "\\n"), out)
fillReminders(event, out, reminderLabel)
fillIgnoredOccurrences(event, out)
eventsExported++
out.writeLn(END_EVENT)
}
out.writeLn(END_CALENDAR)
}
@ -153,4 +127,67 @@ class IcsExporter {
index += MAX_LINE_LENGTH
}
}
private fun writeEvent(writer: BufferedWriter, event: Event) {
with(writer) {
writeLn(BEGIN_EVENT)
event.title.replace("\n", "\\n").let { if (it.isNotEmpty()) writeLn("$SUMMARY:$it") }
event.importId.let { if (it.isNotEmpty()) writeLn("$UID$it") }
writeLn("$CATEGORY_COLOR${activity.eventTypesDB.getEventTypeWithId(event.eventType)?.color}")
writeLn("$CATEGORIES${activity.eventTypesDB.getEventTypeWithId(event.eventType)?.title}")
writeLn("$LAST_MODIFIED:${Formatter.getExportedTime(event.lastUpdated)}")
writeLn("$TRANSP${if (event.availability == Events.AVAILABILITY_FREE) TRANSPARENT else OPAQUE}")
if (event.getIsAllDay()) {
writeLn("$DTSTART;$VALUE=$DATE:${Formatter.getDayCodeFromTS(event.startTS)}")
writeLn("$DTEND;$VALUE=$DATE:${Formatter.getDayCodeFromTS(event.endTS + TWELVE_HOURS)}")
} else {
writeLn("$DTSTART:${Formatter.getExportedTime(event.startTS * 1000L)}")
writeLn("$DTEND:${Formatter.getExportedTime(event.endTS * 1000L)}")
}
writeLn("$MISSING_YEAR${if (event.hasMissingYear()) 1 else 0}")
writeLn("$DTSTAMP$exportTime")
writeLn("$STATUS$CONFIRMED")
Parser().getRepeatCode(event).let { if (it.isNotEmpty()) writeLn("$RRULE$it") }
fillDescription(event.description.replace("\n", "\\n"), writer)
fillReminders(event, writer, reminderLabel)
fillIgnoredOccurrences(event, writer)
eventsExported++
writeLn(END_EVENT)
}
}
private fun writeTask(writer: BufferedWriter, task: Event) {
with(writer) {
writeLn(BEGIN_TASK)
task.title.replace("\n", "\\n").let { if (it.isNotEmpty()) writeLn("$SUMMARY:$it") }
task.importId.let { if (it.isNotEmpty()) writeLn("$UID$it") }
writeLn("$CATEGORY_COLOR${activity.eventTypesDB.getEventTypeWithId(task.eventType)?.color}")
writeLn("$CATEGORIES${activity.eventTypesDB.getEventTypeWithId(task.eventType)?.title}")
writeLn("$LAST_MODIFIED:${Formatter.getExportedTime(task.lastUpdated)}")
task.location.let { if (it.isNotEmpty()) writeLn("$LOCATION:$it") }
if (task.getIsAllDay()) {
writeLn("$DTSTART;$VALUE=$DATE:${Formatter.getDayCodeFromTS(task.startTS)}")
} else {
writeLn("$DTSTART:${Formatter.getExportedTime(task.startTS * 1000L)}")
}
writeLn("$DTSTAMP$exportTime")
if (task.isTaskCompleted()) {
writeLn("$STATUS$COMPLETED")
}
Parser().getRepeatCode(task).let { if (it.isNotEmpty()) writeLn("$RRULE$it") }
fillDescription(task.description.replace("\n", "\\n"), writer)
fillReminders(task, writer, reminderLabel)
fillIgnoredOccurrences(task, writer)
eventsExported++
writeLn(END_TASK)
}
}
}

View file

@ -5,6 +5,7 @@ import com.simplemobiletools.calendar.pro.R
import com.simplemobiletools.calendar.pro.activities.SimpleActivity
import com.simplemobiletools.calendar.pro.extensions.eventsDB
import com.simplemobiletools.calendar.pro.extensions.eventsHelper
import com.simplemobiletools.calendar.pro.extensions.updateTaskCompletion
import com.simplemobiletools.calendar.pro.helpers.IcsImporter.ImportResult.IMPORT_FAIL
import com.simplemobiletools.calendar.pro.helpers.IcsImporter.ImportResult.IMPORT_NOTHING_NEW
import com.simplemobiletools.calendar.pro.helpers.IcsImporter.ImportResult.IMPORT_OK
@ -17,6 +18,7 @@ import com.simplemobiletools.commons.extensions.showErrorToast
import com.simplemobiletools.commons.helpers.HOUR_SECONDS
import org.joda.time.DateTimeZone
import java.io.File
import kotlin.math.min
class IcsImporter(val activity: SimpleActivity) {
enum class ImportResult {
@ -45,7 +47,9 @@ class IcsImporter(val activity: SimpleActivity) {
private var isNotificationDescription = false
private var isProperReminderAction = false
private var isSequence = false
private var curType = TYPE_EVENT
private var isParsingEvent = false
private var isParsingTask = false
private var curReminderTriggerMinutes = REMINDER_OFF
private var curReminderTriggerAction = REMINDER_NOTIFICATION
private val eventsHelper = activity.eventsHelper
@ -63,7 +67,7 @@ class IcsImporter(val activity: SimpleActivity) {
): ImportResult {
try {
val eventTypes = eventsHelper.getEventTypesSync()
val existingEvents = activity.eventsDB.getEventsWithImportIds().toMutableList() as ArrayList<Event>
val existingEvents = activity.eventsDB.getEventsOrTasksWithImportIds().toMutableList() as ArrayList<Event>
val eventsToInsert = ArrayList<Event>()
var line = ""
@ -89,8 +93,13 @@ class IcsImporter(val activity: SimpleActivity) {
resetValues()
curEventTypeId = defaultEventTypeId
isParsingEvent = true
} else if (line.trim() == BEGIN_TASK) {
resetValues()
curEventTypeId = defaultEventTypeId
isParsingTask = true
curType = TYPE_TASK
} else if (line.startsWith(DTSTART)) {
if (isParsingEvent) {
if (isParsingEvent || isParsingTask) {
curStart = getTimestamp(line.substring(DTSTART.length))
if (curRrule != "") {
@ -144,6 +153,14 @@ class IcsImporter(val activity: SimpleActivity) {
if (line.substring(MISSING_YEAR.length) == "1") {
curFlags = curFlags or FLAG_MISSING_YEAR
}
} else if (line.startsWith(STATUS)) {
if (isParsingTask && line.substring(STATUS.length) == COMPLETED) {
curFlags = curFlags or FLAG_TASK_COMPLETED
}
} else if (line.startsWith(COMPLETED)) {
if (isParsingTask && line.substring(COMPLETED.length).trim().isNotEmpty()) {
curFlags = curFlags or FLAG_TASK_COMPLETED
}
} else if (line.startsWith(CATEGORIES) && !overrideFileEventTypes) {
val categories = line.substring(CATEGORIES.length)
tryAddCategories(categories)
@ -182,11 +199,12 @@ class IcsImporter(val activity: SimpleActivity) {
curReminderActions.add(curReminderTriggerAction)
}
isNotificationDescription = false
} else if (line.trim() == END_EVENT) {
isParsingEvent = false
if (curStart != -1L && curEnd == -1L) {
} else if (line.trim() == END_EVENT || line.trim() == END_TASK) {
if (curStart != -1L && (curEnd == -1L || isParsingTask)) {
curEnd = curStart
}
isParsingEvent = false
isParsingTask = false
if (curTitle.isEmpty() || curStart == -1L) {
line = curLine
@ -194,8 +212,7 @@ class IcsImporter(val activity: SimpleActivity) {
}
// repeating event exceptions can have the same import id as their parents, so pick the latest event to update
val eventToUpdate =
existingEvents.filter { curImportId.isNotEmpty() && curImportId == it.importId }.sortedByDescending { it.lastUpdated }.firstOrNull()
val eventToUpdate = existingEvents.filter { curImportId.isNotEmpty() && curImportId == it.importId }.maxByOrNull { it.lastUpdated }
if (eventToUpdate != null && eventToUpdate.lastUpdated >= curLastModified) {
eventsAlreadyExist++
line = curLine
@ -238,10 +255,11 @@ class IcsImporter(val activity: SimpleActivity) {
0,
curLastModified,
source,
curAvailability
curAvailability,
type = curType
)
if (isAllDay && curEnd > curStart) {
if (isAllDay && curEnd > curStart && !event.isTask()) {
event.endTS -= TWELVE_HOURS
// fix some glitches related to daylight saving shifts
if (event.startTS - event.endTS == HOUR_SECONDS.toLong()) {
@ -264,13 +282,13 @@ class IcsImporter(val activity: SimpleActivity) {
// if an event belongs to a sequence insert it immediately, to avoid some glitches with linked events
if (isSequence) {
if (curRecurrenceDayCode.isEmpty()) {
eventsHelper.insertEvent(event, true, false)
eventsHelper.insertEvent(event, addToCalDAV = !event.isTask(), showToasts = false)
} else {
// if an event contains the RECURRENCE-ID field, it is an exception to a recurring event, so update its parent too
val parentEvent = activity.eventsDB.getEventWithImportId(event.importId)
if (parentEvent != null && !parentEvent.repetitionExceptions.contains(curRecurrenceDayCode)) {
parentEvent.addRepetitionException(curRecurrenceDayCode)
eventsHelper.insertEvent(parentEvent, true, false)
eventsHelper.insertEvent(parentEvent, !parentEvent.isTask(), showToasts = false)
event.parentId = parentEvent.id!!
eventsToInsert.add(event)
@ -281,7 +299,7 @@ class IcsImporter(val activity: SimpleActivity) {
}
} else {
event.id = eventToUpdate.id
eventsHelper.updateEvent(event, true, false)
eventsHelper.updateEvent(event, updateAtCalDAV = !event.isTask(), showToasts = false)
}
eventsImported++
resetValues()
@ -290,7 +308,12 @@ class IcsImporter(val activity: SimpleActivity) {
}
}
eventsHelper.insertEvents(eventsToInsert, true)
val (tasks, events) = eventsToInsert.partition { it.isTask() }
eventsHelper.insertEvents(tasks as ArrayList<Event>, addToCalDAV = false)
eventsHelper.insertEvents(events as ArrayList<Event>, addToCalDAV = true)
tasks.filter { it.isTaskCompleted() }.forEach {
activity.updateTaskCompletion(it, completed = true)
}
} catch (e: Exception) {
activity.showErrorToast(e)
eventsFailed++
@ -361,7 +384,7 @@ class IcsImporter(val activity: SimpleActivity) {
return if (title.startsWith(";") && title.contains(":")) {
title.substring(title.lastIndexOf(':') + 1)
} else {
title.substring(1, Math.min(title.length, 180))
title.substring(1, min(title.length, 180))
}
}
@ -397,5 +420,6 @@ class IcsImporter(val activity: SimpleActivity) {
isParsingEvent = false
curReminderTriggerMinutes = REMINDER_OFF
curReminderTriggerAction = REMINDER_NOTIFICATION
curType = TYPE_EVENT
}
}

View file

@ -12,9 +12,21 @@ interface EventsDao {
@Query("SELECT * FROM events")
fun getAllEvents(): List<Event>
@Query("SELECT * FROM events WHERE type = $TYPE_TASK")
fun getAllTasks(): List<Event>
@Query("SELECT * FROM events WHERE event_type IN (:eventTypeIds) AND type = $TYPE_EVENT")
fun getAllEventsWithTypes(eventTypeIds: List<Long>): List<Event>
@Query("SELECT * FROM events WHERE event_type IN (:eventTypeIds) AND type = $TYPE_TASK")
fun getAllTasksWithTypes(eventTypeIds: List<Long>): List<Event>
@Query("SELECT * FROM events WHERE end_ts > :currTS AND event_type IN (:eventTypeIds) AND type = $TYPE_EVENT")
fun getAllFutureEventsWithTypes(currTS: Long, eventTypeIds: List<Long>): List<Event>
@Query("SELECT * FROM events WHERE end_ts > :currTS AND event_type IN (:eventTypeIds) AND type = $TYPE_TASK")
fun getAllFutureTasksWithTypes(currTS: Long, eventTypeIds: List<Long>): List<Event>
@Query("SELECT * FROM events WHERE id = :id AND type = $TYPE_EVENT")
fun getEventWithId(id: Long): Event?
@ -42,9 +54,6 @@ interface EventsDao {
@Query("SELECT * FROM events WHERE start_ts <= :toTS AND end_ts >= :fromTS AND start_ts != 0 AND repeat_interval = 0 AND event_type IN (:eventTypeIds) AND (title LIKE :searchQuery OR location LIKE :searchQuery OR description LIKE :searchQuery)")
fun getOneTimeEventsFromToWithTypesForSearch(toTS: Long, fromTS: Long, eventTypeIds: List<Long>, searchQuery: String): List<Event>
@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")
fun getRepeatableEventsOrTasksWithTypes(toTS: Long): List<Event>
@ -57,9 +66,6 @@ interface EventsDao {
@Query("SELECT * FROM events WHERE start_ts <= :toTS AND start_ts != 0 AND repeat_interval != 0 AND event_type IN (:eventTypeIds) AND (title LIKE :searchQuery OR location LIKE :searchQuery OR description LIKE :searchQuery)")
fun getRepeatableEventsOrTasksWithTypesForSearch(toTS: Long, eventTypeIds: List<Long>, searchQuery: String): 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>
@Query("SELECT * FROM events WHERE id IN (:ids) AND import_id != \"\" AND type = $TYPE_EVENT")
fun getEventsByIdsWithImportIds(ids: List<Long>): List<Event>
@ -69,8 +75,8 @@ interface EventsDao {
@Query("SELECT * FROM events WHERE source = \'$SOURCE_CONTACT_ANNIVERSARY\' AND type = $TYPE_EVENT")
fun getAnniversaries(): List<Event>
@Query("SELECT * FROM events WHERE import_id != \"\" AND type = $TYPE_EVENT")
fun getEventsWithImportIds(): List<Event>
@Query("SELECT * FROM events WHERE import_id != \"\"")
fun getEventsOrTasksWithImportIds(): List<Event>
@Query("SELECT * FROM events WHERE source = :source AND type = $TYPE_EVENT")
fun getEventsFromCalDAVCalendar(source: String): List<Event>
@ -119,6 +125,9 @@ interface EventsDao {
@Query("UPDATE events SET flags = :newFlags WHERE id = :id")
fun updateTaskCompletion(id: Long, newFlags: Int)
@Query("UPDATE events SET import_id = :importId WHERE id = :id AND type = $TYPE_TASK")
fun updateTaskImportId(importId: String, id: Long)
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun insertOrUpdate(event: Event): Long

View file

@ -48,15 +48,67 @@
</com.simplemobiletools.commons.views.MyTextInputLayout>
<RelativeLayout
android:id="@+id/export_events_checkbox_holder"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/medium_margin"
android:background="?attr/selectableItemBackground"
android:paddingVertical="@dimen/smaller_margin"
android:paddingStart="@dimen/normal_margin"
android:paddingEnd="@dimen/activity_margin">
<com.simplemobiletools.commons.views.MyAppCompatCheckbox
android:id="@+id/export_events_checkbox"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@null"
android:checked="true"
android:clickable="false"
android:layoutDirection="rtl"
android:paddingHorizontal="@dimen/medium_margin"
android:text="@string/export_events" />
</RelativeLayout>
<RelativeLayout
android:id="@+id/export_tasks_checkbox_holder"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/selectableItemBackground"
android:paddingVertical="@dimen/smaller_margin"
android:paddingStart="@dimen/normal_margin"
android:paddingEnd="@dimen/activity_margin">
<com.simplemobiletools.commons.views.MyAppCompatCheckbox
android:id="@+id/export_tasks_checkbox"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@null"
android:checked="true"
android:clickable="false"
android:layoutDirection="rtl"
android:paddingHorizontal="@dimen/medium_margin"
android:text="@string/export_tasks" />
</RelativeLayout>
<ImageView
android:id="@+id/export_past_entries_divider"
android:layout_width="match_parent"
android:layout_height="@dimen/divider_height"
android:layout_marginStart="@dimen/activity_margin"
android:background="@color/divider_grey"
android:importantForAccessibility="no" />
<RelativeLayout
android:id="@+id/export_past_events_checkbox_holder"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/selectableItemBackground"
android:paddingVertical="@dimen/smaller_margin"
android:paddingStart="@dimen/normal_margin"
android:paddingTop="@dimen/medium_margin"
android:paddingEnd="@dimen/activity_margin"
android:paddingBottom="@dimen/medium_margin">
android:paddingEnd="@dimen/activity_margin">
<com.simplemobiletools.commons.views.MyAppCompatCheckbox
android:id="@+id/export_past_events_checkbox"
@ -65,8 +117,8 @@
android:background="@null"
android:clickable="false"
android:layoutDirection="rtl"
android:padding="@dimen/medium_margin"
android:text="@string/export_past_events_too" />
android:paddingHorizontal="@dimen/medium_margin"
android:text="@string/export_past_entries" />
</RelativeLayout>