diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9a1108280..f584eb633 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,27 @@
Changelog
==========
+Version 6.3.2 *(2019-03-07)*
+----------------------------
+
+ * Added a "Go to date" to most views for easy jumping between dates
+ * Added an app shortcut for creating new events quickly, from Android 7.1+
+
+Version 6.3.1 *(2019-02-23)*
+----------------------------
+
+ * Allow adding event reminders to birthdays/anniversaries
+ * Filled content description of some views to improve the UX of visually impaired people
+ * A few more stability and UX improvements here and there
+
+Version 6.3.0 *(2019-02-14)*
+----------------------------
+
+ * Allow setting default start time/duration/event type for new events
+ * Allow exporting/importing settings
+ * Fixed a glitch with repeating events older than from year 2001
+ * Fixed some glitches related to overlapping events on the monthly and weekly view
+
Version 6.2.2 *(2019-01-25)*
----------------------------
diff --git a/README.md b/README.md
index b1003e56a..decb2dc13 100644
--- a/README.md
+++ b/README.md
@@ -15,8 +15,8 @@ The Contacts permission is used only at importing contact birthdays and annivers
This app is just one piece of a bigger series of apps. You can find the rest of them at https://www.simplemobiletools.com
-
-
+
+

diff --git a/app/build.gradle b/app/build.gradle
index 467093211..58c7e298c 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -2,6 +2,7 @@ apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-kapt'
+apply plugin: 'de.timfreiheit.resourceplaceholders'
def keystorePropertiesFile = rootProject.file("keystore.properties")
def keystoreProperties = new Properties()
@@ -15,8 +16,8 @@ android {
applicationId "com.simplemobiletools.calendar.pro"
minSdkVersion 21
targetSdkVersion 28
- versionCode 142
- versionName "6.2.2"
+ versionCode 145
+ versionName "6.3.2"
multiDexEnabled true
setProperty("archivesBaseName", "calendar")
}
@@ -49,12 +50,17 @@ android {
checkReleaseBuilds false
abortOnError false
}
+
+ resourcePlaceholders {
+ files = ['xml/shortcuts.xml']
+ }
}
dependencies {
- implementation 'com.simplemobiletools:commons:5.7.7'
+ implementation 'com.simplemobiletools:commons:5.10.10'
implementation 'joda-time:joda-time:2.10.1'
implementation 'androidx.multidex:multidex:2.0.1'
+ implementation 'androidx.constraintlayout:constraintlayout:2.0.0-alpha3'
kapt 'androidx.room:room-compiler:2.0.0'
implementation 'androidx.room:room-runtime:2.0.0'
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index d2c6f9b07..00a9a0f36 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -16,14 +16,18 @@
android:name="android.permission.USE_FINGERPRINT"
tools:node="remove"/>
+
+
+ android:supportsRtl="true"
+ android:theme="@style/AppTheme">
()
+ private var mAttendeeViews = ArrayList()
+ private var mAvailableContacts = ArrayList()
private lateinit var mEventStartDateTime: DateTime
private lateinit var mEventEndDateTime: DateTime
@@ -65,6 +87,7 @@ class EventActivity : SimpleActivity() {
supportActionBar?.setHomeAsUpIndicator(R.drawable.ic_cross)
val intent = intent ?: return
mDialogTheme = getDialogTheme()
+ mWasContactsPermissionChecked = hasPermission(PERMISSION_READ_CONTACTS)
val eventId = intent.getLongExtra(EVENT_ID, 0L)
Thread {
@@ -78,6 +101,7 @@ class EventActivity : SimpleActivity() {
runOnUiThread {
gotEvent(savedInstanceState, localEventType, event)
}
+ fillAvailableContacts()
}.start()
}
@@ -117,6 +141,7 @@ class EventActivity : SimpleActivity() {
updateTexts()
updateEventType()
updateCalDAVCalendar()
+ updateAttendees()
}
event_show_on_map.setOnClickListener { showOnMap() }
@@ -146,19 +171,41 @@ class EventActivity : SimpleActivity() {
event_reminder_2.setOnClickListener { showReminder2Dialog() }
event_reminder_3.setOnClickListener { showReminder3Dialog() }
- event_type_holder.setOnClickListener { showEventTypeDialog() }
+ event_reminder_1_type.setOnClickListener {
+ showReminderTypePicker(mReminder1Type) {
+ mReminder1Type = it
+ updateReminderTypeImage(event_reminder_1_type, Reminder(mReminder1Minutes, mReminder1Type))
+ }
+ }
- if (mEvent.flags and FLAG_ALL_DAY != 0)
- event_all_day.toggle()
+ event_reminder_2_type.setOnClickListener {
+ showReminderTypePicker(mReminder2Type) {
+ mReminder2Type = it
+ updateReminderTypeImage(event_reminder_2_type, Reminder(mReminder2Minutes, mReminder2Type))
+ }
+ }
+
+ event_reminder_3_type.setOnClickListener {
+ showReminderTypePicker(mReminder3Type) {
+ mReminder3Type = it
+ updateReminderTypeImage(event_reminder_3_type, Reminder(mReminder3Minutes, mReminder3Type))
+ }
+ }
+
+ event_type_holder.setOnClickListener { showEventTypeDialog() }
+ event_all_day.apply {
+ isChecked = mEvent.flags and FLAG_ALL_DAY != 0
+ jumpDrawablesToCurrentState()
+ }
updateTextColors(event_scrollview)
updateIconColors()
- wasActivityInitialized = true
+ mWasActivityInitialized = true
}
override fun onCreateOptionsMenu(menu: Menu): Boolean {
menuInflater.inflate(R.menu.menu_event, menu)
- if (wasActivityInitialized) {
+ if (mWasActivityInitialized) {
menu.findItem(R.id.delete).isVisible = mEvent.id != null
menu.findItem(R.id.share).isVisible = mEvent.id != null
menu.findItem(R.id.duplicate).isVisible = mEvent.id != null
@@ -179,7 +226,7 @@ class EventActivity : SimpleActivity() {
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
- if (!wasActivityInitialized) {
+ if (!mWasActivityInitialized) {
return
}
@@ -192,10 +239,16 @@ class EventActivity : SimpleActivity() {
putInt(REMINDER_2_MINUTES, mReminder2Minutes)
putInt(REMINDER_3_MINUTES, mReminder3Minutes)
+ putInt(REMINDER_1_TYPE, mReminder1Type)
+ putInt(REMINDER_2_TYPE, mReminder2Type)
+ putInt(REMINDER_3_TYPE, mReminder3Type)
+
putInt(REPEAT_INTERVAL, mRepeatInterval)
putInt(REPEAT_RULE, mRepeatRule)
putLong(REPEAT_LIMIT, mRepeatLimit)
+ putString(ATTENDEES, getAllAttendees())
+
putLong(EVENT_TYPE_ID, mEventTypeId)
putInt(EVENT_CALENDAR_ID, mEventCalendarId)
}
@@ -217,10 +270,17 @@ class EventActivity : SimpleActivity() {
mReminder2Minutes = getInt(REMINDER_2_MINUTES)
mReminder3Minutes = getInt(REMINDER_3_MINUTES)
+ mReminder1Type = getInt(REMINDER_1_TYPE)
+ mReminder2Type = getInt(REMINDER_2_TYPE)
+ mReminder3Type = getInt(REMINDER_3_TYPE)
+
mRepeatInterval = getInt(REPEAT_INTERVAL)
mRepeatRule = getInt(REPEAT_RULE)
mRepeatLimit = getLong(REPEAT_LIMIT)
+ mAttendees = Gson().fromJson>(getString(ATTENDEES), object : TypeToken>() {}.type)
+ ?: ArrayList()
+
mEventTypeId = getLong(EVENT_TYPE_ID)
mEventCalendarId = getInt(EVENT_CALENDAR_ID)
}
@@ -230,6 +290,7 @@ class EventActivity : SimpleActivity() {
updateTexts()
updateEventType()
updateCalDAVCalendar()
+ updateAttendees()
}
private fun updateTexts() {
@@ -237,6 +298,7 @@ class EventActivity : SimpleActivity() {
checkReminderTexts()
updateStartTexts()
updateEndTexts()
+ updateAttendeesVisibility()
}
private fun setupEditEvent() {
@@ -253,11 +315,15 @@ class EventActivity : SimpleActivity() {
mReminder1Minutes = mEvent.reminder1Minutes
mReminder2Minutes = mEvent.reminder2Minutes
mReminder3Minutes = mEvent.reminder3Minutes
+ mReminder1Type = mEvent.reminder1Type
+ mReminder2Type = mEvent.reminder2Type
+ mReminder3Type = mEvent.reminder3Type
mRepeatInterval = mEvent.repeatInterval
mRepeatLimit = mEvent.repeatLimit
mRepeatRule = mEvent.repeatRule
mEventTypeId = mEvent.eventType
mEventCalendarId = mEvent.getCalDAVCalendarId()
+ mAttendees = Gson().fromJson>(mEvent.attendees, object : TypeToken>() {}.type) ?: ArrayList()
checkRepeatTexts(mRepeatInterval)
}
@@ -558,6 +624,7 @@ class EventActivity : SimpleActivity() {
updateReminder1Text()
updateReminder2Text()
updateReminder3Text()
+ updateReminderTypeImages()
}
private fun updateReminder1Text() {
@@ -590,6 +657,36 @@ class EventActivity : SimpleActivity() {
}
}
+ private fun showReminderTypePicker(currentValue: Int, callback: (Int) -> Unit) {
+ val items = arrayListOf(
+ RadioItem(REMINDER_NOTIFICATION, getString(R.string.notification)),
+ RadioItem(REMINDER_EMAIL, getString(R.string.email))
+ )
+ RadioGroupDialog(this, items, currentValue) {
+ callback(it as Int)
+ }
+ }
+
+ private fun updateReminderTypeImages() {
+ updateReminderTypeImage(event_reminder_1_type, Reminder(mReminder1Minutes, mReminder1Type))
+ updateReminderTypeImage(event_reminder_2_type, Reminder(mReminder2Minutes, mReminder2Type))
+ updateReminderTypeImage(event_reminder_3_type, Reminder(mReminder3Minutes, mReminder3Type))
+ }
+
+ private fun updateAttendeesVisibility() {
+ val isSyncedEvent = mEventCalendarId != STORED_LOCALLY_ONLY
+ event_attendees_image.beVisibleIf(isSyncedEvent)
+ event_attendees_holder.beVisibleIf(isSyncedEvent)
+ event_attendees_divider.beVisibleIf(isSyncedEvent)
+ }
+
+ private fun updateReminderTypeImage(view: ImageView, reminder: Reminder) {
+ view.beVisibleIf(reminder.minutes != REMINDER_OFF && mEventCalendarId != STORED_LOCALLY_ONLY)
+ val drawable = if (reminder.type == REMINDER_NOTIFICATION) R.drawable.ic_bell else R.drawable.ic_email
+ val icon = resources.getColoredDrawableWithColor(drawable, config.textColor)
+ view.setImageDrawable(icon)
+ }
+
private fun updateRepetitionText() {
event_repetition.text = getRepetitionText(mRepeatInterval)
}
@@ -627,6 +724,8 @@ class EventActivity : SimpleActivity() {
mEventCalendarId = it
config.lastUsedCaldavCalendarId = it
updateCurrentCalendarInfo(getCalendarWithId(calendars, it))
+ updateReminderTypeImages()
+ updateAttendeesVisibility()
}
}
} else {
@@ -765,16 +864,26 @@ class EventActivity : SimpleActivity() {
"$CALDAV-$mEventCalendarId"
}
- val reminders = sortedSetOf(mReminder1Minutes, mReminder2Minutes, mReminder3Minutes).filter { it != REMINDER_OFF }
- val reminder1 = reminders.getOrElse(0) { REMINDER_OFF }
- val reminder2 = reminders.getOrElse(1) { REMINDER_OFF }
- val reminder3 = reminders.getOrElse(2) { REMINDER_OFF }
+ var reminders = arrayListOf(
+ Reminder(mReminder1Minutes, mReminder1Type),
+ Reminder(mReminder2Minutes, mReminder2Type),
+ Reminder(mReminder3Minutes, mReminder3Type)
+ )
+ reminders = reminders.filter { it.minutes != REMINDER_OFF }.sortedBy { it.minutes }.toMutableList() as ArrayList
+
+ val reminder1 = reminders.getOrNull(0) ?: Reminder(REMINDER_OFF, REMINDER_NOTIFICATION)
+ val reminder2 = reminders.getOrNull(1) ?: Reminder(REMINDER_OFF, REMINDER_NOTIFICATION)
+ val reminder3 = reminders.getOrNull(2) ?: Reminder(REMINDER_OFF, REMINDER_NOTIFICATION)
+
+ mReminder1Type = if (mEventCalendarId == STORED_LOCALLY_ONLY) REMINDER_NOTIFICATION else reminder1.type
+ mReminder2Type = if (mEventCalendarId == STORED_LOCALLY_ONLY) REMINDER_NOTIFICATION else reminder2.type
+ mReminder3Type = if (mEventCalendarId == STORED_LOCALLY_ONLY) REMINDER_NOTIFICATION else reminder3.type
config.apply {
if (usePreviousEventReminders) {
- lastEventReminderMinutes1 = reminder1
- lastEventReminderMinutes2 = reminder2
- lastEventReminderMinutes3 = reminder3
+ lastEventReminderMinutes1 = reminder1.minutes
+ lastEventReminderMinutes2 = reminder2.minutes
+ lastEventReminderMinutes3 = reminder3.minutes
}
}
@@ -783,14 +892,18 @@ class EventActivity : SimpleActivity() {
endTS = newEndTS
title = newTitle
description = event_description.value
- reminder1Minutes = reminder1
- reminder2Minutes = reminder2
- reminder3Minutes = reminder3
+ reminder1Minutes = reminder1.minutes
+ reminder2Minutes = reminder2.minutes
+ reminder3Minutes = reminder3.minutes
+ reminder1Type = mReminder1Type
+ reminder2Type = mReminder2Type
+ reminder3Type = mReminder3Type
repeatInterval = mRepeatInterval
importId = newImportId
flags = mEvent.flags.addBitIf(event_all_day.isChecked, FLAG_ALL_DAY)
repeatLimit = if (repeatInterval == 0) 0 else mRepeatLimit
repeatRule = mRepeatRule
+ attendees = if (mEventCalendarId == STORED_LOCALLY_ONLY) "" else getAllAttendees()
eventType = newEventType
lastUpdated = System.currentTimeMillis()
source = newSource
@@ -810,7 +923,7 @@ class EventActivity : SimpleActivity() {
if (mEvent.id == null || mEvent.id == null) {
eventsHelper.insertEvent(mEvent, true, true) {
if (DateTime.now().isAfter(mEventStartDateTime.millis)) {
- if (mEvent.repeatInterval == 0 && mEvent.getReminders().isNotEmpty()) {
+ if (mEvent.repeatInterval == 0 && mEvent.getReminders().any { it.type == REMINDER_NOTIFICATION }) {
notifyEvent(mEvent)
}
}
@@ -1006,6 +1119,154 @@ class EventActivity : SimpleActivity() {
}
}
+ private fun fillAvailableContacts() {
+ mAvailableContacts = getEmails()
+
+ val names = getNames()
+ mAvailableContacts.forEach {
+ val contactId = it.contactId
+ val contact = names.firstOrNull { it.contactId == contactId }
+ val name = contact?.name
+ if (name != null) {
+ it.name = name
+ }
+
+ val photoUri = contact?.photoUri
+ if (photoUri != null) {
+ it.photoUri = photoUri
+ }
+ }
+ }
+
+ private fun updateAttendees() {
+ mAttendees.forEach {
+ addAttendee(it.getPublicName())
+ }
+ addAttendee()
+
+ val imageHeight = event_repetition_image.height
+ if (imageHeight > 0) {
+ event_attendees_image.layoutParams.height = imageHeight
+ } else {
+ event_repetition_image.onGlobalLayout {
+ event_attendees_image.layoutParams.height = event_repetition_image.height
+ }
+ }
+ }
+
+ private fun addAttendee(value: String? = null) {
+ val attendeeHolder = layoutInflater.inflate(R.layout.item_attendee, event_attendees_holder, false) as RelativeLayout
+ mAttendeeViews.add(attendeeHolder.event_attendee)
+ attendeeHolder.event_attendee.onTextChangeListener {
+ if (mWasContactsPermissionChecked && value == null) {
+ checkNewAttendeeField(value)
+ } else {
+ handlePermission(PERMISSION_READ_CONTACTS) {
+ checkNewAttendeeField(value)
+ mWasContactsPermissionChecked = true
+ }
+ }
+ }
+
+ event_attendees_holder.addView(attendeeHolder)
+ attendeeHolder.event_attendee.setColors(config.textColor, getAdjustedPrimaryColor(), config.backgroundColor)
+
+ if (value != null) {
+ attendeeHolder.event_attendee.setText(value)
+ }
+
+ val adapter = AutoCompleteTextViewAdapter(this, mAvailableContacts)
+ attendeeHolder.event_attendee.setAdapter(adapter)
+ attendeeHolder.event_attendee.setOnItemClickListener { parent, view, position, id ->
+ val currAttendees = (attendeeHolder.event_attendee.adapter as AutoCompleteTextViewAdapter).resultList
+ val selectedAttendee = currAttendees[position]
+ }
+ }
+
+ private fun checkNewAttendeeField(value: String?) {
+ if (value == null && mAttendeeViews.none { it.value.isEmpty() }) {
+ addAttendee()
+ }
+ }
+
+ private fun getAllAttendees(): String {
+ val attendeeEmails = mAttendeeViews.map { it.value }.filter { it.isNotEmpty() }.toMutableList() as ArrayList
+ val attendees = ArrayList()
+ attendeeEmails.mapTo(attendees) {
+ Attendee(0, "", it, CalendarContract.Attendees.ATTENDEE_STATUS_INVITED, "")
+ }
+ return Gson().toJson(attendees)
+ }
+
+ private fun getNames(): List {
+ val contacts = ArrayList()
+ val uri = ContactsContract.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)
+
+ val selection = "${ContactsContract.Data.MIMETYPE} = ?"
+ val selectionArgs = arrayOf(ContactsContract.CommonDataKinds.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) ?: ""
+
+ 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, "", 0, photoUri)
+ contacts.add(contact)
+ }
+ } while (cursor.moveToNext())
+ }
+ } catch (ignored: Exception) {
+ } finally {
+ cursor?.close()
+ }
+ return contacts
+ }
+
+ private fun getEmails(): ArrayList {
+ val contacts = ArrayList()
+ val uri = ContactsContract.CommonDataKinds.Email.CONTENT_URI
+ val projection = arrayOf(
+ ContactsContract.Data.CONTACT_ID,
+ ContactsContract.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, 0, "")
+ contacts.add(contact)
+ } while (cursor.moveToNext())
+ }
+ } catch (ignored: Exception) {
+ } finally {
+ cursor?.close()
+ }
+ return contacts
+ }
+
private fun updateIconColors() {
val textColor = config.textColor
event_time_image.applyColorFilter(textColor)
@@ -1014,5 +1275,9 @@ class EventActivity : SimpleActivity() {
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)
}
}
diff --git a/app/src/main/kotlin/com/simplemobiletools/calendar/pro/activities/MainActivity.kt b/app/src/main/kotlin/com/simplemobiletools/calendar/pro/activities/MainActivity.kt
index e48a0e349..489e81dcf 100644
--- a/app/src/main/kotlin/com/simplemobiletools/calendar/pro/activities/MainActivity.kt
+++ b/app/src/main/kotlin/com/simplemobiletools/calendar/pro/activities/MainActivity.kt
@@ -1,11 +1,16 @@
package com.simplemobiletools.calendar.pro.activities
+import android.annotation.SuppressLint
import android.app.SearchManager
import android.content.Context
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
@@ -21,6 +26,7 @@ import com.simplemobiletools.calendar.pro.databases.EventsDatabase
import com.simplemobiletools.calendar.pro.dialogs.ExportEventsDialog
import com.simplemobiletools.calendar.pro.dialogs.FilterEventTypesDialog
import com.simplemobiletools.calendar.pro.dialogs.ImportEventsDialog
+import com.simplemobiletools.calendar.pro.dialogs.SetRemindersDialog
import com.simplemobiletools.calendar.pro.extensions.*
import com.simplemobiletools.calendar.pro.fragments.*
import com.simplemobiletools.calendar.pro.helpers.*
@@ -129,6 +135,7 @@ class MainActivity : SimpleActivity(), RefreshRecyclerViewListener {
calendar_fab.setColors(config.textColor, getAdjustedPrimaryColor(), config.backgroundColor)
search_holder.background = ColorDrawable(config.backgroundColor)
checkSwipeRefreshAvailability()
+ checkShortcuts()
}
override fun onPause() {
@@ -154,6 +161,7 @@ class MainActivity : SimpleActivity(), RefreshRecyclerViewListener {
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_date).isVisible = config.storedView != EVENTS_LIST_VIEW
}
setupSearch(menu)
@@ -172,6 +180,7 @@ class MainActivity : SimpleActivity(), RefreshRecyclerViewListener {
when (item.itemId) {
R.id.change_view -> showViewDialog()
R.id.go_to_today -> goToToday()
+ R.id.go_to_date -> showGoToDateDialog()
R.id.filter -> showFilterDialog()
R.id.refresh_caldav_calendars -> refreshCalDAVCalendars(true)
R.id.add_holidays -> addHolidays()
@@ -256,6 +265,33 @@ class MainActivity : SimpleActivity(), RefreshRecyclerViewListener {
mSearchMenuItem?.collapseActionView()
}
+ @SuppressLint("NewApi")
+ private fun checkShortcuts() {
+ val appIconColor = config.appIconColor
+ if (isNougatMR1Plus() && config.lastHandledShortcutColor != appIconColor) {
+ val newEvent = getString(R.string.new_event)
+ val manager = getSystemService(ShortcutManager::class.java)
+ val drawable = resources.getDrawable(R.drawable.shortcut_plus)
+ (drawable as LayerDrawable).findDrawableByLayerId(R.id.shortcut_plus_background).applyColorFilter(appIconColor)
+ val bmp = drawable.convertToBitmap()
+
+ val intent = Intent(this, SplashActivity::class.java)
+ intent.action = SHORTCUT_NEW_EVENT
+ val shortcut = ShortcutInfo.Builder(this, "new_event")
+ .setShortLabel(newEvent)
+ .setLongLabel(newEvent)
+ .setIcon(Icon.createWithBitmap(bmp))
+ .setIntent(intent)
+ .build()
+
+ try {
+ manager.dynamicShortcuts = Arrays.asList(shortcut)
+ config.lastHandledShortcutColor = appIconColor
+ } catch (ignored: Exception) {
+ }
+ }
+ }
+
private fun checkIsOpenIntent(): Boolean {
val dayCodeToOpen = intent.getStringExtra(DAY_CODE) ?: ""
val viewToOpen = intent.getIntExtra(VIEW_TO_OPEN, DAILY_VIEW)
@@ -339,6 +375,10 @@ class MainActivity : SimpleActivity(), RefreshRecyclerViewListener {
currentFragments.last().goToToday()
}
+ fun showGoToDateDialog() {
+ currentFragments.last().showGoToDateDialog()
+ }
+
private fun resetActionBarTitle() {
updateActionBarTitle(getString(R.string.app_launcher_name))
updateActionBarSubtitle("")
@@ -407,16 +447,19 @@ class MainActivity : SimpleActivity(), RefreshRecyclerViewListener {
private fun tryAddBirthdays() {
handlePermission(PERMISSION_READ_CONTACTS) {
if (it) {
- Thread {
- addContactEvents(true) {
- if (it > 0) {
- toast(R.string.birthdays_added)
- updateViewPager()
- } else {
- toast(R.string.no_birthdays)
+ SetRemindersDialog(this) {
+ val reminders = it
+ Thread {
+ addContactEvents(true, reminders) {
+ if (it > 0) {
+ toast(R.string.birthdays_added)
+ updateViewPager()
+ } else {
+ toast(R.string.no_birthdays)
+ }
}
- }
- }.start()
+ }.start()
+ }
} else {
toast(R.string.no_contacts_permission)
}
@@ -426,16 +469,19 @@ class MainActivity : SimpleActivity(), RefreshRecyclerViewListener {
private fun tryAddAnniversaries() {
handlePermission(PERMISSION_READ_CONTACTS) {
if (it) {
- Thread {
- addContactEvents(false) {
- if (it > 0) {
- toast(R.string.anniversaries_added)
- updateViewPager()
- } else {
- toast(R.string.no_anniversaries)
+ SetRemindersDialog(this) {
+ val reminders = it
+ Thread {
+ addContactEvents(false, reminders) {
+ if (it > 0) {
+ toast(R.string.anniversaries_added)
+ updateViewPager()
+ } else {
+ toast(R.string.no_anniversaries)
+ }
}
- }
- }.start()
+ }.start()
+ }
} else {
toast(R.string.no_contacts_permission)
}
@@ -450,7 +496,7 @@ class MainActivity : SimpleActivity(), RefreshRecyclerViewListener {
}, Toast.LENGTH_LONG)
}
- private fun addContactEvents(birthdays: Boolean, callback: (Int) -> Unit) {
+ private fun addContactEvents(birthdays: Boolean, reminders: ArrayList, callback: (Int) -> Unit) {
var eventsAdded = 0
val uri = ContactsContract.Data.CONTENT_URI
val projection = arrayOf(ContactsContract.Contacts.DISPLAY_NAME,
@@ -486,7 +532,8 @@ class MainActivity : SimpleActivity(), RefreshRecyclerViewListener {
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, importId = contactId, flags = FLAG_ALL_DAY, repeatInterval = YEAR,
+ val event = Event(null, timestamp, timestamp, name, reminder1Minutes = reminders[0], reminder2Minutes = reminders[1],
+ reminder3Minutes = reminders[2], importId = contactId, flags = FLAG_ALL_DAY, repeatInterval = YEAR, repeatRule = REPEAT_SAME_DAY,
eventType = eventTypeId, source = source, lastUpdated = lastUpdated)
if (!importIDs.contains(contactId)) {
@@ -861,6 +908,7 @@ class MainActivity : SimpleActivity(), RefreshRecyclerViewListener {
add(Release(117, R.string.release_117))
add(Release(119, R.string.release_119))
add(Release(129, R.string.release_129))
+ add(Release(143, R.string.release_143))
checkWhatsNew(this, BuildConfig.VERSION_CODE)
}
}
diff --git a/app/src/main/kotlin/com/simplemobiletools/calendar/pro/activities/SettingsActivity.kt b/app/src/main/kotlin/com/simplemobiletools/calendar/pro/activities/SettingsActivity.kt
index e56ab5ad1..32b8e1ebe 100644
--- a/app/src/main/kotlin/com/simplemobiletools/calendar/pro/activities/SettingsActivity.kt
+++ b/app/src/main/kotlin/com/simplemobiletools/calendar/pro/activities/SettingsActivity.kt
@@ -315,7 +315,6 @@ class SettingsActivity : SimpleActivity() {
}
}
-
private fun setupWeekNumbers() {
settings_week_numbers.isChecked = config.showWeekNumbers
settings_week_numbers_holder.setOnClickListener {
@@ -692,14 +691,18 @@ class SettingsActivity : SimpleActivity() {
private fun setupImportSettings() {
settings_import_holder.setOnClickListener {
- FilePickerDialog(this) {
- Thread {
- try {
- parseFile(it)
- } catch (e: Exception) {
- showErrorToast(e)
+ handlePermission(PERMISSION_READ_STORAGE) {
+ if (it) {
+ FilePickerDialog(this) {
+ Thread {
+ try {
+ parseFile(it)
+ } catch (e: Exception) {
+ showErrorToast(e)
+ }
+ }.start()
}
- }.start()
+ }
}
}
}
diff --git a/app/src/main/kotlin/com/simplemobiletools/calendar/pro/activities/SplashActivity.kt b/app/src/main/kotlin/com/simplemobiletools/calendar/pro/activities/SplashActivity.kt
index c594973a9..506520e21 100644
--- a/app/src/main/kotlin/com/simplemobiletools/calendar/pro/activities/SplashActivity.kt
+++ b/app/src/main/kotlin/com/simplemobiletools/calendar/pro/activities/SplashActivity.kt
@@ -1,8 +1,10 @@
package com.simplemobiletools.calendar.pro.activities
import android.content.Intent
+import com.simplemobiletools.calendar.pro.extensions.getNewEventTimestampFromCode
import com.simplemobiletools.calendar.pro.helpers.*
import com.simplemobiletools.commons.activities.BaseSplashActivity
+import org.joda.time.DateTime
class SplashActivity : BaseSplashActivity() {
override fun getAppPackageName() = packageName
@@ -19,6 +21,13 @@ class SplashActivity : BaseSplashActivity() {
putExtra(EVENT_OCCURRENCE_TS, intent.getLongExtra(EVENT_OCCURRENCE_TS, 0L))
startActivity(this)
}
+ intent.action == SHORTCUT_NEW_EVENT -> {
+ val dayCode = Formatter.getDayCodeFromDateTime(DateTime())
+ Intent(this, EventActivity::class.java).apply {
+ putExtra(NEW_EVENT_START_TS, getNewEventTimestampFromCode(dayCode))
+ startActivity(this)
+ }
+ }
else -> startActivity(Intent(this, MainActivity::class.java))
}
finish()
diff --git a/app/src/main/kotlin/com/simplemobiletools/calendar/pro/activities/WidgetListConfigureActivity.kt b/app/src/main/kotlin/com/simplemobiletools/calendar/pro/activities/WidgetListConfigureActivity.kt
index 6f8e97f6a..73a605e30 100644
--- a/app/src/main/kotlin/com/simplemobiletools/calendar/pro/activities/WidgetListConfigureActivity.kt
+++ b/app/src/main/kotlin/com/simplemobiletools/calendar/pro/activities/WidgetListConfigureActivity.kt
@@ -68,12 +68,7 @@ class WidgetListConfigureActivity : SimpleActivity() {
updateColors()
mBgColor = config.widgetBgColor
- if (mBgColor == 1) {
- mBgColor = Color.BLACK
- mBgAlpha = .2f
- } else {
- mBgAlpha = Color.alpha(mBgColor) / 255.toFloat()
- }
+ mBgAlpha = Color.alpha(mBgColor) / 255.toFloat()
mBgColorWithoutTransparency = Color.rgb(Color.red(mBgColor), Color.green(mBgColor), Color.blue(mBgColor))
config_bg_seekbar.setOnSeekBarChangeListener(bgSeekbarChangeListener)
diff --git a/app/src/main/kotlin/com/simplemobiletools/calendar/pro/activities/WidgetMonthlyConfigureActivity.kt b/app/src/main/kotlin/com/simplemobiletools/calendar/pro/activities/WidgetMonthlyConfigureActivity.kt
index df3d1e1fd..dc159f658 100644
--- a/app/src/main/kotlin/com/simplemobiletools/calendar/pro/activities/WidgetMonthlyConfigureActivity.kt
+++ b/app/src/main/kotlin/com/simplemobiletools/calendar/pro/activities/WidgetMonthlyConfigureActivity.kt
@@ -77,12 +77,7 @@ class WidgetMonthlyConfigureActivity : SimpleActivity(), MonthlyCalendar {
updateColors()
mBgColor = config.widgetBgColor
- if (mBgColor == 1) {
- mBgColor = Color.BLACK
- mBgAlpha = .2f
- } else {
- mBgAlpha = Color.alpha(mBgColor) / 255.toFloat()
- }
+ mBgAlpha = Color.alpha(mBgColor) / 255.toFloat()
mBgColorWithoutTransparency = Color.rgb(Color.red(mBgColor), Color.green(mBgColor), Color.blue(mBgColor))
config_bg_seekbar.setOnSeekBarChangeListener(bgSeekbarChangeListener)
diff --git a/app/src/main/kotlin/com/simplemobiletools/calendar/pro/adapters/AutoCompleteTextViewAdapter.kt b/app/src/main/kotlin/com/simplemobiletools/calendar/pro/adapters/AutoCompleteTextViewAdapter.kt
new file mode 100644
index 000000000..34ef234dc
--- /dev/null
+++ b/app/src/main/kotlin/com/simplemobiletools/calendar/pro/adapters/AutoCompleteTextViewAdapter.kt
@@ -0,0 +1,101 @@
+package com.simplemobiletools.calendar.pro.adapters
+
+import android.graphics.drawable.LayerDrawable
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.ArrayAdapter
+import android.widget.Filter
+import com.bumptech.glide.Glide
+import com.bumptech.glide.load.engine.DiskCacheStrategy
+import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions
+import com.bumptech.glide.request.RequestOptions
+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.normalizeString
+import kotlinx.android.synthetic.main.item_autocomplete_email_name.view.*
+
+class AutoCompleteTextViewAdapter(val activity: SimpleActivity, val contacts: ArrayList) : ArrayAdapter(activity, 0, contacts) {
+ var resultList = ArrayList()
+ 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]
+ var listItem = convertView
+ if (listItem == null || listItem.tag != contact.name.isNotEmpty()) {
+ val layout = if (contact.name.isNotEmpty()) R.layout.item_autocomplete_email_name else R.layout.item_autocomplete_email
+ listItem = LayoutInflater.from(activity).inflate(layout, parent, false)
+ }
+
+ listItem!!.apply {
+ tag = contact.name.isNotEmpty()
+ item_autocomplete_name?.text = contact.name
+ item_autocomplete_email?.text = contact.email
+
+ if (contact.photoUri.isEmpty()) {
+ item_autocomplete_image.setImageDrawable(placeholder)
+ } else {
+ val options = RequestOptions()
+ .diskCacheStrategy(DiskCacheStrategy.RESOURCE)
+ .error(placeholder)
+ .centerCrop()
+
+ Glide.with(activity)
+ .load(contact.photoUri)
+ .transition(DrawableTransitionOptions.withCrossFade())
+ .apply(options)
+ .apply(RequestOptions.circleCropTransform())
+ .into(item_autocomplete_image)
+ }
+ }
+
+ return listItem
+ }
+
+ override fun getFilter() = object : Filter() {
+ override fun performFiltering(constraint: CharSequence?): FilterResults {
+ val filterResults = Filter.FilterResults()
+ if (constraint != null) {
+ resultList.clear()
+ val searchString = constraint.toString().normalizeString()
+ contacts.forEach {
+ if (it.email.contains(searchString, true) || it.name.contains(searchString, true)) {
+ resultList.add(it)
+ }
+ }
+
+ resultList.sortWith(compareBy
+ { it.name.startsWith(searchString, true) }.thenBy
+ { it.email.startsWith(searchString, true) }.thenBy
+ { it.name.contains(searchString, true) }.thenBy
+ { it.email.contains(searchString, true) })
+ resultList.reverse()
+
+ filterResults.values = resultList
+ filterResults.count = resultList.size
+ }
+ return filterResults
+ }
+
+ override fun publishResults(constraint: CharSequence?, results: FilterResults?) {
+ if (results?.count ?: -1 > 0) {
+ notifyDataSetChanged()
+ } else {
+ notifyDataSetInvalidated()
+ }
+ }
+
+ override fun convertResultToString(resultValue: Any?) = (resultValue as? Attendee)?.getPublicName()
+ }
+
+ override fun getItem(index: Int) = resultList[index]
+
+ override fun getCount() = resultList.size
+}
diff --git a/app/src/main/kotlin/com/simplemobiletools/calendar/pro/databases/EventsDatabase.kt b/app/src/main/kotlin/com/simplemobiletools/calendar/pro/databases/EventsDatabase.kt
index 36679424c..19440190c 100644
--- a/app/src/main/kotlin/com/simplemobiletools/calendar/pro/databases/EventsDatabase.kt
+++ b/app/src/main/kotlin/com/simplemobiletools/calendar/pro/databases/EventsDatabase.kt
@@ -5,6 +5,7 @@ import androidx.room.Database
import androidx.room.Room
import androidx.room.RoomDatabase
import androidx.room.TypeConverters
+import androidx.room.migration.Migration
import androidx.sqlite.db.SupportSQLiteDatabase
import com.simplemobiletools.calendar.pro.R
import com.simplemobiletools.calendar.pro.extensions.config
@@ -16,7 +17,7 @@ import com.simplemobiletools.calendar.pro.models.Event
import com.simplemobiletools.calendar.pro.models.EventType
import java.util.concurrent.Executors
-@Database(entities = [Event::class, EventType::class], version = 1)
+@Database(entities = [Event::class, EventType::class], version = 2)
@TypeConverters(Converters::class)
abstract class EventsDatabase : RoomDatabase() {
@@ -38,6 +39,7 @@ abstract class EventsDatabase : RoomDatabase() {
insertRegularEventType(context)
}
})
+ .addMigrations(MIGRATION_1_2)
.build()
db!!.openHelper.setWriteAheadLoggingEnabled(true)
}
@@ -58,5 +60,16 @@ abstract class EventsDatabase : RoomDatabase() {
context.config.addDisplayEventType(REGULAR_EVENT_TYPE_ID.toString())
}
}
+
+ private val MIGRATION_1_2 = object : Migration(1, 2) {
+ override fun migrate(database: SupportSQLiteDatabase) {
+ database.apply {
+ execSQL("ALTER TABLE events ADD COLUMN reminder_1_type INTEGER NOT NULL DEFAULT 0")
+ execSQL("ALTER TABLE events ADD COLUMN reminder_2_type INTEGER NOT NULL DEFAULT 0")
+ execSQL("ALTER TABLE events ADD COLUMN reminder_3_type INTEGER NOT NULL DEFAULT 0")
+ execSQL("ALTER TABLE events ADD COLUMN attendees TEXT NOT NULL DEFAULT ''")
+ }
+ }
+ }
}
}
diff --git a/app/src/main/kotlin/com/simplemobiletools/calendar/pro/dialogs/SetRemindersDialog.kt b/app/src/main/kotlin/com/simplemobiletools/calendar/pro/dialogs/SetRemindersDialog.kt
new file mode 100644
index 000000000..bdf0d2805
--- /dev/null
+++ b/app/src/main/kotlin/com/simplemobiletools/calendar/pro/dialogs/SetRemindersDialog.kt
@@ -0,0 +1,63 @@
+package com.simplemobiletools.calendar.pro.dialogs
+
+import android.app.Activity
+import androidx.appcompat.app.AlertDialog
+import com.simplemobiletools.calendar.pro.R
+import com.simplemobiletools.calendar.pro.extensions.config
+import com.simplemobiletools.commons.extensions.*
+import kotlinx.android.synthetic.main.dialog_set_reminders.view.*
+
+class SetRemindersDialog(val activity: Activity, val callback: (reminders: ArrayList) -> Unit) {
+ private var mReminder1Minutes = -1
+ private var mReminder2Minutes = -1
+ private var mReminder3Minutes = -1
+
+ init {
+ val view = activity.layoutInflater.inflate(R.layout.dialog_set_reminders, null).apply {
+ set_reminders_image.applyColorFilter(context.config.textColor)
+ set_reminders_1.text = activity.getFormattedMinutes(mReminder1Minutes)
+ set_reminders_2.text = activity.getFormattedMinutes(mReminder1Minutes)
+ set_reminders_3.text = activity.getFormattedMinutes(mReminder1Minutes)
+
+ set_reminders_1.setOnClickListener {
+ activity.showPickSecondsDialogHelper(mReminder1Minutes) {
+ mReminder1Minutes = if (it <= 0) it else it / 60
+ set_reminders_1.text = activity.getFormattedMinutes(mReminder1Minutes)
+ if (mReminder1Minutes != -1) {
+ set_reminders_2.beVisible()
+ }
+ }
+ }
+
+ set_reminders_2.setOnClickListener {
+ activity.showPickSecondsDialogHelper(mReminder2Minutes) {
+ mReminder2Minutes = if (it <= 0) it else it / 60
+ set_reminders_2.text = activity.getFormattedMinutes(mReminder2Minutes)
+ if (mReminder2Minutes != -1) {
+ set_reminders_3.beVisible()
+ }
+ }
+ }
+
+ set_reminders_3.setOnClickListener {
+ activity.showPickSecondsDialogHelper(mReminder3Minutes) {
+ mReminder3Minutes = if (it <= 0) it else it / 60
+ set_reminders_3.text = activity.getFormattedMinutes(mReminder3Minutes)
+ }
+ }
+ }
+
+ AlertDialog.Builder(activity)
+ .setPositiveButton(R.string.ok) { dialog, which -> dialogConfirmed() }
+ .setNegativeButton(R.string.cancel, null)
+ .create().apply {
+ activity.setupDialogStuff(view, this, R.string.event_reminders)
+ }
+ }
+
+ private fun dialogConfirmed() {
+ val reminders = arrayListOf(mReminder1Minutes, mReminder2Minutes, mReminder3Minutes)
+ reminders.sort()
+ callback(reminders)
+ }
+}
diff --git a/app/src/main/kotlin/com/simplemobiletools/calendar/pro/extensions/Context.kt b/app/src/main/kotlin/com/simplemobiletools/calendar/pro/extensions/Context.kt
index a722cb91d..3813e37bc 100644
--- a/app/src/main/kotlin/com/simplemobiletools/calendar/pro/extensions/Context.kt
+++ b/app/src/main/kotlin/com/simplemobiletools/calendar/pro/extensions/Context.kt
@@ -80,7 +80,8 @@ fun Context.scheduleAllEvents() {
}
fun Context.scheduleNextEventReminder(event: Event, showToasts: Boolean) {
- if (event.getReminders().isEmpty()) {
+ val validReminders = event.getReminders().filter { it.type == REMINDER_NOTIFICATION }
+ if (validReminders.isEmpty()) {
if (showToasts) {
toast(R.string.saving)
}
@@ -88,7 +89,7 @@ fun Context.scheduleNextEventReminder(event: Event, showToasts: Boolean) {
}
val now = getNowSeconds()
- val reminderSeconds = event.getReminders().reversed().map { it * 60 }
+ val reminderSeconds = validReminders.reversed().map { it.minutes * 60 }
eventsHelper.getEvents(now, now + YEAR, event.id!!, false) {
if (it.isNotEmpty()) {
for (curEvent in it) {
@@ -159,7 +160,7 @@ fun Context.getRepetitionText(seconds: Int) = when (seconds) {
}
fun Context.notifyRunningEvents() {
- eventsHelper.getRunningEvents().filter { it.getReminders().isNotEmpty() }.forEach {
+ eventsHelper.getRunningEvents().filter { it.getReminders().any { it.type == REMINDER_NOTIFICATION } }.forEach {
notifyEvent(it)
}
}
@@ -408,6 +409,7 @@ fun Context.addDayEvents(day: DayMonthly, linearLayout: LinearLayout, res: Resou
text = it.title.replace(" ", "\u00A0") // allow word break by char
background = backgroundDrawable
layoutParams = eventLayoutParams
+ contentDescription = it.title
linearLayout.addView(this)
}
}
diff --git a/app/src/main/kotlin/com/simplemobiletools/calendar/pro/fragments/DayFragment.kt b/app/src/main/kotlin/com/simplemobiletools/calendar/pro/fragments/DayFragment.kt
index c321d965e..5ed1a9be8 100644
--- a/app/src/main/kotlin/com/simplemobiletools/calendar/pro/fragments/DayFragment.kt
+++ b/app/src/main/kotlin/com/simplemobiletools/calendar/pro/fragments/DayFragment.kt
@@ -5,12 +5,11 @@ import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
-import android.widget.DatePicker
import android.widget.RelativeLayout
-import androidx.appcompat.app.AlertDialog
import androidx.fragment.app.Fragment
import com.simplemobiletools.calendar.pro.R
import com.simplemobiletools.calendar.pro.activities.EventActivity
+import com.simplemobiletools.calendar.pro.activities.MainActivity
import com.simplemobiletools.calendar.pro.activities.SimpleActivity
import com.simplemobiletools.calendar.pro.adapters.DayEventsAdapter
import com.simplemobiletools.calendar.pro.extensions.config
@@ -22,11 +21,8 @@ import com.simplemobiletools.calendar.pro.helpers.Formatter
import com.simplemobiletools.calendar.pro.interfaces.NavigationListener
import com.simplemobiletools.calendar.pro.models.Event
import com.simplemobiletools.commons.extensions.applyColorFilter
-import com.simplemobiletools.commons.extensions.getDialogTheme
-import com.simplemobiletools.commons.extensions.setupDialogStuff
import kotlinx.android.synthetic.main.fragment_day.view.*
import kotlinx.android.synthetic.main.top_navigation.view.*
-import org.joda.time.DateTime
import java.util.*
class DayFragment : Fragment() {
@@ -81,35 +77,14 @@ class DayFragment : Fragment() {
val day = Formatter.getDayTitle(context!!, mDayCode)
mHolder.top_value.apply {
text = day
- setOnClickListener { pickDay() }
+ contentDescription = text
+ setOnClickListener {
+ (activity as MainActivity).showGoToDateDialog()
+ }
setTextColor(context.config.textColor)
}
}
- private fun pickDay() {
- activity!!.setTheme(context!!.getDialogTheme())
- val view = layoutInflater.inflate(R.layout.date_picker, null)
- val datePicker = view.findViewById(R.id.date_picker)
-
- val dateTime = Formatter.getDateTimeFromCode(mDayCode)
- datePicker.init(dateTime.year, dateTime.monthOfYear - 1, dateTime.dayOfMonth, null)
-
- AlertDialog.Builder(context!!)
- .setNegativeButton(R.string.cancel, null)
- .setPositiveButton(R.string.ok) { dialog, which -> positivePressed(dateTime, datePicker) }
- .create().apply {
- activity?.setupDialogStuff(view, this)
- }
- }
-
- private fun positivePressed(dateTime: DateTime, datePicker: DatePicker) {
- val month = datePicker.month + 1
- val year = datePicker.year
- val day = datePicker.dayOfMonth
- val newDateTime = dateTime.withDate(year, month, day)
- mListener?.goToDateTime(newDateTime)
- }
-
fun updateCalendar() {
val startTS = Formatter.getDayStartTS(mDayCode)
val endTS = Formatter.getDayEndTS(mDayCode)
diff --git a/app/src/main/kotlin/com/simplemobiletools/calendar/pro/fragments/DayFragmentsHolder.kt b/app/src/main/kotlin/com/simplemobiletools/calendar/pro/fragments/DayFragmentsHolder.kt
index f7cf5a36c..17c9d508a 100644
--- a/app/src/main/kotlin/com/simplemobiletools/calendar/pro/fragments/DayFragmentsHolder.kt
+++ b/app/src/main/kotlin/com/simplemobiletools/calendar/pro/fragments/DayFragmentsHolder.kt
@@ -5,6 +5,8 @@ import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
+import android.widget.DatePicker
+import androidx.appcompat.app.AlertDialog
import androidx.viewpager.widget.ViewPager
import com.simplemobiletools.calendar.pro.R
import com.simplemobiletools.calendar.pro.activities.MainActivity
@@ -13,6 +15,8 @@ import com.simplemobiletools.calendar.pro.extensions.config
import com.simplemobiletools.calendar.pro.helpers.DAY_CODE
import com.simplemobiletools.calendar.pro.helpers.Formatter
import com.simplemobiletools.calendar.pro.interfaces.NavigationListener
+import com.simplemobiletools.commons.extensions.getDialogTheme
+import com.simplemobiletools.commons.extensions.setupDialogStuff
import com.simplemobiletools.commons.extensions.updateActionBarTitle
import com.simplemobiletools.commons.views.MyViewPager
import kotlinx.android.synthetic.main.fragment_days_holder.view.*
@@ -99,6 +103,30 @@ class DayFragmentsHolder : MyFragmentHolder(), NavigationListener {
setupFragment()
}
+ override fun showGoToDateDialog() {
+ activity!!.setTheme(context!!.getDialogTheme())
+ val view = layoutInflater.inflate(R.layout.date_picker, null)
+ val datePicker = view.findViewById(R.id.date_picker)
+
+ val dateTime = Formatter.getDateTimeFromCode(currentDayCode)
+ datePicker.init(dateTime.year, dateTime.monthOfYear - 1, dateTime.dayOfMonth, null)
+
+ AlertDialog.Builder(context!!)
+ .setNegativeButton(R.string.cancel, null)
+ .setPositiveButton(R.string.ok) { dialog, which -> dateSelected(dateTime, datePicker) }
+ .create().apply {
+ activity?.setupDialogStuff(view, this)
+ }
+ }
+
+ private fun dateSelected(dateTime: DateTime, datePicker: DatePicker) {
+ val month = datePicker.month + 1
+ val year = datePicker.year
+ val day = datePicker.dayOfMonth
+ val newDateTime = dateTime.withDate(year, month, day)
+ goToDateTime(newDateTime)
+ }
+
override fun refreshEvents() {
(viewPager?.adapter as? MyDayPagerAdapter)?.updateCalendars(viewPager?.currentItem ?: 0)
}
diff --git a/app/src/main/kotlin/com/simplemobiletools/calendar/pro/fragments/EventListFragment.kt b/app/src/main/kotlin/com/simplemobiletools/calendar/pro/fragments/EventListFragment.kt
index f6d1d9802..9527b8eb3 100644
--- a/app/src/main/kotlin/com/simplemobiletools/calendar/pro/fragments/EventListFragment.kt
+++ b/app/src/main/kotlin/com/simplemobiletools/calendar/pro/fragments/EventListFragment.kt
@@ -173,8 +173,9 @@ class EventListFragment : MyFragmentHolder(), RefreshRecyclerViewListener {
checkEvents()
}
- override fun goToToday() {
- }
+ override fun goToToday() {}
+
+ override fun showGoToDateDialog() {}
override fun refreshEvents() {
checkEvents()
diff --git a/app/src/main/kotlin/com/simplemobiletools/calendar/pro/fragments/MonthFragment.kt b/app/src/main/kotlin/com/simplemobiletools/calendar/pro/fragments/MonthFragment.kt
index c114ef9b1..e83e21e08 100644
--- a/app/src/main/kotlin/com/simplemobiletools/calendar/pro/fragments/MonthFragment.kt
+++ b/app/src/main/kotlin/com/simplemobiletools/calendar/pro/fragments/MonthFragment.kt
@@ -6,9 +6,7 @@ import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
-import android.widget.DatePicker
import android.widget.RelativeLayout
-import androidx.appcompat.app.AlertDialog
import androidx.fragment.app.Fragment
import com.simplemobiletools.calendar.pro.R
import com.simplemobiletools.calendar.pro.activities.MainActivity
@@ -21,9 +19,6 @@ import com.simplemobiletools.calendar.pro.interfaces.MonthlyCalendar
import com.simplemobiletools.calendar.pro.interfaces.NavigationListener
import com.simplemobiletools.calendar.pro.models.DayMonthly
import com.simplemobiletools.commons.extensions.applyColorFilter
-import com.simplemobiletools.commons.extensions.beGone
-import com.simplemobiletools.commons.extensions.getDialogTheme
-import com.simplemobiletools.commons.extensions.setupDialogStuff
import kotlinx.android.synthetic.main.fragment_month.view.*
import kotlinx.android.synthetic.main.top_navigation.view.*
import org.joda.time.DateTime
@@ -100,6 +95,7 @@ class MonthFragment : Fragment(), MonthlyCalendar {
activity?.runOnUiThread {
mHolder.top_value.apply {
text = month
+ contentDescription = text
setTextColor(mConfig.textColor)
}
updateDays(days)
@@ -136,35 +132,11 @@ class MonthFragment : Fragment(), MonthlyCalendar {
mHolder.top_value.apply {
setTextColor(mConfig.textColor)
setOnClickListener {
- showMonthDialog()
+ (activity as MainActivity).showGoToDateDialog()
}
}
}
- private fun showMonthDialog() {
- activity!!.setTheme(context!!.getDialogTheme())
- val view = layoutInflater.inflate(R.layout.date_picker, null)
- val datePicker = view.findViewById(R.id.date_picker)
- datePicker.findViewById(Resources.getSystem().getIdentifier("day", "id", "android")).beGone()
-
- val dateTime = DateTime(mCalendar!!.mTargetDate.toString())
- datePicker.init(dateTime.year, dateTime.monthOfYear - 1, 1, null)
-
- AlertDialog.Builder(context!!)
- .setNegativeButton(R.string.cancel, null)
- .setPositiveButton(R.string.ok) { dialog, which -> positivePressed(dateTime, datePicker) }
- .create().apply {
- activity?.setupDialogStuff(view, this)
- }
- }
-
- private fun positivePressed(dateTime: DateTime, datePicker: DatePicker) {
- val month = datePicker.month + 1
- val year = datePicker.year
- val newDateTime = dateTime.withDate(year, month, 1)
- listener?.goToDateTime(newDateTime)
- }
-
private fun updateDays(days: ArrayList) {
mHolder.month_view_wrapper.updateDays(days) {
(activity as MainActivity).openDayFromMonthly(Formatter.getDateTimeFromCode(it.code))
diff --git a/app/src/main/kotlin/com/simplemobiletools/calendar/pro/fragments/MonthFragmentsHolder.kt b/app/src/main/kotlin/com/simplemobiletools/calendar/pro/fragments/MonthFragmentsHolder.kt
index 4fc125c97..e9eebd787 100644
--- a/app/src/main/kotlin/com/simplemobiletools/calendar/pro/fragments/MonthFragmentsHolder.kt
+++ b/app/src/main/kotlin/com/simplemobiletools/calendar/pro/fragments/MonthFragmentsHolder.kt
@@ -1,10 +1,13 @@
package com.simplemobiletools.calendar.pro.fragments
+import android.content.res.Resources
import android.graphics.drawable.ColorDrawable
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
+import android.widget.DatePicker
+import androidx.appcompat.app.AlertDialog
import androidx.viewpager.widget.ViewPager
import com.simplemobiletools.calendar.pro.R
import com.simplemobiletools.calendar.pro.activities.MainActivity
@@ -14,6 +17,9 @@ import com.simplemobiletools.calendar.pro.extensions.getMonthCode
import com.simplemobiletools.calendar.pro.helpers.DAY_CODE
import com.simplemobiletools.calendar.pro.helpers.Formatter
import com.simplemobiletools.calendar.pro.interfaces.NavigationListener
+import com.simplemobiletools.commons.extensions.beGone
+import com.simplemobiletools.commons.extensions.getDialogTheme
+import com.simplemobiletools.commons.extensions.setupDialogStuff
import com.simplemobiletools.commons.extensions.updateActionBarTitle
import com.simplemobiletools.commons.views.MyViewPager
import kotlinx.android.synthetic.main.fragment_months_holder.view.*
@@ -99,6 +105,30 @@ class MonthFragmentsHolder : MyFragmentHolder(), NavigationListener {
setupFragment()
}
+ override fun showGoToDateDialog() {
+ activity!!.setTheme(context!!.getDialogTheme())
+ val view = layoutInflater.inflate(R.layout.date_picker, null)
+ val datePicker = view.findViewById(R.id.date_picker)
+ datePicker.findViewById(Resources.getSystem().getIdentifier("day", "id", "android")).beGone()
+
+ val dateTime = DateTime(Formatter.getDateTimeFromCode(currentDayCode).toString())
+ datePicker.init(dateTime.year, dateTime.monthOfYear - 1, 1, null)
+
+ AlertDialog.Builder(context!!)
+ .setNegativeButton(R.string.cancel, null)
+ .setPositiveButton(R.string.ok) { dialog, which -> datePicked(dateTime, datePicker) }
+ .create().apply {
+ activity?.setupDialogStuff(view, this)
+ }
+ }
+
+ private fun datePicked(dateTime: DateTime, datePicker: DatePicker) {
+ val month = datePicker.month + 1
+ val year = datePicker.year
+ val newDateTime = dateTime.withDate(year, month, 1)
+ goToDateTime(newDateTime)
+ }
+
override fun refreshEvents() {
(viewPager?.adapter as? MyMonthPagerAdapter)?.updateCalendars(viewPager?.currentItem ?: 0)
}
diff --git a/app/src/main/kotlin/com/simplemobiletools/calendar/pro/fragments/MyFragmentHolder.kt b/app/src/main/kotlin/com/simplemobiletools/calendar/pro/fragments/MyFragmentHolder.kt
index 49dcb46a4..a7708e98e 100644
--- a/app/src/main/kotlin/com/simplemobiletools/calendar/pro/fragments/MyFragmentHolder.kt
+++ b/app/src/main/kotlin/com/simplemobiletools/calendar/pro/fragments/MyFragmentHolder.kt
@@ -5,6 +5,8 @@ import androidx.fragment.app.Fragment
abstract class MyFragmentHolder : Fragment() {
abstract fun goToToday()
+ abstract fun showGoToDateDialog()
+
abstract fun refreshEvents()
abstract fun shouldGoToTodayBeVisible(): Boolean
diff --git a/app/src/main/kotlin/com/simplemobiletools/calendar/pro/fragments/WeekFragment.kt b/app/src/main/kotlin/com/simplemobiletools/calendar/pro/fragments/WeekFragment.kt
index b5f346b6b..f67f605f2 100644
--- a/app/src/main/kotlin/com/simplemobiletools/calendar/pro/fragments/WeekFragment.kt
+++ b/app/src/main/kotlin/com/simplemobiletools/calendar/pro/fragments/WeekFragment.kt
@@ -322,6 +322,7 @@ class WeekFragment : Fragment(), WeeklyCalendar {
background = ColorDrawable(backgroundColor)
setTextColor(textColor)
text = event.title
+ contentDescription = text
layout.addView(this)
y = startMinutes * minuteHeight
(layoutParams as RelativeLayout.LayoutParams).apply {
@@ -406,6 +407,7 @@ class WeekFragment : Fragment(), WeeklyCalendar {
setTextColor(textColor)
text = event.title
+ contentDescription = text
val startDateTime = Formatter.getDateTimeFromTS(event.startTS)
val endDateTime = Formatter.getDateTimeFromTS(event.endTS)
diff --git a/app/src/main/kotlin/com/simplemobiletools/calendar/pro/fragments/WeekFragmentsHolder.kt b/app/src/main/kotlin/com/simplemobiletools/calendar/pro/fragments/WeekFragmentsHolder.kt
index f8285522f..257785003 100644
--- a/app/src/main/kotlin/com/simplemobiletools/calendar/pro/fragments/WeekFragmentsHolder.kt
+++ b/app/src/main/kotlin/com/simplemobiletools/calendar/pro/fragments/WeekFragmentsHolder.kt
@@ -5,7 +5,9 @@ import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
+import android.widget.DatePicker
import android.widget.TextView
+import androidx.appcompat.app.AlertDialog
import androidx.viewpager.widget.ViewPager
import com.simplemobiletools.calendar.pro.R
import com.simplemobiletools.calendar.pro.activities.MainActivity
@@ -16,6 +18,8 @@ import com.simplemobiletools.calendar.pro.helpers.Formatter
import com.simplemobiletools.calendar.pro.helpers.WEEK_START_DATE_TIME
import com.simplemobiletools.calendar.pro.interfaces.WeekFragmentListener
import com.simplemobiletools.calendar.pro.views.MyScrollView
+import com.simplemobiletools.commons.extensions.getDialogTheme
+import com.simplemobiletools.commons.extensions.setupDialogStuff
import com.simplemobiletools.commons.extensions.updateActionBarSubtitle
import com.simplemobiletools.commons.extensions.updateActionBarTitle
import com.simplemobiletools.commons.helpers.WEEK_SECONDS
@@ -100,10 +104,11 @@ class WeekFragmentsHolder : MyFragmentHolder(), WeekFragmentListener {
private fun getWeekTimestamps(targetSeconds: Long): List {
val weekTSs = ArrayList(PREFILLED_WEEKS)
- var currWeekTS = targetSeconds - (PREFILLED_WEEKS / 2 * WEEK_SECONDS)
+ val dateTime = Formatter.getDateTimeFromTS(targetSeconds)
+ var currentWeek = dateTime.minusWeeks(PREFILLED_WEEKS / 2)
for (i in 0 until PREFILLED_WEEKS) {
- weekTSs.add(currWeekTS)
- currWeekTS += WEEK_SECONDS
+ weekTSs.add(currentWeek.seconds())
+ currentWeek = currentWeek.plusWeeks(1)
}
return weekTSs
}
@@ -130,6 +135,42 @@ class WeekFragmentsHolder : MyFragmentHolder(), WeekFragmentListener {
setupFragment()
}
+ override fun showGoToDateDialog() {
+ activity!!.setTheme(context!!.getDialogTheme())
+ val view = layoutInflater.inflate(R.layout.date_picker, null)
+ val datePicker = view.findViewById(R.id.date_picker)
+
+ val dateTime = Formatter.getDateTimeFromTS(currentWeekTS)
+ datePicker.init(dateTime.year, dateTime.monthOfYear - 1, dateTime.dayOfMonth, null)
+
+ AlertDialog.Builder(context!!)
+ .setNegativeButton(R.string.cancel, null)
+ .setPositiveButton(R.string.ok) { dialog, which -> dateSelected(dateTime, datePicker) }
+ .create().apply {
+ activity?.setupDialogStuff(view, this)
+ }
+ }
+
+ private fun dateSelected(dateTime: DateTime, datePicker: DatePicker) {
+ val isSundayFirst = context!!.config.isSundayFirst
+ val month = datePicker.month + 1
+ val year = datePicker.year
+ val day = datePicker.dayOfMonth
+ var newDateTime = dateTime.withDate(year, month, day)
+
+ if (isSundayFirst) {
+ newDateTime = newDateTime.plusDays(1)
+ }
+
+ var selectedWeek = newDateTime.withDayOfWeek(1).withTimeAtStartOfDay().minusDays(if (isSundayFirst) 1 else 0)
+ if (newDateTime.minusDays(7).seconds() > selectedWeek.seconds()) {
+ selectedWeek = selectedWeek.plusDays(7)
+ }
+
+ currentWeekTS = selectedWeek.seconds()
+ setupFragment()
+ }
+
override fun refreshEvents() {
(viewPager?.adapter as? MyWeekPagerAdapter)?.updateCalendars(viewPager!!.currentItem)
}
diff --git a/app/src/main/kotlin/com/simplemobiletools/calendar/pro/fragments/YearFragmentsHolder.kt b/app/src/main/kotlin/com/simplemobiletools/calendar/pro/fragments/YearFragmentsHolder.kt
index 2fd6f8478..ff3221d8f 100644
--- a/app/src/main/kotlin/com/simplemobiletools/calendar/pro/fragments/YearFragmentsHolder.kt
+++ b/app/src/main/kotlin/com/simplemobiletools/calendar/pro/fragments/YearFragmentsHolder.kt
@@ -1,16 +1,22 @@
package com.simplemobiletools.calendar.pro.fragments
+import android.content.res.Resources
import android.graphics.drawable.ColorDrawable
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
+import android.widget.DatePicker
+import androidx.appcompat.app.AlertDialog
import androidx.viewpager.widget.ViewPager
import com.simplemobiletools.calendar.pro.R
import com.simplemobiletools.calendar.pro.activities.MainActivity
import com.simplemobiletools.calendar.pro.adapters.MyYearPagerAdapter
import com.simplemobiletools.calendar.pro.extensions.config
import com.simplemobiletools.calendar.pro.helpers.Formatter
+import com.simplemobiletools.commons.extensions.beGone
+import com.simplemobiletools.commons.extensions.getDialogTheme
+import com.simplemobiletools.commons.extensions.setupDialogStuff
import com.simplemobiletools.commons.extensions.updateActionBarTitle
import com.simplemobiletools.commons.views.MyViewPager
import kotlinx.android.synthetic.main.fragment_years_holder.view.*
@@ -83,6 +89,32 @@ class YearFragmentsHolder : MyFragmentHolder() {
setupFragment()
}
+ override fun showGoToDateDialog() {
+ activity!!.setTheme(context!!.getDialogTheme())
+ val view = layoutInflater.inflate(R.layout.date_picker, null)
+ val datePicker = view.findViewById(R.id.date_picker)
+ datePicker.findViewById(Resources.getSystem().getIdentifier("day", "id", "android")).beGone()
+ datePicker.findViewById(Resources.getSystem().getIdentifier("month", "id", "android")).beGone()
+
+ val dateTime = DateTime(Formatter.getDateTimeFromCode("${currentYear}0523").toString())
+ datePicker.init(dateTime.year, dateTime.monthOfYear - 1, 1, null)
+
+ AlertDialog.Builder(context!!)
+ .setNegativeButton(R.string.cancel, null)
+ .setPositiveButton(R.string.ok) { dialog, which -> datePicked(datePicker) }
+ .create().apply {
+ activity?.setupDialogStuff(view, this)
+ }
+ }
+
+ private fun datePicked(datePicker: DatePicker) {
+ val pickedYear = datePicker.year
+ if (currentYear != pickedYear) {
+ currentYear = datePicker.year
+ setupFragment()
+ }
+ }
+
override fun refreshEvents() {
(viewPager?.adapter as? MyYearPagerAdapter)?.updateCalendars(viewPager?.currentItem ?: 0)
}
diff --git a/app/src/main/kotlin/com/simplemobiletools/calendar/pro/helpers/CalDAVHelper.kt b/app/src/main/kotlin/com/simplemobiletools/calendar/pro/helpers/CalDAVHelper.kt
index 55c15c003..1bc9af72f 100644
--- a/app/src/main/kotlin/com/simplemobiletools/calendar/pro/helpers/CalDAVHelper.kt
+++ b/app/src/main/kotlin/com/simplemobiletools/calendar/pro/helpers/CalDAVHelper.kt
@@ -8,11 +8,11 @@ import android.database.Cursor
import android.provider.CalendarContract
import android.provider.CalendarContract.Reminders
import android.util.SparseIntArray
+import com.google.gson.Gson
+import com.google.gson.reflect.TypeToken
import com.simplemobiletools.calendar.pro.R
import com.simplemobiletools.calendar.pro.extensions.*
-import com.simplemobiletools.calendar.pro.models.CalDAVCalendar
-import com.simplemobiletools.calendar.pro.models.Event
-import com.simplemobiletools.calendar.pro.models.EventType
+import com.simplemobiletools.calendar.pro.models.*
import com.simplemobiletools.calendar.pro.objects.States.isUpdatingCalDAV
import com.simplemobiletools.commons.extensions.*
import com.simplemobiletools.commons.helpers.PERMISSION_READ_CALENDAR
@@ -52,7 +52,7 @@ class CalDAVHelper(val context: Context) {
}
@SuppressLint("MissingPermission")
- fun getCalDAVCalendars(ids: String, showToasts: Boolean): List {
+ fun getCalDAVCalendars(ids: String, showToasts: Boolean): ArrayList {
val calendars = ArrayList()
if (!context.hasPermission(PERMISSION_WRITE_CALENDAR) || !context.hasPermission(PERMISSION_READ_CALENDAR)) {
return calendars
@@ -72,7 +72,7 @@ class CalDAVHelper(val context: Context) {
var cursor: Cursor? = null
try {
cursor = context.contentResolver.query(uri, projection, selection, null, null)
- if (cursor != null && cursor.moveToFirst()) {
+ if (cursor?.moveToFirst() == true) {
do {
val id = cursor.getIntValue(CalendarContract.Calendars._ID)
val displayName = cursor.getStringValue(CalendarContract.Calendars.CALENDAR_DISPLAY_NAME)
@@ -144,7 +144,7 @@ class CalDAVHelper(val context: Context) {
var cursor: Cursor? = null
try {
cursor = context.contentResolver.query(uri, projection, selection, selectionArgs, null)
- if (cursor != null && cursor.moveToFirst()) {
+ if (cursor?.moveToFirst() == true) {
do {
val colorKey = cursor.getIntValue(CalendarContract.Colors.COLOR_KEY)
val color = cursor.getIntValue(CalendarContract.Colors.COLOR)
@@ -192,39 +192,37 @@ class CalDAVHelper(val context: Context) {
var cursor: Cursor? = null
try {
cursor = context.contentResolver.query(uri, projection, selection, null, null)
- if (cursor != null && cursor.moveToFirst()) {
+ if (cursor?.moveToFirst() == true) {
do {
val id = cursor.getLongValue(CalendarContract.Events._ID)
val title = cursor.getStringValue(CalendarContract.Events.TITLE) ?: ""
val description = cursor.getStringValue(CalendarContract.Events.DESCRIPTION) ?: ""
- var startTS = cursor.getLongValue(CalendarContract.Events.DTSTART)
- var endTS = cursor.getLongValue(CalendarContract.Events.DTEND)
+ 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)
-
- if (startTS.toString().length == 12 || startTS.toString().length == 13) {
- startTS /= 1000L
- }
-
- if (endTS.toString().length == 12 || endTS.toString().length == 13) {
- endTS /= 1000L
- }
+ val attendees = Gson().toJson(getCalDAVEventAttendees(id))
if (endTS == 0L) {
val duration = cursor.getStringValue(CalendarContract.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 source = "$CALDAV-$calendarId"
val repeatRule = Parser().parseRepeatInterval(rrule, startTS)
- val event = Event(null, startTS, endTS, title, location, description, reminders.getOrElse(0) { -1 },
- reminders.getOrElse(1) { -1 }, reminders.getOrElse(2) { -1 }, repeatRule.repeatInterval, repeatRule.repeatRule,
- repeatRule.repeatLimit, ArrayList(), importId, allDay, eventTypeId, source = source)
+ 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, allDay, eventTypeId, source = source)
if (event.getIsAllDay()) {
event.startTS = Formatter.getShiftedImportTimestamp(event.startTS)
@@ -326,6 +324,7 @@ class CalDAVHelper(val context: Context) {
event.importId = getCalDAVEventImportId(calendarId, eventRemoteID)
setupCalDAVEventReminders(event)
+ setupCalDAVEventAttendees(event)
setupCalDAVEventImportId(event)
refreshCalDAVCalendar(event)
}
@@ -340,18 +339,18 @@ class CalDAVHelper(val context: Context) {
context.contentResolver.update(newUri, values, null, null)
setupCalDAVEventReminders(event)
+ setupCalDAVEventAttendees(event)
setupCalDAVEventImportId(event)
refreshCalDAVCalendar(event)
}
- @SuppressLint("MissingPermission")
private fun setupCalDAVEventReminders(event: Event) {
clearEventReminders(event)
event.getReminders().forEach {
val contentValues = ContentValues().apply {
- put(Reminders.MINUTES, it)
+ put(Reminders.MINUTES, it.minutes)
+ put(Reminders.METHOD, if (it.type == REMINDER_EMAIL) Reminders.METHOD_EMAIL else Reminders.METHOD_ALERT)
put(Reminders.EVENT_ID, event.getCalDAVEventId())
- put(Reminders.METHOD, Reminders.METHOD_ALERT)
}
try {
@@ -362,6 +361,25 @@ class CalDAVHelper(val context: Context) {
}
}
+ private fun setupCalDAVEventAttendees(event: Event) {
+ clearEventAttendees(event)
+ val attendees = Gson().fromJson>(event.attendees, object : TypeToken>() {}.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, CalendarContract.Attendees.ATTENDEE_STATUS_ACCEPTED)
+ put(CalendarContract.Attendees.EVENT_ID, event.getCalDAVEventId())
+ }
+
+ try {
+ context.contentResolver.insert(CalendarContract.Attendees.CONTENT_URI, contentValues)
+ } catch (e: Exception) {
+ context.toast(R.string.unknown_error_occurred)
+ }
+ }
+ }
+
private fun setupCalDAVEventImportId(event: Event) {
context.eventsDB.updateEventImportIdAndSource(event.importId, "$CALDAV-${event.getCalDAVCalendarId()}", event.id!!)
}
@@ -397,13 +415,18 @@ class CalDAVHelper(val context: Context) {
}
}
- @SuppressLint("MissingPermission")
private fun clearEventReminders(event: Event) {
val selection = "${Reminders.EVENT_ID} = ?"
val selectionArgs = arrayOf(event.getCalDAVEventId().toString())
context.contentResolver.delete(Reminders.CONTENT_URI, selection, selectionArgs)
}
+ private fun clearEventAttendees(event: Event) {
+ val selection = "${CalendarContract.Attendees.EVENT_ID} = ?"
+ val selectionArgs = arrayOf(event.getCalDAVEventId().toString())
+ context.contentResolver.delete(CalendarContract.Attendees.CONTENT_URI, selection, selectionArgs)
+ }
+
private fun getDurationCode(event: Event): String {
return if (event.getIsAllDay()) {
val dur = Math.max(1, (event.endTS - event.startTS) / DAY)
@@ -428,7 +451,6 @@ class CalDAVHelper(val context: Context) {
refreshCalDAVCalendar(event)
}
- @SuppressLint("MissingPermission")
fun insertEventRepeatException(event: Event, occurrenceTS: Long): Long {
val uri = CalendarContract.Events.CONTENT_URI
val values = fillEventRepeatExceptionValues(event, occurrenceTS)
@@ -449,9 +471,8 @@ class CalDAVHelper(val context: Context) {
}
}
- @SuppressLint("MissingPermission")
- private fun getCalDAVEventReminders(eventId: Long): List {
- val reminders = ArrayList()
+ private fun getCalDAVEventReminders(eventId: Long): List {
+ val reminders = ArrayList()
val uri = CalendarContract.Reminders.CONTENT_URI
val projection = arrayOf(
CalendarContract.Reminders.MINUTES,
@@ -460,19 +481,47 @@ class CalDAVHelper(val context: Context) {
var cursor: Cursor? = null
try {
cursor = context.contentResolver.query(uri, projection, selection, null, null)
- if (cursor != null && cursor.moveToFirst()) {
+ if (cursor?.moveToFirst() == true) {
do {
val minutes = cursor.getIntValue(CalendarContract.Reminders.MINUTES)
val method = cursor.getIntValue(CalendarContract.Reminders.METHOD)
- if (method == CalendarContract.Reminders.METHOD_ALERT) {
- reminders.add(minutes)
+ if (method == CalendarContract.Reminders.METHOD_ALERT || method == CalendarContract.Reminders.METHOD_EMAIL) {
+ val type = if (method == CalendarContract.Reminders.METHOD_EMAIL) REMINDER_EMAIL else REMINDER_NOTIFICATION
+ val reminder = Reminder(minutes, type)
+ reminders.add(reminder)
}
} while (cursor.moveToNext())
}
} finally {
cursor?.close()
}
- return reminders
+ return reminders.sortedBy { it.minutes }
+ }
+
+ private fun getCalDAVEventAttendees(eventId: Long): List {
+ val attendees = ArrayList()
+ val uri = CalendarContract.Attendees.CONTENT_URI
+ val projection = arrayOf(
+ CalendarContract.Attendees.ATTENDEE_NAME,
+ CalendarContract.Attendees.ATTENDEE_EMAIL,
+ CalendarContract.Attendees.ATTENDEE_STATUS)
+ 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 attendee = Attendee(0, name, email, status, "")
+ attendees.add(attendee)
+ } while (cursor.moveToNext())
+ }
+ } finally {
+ cursor?.close()
+ }
+ return attendees
}
private fun getCalDAVEventImportId(calendarId: Int, eventId: Long) = "$CALDAV-$calendarId-$eventId"
diff --git a/app/src/main/kotlin/com/simplemobiletools/calendar/pro/helpers/Constants.kt b/app/src/main/kotlin/com/simplemobiletools/calendar/pro/helpers/Constants.kt
index 9ef594fe4..4878387c7 100644
--- a/app/src/main/kotlin/com/simplemobiletools/calendar/pro/helpers/Constants.kt
+++ b/app/src/main/kotlin/com/simplemobiletools/calendar/pro/helpers/Constants.kt
@@ -15,6 +15,7 @@ const val NEW_EVENT_SET_HOUR_DURATION = "new_event_set_hour_duration"
const val WEEK_START_DATE_TIME = "week_start_date_time"
const val CALDAV = "Caldav"
const val VIEW_TO_OPEN = "view_to_open"
+const val SHORTCUT_NEW_EVENT = "shortcut_new_event"
const val REGULAR_EVENT_TYPE_ID = 1L
const val CHOPPED_LIST_DEFAULT_SIZE = 100
@@ -99,6 +100,8 @@ const val SUMMARY = "SUMMARY"
const val DESCRIPTION = "DESCRIPTION:"
const val UID = "UID:"
const val ACTION = "ACTION:"
+const val ATTENDEE = "ATTENDEE:"
+const val MAILTO = "mailto:"
const val TRIGGER = "TRIGGER:"
const val RRULE = "RRULE:"
const val CATEGORIES = "CATEGORIES:"
@@ -115,6 +118,7 @@ const val SEQUENCE = "SEQUENCE"
const val CATEGORY_COLOR = "CATEGORY_COLOR:"
const val DISPLAY = "DISPLAY"
+const val EMAIL = "EMAIL"
const val FREQ = "FREQ"
const val UNTIL = "UNTIL"
const val COUNT = "COUNT"
@@ -150,4 +154,7 @@ const val DELETE_SELECTED_OCCURRENCE = 0
const val DELETE_FUTURE_OCCURRENCES = 1
const val DELETE_ALL_OCCURRENCES = 2
+const val REMINDER_NOTIFICATION = 0
+const val REMINDER_EMAIL = 1
+
fun getNowSeconds() = System.currentTimeMillis() / 1000L
diff --git a/app/src/main/kotlin/com/simplemobiletools/calendar/pro/helpers/EventsHelper.kt b/app/src/main/kotlin/com/simplemobiletools/calendar/pro/helpers/EventsHelper.kt
index 52aa5af7f..aee17da2b 100644
--- a/app/src/main/kotlin/com/simplemobiletools/calendar/pro/helpers/EventsHelper.kt
+++ b/app/src/main/kotlin/com/simplemobiletools/calendar/pro/helpers/EventsHelper.kt
@@ -253,9 +253,10 @@ 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) ?: context.resources.getColor(R.color.color_primary)
+ it.color = eventTypeColors.get(it.eventType) ?: primaryColor
}
callback(events)
diff --git a/app/src/main/kotlin/com/simplemobiletools/calendar/pro/helpers/IcsExporter.kt b/app/src/main/kotlin/com/simplemobiletools/calendar/pro/helpers/IcsExporter.kt
index 35a959666..1c7355b3c 100644
--- a/app/src/main/kotlin/com/simplemobiletools/calendar/pro/helpers/IcsExporter.kt
+++ b/app/src/main/kotlin/com/simplemobiletools/calendar/pro/helpers/IcsExporter.kt
@@ -1,8 +1,10 @@
package com.simplemobiletools.calendar.pro.helpers
import com.simplemobiletools.calendar.pro.R
+import com.simplemobiletools.calendar.pro.extensions.calDAVHelper
import com.simplemobiletools.calendar.pro.extensions.eventTypesDB
import com.simplemobiletools.calendar.pro.helpers.IcsExporter.ExportResult.*
+import com.simplemobiletools.calendar.pro.models.CalDAVCalendar
import com.simplemobiletools.calendar.pro.models.Event
import com.simplemobiletools.commons.activities.BaseSimpleActivity
import com.simplemobiletools.commons.extensions.getFileOutputStream
@@ -19,6 +21,7 @@ class IcsExporter {
private var eventsExported = 0
private var eventsFailed = 0
+ private var calendars = ArrayList()
fun exportEvents(activity: BaseSimpleActivity, file: File, events: ArrayList, showExportingToast: Boolean, callback: (result: ExportResult) -> Unit) {
val fileDirItem = FileDirItem(file.absolutePath, file.name)
@@ -29,6 +32,7 @@ class IcsExporter {
}
Thread {
+ calendars = activity.calDAVHelper.getCalDAVCalendars("", false)
if (showExportingToast) {
activity.toast(R.string.exporting)
}
@@ -77,17 +81,20 @@ class IcsExporter {
}
private fun fillReminders(event: Event, out: BufferedWriter) {
- checkReminder(event.reminder1Minutes, out)
- checkReminder(event.reminder2Minutes, out)
- checkReminder(event.reminder3Minutes, out)
- }
-
- private fun checkReminder(minutes: Int, out: BufferedWriter) {
- if (minutes != -1) {
+ event.getReminders().forEach {
+ val reminder = it
out.apply {
writeLn(BEGIN_ALARM)
- writeLn("$ACTION$DISPLAY")
- writeLn("$TRIGGER-${Parser().getDurationCode(minutes.toLong())}")
+ if (reminder.type == REMINDER_NOTIFICATION) {
+ writeLn("$ACTION$DISPLAY")
+ } else {
+ writeLn("$ACTION$EMAIL")
+ val attendee = calendars.firstOrNull { it.id == event.getCalDAVCalendarId()}?.accountName
+ if (attendee != null) {
+ writeLn("$ATTENDEE$MAILTO$attendee")
+ }
+ }
+ writeLn("$TRIGGER-${Parser().getDurationCode(reminder.minutes.toLong())}")
writeLn(END_ALARM)
}
}
diff --git a/app/src/main/kotlin/com/simplemobiletools/calendar/pro/helpers/IcsImporter.kt b/app/src/main/kotlin/com/simplemobiletools/calendar/pro/helpers/IcsImporter.kt
index 2595b7669..c834bb5dd 100644
--- a/app/src/main/kotlin/com/simplemobiletools/calendar/pro/helpers/IcsImporter.kt
+++ b/app/src/main/kotlin/com/simplemobiletools/calendar/pro/helpers/IcsImporter.kt
@@ -8,6 +8,7 @@ import com.simplemobiletools.calendar.pro.extensions.eventsHelper
import com.simplemobiletools.calendar.pro.helpers.IcsImporter.ImportResult.*
import com.simplemobiletools.calendar.pro.models.Event
import com.simplemobiletools.calendar.pro.models.EventType
+import com.simplemobiletools.calendar.pro.models.Reminder
import com.simplemobiletools.commons.extensions.areDigitsOnly
import com.simplemobiletools.commons.extensions.showErrorToast
import java.io.File
@@ -26,6 +27,7 @@ class IcsImporter(val activity: SimpleActivity) {
private var curRecurrenceDayCode = ""
private var curFlags = 0
private var curReminderMinutes = ArrayList()
+ private var curReminderActions = ArrayList()
private var curRepeatExceptions = ArrayList()
private var curRepeatInterval = 0
private var curRepeatLimit = 0L
@@ -37,7 +39,8 @@ class IcsImporter(val activity: SimpleActivity) {
private var isProperReminderAction = false
private var isDescription = false
private var isSequence = false
- private var curReminderTriggerMinutes = -1
+ private var curReminderTriggerMinutes = REMINDER_OFF
+ private var curReminderTriggerAction = REMINDER_NOTIFICATION
private val eventsHelper = activity.eventsHelper
private var eventsImported = 0
@@ -101,7 +104,11 @@ class IcsImporter(val activity: SimpleActivity) {
curRepeatLimit = repeatRule.repeatLimit
} else if (line.startsWith(ACTION)) {
isNotificationDescription = true
- isProperReminderAction = line.substring(ACTION.length) == DISPLAY
+ val action = line.substring(ACTION.length)
+ isProperReminderAction = action == DISPLAY || action == EMAIL
+ if (isProperReminderAction) {
+ curReminderTriggerAction = if (action == DISPLAY) REMINDER_NOTIFICATION else REMINDER_EMAIL
+ }
} else if (line.startsWith(TRIGGER)) {
curReminderTriggerMinutes = Parser().parseDurationSeconds(line.substring(TRIGGER.length)) / 60
} else if (line.startsWith(CATEGORY_COLOR)) {
@@ -129,8 +136,9 @@ class IcsImporter(val activity: SimpleActivity) {
} else if (line.startsWith(SEQUENCE)) {
isSequence = true
} else if (line == END_ALARM) {
- if (isProperReminderAction && curReminderTriggerMinutes != -1) {
+ if (isProperReminderAction && curReminderTriggerMinutes != REMINDER_OFF) {
curReminderMinutes.add(curReminderTriggerMinutes)
+ curReminderActions.add(curReminderTriggerAction)
}
} else if (line == END_EVENT) {
if (curStart != -1L && curEnd == -1L) {
@@ -147,11 +155,18 @@ class IcsImporter(val activity: SimpleActivity) {
continue
}
+ var reminders = arrayListOf(
+ Reminder(curReminderMinutes.getOrElse(0) { REMINDER_OFF }, curReminderActions.getOrElse(0) { REMINDER_NOTIFICATION }),
+ Reminder(curReminderMinutes.getOrElse(1) { REMINDER_OFF }, curReminderActions.getOrElse(1) { REMINDER_NOTIFICATION }),
+ Reminder(curReminderMinutes.getOrElse(2) { REMINDER_OFF }, curReminderActions.getOrElse(2) { REMINDER_NOTIFICATION })
+ )
+ reminders = reminders.filter { it.minutes != REMINDER_OFF }.sortedBy { it.minutes }.toMutableList() as ArrayList
+
val eventType = eventTypes.firstOrNull { it.id == curEventTypeId }
val source = if (calDAVCalendarId == 0 || eventType?.isSyncedEventType() == false) SOURCE_IMPORTED_ICS else "$CALDAV-$calDAVCalendarId"
- val event = Event(null, curStart, curEnd, curTitle, curLocation, curDescription, curReminderMinutes.getOrElse(0) { -1 },
- curReminderMinutes.getOrElse(1) { -1 }, curReminderMinutes.getOrElse(2) { -1 }, curRepeatInterval, curRepeatRule,
- curRepeatLimit, curRepeatExceptions, curImportId, curFlags, curEventTypeId, 0, curLastModified, source)
+ val event = Event(null, curStart, curEnd, curTitle, curLocation, curDescription, reminders[0].minutes,
+ reminders[1].minutes, reminders[2].minutes, reminders[0].type, reminders[1].type, reminders[2].type, curRepeatInterval, curRepeatRule,
+ curRepeatLimit, curRepeatExceptions, "", curImportId, curFlags, curEventTypeId, 0, curLastModified, source)
if (event.getIsAllDay() && curEnd > curStart) {
event.endTS -= DAY
@@ -269,6 +284,7 @@ class IcsImporter(val activity: SimpleActivity) {
curRecurrenceDayCode = ""
curFlags = 0
curReminderMinutes = ArrayList()
+ curReminderActions = ArrayList()
curRepeatExceptions = ArrayList()
curRepeatInterval = 0
curRepeatLimit = 0L
@@ -279,6 +295,7 @@ class IcsImporter(val activity: SimpleActivity) {
isNotificationDescription = false
isProperReminderAction = false
isSequence = false
- curReminderTriggerMinutes = -1
+ curReminderTriggerMinutes = REMINDER_OFF
+ curReminderTriggerAction = REMINDER_NOTIFICATION
}
}
diff --git a/app/src/main/kotlin/com/simplemobiletools/calendar/pro/models/Attendee.kt b/app/src/main/kotlin/com/simplemobiletools/calendar/pro/models/Attendee.kt
new file mode 100644
index 000000000..74ebfa8d7
--- /dev/null
+++ b/app/src/main/kotlin/com/simplemobiletools/calendar/pro/models/Attendee.kt
@@ -0,0 +1,5 @@
+package com.simplemobiletools.calendar.pro.models
+
+data class Attendee(val contactId: Int, var name: String, val email: String, val status: Int, var photoUri: String) {
+ fun getPublicName() = if (name.isNotEmpty()) name else email
+}
diff --git a/app/src/main/kotlin/com/simplemobiletools/calendar/pro/models/Event.kt b/app/src/main/kotlin/com/simplemobiletools/calendar/pro/models/Event.kt
index 01e617bd5..72c2dd1f7 100644
--- a/app/src/main/kotlin/com/simplemobiletools/calendar/pro/models/Event.kt
+++ b/app/src/main/kotlin/com/simplemobiletools/calendar/pro/models/Event.kt
@@ -22,10 +22,14 @@ data class Event(
@ColumnInfo(name = "reminder_1_minutes") var reminder1Minutes: Int = -1,
@ColumnInfo(name = "reminder_2_minutes") var reminder2Minutes: Int = -1,
@ColumnInfo(name = "reminder_3_minutes") var reminder3Minutes: Int = -1,
+ @ColumnInfo(name = "reminder_1_type") var reminder1Type: Int = REMINDER_NOTIFICATION,
+ @ColumnInfo(name = "reminder_2_type") var reminder2Type: Int = REMINDER_NOTIFICATION,
+ @ColumnInfo(name = "reminder_3_type") var reminder3Type: Int = REMINDER_NOTIFICATION,
@ColumnInfo(name = "repeat_interval") var repeatInterval: Int = 0,
@ColumnInfo(name = "repeat_rule") var repeatRule: Int = 0,
@ColumnInfo(name = "repeat_limit") var repeatLimit: Long = 0L,
@ColumnInfo(name = "repetition_exceptions") var repetitionExceptions: ArrayList = ArrayList(),
+ @ColumnInfo(name = "attendees") var attendees: String = "",
@ColumnInfo(name = "import_id") var importId: String = "",
@ColumnInfo(name = "flags") var flags: Int = 0,
@ColumnInfo(name = "event_type") var eventType: Long = REGULAR_EVENT_TYPE_ID,
@@ -80,7 +84,7 @@ data class Event(
while (newDateTime.dayOfMonth().maximumValue < Formatter.getDateTimeFromTS(original.startTS).dayOfMonth().maximumValue) {
newDateTime = newDateTime.plusMonths(repeatInterval / MONTH)
- newDateTime = newDateTime.withDayOfMonth(newDateTime.dayOfMonth().maximumValue)
+ newDateTime = newDateTime.withDayOfMonth(currStart.dayOfMonth)
}
return newDateTime
}
@@ -116,7 +120,11 @@ data class Event(
fun getIsAllDay() = flags and FLAG_ALL_DAY != 0
- fun getReminders() = setOf(reminder1Minutes, reminder2Minutes, reminder3Minutes).filter { it != REMINDER_OFF }
+ fun getReminders() = setOf(
+ Reminder(reminder1Minutes, reminder1Type),
+ Reminder(reminder2Minutes, reminder2Type),
+ Reminder(reminder3Minutes, reminder3Type)
+ ).filter { it.minutes != REMINDER_OFF }
// properly return the start time of all-day events as midnight
fun getEventStartTS(): Long {
diff --git a/app/src/main/kotlin/com/simplemobiletools/calendar/pro/models/Reminder.kt b/app/src/main/kotlin/com/simplemobiletools/calendar/pro/models/Reminder.kt
new file mode 100644
index 000000000..6f9b34f5b
--- /dev/null
+++ b/app/src/main/kotlin/com/simplemobiletools/calendar/pro/models/Reminder.kt
@@ -0,0 +1,3 @@
+package com.simplemobiletools.calendar.pro.models
+
+data class Reminder(val minutes: Int, val type: Int)
diff --git a/app/src/main/kotlin/com/simplemobiletools/calendar/pro/receivers/NotificationReceiver.kt b/app/src/main/kotlin/com/simplemobiletools/calendar/pro/receivers/NotificationReceiver.kt
index 37996827d..58e905633 100644
--- a/app/src/main/kotlin/com/simplemobiletools/calendar/pro/receivers/NotificationReceiver.kt
+++ b/app/src/main/kotlin/com/simplemobiletools/calendar/pro/receivers/NotificationReceiver.kt
@@ -10,6 +10,7 @@ import com.simplemobiletools.calendar.pro.extensions.scheduleNextEventReminder
import com.simplemobiletools.calendar.pro.extensions.updateListWidget
import com.simplemobiletools.calendar.pro.helpers.EVENT_ID
import com.simplemobiletools.calendar.pro.helpers.Formatter
+import com.simplemobiletools.calendar.pro.helpers.REMINDER_NOTIFICATION
class NotificationReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
@@ -30,7 +31,7 @@ class NotificationReceiver : BroadcastReceiver() {
context.updateListWidget()
val event = context.eventsDB.getEventWithId(id)
- if (event == null || event.getReminders().isEmpty()) {
+ if (event == null || event.getReminders().none { it.type == REMINDER_NOTIFICATION }) {
return
}
diff --git a/app/src/main/res/drawable-hdpi/ic_people.png b/app/src/main/res/drawable-hdpi/ic_people.png
new file mode 100755
index 000000000..26691bec8
Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_people.png differ
diff --git a/app/src/main/res/drawable-xhdpi/ic_people.png b/app/src/main/res/drawable-xhdpi/ic_people.png
new file mode 100755
index 000000000..93f23280c
Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_people.png differ
diff --git a/app/src/main/res/drawable-xxhdpi/ic_people.png b/app/src/main/res/drawable-xxhdpi/ic_people.png
new file mode 100755
index 000000000..6695583fe
Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_people.png differ
diff --git a/app/src/main/res/drawable-xxxhdpi/ic_people.png b/app/src/main/res/drawable-xxxhdpi/ic_people.png
new file mode 100755
index 000000000..594db41ff
Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_people.png differ
diff --git a/app/src/main/res/drawable/attendee_circular_background.xml b/app/src/main/res/drawable/attendee_circular_background.xml
new file mode 100644
index 000000000..4b622c6eb
--- /dev/null
+++ b/app/src/main/res/drawable/attendee_circular_background.xml
@@ -0,0 +1,16 @@
+
+
+ -
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/activity_event.xml b/app/src/main/res/layout/activity_event.xml
index a0e3bfe5d..7b1bfe893 100644
--- a/app/src/main/res/layout/activity_event.xml
+++ b/app/src/main/res/layout/activity_event.xml
@@ -50,9 +50,7 @@
android:layout_alignTop="@+id/event_location"
android:layout_alignBottom="@+id/event_location"
android:layout_alignParentEnd="true"
- android:layout_alignParentRight="true"
android:layout_marginEnd="@dimen/activity_margin"
- android:layout_marginRight="@dimen/activity_margin"
android:background="?attr/selectableItemBackgroundBorderless"
android:paddingStart="@dimen/small_margin"
android:paddingEnd="@dimen/small_margin"
@@ -104,13 +102,9 @@
android:layout_height="wrap_content"
android:layout_below="@+id/event_description_divider"
android:layout_alignParentEnd="true"
- android:layout_alignParentRight="true"
android:layout_marginStart="@dimen/small_margin"
- android:layout_marginLeft="@dimen/small_margin"
android:layout_marginEnd="@dimen/normal_margin"
- android:layout_marginRight="@dimen/normal_margin"
android:layout_toEndOf="@+id/event_time_image"
- android:layout_toRightOf="@+id/event_time_image"
android:paddingTop="@dimen/normal_margin"
android:paddingBottom="@dimen/normal_margin"
android:text="@string/all_day"
@@ -123,11 +117,9 @@
android:layout_height="wrap_content"
android:layout_below="@+id/event_time_image"
android:layout_alignStart="@+id/event_all_day"
- android:layout_alignLeft="@+id/event_all_day"
android:background="?attr/selectableItemBackground"
android:paddingTop="@dimen/activity_margin"
android:paddingEnd="@dimen/activity_margin"
- android:paddingRight="@dimen/activity_margin"
android:paddingBottom="@dimen/activity_margin"
android:textSize="@dimen/day_text_size"
tools:text="January 1 1970"/>
@@ -138,7 +130,6 @@
android:layout_height="wrap_content"
android:layout_below="@+id/event_time_image"
android:layout_alignParentEnd="true"
- android:layout_alignParentRight="true"
android:background="?attr/selectableItemBackground"
android:padding="@dimen/activity_margin"
android:textSize="@dimen/day_text_size"
@@ -150,11 +141,9 @@
android:layout_height="wrap_content"
android:layout_below="@+id/event_start_date"
android:layout_alignStart="@+id/event_all_day"
- android:layout_alignLeft="@+id/event_all_day"
android:background="?attr/selectableItemBackground"
android:paddingTop="@dimen/activity_margin"
android:paddingEnd="@dimen/activity_margin"
- android:paddingRight="@dimen/activity_margin"
android:paddingBottom="@dimen/activity_margin"
android:textSize="@dimen/day_text_size"
tools:text="January 1 1970"/>
@@ -165,7 +154,6 @@
android:layout_height="wrap_content"
android:layout_below="@+id/event_start_time"
android:layout_alignParentEnd="true"
- android:layout_alignParentRight="true"
android:background="?attr/selectableItemBackground"
android:padding="@dimen/activity_margin"
android:textSize="@dimen/day_text_size"
@@ -189,7 +177,6 @@
android:layout_alignTop="@+id/event_reminder_1"
android:layout_alignBottom="@+id/event_reminder_1"
android:layout_marginStart="@dimen/normal_margin"
- android:layout_marginLeft="@dimen/normal_margin"
android:alpha="0.8"
android:padding="@dimen/medium_margin"
android:src="@drawable/ic_bell"/>
@@ -200,13 +187,28 @@
android:layout_height="wrap_content"
android:layout_below="@+id/event_date_time_divider"
android:layout_marginStart="@dimen/small_margin"
- android:layout_marginLeft="@dimen/small_margin"
+ android:layout_toStartOf="@+id/event_reminder_1_type"
android:layout_toEndOf="@+id/event_reminder_image"
- android:layout_toRightOf="@+id/event_reminder_image"
android:background="?attr/selectableItemBackground"
+ android:ellipsize="end"
+ android:lines="1"
android:paddingTop="@dimen/activity_margin"
android:paddingBottom="@dimen/activity_margin"
- android:textSize="@dimen/day_text_size"/>
+ android:textSize="@dimen/day_text_size"
+ tools:text="@string/add_another_reminder"/>
+
+
+ android:visibility="gone"
+ tools:text="@string/add_another_reminder"/>
+
+
+ android:visibility="gone"
+ tools:text="@string/add_another_reminder"/>
+
+
@@ -267,13 +300,12 @@
android:layout_height="wrap_content"
android:layout_below="@+id/event_reminder_divider"
android:layout_marginStart="@dimen/small_margin"
- android:layout_marginLeft="@dimen/small_margin"
android:layout_toEndOf="@+id/event_repetition_image"
- android:layout_toRightOf="@+id/event_repetition_image"
android:background="?attr/selectableItemBackground"
android:paddingTop="@dimen/normal_margin"
android:paddingBottom="@dimen/normal_margin"
- android:textSize="@dimen/day_text_size"/>
+ android:textSize="@dimen/day_text_size"
+ tools:text="@string/no_repetition"/>
@@ -300,9 +331,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
- android:layout_alignParentRight="true"
android:layout_toEndOf="@+id/event_repetition_rule_label"
- android:layout_toRightOf="@+id/event_repetition_rule_label"
android:clickable="false"
android:gravity="end"
android:padding="@dimen/activity_margin"
@@ -316,7 +345,6 @@
android:layout_height="wrap_content"
android:layout_below="@+id/event_repetition_rule_holder"
android:layout_alignStart="@+id/event_repetition"
- android:layout_alignLeft="@+id/event_repetition"
android:background="?attr/selectableItemBackground"
android:visibility="gone">
@@ -325,7 +353,6 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_toStartOf="@+id/event_repetition_limit"
- android:layout_toLeftOf="@+id/event_repetition_limit"
android:clickable="false"
android:paddingTop="@dimen/activity_margin"
android:paddingBottom="@dimen/activity_margin"
@@ -337,7 +364,6 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
- android:layout_alignParentRight="true"
android:clickable="false"
android:padding="@dimen/activity_margin"
android:text="@string/forever"
@@ -354,14 +380,43 @@
android:importantForAccessibility="no"/>
+
+
+
+
+
+
@@ -382,7 +436,6 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/small_margin"
- android:layout_marginLeft="@dimen/small_margin"
android:layout_toStartOf="@+id/event_caldav_calendar_color"
android:ellipsize="end"
android:maxLines="1"
@@ -398,7 +451,6 @@
android:layout_height="wrap_content"
android:layout_below="@+id/event_caldav_calendar_name"
android:layout_marginStart="@dimen/small_margin"
- android:layout_marginLeft="@dimen/small_margin"
android:layout_toStartOf="@+id/event_caldav_calendar_color"
android:ellipsize="end"
android:maxLines="1"
@@ -412,10 +464,8 @@
android:layout_width="@dimen/color_sample_size"
android:layout_height="@dimen/color_sample_size"
android:layout_alignParentEnd="true"
- android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:layout_marginEnd="@dimen/activity_margin"
- android:layout_marginRight="@dimen/activity_margin"
android:clickable="false"/>
@@ -437,7 +487,6 @@
android:layout_alignTop="@+id/event_type_holder"
android:layout_alignBottom="@+id/event_type_holder"
android:layout_marginStart="@dimen/normal_margin"
- android:layout_marginLeft="@dimen/normal_margin"
android:alpha="0.8"
android:padding="@dimen/medium_margin"
android:src="@drawable/ic_color"/>
@@ -450,7 +499,6 @@
android:layout_marginTop="@dimen/medium_margin"
android:layout_marginBottom="@dimen/medium_margin"
android:layout_toEndOf="@+id/event_type_image"
- android:layout_toRightOf="@+id/event_type_image"
android:background="?attr/selectableItemBackground">
diff --git a/app/src/main/res/layout/dialog_set_reminders.xml b/app/src/main/res/layout/dialog_set_reminders.xml
new file mode 100644
index 000000000..09b8597af
--- /dev/null
+++ b/app/src/main/res/layout/dialog_set_reminders.xml
@@ -0,0 +1,59 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/item_attendee.xml b/app/src/main/res/layout/item_attendee.xml
new file mode 100644
index 000000000..1b04b5433
--- /dev/null
+++ b/app/src/main/res/layout/item_attendee.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/item_autocomplete_email.xml b/app/src/main/res/layout/item_autocomplete_email.xml
new file mode 100644
index 000000000..56fd8b08f
--- /dev/null
+++ b/app/src/main/res/layout/item_autocomplete_email.xml
@@ -0,0 +1,38 @@
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/item_autocomplete_email_name.xml b/app/src/main/res/layout/item_autocomplete_email_name.xml
new file mode 100644
index 000000000..c11ab5886
--- /dev/null
+++ b/app/src/main/res/layout/item_autocomplete_email_name.xml
@@ -0,0 +1,56 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/menu/menu_main.xml b/app/src/main/res/menu/menu_main.xml
index 9d011d54a..2adc71e60 100644
--- a/app/src/main/res/menu/menu_main.xml
+++ b/app/src/main/res/menu/menu_main.xml
@@ -27,6 +27,10 @@
android:icon="@drawable/ic_repeat"
android:title="@string/refresh_caldav_calendars"
app:showAsAction="ifRoom"/>
+
- لائحة أحداث بسيطة
Seems like you don\'t have any upcoming events.
الذهاب الى اليوم
+ Go to date
تقويم شهري
@@ -97,6 +98,9 @@
إضافة تذكير آخر
تذكيرات الحدث
+
+ Add another attendee
+
إستيراد الأحداث
تصدير الأحداث
@@ -224,7 +228,7 @@
- Offline kalendár pre vaše udalosti bez reklám, rešpektujúca vaše súkromie.
+ An offline calendar for your events without ads, respecting your privacy.
A simple calendar with optional CalDAV synchronization. You can easily create recurring events and setup reminders, it can also display week numbers.
diff --git a/app/src/main/res/values-az/strings.xml b/app/src/main/res/values-az/strings.xml
index 2e1b89cc7..6c7a3188d 100644
--- a/app/src/main/res/values-az/strings.xml
+++ b/app/src/main/res/values-az/strings.xml
@@ -10,6 +10,7 @@
Sadə hadisələr siyahısı
Deyəsən yaxınlarda heçbir hadisə yoxdur.
Bugünə get
+ Go to date
Aylıq Təqvim
@@ -97,6 +98,9 @@
Başqa bir xatırladıcı əlavə et
Hadisə xatırladıcıları
+
+ Add another attendee
+
Hadisələri daxil et
Hadisələri xaric et
diff --git a/app/src/main/res/values-br/strings.xml b/app/src/main/res/values-br/strings.xml
index 447a7b0d8..4dc3ed6d6 100644
--- a/app/src/main/res/values-br/strings.xml
+++ b/app/src/main/res/values-br/strings.xml
@@ -10,6 +10,7 @@
Roll darvoudoù een
War a-seblant n\'ho peus darvoud ebet da zont.
Mont da hiziv
+ Go to date
Deiziataer miziek
@@ -97,6 +98,9 @@
Ouzhpennañ un adc\'halv all
Event reminders
+
+ Add another attendee
+
Enporzhiañ darvoudoù
Ezporzhiañ darvoudoù
diff --git a/app/src/main/res/values-cs/strings.xml b/app/src/main/res/values-cs/strings.xml
index 7e55a66c6..3e70a3faf 100644
--- a/app/src/main/res/values-cs/strings.xml
+++ b/app/src/main/res/values-cs/strings.xml
@@ -10,6 +10,7 @@
Jednoduchý seznam událostí
Nemáte žádné nadcházející události.
Přejít na dnešek
+ Go to date
Měsíční kalendář
@@ -97,6 +98,9 @@
Přidat další připomínku
Připomínky událostí
+
+ Add another attendee
+
Import událostí
Export událostí
diff --git a/app/src/main/res/values-da/strings.xml b/app/src/main/res/values-da/strings.xml
index 6bb1d1336..0980297f7 100644
--- a/app/src/main/res/values-da/strings.xml
+++ b/app/src/main/res/values-da/strings.xml
@@ -10,6 +10,7 @@
Begivenhedsliste
Du ser ikke ud til at have nogen forestående begivenheder.
Gå til i dag
+ Gå til dato
Månedlig kalender
@@ -97,6 +98,9 @@
Tilføj en påmindelse mere
Påmindelser
+
+ Tilføj en anden deltager
+
Importer begivenheder
Eksporter begivenheder
@@ -169,13 +173,13 @@
Påmindelse 2
Påmindelse 3
Visning til brug i widget\'en
- Last view
- New events
- Default start time
- Next full hour
- Default duration
- Last used one
- Other time
+ Seneste visning
+ Nye begivenheder
+ Standard starttidspunkt
+ Næste hele time
+ Standard varighed
+ Senest brugte
+ Anden tid
CalDAV
diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml
index b452a6cfa..d00356a86 100644
--- a/app/src/main/res/values-de/strings.xml
+++ b/app/src/main/res/values-de/strings.xml
@@ -10,6 +10,7 @@
Einfache Terminliste
Scheint so, als hättest du keine anstehenden Termine.
Springe zu Heute
+ Go to date
Monatskalender
@@ -97,6 +98,9 @@
Weitere Erinnerung hinzufügen
Termin-Erinnerungen
+
+ Add another attendee
+
Termine importieren
Termine exportieren
diff --git a/app/src/main/res/values-el/strings.xml b/app/src/main/res/values-el/strings.xml
index a3f2734f3..ff658773b 100644
--- a/app/src/main/res/values-el/strings.xml
+++ b/app/src/main/res/values-el/strings.xml
@@ -9,7 +9,8 @@
Ετήσια προβολή
Απλή λίστα εκδηλώσεων
Φαίνεται πως δεν έχετε επερχόμενες εκδηλώσεις.
- Πηγαίνετε στο σήμερα
+ Μετάβαση στο σήμερα
+ Μετάβαση σε ημερ/νία
Μηνιαίο ημερολόγιο
@@ -49,7 +50,7 @@
Ενημέρωση μόνο του επιλεγμένου περιστατικού
Ενημέρωση όλων των περιστατικών
Επαναλάβετε μέχρι μια ημερομηνία
- Stop repeating after x occurrences
+ Παύση επαναλήψεων μετά από x εμφανίσεις
Επαναλάβετε για πάντα
times
Επανάληψη
@@ -58,9 +59,9 @@
Σε επιλεγμένες μέρες
Την ίδια μέρα
Την τελευταία μέρα
- Επαναλάβετε την ίδια ημέρα κάθε μήνα
- Επαναλάβετε την τελευταία ημέρα του μήνα
- Επαναλάβετε την τελευταία ημέρα του μήνα
+ Επανάληψη την ίδια ημέρα κάθε μήνα
+ Επανάληψη την τελευταία ημέρα του μήνα
+ Επανάληψη την τελευταία ημέρα του έτους
Επανάληψη κάθε
Κάθε
πρώτη
@@ -97,6 +98,9 @@
Προσθέστε μια άλλη υπενθύμιση
Υπενθυμίσεις εκδηλώσεων
+
+ Προσθήκη άλλης συμμετοχής
+
Εισαγωγή εκδηλώσεων
Εξαγωγή εκδηλώσεων
@@ -168,14 +172,14 @@
Προεπιλογή υπενθύμισης 1
Προεπιλογή υπενθύμισης 2
Προεπιλογή υπενθύμισης 3
- Προβολή για να ανοίξετε από το widget λίστας συμβάντων
+ Προβολή για άνοιγμα από το widget λίστας συμβάντων
Τελευταία προβολή
Νέα γεγονότα
Προεπιλεγμένη ώρα έναρξης
Την επόμενη πλήρη ώρα
Προεπιλεγμένη διάρκεια
Τελευταία χρήση
- Other time
+ Άλλη ώρα
CalDAV
diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml
index 9bee7c773..5094cdd06 100644
--- a/app/src/main/res/values-es/strings.xml
+++ b/app/src/main/res/values-es/strings.xml
@@ -10,6 +10,7 @@
Lista de eventos sencilla
Parece que no tienes ningún evento próximo.
Ir al día de hoy
+ Go to date
Calendario mensual
@@ -97,6 +98,9 @@
Agregar otro recordatorio
Recordatorio de eventos
+
+ Add another attendee
+
Importar eventos
Exportar eventos
diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml
index f7f107fb4..061ab69bf 100644
--- a/app/src/main/res/values-fr/strings.xml
+++ b/app/src/main/res/values-fr/strings.xml
@@ -10,6 +10,7 @@
Liste simple d\'événements
Il semblerait que vous n\'ayez aucun événement à venir.
Aller à aujourd\'hui
+ Go to date
Calendrier mensuel
@@ -97,6 +98,9 @@
Ajouter un autre rappel
Rappels d\'événements
+
+ Add another attendee
+
Importer des événements
Exporter des événements
diff --git a/app/src/main/res/values-gl/strings.xml b/app/src/main/res/values-gl/strings.xml
index 9226332ea..aa4cd2dfc 100644
--- a/app/src/main/res/values-gl/strings.xml
+++ b/app/src/main/res/values-gl/strings.xml
@@ -10,6 +10,7 @@
Lista de eventos simple
Semella que non ten ningún evento próximo.
Ir ao día de hoxe
+ Go to date
Calendario mensual
@@ -97,6 +98,9 @@
Engadir outro recordatorio
Recordatorios de eventos
+
+ Add another attendee
+
Importar eventos
Exportar eventos
diff --git a/app/src/main/res/values-hi-rIN/strings.xml b/app/src/main/res/values-hi-rIN/strings.xml
index 779d42516..2f910dd7b 100644
--- a/app/src/main/res/values-hi-rIN/strings.xml
+++ b/app/src/main/res/values-hi-rIN/strings.xml
@@ -10,6 +10,7 @@
सरल इवेंट सूची
Seems like you don\'t have any upcoming events.
Go to today
+ Go to date
Calendar monthly
@@ -98,6 +99,9 @@
Add another reminder
Event reminders
+
+ Add another attendee
+
Import events
Export events
diff --git a/app/src/main/res/values-hr/strings.xml b/app/src/main/res/values-hr/strings.xml
index 805eaa53b..b00c1ed47 100644
--- a/app/src/main/res/values-hr/strings.xml
+++ b/app/src/main/res/values-hr/strings.xml
@@ -10,6 +10,7 @@
Jednostavan popis događaja
Izgleda da nemate nadolazećih događaja.
Idi na danas
+ Go to date
Mjesečni raspored
@@ -97,6 +98,9 @@
Dodaj još jedan podsjetnik
Podsjetnici na događaj
+
+ Add another attendee
+
Uvezi događaje
Izvezi događaje
diff --git a/app/src/main/res/values-hu/strings.xml b/app/src/main/res/values-hu/strings.xml
index 0e68b26fe..6ba16b8b4 100644
--- a/app/src/main/res/values-hu/strings.xml
+++ b/app/src/main/res/values-hu/strings.xml
@@ -10,6 +10,7 @@
Egyszerű bejegyzéslista
Seems like you don\'t have any upcoming events.
Go to today
+ Go to date
Havi naptár
@@ -97,6 +98,9 @@
Add another reminder
Event reminders
+
+ Add another attendee
+
Import events
Export events
diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml
index 3374a2ab6..a74e2503a 100644
--- a/app/src/main/res/values-it/strings.xml
+++ b/app/src/main/res/values-it/strings.xml
@@ -10,6 +10,7 @@
Lista semplice deli eventi
Non c\'è nessun evento imminente.
Vai a oggi
+ Vai alla data
Calendario mensile
@@ -97,6 +98,9 @@
Aggiungi un altro promemoria
Promemoria eventi
+
+ Aggiungi un partecipante
+
Importa eventi
Esporta eventi
@@ -170,12 +174,12 @@
Promemoria predefinito 3
Vista da aprire dalla lista degli eventi del widget
Ultima vista
- New events
- Default start time
- Next full hour
- Default duration
- Last used one
- Other time
+ Nuovi eventi
+ Tempo di inizio predefinito
+ Prossima ora completa
+ Durata predefinita
+ L\'ultimo utilizzato
+ Altro periodo
CalDAV
diff --git a/app/src/main/res/values-iw/strings.xml b/app/src/main/res/values-iw/strings.xml
index 2929d5a8f..2deca09aa 100644
--- a/app/src/main/res/values-iw/strings.xml
+++ b/app/src/main/res/values-iw/strings.xml
@@ -10,6 +10,7 @@
רשימת אירועים פשוטה
נראה שאין לך אירועים בזמן הקרוב.
Go to today
+ Go to date
לוח השנה חודשית
@@ -98,6 +99,9 @@
Add another reminder
Event reminders
+
+ Add another attendee
+
ייבוא אירועים
Export events
diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml
index 6febd4965..48e37bb40 100644
--- a/app/src/main/res/values-ja/strings.xml
+++ b/app/src/main/res/values-ja/strings.xml
@@ -10,6 +10,7 @@
シンプル 予定 リスト
今後の予定はありません。
今日へ移動
+ Go to date
カレンダー月
@@ -97,6 +98,9 @@
Add another reminder
予定のリマインダー
+
+ Add another attendee
+
予定をインポート
予定をエクスポート
diff --git a/app/src/main/res/values-ko/strings.xml b/app/src/main/res/values-ko/strings.xml
index 47bcc98a6..dcc3cf051 100644
--- a/app/src/main/res/values-ko/strings.xml
+++ b/app/src/main/res/values-ko/strings.xml
@@ -10,6 +10,7 @@
간단한 이벤트 목록
다가올 이벤트가 없는 것 같습니다.
오늘로 이동
+ Go to date
월별 달력
@@ -97,6 +98,9 @@
다른 알림 추가
Event reminders
+
+ Add another attendee
+
이벤트들 가져 오기
이벤트들 내보내기
diff --git a/app/src/main/res/values-lt/strings.xml b/app/src/main/res/values-lt/strings.xml
index be6bef018..6565d1c17 100644
--- a/app/src/main/res/values-lt/strings.xml
+++ b/app/src/main/res/values-lt/strings.xml
@@ -10,6 +10,7 @@
Paprastas įvykių sąrašas
Atrodo jog Jūs neturite jokių įvyksiančių įvykių.
Eiti į šiandieną
+ Go to date
Mėnesio kalendorius
@@ -97,6 +98,9 @@
Pridėti kitą priminimą
Įvykių priminimai
+
+ Add another attendee
+
Importuoti įvykius
Eksportuoti įvykius
diff --git a/app/src/main/res/values-nb/strings.xml b/app/src/main/res/values-nb/strings.xml
index cf12f52d8..61e702c6e 100644
--- a/app/src/main/res/values-nb/strings.xml
+++ b/app/src/main/res/values-nb/strings.xml
@@ -10,6 +10,7 @@
Enkel hendelsesliste
Ser ut som du ikke har noen kommende hendelser.
Gå til idag
+ Go to date
Månedskalender
@@ -97,6 +98,9 @@
Legg til en annen påminnelse
Hendelsespåminnelser
+
+ Add another attendee
+
Importer hendelser
Eksporter hendelser
diff --git a/app/src/main/res/values-nl/strings.xml b/app/src/main/res/values-nl/strings.xml
index 1ef91db9c..90b682ce3 100644
--- a/app/src/main/res/values-nl/strings.xml
+++ b/app/src/main/res/values-nl/strings.xml
@@ -10,6 +10,7 @@
Lijst
Niets gepland.
Naar vandaag
+ Naar datum
Maandweergave
@@ -97,6 +98,9 @@
Herinnering toevoegen
Herinneringen
+
+ Persoon uitnodigen
+
Afspraken importeren
Afspraken exporteren
diff --git a/app/src/main/res/values-no/strings.xml b/app/src/main/res/values-no/strings.xml
index c1b1eb462..bc2d43845 100644
--- a/app/src/main/res/values-no/strings.xml
+++ b/app/src/main/res/values-no/strings.xml
@@ -10,6 +10,7 @@
Enkel hendelsesliste
Ser ut som du ikke har noen kommende hendelser.
Gå til idag
+ Go to date
Månedskalender
@@ -97,6 +98,9 @@
Legg til en annen påminnelse
Event reminders
+
+ Add another attendee
+
Importer hendelser
Eksporter hendelser
diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml
index cbff19fce..d42131618 100644
--- a/app/src/main/res/values-pl/strings.xml
+++ b/app/src/main/res/values-pl/strings.xml
@@ -10,6 +10,7 @@
Prosta lista wydarzeń
Wygląda na to, że nie masz żadnych nadchodzących wydarzeń.
Przejdź do dnia dzisiejszego
+ Go to date
Prosty kalendarz - widok miesięczny
@@ -97,6 +98,9 @@
Dodaj inne przypomnienie
Przypomnienia
+
+ Add another attendee
+
Importuj wydarzenia
Eksportuj wydarzenia
diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml
index 297124ae1..23d15b340 100644
--- a/app/src/main/res/values-pt-rBR/strings.xml
+++ b/app/src/main/res/values-pt-rBR/strings.xml
@@ -10,6 +10,7 @@
Lista de eventos
Parece que você não tem próximos eventos.
Ir para hoje
+ Go to date
Calendário mensal
@@ -97,6 +98,9 @@
Adicionar outro lembrete
Lembretes de eventos
+
+ Add another attendee
+
Importar eventos
Exportar eventos
diff --git a/app/src/main/res/values-pt/strings.xml b/app/src/main/res/values-pt/strings.xml
index d641c51ae..7c94ba846 100644
--- a/app/src/main/res/values-pt/strings.xml
+++ b/app/src/main/res/values-pt/strings.xml
@@ -10,6 +10,7 @@
Lista de eventos
Parece que você não tem eventos para breve
Ir para hoje
+ Go to date
Calendário mensal
@@ -97,6 +98,9 @@
Adicionar outro lembrete
Lembretes para eventos
+
+ Add another attendee
+
Importar eventos
Exportar eventos
diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml
index e38463045..8027936fc 100644
--- a/app/src/main/res/values-ru/strings.xml
+++ b/app/src/main/res/values-ru/strings.xml
@@ -10,6 +10,7 @@
Список событий
Похоже, нет предстоящих событий.
Сегодня
+ Go to date
Календарь на месяц
@@ -97,6 +98,9 @@
Добавить ещё одно напоминание
Напоминания о событиях
+
+ Add another attendee
+
Импорт событий
Экспорт событий
diff --git a/app/src/main/res/values-sk/strings.xml b/app/src/main/res/values-sk/strings.xml
index 81487f4e9..5bfab56f0 100644
--- a/app/src/main/res/values-sk/strings.xml
+++ b/app/src/main/res/values-sk/strings.xml
@@ -10,6 +10,7 @@
Jednoduchý zoznam
Nemáte žiadne naplánované udalosti.
Prejsť na dnešok
+ Prejsť na dátum
Kalendár mesačný
@@ -97,6 +98,9 @@
Pridať ďalšiu pripomienku
Pripomienky udalostí
+
+ Pridať ďalšieho účastníka
+
Importovať udalosti
Exportovať udalosti
@@ -224,7 +228,7 @@
- Jednoduchý kalendár s udalosťami, upraviteľným widgetom a bez reklám.
+ Offline kalendár pre vaše udalosti bez reklám, rešpektujúca vaše súkromie.
Jednoduchý kalendár s možnosťou CalDAV synchronizácie. Viete si jednoducho vytvoriť opakujúce sa udalosti s pripomienkami, alebo zobraziť čísla týždňov.
diff --git a/app/src/main/res/values-sv/strings.xml b/app/src/main/res/values-sv/strings.xml
index f7e1eb566..d706923a5 100644
--- a/app/src/main/res/values-sv/strings.xml
+++ b/app/src/main/res/values-sv/strings.xml
@@ -10,6 +10,7 @@
Händelselista
Det verkar som att du inte har några kommande händelser.
Gå till idag
+ Gå till datum
Kalender månadsvis
@@ -97,6 +98,9 @@
Lägg till en annan påminnelse
Händelsepåminnelser
+
+ Add another attendee
+
Importera händelser
Exportera händelser
@@ -136,13 +140,13 @@
Nationella helgdagar
Religiösa helgdagar
Helgdagarna har importerats till händelsetypen \"Helgdagar\"
- Vissa händelser kunde inte importeras
- Helgdagar kunde inte importeras
+ Importen av vissa händelser misslyckades
+ Importen av helgdagarna misslyckades
Hantera händelsetyper
- Starta veckovydagen vid
- Sluta veckovydagen vid
+ Dagen börjar
+ Dagen slutar
Visa veckonummer
Vibrera vid påminnelseaviseringar
Påminnelseljud
@@ -170,12 +174,12 @@
Standardpåminnelse 3
Vy som öppnas från händelselistwidgeten
Senaste vy
- New events
- Default start time
- Next full hour
- Default duration
- Last used one
- Other time
+ Nya händelser
+ Standardstarttid
+ Nästa heltimme
+ Standardvaraktighet
+ Senast använda
+ Annan tid
CalDAV
diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml
index fe67ecd8a..8974088ad 100644
--- a/app/src/main/res/values-tr/strings.xml
+++ b/app/src/main/res/values-tr/strings.xml
@@ -10,6 +10,7 @@
Basit etkinlik listesi
Yaklaşan etkinlikleriniz yok gibi görünüyor.
Bugüne git
+ Go to date
Aylık takvim
@@ -97,6 +98,9 @@
Başka hatırlatma ekle
Etkinlik hatırlatıcılar
+
+ Add another attendee
+
Etkinlikleri içe aktar
Etkinlikleri dışa aktar
@@ -170,12 +174,12 @@
Varsayılan hatırlatıcı 3
Etkinlik listesi widget\'ından açılacak görünüm
Son görünüm
- New events
- Default start time
- Next full hour
- Default duration
- Last used one
- Other time
+ Yeni etkinlikler
+ Varsayılan başlangıç zamanı
+ Gelecek tam saat
+ Varsayılan süre
+ Son kullanılan
+ Başka zaman
CalDAV
@@ -189,7 +193,7 @@
Senkronize ediliyor…
Senkronizasyon tamamlandı
Farklı bir renk seçin (yalnızca yerel olarak uygulanabilir)
- You are not allowed to write in the selected calendar
+ Seçili takvime yazmanıza izin verilmiyor
@@ -223,7 +227,7 @@
- An offline calendar for your events without ads, respecting your privacy.
+ Etkinlikleriniz için reklamsız, gizliliğinizi önemseyen bir çevrimdışı takvim.
İsteğe bağlı CalDAV senkronizasyonu ile basit bir takvim. Kolayca tekrarlanan etkinlikler oluşturabilir ve hatırlatıcılar ayarlayabilir, ayrıca hafta sayılarını görüntüleyebilirsiniz.
@@ -235,7 +239,7 @@
Kişiler izni yalnızca doğum günlerini ve yıldönümlerini içe aktarırken kullanılır.
- Bu uygulama, daha büyük bir uygulama serisinden sadece bir parça. Geri kalanı http://www.simplemobiletools.com adresinde bulabilirsiniz
+ Bu uygulama, daha büyük bir uygulama serisinden sadece bir parça. Geri kalanı http://www.simplemobiletools.com adresinde bulabilirsiniz.
月曆
@@ -97,6 +98,9 @@
新增另一個提醒
活動提醒
+
+ Add another attendee
+
匯入活動
匯出活動
@@ -170,12 +174,12 @@
預設提醒3
從活動列表小工具開啟的檢視畫面
最後的檢視畫面
- New events
- Default start time
- Next full hour
- Default duration
- Last used one
- Other time
+ 新活動
+ 預設開始時間
+ 整整下個小時
+ 預設持續時間
+ 最後使用
+ 其他時間
CalDAV
diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml
index 7cd978d17..bdb6678fe 100644
--- a/app/src/main/res/values/dimens.xml
+++ b/app/src/main/res/values/dimens.xml
@@ -28,4 +28,6 @@
4dp
100dp
+
+ 40dp
diff --git a/app/src/main/res/values/donottranslate.xml b/app/src/main/res/values/donottranslate.xml
index ab34a38ae..e2fbb264c 100644
--- a/app/src/main/res/values/donottranslate.xml
+++ b/app/src/main/res/values/donottranslate.xml
@@ -2,6 +2,10 @@
+
+ Allow setting default start time/duration/event type for new events\n
+ Allow exporting/importing settings
+
Allow setting default event reminders, not always reuse the last events\' ones
Allow changing the app launcher icon color\n
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index bfb4a1285..d76ea886e 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -10,6 +10,7 @@
Simple event list
Seems like you don\'t have any upcoming events.
Go to today
+ Go to date
Calendar monthly
@@ -97,6 +98,9 @@
Add another reminder
Event reminders
+
+ Add another attendee
+
Import events
Export events
@@ -224,7 +228,7 @@
- Offline kalendár pre vaše udalosti bez reklám, rešpektujúca vaše súkromie.
+ An offline calendar for your events without ads, respecting your privacy.
A simple calendar with optional CalDAV synchronization. You can easily create recurring events and setup reminders, it can also display week numbers.
diff --git a/build.gradle b/build.gradle
index 0be76662a..c2453164b 100644
--- a/build.gradle
+++ b/build.gradle
@@ -6,11 +6,13 @@ buildscript {
repositories {
google()
jcenter()
+ maven { url "https://plugins.gradle.org/m2" }
}
dependencies {
- classpath 'com.android.tools.build:gradle:3.3.1'
+ classpath 'com.android.tools.build:gradle:3.3.2'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
+ classpath "de.timfreiheit.resourceplaceholders:placeholders:0.3"
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
diff --git a/fastlane/metadata/android/en-US/images/featureGraphic.png b/fastlane/metadata/android/en-US/images/featureGraphic.png
index e328550e4..faa9471fe 100644
Binary files a/fastlane/metadata/android/en-US/images/featureGraphic.png and b/fastlane/metadata/android/en-US/images/featureGraphic.png differ
diff --git a/fastlane/metadata/android/en-US/images/sevenInchScreenshots/tablet-7.png b/fastlane/metadata/android/en-US/images/sevenInchScreenshots/tablet-7.png
index 81571cbde..df6f6ea8c 100644
Binary files a/fastlane/metadata/android/en-US/images/sevenInchScreenshots/tablet-7.png and b/fastlane/metadata/android/en-US/images/sevenInchScreenshots/tablet-7.png differ
diff --git a/fastlane/metadata/android/en-US/images/tenInchScreenshots/tablet-10.png b/fastlane/metadata/android/en-US/images/tenInchScreenshots/tablet-10.png
index fb53897ac..34d04eaab 100644
Binary files a/fastlane/metadata/android/en-US/images/tenInchScreenshots/tablet-10.png and b/fastlane/metadata/android/en-US/images/tenInchScreenshots/tablet-10.png differ