Skip to content

Commit

Permalink
Add support for VCALENDAR types
Browse files Browse the repository at this point in the history
Treat VCALENDAR like VEVENT types as VCALENDAR is just a wrapper
around a VEVENT.

Also add a few simple tests for VCARD, VCALENDAR and VEVENT to
make sure everything works as expected.
  • Loading branch information
markusfisch committed May 27, 2020
1 parent ba9970e commit 2525b69
Show file tree
Hide file tree
Showing 3 changed files with 112 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,25 @@ object VTypeParser {
private const val BACKSLASH_R_LEGACY =
"(?:\u000D\u000A|[\u000A\u000B\u000C\u000D\u0085\u2028\u2029])"
private val vTypeRegex =
"""^BEGIN:(VCARD|VEVENT)(?:$BACKSLASH_R_LEGACY.+?:[\s\S]+?)+?${BACKSLASH_R_LEGACY}END:\1$BACKSLASH_R_LEGACY?$""".toRegex(
"""^BEGIN:(V.+)(?:$BACKSLASH_R_LEGACY.+?:[\s\S]+?)+?${BACKSLASH_R_LEGACY}END:\1$BACKSLASH_R_LEGACY?$""".toRegex(
RegexOption.IGNORE_CASE
)
private val propertyRegex = """^(.+?):([\s\S]*?)$""".toRegex(RegexOption.MULTILINE)
private val propertyRegex = """^(.+?):([\s\S]*?)$""".toRegex(
RegexOption.MULTILINE
)

fun parseVType(data: String): String? =
vTypeRegex.matchEntire(data)?.groupValues?.get(1)?.toUpperCase(Locale.US)
fun parseVType(data: String): String? = vTypeRegex.matchEntire(
data
)?.groupValues?.get(1)?.toUpperCase(Locale.US)

fun parseMap(data: String): Map<String, List<VTypeProperty>> = propertyRegex.findAll(data).map {
fun parseMap(
data: String
): Map<String, List<VTypeProperty>> = propertyRegex.findAll(data).map {
it.groupValues[1].split(';').let { typeAndInfo ->
typeAndInfo[0] to VTypeProperty(typeAndInfo.drop(1), it.groupValues[2])
typeAndInfo[0] to VTypeProperty(
typeAndInfo.drop(1),
it.groupValues[2]
)
}
}.groupBy({ it.first.toUpperCase(Locale.US) }) { it.second }
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,11 @@ object VEventAction : IntentAction() {
get() = R.string.vevent_failed

override fun canExecuteOn(data: ByteArray): Boolean {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH
&& VTypeParser.parseVType(String(data)) == "VEVENT"
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
return false
}
val type = VTypeParser.parseVType(String(data))
return type == "VEVENT" || type == "VCALENDAR"
}

@RequiresApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
package de.markusfisch.android.binaryeye.actions.vtype

import de.markusfisch.android.binaryeye.simpleFail
import junit.framework.TestCase.*
import org.junit.Test

class VTypeTest {
@Test
fun vcard() {
val content = """BEGIN:VCARD
N:Doe;John
END:VCARD"""

assertEquals("VCARD", VTypeParser.parseVType(content))

val info = VTypeParser.parseMap(content)

info["N"]?.singleOrNull()?.also {
assertEquals("Doe;John", it.value)
}
}

@Test
fun vcalendar() {
val content = """BEGIN:VCALENDAR
BEGIN:VEVENT
SUMMARY:foo
DTSTART:20080504T123456Z
DTEND:20080505T234555Z
END:VEVENT
END:VCALENDAR"""

assertEquals("VCALENDAR", VTypeParser.parseVType(content))

val info = VTypeParser.parseMap(content)

info["SUMMARY"]?.singleOrNull()?.also {
assertEquals("foo", it.value)
}
info["DTSTART"]?.singleOrNull()?.also {
assertEquals("20080504T123456Z", it.value)
}
info["DTEND"]?.singleOrNull()?.also {
assertEquals("20080505T234555Z", it.value)
}
}

@Test
fun vevent() {
val content = """BEGIN:VEVENT
SUMMARY:foo
DTSTART:20080504T123456Z
DTEND:20080505T234555Z
END:VEVENT"""

assertEquals("VEVENT", VTypeParser.parseVType(content))

val info = VTypeParser.parseMap(content)

info["SUMMARY"]?.singleOrNull()?.also {
assertEquals("foo", it.value)
}
info["DTSTART"]?.singleOrNull()?.also {
assertEquals("20080504T123456Z", it.value)
}
info["DTEND"]?.singleOrNull()?.also {
assertEquals("20080505T234555Z", it.value)
}
}

@Test
fun veventWithCarriageReturn() {
val content = """BEGIN:VEVENT${'\r'}
SUMMARY:foo${'\r'}
DTSTART:20080504T123456Z${'\r'}
DTEND:20080505T234555Z${'\r'}
END:VEVENT"""

assertEquals("VEVENT", VTypeParser.parseVType(content))

val info = VTypeParser.parseMap(content)

info["SUMMARY"]?.singleOrNull()?.also {
assertEquals("foo", it.value)
}
info["DTSTART"]?.singleOrNull()?.also {
assertEquals("20080504T123456Z", it.value)
}
info["DTEND"]?.singleOrNull()?.also {
assertEquals("20080505T234555Z", it.value)
}
}
}

0 comments on commit 2525b69

Please sign in to comment.