Extract contact permission check to ContactPermissionResolver

This commit is contained in:
cketti 2023-03-10 15:38:00 +01:00
parent c642d7615f
commit b80f153ce3
6 changed files with 87 additions and 32 deletions

View file

@ -1,13 +1,9 @@
package app.k9mail.core.android.common.contact
import android.Manifest
import android.content.ContentResolver
import android.content.Context
import android.content.pm.PackageManager
import android.database.Cursor
import android.net.Uri
import android.provider.ContactsContract
import androidx.core.content.ContextCompat
import app.k9mail.core.android.common.database.EmptyCursor
import app.k9mail.core.android.common.database.getLongOrThrow
import app.k9mail.core.android.common.database.getStringOrNull
@ -21,8 +17,8 @@ interface ContactDataSource {
}
internal class ContentResolverContactDataSource(
private val context: Context,
private val contentResolver: ContentResolver = context.contentResolver,
private val contentResolver: ContentResolver,
private val contactPermissionResolver: ContactPermissionResolver,
) : ContactDataSource {
override fun getContactFor(emailAddress: EmailAddress): Contact? {
@ -57,12 +53,12 @@ internal class ContentResolverContactDataSource(
}
private fun getCursorFor(emailAddress: EmailAddress): Cursor {
val uri = Uri.withAppendedPath(
ContactsContract.CommonDataKinds.Email.CONTENT_LOOKUP_URI,
Uri.encode(emailAddress.address),
)
return if (contactPermissionResolver.hasContactPermission()) {
val uri = Uri.withAppendedPath(
ContactsContract.CommonDataKinds.Email.CONTENT_LOOKUP_URI,
Uri.encode(emailAddress.address),
)
return if (hasContactPermission()) {
contentResolver.query(
uri,
PROJECTION,
@ -75,13 +71,6 @@ internal class ContentResolverContactDataSource(
}
}
private fun hasContactPermission(): Boolean {
return ContextCompat.checkSelfPermission(
context,
Manifest.permission.READ_CONTACTS,
) == PackageManager.PERMISSION_GRANTED
}
private companion object {
private const val SORT_ORDER = ContactsContract.Contacts.DISPLAY_NAME +

View file

@ -1,5 +1,6 @@
package app.k9mail.core.android.common.contact
import android.content.Context
import app.k9mail.core.common.cache.Cache
import app.k9mail.core.common.cache.ExpiringCache
import app.k9mail.core.common.cache.SynchronizedCache
@ -14,7 +15,10 @@ internal val contactModule = module {
)
}
factory<ContactDataSource> {
ContentResolverContactDataSource(context = get())
ContentResolverContactDataSource(
contentResolver = get<Context>().contentResolver,
contactPermissionResolver = get(),
)
}
factory<ContactRepository> {
CachingContactRepository(
@ -22,6 +26,9 @@ internal val contactModule = module {
dataSource = get(),
)
}
factory<ContactPermissionResolver> {
AndroidContactPermissionResolver(context = get())
}
}
internal const val CACHE_NAME = "ContactCache"

View file

@ -0,0 +1,16 @@
package app.k9mail.core.android.common.contact
import android.Manifest.permission.READ_CONTACTS
import android.content.Context
import android.content.pm.PackageManager.PERMISSION_GRANTED
import androidx.core.content.ContextCompat
interface ContactPermissionResolver {
fun hasContactPermission(): Boolean
}
internal class AndroidContactPermissionResolver(private val context: Context) : ContactPermissionResolver {
override fun hasContactPermission(): Boolean {
return ContextCompat.checkSelfPermission(context, READ_CONTACTS) == PERMISSION_GRANTED
}
}

View file

@ -0,0 +1,43 @@
package app.k9mail.core.android.common.contact
import android.Manifest
import assertk.assertThat
import assertk.assertions.isFalse
import assertk.assertions.isTrue
import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.RobolectricTestRunner
import org.robolectric.RuntimeEnvironment
import org.robolectric.Shadows
@RunWith(RobolectricTestRunner::class)
class AndroidContactPermissionResolverTest {
private val application = RuntimeEnvironment.getApplication()
private val testSubject = AndroidContactPermissionResolver(context = application)
@Test
fun `hasPermission() with contact permission`() {
grantContactPermission()
val result = testSubject.hasContactPermission()
assertThat(result).isTrue()
}
@Test
fun `hasPermission() without contact permission`() {
denyContactPermission()
val result = testSubject.hasContactPermission()
assertThat(result).isFalse()
}
private fun grantContactPermission() {
Shadows.shadowOf(application).grantPermissions(Manifest.permission.READ_CONTACTS)
}
private fun denyContactPermission() {
Shadows.shadowOf(application).denyPermissions(Manifest.permission.READ_CONTACTS)
}
}

View file

@ -1,6 +1,5 @@
package app.k9mail.core.android.common.contact
import android.Manifest
import android.content.ContentResolver
import android.database.Cursor
import android.database.MatrixCursor
@ -12,7 +11,6 @@ import assertk.assertions.isFalse
import assertk.assertions.isNull
import assertk.assertions.isTrue
import kotlin.test.Test
import org.junit.Before
import org.junit.runner.RunWith
import org.mockito.kotlin.anyOrNull
import org.mockito.kotlin.doReturn
@ -20,27 +18,20 @@ import org.mockito.kotlin.eq
import org.mockito.kotlin.mock
import org.mockito.kotlin.stub
import org.robolectric.RobolectricTestRunner
import org.robolectric.RuntimeEnvironment
import org.robolectric.Shadows
@RunWith(RobolectricTestRunner::class)
internal class ContentResolverContactDataSourceTest {
private val contactPermissionResolver = TestContactPermissionResolver(hasPermission = true)
private val contentResolver = mock<ContentResolver>()
private val testSubject = ContentResolverContactDataSource(
context = RuntimeEnvironment.getApplication(),
contentResolver = contentResolver,
contactPermissionResolver = contactPermissionResolver,
)
@Before
fun setUp() {
Shadows.shadowOf(RuntimeEnvironment.getApplication()).grantPermissions(Manifest.permission.READ_CONTACTS)
}
@Test
fun `getContactForEmail() returns null if permission is not granted`() {
Shadows.shadowOf(RuntimeEnvironment.getApplication()).denyPermissions(Manifest.permission.READ_CONTACTS)
contactPermissionResolver.hasContactPermission = false
val result = testSubject.getContactFor(CONTACT_EMAIL_ADDRESS)
@ -67,7 +58,7 @@ internal class ContentResolverContactDataSourceTest {
@Test
fun `hasContactForEmail() returns false if permission is not granted`() {
Shadows.shadowOf(RuntimeEnvironment.getApplication()).denyPermissions(Manifest.permission.READ_CONTACTS)
contactPermissionResolver.hasContactPermission = false
val result = testSubject.hasContactFor(CONTACT_EMAIL_ADDRESS)

View file

@ -0,0 +1,9 @@
package app.k9mail.core.android.common.contact
class TestContactPermissionResolver(hasPermission: Boolean) : ContactPermissionResolver {
var hasContactPermission = hasPermission
override fun hasContactPermission(): Boolean {
return hasContactPermission
}
}