diff options
| author | JustZvan <justzvan@justzvan.xyz> | 2026-02-06 13:38:36 +0100 |
|---|---|---|
| committer | JustZvan <justzvan@justzvan.xyz> | 2026-02-06 13:38:36 +0100 |
| commit | adb6a4fd9ec3a23c04d5e4c2ce799448237915c4 (patch) | |
| tree | 786edcf5888788e0667a90fae96d7ebec68c507a /app/src/main/java/sh/lajo/buddy/ContactsObserver.kt | |
feat: initial commit
Diffstat (limited to 'app/src/main/java/sh/lajo/buddy/ContactsObserver.kt')
| -rw-r--r-- | app/src/main/java/sh/lajo/buddy/ContactsObserver.kt | 186 |
1 files changed, 186 insertions, 0 deletions
diff --git a/app/src/main/java/sh/lajo/buddy/ContactsObserver.kt b/app/src/main/java/sh/lajo/buddy/ContactsObserver.kt new file mode 100644 index 0000000..50b7a48 --- /dev/null +++ b/app/src/main/java/sh/lajo/buddy/ContactsObserver.kt @@ -0,0 +1,186 @@ +package sh.lajo.buddy + +import android.Manifest +import android.content.ContentResolver +import android.content.Context +import android.content.Intent +import android.content.pm.PackageManager +import android.database.ContentObserver +import android.net.Uri +import android.os.Handler +import android.os.Looper +import android.provider.ContactsContract +import android.util.Log +import androidx.core.content.ContextCompat + +class ContactsObserver( + private val context: Context, + private val contentResolver: ContentResolver +) : ContentObserver(Handler(Looper.getMainLooper())) { + + companion object { + private const val TAG = "ContactsObserver" + } + + private var lastContactCount = -1 + + init { + // Initialize the contact count + lastContactCount = getContactCount() + } + + override fun onChange(selfChange: Boolean) { + onChange(selfChange, null) + } + + override fun onChange(selfChange: Boolean, uri: Uri?) { + + // Check if we have READ_CONTACTS permission + if (ContextCompat.checkSelfPermission( + context, + Manifest.permission.READ_CONTACTS + ) != PackageManager.PERMISSION_GRANTED + ) { + Log.w(TAG, "No READ_CONTACTS permission, skipping contact change") + return + } + + Log.d(TAG, "Contact change detected, URI: $uri") + + // Get current contact count + val currentCount = getContactCount() + + // If count increased, a contact was likely added + if (lastContactCount >= 0 && currentCount > lastContactCount) { + Log.d(TAG, "Contact added detected (count: $lastContactCount -> $currentCount)") + // Find and send the new contact(s) + findAndSendNewContacts() + } + + lastContactCount = currentCount + } + + private fun getContactCount(): Int { + try { + val cursor = contentResolver.query( + ContactsContract.Contacts.CONTENT_URI, + arrayOf(ContactsContract.Contacts._ID), + null, + null, + null + ) + cursor?.use { + return it.count + } + } catch (e: Exception) { + Log.e(TAG, "Error getting contact count", e) + } + return 0 + } + + private fun findAndSendNewContacts() { + try { + // Query the most recently added contacts + val cursor = contentResolver.query( + ContactsContract.Contacts.CONTENT_URI, + arrayOf( + ContactsContract.Contacts._ID, + ContactsContract.Contacts.DISPLAY_NAME + ), + null, + null, + "${ContactsContract.Contacts.CONTACT_LAST_UPDATED_TIMESTAMP} DESC" + ) + + cursor?.use { + if (it.moveToFirst()) { + // Get the most recent contact + val contactId = it.getString(it.getColumnIndexOrThrow(ContactsContract.Contacts._ID)) + val name = it.getString(it.getColumnIndexOrThrow(ContactsContract.Contacts.DISPLAY_NAME)) + + val phoneNumbers = getPhoneNumbers(contactId) + val emails = getEmails(contactId) + + // Send the contact info via WebSocket + sendContactAddedEvent(name, phoneNumbers, emails) + } + } + } catch (e: Exception) { + Log.e(TAG, "Error finding new contacts", e) + } + } + + private fun getPhoneNumbers(contactId: String): List<String> { + val phoneNumbers = mutableListOf<String>() + try { + val cursor = contentResolver.query( + ContactsContract.CommonDataKinds.Phone.CONTENT_URI, + arrayOf(ContactsContract.CommonDataKinds.Phone.NUMBER), + "${ContactsContract.CommonDataKinds.Phone.CONTACT_ID} = ?", + arrayOf(contactId), + null + ) + + cursor?.use { + while (it.moveToNext()) { + val number = it.getString(it.getColumnIndexOrThrow(ContactsContract.CommonDataKinds.Phone.NUMBER)) + phoneNumbers.add(number) + } + } + } catch (e: Exception) { + Log.e(TAG, "Error getting phone numbers", e) + } + return phoneNumbers + } + + private fun getEmails(contactId: String): List<String> { + val emails = mutableListOf<String>() + try { + val cursor = contentResolver.query( + ContactsContract.CommonDataKinds.Email.CONTENT_URI, + arrayOf(ContactsContract.CommonDataKinds.Email.ADDRESS), + "${ContactsContract.CommonDataKinds.Email.CONTACT_ID} = ?", + arrayOf(contactId), + null + ) + + cursor?.use { + while (it.moveToNext()) { + val email = it.getString(it.getColumnIndexOrThrow(ContactsContract.CommonDataKinds.Email.ADDRESS)) + emails.add(email) + } + } + } catch (e: Exception) { + Log.e(TAG, "Error getting emails", e) + } + return emails + } + + private fun sendContactAddedEvent(name: String, phoneNumbers: List<String>, emails: List<String>) { + Log.d(TAG, "Sending contact added event: $name, phones: $phoneNumbers, emails: $emails") + + val intent = Intent(context, WebSocketService::class.java).apply { + action = WebSocketService.ACTION_SEND_CONTACT + putExtra(WebSocketService.EXTRA_CONTACT_NAME, name) + putExtra(WebSocketService.EXTRA_CONTACT_PHONES, phoneNumbers.toTypedArray()) + putExtra(WebSocketService.EXTRA_CONTACT_EMAILS, emails.toTypedArray()) + } + + context.startForegroundService(intent) + } + + fun register() { + contentResolver.registerContentObserver( + ContactsContract.Contacts.CONTENT_URI, + true, + this + ) + Log.d(TAG, "ContactsObserver registered") + } + + fun unregister() { + contentResolver.unregisterContentObserver(this) + Log.d(TAG, "ContactsObserver unregistered") + } +} + |