Create appshortcuts module

This commit is contained in:
MM20
2022-03-19 15:46:13 +01:00
parent fb762736a1
commit b73c9fabc9
22 changed files with 244 additions and 95 deletions
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="de.mm20.launcher2.appshortcuts">
</manifest>
@@ -0,0 +1,51 @@
package de.mm20.launcher2.appshortcuts
import android.content.Context
import android.content.pm.LauncherActivityInfo
import android.content.pm.LauncherApps
import android.content.pm.ShortcutInfo
import android.os.UserHandle
import androidx.core.content.getSystemService
import de.mm20.launcher2.search.data.AppShortcut
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
interface AppShortcutRepository {
suspend fun getShortcutsForActivity(launcherActivityInfo: LauncherActivityInfo, count: Int = 5): List<AppShortcut>
}
internal class AppShortcutRepositoryImpl(
private val context: Context
): AppShortcutRepository {
override suspend fun getShortcutsForActivity(
launcherActivityInfo: LauncherActivityInfo,
count: Int,
) = withContext(Dispatchers.IO){
val launcherApps = context.getSystemService<LauncherApps>()!!
if (!launcherApps.hasShortcutHostPermission()) return@withContext emptyList()
val query = LauncherApps.ShortcutQuery()
.setPackage(launcherActivityInfo.applicationInfo.packageName)
.setQueryFlags(LauncherApps.ShortcutQuery.FLAG_MATCH_DYNAMIC or LauncherApps.ShortcutQuery.FLAG_MATCH_MANIFEST)
val shortcuts = try {
launcherApps.getShortcuts(query, launcherActivityInfo.user)
} catch (e: IllegalStateException) {
emptyList()
}
val appShortcuts = mutableListOf<AppShortcut>()
appShortcuts.addAll(shortcuts
?.let {
if (it.size > count) it.subList(0, count)
else it
}
?.map {
AppShortcut(
context,
it,
launcherActivityInfo.label.toString()
)
} ?: emptyList())
appShortcuts
}
}
@@ -0,0 +1,78 @@
package de.mm20.launcher2.appshortcuts
import android.content.Context
import android.content.pm.LauncherApps
import android.content.pm.PackageManager
import android.os.Process
import android.os.UserManager
import androidx.core.content.getSystemService
import de.mm20.launcher2.ktx.jsonObjectOf
import de.mm20.launcher2.search.SearchableDeserializer
import de.mm20.launcher2.search.SearchableSerializer
import de.mm20.launcher2.search.data.AppShortcut
import de.mm20.launcher2.search.data.Searchable
import org.json.JSONObject
import org.koin.core.component.KoinComponent
class AppShortcutSerializer : SearchableSerializer {
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 {
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 {
val activity = shortcuts[0].activity
return AppShortcut(
context = context,
launcherShortcut = shortcuts[0],
appName = appName
)
}
}
}
}
@@ -0,0 +1,8 @@
package de.mm20.launcher2.appshortcuts
import org.koin.android.ext.koin.androidContext
import org.koin.dsl.module
val appShortcutsModule = module {
single<AppShortcutRepository> { AppShortcutRepositoryImpl(androidContext()) }
}
@@ -0,0 +1,88 @@
package de.mm20.launcher2.search.data
import android.content.Context
import android.content.Intent
import android.content.pm.LauncherApps
import android.content.pm.ShortcutInfo
import android.graphics.drawable.AdaptiveIconDrawable
import android.graphics.drawable.ColorDrawable
import android.os.Bundle
import android.os.Process
import androidx.core.content.ContextCompat
import androidx.core.content.getSystemService
import de.mm20.launcher2.appshortcuts.R
import de.mm20.launcher2.icons.LauncherIcon
import de.mm20.launcher2.ktx.getSerialNumber
import de.mm20.launcher2.preferences.Settings.IconSettings.LegacyIconBackground
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
class AppShortcut(
context: Context,
val launcherShortcut: ShortcutInfo,
val appName: String
) : Searchable() {
override val label: String
get() = launcherShortcut.shortLabel?.toString() ?: ""
internal val userSerialNumber: Long = launcherShortcut.userHandle.getSerialNumber(context)
val isMainProfile = launcherShortcut.userHandle == Process.myUserHandle()
override val key: String
get() = if (isMainProfile) {
"shortcut://${launcherShortcut.`package`}/${launcherShortcut.id}"
} else {
"shortcut://${launcherShortcut.`package`}/${launcherShortcut.id}:${userSerialNumber}"
}
override fun getLaunchIntent(context: Context): Intent? {
return launcherShortcut.intent
}
override fun launch(context: Context, options: Bundle?): Boolean {
val launcherApps = context.getSystemService<LauncherApps>()!!
try {
launcherApps.startShortcut(launcherShortcut, null, options)
} catch (e: IllegalStateException) {
return false
}
return true
}
override fun getPlaceholderIcon(context: Context): LauncherIcon {
return LauncherIcon(
foreground = ContextCompat.getDrawable(context, R.drawable.ic_file_android)!!,
background = ColorDrawable(ContextCompat.getColor(context, R.color.green)),
foregroundScale = 0.5f
)
}
override suspend fun loadIcon(
context: Context,
size: Int,
legacyIconBackground: LegacyIconBackground
): LauncherIcon? {
val launcherApps = context.getSystemService(Context.LAUNCHER_APPS_SERVICE) as LauncherApps
val icon = withContext(Dispatchers.IO) {
launcherApps.getShortcutIconDrawable(
launcherShortcut,
context.resources.displayMetrics.densityDpi
)
} ?: return null
if (icon is AdaptiveIconDrawable) {
return LauncherIcon(
foreground = icon.foreground,
background = icon.background,
foregroundScale = 1.5f,
backgroundScale = 1.5f
)
}
return LauncherIcon(
foreground = icon,
foregroundScale = 1f,
autoGenerateBackgroundMode = legacyIconBackground.number
)
}
}