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
|
package org.meshbay.client.phonesync
import android.Manifest
import android.content.Context
import android.net.Uri
import android.provider.ContactsContract
import org.meshbay.client.photos.PhotoChannels
import java.io.File
import java.security.MessageDigest
import java.util.TimeZone
/**
* The phone's contacts, as the platform itself exports them: one vCard per
* contact from ContactsContract, the format any phone imports back.
*
* Every run writes the whole address book and sends it only when it differs
* from the last one sent (`marker` is its SHA-256): a file is a complete
* copy as of its date, so restoring is importing the newest one.
*/
class ContactSource(private val context: Context) : DocSource {
override val permissions = arrayOf(Manifest.permission.READ_CONTACTS)
override val suffix = "contacts"
override fun export(marker: String?, dir: File, now: Long, zone: TimeZone): Export? {
val keys = ArrayList<String>()
context.contentResolver.query(
ContactsContract.Contacts.CONTENT_URI, arrayOf(ContactsContract.Contacts.LOOKUP_KEY),
null, null, ContactsContract.Contacts._ID,
)?.use { c -> while (c.moveToNext()) c.getString(0)?.let { keys += it } }
if (keys.isEmpty()) return null
val file = File(dir, "contacts.vcf")
val digest = MessageDigest.getInstance("SHA-256")
var count = 0
file.outputStream().buffered().use { out ->
for (key in keys) {
val uri = Uri.withAppendedPath(ContactsContract.Contacts.CONTENT_VCARD_URI, Uri.encode(key))
// A contact removed between the list and its card is simply not in this copy.
val bytes = try {
context.contentResolver.openInputStream(uri)?.use { it.readBytes() }
} catch (e: Exception) { null } ?: continue
out.write(bytes)
digest.update(bytes)
count += 1
}
}
val sha = PhotoChannels.hex(digest.digest())
if (count == 0 || sha == marker) { file.delete(); return null }
return Export(file, DocPlan.nameFor("contacts", "vcf", now, zone), "text/vcard", sha, count)
}
}
|