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

Refactoring

* use Kotlin getters/setters, if possible
* simplify interfaces
* reduce importance of ContactsStorageException (now used as a separate exception only,
  not as a wrapper for RemoteException)
parent c5959661
Pipeline #18019799 passed with stages
in 2 minutes and 29 seconds
......@@ -51,16 +51,16 @@ class AndroidAddressBookTest {
var values = ContentValues()
values.put(ContactsContract.Settings.SHOULD_SYNC, false)
values.put(ContactsContract.Settings.UNGROUPED_VISIBLE, false)
addressBook.updateSettings(values)
values = addressBook.getSettings()
addressBook.settings = values
values = addressBook.settings
assertFalse(values.getAsInteger(ContactsContract.Settings.SHOULD_SYNC) != 0)
assertFalse(values.getAsInteger(ContactsContract.Settings.UNGROUPED_VISIBLE) != 0)
values = ContentValues()
values.put(ContactsContract.Settings.SHOULD_SYNC, true)
values.put(ContactsContract.Settings.UNGROUPED_VISIBLE, true)
addressBook.updateSettings(values)
values = addressBook.getSettings()
addressBook.settings = values
values = addressBook.settings
assertTrue(values.getAsInteger(ContactsContract.Settings.SHOULD_SYNC) != 0)
assertTrue(values.getAsInteger(ContactsContract.Settings.UNGROUPED_VISIBLE) != 0)
}
......@@ -69,12 +69,12 @@ class AndroidAddressBookTest {
fun testSyncState() {
val addressBook = TestAddressBook(testAccount, provider)
addressBook.writeSyncState(ByteArray(0))
assertEquals(0, addressBook.readSyncState()!!.size)
addressBook.syncState = ByteArray(0)
assertEquals(0, addressBook.syncState!!.size)
val random = byteArrayOf(1, 2, 3, 4, 5)
addressBook.writeSyncState(random)
assertArrayEquals(random, addressBook.readSyncState())
addressBook.syncState = random
assertArrayEquals(random, addressBook.syncState)
}
}
......@@ -78,7 +78,7 @@ class AndroidContactTest {
val contact = AndroidContact(addressBook, vcard, null, null)
contact.create()
val contact2 = AndroidContact(addressBook, contact.id!!, null, null)
val contact2 = addressBook.findContactByID(contact.id!!)
try {
val vcard2 = contact2.contact!!
assertEquals(vcard.displayName, vcard2.displayName)
......@@ -110,7 +110,7 @@ class AndroidContactTest {
val dbContact = AndroidContact(addressBook, contacts.first(), null, null)
dbContact.create()
val dbContact2 = AndroidContact(addressBook, dbContact.id!!, null, null)
val dbContact2 = addressBook.findContactByID(dbContact.id!!)
try {
val contact2 = dbContact2.contact!!
assertEquals("Test", contact2.displayName)
......@@ -132,7 +132,7 @@ class AndroidContactTest {
val contact = AndroidContact(addressBook, vcard, null, null)
contact.create()
val contact2 = AndroidContact(addressBook, contact.id!!, null, null)
val contact2 = addressBook.findContactByID(contact.id!!)
try {
val vcard2 = contact2.contact!!
assertEquals(4000, vcard2.emails.size)
......@@ -191,7 +191,7 @@ class AndroidContactTest {
val contact = AndroidContact(addressBook, vcard, null, null)
contact.create()
val contact2 = AndroidContact(addressBook, contact.id!!, null, null)
val contact2 = addressBook.findContactByID(contact.id!!)
try {
val vcard2 = contact2.contact!!
assertEquals(vcard.displayName, vcard2.displayName)
......
......@@ -10,6 +10,7 @@ package at.bitfire.vcard4android.impl
import android.accounts.Account
import android.content.ContentProviderClient
import android.content.ContentValues
import at.bitfire.vcard4android.*
class TestAddressBook(
......@@ -19,22 +20,16 @@ class TestAddressBook(
object ContactFactory: AndroidContactFactory<AndroidContact> {
override fun newInstance(addressBook: AndroidAddressBook<AndroidContact, *>, id: Long, fileName: String?, eTag: String?) =
AndroidContact(addressBook, id, fileName, eTag)
override fun newInstance(addressBook: AndroidAddressBook<AndroidContact, *>, contact: Contact, fileName: String?, eTag: String?): AndroidContact =
AndroidContact(addressBook, contact, fileName, eTag)
override fun fromProvider(addressBook: AndroidAddressBook<AndroidContact, *>, values: ContentValues) =
AndroidContact(addressBook, values)
}
object GroupFactory: AndroidGroupFactory<AndroidGroup> {
override fun newInstance(addressBook: AndroidAddressBook<*, AndroidGroup>, id: Long, fileName: String?, eTag: String?) =
AndroidGroup(addressBook, id, fileName, eTag)
override fun newInstance(addressBook: AndroidAddressBook<*, AndroidGroup>, contact: Contact, fileName: String?, eTag: String?) =
AndroidGroup(addressBook, contact, fileName, eTag)
override fun fromProvider(addressBook: AndroidAddressBook<*, AndroidGroup>, values: ContentValues) =
AndroidGroup(addressBook, values)
}
......
......@@ -26,50 +26,49 @@ open class AndroidAddressBook<T1: AndroidContact, T2: AndroidGroup>(
val groupFactory: AndroidGroupFactory<T2>
) {
// account-specific address book settings
/**
* Retrieves [ContactsContract.Settings] for the current address book.
* @throws FileNotFoundException if the settings row couldn't be fetched.
*/
fun getSettings(): ContentValues {
provider!!.query(syncAdapterURI(ContactsContract.Settings.CONTENT_URI), null, null, null, null)?.use { cursor ->
if (cursor.moveToNext()) {
val values = ContentValues(cursor.columnCount)
DatabaseUtils.cursorRowToContentValues(cursor, values)
return values
var settings: ContentValues
/**
* Retrieves [ContactsContract.Settings] for the current address book.
* @throws FileNotFoundException if the settings row couldn't be fetched.
* @throws android.os.RemoteException on content provider errors
*/
get() {
provider!!.query(syncAdapterURI(ContactsContract.Settings.CONTENT_URI), null, null, null, null)?.use { cursor ->
if (cursor.moveToNext()) {
val values = ContentValues(cursor.columnCount)
DatabaseUtils.cursorRowToContentValues(cursor, values)
return values
}
}
throw FileNotFoundException()
}
throw FileNotFoundException()
}
/**
* Updates [ContactsContract.Settings] by inserting the given values into
* the current address book.
* @param values settings to be updated
*/
fun updateSettings(values: ContentValues) {
values.put(ContactsContract.Settings.ACCOUNT_NAME, account.name)
values.put(ContactsContract.Settings.ACCOUNT_TYPE, account.type)
provider!!.insert(syncAdapterURI(ContactsContract.Settings.CONTENT_URI), values)
}
// account-specific address book sync state
fun readSyncState(): ByteArray? = ContactsContract.SyncState.get(provider, account)
fun writeSyncState(data: ByteArray) = ContactsContract.SyncState.set(provider, account, data)
/**
* Updates [ContactsContract.Settings] by inserting the given values into
* the current address book.
* @param values settings to be updated
* @throws android.os.RemoteException on content provider errors
*/
set(values) {
values.put(ContactsContract.Settings.ACCOUNT_NAME, account.name)
values.put(ContactsContract.Settings.ACCOUNT_TYPE, account.type)
provider!!.insert(syncAdapterURI(ContactsContract.Settings.CONTENT_URI), values)
}
var syncState: ByteArray?
get() = ContactsContract.SyncState.get(provider, account)
set(data: ByteArray?) = ContactsContract.SyncState.set(provider, account, data)
// groups
protected fun queryContacts(where: String?, whereArgs: Array<String>?): List<T1> {
fun queryContacts(where: String?, whereArgs: Array<String>?): List<T1> {
val contacts = LinkedList<T1>()
provider!!.query(rawContactsSyncUri(),
arrayOf(RawContacts._ID, AndroidContact.COLUMN_FILENAME, AndroidContact.COLUMN_ETAG),
where, whereArgs, null)?.let { cursor ->
while (cursor.moveToNext())
contacts += contactFactory.newInstance(this, cursor.getLong(0), cursor.getString(1), cursor.getString(2))
null, where, whereArgs, null)?.let { cursor ->
while (cursor.moveToNext()) {
val values = ContentValues(cursor.columnCount)
DatabaseUtils.cursorRowToContentValues(cursor, values)
contacts += contactFactory.fromProvider(this, values)
}
}
return contacts
}
......@@ -79,20 +78,30 @@ open class AndroidAddressBook<T1: AndroidContact, T2: AndroidGroup>(
provider!!.query(groupsSyncUri(),
arrayOf(Groups._ID, AndroidGroup.COLUMN_FILENAME, AndroidGroup.COLUMN_ETAG),
where, whereArgs, null)?.use { cursor ->
while (cursor.moveToNext())
groups += groupFactory.newInstance(this, cursor.getLong(0), cursor.getString(1), cursor.getString(2))
while (cursor.moveToNext()) {
val values = ContentValues(cursor.columnCount)
DatabaseUtils.cursorRowToContentValues(cursor, values)
groups += groupFactory.fromProvider(this, values)
}
}
return groups
}
fun findContactByID(id: Long) =
queryContacts("${RawContacts._ID}=?", arrayOf(id.toString())).firstOrNull()
?: throw FileNotFoundException()
fun findContactByUID(uid: String) =
queryContacts("${AndroidContact.COLUMN_UID}=?", arrayOf(uid)).firstOrNull()
// helpers
fun syncAdapterURI(uri: Uri) = uri.buildUpon()
.appendQueryParameter(RawContacts.ACCOUNT_NAME, account.name)
.appendQueryParameter(RawContacts.ACCOUNT_TYPE, account.type)
.appendQueryParameter(ContactsContract.CALLER_IS_SYNCADAPTER, "true")
.build()!!
.appendQueryParameter(ContactsContract.CALLER_IS_SYNCADAPTER, "true")
.appendQueryParameter(RawContacts.ACCOUNT_NAME, account.name)
.appendQueryParameter(RawContacts.ACCOUNT_TYPE, account.type)
.build()!!
fun rawContactsSyncUri() = syncAdapterURI(RawContacts.CONTENT_URI)
fun groupsSyncUri() = syncAdapterURI(Groups.CONTENT_URI)
......
......@@ -8,7 +8,6 @@
package at.bitfire.vcard4android;
import android.annotation.SuppressLint
import android.content.ContentProviderOperation
import android.content.ContentUris
import android.content.ContentValues
......@@ -48,17 +47,15 @@ open class AndroidContact(
companion object {
@JvmField val COLUMN_FILENAME = RawContacts.SOURCE_ID
@JvmField val COLUMN_UID = RawContacts.SYNC1
@JvmField val COLUMN_ETAG = RawContacts.SYNC2
const val COLUMN_FILENAME = RawContacts.SOURCE_ID
const val COLUMN_UID = RawContacts.SYNC1
const val COLUMN_ETAG = RawContacts.SYNC2
@JvmStatic
fun labelToXName(label: String) = "X-" + label
.replace(" ","_")
.replace(Regex("[^\\p{L}\\p{Nd}\\-_]"), "") // TODO check regex
.replace(Regex("[^\\p{L}\\p{Nd}\\-_]"), "")
.toUpperCase()
@JvmStatic
fun xNameToLabel(xname: String): String {
// "X-MY_PROPERTY"
var s = xname.toLowerCase() // 1. ensure lower case -> "x-my_property"
......@@ -68,7 +65,6 @@ open class AndroidContact(
return WordUtils.capitalize(s) // 4. capitalize -> "My Property"
}
@JvmStatic
fun toURIScheme(s: String?) =
// RFC 3986 3.1
// scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." )
......@@ -79,108 +75,116 @@ open class AndroidContact(
}
var id: Long? = null
protected set
var fileName: String? = null
protected set
var eTag: String? = null
val photoMaxDimensions: Int by lazy { queryPhotoMaxDimensions() }
protected val photoMaxDimensions: Int by lazy { queryPhotoMaxDimensions() }
constructor(addressBook: AndroidAddressBook<out AndroidContact, out AndroidGroup>, id: Long, fileName: String?, eTag: String?): this(addressBook) {
this.id = id
this.fileName = fileName
this.eTag = eTag
constructor(addressBook: AndroidAddressBook<out AndroidContact, out AndroidGroup>, values: ContentValues)
: this(addressBook) {
this.id = values.getAsLong(RawContacts._ID)
this.fileName = values.getAsString(COLUMN_FILENAME)
this.eTag = values.getAsString(COLUMN_ETAG)
}
constructor(addressBook: AndroidAddressBook<out AndroidContact, out AndroidGroup>, contact: Contact, fileName: String?, eTag: String?): this(addressBook) {
constructor(addressBook: AndroidAddressBook<out AndroidContact, out AndroidGroup>, contact: Contact, fileName: String?, eTag: String?)
: this(addressBook) {
this.contact = contact
this.fileName = fileName
this.eTag = eTag
}
var contact: Contact? = null
@Throws(FileNotFoundException::class, ContactsStorageException::class)
get() {
field?.let { return field }
val id = requireNotNull(id)
var iter: EntityIterator? = null
try {
@SuppressLint("Recycle")
iter = RawContacts.newEntityIterator(addressBook.provider!!.query(
addressBook.syncAdapterURI(ContactsContract.RawContactsEntity.CONTENT_URI),
null, ContactsContract.RawContacts._ID + "=?", arrayOf(id.toString()), null))
if (iter.hasNext()) {
val e = iter.next()
field = Contact()
populateContact(e.entityValues)
val subValues = e.subValues
for (subValue in subValues) {
val values = subValue.values
// remove empty values
val it = values.keySet().iterator()
while (it.hasNext()) {
val obj = values[it.next()]
if (obj is String && obj.isEmpty())
it.remove()
}
val mimeType = values.getAsString(ContactsContract.RawContactsEntity.MIMETYPE)
when (mimeType) {
StructuredName.CONTENT_ITEM_TYPE ->
populateStructuredName(values)
Phone.CONTENT_ITEM_TYPE ->
populatePhoneNumber(values)
Email.CONTENT_ITEM_TYPE ->
populateEmail(values)
Photo.CONTENT_ITEM_TYPE ->
populatePhoto(values)
Organization.CONTENT_ITEM_TYPE ->
populateOrganization(values)
Im.CONTENT_ITEM_TYPE ->
populateIMPP(values)
Nickname.CONTENT_ITEM_TYPE ->
populateNickname(values)
Note.CONTENT_ITEM_TYPE ->
populateNote(values)
StructuredPostal.CONTENT_ITEM_TYPE ->
populateStructuredPostal(values)
Website.CONTENT_ITEM_TYPE ->
populateWebsite(values)
Event.CONTENT_ITEM_TYPE ->
populateEvent(values)
Relation.CONTENT_ITEM_TYPE ->
populateRelation(values)
SipAddress.CONTENT_ITEM_TYPE ->
populateSipAddress(values)
null ->
Constants.log.warning("Ignoring raw contact data row without ${ContactsContract.RawContactsEntity.MIMETYPE}")
else ->
populateData(mimeType, values)
/**
* Fetches contact data from the contacts provider.
* @throws IllegalArgumentException if contact has not been saved yet
* @throws FileNotFoundException when the contact is not available (anymore)
* @throws RemoteException on contact provider errors
*/
get() {
field?.let { return field }
val id = requireNotNull(id)
var iter: EntityIterator? = null
try {
iter = RawContacts.newEntityIterator(addressBook.provider!!.query(
addressBook.syncAdapterURI(ContactsContract.RawContactsEntity.CONTENT_URI),
null, ContactsContract.RawContacts._ID + "=?", arrayOf(id.toString()), null))
if (iter.hasNext()) {
val e = iter.next()
field = Contact()
populateContact(e.entityValues)
val subValues = e.subValues
for (subValue in subValues) {
val values = subValue.values
// remove empty values
val it = values.keySet().iterator()
while (it.hasNext()) {
val obj = values[it.next()]
if (obj is String && obj.isEmpty())
it.remove()
}
val mimeType = values.getAsString(ContactsContract.RawContactsEntity.MIMETYPE)
when (mimeType) {
StructuredName.CONTENT_ITEM_TYPE ->
populateStructuredName(values)
Phone.CONTENT_ITEM_TYPE ->
populatePhoneNumber(values)
Email.CONTENT_ITEM_TYPE ->
populateEmail(values)
Photo.CONTENT_ITEM_TYPE ->
populatePhoto(values)
Organization.CONTENT_ITEM_TYPE ->
populateOrganization(values)
Im.CONTENT_ITEM_TYPE ->
populateIMPP(values)
Nickname.CONTENT_ITEM_TYPE ->
populateNickname(values)
Note.CONTENT_ITEM_TYPE ->
populateNote(values)
StructuredPostal.CONTENT_ITEM_TYPE ->
populateStructuredPostal(values)
Website.CONTENT_ITEM_TYPE ->
populateWebsite(values)
Event.CONTENT_ITEM_TYPE ->
populateEvent(values)
Relation.CONTENT_ITEM_TYPE ->
populateRelation(values)
SipAddress.CONTENT_ITEM_TYPE ->
populateSipAddress(values)
null ->
Constants.log.warning("Ignoring raw contact data row without ${ContactsContract.RawContactsEntity.MIMETYPE}")
else ->
populateData(mimeType, values)
}
}
}
return field
} else
throw FileNotFoundException()
} catch(e: RemoteException) {
throw ContactsStorageException("Couldn't read local contact", e)
} finally {
iter?.close()
return field
} else
throw FileNotFoundException()
} finally {
iter?.close()
}
}
}
protected fun populateContact(row: ContentValues) {
protected open fun populateContact(row: ContentValues) {
fileName = row.getAsString(COLUMN_FILENAME)
eTag = row.getAsString(COLUMN_ETAG)
contact!!.uid = row.getAsString(COLUMN_UID)
}
protected fun populateStructuredName(row: ContentValues) {
protected open fun populateStructuredName(row: ContentValues) {
val contact = requireNotNull(contact)
contact.displayName = row.getAsString(StructuredName.DISPLAY_NAME)
......@@ -195,7 +199,7 @@ open class AndroidContact(
contact.phoneticFamilyName = row.getAsString(StructuredName.PHONETIC_FAMILY_NAME)
}
protected fun populatePhoneNumber(row: ContentValues) {
protected open fun populatePhoneNumber(row: ContentValues) {
val number = Telephone(row.getAsString(Phone.NUMBER))
val labeledNumber = LabeledProperty(number)
......@@ -259,7 +263,7 @@ open class AndroidContact(
contact!!.phoneNumbers += labeledNumber
}
protected fun populateEmail(row: ContentValues) {
protected open fun populateEmail(row: ContentValues) {
val email = ezvcard.property.Email(row.getAsString(Email.ADDRESS))
val labeledEmail = LabeledProperty(email)
......@@ -282,8 +286,7 @@ open class AndroidContact(
contact!!.emails += labeledEmail
}
@Throws(RemoteException::class)
protected fun populatePhoto(row: ContentValues) {
protected open fun populatePhoto(row: ContentValues) {
val contact = requireNotNull(contact)
if (row.containsKey(Photo.PHOTO_FILE_ID)) {
val photoUri = Uri.withAppendedPath(
......@@ -300,7 +303,7 @@ open class AndroidContact(
contact.photo = row.getAsByteArray(Photo.PHOTO)
}
protected fun populateOrganization(row: ContentValues) {
protected open fun populateOrganization(row: ContentValues) {
val contact = requireNotNull(contact)
val company = row.getAsString(Organization.COMPANY)
......@@ -316,7 +319,7 @@ open class AndroidContact(
row.getAsString(Organization.JOB_DESCRIPTION)?.let { contact.jobDescription = it }
}
protected fun populateIMPP(row: ContentValues) {
protected open fun populateIMPP(row: ContentValues) {
val handle = row.getAsString(Im.DATA)
if (handle == null) {
Constants.log.warning("Ignoring instant messenger record without handle")
......@@ -370,7 +373,7 @@ open class AndroidContact(
}
}
protected fun populateNickname(row: ContentValues) {
protected open fun populateNickname(row: ContentValues) {
row.getAsString(Nickname.NAME)?.let { name ->
val nick = ezvcard.property.Nickname()
nick.values += name
......@@ -392,11 +395,11 @@ open class AndroidContact(
}
}
protected fun populateNote(row: ContentValues) {
protected open fun populateNote(row: ContentValues) {
contact!!.note = row.getAsString(Note.NOTE)
}
protected fun populateStructuredPostal(row: ContentValues) {
protected open fun populateStructuredPostal(row: ContentValues) {
val address = Address()
val labeledAddress = LabeledProperty(address)
......@@ -423,7 +426,7 @@ open class AndroidContact(
contact!!.addresses += labeledAddress
}
protected fun populateWebsite(row: ContentValues) {
protected open fun populateWebsite(row: ContentValues) {
val url = Url(row.getAsString(Website.URL))
val labeledUrl = LabeledProperty(url)
......@@ -449,7 +452,7 @@ open class AndroidContact(
contact!!.urls += labeledUrl
}
protected fun populateEvent(row: ContentValues) {
protected open fun populateEvent(row: ContentValues) {
val dateStr = row.getAsString(CommonDataKinds.Event.START_DATE)
var full: Date? = null
var partial: PartialDate? = null
......@@ -473,7 +476,7 @@ open class AndroidContact(
}
}
protected fun populateRelation(row: ContentValues) {
protected open fun populateRelation(row: ContentValues) {
row.getAsString(Relation.NAME)?.let { name ->
val related = Related()
related.text = name
......@@ -509,7 +512,7 @@ open class AndroidContact(
}
}
protected fun populateSipAddress(row: ContentValues) {
protected open fun populateSipAddress(row: ContentValues) {
try {
val impp = Impp("sip:" + row.getAsString(SipAddress.SIP_ADDRESS))
val labeledImpp = LabeledProperty(impp)
......@@ -537,12 +540,10 @@ open class AndroidContact(
* @param mimeType MIME type of the row
* @param row values of the row
*/
@Throws(ContactsStorageException::class)
open protected fun populateData(mimeType: String, row: ContentValues) {
protected open fun populateData(mimeType: String, row: ContentValues) {
}
@Throws(ContactsStorageException::class)
fun create(): Uri {
val batch = BatchOperation(addressBook.provider!!)
......@@ -562,7 +563,6 @@ open class AndroidContact(
return result.uri
}
@Throws(ContactsStorageException::class)
fun update(contact: Contact): Int {
this.contact = contact
......@@ -586,16 +586,10 @@ open class AndroidContact(
return results
}
@Throws(ContactsStorageException::class)
fun delete() =
try {
addressBook.provider!!.delete(rawContactSyncURI(), null, null)
} catch (e: RemoteException) {
throw ContactsStorageException("Couldn't delete local contact", e)
}
fun delete() = addressBook.provider!!.delete(rawContactSyncURI(), null, null)
protected fun buildContact(builder: ContentProviderOperation.Builder, update: Boolean) {
protected open fun buildContact(builder: ContentProviderOperation.Builder, update: Boolean) {
if (!update)
builder .withValue(RawContacts.ACCOUNT_NAME, addressBook.account.name)
.withValue(RawContacts.ACCOUNT_TYPE, addressBook.account.type)
......@@ -615,10 +609,9 @@ open class AndroidContact(
* Override this (and call [super]!) to add custom data rows,
* for example generated from some properties of [contact].
* @param batch batch operation used to insert the data rows
* @throws ContactsStorageException on contact provider errors
* @throws RemoteException on contact provider errors
*/
@Throws(ContactsStorageException::class)
open protected fun insertDataRows(batch: BatchOperation) {
protected open fun insertDataRows(batch: BatchOperation) {
val contact = requireNotNull(contact)
insertStructuredName(batch)
......@@ -638,7 +631,7 @@ open class AndroidContact(
contact.birthDay?.let { insertEvent(batch, Event.TYPE_BIRTHDAY, it) }
}
protected fun insertStructuredName(batch: BatchOperation) {
protected open fun insertStructuredName(batch: BatchOperation) {
val contact = requireNotNull(contact)
if (contact.displayName == null &&
contact.prefix == null &&
......@@ -669,7 +662,7 @@ open class AndroidContact(
batch.enqueue(op)
}
protected fun insertPhoneNumber(batch: BatchOperation, labeledNumber: LabeledProperty<Telephone>) {
protected open fun insertPhoneNumber(batch: BatchOperation, labeledNumber: LabeledProperty<Telephone>) {
val number = labeledNumber.property
val types = number.types
......@@ -761,7 +754,7 @@ open class AndroidContact(
batch.enqueue(op)
}
protected fun insertEmail(batch: BatchOperation, labeledEmail: LabeledProperty<ezvcard.property.Email>) {
protected open fun insertEmail(batch: BatchOperation, labeledEmail: LabeledProperty<ezvcard.property.Email>) {
val email = labeledEmail.property
val types = email.types
......@@ -819,7 +812,7 @@ open class AndroidContact(
batch.enqueue(op)
}
protected fun insertOrganization(batch: BatchOperation) {
protected open fun insertOrganization(batch: BatchOperation) {
val contact = requireNotNull(contact)
if (contact.organization == null && contact.jobTitle == null && contact.jobDescription == null)
return
......@@ -852,7 +845,7 @@ open class AndroidContact(
batch.enqueue(op)
}
protected fun insertIMPP(batch: BatchOperation, labeledImpp: LabeledProperty<Impp>) {
protected open fun insertIMPP(batch: BatchOperation, labeledImpp: LabeledProperty<Impp>) {
val impp = labeledImpp.property
var typeCode: Int = Im.TYPE_OTHER
......@@ -932,7 +925,7 @@ open class AndroidContact(
batch.enqueue(op)
}
protected fun insertNickname(batch: BatchOperation) {
protected open fun insertNickname(batch: BatchOperation) {
val nick = contact!!.nickName
if (nick == null || nick.values.isEmpty())
return
......@@ -969,7 +962,7 @@ open class AndroidContact(
batch.enqueue(op)
}
protected fun insertNote(batch: BatchOperation) {
protected open fun insertNote(batch: BatchOperation) {
val contact = requireNotNull(contact)
if (contact.note.isNullOrEmpty())
return
......@@ -988,7 +981,7 @@ open class AndroidContact(
batch.enqueue(op)
}