Annual refactor

This commit is contained in:
MM20
2023-10-28 14:20:16 +02:00
parent 4f64652aae
commit 957358c79a
110 changed files with 1786 additions and 1663 deletions
@@ -4,7 +4,7 @@ import android.content.Context
import android.net.Uri
import android.os.Build
import de.mm20.launcher2.data.customattrs.CustomAttributesRepository
import de.mm20.launcher2.searchable.SearchableRepository
import de.mm20.launcher2.searchable.SavableSearchableRepository
import de.mm20.launcher2.preferences.LauncherDataStore
import de.mm20.launcher2.preferences.export
import de.mm20.launcher2.preferences.import
@@ -22,7 +22,7 @@ import java.util.zip.ZipOutputStream
class BackupManager(
private val context: Context,
private val dataStore: LauncherDataStore,
private val searchableRepository: SearchableRepository,
private val searchableRepository: SavableSearchableRepository,
private val widgetRepository: WidgetRepository,
private val searchActionRepository: SearchActionRepository,
private val customAttrsRepository: CustomAttributesRepository,
@@ -4,10 +4,8 @@ import android.content.Context
import android.content.pm.PackageManager
import de.mm20.launcher2.badges.Badge
import de.mm20.launcher2.graphics.BadgeDrawable
import de.mm20.launcher2.search.data.LauncherShortcut
import de.mm20.launcher2.search.data.LegacyShortcut
import de.mm20.launcher2.search.AppShortcut
import de.mm20.launcher2.search.Searchable
import de.mm20.launcher2.search.data.UnavailableShortcut
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.channelFlow
@@ -17,53 +15,37 @@ class AppShortcutBadgeProvider(
private val context: Context
) : BadgeProvider {
override fun getBadge(searchable: Searchable): Flow<Badge?> = channelFlow {
if (searchable is LauncherShortcut) {
val componentName = searchable.launcherShortcut.activity
if (componentName == null) {
if (searchable is AppShortcut) {
val componentName = searchable.componentName
val packageName = searchable.packageName
if (componentName != null) {
withContext(Dispatchers.IO) {
val icon = try {
context.packageManager.getActivityIcon(
componentName
)
} catch (e: PackageManager.NameNotFoundException) {
return@withContext
}
val badge = Badge(icon = BadgeDrawable(context, icon))
send(badge)
}
} else if (packageName != null) {
withContext(Dispatchers.IO) {
val icon = try {
context.packageManager.getApplicationIcon(
packageName
)
} catch (e: PackageManager.NameNotFoundException) {
return@withContext
}
val badge = Badge(icon = BadgeDrawable(context, icon))
send(badge)
}
} else {
send(null)
return@channelFlow
}
withContext(Dispatchers.IO) {
val icon = try {
context.packageManager.getActivityIcon(
componentName
)
} catch (e: PackageManager.NameNotFoundException) {
return@withContext
}
val badge = Badge(icon = BadgeDrawable(context, icon))
send(badge)
}
} else if (searchable is LegacyShortcut) {
val packageName = searchable.packageName
if (packageName == null) {
send(null)
return@channelFlow
}
withContext(Dispatchers.IO) {
val icon = try {
context.packageManager.getApplicationIcon(
packageName
)
} catch (e: PackageManager.NameNotFoundException) {
return@withContext
}
val badge = Badge(icon = BadgeDrawable(context, icon))
send(badge)
}
} else if (searchable is UnavailableShortcut) {
val packageName = searchable.packageName
withContext(Dispatchers.IO) {
val icon = try {
context.packageManager.getApplicationIcon(
packageName
)
} catch (e: PackageManager.NameNotFoundException) {
return@withContext
}
val badge = Badge(icon = BadgeDrawable(context, icon))
send(badge)
}
} else {
send(null)
}
@@ -1,7 +1,7 @@
package de.mm20.launcher2.badges.providers
import de.mm20.launcher2.badges.Badge
import de.mm20.launcher2.search.data.File
import de.mm20.launcher2.search.File
import de.mm20.launcher2.search.Searchable
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flow
@@ -3,7 +3,7 @@ package de.mm20.launcher2.badges.providers
import de.mm20.launcher2.badges.Badge
import de.mm20.launcher2.notifications.NotificationRepository
import de.mm20.launcher2.search.Searchable
import de.mm20.launcher2.search.data.LauncherApp
import de.mm20.launcher2.search.Application
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.channelFlow
import kotlinx.coroutines.flow.collectLatest
@@ -15,8 +15,8 @@ class NotificationBadgeProvider : BadgeProvider, KoinComponent {
private val notificationRepository: NotificationRepository by inject()
override fun getBadge(searchable: Searchable): Flow<Badge?> = channelFlow {
if (searchable is LauncherApp) {
val packageName = searchable.`package`
if (searchable is Application) {
val packageName = searchable.componentName.packageName
notificationRepository.notifications.map {
it.filter { it.packageName == packageName }
}.collectLatest {
@@ -1,33 +1,18 @@
package de.mm20.launcher2.badges.providers
import de.mm20.launcher2.applications.AppRepository
import de.mm20.launcher2.badges.Badge
import de.mm20.launcher2.badges.R
import de.mm20.launcher2.search.Application
import de.mm20.launcher2.search.Searchable
import de.mm20.launcher2.search.data.LauncherApp
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.channelFlow
import kotlinx.coroutines.flow.collectLatest
import org.koin.core.component.KoinComponent
import org.koin.core.component.inject
class SuspendedAppsBadgeProvider : BadgeProvider, KoinComponent {
private val appRepository: AppRepository by inject()
override fun getBadge(searchable: Searchable): Flow<Badge?> = channelFlow {
if (searchable is LauncherApp) {
val packageName = searchable.`package`
appRepository.getSuspendedPackages().collectLatest {
if (it.contains(packageName)) {
send(
Badge(
iconRes = R.drawable.ic_badge_suspended
)
)
} else {
send(null)
}
}
if (searchable is Application && searchable.isSuspended) {
send(Badge(iconRes = R.drawable.ic_badge_suspended))
} else {
send(null)
}
@@ -2,15 +2,16 @@ package de.mm20.launcher2.badges.providers
import de.mm20.launcher2.badges.Badge
import de.mm20.launcher2.badges.R
import de.mm20.launcher2.search.data.LauncherApp
import de.mm20.launcher2.search.data.LauncherShortcut
import de.mm20.launcher2.search.AppProfile
import de.mm20.launcher2.search.AppShortcut
import de.mm20.launcher2.search.Application
import de.mm20.launcher2.search.Searchable
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flow
class WorkProfileBadgeProvider : BadgeProvider {
override fun getBadge(searchable: Searchable): Flow<Badge?> = flow {
if (searchable is LauncherApp && !searchable.isMainProfile || searchable is LauncherShortcut && !searchable.isMainProfile) {
if (searchable is Application && searchable.profile == AppProfile.Work || searchable is AppShortcut && searchable.profile == AppProfile.Work) {
emit(
Badge(
iconRes = R.drawable.ic_badge_workprofile
@@ -1,11 +1,11 @@
package de.mm20.launcher2.services.favorites
import de.mm20.launcher2.search.SavableSearchable
import de.mm20.launcher2.searchable.SearchableRepository
import de.mm20.launcher2.searchable.SavableSearchableRepository
import kotlinx.coroutines.flow.Flow
class FavoritesService(
val searchableRepository: SearchableRepository,
val searchableRepository: SavableSearchableRepository,
) {
@@ -32,15 +32,14 @@ import de.mm20.launcher2.icons.transformations.LegacyToAdaptiveTransformation
import de.mm20.launcher2.icons.transformations.transform
import de.mm20.launcher2.ktx.isAtLeastApiLevel
import de.mm20.launcher2.preferences.LauncherDataStore
import de.mm20.launcher2.search.Application
import de.mm20.launcher2.search.SavableSearchable
import de.mm20.launcher2.search.data.LauncherApp
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.channelFlow
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
@@ -283,9 +282,9 @@ class IconService(
val providerOptions = mutableListOf<CustomIcon>()
if (searchable is LauncherApp) {
if (searchable is Application) {
val iconPackIcons = iconPackManager.getAllIconPackIcons(
searchable.launcherActivityInfo.componentName
searchable.componentName
)
providerOptions.addAll(
@@ -6,15 +6,15 @@ import android.content.pm.PackageManager
import de.mm20.launcher2.icons.DynamicCalendarIcon
import de.mm20.launcher2.icons.LauncherIcon
import de.mm20.launcher2.ktx.obtainTypedArrayOrNull
import de.mm20.launcher2.search.Application
import de.mm20.launcher2.search.SavableSearchable
import de.mm20.launcher2.search.data.LauncherApp
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
class CalendarIconProvider(val context: Context, val themed: Boolean): IconProvider {
override suspend fun getIcon(searchable: SavableSearchable, size: Int): LauncherIcon? = withContext(Dispatchers.IO) {
if(searchable !is LauncherApp) return@withContext null
val component = ComponentName(searchable.`package`, searchable.activity)
if(searchable !is Application) return@withContext null
val component = searchable.componentName
val pm = context.packageManager
val ai = try {
pm.getActivityInfo(component, PackageManager.GET_META_DATA)
@@ -6,8 +6,8 @@ import android.content.pm.PackageManager
import de.mm20.launcher2.icons.LauncherIcon
import de.mm20.launcher2.icons.compat.AdaptiveIconDrawableCompat
import de.mm20.launcher2.icons.compat.toLauncherIcon
import de.mm20.launcher2.search.Application
import de.mm20.launcher2.search.SavableSearchable
import de.mm20.launcher2.search.data.LauncherApp
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
@@ -16,8 +16,8 @@ class CompatIconProvider(
private val themed: Boolean = false,
) : IconProvider {
override suspend fun getIcon(searchable: SavableSearchable, size: Int): LauncherIcon? {
if (searchable !is LauncherApp) return null
val component = ComponentName(searchable.`package`, searchable.activity)
if (searchable !is Application) return null
val component = searchable.componentName
val icon = withContext(Dispatchers.IO) {
val activityInfo = try {
@@ -6,18 +6,18 @@ import de.mm20.launcher2.icons.LauncherIcon
import de.mm20.launcher2.icons.compat.AdaptiveIconDrawableCompat
import de.mm20.launcher2.icons.compat.ClockIconConfig
import de.mm20.launcher2.icons.compat.toLauncherIcon
import de.mm20.launcher2.search.Application
import de.mm20.launcher2.search.SavableSearchable
import de.mm20.launcher2.search.data.LauncherApp
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
class DynamicClockIconProvider(val context: Context, private val themed: Boolean) : IconProvider {
override suspend fun getIcon(searchable: SavableSearchable, size: Int): LauncherIcon? = withContext(Dispatchers.IO) {
if (searchable !is LauncherApp) return@withContext null
if (searchable !is Application) return@withContext null
val pm = context.packageManager
val appInfo = try {
pm.getApplicationInfo(
searchable.`package`,
searchable.componentName.packageName,
PackageManager.GET_META_DATA
)
} catch (e: PackageManager.NameNotFoundException) {
@@ -47,7 +47,7 @@ class DynamicClockIconProvider(val context: Context, private val themed: Boolean
// Workaround for Google Clock themed icon because it is weird and I don't understand
// how to get the correct layers from the drawable without hardcoding them here.
val clockConfig = if (themed && searchable.`package` == "com.google.android.deskclock") {
val clockConfig = if (themed && searchable.componentName.packageName == "com.google.android.deskclock") {
ClockIconConfig(
hourLayer = 0,
minuteLayer = 2,
@@ -1,10 +1,12 @@
package de.mm20.launcher2.icons.providers
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.pm.LauncherApps
import androidx.core.content.getSystemService
import de.mm20.launcher2.icons.*
import de.mm20.launcher2.search.Application
import de.mm20.launcher2.search.SavableSearchable
import de.mm20.launcher2.search.data.LauncherApp
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
@@ -15,15 +17,20 @@ class IconPackIconProvider(
private val allowThemed: Boolean,
): IconProvider {
override suspend fun getIcon(searchable: SavableSearchable, size: Int): LauncherIcon? {
if (searchable !is LauncherApp) return null
if (searchable !is Application) return null
return iconPackManager.getIcon(iconPack.packageName, searchable.`package`, searchable.activity, allowThemed)
return iconPackManager.getIcon(iconPack.packageName, searchable.componentName.packageName, searchable.componentName.className, allowThemed)
?: iconPackManager.generateIcon(
context,
iconPack.packageName,
baseIcon = withContext(Dispatchers.IO) {
searchable.launcherActivityInfo.getIcon(context.resources.displayMetrics.densityDpi)
},
val ai = context.getSystemService<LauncherApps>()?.resolveActivity(
Intent().setComponent(searchable.componentName),
searchable.user
)
ai?.getIcon(context.resources.displayMetrics.densityDpi)
} ?: return null,
size = size,
)
}
-8
View File
@@ -46,20 +46,12 @@ dependencies {
implementation(libs.okhttp)
implementation(libs.coil.core)
implementation(project(":data:applications"))
implementation(project(":data:appshortcuts"))
implementation(project(":data:calculator"))
implementation(project(":data:calendar"))
implementation(project(":data:contacts"))
implementation(project(":data:files"))
implementation(project(":data:unitconverter"))
implementation(project(":data:websites"))
implementation(project(":data:wikipedia"))
implementation(project(":data:customattrs"))
implementation(project(":data:search-actions"))
implementation(project(":core:base"))
implementation(project(":core:database"))
implementation(project(":core:preferences"))
implementation(project(":core:crashreporter"))
implementation(project(":core:ktx"))
@@ -1,20 +1,20 @@
package de.mm20.launcher2.search
import org.koin.android.ext.koin.androidContext
import org.koin.core.qualifier.named
import org.koin.dsl.module
val searchModule = module {
single<SearchService> {
SearchServiceImpl(
get(named<Application>()),
get(named<AppShortcut>()),
get(named<CalendarEvent>()),
get(named<Contact>()),
get(named<File>()),
get(),
get(),
get(),
get(),
get(),
get(),
get(),
get(),
get(),
get(named<Website>()),
get(),
get(),
)
@@ -1,13 +1,8 @@
package de.mm20.launcher2.search
import de.mm20.launcher2.applications.AppRepository
import de.mm20.launcher2.appshortcuts.AppShortcutRepository
import de.mm20.launcher2.calculator.CalculatorRepository
import de.mm20.launcher2.calendar.CalendarRepository
import de.mm20.launcher2.contacts.ContactRepository
import de.mm20.launcher2.data.customattrs.CustomAttributesRepository
import de.mm20.launcher2.data.customattrs.utils.withCustomLabels
import de.mm20.launcher2.files.FileRepository
import de.mm20.launcher2.preferences.Settings
import de.mm20.launcher2.preferences.Settings.AppShortcutSearchSettings
import de.mm20.launcher2.preferences.Settings.CalculatorSearchSettings
@@ -17,25 +12,11 @@ import de.mm20.launcher2.preferences.Settings.FilesSearchSettings
import de.mm20.launcher2.preferences.Settings.UnitConverterSearchSettings
import de.mm20.launcher2.preferences.Settings.WebsiteSearchSettings
import de.mm20.launcher2.preferences.Settings.WikipediaSearchSettings
import de.mm20.launcher2.search.data.AppShortcut
import de.mm20.launcher2.search.data.Calculator
import de.mm20.launcher2.search.data.CalendarEvent
import de.mm20.launcher2.search.data.Contact
import de.mm20.launcher2.search.data.File
import de.mm20.launcher2.search.data.GDriveFile
import de.mm20.launcher2.search.data.LauncherApp
import de.mm20.launcher2.search.data.LocalFile
import de.mm20.launcher2.search.data.NextcloudFile
import de.mm20.launcher2.search.data.OneDriveFile
import de.mm20.launcher2.search.data.OwncloudFile
import de.mm20.launcher2.search.data.UnitConverter
import de.mm20.launcher2.search.data.Website
import de.mm20.launcher2.search.data.Wikipedia
import de.mm20.launcher2.searchactions.SearchActionService
import de.mm20.launcher2.searchactions.actions.SearchAction
import de.mm20.launcher2.unitconverter.UnitConverterRepository
import de.mm20.launcher2.websites.WebsiteRepository
import de.mm20.launcher2.wikipedia.WikipediaRepository
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.toImmutableList
@@ -84,15 +65,15 @@ interface SearchService {
}
internal class SearchServiceImpl(
private val appRepository: AppRepository,
private val appShortcutRepository: AppShortcutRepository,
private val calendarRepository: CalendarRepository,
private val contactRepository: ContactRepository,
private val fileRepository: FileRepository,
private val wikipediaRepository: WikipediaRepository,
private val appRepository: SearchableRepository<Application>,
private val appShortcutRepository: SearchableRepository<AppShortcut>,
private val calendarRepository: SearchableRepository<CalendarEvent>,
private val contactRepository: SearchableRepository<Contact>,
private val fileRepository: SearchableRepository<File>,
private val wikipediaRepository: SearchableRepository<Article>,
private val unitConverterRepository: UnitConverterRepository,
private val calculatorRepository: CalculatorRepository,
private val websiteRepository: WebsiteRepository,
private val websiteRepository: SearchableRepository<Website>,
private val searchActionService: SearchActionService,
private val customAttributesRepository: CustomAttributesRepository,
) : SearchService {
@@ -184,7 +165,6 @@ internal class SearchServiceImpl(
if (websites.enabled) {
launch {
websiteRepository.search(query)
.map { it?.let { listOf(it) } ?: listOf() }
.withCustomLabels(customAttributesRepository)
.collectLatest { r ->
results.update {
@@ -196,8 +176,7 @@ internal class SearchServiceImpl(
if (wikipedia.enabled) {
launch {
delay(750)
wikipediaRepository.search(query, loadImages = wikipedia.images)
.map { it?.let { listOf(it) } ?: listOf() }
wikipediaRepository.search(query)
.withCustomLabels(customAttributesRepository)
.collectLatest { r ->
results.update {
@@ -210,11 +189,6 @@ internal class SearchServiceImpl(
launch {
fileRepository.search(
query,
local = files.localFiles,
nextcloud = files.nextcloud,
owncloud = files.owncloud,
onedrive = files.onedrive,
gdrive = files.gdrive,
)
.withCustomLabels(customAttributesRepository)
.collectLatest { r ->
@@ -229,22 +203,7 @@ internal class SearchServiceImpl(
.withCustomLabels(customAttributesRepository)
.collectLatest { r ->
results.update {
it.copy(
other = r
.filter {
it is LauncherApp ||
shortcuts.enabled && it is AppShortcut ||
files.localFiles && it is LocalFile ||
files.nextcloud && it is NextcloudFile ||
files.owncloud && it is OwncloudFile ||
files.onedrive && it is OneDriveFile ||
files.gdrive && it is GDriveFile ||
wikipedia.enabled && it is Wikipedia ||
websites.enabled && it is Website ||
calendars.enabled && it is CalendarEvent ||
contacts.enabled && it is Contact
}.toImmutableList()
)
it.copy(other = r.toImmutableList())
}
}
}
@@ -257,7 +216,7 @@ internal class SearchServiceImpl(
}
data class SearchResults(
val apps: ImmutableList<LauncherApp>? = null,
val apps: ImmutableList<Application>? = null,
val shortcuts: ImmutableList<AppShortcut>? = null,
val contacts: ImmutableList<Contact>? = null,
val calendars: ImmutableList<CalendarEvent>? = null,
@@ -265,7 +224,7 @@ data class SearchResults(
val calculators: ImmutableList<Calculator>? = null,
val unitConverters: ImmutableList<UnitConverter>? = null,
val websites: ImmutableList<Website>? = null,
val wikipedia: ImmutableList<Wikipedia>? = null,
val wikipedia: ImmutableList<Article>? = null,
val searchActions: ImmutableList<SearchAction>? = null,
val other: ImmutableList<SavableSearchable>? = null,
)
@@ -1,81 +0,0 @@
package de.mm20.launcher2.search.data
import android.content.Intent
import android.net.Uri
import de.mm20.launcher2.database.entities.WebsearchEntity
import java.net.URLEncoder
import java.nio.charset.Charset
import java.nio.charset.StandardCharsets
class Websearch(
var urlTemplate: String,
var label: String,
var color: Int,
var icon: String?,
var id: Long? = null,
var encoding: QueryEncoding = QueryEncoding.UrlEncode,
val query: String? = null,
) {
constructor(entity: WebsearchEntity, query: String? = null) : this(
urlTemplate = entity.urlTemplate,
label = entity.label,
icon = entity.icon,
color = entity.color,
id = entity.id,
query = query,
encoding = QueryEncoding.fromInt(entity.encoding)
)
fun toDatabaseEntity(): WebsearchEntity {
return WebsearchEntity(
urlTemplate = urlTemplate,
color = color,
icon = icon,
label = label,
id = id,
encoding = encoding.toInt()
)
}
fun getLaunchIntent(): Intent? {
if (query == null) return null
val intent = Intent(Intent.ACTION_VIEW)
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
val url = urlTemplate.replace("\${1}", encodeQuery(query, encoding))
intent.data = Uri.parse(url)
return intent
}
private fun encodeQuery(query: String, encoding: QueryEncoding): String {
return when(encoding) {
QueryEncoding.UrlEncode -> Uri.encode(query)
QueryEncoding.FormData -> URLEncoder.encode(query, "UTF-8")
QueryEncoding.None -> query
}
}
enum class QueryEncoding {
UrlEncode,
FormData,
None;
fun toInt(): Int {
return when (this) {
UrlEncode -> 0
FormData -> 1
None -> 2
}
}
companion object {
fun fromInt(value: Int?): QueryEncoding {
return when (value) {
1 -> FormData
2 -> None
else -> UrlEncode
}
}
}
}
}
@@ -1,7 +1,7 @@
package de.mm20.launcher2.services.tags.impl
import de.mm20.launcher2.data.customattrs.CustomAttributesRepository
import de.mm20.launcher2.searchable.SearchableRepository
import de.mm20.launcher2.searchable.SavableSearchableRepository
import de.mm20.launcher2.search.SavableSearchable
import de.mm20.launcher2.search.data.Tag
import de.mm20.launcher2.services.tags.TagsService
@@ -14,7 +14,7 @@ import kotlinx.coroutines.launch
internal class TagsServiceImpl(
private val customAttributesRepository: CustomAttributesRepository,
private val searchableRepository: SearchableRepository,
private val searchableRepository: SavableSearchableRepository,
) : TagsService {
private val scope = CoroutineScope(Job() + Dispatchers.Default)
override fun getAllTags(startsWith: String?): Flow<List<String>> {