Refactor search, favorites and calendar widget

This commit is contained in:
MM20
2021-12-31 20:16:08 +01:00
parent 0193d4e495
commit a2ccef64ce
81 changed files with 1335 additions and 1671 deletions
@@ -10,70 +10,69 @@ import android.content.pm.ShortcutInfo
import android.os.Process
import android.os.UserHandle
import android.util.Log
import androidx.lifecycle.MediatorLiveData
import androidx.lifecycle.MutableLiveData
import de.mm20.launcher2.badges.Badge
import de.mm20.launcher2.badges.BadgeProvider
import de.mm20.launcher2.hiddenitems.HiddenItemsRepository
import de.mm20.launcher2.preferences.LauncherPreferences
import de.mm20.launcher2.search.BaseSearchableRepository
import de.mm20.launcher2.search.data.AppInstallation
import de.mm20.launcher2.search.data.Application
import de.mm20.launcher2.search.data.LauncherApp
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.withContext
class AppRepository(
val context: Context,
interface AppRepository {
fun search(query: String): Flow<List<Application>>
}
class AppRepositoryImpl(
private val context: Context,
hiddenItemsRepository: HiddenItemsRepository,
badgeProvider: BadgeProvider
) : BaseSearchableRepository() {
private val badgeProvider: BadgeProvider
) : AppRepository {
private val launcherApps = context.getSystemService(Context.LAUNCHER_APPS_SERVICE) as LauncherApps
private val launcherApps =
context.getSystemService(Context.LAUNCHER_APPS_SERVICE) as LauncherApps
val applications = MediatorLiveData<List<Application>>()
private val installedApps = MutableStateFlow<List<Application>>(emptyList())
private val installations = MutableStateFlow<MutableList<AppInstallation>>(mutableListOf())
private val hiddenItems = hiddenItemsRepository.hiddenItemsKeys
private val installedApps = MutableLiveData<List<Application>>(emptyList())
private val installations = MutableLiveData<MutableList<AppInstallation>>(mutableListOf())
private val hiddenItemKeys = hiddenItemsRepository.hiddenItemsKeys
private val profiles: List<UserHandle> =
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
launcherApps.profiles.takeIf { it.isNotEmpty() } ?: listOf(Process.myUserHandle())
} else {
listOf(Process.myUserHandle())
}
private val installingPackages = mutableMapOf<Int, String>()
private val profiles: List<UserHandle> = if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
launcherApps.profiles.takeIf { it.isNotEmpty() } ?: listOf(Process.myUserHandle())
} else {
listOf(Process.myUserHandle())
}
init {
applications.addSource(installedApps) {
launch { updateAppsForDisplay() }
}
applications.addSource(installations) {
launch { updateAppsForDisplay() }
}
applications.addSource(hiddenItemKeys) {
launch { updateAppsForDisplay() }
}
launcherApps.registerCallback(object : LauncherApps.Callback() {
override fun onPackagesUnavailable(packageNames: Array<out String>, user: UserHandle, replacing: Boolean) {
installedApps.value = installedApps.value?.filter { !packageNames.contains(it.`package`) }
override fun onPackagesUnavailable(
packageNames: Array<out String>,
user: UserHandle,
replacing: Boolean
) {
installedApps.value =
installedApps.value.filter { !packageNames.contains(it.`package`) }
}
override fun onPackageChanged(packageName: String, user: UserHandle) {
val apps = installedApps.value?.toMutableList() ?: return
val apps = installedApps.value.toMutableList()
apps.removeAll { packageName == it.`package` }
apps.addAll(getApplications(packageName))
installedApps.value = apps
}
override fun onPackagesAvailable(packageNames: Array<out String>, user: UserHandle, replacing: Boolean) {
val apps = installedApps.value?.toMutableList() ?: return
override fun onPackagesAvailable(
packageNames: Array<out String>,
user: UserHandle,
replacing: Boolean
) {
val apps = installedApps.value.toMutableList()
for (packageName in packageNames) {
apps.addAll(getApplications(packageName))
}
@@ -82,16 +81,20 @@ class AppRepository(
override fun onPackageAdded(packageName: String, user: UserHandle) {
Log.d("MM20", "App installed: $packageName")
val apps = installedApps.value?.toMutableList() ?: return
val apps = installedApps.value.toMutableList()
apps.addAll(getApplications(packageName))
installedApps.value = apps
}
override fun onPackageRemoved(packageName: String, user: UserHandle) {
installedApps.value = installedApps.value?.filter { packageName != (it.`package`) }
installedApps.value = installedApps.value.filter { packageName != (it.`package`) }
}
override fun onShortcutsChanged(packageName: String, shortcuts: MutableList<ShortcutInfo>, user: UserHandle) {
override fun onShortcutsChanged(
packageName: String,
shortcuts: MutableList<ShortcutInfo>,
user: UserHandle
) {
super.onShortcutsChanged(packageName, shortcuts, user)
onPackageChanged(packageName, user)
}
@@ -99,11 +102,17 @@ class AppRepository(
override fun onPackagesSuspended(packageNames: Array<out String>?, user: UserHandle?) {
super.onPackagesSuspended(packageNames, user)
packageNames?.forEach {
badgeProvider.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?) {
override fun onPackagesUnsuspended(
packageNames: Array<out String>?,
user: UserHandle?
) {
super.onPackagesUnsuspended(packageNames, user)
packageNames?.forEach {
badgeProvider.removeBadge("app://$it")
@@ -111,7 +120,10 @@ class AppRepository(
}
})
val apps = profiles.map { p ->
launcherApps.getActivityList(null, p).mapNotNull { getApplication(it, p) }
}.flatten()
installedApps.value = apps
val packageInstaller = context.packageManager.packageInstaller
@@ -139,13 +151,13 @@ class AppRepository(
installingPackages.remove(sessionId)
val key = "app://$pkg"
val badge = badgeProvider.getBadge(key)?.apply { progress = null }
?: Badge()
?: Badge()
badgeProvider.setBadge(key, badge)
val inst = installations.value ?: return
val inst = installations.value
inst.removeAll {
it.session.sessionId == sessionId
}
installations.postValue(inst)
installations.value = inst
}
@@ -165,41 +177,66 @@ class AppRepository(
val appInstallation = AppInstallation(session)
val inst = installations.value ?: mutableListOf()
inst.add(appInstallation)
installations.postValue(inst)
installations.value = inst
}
})
val apps = profiles.map { p -> launcherApps.getActivityList(null, p).mapNotNull { getApplication(it, p) } }.flatten()
installedApps.value = apps
}
override suspend fun search(query: String) {
updateAppsForDisplay()
private fun getApplications(packageName: String): List<Application> {
if (packageName == context.packageName) return emptyList()
return profiles.map { p ->
launcherApps.getActivityList(packageName, p).mapNotNull { getApplication(it, p) }
}.flatten()
}
private suspend fun updateAppsForDisplay() {
val query = searchRepository.currentQuery.value ?: ""
val componentName = ComponentName.unflattenFromString(query)
private fun getApplication(
launcherActivityInfo: LauncherActivityInfo,
profile: UserHandle
): Application? {
if (launcherActivityInfo.applicationInfo.packageName == context.packageName && !context.packageName.endsWith(
".debug"
)
) return null
return LauncherApp(context, launcherActivityInfo)
}
val apps = withContext(Dispatchers.Default) {
val hiddenItems = hiddenItemKeys.value ?: emptyList()
val installed = installedApps.value ?: emptyList()
val installing = installations.value ?: emptyList<AppInstallation>()
val results = mutableListOf<Application>()
results.addAll(installed)
results.addAll(installing)
if (query.isNotEmpty()) {
results.removeAll { !it.label.contains(query, ignoreCase = true) }
getActivityByComponentName(componentName)?.let { results.add(it) }
override fun search(query: String): Flow<List<Application>> = channelFlow {
combine(installedApps, hiddenItems, installations) {_, _, _ ->
null
}.collectLatest {
withContext(Dispatchers.IO) {
val appResults = mutableListOf<Application>()
if (query.isEmpty()) {
appResults.addAll(installedApps.value)
appResults.addAll(installations.value)
} else {
appResults.addAll(installedApps.value.filter {
it.label.contains(
query,
ignoreCase = true
)
})
appResults.addAll(installations.value.filter {
it.label.contains(
query,
ignoreCase = true
)
})
}
val componentName = ComponentName.unflattenFromString(query)
getActivityByComponentName(componentName)?.let { appResults.add(it) }
appResults.removeAll { hiddenItems.value.contains(it.key) }
appResults.sort()
send(appResults)
}
results.removeAll { hiddenItems.contains(it.key) }
results.sort()
results
}
applications.value = apps
}
private fun getActivityByComponentName(componentName: ComponentName?): Application? {
@@ -211,15 +248,4 @@ class AppRepository(
LauncherApp(context, lai)
}
}
private fun getApplication(launcherActivityInfo: LauncherActivityInfo, profile: UserHandle): Application? {
if (launcherActivityInfo.applicationInfo.packageName == context.packageName && !context.packageName.endsWith(".debug")) return null
return LauncherApp(context, launcherActivityInfo)
}
private fun getApplications(packageName: String): List<Application> {
if (packageName == context.packageName) return emptyList()
return profiles.map { p -> launcherApps.getActivityList(packageName, p).mapNotNull { getApplication(it, p) } }.flatten()
}
}
@@ -1,12 +0,0 @@
package de.mm20.launcher2.applications
import androidx.lifecycle.LiveData
import androidx.lifecycle.ViewModel
import de.mm20.launcher2.search.data.Application
class AppViewModel(
appRepository: AppRepository
): ViewModel() {
val applications: LiveData<List<Application>> = appRepository.applications
}
@@ -5,6 +5,5 @@ import org.koin.androidx.viewmodel.dsl.viewModel
import org.koin.dsl.module
val applicationsModule = module {
single { AppRepository(androidContext(), get(), get()) }
viewModel { AppViewModel(get()) }
single<AppRepository> { AppRepositoryImpl(androidContext(), get(), get()) }
}