Use dependency injection for most singletons
This commit is contained in:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user