Commit c9af1962 authored by Ricki Hirner's avatar Ricki Hirner 🐑

Add suport for event colors

parent 17f26963
Pipeline #10661161 passed with stage
in 2 minutes and 34 seconds
......@@ -13,7 +13,6 @@ import android.annotation.SuppressLint
import android.content.ContentProviderClient
import android.content.ContentUris
import android.content.ContentValues
import android.content.EntityIterator
import android.database.DatabaseUtils
import android.net.Uri
import android.os.RemoteException
......@@ -67,6 +66,7 @@ abstract class AndroidCalendar<out T: AndroidEvent>(
}
}
@SuppressLint("Recycle")
@JvmStatic
@Throws(FileNotFoundException::class, CalendarStorageException::class)
fun<T: AndroidCalendar<AndroidEvent>> findByID(account: Account, provider: ContentProviderClient, factory: AndroidCalendarFactory<T>, id: Long): T {
......
......@@ -8,6 +8,7 @@
package at.bitfire.ical4android
import android.annotation.SuppressLint
import android.content.ContentProviderOperation
import android.content.ContentProviderOperation.Builder
import android.content.ContentUris
......@@ -70,6 +71,7 @@ abstract class AndroidEvent(
* @throws FileNotFoundException if there's no event with [id] in the calendar storage
* @throws CalendarStorageException on calendar storage I/O errors
*/
@SuppressLint("Recycle")
@Throws(FileNotFoundException::class, CalendarStorageException::class)
get() {
if (field != null)
......@@ -127,6 +129,14 @@ abstract class AndroidEvent(
event.location = row.getAsString(Events.EVENT_LOCATION)
event.description = row.getAsString(Events.DESCRIPTION)
row.getAsString(Events.CALENDAR_COLOR_KEY)?.let { name ->
try {
event.color = EventColor.valueOf(name)
} catch(e: IllegalArgumentException) {
Constants.log.warning("Ignoring unknown color $name from Calendar Provider")
}
}
val allDay = (row.getAsInteger(Events.ALL_DAY) ?: 0) != 0
val tsStart = row.getAsLong(Events.DTSTART)
val tsEnd = row.getAsLong(Events.DTEND)
......@@ -517,6 +527,7 @@ abstract class AndroidEvent(
event.summary?.let { builder.withValue(Events.TITLE, it) }
event.location?.let { builder.withValue(Events.EVENT_LOCATION, it) }
event.description?.let { builder.withValue(Events.DESCRIPTION, it) }
event.color?.let { builder.withValue(Events.EVENT_COLOR_KEY, it.name) }
event.organizer?.let { organizer ->
val email: String?
......
......@@ -30,6 +30,7 @@ class Event: iCalendar() {
var summary: String? = null
var location: String? = null
var description: String? = null
var color: EventColor? = null
var dtStart: DtStart? = null
var dtEnd: DtEnd? = null
......@@ -160,6 +161,10 @@ class Event: iCalendar() {
is Uid -> e.uid = prop.value
is RecurrenceId -> e.recurrenceId = prop
is Sequence -> e.sequence = prop.sequenceNo
is Summary -> e.summary = prop.value
is Location -> e.location = prop.value
is Description -> e.description = prop.value
is Color -> e.color = prop.value
is DtStart -> e.dtStart = prop
is DtEnd -> e.dtEnd = prop
is Duration -> e.duration = prop
......@@ -167,12 +172,9 @@ class Event: iCalendar() {
is RDate -> e.rDates += prop
is ExRule -> e.exRule = prop
is ExDate -> e.exDates += prop
is Summary -> e.summary = prop.value
is Location -> e.location = prop.value
is Description -> e.description = prop.value
is Clazz -> e.forPublic = prop == Clazz.PUBLIC
is Status -> e.status = prop
is Transp -> e.opaque = prop == Transp.OPAQUE
is Clazz -> e.forPublic = prop == Clazz.PUBLIC
is Organizer -> e.organizer = prop
is Attendee -> e.attendees += prop
is LastModified -> e.lastModified = prop
......@@ -239,6 +241,11 @@ class Event: iCalendar() {
recurrenceId?.let { props += it }
sequence?.let { if (it != 0) props += Sequence(it) }
summary?.let { props += Summary(it) }
location?.let { props += Location(it) }
description?.let { props += Description(it) }
color?.let { props += Color(it) }
props += dtStart
dtEnd?.let { props += it }
duration?.let { props += it }
......@@ -248,10 +255,6 @@ class Event: iCalendar() {
exRule?.let { props += it }
props.addAll(exDates)
summary?.let { props += Summary(it) }
location?.let { props += Location(it) }
description?.let { props += Description(it) }
status?.let { props += it }
if (!opaque)
props += Transp.TRANSPARENT
......
/*
* Copyright © Ricki Hirner (bitfire web engineering).
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the GNU Public License v3.0
* which accompanies this distribution, and is available at
* http://www.gnu.org/licenses/gpl.html
*/
package at.bitfire.ical4android
enum class EventColor(val rgba: Int) {
// for use as COLOR property [https://tools.ietf.org/html/rfc7986#section-5.9]
// values taken from https://www.w3.org/TR/2011/REC-css3-color-20110607/#svg-color
aliceblue(0xf0f8ff.toInt()),
antiquewhite(0xfaebd7.toInt()),
aqua(0x00ffff.toInt()),
aquamarine(0x7fffd4.toInt()),
azure(0xf0ffff.toInt()),
beige(0xf5f5dc.toInt()),
bisque(0xffe4c4.toInt()),
black(0x000000.toInt()),
blanchedalmond(0xffebcd.toInt()),
blue(0x0000ff.toInt()),
blueviolet(0x8a2be2.toInt()),
brown(0xa52a2a.toInt()),
burlywood(0xdeb887.toInt()),
cadetblue(0x5f9ea0.toInt()),
chartreuse(0x7fff00.toInt()),
chocolate(0xd2691e.toInt()),
coral(0xff7f50.toInt()),
cornflowerblue(0x6495ed.toInt()),
cornsilk(0xfff8dc.toInt()),
crimson(0xdc143c.toInt()),
cyan(0x00ffff.toInt()),
darkblue(0x00008b.toInt()),
darkcyan(0x008b8b.toInt()),
darkgoldenrod(0xb8860b.toInt()),
darkgray(0xa9a9a9.toInt()),
darkgreen(0x006400.toInt()),
darkgrey(0xa9a9a9.toInt()),
darkkhaki(0xbdb76b.toInt()),
darkmagenta(0x8b008b.toInt()),
darkolivegreen(0x556b2f.toInt()),
darkorange(0xff8c00.toInt()),
darkorchid(0x9932cc.toInt()),
darkred(0x8b0000.toInt()),
darksalmon(0xe9967a.toInt()),
darkseagreen(0x8fbc8f.toInt()),
darkslateblue(0x483d8b.toInt()),
darkslategray(0x2f4f4f.toInt()),
darkslategrey(0x2f4f4f.toInt()),
darkturquoise(0x00ced1.toInt()),
darkviolet(0x9400d3.toInt()),
deeppink(0xff1493.toInt()),
deepskyblue(0x00bfff.toInt()),
dimgray(0x696969.toInt()),
dimgrey(0x696969.toInt()),
dodgerblue(0x1e90ff.toInt()),
firebrick(0xb22222.toInt()),
floralwhite(0xfffaf0.toInt()),
forestgreen(0x228b22.toInt()),
fuchsia(0xff00ff.toInt()),
gainsboro(0xdcdcdc.toInt()),
ghostwhite(0xf8f8ff.toInt()),
gold(0xffd700.toInt()),
goldenrod(0xdaa520.toInt()),
gray(0x808080.toInt()),
green(0x008000.toInt()),
greenyellow(0xadff2f.toInt()),
grey(0x808080.toInt()),
honeydew(0xf0fff0.toInt()),
hotpink(0xff69b4.toInt()),
indianred(0xcd5c5c.toInt()),
indigo(0x4b0082.toInt()),
ivory(0xfffff0.toInt()),
khaki(0xf0e68c.toInt()),
lavender(0xe6e6fa.toInt()),
lavenderblush(0xfff0f5.toInt()),
lawngreen(0x7cfc00.toInt()),
lemonchiffon(0xfffacd.toInt()),
lightblue(0xadd8e6.toInt()),
lightcoral(0xf08080.toInt()),
lightcyan(0xe0ffff.toInt()),
lightgoldenrodyellow(0xfafad2.toInt()),
lightgray(0xd3d3d3.toInt()),
lightgreen(0x90ee90.toInt()),
lightgrey(0xd3d3d3.toInt()),
lightpink(0xffb6c1.toInt()),
lightsalmon(0xffa07a.toInt()),
lightseagreen(0x20b2aa.toInt()),
lightskyblue(0x87cefa.toInt()),
lightslategray(0x778899.toInt()),
lightslategrey(0x778899.toInt()),
lightsteelblue(0xb0c4de.toInt()),
lightyellow(0xffffe0.toInt()),
lime(0x00ff00.toInt()),
limegreen(0x32cd32.toInt()),
linen(0xfaf0e6.toInt()),
magenta(0xff00ff.toInt()),
maroon(0x800000.toInt()),
mediumaquamarine(0x66cdaa.toInt()),
mediumblue(0x0000cd.toInt()),
mediumorchid(0xba55d3.toInt()),
mediumpurple(0x9370db.toInt()),
mediumseagreen(0x3cb371.toInt()),
mediumslateblue(0x7b68ee.toInt()),
mediumspringgreen(0x00fa9a.toInt()),
mediumturquoise(0x48d1cc.toInt()),
mediumvioletred(0xc71585.toInt()),
midnightblue(0x191970.toInt()),
mintcream(0xf5fffa.toInt()),
mistyrose(0xffe4e1.toInt()),
moccasin(0xffe4b5.toInt()),
navajowhite(0xffdead.toInt()),
navy(0x000080.toInt()),
oldlace(0xfdf5e6.toInt()),
olive(0x808000.toInt()),
olivedrab(0x6b8e23.toInt()),
orange(0xffa500.toInt()),
orangered(0xff4500.toInt()),
orchid(0xda70d6.toInt()),
palegoldenrod(0xeee8aa.toInt()),
palegreen(0x98fb98.toInt()),
paleturquoise(0xafeeee.toInt()),
palevioletred(0xdb7093.toInt()),
papayawhip(0xffefd5.toInt()),
peachpuff(0xffdab9.toInt()),
peru(0xcd853f.toInt()),
pink(0xffc0cb.toInt()),
plum(0xdda0dd.toInt()),
powderblue(0xb0e0e6.toInt()),
purple(0x800080.toInt()),
red(0xff0000.toInt()),
rosybrown(0xbc8f8f.toInt()),
royalblue(0x4169e1.toInt()),
saddlebrown(0x8b4513.toInt()),
salmon(0xfa8072.toInt()),
sandybrown(0xf4a460.toInt()),
seagreen(0x2e8b57.toInt()),
seashell(0xfff5ee.toInt()),
sienna(0xa0522d.toInt()),
silver(0xc0c0c0.toInt()),
skyblue(0x87ceeb.toInt()),
slateblue(0x6a5acd.toInt()),
slategray(0x708090.toInt()),
slategrey(0x708090.toInt()),
snow(0xfffafa.toInt()),
springgreen(0x00ff7f.toInt()),
steelblue(0x4682b4.toInt()),
tan(0xd2b48c.toInt()),
teal(0x008080.toInt()),
thistle(0xd8bfd8.toInt()),
tomato(0xff6347.toInt()),
turquoise(0x40e0d0.toInt()),
violet(0xee82ee.toInt()),
wheat(0xf5deb3.toInt()),
white(0xffffff.toInt()),
whitesmoke(0xf5f5f5.toInt()),
yellow(0xffff00.toInt()),
yellowgreen(0x9acd32.toInt())
}
\ No newline at end of file
......@@ -42,19 +42,22 @@ open class iCalendar {
CompatibilityHints.setHintEnabled(CompatibilityHints.KEY_OUTLOOK_COMPATIBILITY, true)
}
@JvmStatic
var prodId = ProdId("+//IDN bitfire.at//ical4android")
@JvmStatic
private val parameterFactoryRegistry = ParameterFactoryRegistry()
init {
parameterFactoryRegistry.register(Email.PARAMETER_NAME, Email.FACTORY)
}
private val propertyFactoryRegistry = PropertyFactoryRegistry()
init {
propertyFactoryRegistry.register(Color.PROPERTY_NAME, Color.FACTORY)
}
@JvmStatic
protected fun calendarBuilder() = CalendarBuilder(
CalendarParserFactory.getInstance().createParser(),
PropertyFactoryRegistry(), parameterFactoryRegistry, DateUtils.tzRegistry)
propertyFactoryRegistry, parameterFactoryRegistry, DateUtils.tzRegistry)
// time zone helpers
......@@ -102,7 +105,7 @@ open class iCalendar {
val timezone = cal.getComponent(VTimeZone.VTIMEZONE) as VTimeZone?
timezone?.timeZoneId?.let { return it.value }
} catch (e: ParserException) {
Constants.log.log(Level.SEVERE, "Can't understand time zone definition", e);
Constants.log.log(Level.SEVERE, "Can't understand time zone definition", e)
}
return null
}
......@@ -135,15 +138,47 @@ open class iCalendar {
// ical4j helpers and extensions
/** EMAIL property for ATTENDEE properties, as used by iCloud:
/** COLOR property for VEVENT components [RFC 7986 5.9 COLOR] */
class Color(
var value: EventColor? = null
): Property(PROPERTY_NAME, PropertyFactoryImpl.getInstance()) {
companion object {
val FACTORY = Factory()
val PROPERTY_NAME = "COLOR"
}
override fun getValue() = value?.name
override fun setValue(name: String?) {
name?.let {
try {
value = EventColor.valueOf(name.toLowerCase())
} catch(e: IllegalArgumentException) {
Constants.log.warning("Ignoring unknown COLOR $name")
}
}
}
class Factory: PropertyFactory<Color> {
override fun createProperty() = Color()
override fun createProperty(params: ParameterList?, value: String?): Color {
val c = Color()
c.setValue(value)
return c
}
override fun supports(property: String?) = property == PROPERTY_NAME
}
}
/** EMAIL parameter for ATTENDEE properties, as used by iCloud:
ATTENDEE;[email protected];/path/to/principal
*/
class Email(): Parameter(PARAMETER_NAME, ParameterFactoryImpl.getInstance()) {
companion object {
val FACTORY = Factory()
@JvmField
val PARAMETER_NAME = "EMAIL"
}
......@@ -155,14 +190,11 @@ open class iCalendar {
email = Strings.unquote(aValue)
}
class Factory: ParameterFactory<Email> {
@Throws(URISyntaxException::class)
override fun createParameter(value: String) = Email(value)
override fun supports(name: String) = name == PARAMETER_NAME
}
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment