diff --git a/app/src/main/kotlin/com/simplemobiletools/calendar/pro/activities/EventActivity.kt b/app/src/main/kotlin/com/simplemobiletools/calendar/pro/activities/EventActivity.kt index b5dc7156c..e067183de 100644 --- a/app/src/main/kotlin/com/simplemobiletools/calendar/pro/activities/EventActivity.kt +++ b/app/src/main/kotlin/com/simplemobiletools/calendar/pro/activities/EventActivity.kt @@ -10,6 +10,7 @@ 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 @@ -76,6 +77,7 @@ class EventActivity : SimpleActivity() { 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 @@ -164,6 +166,7 @@ class EventActivity : SimpleActivity() { putString(ATTENDEES, getAllAttendees(false)) putInt(AVAILABILITY, mAvailability) + putInt(EVENT_COLOR, mEventColor) putLong(EVENT_TYPE_ID, mEventTypeId) putInt(EVENT_CALENDAR_ID, mEventCalendarId) @@ -196,6 +199,7 @@ class EventActivity : SimpleActivity() { mReminder3Type = getInt(REMINDER_3_TYPE) mAvailability = getInt(AVAILABILITY) + mEventColor = getInt(EVENT_COLOR) mRepeatInterval = getInt(REPEAT_INTERVAL) mRepeatRule = getInt(REPEAT_RULE) @@ -429,6 +433,7 @@ class EventActivity : SimpleActivity() { mEventTypeId != mEvent.eventType || mWasCalendarChanged || mIsAllDayEvent != mEvent.getIsAllDay() || + mEventColor != mEvent.color || hasTimeChanged ) { return true @@ -488,6 +493,7 @@ class EventActivity : SimpleActivity() { mEventTypeId = mEvent.eventType mEventCalendarId = mEvent.getCalDAVCalendarId() mAvailability = mEvent.availability + mEventColor = mEvent.color val token = object : TypeToken>() {}.type mAttendees = Gson().fromJson>(mEvent.attendees, token) ?: ArrayList() @@ -825,6 +831,28 @@ class EventActivity : SimpleActivity() { } } + private fun showEventColorDialog() { + hideKeyboard() + ensureBackgroundThread { + val eventType = eventsHelper.getEventTypeWithCalDAVCalendarId(calendarId = mEventCalendarId)!! + val eventColors = getEventColors(eventType) + runOnUiThread { + val currentColor = if (mEventColor == 0) { + eventType.color + } else { + mEventColor + } + + SelectEventColorDialog(activity = this, colors = eventColors, currentColor = currentColor) { newColor -> + if (newColor != currentColor) { + mEventColor = newColor + updateEventColorInfo(defaultColor = eventType.color) + } + } + } + } + } + private fun checkReminderTexts() { updateReminder1Text() updateReminder2Text() @@ -959,6 +987,10 @@ class EventActivity : SimpleActivity() { updateAvailabilityImage() } } + + event_caldav_color_holder.setOnClickListener { + showEventColorDialog() + } } else { updateCurrentCalendarInfo(null) } @@ -973,7 +1005,6 @@ class EventActivity : SimpleActivity() { event_type_holder.beVisibleIf(currentCalendar == null) event_caldav_calendar_divider.beVisibleIf(currentCalendar == null) event_caldav_calendar_email.beGoneIf(currentCalendar == null) - event_caldav_calendar_color.beGoneIf(currentCalendar == null) if (currentCalendar == null) { mEventCalendarId = STORED_LOCALLY_ONLY @@ -986,14 +1017,23 @@ class EventActivity : SimpleActivity() { event_caldav_calendar_holder.apply { setPadding(paddingLeft, mediumMargin, paddingRight, mediumMargin) } + + event_caldav_color_image.beGone() + event_caldav_color_holder.beGone() + event_caldav_color_divider.beGone() } else { event_caldav_calendar_email.text = currentCalendar.accountName ensureBackgroundThread { - val calendarColor = eventsHelper.getEventTypeWithCalDAVCalendarId(currentCalendar.id)?.color ?: currentCalendar.color + val eventType = eventsHelper.getEventTypeWithCalDAVCalendarId(currentCalendar.id) + val calendarColor = eventType?.color ?: currentCalendar.color + val canCustomizeColors = if (eventType != null) { + getEventColors(eventType).isNotEmpty() + } else { + false + } runOnUiThread { - event_caldav_calendar_color.setFillWithStroke(calendarColor, getProperBackgroundColor()) event_caldav_calendar_name.apply { text = currentCalendar.displayName setPadding(paddingLeft, paddingTop, paddingRight, resources.getDimension(R.dimen.tiny_margin).toInt()) @@ -1002,11 +1042,31 @@ class EventActivity : SimpleActivity() { event_caldav_calendar_holder.apply { setPadding(paddingLeft, 0, paddingRight, 0) } + + event_caldav_color_image.beVisibleIf(canCustomizeColors) + event_caldav_color_holder.beVisibleIf(canCustomizeColors) + event_caldav_color_divider.beVisibleIf(canCustomizeColors) + if (canCustomizeColors) { + updateEventColorInfo(calendarColor) + } } } } } + private fun updateEventColorInfo(defaultColor: Int) { + val eventColor = if (mEventColor == 0) { + defaultColor + } else { + mEventColor + } + event_caldav_color.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() && @@ -1195,6 +1255,7 @@ class EventActivity : SimpleActivity() { source = newSource location = event_location.value availability = mAvailability + color = mEventColor } // recreate the event if it was moved in a different CalDAV calendar @@ -1791,7 +1852,7 @@ class EventActivity : SimpleActivity() { val textColor = getProperTextColor() arrayOf( event_time_image, event_time_zone_image, event_repetition_image, event_reminder_image, event_type_image, event_caldav_calendar_image, - event_reminder_1_type, event_reminder_2_type, event_reminder_3_type, event_attendees_image, event_availability_image + event_reminder_1_type, event_reminder_2_type, event_reminder_3_type, event_attendees_image, event_availability_image, event_caldav_color_image ).forEach { it.applyColorFilter(textColor) } diff --git a/app/src/main/kotlin/com/simplemobiletools/calendar/pro/adapters/CheckableColorAdapter.kt b/app/src/main/kotlin/com/simplemobiletools/calendar/pro/adapters/CheckableColorAdapter.kt new file mode 100644 index 000000000..9305aed01 --- /dev/null +++ b/app/src/main/kotlin/com/simplemobiletools/calendar/pro/adapters/CheckableColorAdapter.kt @@ -0,0 +1,52 @@ +package com.simplemobiletools.calendar.pro.adapters + +import android.app.Activity +import android.content.res.ColorStateList +import android.graphics.Color +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.recyclerview.widget.RecyclerView +import com.simplemobiletools.calendar.pro.R +import com.simplemobiletools.commons.extensions.applyColorFilter +import kotlinx.android.synthetic.main.checkable_color_button.view.* + +class CheckableColorAdapter(private val activity: Activity, private val colors: IntArray, var currentColor: Int, val callback: (color: Int) -> Unit) : + RecyclerView.Adapter() { + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CheckableColorViewHolder { + val itemView = LayoutInflater.from(activity).inflate(R.layout.checkable_color_button, parent, false) + return CheckableColorViewHolder(itemView) + } + + override fun onBindViewHolder(holder: CheckableColorViewHolder, position: Int) { + val color = colors[position] + holder.bindView(color = color, checked = color == currentColor) + } + + override fun getItemCount() = colors.size + + private fun updateSelection(color: Int) { + currentColor = color + callback(color) + } + + inner class CheckableColorViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { + + fun bindView(color: Int, checked: Boolean) { + itemView.checkable_color_button.apply { + backgroundTintList = ColorStateList.valueOf(color) + setOnClickListener { + updateSelection(color) + } + + if (checked) { + setImageResource(R.drawable.ic_check_vector) + applyColorFilter(Color.WHITE) + } else { + setImageDrawable(null) + } + } + } + } +} diff --git a/app/src/main/kotlin/com/simplemobiletools/calendar/pro/dialogs/EditEventTypeDialog.kt b/app/src/main/kotlin/com/simplemobiletools/calendar/pro/dialogs/EditEventTypeDialog.kt index 0133c3d2f..1b73dd378 100644 --- a/app/src/main/kotlin/com/simplemobiletools/calendar/pro/dialogs/EditEventTypeDialog.kt +++ b/app/src/main/kotlin/com/simplemobiletools/calendar/pro/dialogs/EditEventTypeDialog.kt @@ -4,6 +4,7 @@ import android.app.Activity import android.widget.ImageView import androidx.appcompat.app.AlertDialog import com.simplemobiletools.calendar.pro.R +import com.simplemobiletools.calendar.pro.extensions.calDAVHelper import com.simplemobiletools.calendar.pro.extensions.eventsHelper import com.simplemobiletools.calendar.pro.helpers.OTHER_EVENT import com.simplemobiletools.calendar.pro.models.EventType @@ -32,7 +33,9 @@ class EditEventTypeDialog(val activity: Activity, var eventType: EventType? = nu } } } else { - SelectEventTypeColorDialog(activity, eventType!!) { + val currentColor = eventType!!.color + val colors = activity.calDAVHelper.getAvailableCalDAVCalendarColors(eventType!!).keys.toIntArray() + SelectEventTypeColorDialog(activity, colors = colors, currentColor = currentColor) { eventType!!.color = it setupColor(type_color) } diff --git a/app/src/main/kotlin/com/simplemobiletools/calendar/pro/dialogs/SelectEventColorDialog.kt b/app/src/main/kotlin/com/simplemobiletools/calendar/pro/dialogs/SelectEventColorDialog.kt new file mode 100644 index 000000000..68a9a395c --- /dev/null +++ b/app/src/main/kotlin/com/simplemobiletools/calendar/pro/dialogs/SelectEventColorDialog.kt @@ -0,0 +1,42 @@ +package com.simplemobiletools.calendar.pro.dialogs + +import android.app.Activity +import android.view.ViewGroup +import androidx.appcompat.app.AlertDialog +import com.simplemobiletools.calendar.pro.R +import com.simplemobiletools.calendar.pro.adapters.CheckableColorAdapter +import com.simplemobiletools.calendar.pro.views.AutoGridLayoutManager +import com.simplemobiletools.commons.extensions.getAlertDialogBuilder +import com.simplemobiletools.commons.extensions.setupDialogStuff +import kotlinx.android.synthetic.main.dialog_select_color.view.* + +class SelectEventColorDialog(val activity: Activity, val colors: IntArray, var currentColor: Int, val callback: (color: Int) -> Unit) { + private var dialog: AlertDialog? = null + + init { + val view = activity.layoutInflater.inflate(R.layout.dialog_select_color, null) as ViewGroup + val colorAdapter = CheckableColorAdapter(activity, colors, currentColor) { color -> + callback(color) + dialog?.dismiss() + } + + view.color_grid.apply { + val width = activity.resources.getDimensionPixelSize(R.dimen.smaller_icon_size) + val spacing = activity.resources.getDimensionPixelSize(R.dimen.small_margin) * 2 + layoutManager = AutoGridLayoutManager(context = activity, itemWidth = width + spacing) + adapter = colorAdapter + } + + activity.getAlertDialogBuilder() + .apply { + setNeutralButton(R.string.default_calendar_color) { dialog, _ -> + callback(0) + dialog?.dismiss() + } + + activity.setupDialogStuff(view, this, R.string.event_color) { + dialog = it + } + } + } +} diff --git a/app/src/main/kotlin/com/simplemobiletools/calendar/pro/dialogs/SelectEventTypeColorDialog.kt b/app/src/main/kotlin/com/simplemobiletools/calendar/pro/dialogs/SelectEventTypeColorDialog.kt index bc451de2d..281a1f3ec 100644 --- a/app/src/main/kotlin/com/simplemobiletools/calendar/pro/dialogs/SelectEventTypeColorDialog.kt +++ b/app/src/main/kotlin/com/simplemobiletools/calendar/pro/dialogs/SelectEventTypeColorDialog.kt @@ -2,42 +2,37 @@ package com.simplemobiletools.calendar.pro.dialogs import android.app.Activity import android.view.ViewGroup -import android.widget.RadioButton -import android.widget.RadioGroup import androidx.appcompat.app.AlertDialog import com.simplemobiletools.calendar.pro.R -import com.simplemobiletools.calendar.pro.extensions.calDAVHelper -import com.simplemobiletools.calendar.pro.models.EventType +import com.simplemobiletools.calendar.pro.adapters.CheckableColorAdapter +import com.simplemobiletools.calendar.pro.views.AutoGridLayoutManager import com.simplemobiletools.commons.dialogs.ColorPickerDialog import com.simplemobiletools.commons.extensions.getAlertDialogBuilder -import com.simplemobiletools.commons.extensions.getProperBackgroundColor -import com.simplemobiletools.commons.extensions.setFillWithStroke import com.simplemobiletools.commons.extensions.setupDialogStuff -import kotlinx.android.synthetic.main.dialog_select_event_type_color.view.* -import kotlinx.android.synthetic.main.radio_button_with_color.view.* +import kotlinx.android.synthetic.main.dialog_select_color.view.* -class SelectEventTypeColorDialog(val activity: Activity, val eventType: EventType, val callback: (color: Int) -> Unit) { +class SelectEventTypeColorDialog(val activity: Activity, val colors: IntArray, var currentColor: Int, val callback: (color: Int) -> Unit) { private var dialog: AlertDialog? = null - private val radioGroup: RadioGroup - private var wasInit = false - private val colors = activity.calDAVHelper.getAvailableCalDAVCalendarColors(eventType) init { - val view = activity.layoutInflater.inflate(R.layout.dialog_select_event_type_color, null) as ViewGroup - radioGroup = view.dialog_select_event_type_color_radio - view.dialog_select_event_type_other_value.setOnClickListener { - showCustomColorPicker() + val view = activity.layoutInflater.inflate(R.layout.dialog_select_color, null) as ViewGroup + val colorAdapter = CheckableColorAdapter(activity, colors, currentColor) { color -> + callback(color) + dialog?.dismiss() } - colors.forEachIndexed { index, value -> - addRadioButton(index, value) + view.color_grid.apply { + val width = activity.resources.getDimensionPixelSize(R.dimen.smaller_icon_size) + val spacing = activity.resources.getDimensionPixelSize(R.dimen.small_margin) * 2 + layoutManager = AutoGridLayoutManager(context = activity, itemWidth = width + spacing) + adapter = colorAdapter } - wasInit = true activity.getAlertDialogBuilder() + .setNegativeButton(R.string.cancel, null) .apply { - activity.setupDialogStuff(view, this) { alertDialog -> - dialog = alertDialog + activity.setupDialogStuff(view, this, R.string.color) { + dialog = it } if (colors.isEmpty()) { @@ -46,34 +41,12 @@ class SelectEventTypeColorDialog(val activity: Activity, val eventType: EventTyp } } - private fun addRadioButton(colorKey: Int, color: Int) { - val view = activity.layoutInflater.inflate(R.layout.radio_button_with_color, null) - (view.dialog_radio_button as RadioButton).apply { - text = if (color == 0) activity.getString(R.string.transparent) else String.format("#%06X", 0xFFFFFF and color) - isChecked = color == eventType.color - id = colorKey - } - - view.dialog_radio_color.setFillWithStroke(color, activity.getProperBackgroundColor()) - view.setOnClickListener { - viewClicked(colorKey) - } - radioGroup.addView(view, RadioGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)) - } - - private fun viewClicked(colorKey: Int) { - if (!wasInit) - return - - callback(colors[colorKey]) - dialog?.dismiss() - } - private fun showCustomColorPicker() { - ColorPickerDialog(activity, eventType.color) { wasPositivePressed, color -> + ColorPickerDialog(activity, currentColor) { wasPositivePressed, color -> if (wasPositivePressed) { callback(color) } + dialog?.dismiss() } } diff --git a/app/src/main/kotlin/com/simplemobiletools/calendar/pro/fragments/WeekFragment.kt b/app/src/main/kotlin/com/simplemobiletools/calendar/pro/fragments/WeekFragment.kt index 45e3be372..7418548a9 100644 --- a/app/src/main/kotlin/com/simplemobiletools/calendar/pro/fragments/WeekFragment.kt +++ b/app/src/main/kotlin/com/simplemobiletools/calendar/pro/fragments/WeekFragment.kt @@ -557,9 +557,13 @@ class WeekFragment : Fragment(), WeeklyCalendar { val dayColumn = dayColumns[dayOfWeek] (inflater.inflate(R.layout.week_event_marker, null, false) as ConstraintLayout).apply { - var backgroundColor = eventTypeColors.get(event.eventType, primaryColor) + var backgroundColor = if (event.color == 0) { + eventTypeColors.get(event.eventType, primaryColor) + } else { + event.color + } var textColor = backgroundColor.getContrastColor() - val currentEventWeeklyView = eventTimeRanges[currentDayCode]!!.get(event.id) + val currentEventWeeklyView = eventTimeRanges[currentDayCode]!![event.id] val adjustAlpha = if (event.isTask()) { dimCompletedTasks && event.isTaskCompleted() diff --git a/app/src/main/kotlin/com/simplemobiletools/calendar/pro/helpers/CalDAVHelper.kt b/app/src/main/kotlin/com/simplemobiletools/calendar/pro/helpers/CalDAVHelper.kt index adc41c6af..9f5bb195f 100644 --- a/app/src/main/kotlin/com/simplemobiletools/calendar/pro/helpers/CalDAVHelper.kt +++ b/app/src/main/kotlin/com/simplemobiletools/calendar/pro/helpers/CalDAVHelper.kt @@ -4,8 +4,8 @@ import android.annotation.SuppressLint import android.content.ContentUris import android.content.ContentValues import android.content.Context +import android.graphics.Color import android.provider.CalendarContract.* -import android.util.SparseIntArray import android.widget.Toast import com.google.gson.Gson import com.google.gson.reflect.TypeToken @@ -20,6 +20,7 @@ import org.joda.time.DateTimeZone import org.joda.time.format.DateTimeFormat import java.util.* import kotlin.math.max +import kotlin.math.min @SuppressLint("MissingPermission") class CalDAVHelper(val context: Context) { @@ -84,8 +85,9 @@ class CalDAVHelper(val context: Context) { val accountType = cursor.getStringValue(Calendars.ACCOUNT_TYPE) val ownerName = cursor.getStringValue(Calendars.OWNER_ACCOUNT) ?: "" val color = cursor.getIntValue(Calendars.CALENDAR_COLOR) + val displayColor = getDisplayColorFromColor(color) val accessLevel = cursor.getIntValue(Calendars.CALENDAR_ACCESS_LEVEL) - val calendar = CalDAVCalendar(id, displayName, accountName, accountType, ownerName, color, accessLevel) + val calendar = CalDAVCalendar(id, displayName, accountName, accountType, ownerName, displayColor, accessLevel) calendars.add(calendar) } @@ -116,35 +118,33 @@ class CalDAVHelper(val context: Context) { private fun getCalDAVColorKey(eventType: EventType): String? { val colors = getAvailableCalDAVCalendarColors(eventType) - val colorKey = colors.indexOf(eventType.color) - return if (colorKey > 0) { - colorKey.toString() - } else { - null - } + return colors[eventType.color] + } + + // darkens the given color to ensure that white text is clearly visible on top of it + private fun getDisplayColorFromColor(color: Int): Int { + val hsv = FloatArray(3) + Color.colorToHSV(color, hsv) + hsv[1] = min(hsv[1] * SATURATION_ADJUST, 1.0f) + hsv[2] = hsv[2] * INTENSITY_ADJUST + return Color.HSVToColor(hsv) } @SuppressLint("MissingPermission") - fun getAvailableCalDAVCalendarColors(eventType: EventType): ArrayList { - val colors = SparseIntArray() + fun getAvailableCalDAVCalendarColors(eventType: EventType, colorType: Int = Colors.TYPE_CALENDAR): Map { + val colors = mutableMapOf() val uri = Colors.CONTENT_URI val projection = arrayOf(Colors.COLOR, Colors.COLOR_KEY) val selection = "${Colors.COLOR_TYPE} = ? AND ${Colors.ACCOUNT_NAME} = ?" - val selectionArgs = arrayOf(Colors.TYPE_CALENDAR.toString(), eventType.caldavEmail) + val selectionArgs = arrayOf(colorType.toString(), eventType.caldavEmail) context.queryCursor(uri, projection, selection, selectionArgs) { cursor -> - val colorKey = cursor.getIntValue(Colors.COLOR_KEY) + val colorKey = cursor.getStringValue(Colors.COLOR_KEY) val color = cursor.getIntValue(Colors.COLOR) - colors.put(colorKey, color) + val displayColor = getDisplayColorFromColor(color) + colors[displayColor] = colorKey } - - var sortedColors = ArrayList(colors.size()) - (0 until colors.size()).mapTo(sortedColors) { colors[it] } - if (sortedColors.isNotEmpty()) { - sortedColors = sortedColors.distinct() as ArrayList - } - - return sortedColors + return colors.toSortedMap(HsvColorComparator()) } @SuppressLint("MissingPermission") @@ -181,7 +181,8 @@ class CalDAVHelper(val context: Context) { Events.EVENT_TIMEZONE, Events.CALENDAR_TIME_ZONE, Events.DELETED, - Events.AVAILABILITY + Events.AVAILABILITY, + Events.EVENT_COLOR ) val selection = "${Events.CALENDAR_ID} = $calendarId" @@ -210,6 +211,12 @@ class CalDAVHelper(val context: Context) { val reminders = getCalDAVEventReminders(id) val attendees = Gson().toJson(getCalDAVEventAttendees(id)) val availability = cursor.getIntValue(Events.AVAILABILITY) + val color = cursor.getIntValueOrNull(Events.EVENT_COLOR) + val displayColor = if (color != null) { + getDisplayColorFromColor(color) + } else { + 0 + } if (endTS == 0L) { val duration = cursor.getStringValue(Events.DURATION) ?: "" @@ -230,7 +237,8 @@ class CalDAVHelper(val context: Context) { reminder2?.minutes ?: REMINDER_OFF, reminder3?.minutes ?: REMINDER_OFF, reminder1?.type ?: REMINDER_NOTIFICATION, reminder2?.type ?: REMINDER_NOTIFICATION, reminder3?.type ?: REMINDER_NOTIFICATION, repeatRule.repeatInterval, repeatRule.repeatRule, - repeatRule.repeatLimit, ArrayList(), attendees, importId, eventTimeZone, allDay, eventTypeId, source = source, availability = availability + repeatRule.repeatLimit, ArrayList(), attendees, importId, eventTimeZone, allDay, eventTypeId, + source = source, availability = availability, color = displayColor ) if (event.getIsAllDay()) { @@ -291,7 +299,6 @@ class CalDAVHelper(val context: Context) { existingEvent.apply { this.id = null - color = 0 lastUpdated = 0L repetitionExceptions = ArrayList() } @@ -394,14 +401,23 @@ class CalDAVHelper(val context: Context) { } private fun fillEventContentValues(event: Event): ContentValues { + val calendarId = event.getCalDAVCalendarId() return ContentValues().apply { - put(Events.CALENDAR_ID, event.getCalDAVCalendarId()) + put(Events.CALENDAR_ID, calendarId) put(Events.TITLE, event.title) put(Events.DESCRIPTION, event.description) put(Events.EVENT_LOCATION, event.location) put(Events.STATUS, Events.STATUS_CONFIRMED) put(Events.AVAILABILITY, event.availability) + if (event.color == 0) { + put(Events.EVENT_COLOR_KEY, "") + } else { + val eventType = eventsHelper.getEventTypeWithCalDAVCalendarId(calendarId)!! + val colors = getAvailableCalDAVCalendarColors(eventType, Colors.TYPE_EVENT) + put(Events.EVENT_COLOR_KEY, colors[event.color]) + } + val repeatRule = Parser().getRepeatCode(event) if (repeatRule.isEmpty()) { putNull(Events.RRULE) @@ -534,4 +550,9 @@ class CalDAVHelper(val context: Context) { private fun getCalDAVEventImportId(calendarId: Int, eventId: Long) = "$CALDAV-$calendarId-$eventId" private fun refreshCalDAVCalendar(event: Event) = context.refreshCalDAVCalendars(event.getCalDAVCalendarId().toString(), false) + + companion object { + private const val INTENSITY_ADJUST = 0.8f + private const val SATURATION_ADJUST = 1.3f + } } diff --git a/app/src/main/kotlin/com/simplemobiletools/calendar/pro/helpers/Constants.kt b/app/src/main/kotlin/com/simplemobiletools/calendar/pro/helpers/Constants.kt index 1a3793d81..b3581a4c0 100644 --- a/app/src/main/kotlin/com/simplemobiletools/calendar/pro/helpers/Constants.kt +++ b/app/src/main/kotlin/com/simplemobiletools/calendar/pro/helpers/Constants.kt @@ -255,6 +255,7 @@ const val AVAILABILITY = "AVAILABILITY" const val EVENT_TYPE_ID = "EVENT_TYPE_ID" const val EVENT_CALENDAR_ID = "EVENT_CALENDAR_ID" const val IS_NEW_EVENT = "IS_NEW_EVENT" +const val EVENT_COLOR = "EVENT_COLOR" // actions const val ACTION_MARK_COMPLETED = "ACTION_MARK_COMPLETED" diff --git a/app/src/main/kotlin/com/simplemobiletools/calendar/pro/helpers/EventsHelper.kt b/app/src/main/kotlin/com/simplemobiletools/calendar/pro/helpers/EventsHelper.kt index 31c635149..fc9327c71 100644 --- a/app/src/main/kotlin/com/simplemobiletools/calendar/pro/helpers/EventsHelper.kt +++ b/app/src/main/kotlin/com/simplemobiletools/calendar/pro/helpers/EventsHelper.kt @@ -337,7 +337,10 @@ class EventsHelper(val context: Context) { } } } - it.color = eventTypeColors.get(it.eventType) ?: context.getProperPrimaryColor() + + if (it.color == 0) { + it.color = eventTypeColors.get(it.eventType) ?: context.getProperPrimaryColor() + } } callback(events) diff --git a/app/src/main/kotlin/com/simplemobiletools/calendar/pro/helpers/HsvColorComparator.kt b/app/src/main/kotlin/com/simplemobiletools/calendar/pro/helpers/HsvColorComparator.kt new file mode 100644 index 000000000..3e83c2b68 --- /dev/null +++ b/app/src/main/kotlin/com/simplemobiletools/calendar/pro/helpers/HsvColorComparator.kt @@ -0,0 +1,40 @@ +package com.simplemobiletools.calendar.pro.helpers + +import android.graphics.Color + +/** + * A color comparator which compares based on hue, saturation, and value. + * Source: AOSP Color picker, https://cs.android.com/android/platform/superproject/+/master:frameworks/opt/colorpicker/src/com/android/colorpicker/HsvColorComparator.java + */ +class HsvColorComparator : Comparator { + override fun compare(lhs: Int?, rhs: Int?): Int { + val hsv = FloatArray(3) + Color.colorToHSV(lhs!!, hsv) + val hue1 = hsv[0] + val sat1 = hsv[1] + val val1 = hsv[2] + val hsv2 = FloatArray(3) + Color.colorToHSV(rhs!!, hsv2) + val hue2 = hsv2[0] + val sat2 = hsv2[1] + val val2 = hsv2[2] + if (hue1 < hue2) { + return 1 + } else if (hue1 > hue2) { + return -1 + } else { + if (sat1 < sat2) { + return 1 + } else if (sat1 > sat2) { + return -1 + } else { + if (val1 < val2) { + return 1 + } else if (val1 > val2) { + return -1 + } + } + } + return 0 + } +} diff --git a/app/src/main/kotlin/com/simplemobiletools/calendar/pro/views/AutoGridLayoutManager.kt b/app/src/main/kotlin/com/simplemobiletools/calendar/pro/views/AutoGridLayoutManager.kt new file mode 100644 index 000000000..2428a85d2 --- /dev/null +++ b/app/src/main/kotlin/com/simplemobiletools/calendar/pro/views/AutoGridLayoutManager.kt @@ -0,0 +1,34 @@ +package com.simplemobiletools.calendar.pro.views + +import android.content.Context +import androidx.recyclerview.widget.GridLayoutManager +import androidx.recyclerview.widget.RecyclerView +import kotlin.math.max + +/** + * RecyclerView GridLayoutManager but with automatic spanCount calculation + * @param itemWidth: Grid item width in pixels. Will be used to calculate span count. + */ +class AutoGridLayoutManager( + context: Context, + private var itemWidth: Int +) : GridLayoutManager(context, 1) { + + init { + require(itemWidth >= 0) + } + + override fun onLayoutChildren(recycler: RecyclerView.Recycler?, state: RecyclerView.State?) { + val width = width + val height = height + if (itemWidth > 0 && width > 0 && height > 0) { + val totalSpace = if (orientation == VERTICAL) { + width - paddingRight - paddingLeft + } else { + height - paddingTop - paddingBottom + } + spanCount = max(1, totalSpace / itemWidth) + } + super.onLayoutChildren(recycler, state) + } +} diff --git a/app/src/main/res/layout/activity_event.xml b/app/src/main/res/layout/activity_event.xml index c467c41ef..5f5382cf9 100644 --- a/app/src/main/res/layout/activity_event.xml +++ b/app/src/main/res/layout/activity_event.xml @@ -507,10 +507,63 @@ android:importantForAccessibility="no" /> + + + + + + + + + + + + @@ -533,7 +586,6 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginStart="@dimen/small_margin" - android:layout_toStartOf="@+id/event_caldav_calendar_color" android:ellipsize="end" android:maxLines="1" android:paddingTop="@dimen/medium_margin" @@ -548,7 +600,6 @@ android:layout_height="wrap_content" android:layout_below="@+id/event_caldav_calendar_name" android:layout_marginStart="@dimen/small_margin" - android:layout_toStartOf="@+id/event_caldav_calendar_color" android:ellipsize="end" android:maxLines="1" android:paddingEnd="@dimen/medium_margin" @@ -556,15 +607,6 @@ android:textSize="@dimen/meta_text_size" tools:text="hello@simplemobiletools.com" /> - - + + + + + + diff --git a/app/src/main/res/layout/dialog_select_color.xml b/app/src/main/res/layout/dialog_select_color.xml new file mode 100644 index 000000000..86ff4f649 --- /dev/null +++ b/app/src/main/res/layout/dialog_select_color.xml @@ -0,0 +1,26 @@ + + + + + + diff --git a/app/src/main/res/layout/dialog_select_event_type_color.xml b/app/src/main/res/layout/dialog_select_event_type_color.xml deleted file mode 100644 index a4960b141..000000000 --- a/app/src/main/res/layout/dialog_select_event_type_color.xml +++ /dev/null @@ -1,45 +0,0 @@ - - - - - - - - - - - - -