1916 lines
74 KiB
Kotlin
1916 lines
74 KiB
Kotlin
package com.simplemobiletools.calendar.pro.activities
|
|
|
|
import android.app.Activity
|
|
import android.app.DatePickerDialog
|
|
import android.app.TimePickerDialog
|
|
import android.content.Intent
|
|
import android.graphics.drawable.BitmapDrawable
|
|
import android.graphics.drawable.Drawable
|
|
import android.graphics.drawable.LayerDrawable
|
|
import android.net.Uri
|
|
import android.os.Bundle
|
|
import android.provider.CalendarContract.Attendees
|
|
import android.provider.CalendarContract.Colors
|
|
import android.provider.ContactsContract.CommonDataKinds
|
|
import android.provider.ContactsContract.CommonDataKinds.StructuredName
|
|
import android.provider.ContactsContract.Data
|
|
import android.text.TextUtils
|
|
import android.text.method.LinkMovementMethod
|
|
import android.view.View
|
|
import android.view.WindowManager
|
|
import android.view.inputmethod.EditorInfo
|
|
import android.widget.ImageView
|
|
import android.widget.RelativeLayout
|
|
import com.google.android.material.timepicker.MaterialTimePicker
|
|
import com.google.android.material.timepicker.MaterialTimePicker.INPUT_MODE_CLOCK
|
|
import com.google.android.material.timepicker.TimeFormat
|
|
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.databinding.ActivityEventBinding
|
|
import com.simplemobiletools.calendar.pro.databinding.ItemAttendeeBinding
|
|
import com.simplemobiletools.calendar.pro.dialogs.*
|
|
import com.simplemobiletools.calendar.pro.extensions.*
|
|
import com.simplemobiletools.calendar.pro.helpers.*
|
|
import com.simplemobiletools.calendar.pro.models.*
|
|
import com.simplemobiletools.commons.dialogs.ColorPickerDialog
|
|
import com.simplemobiletools.commons.dialogs.ConfirmationAdvancedDialog
|
|
import com.simplemobiletools.commons.dialogs.PermissionRequiredDialog
|
|
import com.simplemobiletools.commons.dialogs.RadioGroupDialog
|
|
import com.simplemobiletools.commons.extensions.*
|
|
import com.simplemobiletools.commons.helpers.*
|
|
import com.simplemobiletools.commons.models.RadioItem
|
|
import com.simplemobiletools.commons.views.MyAutoCompleteTextView
|
|
import org.joda.time.DateTime
|
|
import org.joda.time.DateTimeZone
|
|
import java.util.TimeZone
|
|
import java.util.regex.Pattern
|
|
|
|
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+)?)\$"
|
|
private val SELECT_TIME_ZONE_INTENT = 1
|
|
|
|
private var mIsAllDayEvent = false
|
|
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
|
|
private var mEventTypeId = REGULAR_EVENT_TYPE_ID
|
|
private var mEventOccurrenceTS = 0L
|
|
private var mLastSavePromptTS = 0L
|
|
private var mEventCalendarId = STORED_LOCALLY_ONLY
|
|
private var mWasContactsPermissionChecked = false
|
|
private var mWasCalendarChanged = false
|
|
private var mAttendees = ArrayList<Attendee>()
|
|
private var mAttendeeAutoCompleteViews = ArrayList<MyAutoCompleteTextView>()
|
|
private var mAvailableContacts = ArrayList<Attendee>()
|
|
private var mSelectedContacts = ArrayList<Attendee>()
|
|
private var mAvailability = Attendees.AVAILABILITY_BUSY
|
|
private var mStoredEventTypes = ArrayList<EventType>()
|
|
private var mOriginalTimeZone = DateTimeZone.getDefault().id
|
|
private var mOriginalStartTS = 0L
|
|
private var mOriginalEndTS = 0L
|
|
private var mIsNewEvent = true
|
|
private var mEventColor = 0
|
|
|
|
private lateinit var mEventStartDateTime: DateTime
|
|
private lateinit var mEventEndDateTime: DateTime
|
|
private lateinit var mEvent: Event
|
|
|
|
private val binding by viewBinding(ActivityEventBinding::inflate)
|
|
|
|
override fun onCreate(savedInstanceState: Bundle?) {
|
|
isMaterialActivity = true
|
|
super.onCreate(savedInstanceState)
|
|
setContentView(binding.root)
|
|
setupOptionsMenu()
|
|
refreshMenuItems()
|
|
|
|
if (checkAppSideloading()) {
|
|
return
|
|
}
|
|
|
|
updateMaterialActivityViews(binding.eventCoordinator, binding.eventHolder, useTransparentNavigation = true, useTopSearchMenu = false)
|
|
setupMaterialScrollListener(binding.eventNestedScrollview, binding.eventToolbar)
|
|
|
|
val intent = intent ?: return
|
|
mWasContactsPermissionChecked = hasPermission(PERMISSION_READ_CONTACTS)
|
|
|
|
val eventId = intent.getLongExtra(EVENT_ID, 0L)
|
|
ensureBackgroundThread {
|
|
mStoredEventTypes = eventTypesDB.getEventTypes().toMutableList() as ArrayList<EventType>
|
|
val event = eventsDB.getEventWithId(eventId)
|
|
if (eventId != 0L && event == null) {
|
|
hideKeyboard()
|
|
finish()
|
|
return@ensureBackgroundThread
|
|
}
|
|
|
|
val localEventType = mStoredEventTypes.firstOrNull { it.id == config.lastUsedLocalEventTypeId }
|
|
runOnUiThread {
|
|
if (!isDestroyed && !isFinishing) {
|
|
gotEvent(savedInstanceState, localEventType, event)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
override fun onResume() {
|
|
super.onResume()
|
|
setupToolbar(binding.eventToolbar, NavigationIcon.Arrow)
|
|
}
|
|
|
|
override fun onBackPressed() {
|
|
if (System.currentTimeMillis() - mLastSavePromptTS > SAVE_DISCARD_PROMPT_INTERVAL && isEventChanged()) {
|
|
mLastSavePromptTS = System.currentTimeMillis()
|
|
ConfirmationAdvancedDialog(
|
|
activity = this,
|
|
message = "",
|
|
messageId = com.simplemobiletools.commons.R.string.save_before_closing,
|
|
positive = com.simplemobiletools.commons.R.string.save,
|
|
negative = com.simplemobiletools.commons.R.string.discard
|
|
) {
|
|
if (it) {
|
|
saveCurrentEvent()
|
|
} else {
|
|
super.onBackPressed()
|
|
}
|
|
}
|
|
} else {
|
|
super.onBackPressed()
|
|
}
|
|
}
|
|
|
|
override fun onSaveInstanceState(outState: Bundle) {
|
|
super.onSaveInstanceState(outState)
|
|
if (!::mEvent.isInitialized) {
|
|
return
|
|
}
|
|
|
|
outState.apply {
|
|
putSerializable(EVENT, mEvent)
|
|
putLong(START_TS, mEventStartDateTime.seconds())
|
|
putLong(END_TS, mEventEndDateTime.seconds())
|
|
putString(TIME_ZONE, mEvent.timeZone)
|
|
|
|
putInt(REMINDER_1_MINUTES, mReminder1Minutes)
|
|
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, Gson().toJson(getAllAttendees(false)))
|
|
|
|
putInt(AVAILABILITY, mAvailability)
|
|
putInt(EVENT_COLOR, mEventColor)
|
|
|
|
putLong(EVENT_TYPE_ID, mEventTypeId)
|
|
putInt(EVENT_CALENDAR_ID, mEventCalendarId)
|
|
putBoolean(IS_NEW_EVENT, mIsNewEvent)
|
|
putLong(ORIGINAL_START_TS, mOriginalStartTS)
|
|
putLong(ORIGINAL_END_TS, mOriginalEndTS)
|
|
}
|
|
}
|
|
|
|
override fun onRestoreInstanceState(savedInstanceState: Bundle) {
|
|
super.onRestoreInstanceState(savedInstanceState)
|
|
if (!savedInstanceState.containsKey(START_TS)) {
|
|
hideKeyboard()
|
|
finish()
|
|
return
|
|
}
|
|
|
|
savedInstanceState.apply {
|
|
mEvent = getSerializable(EVENT) as Event
|
|
mEventStartDateTime = Formatter.getDateTimeFromTS(getLong(START_TS))
|
|
mEventEndDateTime = Formatter.getDateTimeFromTS(getLong(END_TS))
|
|
mEvent.timeZone = getString(TIME_ZONE) ?: TimeZone.getDefault().id
|
|
|
|
mReminder1Minutes = getInt(REMINDER_1_MINUTES)
|
|
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)
|
|
|
|
mAvailability = getInt(AVAILABILITY)
|
|
mEventColor = getInt(EVENT_COLOR)
|
|
|
|
mRepeatInterval = getInt(REPEAT_INTERVAL)
|
|
mRepeatRule = getInt(REPEAT_RULE)
|
|
mRepeatLimit = getLong(REPEAT_LIMIT)
|
|
|
|
val token = object : TypeToken<List<Attendee>>() {}.type
|
|
mAttendees = Gson().fromJson<ArrayList<Attendee>>(getString(ATTENDEES), token) ?: ArrayList()
|
|
|
|
mEventTypeId = getLong(EVENT_TYPE_ID)
|
|
mEventCalendarId = getInt(EVENT_CALENDAR_ID)
|
|
mIsNewEvent = getBoolean(IS_NEW_EVENT)
|
|
mOriginalStartTS = getLong(ORIGINAL_START_TS)
|
|
mOriginalEndTS = getLong(ORIGINAL_END_TS)
|
|
}
|
|
|
|
checkRepeatTexts(mRepeatInterval)
|
|
checkRepeatRule()
|
|
updateTexts()
|
|
updateEventType()
|
|
updateCalDAVCalendar()
|
|
checkAttendees()
|
|
updateActionBarTitle()
|
|
}
|
|
|
|
override fun onActivityResult(requestCode: Int, resultCode: Int, resultData: Intent?) {
|
|
if (requestCode == SELECT_TIME_ZONE_INTENT && resultCode == Activity.RESULT_OK && resultData?.hasExtra(TIME_ZONE) == true) {
|
|
val timeZone = resultData.getSerializableExtra(TIME_ZONE) as MyTimeZone
|
|
mEvent.timeZone = timeZone.zoneName
|
|
updateTimeZoneText()
|
|
}
|
|
super.onActivityResult(requestCode, resultCode, resultData)
|
|
}
|
|
|
|
private fun gotEvent(savedInstanceState: Bundle?, localEventType: EventType?, event: Event?) = binding.apply {
|
|
if (localEventType == null || localEventType.caldavCalendarId != 0) {
|
|
config.lastUsedLocalEventTypeId = REGULAR_EVENT_TYPE_ID
|
|
}
|
|
|
|
mEventTypeId = if (config.defaultEventTypeId == -1L) config.lastUsedLocalEventTypeId else config.defaultEventTypeId
|
|
|
|
if (event != null) {
|
|
mEvent = event
|
|
mEventOccurrenceTS = intent.getLongExtra(EVENT_OCCURRENCE_TS, 0L)
|
|
if (savedInstanceState == null) {
|
|
setupEditEvent()
|
|
}
|
|
|
|
if (intent.getBooleanExtra(IS_DUPLICATE_INTENT, false)) {
|
|
mEvent.id = null
|
|
eventToolbar.title = getString(R.string.new_event)
|
|
} else {
|
|
cancelNotification(mEvent.id!!)
|
|
}
|
|
} else {
|
|
mEvent = Event(null)
|
|
config.apply {
|
|
mReminder1Minutes = if (usePreviousEventReminders && lastEventReminderMinutes1 >= -1) lastEventReminderMinutes1 else defaultReminder1
|
|
mReminder2Minutes = if (usePreviousEventReminders && lastEventReminderMinutes2 >= -1) lastEventReminderMinutes2 else defaultReminder2
|
|
mReminder3Minutes = if (usePreviousEventReminders && lastEventReminderMinutes3 >= -1) lastEventReminderMinutes3 else defaultReminder3
|
|
}
|
|
|
|
if (savedInstanceState == null) {
|
|
setupNewEvent()
|
|
}
|
|
}
|
|
|
|
if (savedInstanceState == null) {
|
|
updateTexts()
|
|
updateEventType()
|
|
updateCalDAVCalendar()
|
|
}
|
|
|
|
eventShowOnMap.setOnClickListener { showOnMap() }
|
|
eventStartDate.setOnClickListener { setupStartDate() }
|
|
eventStartTime.setOnClickListener { setupStartTime() }
|
|
eventEndDate.setOnClickListener { setupEndDate() }
|
|
eventEndTime.setOnClickListener { setupEndTime() }
|
|
eventTimeZone.setOnClickListener { setupTimeZone() }
|
|
|
|
eventAllDay.setOnCheckedChangeListener { _, isChecked -> toggleAllDay(isChecked) }
|
|
eventRepetition.setOnClickListener { showRepeatIntervalDialog() }
|
|
eventRepetitionRuleHolder.setOnClickListener { showRepetitionRuleDialog() }
|
|
eventRepetitionLimitHolder.setOnClickListener { showRepetitionTypePicker() }
|
|
|
|
eventReminder1.setOnClickListener {
|
|
handleNotificationAvailability {
|
|
if (config.wasAlarmWarningShown) {
|
|
showReminder1Dialog()
|
|
} else {
|
|
ReminderWarningDialog(this@EventActivity) {
|
|
config.wasAlarmWarningShown = true
|
|
showReminder1Dialog()
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
eventReminder2.setOnClickListener { showReminder2Dialog() }
|
|
eventReminder3.setOnClickListener { showReminder3Dialog() }
|
|
|
|
eventReminder1Type.setOnClickListener {
|
|
showReminderTypePicker(mReminder1Type) {
|
|
mReminder1Type = it
|
|
updateReminderTypeImage(eventReminder1Type, Reminder(mReminder1Minutes, mReminder1Type))
|
|
}
|
|
}
|
|
|
|
eventReminder2Type.setOnClickListener {
|
|
showReminderTypePicker(mReminder2Type) {
|
|
mReminder2Type = it
|
|
updateReminderTypeImage(eventReminder2Type, Reminder(mReminder2Minutes, mReminder2Type))
|
|
}
|
|
}
|
|
|
|
eventReminder3Type.setOnClickListener {
|
|
showReminderTypePicker(mReminder3Type) {
|
|
mReminder3Type = it
|
|
updateReminderTypeImage(eventReminder3Type, Reminder(mReminder3Minutes, mReminder3Type))
|
|
}
|
|
}
|
|
|
|
eventAvailability.setOnClickListener {
|
|
showAvailabilityPicker(mAvailability) {
|
|
mAvailability = it
|
|
updateAvailabilityText()
|
|
updateAvailabilityImage()
|
|
}
|
|
}
|
|
|
|
eventTypeHolder.setOnClickListener { showEventTypeDialog() }
|
|
eventAllDay.apply {
|
|
isChecked = mEvent.getIsAllDay()
|
|
jumpDrawablesToCurrentState()
|
|
}
|
|
|
|
eventAllDayHolder.setOnClickListener {
|
|
eventAllDay.toggle()
|
|
}
|
|
|
|
eventColorHolder.setOnClickListener {
|
|
showEventColorDialog()
|
|
}
|
|
|
|
updateTextColors(eventNestedScrollview)
|
|
updateIconColors()
|
|
refreshMenuItems()
|
|
showOrHideTimeZone()
|
|
}
|
|
|
|
private fun refreshMenuItems() {
|
|
if (::mEvent.isInitialized) {
|
|
binding.eventToolbar.menu.apply {
|
|
findItem(R.id.delete).isVisible = mEvent.id != null
|
|
findItem(R.id.share).isVisible = mEvent.id != null
|
|
findItem(R.id.duplicate).isVisible = mEvent.id != null
|
|
}
|
|
}
|
|
}
|
|
|
|
private fun setupOptionsMenu() {
|
|
binding.eventToolbar.setOnMenuItemClickListener { menuItem ->
|
|
if (!::mEvent.isInitialized) {
|
|
return@setOnMenuItemClickListener true
|
|
}
|
|
|
|
when (menuItem.itemId) {
|
|
R.id.save -> saveCurrentEvent()
|
|
R.id.delete -> deleteEvent()
|
|
R.id.duplicate -> duplicateEvent()
|
|
R.id.share -> shareEvent()
|
|
else -> return@setOnMenuItemClickListener false
|
|
}
|
|
return@setOnMenuItemClickListener true
|
|
}
|
|
}
|
|
|
|
private fun getStartEndTimes(): Pair<Long, Long> {
|
|
if (mIsAllDayEvent) {
|
|
val newStartTS = mEventStartDateTime.withTimeAtStartOfDay().seconds()
|
|
val newEndTS = mEventEndDateTime.withTimeAtStartOfDay().withHourOfDay(12).seconds()
|
|
return Pair(newStartTS, newEndTS)
|
|
} else {
|
|
val offset = if (!config.allowChangingTimeZones || mEvent.getTimeZoneString().equals(mOriginalTimeZone, true)) {
|
|
0
|
|
} else {
|
|
val original = mOriginalTimeZone.ifEmpty { DateTimeZone.getDefault().id }
|
|
val millis = System.currentTimeMillis()
|
|
val newOffset = DateTimeZone.forID(mEvent.getTimeZoneString()).getOffset(millis)
|
|
val oldOffset = DateTimeZone.forID(original).getOffset(millis)
|
|
(newOffset - oldOffset) / 1000L
|
|
}
|
|
|
|
val newStartTS = mEventStartDateTime.seconds() - offset
|
|
val newEndTS = mEventEndDateTime.seconds() - offset
|
|
return Pair(newStartTS, newEndTS)
|
|
}
|
|
}
|
|
|
|
private fun getReminders(): ArrayList<Reminder> {
|
|
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>
|
|
return reminders
|
|
}
|
|
|
|
private fun isEventChanged(): Boolean {
|
|
if (!::mEvent.isInitialized) {
|
|
return false
|
|
}
|
|
|
|
var newStartTS: Long
|
|
var newEndTS: Long
|
|
getStartEndTimes().apply {
|
|
newStartTS = first
|
|
newEndTS = second
|
|
}
|
|
|
|
val hasTimeChanged = if (mOriginalStartTS == 0L) {
|
|
mEvent.startTS != newStartTS || mEvent.endTS != newEndTS
|
|
} else {
|
|
mOriginalStartTS != newStartTS || mOriginalEndTS != newEndTS
|
|
}
|
|
|
|
val reminders = getReminders()
|
|
if (binding.eventTitle.text.toString() != mEvent.title ||
|
|
binding.eventLocation.text.toString() != mEvent.location ||
|
|
binding.eventDescription.text.toString() != mEvent.description ||
|
|
binding.eventTimeZone.text != mEvent.getTimeZoneString() ||
|
|
reminders != mEvent.getReminders() ||
|
|
mRepeatInterval != mEvent.repeatInterval ||
|
|
mRepeatRule != mEvent.repeatRule ||
|
|
mEventTypeId != mEvent.eventType ||
|
|
mWasCalendarChanged ||
|
|
mIsAllDayEvent != mEvent.getIsAllDay() ||
|
|
mEventColor != mEvent.color ||
|
|
hasTimeChanged
|
|
) {
|
|
return true
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
private fun updateTexts() {
|
|
updateRepetitionText()
|
|
checkReminderTexts()
|
|
updateStartTexts()
|
|
updateEndTexts()
|
|
updateTimeZoneText()
|
|
updateCalDAVVisibility()
|
|
updateAvailabilityText()
|
|
updateAvailabilityImage()
|
|
}
|
|
|
|
private fun setupEditEvent() {
|
|
mIsNewEvent = false
|
|
val realStart = if (mEventOccurrenceTS == 0L) mEvent.startTS else mEventOccurrenceTS
|
|
val duration = mEvent.endTS - mEvent.startTS
|
|
mOriginalStartTS = realStart
|
|
mOriginalEndTS = realStart + duration
|
|
|
|
window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN)
|
|
binding.eventToolbar.title = getString(R.string.edit_event)
|
|
mOriginalTimeZone = mEvent.timeZone
|
|
if (config.allowChangingTimeZones) {
|
|
try {
|
|
mEventStartDateTime = Formatter.getDateTimeFromTS(realStart).withZone(DateTimeZone.forID(mOriginalTimeZone))
|
|
mEventEndDateTime = Formatter.getDateTimeFromTS(realStart + duration).withZone(DateTimeZone.forID(mOriginalTimeZone))
|
|
} catch (e: Exception) {
|
|
showErrorToast(e)
|
|
mEventStartDateTime = Formatter.getDateTimeFromTS(realStart)
|
|
mEventEndDateTime = Formatter.getDateTimeFromTS(realStart + duration)
|
|
}
|
|
} else {
|
|
mEventStartDateTime = Formatter.getDateTimeFromTS(realStart)
|
|
mEventEndDateTime = Formatter.getDateTimeFromTS(realStart + duration)
|
|
}
|
|
|
|
binding.eventTitle.setText(mEvent.title)
|
|
binding.eventLocation.setText(mEvent.location)
|
|
binding.eventDescription.setText(mEvent.description)
|
|
|
|
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()
|
|
mAvailability = mEvent.availability
|
|
mEventColor = mEvent.color
|
|
|
|
mAttendees = mEvent.attendees.toMutableList() as ArrayList<Attendee>
|
|
|
|
checkRepeatTexts(mRepeatInterval)
|
|
checkAttendees()
|
|
}
|
|
|
|
private fun setupNewEvent() {
|
|
window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE)
|
|
binding.eventTitle.requestFocus()
|
|
binding.eventToolbar.title = getString(R.string.new_event)
|
|
if (config.defaultEventTypeId != -1L) {
|
|
config.lastUsedCaldavCalendarId = mStoredEventTypes.firstOrNull { it.id == config.defaultEventTypeId }?.caldavCalendarId ?: 0
|
|
}
|
|
|
|
val isLastCaldavCalendarOK = config.caldavSync && config.getSyncedCalendarIdsAsList().contains(config.lastUsedCaldavCalendarId)
|
|
mEventCalendarId = if (isLastCaldavCalendarOK) config.lastUsedCaldavCalendarId else STORED_LOCALLY_ONLY
|
|
|
|
if (intent.action == Intent.ACTION_EDIT || intent.action == Intent.ACTION_INSERT) {
|
|
val startTS = intent.getLongExtra("beginTime", System.currentTimeMillis()) / 1000L
|
|
mEventStartDateTime = Formatter.getDateTimeFromTS(startTS)
|
|
|
|
val endTS = intent.getLongExtra("endTime", System.currentTimeMillis()) / 1000L
|
|
mEventEndDateTime = Formatter.getDateTimeFromTS(endTS)
|
|
|
|
if (intent.getBooleanExtra("allDay", false)) {
|
|
mEvent.flags = mEvent.flags or FLAG_ALL_DAY
|
|
binding.eventAllDay.isChecked = true
|
|
toggleAllDay(true)
|
|
}
|
|
|
|
binding.eventTitle.setText(intent.getStringExtra("title"))
|
|
binding.eventLocation.setText(intent.getStringExtra("eventLocation"))
|
|
binding.eventDescription.setText(intent.getStringExtra("description"))
|
|
if (binding.eventDescription.value.isNotEmpty()) {
|
|
binding.eventDescription.movementMethod = LinkMovementMethod.getInstance()
|
|
}
|
|
} else {
|
|
val startTS = intent.getLongExtra(NEW_EVENT_START_TS, 0L)
|
|
val dateTime = Formatter.getDateTimeFromTS(startTS)
|
|
mEventStartDateTime = dateTime
|
|
|
|
val addMinutes = if (intent.getBooleanExtra(NEW_EVENT_SET_HOUR_DURATION, false)) {
|
|
// if an event is created at 23:00 on the weekly view, make it end on 23:59 by default to avoid spanning across multiple days
|
|
if (mEventStartDateTime.hourOfDay == 23) {
|
|
59
|
|
} else {
|
|
60
|
|
}
|
|
} else {
|
|
config.defaultDuration
|
|
}
|
|
mEventEndDateTime = mEventStartDateTime.plusMinutes(addMinutes)
|
|
}
|
|
addDefValuesToNewEvent()
|
|
checkAttendees()
|
|
}
|
|
|
|
private fun addDefValuesToNewEvent() {
|
|
var newStartTS: Long
|
|
var newEndTS: Long
|
|
getStartEndTimes().apply {
|
|
newStartTS = first
|
|
newEndTS = second
|
|
}
|
|
|
|
mEvent.apply {
|
|
startTS = newStartTS
|
|
endTS = newEndTS
|
|
reminder1Minutes = mReminder1Minutes
|
|
reminder1Type = mReminder1Type
|
|
reminder2Minutes = mReminder2Minutes
|
|
reminder2Type = mReminder2Type
|
|
reminder3Minutes = mReminder3Minutes
|
|
reminder3Type = mReminder3Type
|
|
eventType = mEventTypeId
|
|
}
|
|
}
|
|
|
|
private fun checkAttendees() {
|
|
ensureBackgroundThread {
|
|
fillAvailableContacts()
|
|
updateAttendees()
|
|
}
|
|
}
|
|
|
|
private fun showReminder1Dialog() {
|
|
showPickSecondsDialogHelper(mReminder1Minutes, showDuringDayOption = mIsAllDayEvent) {
|
|
mReminder1Minutes = if (it == -1 || it == 0) it else it / 60
|
|
checkReminderTexts()
|
|
}
|
|
}
|
|
|
|
private fun showReminder2Dialog() {
|
|
showPickSecondsDialogHelper(mReminder2Minutes, showDuringDayOption = mIsAllDayEvent) {
|
|
mReminder2Minutes = if (it == -1 || it == 0) it else it / 60
|
|
checkReminderTexts()
|
|
}
|
|
}
|
|
|
|
private fun showReminder3Dialog() {
|
|
showPickSecondsDialogHelper(mReminder3Minutes, showDuringDayOption = mIsAllDayEvent) {
|
|
mReminder3Minutes = if (it == -1 || it == 0) it else it / 60
|
|
checkReminderTexts()
|
|
}
|
|
}
|
|
|
|
private fun showRepeatIntervalDialog() {
|
|
showEventRepeatIntervalDialog(mRepeatInterval) {
|
|
setRepeatInterval(it)
|
|
}
|
|
}
|
|
|
|
private fun setRepeatInterval(interval: Int) {
|
|
mRepeatInterval = interval
|
|
updateRepetitionText()
|
|
checkRepeatTexts(interval)
|
|
|
|
when {
|
|
mRepeatInterval.isXWeeklyRepetition() -> setRepeatRule(Math.pow(2.0, (mEventStartDateTime.dayOfWeek - 1).toDouble()).toInt())
|
|
mRepeatInterval.isXMonthlyRepetition() -> setRepeatRule(REPEAT_SAME_DAY)
|
|
mRepeatInterval.isXYearlyRepetition() -> setRepeatRule(REPEAT_SAME_DAY)
|
|
}
|
|
}
|
|
|
|
private fun checkRepeatTexts(limit: Int) {
|
|
binding.eventRepetitionLimitHolder.beGoneIf(limit == 0)
|
|
checkRepetitionLimitText()
|
|
|
|
binding.eventRepetitionRuleHolder.beVisibleIf(mRepeatInterval.isXWeeklyRepetition() || mRepeatInterval.isXMonthlyRepetition() || mRepeatInterval.isXYearlyRepetition())
|
|
checkRepetitionRuleText()
|
|
}
|
|
|
|
private fun showRepetitionTypePicker() {
|
|
hideKeyboard()
|
|
RepeatLimitTypePickerDialog(this, mRepeatLimit, mEventStartDateTime.seconds()) {
|
|
setRepeatLimit(it)
|
|
}
|
|
}
|
|
|
|
private fun setRepeatLimit(limit: Long) {
|
|
mRepeatLimit = limit
|
|
checkRepetitionLimitText()
|
|
}
|
|
|
|
private fun checkRepetitionLimitText() {
|
|
binding.eventRepetitionLimit.text = when {
|
|
mRepeatLimit == 0L -> {
|
|
binding.eventRepetitionLimitLabel.text = getString(R.string.repeat)
|
|
resources.getString(R.string.forever)
|
|
}
|
|
|
|
mRepeatLimit > 0 -> {
|
|
binding.eventRepetitionLimitLabel.text = getString(R.string.repeat_till)
|
|
val repeatLimitDateTime = Formatter.getDateTimeFromTS(mRepeatLimit)
|
|
Formatter.getFullDate(this, repeatLimitDateTime)
|
|
}
|
|
|
|
else -> {
|
|
binding.eventRepetitionLimitLabel.text = getString(R.string.repeat)
|
|
"${-mRepeatLimit} ${getString(R.string.times)}"
|
|
}
|
|
}
|
|
}
|
|
|
|
private fun showRepetitionRuleDialog() {
|
|
hideKeyboard()
|
|
when {
|
|
mRepeatInterval.isXWeeklyRepetition() -> RepeatRuleWeeklyDialog(this, mRepeatRule) {
|
|
setRepeatRule(it)
|
|
}
|
|
|
|
mRepeatInterval.isXMonthlyRepetition() -> {
|
|
val items = getAvailableMonthlyRepetitionRules()
|
|
RadioGroupDialog(this, items, mRepeatRule) {
|
|
setRepeatRule(it as Int)
|
|
}
|
|
}
|
|
|
|
mRepeatInterval.isXYearlyRepetition() -> {
|
|
val items = getAvailableYearlyRepetitionRules()
|
|
RadioGroupDialog(this, items, mRepeatRule) {
|
|
setRepeatRule(it as Int)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private fun getAvailableMonthlyRepetitionRules(): ArrayList<RadioItem> {
|
|
val items = arrayListOf(RadioItem(REPEAT_SAME_DAY, getString(R.string.repeat_on_the_same_day_monthly)))
|
|
|
|
items.add(RadioItem(REPEAT_ORDER_WEEKDAY, getRepeatXthDayString(true, REPEAT_ORDER_WEEKDAY)))
|
|
if (isLastWeekDayOfMonth()) {
|
|
items.add(RadioItem(REPEAT_ORDER_WEEKDAY_USE_LAST, getRepeatXthDayString(true, REPEAT_ORDER_WEEKDAY_USE_LAST)))
|
|
}
|
|
|
|
if (isLastDayOfTheMonth()) {
|
|
items.add(RadioItem(REPEAT_LAST_DAY, getString(R.string.repeat_on_the_last_day_monthly)))
|
|
}
|
|
return items
|
|
}
|
|
|
|
private fun getAvailableYearlyRepetitionRules(): ArrayList<RadioItem> {
|
|
val items = arrayListOf(RadioItem(REPEAT_SAME_DAY, getString(R.string.repeat_on_the_same_day_yearly)))
|
|
|
|
items.add(RadioItem(REPEAT_ORDER_WEEKDAY, getRepeatXthDayInMonthString(true, REPEAT_ORDER_WEEKDAY)))
|
|
if (isLastWeekDayOfMonth()) {
|
|
items.add(RadioItem(REPEAT_ORDER_WEEKDAY_USE_LAST, getRepeatXthDayInMonthString(true, REPEAT_ORDER_WEEKDAY_USE_LAST)))
|
|
}
|
|
|
|
return items
|
|
}
|
|
|
|
private fun isLastDayOfTheMonth() = mEventStartDateTime.dayOfMonth == mEventStartDateTime.dayOfMonth().withMaximumValue().dayOfMonth
|
|
|
|
private fun isLastWeekDayOfMonth() = mEventStartDateTime.monthOfYear != mEventStartDateTime.plusDays(7).monthOfYear
|
|
|
|
private fun getRepeatXthDayString(includeBase: Boolean, repeatRule: Int): String {
|
|
val dayOfWeek = mEventStartDateTime.dayOfWeek
|
|
val base = getBaseString(dayOfWeek)
|
|
val order = getOrderString(repeatRule)
|
|
val dayString = getDayString(dayOfWeek)
|
|
return if (includeBase) {
|
|
"$base $order $dayString"
|
|
} else {
|
|
val everyString = getString(if (isMaleGender(mEventStartDateTime.dayOfWeek)) R.string.every_m else R.string.every_f)
|
|
"$everyString $order $dayString"
|
|
}
|
|
}
|
|
|
|
private fun getBaseString(day: Int): String {
|
|
return getString(
|
|
if (isMaleGender(day)) {
|
|
R.string.repeat_every_m
|
|
} else {
|
|
R.string.repeat_every_f
|
|
}
|
|
)
|
|
}
|
|
|
|
private fun isMaleGender(day: Int) = day == 1 || day == 2 || day == 4 || day == 5
|
|
|
|
private fun getOrderString(repeatRule: Int): String {
|
|
val dayOfMonth = mEventStartDateTime.dayOfMonth
|
|
var order = (dayOfMonth - 1) / 7 + 1
|
|
if (isLastWeekDayOfMonth() && repeatRule == REPEAT_ORDER_WEEKDAY_USE_LAST) {
|
|
order = -1
|
|
}
|
|
|
|
val isMale = isMaleGender(mEventStartDateTime.dayOfWeek)
|
|
return getString(
|
|
when (order) {
|
|
1 -> if (isMale) R.string.first_m else R.string.first_f
|
|
2 -> if (isMale) R.string.second_m else R.string.second_f
|
|
3 -> if (isMale) R.string.third_m else R.string.third_f
|
|
4 -> if (isMale) R.string.fourth_m else R.string.fourth_f
|
|
5 -> if (isMale) R.string.fifth_m else R.string.fifth_f
|
|
else -> if (isMale) R.string.last_m else R.string.last_f
|
|
}
|
|
)
|
|
}
|
|
|
|
private fun getDayString(day: Int): String {
|
|
return getString(
|
|
when (day) {
|
|
1 -> R.string.monday_alt
|
|
2 -> R.string.tuesday_alt
|
|
3 -> R.string.wednesday_alt
|
|
4 -> R.string.thursday_alt
|
|
5 -> R.string.friday_alt
|
|
6 -> R.string.saturday_alt
|
|
else -> R.string.sunday_alt
|
|
}
|
|
)
|
|
}
|
|
|
|
private fun getRepeatXthDayInMonthString(includeBase: Boolean, repeatRule: Int): String {
|
|
val weekDayString = getRepeatXthDayString(includeBase, repeatRule)
|
|
val monthString = resources.getStringArray(com.simplemobiletools.commons.R.array.in_months)[mEventStartDateTime.monthOfYear - 1]
|
|
return "$weekDayString $monthString"
|
|
}
|
|
|
|
private fun setRepeatRule(rule: Int) {
|
|
mRepeatRule = rule
|
|
checkRepetitionRuleText()
|
|
if (rule == 0) {
|
|
setRepeatInterval(0)
|
|
}
|
|
}
|
|
|
|
private fun checkRepetitionRuleText() {
|
|
when {
|
|
mRepeatInterval.isXWeeklyRepetition() -> {
|
|
binding.eventRepetitionRule.text = if (mRepeatRule == EVERY_DAY_BIT) {
|
|
getString(com.simplemobiletools.commons.R.string.every_day)
|
|
} else {
|
|
getShortDaysFromBitmask(mRepeatRule)
|
|
}
|
|
}
|
|
|
|
mRepeatInterval.isXMonthlyRepetition() -> {
|
|
val repeatString = if (mRepeatRule == REPEAT_ORDER_WEEKDAY_USE_LAST || mRepeatRule == REPEAT_ORDER_WEEKDAY)
|
|
R.string.repeat else R.string.repeat_on
|
|
|
|
binding.eventRepetitionRuleLabel.text = getString(repeatString)
|
|
binding.eventRepetitionRule.text = getMonthlyRepetitionRuleText()
|
|
}
|
|
|
|
mRepeatInterval.isXYearlyRepetition() -> {
|
|
val repeatString = if (mRepeatRule == REPEAT_ORDER_WEEKDAY_USE_LAST || mRepeatRule == REPEAT_ORDER_WEEKDAY)
|
|
R.string.repeat else R.string.repeat_on
|
|
|
|
binding.eventRepetitionRuleLabel.text = getString(repeatString)
|
|
binding.eventRepetitionRule.text = getYearlyRepetitionRuleText()
|
|
}
|
|
}
|
|
}
|
|
|
|
private fun getMonthlyRepetitionRuleText() = when (mRepeatRule) {
|
|
REPEAT_SAME_DAY -> getString(R.string.the_same_day)
|
|
REPEAT_LAST_DAY -> getString(R.string.the_last_day)
|
|
else -> getRepeatXthDayString(false, mRepeatRule)
|
|
}
|
|
|
|
private fun getYearlyRepetitionRuleText() = when (mRepeatRule) {
|
|
REPEAT_SAME_DAY -> getString(R.string.the_same_day)
|
|
else -> getRepeatXthDayInMonthString(false, mRepeatRule)
|
|
}
|
|
|
|
private fun showEventTypeDialog() {
|
|
hideKeyboard()
|
|
|
|
SelectEventTypeDialog(
|
|
activity = this,
|
|
currEventType = mEventTypeId,
|
|
showCalDAVCalendars = false,
|
|
showNewEventTypeOption = true,
|
|
addLastUsedOneAsFirstOption = false,
|
|
showOnlyWritable = true,
|
|
showManageEventTypes = true
|
|
) {
|
|
mEventTypeId = it.id!!
|
|
updateEventType()
|
|
}
|
|
}
|
|
|
|
private fun showEventColorDialog() {
|
|
hideKeyboard()
|
|
ensureBackgroundThread {
|
|
val isLocalEvent = mEventCalendarId == STORED_LOCALLY_ONLY
|
|
if (isLocalEvent) {
|
|
showCustomEventColorDialog()
|
|
} else {
|
|
showCalDAVEventColorDialog()
|
|
}
|
|
}
|
|
}
|
|
|
|
private fun showCustomEventColorDialog() {
|
|
val eventType = eventTypesDB.getEventTypeWithId(mEventTypeId)!!
|
|
val currentColor = if (mEventColor == 0) {
|
|
eventType.color
|
|
} else {
|
|
mEventColor
|
|
}
|
|
|
|
runOnUiThread {
|
|
ColorPickerDialog(activity = this, color = currentColor, addDefaultColorButton = true) { wasPositivePressed, newColor ->
|
|
if (wasPositivePressed) {
|
|
gotNewEventColor(newColor, currentColor, eventType.color)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private fun showCalDAVEventColorDialog() {
|
|
val eventType = eventsHelper.getEventTypeWithCalDAVCalendarId(calendarId = mEventCalendarId)!!
|
|
val eventColors = getEventColors(eventType)
|
|
val currentColor = if (mEventColor == 0) {
|
|
eventType.color
|
|
} else {
|
|
mEventColor
|
|
}
|
|
|
|
runOnUiThread {
|
|
SelectEventColorDialog(activity = this, colors = eventColors, currentColor = currentColor) { newColor ->
|
|
gotNewEventColor(newColor, currentColor, eventType.color)
|
|
}
|
|
}
|
|
}
|
|
|
|
private fun gotNewEventColor(newColor: Int, currentColor: Int, defaultColor: Int) {
|
|
if (newColor != currentColor) {
|
|
mEventColor = newColor
|
|
updateEventColorInfo(defaultColor = defaultColor)
|
|
}
|
|
}
|
|
|
|
private fun checkReminderTexts() {
|
|
updateReminder1Text()
|
|
updateReminder2Text()
|
|
updateReminder3Text()
|
|
updateReminderTypeImages()
|
|
}
|
|
|
|
private fun updateReminder1Text() {
|
|
binding.eventReminder1.text = getFormattedMinutes(mReminder1Minutes)
|
|
}
|
|
|
|
private fun updateReminder2Text() {
|
|
binding.eventReminder2.apply {
|
|
beGoneIf(isGone() && mReminder1Minutes == REMINDER_OFF)
|
|
if (mReminder2Minutes == REMINDER_OFF) {
|
|
text = resources.getString(R.string.add_another_reminder)
|
|
alpha = 0.4f
|
|
} else {
|
|
text = getFormattedMinutes(mReminder2Minutes)
|
|
alpha = 1f
|
|
}
|
|
}
|
|
}
|
|
|
|
private fun updateReminder3Text() {
|
|
binding.eventReminder3.apply {
|
|
beGoneIf(isGone() && (mReminder2Minutes == REMINDER_OFF || mReminder1Minutes == REMINDER_OFF))
|
|
if (mReminder3Minutes == REMINDER_OFF) {
|
|
text = resources.getString(R.string.add_another_reminder)
|
|
alpha = 0.4f
|
|
} else {
|
|
text = getFormattedMinutes(mReminder3Minutes)
|
|
alpha = 1f
|
|
}
|
|
}
|
|
}
|
|
|
|
private fun showReminderTypePicker(currentValue: Int, callback: (Int) -> Unit) {
|
|
val items = arrayListOf(
|
|
RadioItem(REMINDER_NOTIFICATION, getString(com.simplemobiletools.commons.R.string.notification)),
|
|
RadioItem(REMINDER_EMAIL, getString(com.simplemobiletools.commons.R.string.email))
|
|
)
|
|
RadioGroupDialog(this, items, currentValue) {
|
|
callback(it as Int)
|
|
}
|
|
}
|
|
|
|
private fun showAvailabilityPicker(currentValue: Int, callback: (Int) -> Unit) {
|
|
val items = arrayListOf(
|
|
RadioItem(Attendees.AVAILABILITY_BUSY, getString(R.string.status_busy)),
|
|
RadioItem(Attendees.AVAILABILITY_FREE, getString(R.string.status_free))
|
|
)
|
|
RadioGroupDialog(this, items, currentValue) {
|
|
callback(it as Int)
|
|
}
|
|
}
|
|
|
|
private fun updateReminderTypeImages() {
|
|
updateReminderTypeImage(binding.eventReminder1Type, Reminder(mReminder1Minutes, mReminder1Type))
|
|
updateReminderTypeImage(binding.eventReminder2Type, Reminder(mReminder2Minutes, mReminder2Type))
|
|
updateReminderTypeImage(binding.eventReminder3Type, Reminder(mReminder3Minutes, mReminder3Type))
|
|
}
|
|
|
|
private fun updateCalDAVVisibility() {
|
|
val isSyncedEvent = mEventCalendarId != STORED_LOCALLY_ONLY
|
|
binding.eventAttendeesImage.beVisibleIf(isSyncedEvent)
|
|
binding.eventAttendeesHolder.beVisibleIf(isSyncedEvent)
|
|
binding.eventAttendeesDivider.beVisibleIf(isSyncedEvent)
|
|
binding.eventAvailabilityDivider.beVisibleIf(isSyncedEvent)
|
|
binding.eventAvailabilityImage.beVisibleIf(isSyncedEvent)
|
|
binding.eventAvailability.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) {
|
|
com.simplemobiletools.commons.R.drawable.ic_bell_vector
|
|
} else {
|
|
com.simplemobiletools.commons.R.drawable.ic_mail_vector
|
|
}
|
|
|
|
val icon = resources.getColoredDrawableWithColor(drawable, getProperTextColor())
|
|
view.setImageDrawable(icon)
|
|
}
|
|
|
|
private fun updateAvailabilityImage() {
|
|
val drawable = if (mAvailability == Attendees.AVAILABILITY_FREE) R.drawable.ic_event_available_vector else R.drawable.ic_event_busy_vector
|
|
val icon = resources.getColoredDrawableWithColor(drawable, getProperTextColor())
|
|
binding.eventAvailabilityImage.setImageDrawable(icon)
|
|
}
|
|
|
|
private fun updateAvailabilityText() {
|
|
binding.eventAvailability.text = if (mAvailability == Attendees.AVAILABILITY_FREE) getString(R.string.status_free) else getString(R.string.status_busy)
|
|
}
|
|
|
|
private fun updateRepetitionText() {
|
|
binding.eventRepetition.text = getRepetitionText(mRepeatInterval)
|
|
}
|
|
|
|
private fun updateEventType() {
|
|
ensureBackgroundThread {
|
|
val eventType = eventTypesDB.getEventTypeWithId(mEventTypeId)
|
|
if (eventType != null) {
|
|
runOnUiThread {
|
|
binding.eventType.text = eventType.title
|
|
updateEventColorInfo(eventType.color)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private fun updateCalDAVCalendar() {
|
|
if (config.caldavSync) {
|
|
binding.eventCaldavCalendarImage.beVisible()
|
|
binding.eventCaldavCalendarHolder.beVisible()
|
|
binding.eventCaldavCalendarDivider.beVisible()
|
|
|
|
val calendars = calDAVHelper.getCalDAVCalendars("", true).filter {
|
|
it.canWrite() && config.getSyncedCalendarIdsAsList().contains(it.id)
|
|
}
|
|
updateCurrentCalendarInfo(if (mEventCalendarId == STORED_LOCALLY_ONLY) null else getCalendarWithId(calendars, getCalendarId()))
|
|
|
|
binding.eventCaldavCalendarHolder.setOnClickListener {
|
|
hideKeyboard()
|
|
SelectEventCalendarDialog(this, calendars, mEventCalendarId) {
|
|
if (mEventCalendarId != STORED_LOCALLY_ONLY && it == STORED_LOCALLY_ONLY) {
|
|
mEventTypeId = config.lastUsedLocalEventTypeId
|
|
updateEventType()
|
|
}
|
|
mWasCalendarChanged = true
|
|
mEventCalendarId = it
|
|
config.lastUsedCaldavCalendarId = it
|
|
updateCurrentCalendarInfo(getCalendarWithId(calendars, it))
|
|
updateReminderTypeImages()
|
|
updateCalDAVVisibility()
|
|
updateAvailabilityText()
|
|
updateAvailabilityImage()
|
|
}
|
|
}
|
|
} else {
|
|
updateCurrentCalendarInfo(null)
|
|
}
|
|
}
|
|
|
|
private fun getCalendarId() = if (mEvent.source == SOURCE_SIMPLE_CALENDAR) config.lastUsedCaldavCalendarId else mEvent.getCalDAVCalendarId()
|
|
|
|
private fun getCalendarWithId(calendars: List<CalDAVCalendar>, calendarId: Int) = calendars.firstOrNull { it.id == calendarId }
|
|
|
|
private fun updateCurrentCalendarInfo(currentCalendar: CalDAVCalendar?) = binding.apply {
|
|
eventTypeImage.beVisibleIf(currentCalendar == null)
|
|
eventTypeHolder.beVisibleIf(currentCalendar == null)
|
|
eventCaldavCalendarDivider.beVisibleIf(currentCalendar == null)
|
|
eventCaldavCalendarEmail.beGoneIf(currentCalendar == null)
|
|
|
|
if (currentCalendar == null) {
|
|
mEventCalendarId = STORED_LOCALLY_ONLY
|
|
val mediumMargin = resources.getDimension(com.simplemobiletools.commons.R.dimen.medium_margin).toInt()
|
|
eventCaldavCalendarName.apply {
|
|
text = getString(R.string.store_locally_only)
|
|
setPadding(paddingLeft, paddingTop, paddingRight, mediumMargin)
|
|
}
|
|
|
|
eventCaldavCalendarHolder.apply {
|
|
setPadding(paddingLeft, mediumMargin, paddingRight, mediumMargin)
|
|
}
|
|
|
|
ensureBackgroundThread {
|
|
val eventType = eventTypesDB.getEventTypeWithId(mEventTypeId)
|
|
runOnUiThread {
|
|
eventColorImage.beVisibleIf(eventType != null)
|
|
eventColorHolder.beVisibleIf(eventType != null)
|
|
eventColorDivider.beVisibleIf(eventType != null)
|
|
if (eventType != null) {
|
|
updateEventColorInfo(eventType.color)
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
eventCaldavCalendarEmail.text = currentCalendar.accountName
|
|
|
|
ensureBackgroundThread {
|
|
val eventType = eventsHelper.getEventTypeWithCalDAVCalendarId(currentCalendar.id)
|
|
val calendarColor = eventType?.color ?: currentCalendar.color
|
|
val canCustomizeColors = if (eventType != null) {
|
|
getEventColors(eventType).isNotEmpty()
|
|
} else {
|
|
false
|
|
}
|
|
|
|
runOnUiThread {
|
|
eventCaldavCalendarName.apply {
|
|
text = currentCalendar.displayName
|
|
setPadding(paddingLeft, paddingTop, paddingRight, resources.getDimension(com.simplemobiletools.commons.R.dimen.tiny_margin).toInt())
|
|
}
|
|
|
|
eventCaldavCalendarHolder.apply {
|
|
setPadding(paddingLeft, 0, paddingRight, 0)
|
|
}
|
|
|
|
eventColorImage.beVisibleIf(canCustomizeColors)
|
|
eventColorHolder.beVisibleIf(canCustomizeColors)
|
|
eventColorDivider.beVisibleIf(canCustomizeColors)
|
|
if (canCustomizeColors) {
|
|
updateEventColorInfo(calendarColor)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private fun updateEventColorInfo(defaultColor: Int) {
|
|
val eventColor = if (mEventColor == 0) {
|
|
defaultColor
|
|
} else {
|
|
mEventColor
|
|
}
|
|
binding.eventColor.setFillWithStroke(eventColor, getProperBackgroundColor())
|
|
}
|
|
|
|
private fun getEventColors(eventType: EventType): IntArray {
|
|
return calDAVHelper.getAvailableCalDAVCalendarColors(eventType, Colors.TYPE_EVENT).keys.toIntArray()
|
|
}
|
|
|
|
private fun resetTime() {
|
|
if (mEventEndDateTime.isBefore(mEventStartDateTime) &&
|
|
mEventStartDateTime.dayOfMonth() == mEventEndDateTime.dayOfMonth() &&
|
|
mEventStartDateTime.monthOfYear() == mEventEndDateTime.monthOfYear()
|
|
) {
|
|
|
|
mEventEndDateTime =
|
|
mEventEndDateTime.withTime(mEventStartDateTime.hourOfDay, mEventStartDateTime.minuteOfHour, mEventStartDateTime.secondOfMinute, 0)
|
|
updateEndTimeText()
|
|
checkStartEndValidity()
|
|
}
|
|
}
|
|
|
|
private fun toggleAllDay(isAllDay: Boolean) {
|
|
hideKeyboard()
|
|
mIsAllDayEvent = isAllDay
|
|
binding.eventStartTime.beGoneIf(isAllDay)
|
|
binding.eventEndTime.beGoneIf(isAllDay)
|
|
updateTimeZoneText()
|
|
showOrHideTimeZone()
|
|
resetTime()
|
|
}
|
|
|
|
private fun showOrHideTimeZone() {
|
|
val allowChangingTimeZones = config.allowChangingTimeZones && !mIsAllDayEvent
|
|
binding.eventTimeZoneDivider.beVisibleIf(allowChangingTimeZones)
|
|
binding.eventTimeZoneImage.beVisibleIf(allowChangingTimeZones)
|
|
binding.eventTimeZone.beVisibleIf(allowChangingTimeZones)
|
|
}
|
|
|
|
private fun shareEvent() {
|
|
shareEvents(arrayListOf(mEvent.id!!))
|
|
}
|
|
|
|
private fun deleteEvent() {
|
|
if (mEvent.id == null) {
|
|
return
|
|
}
|
|
|
|
DeleteEventDialog(this, arrayListOf(mEvent.id!!), mEvent.repeatInterval > 0) {
|
|
ensureBackgroundThread {
|
|
when (it) {
|
|
DELETE_SELECTED_OCCURRENCE -> eventsHelper.deleteRepeatingEventOccurrence(mEvent.id!!, mEventOccurrenceTS, true)
|
|
DELETE_FUTURE_OCCURRENCES -> eventsHelper.addEventRepeatLimit(mEvent.id!!, mEventOccurrenceTS)
|
|
DELETE_ALL_OCCURRENCES -> eventsHelper.deleteEvent(mEvent.id!!, true)
|
|
}
|
|
|
|
runOnUiThread {
|
|
hideKeyboard()
|
|
finish()
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private fun duplicateEvent() {
|
|
// the activity has the singleTask launchMode to avoid some glitches, so finish it before relaunching
|
|
hideKeyboard()
|
|
finish()
|
|
Intent(this, EventActivity::class.java).apply {
|
|
putExtra(EVENT_ID, mEvent.id)
|
|
putExtra(EVENT_OCCURRENCE_TS, mEventOccurrenceTS)
|
|
putExtra(IS_DUPLICATE_INTENT, true)
|
|
startActivity(this)
|
|
}
|
|
}
|
|
|
|
private fun saveCurrentEvent() {
|
|
if (config.wasAlarmWarningShown || (mReminder1Minutes == REMINDER_OFF && mReminder2Minutes == REMINDER_OFF && mReminder3Minutes == REMINDER_OFF)) {
|
|
ensureBackgroundThread {
|
|
saveEvent()
|
|
}
|
|
} else {
|
|
ReminderWarningDialog(this) {
|
|
config.wasAlarmWarningShown = true
|
|
ensureBackgroundThread {
|
|
saveEvent()
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private fun saveEvent() {
|
|
val newTitle = binding.eventTitle.value
|
|
if (newTitle.isEmpty()) {
|
|
toast(R.string.title_empty)
|
|
runOnUiThread {
|
|
binding.eventTitle.requestFocus()
|
|
}
|
|
return
|
|
}
|
|
|
|
var newStartTS: Long
|
|
var newEndTS: Long
|
|
getStartEndTimes().apply {
|
|
newStartTS = first
|
|
newEndTS = second
|
|
}
|
|
|
|
if (newStartTS > newEndTS) {
|
|
toast(R.string.end_before_start)
|
|
return
|
|
}
|
|
|
|
val wasRepeatable = mEvent.repeatInterval > 0
|
|
val oldSource = mEvent.source
|
|
val newImportId = if (mEvent.id != null) {
|
|
mEvent.importId
|
|
} else {
|
|
generateImportId()
|
|
}
|
|
|
|
val newEventType = if (!config.caldavSync || config.lastUsedCaldavCalendarId == 0 || mEventCalendarId == STORED_LOCALLY_ONLY) {
|
|
mEventTypeId
|
|
} else {
|
|
calDAVHelper.getCalDAVCalendars("", true).firstOrNull { it.id == mEventCalendarId }?.apply {
|
|
if (!canWrite()) {
|
|
runOnUiThread {
|
|
toast(R.string.insufficient_permissions)
|
|
}
|
|
return
|
|
}
|
|
}
|
|
|
|
eventsHelper.getEventTypeWithCalDAVCalendarId(mEventCalendarId)?.id ?: config.lastUsedLocalEventTypeId
|
|
}
|
|
|
|
val newSource = if (!config.caldavSync || mEventCalendarId == STORED_LOCALLY_ONLY) {
|
|
config.lastUsedLocalEventTypeId = newEventType
|
|
SOURCE_SIMPLE_CALENDAR
|
|
} else {
|
|
"$CALDAV-$mEventCalendarId"
|
|
}
|
|
|
|
val reminders = getReminders()
|
|
if (!binding.eventAllDay.isChecked) {
|
|
if ((reminders.getOrNull(2)?.minutes ?: 0) < -1) {
|
|
reminders.removeAt(2)
|
|
}
|
|
|
|
if ((reminders.getOrNull(1)?.minutes ?: 0) < -1) {
|
|
reminders.removeAt(1)
|
|
}
|
|
|
|
if ((reminders.getOrNull(0)?.minutes ?: 0) < -1) {
|
|
reminders.removeAt(0)
|
|
}
|
|
}
|
|
|
|
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.minutes
|
|
lastEventReminderMinutes2 = reminder2.minutes
|
|
lastEventReminderMinutes3 = reminder3.minutes
|
|
}
|
|
}
|
|
|
|
mEvent.apply {
|
|
startTS = newStartTS
|
|
endTS = newEndTS
|
|
title = newTitle
|
|
description = binding.eventDescription.value
|
|
reminder1Minutes = reminder1.minutes
|
|
reminder2Minutes = reminder2.minutes
|
|
reminder3Minutes = reminder3.minutes
|
|
reminder1Type = mReminder1Type
|
|
reminder2Type = mReminder2Type
|
|
reminder3Type = mReminder3Type
|
|
repeatInterval = mRepeatInterval
|
|
importId = newImportId
|
|
timeZone = if (mIsAllDayEvent || timeZone.isEmpty()) DateTimeZone.getDefault().id else timeZone
|
|
flags = mEvent.flags.addBitIf(binding.eventAllDay.isChecked, FLAG_ALL_DAY)
|
|
repeatLimit = if (repeatInterval == 0) 0 else mRepeatLimit
|
|
repeatRule = mRepeatRule
|
|
attendees = if (mEventCalendarId == STORED_LOCALLY_ONLY) emptyList() else getAllAttendees(true)
|
|
eventType = newEventType
|
|
lastUpdated = System.currentTimeMillis()
|
|
source = newSource
|
|
location = binding.eventLocation.value
|
|
availability = mAvailability
|
|
color = mEventColor
|
|
}
|
|
|
|
// recreate the event if it was moved in a different CalDAV calendar
|
|
if (mEvent.id != null && oldSource != newSource && oldSource != SOURCE_IMPORTED_ICS) {
|
|
if (mRepeatInterval > 0 && wasRepeatable) {
|
|
eventsHelper.applyOriginalStartEndTimes(mEvent, mOriginalStartTS, mOriginalEndTS)
|
|
}
|
|
eventsHelper.deleteEvent(mEvent.id!!, true)
|
|
mEvent.id = null
|
|
}
|
|
|
|
if (mEvent.getReminders().isNotEmpty()) {
|
|
handleNotificationPermission { granted ->
|
|
if (granted) {
|
|
ensureBackgroundThread {
|
|
storeEvent(wasRepeatable)
|
|
}
|
|
} else {
|
|
PermissionRequiredDialog(this, com.simplemobiletools.commons.R.string.allow_notifications_reminders, { openNotificationSettings() })
|
|
}
|
|
}
|
|
} else {
|
|
storeEvent(wasRepeatable)
|
|
}
|
|
}
|
|
|
|
private fun storeEvent(wasRepeatable: Boolean) {
|
|
if (mEvent.id == null) {
|
|
eventsHelper.insertEvent(mEvent, addToCalDAV = true, showToasts = true) {
|
|
hideKeyboard()
|
|
|
|
if (DateTime.now().isAfter(mEventStartDateTime.millis)) {
|
|
if (mEvent.repeatInterval == 0 && mEvent.getReminders().any { it.type == REMINDER_NOTIFICATION }) {
|
|
notifyEvent(mEvent)
|
|
}
|
|
}
|
|
|
|
finish()
|
|
}
|
|
} else {
|
|
if (mRepeatInterval > 0 && wasRepeatable) {
|
|
runOnUiThread {
|
|
showEditRepeatingEventDialog()
|
|
}
|
|
} else {
|
|
hideKeyboard()
|
|
eventsHelper.updateEvent(mEvent, updateAtCalDAV = true, showToasts = true) {
|
|
finish()
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private fun showEditRepeatingEventDialog() {
|
|
EditRepeatingEventDialog(this) {
|
|
hideKeyboard()
|
|
if (it == null) {
|
|
return@EditRepeatingEventDialog
|
|
}
|
|
when (it) {
|
|
EDIT_SELECTED_OCCURRENCE -> {
|
|
eventsHelper.editSelectedOccurrence(mEvent, true) {
|
|
finish()
|
|
}
|
|
}
|
|
|
|
EDIT_FUTURE_OCCURRENCES -> {
|
|
eventsHelper.editFutureOccurrences(mEvent, mEventOccurrenceTS, true) {
|
|
finish()
|
|
}
|
|
}
|
|
|
|
EDIT_ALL_OCCURRENCES -> {
|
|
eventsHelper.editAllOccurrences(mEvent, mOriginalStartTS, mOriginalEndTS, true) {
|
|
finish()
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private fun updateStartTexts() {
|
|
updateStartDateText()
|
|
updateStartTimeText()
|
|
}
|
|
|
|
private fun updateStartDateText() {
|
|
binding.eventStartDate.text = Formatter.getDate(this, mEventStartDateTime)
|
|
checkStartEndValidity()
|
|
}
|
|
|
|
private fun updateStartTimeText() {
|
|
binding.eventStartTime.text = Formatter.getTime(this, mEventStartDateTime)
|
|
checkStartEndValidity()
|
|
}
|
|
|
|
private fun updateEndTexts() {
|
|
updateEndDateText()
|
|
updateEndTimeText()
|
|
}
|
|
|
|
private fun updateEndDateText() {
|
|
binding.eventEndDate.text = Formatter.getDate(this, mEventEndDateTime)
|
|
checkStartEndValidity()
|
|
}
|
|
|
|
private fun updateEndTimeText() {
|
|
binding.eventEndTime.text = Formatter.getTime(this, mEventEndDateTime)
|
|
checkStartEndValidity()
|
|
}
|
|
|
|
private fun updateTimeZoneText() {
|
|
binding.eventTimeZone.text = mEvent.getTimeZoneString()
|
|
}
|
|
|
|
private fun checkStartEndValidity() {
|
|
val textColor = if (mEventStartDateTime.isAfter(mEventEndDateTime)) resources.getColor(R.color.red_text) else getProperTextColor()
|
|
binding.eventEndDate.setTextColor(textColor)
|
|
binding.eventEndTime.setTextColor(textColor)
|
|
}
|
|
|
|
private fun showOnMap() {
|
|
if (binding.eventLocation.value.isEmpty()) {
|
|
toast(R.string.please_fill_location)
|
|
return
|
|
}
|
|
|
|
val pattern = Pattern.compile(LAT_LON_PATTERN)
|
|
val locationValue = binding.eventLocation.value
|
|
val uri = if (pattern.matcher(locationValue).find()) {
|
|
val delimiter = if (locationValue.contains(';')) ";" else ","
|
|
val parts = locationValue.split(delimiter)
|
|
val latitude = parts.first()
|
|
val longitude = parts.last()
|
|
Uri.parse("geo:$latitude,$longitude")
|
|
} else {
|
|
val location = Uri.encode(locationValue)
|
|
Uri.parse("geo:0,0?q=$location")
|
|
}
|
|
|
|
val intent = Intent(Intent.ACTION_VIEW, uri)
|
|
launchActivityIntent(intent)
|
|
}
|
|
|
|
private fun setupStartDate() {
|
|
hideKeyboard()
|
|
val datePicker = DatePickerDialog(
|
|
this, getDatePickerDialogTheme(), startDateSetListener, mEventStartDateTime.year, mEventStartDateTime.monthOfYear - 1,
|
|
mEventStartDateTime.dayOfMonth
|
|
)
|
|
|
|
datePicker.datePicker.firstDayOfWeek = getJavaDayOfWeekFromJoda(config.firstDayOfWeek)
|
|
datePicker.show()
|
|
}
|
|
|
|
private fun setupStartTime() {
|
|
hideKeyboard()
|
|
if (config.isUsingSystemTheme) {
|
|
val timeFormat = if (config.use24HourFormat) {
|
|
TimeFormat.CLOCK_24H
|
|
} else {
|
|
TimeFormat.CLOCK_12H
|
|
}
|
|
|
|
val timePicker = MaterialTimePicker.Builder()
|
|
.setTimeFormat(timeFormat)
|
|
.setHour(mEventStartDateTime.hourOfDay)
|
|
.setMinute(mEventStartDateTime.minuteOfHour)
|
|
.setInputMode(INPUT_MODE_CLOCK)
|
|
.build()
|
|
|
|
timePicker.addOnPositiveButtonClickListener {
|
|
timeSet(timePicker.hour, timePicker.minute, true)
|
|
}
|
|
|
|
timePicker.show(supportFragmentManager, "")
|
|
} else {
|
|
TimePickerDialog(
|
|
this,
|
|
getTimePickerDialogTheme(),
|
|
startTimeSetListener,
|
|
mEventStartDateTime.hourOfDay,
|
|
mEventStartDateTime.minuteOfHour,
|
|
config.use24HourFormat
|
|
).show()
|
|
}
|
|
}
|
|
|
|
private fun setupEndDate() {
|
|
hideKeyboard()
|
|
val datePicker = DatePickerDialog(
|
|
this, getDatePickerDialogTheme(), endDateSetListener, mEventEndDateTime.year, mEventEndDateTime.monthOfYear - 1,
|
|
mEventEndDateTime.dayOfMonth
|
|
)
|
|
|
|
datePicker.datePicker.firstDayOfWeek = getJavaDayOfWeekFromJoda(config.firstDayOfWeek)
|
|
datePicker.show()
|
|
}
|
|
|
|
private fun setupEndTime() {
|
|
hideKeyboard()
|
|
if (config.isUsingSystemTheme) {
|
|
val timeFormat = if (config.use24HourFormat) {
|
|
TimeFormat.CLOCK_24H
|
|
} else {
|
|
TimeFormat.CLOCK_12H
|
|
}
|
|
|
|
val timePicker = MaterialTimePicker.Builder()
|
|
.setTimeFormat(timeFormat)
|
|
.setHour(mEventEndDateTime.hourOfDay)
|
|
.setMinute(mEventEndDateTime.minuteOfHour)
|
|
.setInputMode(INPUT_MODE_CLOCK)
|
|
.build()
|
|
|
|
timePicker.addOnPositiveButtonClickListener {
|
|
timeSet(timePicker.hour, timePicker.minute, false)
|
|
}
|
|
|
|
timePicker.show(supportFragmentManager, "")
|
|
} else {
|
|
TimePickerDialog(
|
|
this,
|
|
getTimePickerDialogTheme(),
|
|
endTimeSetListener,
|
|
mEventEndDateTime.hourOfDay,
|
|
mEventEndDateTime.minuteOfHour,
|
|
config.use24HourFormat
|
|
).show()
|
|
}
|
|
}
|
|
|
|
private val startDateSetListener = DatePickerDialog.OnDateSetListener { view, year, monthOfYear, dayOfMonth ->
|
|
dateSet(year, monthOfYear, dayOfMonth, true)
|
|
}
|
|
|
|
private val startTimeSetListener = TimePickerDialog.OnTimeSetListener { view, hourOfDay, minute ->
|
|
timeSet(hourOfDay, minute, true)
|
|
}
|
|
|
|
private val endDateSetListener = DatePickerDialog.OnDateSetListener { view, year, monthOfYear, dayOfMonth -> dateSet(year, monthOfYear, dayOfMonth, false) }
|
|
|
|
private val endTimeSetListener = TimePickerDialog.OnTimeSetListener { view, hourOfDay, minute -> timeSet(hourOfDay, minute, false) }
|
|
|
|
private fun dateSet(year: Int, month: Int, day: Int, isStart: Boolean) {
|
|
if (isStart) {
|
|
val diff = mEventEndDateTime.seconds() - mEventStartDateTime.seconds()
|
|
|
|
mEventStartDateTime = mEventStartDateTime.withDate(year, month + 1, day)
|
|
updateStartDateText()
|
|
checkRepeatRule()
|
|
|
|
mEventEndDateTime = mEventStartDateTime.plusSeconds(diff.toInt())
|
|
updateEndTexts()
|
|
} else {
|
|
mEventEndDateTime = mEventEndDateTime.withDate(year, month + 1, day)
|
|
updateEndDateText()
|
|
}
|
|
}
|
|
|
|
private fun timeSet(hours: Int, minutes: Int, isStart: Boolean) {
|
|
try {
|
|
if (isStart) {
|
|
val diff = mEventEndDateTime.seconds() - mEventStartDateTime.seconds()
|
|
|
|
mEventStartDateTime = mEventStartDateTime.withHourOfDay(hours).withMinuteOfHour(minutes)
|
|
updateStartTimeText()
|
|
|
|
mEventEndDateTime = mEventStartDateTime.plusSeconds(diff.toInt())
|
|
updateEndTexts()
|
|
} else {
|
|
mEventEndDateTime = mEventEndDateTime.withHourOfDay(hours).withMinuteOfHour(minutes)
|
|
updateEndTimeText()
|
|
}
|
|
} catch (e: Exception) {
|
|
timeSet(hours + 1, minutes, isStart)
|
|
return
|
|
}
|
|
}
|
|
|
|
private fun setupTimeZone() {
|
|
hideKeyboard()
|
|
Intent(this, SelectTimeZoneActivity::class.java).apply {
|
|
putExtra(CURRENT_TIME_ZONE, mEvent.getTimeZoneString())
|
|
startActivityForResult(this, SELECT_TIME_ZONE_INTENT)
|
|
}
|
|
}
|
|
|
|
private fun checkRepeatRule() {
|
|
if (mRepeatInterval.isXWeeklyRepetition()) {
|
|
val day = mRepeatRule
|
|
if (day == MONDAY_BIT || day == TUESDAY_BIT || day == WEDNESDAY_BIT || day == THURSDAY_BIT || day == FRIDAY_BIT || day == SATURDAY_BIT || day == SUNDAY_BIT) {
|
|
setRepeatRule(Math.pow(2.0, (mEventStartDateTime.dayOfWeek - 1).toDouble()).toInt())
|
|
}
|
|
} else if (mRepeatInterval.isXMonthlyRepetition() || mRepeatInterval.isXYearlyRepetition()) {
|
|
if (mRepeatRule == REPEAT_LAST_DAY && !isLastDayOfTheMonth()) {
|
|
mRepeatRule = REPEAT_SAME_DAY
|
|
}
|
|
checkRepetitionRuleText()
|
|
}
|
|
}
|
|
|
|
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() {
|
|
val currentCalendar = calDAVHelper.getCalDAVCalendars("", true).firstOrNull { it.id == mEventCalendarId }
|
|
mAttendees.forEach {
|
|
it.isMe = it.email == currentCalendar?.ownerName
|
|
}
|
|
|
|
mAttendees.sortWith(compareBy<Attendee>
|
|
{ it.isMe }.thenBy
|
|
{ it.status == Attendees.ATTENDEE_STATUS_ACCEPTED }.thenBy
|
|
{ it.status == Attendees.ATTENDEE_STATUS_DECLINED }.thenBy
|
|
{ it.status == Attendees.ATTENDEE_STATUS_TENTATIVE }.thenBy
|
|
{ it.status })
|
|
mAttendees.reverse()
|
|
|
|
runOnUiThread {
|
|
mAttendees.forEach {
|
|
val attendee = it
|
|
val deviceContact = mAvailableContacts.firstOrNull { it.email.isNotEmpty() && it.email == attendee.email && it.photoUri.isNotEmpty() }
|
|
if (deviceContact != null) {
|
|
attendee.photoUri = deviceContact.photoUri
|
|
}
|
|
addAttendee(attendee)
|
|
}
|
|
addAttendee()
|
|
|
|
binding.apply {
|
|
val imageHeight = eventRepetitionImage.height
|
|
if (imageHeight > 0) {
|
|
eventAttendeesImage.layoutParams.height = imageHeight
|
|
} else {
|
|
eventRepetitionImage.onGlobalLayout {
|
|
eventAttendeesImage.layoutParams.height = eventRepetitionImage.height
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private fun addAttendee(attendee: Attendee? = null) {
|
|
val attendeeHolder = ItemAttendeeBinding.inflate(layoutInflater, binding.eventAttendeesHolder, false)
|
|
val autoCompleteView = attendeeHolder.eventAttendee
|
|
val selectedAttendeeDismiss = attendeeHolder.eventContactDismiss
|
|
|
|
mAttendeeAutoCompleteViews.add(autoCompleteView)
|
|
autoCompleteView.onTextChangeListener {
|
|
if (mWasContactsPermissionChecked) {
|
|
checkNewAttendeeField()
|
|
} else {
|
|
handlePermission(PERMISSION_READ_CONTACTS) {
|
|
checkNewAttendeeField()
|
|
mWasContactsPermissionChecked = true
|
|
}
|
|
}
|
|
}
|
|
|
|
binding.eventAttendeesHolder.addView(attendeeHolder.root)
|
|
|
|
val textColor = getProperTextColor()
|
|
val backgroundColor = getProperBackgroundColor()
|
|
val primaryColor = getProperPrimaryColor()
|
|
autoCompleteView.setColors(textColor, primaryColor, backgroundColor)
|
|
attendeeHolder.eventContactName.setColors(textColor, primaryColor, backgroundColor)
|
|
attendeeHolder.eventContactMeStatus.setColors(textColor, primaryColor, backgroundColor)
|
|
selectedAttendeeDismiss.applyColorFilter(textColor)
|
|
|
|
selectedAttendeeDismiss.setOnClickListener {
|
|
attendeeHolder.root.beGone()
|
|
mSelectedContacts = mSelectedContacts.filter { it.toString() != selectedAttendeeDismiss.tag }.toMutableList() as ArrayList<Attendee>
|
|
}
|
|
|
|
val adapter = AutoCompleteTextViewAdapter(this, mAvailableContacts)
|
|
autoCompleteView.setAdapter(adapter)
|
|
autoCompleteView.imeOptions = EditorInfo.IME_ACTION_NEXT
|
|
autoCompleteView.setOnItemClickListener { parent, view, position, id ->
|
|
val currAttendees = (autoCompleteView.adapter as AutoCompleteTextViewAdapter).resultList
|
|
val selectedAttendee = currAttendees[position]
|
|
addSelectedAttendee(selectedAttendee, autoCompleteView, attendeeHolder)
|
|
}
|
|
|
|
if (attendee != null) {
|
|
addSelectedAttendee(attendee, autoCompleteView, attendeeHolder)
|
|
}
|
|
}
|
|
|
|
private fun addSelectedAttendee(attendee: Attendee, autoCompleteView: MyAutoCompleteTextView, attendeeHolder: ItemAttendeeBinding) {
|
|
mSelectedContacts.add(attendee)
|
|
|
|
autoCompleteView.beGone()
|
|
autoCompleteView.focusSearch(View.FOCUS_DOWN)?.requestFocus()
|
|
|
|
attendeeHolder.apply {
|
|
eventContactAttendee.beVisible()
|
|
|
|
val attendeeStatusBackground = resources.getDrawable(R.drawable.attendee_status_circular_background)
|
|
(attendeeStatusBackground as LayerDrawable).findDrawableByLayerId(R.id.attendee_status_circular_background)
|
|
.applyColorFilter(getProperBackgroundColor())
|
|
eventContactStatusImage.apply {
|
|
background = attendeeStatusBackground
|
|
setImageDrawable(getAttendeeStatusImage(attendee))
|
|
beVisibleIf(attendee.showStatusImage())
|
|
}
|
|
|
|
eventContactName.text = if (attendee.isMe) getString(R.string.my_status) else attendee.getPublicName()
|
|
if (attendee.isMe) {
|
|
(eventContactName.layoutParams as RelativeLayout.LayoutParams).addRule(RelativeLayout.START_OF, eventContactMeStatus.id)
|
|
}
|
|
|
|
val placeholder = BitmapDrawable(resources, SimpleContactsHelper(this@EventActivity).getContactLetterIcon(eventContactName.value))
|
|
eventContactImage.apply {
|
|
attendee.updateImage(this@EventActivity, this, placeholder)
|
|
beVisible()
|
|
}
|
|
|
|
eventContactDismiss.apply {
|
|
tag = attendee.toString()
|
|
beGoneIf(attendee.isMe)
|
|
}
|
|
|
|
if (attendee.isMe) {
|
|
updateAttendeeMe(this, attendee)
|
|
}
|
|
|
|
eventContactMeStatus.apply {
|
|
beVisibleIf(attendee.isMe)
|
|
}
|
|
|
|
if (attendee.isMe) {
|
|
eventContactAttendee.setOnClickListener {
|
|
val items = arrayListOf(
|
|
RadioItem(Attendees.ATTENDEE_STATUS_ACCEPTED, getString(R.string.going)),
|
|
RadioItem(Attendees.ATTENDEE_STATUS_DECLINED, getString(R.string.not_going)),
|
|
RadioItem(Attendees.ATTENDEE_STATUS_TENTATIVE, getString(R.string.maybe_going))
|
|
)
|
|
|
|
RadioGroupDialog(this@EventActivity, items, attendee.status) {
|
|
attendee.status = it as Int
|
|
updateAttendeeMe(this, attendee)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private fun getAttendeeStatusImage(attendee: Attendee): Drawable {
|
|
return resources.getDrawable(
|
|
when (attendee.status) {
|
|
Attendees.ATTENDEE_STATUS_ACCEPTED -> R.drawable.ic_check_green
|
|
Attendees.ATTENDEE_STATUS_DECLINED -> R.drawable.ic_cross_red
|
|
else -> R.drawable.ic_question_yellow
|
|
}
|
|
)
|
|
}
|
|
|
|
private fun updateAttendeeMe(holder: ItemAttendeeBinding, attendee: Attendee) {
|
|
holder.apply {
|
|
eventContactMeStatus.text = getString(
|
|
when (attendee.status) {
|
|
Attendees.ATTENDEE_STATUS_ACCEPTED -> R.string.going
|
|
Attendees.ATTENDEE_STATUS_DECLINED -> R.string.not_going
|
|
Attendees.ATTENDEE_STATUS_TENTATIVE -> R.string.maybe_going
|
|
else -> R.string.invited
|
|
}
|
|
)
|
|
|
|
eventContactStatusImage.apply {
|
|
beVisibleIf(attendee.showStatusImage())
|
|
setImageDrawable(getAttendeeStatusImage(attendee))
|
|
}
|
|
|
|
mAttendees.firstOrNull { it.isMe }?.status = attendee.status
|
|
}
|
|
}
|
|
|
|
private fun checkNewAttendeeField() {
|
|
if (mAttendeeAutoCompleteViews.none { it.isVisible() && it.value.isEmpty() }) {
|
|
addAttendee()
|
|
}
|
|
}
|
|
|
|
private fun getAllAttendees(isSavingEvent: Boolean): ArrayList<Attendee> {
|
|
var attendees = ArrayList<Attendee>()
|
|
mSelectedContacts.forEach {
|
|
attendees.add(it)
|
|
}
|
|
|
|
val customEmails = mAttendeeAutoCompleteViews.filter { it.isVisible() }.map { it.value }.filter { it.isNotEmpty() }.toMutableList() as ArrayList<String>
|
|
customEmails.mapTo(attendees) {
|
|
Attendee(0, "", it, Attendees.ATTENDEE_STATUS_INVITED, "", false, Attendees.RELATIONSHIP_NONE)
|
|
}
|
|
attendees = attendees.distinctBy { it.email }.toMutableList() as ArrayList<Attendee>
|
|
|
|
if (mEvent.id == null && isSavingEvent && attendees.isNotEmpty()) {
|
|
val currentCalendar = calDAVHelper.getCalDAVCalendars("", true).firstOrNull { it.id == mEventCalendarId }
|
|
mAvailableContacts.firstOrNull { it.email == currentCalendar?.ownerName }?.apply {
|
|
attendees = attendees.filter { it.email != currentCalendar?.ownerName }.toMutableList() as ArrayList<Attendee>
|
|
status = Attendees.ATTENDEE_STATUS_ACCEPTED
|
|
relationship = Attendees.RELATIONSHIP_ORGANIZER
|
|
attendees.add(this)
|
|
}
|
|
}
|
|
|
|
return attendees
|
|
}
|
|
|
|
private fun getNames(): List<Attendee> {
|
|
val contacts = ArrayList<Attendee>()
|
|
val uri = Data.CONTENT_URI
|
|
val projection = arrayOf(
|
|
Data.CONTACT_ID,
|
|
StructuredName.PREFIX,
|
|
StructuredName.GIVEN_NAME,
|
|
StructuredName.MIDDLE_NAME,
|
|
StructuredName.FAMILY_NAME,
|
|
StructuredName.SUFFIX,
|
|
StructuredName.PHOTO_THUMBNAIL_URI
|
|
)
|
|
|
|
val selection = "${Data.MIMETYPE} = ?"
|
|
val selectionArgs = arrayOf(StructuredName.CONTENT_ITEM_TYPE)
|
|
|
|
queryCursor(uri, projection, selection, selectionArgs) { cursor ->
|
|
val id = cursor.getIntValue(Data.CONTACT_ID)
|
|
val prefix = cursor.getStringValue(StructuredName.PREFIX) ?: ""
|
|
val firstName = cursor.getStringValue(StructuredName.GIVEN_NAME) ?: ""
|
|
val middleName = cursor.getStringValue(StructuredName.MIDDLE_NAME) ?: ""
|
|
val surname = cursor.getStringValue(StructuredName.FAMILY_NAME) ?: ""
|
|
val suffix = cursor.getStringValue(StructuredName.SUFFIX) ?: ""
|
|
val photoUri = cursor.getStringValue(StructuredName.PHOTO_THUMBNAIL_URI) ?: ""
|
|
|
|
val names = arrayListOf(prefix, firstName, middleName, surname, suffix).filter { it.trim().isNotEmpty() }
|
|
val fullName = TextUtils.join(" ", names).trim()
|
|
if (fullName.isNotEmpty() || photoUri.isNotEmpty()) {
|
|
val contact = Attendee(id, fullName, "", Attendees.ATTENDEE_STATUS_NONE, photoUri, false, Attendees.RELATIONSHIP_NONE)
|
|
contacts.add(contact)
|
|
}
|
|
}
|
|
return contacts
|
|
}
|
|
|
|
private fun getEmails(): ArrayList<Attendee> {
|
|
val contacts = ArrayList<Attendee>()
|
|
val uri = CommonDataKinds.Email.CONTENT_URI
|
|
val projection = arrayOf(
|
|
Data.CONTACT_ID,
|
|
CommonDataKinds.Email.DATA
|
|
)
|
|
|
|
queryCursor(uri, projection) { cursor ->
|
|
val id = cursor.getIntValue(Data.CONTACT_ID)
|
|
val email = cursor.getStringValue(CommonDataKinds.Email.DATA) ?: return@queryCursor
|
|
val contact = Attendee(id, "", email, Attendees.ATTENDEE_STATUS_NONE, "", false, Attendees.RELATIONSHIP_NONE)
|
|
contacts.add(contact)
|
|
}
|
|
|
|
return contacts
|
|
}
|
|
|
|
private fun updateIconColors() = binding.apply {
|
|
eventShowOnMap.applyColorFilter(getProperPrimaryColor())
|
|
val textColor = getProperTextColor()
|
|
arrayOf(
|
|
eventTimeImage, eventTimeZoneImage, eventRepetitionImage, eventReminderImage, eventTypeImage, eventCaldavCalendarImage,
|
|
eventReminder1Type, eventReminder2Type, eventReminder3Type, eventAttendeesImage, eventAvailabilityImage, eventColorImage
|
|
).forEach {
|
|
it.applyColorFilter(textColor)
|
|
}
|
|
}
|
|
|
|
private fun updateActionBarTitle() {
|
|
binding.eventToolbar.title = if (mIsNewEvent) {
|
|
getString(R.string.new_event)
|
|
} else {
|
|
getString(R.string.edit_event)
|
|
}
|
|
}
|
|
}
|