Modify badge API

Pass searchable as argument instead of badgeKey which is now obsolete
This commit is contained in:
MM20 2022-03-05 15:00:33 +01:00
parent 9100af75bf
commit 47e7591975
No known key found for this signature in database
GPG Key ID: 0B61A8F2DEAFA389
25 changed files with 60 additions and 66 deletions

BIN
app/release/app-release.zip Normal file

Binary file not shown.

View File

@ -26,9 +26,6 @@ class AppInstallation(
override val key: String override val key: String
get() = "installer://${session.installerPackageName}:${session.appPackageName}" get() = "installer://${session.installerPackageName}:${session.appPackageName}"
override val badgeKey: String
get() = "app://${session.appPackageName}"
override fun getLaunchIntent(context: Context): Intent? { override fun getLaunchIntent(context: Context): Intent? {
return session.createDetailsIntent() return session.createDetailsIntent()
} }

View File

@ -32,7 +32,7 @@ class AppShortcut(
internal val userSerialNumber: Long = launcherShortcut.userHandle.getSerialNumber(context) internal val userSerialNumber: Long = launcherShortcut.userHandle.getSerialNumber(context)
private val isMainProfile = launcherShortcut.userHandle == Process.myUserHandle() val isMainProfile = launcherShortcut.userHandle == Process.myUserHandle()
override val key: String override val key: String
get() = if (isMainProfile) { get() = if (isMainProfile) {
@ -41,11 +41,6 @@ class AppShortcut(
"shortcut://${launcherShortcut.`package`}/${launcherShortcut.id}:${userSerialNumber}" "shortcut://${launcherShortcut.`package`}/${launcherShortcut.id}:${userSerialNumber}"
} }
override val badgeKey: String
get() {
return if (isMainProfile) "shortcut://${launcherShortcut.activity?.flattenToShortString()}" else "profile://$userSerialNumber"
}
override fun getLaunchIntent(context: Context): Intent? { override fun getLaunchIntent(context: Context): Intent? {
return launcherShortcut.intent return launcherShortcut.intent
} }

View File

@ -20,9 +20,6 @@ abstract class Application(
val shortcuts: List<AppShortcut> = emptyList() val shortcuts: List<AppShortcut> = emptyList()
) : Searchable() { ) : Searchable() {
override val badgeKey: String
get() = "app://${`package`}"
override fun serialize(): String { override fun serialize(): String {
val json = JSONObject() val json = JSONObject()
json.put("package", `package`) json.put("package", `package`)

View File

@ -57,10 +57,7 @@ class LauncherApp(
), KoinComponent { ), KoinComponent {
internal val userSerialNumber: Long = launcherActivityInfo.user.getSerialNumber(context) internal val userSerialNumber: Long = launcherActivityInfo.user.getSerialNumber(context)
private val isMainProfile = launcherActivityInfo.user == Process.myUserHandle() val isMainProfile = launcherActivityInfo.user == Process.myUserHandle()
override val badgeKey: String =
if (isMainProfile) "app://${`package`}" else "profile://$userSerialNumber"
override val key: String override val key: String
get() = if (isMainProfile) "app://$`package`:$activity" else "app://$`package`:$activity:${userSerialNumber}" get() = if (isMainProfile) "app://$`package`:$activity" else "app://$`package`:$activity:${userSerialNumber}"

View File

@ -47,5 +47,6 @@ dependencies {
implementation(project(":notifications")) implementation(project(":notifications"))
implementation(project(":preferences")) implementation(project(":preferences"))
implementation(project(":base")) implementation(project(":base"))
implementation(project(":search"))
implementation(project(":files"))
} }

View File

@ -3,13 +3,14 @@ package de.mm20.launcher2.badges
import android.content.Context import android.content.Context
import de.mm20.launcher2.badges.providers.* import de.mm20.launcher2.badges.providers.*
import de.mm20.launcher2.preferences.LauncherDataStore import de.mm20.launcher2.preferences.LauncherDataStore
import de.mm20.launcher2.search.data.Searchable
import kotlinx.coroutines.* import kotlinx.coroutines.*
import kotlinx.coroutines.flow.* import kotlinx.coroutines.flow.*
import org.koin.core.component.KoinComponent import org.koin.core.component.KoinComponent
import org.koin.core.component.inject import org.koin.core.component.inject
interface BadgeRepository { interface BadgeRepository {
fun getBadge(badgeKey: String): Flow<Badge?> fun getBadge(searchable: Searchable): Flow<Badge?>
} }
internal class BadgeRepositoryImpl(private val context: Context) : BadgeRepository, KoinComponent { internal class BadgeRepositoryImpl(private val context: Context) : BadgeRepository, KoinComponent {
@ -23,6 +24,7 @@ internal class BadgeRepositoryImpl(private val context: Context) : BadgeReposito
scope.launch { scope.launch {
dataStore.data.map { it.badges }.distinctUntilChanged().collectLatest { dataStore.data.map { it.badges }.distinctUntilChanged().collectLatest {
val providers = mutableListOf<BadgeProvider>() val providers = mutableListOf<BadgeProvider>()
providers += WorkProfileBadgeProvider()
if (it.notifications) { if (it.notifications) {
providers += NotificationBadgeProvider() providers += NotificationBadgeProvider()
} }
@ -35,20 +37,19 @@ internal class BadgeRepositoryImpl(private val context: Context) : BadgeReposito
if (it.suspendedApps) { if (it.suspendedApps) {
providers += SuspendedAppsBadgeProvider() providers += SuspendedAppsBadgeProvider()
} }
providers += WorkProfileBadgeProvider(context)
badgeProviders.value = providers badgeProviders.value = providers
} }
} }
} }
override fun getBadge(badgeKey: String): Flow<Badge?> = channelFlow { override fun getBadge(searchable: Searchable): Flow<Badge?> = channelFlow {
withContext(Dispatchers.Default) { withContext(Dispatchers.Default) {
badgeProviders.collectLatest { providers -> badgeProviders.collectLatest { providers ->
if (providers.isEmpty()) { if (providers.isEmpty()) {
send(null) send(null)
return@collectLatest return@collectLatest
} }
combine(providers.map { it.getBadge(badgeKey) }) { badges -> combine(providers.map { it.getBadge(searchable) }) { badges ->
if (badges.all { it == null }) { if (badges.all { it == null }) {
return@combine null return@combine null
} }

View File

@ -5,6 +5,8 @@ import android.content.Context
import android.content.pm.PackageManager import android.content.pm.PackageManager
import de.mm20.launcher2.badges.Badge import de.mm20.launcher2.badges.Badge
import de.mm20.launcher2.graphics.BadgeDrawable import de.mm20.launcher2.graphics.BadgeDrawable
import de.mm20.launcher2.search.data.AppShortcut
import de.mm20.launcher2.search.data.Searchable
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.channelFlow import kotlinx.coroutines.flow.channelFlow
@ -13,9 +15,9 @@ import kotlinx.coroutines.withContext
class AppShortcutBadgeProvider( class AppShortcutBadgeProvider(
private val context: Context private val context: Context
) : BadgeProvider { ) : BadgeProvider {
override fun getBadge(badgeKey: String): Flow<Badge?> = channelFlow { override fun getBadge(searchable: Searchable): Flow<Badge?> = channelFlow {
if (badgeKey.startsWith("shortcut://")) { if (searchable is AppShortcut) {
val componentName = ComponentName.unflattenFromString(badgeKey.substring(11)) val componentName = searchable.launcherShortcut.activity
if (componentName == null) { if (componentName == null) {
send(null) send(null)
return@channelFlow return@channelFlow

View File

@ -1,6 +1,7 @@
package de.mm20.launcher2.badges.providers package de.mm20.launcher2.badges.providers
import de.mm20.launcher2.badges.Badge import de.mm20.launcher2.badges.Badge
import de.mm20.launcher2.search.data.Searchable
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
interface BadgeProvider { interface BadgeProvider {
@ -9,5 +10,5 @@ interface BadgeProvider {
* BadgeRepository is waiting for values from every provider. * BadgeRepository is waiting for values from every provider.
* null must be emitted if no badge should be shown. * null must be emitted if no badge should be shown.
*/ */
fun getBadge(badgeKey: String): Flow<Badge?> fun getBadge(searchable: Searchable): Flow<Badge?>
} }

View File

@ -3,17 +3,20 @@ package de.mm20.launcher2.badges.providers
import android.util.Log import android.util.Log
import de.mm20.launcher2.badges.Badge import de.mm20.launcher2.badges.Badge
import de.mm20.launcher2.badges.R import de.mm20.launcher2.badges.R
import de.mm20.launcher2.search.data.File
import de.mm20.launcher2.search.data.Searchable
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.flow
class CloudBadgeProvider: BadgeProvider { class CloudBadgeProvider: BadgeProvider {
override fun getBadge(badgeKey: String): Flow<Badge?> = flow { override fun getBadge(searchable: Searchable): Flow<Badge?> = flow {
when(badgeKey) { if (searchable is File) {
"gdrive://" -> emit(Badge(iconRes = R.drawable.ic_badge_gdrive)) val iconResId = searchable.providerIconRes
"onedrive://" -> emit(Badge(iconRes = R.drawable.ic_badge_onedrive)) if (iconResId != null) {
"owncloud://" -> emit(Badge(iconRes = R.drawable.ic_badge_owncloud)) emit(Badge(iconRes = iconResId))
"nextcloud://" -> emit(Badge(iconRes = R.drawable.ic_badge_nextcloud)) return@flow
else -> emit(null) }
} }
emit(null)
} }
} }

View File

@ -3,6 +3,9 @@ package de.mm20.launcher2.badges.providers
import android.app.Notification import android.app.Notification
import de.mm20.launcher2.badges.Badge import de.mm20.launcher2.badges.Badge
import de.mm20.launcher2.notifications.NotificationRepository import de.mm20.launcher2.notifications.NotificationRepository
import de.mm20.launcher2.search.data.Application
import de.mm20.launcher2.search.data.LauncherApp
import de.mm20.launcher2.search.data.Searchable
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.channelFlow import kotlinx.coroutines.flow.channelFlow
import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.collectLatest
@ -13,9 +16,9 @@ import org.koin.core.component.inject
class NotificationBadgeProvider : BadgeProvider, KoinComponent { class NotificationBadgeProvider : BadgeProvider, KoinComponent {
private val notificationRepository: NotificationRepository by inject() private val notificationRepository: NotificationRepository by inject()
override fun getBadge(badgeKey: String): Flow<Badge?> = channelFlow { override fun getBadge(searchable: Searchable): Flow<Badge?> = channelFlow {
if (badgeKey.startsWith("app://")) { if (searchable is Application) {
val packageName = badgeKey.substring(6) val packageName = searchable.`package`
notificationRepository.notifications.map { notificationRepository.notifications.map {
it.filter { it.packageName == packageName } it.filter { it.packageName == packageName }
}.collectLatest { }.collectLatest {

View File

@ -3,6 +3,9 @@ package de.mm20.launcher2.badges.providers
import de.mm20.launcher2.applications.AppRepository import de.mm20.launcher2.applications.AppRepository
import de.mm20.launcher2.badges.Badge import de.mm20.launcher2.badges.Badge
import de.mm20.launcher2.badges.R import de.mm20.launcher2.badges.R
import de.mm20.launcher2.search.data.Application
import de.mm20.launcher2.search.data.LauncherApp
import de.mm20.launcher2.search.data.Searchable
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.channelFlow import kotlinx.coroutines.flow.channelFlow
import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.collectLatest
@ -12,9 +15,9 @@ import org.koin.core.component.inject
class SuspendedAppsBadgeProvider : BadgeProvider, KoinComponent { class SuspendedAppsBadgeProvider : BadgeProvider, KoinComponent {
private val appRepository: AppRepository by inject() private val appRepository: AppRepository by inject()
override fun getBadge(badgeKey: String): Flow<Badge?> = channelFlow { override fun getBadge(searchable: Searchable): Flow<Badge?> = channelFlow {
if (badgeKey.startsWith("app://")) { if (searchable is Application) {
val packageName = badgeKey.substring(6) val packageName = searchable.`package`
appRepository.getSuspendedPackages().collectLatest { appRepository.getSuspendedPackages().collectLatest {
if (it.contains(packageName)) { if (it.contains(packageName)) {
send( send(

View File

@ -1,26 +1,21 @@
package de.mm20.launcher2.badges.providers package de.mm20.launcher2.badges.providers
import android.content.Context
import android.os.Process
import de.mm20.launcher2.badges.Badge import de.mm20.launcher2.badges.Badge
import de.mm20.launcher2.badges.R import de.mm20.launcher2.badges.R
import de.mm20.launcher2.ktx.getSerialNumber import de.mm20.launcher2.search.data.AppShortcut
import de.mm20.launcher2.search.data.LauncherApp
import de.mm20.launcher2.search.data.Searchable
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.flow
class WorkProfileBadgeProvider(private val context: Context) : BadgeProvider { class WorkProfileBadgeProvider : BadgeProvider {
override fun getBadge(badgeKey: String): Flow<Badge?> = flow { override fun getBadge(searchable: Searchable): Flow<Badge?> = flow {
if (badgeKey.startsWith("profile://")) { if (searchable is LauncherApp && !searchable.isMainProfile || searchable is AppShortcut && !searchable.isMainProfile) {
val serialNumber = badgeKey.substring(10).toLong() emit(
if (serialNumber != Process.myUserHandle().getSerialNumber(context)) { Badge(
emit( iconRes = R.drawable.ic_badge_workprofile
Badge(
iconRes = R.drawable.ic_badge_workprofile
)
) )
} else { )
emit(null)
}
} else { } else {
emit(null) emit(null)
} }

View File

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 10 KiB

View File

Before

Width:  |  Height:  |  Size: 9.0 KiB

After

Width:  |  Height:  |  Size: 9.0 KiB

View File

Before

Width:  |  Height:  |  Size: 9.4 KiB

After

Width:  |  Height:  |  Size: 9.4 KiB

View File

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

View File

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 10 KiB

View File

@ -17,6 +17,8 @@ abstract class File(
) : Searchable() { ) : Searchable() {
abstract val isStoredInCloud: Boolean abstract val isStoredInCloud: Boolean
open val providerIconRes: Int? = null
override fun getPlaceholderIcon(context: Context): LauncherIcon { override fun getPlaceholderIcon(context: Context): LauncherIcon {
val (resId, bgColor) = when { val (resId, bgColor) = when {
isDirectory -> R.drawable.ic_file_folder to R.color.lightblue isDirectory -> R.drawable.ic_file_folder to R.color.lightblue

View File

@ -23,11 +23,10 @@ class GDriveFile(
override val key: String = "gdrive://$fileId" override val key: String = "gdrive://$fileId"
override val badgeKey: String
get() = "gdrive://"
override val isStoredInCloud = true override val isStoredInCloud = true
override val providerIconRes = R.drawable.ic_badge_gdrive
override fun getLaunchIntent(context: Context): Intent? { override fun getLaunchIntent(context: Context): Intent? {
return Intent(Intent.ACTION_VIEW).apply { return Intent(Intent.ACTION_VIEW).apply {
data = Uri.parse(viewUri) data = Uri.parse(viewUri)

View File

@ -4,6 +4,7 @@ import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.pm.PackageManager import android.content.pm.PackageManager
import android.net.Uri import android.net.Uri
import de.mm20.launcher2.files.R
class NextcloudFile( class NextcloudFile(
fileId: Long, fileId: Long,
@ -15,13 +16,13 @@ class NextcloudFile(
val server: String, val server: String,
metaData: List<Pair<Int, String>> metaData: List<Pair<Int, String>>
) : File(fileId, path, mimeType, size, isDirectory, metaData) { ) : File(fileId, path, mimeType, size, isDirectory, metaData) {
override val badgeKey: String = "nextcloud://"
override val key: String = "nextcloud://$server/$fileId" override val key: String = "nextcloud://$server/$fileId"
override val isStoredInCloud: Boolean override val isStoredInCloud: Boolean
get() = true get() = true
override val providerIconRes = R.drawable.ic_badge_nextcloud
override fun getLaunchIntent(context: Context): Intent? { override fun getLaunchIntent(context: Context): Intent? {
return Intent(Intent.ACTION_VIEW).apply { return Intent(Intent.ACTION_VIEW).apply {
data = Uri.parse("$server/f/$id") data = Uri.parse("$server/f/$id")

View File

@ -19,10 +19,10 @@ class OneDriveFile(
val webUrl: String val webUrl: String
) : File(0, path, mimeType, size, isDirectory, metaData) { ) : File(0, path, mimeType, size, isDirectory, metaData) {
override val badgeKey: String = "onedrive://"
override val key: String = "onedrive://$fileId" override val key: String = "onedrive://$fileId"
override val providerIconRes = R.drawable.ic_badge_onedrive
override val isStoredInCloud = true override val isStoredInCloud = true
override fun getLaunchIntent(context: Context): Intent? { override fun getLaunchIntent(context: Context): Intent? {

View File

@ -17,13 +17,13 @@ class OwncloudFile(
val server: String, val server: String,
metaData: List<Pair<Int, String>> metaData: List<Pair<Int, String>>
) : File(fileId, path, mimeType, size, isDirectory, metaData) { ) : File(fileId, path, mimeType, size, isDirectory, metaData) {
override val badgeKey: String = "owncloud://"
override val key: String = "owncloud://$server/$fileId" override val key: String = "owncloud://$server/$fileId"
override val isStoredInCloud: Boolean override val isStoredInCloud: Boolean
get() = true get() = true
override val providerIconRes = R.drawable.ic_badge_owncloud
override fun getLaunchIntent(context: Context): Intent? { override fun getLaunchIntent(context: Context): Intent? {
return Intent(Intent.ACTION_VIEW).apply { return Intent(Intent.ACTION_VIEW).apply {
data = Uri.parse("$server/f/$id") data = Uri.parse("$server/f/$id")

View File

@ -16,9 +16,6 @@ abstract class Searchable : Comparable<Searchable> {
abstract val key: String abstract val key: String
abstract val label: String abstract val label: String
open val badgeKey
get() = key
open fun serialize(): String = "" open fun serialize(): String = ""
open fun getLaunchIntent(context: Context): Intent? = null open fun getLaunchIntent(context: Context): Intent? = null

View File

@ -38,7 +38,7 @@ abstract class SearchableItemVM(
favoritesRepository.unhideItem(searchable) favoritesRepository.unhideItem(searchable)
} }
val badge = badgeRepository.getBadge(searchable.badgeKey) val badge = badgeRepository.getBadge(searchable)
fun getIcon(size: Int): Flow<LauncherIcon> { fun getIcon(size: Int): Flow<LauncherIcon> {
return iconRepository.getIcon(searchable, size) return iconRepository.getIcon(searchable, size)