diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 289390e..bf362c6 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -3,10 +3,6 @@ agp = "8.5.1" androidx-annotation = "1.8.0" androidx-test-runner = "1.6.1" androidx-rest-rules = "1.6.1" -# 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 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 bc8d607..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,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.TestUtils import at.bitfire.vcard4android.impl.TestAddressBook -import org.apache.commons.io.IOUtils import org.junit.Assert.* import org.junit.BeforeClass import org.junit.ClassRule @@ -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 004995e..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,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.TestUtils import at.bitfire.vcard4android.impl.TestAddressBook -import org.apache.commons.io.IOUtils import org.junit.Assert import org.junit.Assert.* import org.junit.BeforeClass @@ -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()) diff --git a/lib/src/main/java/at/bitfire/vcard4android/AndroidContact.kt b/lib/src/main/java/at/bitfire/vcard4android/AndroidContact.kt index d3d7350..ccf91ce 100644 --- a/lib/src/main/java/at/bitfire/vcard4android/AndroidContact.kt +++ b/lib/src/main/java/at/bitfire/vcard4android/AndroidContact.kt @@ -16,11 +16,10 @@ 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( - open val addressBook: AndroidAddressBook + open val addressBook: AndroidAddressBook ) { companion object { @@ -214,6 +213,7 @@ open class AndroidContact( protected fun dataSyncURI() = addressBook.syncAdapterURI(ContactsContract.Data.CONTENT_URI) - override fun toString() = ToStringBuilder.reflectionToString(this)!! + 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 16b3428..04144fb 100644 --- a/lib/src/main/java/at/bitfire/vcard4android/AndroidGroup.kt +++ b/lib/src/main/java/at/bitfire/vcard4android/AndroidGroup.kt @@ -14,11 +14,10 @@ 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( - val addressBook: AndroidAddressBook + val addressBook: AndroidAddressBook ) { companion object { @@ -164,6 +163,7 @@ open class AndroidGroup( return addressBook.syncAdapterURI(ContentUris.withAppendedId(Groups.CONTENT_URI, id)) } - override fun toString() = ToStringBuilder.reflectionToString(this)!! + 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 d266a39..9d8d52b 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 @@ -29,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. @@ -143,22 +140,13 @@ class Contact { /* unknownProperties */ ) - override fun hashCode(): Int { - val builder = HashCodeBuilder(29, 3).append(compareFields()) - return builder.toHashCode() - } - override fun equals(other: Any?) = if (other is Contact) compareFields().contentDeepEquals(other.compareFields()) else false - override fun toString(): String { - val builder = ReflectionToStringBuilder(this) - builder.setExcludeFieldNames("photo") - return builder.toString() - } + override fun hashCode() = compareFields().contentHashCode() 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..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 @@ -11,7 +12,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 +105,7 @@ class ContactReader internal constructor(val vCard: VCard, val downloader: Conta uriString } - return StringUtils.trimToNull(uid) + return uid?.trimToNull() } } @@ -131,20 +131,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?.trimToNull() 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(" ").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 = StringUtils.trimToNull(prop.value) + c.phoneticGivenName = prop.value?.trimToNull() is XPhoneticMiddleName -> - c.phoneticMiddleName = StringUtils.trimToNull(prop.value) + c.phoneticMiddleName = prop.value?.trimToNull() is XPhoneticLastName -> - c.phoneticFamilyName = StringUtils.trimToNull(prop.value) + c.phoneticFamilyName = prop.value?.trimToNull() is Nickname -> c.nickName = LabeledProperty(prop, findAndRemoveLabel(prop.group)) @@ -154,9 +154,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?.trimToNull() is Role -> - c.jobDescription = StringUtils.trimToNull(prop.value) + c.jobDescription = prop.value?.trimToNull() is Telephone -> if (!prop.text.isNullOrBlank()) @@ -252,7 +252,7 @@ class ContactReader internal constructor(val vCard: VCard, val downloader: Conta } is Note -> { - StringUtils.trimToNull(prop.value)?.let { note -> + prop.value.trimToNull()?.let { note -> if (c.note == null) c.note = note else @@ -324,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 StringUtils.trimToNull(label.value) + 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 9b6a557..04d98fc 100644 --- a/lib/src/main/java/at/bitfire/vcard4android/ContactWriter.kt +++ b/lib/src/main/java/at/bitfire/vcard4android/ContactWriter.kt @@ -4,7 +4,9 @@ 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 @@ -15,13 +17,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 +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 - StringUtils.trimToNull(contact.displayName) ?: + contact.displayName.trimToNull() ?: // no display name, try organization contact.organization?.values?.joinToString(" / ") ?: // otherwise, try nickname @@ -201,7 +198,9 @@ 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.capitalize() + } } } diff --git a/lib/src/main/java/at/bitfire/vcard4android/Utils.kt b/lib/src/main/java/at/bitfire/vcard4android/Utils.kt index 6cec2be..24bbee8 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 { @@ -20,6 +21,18 @@ object Utils { return values } + fun String.capitalize(): String = split(' ').joinToString(" ") { word -> + word.replaceFirstChar { + if (it.isLowerCase()) it.titlecase(Locale.getDefault()) else it.toString() + } + } + + /** + * 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() } + fun StructuredName.isEmpty() = prefixes.isEmpty() && given == null && additionalNames.isEmpty() && family == null && suffixes.isEmpty() 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..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,13 +9,11 @@ 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 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 +49,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).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 aa0507a..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,7 +8,7 @@ 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 at.bitfire.vcard4android.Utils.trimToNull import java.util.* class OrganizationBuilder(dataRowUri: Uri, rawContactId: Long?, contact: Contact, readOnly: Boolean) @@ -25,7 +25,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(" / ").trimToNull() } if (company == null && department == null && contact.jobTitle == null && contact.jobDescription == null) 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..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 org.apache.commons.io.FileUtils 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 (${FileUtils.byteCountToDisplaySize(data.size.toLong())})") + 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 d807f85..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,8 +13,6 @@ 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 java.io.ByteArrayOutputStream import java.io.IOException import java.util.logging.Level @@ -73,8 +71,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} bytes)") contact.photo = jpeg } } @@ -88,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 (${FileUtils.byteCountToDisplaySize(jpeg.size.toLong())})") + Constants.log.log(Level.FINE, "Got contact photo thumbnail (${jpeg.size} bytes)") contact.photo = jpeg } } 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..21284cd 100644 --- a/lib/src/main/java/at/bitfire/vcard4android/contactrow/RelationBuilder.kt +++ b/lib/src/main/java/at/bitfire/vcard4android/contactrow/RelationBuilder.kt @@ -8,10 +8,10 @@ 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.Utils.trimToNull 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 +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 = StringUtils.trimToNull(related.text) ?: StringUtils.trimToNull(related.uri) + val name = related.text.trimToNull() ?: related.uri.trimToNull() if (name.isNullOrBlank()) continue @@ -55,7 +55,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.capitalize() } 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..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,10 +7,10 @@ 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 -import org.apache.commons.lang3.StringUtils object RelationHandler: DataRowHandler() { @@ -67,7 +67,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).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 698f8e0..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,8 +8,8 @@ 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 org.apache.commons.lang3.StringUtils import java.util.* /** @@ -40,14 +40,14 @@ class StructuredPostalBuilder(dataRowUri: Uri, rawContactId: Long?, contact: Con lines += extended val postalAndCity = LinkedList() - if (StringUtils.trimToNull(address.postalCode) != null) + if (address.postalCode.trimToNull() != null) postalAndCity += address.postalCodes.joinToString(" / ") - if (StringUtils.trimToNull(address.locality) != null) + if (address.locality.trimToNull() != null) postalAndCity += address.localities.joinToString(" / ") if (postalAndCity.isNotEmpty()) lines += postalAndCity.joinToString(" ") - if (StringUtils.trimToNull(address.country) != null) { + if (address.country.trimToNull() != null) { val line = StringBuilder(address.countries.joinToString(" / ")) if (!address.region.isNullOrBlank()) { val regions = address.regions.joinToString(" / ") 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) } } 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 + } +}