From 98948ac9b6e96b1e3c7d247460ece8e4ab4a5a66 Mon Sep 17 00:00:00 2001 From: dsremo Date: Wed, 13 May 2026 14:41:41 +0530 Subject: [PATCH] MainActivity: auto-refresh contact list on system contact changes When a contact is added/edited/deleted by another app (e.g., system contact picker, sync adapter, contact-import tools), Fossify Contacts currently only re-reads the contact list on next onResume(). If the user is already on the contact list when the change happens, the stale list stays on screen until they navigate away and back. This registers a ContentObserver on ContactsContract.Contacts.CONTENT_URI in onResume() and unregisters in onPause(). When the observer fires, refreshContacts(ALL_TABS_MASK) is debounced by 500ms (to coalesce multi-row bulk operations) and executed on the main thread handler. Behaviour outside of registered visibility (i.e., when the activity is paused/destroyed) is unchanged - no work happens off-screen. The observer runs only while the activity is visible. --- .../contacts/activities/MainActivity.kt | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/app/src/main/kotlin/org/fossify/contacts/activities/MainActivity.kt b/app/src/main/kotlin/org/fossify/contacts/activities/MainActivity.kt index d26b748f5..302e0ccc7 100644 --- a/app/src/main/kotlin/org/fossify/contacts/activities/MainActivity.kt +++ b/app/src/main/kotlin/org/fossify/contacts/activities/MainActivity.kt @@ -4,10 +4,14 @@ import android.annotation.SuppressLint import android.content.ActivityNotFoundException import android.content.Intent import android.content.pm.ShortcutInfo +import android.database.ContentObserver import android.graphics.drawable.ColorDrawable import android.graphics.drawable.Icon import android.graphics.drawable.LayerDrawable import android.os.Bundle +import android.os.Handler +import android.os.Looper +import android.provider.ContactsContract import androidx.viewpager.widget.ViewPager import me.grantland.widget.AutofitHelper import org.fossify.commons.databases.ContactsDatabase @@ -143,12 +147,14 @@ class MainActivity : SimpleActivity(), RefreshContactsListener { isFirstResume = false checkShortcuts() + registerContactObserver() } override fun onPause() { super.onPause() storeStateVariables() config.lastUsedViewPagerPage = binding.viewPager.currentItem + unregisterContactObserver() } override fun onDestroy() { @@ -158,6 +164,38 @@ class MainActivity : SimpleActivity(), RefreshContactsListener { } } + private val contactObserverHandler = Handler(Looper.getMainLooper()) + private val contactReloadRunnable = Runnable { + if (werePermissionsHandled) { + refreshContacts(ALL_TABS_MASK) + } + } + private var contactObserver: ContentObserver? = null + + private fun registerContactObserver() { + unregisterContactObserver() + val observer = object : ContentObserver(contactObserverHandler) { + override fun onChange(selfChange: Boolean) { + contactObserverHandler.removeCallbacks(contactReloadRunnable) + contactObserverHandler.postDelayed(contactReloadRunnable, 500) + } + } + runCatching { + contentResolver.registerContentObserver( + ContactsContract.Contacts.CONTENT_URI, true, observer + ) + contactObserver = observer + } + } + + private fun unregisterContactObserver() { + contactObserverHandler.removeCallbacks(contactReloadRunnable) + contactObserver?.let { observer -> + runCatching { contentResolver.unregisterContentObserver(observer) } + } + contactObserver = null + } + override fun onBackPressedCompat(): Boolean { return if (binding.mainMenu.isSearchOpen) { binding.mainMenu.closeSearch()