Annual refactor
This commit is contained in:
@@ -0,0 +1,58 @@
|
||||
package de.mm20.launcher2.applications
|
||||
|
||||
import android.content.ComponentName
|
||||
import android.content.Context
|
||||
import android.os.Bundle
|
||||
import android.os.Process
|
||||
import android.os.UserHandle
|
||||
import de.mm20.launcher2.search.AppProfile
|
||||
import de.mm20.launcher2.search.Application
|
||||
import de.mm20.launcher2.search.NullSerializer
|
||||
import de.mm20.launcher2.search.SavableSearchable
|
||||
import de.mm20.launcher2.search.SearchableSerializer
|
||||
|
||||
class FakeApp: Application {
|
||||
override val componentName: ComponentName = ComponentName(randomString(), randomString())
|
||||
override val isSystemApp: Boolean = false
|
||||
override val isSuspended: Boolean = false
|
||||
override val profile: AppProfile = AppProfile.Personal
|
||||
override val user: UserHandle = Process.myUserHandle()
|
||||
override val versionName: String = "1.0"
|
||||
override val canUninstall: Boolean = false
|
||||
|
||||
override fun uninstall(context: Context) {
|
||||
|
||||
}
|
||||
|
||||
override fun openAppDetails(context: Context) {
|
||||
|
||||
}
|
||||
|
||||
override val canShareApk: Boolean = false
|
||||
override val key: String = "fake://${randomString()}"
|
||||
override val label: String = randomString()
|
||||
|
||||
override fun overrideLabel(label: String): SavableSearchable {
|
||||
return this
|
||||
}
|
||||
|
||||
override fun launch(context: Context, options: Bundle?): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override val domain: String
|
||||
get() = "fake"
|
||||
|
||||
override fun getSerializer(): SearchableSerializer {
|
||||
return NullSerializer()
|
||||
}
|
||||
|
||||
private companion object {
|
||||
private fun randomString(): String {
|
||||
val charset = "abcdefghijklmnopqrstuvwxyz"
|
||||
return (1..10)
|
||||
.map { charset.random() }
|
||||
.joinToString("")
|
||||
}
|
||||
}
|
||||
}
|
||||
+10
-43
@@ -6,8 +6,8 @@ import android.content.Intent
|
||||
import android.content.pm.LauncherApps
|
||||
import android.os.Process
|
||||
import androidx.core.content.getSystemService
|
||||
import de.mm20.launcher2.ktx.getSerialNumber
|
||||
import de.mm20.launcher2.search.data.LauncherApp
|
||||
import de.mm20.launcher2.search.Application
|
||||
import de.mm20.launcher2.search.SearchableRepository
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
@@ -16,51 +16,18 @@ import kotlinx.coroutines.flow.flowOf
|
||||
import kotlinx.coroutines.flow.map
|
||||
|
||||
/**
|
||||
* A fake implementation of [AppRepository] to simulate many installed apps.
|
||||
* App repository that returns a fixed number of fake apps to simulate a large number of apps.
|
||||
*/
|
||||
class FakeAppRepository(private val context: Context, private val fakePackages: Int) : AppRepository {
|
||||
class FakeAppRepository(private val context: Context, private val fakePackages: Int) : SearchableRepository<Application> {
|
||||
|
||||
|
||||
private val fakeApp: LauncherApp
|
||||
|
||||
init {
|
||||
val launcherApps = context.getSystemService<LauncherApps>()!!
|
||||
fakeApp = LauncherApp(
|
||||
context,
|
||||
launcherApps.resolveActivity(
|
||||
Intent().apply {
|
||||
component = ComponentName(
|
||||
context.packageName,
|
||||
"de.mm20.launcher2.ui.launcher.LauncherActivity"
|
||||
)
|
||||
},
|
||||
Process.myUserHandle()
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
private fun randomString(): String {
|
||||
val charset = "abcdefghijklmnopqrstuvwxyz"
|
||||
return (1..10)
|
||||
.map { charset.random() }
|
||||
.joinToString("")
|
||||
}
|
||||
|
||||
override fun getAllInstalledApps(): Flow<List<LauncherApp>> {
|
||||
return flowOf(buildList {
|
||||
repeat(fakePackages) {
|
||||
add(fakeApp.copy(`package` = randomString(), activity = randomString()))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
override fun getSuspendedPackages(): Flow<List<String>> {
|
||||
return flowOf(emptyList())
|
||||
}
|
||||
|
||||
override fun search(query: String): Flow<ImmutableList<LauncherApp>> {
|
||||
override fun search(query: String): Flow<ImmutableList<Application>> {
|
||||
return if (query.isEmpty()) {
|
||||
getAllInstalledApps().map { it.toImmutableList() }
|
||||
buildList {
|
||||
repeat(fakePackages) {
|
||||
add(FakeApp())
|
||||
}
|
||||
}.toImmutableList().let { flowOf(it) }
|
||||
} else {
|
||||
flowOf(persistentListOf())
|
||||
}
|
||||
|
||||
@@ -10,28 +10,34 @@ import android.os.Handler
|
||||
import android.os.Looper
|
||||
import android.os.Process
|
||||
import android.os.UserHandle
|
||||
import android.util.Log
|
||||
import de.mm20.launcher2.ktx.normalize
|
||||
import de.mm20.launcher2.search.data.LauncherApp
|
||||
import de.mm20.launcher2.search.Application
|
||||
import de.mm20.launcher2.search.SearchableRepository
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.apache.commons.text.similarity.FuzzyScore
|
||||
import java.util.Locale
|
||||
|
||||
interface AppRepository {
|
||||
fun getAllInstalledApps(): Flow<List<LauncherApp>>
|
||||
fun getSuspendedPackages(): Flow<List<String>>
|
||||
fun search(query: String): Flow<ImmutableList<LauncherApp>>
|
||||
interface AppRepository : SearchableRepository<Application> {
|
||||
override fun search(query: String): Flow<ImmutableList<Application>>
|
||||
|
||||
fun findMany(): Flow<ImmutableList<Application>>
|
||||
}
|
||||
|
||||
internal class AppRepositoryImpl(
|
||||
private val context: Context,
|
||||
) : AppRepository {
|
||||
private val scope = CoroutineScope(Dispatchers.Default + Job())
|
||||
|
||||
private val launcherApps =
|
||||
context.getSystemService(Context.LAUNCHER_APPS_SERVICE) as LauncherApps
|
||||
@@ -40,9 +46,10 @@ internal class AppRepositoryImpl(
|
||||
private val suspendedPackages = MutableStateFlow<List<String>>(emptyList())
|
||||
|
||||
|
||||
private val profiles: List<UserHandle> =
|
||||
launcherApps.profiles.takeIf { it.isNotEmpty() } ?: listOf(Process.myUserHandle())
|
||||
private val profiles: List<UserHandle>
|
||||
get() = launcherApps.profiles.takeIf { it.isNotEmpty() } ?: listOf(Process.myUserHandle())
|
||||
|
||||
private val mutex = Mutex()
|
||||
|
||||
init {
|
||||
launcherApps.registerCallback(object : LauncherApps.Callback() {
|
||||
@@ -51,15 +58,23 @@ internal class AppRepositoryImpl(
|
||||
user: UserHandle,
|
||||
replacing: Boolean
|
||||
) {
|
||||
installedApps.value =
|
||||
installedApps.value.filter { !packageNames.contains(it.`package`) }
|
||||
scope.launch {
|
||||
mutex.withLock {
|
||||
installedApps.value =
|
||||
installedApps.value.filter { !packageNames.contains(it.componentName.packageName) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onPackageChanged(packageName: String, user: UserHandle) {
|
||||
val apps = installedApps.value.toMutableList()
|
||||
apps.removeAll { packageName == it.`package` }
|
||||
apps.addAll(getApplications(packageName))
|
||||
installedApps.value = apps
|
||||
scope.launch {
|
||||
mutex.withLock {
|
||||
val apps = installedApps.value.toMutableList()
|
||||
apps.removeAll { packageName == it.componentName.packageName }
|
||||
apps.addAll(getApplications(packageName))
|
||||
installedApps.value = apps
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onPackagesAvailable(
|
||||
@@ -67,23 +82,35 @@ internal class AppRepositoryImpl(
|
||||
user: UserHandle,
|
||||
replacing: Boolean
|
||||
) {
|
||||
val apps = installedApps.value.toMutableList()
|
||||
for (packageName in packageNames) {
|
||||
apps.addAll(getApplications(packageName))
|
||||
scope.launch {
|
||||
mutex.withLock {
|
||||
val apps = installedApps.value.toMutableList()
|
||||
for (packageName in packageNames) {
|
||||
apps.addAll(getApplications(packageName))
|
||||
}
|
||||
installedApps.value = apps
|
||||
}
|
||||
}
|
||||
installedApps.value = apps
|
||||
}
|
||||
|
||||
override fun onPackageAdded(packageName: String, user: UserHandle) {
|
||||
Log.d("MM20", "App installed: $packageName")
|
||||
val apps = installedApps.value.toMutableList()
|
||||
apps.addAll(getApplications(packageName))
|
||||
installedApps.value = apps
|
||||
scope.launch {
|
||||
mutex.withLock {
|
||||
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`) || it.getUser() != user }
|
||||
scope.launch {
|
||||
mutex.withLock {
|
||||
installedApps.value =
|
||||
installedApps.value.filter { packageName != (it.componentName.packageName) || it.user != user }
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onShortcutsChanged(
|
||||
@@ -91,40 +118,55 @@ internal class AppRepositoryImpl(
|
||||
shortcuts: MutableList<ShortcutInfo>,
|
||||
user: UserHandle
|
||||
) {
|
||||
super.onShortcutsChanged(packageName, shortcuts, user)
|
||||
onPackageChanged(packageName, user)
|
||||
}
|
||||
|
||||
override fun onPackagesSuspended(packageNames: Array<out String>?, user: UserHandle?) {
|
||||
super.onPackagesSuspended(packageNames, user)
|
||||
packageNames ?: return
|
||||
suspendedPackages.value = suspendedPackages.value + packageNames
|
||||
scope.launch {
|
||||
mutex.withLock {
|
||||
installedApps.value = installedApps.value.map {
|
||||
if (packageNames.contains(it.componentName.packageName)) {
|
||||
it.copy(isSuspended = true)
|
||||
} else {
|
||||
it
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onPackagesUnsuspended(
|
||||
packageNames: Array<out String>?,
|
||||
user: UserHandle?
|
||||
) {
|
||||
super.onPackagesUnsuspended(packageNames, user)
|
||||
packageNames ?: return
|
||||
suspendedPackages.value =
|
||||
suspendedPackages.value.filter { packageNames.contains(it) }
|
||||
scope.launch {
|
||||
mutex.withLock {
|
||||
installedApps.value = installedApps.value.map {
|
||||
if (packageNames.contains(it.componentName.packageName)) {
|
||||
it.copy(isSuspended = false)
|
||||
} else {
|
||||
it
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}, Handler(Looper.getMainLooper()))
|
||||
val apps = profiles.map { p ->
|
||||
try {
|
||||
launcherApps.getActivityList(null, p).mapNotNull { getApplication(it, p) }
|
||||
} catch (e: SecurityException) {
|
||||
emptyList()
|
||||
scope.launch {
|
||||
mutex.withLock {
|
||||
val apps = profiles.map { p ->
|
||||
try {
|
||||
launcherApps.getActivityList(null, p).mapNotNull { getApplication(it, p) }
|
||||
} catch (e: SecurityException) {
|
||||
emptyList()
|
||||
}
|
||||
}.flatten()
|
||||
installedApps.value = apps
|
||||
}
|
||||
}.flatten()
|
||||
installedApps.value = apps
|
||||
}
|
||||
|
||||
|
||||
override fun getSuspendedPackages(): Flow<List<String>> {
|
||||
return suspendedPackages
|
||||
}
|
||||
}
|
||||
|
||||
private fun getApplications(packageName: String): List<LauncherApp> {
|
||||
@@ -151,6 +193,10 @@ internal class AppRepositoryImpl(
|
||||
return LauncherApp(context, launcherActivityInfo)
|
||||
}
|
||||
|
||||
override fun findMany(): Flow<ImmutableList<Application>> {
|
||||
return installedApps.map { it.toImmutableList() }
|
||||
}
|
||||
|
||||
override fun search(query: String): Flow<ImmutableList<LauncherApp>> {
|
||||
return installedApps.map { apps ->
|
||||
withContext(Dispatchers.Default) {
|
||||
@@ -171,10 +217,6 @@ internal class AppRepositoryImpl(
|
||||
}
|
||||
}
|
||||
|
||||
override fun getAllInstalledApps(): Flow<List<LauncherApp>> {
|
||||
return installedApps
|
||||
}
|
||||
|
||||
private fun matches(label: String, query: String): Boolean {
|
||||
val normalizedLabel = label.normalize()
|
||||
val normalizedQuery = query.normalize()
|
||||
|
||||
+4
-4
@@ -1,4 +1,4 @@
|
||||
package de.mm20.launcher2.search.data
|
||||
package de.mm20.launcher2.applications
|
||||
|
||||
import android.content.ComponentName
|
||||
import android.content.Context
|
||||
@@ -15,8 +15,8 @@ class LauncherAppSerializer : SearchableSerializer {
|
||||
override fun serialize(searchable: SavableSearchable): String {
|
||||
searchable as LauncherApp
|
||||
val json = JSONObject()
|
||||
json.put("package", searchable.`package`)
|
||||
json.put("activity", searchable.activity)
|
||||
json.put("package", searchable.componentName.packageName)
|
||||
json.put("activity", searchable.componentName.className)
|
||||
json.put("user", searchable.userSerialNumber)
|
||||
return json.toString()
|
||||
}
|
||||
@@ -26,7 +26,7 @@ class LauncherAppSerializer : SearchableSerializer {
|
||||
}
|
||||
|
||||
class LauncherAppDeserializer(val context: Context) : SearchableDeserializer {
|
||||
override fun deserialize(serialized: String): SavableSearchable? {
|
||||
override suspend fun deserialize(serialized: String): SavableSearchable? {
|
||||
val json = JSONObject(serialized)
|
||||
val launcherApps = context.getSystemService<LauncherApps>()!!
|
||||
val userManager = context.getSystemService<UserManager>()!!
|
||||
+70
-59
@@ -1,9 +1,10 @@
|
||||
package de.mm20.launcher2.search.data
|
||||
package de.mm20.launcher2.applications
|
||||
|
||||
import android.content.ActivityNotFoundException
|
||||
import android.content.ComponentName
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.pm.ActivityInfo
|
||||
import android.content.pm.ApplicationInfo
|
||||
import android.content.pm.LauncherActivityInfo
|
||||
import android.content.pm.LauncherApps
|
||||
@@ -13,43 +14,56 @@ import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.os.Process
|
||||
import android.os.UserHandle
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.content.FileProvider
|
||||
import androidx.core.content.getSystemService
|
||||
import de.mm20.launcher2.applications.R
|
||||
import de.mm20.launcher2.compat.PackageManagerCompat
|
||||
import de.mm20.launcher2.icons.*
|
||||
import de.mm20.launcher2.icons.ColorLayer
|
||||
import de.mm20.launcher2.icons.LauncherIcon
|
||||
import de.mm20.launcher2.icons.StaticIconLayer
|
||||
import de.mm20.launcher2.icons.StaticLauncherIcon
|
||||
import de.mm20.launcher2.icons.TintedIconLayer
|
||||
import de.mm20.launcher2.icons.TransparentLayer
|
||||
import de.mm20.launcher2.ktx.getSerialNumber
|
||||
import de.mm20.launcher2.ktx.isAtLeastApiLevel
|
||||
import de.mm20.launcher2.search.SavableSearchable
|
||||
import de.mm20.launcher2.search.AppProfile
|
||||
import de.mm20.launcher2.search.Application
|
||||
import de.mm20.launcher2.search.SearchableSerializer
|
||||
import de.mm20.launcher2.search.StoreLink
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
data class LauncherApp(
|
||||
val launcherActivityInfo: LauncherActivityInfo,
|
||||
override val label: String,
|
||||
val `package`: String,
|
||||
val activity: String,
|
||||
val flags: Int,
|
||||
val version: String?,
|
||||
internal data class LauncherApp(
|
||||
private val launcherActivityInfo: LauncherActivityInfo,
|
||||
override val versionName: String?,
|
||||
override val isSuspended: Boolean = false,
|
||||
internal val userSerialNumber: Long,
|
||||
override val labelOverride: String? = null,
|
||||
) : SavableSearchable {
|
||||
) : Application {
|
||||
|
||||
constructor(context: Context, launcherActivityInfo: LauncherActivityInfo): this(
|
||||
override val componentName: ComponentName
|
||||
get() = launcherActivityInfo.componentName
|
||||
|
||||
override val label: String = launcherActivityInfo.label.toString()
|
||||
|
||||
|
||||
constructor(context: Context, launcherActivityInfo: LauncherActivityInfo) : this(
|
||||
launcherActivityInfo,
|
||||
label = launcherActivityInfo.label.toString(),
|
||||
`package` = launcherActivityInfo.applicationInfo.packageName,
|
||||
activity = launcherActivityInfo.name,
|
||||
flags = launcherActivityInfo.applicationInfo.flags,
|
||||
version = getPackageVersionName(context, launcherActivityInfo.applicationInfo.packageName),
|
||||
versionName = getPackageVersionName(context, launcherActivityInfo.applicationInfo.packageName),
|
||||
userSerialNumber = launcherActivityInfo.user.getSerialNumber(context)
|
||||
)
|
||||
|
||||
val isMainProfile = launcherActivityInfo.user == Process.myUserHandle()
|
||||
override val user: UserHandle
|
||||
get() = launcherActivityInfo.user
|
||||
|
||||
val canUninstall: Boolean
|
||||
get() = flags and ApplicationInfo.FLAG_SYSTEM == 0 && isMainProfile
|
||||
private val isMainProfile = launcherActivityInfo.user == Process.myUserHandle()
|
||||
|
||||
override val profile: AppProfile
|
||||
get() = if (isMainProfile) AppProfile.Personal else AppProfile.Work
|
||||
|
||||
override val isSystemApp: Boolean = launcherActivityInfo.applicationInfo.flags and ApplicationInfo.FLAG_SYSTEM == 0
|
||||
|
||||
override val canUninstall: Boolean
|
||||
get() = !isSystemApp && isMainProfile
|
||||
|
||||
override val domain: String = Domain
|
||||
override val preferDetailsOverLaunch: Boolean = false
|
||||
@@ -59,22 +73,10 @@ data class LauncherApp(
|
||||
}
|
||||
|
||||
override val key: String
|
||||
get() = if (isMainProfile) "${domain}://$`package`:$activity" else "${domain}://$`package`:$activity:${userSerialNumber}"
|
||||
// For backwards compatibility, user serial number is not included in main profile
|
||||
get() = if (isMainProfile) "${domain}://${componentName.packageName}:${componentName.packageName}"
|
||||
else "${domain}://${componentName.packageName}:${componentName.className}:${userSerialNumber}"
|
||||
|
||||
fun getUser(): UserHandle? {
|
||||
return launcherActivityInfo.user
|
||||
}
|
||||
|
||||
override fun getPlaceholderIcon(context: Context): StaticLauncherIcon {
|
||||
return StaticLauncherIcon(
|
||||
foregroundLayer = TintedIconLayer(
|
||||
icon = ContextCompat.getDrawable(context, R.drawable.ic_file_android)!!,
|
||||
scale = 0.65f,
|
||||
color = 0xff3dda84.toInt(),
|
||||
),
|
||||
backgroundLayer = ColorLayer(0xff3dda84.toInt())
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun loadIcon(
|
||||
context: Context,
|
||||
@@ -132,7 +134,7 @@ data class LauncherApp(
|
||||
}
|
||||
try {
|
||||
launcherApps.startMainActivity(
|
||||
ComponentName(`package`, activity),
|
||||
componentName,
|
||||
launcherActivityInfo.user,
|
||||
null,
|
||||
options
|
||||
@@ -145,11 +147,15 @@ data class LauncherApp(
|
||||
return true
|
||||
}
|
||||
|
||||
fun getStoreDetails(context: Context): StoreLink? {
|
||||
override fun getStoreDetails(context: Context): StoreLink? {
|
||||
val pm = context.packageManager
|
||||
return try {
|
||||
val installSourceInfo = PackageManagerCompat.getInstallSource(pm, `package`)
|
||||
getStoreLinkForInstaller(installSourceInfo.initiatingPackageName, `package`)
|
||||
val installSourceInfo =
|
||||
PackageManagerCompat.getInstallSource(pm, componentName.packageName)
|
||||
getStoreLinkForInstaller(
|
||||
installSourceInfo.initiatingPackageName,
|
||||
componentName.packageName
|
||||
)
|
||||
} catch (e: PackageManager.NameNotFoundException) {
|
||||
null
|
||||
} catch (e: IllegalArgumentException) {
|
||||
@@ -157,37 +163,33 @@ data class LauncherApp(
|
||||
}
|
||||
}
|
||||
|
||||
fun uninstall(context: Context) {
|
||||
override fun uninstall(context: Context) {
|
||||
val intent = Intent(Intent.ACTION_DELETE)
|
||||
intent.data = Uri.parse("package:$`package`")
|
||||
intent.data = Uri.parse("package:${componentName.packageName}")
|
||||
context.startActivity(intent)
|
||||
}
|
||||
|
||||
fun openAppInfo(context: Context) {
|
||||
override fun openAppDetails(context: Context) {
|
||||
val launcherApps = context.getSystemService<LauncherApps>()!!
|
||||
|
||||
launcherApps.startAppDetailsActivity(
|
||||
ComponentName(`package`, activity),
|
||||
getUser(),
|
||||
componentName,
|
||||
user,
|
||||
null,
|
||||
null
|
||||
)
|
||||
}
|
||||
|
||||
suspend fun shareApkFile(context: Context) {
|
||||
override val canShareApk: Boolean = true
|
||||
override suspend fun shareApkFile(context: Context) {
|
||||
val launcherApps = context.getSystemService<LauncherApps>()!!
|
||||
val fileCopy = java.io.File(
|
||||
context.cacheDir,
|
||||
"${`package`}-${version}.apk"
|
||||
"${componentName.packageName}-${versionName}.apk"
|
||||
)
|
||||
withContext(Dispatchers.IO) {
|
||||
try {
|
||||
val user = getUser()
|
||||
val info = if (user != null) {
|
||||
launcherApps.getApplicationInfo(`package`, 0, user)
|
||||
} else {
|
||||
context.packageManager.getApplicationInfo(`package`, 0)
|
||||
}
|
||||
val info = launcherApps.getApplicationInfo(componentName.packageName, 0, user)
|
||||
val file = java.io.File(info.publicSourceDir)
|
||||
|
||||
try {
|
||||
@@ -212,6 +214,13 @@ data class LauncherApp(
|
||||
}
|
||||
}
|
||||
|
||||
override fun getActivityInfo(context: Context): ActivityInfo? {
|
||||
if (isAtLeastApiLevel(31)) {
|
||||
return launcherActivityInfo.activityInfo
|
||||
}
|
||||
return super.getActivityInfo(context)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private fun getStoreLinkForInstaller(
|
||||
installerPackage: String?,
|
||||
@@ -225,18 +234,21 @@ data class LauncherApp(
|
||||
"http://www.amazon.com/gp/mas/dl/android?p=${packageName}"
|
||||
)
|
||||
}
|
||||
|
||||
"com.android.vending" -> {
|
||||
StoreLink(
|
||||
"Google Play Store",
|
||||
"https://play.google.com/store/apps/details?id=${packageName}"
|
||||
)
|
||||
}
|
||||
|
||||
"org.fdroid.fdroid", "com.aurora.adroid" -> {
|
||||
StoreLink(
|
||||
"F-Droid",
|
||||
"https://f-droid.org/packages/${packageName}"
|
||||
)
|
||||
}
|
||||
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
@@ -251,9 +263,8 @@ data class LauncherApp(
|
||||
|
||||
const val Domain = "app"
|
||||
}
|
||||
}
|
||||
|
||||
data class StoreLink(
|
||||
val label: String,
|
||||
val url: String
|
||||
)
|
||||
override fun getSerializer(): SearchableSerializer {
|
||||
return LauncherAppSerializer()
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,14 @@
|
||||
package de.mm20.launcher2.applications
|
||||
|
||||
import de.mm20.launcher2.search.SearchableDeserializer
|
||||
import de.mm20.launcher2.search.Application
|
||||
import de.mm20.launcher2.search.SearchableRepository
|
||||
import org.koin.android.ext.koin.androidContext
|
||||
import org.koin.core.qualifier.named
|
||||
import org.koin.dsl.module
|
||||
|
||||
val applicationsModule = module {
|
||||
single<AppRepository> { AppRepositoryImpl(androidContext()) }
|
||||
factory<SearchableRepository<Application>>(named<Application>()) { AppRepositoryImpl(androidContext()) }
|
||||
factory<AppRepository> { AppRepositoryImpl(androidContext()) }
|
||||
factory<SearchableDeserializer>(named(LauncherApp.Domain)) { LauncherAppDeserializer(androidContext()) }
|
||||
}
|
||||
Reference in New Issue
Block a user