AndroidAddressBook.kt 4.37 KB
Newer Older
Ricki Hirner's avatar
Ricki Hirner committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126
/*
 * Copyright © 2013 – 2015 Ricki Hirner (bitfire web engineering).
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the GNU Public License v3.0
 * which accompanies this distribution, and is available at
 * http://www.gnu.org/licenses/gpl.html
 */

package at.bitfire.vcard4android

import android.accounts.Account
import android.annotation.SuppressLint
import android.content.ContentProviderClient
import android.content.ContentValues
import android.database.DatabaseUtils
import android.net.Uri
import android.os.RemoteException
import android.provider.ContactsContract
import android.provider.ContactsContract.Groups
import android.provider.ContactsContract.RawContacts
import java.io.FileNotFoundException
import java.util.*

open class AndroidAddressBook<out T1: AndroidContact, out T2: AndroidGroup>(
        var account: Account,
        val provider: ContentProviderClient,
        val contactFactory: AndroidContactFactory<T1>,
        val groupFactory: AndroidGroupFactory<T2>
) {

	// account-specific address book settings

    @Throws(ContactsStorageException::class)
	fun getSettings(): ContentValues {
        val values = ContentValues()
        try {
			provider.query(syncAdapterURI(ContactsContract.Settings.CONTENT_URI), null, null, null, null)?.use { cursor ->
                if (cursor.moveToNext())
                    DatabaseUtils.cursorRowToContentValues(cursor, values)
                else
                    throw FileNotFoundException()
            }
		} catch(e: Exception) {
			throw ContactsStorageException("Couldn't read address book settings", e)
		}
        return values
	}

    @Throws(ContactsStorageException::class)
	fun updateSettings(values: ContentValues) {
		values.put(ContactsContract.Settings.ACCOUNT_NAME, account.name)
		values.put(ContactsContract.Settings.ACCOUNT_TYPE, account.type)
		try {
			provider.insert(syncAdapterURI(ContactsContract.Settings.CONTENT_URI), values)
		} catch(e: RemoteException) {
			throw ContactsStorageException("Couldn't write address book settings", e)
		}
	}


	// account-specific address book sync state

    @Throws(ContactsStorageException::class)
	fun getSyncState(): ByteArray =
		try {
			ContactsContract.SyncState.get(provider, account)
		} catch(e: RemoteException) {
			throw ContactsStorageException("Couldn't read address book sync state", e)
		}

    @Throws(ContactsStorageException::class)
	fun setSyncState(data: ByteArray) {
		try {
			ContactsContract.SyncState.set(provider, account, data)
		} catch(e: RemoteException) {
			throw ContactsStorageException("Couldn't write contacts sync state", e)
		}
	}


	// groups

    @SuppressLint("Recycle")
    @Throws(ContactsStorageException::class)
    protected fun queryContacts(where: String?, whereArgs: Array<String>?): List<T1> {
        val contacts = LinkedList<T1>()
        try {
            provider.query(syncAdapterURI(RawContacts.CONTENT_URI),
                    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))
            }
        } catch(e: RemoteException) {
            throw ContactsStorageException("Couldn't query contacts", e)
        }
        return contacts
    }

    @SuppressLint("Recycle")
    @Throws(ContactsStorageException::class)
	protected fun queryGroups(where: String?, whereArgs: Array<String>?): List<T2> {
        val groups = LinkedList<T2>()
		try {
			provider.query(syncAdapterURI(Groups.CONTENT_URI),
					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))
            }
		} catch(e: RemoteException) {
			throw ContactsStorageException("Couldn't query contact groups", e)
		}
        return groups
	}


	// 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()!!

}