Merge pull request #20 from SimpleMobileTools/master

upd
This commit is contained in:
solokot
2019-03-14 23:30:26 +03:00
committed by GitHub
79 changed files with 1360 additions and 283 deletions

View File

@@ -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)*
----------------------------

View File

@@ -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
<a href='https://play.google.com/store/apps/details?id=com.simplemobiletools.calendar.pro'><img src='http://simplemobiletools.github.io/assets/public/google-play.png' alt='Get it on Google Play' height='45' /></a>
<a href='https://f-droid.org/packages/com.simplemobiletools.calendar.pro'><img src='http://simplemobiletools.github.io/assets/public/f-droid.png' alt='Get it on F-Droid' height='45' /></a>
<a href='https://play.google.com/store/apps/details?id=com.simplemobiletools.calendar.pro'><img src='https://simplemobiletools.com/assets/images/google-play.png' alt='Get it on Google Play' height='45' /></a>
<a href='https://f-droid.org/packages/com.simplemobiletools.calendar.pro'><img src='https://simplemobiletools.com/assets/images/f-droid.png' alt='Get it on F-Droid' height='45' /></a>
<div style="display:flex;">
<img alt="App image" src="fastlane/metadata/android/en-US/images/phoneScreenshots/app.png" width="30%">

View File

@@ -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'

View File

@@ -16,14 +16,18 @@
android:name="android.permission.USE_FINGERPRINT"
tools:node="remove"/>
<uses-feature
android:name="android.hardware.faketouch"
android:required="false"/>
<application
android:name=".App"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_launcher_name"
android:roundIcon="@mipmap/ic_launcher"
android:theme="@style/AppTheme"
android:supportsRtl="true">
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity
android:name=".activities.SplashActivity"

View File

@@ -3,30 +3,41 @@ package com.simplemobiletools.calendar.pro.activities
import android.app.DatePickerDialog
import android.app.TimePickerDialog
import android.content.Intent
import android.database.Cursor
import android.net.Uri
import android.os.Bundle
import android.provider.CalendarContract
import android.provider.ContactsContract
import android.text.TextUtils
import android.text.method.LinkMovementMethod
import android.view.Menu
import android.view.MenuItem
import android.view.WindowManager
import android.widget.EditText
import android.widget.ImageView
import android.widget.RelativeLayout
import androidx.core.app.NotificationManagerCompat
import com.google.gson.Gson
import com.google.gson.reflect.TypeToken
import com.simplemobiletools.calendar.pro.R
import com.simplemobiletools.calendar.pro.adapters.AutoCompleteTextViewAdapter
import com.simplemobiletools.calendar.pro.dialogs.*
import com.simplemobiletools.calendar.pro.extensions.*
import com.simplemobiletools.calendar.pro.helpers.*
import com.simplemobiletools.calendar.pro.helpers.Formatter
import com.simplemobiletools.calendar.pro.models.CalDAVCalendar
import com.simplemobiletools.calendar.pro.models.Event
import com.simplemobiletools.calendar.pro.models.EventType
import com.simplemobiletools.calendar.pro.models.*
import com.simplemobiletools.commons.dialogs.ConfirmationDialog
import com.simplemobiletools.commons.dialogs.RadioGroupDialog
import com.simplemobiletools.commons.extensions.*
import com.simplemobiletools.commons.helpers.*
import com.simplemobiletools.commons.models.RadioItem
import kotlinx.android.synthetic.main.activity_event.*
import kotlinx.android.synthetic.main.activity_event.view.*
import kotlinx.android.synthetic.main.item_attendee.view.*
import org.joda.time.DateTime
import java.util.*
import java.util.regex.Pattern
import kotlin.collections.ArrayList
class EventActivity : SimpleActivity() {
private val LAT_LON_PATTERN = "^[-+]?([1-8]?\\d(\\.\\d+)?|90(\\.0+)?)([,;])\\s*[-+]?(180(\\.0+)?|((1[0-7]\\d)|([1-9]?\\d))(\\.\\d+)?)\$"
@@ -36,15 +47,22 @@ class EventActivity : SimpleActivity() {
private val REMINDER_1_MINUTES = "REMINDER_1_MINUTES"
private val REMINDER_2_MINUTES = "REMINDER_2_MINUTES"
private val REMINDER_3_MINUTES = "REMINDER_3_MINUTES"
private val REMINDER_1_TYPE = "REMINDER_1_TYPE"
private val REMINDER_2_TYPE = "REMINDER_2_TYPE"
private val REMINDER_3_TYPE = "REMINDER_3_TYPE"
private val REPEAT_INTERVAL = "REPEAT_INTERVAL"
private val REPEAT_LIMIT = "REPEAT_LIMIT"
private val REPEAT_RULE = "REPEAT_RULE"
private val ATTENDEES = "ATTENDEES"
private val EVENT_TYPE_ID = "EVENT_TYPE_ID"
private val EVENT_CALENDAR_ID = "EVENT_CALENDAR_ID"
private var mReminder1Minutes = 0
private var mReminder2Minutes = 0
private var mReminder3Minutes = 0
private var mReminder1Minutes = REMINDER_OFF
private var mReminder2Minutes = REMINDER_OFF
private var mReminder3Minutes = REMINDER_OFF
private var mReminder1Type = REMINDER_NOTIFICATION
private var mReminder2Type = REMINDER_NOTIFICATION
private var mReminder3Type = REMINDER_NOTIFICATION
private var mRepeatInterval = 0
private var mRepeatLimit = 0L
private var mRepeatRule = 0
@@ -52,7 +70,11 @@ class EventActivity : SimpleActivity() {
private var mDialogTheme = 0
private var mEventOccurrenceTS = 0L
private var mEventCalendarId = STORED_LOCALLY_ONLY
private var wasActivityInitialized = false
private var mWasActivityInitialized = false
private var mWasContactsPermissionChecked = false
private var mAttendees = ArrayList<Attendee>()
private var mAttendeeViews = ArrayList<EditText>()
private var mAvailableContacts = ArrayList<Attendee>()
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<ArrayList<Attendee>>(getString(ATTENDEES), object : TypeToken<List<Attendee>>() {}.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<ArrayList<Attendee>>(mEvent.attendees, object : TypeToken<List<Attendee>>() {}.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<Reminder>
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<String>
val attendees = ArrayList<Attendee>()
attendeeEmails.mapTo(attendees) {
Attendee(0, "", it, CalendarContract.Attendees.ATTENDEE_STATUS_INVITED, "")
}
return Gson().toJson(attendees)
}
private fun getNames(): List<Attendee> {
val contacts = ArrayList<Attendee>()
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<Attendee> {
val contacts = ArrayList<Attendee>()
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)
}
}

View File

@@ -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<Int>, 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)
}
}

View File

@@ -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()
}
}
}
}

View File

@@ -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()

View File

@@ -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)

View File

@@ -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)

View File

@@ -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<Attendee>) : ArrayAdapter<Attendee>(activity, 0, contacts) {
var resultList = ArrayList<Attendee>()
private var placeholder = activity.resources.getDrawable(R.drawable.attendee_circular_background)
init {
(placeholder as LayerDrawable).findDrawableByLayerId(R.id.attendee_circular_background).applyColorFilter(activity.config.primaryColor)
}
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
val contact = resultList[position]
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<Attendee>
{ 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
}

View File

@@ -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 ''")
}
}
}
}
}

View File

@@ -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<Int>) -> 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)
}
}

View File

@@ -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)
}
}

View File

@@ -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<DatePicker>(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)

View File

@@ -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<DatePicker>(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)
}

View File

@@ -173,8 +173,9 @@ class EventListFragment : MyFragmentHolder(), RefreshRecyclerViewListener {
checkEvents()
}
override fun goToToday() {
}
override fun goToToday() {}
override fun showGoToDateDialog() {}
override fun refreshEvents() {
checkEvents()

View File

@@ -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<DatePicker>(R.id.date_picker)
datePicker.findViewById<View>(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<DayMonthly>) {
mHolder.month_view_wrapper.updateDays(days) {
(activity as MainActivity).openDayFromMonthly(Formatter.getDateTimeFromCode(it.code))

View File

@@ -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<DatePicker>(R.id.date_picker)
datePicker.findViewById<View>(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)
}

View File

@@ -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

View File

@@ -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)

View File

@@ -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<Long> {
val weekTSs = ArrayList<Long>(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<DatePicker>(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)
}

View File

@@ -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<DatePicker>(R.id.date_picker)
datePicker.findViewById<View>(Resources.getSystem().getIdentifier("day", "id", "android")).beGone()
datePicker.findViewById<View>(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)
}

View File

@@ -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<CalDAVCalendar> {
fun getCalDAVCalendars(ids: String, showToasts: Boolean): ArrayList<CalDAVCalendar> {
val calendars = ArrayList<CalDAVCalendar>()
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<ArrayList<Attendee>>(event.attendees, object : TypeToken<List<Attendee>>() {}.type) ?: ArrayList()
attendees.forEach {
val contentValues = ContentValues().apply {
put(CalendarContract.Attendees.ATTENDEE_NAME, it.name)
put(CalendarContract.Attendees.ATTENDEE_EMAIL, it.email)
put(CalendarContract.Attendees.ATTENDEE_STATUS, 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<Int> {
val reminders = ArrayList<Int>()
private fun getCalDAVEventReminders(eventId: Long): List<Reminder> {
val reminders = ArrayList<Reminder>()
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<Attendee> {
val attendees = ArrayList<Attendee>()
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"

View File

@@ -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

View File

@@ -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)

View File

@@ -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<CalDAVCalendar>()
fun exportEvents(activity: BaseSimpleActivity, file: File, events: ArrayList<Event>, 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)
}
}

View File

@@ -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<Int>()
private var curReminderActions = ArrayList<Int>()
private var curRepeatExceptions = ArrayList<String>()
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<Reminder>
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
}
}

View File

@@ -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
}

View File

@@ -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<String> = 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 {

View File

@@ -0,0 +1,3 @@
package com.simplemobiletools.calendar.pro.models
data class Reminder(val minutes: Int, val type: Int)

View File

@@ -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
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 260 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 313 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 425 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 554 B

View File

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

View File

@@ -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"/>
<ImageView
android:id="@+id/event_reminder_1_type"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignTop="@+id/event_reminder_1"
android:layout_alignBottom="@+id/event_reminder_1"
android:layout_alignParentEnd="true"
android:layout_marginStart="@dimen/small_margin"
android:alpha="0.8"
android:background="?attr/selectableItemBackground"
android:padding="@dimen/activity_margin"
android:src="@drawable/ic_bell"/>
<com.simplemobiletools.commons.views.MyTextView
android:id="@+id/event_reminder_2"
@@ -214,14 +216,30 @@
android:layout_height="wrap_content"
android:layout_below="@+id/event_reminder_1"
android:layout_alignStart="@+id/event_reminder_1"
android:layout_alignLeft="@+id/event_reminder_1"
android:layout_toStartOf="@+id/event_reminder_2_type"
android:alpha="0.4"
android:background="?attr/selectableItemBackground"
android:ellipsize="end"
android:lines="1"
android:paddingTop="@dimen/activity_margin"
android:paddingBottom="@dimen/activity_margin"
android:text="@string/add_another_reminder"
android:textSize="@dimen/day_text_size"
android:visibility="gone"/>
android:visibility="gone"
tools:text="@string/add_another_reminder"/>
<ImageView
android:id="@+id/event_reminder_2_type"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignTop="@+id/event_reminder_2"
android:layout_alignBottom="@+id/event_reminder_2"
android:layout_alignParentEnd="true"
android:layout_marginStart="@dimen/small_margin"
android:alpha="0.8"
android:background="?attr/selectableItemBackground"
android:padding="@dimen/activity_margin"
android:src="@drawable/ic_bell"/>
<com.simplemobiletools.commons.views.MyTextView
android:id="@+id/event_reminder_3"
@@ -229,14 +247,30 @@
android:layout_height="wrap_content"
android:layout_below="@+id/event_reminder_2"
android:layout_alignStart="@+id/event_reminder_1"
android:layout_alignLeft="@+id/event_reminder_1"
android:layout_toStartOf="@+id/event_reminder_3_type"
android:alpha="0.4"
android:background="?attr/selectableItemBackground"
android:ellipsize="end"
android:lines="1"
android:paddingTop="@dimen/activity_margin"
android:paddingBottom="@dimen/activity_margin"
android:text="@string/add_another_reminder"
android:textSize="@dimen/day_text_size"
android:visibility="gone"/>
android:visibility="gone"
tools:text="@string/add_another_reminder"/>
<ImageView
android:id="@+id/event_reminder_3_type"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignTop="@+id/event_reminder_3"
android:layout_alignBottom="@+id/event_reminder_3"
android:layout_alignParentEnd="true"
android:layout_marginStart="@dimen/small_margin"
android:alpha="0.8"
android:background="?attr/selectableItemBackground"
android:padding="@dimen/activity_margin"
android:src="@drawable/ic_bell"/>
<ImageView
android:id="@+id/event_reminder_divider"
@@ -256,7 +290,6 @@
android:layout_alignTop="@+id/event_repetition"
android:layout_alignBottom="@+id/event_repetition"
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_repeat"/>
@@ -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"/>
<RelativeLayout
android:id="@+id/event_repetition_rule_holder"
@@ -281,7 +313,6 @@
android:layout_height="wrap_content"
android:layout_below="@+id/event_repetition"
android:layout_alignStart="@+id/event_repetition"
android:layout_alignLeft="@+id/event_repetition"
android:background="?attr/selectableItemBackground"
android:visibility="gone">
@@ -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"/>
<ImageView
android:id="@+id/event_caldav_calendar_image"
android:id="@+id/event_attendees_image"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_below="@+id/event_repetition_divider"
android:layout_alignTop="@+id/event_attendees_holder"
android:layout_marginStart="@dimen/normal_margin"
android:layout_marginTop="@dimen/small_margin"
android:alpha="0.8"
android:padding="@dimen/medium_margin"
android:src="@drawable/ic_people"/>
<LinearLayout
android:id="@+id/event_attendees_holder"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/event_repetition_divider"
android:layout_marginTop="@dimen/medium_margin"
android:layout_toEndOf="@+id/event_attendees_image"
android:orientation="vertical"/>
<ImageView
android:id="@+id/event_attendees_divider"
android:layout_width="match_parent"
android:layout_height="1px"
android:layout_below="@+id/event_attendees_holder"
android:layout_marginTop="@dimen/medium_margin"
android:background="@color/divider_grey"
android:importantForAccessibility="no"/>
<ImageView
android:id="@+id/event_caldav_calendar_image"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_below="@+id/event_attendees_divider"
android:layout_alignTop="@+id/event_caldav_calendar_holder"
android:layout_alignBottom="@+id/event_caldav_calendar_holder"
android:layout_marginStart="@dimen/normal_margin"
android:layout_marginLeft="@dimen/normal_margin"
android:alpha="0.8"
android:padding="@dimen/medium_margin"
android:src="@drawable/ic_calendar"
@@ -371,9 +426,8 @@
android:id="@+id/event_caldav_calendar_holder"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/event_repetition_divider"
android:layout_below="@+id/event_attendees_divider"
android:layout_toEndOf="@+id/event_caldav_calendar_image"
android:layout_toRightOf="@+id/event_caldav_calendar_image"
android:background="?attr/selectableItemBackground"
android:visibility="gone">
@@ -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"/>
</RelativeLayout>
@@ -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">
<com.simplemobiletools.commons.views.MyTextView
@@ -469,10 +517,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"/>
</RelativeLayout>

View File

@@ -0,0 +1,59 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/set_reminders_holder_dialog_holder"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingTop="@dimen/activity_margin"
android:paddingEnd="@dimen/activity_margin">
<ImageView
android:id="@+id/set_reminders_image"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_alignTop="@+id/set_reminders_1"
android:layout_alignBottom="@+id/set_reminders_1"
android:layout_marginStart="@dimen/normal_margin"
android:alpha="0.8"
android:padding="@dimen/medium_margin"
android:src="@drawable/ic_bell"/>
<com.simplemobiletools.commons.views.MyTextView
android:id="@+id/set_reminders_1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/small_margin"
android:layout_toEndOf="@+id/set_reminders_image"
android:background="?attr/selectableItemBackground"
android:paddingTop="@dimen/activity_margin"
android:paddingBottom="@dimen/activity_margin"
android:textSize="@dimen/day_text_size"/>
<com.simplemobiletools.commons.views.MyTextView
android:id="@+id/set_reminders_2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/set_reminders_1"
android:layout_alignStart="@+id/set_reminders_1"
android:background="?attr/selectableItemBackground"
android:paddingTop="@dimen/activity_margin"
android:paddingBottom="@dimen/activity_margin"
android:text="@string/add_another_reminder"
android:textSize="@dimen/day_text_size"
android:visibility="gone"/>
<com.simplemobiletools.commons.views.MyTextView
android:id="@+id/set_reminders_3"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/set_reminders_2"
android:layout_alignStart="@+id/set_reminders_1"
android:background="?attr/selectableItemBackground"
android:paddingTop="@dimen/activity_margin"
android:paddingBottom="@dimen/activity_margin"
android:text="@string/add_another_reminder"
android:textSize="@dimen/day_text_size"
android:visibility="gone"/>
</RelativeLayout>

View File

@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/event_attendee_holder"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="@dimen/medium_margin"
android:paddingBottom="@dimen/medium_margin">
<com.simplemobiletools.commons.views.MyAutoCompleteTextView
android:id="@+id/event_attendee"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginEnd="@dimen/activity_margin"
android:completionThreshold="2"
android:hint="@string/add_another_attendee"
android:lines="1"
android:maxLines="1"
android:singleLine="true"
android:textCursorDrawable="@null"
android:textSize="@dimen/bigger_text_size"/>
</RelativeLayout>

View File

@@ -0,0 +1,38 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/item_autocomplete_holder"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingStart="@dimen/small_margin"
android:paddingTop="@dimen/medium_margin"
android:paddingEnd="@dimen/medium_margin"
android:paddingBottom="@dimen/medium_margin">
<ImageView
android:id="@+id/item_autocomplete_image"
android:layout_width="@dimen/avatar_size"
android:layout_height="@dimen/avatar_size"
android:layout_margin="@dimen/tiny_margin"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
<TextView
android:id="@+id/item_autocomplete_email"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:lines="1"
android:maxLines="1"
android:paddingStart="@dimen/medium_margin"
android:singleLine="true"
android:textSize="@dimen/bigger_text_size"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/item_autocomplete_image"
app:layout_constraintTop_toTopOf="parent"
tools:text="hello@simplemobiletools.com"/>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -0,0 +1,56 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/item_autocomplete_holder"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingStart="@dimen/small_margin"
android:paddingTop="@dimen/medium_margin"
android:paddingEnd="@dimen/medium_margin"
android:paddingBottom="@dimen/medium_margin">
<ImageView
android:id="@+id/item_autocomplete_image"
android:layout_width="@dimen/avatar_size"
android:layout_height="@dimen/avatar_size"
android:layout_margin="@dimen/tiny_margin"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
<TextView
android:id="@+id/item_autocomplete_name"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:lines="1"
android:maxLines="1"
android:paddingStart="@dimen/medium_margin"
android:singleLine="true"
android:textSize="@dimen/bigger_text_size"
app:layout_constraintBottom_toTopOf="@+id/item_autocomplete_email"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/item_autocomplete_image"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_chainStyle="packed"
tools:text="Simple Mobile"/>
<TextView
android:id="@+id/item_autocomplete_email"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_below="@+id/item_autocomplete_name"
android:layout_marginEnd="8dp"
android:alpha="0.8"
android:lines="1"
android:maxLines="1"
android:paddingStart="@dimen/medium_margin"
android:singleLine="true"
android:textSize="@dimen/normal_text_size"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/item_autocomplete_image"
app:layout_constraintTop_toBottomOf="@+id/item_autocomplete_name"
tools:text="hello@simplemobiletools.com"/>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -27,6 +27,10 @@
android:icon="@drawable/ic_repeat"
android:title="@string/refresh_caldav_calendars"
app:showAsAction="ifRoom"/>
<item
android:id="@+id/go_to_date"
android:title="@string/go_to_date"
app:showAsAction="never"/>
<item
android:id="@+id/add_holidays"
android:title="@string/add_holidays"

View File

@@ -10,6 +10,7 @@
<string name="simple_event_list">لائحة أحداث بسيطة</string>
<string name="no_upcoming_events">Seems like you don\'t have any upcoming events.</string>
<string name="go_to_today">الذهاب الى اليوم</string>
<string name="go_to_date">Go to date</string>
<!-- Widget titles -->
<string name="widget_monthly">تقويم شهري</string>
@@ -97,6 +98,9 @@
<string name="add_another_reminder">إضافة تذكير آخر</string>
<string name="event_reminders">تذكيرات الحدث</string>
<!-- Event attendees -->
<string name="add_another_attendee">Add another attendee</string>
<!-- Export / Import -->
<string name="import_events">إستيراد الأحداث</string>
<string name="export_events">تصدير الأحداث</string>
@@ -224,7 +228,7 @@
<!-- Strings displayed only on Google Playstore. Optional, but good to have -->
<!-- Short description has to have less than 80 chars -->
<string name="app_short_description">Offline kalendár pre vaše udalosti bez reklám, rešpektujúca vaše súkromie.</string>
<string name="app_short_description">An offline calendar for your events without ads, respecting your privacy.</string>
<string name="app_long_description">
A simple calendar with optional CalDAV synchronization. You can easily create recurring events and setup reminders, it can also display week numbers.

View File

@@ -10,6 +10,7 @@
<string name="simple_event_list">Sadə hadisələr siyahısı</string>
<string name="no_upcoming_events">Deyəsən yaxınlarda heçbir hadisə yoxdur.</string>
<string name="go_to_today">Bugünə get</string>
<string name="go_to_date">Go to date</string>
<!-- Widget titles -->
<string name="widget_monthly">Aylıq Təqvim</string>
@@ -97,6 +98,9 @@
<string name="add_another_reminder">Başqa bir xatırladıcı əlavə et</string>
<string name="event_reminders">Hadisə xatırladıcıları</string>
<!-- Event attendees -->
<string name="add_another_attendee">Add another attendee</string>
<!-- Export / Import -->
<string name="import_events">Hadisələri daxil et</string>
<string name="export_events">Hadisələri xaric et</string>

View File

@@ -10,6 +10,7 @@
<string name="simple_event_list">Roll darvoudoù een</string>
<string name="no_upcoming_events">War a-seblant n\'ho peus darvoud ebet da zont.</string>
<string name="go_to_today">Mont da hiziv</string>
<string name="go_to_date">Go to date</string>
<!-- Widget titles -->
<string name="widget_monthly">Deiziataer miziek</string>
@@ -97,6 +98,9 @@
<string name="add_another_reminder">Ouzhpennañ un adc\'halv all</string>
<string name="event_reminders">Event reminders</string>
<!-- Event attendees -->
<string name="add_another_attendee">Add another attendee</string>
<!-- Export / Import -->
<string name="import_events">Enporzhiañ darvoudoù</string>
<string name="export_events">Ezporzhiañ darvoudoù</string>

View File

@@ -10,6 +10,7 @@
<string name="simple_event_list">Jednoduchý seznam událostí</string>
<string name="no_upcoming_events">Nemáte žádné nadcházející události.</string>
<string name="go_to_today">Přejít na dnešek</string>
<string name="go_to_date">Go to date</string>
<!-- Widget titles -->
<string name="widget_monthly">Měsíční kalendář</string>
@@ -97,6 +98,9 @@
<string name="add_another_reminder">Přidat další připomínku</string>
<string name="event_reminders">Připomínky událostí</string>
<!-- Event attendees -->
<string name="add_another_attendee">Add another attendee</string>
<!-- Export / Import -->
<string name="import_events">Import událostí</string>
<string name="export_events">Export událostí</string>

View File

@@ -10,6 +10,7 @@
<string name="simple_event_list">Begivenhedsliste</string>
<string name="no_upcoming_events">Du ser ikke ud til at have nogen forestående begivenheder.</string>
<string name="go_to_today">Gå til i dag</string>
<string name="go_to_date">Gå til dato</string>
<!-- Widget titles -->
<string name="widget_monthly">Månedlig kalender</string>
@@ -97,6 +98,9 @@
<string name="add_another_reminder">Tilføj en påmindelse mere</string>
<string name="event_reminders">Påmindelser</string>
<!-- Event attendees -->
<string name="add_another_attendee">Tilføj en anden deltager</string>
<!-- Export / Import -->
<string name="import_events">Importer begivenheder</string>
<string name="export_events">Eksporter begivenheder</string>
@@ -169,13 +173,13 @@
<string name="default_reminder_2">Påmindelse 2</string>
<string name="default_reminder_3">Påmindelse 3</string>
<string name="view_to_open_from_widget">Visning til brug i widget\'en</string>
<string name="last_view">Last view</string>
<string name="new_events">New events</string>
<string name="default_start_time">Default start time</string>
<string name="next_full_hour">Next full hour</string>
<string name="default_duration">Default duration</string>
<string name="last_used_one">Last used one</string>
<string name="other_time">Other time</string>
<string name="last_view">Seneste visning</string>
<string name="new_events">Nye begivenheder</string>
<string name="default_start_time">Standard starttidspunkt</string>
<string name="next_full_hour">Næste hele time</string>
<string name="default_duration">Standard varighed</string>
<string name="last_used_one">Senest brugte</string>
<string name="other_time">Anden tid</string>
<!-- CalDAV sync -->
<string name="caldav">CalDAV</string>

View File

@@ -10,6 +10,7 @@
<string name="simple_event_list">Einfache Terminliste</string>
<string name="no_upcoming_events">Scheint so, als hättest du keine anstehenden Termine.</string>
<string name="go_to_today">Springe zu Heute</string>
<string name="go_to_date">Go to date</string>
<!-- Widget titles -->
<string name="widget_monthly">Monatskalender</string>
@@ -97,6 +98,9 @@
<string name="add_another_reminder">Weitere Erinnerung hinzufügen</string>
<string name="event_reminders">Termin-Erinnerungen</string>
<!-- Event attendees -->
<string name="add_another_attendee">Add another attendee</string>
<!-- Export / Import -->
<string name="import_events">Termine importieren</string>
<string name="export_events">Termine exportieren</string>

View File

@@ -9,7 +9,8 @@
<string name="yearly_view">Ετήσια προβολή</string>
<string name="simple_event_list">Απλή λίστα εκδηλώσεων</string>
<string name="no_upcoming_events">Φαίνεται πως δεν έχετε επερχόμενες εκδηλώσεις.</string>
<string name="go_to_today">Πηγαίνετε στο σήμερα</string>
<string name="go_to_today">Μετάβαση στο σήμερα</string>
<string name="go_to_date">Μετάβαση σε ημερ/νία</string>
<!-- Widget titles -->
<string name="widget_monthly">Μηνιαίο ημερολόγιο</string>
@@ -49,7 +50,7 @@
<string name="update_one_only">Ενημέρωση μόνο του επιλεγμένου περιστατικού</string>
<string name="update_all_occurrences">Ενημέρωση όλων των περιστατικών</string>
<string name="repeat_till_date">Επαναλάβετε μέχρι μια ημερομηνία</string>
<string name="stop_repeating_after_x">Stop repeating after x occurrences</string>
<string name="stop_repeating_after_x">Παύση επαναλήψεων μετά από x εμφανίσεις</string>
<string name="repeat_forever">Επαναλάβετε για πάντα</string>
<string name="times">times</string>
<string name="repeat">Επανάληψη</string>
@@ -58,9 +59,9 @@
<string name="selected_days">Σε επιλεγμένες μέρες</string>
<string name="the_same_day">Την ίδια μέρα</string>
<string name="the_last_day">Την τελευταία μέρα</string>
<string name="repeat_on_the_same_day_monthly">Επαναλάβετε την ίδια ημέρα κάθε μήνα</string>
<string name="repeat_on_the_last_day_monthly">Επαναλάβετε την τελευταία ημέρα του μήνα</string>
<string name="repeat_on_the_same_day_yearly">Επαναλάβετε την τελευταία ημέρα του μήνα</string>
<string name="repeat_on_the_same_day_monthly">Επανάληψη την ίδια ημέρα κάθε μήνα</string>
<string name="repeat_on_the_last_day_monthly">Επανάληψη την τελευταία ημέρα του μήνα</string>
<string name="repeat_on_the_same_day_yearly">Επανάληψη την τελευταία ημέρα του έτους</string>
<string name="repeat_every_m">Επανάληψη κάθε</string>
<string name="every_m">Κάθε</string>
<string name="first_m">πρώτη</string>
@@ -97,6 +98,9 @@
<string name="add_another_reminder">Προσθέστε μια άλλη υπενθύμιση</string>
<string name="event_reminders">Υπενθυμίσεις εκδηλώσεων</string>
<!-- Event attendees -->
<string name="add_another_attendee">Προσθήκη άλλης συμμετοχής</string>
<!-- Export / Import -->
<string name="import_events">Εισαγωγή εκδηλώσεων</string>
<string name="export_events">Εξαγωγή εκδηλώσεων</string>
@@ -168,14 +172,14 @@
<string name="default_reminder_1">Προεπιλογή υπενθύμισης 1</string>
<string name="default_reminder_2">Προεπιλογή υπενθύμισης 2</string>
<string name="default_reminder_3">Προεπιλογή υπενθύμισης 3</string>
<string name="view_to_open_from_widget">Προβολή για να ανοίξετε από το widget λίστας συμβάντων</string>
<string name="view_to_open_from_widget">Προβολή για άνοιγμα από το widget λίστας συμβάντων</string>
<string name="last_view">Τελευταία προβολή</string>
<string name="new_events">Νέα γεγονότα</string>
<string name="default_start_time">Προεπιλεγμένη ώρα έναρξης</string>
<string name="next_full_hour">Την επόμενη πλήρη ώρα</string>
<string name="default_duration">Προεπιλεγμένη διάρκεια</string>
<string name="last_used_one">Τελευταία χρήση</string>
<string name="other_time">Other time</string>
<string name="other_time">Άλλη ώρα</string>
<!-- CalDAV sync -->
<string name="caldav">CalDAV</string>

View File

@@ -10,6 +10,7 @@
<string name="simple_event_list">Lista de eventos sencilla</string>
<string name="no_upcoming_events">Parece que no tienes ningún evento próximo.</string>
<string name="go_to_today">Ir al día de hoy</string>
<string name="go_to_date">Go to date</string>
<!-- Widget titles -->
<string name="widget_monthly">Calendario mensual</string>
@@ -97,6 +98,9 @@
<string name="add_another_reminder">Agregar otro recordatorio</string>
<string name="event_reminders">Recordatorio de eventos</string>
<!-- Event attendees -->
<string name="add_another_attendee">Add another attendee</string>
<!-- Export / Import -->
<string name="import_events">Importar eventos</string>
<string name="export_events">Exportar eventos</string>

View File

@@ -10,6 +10,7 @@
<string name="simple_event_list">Liste simple d\'événements</string>
<string name="no_upcoming_events">Il semblerait que vous n\'ayez aucun événement à venir.</string>
<string name="go_to_today">Aller à aujourd\'hui</string>
<string name="go_to_date">Go to date</string>
<!-- Widget titles -->
<string name="widget_monthly">Calendrier mensuel</string>
@@ -97,6 +98,9 @@
<string name="add_another_reminder">Ajouter un autre rappel</string>
<string name="event_reminders">Rappels d\'événements</string>
<!-- Event attendees -->
<string name="add_another_attendee">Add another attendee</string>
<!-- Export / Import -->
<string name="import_events">Importer des événements</string>
<string name="export_events">Exporter des événements</string>

View File

@@ -10,6 +10,7 @@
<string name="simple_event_list">Lista de eventos simple</string>
<string name="no_upcoming_events">Semella que non ten ningún evento próximo.</string>
<string name="go_to_today">Ir ao día de hoxe</string>
<string name="go_to_date">Go to date</string>
<!-- Widget titles -->
<string name="widget_monthly">Calendario mensual</string>
@@ -97,6 +98,9 @@
<string name="add_another_reminder">Engadir outro recordatorio</string>
<string name="event_reminders">Recordatorios de eventos</string>
<!-- Event attendees -->
<string name="add_another_attendee">Add another attendee</string>
<!-- Export / Import -->
<string name="import_events">Importar eventos</string>
<string name="export_events">Exportar eventos</string>

View File

@@ -10,6 +10,7 @@
<string name="simple_event_list">सरल इवेंट सूची</string>
<string name="no_upcoming_events">Seems like you don\'t have any upcoming events.</string>
<string name="go_to_today">Go to today</string>
<string name="go_to_date">Go to date</string>
<!-- Widget titles -->
<string name="widget_monthly">Calendar monthly</string>
@@ -98,6 +99,9 @@
<string name="add_another_reminder">Add another reminder</string>
<string name="event_reminders">Event reminders</string>
<!-- Event attendees -->
<string name="add_another_attendee">Add another attendee</string>
<!-- Export / Import -->
<string name="import_events">Import events</string>
<string name="export_events">Export events</string>

View File

@@ -10,6 +10,7 @@
<string name="simple_event_list">Jednostavan popis događaja</string>
<string name="no_upcoming_events">Izgleda da nemate nadolazećih događaja.</string>
<string name="go_to_today">Idi na danas</string>
<string name="go_to_date">Go to date</string>
<!-- Widget titles -->
<string name="widget_monthly">Mjesečni raspored</string>
@@ -97,6 +98,9 @@
<string name="add_another_reminder">Dodaj još jedan podsjetnik</string>
<string name="event_reminders">Podsjetnici na događaj</string>
<!-- Event attendees -->
<string name="add_another_attendee">Add another attendee</string>
<!-- Export / Import -->
<string name="import_events">Uvezi događaje</string>
<string name="export_events">Izvezi događaje</string>

View File

@@ -10,6 +10,7 @@
<string name="simple_event_list">Egyszerű bejegyzéslista</string>
<string name="no_upcoming_events">Seems like you don\'t have any upcoming events.</string>
<string name="go_to_today">Go to today</string>
<string name="go_to_date">Go to date</string>
<!-- Widget titles -->
<string name="widget_monthly">Havi naptár</string>
@@ -97,6 +98,9 @@
<string name="add_another_reminder">Add another reminder</string>
<string name="event_reminders">Event reminders</string>
<!-- Event attendees -->
<string name="add_another_attendee">Add another attendee</string>
<!-- Export / Import -->
<string name="import_events">Import events</string>
<string name="export_events">Export events</string>

View File

@@ -10,6 +10,7 @@
<string name="simple_event_list">Lista semplice deli eventi</string>
<string name="no_upcoming_events">Non c\'è nessun evento imminente.</string>
<string name="go_to_today">Vai a oggi</string>
<string name="go_to_date">Vai alla data</string>
<!-- Widget titles -->
<string name="widget_monthly">Calendario mensile</string>
@@ -97,6 +98,9 @@
<string name="add_another_reminder">Aggiungi un altro promemoria</string>
<string name="event_reminders">Promemoria eventi</string>
<!-- Event attendees -->
<string name="add_another_attendee">Aggiungi un partecipante</string>
<!-- Export / Import -->
<string name="import_events">Importa eventi</string>
<string name="export_events">Esporta eventi</string>
@@ -170,12 +174,12 @@
<string name="default_reminder_3">Promemoria predefinito 3</string>
<string name="view_to_open_from_widget">Vista da aprire dalla lista degli eventi del widget</string>
<string name="last_view">Ultima vista</string>
<string name="new_events">New events</string>
<string name="default_start_time">Default start time</string>
<string name="next_full_hour">Next full hour</string>
<string name="default_duration">Default duration</string>
<string name="last_used_one">Last used one</string>
<string name="other_time">Other time</string>
<string name="new_events">Nuovi eventi</string>
<string name="default_start_time">Tempo di inizio predefinito</string>
<string name="next_full_hour">Prossima ora completa</string>
<string name="default_duration">Durata predefinita</string>
<string name="last_used_one">L\'ultimo utilizzato</string>
<string name="other_time">Altro periodo</string>
<!-- CalDAV sync -->
<string name="caldav">CalDAV</string>

View File

@@ -10,6 +10,7 @@
<string name="simple_event_list">רשימת אירועים פשוטה</string>
<string name="no_upcoming_events">נראה שאין לך אירועים בזמן הקרוב.</string>
<string name="go_to_today">Go to today</string>
<string name="go_to_date">Go to date</string>
<!-- Widget titles -->
<string name="widget_monthly">לוח השנה חודשית</string>
@@ -98,6 +99,9 @@
<string name="add_another_reminder">Add another reminder</string>
<string name="event_reminders">Event reminders</string>
<!-- Event attendees -->
<string name="add_another_attendee">Add another attendee</string>
<!-- Export / Import -->
<string name="import_events">ייבוא אירועים</string>
<string name="export_events">Export events</string>

View File

@@ -10,6 +10,7 @@
<string name="simple_event_list">シンプル 予定 リスト</string>
<string name="no_upcoming_events">今後の予定はありません。</string>
<string name="go_to_today">今日へ移動</string>
<string name="go_to_date">Go to date</string>
<!-- Widget titles -->
<string name="widget_monthly">カレンダー月</string>
@@ -97,6 +98,9 @@
<string name="add_another_reminder">Add another reminder</string>
<string name="event_reminders">予定のリマインダー</string>
<!-- Event attendees -->
<string name="add_another_attendee">Add another attendee</string>
<!-- Export / Import -->
<string name="import_events">予定をインポート</string>
<string name="export_events">予定をエクスポート</string>

View File

@@ -10,6 +10,7 @@
<string name="simple_event_list">간단한 이벤트 목록</string>
<string name="no_upcoming_events">다가올 이벤트가 없는 것 같습니다.</string>
<string name="go_to_today">오늘로 이동</string>
<string name="go_to_date">Go to date</string>
<!-- Widget titles -->
<string name="widget_monthly">월별 달력</string>
@@ -97,6 +98,9 @@
<string name="add_another_reminder">다른 알림 추가</string>
<string name="event_reminders">Event reminders</string>
<!-- Event attendees -->
<string name="add_another_attendee">Add another attendee</string>
<!-- Export / Import -->
<string name="import_events">이벤트들 가져 오기</string>
<string name="export_events">이벤트들 내보내기</string>

View File

@@ -10,6 +10,7 @@
<string name="simple_event_list">Paprastas įvykių sąrašas</string>
<string name="no_upcoming_events">Atrodo jog Jūs neturite jokių įvyksiančių įvykių.</string>
<string name="go_to_today">Eiti į šiandieną</string>
<string name="go_to_date">Go to date</string>
<!-- Widget titles -->
<string name="widget_monthly">Mėnesio kalendorius</string>
@@ -97,6 +98,9 @@
<string name="add_another_reminder">Pridėti kitą priminimą</string>
<string name="event_reminders">Įvykių priminimai</string>
<!-- Event attendees -->
<string name="add_another_attendee">Add another attendee</string>
<!-- Export / Import -->
<string name="import_events">Importuoti įvykius</string>
<string name="export_events">Eksportuoti įvykius</string>

View File

@@ -10,6 +10,7 @@
<string name="simple_event_list">Enkel hendelsesliste</string>
<string name="no_upcoming_events">Ser ut som du ikke har noen kommende hendelser.</string>
<string name="go_to_today">Gå til idag</string>
<string name="go_to_date">Go to date</string>
<!-- Widget titles -->
<string name="widget_monthly">Månedskalender</string>
@@ -97,6 +98,9 @@
<string name="add_another_reminder">Legg til en annen påminnelse</string>
<string name="event_reminders">Hendelsespåminnelser</string>
<!-- Event attendees -->
<string name="add_another_attendee">Add another attendee</string>
<!-- Export / Import -->
<string name="import_events">Importer hendelser</string>
<string name="export_events">Eksporter hendelser</string>

View File

@@ -10,6 +10,7 @@
<string name="simple_event_list">Lijst</string>
<string name="no_upcoming_events">Niets gepland.</string>
<string name="go_to_today">Naar vandaag</string>
<string name="go_to_date">Naar datum</string>
<!-- Widget titles -->
<string name="widget_monthly">Maandweergave</string>
@@ -97,6 +98,9 @@
<string name="add_another_reminder">Herinnering toevoegen</string>
<string name="event_reminders">Herinneringen</string>
<!-- Event attendees -->
<string name="add_another_attendee">Persoon uitnodigen</string>
<!-- Export / Import -->
<string name="import_events">Afspraken importeren</string>
<string name="export_events">Afspraken exporteren</string>

View File

@@ -10,6 +10,7 @@
<string name="simple_event_list">Enkel hendelsesliste</string>
<string name="no_upcoming_events">Ser ut som du ikke har noen kommende hendelser.</string>
<string name="go_to_today">Gå til idag</string>
<string name="go_to_date">Go to date</string>
<!-- Widget titles -->
<string name="widget_monthly">Månedskalender</string>
@@ -97,6 +98,9 @@
<string name="add_another_reminder">Legg til en annen påminnelse</string>
<string name="event_reminders">Event reminders</string>
<!-- Event attendees -->
<string name="add_another_attendee">Add another attendee</string>
<!-- Export / Import -->
<string name="import_events">Importer hendelser</string>
<string name="export_events">Eksporter hendelser</string>

View File

@@ -10,6 +10,7 @@
<string name="simple_event_list">Prosta lista wydarzeń</string>
<string name="no_upcoming_events">Wygląda na to, że nie masz żadnych nadchodzących wydarzeń.</string>
<string name="go_to_today">Przejdź do dnia dzisiejszego</string>
<string name="go_to_date">Go to date</string>
<!-- Widget titles -->
<string name="widget_monthly">Prosty kalendarz - widok miesięczny</string>
@@ -97,6 +98,9 @@
<string name="add_another_reminder">Dodaj inne przypomnienie</string>
   <string name="event_reminders">Przypomnienia</string>
<!-- Event attendees -->
<string name="add_another_attendee">Add another attendee</string>
<!-- Export / Import -->
<string name="import_events">Importuj wydarzenia</string>
<string name="export_events">Eksportuj wydarzenia</string>

View File

@@ -10,6 +10,7 @@
<string name="simple_event_list">Lista de eventos</string>
<string name="no_upcoming_events">Parece que você não tem próximos eventos.</string>
<string name="go_to_today">Ir para hoje</string>
<string name="go_to_date">Go to date</string>
<!-- Widget titles -->
<string name="widget_monthly">Calendário mensal</string>
@@ -97,6 +98,9 @@
<string name="add_another_reminder">Adicionar outro lembrete</string>
<string name="event_reminders">Lembretes de eventos</string>
<!-- Event attendees -->
<string name="add_another_attendee">Add another attendee</string>
<!-- Export / Import -->
<string name="import_events">Importar eventos</string>
<string name="export_events">Exportar eventos</string>

View File

@@ -10,6 +10,7 @@
<string name="simple_event_list">Lista de eventos</string>
<string name="no_upcoming_events">Parece que você não tem eventos para breve</string>
<string name="go_to_today">Ir para hoje</string>
<string name="go_to_date">Go to date</string>
<!-- Widget titles -->
<string name="widget_monthly">Calendário mensal</string>
@@ -97,6 +98,9 @@
<string name="add_another_reminder">Adicionar outro lembrete</string>
<string name="event_reminders">Lembretes para eventos</string>
<!-- Event attendees -->
<string name="add_another_attendee">Add another attendee</string>
<!-- Export / Import -->
<string name="import_events">Importar eventos</string>
<string name="export_events">Exportar eventos</string>

View File

@@ -10,6 +10,7 @@
<string name="simple_event_list">Список событий</string>
<string name="no_upcoming_events">Похоже, нет предстоящих событий.</string>
<string name="go_to_today">Сегодня</string>
<string name="go_to_date">Go to date</string>
<!-- Widget titles -->
<string name="widget_monthly">Календарь на месяц</string>
@@ -97,6 +98,9 @@
<string name="add_another_reminder">Добавить ещё одно напоминание</string>
<string name="event_reminders">Напоминания о событиях</string>
<!-- Event attendees -->
<string name="add_another_attendee">Add another attendee</string>
<!-- Export / Import -->
<string name="import_events">Импорт событий</string>
<string name="export_events">Экспорт событий</string>

View File

@@ -10,6 +10,7 @@
<string name="simple_event_list">Jednoduchý zoznam</string>
<string name="no_upcoming_events">Nemáte žiadne naplánované udalosti.</string>
<string name="go_to_today">Prejsť na dnešok</string>
<string name="go_to_date">Prejsť na dátum</string>
<!-- Widget titles -->
<string name="widget_monthly">Kalendár mesačný</string>
@@ -97,6 +98,9 @@
<string name="add_another_reminder">Pridať ďalšiu pripomienku</string>
<string name="event_reminders">Pripomienky udalostí</string>
<!-- Event attendees -->
<string name="add_another_attendee">Pridať ďalšieho účastníka</string>
<!-- Export / Import -->
<string name="import_events">Importovať udalosti</string>
<string name="export_events">Exportovať udalosti</string>
@@ -224,7 +228,7 @@
<!-- Strings displayed only on Google Playstore. Optional, but good to have -->
<!-- Short description has to have less than 80 chars -->
<string name="app_short_description">Jednoduchý kalendár s udalosťami, upraviteľným widgetom a bez reklám.</string>
<string name="app_short_description">Offline kalendár pre vaše udalosti bez reklám, rešpektujúca vaše súkromie.</string>
<string name="app_long_description">
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.

View File

@@ -10,6 +10,7 @@
<string name="simple_event_list">Händelselista</string>
<string name="no_upcoming_events">Det verkar som att du inte har några kommande händelser.</string>
<string name="go_to_today">Gå till idag</string>
<string name="go_to_date">Gå till datum</string>
<!-- Widget titles -->
<string name="widget_monthly">Kalender månadsvis</string>
@@ -97,6 +98,9 @@
<string name="add_another_reminder">Lägg till en annan påminnelse</string>
<string name="event_reminders">Händelsepåminnelser</string>
<!-- Event attendees -->
<string name="add_another_attendee">Add another attendee</string>
<!-- Export / Import -->
<string name="import_events">Importera händelser</string>
<string name="export_events">Exportera händelser</string>
@@ -136,13 +140,13 @@
<string name="national_holidays">Nationella helgdagar</string>
<string name="religious_holidays">Religiösa helgdagar</string>
<string name="holidays_imported_successfully">Helgdagarna har importerats till händelsetypen \"Helgdagar\"</string>
<string name="importing_some_holidays_failed">Vissa händelser kunde inte importeras</string>
<string name="importing_holidays_failed">Helgdagar kunde inte importeras</string>
<string name="importing_some_holidays_failed">Importen av vissa händelser misslyckades</string>
<string name="importing_holidays_failed">Importen av helgdagarna misslyckades</string>
<!-- Settings -->
<string name="manage_event_types">Hantera händelsetyper</string>
<string name="start_day_at">Starta veckovydagen vid</string>
<string name="end_day_at">Sluta veckovydagen vid</string>
<string name="start_day_at">Dagen börjar</string>
<string name="end_day_at">Dagen slutar</string>
<string name="week_numbers">Visa veckonummer</string>
<string name="vibrate">Vibrera vid påminnelseaviseringar</string>
<string name="reminder_sound">Påminnelseljud</string>
@@ -170,12 +174,12 @@
<string name="default_reminder_3">Standardpåminnelse 3</string>
<string name="view_to_open_from_widget">Vy som öppnas från händelselistwidgeten</string>
<string name="last_view">Senaste vy</string>
<string name="new_events">New events</string>
<string name="default_start_time">Default start time</string>
<string name="next_full_hour">Next full hour</string>
<string name="default_duration">Default duration</string>
<string name="last_used_one">Last used one</string>
<string name="other_time">Other time</string>
<string name="new_events">Nya händelser</string>
<string name="default_start_time">Standardstarttid</string>
<string name="next_full_hour">Nästa heltimme</string>
<string name="default_duration">Standardvaraktighet</string>
<string name="last_used_one">Senast använda</string>
<string name="other_time">Annan tid</string>
<!-- CalDAV sync -->
<string name="caldav">CalDAV</string>

View File

@@ -10,6 +10,7 @@
<string name="simple_event_list">Basit etkinlik listesi</string>
<string name="no_upcoming_events">Yaklaşan etkinlikleriniz yok gibi görünüyor.</string>
<string name="go_to_today">Bugüne git</string>
<string name="go_to_date">Go to date</string>
<!-- Widget titles -->
<string name="widget_monthly">Aylık takvim</string>
@@ -97,6 +98,9 @@
<string name="add_another_reminder">Başka hatırlatma ekle</string>
<string name="event_reminders">Etkinlik hatırlatıcılar</string>
<!-- Event attendees -->
<string name="add_another_attendee">Add another attendee</string>
<!-- Export / Import -->
<string name="import_events">Etkinlikleri içe aktar</string>
<string name="export_events">Etkinlikleri dışa aktar</string>
@@ -170,12 +174,12 @@
<string name="default_reminder_3">Varsayılan hatırlatıcı 3</string>
<string name="view_to_open_from_widget">Etkinlik listesi widget\'ından açılacak görünüm</string>
<string name="last_view">Son görünüm</string>
<string name="new_events">New events</string>
<string name="default_start_time">Default start time</string>
<string name="next_full_hour">Next full hour</string>
<string name="default_duration">Default duration</string>
<string name="last_used_one">Last used one</string>
<string name="other_time">Other time</string>
<string name="new_events">Yeni etkinlikler</string>
<string name="default_start_time">Varsayılan başlangıç zamanı</string>
<string name="next_full_hour">Gelecek tam saat</string>
<string name="default_duration">Varsayılan süre</string>
<string name="last_used_one">Son kullanılan</string>
<string name="other_time">Başka zaman</string>
<!-- CalDAV sync -->
<string name="caldav">CalDAV</string>
@@ -189,7 +193,7 @@
<string name="syncing">Senkronize ediliyor…</string>
<string name="synchronization_completed">Senkronizasyon tamamlandı</string>
<string name="select_a_different_caldav_color">Farklı bir renk seçin (yalnızca yerel olarak uygulanabilir)</string>
<string name="insufficient_permissions">You are not allowed to write in the selected calendar</string>
<string name="insufficient_permissions">Seçili takvime yazmanıza izin verilmiyor</string>
<!-- alternative versions for some languages, use the same translations if you are not sure what this means -->
<!-- used in repetition, like "Every last Sunday" -->
@@ -223,7 +227,7 @@
<!-- Strings displayed only on Google Playstore. Optional, but good to have -->
<!-- Short description has to have less than 80 chars -->
<string name="app_short_description">An offline calendar for your events without ads, respecting your privacy.</string>
<string name="app_short_description">Etkinlikleriniz için reklamsız, gizliliğinizi önemseyen bir çevrimdışı takvim.</string>
<string name="app_long_description">
İ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.
</string>
<!--

View File

@@ -10,6 +10,7 @@
<string name="simple_event_list">簡易活動列表</string>
<string name="no_upcoming_events">你近期似乎沒有任何活動。</string>
<string name="go_to_today">前往今天</string>
<string name="go_to_date">Go to date</string>
<!-- Widget titles -->
<string name="widget_monthly">月曆</string>
@@ -97,6 +98,9 @@
<string name="add_another_reminder">新增另一個提醒</string>
<string name="event_reminders">活動提醒</string>
<!-- Event attendees -->
<string name="add_another_attendee">Add another attendee</string>
<!-- Export / Import -->
<string name="import_events">匯入活動</string>
<string name="export_events">匯出活動</string>
@@ -170,12 +174,12 @@
<string name="default_reminder_3">預設提醒3</string>
<string name="view_to_open_from_widget">從活動列表小工具開啟的檢視畫面</string>
<string name="last_view">最後的檢視畫面</string>
<string name="new_events">New events</string>
<string name="default_start_time">Default start time</string>
<string name="next_full_hour">Next full hour</string>
<string name="default_duration">Default duration</string>
<string name="last_used_one">Last used one</string>
<string name="other_time">Other time</string>
<string name="new_events">新活動</string>
<string name="default_start_time">預設開始時間</string>
<string name="next_full_hour">整整下個小時</string>
<string name="default_duration">預設持續時間</string>
<string name="last_used_one">最後使用</string>
<string name="other_time">其他時間</string>
<!-- CalDAV sync -->
<string name="caldav">CalDAV</string>

View File

@@ -28,4 +28,6 @@
<dimen name="event_color_bar_width">4dp</dimen>
<dimen name="event_color_bar_height">100dp</dimen>
<dimen name="avatar_size">40dp</dimen>
</resources>

View File

@@ -2,6 +2,10 @@
<resources>
<!-- Release notes -->
<string name="release_143">
Allow setting default start time/duration/event type for new events\n
Allow exporting/importing settings
</string>
<string name="release_129">Allow setting default event reminders, not always reuse the last events\' ones</string>
<string name="release_119">
Allow changing the app launcher icon color\n

View File

@@ -10,6 +10,7 @@
<string name="simple_event_list">Simple event list</string>
<string name="no_upcoming_events">Seems like you don\'t have any upcoming events.</string>
<string name="go_to_today">Go to today</string>
<string name="go_to_date">Go to date</string>
<!-- Widget titles -->
<string name="widget_monthly">Calendar monthly</string>
@@ -97,6 +98,9 @@
<string name="add_another_reminder">Add another reminder</string>
<string name="event_reminders">Event reminders</string>
<!-- Event attendees -->
<string name="add_another_attendee">Add another attendee</string>
<!-- Export / Import -->
<string name="import_events">Import events</string>
<string name="export_events">Export events</string>
@@ -224,7 +228,7 @@
<!-- Strings displayed only on Google Playstore. Optional, but good to have -->
<!-- Short description has to have less than 80 chars -->
<string name="app_short_description">Offline kalendár pre vaše udalosti bez reklám, rešpektujúca vaše súkromie.</string>
<string name="app_short_description">An offline calendar for your events without ads, respecting your privacy.</string>
<string name="app_long_description">
A simple calendar with optional CalDAV synchronization. You can easily create recurring events and setup reminders, it can also display week numbers.

View File

@@ -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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 50 KiB

After

Width:  |  Height:  |  Size: 128 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 96 KiB

After

Width:  |  Height:  |  Size: 149 KiB