refactor(contacts): one Context.hasContactsPermission() helper

The READ_CONTACTS check was written out three times (data source, RootScreen
resume trigger, settings screen). A single extension in the contacts package
mirrors the existing Context.hasCalendarPermission() precedent; the settings
call site follows in the settings cleanup commit.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-02 22:40:44 +02:00
parent 442697d57c
commit b8a420bdab
2 changed files with 8 additions and 7 deletions

View File

@@ -28,6 +28,11 @@ interface ContactSpecialDatesDataSource {
fun readSpecialDates(): List<ContactSpecialDate> fun readSpecialDates(): List<ContactSpecialDate>
} }
/** Whether `READ_CONTACTS` is granted — the gate for every contacts read. */
fun Context.hasContactsPermission(): Boolean =
ContextCompat.checkSelfPermission(this, Manifest.permission.READ_CONTACTS) ==
PackageManager.PERMISSION_GRANTED
/** Map a `ContactsContract` event `TYPE` to our calendar bucket. */ /** Map a `ContactsContract` event `TYPE` to our calendar bucket. */
internal fun specialDateTypeForRawEventType(type: Int): SpecialDateType = when (type) { internal fun specialDateTypeForRawEventType(type: Int): SpecialDateType = when (type) {
Event.TYPE_BIRTHDAY -> SpecialDateType.Birthday Event.TYPE_BIRTHDAY -> SpecialDateType.Birthday
@@ -40,9 +45,7 @@ class AndroidContactSpecialDatesDataSource @Inject constructor(
@ApplicationContext private val context: Context, @ApplicationContext private val context: Context,
) : ContactSpecialDatesDataSource { ) : ContactSpecialDatesDataSource {
override fun hasPermission(): Boolean = override fun hasPermission(): Boolean = context.hasContactsPermission()
ContextCompat.checkSelfPermission(context, Manifest.permission.READ_CONTACTS) ==
PackageManager.PERMISSION_GRANTED
override fun readSpecialDates(): List<ContactSpecialDate> { override fun readSpecialDates(): List<ContactSpecialDate> {
if (!hasPermission()) return emptyList() if (!hasPermission()) return emptyList()

View File

@@ -20,6 +20,7 @@ import androidx.core.content.ContextCompat
import androidx.lifecycle.Lifecycle import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleEventObserver import androidx.lifecycle.LifecycleEventObserver
import de.jeanlucmakiola.calendula.data.contacts.SpecialDatesScheduler import de.jeanlucmakiola.calendula.data.contacts.SpecialDatesScheduler
import de.jeanlucmakiola.calendula.data.contacts.hasContactsPermission
import de.jeanlucmakiola.calendula.ui.permission.PermissionScreen import de.jeanlucmakiola.calendula.ui.permission.PermissionScreen
import de.jeanlucmakiola.calendula.ui.permission.ReminderOnboardingScreen import de.jeanlucmakiola.calendula.ui.permission.ReminderOnboardingScreen
import de.jeanlucmakiola.calendula.ui.permission.ReminderOnboardingViewModel import de.jeanlucmakiola.calendula.ui.permission.ReminderOnboardingViewModel
@@ -53,10 +54,7 @@ fun RootScreen(
// Refresh the contact special-dates mirror on foreground (the // Refresh the contact special-dates mirror on foreground (the
// worker is debounced and no-ops when the feature is off). Gated // worker is debounced and no-ops when the feature is off). Gated
// on the permission so users without the opt-in never enqueue it. // on the permission so users without the opt-in never enqueue it.
if (ContextCompat.checkSelfPermission( if (context.hasContactsPermission()) {
context, Manifest.permission.READ_CONTACTS
) == PackageManager.PERMISSION_GRANTED
) {
SpecialDatesScheduler.runNow(context, foreground = true) SpecialDatesScheduler.runNow(context, foreground = true)
} }
} }