From 43fcf0bc2834af2756a9422949393c489c8cc321 Mon Sep 17 00:00:00 2001 From: Arnau Mora Date: Fri, 5 Jul 2024 10:26:10 +0200 Subject: [PATCH 01/17] Got rid of commons dependency Signed-off-by: Arnau Mora --- gradle/libs.versions.toml | 6 ------ lib/build.gradle.kts | 4 ---- 2 files changed, 10 deletions(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 970d5b7..8637141 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -3,10 +3,6 @@ agp = "8.4.0" androidx-annotation = "1.7.1" androidx-test-runner = "1.5.2" androidx-rest-rules = "1.5.0" -# noinspection GradleDependency -commons-io = "2.6" -# noinspection GradleDependency -commons-text = "1.3" desugar = "2.0.4" dokka = "1.9.20" ezvcard = "0.12.1" @@ -17,8 +13,6 @@ junit = "4.13.2" androidx-annotation = { module = "androidx.annotation:annotation", version.ref = "androidx-annotation" } androidx-test-runner = { module = "androidx.test:runner", version.ref = "androidx-test-runner" } androidx-test-rules = { module = "androidx.test:rules", version.ref = "androidx-rest-rules" } -commons-io = { module = "commons-io:commons-io", version.ref = "commons-io" } -commons-text = { module = "org.apache.commons:commons-text", version.ref = "commons-text" } desugar = { module = "com.android.tools:desugar_jdk_libs", version.ref = "desugar" } ezvcard = { module = "com.googlecode.ez-vcard:ez-vcard", version.ref = "ezvcard" } kotlin-stdlib = { module = "org.jetbrains.kotlin:kotlin-stdlib", version.ref = "kotlin" } diff --git a/lib/build.gradle.kts b/lib/build.gradle.kts index 85153be..73d1c02 100644 --- a/lib/build.gradle.kts +++ b/lib/build.gradle.kts @@ -94,10 +94,6 @@ dependencies { coreLibraryDesugaring(libs.desugar) implementation(libs.androidx.annotation) - @Suppress("RedundantSuppression") - implementation(libs.commons.io) - @Suppress("RedundantSuppression") - implementation(libs.commons.text) // ez-vcard to parse/generate vCards api(libs.ezvcard) { // requires Java 8 From 12d29d1ff24c740629acfbf956b07ef004c3472b Mon Sep 17 00:00:00 2001 From: Arnau Mora Date: Fri, 5 Jul 2024 10:42:29 +0200 Subject: [PATCH 02/17] Migrated all commons calls Signed-off-by: Arnau Mora --- .../bitfire/vcard4android/AndroidContact.kt | 6 ++- .../at/bitfire/vcard4android/AndroidGroup.kt | 6 ++- .../java/at/bitfire/vcard4android/Contact.kt | 44 +++++++++++++++---- .../at/bitfire/vcard4android/ContactReader.kt | 29 ++++++------ .../at/bitfire/vcard4android/ContactWriter.kt | 9 +--- .../vcard4android/contactrow/EventHandler.kt | 5 +-- .../contactrow/OrganizationBuilder.kt | 3 +- .../contactrow/RelationBuilder.kt | 6 +-- .../contactrow/RelationHandler.kt | 3 +- .../contactrow/StructuredPostalBuilder.kt | 7 ++- 10 files changed, 67 insertions(+), 51 deletions(-) diff --git a/lib/src/main/java/at/bitfire/vcard4android/AndroidContact.kt b/lib/src/main/java/at/bitfire/vcard4android/AndroidContact.kt index d3d7350..f6ef4ea 100644 --- a/lib/src/main/java/at/bitfire/vcard4android/AndroidContact.kt +++ b/lib/src/main/java/at/bitfire/vcard4android/AndroidContact.kt @@ -16,7 +16,6 @@ import android.provider.ContactsContract.RawContacts.Data import androidx.annotation.CallSuper import at.bitfire.vcard4android.contactrow.ContactProcessor import at.bitfire.vcard4android.contactrow.PhotoBuilder -import org.apache.commons.lang3.builder.ToStringBuilder import java.io.FileNotFoundException open class AndroidContact( @@ -214,6 +213,9 @@ open class AndroidContact( protected fun dataSyncURI() = addressBook.syncAdapterURI(ContactsContract.Data.CONTENT_URI) - override fun toString() = ToStringBuilder.reflectionToString(this)!! + override fun toString() = mapOf("id" to id, "fileName" to fileName, "eTag" to eTag) + .toList() + .joinToString(",") { (k, v) -> "$k=$v" } + .let { "AndroidContact[$it]" } } \ No newline at end of file diff --git a/lib/src/main/java/at/bitfire/vcard4android/AndroidGroup.kt b/lib/src/main/java/at/bitfire/vcard4android/AndroidGroup.kt index 16b3428..01e034e 100644 --- a/lib/src/main/java/at/bitfire/vcard4android/AndroidGroup.kt +++ b/lib/src/main/java/at/bitfire/vcard4android/AndroidGroup.kt @@ -14,7 +14,6 @@ import android.provider.ContactsContract.Groups import android.provider.ContactsContract.RawContacts import android.provider.ContactsContract.RawContacts.Data import androidx.annotation.CallSuper -import org.apache.commons.lang3.builder.ToStringBuilder import java.io.FileNotFoundException open class AndroidGroup( @@ -164,6 +163,9 @@ open class AndroidGroup( return addressBook.syncAdapterURI(ContentUris.withAppendedId(Groups.CONTENT_URI, id)) } - override fun toString() = ToStringBuilder.reflectionToString(this)!! + override fun toString() = mapOf("id" to id, "fileName" to fileName, "eTag" to eTag) + .toList() + .joinToString(",") { (k, v) -> "$k=$v" } + .let { "AndroidGroup[$it]" } } diff --git a/lib/src/main/java/at/bitfire/vcard4android/Contact.kt b/lib/src/main/java/at/bitfire/vcard4android/Contact.kt index d266a39..080bd4b 100644 --- a/lib/src/main/java/at/bitfire/vcard4android/Contact.kt +++ b/lib/src/main/java/at/bitfire/vcard4android/Contact.kt @@ -10,8 +10,6 @@ import ezvcard.VCardVersion import ezvcard.io.json.JCardReader import ezvcard.io.text.VCardReader import ezvcard.property.* -import org.apache.commons.lang3.builder.HashCodeBuilder -import org.apache.commons.lang3.builder.ReflectionToStringBuilder import java.io.IOException import java.io.OutputStream import java.io.Reader @@ -144,8 +142,7 @@ class Contact { ) override fun hashCode(): Int { - val builder = HashCodeBuilder(29, 3).append(compareFields()) - return builder.toHashCode() + return compareFields().sumOf { 29 * 3 + it.hashCode() } } override fun equals(other: Any?) = @@ -154,11 +151,40 @@ class Contact { else false - override fun toString(): String { - val builder = ReflectionToStringBuilder(this) - builder.setExcludeFieldNames("photo") - return builder.toString() - } + override fun toString(): String = + mapOf( + "uid" to uid, + "group" to group, + "members" to members, + "displayName" to displayName, + "prefix" to prefix, + "givenName" to givenName, + "middleName" to middleName, + "familyName" to familyName, + "suffix" to suffix, + "phoneticGivenName" to phoneticGivenName, + "phoneticMiddleName" to phoneticMiddleName, + "phoneticFamilyName" to phoneticFamilyName, + "nickName" to nickName, + "organization" to organization, + "jobTitle" to jobTitle, + "jobDescription" to jobDescription, + "phoneNumbers" to phoneNumbers, + "emails" to emails, + "impps" to impps, + "addresses" to addresses, + "categories" to categories, + "urls" to urls, + "relations" to relations, + "note" to note, + "anniversary" to anniversary, + "birthDay" to birthDay, + "customDates" to customDates, + "unknownProperties" to unknownProperties + ) + .toList() + .joinToString(",") { (k, v) -> "$k=$v" } + .let { "Contact[$it]" } interface Downloader { diff --git a/lib/src/main/java/at/bitfire/vcard4android/ContactReader.kt b/lib/src/main/java/at/bitfire/vcard4android/ContactReader.kt index 373f993..67cb943 100644 --- a/lib/src/main/java/at/bitfire/vcard4android/ContactReader.kt +++ b/lib/src/main/java/at/bitfire/vcard4android/ContactReader.kt @@ -11,7 +11,6 @@ import ezvcard.VCard import ezvcard.parameter.RelatedType import ezvcard.property.* import ezvcard.util.PartialDate -import org.apache.commons.lang3.StringUtils import java.net.URI import java.net.URISyntaxException import java.time.Instant @@ -105,7 +104,7 @@ class ContactReader internal constructor(val vCard: VCard, val downloader: Conta uriString } - return StringUtils.trimToNull(uid) + return uid.trim().takeIf { it.isNotBlank() } } } @@ -131,20 +130,20 @@ class ContactReader internal constructor(val vCard: VCard, val downloader: Conta uriToUid(prop.uri)?.let { c.members += it } is FormattedName -> - c.displayName = StringUtils.trimToNull(prop.value) + c.displayName = prop.value.trim().takeIf { it.isNotBlank() } is StructuredName -> { - c.prefix = StringUtils.trimToNull(prop.prefixes.joinToString(" ")) - c.givenName = StringUtils.trimToNull(prop.given) - c.middleName = StringUtils.trimToNull(prop.additionalNames.joinToString(" ")) - c.familyName = StringUtils.trimToNull(prop.family) - c.suffix = StringUtils.trimToNull(prop.suffixes.joinToString(" ")) + c.prefix = prop.prefixes.joinToString(" ").trim().takeIf { it.isNotBlank() } + c.givenName = prop.given.trim().takeIf { it.isNotBlank() } + c.middleName = prop.additionalNames.joinToString(" ").trim().takeIf { it.isNotBlank() } + c.familyName = prop.family.trim().takeIf { it.isNotBlank() } + c.suffix = prop.suffixes.joinToString(" ").trim().takeIf { it.isNotBlank() } } is XPhoneticFirstName -> - c.phoneticGivenName = StringUtils.trimToNull(prop.value) + c.phoneticGivenName = prop.value.trim().takeIf { it.isNotBlank() } is XPhoneticMiddleName -> - c.phoneticMiddleName = StringUtils.trimToNull(prop.value) + c.phoneticMiddleName = prop.value.trim().takeIf { it.isNotBlank() } is XPhoneticLastName -> - c.phoneticFamilyName = StringUtils.trimToNull(prop.value) + c.phoneticFamilyName = prop.value.trim().takeIf { it.isNotBlank() } is Nickname -> c.nickName = LabeledProperty(prop, findAndRemoveLabel(prop.group)) @@ -154,9 +153,9 @@ class ContactReader internal constructor(val vCard: VCard, val downloader: Conta is Organization -> c.organization = prop is Title -> - c.jobTitle = StringUtils.trimToNull(prop.value) + c.jobTitle = prop.value.trim().takeIf { it.isNotBlank() } is Role -> - c.jobDescription = StringUtils.trimToNull(prop.value) + c.jobDescription = prop.value.trim().takeIf { it.isNotBlank() } is Telephone -> if (!prop.text.isNullOrBlank()) @@ -252,7 +251,7 @@ class ContactReader internal constructor(val vCard: VCard, val downloader: Conta } is Note -> { - StringUtils.trimToNull(prop.value)?.let { note -> + prop.value.trim().takeIf { it.isNotBlank() }?.let { note -> if (c.note == null) c.note = note else @@ -324,7 +323,7 @@ class ContactReader internal constructor(val vCard: VCard, val downloader: Conta for (label in vCard.getProperties(XAbLabel::class.java)) { if (label.group.equals(group, true)) { vCard.removeProperty(label) - return StringUtils.trimToNull(label.value) + return label.value.trim().takeIf { it.isNotBlank() } } } diff --git a/lib/src/main/java/at/bitfire/vcard4android/ContactWriter.kt b/lib/src/main/java/at/bitfire/vcard4android/ContactWriter.kt index 9b6a557..fd055f9 100644 --- a/lib/src/main/java/at/bitfire/vcard4android/ContactWriter.kt +++ b/lib/src/main/java/at/bitfire/vcard4android/ContactWriter.kt @@ -15,13 +15,8 @@ import ezvcard.io.text.VCardWriter import ezvcard.parameter.ImageType import ezvcard.parameter.RelatedType import ezvcard.property.* -import org.apache.commons.lang3.StringUtils -import org.apache.commons.text.WordUtils import java.io.OutputStream -import java.time.Instant import java.time.LocalDate -import java.time.LocalDateTime -import java.time.temporal.ChronoField import java.util.* import java.util.logging.Level @@ -128,7 +123,7 @@ class ContactWriter private constructor(val contact: Contact, val version: VCard // vCard 4 REQUIRES FN [RFC 6350 6.2.1 FN] val fn = // use display name, if available - StringUtils.trimToNull(contact.displayName) ?: + contact.displayName?.trim()?.takeIf { it.isNotBlank() } ?: // no display name, try organization contact.organization?.values?.joinToString(" / ") ?: // otherwise, try nickname @@ -201,7 +196,7 @@ class ContactWriter private constructor(val contact: Contact, val version: VCard if (relation.types.isEmpty()) name.addParameter("TYPE", "other") else - label = relation.types.joinToString(", ") { type -> WordUtils.capitalize(type.value) } + label = relation.types.joinToString(", ") { type -> type.value.uppercase() } } } diff --git a/lib/src/main/java/at/bitfire/vcard4android/contactrow/EventHandler.kt b/lib/src/main/java/at/bitfire/vcard4android/contactrow/EventHandler.kt index cd6a282..6a5427f 100644 --- a/lib/src/main/java/at/bitfire/vcard4android/contactrow/EventHandler.kt +++ b/lib/src/main/java/at/bitfire/vcard4android/contactrow/EventHandler.kt @@ -13,9 +13,6 @@ import at.bitfire.vcard4android.property.XAbDate import ezvcard.property.Anniversary import ezvcard.property.Birthday import ezvcard.util.PartialDate -import org.apache.commons.lang3.StringUtils -import java.text.ParseException -import java.text.SimpleDateFormat import java.time.LocalDate import java.time.format.DateTimeParseException import java.util.* @@ -51,7 +48,7 @@ object EventHandler: DataRowHandler() { Event.TYPE_CUSTOM */ else -> { val abDate = if (full != null) XAbDate(full) else XAbDate(partial) - val label = StringUtils.trimToNull(values.getAsString(Event.LABEL)) + val label = values.getAsString(Event.LABEL).trim().takeIf { it.isNotBlank() } contact.customDates += LabeledProperty(abDate, label) } } diff --git a/lib/src/main/java/at/bitfire/vcard4android/contactrow/OrganizationBuilder.kt b/lib/src/main/java/at/bitfire/vcard4android/contactrow/OrganizationBuilder.kt index aa0507a..06b8ee4 100644 --- a/lib/src/main/java/at/bitfire/vcard4android/contactrow/OrganizationBuilder.kt +++ b/lib/src/main/java/at/bitfire/vcard4android/contactrow/OrganizationBuilder.kt @@ -8,7 +8,6 @@ import android.net.Uri import android.provider.ContactsContract.CommonDataKinds.Organization import at.bitfire.vcard4android.BatchOperation import at.bitfire.vcard4android.Contact -import org.apache.commons.lang3.StringUtils import java.util.* class OrganizationBuilder(dataRowUri: Uri, rawContactId: Long?, contact: Contact, readOnly: Boolean) @@ -25,7 +24,7 @@ class OrganizationBuilder(dataRowUri: Uri, rawContactId: Long?, contact: Contact val depts = LinkedList() while (org.hasNext()) depts += org.next() - department = StringUtils.trimToNull(depts.joinToString(" / ")) + department = depts.joinToString(" / ").trim().takeIf { it.isNotBlank() } } if (company == null && department == null && contact.jobTitle == null && contact.jobDescription == null) diff --git a/lib/src/main/java/at/bitfire/vcard4android/contactrow/RelationBuilder.kt b/lib/src/main/java/at/bitfire/vcard4android/contactrow/RelationBuilder.kt index d746fcd..5661a2b 100644 --- a/lib/src/main/java/at/bitfire/vcard4android/contactrow/RelationBuilder.kt +++ b/lib/src/main/java/at/bitfire/vcard4android/contactrow/RelationBuilder.kt @@ -10,8 +10,6 @@ import at.bitfire.vcard4android.BatchOperation import at.bitfire.vcard4android.Contact import at.bitfire.vcard4android.property.CustomType import ezvcard.parameter.RelatedType -import org.apache.commons.lang3.StringUtils -import org.apache.commons.text.WordUtils import java.util.* class RelationBuilder(dataRowUri: Uri, rawContactId: Long?, contact: Contact, readOnly: Boolean) @@ -20,7 +18,7 @@ class RelationBuilder(dataRowUri: Uri, rawContactId: Long?, contact: Contact, re override fun build(): List { val result = LinkedList() for (related in contact.relations) { - val name = StringUtils.trimToNull(related.text) ?: StringUtils.trimToNull(related.uri) + val name = related.text.trim().takeIf { it.isNotBlank() } ?: related.uri.trim().takeIf { it.isNotBlank() } if (name.isNullOrBlank()) continue @@ -55,7 +53,7 @@ class RelationBuilder(dataRowUri: Uri, rawContactId: Long?, contact: Contact, re if (related.types.isEmpty()) related.types += CustomType.Related.OTHER - val types = related.types.map { type -> WordUtils.capitalize(type.value) } + val types = related.types.map { type -> type.value.uppercase() } val typesStr = types.joinToString(", ") builder.withValue(Relation.LABEL, typesStr) } diff --git a/lib/src/main/java/at/bitfire/vcard4android/contactrow/RelationHandler.kt b/lib/src/main/java/at/bitfire/vcard4android/contactrow/RelationHandler.kt index 69a36bc..91ec5f0 100644 --- a/lib/src/main/java/at/bitfire/vcard4android/contactrow/RelationHandler.kt +++ b/lib/src/main/java/at/bitfire/vcard4android/contactrow/RelationHandler.kt @@ -10,7 +10,6 @@ import at.bitfire.vcard4android.Contact import at.bitfire.vcard4android.property.CustomType import ezvcard.parameter.RelatedType import ezvcard.property.Related -import org.apache.commons.lang3.StringUtils object RelationHandler: DataRowHandler() { @@ -67,7 +66,7 @@ object RelationHandler: DataRowHandler() { Relation.TYPE_SPOUSE -> related.types += RelatedType.SPOUSE Relation.TYPE_CUSTOM -> - StringUtils.trimToNull(values.getAsString(Relation.LABEL))?.let { typeStr -> + values.getAsString(Relation.LABEL)?.trim()?.takeIf { it.isNotBlank() }?.let { typeStr -> for (type in typeStr.split(',')) related.types += RelatedType.get(type.lowercase().trim()) } diff --git a/lib/src/main/java/at/bitfire/vcard4android/contactrow/StructuredPostalBuilder.kt b/lib/src/main/java/at/bitfire/vcard4android/contactrow/StructuredPostalBuilder.kt index 698f8e0..6447bde 100644 --- a/lib/src/main/java/at/bitfire/vcard4android/contactrow/StructuredPostalBuilder.kt +++ b/lib/src/main/java/at/bitfire/vcard4android/contactrow/StructuredPostalBuilder.kt @@ -9,7 +9,6 @@ import android.provider.ContactsContract.CommonDataKinds.StructuredPostal import at.bitfire.vcard4android.BatchOperation import at.bitfire.vcard4android.Contact import ezvcard.parameter.AddressType -import org.apache.commons.lang3.StringUtils import java.util.* /** @@ -40,14 +39,14 @@ class StructuredPostalBuilder(dataRowUri: Uri, rawContactId: Long?, contact: Con lines += extended val postalAndCity = LinkedList() - if (StringUtils.trimToNull(address.postalCode) != null) + if (address.postalCode.trim().takeIf { it.isNotBlank() } != null) postalAndCity += address.postalCodes.joinToString(" / ") - if (StringUtils.trimToNull(address.locality) != null) + if (address.locality.trim().takeIf { it.isNotBlank() } != null) postalAndCity += address.localities.joinToString(" / ") if (postalAndCity.isNotEmpty()) lines += postalAndCity.joinToString(" ") - if (StringUtils.trimToNull(address.country) != null) { + if (address.country.trim().takeIf { it.isNotBlank() } != null) { val line = StringBuilder(address.countries.joinToString(" / ")) if (!address.region.isNullOrBlank()) { val regions = address.regions.joinToString(" / ") From ff9f8ed5220d7bbe467f642665f3f16069e7eaea Mon Sep 17 00:00:00 2001 From: Arnau Mora Date: Fri, 5 Jul 2024 10:42:39 +0200 Subject: [PATCH 03/17] Added `byteCountToDisplaySize` Signed-off-by: Arnau Mora --- .../main/java/at/bitfire/vcard4android/Utils.kt | 16 ++++++++++++++++ .../vcard4android/contactrow/PhotoBuilder.kt | 4 ++-- .../vcard4android/contactrow/PhotoHandler.kt | 9 ++++----- 3 files changed, 22 insertions(+), 7 deletions(-) diff --git a/lib/src/main/java/at/bitfire/vcard4android/Utils.kt b/lib/src/main/java/at/bitfire/vcard4android/Utils.kt index 6cec2be..929a943 100644 --- a/lib/src/main/java/at/bitfire/vcard4android/Utils.kt +++ b/lib/src/main/java/at/bitfire/vcard4android/Utils.kt @@ -29,4 +29,20 @@ object Utils { .appendQueryParameter(ContactsContract.CALLER_IS_SYNCADAPTER, "true") .build() + fun Long.byteCountToDisplaySize(): String { + val gb = this / 1_073_741_824 // 2^30 + if (gb > 0) { + return "$gb GB" + } + val mb = this / 1_048_576 // 2^20 + if (mb > 0) { + return "$mb MB" + } + val kb = this / 1024 // 2^10 + if (kb > 0) { + return "$kb KB" + } + return "$this B" + } + } \ No newline at end of file diff --git a/lib/src/main/java/at/bitfire/vcard4android/contactrow/PhotoBuilder.kt b/lib/src/main/java/at/bitfire/vcard4android/contactrow/PhotoBuilder.kt index 4bf4b40..7f0f62c 100644 --- a/lib/src/main/java/at/bitfire/vcard4android/contactrow/PhotoBuilder.kt +++ b/lib/src/main/java/at/bitfire/vcard4android/contactrow/PhotoBuilder.kt @@ -16,7 +16,7 @@ import at.bitfire.vcard4android.BatchOperation import at.bitfire.vcard4android.Constants import at.bitfire.vcard4android.Contact import at.bitfire.vcard4android.Utils.asSyncAdapter -import org.apache.commons.io.FileUtils +import at.bitfire.vcard4android.Utils.byteCountToDisplaySize import java.io.IOException import java.util.logging.Level @@ -56,7 +56,7 @@ class PhotoBuilder(dataRowUri: Uri, rawContactId: Long?, contact: Contact, readO .appendPath(rawContactId.toString()) .appendPath(RawContacts.DisplayPhoto.CONTENT_DIRECTORY) .build() - Constants.log.log(Level.FINE, "Writing photo to $uri (${FileUtils.byteCountToDisplaySize(data.size.toLong())})") + Constants.log.log(Level.FINE, "Writing photo to $uri (${data.size.toLong().byteCountToDisplaySize()})") provider.openAssetFile(uri, "w")?.use { fd -> try { fd.createOutputStream()?.use { os -> diff --git a/lib/src/main/java/at/bitfire/vcard4android/contactrow/PhotoHandler.kt b/lib/src/main/java/at/bitfire/vcard4android/contactrow/PhotoHandler.kt index d807f85..4033b48 100644 --- a/lib/src/main/java/at/bitfire/vcard4android/contactrow/PhotoHandler.kt +++ b/lib/src/main/java/at/bitfire/vcard4android/contactrow/PhotoHandler.kt @@ -13,8 +13,7 @@ import android.provider.ContactsContract import android.provider.ContactsContract.CommonDataKinds.Photo import at.bitfire.vcard4android.Constants import at.bitfire.vcard4android.Contact -import org.apache.commons.io.FileUtils -import org.apache.commons.io.IOUtils +import at.bitfire.vcard4android.Utils.byteCountToDisplaySize import java.io.ByteArrayOutputStream import java.io.IOException import java.util.logging.Level @@ -73,8 +72,8 @@ class PhotoHandler(val provider: ContentProviderClient?): DataRowHandler() { provider?.openAssetFile(photoUri, "r")?.let { file -> file.createInputStream().use { // Samsung Android 12 bug: they return a PNG image with MIME type image/jpeg - convertToJpeg(IOUtils.toByteArray(it), 75)?.let { jpeg -> - Constants.log.log(Level.FINE, "Got high-res contact photo (${FileUtils.byteCountToDisplaySize(jpeg.size.toLong())})") + convertToJpeg(it.readBytes(), 75)?.let { jpeg -> + Constants.log.log(Level.FINE, "Got high-res contact photo (${jpeg.size.toLong().byteCountToDisplaySize()})") contact.photo = jpeg } } @@ -88,7 +87,7 @@ class PhotoHandler(val provider: ContentProviderClient?): DataRowHandler() { values.getAsByteArray(Photo.PHOTO)?.let { thumbnail -> // Samsung Android 12 bug: even the thumbnail is a PNG image convertToJpeg(thumbnail, 95)?.let { jpeg -> - Constants.log.log(Level.FINE, "Got contact photo thumbnail (${FileUtils.byteCountToDisplaySize(jpeg.size.toLong())})") + Constants.log.log(Level.FINE, "Got contact photo thumbnail (${jpeg.size.toLong().byteCountToDisplaySize()})") contact.photo = jpeg } } From 20a24d7a6b9a8325a14e5b7f285502e68ae45a3f Mon Sep 17 00:00:00 2001 From: Arnau Mora Date: Fri, 5 Jul 2024 10:44:27 +0200 Subject: [PATCH 04/17] Added `resourceToByteArray` Signed-off-by: Arnau Mora --- .../androidTest/java/at/bitfire/vcard4android/IOUtils.kt | 6 ++++++ .../at/bitfire/vcard4android/contactrow/PhotoBuilderTest.kt | 2 +- .../at/bitfire/vcard4android/contactrow/PhotoHandlerTest.kt | 2 +- 3 files changed, 8 insertions(+), 2 deletions(-) create mode 100644 lib/src/androidTest/java/at/bitfire/vcard4android/IOUtils.kt diff --git a/lib/src/androidTest/java/at/bitfire/vcard4android/IOUtils.kt b/lib/src/androidTest/java/at/bitfire/vcard4android/IOUtils.kt new file mode 100644 index 0000000..63c5fb2 --- /dev/null +++ b/lib/src/androidTest/java/at/bitfire/vcard4android/IOUtils.kt @@ -0,0 +1,6 @@ +package at.bitfire.vcard4android + +object IOUtils { + fun resourceToByteArray(resource: String): ByteArray = + this::class.java.getResourceAsStream(resource)!!.use { it.readBytes() } +} diff --git a/lib/src/androidTest/java/at/bitfire/vcard4android/contactrow/PhotoBuilderTest.kt b/lib/src/androidTest/java/at/bitfire/vcard4android/contactrow/PhotoBuilderTest.kt index bc8d607..c79e59c 100644 --- a/lib/src/androidTest/java/at/bitfire/vcard4android/contactrow/PhotoBuilderTest.kt +++ b/lib/src/androidTest/java/at/bitfire/vcard4android/contactrow/PhotoBuilderTest.kt @@ -16,8 +16,8 @@ import androidx.test.platform.app.InstrumentationRegistry import androidx.test.rule.GrantPermissionRule import at.bitfire.vcard4android.AndroidContact import at.bitfire.vcard4android.Contact +import at.bitfire.vcard4android.IOUtils import at.bitfire.vcard4android.impl.TestAddressBook -import org.apache.commons.io.IOUtils import org.junit.Assert.* import org.junit.BeforeClass import org.junit.ClassRule diff --git a/lib/src/androidTest/java/at/bitfire/vcard4android/contactrow/PhotoHandlerTest.kt b/lib/src/androidTest/java/at/bitfire/vcard4android/contactrow/PhotoHandlerTest.kt index 004995e..1ab1f0b 100644 --- a/lib/src/androidTest/java/at/bitfire/vcard4android/contactrow/PhotoHandlerTest.kt +++ b/lib/src/androidTest/java/at/bitfire/vcard4android/contactrow/PhotoHandlerTest.kt @@ -16,8 +16,8 @@ import androidx.test.platform.app.InstrumentationRegistry import androidx.test.rule.GrantPermissionRule import at.bitfire.vcard4android.AndroidContact import at.bitfire.vcard4android.Contact +import at.bitfire.vcard4android.IOUtils import at.bitfire.vcard4android.impl.TestAddressBook -import org.apache.commons.io.IOUtils import org.junit.Assert import org.junit.Assert.* import org.junit.BeforeClass From 7e1675aaf12608c840ea98bad76ff58ee16686f1 Mon Sep 17 00:00:00 2001 From: Arnau Mora Date: Fri, 5 Jul 2024 10:49:43 +0200 Subject: [PATCH 05/17] Improved null-handling Signed-off-by: Arnau Mora --- .../at/bitfire/vcard4android/ContactReader.kt | 22 +++++++++---------- .../vcard4android/contactrow/EventHandler.kt | 2 +- .../contactrow/OrganizationBuilder.kt | 2 +- .../contactrow/RelationBuilder.kt | 2 +- .../contactrow/StructuredPostalBuilder.kt | 6 ++--- 5 files changed, 17 insertions(+), 17 deletions(-) diff --git a/lib/src/main/java/at/bitfire/vcard4android/ContactReader.kt b/lib/src/main/java/at/bitfire/vcard4android/ContactReader.kt index 67cb943..1f905a3 100644 --- a/lib/src/main/java/at/bitfire/vcard4android/ContactReader.kt +++ b/lib/src/main/java/at/bitfire/vcard4android/ContactReader.kt @@ -104,7 +104,7 @@ class ContactReader internal constructor(val vCard: VCard, val downloader: Conta uriString } - return uid.trim().takeIf { it.isNotBlank() } + return uid?.trim()?.takeIf { it.isNotBlank() } } } @@ -130,20 +130,20 @@ class ContactReader internal constructor(val vCard: VCard, val downloader: Conta uriToUid(prop.uri)?.let { c.members += it } is FormattedName -> - c.displayName = prop.value.trim().takeIf { it.isNotBlank() } + c.displayName = prop.value?.trim()?.takeIf { it.isNotBlank() } is StructuredName -> { c.prefix = prop.prefixes.joinToString(" ").trim().takeIf { it.isNotBlank() } - c.givenName = prop.given.trim().takeIf { it.isNotBlank() } + c.givenName = prop.given?.trim()?.takeIf { it.isNotBlank() } c.middleName = prop.additionalNames.joinToString(" ").trim().takeIf { it.isNotBlank() } - c.familyName = prop.family.trim().takeIf { it.isNotBlank() } + c.familyName = prop.family?.trim()?.takeIf { it.isNotBlank() } c.suffix = prop.suffixes.joinToString(" ").trim().takeIf { it.isNotBlank() } } is XPhoneticFirstName -> - c.phoneticGivenName = prop.value.trim().takeIf { it.isNotBlank() } + c.phoneticGivenName = prop.value?.trim()?.takeIf { it.isNotBlank() } is XPhoneticMiddleName -> - c.phoneticMiddleName = prop.value.trim().takeIf { it.isNotBlank() } + c.phoneticMiddleName = prop.value?.trim()?.takeIf { it.isNotBlank() } is XPhoneticLastName -> - c.phoneticFamilyName = prop.value.trim().takeIf { it.isNotBlank() } + c.phoneticFamilyName = prop.value?.trim()?.takeIf { it.isNotBlank() } is Nickname -> c.nickName = LabeledProperty(prop, findAndRemoveLabel(prop.group)) @@ -153,9 +153,9 @@ class ContactReader internal constructor(val vCard: VCard, val downloader: Conta is Organization -> c.organization = prop is Title -> - c.jobTitle = prop.value.trim().takeIf { it.isNotBlank() } + c.jobTitle = prop.value?.trim()?.takeIf { it.isNotBlank() } is Role -> - c.jobDescription = prop.value.trim().takeIf { it.isNotBlank() } + c.jobDescription = prop.value?.trim()?.takeIf { it.isNotBlank() } is Telephone -> if (!prop.text.isNullOrBlank()) @@ -251,7 +251,7 @@ class ContactReader internal constructor(val vCard: VCard, val downloader: Conta } is Note -> { - prop.value.trim().takeIf { it.isNotBlank() }?.let { note -> + prop.value?.trim()?.takeIf { it.isNotBlank() }?.let { note -> if (c.note == null) c.note = note else @@ -323,7 +323,7 @@ class ContactReader internal constructor(val vCard: VCard, val downloader: Conta for (label in vCard.getProperties(XAbLabel::class.java)) { if (label.group.equals(group, true)) { vCard.removeProperty(label) - return label.value.trim().takeIf { it.isNotBlank() } + return label.value?.trim()?.takeIf { it.isNotBlank() } } } diff --git a/lib/src/main/java/at/bitfire/vcard4android/contactrow/EventHandler.kt b/lib/src/main/java/at/bitfire/vcard4android/contactrow/EventHandler.kt index 6a5427f..047c5b9 100644 --- a/lib/src/main/java/at/bitfire/vcard4android/contactrow/EventHandler.kt +++ b/lib/src/main/java/at/bitfire/vcard4android/contactrow/EventHandler.kt @@ -48,7 +48,7 @@ object EventHandler: DataRowHandler() { Event.TYPE_CUSTOM */ else -> { val abDate = if (full != null) XAbDate(full) else XAbDate(partial) - val label = values.getAsString(Event.LABEL).trim().takeIf { it.isNotBlank() } + val label = values.getAsString(Event.LABEL)?.trim()?.takeIf { it.isNotBlank() } contact.customDates += LabeledProperty(abDate, label) } } diff --git a/lib/src/main/java/at/bitfire/vcard4android/contactrow/OrganizationBuilder.kt b/lib/src/main/java/at/bitfire/vcard4android/contactrow/OrganizationBuilder.kt index 06b8ee4..d6dabcf 100644 --- a/lib/src/main/java/at/bitfire/vcard4android/contactrow/OrganizationBuilder.kt +++ b/lib/src/main/java/at/bitfire/vcard4android/contactrow/OrganizationBuilder.kt @@ -24,7 +24,7 @@ class OrganizationBuilder(dataRowUri: Uri, rawContactId: Long?, contact: Contact val depts = LinkedList() while (org.hasNext()) depts += org.next() - department = depts.joinToString(" / ").trim().takeIf { it.isNotBlank() } + department = depts.joinToString(" / ").trim().takeIf { t -> t.isNotBlank() } } if (company == null && department == null && contact.jobTitle == null && contact.jobDescription == null) diff --git a/lib/src/main/java/at/bitfire/vcard4android/contactrow/RelationBuilder.kt b/lib/src/main/java/at/bitfire/vcard4android/contactrow/RelationBuilder.kt index 5661a2b..1247147 100644 --- a/lib/src/main/java/at/bitfire/vcard4android/contactrow/RelationBuilder.kt +++ b/lib/src/main/java/at/bitfire/vcard4android/contactrow/RelationBuilder.kt @@ -18,7 +18,7 @@ class RelationBuilder(dataRowUri: Uri, rawContactId: Long?, contact: Contact, re override fun build(): List { val result = LinkedList() for (related in contact.relations) { - val name = related.text.trim().takeIf { it.isNotBlank() } ?: related.uri.trim().takeIf { it.isNotBlank() } + val name = related.text?.trim()?.takeIf { it.isNotBlank() } ?: related.uri?.trim()?.takeIf { it.isNotBlank() } if (name.isNullOrBlank()) continue diff --git a/lib/src/main/java/at/bitfire/vcard4android/contactrow/StructuredPostalBuilder.kt b/lib/src/main/java/at/bitfire/vcard4android/contactrow/StructuredPostalBuilder.kt index 6447bde..616e651 100644 --- a/lib/src/main/java/at/bitfire/vcard4android/contactrow/StructuredPostalBuilder.kt +++ b/lib/src/main/java/at/bitfire/vcard4android/contactrow/StructuredPostalBuilder.kt @@ -39,14 +39,14 @@ class StructuredPostalBuilder(dataRowUri: Uri, rawContactId: Long?, contact: Con lines += extended val postalAndCity = LinkedList() - if (address.postalCode.trim().takeIf { it.isNotBlank() } != null) + if (address.postalCode?.trim()?.takeIf { it.isNotBlank() } != null) postalAndCity += address.postalCodes.joinToString(" / ") - if (address.locality.trim().takeIf { it.isNotBlank() } != null) + if (address.locality?.trim()?.takeIf { it.isNotBlank() } != null) postalAndCity += address.localities.joinToString(" / ") if (postalAndCity.isNotEmpty()) lines += postalAndCity.joinToString(" ") - if (address.country.trim().takeIf { it.isNotBlank() } != null) { + if (address.country?.trim()?.takeIf { it.isNotBlank() } != null) { val line = StringBuilder(address.countries.joinToString(" / ")) if (!address.region.isNullOrBlank()) { val regions = address.regions.joinToString(" / ") From b0526d57d205d468c7fb640733185bf69b7751ab Mon Sep 17 00:00:00 2001 From: Arnau Mora Date: Fri, 5 Jul 2024 10:52:06 +0200 Subject: [PATCH 06/17] Typo Signed-off-by: Arnau Mora --- lib/src/main/java/at/bitfire/vcard4android/ContactWriter.kt | 6 +++++- .../at/bitfire/vcard4android/contactrow/RelationBuilder.kt | 6 +++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/lib/src/main/java/at/bitfire/vcard4android/ContactWriter.kt b/lib/src/main/java/at/bitfire/vcard4android/ContactWriter.kt index fd055f9..2b78efe 100644 --- a/lib/src/main/java/at/bitfire/vcard4android/ContactWriter.kt +++ b/lib/src/main/java/at/bitfire/vcard4android/ContactWriter.kt @@ -196,7 +196,11 @@ class ContactWriter private constructor(val contact: Contact, val version: VCard if (relation.types.isEmpty()) name.addParameter("TYPE", "other") else - label = relation.types.joinToString(", ") { type -> type.value.uppercase() } + label = relation.types.joinToString(", ") { type -> + type.value.replaceFirstChar { + if (it.isLowerCase()) it.titlecase(Locale.getDefault()) else it.toString() + } + } } } diff --git a/lib/src/main/java/at/bitfire/vcard4android/contactrow/RelationBuilder.kt b/lib/src/main/java/at/bitfire/vcard4android/contactrow/RelationBuilder.kt index 1247147..c511db6 100644 --- a/lib/src/main/java/at/bitfire/vcard4android/contactrow/RelationBuilder.kt +++ b/lib/src/main/java/at/bitfire/vcard4android/contactrow/RelationBuilder.kt @@ -53,7 +53,11 @@ class RelationBuilder(dataRowUri: Uri, rawContactId: Long?, contact: Contact, re if (related.types.isEmpty()) related.types += CustomType.Related.OTHER - val types = related.types.map { type -> type.value.uppercase() } + val types = related.types.map { type -> + type.value.replaceFirstChar { + if (it.isLowerCase()) it.titlecase(Locale.getDefault()) else it.toString() + } + } val typesStr = types.joinToString(", ") builder.withValue(Relation.LABEL, typesStr) } From 9c166b1365495a33d691164179c54e9caeb88271 Mon Sep 17 00:00:00 2001 From: Arnau Mora Date: Fri, 5 Jul 2024 11:08:47 +0200 Subject: [PATCH 07/17] Replaced commons usage Signed-off-by: Arnau Mora --- lib/src/test/java/at/bitfire/vcard4android/ContactTest.kt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/src/test/java/at/bitfire/vcard4android/ContactTest.kt b/lib/src/test/java/at/bitfire/vcard4android/ContactTest.kt index 2fa354a..72cdafb 100644 --- a/lib/src/test/java/at/bitfire/vcard4android/ContactTest.kt +++ b/lib/src/test/java/at/bitfire/vcard4android/ContactTest.kt @@ -12,7 +12,6 @@ import ezvcard.parameter.RelatedType import ezvcard.parameter.TelephoneType import ezvcard.property.Birthday import ezvcard.util.PartialDate -import org.apache.commons.io.IOUtils import org.junit.Assert.assertArrayEquals import org.junit.Assert.assertEquals import org.junit.Assert.assertFalse @@ -202,7 +201,7 @@ class ContactTest { // PHOTO javaClass.classLoader!!.getResourceAsStream("lol.jpg").use { photo -> - assertArrayEquals(IOUtils.toByteArray(photo), c.photo) + assertArrayEquals(photo.readBytes(), c.photo) } } From 71e9b663fd29a472697d88e1a410871f32195ae6 Mon Sep 17 00:00:00 2001 From: Arnau Mora Date: Fri, 5 Jul 2024 11:12:20 +0200 Subject: [PATCH 08/17] Added capitalize utility Signed-off-by: Arnau Mora --- .../main/java/at/bitfire/vcard4android/ContactWriter.kt | 5 ++--- lib/src/main/java/at/bitfire/vcard4android/Utils.kt | 7 +++++++ .../at/bitfire/vcard4android/contactrow/RelationBuilder.kt | 7 ++----- 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/lib/src/main/java/at/bitfire/vcard4android/ContactWriter.kt b/lib/src/main/java/at/bitfire/vcard4android/ContactWriter.kt index 2b78efe..edec5e0 100644 --- a/lib/src/main/java/at/bitfire/vcard4android/ContactWriter.kt +++ b/lib/src/main/java/at/bitfire/vcard4android/ContactWriter.kt @@ -4,6 +4,7 @@ package at.bitfire.vcard4android +import at.bitfire.vcard4android.Utils.capitalize import at.bitfire.vcard4android.Utils.isEmpty import at.bitfire.vcard4android.property.* import at.bitfire.vcard4android.property.CustomScribes.registerCustomScribes @@ -197,9 +198,7 @@ class ContactWriter private constructor(val contact: Contact, val version: VCard name.addParameter("TYPE", "other") else label = relation.types.joinToString(", ") { type -> - type.value.replaceFirstChar { - if (it.isLowerCase()) it.titlecase(Locale.getDefault()) else it.toString() - } + type.value.capitalize() } } } diff --git a/lib/src/main/java/at/bitfire/vcard4android/Utils.kt b/lib/src/main/java/at/bitfire/vcard4android/Utils.kt index 929a943..7ddbc46 100644 --- a/lib/src/main/java/at/bitfire/vcard4android/Utils.kt +++ b/lib/src/main/java/at/bitfire/vcard4android/Utils.kt @@ -11,6 +11,7 @@ import android.database.DatabaseUtils import android.net.Uri import android.provider.ContactsContract import ezvcard.property.StructuredName +import java.util.Locale object Utils { @@ -45,4 +46,10 @@ object Utils { return "$this B" } + fun String.capitalize(): String = split(' ').joinToString(" ") { word -> + word.replaceFirstChar { + if (it.isLowerCase()) it.titlecase(Locale.getDefault()) else it.toString() + } + } + } \ No newline at end of file diff --git a/lib/src/main/java/at/bitfire/vcard4android/contactrow/RelationBuilder.kt b/lib/src/main/java/at/bitfire/vcard4android/contactrow/RelationBuilder.kt index c511db6..e3823db 100644 --- a/lib/src/main/java/at/bitfire/vcard4android/contactrow/RelationBuilder.kt +++ b/lib/src/main/java/at/bitfire/vcard4android/contactrow/RelationBuilder.kt @@ -8,6 +8,7 @@ import android.net.Uri import android.provider.ContactsContract.CommonDataKinds.Relation import at.bitfire.vcard4android.BatchOperation import at.bitfire.vcard4android.Contact +import at.bitfire.vcard4android.Utils.capitalize import at.bitfire.vcard4android.property.CustomType import ezvcard.parameter.RelatedType import java.util.* @@ -53,11 +54,7 @@ class RelationBuilder(dataRowUri: Uri, rawContactId: Long?, contact: Contact, re if (related.types.isEmpty()) related.types += CustomType.Related.OTHER - val types = related.types.map { type -> - type.value.replaceFirstChar { - if (it.isLowerCase()) it.titlecase(Locale.getDefault()) else it.toString() - } - } + val types = related.types.map { type -> type.value.capitalize() } val typesStr = types.joinToString(", ") builder.withValue(Relation.LABEL, typesStr) } From d4b1954806936750df9702b28f9ee4514a804596 Mon Sep 17 00:00:00 2001 From: Arnau Mora Date: Mon, 8 Jul 2024 08:52:19 +0200 Subject: [PATCH 09/17] Renamed `IOUtils` to `TestUtils` and added kdoc Signed-off-by: Arnau Mora --- .../java/at/bitfire/vcard4android/IOUtils.kt | 6 ------ .../java/at/bitfire/vcard4android/TestUtils.kt | 13 +++++++++++++ .../vcard4android/contactrow/PhotoBuilderTest.kt | 4 ++-- .../vcard4android/contactrow/PhotoHandlerTest.kt | 10 +++++----- 4 files changed, 20 insertions(+), 13 deletions(-) delete mode 100644 lib/src/androidTest/java/at/bitfire/vcard4android/IOUtils.kt create mode 100644 lib/src/androidTest/java/at/bitfire/vcard4android/TestUtils.kt diff --git a/lib/src/androidTest/java/at/bitfire/vcard4android/IOUtils.kt b/lib/src/androidTest/java/at/bitfire/vcard4android/IOUtils.kt deleted file mode 100644 index 63c5fb2..0000000 --- a/lib/src/androidTest/java/at/bitfire/vcard4android/IOUtils.kt +++ /dev/null @@ -1,6 +0,0 @@ -package at.bitfire.vcard4android - -object IOUtils { - fun resourceToByteArray(resource: String): ByteArray = - this::class.java.getResourceAsStream(resource)!!.use { it.readBytes() } -} diff --git a/lib/src/androidTest/java/at/bitfire/vcard4android/TestUtils.kt b/lib/src/androidTest/java/at/bitfire/vcard4android/TestUtils.kt new file mode 100644 index 0000000..f7587e1 --- /dev/null +++ b/lib/src/androidTest/java/at/bitfire/vcard4android/TestUtils.kt @@ -0,0 +1,13 @@ +package at.bitfire.vcard4android + +object TestUtils { + + /** + * Reads a resource from the classpath and returns it as a byte array. + * @param resource The resource to read. Note that it must start with a `/`. + * @throws NullPointerException If the resource doesn't exist. + */ + fun resourceToByteArray(resource: String): ByteArray = + this::class.java.getResourceAsStream(resource)!!.use { it.readBytes() } + +} diff --git a/lib/src/androidTest/java/at/bitfire/vcard4android/contactrow/PhotoBuilderTest.kt b/lib/src/androidTest/java/at/bitfire/vcard4android/contactrow/PhotoBuilderTest.kt index c79e59c..0b2f67d 100644 --- a/lib/src/androidTest/java/at/bitfire/vcard4android/contactrow/PhotoBuilderTest.kt +++ b/lib/src/androidTest/java/at/bitfire/vcard4android/contactrow/PhotoBuilderTest.kt @@ -16,7 +16,7 @@ import androidx.test.platform.app.InstrumentationRegistry import androidx.test.rule.GrantPermissionRule import at.bitfire.vcard4android.AndroidContact import at.bitfire.vcard4android.Contact -import at.bitfire.vcard4android.IOUtils +import at.bitfire.vcard4android.TestUtils import at.bitfire.vcard4android.impl.TestAddressBook import org.junit.Assert.* import org.junit.BeforeClass @@ -81,7 +81,7 @@ class PhotoBuilderTest { val rawContactId = ContentUris.parseId(contactUri) try { - val photo = IOUtils.resourceToByteArray("/large.jpg") + val photo = TestUtils.resourceToByteArray("/large.jpg") val photoUri = PhotoBuilder.insertPhoto(provider, testAccount, rawContactId, photo) assertNotNull(photoUri) diff --git a/lib/src/androidTest/java/at/bitfire/vcard4android/contactrow/PhotoHandlerTest.kt b/lib/src/androidTest/java/at/bitfire/vcard4android/contactrow/PhotoHandlerTest.kt index 1ab1f0b..27c515b 100644 --- a/lib/src/androidTest/java/at/bitfire/vcard4android/contactrow/PhotoHandlerTest.kt +++ b/lib/src/androidTest/java/at/bitfire/vcard4android/contactrow/PhotoHandlerTest.kt @@ -16,7 +16,7 @@ import androidx.test.platform.app.InstrumentationRegistry import androidx.test.rule.GrantPermissionRule import at.bitfire.vcard4android.AndroidContact import at.bitfire.vcard4android.Contact -import at.bitfire.vcard4android.IOUtils +import at.bitfire.vcard4android.TestUtils import at.bitfire.vcard4android.impl.TestAddressBook import org.junit.Assert import org.junit.Assert.* @@ -64,13 +64,13 @@ class PhotoHandlerTest { @Test fun testConvertToJpeg_Jpeg() { - val blob = IOUtils.resourceToByteArray("/small.jpg") + val blob = TestUtils.resourceToByteArray("/small.jpg") assertArrayEquals(blob, PhotoHandler.convertToJpeg(blob, 75)) } @Test fun testConvertToJpeg_Png() { - val blob = IOUtils.resourceToByteArray("/small.png") + val blob = TestUtils.resourceToByteArray("/small.png") assertFalse(Arrays.equals(blob, PhotoHandler.convertToJpeg(blob, 75))) } @@ -86,7 +86,7 @@ class PhotoHandlerTest { @Test fun testPhoto_Blob() { - val blob = IOUtils.resourceToByteArray("/small.jpg") + val blob = TestUtils.resourceToByteArray("/small.jpg") val contact = Contact() PhotoHandler(null).handle(ContentValues().apply { put(Photo.PHOTO, blob) @@ -98,7 +98,7 @@ class PhotoHandlerTest { fun testPhoto_FileId() { val contact = Contact().apply { displayName = "Contact with photo" - photo = IOUtils.resourceToByteArray("/large.jpg") + photo = TestUtils.resourceToByteArray("/large.jpg") } val androidContact = AndroidContact(addressBook, contact, null, null) val rawContactId = ContentUris.parseId(androidContact.add()) From a9458831b424505d06dc9a7862b7841737890fc7 Mon Sep 17 00:00:00 2001 From: Arnau Mora Date: Mon, 8 Jul 2024 08:56:45 +0200 Subject: [PATCH 10/17] Turned into `data class` Signed-off-by: Arnau Mora --- .../java/at/bitfire/vcard4android/Contact.kt | 96 ++++++------------- 1 file changed, 30 insertions(+), 66 deletions(-) diff --git a/lib/src/main/java/at/bitfire/vcard4android/Contact.kt b/lib/src/main/java/at/bitfire/vcard4android/Contact.kt index 080bd4b..f01702d 100644 --- a/lib/src/main/java/at/bitfire/vcard4android/Contact.kt +++ b/lib/src/main/java/at/bitfire/vcard4android/Contact.kt @@ -27,51 +27,50 @@ import java.util.* * * [Contact]s are written to and read from the Android storage by [AndroidContact]. */ -class Contact { - - var uid: String? = null - var group = false +data class Contact( + var uid: String? = null, + var group: Boolean = false, /** list of UIDs of group members without urn:uuid prefix (only meaningful if [group] is true) */ - val members = mutableSetOf() + val members: MutableSet = mutableSetOf(), - var displayName: String? = null - var prefix: String? = null - var givenName: String? = null - var middleName: String? = null - var familyName: String? = null - var suffix: String? = null + var displayName: String? = null, + var prefix: String? = null, + var givenName: String? = null, + var middleName: String? = null, + var familyName: String? = null, + var suffix: String? = null, - var phoneticGivenName: String? = null - var phoneticMiddleName: String? = null - var phoneticFamilyName: String? = null + var phoneticGivenName: String? = null, + var phoneticMiddleName: String? = null, + var phoneticFamilyName: String? = null, /** vCard NICKNAME – Android only supports one nickname **/ - var nickName: LabeledProperty? = null + var nickName: LabeledProperty? = null, - var organization: Organization? = null - var jobTitle: String? = null // vCard TITLE - var jobDescription: String? = null // vCard ROLE + var organization: Organization? = null, + var jobTitle: String? = null, // vCard TITLE + var jobDescription: String? = null, // vCard ROLE - val phoneNumbers = LinkedList>() - val emails = LinkedList>() - val impps = LinkedList>() - val addresses = LinkedList>() - val categories = LinkedList() - val urls = LinkedList>() - val relations = LinkedList() + val phoneNumbers: LinkedList> = LinkedList(), + val emails: LinkedList> = LinkedList(), + val impps: LinkedList> = LinkedList(), + val addresses: LinkedList> = LinkedList(), + val categories: LinkedList = LinkedList(), + val urls: LinkedList> = LinkedList(), + val relations: LinkedList = LinkedList(), - var note: String? = null + var note: String? = null, - var anniversary: Anniversary? = null - var birthDay: Birthday? = null - val customDates = LinkedList>() + var anniversary: Anniversary? = null, + var birthDay: Birthday? = null, + val customDates: LinkedList> = LinkedList(), - var photo: ByteArray? = null + var photo: ByteArray? = null, /** unknown properties in text vCard format */ var unknownProperties: String? = null - +) { companion object { // productID (if set) will be used to generate a PRODID property. @@ -151,41 +150,6 @@ class Contact { else false - override fun toString(): String = - mapOf( - "uid" to uid, - "group" to group, - "members" to members, - "displayName" to displayName, - "prefix" to prefix, - "givenName" to givenName, - "middleName" to middleName, - "familyName" to familyName, - "suffix" to suffix, - "phoneticGivenName" to phoneticGivenName, - "phoneticMiddleName" to phoneticMiddleName, - "phoneticFamilyName" to phoneticFamilyName, - "nickName" to nickName, - "organization" to organization, - "jobTitle" to jobTitle, - "jobDescription" to jobDescription, - "phoneNumbers" to phoneNumbers, - "emails" to emails, - "impps" to impps, - "addresses" to addresses, - "categories" to categories, - "urls" to urls, - "relations" to relations, - "note" to note, - "anniversary" to anniversary, - "birthDay" to birthDay, - "customDates" to customDates, - "unknownProperties" to unknownProperties - ) - .toList() - .joinToString(",") { (k, v) -> "$k=$v" } - .let { "Contact[$it]" } - interface Downloader { fun download(url: String, accepts: String): ByteArray? From e132ff0eb751d99d0161519700a1b0a11ae5a74b Mon Sep 17 00:00:00 2001 From: Arnau Mora Date: Mon, 8 Jul 2024 09:01:30 +0200 Subject: [PATCH 11/17] Added `String.trimToNull` Signed-off-by: Arnau Mora --- .../at/bitfire/vcard4android/ContactReader.kt | 29 ++++++++++--------- .../at/bitfire/vcard4android/ContactWriter.kt | 3 +- .../java/at/bitfire/vcard4android/Utils.kt | 6 ++++ .../vcard4android/contactrow/EventHandler.kt | 3 +- .../contactrow/OrganizationBuilder.kt | 3 +- .../contactrow/RelationBuilder.kt | 3 +- .../contactrow/RelationHandler.kt | 3 +- .../contactrow/StructuredPostalBuilder.kt | 7 +++-- 8 files changed, 35 insertions(+), 22 deletions(-) diff --git a/lib/src/main/java/at/bitfire/vcard4android/ContactReader.kt b/lib/src/main/java/at/bitfire/vcard4android/ContactReader.kt index 1f905a3..1f91dd2 100644 --- a/lib/src/main/java/at/bitfire/vcard4android/ContactReader.kt +++ b/lib/src/main/java/at/bitfire/vcard4android/ContactReader.kt @@ -4,6 +4,7 @@ package at.bitfire.vcard4android +import at.bitfire.vcard4android.Utils.trimToNull import at.bitfire.vcard4android.property.* import at.bitfire.vcard4android.property.CustomScribes.registerCustomScribes import ezvcard.Ezvcard @@ -104,7 +105,7 @@ class ContactReader internal constructor(val vCard: VCard, val downloader: Conta uriString } - return uid?.trim()?.takeIf { it.isNotBlank() } + return uid?.trimToNull() } } @@ -130,20 +131,20 @@ class ContactReader internal constructor(val vCard: VCard, val downloader: Conta uriToUid(prop.uri)?.let { c.members += it } is FormattedName -> - c.displayName = prop.value?.trim()?.takeIf { it.isNotBlank() } + c.displayName = prop.value?.trimToNull() is StructuredName -> { - c.prefix = prop.prefixes.joinToString(" ").trim().takeIf { it.isNotBlank() } - c.givenName = prop.given?.trim()?.takeIf { it.isNotBlank() } - c.middleName = prop.additionalNames.joinToString(" ").trim().takeIf { it.isNotBlank() } - c.familyName = prop.family?.trim()?.takeIf { it.isNotBlank() } - c.suffix = prop.suffixes.joinToString(" ").trim().takeIf { it.isNotBlank() } + c.prefix = prop.prefixes.joinToString(" ").trimToNull() + c.givenName = prop.given?.trimToNull() + c.middleName = prop.additionalNames.joinToString(" ").trimToNull() + c.familyName = prop.family?.trimToNull() + c.suffix = prop.suffixes.joinToString(" ").trimToNull() } is XPhoneticFirstName -> - c.phoneticGivenName = prop.value?.trim()?.takeIf { it.isNotBlank() } + c.phoneticGivenName = prop.value?.trimToNull() is XPhoneticMiddleName -> - c.phoneticMiddleName = prop.value?.trim()?.takeIf { it.isNotBlank() } + c.phoneticMiddleName = prop.value?.trimToNull() is XPhoneticLastName -> - c.phoneticFamilyName = prop.value?.trim()?.takeIf { it.isNotBlank() } + c.phoneticFamilyName = prop.value?.trimToNull() is Nickname -> c.nickName = LabeledProperty(prop, findAndRemoveLabel(prop.group)) @@ -153,9 +154,9 @@ class ContactReader internal constructor(val vCard: VCard, val downloader: Conta is Organization -> c.organization = prop is Title -> - c.jobTitle = prop.value?.trim()?.takeIf { it.isNotBlank() } + c.jobTitle = prop.value?.trimToNull() is Role -> - c.jobDescription = prop.value?.trim()?.takeIf { it.isNotBlank() } + c.jobDescription = prop.value?.trimToNull() is Telephone -> if (!prop.text.isNullOrBlank()) @@ -251,7 +252,7 @@ class ContactReader internal constructor(val vCard: VCard, val downloader: Conta } is Note -> { - prop.value?.trim()?.takeIf { it.isNotBlank() }?.let { note -> + prop.value.trimToNull()?.let { note -> if (c.note == null) c.note = note else @@ -323,7 +324,7 @@ class ContactReader internal constructor(val vCard: VCard, val downloader: Conta for (label in vCard.getProperties(XAbLabel::class.java)) { if (label.group.equals(group, true)) { vCard.removeProperty(label) - return label.value?.trim()?.takeIf { it.isNotBlank() } + return label.value.trimToNull() } } diff --git a/lib/src/main/java/at/bitfire/vcard4android/ContactWriter.kt b/lib/src/main/java/at/bitfire/vcard4android/ContactWriter.kt index edec5e0..04d98fc 100644 --- a/lib/src/main/java/at/bitfire/vcard4android/ContactWriter.kt +++ b/lib/src/main/java/at/bitfire/vcard4android/ContactWriter.kt @@ -6,6 +6,7 @@ package at.bitfire.vcard4android import at.bitfire.vcard4android.Utils.capitalize import at.bitfire.vcard4android.Utils.isEmpty +import at.bitfire.vcard4android.Utils.trimToNull import at.bitfire.vcard4android.property.* import at.bitfire.vcard4android.property.CustomScribes.registerCustomScribes import ezvcard.Ezvcard @@ -124,7 +125,7 @@ class ContactWriter private constructor(val contact: Contact, val version: VCard // vCard 4 REQUIRES FN [RFC 6350 6.2.1 FN] val fn = // use display name, if available - contact.displayName?.trim()?.takeIf { it.isNotBlank() } ?: + contact.displayName.trimToNull() ?: // no display name, try organization contact.organization?.values?.joinToString(" / ") ?: // otherwise, try nickname diff --git a/lib/src/main/java/at/bitfire/vcard4android/Utils.kt b/lib/src/main/java/at/bitfire/vcard4android/Utils.kt index 7ddbc46..b21c283 100644 --- a/lib/src/main/java/at/bitfire/vcard4android/Utils.kt +++ b/lib/src/main/java/at/bitfire/vcard4android/Utils.kt @@ -52,4 +52,10 @@ object Utils { } } + /** + * Returns a string having leading and trailing whitespace removed. + * If the resulting string is empty, returns null. + */ + fun String?.trimToNull(): String? = this?.trim()?.takeIf { it.isNotBlank() } + } \ No newline at end of file diff --git a/lib/src/main/java/at/bitfire/vcard4android/contactrow/EventHandler.kt b/lib/src/main/java/at/bitfire/vcard4android/contactrow/EventHandler.kt index 047c5b9..0d07380 100644 --- a/lib/src/main/java/at/bitfire/vcard4android/contactrow/EventHandler.kt +++ b/lib/src/main/java/at/bitfire/vcard4android/contactrow/EventHandler.kt @@ -9,6 +9,7 @@ import android.provider.ContactsContract.CommonDataKinds.Event import at.bitfire.vcard4android.Constants import at.bitfire.vcard4android.Contact import at.bitfire.vcard4android.LabeledProperty +import at.bitfire.vcard4android.Utils.trimToNull import at.bitfire.vcard4android.property.XAbDate import ezvcard.property.Anniversary import ezvcard.property.Birthday @@ -48,7 +49,7 @@ object EventHandler: DataRowHandler() { Event.TYPE_CUSTOM */ else -> { val abDate = if (full != null) XAbDate(full) else XAbDate(partial) - val label = values.getAsString(Event.LABEL)?.trim()?.takeIf { it.isNotBlank() } + val label = values.getAsString(Event.LABEL).trimToNull() contact.customDates += LabeledProperty(abDate, label) } } diff --git a/lib/src/main/java/at/bitfire/vcard4android/contactrow/OrganizationBuilder.kt b/lib/src/main/java/at/bitfire/vcard4android/contactrow/OrganizationBuilder.kt index d6dabcf..c96aea6 100644 --- a/lib/src/main/java/at/bitfire/vcard4android/contactrow/OrganizationBuilder.kt +++ b/lib/src/main/java/at/bitfire/vcard4android/contactrow/OrganizationBuilder.kt @@ -8,6 +8,7 @@ import android.net.Uri import android.provider.ContactsContract.CommonDataKinds.Organization import at.bitfire.vcard4android.BatchOperation import at.bitfire.vcard4android.Contact +import at.bitfire.vcard4android.Utils.trimToNull import java.util.* class OrganizationBuilder(dataRowUri: Uri, rawContactId: Long?, contact: Contact, readOnly: Boolean) @@ -24,7 +25,7 @@ class OrganizationBuilder(dataRowUri: Uri, rawContactId: Long?, contact: Contact val depts = LinkedList() while (org.hasNext()) depts += org.next() - department = depts.joinToString(" / ").trim().takeIf { t -> t.isNotBlank() } + department = depts.joinToString(" / ").trimToNull() } if (company == null && department == null && contact.jobTitle == null && contact.jobDescription == null) diff --git a/lib/src/main/java/at/bitfire/vcard4android/contactrow/RelationBuilder.kt b/lib/src/main/java/at/bitfire/vcard4android/contactrow/RelationBuilder.kt index e3823db..21284cd 100644 --- a/lib/src/main/java/at/bitfire/vcard4android/contactrow/RelationBuilder.kt +++ b/lib/src/main/java/at/bitfire/vcard4android/contactrow/RelationBuilder.kt @@ -9,6 +9,7 @@ import android.provider.ContactsContract.CommonDataKinds.Relation import at.bitfire.vcard4android.BatchOperation import at.bitfire.vcard4android.Contact import at.bitfire.vcard4android.Utils.capitalize +import at.bitfire.vcard4android.Utils.trimToNull import at.bitfire.vcard4android.property.CustomType import ezvcard.parameter.RelatedType import java.util.* @@ -19,7 +20,7 @@ class RelationBuilder(dataRowUri: Uri, rawContactId: Long?, contact: Contact, re override fun build(): List { val result = LinkedList() for (related in contact.relations) { - val name = related.text?.trim()?.takeIf { it.isNotBlank() } ?: related.uri?.trim()?.takeIf { it.isNotBlank() } + val name = related.text.trimToNull() ?: related.uri.trimToNull() if (name.isNullOrBlank()) continue diff --git a/lib/src/main/java/at/bitfire/vcard4android/contactrow/RelationHandler.kt b/lib/src/main/java/at/bitfire/vcard4android/contactrow/RelationHandler.kt index 91ec5f0..be11a59 100644 --- a/lib/src/main/java/at/bitfire/vcard4android/contactrow/RelationHandler.kt +++ b/lib/src/main/java/at/bitfire/vcard4android/contactrow/RelationHandler.kt @@ -7,6 +7,7 @@ package at.bitfire.vcard4android.contactrow import android.content.ContentValues import android.provider.ContactsContract.CommonDataKinds.Relation import at.bitfire.vcard4android.Contact +import at.bitfire.vcard4android.Utils.trimToNull import at.bitfire.vcard4android.property.CustomType import ezvcard.parameter.RelatedType import ezvcard.property.Related @@ -66,7 +67,7 @@ object RelationHandler: DataRowHandler() { Relation.TYPE_SPOUSE -> related.types += RelatedType.SPOUSE Relation.TYPE_CUSTOM -> - values.getAsString(Relation.LABEL)?.trim()?.takeIf { it.isNotBlank() }?.let { typeStr -> + values.getAsString(Relation.LABEL).trimToNull()?.let { typeStr -> for (type in typeStr.split(',')) related.types += RelatedType.get(type.lowercase().trim()) } diff --git a/lib/src/main/java/at/bitfire/vcard4android/contactrow/StructuredPostalBuilder.kt b/lib/src/main/java/at/bitfire/vcard4android/contactrow/StructuredPostalBuilder.kt index 616e651..19bd73c 100644 --- a/lib/src/main/java/at/bitfire/vcard4android/contactrow/StructuredPostalBuilder.kt +++ b/lib/src/main/java/at/bitfire/vcard4android/contactrow/StructuredPostalBuilder.kt @@ -8,6 +8,7 @@ import android.net.Uri import android.provider.ContactsContract.CommonDataKinds.StructuredPostal import at.bitfire.vcard4android.BatchOperation import at.bitfire.vcard4android.Contact +import at.bitfire.vcard4android.Utils.trimToNull import ezvcard.parameter.AddressType import java.util.* @@ -39,14 +40,14 @@ class StructuredPostalBuilder(dataRowUri: Uri, rawContactId: Long?, contact: Con lines += extended val postalAndCity = LinkedList() - if (address.postalCode?.trim()?.takeIf { it.isNotBlank() } != null) + if (address.postalCode.trimToNull() != null) postalAndCity += address.postalCodes.joinToString(" / ") - if (address.locality?.trim()?.takeIf { it.isNotBlank() } != null) + if (address.locality.trimToNull() != null) postalAndCity += address.localities.joinToString(" / ") if (postalAndCity.isNotEmpty()) lines += postalAndCity.joinToString(" ") - if (address.country?.trim()?.takeIf { it.isNotBlank() } != null) { + if (address.country.trimToNull() != null) { val line = StringBuilder(address.countries.joinToString(" / ")) if (!address.region.isNullOrBlank()) { val regions = address.regions.joinToString(" / ") From 714824d35307d0a954b1f45d2f0d464e97ecdbb2 Mon Sep 17 00:00:00 2001 From: Arnau Mora Date: Mon, 8 Jul 2024 09:02:16 +0200 Subject: [PATCH 12/17] Ordered methods Signed-off-by: Arnau Mora --- .../java/at/bitfire/vcard4android/Utils.kt | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/lib/src/main/java/at/bitfire/vcard4android/Utils.kt b/lib/src/main/java/at/bitfire/vcard4android/Utils.kt index b21c283..ea8e073 100644 --- a/lib/src/main/java/at/bitfire/vcard4android/Utils.kt +++ b/lib/src/main/java/at/bitfire/vcard4android/Utils.kt @@ -21,15 +21,6 @@ object Utils { return values } - fun StructuredName.isEmpty() = - prefixes.isEmpty() && given == null && additionalNames.isEmpty() && family == null && suffixes.isEmpty() - - fun Uri.asSyncAdapter(account: Account): Uri = buildUpon() - .appendQueryParameter(ContactsContract.RawContacts.ACCOUNT_NAME, account.name) - .appendQueryParameter(ContactsContract.RawContacts.ACCOUNT_TYPE, account.type) - .appendQueryParameter(ContactsContract.CALLER_IS_SYNCADAPTER, "true") - .build() - fun Long.byteCountToDisplaySize(): String { val gb = this / 1_073_741_824 // 2^30 if (gb > 0) { @@ -58,4 +49,13 @@ object Utils { */ fun String?.trimToNull(): String? = this?.trim()?.takeIf { it.isNotBlank() } + fun StructuredName.isEmpty() = + prefixes.isEmpty() && given == null && additionalNames.isEmpty() && family == null && suffixes.isEmpty() + + fun Uri.asSyncAdapter(account: Account): Uri = buildUpon() + .appendQueryParameter(ContactsContract.RawContacts.ACCOUNT_NAME, account.name) + .appendQueryParameter(ContactsContract.RawContacts.ACCOUNT_TYPE, account.type) + .appendQueryParameter(ContactsContract.CALLER_IS_SYNCADAPTER, "true") + .build() + } \ No newline at end of file From e58914a53d5d83443c68bf78d28d5b220581afae Mon Sep 17 00:00:00 2001 From: Arnau Mora Date: Mon, 8 Jul 2024 09:05:59 +0200 Subject: [PATCH 13/17] Hash code will be generated by data class Signed-off-by: Arnau Mora --- lib/src/main/java/at/bitfire/vcard4android/Contact.kt | 4 ---- 1 file changed, 4 deletions(-) diff --git a/lib/src/main/java/at/bitfire/vcard4android/Contact.kt b/lib/src/main/java/at/bitfire/vcard4android/Contact.kt index f01702d..b46bee5 100644 --- a/lib/src/main/java/at/bitfire/vcard4android/Contact.kt +++ b/lib/src/main/java/at/bitfire/vcard4android/Contact.kt @@ -140,10 +140,6 @@ data class Contact( /* unknownProperties */ ) - override fun hashCode(): Int { - return compareFields().sumOf { 29 * 3 + it.hashCode() } - } - override fun equals(other: Any?) = if (other is Contact) compareFields().contentDeepEquals(other.compareFields()) From 2c8cf2006a5d654b7d71cd54748020f37e3b655d Mon Sep 17 00:00:00 2001 From: Arnau Mora Date: Mon, 15 Jul 2024 16:14:19 +0200 Subject: [PATCH 14/17] Generated `toString` Signed-off-by: Arnau Mora --- .../main/java/at/bitfire/vcard4android/AndroidContact.kt | 7 +++---- lib/src/main/java/at/bitfire/vcard4android/AndroidGroup.kt | 7 +++---- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/lib/src/main/java/at/bitfire/vcard4android/AndroidContact.kt b/lib/src/main/java/at/bitfire/vcard4android/AndroidContact.kt index f6ef4ea..a89b5c3 100644 --- a/lib/src/main/java/at/bitfire/vcard4android/AndroidContact.kt +++ b/lib/src/main/java/at/bitfire/vcard4android/AndroidContact.kt @@ -213,9 +213,8 @@ open class AndroidContact( protected fun dataSyncURI() = addressBook.syncAdapterURI(ContactsContract.Data.CONTENT_URI) - override fun toString() = mapOf("id" to id, "fileName" to fileName, "eTag" to eTag) - .toList() - .joinToString(",") { (k, v) -> "$k=$v" } - .let { "AndroidContact[$it]" } + override fun toString(): String { + return "AndroidContact(id=$id, fileName=$fileName, eTag=$eTag)" + } } \ No newline at end of file diff --git a/lib/src/main/java/at/bitfire/vcard4android/AndroidGroup.kt b/lib/src/main/java/at/bitfire/vcard4android/AndroidGroup.kt index 01e034e..260e1be 100644 --- a/lib/src/main/java/at/bitfire/vcard4android/AndroidGroup.kt +++ b/lib/src/main/java/at/bitfire/vcard4android/AndroidGroup.kt @@ -163,9 +163,8 @@ open class AndroidGroup( return addressBook.syncAdapterURI(ContentUris.withAppendedId(Groups.CONTENT_URI, id)) } - override fun toString() = mapOf("id" to id, "fileName" to fileName, "eTag" to eTag) - .toList() - .joinToString(",") { (k, v) -> "$k=$v" } - .let { "AndroidGroup[$it]" } + override fun toString(): String { + return "AndroidGroup(id=$id, fileName=$fileName, eTag=$eTag)" + } } From 279db1f7d61afb839febbc2f4a0949bb576d280e Mon Sep 17 00:00:00 2001 From: Arnau Mora Date: Mon, 15 Jul 2024 16:22:43 +0200 Subject: [PATCH 15/17] Added test for `capitalize` and `trimToNull` Signed-off-by: Arnau Mora --- .../at/bitfire/vcard4android/UtilsTest.kt | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 lib/src/test/java/at/bitfire/vcard4android/UtilsTest.kt diff --git a/lib/src/test/java/at/bitfire/vcard4android/UtilsTest.kt b/lib/src/test/java/at/bitfire/vcard4android/UtilsTest.kt new file mode 100644 index 0000000..be31a97 --- /dev/null +++ b/lib/src/test/java/at/bitfire/vcard4android/UtilsTest.kt @@ -0,0 +1,25 @@ +package at.bitfire.vcard4android + +import at.bitfire.vcard4android.Utils.capitalize +import at.bitfire.vcard4android.Utils.trimToNull +import org.junit.Assert.assertEquals +import org.junit.Assert.assertNull +import org.junit.Test + +class UtilsTest { + @Test + fun testCapitalize() { + assertEquals("Utils Test", "utils test".capitalize()) // Test multiple words + assertEquals("Utils", "utils".capitalize()) // Test single word + assertEquals("", "".capitalize()) // Test empty string + } + + @Test + fun testTrimToNull() { + assertEquals("test", " test".trimToNull()) // Test spaces only before + assertEquals("test", "test ".trimToNull()) // Test spaces only after + assertEquals("test", " test ".trimToNull()) // Test spaces before and after + assertNull(" ".trimToNull()) // Test spaces + assertNull("".trimToNull()) // Test empty string + } +} From 3f3bfc0ae5f8f7c0d51fa4458860eaf13a12ebc8 Mon Sep 17 00:00:00 2001 From: Arnau Mora Date: Mon, 15 Jul 2024 16:29:40 +0200 Subject: [PATCH 16/17] Removed `byteCountToDisplaySize` Signed-off-by: Arnau Mora --- .../main/java/at/bitfire/vcard4android/Utils.kt | 16 ---------------- .../vcard4android/contactrow/PhotoBuilder.kt | 3 +-- .../vcard4android/contactrow/PhotoHandler.kt | 5 ++--- 3 files changed, 3 insertions(+), 21 deletions(-) diff --git a/lib/src/main/java/at/bitfire/vcard4android/Utils.kt b/lib/src/main/java/at/bitfire/vcard4android/Utils.kt index ea8e073..24bbee8 100644 --- a/lib/src/main/java/at/bitfire/vcard4android/Utils.kt +++ b/lib/src/main/java/at/bitfire/vcard4android/Utils.kt @@ -21,22 +21,6 @@ object Utils { return values } - fun Long.byteCountToDisplaySize(): String { - val gb = this / 1_073_741_824 // 2^30 - if (gb > 0) { - return "$gb GB" - } - val mb = this / 1_048_576 // 2^20 - if (mb > 0) { - return "$mb MB" - } - val kb = this / 1024 // 2^10 - if (kb > 0) { - return "$kb KB" - } - return "$this B" - } - fun String.capitalize(): String = split(' ').joinToString(" ") { word -> word.replaceFirstChar { if (it.isLowerCase()) it.titlecase(Locale.getDefault()) else it.toString() diff --git a/lib/src/main/java/at/bitfire/vcard4android/contactrow/PhotoBuilder.kt b/lib/src/main/java/at/bitfire/vcard4android/contactrow/PhotoBuilder.kt index 7f0f62c..368bb02 100644 --- a/lib/src/main/java/at/bitfire/vcard4android/contactrow/PhotoBuilder.kt +++ b/lib/src/main/java/at/bitfire/vcard4android/contactrow/PhotoBuilder.kt @@ -16,7 +16,6 @@ import at.bitfire.vcard4android.BatchOperation import at.bitfire.vcard4android.Constants import at.bitfire.vcard4android.Contact import at.bitfire.vcard4android.Utils.asSyncAdapter -import at.bitfire.vcard4android.Utils.byteCountToDisplaySize import java.io.IOException import java.util.logging.Level @@ -56,7 +55,7 @@ class PhotoBuilder(dataRowUri: Uri, rawContactId: Long?, contact: Contact, readO .appendPath(rawContactId.toString()) .appendPath(RawContacts.DisplayPhoto.CONTENT_DIRECTORY) .build() - Constants.log.log(Level.FINE, "Writing photo to $uri (${data.size.toLong().byteCountToDisplaySize()})") + Constants.log.log(Level.FINE, "Writing photo to $uri (${data.size} bytes)") provider.openAssetFile(uri, "w")?.use { fd -> try { fd.createOutputStream()?.use { os -> diff --git a/lib/src/main/java/at/bitfire/vcard4android/contactrow/PhotoHandler.kt b/lib/src/main/java/at/bitfire/vcard4android/contactrow/PhotoHandler.kt index 4033b48..1daef1d 100644 --- a/lib/src/main/java/at/bitfire/vcard4android/contactrow/PhotoHandler.kt +++ b/lib/src/main/java/at/bitfire/vcard4android/contactrow/PhotoHandler.kt @@ -13,7 +13,6 @@ import android.provider.ContactsContract import android.provider.ContactsContract.CommonDataKinds.Photo import at.bitfire.vcard4android.Constants import at.bitfire.vcard4android.Contact -import at.bitfire.vcard4android.Utils.byteCountToDisplaySize import java.io.ByteArrayOutputStream import java.io.IOException import java.util.logging.Level @@ -73,7 +72,7 @@ class PhotoHandler(val provider: ContentProviderClient?): DataRowHandler() { file.createInputStream().use { // Samsung Android 12 bug: they return a PNG image with MIME type image/jpeg convertToJpeg(it.readBytes(), 75)?.let { jpeg -> - Constants.log.log(Level.FINE, "Got high-res contact photo (${jpeg.size.toLong().byteCountToDisplaySize()})") + Constants.log.log(Level.FINE, "Got high-res contact photo (${jpeg.size} bytes)") contact.photo = jpeg } } @@ -87,7 +86,7 @@ class PhotoHandler(val provider: ContentProviderClient?): DataRowHandler() { values.getAsByteArray(Photo.PHOTO)?.let { thumbnail -> // Samsung Android 12 bug: even the thumbnail is a PNG image convertToJpeg(thumbnail, 95)?.let { jpeg -> - Constants.log.log(Level.FINE, "Got contact photo thumbnail (${jpeg.size.toLong().byteCountToDisplaySize()})") + Constants.log.log(Level.FINE, "Got contact photo thumbnail (${jpeg.size} bytes)") contact.photo = jpeg } } From 5b3b8ef5763b885ebac773240c12807be8e2e0b6 Mon Sep 17 00:00:00 2001 From: Ricki Hirner Date: Mon, 15 Jul 2024 16:57:37 +0200 Subject: [PATCH 17/17] AndroidContact, AndroidGroup: add _contact to toString; Contact: add hashCode() --- .../main/java/at/bitfire/vcard4android/AndroidContact.kt | 7 +++---- lib/src/main/java/at/bitfire/vcard4android/AndroidGroup.kt | 7 +++---- lib/src/main/java/at/bitfire/vcard4android/Contact.kt | 2 ++ 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/lib/src/main/java/at/bitfire/vcard4android/AndroidContact.kt b/lib/src/main/java/at/bitfire/vcard4android/AndroidContact.kt index a89b5c3..ccf91ce 100644 --- a/lib/src/main/java/at/bitfire/vcard4android/AndroidContact.kt +++ b/lib/src/main/java/at/bitfire/vcard4android/AndroidContact.kt @@ -19,7 +19,7 @@ import at.bitfire.vcard4android.contactrow.PhotoBuilder import java.io.FileNotFoundException open class AndroidContact( - open val addressBook: AndroidAddressBook + open val addressBook: AndroidAddressBook ) { companion object { @@ -213,8 +213,7 @@ open class AndroidContact( protected fun dataSyncURI() = addressBook.syncAdapterURI(ContactsContract.Data.CONTENT_URI) - override fun toString(): String { - return "AndroidContact(id=$id, fileName=$fileName, eTag=$eTag)" - } + override fun toString() = + "AndroidContact(id=$id, fileName=$fileName, eTag=$eTag, _contact=$_contact)" } \ No newline at end of file diff --git a/lib/src/main/java/at/bitfire/vcard4android/AndroidGroup.kt b/lib/src/main/java/at/bitfire/vcard4android/AndroidGroup.kt index 260e1be..04144fb 100644 --- a/lib/src/main/java/at/bitfire/vcard4android/AndroidGroup.kt +++ b/lib/src/main/java/at/bitfire/vcard4android/AndroidGroup.kt @@ -17,7 +17,7 @@ import androidx.annotation.CallSuper import java.io.FileNotFoundException open class AndroidGroup( - val addressBook: AndroidAddressBook + val addressBook: AndroidAddressBook ) { companion object { @@ -163,8 +163,7 @@ open class AndroidGroup( return addressBook.syncAdapterURI(ContentUris.withAppendedId(Groups.CONTENT_URI, id)) } - override fun toString(): String { - return "AndroidGroup(id=$id, fileName=$fileName, eTag=$eTag)" - } + override fun toString() = + "AndroidGroup(id=$id, fileName=$fileName, eTag=$eTag, _contact=$_contact)" } diff --git a/lib/src/main/java/at/bitfire/vcard4android/Contact.kt b/lib/src/main/java/at/bitfire/vcard4android/Contact.kt index b46bee5..9d8d52b 100644 --- a/lib/src/main/java/at/bitfire/vcard4android/Contact.kt +++ b/lib/src/main/java/at/bitfire/vcard4android/Contact.kt @@ -146,6 +146,8 @@ data class Contact( else false + override fun hashCode() = compareFields().contentHashCode() + interface Downloader { fun download(url: String, accepts: String): ByteArray?