mirror of
https://github.com/SimpleMobileTools/Simple-Calendar.git
synced 2025-06-05 21:59:17 +02:00
Merge pull request #2027 from Naveen3Singh/feature_automatic_backups
Implement automatic backups
This commit is contained in:
@@ -283,9 +283,14 @@
|
|||||||
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
||||||
<action android:name="android.intent.action.QUICKBOOT_POWERON" />
|
<action android:name="android.intent.action.QUICKBOOT_POWERON" />
|
||||||
<action android:name="com.htc.intent.action.QUICKBOOT_POWERON" />
|
<action android:name="com.htc.intent.action.QUICKBOOT_POWERON" />
|
||||||
|
<action android:name="android.intent.action.MY_PACKAGE_REPLACED" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</receiver>
|
</receiver>
|
||||||
|
|
||||||
|
<receiver
|
||||||
|
android:name=".receivers.AutomaticBackupReceiver"
|
||||||
|
android:exported="false" />
|
||||||
|
|
||||||
<provider
|
<provider
|
||||||
android:name="androidx.core.content.FileProvider"
|
android:name="androidx.core.content.FileProvider"
|
||||||
android:authorities="${applicationId}.provider"
|
android:authorities="${applicationId}.provider"
|
||||||
|
@@ -22,8 +22,8 @@ import com.simplemobiletools.calendar.pro.adapters.EventListAdapter
|
|||||||
import com.simplemobiletools.calendar.pro.adapters.QuickFilterEventTypeAdapter
|
import com.simplemobiletools.calendar.pro.adapters.QuickFilterEventTypeAdapter
|
||||||
import com.simplemobiletools.calendar.pro.databases.EventsDatabase
|
import com.simplemobiletools.calendar.pro.databases.EventsDatabase
|
||||||
import com.simplemobiletools.calendar.pro.dialogs.ExportEventsDialog
|
import com.simplemobiletools.calendar.pro.dialogs.ExportEventsDialog
|
||||||
import com.simplemobiletools.calendar.pro.dialogs.FilterEventTypesDialog
|
|
||||||
import com.simplemobiletools.calendar.pro.dialogs.ImportEventsDialog
|
import com.simplemobiletools.calendar.pro.dialogs.ImportEventsDialog
|
||||||
|
import com.simplemobiletools.calendar.pro.dialogs.SelectEventTypesDialog
|
||||||
import com.simplemobiletools.calendar.pro.dialogs.SetRemindersDialog
|
import com.simplemobiletools.calendar.pro.dialogs.SetRemindersDialog
|
||||||
import com.simplemobiletools.calendar.pro.extensions.*
|
import com.simplemobiletools.calendar.pro.extensions.*
|
||||||
import com.simplemobiletools.calendar.pro.fragments.*
|
import com.simplemobiletools.calendar.pro.fragments.*
|
||||||
@@ -526,12 +526,16 @@ class MainActivity : SimpleActivity(), RefreshRecyclerViewListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun showFilterDialog() {
|
private fun showFilterDialog() {
|
||||||
FilterEventTypesDialog(this) {
|
SelectEventTypesDialog(this, config.displayEventTypes) {
|
||||||
|
if (config.displayEventTypes != it) {
|
||||||
|
config.displayEventTypes = it
|
||||||
|
|
||||||
refreshViewPager()
|
refreshViewPager()
|
||||||
setupQuickFilter()
|
setupQuickFilter()
|
||||||
updateWidgets()
|
updateWidgets()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun toggleGoToTodayVisibility(beVisible: Boolean) {
|
fun toggleGoToTodayVisibility(beVisible: Boolean) {
|
||||||
shouldGoToTodayBeVisible = beVisible
|
shouldGoToTodayBeVisible = beVisible
|
||||||
@@ -1149,7 +1153,7 @@ class MainActivity : SimpleActivity(), RefreshRecyclerViewListener {
|
|||||||
|
|
||||||
private fun exportEventsTo(eventTypes: ArrayList<Long>, outputStream: OutputStream?) {
|
private fun exportEventsTo(eventTypes: ArrayList<Long>, outputStream: OutputStream?) {
|
||||||
ensureBackgroundThread {
|
ensureBackgroundThread {
|
||||||
val events = eventsHelper.getEventsToExport(eventTypes)
|
val events = eventsHelper.getEventsToExport(eventTypes, config.exportEvents, config.exportTasks, config.exportPastEntries)
|
||||||
if (events.isEmpty()) {
|
if (events.isEmpty()) {
|
||||||
toast(R.string.no_entries_for_exporting)
|
toast(R.string.no_entries_for_exporting)
|
||||||
} else {
|
} else {
|
||||||
|
@@ -11,9 +11,10 @@ import android.widget.Toast
|
|||||||
import com.google.android.material.timepicker.MaterialTimePicker
|
import com.google.android.material.timepicker.MaterialTimePicker
|
||||||
import com.google.android.material.timepicker.TimeFormat
|
import com.google.android.material.timepicker.TimeFormat
|
||||||
import com.simplemobiletools.calendar.pro.R
|
import com.simplemobiletools.calendar.pro.R
|
||||||
|
import com.simplemobiletools.calendar.pro.dialogs.ManageAutomaticBackupsDialog
|
||||||
import com.simplemobiletools.calendar.pro.dialogs.SelectCalendarsDialog
|
import com.simplemobiletools.calendar.pro.dialogs.SelectCalendarsDialog
|
||||||
import com.simplemobiletools.calendar.pro.dialogs.SelectEventTypeDialog
|
import com.simplemobiletools.calendar.pro.dialogs.SelectEventTypeDialog
|
||||||
import com.simplemobiletools.calendar.pro.dialogs.SelectQuickFilterEventTypesDialog
|
import com.simplemobiletools.calendar.pro.dialogs.SelectEventTypesDialog
|
||||||
import com.simplemobiletools.calendar.pro.extensions.*
|
import com.simplemobiletools.calendar.pro.extensions.*
|
||||||
import com.simplemobiletools.calendar.pro.helpers.*
|
import com.simplemobiletools.calendar.pro.helpers.*
|
||||||
import com.simplemobiletools.calendar.pro.helpers.Formatter
|
import com.simplemobiletools.calendar.pro.helpers.Formatter
|
||||||
@@ -99,6 +100,8 @@ class SettingsActivity : SimpleActivity() {
|
|||||||
setupAllowChangingTimeZones()
|
setupAllowChangingTimeZones()
|
||||||
updateTextColors(settings_holder)
|
updateTextColors(settings_holder)
|
||||||
checkPrimaryColor()
|
checkPrimaryColor()
|
||||||
|
setupEnableAutomaticBackups()
|
||||||
|
setupManageAutomaticBackups()
|
||||||
setupExportSettings()
|
setupExportSettings()
|
||||||
setupImportSettings()
|
setupImportSettings()
|
||||||
|
|
||||||
@@ -114,6 +117,7 @@ class SettingsActivity : SimpleActivity() {
|
|||||||
settings_widgets_label,
|
settings_widgets_label,
|
||||||
settings_events_label,
|
settings_events_label,
|
||||||
settings_tasks_label,
|
settings_tasks_label,
|
||||||
|
settings_backups_label,
|
||||||
settings_migrating_label
|
settings_migrating_label
|
||||||
).forEach {
|
).forEach {
|
||||||
it.setTextColor(getProperPrimaryColor())
|
it.setTextColor(getProperPrimaryColor())
|
||||||
@@ -334,7 +338,9 @@ class SettingsActivity : SimpleActivity() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun showQuickFilterPicker() {
|
private fun showQuickFilterPicker() {
|
||||||
SelectQuickFilterEventTypesDialog(this)
|
SelectEventTypesDialog(this, config.quickFilterEventTypes) {
|
||||||
|
config.quickFilterEventTypes = it
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setupSundayFirst() {
|
private fun setupSundayFirst() {
|
||||||
@@ -830,6 +836,46 @@ class SettingsActivity : SimpleActivity() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun setupEnableAutomaticBackups() {
|
||||||
|
settings_backups_label.beVisibleIf(isRPlus())
|
||||||
|
settings_backups_divider.beVisibleIf(isRPlus())
|
||||||
|
settings_enable_automatic_backups_holder.beVisibleIf(isRPlus())
|
||||||
|
settings_enable_automatic_backups.isChecked = config.autoBackup
|
||||||
|
settings_enable_automatic_backups_holder.setOnClickListener {
|
||||||
|
val wasBackupDisabled = !config.autoBackup
|
||||||
|
if (wasBackupDisabled) {
|
||||||
|
ManageAutomaticBackupsDialog(
|
||||||
|
activity = this,
|
||||||
|
onSuccess = {
|
||||||
|
enableOrDisableAutomaticBackups(true)
|
||||||
|
scheduleNextAutomaticBackup()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
cancelScheduledAutomaticBackup()
|
||||||
|
enableOrDisableAutomaticBackups(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setupManageAutomaticBackups() {
|
||||||
|
settings_manage_automatic_backups_holder.beVisibleIf(isRPlus() && config.autoBackup)
|
||||||
|
settings_manage_automatic_backups_holder.setOnClickListener {
|
||||||
|
ManageAutomaticBackupsDialog(
|
||||||
|
activity = this,
|
||||||
|
onSuccess = {
|
||||||
|
scheduleNextAutomaticBackup()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun enableOrDisableAutomaticBackups(enable: Boolean) {
|
||||||
|
config.autoBackup = enable
|
||||||
|
settings_enable_automatic_backups.isChecked = enable
|
||||||
|
settings_manage_automatic_backups_holder.beVisibleIf(enable)
|
||||||
|
}
|
||||||
|
|
||||||
private fun setupExportSettings() {
|
private fun setupExportSettings() {
|
||||||
settings_export_holder.setOnClickListener {
|
settings_export_holder.setOnClickListener {
|
||||||
val configItems = LinkedHashMap<String, Any>().apply {
|
val configItems = LinkedHashMap<String, Any>().apply {
|
||||||
|
@@ -0,0 +1,18 @@
|
|||||||
|
package com.simplemobiletools.calendar.pro.dialogs
|
||||||
|
|
||||||
|
import com.simplemobiletools.calendar.pro.R
|
||||||
|
import com.simplemobiletools.commons.activities.BaseSimpleActivity
|
||||||
|
import com.simplemobiletools.commons.extensions.getAlertDialogBuilder
|
||||||
|
import com.simplemobiletools.commons.extensions.setupDialogStuff
|
||||||
|
|
||||||
|
class DateTimePatternInfoDialog(activity: BaseSimpleActivity) {
|
||||||
|
|
||||||
|
init {
|
||||||
|
val view = activity.layoutInflater.inflate(R.layout.datetime_pattern_info_layout, null)
|
||||||
|
activity.getAlertDialogBuilder()
|
||||||
|
.setPositiveButton(R.string.ok) { _, _ -> { } }
|
||||||
|
.apply {
|
||||||
|
activity.setupDialogStuff(view, this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,131 @@
|
|||||||
|
package com.simplemobiletools.calendar.pro.dialogs
|
||||||
|
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import androidx.appcompat.app.AlertDialog
|
||||||
|
import com.simplemobiletools.calendar.pro.R
|
||||||
|
import com.simplemobiletools.calendar.pro.activities.SimpleActivity
|
||||||
|
import com.simplemobiletools.calendar.pro.extensions.config
|
||||||
|
import com.simplemobiletools.commons.dialogs.FilePickerDialog
|
||||||
|
import com.simplemobiletools.commons.extensions.*
|
||||||
|
import com.simplemobiletools.commons.helpers.ensureBackgroundThread
|
||||||
|
import kotlinx.android.synthetic.main.dialog_manage_automatic_backups.view.*
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
class ManageAutomaticBackupsDialog(private val activity: SimpleActivity, onSuccess: () -> Unit) {
|
||||||
|
private val view = (activity.layoutInflater.inflate(R.layout.dialog_manage_automatic_backups, null) as ViewGroup)
|
||||||
|
private val config = activity.config
|
||||||
|
private var backupFolder = config.autoBackupFolder
|
||||||
|
private var selectedEventTypes = config.autoBackupEventTypes.ifEmpty { config.displayEventTypes }
|
||||||
|
|
||||||
|
init {
|
||||||
|
view.apply {
|
||||||
|
backup_events_folder.setText(activity.humanizePath(backupFolder))
|
||||||
|
val filename = config.autoBackupFilename.ifEmpty {
|
||||||
|
"${activity.getString(R.string.events)}_%Y%M%D_%h%m%s"
|
||||||
|
}
|
||||||
|
|
||||||
|
backup_events_filename.setText(filename)
|
||||||
|
backup_events_filename_hint.setEndIconOnClickListener {
|
||||||
|
DateTimePatternInfoDialog(activity)
|
||||||
|
}
|
||||||
|
|
||||||
|
backup_events_filename_hint.setEndIconOnLongClickListener {
|
||||||
|
DateTimePatternInfoDialog(activity)
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
backup_events_checkbox.isChecked = config.autoBackupEvents
|
||||||
|
backup_events_checkbox_holder.setOnClickListener {
|
||||||
|
backup_events_checkbox.toggle()
|
||||||
|
}
|
||||||
|
|
||||||
|
backup_tasks_checkbox.isChecked = config.autoBackupTasks
|
||||||
|
backup_tasks_checkbox_holder.setOnClickListener {
|
||||||
|
backup_tasks_checkbox.toggle()
|
||||||
|
}
|
||||||
|
|
||||||
|
backup_past_events_checkbox.isChecked = config.autoBackupPastEntries
|
||||||
|
backup_past_events_checkbox_holder.setOnClickListener {
|
||||||
|
backup_past_events_checkbox.toggle()
|
||||||
|
}
|
||||||
|
|
||||||
|
backup_events_folder.setOnClickListener {
|
||||||
|
selectBackupFolder()
|
||||||
|
}
|
||||||
|
|
||||||
|
manage_event_types_holder.setOnClickListener {
|
||||||
|
SelectEventTypesDialog(activity, selectedEventTypes) {
|
||||||
|
selectedEventTypes = it
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
activity.getAlertDialogBuilder()
|
||||||
|
.setPositiveButton(R.string.ok, null)
|
||||||
|
.setNegativeButton(R.string.cancel, null)
|
||||||
|
.apply {
|
||||||
|
activity.setupDialogStuff(view, this, R.string.manage_automatic_backups) { dialog ->
|
||||||
|
dialog.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener {
|
||||||
|
val filename = view.backup_events_filename.value
|
||||||
|
when {
|
||||||
|
filename.isEmpty() -> activity.toast(R.string.empty_name)
|
||||||
|
filename.isAValidFilename() -> {
|
||||||
|
val file = File(backupFolder, "$filename.ics")
|
||||||
|
if (file.exists() && !file.canWrite()) {
|
||||||
|
activity.toast(R.string.name_taken)
|
||||||
|
return@setOnClickListener
|
||||||
|
}
|
||||||
|
|
||||||
|
val backupEventsChecked = view.backup_events_checkbox.isChecked
|
||||||
|
val backupTasksChecked = view.backup_tasks_checkbox.isChecked
|
||||||
|
if (!backupEventsChecked && !backupTasksChecked || selectedEventTypes.isEmpty()) {
|
||||||
|
activity.toast(R.string.no_entries_for_exporting)
|
||||||
|
return@setOnClickListener
|
||||||
|
}
|
||||||
|
|
||||||
|
ensureBackgroundThread {
|
||||||
|
config.apply {
|
||||||
|
autoBackupFolder = backupFolder
|
||||||
|
autoBackupFilename = filename
|
||||||
|
autoBackupEvents = backupEventsChecked
|
||||||
|
autoBackupTasks = backupTasksChecked
|
||||||
|
autoBackupPastEntries = view.backup_past_events_checkbox.isChecked
|
||||||
|
if (autoBackupEventTypes != selectedEventTypes) {
|
||||||
|
autoBackupEventTypes = selectedEventTypes
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
activity.runOnUiThread {
|
||||||
|
onSuccess()
|
||||||
|
}
|
||||||
|
|
||||||
|
dialog.dismiss()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> activity.toast(R.string.invalid_name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun selectBackupFolder() {
|
||||||
|
activity.hideKeyboard(view.backup_events_filename)
|
||||||
|
FilePickerDialog(activity, backupFolder, false, showFAB = true) { path ->
|
||||||
|
activity.handleSAFDialog(path) { grantedSAF ->
|
||||||
|
if (!grantedSAF) {
|
||||||
|
return@handleSAFDialog
|
||||||
|
}
|
||||||
|
|
||||||
|
activity.handleSAFDialogSdk30(path) { grantedSAF30 ->
|
||||||
|
if (!grantedSAF30) {
|
||||||
|
return@handleSAFDialogSdk30
|
||||||
|
}
|
||||||
|
|
||||||
|
backupFolder = path
|
||||||
|
view.backup_events_folder.setText(activity.humanizePath(path))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@@ -4,23 +4,21 @@ import androidx.appcompat.app.AlertDialog
|
|||||||
import com.simplemobiletools.calendar.pro.R
|
import com.simplemobiletools.calendar.pro.R
|
||||||
import com.simplemobiletools.calendar.pro.activities.SimpleActivity
|
import com.simplemobiletools.calendar.pro.activities.SimpleActivity
|
||||||
import com.simplemobiletools.calendar.pro.adapters.FilterEventTypeAdapter
|
import com.simplemobiletools.calendar.pro.adapters.FilterEventTypeAdapter
|
||||||
import com.simplemobiletools.calendar.pro.extensions.config
|
|
||||||
import com.simplemobiletools.calendar.pro.extensions.eventsHelper
|
import com.simplemobiletools.calendar.pro.extensions.eventsHelper
|
||||||
import com.simplemobiletools.commons.extensions.getAlertDialogBuilder
|
import com.simplemobiletools.commons.extensions.getAlertDialogBuilder
|
||||||
import com.simplemobiletools.commons.extensions.setupDialogStuff
|
import com.simplemobiletools.commons.extensions.setupDialogStuff
|
||||||
import kotlinx.android.synthetic.main.dialog_filter_event_types.view.*
|
import kotlinx.android.synthetic.main.dialog_filter_event_types.view.*
|
||||||
|
|
||||||
class FilterEventTypesDialog(val activity: SimpleActivity, val callback: () -> Unit) {
|
class SelectEventTypesDialog(val activity: SimpleActivity, selectedEventTypes: Set<String>, val callback: (HashSet<String>) -> Unit) {
|
||||||
private var dialog: AlertDialog? = null
|
private var dialog: AlertDialog? = null
|
||||||
private val view = activity.layoutInflater.inflate(R.layout.dialog_filter_event_types, null)
|
private val view = activity.layoutInflater.inflate(R.layout.dialog_filter_event_types, null)
|
||||||
|
|
||||||
init {
|
init {
|
||||||
activity.eventsHelper.getEventTypes(activity, false) {
|
activity.eventsHelper.getEventTypes(activity, false) {
|
||||||
val displayEventTypes = activity.config.displayEventTypes
|
view.filter_event_types_list.adapter = FilterEventTypeAdapter(activity, it, selectedEventTypes)
|
||||||
view.filter_event_types_list.adapter = FilterEventTypeAdapter(activity, it, displayEventTypes)
|
|
||||||
|
|
||||||
activity.getAlertDialogBuilder()
|
activity.getAlertDialogBuilder()
|
||||||
.setPositiveButton(R.string.ok) { dialogInterface, i -> confirmEventTypes() }
|
.setPositiveButton(R.string.ok) { _, _ -> confirmEventTypes() }
|
||||||
.setNegativeButton(R.string.cancel, null)
|
.setNegativeButton(R.string.cancel, null)
|
||||||
.apply {
|
.apply {
|
||||||
activity.setupDialogStuff(view, this) { alertDialog ->
|
activity.setupDialogStuff(view, this) { alertDialog ->
|
||||||
@@ -31,11 +29,11 @@ class FilterEventTypesDialog(val activity: SimpleActivity, val callback: () -> U
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun confirmEventTypes() {
|
private fun confirmEventTypes() {
|
||||||
val selectedItems = (view.filter_event_types_list.adapter as FilterEventTypeAdapter).getSelectedItemsList().map { it.toString() }.toHashSet()
|
val adapter = view.filter_event_types_list.adapter as FilterEventTypeAdapter
|
||||||
if (activity.config.displayEventTypes != selectedItems) {
|
val selectedItems = adapter.getSelectedItemsList()
|
||||||
activity.config.displayEventTypes = selectedItems
|
.map { it.toString() }
|
||||||
callback()
|
.toHashSet()
|
||||||
}
|
callback(selectedItems)
|
||||||
dialog?.dismiss()
|
dialog?.dismiss()
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -1,43 +0,0 @@
|
|||||||
package com.simplemobiletools.calendar.pro.dialogs
|
|
||||||
|
|
||||||
import androidx.appcompat.app.AlertDialog
|
|
||||||
import com.simplemobiletools.calendar.pro.R
|
|
||||||
import com.simplemobiletools.calendar.pro.activities.SimpleActivity
|
|
||||||
import com.simplemobiletools.calendar.pro.adapters.FilterEventTypeAdapter
|
|
||||||
import com.simplemobiletools.calendar.pro.extensions.config
|
|
||||||
import com.simplemobiletools.calendar.pro.extensions.eventsHelper
|
|
||||||
import com.simplemobiletools.commons.extensions.getAlertDialogBuilder
|
|
||||||
import com.simplemobiletools.commons.extensions.setupDialogStuff
|
|
||||||
import kotlinx.android.synthetic.main.dialog_filter_event_types.view.*
|
|
||||||
|
|
||||||
class SelectQuickFilterEventTypesDialog(val activity: SimpleActivity) {
|
|
||||||
private var dialog: AlertDialog? = null
|
|
||||||
private val view = activity.layoutInflater.inflate(R.layout.dialog_filter_event_types, null)
|
|
||||||
|
|
||||||
init {
|
|
||||||
activity.eventsHelper.getEventTypes(activity, false) {
|
|
||||||
val quickFilterEventTypes = activity.config.quickFilterEventTypes
|
|
||||||
view.filter_event_types_list.adapter = FilterEventTypeAdapter(activity, it, quickFilterEventTypes)
|
|
||||||
|
|
||||||
activity.getAlertDialogBuilder()
|
|
||||||
.setPositiveButton(R.string.ok) { dialogInterface, i -> confirmEventTypes() }
|
|
||||||
.setNegativeButton(R.string.cancel, null)
|
|
||||||
.apply {
|
|
||||||
activity.setupDialogStuff(view, this) { alertDialog ->
|
|
||||||
dialog = alertDialog
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun confirmEventTypes() {
|
|
||||||
val selectedItems = (view.filter_event_types_list.adapter as FilterEventTypeAdapter).getSelectedItemsList().map {
|
|
||||||
it.toString()
|
|
||||||
}.toHashSet()
|
|
||||||
|
|
||||||
if (activity.config.quickFilterEventTypes != selectedItems) {
|
|
||||||
activity.config.quickFilterEventTypes = selectedItems
|
|
||||||
}
|
|
||||||
dialog?.dismiss()
|
|
||||||
}
|
|
||||||
}
|
|
@@ -13,6 +13,7 @@ import android.content.res.Resources
|
|||||||
import android.database.Cursor
|
import android.database.Cursor
|
||||||
import android.graphics.Bitmap
|
import android.graphics.Bitmap
|
||||||
import android.media.AudioAttributes
|
import android.media.AudioAttributes
|
||||||
|
import android.media.MediaScannerConnection
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.provider.CalendarContract
|
import android.provider.CalendarContract
|
||||||
@@ -37,6 +38,7 @@ import com.simplemobiletools.calendar.pro.interfaces.EventsDao
|
|||||||
import com.simplemobiletools.calendar.pro.interfaces.TasksDao
|
import com.simplemobiletools.calendar.pro.interfaces.TasksDao
|
||||||
import com.simplemobiletools.calendar.pro.interfaces.WidgetsDao
|
import com.simplemobiletools.calendar.pro.interfaces.WidgetsDao
|
||||||
import com.simplemobiletools.calendar.pro.models.*
|
import com.simplemobiletools.calendar.pro.models.*
|
||||||
|
import com.simplemobiletools.calendar.pro.receivers.AutomaticBackupReceiver
|
||||||
import com.simplemobiletools.calendar.pro.receivers.CalDAVSyncReceiver
|
import com.simplemobiletools.calendar.pro.receivers.CalDAVSyncReceiver
|
||||||
import com.simplemobiletools.calendar.pro.receivers.NotificationReceiver
|
import com.simplemobiletools.calendar.pro.receivers.NotificationReceiver
|
||||||
import com.simplemobiletools.calendar.pro.services.MarkCompletedService
|
import com.simplemobiletools.calendar.pro.services.MarkCompletedService
|
||||||
@@ -47,6 +49,7 @@ import kotlinx.android.synthetic.main.day_monthly_event_view.view.*
|
|||||||
import org.joda.time.DateTime
|
import org.joda.time.DateTime
|
||||||
import org.joda.time.DateTimeZone
|
import org.joda.time.DateTimeZone
|
||||||
import org.joda.time.LocalDate
|
import org.joda.time.LocalDate
|
||||||
|
import java.io.File
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
val Context.config: Config get() = Config.newInstance(applicationContext)
|
val Context.config: Config get() = Config.newInstance(applicationContext)
|
||||||
@@ -183,6 +186,103 @@ fun Context.cancelPendingIntent(id: Long) {
|
|||||||
PendingIntent.getBroadcast(this, id.toInt(), intent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE).cancel()
|
PendingIntent.getBroadcast(this, id.toInt(), intent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE).cancel()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun Context.getAutomaticBackupIntent(): PendingIntent {
|
||||||
|
val intent = Intent(this, AutomaticBackupReceiver::class.java)
|
||||||
|
return PendingIntent.getBroadcast(this, AUTOMATIC_BACKUP_REQUEST_CODE, intent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Context.scheduleNextAutomaticBackup() {
|
||||||
|
if (config.autoBackup) {
|
||||||
|
val backupAtMillis = getNextAutoBackupTime().millis
|
||||||
|
val pendingIntent = getAutomaticBackupIntent()
|
||||||
|
val alarmManager = getSystemService(Context.ALARM_SERVICE) as AlarmManager
|
||||||
|
try {
|
||||||
|
AlarmManagerCompat.setExactAndAllowWhileIdle(alarmManager, AlarmManager.RTC_WAKEUP, backupAtMillis, pendingIntent)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
showErrorToast(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Context.cancelScheduledAutomaticBackup() {
|
||||||
|
val alarmManager = getSystemService(Context.ALARM_SERVICE) as AlarmManager
|
||||||
|
alarmManager.cancel(getAutomaticBackupIntent())
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Context.checkAndBackupEventsOnBoot() {
|
||||||
|
if (config.autoBackup) {
|
||||||
|
val previousRealBackupTime = config.lastAutoBackupTime
|
||||||
|
val previousScheduledBackupTime = getPreviousAutoBackupTime().seconds()
|
||||||
|
val missedPreviousBackup = previousRealBackupTime < previousScheduledBackupTime
|
||||||
|
if (missedPreviousBackup) {
|
||||||
|
// device was probably off at the scheduled time so backup now
|
||||||
|
backupEventsAndTasks()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Context.backupEventsAndTasks() {
|
||||||
|
ensureBackgroundThread {
|
||||||
|
val config = config
|
||||||
|
val events = eventsHelper.getEventsToExport(
|
||||||
|
eventTypes = config.autoBackupEventTypes.map { it.toLong() } as ArrayList<Long>,
|
||||||
|
exportEvents = config.autoBackupEvents,
|
||||||
|
exportTasks = config.autoBackupTasks,
|
||||||
|
exportPastEntries = config.autoBackupPastEntries
|
||||||
|
)
|
||||||
|
if (events.isEmpty()) {
|
||||||
|
toast(R.string.no_entries_for_exporting)
|
||||||
|
config.lastAutoBackupTime = getNowSeconds()
|
||||||
|
scheduleNextAutomaticBackup()
|
||||||
|
return@ensureBackgroundThread
|
||||||
|
}
|
||||||
|
|
||||||
|
val now = DateTime.now()
|
||||||
|
val year = now.year.toString()
|
||||||
|
val month = now.monthOfYear.ensureTwoDigits()
|
||||||
|
val day = now.dayOfMonth.ensureTwoDigits()
|
||||||
|
val hours = now.hourOfDay.ensureTwoDigits()
|
||||||
|
val minutes = now.minuteOfHour.ensureTwoDigits()
|
||||||
|
val seconds = now.secondOfMinute.ensureTwoDigits()
|
||||||
|
|
||||||
|
val filename = config.autoBackupFilename
|
||||||
|
.replace("%Y", year, false)
|
||||||
|
.replace("%M", month, false)
|
||||||
|
.replace("%D", day, false)
|
||||||
|
.replace("%h", hours, false)
|
||||||
|
.replace("%m", minutes, false)
|
||||||
|
.replace("%s", seconds, false)
|
||||||
|
|
||||||
|
val outputFolder = File(config.autoBackupFolder).apply {
|
||||||
|
mkdirs()
|
||||||
|
}
|
||||||
|
|
||||||
|
val exportFile = File(outputFolder, "$filename.ics")
|
||||||
|
val outputStream = try {
|
||||||
|
exportFile.outputStream()
|
||||||
|
} catch (e: Exception) {
|
||||||
|
showErrorToast(e)
|
||||||
|
null
|
||||||
|
}
|
||||||
|
|
||||||
|
IcsExporter(this).exportEvents(outputStream, events, showExportingToast = false) { result ->
|
||||||
|
when (result) {
|
||||||
|
IcsExporter.ExportResult.EXPORT_PARTIAL -> toast(R.string.exporting_some_entries_failed)
|
||||||
|
IcsExporter.ExportResult.EXPORT_FAIL -> toast(R.string.exporting_failed)
|
||||||
|
else -> {}
|
||||||
|
}
|
||||||
|
MediaScannerConnection.scanFile(
|
||||||
|
this,
|
||||||
|
arrayOf(exportFile.absolutePath),
|
||||||
|
arrayOf(exportFile.getMimeType())
|
||||||
|
) { _, _ -> }
|
||||||
|
|
||||||
|
config.lastAutoBackupTime = getNowSeconds()
|
||||||
|
}
|
||||||
|
scheduleNextAutomaticBackup()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun Context.getRepetitionText(seconds: Int) = when (seconds) {
|
fun Context.getRepetitionText(seconds: Int) = when (seconds) {
|
||||||
0 -> getString(R.string.no_repetition)
|
0 -> getString(R.string.no_repetition)
|
||||||
DAY -> getString(R.string.daily)
|
DAY -> getString(R.string.daily)
|
||||||
|
@@ -3,6 +3,8 @@ package com.simplemobiletools.calendar.pro.helpers
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.media.AudioManager
|
import android.media.AudioManager
|
||||||
import android.media.RingtoneManager
|
import android.media.RingtoneManager
|
||||||
|
import android.os.Environment
|
||||||
|
import android.os.Environment.DIRECTORY_DOWNLOADS
|
||||||
import com.simplemobiletools.calendar.pro.R
|
import com.simplemobiletools.calendar.pro.R
|
||||||
import com.simplemobiletools.calendar.pro.extensions.config
|
import com.simplemobiletools.calendar.pro.extensions.config
|
||||||
import com.simplemobiletools.calendar.pro.extensions.scheduleCalDAVSync
|
import com.simplemobiletools.calendar.pro.extensions.scheduleCalDAVSync
|
||||||
@@ -258,4 +260,36 @@ class Config(context: Context) : BaseConfig(context) {
|
|||||||
var wasFilteredOutWarningShown: Boolean
|
var wasFilteredOutWarningShown: Boolean
|
||||||
get() = prefs.getBoolean(WAS_FILTERED_OUT_WARNING_SHOWN, false)
|
get() = prefs.getBoolean(WAS_FILTERED_OUT_WARNING_SHOWN, false)
|
||||||
set(wasFilteredOutWarningShown) = prefs.edit().putBoolean(WAS_FILTERED_OUT_WARNING_SHOWN, wasFilteredOutWarningShown).apply()
|
set(wasFilteredOutWarningShown) = prefs.edit().putBoolean(WAS_FILTERED_OUT_WARNING_SHOWN, wasFilteredOutWarningShown).apply()
|
||||||
|
|
||||||
|
var autoBackup: Boolean
|
||||||
|
get() = prefs.getBoolean(AUTO_BACKUP, false)
|
||||||
|
set(enableAutomaticBackups) = prefs.edit().putBoolean(AUTO_BACKUP, enableAutomaticBackups).apply()
|
||||||
|
|
||||||
|
var autoBackupFolder: String
|
||||||
|
get() = prefs.getString(AUTO_BACKUP_FOLDER, Environment.getExternalStoragePublicDirectory(DIRECTORY_DOWNLOADS).absolutePath)!!
|
||||||
|
set(autoBackupPath) = prefs.edit().putString(AUTO_BACKUP_FOLDER, autoBackupPath).apply()
|
||||||
|
|
||||||
|
var autoBackupFilename: String
|
||||||
|
get() = prefs.getString(AUTO_BACKUP_FILENAME, "")!!
|
||||||
|
set(autoBackupFilename) = prefs.edit().putString(AUTO_BACKUP_FILENAME, autoBackupFilename).apply()
|
||||||
|
|
||||||
|
var autoBackupEventTypes: Set<String>
|
||||||
|
get() = prefs.getStringSet(AUTO_BACKUP_EVENT_TYPES, HashSet())!!
|
||||||
|
set(autoBackupEventTypes) = prefs.edit().remove(AUTO_BACKUP_EVENT_TYPES).putStringSet(AUTO_BACKUP_EVENT_TYPES, autoBackupEventTypes).apply()
|
||||||
|
|
||||||
|
var autoBackupEvents: Boolean
|
||||||
|
get() = prefs.getBoolean(AUTO_BACKUP_EVENTS, true)
|
||||||
|
set(autoBackupEvents) = prefs.edit().putBoolean(AUTO_BACKUP_EVENTS, autoBackupEvents).apply()
|
||||||
|
|
||||||
|
var autoBackupTasks: Boolean
|
||||||
|
get() = prefs.getBoolean(AUTO_BACKUP_TASKS, true)
|
||||||
|
set(autoBackupTasks) = prefs.edit().putBoolean(AUTO_BACKUP_TASKS, autoBackupTasks).apply()
|
||||||
|
|
||||||
|
var autoBackupPastEntries: Boolean
|
||||||
|
get() = prefs.getBoolean(AUTO_BACKUP_PAST_ENTRIES, true)
|
||||||
|
set(autoBackupPastEntries) = prefs.edit().putBoolean(AUTO_BACKUP_PAST_ENTRIES, autoBackupPastEntries).apply()
|
||||||
|
|
||||||
|
var lastAutoBackupTime: Long
|
||||||
|
get() = prefs.getLong(LAST_AUTO_BACKUP_TIME, 0L)
|
||||||
|
set(lastAutoBackupTime) = prefs.edit().putLong(LAST_AUTO_BACKUP_TIME, lastAutoBackupTime).apply()
|
||||||
}
|
}
|
||||||
|
@@ -3,12 +3,14 @@ package com.simplemobiletools.calendar.pro.helpers
|
|||||||
import com.simplemobiletools.calendar.pro.activities.EventActivity
|
import com.simplemobiletools.calendar.pro.activities.EventActivity
|
||||||
import com.simplemobiletools.calendar.pro.activities.TaskActivity
|
import com.simplemobiletools.calendar.pro.activities.TaskActivity
|
||||||
import com.simplemobiletools.commons.helpers.MONTH_SECONDS
|
import com.simplemobiletools.commons.helpers.MONTH_SECONDS
|
||||||
|
import org.joda.time.DateTime
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
const val STORED_LOCALLY_ONLY = 0
|
const val STORED_LOCALLY_ONLY = 0
|
||||||
const val ROW_COUNT = 6
|
const val ROW_COUNT = 6
|
||||||
const val COLUMN_COUNT = 7
|
const val COLUMN_COUNT = 7
|
||||||
const val SCHEDULE_CALDAV_REQUEST_CODE = 10000
|
const val SCHEDULE_CALDAV_REQUEST_CODE = 10000
|
||||||
|
const val AUTOMATIC_BACKUP_REQUEST_CODE = 10001
|
||||||
const val FETCH_INTERVAL = 3 * MONTH_SECONDS
|
const val FETCH_INTERVAL = 3 * MONTH_SECONDS
|
||||||
const val MAX_SEARCH_YEAR = 2051218800L // 2035, limit search results for events repeating indefinitely
|
const val MAX_SEARCH_YEAR = 2051218800L // 2035, limit search results for events repeating indefinitely
|
||||||
|
|
||||||
@@ -72,6 +74,8 @@ const val YEAR = 31536000
|
|||||||
const val EVENT_PERIOD_TODAY = -1
|
const val EVENT_PERIOD_TODAY = -1
|
||||||
const val EVENT_PERIOD_CUSTOM = -2
|
const val EVENT_PERIOD_CUSTOM = -2
|
||||||
|
|
||||||
|
const val AUTO_BACKUP_INTERVAL_IN_DAYS = 1
|
||||||
|
|
||||||
const val EVENT_LIST_PERIOD = "event_list_period"
|
const val EVENT_LIST_PERIOD = "event_list_period"
|
||||||
|
|
||||||
// Shared Preferences
|
// Shared Preferences
|
||||||
@@ -129,6 +133,14 @@ const val HIGHLIGHT_WEEKENDS_COLOR = "highlight_weekends_color"
|
|||||||
const val LAST_USED_EVENT_SPAN = "last_used_event_span"
|
const val LAST_USED_EVENT_SPAN = "last_used_event_span"
|
||||||
const val ALLOW_CREATING_TASKS = "allow_creating_tasks"
|
const val ALLOW_CREATING_TASKS = "allow_creating_tasks"
|
||||||
const val WAS_FILTERED_OUT_WARNING_SHOWN = "was_filtered_out_warning_shown"
|
const val WAS_FILTERED_OUT_WARNING_SHOWN = "was_filtered_out_warning_shown"
|
||||||
|
const val AUTO_BACKUP = "auto_backup"
|
||||||
|
const val AUTO_BACKUP_FOLDER = "auto_backup_folder"
|
||||||
|
const val AUTO_BACKUP_FILENAME = "auto_backup_filename"
|
||||||
|
const val AUTO_BACKUP_EVENT_TYPES = "auto_backup_event_types"
|
||||||
|
const val AUTO_BACKUP_EVENTS = "auto_backup_events"
|
||||||
|
const val AUTO_BACKUP_TASKS = "auto_backup_tasks"
|
||||||
|
const val AUTO_BACKUP_PAST_ENTRIES = "auto_backup_past_entries"
|
||||||
|
const val LAST_AUTO_BACKUP_TIME = "last_auto_backup_time"
|
||||||
|
|
||||||
// repeat_rule for monthly and yearly repetition
|
// repeat_rule for monthly and yearly repetition
|
||||||
const val REPEAT_SAME_DAY = 1 // i.e. 25th every month, or 3rd june (if yearly repetition)
|
const val REPEAT_SAME_DAY = 1 // i.e. 25th every month, or 3rd june (if yearly repetition)
|
||||||
@@ -264,3 +276,19 @@ fun getActivityToOpen(isTask: Boolean) = if (isTask) {
|
|||||||
fun generateImportId(): String {
|
fun generateImportId(): String {
|
||||||
return UUID.randomUUID().toString().replace("-", "") + System.currentTimeMillis().toString()
|
return UUID.randomUUID().toString().replace("-", "") + System.currentTimeMillis().toString()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 6 am is the hardcoded automatic backup time, intervals shorter than 1 day are not yet supported.
|
||||||
|
fun getNextAutoBackupTime(): DateTime {
|
||||||
|
val now = DateTime.now()
|
||||||
|
val sixHour = now.withHourOfDay(6)
|
||||||
|
return if (now.millis < sixHour.millis) {
|
||||||
|
sixHour
|
||||||
|
} else {
|
||||||
|
sixHour.plusDays(AUTO_BACKUP_INTERVAL_IN_DAYS)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getPreviousAutoBackupTime(): DateTime {
|
||||||
|
val nextBackupTime = getNextAutoBackupTime()
|
||||||
|
return nextBackupTime.minusDays(AUTO_BACKUP_INTERVAL_IN_DAYS)
|
||||||
|
}
|
||||||
|
@@ -519,22 +519,22 @@ class EventsHelper(val context: Context) {
|
|||||||
return events
|
return events
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getEventsToExport(eventTypes: ArrayList<Long>): ArrayList<Event> {
|
fun getEventsToExport(eventTypes: ArrayList<Long>, exportEvents: Boolean, exportTasks: Boolean, exportPastEntries: Boolean): ArrayList<Event> {
|
||||||
val currTS = getNowSeconds()
|
val currTS = getNowSeconds()
|
||||||
var events = ArrayList<Event>()
|
var events = ArrayList<Event>()
|
||||||
val tasks = ArrayList<Event>()
|
val tasks = ArrayList<Event>()
|
||||||
if (config.exportPastEntries) {
|
if (exportPastEntries) {
|
||||||
if (config.exportEvents) {
|
if (exportEvents) {
|
||||||
events.addAll(eventsDB.getAllEventsWithTypes(eventTypes))
|
events.addAll(eventsDB.getAllEventsWithTypes(eventTypes))
|
||||||
}
|
}
|
||||||
if (config.exportTasks) {
|
if (exportTasks) {
|
||||||
tasks.addAll(eventsDB.getAllTasksWithTypes(eventTypes))
|
tasks.addAll(eventsDB.getAllTasksWithTypes(eventTypes))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (config.exportEvents) {
|
if (exportEvents) {
|
||||||
events.addAll(eventsDB.getAllFutureEventsWithTypes(currTS, eventTypes))
|
events.addAll(eventsDB.getAllFutureEventsWithTypes(currTS, eventTypes))
|
||||||
}
|
}
|
||||||
if (config.exportTasks) {
|
if (exportTasks) {
|
||||||
tasks.addAll(eventsDB.getAllFutureTasksWithTypes(currTS, eventTypes))
|
tasks.addAll(eventsDB.getAllFutureTasksWithTypes(currTS, eventTypes))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
package com.simplemobiletools.calendar.pro.helpers
|
package com.simplemobiletools.calendar.pro.helpers
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
import android.provider.CalendarContract.Events
|
import android.provider.CalendarContract.Events
|
||||||
import com.simplemobiletools.calendar.pro.R
|
import com.simplemobiletools.calendar.pro.R
|
||||||
import com.simplemobiletools.calendar.pro.extensions.calDAVHelper
|
import com.simplemobiletools.calendar.pro.extensions.calDAVHelper
|
||||||
@@ -9,7 +10,6 @@ import com.simplemobiletools.calendar.pro.helpers.IcsExporter.ExportResult.EXPOR
|
|||||||
import com.simplemobiletools.calendar.pro.helpers.IcsExporter.ExportResult.EXPORT_PARTIAL
|
import com.simplemobiletools.calendar.pro.helpers.IcsExporter.ExportResult.EXPORT_PARTIAL
|
||||||
import com.simplemobiletools.calendar.pro.models.CalDAVCalendar
|
import com.simplemobiletools.calendar.pro.models.CalDAVCalendar
|
||||||
import com.simplemobiletools.calendar.pro.models.Event
|
import com.simplemobiletools.calendar.pro.models.Event
|
||||||
import com.simplemobiletools.commons.activities.BaseSimpleActivity
|
|
||||||
import com.simplemobiletools.commons.extensions.toast
|
import com.simplemobiletools.commons.extensions.toast
|
||||||
import com.simplemobiletools.commons.extensions.writeLn
|
import com.simplemobiletools.commons.extensions.writeLn
|
||||||
import com.simplemobiletools.commons.helpers.ensureBackgroundThread
|
import com.simplemobiletools.commons.helpers.ensureBackgroundThread
|
||||||
@@ -17,7 +17,7 @@ import java.io.BufferedWriter
|
|||||||
import java.io.OutputStream
|
import java.io.OutputStream
|
||||||
import java.io.OutputStreamWriter
|
import java.io.OutputStreamWriter
|
||||||
|
|
||||||
class IcsExporter(private val activity: BaseSimpleActivity) {
|
class IcsExporter(private val context: Context) {
|
||||||
enum class ExportResult {
|
enum class ExportResult {
|
||||||
EXPORT_FAIL, EXPORT_OK, EXPORT_PARTIAL
|
EXPORT_FAIL, EXPORT_OK, EXPORT_PARTIAL
|
||||||
}
|
}
|
||||||
@@ -26,7 +26,7 @@ class IcsExporter(private val activity: BaseSimpleActivity) {
|
|||||||
private var eventsExported = 0
|
private var eventsExported = 0
|
||||||
private var eventsFailed = 0
|
private var eventsFailed = 0
|
||||||
private var calendars = ArrayList<CalDAVCalendar>()
|
private var calendars = ArrayList<CalDAVCalendar>()
|
||||||
private val reminderLabel = activity.getString(R.string.reminder)
|
private val reminderLabel = context.getString(R.string.reminder)
|
||||||
private val exportTime = Formatter.getExportedTime(System.currentTimeMillis())
|
private val exportTime = Formatter.getExportedTime(System.currentTimeMillis())
|
||||||
|
|
||||||
fun exportEvents(
|
fun exportEvents(
|
||||||
@@ -41,9 +41,9 @@ class IcsExporter(private val activity: BaseSimpleActivity) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ensureBackgroundThread {
|
ensureBackgroundThread {
|
||||||
calendars = activity.calDAVHelper.getCalDAVCalendars("", false)
|
calendars = context.calDAVHelper.getCalDAVCalendars("", false)
|
||||||
if (showExportingToast) {
|
if (showExportingToast) {
|
||||||
activity.toast(R.string.exporting)
|
context.toast(R.string.exporting)
|
||||||
}
|
}
|
||||||
|
|
||||||
object : BufferedWriter(OutputStreamWriter(outputStream, Charsets.UTF_8)) {
|
object : BufferedWriter(OutputStreamWriter(outputStream, Charsets.UTF_8)) {
|
||||||
@@ -133,8 +133,8 @@ class IcsExporter(private val activity: BaseSimpleActivity) {
|
|||||||
writeLn(BEGIN_EVENT)
|
writeLn(BEGIN_EVENT)
|
||||||
event.title.replace("\n", "\\n").let { if (it.isNotEmpty()) writeLn("$SUMMARY:$it") }
|
event.title.replace("\n", "\\n").let { if (it.isNotEmpty()) writeLn("$SUMMARY:$it") }
|
||||||
event.importId.let { if (it.isNotEmpty()) writeLn("$UID$it") }
|
event.importId.let { if (it.isNotEmpty()) writeLn("$UID$it") }
|
||||||
writeLn("$CATEGORY_COLOR${activity.eventTypesDB.getEventTypeWithId(event.eventType)?.color}")
|
writeLn("$CATEGORY_COLOR${context.eventTypesDB.getEventTypeWithId(event.eventType)?.color}")
|
||||||
writeLn("$CATEGORIES${activity.eventTypesDB.getEventTypeWithId(event.eventType)?.title}")
|
writeLn("$CATEGORIES${context.eventTypesDB.getEventTypeWithId(event.eventType)?.title}")
|
||||||
writeLn("$LAST_MODIFIED:${Formatter.getExportedTime(event.lastUpdated)}")
|
writeLn("$LAST_MODIFIED:${Formatter.getExportedTime(event.lastUpdated)}")
|
||||||
writeLn("$TRANSP${if (event.availability == Events.AVAILABILITY_FREE) TRANSPARENT else OPAQUE}")
|
writeLn("$TRANSP${if (event.availability == Events.AVAILABILITY_FREE) TRANSPARENT else OPAQUE}")
|
||||||
event.location.let { if (it.isNotEmpty()) writeLn("$LOCATION:$it") }
|
event.location.let { if (it.isNotEmpty()) writeLn("$LOCATION:$it") }
|
||||||
@@ -166,8 +166,8 @@ class IcsExporter(private val activity: BaseSimpleActivity) {
|
|||||||
writeLn(BEGIN_TASK)
|
writeLn(BEGIN_TASK)
|
||||||
task.title.replace("\n", "\\n").let { if (it.isNotEmpty()) writeLn("$SUMMARY:$it") }
|
task.title.replace("\n", "\\n").let { if (it.isNotEmpty()) writeLn("$SUMMARY:$it") }
|
||||||
task.importId.let { if (it.isNotEmpty()) writeLn("$UID$it") }
|
task.importId.let { if (it.isNotEmpty()) writeLn("$UID$it") }
|
||||||
writeLn("$CATEGORY_COLOR${activity.eventTypesDB.getEventTypeWithId(task.eventType)?.color}")
|
writeLn("$CATEGORY_COLOR${context.eventTypesDB.getEventTypeWithId(task.eventType)?.color}")
|
||||||
writeLn("$CATEGORIES${activity.eventTypesDB.getEventTypeWithId(task.eventType)?.title}")
|
writeLn("$CATEGORIES${context.eventTypesDB.getEventTypeWithId(task.eventType)?.title}")
|
||||||
writeLn("$LAST_MODIFIED:${Formatter.getExportedTime(task.lastUpdated)}")
|
writeLn("$LAST_MODIFIED:${Formatter.getExportedTime(task.lastUpdated)}")
|
||||||
task.location.let { if (it.isNotEmpty()) writeLn("$LOCATION:$it") }
|
task.location.let { if (it.isNotEmpty()) writeLn("$LOCATION:$it") }
|
||||||
|
|
||||||
|
@@ -0,0 +1,17 @@
|
|||||||
|
package com.simplemobiletools.calendar.pro.receivers
|
||||||
|
|
||||||
|
import android.content.BroadcastReceiver
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.os.PowerManager
|
||||||
|
import com.simplemobiletools.calendar.pro.extensions.backupEventsAndTasks
|
||||||
|
|
||||||
|
class AutomaticBackupReceiver : BroadcastReceiver() {
|
||||||
|
|
||||||
|
override fun onReceive(context: Context, intent: Intent) {
|
||||||
|
val powerManager = context.getSystemService(Context.POWER_SERVICE) as PowerManager
|
||||||
|
val wakelock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "simplecalendar:automaticbackupreceiver")
|
||||||
|
wakelock.acquire(3000)
|
||||||
|
context.backupEventsAndTasks()
|
||||||
|
}
|
||||||
|
}
|
@@ -3,9 +3,7 @@ package com.simplemobiletools.calendar.pro.receivers
|
|||||||
import android.content.BroadcastReceiver
|
import android.content.BroadcastReceiver
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import com.simplemobiletools.calendar.pro.extensions.notifyRunningEvents
|
import com.simplemobiletools.calendar.pro.extensions.*
|
||||||
import com.simplemobiletools.calendar.pro.extensions.recheckCalDAVCalendars
|
|
||||||
import com.simplemobiletools.calendar.pro.extensions.scheduleAllEvents
|
|
||||||
import com.simplemobiletools.commons.helpers.ensureBackgroundThread
|
import com.simplemobiletools.commons.helpers.ensureBackgroundThread
|
||||||
|
|
||||||
class BootCompletedReceiver : BroadcastReceiver() {
|
class BootCompletedReceiver : BroadcastReceiver() {
|
||||||
@@ -16,6 +14,8 @@ class BootCompletedReceiver : BroadcastReceiver() {
|
|||||||
scheduleAllEvents()
|
scheduleAllEvents()
|
||||||
notifyRunningEvents()
|
notifyRunningEvents()
|
||||||
recheckCalDAVCalendars(true) {}
|
recheckCalDAVCalendars(true) {}
|
||||||
|
scheduleNextAutomaticBackup()
|
||||||
|
checkAndBackupEventsOnBoot()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -921,6 +921,47 @@
|
|||||||
android:id="@+id/settings_tasks_divider"
|
android:id="@+id/settings_tasks_divider"
|
||||||
layout="@layout/divider" />
|
layout="@layout/divider" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/settings_backups_label"
|
||||||
|
style="@style/SettingsSectionLabelStyle"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/backups" />
|
||||||
|
|
||||||
|
<RelativeLayout
|
||||||
|
android:id="@+id/settings_enable_automatic_backups_holder"
|
||||||
|
style="@style/SettingsHolderCheckboxStyle"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<com.simplemobiletools.commons.views.MyAppCompatCheckbox
|
||||||
|
android:id="@+id/settings_enable_automatic_backups"
|
||||||
|
style="@style/SettingsCheckboxStyle"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/enable_automatic_backups" />
|
||||||
|
|
||||||
|
</RelativeLayout>
|
||||||
|
|
||||||
|
<RelativeLayout
|
||||||
|
android:id="@+id/settings_manage_automatic_backups_holder"
|
||||||
|
style="@style/SettingsHolderTextViewOneLinerStyle"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<com.simplemobiletools.commons.views.MyTextView
|
||||||
|
android:id="@+id/settings_manage_automatic_backups"
|
||||||
|
style="@style/SettingsTextLabelStyle"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/manage_automatic_backups" />
|
||||||
|
|
||||||
|
</RelativeLayout>
|
||||||
|
|
||||||
|
<include
|
||||||
|
android:id="@+id/settings_backups_divider"
|
||||||
|
layout="@layout/divider" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/settings_migrating_label"
|
android:id="@+id/settings_migrating_label"
|
||||||
style="@style/SettingsSectionLabelStyle"
|
style="@style/SettingsSectionLabelStyle"
|
||||||
|
9
app/src/main/res/layout/datetime_pattern_info_layout.xml
Normal file
9
app/src/main/res/layout/datetime_pattern_info_layout.xml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<com.simplemobiletools.commons.views.MyTextView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:id="@+id/date_time_pattern_info"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:paddingHorizontal="@dimen/big_margin"
|
||||||
|
android:paddingTop="@dimen/big_margin"
|
||||||
|
android:text="@string/date_time_pattern_info"
|
||||||
|
android:textIsSelectable="true" />
|
158
app/src/main/res/layout/dialog_manage_automatic_backups.xml
Normal file
158
app/src/main/res/layout/dialog_manage_automatic_backups.xml
Normal file
@@ -0,0 +1,158 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:id="@+id/export_events_scrollview"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/backup_events_holder"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:paddingTop="@dimen/activity_margin">
|
||||||
|
|
||||||
|
<com.simplemobiletools.commons.views.MyTextInputLayout
|
||||||
|
android:id="@+id/backup_events_folder_hint"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="@dimen/activity_margin"
|
||||||
|
android:layout_marginEnd="@dimen/activity_margin"
|
||||||
|
android:layout_marginBottom="@dimen/activity_margin"
|
||||||
|
android:hint="@string/folder">
|
||||||
|
|
||||||
|
<com.google.android.material.textfield.TextInputEditText
|
||||||
|
android:id="@+id/backup_events_folder"
|
||||||
|
style="@style/UnclickableEditText"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content" />
|
||||||
|
|
||||||
|
</com.simplemobiletools.commons.views.MyTextInputLayout>
|
||||||
|
|
||||||
|
<com.simplemobiletools.commons.views.MyTextInputLayout
|
||||||
|
android:id="@+id/backup_events_filename_hint"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="@dimen/activity_margin"
|
||||||
|
android:layout_marginEnd="@dimen/activity_margin"
|
||||||
|
android:hint="@string/filename_without_ics"
|
||||||
|
app:endIconDrawable="@drawable/ic_info_vector"
|
||||||
|
app:endIconMode="custom">
|
||||||
|
|
||||||
|
<com.google.android.material.textfield.TextInputEditText
|
||||||
|
android:id="@+id/backup_events_filename"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:inputType="textCapWords"
|
||||||
|
android:singleLine="true"
|
||||||
|
android:textCursorDrawable="@null"
|
||||||
|
android:textSize="@dimen/bigger_text_size" />
|
||||||
|
|
||||||
|
</com.simplemobiletools.commons.views.MyTextInputLayout>
|
||||||
|
|
||||||
|
<RelativeLayout
|
||||||
|
android:id="@+id/backup_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/backup_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/backup_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/backup_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/backup_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/backup_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:paddingEnd="@dimen/activity_margin">
|
||||||
|
|
||||||
|
<com.simplemobiletools.commons.views.MyAppCompatCheckbox
|
||||||
|
android:id="@+id/backup_past_events_checkbox"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="@null"
|
||||||
|
android:clickable="false"
|
||||||
|
android:layoutDirection="rtl"
|
||||||
|
android:paddingHorizontal="@dimen/medium_margin"
|
||||||
|
android:text="@string/export_past_entries" />
|
||||||
|
|
||||||
|
</RelativeLayout>
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/select_event_types_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/manage_event_types_holder"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="?attr/selectableItemBackground"
|
||||||
|
android:paddingVertical="@dimen/medium_margin"
|
||||||
|
android:paddingStart="@dimen/normal_margin"
|
||||||
|
android:paddingEnd="@dimen/activity_margin">
|
||||||
|
|
||||||
|
<com.simplemobiletools.commons.views.MyTextView
|
||||||
|
android:id="@+id/manage_event_types"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="@null"
|
||||||
|
android:clickable="false"
|
||||||
|
android:paddingHorizontal="@dimen/medium_margin"
|
||||||
|
android:paddingVertical="@dimen/normal_margin"
|
||||||
|
android:text="@string/manage_event_types"
|
||||||
|
android:textSize="@dimen/normal_text_size" />
|
||||||
|
|
||||||
|
</RelativeLayout>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
</ScrollView>
|
Reference in New Issue
Block a user