Use dependency injection for most singletons

This commit is contained in:
MM20
2021-10-10 12:15:54 +02:00
parent 023bb2cbb1
commit 087d4fd455
144 changed files with 1789 additions and 1277 deletions
+2
View File
@@ -42,6 +42,8 @@ dependencies {
implementation(libs.textdrawable)
implementation(libs.koin.android)
implementation(project(":search"))
implementation(project(":preferences"))
implementation(project(":ktx"))
@@ -9,12 +9,15 @@ import de.mm20.launcher2.search.data.Contact
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
class ContactRepository private constructor(val context: Context) : BaseSearchableRepository() {
class ContactRepository(
val context: Context,
hiddenItemsRepository: HiddenItemsRepository
) : BaseSearchableRepository() {
val contacts = MediatorLiveData<List<Contact>?>()
private val allContacts = MutableLiveData<List<Contact>?>(emptyList())
private val hiddenItemKeys = HiddenItemsRepository.getInstance(context).hiddenItemsKeys
private val hiddenItemKeys = hiddenItemsRepository.hiddenItemsKeys
init {
contacts.addSource(hiddenItemKeys) { keys ->
@@ -35,13 +38,4 @@ class ContactRepository private constructor(val context: Context) : BaseSearchab
}
allContacts.value = results
}
companion object {
private lateinit var instance: ContactRepository
fun getInstance(context: Context): ContactRepository {
if (!::instance.isInitialized) instance = ContactRepository(context.applicationContext)
return instance
}
}
}
@@ -0,0 +1,51 @@
package de.mm20.launcher2.contacts
import android.Manifest
import android.content.Context
import android.content.pm.PackageManager
import android.provider.ContactsContract
import androidx.core.content.ContextCompat
import de.mm20.launcher2.ktx.jsonObjectOf
import de.mm20.launcher2.search.SearchableDeserializer
import de.mm20.launcher2.search.SearchableSerializer
import de.mm20.launcher2.search.data.Contact
import de.mm20.launcher2.search.data.Searchable
import org.json.JSONObject
class ContactSerializer : SearchableSerializer {
override fun serialize(searchable: Searchable): String {
searchable as Contact
return jsonObjectOf(
"id" to searchable.id
).toString()
}
override val typePrefix: String
get() = "contact"
}
class ContactDeserializer(val context: Context) : SearchableDeserializer {
override fun deserialize(serialized: String): Searchable? {
if (ContextCompat.checkSelfPermission(
context,
Manifest.permission.READ_CONTACTS
) != PackageManager.PERMISSION_GRANTED
) return null
val id = JSONObject(serialized).getLong("id")
val rawContactsCursor = context.contentResolver.query(
ContactsContract.RawContacts.CONTENT_URI,
arrayOf(ContactsContract.RawContacts._ID),
"${ContactsContract.RawContacts.CONTACT_ID} = ?",
arrayOf(id.toString()),
null
) ?: return null
val rawContacts = mutableSetOf<Long>()
while (rawContactsCursor.moveToNext()) {
rawContacts.add(rawContactsCursor.getLong(0))
}
rawContactsCursor.close()
if (rawContacts.isEmpty()) return null
return Contact.contactById(context, id, rawContacts)
}
}
@@ -1,10 +1,11 @@
package de.mm20.launcher2.contacts
import android.app.Application
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.LiveData
import androidx.lifecycle.ViewModel
import de.mm20.launcher2.search.data.Contact
class ContactViewModel(app: Application) : AndroidViewModel(app) {
val contacts: LiveData<List<Contact>?> = ContactRepository.getInstance(app).contacts
class ContactViewModel(
contactRepository: ContactRepository
) : ViewModel() {
val contacts: LiveData<List<Contact>?> = contactRepository.contacts
}
@@ -0,0 +1,10 @@
package de.mm20.launcher2.contacts
import org.koin.android.ext.koin.androidContext
import org.koin.androidx.viewmodel.dsl.viewModel
import org.koin.dsl.module
val contactsModule = module {
single { ContactRepository(androidContext(), get()) }
viewModel { ContactViewModel(get()) }
}
@@ -39,12 +39,6 @@ class Contact(
return phones.union(emails).joinToString(separator = ", ")
}
override fun serialize(): String {
return jsonObjectOf(
"id" to id
).toString()
}
override fun getPlaceholderIcon(context: Context): LauncherIcon {
val iconText = if (firstName.isNotEmpty()) firstName[0].toString() else "" + if (lastName.isNotEmpty()) lastName[0].toString() else ""
return LauncherIcon(
@@ -96,7 +90,7 @@ class Contact(
return results.sortedBy { it }
}
private fun contactById(context: Context, id: Long, rawIds: Set<Long>): Contact? {
internal fun contactById(context: Context, id: Long, rawIds: Set<Long>): Contact? {
val s = "(" + rawIds.joinToString(separator = " OR ",
transform = { "${ContactsContract.Data.RAW_CONTACT_ID} = $it" }) + ")" +
" AND (${ContactsContract.Data.MIMETYPE} = \"${ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE}\"" +
@@ -187,24 +181,5 @@ class Contact(
)
}
fun deserialize(context: Context, serialized: String): Contact? {
if (ContextCompat.checkSelfPermission(context, Manifest.permission.READ_CONTACTS) != PackageManager.PERMISSION_GRANTED) return null
val id = JSONObject(serialized).getLong("id")
val rawContactsCursor = context.contentResolver.query(
ContactsContract.RawContacts.CONTENT_URI,
arrayOf(ContactsContract.RawContacts._ID),
"${ContactsContract.RawContacts.CONTACT_ID} = ?",
arrayOf(id.toString()),
null
) ?: return null
val rawContacts = mutableSetOf<Long>()
while (rawContactsCursor.moveToNext()) {
rawContacts.add(rawContactsCursor.getLong(0))
}
rawContactsCursor.close()
if (rawContacts.isEmpty()) return null
return contactById(context, id, rawContacts)
}
}
}