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
@@ -25,7 +25,12 @@ import de.mm20.launcher2.search.data.LauncherApp
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
class AppRepository private constructor(val context: Context) : BaseSearchableRepository() {
class AppRepository(
val context: Context,
val iconRepository: IconRepository,
hiddenItemsRepository: HiddenItemsRepository,
badgeProvider: BadgeProvider
) : BaseSearchableRepository() {
private val launcherApps = context.getSystemService(Context.LAUNCHER_APPS_SERVICE) as LauncherApps
@@ -34,7 +39,7 @@ class AppRepository private constructor(val context: Context) : BaseSearchableRe
private val installedApps = MutableLiveData<List<Application>>(emptyList())
private val installations = MutableLiveData<MutableList<AppInstallation>>(mutableListOf())
private val hiddenItemKeys = HiddenItemsRepository.getInstance(context).hiddenItemsKeys
private val hiddenItemKeys = hiddenItemsRepository.hiddenItemsKeys
private val installingPackages = mutableMapOf<Int, String>()
@@ -97,14 +102,14 @@ class AppRepository private constructor(val context: Context) : BaseSearchableRe
override fun onPackagesSuspended(packageNames: Array<out String>?, user: UserHandle?) {
super.onPackagesSuspended(packageNames, user)
packageNames?.forEach {
BadgeProvider.getInstance(context).setBadge("app://$it", Badge(iconRes = R.drawable.ic_badge_suspended))
badgeProvider.setBadge("app://$it", Badge(iconRes = R.drawable.ic_badge_suspended))
}
}
override fun onPackagesUnsuspended(packageNames: Array<out String>?, user: UserHandle?) {
super.onPackagesUnsuspended(packageNames, user)
packageNames?.forEach {
BadgeProvider.getInstance(context).removeBadge("app://$it")
badgeProvider.removeBadge("app://$it")
}
}
@@ -117,7 +122,7 @@ class AppRepository private constructor(val context: Context) : BaseSearchableRe
override fun onProgressChanged(sessionId: Int, progress: Float) {
val session = packageInstaller.getSessionInfo(sessionId) ?: return
val pkg = session.appPackageName ?: return
BadgeProvider.getInstance(context).updateBadge("app://$pkg", Badge(progress = progress))
badgeProvider.updateBadge("app://$pkg", Badge(progress = progress))
}
override fun onActiveChanged(sessionId: Int, active: Boolean) {
@@ -129,9 +134,9 @@ class AppRepository private constructor(val context: Context) : BaseSearchableRe
val pkg = installingPackages[sessionId]
installingPackages.remove(sessionId)
val key = "app://$pkg"
val badge = BadgeProvider.getInstance(context).getBadge(key)?.apply { progress = null }
val badge = badgeProvider.getBadge(key)?.apply { progress = null }
?: Badge()
BadgeProvider.getInstance(context).setBadge(key, badge)
badgeProvider.setBadge(key, badge)
val inst = installations.value ?: return
inst.removeAll {
it.session.sessionId == sessionId
@@ -144,7 +149,7 @@ class AppRepository private constructor(val context: Context) : BaseSearchableRe
val inst = installations.value ?: mutableListOf()
inst.removeAll {
if (it.session.sessionId == sessionId) {
IconRepository.getInstance(context).removeIconFromCache(it)
iconRepository.removeIconFromCache(it)
true
} else false
}
@@ -173,7 +178,7 @@ class AppRepository private constructor(val context: Context) : BaseSearchableRe
}
private suspend fun updateAppsForDisplay() {
val query = SearchRepository.getInstance().currentQuery.value ?: ""
val query = searchRepository.currentQuery.value ?: ""
val componentName = ComponentName.unflattenFromString(query)
@@ -216,12 +221,4 @@ class AppRepository private constructor(val context: Context) : BaseSearchableRe
return profiles.map { p -> launcherApps.getActivityList(packageName, p).mapNotNull { getApplication(it, p) } }.flatten()
}
companion object {
private lateinit var instance: AppRepository
fun getInstance(context: Context): AppRepository {
if (!::instance.isInitialized) instance = AppRepository(context.applicationContext)
return instance
}
}
}
@@ -1,13 +1,12 @@
package de.mm20.launcher2.applications
import android.app.Application as AndroidApp
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.LiveData
import de.mm20.launcher2.applications.AppRepository
import androidx.lifecycle.ViewModel
import de.mm20.launcher2.search.data.Application
class AppViewModel(app: AndroidApp): AndroidViewModel(app) {
private val repository = AppRepository.getInstance(app)
val applications: LiveData<List<Application>> = repository.applications
class AppViewModel(
appRepository: AppRepository
): ViewModel() {
val applications: LiveData<List<Application>> = appRepository.applications
}
@@ -0,0 +1,10 @@
package de.mm20.launcher2.applications
import org.koin.android.ext.koin.androidContext
import org.koin.androidx.viewmodel.dsl.viewModel
import org.koin.dsl.module
val applicationsModule = module {
single { AppRepository(androidContext(), get(), get(), get()) }
viewModel { AppViewModel(get()) }
}
@@ -0,0 +1,139 @@
package de.mm20.launcher2.search.data
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.pm.LauncherApps
import android.content.pm.PackageManager
import android.os.Build
import android.os.Process
import android.os.UserManager
import androidx.annotation.RequiresApi
import androidx.core.content.getSystemService
import de.mm20.launcher2.badges.Badge
import de.mm20.launcher2.badges.BadgeProvider
import de.mm20.launcher2.graphics.BadgeDrawable
import de.mm20.launcher2.ktx.jsonObjectOf
import de.mm20.launcher2.search.SearchableDeserializer
import de.mm20.launcher2.search.SearchableSerializer
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.json.JSONObject
import org.koin.core.component.KoinComponent
import org.koin.core.component.inject
class LauncherAppSerializer : SearchableSerializer {
override fun serialize(searchable: Searchable): String {
searchable as LauncherApp
val json = JSONObject()
json.put("package", searchable.`package`)
json.put("activity", searchable.activity)
json.put("user", searchable.userSerialNumber)
return json.toString()
}
override val typePrefix: String
get() = "app"
}
class LauncherAppDeserializer(val context: Context) : SearchableDeserializer {
override fun deserialize(serialized: String): Searchable? {
val json = JSONObject(serialized)
val launcherApps = context.getSystemService<LauncherApps>()!!
val userManager = context.getSystemService<UserManager>()!!
val userSerial = json.optLong("user")
val user = userManager.getUserForSerialNumber(userSerial) ?: Process.myUserHandle()
val pkg = json.getString("package")
val intent = Intent().also {
it.component = ComponentName(pkg, json.getString("activity"))
}
val launcherActivityInfo = launcherApps.resolveActivity(intent, user) ?: return null
return LauncherApp(context, launcherActivityInfo)
}
}
class AppShortcutSerializer : SearchableSerializer {
@RequiresApi(Build.VERSION_CODES.N_MR1)
override fun serialize(searchable: Searchable): String {
searchable as AppShortcut
return jsonObjectOf(
"packagename" to searchable.launcherShortcut.`package`,
"id" to searchable.launcherShortcut.id,
"user" to searchable.userSerialNumber,
).toString()
}
override val typePrefix: String
get() = "shortcut"
}
class AppShortcutDeserializer(
val context: Context,
) : SearchableDeserializer, KoinComponent {
private val badgeProvider: BadgeProvider by inject()
@RequiresApi(Build.VERSION_CODES.N_MR1)
override fun deserialize(serialized: String): Searchable? {
val launcherApps = context.getSystemService(Context.LAUNCHER_APPS_SERVICE) as LauncherApps
if (!launcherApps.hasShortcutHostPermission()) return null
else {
val json = JSONObject(serialized)
val packageName = json.getString("packagename")
val id = json.getString("id")
val userSerial = json.optLong("user")
val query = LauncherApps.ShortcutQuery()
query.setPackage(packageName)
query.setQueryFlags(
LauncherApps.ShortcutQuery.FLAG_MATCH_DYNAMIC or
LauncherApps.ShortcutQuery.FLAG_MATCH_MANIFEST or
LauncherApps.ShortcutQuery.FLAG_MATCH_PINNED
)
query.setShortcutIds(mutableListOf(id))
val userManager = context.getSystemService<UserManager>()!!
val user = userManager.getUserForSerialNumber(userSerial) ?: Process.myUserHandle()
val shortcuts = try {
launcherApps.getShortcuts(query, user)
} catch (e: IllegalStateException) {
return null
}
val pm = context.packageManager
val appName = try {
pm.getApplicationInfo(packageName, 0).loadLabel(pm).toString()
} catch (e: PackageManager.NameNotFoundException) {
return null
}
if (shortcuts == null || shortcuts.isEmpty()) {
return null
} else {
GlobalScope.launch {
val activity = shortcuts[0].activity
withContext(Dispatchers.IO) {
val icon = try {
context.packageManager.getActivityIcon(
activity
?: return@withContext
)
} catch (e: PackageManager.NameNotFoundException) {
return@withContext
}
val badge = Badge(icon = BadgeDrawable(context, icon))
badgeProvider.setBadge(
"shortcut://${activity.flattenToShortString()}",
badge
)
}
}
return AppShortcut(
context = context,
launcherShortcut = shortcuts[0],
appName = appName
)
}
}
}
}
@@ -39,7 +39,7 @@ class AppShortcut(
private val userSerialNumber: Long = launcherShortcut.userHandle.getSerialNumber(context)
internal val userSerialNumber: Long = launcherShortcut.userHandle.getSerialNumber(context)
private val isMainProfile = launcherShortcut.userHandle == Process.myUserHandle()
override val key: String
@@ -58,15 +58,6 @@ class AppShortcut(
}
}
override fun serialize(): String {
return jsonObjectOf(
"packagename" to launcherShortcut.`package`,
"id" to launcherShortcut.id,
"user" to userSerialNumber,
).toString()
}
override fun getLaunchIntent(context: Context): Intent? {
return launcherShortcut.intent
}
@@ -106,56 +97,4 @@ class AppShortcut(
autoGenerateBackgroundMode = LauncherPreferences.instance.legacyIconBg.toInt()
)
}
companion object {
fun deserialize(context: Context, serialized: String): AppShortcut? {
val launcherApps = context.getSystemService(Context.LAUNCHER_APPS_SERVICE) as LauncherApps
if (!launcherApps.hasShortcutHostPermission()) return null
else {
val json = JSONObject(serialized)
val packageName = json.getString("packagename")
val id = json.getString("id")
val userSerial = json.optLong("user")
val query = LauncherApps.ShortcutQuery()
query.setPackage(packageName)
query.setQueryFlags(LauncherApps.ShortcutQuery.FLAG_MATCH_DYNAMIC or
LauncherApps.ShortcutQuery.FLAG_MATCH_MANIFEST or
LauncherApps.ShortcutQuery.FLAG_MATCH_PINNED)
query.setShortcutIds(mutableListOf(id))
val userManager = context.getSystemService<UserManager>()!!
val user = userManager.getUserForSerialNumber(userSerial) ?: Process.myUserHandle()
val shortcuts = try {
launcherApps.getShortcuts(query, user)
} catch (e: IllegalStateException) {
return null
}
val pm = context.packageManager
val appName = try {
pm.getApplicationInfo(packageName, 0).loadLabel(pm).toString()
} catch (e: PackageManager.NameNotFoundException) {
return null
}
if (shortcuts == null || shortcuts.isEmpty()) return null else {
GlobalScope.launch {
val activity = shortcuts[0].activity
withContext(Dispatchers.IO) {
val icon = try {
context.packageManager.getActivityIcon(activity
?: return@withContext)
} catch (e: PackageManager.NameNotFoundException) {
return@withContext
}
val badge = Badge(icon = BadgeDrawable(context, icon))
BadgeProvider.getInstance(context).setBadge("shortcut://${activity.flattenToShortString()}", badge)
}
}
return AppShortcut(
context = context,
launcherShortcut = shortcuts[0],
appName = appName
)
}
}
}
}
}
@@ -15,6 +15,8 @@ import de.mm20.launcher2.ktx.getSerialNumber
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import org.json.JSONObject
import org.koin.core.component.KoinComponent
import org.koin.core.component.inject
/**
* An [Application] based on an [android.content.pm.LauncherActivityInfo]
@@ -46,9 +48,9 @@ class LauncherApp(
}
appShortcuts
}
) {
), KoinComponent {
private val userSerialNumber: Long = launcherActivityInfo.user.getSerialNumber(context)
internal val userSerialNumber: Long = launcherActivityInfo.user.getSerialNumber(context)
private val isMainProfile = launcherActivityInfo.user == Process.myUserHandle()
override val badgeKey: String = if (isMainProfile) "app://${`package`}" else "profile://$userSerialNumber"
@@ -56,21 +58,14 @@ class LauncherApp(
override val key: String
get() = if (isMainProfile) "app://$`package`:$activity" else "app://$`package`:$activity:${userSerialNumber}"
override fun serialize(): String {
val json = JSONObject()
json.put("package", `package`)
json.put("activity", activity)
json.put("user", userSerialNumber)
return json.toString()
}
fun getUser(): UserHandle? {
return launcherActivityInfo.user
}
override suspend fun loadIconAsync(context: Context, size: Int): LauncherIcon? {
val iconPackManager: IconPackManager by inject()
return withContext(Dispatchers.IO) {
IconPackManager.getInstance(context).getIcon(context, launcherActivityInfo, size)
iconPackManager.getIcon(context, launcherActivityInfo, size)
}
}
@@ -98,20 +93,6 @@ class LauncherApp(
companion object {
fun deserialize(context: Context, serialized: String): LauncherApp? {
val json = JSONObject(serialized)
val launcherApps = context.getSystemService<LauncherApps>()!!
val userManager = context.getSystemService<UserManager>()!!
val userSerial = json.optLong("user")
val user = userManager.getUserForSerialNumber(userSerial) ?: Process.myUserHandle()
val pkg = json.getString("package")
val intent = Intent().also {
it.component = ComponentName(pkg, json.getString("activity"))
}
val launcherActivityInfo = launcherApps.resolveActivity(intent, user) ?: return null
return LauncherApp(context, launcherActivityInfo)
}
fun getPackageVersionName(context: Context, packageName: String): String? {
return try {
context.packageManager.getPackageInfo(packageName, 0).versionName