Merge pull request #32 from SimpleMobileTools/master

upd
This commit is contained in:
solokot 2020-04-17 23:02:36 +03:00 committed by GitHub
commit 56a74d80be
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
67 changed files with 554 additions and 605 deletions

View file

@ -64,12 +64,12 @@ android {
}
dependencies {
implementation 'com.simplemobiletools:commons:5.23.10'
implementation 'com.simplemobiletools:commons:5.25.22'
implementation 'joda-time:joda-time:2.10.1'
implementation 'androidx.multidex:multidex:2.0.1'
implementation 'androidx.constraintlayout:constraintlayout:2.0.0-beta4'
kapt 'androidx.room:room-compiler:2.2.4'
implementation 'androidx.room:room-runtime:2.2.4'
annotationProcessor 'androidx.room:room-compiler:2.2.4'
kapt 'androidx.room:room-compiler:2.2.5'
implementation 'androidx.room:room-runtime:2.2.5'
annotationProcessor 'androidx.room:room-compiler:2.2.5'
}

View file

@ -10,12 +10,19 @@ END:VEVENT
BEGIN:VEVENT
SUMMARY:Early May Bank Holiday
UID:21626542-636f-43d7-8fa9-bad05bb82dca
DTSTART;VALUE=DATE:20100503
DTEND;VALUE=DATE:20100504
DTSTART;VALUE=DATE:20210503
DTEND;VALUE=DATE:20210504
RRULE:FREQ=YEARLY;INTERVAL=1;BYMONTH=5;BYDAY=1MO
STATUS:CONFIRMED
END:VEVENT
BEGIN:VEVENT
SUMMARY:Early May Bank Holiday
UID:21626542-636f-43d7-8fa9-bad05bbsds
DTSTART;VALUE=DATE:20200508
DTEND;VALUE=DATE:20200509
STATUS:CONFIRMED
END:VEVENT
BEGIN:VEVENT
SUMMARY:Summer Bank Holiday
UID:5dac6a63-e519-4ad1-a687-2fd5fccb4656
DTSTART;VALUE=DATE:20130826
@ -47,13 +54,40 @@ UID:ca6af7456b0088abad9a69f9f620f5ac-59@gov.uk
STATUS:CONFIRMED
END:VEVENT
BEGIN:VEVENT
DTEND;VALUE=DATE:20190420
DTSTART;VALUE=DATE:20190419
DTEND;VALUE=DATE:20190420
SUMMARY:Good Friday
UID:ca6af7456b0088abad9a69f9f620f5ac-58@gov.uk
STATUS:CONFIRMED
END:VEVENT
BEGIN:VEVENT
DTSTART;VALUE=DATE:20200410
DTEND;VALUE=DATE:20200411
SUMMARY:Good Friday
UID:ca6af7456b0088abad9a69f9f620f5ac-2020-04-10-GoodFriday@gov.uk
STATUS:CONFIRMED
END:VEVENT
BEGIN:VEVENT
DTSTART;VALUE=DATE:20200413
DTEND;VALUE=DATE:20200414
SUMMARY:Easter Monday
UID:ca6af7456b0088abad9a69f9f620f5ac-2020-04-13-EasterMonday@gov.uk
STATUS:CONFIRMED
END:VEVENT
BEGIN:VEVENT
DTSTART;VALUE=DATE:20210402
DTEND;VALUE=DATE:20210403
SUMMARY:Good Friday
STATUS:CONFIRMED
END:VEVENT
BEGIN:VEVENT
DTSTART;VALUE=DATE:20210405
DTEND;VALUE=DATE:20210406
SUMMARY:Easter Monday
UID:ca6af7456b0088abad9a69f9f620f5ac-2021-04-05-EasterMonday@gov.uk
STATUS:CONFIRMED
END:VEVENT
BEGIN:VEVENT
SUMMARY:Spring Bank Holiday
UID:5dac6a63-e519-4ad1-a687-2fd5fccb4
DTSTART;VALUE=DATE:20130527

View file

@ -4,13 +4,15 @@ import android.app.Activity
import android.app.DatePickerDialog
import android.app.TimePickerDialog
import android.content.Intent
import android.database.Cursor
import android.graphics.drawable.BitmapDrawable
import android.graphics.drawable.Drawable
import android.graphics.drawable.LayerDrawable
import android.net.Uri
import android.os.Bundle
import android.provider.CalendarContract
import android.provider.ContactsContract
import android.provider.CalendarContract.Attendees
import android.provider.ContactsContract.CommonDataKinds
import android.provider.ContactsContract.CommonDataKinds.StructuredName
import android.provider.ContactsContract.Data
import android.text.TextUtils
import android.text.method.LinkMovementMethod
import android.view.Menu
@ -86,7 +88,6 @@ class EventActivity : SimpleActivity() {
private var mStoredEventTypes = ArrayList<EventType>()
private var mOriginalTimeZone = DateTimeZone.getDefault().id
private lateinit var mAttendeePlaceholder: Drawable
private lateinit var mEventStartDateTime: DateTime
private lateinit var mEventEndDateTime: DateTime
private lateinit var mEvent: Event
@ -103,8 +104,6 @@ class EventActivity : SimpleActivity() {
val intent = intent ?: return
mDialogTheme = getDialogTheme()
mWasContactsPermissionChecked = hasPermission(PERMISSION_READ_CONTACTS)
mAttendeePlaceholder = resources.getDrawable(R.drawable.attendee_circular_background)
(mAttendeePlaceholder as LayerDrawable).findDrawableByLayerId(R.id.attendee_circular_background).applyColorFilter(config.primaryColor)
val eventId = intent.getLongExtra(EVENT_ID, 0L)
ensureBackgroundThread {
@ -1259,9 +1258,9 @@ class EventActivity : SimpleActivity() {
mAttendees.sortWith(compareBy<Attendee>
{ it.isMe }.thenBy
{ it.status == CalendarContract.Attendees.ATTENDEE_STATUS_ACCEPTED }.thenBy
{ it.status == CalendarContract.Attendees.ATTENDEE_STATUS_DECLINED }.thenBy
{ it.status == CalendarContract.Attendees.ATTENDEE_STATUS_TENTATIVE }.thenBy
{ it.status == Attendees.ATTENDEE_STATUS_ACCEPTED }.thenBy
{ it.status == Attendees.ATTENDEE_STATUS_DECLINED }.thenBy
{ it.status == Attendees.ATTENDEE_STATUS_TENTATIVE }.thenBy
{ it.status })
mAttendees.reverse()
@ -1347,8 +1346,14 @@ class EventActivity : SimpleActivity() {
beVisibleIf(attendee.showStatusImage())
}
event_contact_name.text = if (attendee.isMe) getString(R.string.my_status) else attendee.getPublicName()
if (attendee.isMe) {
(event_contact_name.layoutParams as RelativeLayout.LayoutParams).addRule(RelativeLayout.START_OF, event_contact_me_status.id)
}
val placeholder = BitmapDrawable(resources, context.getContactLetterIcon(event_contact_name.value))
event_contact_image.apply {
attendee.updateImage(applicationContext, this, mAttendeePlaceholder)
attendee.updateImage(applicationContext, this, placeholder)
beVisible()
}
@ -1357,11 +1362,6 @@ class EventActivity : SimpleActivity() {
beGoneIf(attendee.isMe)
}
event_contact_name.text = if (attendee.isMe) getString(R.string.my_status) else attendee.getPublicName()
if (attendee.isMe) {
(event_contact_name.layoutParams as RelativeLayout.LayoutParams).addRule(RelativeLayout.START_OF, event_contact_me_status.id)
}
if (attendee.isMe) {
updateAttendeeMe(this, attendee)
}
@ -1373,9 +1373,9 @@ class EventActivity : SimpleActivity() {
if (attendee.isMe) {
event_contact_attendee.setOnClickListener {
val items = arrayListOf(
RadioItem(CalendarContract.Attendees.ATTENDEE_STATUS_ACCEPTED, getString(R.string.going)),
RadioItem(CalendarContract.Attendees.ATTENDEE_STATUS_DECLINED, getString(R.string.not_going)),
RadioItem(CalendarContract.Attendees.ATTENDEE_STATUS_TENTATIVE, getString(R.string.maybe_going))
RadioItem(Attendees.ATTENDEE_STATUS_ACCEPTED, getString(R.string.going)),
RadioItem(Attendees.ATTENDEE_STATUS_DECLINED, getString(R.string.not_going)),
RadioItem(Attendees.ATTENDEE_STATUS_TENTATIVE, getString(R.string.maybe_going))
)
RadioGroupDialog(this@EventActivity, items, attendee.status) {
@ -1389,8 +1389,8 @@ class EventActivity : SimpleActivity() {
private fun getAttendeeStatusImage(attendee: Attendee): Drawable {
return resources.getDrawable(when (attendee.status) {
CalendarContract.Attendees.ATTENDEE_STATUS_ACCEPTED -> R.drawable.ic_check_green
CalendarContract.Attendees.ATTENDEE_STATUS_DECLINED -> R.drawable.ic_cross_red
Attendees.ATTENDEE_STATUS_ACCEPTED -> R.drawable.ic_check_green
Attendees.ATTENDEE_STATUS_DECLINED -> R.drawable.ic_cross_red
else -> R.drawable.ic_question_yellow
})
}
@ -1398,9 +1398,9 @@ class EventActivity : SimpleActivity() {
private fun updateAttendeeMe(holder: RelativeLayout, attendee: Attendee) {
holder.apply {
event_contact_me_status.text = getString(when (attendee.status) {
CalendarContract.Attendees.ATTENDEE_STATUS_ACCEPTED -> R.string.going
CalendarContract.Attendees.ATTENDEE_STATUS_DECLINED -> R.string.not_going
CalendarContract.Attendees.ATTENDEE_STATUS_TENTATIVE -> R.string.maybe_going
Attendees.ATTENDEE_STATUS_ACCEPTED -> R.string.going
Attendees.ATTENDEE_STATUS_DECLINED -> R.string.not_going
Attendees.ATTENDEE_STATUS_TENTATIVE -> R.string.maybe_going
else -> R.string.invited
})
@ -1427,7 +1427,7 @@ class EventActivity : SimpleActivity() {
val customEmails = mAttendeeAutoCompleteViews.filter { it.isVisible() }.map { it.value }.filter { it.isNotEmpty() }.toMutableList() as ArrayList<String>
customEmails.mapTo(attendees) {
Attendee(0, "", it, CalendarContract.Attendees.ATTENDEE_STATUS_INVITED, "", false, CalendarContract.Attendees.RELATIONSHIP_NONE)
Attendee(0, "", it, Attendees.ATTENDEE_STATUS_INVITED, "", false, Attendees.RELATIONSHIP_NONE)
}
attendees = attendees.distinctBy { it.email }.toMutableList() as ArrayList<Attendee>
@ -1435,8 +1435,8 @@ class EventActivity : SimpleActivity() {
val currentCalendar = calDAVHelper.getCalDAVCalendars("", true).firstOrNull { it.id == mEventCalendarId }
mAvailableContacts.firstOrNull { it.email == currentCalendar?.accountName }?.apply {
attendees = attendees.filter { it.email != currentCalendar?.accountName }.toMutableList() as ArrayList<Attendee>
status = CalendarContract.Attendees.ATTENDEE_STATUS_ACCEPTED
relationship = CalendarContract.Attendees.RELATIONSHIP_ORGANIZER
status = Attendees.ATTENDEE_STATUS_ACCEPTED
relationship = Attendees.RELATIONSHIP_ORGANIZER
attendees.add(this)
}
}
@ -1446,85 +1446,62 @@ class EventActivity : SimpleActivity() {
private fun getNames(): List<Attendee> {
val contacts = ArrayList<Attendee>()
val uri = ContactsContract.Data.CONTENT_URI
val uri = Data.CONTENT_URI
val projection = arrayOf(
ContactsContract.Data.CONTACT_ID,
ContactsContract.CommonDataKinds.StructuredName.PREFIX,
ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME,
ContactsContract.CommonDataKinds.StructuredName.MIDDLE_NAME,
ContactsContract.CommonDataKinds.StructuredName.FAMILY_NAME,
ContactsContract.CommonDataKinds.StructuredName.SUFFIX,
ContactsContract.CommonDataKinds.StructuredName.PHOTO_THUMBNAIL_URI)
Data.CONTACT_ID,
StructuredName.PREFIX,
StructuredName.GIVEN_NAME,
StructuredName.MIDDLE_NAME,
StructuredName.FAMILY_NAME,
StructuredName.SUFFIX,
StructuredName.PHOTO_THUMBNAIL_URI)
val selection = "${ContactsContract.Data.MIMETYPE} = ?"
val selectionArgs = arrayOf(ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE)
val selection = "${Data.MIMETYPE} = ?"
val selectionArgs = arrayOf(StructuredName.CONTENT_ITEM_TYPE)
var cursor: Cursor? = null
try {
cursor = contentResolver.query(uri, projection, selection, selectionArgs, null)
if (cursor?.moveToFirst() == true) {
do {
val id = cursor.getIntValue(ContactsContract.Data.CONTACT_ID)
val prefix = cursor.getStringValue(ContactsContract.CommonDataKinds.StructuredName.PREFIX) ?: ""
val firstName = cursor.getStringValue(ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME) ?: ""
val middleName = cursor.getStringValue(ContactsContract.CommonDataKinds.StructuredName.MIDDLE_NAME) ?: ""
val surname = cursor.getStringValue(ContactsContract.CommonDataKinds.StructuredName.FAMILY_NAME) ?: ""
val suffix = cursor.getStringValue(ContactsContract.CommonDataKinds.StructuredName.SUFFIX) ?: ""
val photoUri = cursor.getStringValue(ContactsContract.CommonDataKinds.StructuredName.PHOTO_THUMBNAIL_URI) ?: ""
queryCursor(uri, projection, selection, selectionArgs) { cursor ->
val id = cursor.getIntValue(Data.CONTACT_ID)
val prefix = cursor.getStringValue(StructuredName.PREFIX) ?: ""
val firstName = cursor.getStringValue(StructuredName.GIVEN_NAME) ?: ""
val middleName = cursor.getStringValue(StructuredName.MIDDLE_NAME) ?: ""
val surname = cursor.getStringValue(StructuredName.FAMILY_NAME) ?: ""
val suffix = cursor.getStringValue(StructuredName.SUFFIX) ?: ""
val photoUri = cursor.getStringValue(StructuredName.PHOTO_THUMBNAIL_URI) ?: ""
val names = arrayListOf(prefix, firstName, middleName, surname, suffix).filter { it.trim().isNotEmpty() }
val fullName = TextUtils.join("", names)
if (fullName.isNotEmpty() || photoUri.isNotEmpty()) {
val contact = Attendee(id, fullName, "", CalendarContract.Attendees.ATTENDEE_STATUS_NONE, photoUri, false, CalendarContract.Attendees.RELATIONSHIP_NONE)
contacts.add(contact)
}
} while (cursor.moveToNext())
val names = arrayListOf(prefix, firstName, middleName, surname, suffix).filter { it.trim().isNotEmpty() }
val fullName = TextUtils.join(" ", names).trim()
if (fullName.isNotEmpty() || photoUri.isNotEmpty()) {
val contact = Attendee(id, fullName, "", Attendees.ATTENDEE_STATUS_NONE, photoUri, false, Attendees.RELATIONSHIP_NONE)
contacts.add(contact)
}
} catch (ignored: Exception) {
} finally {
cursor?.close()
}
return contacts
}
private fun getEmails(): ArrayList<Attendee> {
val contacts = ArrayList<Attendee>()
val uri = ContactsContract.CommonDataKinds.Email.CONTENT_URI
val uri = CommonDataKinds.Email.CONTENT_URI
val projection = arrayOf(
ContactsContract.Data.CONTACT_ID,
ContactsContract.CommonDataKinds.Email.DATA
Data.CONTACT_ID,
CommonDataKinds.Email.DATA
)
var cursor: Cursor? = null
try {
cursor = contentResolver.query(uri, projection, null, null, null)
if (cursor?.moveToFirst() == true) {
do {
val id = cursor.getIntValue(ContactsContract.Data.CONTACT_ID)
val email = cursor.getStringValue(ContactsContract.CommonDataKinds.Email.DATA) ?: continue
val contact = Attendee(id, "", email, CalendarContract.Attendees.ATTENDEE_STATUS_NONE, "", false, CalendarContract.Attendees.RELATIONSHIP_NONE)
contacts.add(contact)
} while (cursor.moveToNext())
}
} catch (ignored: Exception) {
} finally {
cursor?.close()
queryCursor(uri, projection) { cursor ->
val id = cursor.getIntValue(Data.CONTACT_ID)
val email = cursor.getStringValue(CommonDataKinds.Email.DATA) ?: return@queryCursor
val contact = Attendee(id, "", email, Attendees.ATTENDEE_STATUS_NONE, "", false, Attendees.RELATIONSHIP_NONE)
contacts.add(contact)
}
return contacts
}
private fun updateIconColors() {
val textColor = config.textColor
event_time_image.applyColorFilter(textColor)
event_time_zone_image.applyColorFilter(textColor)
event_repetition_image.applyColorFilter(textColor)
event_reminder_image.applyColorFilter(textColor)
event_type_image.applyColorFilter(textColor)
event_caldav_calendar_image.applyColorFilter(textColor)
event_show_on_map.applyColorFilter(getAdjustedPrimaryColor())
event_reminder_1_type.applyColorFilter(textColor)
event_reminder_2_type.applyColorFilter(textColor)
event_reminder_3_type.applyColorFilter(textColor)
event_attendees_image.applyColorFilter(textColor)
val textColor = config.textColor
arrayOf(event_time_image, event_time_zone_image, event_repetition_image, event_reminder_image, event_type_image, event_caldav_calendar_image,
event_reminder_1_type, event_reminder_2_type, event_reminder_3_type, event_attendees_image).forEach {
it.applyColorFilter(textColor)
}
}
}

View file

@ -8,13 +8,12 @@ import android.content.Intent
import android.content.pm.ActivityInfo
import android.content.pm.ShortcutInfo
import android.content.pm.ShortcutManager
import android.database.Cursor
import android.graphics.drawable.ColorDrawable
import android.graphics.drawable.Icon
import android.graphics.drawable.LayerDrawable
import android.net.Uri
import android.os.Bundle
import android.provider.ContactsContract
import android.provider.ContactsContract.*
import android.view.Menu
import android.view.MenuItem
import android.widget.Toast
@ -32,6 +31,8 @@ import com.simplemobiletools.calendar.pro.extensions.*
import com.simplemobiletools.calendar.pro.fragments.*
import com.simplemobiletools.calendar.pro.helpers.*
import com.simplemobiletools.calendar.pro.helpers.Formatter
import com.simplemobiletools.calendar.pro.helpers.IcsExporter.ExportResult
import com.simplemobiletools.calendar.pro.helpers.IcsImporter.ImportResult
import com.simplemobiletools.calendar.pro.jobs.CalDAVUpdateListener
import com.simplemobiletools.calendar.pro.models.Event
import com.simplemobiletools.calendar.pro.models.EventType
@ -144,12 +145,8 @@ class MainActivity : SimpleActivity(), RefreshRecyclerViewListener {
storeStateVariables()
updateWidgets()
if (config.storedView != EVENTS_LIST_VIEW) {
updateTextColors(calendar_coordinator)
}
search_placeholder.setTextColor(config.textColor)
search_placeholder_2.setTextColor(config.textColor)
calendar_fab.setColors(config.textColor, getAdjustedPrimaryColor(), config.backgroundColor)
updateTextColors(calendar_coordinator)
search_holder.background = ColorDrawable(config.backgroundColor)
checkSwipeRefreshAvailability()
checkShortcuts()
@ -179,7 +176,7 @@ class MainActivity : SimpleActivity(), RefreshRecyclerViewListener {
menu.apply {
goToTodayButton = findItem(R.id.go_to_today)
findItem(R.id.filter).isVisible = mShouldFilterBeVisible
findItem(R.id.go_to_today).isVisible = shouldGoToTodayBeVisible || config.storedView == EVENTS_LIST_VIEW
findItem(R.id.go_to_today).isVisible = (shouldGoToTodayBeVisible || config.storedView == EVENTS_LIST_VIEW) && !mIsSearchOpen
findItem(R.id.go_to_date).isVisible = config.storedView != EVENTS_LIST_VIEW
}
@ -188,8 +185,8 @@ class MainActivity : SimpleActivity(), RefreshRecyclerViewListener {
return true
}
override fun onPrepareOptionsMenu(menu: Menu?): Boolean {
menu!!.apply {
override fun onPrepareOptionsMenu(menu: Menu): Boolean {
menu.apply {
findItem(R.id.refresh_caldav_calendars).isVisible = config.caldavSync
}
@ -279,6 +276,7 @@ class MainActivity : SimpleActivity(), RefreshRecyclerViewListener {
search_holder.beVisible()
calendar_fab.beGone()
searchQueryChanged("")
invalidateOptionsMenu()
return true
}
@ -286,6 +284,7 @@ class MainActivity : SimpleActivity(), RefreshRecyclerViewListener {
mIsSearchOpen = false
search_holder.beGone()
calendar_fab.beVisibleIf(currentFragments.last() !is YearFragmentsHolder)
invalidateOptionsMenu()
return true
}
})
@ -488,7 +487,7 @@ class MainActivity : SimpleActivity(), RefreshRecyclerViewListener {
val result = IcsImporter(this).importEvents(it as String, eventTypeId, 0, false)
handleParseResult(result)
if (result != IcsImporter.ImportResult.IMPORT_FAIL) {
if (result != ImportResult.IMPORT_FAIL) {
runOnUiThread {
updateViewPager()
}
@ -545,11 +544,11 @@ class MainActivity : SimpleActivity(), RefreshRecyclerViewListener {
}
}
private fun handleParseResult(result: IcsImporter.ImportResult) {
private fun handleParseResult(result: ImportResult) {
toast(when (result) {
IcsImporter.ImportResult.IMPORT_NOTHING_NEW -> R.string.no_new_items
IcsImporter.ImportResult.IMPORT_OK -> R.string.holidays_imported_successfully
IcsImporter.ImportResult.IMPORT_PARTIAL -> R.string.importing_some_holidays_failed
ImportResult.IMPORT_NOTHING_NEW -> R.string.no_new_items
ImportResult.IMPORT_OK -> R.string.holidays_imported_successfully
ImportResult.IMPORT_PARTIAL -> R.string.importing_some_holidays_failed
else -> R.string.importing_holidays_failed
}, Toast.LENGTH_LONG)
}
@ -557,78 +556,69 @@ class MainActivity : SimpleActivity(), RefreshRecyclerViewListener {
private fun addContactEvents(birthdays: Boolean, reminders: ArrayList<Int>, callback: (Int) -> Unit) {
var eventsAdded = 0
var eventsFound = 0
val uri = ContactsContract.Data.CONTENT_URI
val projection = arrayOf(ContactsContract.Contacts.DISPLAY_NAME,
ContactsContract.CommonDataKinds.Event.CONTACT_ID,
ContactsContract.CommonDataKinds.Event.CONTACT_LAST_UPDATED_TIMESTAMP,
ContactsContract.CommonDataKinds.Event.START_DATE)
val uri = Data.CONTENT_URI
val projection = arrayOf(Contacts.DISPLAY_NAME,
CommonDataKinds.Event.CONTACT_ID,
CommonDataKinds.Event.CONTACT_LAST_UPDATED_TIMESTAMP,
CommonDataKinds.Event.START_DATE)
val selection = "${ContactsContract.Data.MIMETYPE} = ? AND ${ContactsContract.CommonDataKinds.Event.TYPE} = ?"
val type = if (birthdays) ContactsContract.CommonDataKinds.Event.TYPE_BIRTHDAY else ContactsContract.CommonDataKinds.Event.TYPE_ANNIVERSARY
val selectionArgs = arrayOf(ContactsContract.CommonDataKinds.Event.CONTENT_ITEM_TYPE, type.toString())
var cursor: Cursor? = null
try {
cursor = contentResolver.query(uri, projection, selection, selectionArgs, null)
if (cursor?.moveToFirst() == true) {
val dateFormats = getDateFormats()
val existingEvents = if (birthdays) eventsDB.getBirthdays() else eventsDB.getAnniversaries()
val importIDs = HashMap<String, Long>()
existingEvents.forEach {
importIDs[it.importId] = it.startTS
}
val selection = "${Data.MIMETYPE} = ? AND ${CommonDataKinds.Event.TYPE} = ?"
val type = if (birthdays) CommonDataKinds.Event.TYPE_BIRTHDAY else CommonDataKinds.Event.TYPE_ANNIVERSARY
val selectionArgs = arrayOf(CommonDataKinds.Event.CONTENT_ITEM_TYPE, type.toString())
val eventTypeId = if (birthdays) getBirthdaysEventTypeId() else getAnniversariesEventTypeId()
val dateFormats = getDateFormats()
val existingEvents = if (birthdays) eventsDB.getBirthdays() else eventsDB.getAnniversaries()
val importIDs = HashMap<String, Long>()
existingEvents.forEach {
importIDs[it.importId] = it.startTS
}
do {
val contactId = cursor.getIntValue(ContactsContract.CommonDataKinds.Event.CONTACT_ID).toString()
val name = cursor.getStringValue(ContactsContract.Contacts.DISPLAY_NAME)
val startDate = cursor.getStringValue(ContactsContract.CommonDataKinds.Event.START_DATE)
val eventTypeId = if (birthdays) getBirthdaysEventTypeId() else getAnniversariesEventTypeId()
for (format in dateFormats) {
try {
val formatter = SimpleDateFormat(format, Locale.getDefault())
val date = formatter.parse(startDate)
if (date.year < 70) {
date.year = 70
queryCursor(uri, projection, selection, selectionArgs, showErrors = true) { cursor ->
val contactId = cursor.getIntValue(CommonDataKinds.Event.CONTACT_ID).toString()
val name = cursor.getStringValue(Contacts.DISPLAY_NAME)
val startDate = cursor.getStringValue(CommonDataKinds.Event.START_DATE)
for (format in dateFormats) {
try {
val formatter = SimpleDateFormat(format, Locale.getDefault())
val date = formatter.parse(startDate)
if (date.year < 70) {
date.year = 70
}
val timestamp = date.time / 1000L
val source = if (birthdays) SOURCE_CONTACT_BIRTHDAY else SOURCE_CONTACT_ANNIVERSARY
val lastUpdated = cursor.getLongValue(CommonDataKinds.Event.CONTACT_LAST_UPDATED_TIMESTAMP)
val event = Event(null, timestamp, timestamp, name, reminder1Minutes = reminders[0], reminder2Minutes = reminders[1],
reminder3Minutes = reminders[2], importId = contactId, timeZone = DateTimeZone.getDefault().id, flags = FLAG_ALL_DAY,
repeatInterval = YEAR, repeatRule = REPEAT_SAME_DAY, eventType = eventTypeId, source = source, lastUpdated = lastUpdated)
val importIDsToDelete = ArrayList<String>()
for ((key, value) in importIDs) {
if (key == contactId && value != timestamp) {
val deleted = eventsDB.deleteBirthdayAnniversary(source, key)
if (deleted == 1) {
importIDsToDelete.add(key)
}
val timestamp = date.time / 1000L
val source = if (birthdays) SOURCE_CONTACT_BIRTHDAY else SOURCE_CONTACT_ANNIVERSARY
val lastUpdated = cursor.getLongValue(ContactsContract.CommonDataKinds.Event.CONTACT_LAST_UPDATED_TIMESTAMP)
val event = Event(null, timestamp, timestamp, name, reminder1Minutes = reminders[0], reminder2Minutes = reminders[1],
reminder3Minutes = reminders[2], importId = contactId, timeZone = DateTimeZone.getDefault().id, flags = FLAG_ALL_DAY,
repeatInterval = YEAR, repeatRule = REPEAT_SAME_DAY, eventType = eventTypeId, source = source, lastUpdated = lastUpdated)
val importIDsToDelete = ArrayList<String>()
for ((key, value) in importIDs) {
if (key == contactId && value != timestamp) {
val deleted = eventsDB.deleteBirthdayAnniversary(source, key)
if (deleted == 1) {
importIDsToDelete.add(key)
}
}
}
importIDsToDelete.forEach {
importIDs.remove(it)
}
eventsFound++
if (!importIDs.containsKey(contactId)) {
eventsHelper.insertEvent(event, false, false) {
eventsAdded++
}
}
break
} catch (e: Exception) {
}
}
} while (cursor.moveToNext())
importIDsToDelete.forEach {
importIDs.remove(it)
}
eventsFound++
if (!importIDs.containsKey(contactId)) {
eventsHelper.insertEvent(event, false, false) {
eventsAdded++
}
}
break
} catch (e: Exception) {
}
}
} catch (e: Exception) {
showErrorToast(e)
} finally {
cursor?.close()
}
runOnUiThread {
@ -839,8 +829,8 @@ class MainActivity : SimpleActivity(), RefreshRecyclerViewListener {
} else {
IcsExporter().exportEvents(this, outputStream, events, true) {
toast(when (it) {
IcsExporter.ExportResult.EXPORT_OK -> R.string.exporting_successful
IcsExporter.ExportResult.EXPORT_PARTIAL -> R.string.exporting_some_entries_failed
ExportResult.EXPORT_OK -> R.string.exporting_successful
ExportResult.EXPORT_PARTIAL -> R.string.exporting_some_entries_failed
else -> R.string.exporting_failed
})
}

View file

@ -3,7 +3,6 @@ package com.simplemobiletools.calendar.pro.activities
import android.app.Activity
import android.app.TimePickerDialog
import android.content.Intent
import android.content.res.Resources
import android.media.AudioManager
import android.os.Bundle
import android.view.Menu
@ -28,13 +27,11 @@ class SettingsActivity : SimpleActivity() {
private val GET_RINGTONE_URI = 1
private val PICK_IMPORT_SOURCE_INTENT = 2
lateinit var res: Resources
private var mStoredPrimaryColor = 0
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_settings)
res = resources
mStoredPrimaryColor = config.primaryColor
}
@ -351,10 +348,10 @@ class SettingsActivity : SimpleActivity() {
settings_reminder_audio_stream.text = getAudioStreamText()
settings_reminder_audio_stream_holder.setOnClickListener {
val items = arrayListOf(
RadioItem(AudioManager.STREAM_ALARM, res.getString(R.string.alarm_stream)),
RadioItem(AudioManager.STREAM_SYSTEM, res.getString(R.string.system_stream)),
RadioItem(AudioManager.STREAM_NOTIFICATION, res.getString(R.string.notification_stream)),
RadioItem(AudioManager.STREAM_RING, res.getString(R.string.ring_stream)))
RadioItem(AudioManager.STREAM_ALARM, getString(R.string.alarm_stream)),
RadioItem(AudioManager.STREAM_SYSTEM, getString(R.string.system_stream)),
RadioItem(AudioManager.STREAM_NOTIFICATION, getString(R.string.notification_stream)),
RadioItem(AudioManager.STREAM_RING, getString(R.string.ring_stream)))
RadioGroupDialog(this@SettingsActivity, items, config.reminderAudioStream) {
config.reminderAudioStream = it as Int
@ -487,10 +484,10 @@ class SettingsActivity : SimpleActivity() {
settings_font_size.text = getFontSizeText()
settings_font_size_holder.setOnClickListener {
val items = arrayListOf(
RadioItem(FONT_SIZE_SMALL, res.getString(R.string.small)),
RadioItem(FONT_SIZE_MEDIUM, res.getString(R.string.medium)),
RadioItem(FONT_SIZE_LARGE, res.getString(R.string.large)),
RadioItem(FONT_SIZE_EXTRA_LARGE, res.getString(R.string.extra_large)))
RadioItem(FONT_SIZE_SMALL, getString(R.string.small)),
RadioItem(FONT_SIZE_MEDIUM, getString(R.string.medium)),
RadioItem(FONT_SIZE_LARGE, getString(R.string.large)),
RadioItem(FONT_SIZE_EXTRA_LARGE, getString(R.string.extra_large)))
RadioGroupDialog(this@SettingsActivity, items, config.fontSize) {
config.fontSize = it as Int
@ -513,12 +510,12 @@ class SettingsActivity : SimpleActivity() {
settings_list_widget_view_to_open.text = getDefaultViewText()
settings_list_widget_view_to_open_holder.setOnClickListener {
val items = arrayListOf(
RadioItem(DAILY_VIEW, res.getString(R.string.daily_view)),
RadioItem(WEEKLY_VIEW, res.getString(R.string.weekly_view)),
RadioItem(MONTHLY_VIEW, res.getString(R.string.monthly_view)),
RadioItem(YEARLY_VIEW, res.getString(R.string.yearly_view)),
RadioItem(EVENTS_LIST_VIEW, res.getString(R.string.simple_event_list)),
RadioItem(LAST_VIEW, res.getString(R.string.last_view)))
RadioItem(DAILY_VIEW, getString(R.string.daily_view)),
RadioItem(WEEKLY_VIEW, getString(R.string.weekly_view)),
RadioItem(MONTHLY_VIEW, getString(R.string.monthly_view)),
RadioItem(YEARLY_VIEW, getString(R.string.yearly_view)),
RadioItem(EVENTS_LIST_VIEW, getString(R.string.simple_event_list)),
RadioItem(LAST_VIEW, getString(R.string.last_view)))
RadioGroupDialog(this@SettingsActivity, items, config.listWidgetViewToOpen) {
config.listWidgetViewToOpen = it as Int

View file

@ -1,6 +1,6 @@
package com.simplemobiletools.calendar.pro.adapters
import android.graphics.drawable.LayerDrawable
import android.graphics.drawable.BitmapDrawable
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
@ -8,19 +8,13 @@ import android.widget.ArrayAdapter
import android.widget.Filter
import com.simplemobiletools.calendar.pro.R
import com.simplemobiletools.calendar.pro.activities.SimpleActivity
import com.simplemobiletools.calendar.pro.extensions.config
import com.simplemobiletools.calendar.pro.models.Attendee
import com.simplemobiletools.commons.extensions.applyColorFilter
import com.simplemobiletools.commons.extensions.getContactLetterIcon
import com.simplemobiletools.commons.extensions.normalizeString
import kotlinx.android.synthetic.main.item_autocomplete_email_name.view.*
class AutoCompleteTextViewAdapter(val activity: SimpleActivity, val contacts: ArrayList<Attendee>) : ArrayAdapter<Attendee>(activity, 0, contacts) {
var resultList = ArrayList<Attendee>()
private var placeholder = activity.resources.getDrawable(R.drawable.attendee_circular_background)
init {
(placeholder as LayerDrawable).findDrawableByLayerId(R.id.attendee_circular_background).applyColorFilter(activity.config.primaryColor)
}
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
val contact = resultList[position]
@ -30,6 +24,13 @@ class AutoCompleteTextViewAdapter(val activity: SimpleActivity, val contacts: Ar
listItem = LayoutInflater.from(activity).inflate(layout, parent, false)
}
val nameToUse = when {
contact.name.isNotEmpty() -> contact.name
contact.email.isNotEmpty() -> contact.email
else -> "S"
}
val placeholder = BitmapDrawable(activity.resources, context.getContactLetterIcon(nameToUse))
listItem!!.apply {
tag = contact.name.isNotEmpty()
item_autocomplete_name?.text = contact.name

View file

@ -9,19 +9,25 @@ import com.simplemobiletools.calendar.pro.R
import com.simplemobiletools.calendar.pro.activities.SimpleActivity
import com.simplemobiletools.calendar.pro.extensions.calDAVHelper
import com.simplemobiletools.calendar.pro.extensions.config
import com.simplemobiletools.commons.extensions.beVisibleIf
import com.simplemobiletools.commons.extensions.setupDialogStuff
import kotlinx.android.synthetic.main.calendar_item_account.view.*
import kotlinx.android.synthetic.main.calendar_item_calendar.view.*
import kotlinx.android.synthetic.main.dialog_select_calendars.view.*
class SelectCalendarsDialog(val activity: SimpleActivity, val callback: () -> Unit) {
var prevAccount = ""
var dialog: AlertDialog
var view = (activity.layoutInflater.inflate(R.layout.dialog_select_calendars, null) as ViewGroup)
private var prevAccount = ""
private var dialog: AlertDialog
private var view = (activity.layoutInflater.inflate(R.layout.dialog_select_calendars, null) as ViewGroup)
init {
val ids = activity.config.getSyncedCalendarIdsAsList()
val calendars = activity.calDAVHelper.getCalDAVCalendars("", true)
view.apply {
dialog_select_calendars_placeholder.beVisibleIf(calendars.isEmpty())
dialog_select_calendars_holder.beVisibleIf(calendars.isNotEmpty())
}
val sorted = calendars.sortedWith(compareBy({ it.accountName }, { it.displayName }))
sorted.forEach {
if (prevAccount != it.accountName) {
@ -41,8 +47,8 @@ class SelectCalendarsDialog(val activity: SimpleActivity, val callback: () -> Un
}
private fun addCalendarItem(isEvent: Boolean, text: String, tag: Int = 0, shouldCheck: Boolean = false) {
val calendarItem = activity.layoutInflater.inflate(if (isEvent) R.layout.calendar_item_calendar else R.layout.calendar_item_account,
view.dialog_select_calendars_holder, false)
val layout = if (isEvent) R.layout.calendar_item_calendar else R.layout.calendar_item_account
val calendarItem = activity.layoutInflater.inflate(layout, view.dialog_select_calendars_holder, false)
if (isEvent) {
calendarItem.calendar_item_calendar_switch.apply {

View file

@ -262,7 +262,7 @@ fun Context.getNotification(pendingIntent: PendingIntent, event: Event, content:
val builder = NotificationCompat.Builder(this, channelId)
.setContentTitle(contentTitle)
.setContentText(contentText)
.setSmallIcon(R.drawable.ic_calendar)
.setSmallIcon(R.drawable.ic_calendar_vector)
.setContentIntent(pendingIntent)
.setPriority(NotificationCompat.PRIORITY_MAX)
.setDefaults(Notification.DEFAULT_LIGHTS)

View file

@ -4,9 +4,7 @@ import android.annotation.SuppressLint
import android.content.ContentUris
import android.content.ContentValues
import android.content.Context
import android.database.Cursor
import android.provider.CalendarContract
import android.provider.CalendarContract.Reminders
import android.provider.CalendarContract.*
import android.util.SparseIntArray
import com.google.gson.Gson
import com.google.gson.reflect.TypeToken
@ -22,6 +20,7 @@ import org.joda.time.format.DateTimeFormat
import java.util.*
import kotlin.collections.ArrayList
@SuppressLint("MissingPermission")
class CalDAVHelper(val context: Context) {
private val eventsHelper = context.eventsHelper
@ -58,45 +57,34 @@ class CalDAVHelper(val context: Context) {
return calendars
}
val uri = CalendarContract.Calendars.CONTENT_URI
val uri = Calendars.CONTENT_URI
val projection = arrayOf(
CalendarContract.Calendars._ID,
CalendarContract.Calendars.CALENDAR_DISPLAY_NAME,
CalendarContract.Calendars.ACCOUNT_NAME,
CalendarContract.Calendars.ACCOUNT_TYPE,
CalendarContract.Calendars.OWNER_ACCOUNT,
CalendarContract.Calendars.CALENDAR_COLOR,
CalendarContract.Calendars.CALENDAR_ACCESS_LEVEL)
Calendars._ID,
Calendars.CALENDAR_DISPLAY_NAME,
Calendars.ACCOUNT_NAME,
Calendars.ACCOUNT_TYPE,
Calendars.OWNER_ACCOUNT,
Calendars.CALENDAR_COLOR,
Calendars.CALENDAR_ACCESS_LEVEL)
val selection = if (ids.trim().isNotEmpty()) "${CalendarContract.Calendars._ID} IN ($ids)" else null
var cursor: Cursor? = null
try {
cursor = context.contentResolver.query(uri, projection, selection, null, null)
if (cursor?.moveToFirst() == true) {
do {
val id = cursor.getIntValue(CalendarContract.Calendars._ID)
val displayName = cursor.getStringValue(CalendarContract.Calendars.CALENDAR_DISPLAY_NAME)
val accountName = cursor.getStringValue(CalendarContract.Calendars.ACCOUNT_NAME)
val accountType = cursor.getStringValue(CalendarContract.Calendars.ACCOUNT_TYPE)
val ownerName = cursor.getStringValue(CalendarContract.Calendars.OWNER_ACCOUNT) ?: ""
val color = cursor.getIntValue(CalendarContract.Calendars.CALENDAR_COLOR)
val accessLevel = cursor.getIntValue(CalendarContract.Calendars.CALENDAR_ACCESS_LEVEL)
val calendar = CalDAVCalendar(id, displayName, accountName, accountType, ownerName, color, accessLevel)
calendars.add(calendar)
} while (cursor.moveToNext())
}
} catch (e: Exception) {
if (showToasts) {
context.showErrorToast(e)
}
} finally {
cursor?.close()
val selection = if (ids.trim().isNotEmpty()) "${Calendars._ID} IN ($ids)" else null
context.queryCursor(uri, projection, selection, showErrors = showToasts) { cursor ->
val id = cursor.getIntValue(Calendars._ID)
val displayName = cursor.getStringValue(Calendars.CALENDAR_DISPLAY_NAME)
val accountName = cursor.getStringValue(Calendars.ACCOUNT_NAME)
val accountType = cursor.getStringValue(Calendars.ACCOUNT_TYPE)
val ownerName = cursor.getStringValue(Calendars.OWNER_ACCOUNT) ?: ""
val color = cursor.getIntValue(Calendars.CALENDAR_COLOR)
val accessLevel = cursor.getIntValue(Calendars.CALENDAR_ACCESS_LEVEL)
val calendar = CalDAVCalendar(id, displayName, accountName, accountType, ownerName, color, accessLevel)
calendars.add(calendar)
}
return calendars
}
fun updateCalDAVCalendar(eventType: EventType) {
val uri = CalendarContract.Calendars.CONTENT_URI
val uri = Calendars.CONTENT_URI
val values = fillCalendarContentValues(eventType)
val newUri = ContentUris.withAppendedId(uri, eventType.caldavCalendarId.toLong())
try {
@ -108,26 +96,23 @@ class CalDAVHelper(val context: Context) {
private fun fillCalendarContentValues(eventType: EventType): ContentValues {
val colorKey = getEventTypeColorKey(eventType)
return ContentValues().apply {
put(CalendarContract.Calendars.CALENDAR_COLOR_KEY, colorKey)
put(CalendarContract.Calendars.CALENDAR_DISPLAY_NAME, eventType.title)
put(Calendars.CALENDAR_COLOR_KEY, colorKey)
put(Calendars.CALENDAR_DISPLAY_NAME, eventType.title)
}
}
@SuppressLint("MissingPermission")
private fun getEventTypeColorKey(eventType: EventType): Int {
val uri = CalendarContract.Colors.CONTENT_URI
val projection = arrayOf(CalendarContract.Colors.COLOR_KEY)
val selection = "${CalendarContract.Colors.COLOR_TYPE} = ? AND ${CalendarContract.Colors.COLOR} = ? AND ${CalendarContract.Colors.ACCOUNT_NAME} = ?"
val selectionArgs = arrayOf(CalendarContract.Colors.TYPE_CALENDAR.toString(), eventType.color.toString(), eventType.caldavEmail)
val uri = Colors.CONTENT_URI
val projection = arrayOf(Colors.COLOR_KEY)
val selection = "${Colors.COLOR_TYPE} = ? AND ${Colors.COLOR} = ? AND ${Colors.ACCOUNT_NAME} = ?"
val selectionArgs = arrayOf(Colors.TYPE_CALENDAR.toString(), eventType.color.toString(), eventType.caldavEmail)
var cursor: Cursor? = null
try {
cursor = context.contentResolver.query(uri, projection, selection, selectionArgs, null)
if (cursor?.moveToFirst() == true) {
return cursor.getStringValue(CalendarContract.Colors.COLOR_KEY).toInt()
val cursor = context.contentResolver.query(uri, projection, selection, selectionArgs, null)
cursor?.use {
if (cursor.moveToFirst()) {
return cursor.getStringValue(Colors.COLOR_KEY).toInt()
}
} finally {
cursor?.close()
}
return -1
@ -136,23 +121,15 @@ class CalDAVHelper(val context: Context) {
@SuppressLint("MissingPermission")
fun getAvailableCalDAVCalendarColors(eventType: EventType): ArrayList<Int> {
val colors = SparseIntArray()
val uri = CalendarContract.Colors.CONTENT_URI
val projection = arrayOf(CalendarContract.Colors.COLOR, CalendarContract.Colors.COLOR_KEY)
val selection = "${CalendarContract.Colors.COLOR_TYPE} = ? AND ${CalendarContract.Colors.ACCOUNT_NAME} = ?"
val selectionArgs = arrayOf(CalendarContract.Colors.TYPE_CALENDAR.toString(), eventType.caldavEmail)
val uri = Colors.CONTENT_URI
val projection = arrayOf(Colors.COLOR, Colors.COLOR_KEY)
val selection = "${Colors.COLOR_TYPE} = ? AND ${Colors.ACCOUNT_NAME} = ?"
val selectionArgs = arrayOf(Colors.TYPE_CALENDAR.toString(), eventType.caldavEmail)
var cursor: Cursor? = null
try {
cursor = context.contentResolver.query(uri, projection, selection, selectionArgs, null)
if (cursor?.moveToFirst() == true) {
do {
val colorKey = cursor.getIntValue(CalendarContract.Colors.COLOR_KEY)
val color = cursor.getIntValue(CalendarContract.Colors.COLOR)
colors.put(colorKey, color)
} while (cursor.moveToNext())
}
} finally {
cursor?.close()
context.queryCursor(uri, projection, selection, selectionArgs) { cursor ->
val colorKey = cursor.getIntValue(Colors.COLOR_KEY)
val color = cursor.getIntValue(Colors.COLOR)
colors.put(colorKey, color)
}
var sortedColors = ArrayList<Int>(colors.size())
@ -173,151 +150,139 @@ class CalDAVHelper(val context: Context) {
importIdsMap[it.importId] = it
}
val uri = CalendarContract.Events.CONTENT_URI
val uri = Events.CONTENT_URI
val projection = arrayOf(
CalendarContract.Events._ID,
CalendarContract.Events.TITLE,
CalendarContract.Events.DESCRIPTION,
CalendarContract.Events.DTSTART,
CalendarContract.Events.DTEND,
CalendarContract.Events.DURATION,
CalendarContract.Events.EXDATE,
CalendarContract.Events.ALL_DAY,
CalendarContract.Events.RRULE,
CalendarContract.Events.ORIGINAL_ID,
CalendarContract.Events.ORIGINAL_INSTANCE_TIME,
CalendarContract.Events.EVENT_LOCATION,
CalendarContract.Events.EVENT_TIMEZONE,
CalendarContract.Events.CALENDAR_TIME_ZONE,
CalendarContract.Events.DELETED)
Events._ID,
Events.TITLE,
Events.DESCRIPTION,
Events.DTSTART,
Events.DTEND,
Events.DURATION,
Events.EXDATE,
Events.ALL_DAY,
Events.RRULE,
Events.ORIGINAL_ID,
Events.ORIGINAL_INSTANCE_TIME,
Events.EVENT_LOCATION,
Events.EVENT_TIMEZONE,
Events.CALENDAR_TIME_ZONE,
Events.DELETED)
val selection = "${CalendarContract.Events.CALENDAR_ID} = $calendarId"
var cursor: Cursor? = null
try {
cursor = context.contentResolver.query(uri, projection, selection, null, null)
if (cursor?.moveToFirst() == true) {
do {
val deleted = cursor.getIntValue(CalendarContract.Events.DELETED)
if (deleted == 1) {
continue
}
val selection = "${Events.CALENDAR_ID} = $calendarId"
context.queryCursor(uri, projection, selection, showErrors = showToasts) { cursor ->
val deleted = cursor.getIntValue(Events.DELETED)
if (deleted == 1) {
return@queryCursor
}
val id = cursor.getLongValue(CalendarContract.Events._ID)
val title = cursor.getStringValue(CalendarContract.Events.TITLE) ?: ""
val description = cursor.getStringValue(CalendarContract.Events.DESCRIPTION) ?: ""
val startTS = cursor.getLongValue(CalendarContract.Events.DTSTART) / 1000L
var endTS = cursor.getLongValue(CalendarContract.Events.DTEND) / 1000L
val allDay = cursor.getIntValue(CalendarContract.Events.ALL_DAY)
val rrule = cursor.getStringValue(CalendarContract.Events.RRULE) ?: ""
val location = cursor.getStringValue(CalendarContract.Events.EVENT_LOCATION) ?: ""
val originalId = cursor.getStringValue(CalendarContract.Events.ORIGINAL_ID)
val originalInstanceTime = cursor.getLongValue(CalendarContract.Events.ORIGINAL_INSTANCE_TIME)
val reminders = getCalDAVEventReminders(id)
val attendees = Gson().toJson(getCalDAVEventAttendees(id))
val id = cursor.getLongValue(Events._ID)
val title = cursor.getStringValue(Events.TITLE) ?: ""
val description = cursor.getStringValue(Events.DESCRIPTION) ?: ""
val startTS = cursor.getLongValue(Events.DTSTART) / 1000L
var endTS = cursor.getLongValue(Events.DTEND) / 1000L
val allDay = cursor.getIntValue(Events.ALL_DAY)
val rrule = cursor.getStringValue(Events.RRULE) ?: ""
val location = cursor.getStringValue(Events.EVENT_LOCATION) ?: ""
val originalId = cursor.getStringValue(Events.ORIGINAL_ID)
val originalInstanceTime = cursor.getLongValue(Events.ORIGINAL_INSTANCE_TIME)
val reminders = getCalDAVEventReminders(id)
val attendees = Gson().toJson(getCalDAVEventAttendees(id))
if (endTS == 0L) {
val duration = cursor.getStringValue(CalendarContract.Events.DURATION) ?: ""
endTS = startTS + Parser().parseDurationSeconds(duration)
}
if (endTS == 0L) {
val duration = cursor.getStringValue(Events.DURATION) ?: ""
endTS = startTS + Parser().parseDurationSeconds(duration)
}
val reminder1 = reminders.getOrNull(0)
val reminder2 = reminders.getOrNull(1)
val reminder3 = reminders.getOrNull(2)
val importId = getCalDAVEventImportId(calendarId, id)
val eventTimeZone = cursor.getStringValue(CalendarContract.Events.EVENT_TIMEZONE)
?: cursor.getStringValue(CalendarContract.Events.CALENDAR_TIME_ZONE) ?: DateTimeZone.getDefault().id
val reminder1 = reminders.getOrNull(0)
val reminder2 = reminders.getOrNull(1)
val reminder3 = reminders.getOrNull(2)
val importId = getCalDAVEventImportId(calendarId, id)
val eventTimeZone = cursor.getStringValue(Events.EVENT_TIMEZONE)
?: cursor.getStringValue(Events.CALENDAR_TIME_ZONE) ?: DateTimeZone.getDefault().id
val source = "$CALDAV-$calendarId"
val repeatRule = Parser().parseRepeatInterval(rrule, startTS)
val event = Event(null, startTS, endTS, title, location, description, reminder1?.minutes ?: REMINDER_OFF,
reminder2?.minutes ?: REMINDER_OFF, reminder3?.minutes ?: REMINDER_OFF, reminder1?.type
?: REMINDER_NOTIFICATION, reminder2?.type ?: REMINDER_NOTIFICATION, reminder3?.type
?: REMINDER_NOTIFICATION, repeatRule.repeatInterval, repeatRule.repeatRule,
repeatRule.repeatLimit, ArrayList(), attendees, importId, eventTimeZone, allDay, eventTypeId, source = source)
val source = "$CALDAV-$calendarId"
val repeatRule = Parser().parseRepeatInterval(rrule, startTS)
val event = Event(null, startTS, endTS, title, location, description, reminder1?.minutes ?: REMINDER_OFF,
reminder2?.minutes ?: REMINDER_OFF, reminder3?.minutes ?: REMINDER_OFF, reminder1?.type
?: REMINDER_NOTIFICATION, reminder2?.type ?: REMINDER_NOTIFICATION, reminder3?.type
?: REMINDER_NOTIFICATION, repeatRule.repeatInterval, repeatRule.repeatRule,
repeatRule.repeatLimit, ArrayList(), attendees, importId, eventTimeZone, allDay, eventTypeId, source = source)
if (event.getIsAllDay()) {
event.startTS = Formatter.getShiftedImportTimestamp(event.startTS)
event.endTS = Formatter.getShiftedImportTimestamp(event.endTS)
if (event.endTS > event.startTS) {
event.endTS -= DAY
}
}
if (event.getIsAllDay()) {
event.startTS = Formatter.getShiftedImportTimestamp(event.startTS)
event.endTS = Formatter.getShiftedImportTimestamp(event.endTS)
if (event.endTS > event.startTS) {
event.endTS -= DAY
}
}
fetchedEventIds.add(importId)
fetchedEventIds.add(importId)
// if the event is an exception from another events repeat rule, find the original parent event
if (originalInstanceTime != 0L) {
val parentImportId = "$source-$originalId"
val parentEvent = context.eventsDB.getEventWithImportId(parentImportId)
val originalDayCode = Formatter.getDayCodeFromTS(originalInstanceTime / 1000L)
if (parentEvent != null && !parentEvent.repetitionExceptions.contains(originalDayCode)) {
event.parentId = parentEvent.id!!
parentEvent.addRepetitionException(originalDayCode)
eventsHelper.insertEvent(parentEvent, false, false)
// if the event is an exception from another events repeat rule, find the original parent event
if (originalInstanceTime != 0L) {
val parentImportId = "$source-$originalId"
val parentEvent = context.eventsDB.getEventWithImportId(parentImportId)
val originalDayCode = Formatter.getDayCodeFromTS(originalInstanceTime / 1000L)
if (parentEvent != null && !parentEvent.repetitionExceptions.contains(originalDayCode)) {
event.parentId = parentEvent.id!!
parentEvent.addRepetitionException(originalDayCode)
eventsHelper.insertEvent(parentEvent, false, false)
event.parentId = parentEvent.id!!
event.addRepetitionException(originalDayCode)
eventsHelper.insertEvent(event, false, false)
continue
}
}
event.parentId = parentEvent.id!!
event.addRepetitionException(originalDayCode)
eventsHelper.insertEvent(event, false, false)
return@queryCursor
}
}
// some calendars add repeatable event exceptions with using the "exdate" field, not by creating a child event that is an exception
val exdate = cursor.getStringValue(CalendarContract.Events.EXDATE) ?: ""
if (exdate.length > 8) {
val lines = exdate.split("\n")
for (line in lines) {
val dates = line.split(",")
dates.forEach {
if (it.endsWith("Z")) {
// convert for example "20190216T230000Z" to "20190217000000" in Slovakia in a weird way
val formatter = DateTimeFormat.forPattern("yyyyMMdd'T'HHmmss'Z'")
val offset = DateTimeZone.getDefault().getOffset(System.currentTimeMillis())
val dt = formatter.parseDateTime(it).plusMillis(offset)
val daycode = Formatter.getDayCodeFromDateTime(dt)
event.repetitionExceptions.add(daycode)
} else {
var potentialTS = it.substring(0, 8)
if (potentialTS.areDigitsOnly()) {
event.repetitionExceptions.add(potentialTS)
} else if (it.contains(";")) {
potentialTS = it.substringAfter(";").substring(0, 8)
event.repetitionExceptions.add(potentialTS)
}
}
// some calendars add repeatable event exceptions with using the "exdate" field, not by creating a child event that is an exception
val exdate = cursor.getStringValue(Events.EXDATE) ?: ""
if (exdate.length > 8) {
val lines = exdate.split("\n")
for (line in lines) {
val dates = line.split(",")
dates.forEach {
if (it.endsWith("Z")) {
// convert for example "20190216T230000Z" to "20190217000000" in Slovakia in a weird way
val formatter = DateTimeFormat.forPattern("yyyyMMdd'T'HHmmss'Z'")
val offset = DateTimeZone.getDefault().getOffset(System.currentTimeMillis())
val dt = formatter.parseDateTime(it).plusMillis(offset)
val daycode = Formatter.getDayCodeFromDateTime(dt)
event.repetitionExceptions.add(daycode)
} else {
var potentialTS = it.substring(0, 8)
if (potentialTS.areDigitsOnly()) {
event.repetitionExceptions.add(potentialTS)
} else if (it.contains(";")) {
potentialTS = it.substringAfter(";").substring(0, 8)
event.repetitionExceptions.add(potentialTS)
}
}
}
if (importIdsMap.containsKey(event.importId)) {
val existingEvent = importIdsMap[importId]
val originalEventId = existingEvent!!.id
existingEvent.apply {
this.id = null
color = 0
lastUpdated = 0L
repetitionExceptions = ArrayList()
}
if (existingEvent.hashCode() != event.hashCode() && title.isNotEmpty()) {
event.id = originalEventId
eventsHelper.updateEvent(event, false, false)
}
} else {
if (title.isNotEmpty()) {
importIdsMap[event.importId] = event
eventsHelper.insertEvent(event, false, false)
}
}
} while (cursor.moveToNext())
}
}
} catch (e: Exception) {
if (showToasts) {
context.showErrorToast(e)
if (importIdsMap.containsKey(event.importId)) {
val existingEvent = importIdsMap[importId]
val originalEventId = existingEvent!!.id
existingEvent.apply {
this.id = null
color = 0
lastUpdated = 0L
repetitionExceptions = ArrayList()
}
if (existingEvent.hashCode() != event.hashCode() && title.isNotEmpty()) {
event.id = originalEventId
eventsHelper.updateEvent(event, false, false)
}
} else {
if (title.isNotEmpty()) {
importIdsMap[event.importId] = event
eventsHelper.insertEvent(event, false, false)
}
}
} finally {
cursor?.close()
}
val eventIdsToDelete = ArrayList<Long>()
@ -335,7 +300,7 @@ class CalDAVHelper(val context: Context) {
@SuppressLint("MissingPermission")
fun insertCalDAVEvent(event: Event) {
val uri = CalendarContract.Events.CONTENT_URI
val uri = Events.CONTENT_URI
val values = fillEventContentValues(event)
val newUri = context.contentResolver.insert(uri, values)
@ -350,7 +315,7 @@ class CalDAVHelper(val context: Context) {
}
fun updateCalDAVEvent(event: Event) {
val uri = CalendarContract.Events.CONTENT_URI
val uri = Events.CONTENT_URI
val values = fillEventContentValues(event)
val eventRemoteID = event.getCalDAVEventId()
event.importId = getCalDAVEventImportId(event.getCalDAVCalendarId(), eventRemoteID)
@ -386,15 +351,15 @@ class CalDAVHelper(val context: Context) {
val attendees = Gson().fromJson<ArrayList<Attendee>>(event.attendees, object : TypeToken<List<Attendee>>() {}.type) ?: ArrayList()
attendees.forEach {
val contentValues = ContentValues().apply {
put(CalendarContract.Attendees.ATTENDEE_NAME, it.name)
put(CalendarContract.Attendees.ATTENDEE_EMAIL, it.email)
put(CalendarContract.Attendees.ATTENDEE_STATUS, it.status)
put(CalendarContract.Attendees.ATTENDEE_RELATIONSHIP, it.relationship)
put(CalendarContract.Attendees.EVENT_ID, event.getCalDAVEventId())
put(Attendees.ATTENDEE_NAME, it.name)
put(Attendees.ATTENDEE_EMAIL, it.email)
put(Attendees.ATTENDEE_STATUS, it.status)
put(Attendees.ATTENDEE_RELATIONSHIP, it.relationship)
put(Attendees.EVENT_ID, event.getCalDAVEventId())
}
try {
context.contentResolver.insert(CalendarContract.Attendees.CONTENT_URI, contentValues)
context.contentResolver.insert(Attendees.CONTENT_URI, contentValues)
} catch (e: Exception) {
context.toast(R.string.unknown_error_occurred)
}
@ -407,31 +372,31 @@ class CalDAVHelper(val context: Context) {
private fun fillEventContentValues(event: Event): ContentValues {
return ContentValues().apply {
put(CalendarContract.Events.CALENDAR_ID, event.getCalDAVCalendarId())
put(CalendarContract.Events.TITLE, event.title)
put(CalendarContract.Events.DESCRIPTION, event.description)
put(CalendarContract.Events.DTSTART, event.startTS * 1000L)
put(CalendarContract.Events.ALL_DAY, if (event.getIsAllDay()) 1 else 0)
put(CalendarContract.Events.EVENT_TIMEZONE, event.getTimeZoneString())
put(CalendarContract.Events.EVENT_LOCATION, event.location)
put(CalendarContract.Events.STATUS, CalendarContract.Events.STATUS_CONFIRMED)
put(Events.CALENDAR_ID, event.getCalDAVCalendarId())
put(Events.TITLE, event.title)
put(Events.DESCRIPTION, event.description)
put(Events.DTSTART, event.startTS * 1000L)
put(Events.ALL_DAY, if (event.getIsAllDay()) 1 else 0)
put(Events.EVENT_TIMEZONE, event.getTimeZoneString())
put(Events.EVENT_LOCATION, event.location)
put(Events.STATUS, Events.STATUS_CONFIRMED)
val repeatRule = Parser().getRepeatCode(event)
if (repeatRule.isEmpty()) {
putNull(CalendarContract.Events.RRULE)
putNull(Events.RRULE)
} else {
put(CalendarContract.Events.RRULE, repeatRule)
put(Events.RRULE, repeatRule)
}
if (event.getIsAllDay() && event.endTS >= event.startTS)
event.endTS += DAY
if (event.repeatInterval > 0) {
put(CalendarContract.Events.DURATION, getDurationCode(event))
putNull(CalendarContract.Events.DTEND)
put(Events.DURATION, getDurationCode(event))
putNull(Events.DTEND)
} else {
put(CalendarContract.Events.DTEND, event.endTS * 1000L)
putNull(CalendarContract.Events.DURATION)
put(Events.DTEND, event.endTS * 1000L)
putNull(Events.DURATION)
}
}
}
@ -443,9 +408,9 @@ class CalDAVHelper(val context: Context) {
}
private fun clearEventAttendees(event: Event) {
val selection = "${CalendarContract.Attendees.EVENT_ID} = ?"
val selection = "${Attendees.EVENT_ID} = ?"
val selectionArgs = arrayOf(event.getCalDAVEventId().toString())
context.contentResolver.delete(CalendarContract.Attendees.CONTENT_URI, selection, selectionArgs)
context.contentResolver.delete(Attendees.CONTENT_URI, selection, selectionArgs)
}
private fun getDurationCode(event: Event): String {
@ -463,7 +428,7 @@ class CalDAVHelper(val context: Context) {
}
fun deleteCalDAVEvent(event: Event) {
val uri = CalendarContract.Events.CONTENT_URI
val uri = Events.CONTENT_URI
val contentUri = ContentUris.withAppendedId(uri, event.getCalDAVEventId())
try {
context.contentResolver.delete(contentUri, null, null)
@ -473,7 +438,7 @@ class CalDAVHelper(val context: Context) {
}
fun insertEventRepeatException(event: Event, occurrenceTS: Long) {
val uri = CalendarContract.Events.CONTENT_URI
val uri = Events.CONTENT_URI
val values = fillEventRepeatExceptionValues(event, occurrenceTS)
try {
context.contentResolver.insert(uri, values)
@ -485,13 +450,13 @@ class CalDAVHelper(val context: Context) {
private fun fillEventRepeatExceptionValues(event: Event, occurrenceTS: Long): ContentValues {
return ContentValues().apply {
put(CalendarContract.Events.CALENDAR_ID, event.getCalDAVCalendarId())
put(CalendarContract.Events.DTSTART, occurrenceTS)
put(CalendarContract.Events.DTEND, occurrenceTS + (event.endTS - event.startTS))
put(CalendarContract.Events.ORIGINAL_ID, event.getCalDAVEventId())
put(CalendarContract.Events.EVENT_TIMEZONE, TimeZone.getDefault().id.toString())
put(CalendarContract.Events.ORIGINAL_INSTANCE_TIME, occurrenceTS * 1000L)
put(CalendarContract.Events.EXDATE, Formatter.getDayCodeFromTS(occurrenceTS))
put(Events.CALENDAR_ID, event.getCalDAVCalendarId())
put(Events.DTSTART, occurrenceTS)
put(Events.DTEND, occurrenceTS + (event.endTS - event.startTS))
put(Events.ORIGINAL_ID, event.getCalDAVEventId())
put(Events.EVENT_TIMEZONE, TimeZone.getDefault().id.toString())
put(Events.ORIGINAL_INSTANCE_TIME, occurrenceTS * 1000L)
put(Events.EXDATE, Formatter.getDayCodeFromTS(occurrenceTS))
}
}
@ -502,51 +467,38 @@ class CalDAVHelper(val context: Context) {
Reminders.MINUTES,
Reminders.METHOD)
val selection = "${Reminders.EVENT_ID} = $eventId"
var cursor: Cursor? = null
try {
cursor = context.contentResolver.query(uri, projection, selection, null, null)
if (cursor?.moveToFirst() == true) {
do {
val minutes = cursor.getIntValue(Reminders.MINUTES)
val method = cursor.getIntValue(Reminders.METHOD)
if (method == Reminders.METHOD_ALERT || method == Reminders.METHOD_EMAIL) {
val type = if (method == Reminders.METHOD_EMAIL) REMINDER_EMAIL else REMINDER_NOTIFICATION
val reminder = Reminder(minutes, type)
reminders.add(reminder)
}
} while (cursor.moveToNext())
context.queryCursor(uri, projection, selection) { cursor ->
val minutes = cursor.getIntValue(Reminders.MINUTES)
val method = cursor.getIntValue(Reminders.METHOD)
if (method == Reminders.METHOD_ALERT || method == Reminders.METHOD_EMAIL) {
val type = if (method == Reminders.METHOD_EMAIL) REMINDER_EMAIL else REMINDER_NOTIFICATION
val reminder = Reminder(minutes, type)
reminders.add(reminder)
}
} finally {
cursor?.close()
}
return reminders.sortedBy { it.minutes }
}
private fun getCalDAVEventAttendees(eventId: Long): List<Attendee> {
val attendees = ArrayList<Attendee>()
val uri = CalendarContract.Attendees.CONTENT_URI
val uri = Attendees.CONTENT_URI
val projection = arrayOf(
CalendarContract.Attendees.ATTENDEE_NAME,
CalendarContract.Attendees.ATTENDEE_EMAIL,
CalendarContract.Attendees.ATTENDEE_STATUS,
CalendarContract.Attendees.ATTENDEE_RELATIONSHIP)
val selection = "${CalendarContract.Attendees.EVENT_ID} = $eventId"
var cursor: Cursor? = null
try {
cursor = context.contentResolver.query(uri, projection, selection, null, null)
if (cursor?.moveToFirst() == true) {
do {
val name = cursor.getStringValue(CalendarContract.Attendees.ATTENDEE_NAME) ?: ""
val email = cursor.getStringValue(CalendarContract.Attendees.ATTENDEE_EMAIL) ?: ""
val status = cursor.getIntValue(CalendarContract.Attendees.ATTENDEE_STATUS)
val relationship = cursor.getIntValue(CalendarContract.Attendees.ATTENDEE_RELATIONSHIP)
val attendee = Attendee(0, name, email, status, "", false, relationship)
attendees.add(attendee)
} while (cursor.moveToNext())
}
} finally {
cursor?.close()
Attendees.ATTENDEE_NAME,
Attendees.ATTENDEE_EMAIL,
Attendees.ATTENDEE_STATUS,
Attendees.ATTENDEE_RELATIONSHIP)
val selection = "${Attendees.EVENT_ID} = $eventId"
context.queryCursor(uri, projection, selection) { cursor ->
val name = cursor.getStringValue(Attendees.ATTENDEE_NAME) ?: ""
val email = cursor.getStringValue(Attendees.ATTENDEE_EMAIL) ?: ""
val status = cursor.getIntValue(Attendees.ATTENDEE_STATUS)
val relationship = cursor.getIntValue(Attendees.ATTENDEE_RELATIONSHIP)
val attendee = Attendee(0, name, email, status, "", false, relationship)
attendees.add(attendee)
}
return attendees
}

View file

@ -3,7 +3,6 @@ package com.simplemobiletools.calendar.pro.helpers
import android.app.Activity
import android.content.Context
import androidx.collection.LongSparseArray
import com.simplemobiletools.calendar.pro.R
import com.simplemobiletools.calendar.pro.extensions.*
import com.simplemobiletools.calendar.pro.models.Event
import com.simplemobiletools.calendar.pro.models.EventType
@ -203,6 +202,17 @@ class EventsHelper(val context: Context) {
val events = eventsDB.getEventsForSearch(searchQuery)
val displayEventTypes = config.displayEventTypes
val filteredEvents = events.filter { displayEventTypes.contains(it.eventType.toString()) }
val eventTypeColors = LongSparseArray<Int>()
eventTypesDB.getEventTypes().forEach {
eventTypeColors.put(it.id!!, it.color)
}
filteredEvents.forEach {
it.updateIsPastEvent()
it.color = eventTypeColors.get(it.eventType) ?: config.primaryColor
}
activity.runOnUiThread {
callback(text, filteredEvents)
}
@ -261,10 +271,9 @@ class EventsHelper(val context: Context) {
eventTypeColors.put(it.id!!, it.color)
}
val primaryColor = context.resources.getColor(R.color.color_primary)
events.forEach {
it.updateIsPastEvent()
it.color = eventTypeColors.get(it.eventType) ?: primaryColor
it.color = eventTypeColors.get(it.eventType) ?: config.primaryColor
}
callback(events)

View file

@ -1,6 +1,5 @@
package com.simplemobiletools.calendar.pro.helpers
import android.widget.Toast
import com.simplemobiletools.calendar.pro.R
import com.simplemobiletools.calendar.pro.activities.SimpleActivity
import com.simplemobiletools.calendar.pro.extensions.eventsDB
@ -110,7 +109,7 @@ class IcsImporter(val activity: SimpleActivity) {
curImportId = line.substring(UID.length).trim()
} else if (line.startsWith(RRULE)) {
curRrule = line.substring(RRULE.length)
// some RRULRs need to know the events start datetime. If it's yet unknown, postpone RRULE parsing
// some RRULEs need to know the events start datetime. If it's yet unknown, postpone RRULE parsing
if (curStart != -1L) {
parseRepeatRule()
}
@ -227,7 +226,7 @@ class IcsImporter(val activity: SimpleActivity) {
eventsHelper.insertEvents(eventsToInsert, true)
} catch (e: Exception) {
activity.showErrorToast(e, Toast.LENGTH_LONG)
activity.showErrorToast(e)
eventsFailed++
}
@ -248,7 +247,9 @@ class IcsImporter(val activity: SimpleActivity) {
return try {
if (fullString.startsWith(';')) {
val value = fullString.substring(fullString.lastIndexOf(':') + 1).replace(" ", "")
if (!value.contains("T")) {
if (value.isEmpty()) {
return 0
} else if (!value.contains("T")) {
curFlags = curFlags or FLAG_ALL_DAY
}
@ -257,7 +258,7 @@ class IcsImporter(val activity: SimpleActivity) {
Parser().parseDateTimeValue(fullString.substring(1))
}
} catch (e: Exception) {
activity.showErrorToast(e, Toast.LENGTH_LONG)
activity.showErrorToast(e)
eventsFailed++
-1
}

View file

@ -40,6 +40,12 @@ class Parser {
if (interval.areDigitsOnly() && interval.toInt() % 7 == 0) {
val dateTime = Formatter.getDateTimeFromTS(startTS)
repeatRule = Math.pow(2.0, (dateTime.dayOfWeek - 1).toDouble()).toInt()
} else if (fullString.contains("BYDAY")) {
// some services use weekly repetition for repeating on specific week days, some use daily
// make these produce the same result
// RRULE:FREQ=DAILY;BYDAY=MO,TU,WE,TH,FR
// RRULE:FREQ=WEEKLY;BYDAY=MO,TU,WE,TH,FR
repeatInterval = WEEK_SECONDS
}
}
} else if (key == COUNT) {

Binary file not shown.

Before

Width:  |  Height:  |  Size: 698 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 782 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

View file

@ -1,16 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@+id/attendee_circular_background">
<shape android:shape="oval">
<solid android:color="@color/color_primary"/>
</shape>
</item>
<item
android:bottom="@dimen/medium_margin"
android:drawable="@drawable/ic_person_vector"
android:left="@dimen/medium_margin"
android:right="@dimen/medium_margin"
android:top="@dimen/medium_margin"/>
</layer-list>

File diff suppressed because one or more lines are too long

View file

@ -1,9 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#FFFFFFFF"
android:pathData="M16,11c1.66,0 2.99,-1.34 2.99,-3S17.66,5 16,5c-1.66,0 -3,1.34 -3,3s1.34,3 3,3zM8,11c1.66,0 2.99,-1.34 2.99,-3S9.66,5 8,5C6.34,5 5,6.34 5,8s1.34,3 3,3zM8,13c-2.33,0 -7,1.17 -7,3.5L1,19h14v-2.5c0,-2.33 -4.67,-3.5 -7,-3.5zM16,13c-0.29,0 -0.62,0.02 -0.97,0.05 1.16,0.84 1.97,1.97 1.97,3.45L17,19h6v-2.5c0,-2.33 -4.67,-3.5 -7,-3.5z"/>
</vector>

View file

@ -408,7 +408,7 @@
android:layout_marginStart="@dimen/normal_margin"
android:layout_marginTop="@dimen/small_margin"
android:padding="@dimen/medium_margin"
android:src="@drawable/ic_group_vector"/>
android:src="@drawable/ic_people_vector"/>
<LinearLayout
android:id="@+id/event_attendees_holder"
@ -435,9 +435,10 @@
android:layout_below="@+id/event_attendees_divider"
android:layout_alignTop="@+id/event_caldav_calendar_holder"
android:layout_alignBottom="@+id/event_caldav_calendar_holder"
android:layout_alignEnd="@+id/event_time_image"
android:layout_marginStart="@dimen/normal_margin"
android:padding="@dimen/medium_margin"
android:src="@drawable/ic_calendar"
android:src="@drawable/ic_calendar_vector"
android:visibility="gone"/>
<RelativeLayout

View file

@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/calendar_coordinator"
android:layout_width="match_parent"
@ -14,7 +13,7 @@
<FrameLayout
android:id="@+id/fragments_holder"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
android:layout_height="match_parent" />
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
@ -27,7 +26,7 @@
android:contentDescription="@string/new_event"
android:src="@drawable/ic_plus_vector"
app:backgroundTint="@color/color_primary"
app:rippleColor="@color/pressed_item_foreground"/>
app:rippleColor="@color/pressed_item_foreground" />
<RelativeLayout
android:id="@+id/search_holder"
@ -43,11 +42,13 @@
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_marginTop="@dimen/activity_margin"
android:alpha="0.8"
android:gravity="center"
android:paddingStart="@dimen/activity_margin"
android:paddingEnd="@dimen/activity_margin"
android:text="@string/no_items_found"
android:textSize="@dimen/bigger_text_size"/>
android:textSize="@dimen/bigger_text_size"
android:textStyle="italic" />
<com.simplemobiletools.commons.views.MyTextView
android:id="@+id/search_placeholder_2"
@ -55,13 +56,15 @@
android:layout_height="wrap_content"
android:layout_below="@+id/search_placeholder"
android:layout_centerHorizontal="true"
android:alpha="0.8"
android:gravity="center"
android:paddingBottom="@dimen/medium_margin"
android:paddingStart="@dimen/activity_margin"
android:paddingEnd="@dimen/activity_margin"
android:paddingTop="@dimen/medium_margin"
android:paddingEnd="@dimen/activity_margin"
android:paddingBottom="@dimen/medium_margin"
android:text="@string/type_2_characters"
android:textSize="@dimen/bigger_text_size"/>
android:textSize="@dimen/bigger_text_size"
android:textStyle="italic" />
<com.simplemobiletools.commons.views.MyRecyclerView
android:id="@+id/search_results_list"
@ -69,7 +72,7 @@
android:layout_height="match_parent"
android:clipToPadding="false"
android:scrollbars="vertical"
app:layoutManager="com.simplemobiletools.commons.views.MyLinearLayoutManager"/>
app:layoutManager="com.simplemobiletools.commons.views.MyLinearLayoutManager" />
</RelativeLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>

View file

@ -120,13 +120,6 @@
</RelativeLayout>
<View
android:id="@+id/reminders_divider"
android:layout_width="match_parent"
android:layout_height="1px"
android:background="@color/divider_grey"
android:importantForAccessibility="no"/>
<com.simplemobiletools.commons.views.MyTextView
android:id="@+id/reminders_label"
android:layout_width="wrap_content"
@ -298,13 +291,6 @@
</RelativeLayout>
<View
android:id="@+id/caldav_divider"
android:layout_width="match_parent"
android:layout_height="1px"
android:background="@color/divider_grey"
android:importantForAccessibility="no"/>
<com.simplemobiletools.commons.views.MyTextView
android:id="@+id/caldav_label"
android:layout_width="wrap_content"
@ -381,13 +367,6 @@
</RelativeLayout>
<View
android:id="@+id/new_events_divider"
android:layout_width="match_parent"
android:layout_height="1px"
android:background="@color/divider_grey"
android:importantForAccessibility="no"/>
<com.simplemobiletools.commons.views.MyTextView
android:id="@+id/new_events_label"
android:layout_width="wrap_content"
@ -612,13 +591,6 @@
</RelativeLayout>
<View
android:id="@+id/weekly_view_divider"
android:layout_width="match_parent"
android:layout_height="1px"
android:background="@color/divider_grey"
android:importantForAccessibility="no"/>
<com.simplemobiletools.commons.views.MyTextView
android:id="@+id/weekly_view_label"
android:layout_width="wrap_content"
@ -661,13 +633,6 @@
</RelativeLayout>
<View
android:id="@+id/monthly_view_divider"
android:layout_width="match_parent"
android:layout_height="1px"
android:background="@color/divider_grey"
android:importantForAccessibility="no"/>
<com.simplemobiletools.commons.views.MyTextView
android:id="@+id/monthly_view_label"
android:layout_width="wrap_content"
@ -721,13 +686,6 @@
</RelativeLayout>
<View
android:id="@+id/events_list_view_divider"
android:layout_width="match_parent"
android:layout_height="1px"
android:background="@color/divider_grey"
android:importantForAccessibility="no"/>
<com.simplemobiletools.commons.views.MyTextView
android:id="@+id/simple_event_list_label"
android:layout_width="wrap_content"
@ -791,13 +749,6 @@
</RelativeLayout>
<View
android:id="@+id/widgets_divider"
android:layout_width="match_parent"
android:layout_height="1px"
android:background="@color/divider_grey"
android:importantForAccessibility="no"/>
<com.simplemobiletools.commons.views.MyTextView
android:id="@+id/widgets_label"
android:layout_width="wrap_content"
@ -890,13 +841,6 @@
</RelativeLayout>
<View
android:id="@+id/events_divider"
android:layout_width="match_parent"
android:layout_height="1px"
android:background="@color/divider_grey"
android:importantForAccessibility="no"/>
<com.simplemobiletools.commons.views.MyTextView
android:id="@+id/events_label"
android:layout_width="wrap_content"
@ -971,13 +915,6 @@
</RelativeLayout>
<View
android:id="@+id/migrating_divider"
android:layout_width="match_parent"
android:layout_height="1px"
android:background="@color/divider_grey"
android:importantForAccessibility="no"/>
<com.simplemobiletools.commons.views.MyTextView
android:id="@+id/migrating_label"
android:layout_width="wrap_content"

View file

@ -1,16 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<com.simplemobiletools.commons.views.MyTextView
xmlns:android="http://schemas.android.com/apk/res/android"
<com.simplemobiletools.commons.views.MyTextView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/calendar_item_account"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/activity_margin"
android:layout_marginLeft="@dimen/big_margin"
android:layout_marginStart="@dimen/big_margin"
android:layout_marginTop="@dimen/activity_margin"
android:alpha="0.6"
android:layout_marginBottom="@dimen/activity_margin"
android:alpha="0.8"
android:textAllCaps="true"
android:textColor="@color/divider_grey"
android:textSize="@dimen/small_text_size"
tools:text="Account"/>
android:textSize="@dimen/normal_text_size"
tools:text="Account" />

View file

@ -1,16 +1,29 @@
<?xml version="1.0" encoding="utf-8"?>
<ScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/dialog_select_calendars_scrollview"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="@dimen/activity_margin">
<LinearLayout
android:id="@+id/dialog_select_calendars_holder"
<RelativeLayout
android:id="@+id/dialog_select_calendars_wrapper"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
android:layout_height="wrap_content">
</LinearLayout>
<com.simplemobiletools.commons.views.MyTextView
android:id="@+id/dialog_select_calendars_placeholder"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:alpha="0.8"
android:text="@string/no_synchronized_calendars"
android:textStyle="italic" />
<LinearLayout
android:id="@+id/dialog_select_calendars_holder"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" />
</RelativeLayout>
</ScrollView>

View file

@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/calendar_events_list_holder"
android:layout_width="match_parent"
@ -15,7 +14,7 @@
android:paddingTop="@dimen/medium_margin"
android:scrollbars="vertical"
android:visibility="gone"
app:layoutManager="com.simplemobiletools.commons.views.MyLinearLayoutManager"/>
app:layoutManager="com.simplemobiletools.commons.views.MyLinearLayoutManager" />
<com.simplemobiletools.commons.views.MyTextView
android:id="@+id/calendar_empty_list_placeholder"
@ -23,24 +22,26 @@
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_marginTop="@dimen/activity_margin"
android:alpha="0.8"
android:gravity="center"
android:paddingStart="@dimen/activity_margin"
android:paddingEnd="@dimen/activity_margin"
android:text="@string/no_upcoming_events"
android:textSize="@dimen/bigger_text_size"
android:visibility="gone"/>
android:textStyle="italic"
android:visibility="gone" />
<com.simplemobiletools.commons.views.MyTextView
android:id="@+id/calendar_empty_list_placeholder_2"
android:layout_width="match_parent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/calendar_empty_list_placeholder"
android:background="?attr/selectableItemBackground"
android:layout_centerHorizontal="true"
android:gravity="center"
android:paddingBottom="@dimen/medium_margin"
android:paddingTop="@dimen/medium_margin"
android:padding="@dimen/activity_margin"
android:text="@string/create_new_event"
android:textSize="@dimen/bigger_text_size"
android:visibility="gone"/>
android:visibility="gone" />
</RelativeLayout>

View file

@ -27,6 +27,7 @@
android:lines="1"
android:maxLines="1"
android:paddingStart="@dimen/medium_margin"
android:paddingEnd="@dimen/medium_margin"
android:singleLine="true"
android:textSize="@dimen/bigger_text_size"
app:layout_constraintBottom_toBottomOf="parent"

View file

@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/item_autocomplete_holder"
@ -17,7 +16,7 @@
android:layout_height="@dimen/avatar_size"
android:layout_margin="@dimen/tiny_margin"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/item_autocomplete_name"
@ -26,6 +25,7 @@
android:lines="1"
android:maxLines="1"
android:paddingStart="@dimen/medium_margin"
android:paddingEnd="@dimen/medium_margin"
android:singleLine="true"
android:textSize="@dimen/bigger_text_size"
app:layout_constraintBottom_toTopOf="@+id/item_autocomplete_email"
@ -33,7 +33,7 @@
app:layout_constraintStart_toEndOf="@+id/item_autocomplete_image"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_chainStyle="packed"
tools:text="Simple Mobile"/>
tools:text="Simple Mobile" />
<TextView
android:id="@+id/item_autocomplete_email"
@ -45,12 +45,13 @@
android:lines="1"
android:maxLines="1"
android:paddingStart="@dimen/medium_margin"
android:paddingEnd="@dimen/medium_margin"
android:singleLine="true"
android:textSize="@dimen/normal_text_size"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/item_autocomplete_image"
app:layout_constraintTop_toBottomOf="@+id/item_autocomplete_name"
tools:text="hello@simplemobiletools.com"/>
tools:text="hello@simplemobiletools.com" />
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -207,6 +207,7 @@
<string name="select_a_different_caldav_color">اختر لون مختلف (يمكن تطبيقه محليا فقط)</string>
<string name="insufficient_permissions">لا يمكنك التعديل في هذا التقويم</string>
<string name="caldav_event_not_found">مناسبة غير موجودة. من فضلك فَعّل مُزامنة CalDAV للحصول على التقويم المناسب من الإعدادات.</string>
<string name="no_synchronized_calendars">No synchronizable calendars have been found</string>
<!-- alternative versions for some languages, use the same translations if you are not sure what this means -->
<!-- used in repetition, like "Every last Sunday" -->

View file

@ -207,6 +207,7 @@
<string name="select_a_different_caldav_color">Select a different color (might be applied locally only)</string>
<string name="insufficient_permissions">You are not allowed to write in the selected calendar</string>
<string name="caldav_event_not_found">Event not found. Please enable CalDAV sync for the appropriate calendar in the app settings.</string>
<string name="no_synchronized_calendars">No synchronizable calendars have been found</string>
<!-- alternative versions for some languages, use the same translations if you are not sure what this means -->
<!-- used in repetition, like "Every last Sunday" -->

View file

@ -211,6 +211,7 @@
<string name="select_a_different_caldav_color">একটি আলাদা রঙ সিলেক্ট করুন(কেবল স্থানীয়ভাবে প্রয়োগ করা যেতে পারে)</string>
<string name="insufficient_permissions">আপনার সিলেক্টেড ক্যালেন্ডারে লেখার অনুমতি নেই</string>
<string name="caldav_event_not_found">ইভেন্ট পাওয়া যায় নি। অ্যাপ্লিকেশন সেটিংসে উপযুক্ত ক্যালেন্ডারের জন্য দয়া করে CalDAV সিঙ্ক সক্ষম করুন।</string>
<string name="no_synchronized_calendars">No synchronizable calendars have been found</string>
<!-- alternative versions for some languages, use the same translations if you are not sure what this means -->
<!-- used in repetition, like "Every last Sunday" -->

View file

@ -207,6 +207,7 @@
<string name="select_a_different_caldav_color">Select a different color (might be applied locally only)</string>
<string name="insufficient_permissions">You are not allowed to write in the selected calendar</string>
<string name="caldav_event_not_found">Event not found. Please enable CalDAV sync for the appropriate calendar in the app settings.</string>
<string name="no_synchronized_calendars">No synchronizable calendars have been found</string>
<!-- alternative versions for some languages, use the same translations if you are not sure what this means -->
<!-- used in repetition, like "Every last Sunday" -->

View file

@ -207,6 +207,7 @@
<string name="select_a_different_caldav_color">Zvolit jinou barvu (možná bude nastavena pouze lokálně)</string>
<string name="insufficient_permissions">Nemáte oprávnění pro zápis do zvoleného kalendáře</string>
<string name="caldav_event_not_found">Událost nenalezena. Prosím, povolte CalDAV synchronizaci příslušného kalendáře v nastavení aplikace.</string>
<string name="no_synchronized_calendars">No synchronizable calendars have been found</string>
<!-- alternative versions for some languages, use the same translations if you are not sure what this means -->
<!-- used in repetition, like "Every last Sunday" -->

View file

@ -207,6 +207,7 @@
<string name="select_a_different_caldav_color">Vælg en anden farve (den kan være tilføjet lokalt)</string>
<string name="insufficient_permissions">Du har ikke tilladelse til at skrive i den valgte kalender</string>
<string name="caldav_event_not_found">Begivenheden blev ikke fundet. Aktiver CalDAV-synkronisering med den relevante kalender i app-indstillingerne.</string>
<string name="no_synchronized_calendars">No synchronizable calendars have been found</string>
<!-- alternative versions for some languages, use the same translations if you are not sure what this means -->
<!-- used in repetition, like "Every last Sunday" -->

View file

@ -207,6 +207,7 @@
<string name="select_a_different_caldav_color">Wähle eine andere Farbe (wird möglicherweise nur lokal angewendet)</string>
<string name="insufficient_permissions">Dir fehlt die Berechtigung zum Ändern des gewählten Kalenders</string>
<string name="caldav_event_not_found">Der Termin wurde nicht gefunden. Bitte aktiviere die Synchronisierung für den Kalender in den Einstellungen.</string>
<string name="no_synchronized_calendars">No synchronizable calendars have been found</string>
<!-- alternative versions for some languages, use the same translations if you are not sure what this means -->
<!-- used in repetition, like "Every last Sunday" -->

View file

@ -207,6 +207,7 @@
<string name="select_a_different_caldav_color">Επιλέξτε διαφορετικό χρώμα (μπορεί να εφαρμοστεί μόνο τοπικά)</string>
<string name="insufficient_permissions">Δεν επιτρέπεται η εγγραφή στο επιλεγμένο ημερολόγιο</string>
<string name="caldav_event_not_found">Δεν βρέθηκε Εκδήλωση. Παρακαλώ ενεργοποιήστε το συγχρονισμό CalDAV του Ημερολογίου από τις ρυθμίσεις της εφαρμογής.</string>
<string name="no_synchronized_calendars">Δεν μπόρεσαν να βρεθούν συγχρονισμένα Ημερολόγια</string>
<!-- alternative versions for some languages, use the same translations if you are not sure what this means -->
<!-- used in repetition, like "Every last Sunday" -->

View file

@ -207,6 +207,7 @@
<string name="select_a_different_caldav_color">Seleccionar un color diferente (localmente)</string>
<string name="insufficient_permissions">No tiene permiso para modificar el calendario</string>
<string name="caldav_event_not_found">Evento no encontrado. Habilite la sincronización de CalDAV para los calendarios en la configuración.</string>
<string name="no_synchronized_calendars">No synchronizable calendars have been found</string>
<!-- alternative versions for some languages, use the same translations if you are not sure what this means -->
<!-- used in repetition, like "Every last Sunday" -->

View file

@ -208,6 +208,7 @@
<string name="select_a_different_caldav_color">Sélectionnez une couleur différente (peut être appliqué localement uniquement)</string>
<string name="insufficient_permissions">Vous n\êtes pas autorisé à écrire dans l\agenda sélectionné</string>
<string name="caldav_event_not_found">Événement introuvable. Veuillez activer la synchronisation CalDAV pour le calendrier approprié dans les paramètres de lapplication.</string>
<string name="no_synchronized_calendars">No synchronizable calendars have been found</string>
<!-- alternative versions for some languages, use the same translations if you are not sure what this means -->
<!-- used in repetition, like "Every last Sunday" -->

View file

@ -207,6 +207,7 @@
<string name="select_a_different_caldav_color">Escolle un color diferente (pode que só se aplique localmente)</string>
<string name="insufficient_permissions">Non tes permiso para escribir no calendario seleccionado</string>
<string name="caldav_event_not_found">Evento non atopado. Activa a sincronización de CalDAV para o calendario apropiado nos axustes da aplicación.</string>
<string name="no_synchronized_calendars">No synchronizable calendars have been found</string>
<!-- alternative versions for some languages, use the same translations if you are not sure what this means -->
<!-- used in repetition, like "Every last Sunday" -->

View file

@ -207,6 +207,7 @@
<string name="select_a_different_caldav_color">בחירת צבע שונה (ייתכן שיישמר מקומית בלבד)</string>
<string name="insufficient_permissions">לא קיימת הרשאת כתיבה ליומנים הנבחרים</string>
<string name="caldav_event_not_found">Event not found. Please enable CalDAV sync for the appropriate calendar in the app settings.</string>
<string name="no_synchronized_calendars">No synchronizable calendars have been found</string>
<!-- alternative versions for some languages, use the same translations if you are not sure what this means -->
<!-- used in repetition, like "Every last Sunday" -->

View file

@ -207,6 +207,7 @@
<string name="select_a_different_caldav_color">Select a different color (might be applied locally only)</string>
<string name="insufficient_permissions">You are not allowed to write in the selected calendar</string>
<string name="caldav_event_not_found">Event not found. Please enable CalDAV sync for the appropriate calendar in the app settings.</string>
<string name="no_synchronized_calendars">No synchronizable calendars have been found</string>
<!-- alternative versions for some languages, use the same translations if you are not sure what this means -->
<!-- used in repetition, like "Every last Sunday" -->

View file

@ -207,6 +207,7 @@
<string name="select_a_different_caldav_color">Odaberite drugu boju (može se primijeniti samo lokalno)</string>
<string name="insufficient_permissions">Nije vam dopušteno pisati u odabranom kalendaru</string>
<string name="caldav_event_not_found">Event not found. Please enable CalDAV sync for the appropriate calendar in the app settings.</string>
<string name="no_synchronized_calendars">No synchronizable calendars have been found</string>
<!-- alternative versions for some languages, use the same translations if you are not sure what this means -->
<!-- used in repetition, like "Every last Sunday" -->

View file

@ -207,6 +207,7 @@
<string name="select_a_different_caldav_color">Select a different color (might be applied locally only)</string>
<string name="insufficient_permissions">You are not allowed to write in the selected calendar</string>
<string name="caldav_event_not_found">Event not found. Please enable CalDAV sync for the appropriate calendar in the app settings.</string>
<string name="no_synchronized_calendars">No synchronizable calendars have been found</string>
<!-- alternative versions for some languages, use the same translations if you are not sure what this means -->
<!-- used in repetition, like "Every last Sunday" -->

View file

@ -207,6 +207,7 @@
<string name="select_a_different_caldav_color">Pilih warna yang berbeda (mungkin hanya diterapkan secara lokal)</string>
<string name="insufficient_permissions">Anda tidak diizinkan untuk membuat/mengubah acara pada kalender yang dipilih</string>
<string name="caldav_event_not_found">Acara tidak ditemukan. Silakan aktifkan sinkronisasi CalDAV untuk kalender terkait di dalam pengaturan aplikasi.</string>
<string name="no_synchronized_calendars">No synchronizable calendars have been found</string>
<!-- alternative versions for some languages, use the same translations if you are not sure what this means -->
<!-- used in repetition, like "Every last Sunday" -->

View file

@ -207,6 +207,7 @@
<string name="select_a_different_caldav_color">Pilih warna yang berbeda (mungkin hanya diterapkan secara lokal)</string>
<string name="insufficient_permissions">Anda tidak diizinkan untuk membuat/mengubah acara pada kalender yang dipilih</string>
<string name="caldav_event_not_found">Acara tidak ditemukan. Silakan aktifkan sinkronisasi CalDAV untuk kalender terkait di dalam pengaturan aplikasi.</string>
<string name="no_synchronized_calendars">No synchronizable calendars have been found</string>
<!-- alternative versions for some languages, use the same translations if you are not sure what this means -->
<!-- used in repetition, like "Every last Sunday" -->

View file

@ -207,6 +207,7 @@
<string name="select_a_different_caldav_color">Seleziona un colore differente (potrebbe essere applicato solamente in locale)</string>
<string name="insufficient_permissions">Non si hanno i permessi per scrivere nel calendario selezionato</string>
<string name="caldav_event_not_found">Event not found. Please enable CalDAV sync for the appropriate calendar in the app settings.</string>
<string name="no_synchronized_calendars">No synchronizable calendars have been found</string>
<!-- alternative versions for some languages, use the same translations if you are not sure what this means -->
<!-- used in repetition, like "Every last Sunday" -->

View file

@ -207,6 +207,7 @@
<string name="select_a_different_caldav_color">בחירת צבע שונה (ייתכן שיישמר מקומית בלבד)</string>
<string name="insufficient_permissions">לא קיימת הרשאת כתיבה ליומנים הנבחרים</string>
<string name="caldav_event_not_found">Event not found. Please enable CalDAV sync for the appropriate calendar in the app settings.</string>
<string name="no_synchronized_calendars">No synchronizable calendars have been found</string>
<!-- alternative versions for some languages, use the same translations if you are not sure what this means -->
<!-- used in repetition, like "Every last Sunday" -->

View file

@ -207,6 +207,7 @@
<string name="select_a_different_caldav_color">Select a different color (might be applied locally only)</string>
<string name="insufficient_permissions">You are not allowed to write in the selected calendar</string>
<string name="caldav_event_not_found">Event not found. Please enable CalDAV sync for the appropriate calendar in the app settings.</string>
<string name="no_synchronized_calendars">No synchronizable calendars have been found</string>
<!-- alternative versions for some languages, use the same translations if you are not sure what this means -->
<!-- used in repetition, like "Every last Sunday" -->

View file

@ -207,6 +207,7 @@
<string name="select_a_different_caldav_color">다른 색상을 선택해주세요(로컬에만 적용 가능)</string>
<string name="insufficient_permissions">선택한 캘린더에 작성할 수 없습니다.</string>
<string name="caldav_event_not_found">일정을 찾을 수 없습니다. 앱 설정에서 해당 캘린더에 대해 CalDAV 동기화를 활성화해주세요.</string>
<string name="no_synchronized_calendars">No synchronizable calendars have been found</string>
<!-- alternative versions for some languages, use the same translations if you are not sure what this means -->
<!-- used in repetition, like "Every last Sunday" -->

View file

@ -207,6 +207,7 @@
<string name="select_a_different_caldav_color">Select a different color (might be applied locally only)</string>
<string name="insufficient_permissions">You are not allowed to write in the selected calendar</string>
<string name="caldav_event_not_found">Event not found. Please enable CalDAV sync for the appropriate calendar in the app settings.</string>
<string name="no_synchronized_calendars">No synchronizable calendars have been found</string>
<!-- alternative versions for some languages, use the same translations if you are not sure what this means -->
<!-- used in repetition, like "Every last Sunday" -->

View file

@ -207,6 +207,7 @@
<string name="select_a_different_caldav_color">Izvēlieties citu krāsu (var tikt izmantota tikai lokāli)</string>
<string name="insufficient_permissions">Jums izvēlētajā kalendārā rakstīt nav ļauts</string>
<string name="caldav_event_not_found">Notikums nav atrasts. Lūdzu, lietotnes iestatījumos iespējojiet CalDAV sinhronizāciju attiecīgajam kalendāram.</string>
<string name="no_synchronized_calendars">No synchronizable calendars have been found</string>
<!-- alternative versions for some languages, use the same translations if you are not sure what this means -->
<!-- used in repetition, like "Every last Sunday" -->

View file

@ -207,6 +207,7 @@
<string name="select_a_different_caldav_color">Select a different color (might be applied locally only)</string>
<string name="insufficient_permissions">You are not allowed to write in the selected calendar</string>
<string name="caldav_event_not_found">Event not found. Please enable CalDAV sync for the appropriate calendar in the app settings.</string>
<string name="no_synchronized_calendars">No synchronizable calendars have been found</string>
<!-- alternative versions for some languages, use the same translations if you are not sure what this means -->
<!-- used in repetition, like "Every last Sunday" -->

View file

@ -207,6 +207,7 @@
<string name="select_a_different_caldav_color">Kies een andere kleur (mogelijk alleen voor dit apparaat)</string>
<string name="insufficient_permissions">Kan geen wijzigingen aanbrengen in deze agenda</string>
<string name="caldav_event_not_found">Afspraak niet gevonden. Schakel via Instellingen de CalDAV-synchronisatie in voor deze agenda.</string>
<string name="no_synchronized_calendars">No synchronizable calendars have been found</string>
<!-- alternative versions for some languages, use the same translations if you are not sure what this means -->
<!-- used in repetition, like "Every last Sunday" -->

View file

@ -207,6 +207,7 @@
<string name="select_a_different_caldav_color">Select a different color (might be applied locally only)</string>
<string name="insufficient_permissions">You are not allowed to write in the selected calendar</string>
<string name="caldav_event_not_found">Event not found. Please enable CalDAV sync for the appropriate calendar in the app settings.</string>
<string name="no_synchronized_calendars">No synchronizable calendars have been found</string>
<!-- alternative versions for some languages, use the same translations if you are not sure what this means -->
<!-- used in repetition, like "Every last Sunday" -->

View file

@ -207,6 +207,7 @@
<string name="select_a_different_caldav_color">Select a different color (might be applied locally only)</string>
<string name="insufficient_permissions">You are not allowed to write in the selected calendar</string>
<string name="caldav_event_not_found">Event not found. Please enable CalDAV sync for the appropriate calendar in the app settings.</string>
<string name="no_synchronized_calendars">No synchronizable calendars have been found</string>
<!-- alternative versions for some languages, use the same translations if you are not sure what this means -->
<!-- used in repetition, like "Every last Sunday" -->

View file

@ -207,6 +207,7 @@
<string name="select_a_different_caldav_color">Select a different color (might be applied locally only)</string>
<string name="insufficient_permissions">You are not allowed to write in the selected calendar</string>
<string name="caldav_event_not_found">Event not found. Please enable CalDAV sync for the appropriate calendar in the app settings.</string>
<string name="no_synchronized_calendars">No synchronizable calendars have been found</string>
<!-- alternative versions for some languages, use the same translations if you are not sure what this means -->
<!-- used in repetition, like "Every last Sunday" -->

View file

@ -207,6 +207,7 @@
<string name="select_a_different_caldav_color">Selecione uma cor diferente (pode ser aplicada apenas localmente)</string>
<string name="insufficient_permissions">Não tem permissão para escrever no calendário selecionado</string>
<string name="caldav_event_not_found">Evento não encontrado. Ative a sincronização CalDAV para os calendários apropriados nas definições.</string>
<string name="no_synchronized_calendars">No synchronizable calendars have been found</string>
<!-- alternative versions for some languages, use the same translations if you are not sure what this means -->
<!-- used in repetition, like "Every last Sunday" -->

View file

@ -207,6 +207,7 @@
<string name="select_a_different_caldav_color">Выберите другой цвет (применяется локально)</string>
<string name="insufficient_permissions">Запись в выбранный календарь запрещена</string>
<string name="caldav_event_not_found">Событие не найдено. Пожалуйста, включите CalDAV-синхронизацию для соответствующего календаря в настройках приложения.</string>
<string name="no_synchronized_calendars">No synchronizable calendars have been found</string>
<!-- alternative versions for some languages, use the same translations if you are not sure what this means -->
<!-- used in repetition, like "Every last Sunday" -->

View file

@ -207,6 +207,7 @@
<string name="select_a_different_caldav_color">Zvoliť inú farbu (možno bude aplikovaná iba lokálne)</string>
<string name="insufficient_permissions">Nemáte dostatočné oprávnenie na písanie do zvoleného kalendára</string>
<string name="caldav_event_not_found">Udalosť nebola nájdená. Prosím povoľte CalDAV synchronizáciu príslušného kalendára v nastaveniach aplikácie.</string>
<string name="no_synchronized_calendars">Nenašli sa žiadne synchronizovateľné kalendáre</string>
<!-- alternative versions for some languages, use the same translations if you are not sure what this means -->
<!-- used in repetition, like "Every last Sunday" -->

View file

@ -207,6 +207,7 @@
<string name="select_a_different_caldav_color">Välj en annan färg (tillämpas kanske bara lokalt)</string>
<string name="insufficient_permissions">Du har inte behörighet att redigera den valda kalendern</string>
<string name="caldav_event_not_found">Event not found. Please enable CalDAV sync for the appropriate calendar in the app settings.</string>
<string name="no_synchronized_calendars">No synchronizable calendars have been found</string>
<!-- alternative versions for some languages, use the same translations if you are not sure what this means -->
<!-- used in repetition, like "Every last Sunday" -->

View file

@ -207,6 +207,7 @@
<string name="select_a_different_caldav_color">Farklı bir renk seçin (yalnızca yerel olarak uygulanabilir)</string>
<string name="insufficient_permissions">Seçili takvime yazmanıza izin verilmiyor</string>
<string name="caldav_event_not_found">Etkinlik bulunamadı. Lütfen uygulama ayarlarından uygun takvim için CalDAV senkronizasyonunu etkinleştirin.</string>
<string name="no_synchronized_calendars">No synchronizable calendars have been found</string>
<!-- alternative versions for some languages, use the same translations if you are not sure what this means -->
<!-- used in repetition, like "Every last Sunday" -->

View file

@ -207,6 +207,7 @@
<string name="select_a_different_caldav_color">Обрати інший колір (можна застосувати лише локально)</string>
<string name="insufficient_permissions">Ви не можете редагувати обраний календар</string>
<string name="caldav_event_not_found">Event not found. Please enable CalDAV sync for the appropriate calendar in the app settings.</string>
<string name="no_synchronized_calendars">No synchronizable calendars have been found</string>
<!-- alternative versions for some languages, use the same translations if you are not sure what this means -->
<!-- used in repetition, like "Every last Sunday" -->

View file

@ -207,6 +207,7 @@
    <string name="select_a_different_caldav_color">选择不同的颜色 (只能被添加于本机端)</string>
    <string name="insufficient_permissions">你不被允许对选择的行事历写入</string>
<string name="caldav_event_not_found">Event not found. Please enable CalDAV sync for the appropriate calendar in the app settings.</string>
<string name="no_synchronized_calendars">No synchronizable calendars have been found</string>
    <!-- alternative versions for some languages, use the same translations if you are not sure what this means -->
    <!-- used in repetition, like "Every last Sunday" -->

View file

@ -207,6 +207,7 @@
<string name="select_a_different_caldav_color">選擇不同的顏色 (只能被添加於本機端)</string>
<string name="insufficient_permissions">你不被允許對選擇的行事曆寫入</string>
<string name="caldav_event_not_found">Event not found. Please enable CalDAV sync for the appropriate calendar in the app settings.</string>
<string name="no_synchronized_calendars">No synchronizable calendars have been found</string>
<!-- alternative versions for some languages, use the same translations if you are not sure what this means -->
<!-- used in repetition, like "Every last Sunday" -->

View file

@ -207,6 +207,7 @@
<string name="select_a_different_caldav_color">選擇不同的顏色 (只能被添加於本機端)</string>
<string name="insufficient_permissions">你不被允許對選擇的行事曆寫入</string>
<string name="caldav_event_not_found">未發現活動。請在程式設定中為合適的行事曆啟用CalDAV同步。</string>
<string name="no_synchronized_calendars">No synchronizable calendars have been found</string>
<!-- alternative versions for some languages, use the same translations if you are not sure what this means -->
<!-- used in repetition, like "Every last Sunday" -->

View file

@ -207,6 +207,7 @@
<string name="select_a_different_caldav_color">Select a different color (might be applied locally only)</string>
<string name="insufficient_permissions">You are not allowed to write in the selected calendar</string>
<string name="caldav_event_not_found">Event not found. Please enable CalDAV sync for the appropriate calendar in the app settings.</string>
<string name="no_synchronized_calendars">No synchronizable calendars have been found</string>
<!-- alternative versions for some languages, use the same translations if you are not sure what this means -->
<!-- used in repetition, like "Every last Sunday" -->

View file

@ -1,7 +1,7 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
ext.kotlin_version = '1.3.70'
ext.kotlin_version = '1.3.72'
repositories {
google()
@ -10,7 +10,7 @@ buildscript {
}
dependencies {
classpath 'com.android.tools.build:gradle:3.6.1'
classpath 'com.android.tools.build:gradle:3.6.2'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath "de.timfreiheit.resourceplaceholders:placeholders:0.3"

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB