Refactor preferences module
This commit is contained in:
@@ -44,5 +44,6 @@ dependencies {
|
||||
implementation(project(":core:ktx"))
|
||||
implementation(project(":core:base"))
|
||||
implementation(project(":core:permissions"))
|
||||
implementation(project(":core:preferences"))
|
||||
|
||||
}
|
||||
@@ -5,6 +5,7 @@ import android.provider.ContactsContract
|
||||
import androidx.core.database.getStringOrNull
|
||||
import de.mm20.launcher2.permissions.PermissionGroup
|
||||
import de.mm20.launcher2.permissions.PermissionsManager
|
||||
import de.mm20.launcher2.preferences.search.ContactSearchSettings
|
||||
import de.mm20.launcher2.search.Contact
|
||||
import de.mm20.launcher2.search.ContactInfo
|
||||
import de.mm20.launcher2.search.SearchableRepository
|
||||
@@ -12,12 +13,16 @@ import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.*
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.flow
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
internal class ContactRepository(
|
||||
private val context: Context,
|
||||
private val permissionsManager: PermissionsManager
|
||||
private val permissionsManager: PermissionsManager,
|
||||
private val settings: ContactSearchSettings,
|
||||
) : SearchableRepository<Contact> {
|
||||
|
||||
fun get(id: Long): Flow<Contact?> = flow {
|
||||
@@ -44,114 +49,120 @@ internal class ContactRepository(
|
||||
emit(getWithRawIds(id, rawContacts))
|
||||
}
|
||||
|
||||
private suspend fun getWithRawIds(id: Long, rawIds: Set<Long>): Contact? = withContext(Dispatchers.IO) {
|
||||
val s = "(" + rawIds.joinToString(separator = " OR ",
|
||||
transform = { "${ContactsContract.Data.RAW_CONTACT_ID} = $it" }) + ")" +
|
||||
" AND (${ContactsContract.Data.MIMETYPE} = \"${ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE}\"" +
|
||||
" OR ${ContactsContract.Data.MIMETYPE} = \"${ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE}\"" +
|
||||
" OR ${ContactsContract.Data.MIMETYPE} = \"${ContactsContract.CommonDataKinds.StructuredPostal.CONTENT_ITEM_TYPE}\"" +
|
||||
" OR ${ContactsContract.Data.MIMETYPE} = \"${ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE}\"" +
|
||||
" OR ${ContactsContract.Data.MIMETYPE} = \"${TelegramContactInfo.ItemType}\"" +
|
||||
" OR ${ContactsContract.Data.MIMETYPE} = \"${WhatsAppContactInfo.ItemType}\"" +
|
||||
" OR ${ContactsContract.Data.MIMETYPE} = \"${SignalContactInfo.ItemType}\"" +
|
||||
")"
|
||||
val dataCursor = context.contentResolver.query(
|
||||
ContactsContract.Data.CONTENT_URI,
|
||||
null, s, null, null
|
||||
) ?: return@withContext null
|
||||
val contactInfos = mutableSetOf<ContactInfo>()
|
||||
var firstName = ""
|
||||
var lastName = ""
|
||||
var displayName = ""
|
||||
val mimeTypeColumn = dataCursor.getColumnIndex(ContactsContract.Data.MIMETYPE)
|
||||
val emailAddressColumn =
|
||||
dataCursor.getColumnIndex(ContactsContract.CommonDataKinds.Email.ADDRESS)
|
||||
val numberColumn =
|
||||
dataCursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER)
|
||||
val addressColumn =
|
||||
dataCursor.getColumnIndex(ContactsContract.CommonDataKinds.StructuredPostal.FORMATTED_ADDRESS)
|
||||
val displayNameColumn =
|
||||
dataCursor.getColumnIndex(ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME)
|
||||
val givenNameColumn =
|
||||
dataCursor.getColumnIndex(ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME)
|
||||
val familyNameColumn =
|
||||
dataCursor.getColumnIndex(ContactsContract.CommonDataKinds.StructuredName.FAMILY_NAME)
|
||||
val data1Column = dataCursor.getColumnIndex(ContactsContract.Data.DATA1)
|
||||
val data3Column = dataCursor.getColumnIndex(ContactsContract.Data.DATA3)
|
||||
val idColumn = dataCursor.getColumnIndex(ContactsContract.Data._ID)
|
||||
loop@ while (dataCursor.moveToNext()) {
|
||||
when (dataCursor.getStringOrNull(mimeTypeColumn)) {
|
||||
ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE ->
|
||||
dataCursor.getStringOrNull(emailAddressColumn)?.let {
|
||||
contactInfos.add(MailContactInfo(it))
|
||||
private suspend fun getWithRawIds(id: Long, rawIds: Set<Long>): Contact? =
|
||||
withContext(Dispatchers.IO) {
|
||||
val s = "(" + rawIds.joinToString(separator = " OR ",
|
||||
transform = { "${ContactsContract.Data.RAW_CONTACT_ID} = $it" }) + ")" +
|
||||
" AND (${ContactsContract.Data.MIMETYPE} = \"${ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE}\"" +
|
||||
" OR ${ContactsContract.Data.MIMETYPE} = \"${ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE}\"" +
|
||||
" OR ${ContactsContract.Data.MIMETYPE} = \"${ContactsContract.CommonDataKinds.StructuredPostal.CONTENT_ITEM_TYPE}\"" +
|
||||
" OR ${ContactsContract.Data.MIMETYPE} = \"${ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE}\"" +
|
||||
" OR ${ContactsContract.Data.MIMETYPE} = \"${TelegramContactInfo.ItemType}\"" +
|
||||
" OR ${ContactsContract.Data.MIMETYPE} = \"${WhatsAppContactInfo.ItemType}\"" +
|
||||
" OR ${ContactsContract.Data.MIMETYPE} = \"${SignalContactInfo.ItemType}\"" +
|
||||
")"
|
||||
val dataCursor = context.contentResolver.query(
|
||||
ContactsContract.Data.CONTENT_URI,
|
||||
null, s, null, null
|
||||
) ?: return@withContext null
|
||||
val contactInfos = mutableSetOf<ContactInfo>()
|
||||
var firstName = ""
|
||||
var lastName = ""
|
||||
var displayName = ""
|
||||
val mimeTypeColumn = dataCursor.getColumnIndex(ContactsContract.Data.MIMETYPE)
|
||||
val emailAddressColumn =
|
||||
dataCursor.getColumnIndex(ContactsContract.CommonDataKinds.Email.ADDRESS)
|
||||
val numberColumn =
|
||||
dataCursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER)
|
||||
val addressColumn =
|
||||
dataCursor.getColumnIndex(ContactsContract.CommonDataKinds.StructuredPostal.FORMATTED_ADDRESS)
|
||||
val displayNameColumn =
|
||||
dataCursor.getColumnIndex(ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME)
|
||||
val givenNameColumn =
|
||||
dataCursor.getColumnIndex(ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME)
|
||||
val familyNameColumn =
|
||||
dataCursor.getColumnIndex(ContactsContract.CommonDataKinds.StructuredName.FAMILY_NAME)
|
||||
val data1Column = dataCursor.getColumnIndex(ContactsContract.Data.DATA1)
|
||||
val data3Column = dataCursor.getColumnIndex(ContactsContract.Data.DATA3)
|
||||
val idColumn = dataCursor.getColumnIndex(ContactsContract.Data._ID)
|
||||
loop@ while (dataCursor.moveToNext()) {
|
||||
when (dataCursor.getStringOrNull(mimeTypeColumn)) {
|
||||
ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE ->
|
||||
dataCursor.getStringOrNull(emailAddressColumn)?.let {
|
||||
contactInfos.add(MailContactInfo(it))
|
||||
}
|
||||
|
||||
ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE ->
|
||||
dataCursor.getStringOrNull(numberColumn)?.let {
|
||||
val phone = it.replace(Regex("[^+0-9]"), "")
|
||||
contactInfos.add(PhoneContactInfo(phone))
|
||||
}
|
||||
|
||||
ContactsContract.CommonDataKinds.StructuredPostal.CONTENT_ITEM_TYPE ->
|
||||
dataCursor.getStringOrNull(addressColumn)?.let {
|
||||
contactInfos.add(PostalContactInfo(it))
|
||||
}
|
||||
|
||||
ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE -> {
|
||||
firstName = dataCursor.getStringOrNull(givenNameColumn) ?: ""
|
||||
lastName = dataCursor.getStringOrNull(familyNameColumn) ?: ""
|
||||
displayName = dataCursor.getStringOrNull(displayNameColumn) ?: ""
|
||||
}
|
||||
|
||||
ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE ->
|
||||
dataCursor.getStringOrNull(numberColumn)?.let {
|
||||
val phone = it.replace(Regex("[^+0-9]"), "")
|
||||
contactInfos.add(PhoneContactInfo(phone))
|
||||
TelegramContactInfo.ItemType -> {
|
||||
val data1 = dataCursor.getStringOrNull(data1Column)
|
||||
?: continue@loop
|
||||
val data3 = dataCursor.getStringOrNull(data3Column)
|
||||
?: continue@loop
|
||||
contactInfos.add(
|
||||
TelegramContactInfo(data3.substringAfterLast(" "), data1)
|
||||
)
|
||||
}
|
||||
|
||||
ContactsContract.CommonDataKinds.StructuredPostal.CONTENT_ITEM_TYPE ->
|
||||
dataCursor.getStringOrNull(addressColumn)?.let {
|
||||
contactInfos.add(PostalContactInfo(it))
|
||||
WhatsAppContactInfo.ItemType -> {
|
||||
val data1 = dataCursor.getStringOrNull(data1Column)
|
||||
?: continue@loop
|
||||
val dataId = dataCursor.getLong(idColumn)
|
||||
contactInfos.add(
|
||||
WhatsAppContactInfo(
|
||||
"+${data1.substringBefore('@')}",
|
||||
dataId
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE -> {
|
||||
firstName = dataCursor.getStringOrNull(givenNameColumn) ?: ""
|
||||
lastName = dataCursor.getStringOrNull(familyNameColumn) ?: ""
|
||||
displayName = dataCursor.getStringOrNull(displayNameColumn) ?: ""
|
||||
}
|
||||
|
||||
TelegramContactInfo.ItemType -> {
|
||||
val data1 = dataCursor.getStringOrNull(data1Column)
|
||||
?: continue@loop
|
||||
val data3 = dataCursor.getStringOrNull(data3Column)
|
||||
?: continue@loop
|
||||
contactInfos.add(
|
||||
TelegramContactInfo(data3.substringAfterLast(" "), data1)
|
||||
)
|
||||
}
|
||||
|
||||
WhatsAppContactInfo.ItemType -> {
|
||||
val data1 = dataCursor.getStringOrNull(data1Column)
|
||||
?: continue@loop
|
||||
val dataId = dataCursor.getLong(idColumn)
|
||||
contactInfos.add(WhatsAppContactInfo("+${data1.substringBefore('@')}", dataId))
|
||||
}
|
||||
|
||||
SignalContactInfo.ItemType -> {
|
||||
val data1 = dataCursor.getStringOrNull(data1Column)
|
||||
?: continue@loop
|
||||
val dataId = dataCursor.getLong(idColumn)
|
||||
contactInfos.add(SignalContactInfo(data1, dataId))
|
||||
SignalContactInfo.ItemType -> {
|
||||
val data1 = dataCursor.getStringOrNull(data1Column)
|
||||
?: continue@loop
|
||||
val dataId = dataCursor.getLong(idColumn)
|
||||
contactInfos.add(SignalContactInfo(data1, dataId))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
dataCursor.close()
|
||||
dataCursor.close()
|
||||
|
||||
val lookupKeyCursor = context.contentResolver.query(
|
||||
ContactsContract.Contacts.CONTENT_URI,
|
||||
arrayOf(ContactsContract.Contacts.LOOKUP_KEY),
|
||||
"${ContactsContract.Contacts._ID} = ?",
|
||||
arrayOf(id.toString()),
|
||||
null
|
||||
) ?: return@withContext null
|
||||
var lookUpKey = ""
|
||||
if (lookupKeyCursor.moveToNext()) {
|
||||
lookUpKey = lookupKeyCursor.getString(0)
|
||||
}
|
||||
lookupKeyCursor.close()
|
||||
val lookupKeyCursor = context.contentResolver.query(
|
||||
ContactsContract.Contacts.CONTENT_URI,
|
||||
arrayOf(ContactsContract.Contacts.LOOKUP_KEY),
|
||||
"${ContactsContract.Contacts._ID} = ?",
|
||||
arrayOf(id.toString()),
|
||||
null
|
||||
) ?: return@withContext null
|
||||
var lookUpKey = ""
|
||||
if (lookupKeyCursor.moveToNext()) {
|
||||
lookUpKey = lookupKeyCursor.getString(0)
|
||||
}
|
||||
lookupKeyCursor.close()
|
||||
|
||||
return@withContext AndroidContact(
|
||||
id = id,
|
||||
firstName = firstName,
|
||||
lastName = lastName,
|
||||
displayName = displayName,
|
||||
contactInfos = contactInfos,
|
||||
lookupKey = lookUpKey
|
||||
)
|
||||
}
|
||||
return@withContext AndroidContact(
|
||||
id = id,
|
||||
firstName = firstName,
|
||||
lastName = lastName,
|
||||
displayName = displayName,
|
||||
contactInfos = contactInfos,
|
||||
lookupKey = lookUpKey
|
||||
)
|
||||
}
|
||||
|
||||
override fun search(query: String): Flow<ImmutableList<Contact>> {
|
||||
val hasPermission = permissionsManager.hasPermission(PermissionGroup.Contacts)
|
||||
@@ -162,7 +173,7 @@ internal class ContactRepository(
|
||||
}
|
||||
}
|
||||
|
||||
return hasPermission.map {
|
||||
return hasPermission.combine(settings.enabled) { perm, en -> perm && en }.map {
|
||||
if (it) {
|
||||
queryContacts(query)
|
||||
} else {
|
||||
|
||||
@@ -8,7 +8,7 @@ import org.koin.core.qualifier.named
|
||||
import org.koin.dsl.module
|
||||
|
||||
val contactsModule = module {
|
||||
factory { ContactRepository(androidContext(), get()) }
|
||||
factory<SearchableRepository<Contact>>(named<Contact>()) { ContactRepository(androidContext(), get()) }
|
||||
factory { ContactRepository(androidContext(), get(), get()) }
|
||||
factory<SearchableRepository<Contact>>(named<Contact>()) { get<ContactRepository>() }
|
||||
factory<SearchableDeserializer>(named(AndroidContact.Domain)) { ContactDeserializer(get(), get()) }
|
||||
}
|
||||
Reference in New Issue
Block a user