Update: Merge AppSheet's Installed into main Installed

This commit is contained in:
machiav3lli 2022-05-13 11:00:33 +02:00
parent 099b1bdf28
commit 1477fba932
7 changed files with 85 additions and 58 deletions

View File

@ -68,9 +68,29 @@ class MainApplication : Application(), ImageLoaderFactory {
addDataScheme("package") addDataScheme("package")
} }
) )
val launcherActivitiesMap =
packageManager
.queryIntentActivities(
Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_LAUNCHER),
0
)
.mapNotNull { resolveInfo -> resolveInfo.activityInfo }
.groupBy { it.packageName }
.mapNotNull { (packageName, activityInfos) ->
val aiNameLabels = activityInfos.mapNotNull {
val label = try {
it.loadLabel(packageManager).toString()
} catch (e: Exception) {
e.printStackTrace()
null
}
label?.let { label -> Pair(it.name, label) }
}
Pair(packageName, aiNameLabels)
}.toMap()
val installedItems = packageManager val installedItems = packageManager
.getInstalledPackages(Android.PackageManager.signaturesFlag) .getInstalledPackages(Android.PackageManager.signaturesFlag)
.map { it.toInstalledItem() } .map { it.toInstalledItem(launcherActivitiesMap[it.packageName].orEmpty()) }
CoroutineScope(Dispatchers.Default).launch { CoroutineScope(Dispatchers.Default).launch {
db.installedDao.emptyTable() db.installedDao.emptyTable()
db.installedDao.put(*installedItems.toTypedArray()) db.installedDao.put(*installedItems.toTypedArray())

View File

@ -21,6 +21,23 @@ object Converters {
@JvmStatic @JvmStatic
fun toByteArray(list: List<String>): ByteArray = list.toString().toByteArray() fun toByteArray(list: List<String>): ByteArray = list.toString().toByteArray()
@TypeConverter
@JvmStatic
fun toPairStringList(byteArray: ByteArray): List<Pair<String, String>> {
val string = String(byteArray)
return if (string == "") emptyList()
else string.removeSurrounding("[", "]").split(",").filter(String::isNotEmpty).map {
val pairs = it.split("|")
Pair(pairs[0], pairs[1])
}
}
@JvmName("pairStringListToByteArray")
@TypeConverter
@JvmStatic
fun toByteArray(list: List<Pair<String, String>>): ByteArray =
list.map { it.toList().joinToString("|") }.toString().toByteArray()
@TypeConverter @TypeConverter
@JvmStatic @JvmStatic
fun toAuthor(byteArray: ByteArray) = Author.fromJson(String(byteArray)) fun toAuthor(byteArray: ByteArray) = Author.fromJson(String(byteArray))

View File

@ -21,7 +21,7 @@ import kotlinx.coroutines.launch
CategoryTemp::class, CategoryTemp::class,
Installed::class, Installed::class,
Ignored::class Ignored::class
], version = 6 ], version = 7
) )
@TypeConverters(Converters::class) @TypeConverters(Converters::class)
abstract class DatabaseX : RoomDatabase() { abstract class DatabaseX : RoomDatabase() {

View File

@ -10,5 +10,7 @@ data class Installed(
var packageName: String = "", var packageName: String = "",
var version: String = "", var version: String = "",
var versionCode: Long = 0L, var versionCode: Long = 0L,
var signature: String = "" var signature: String = "",
var isSystem: Boolean = false,
val launcherActivities: List<Pair<String, String>> = emptyList()
) )

View File

@ -6,6 +6,7 @@ import android.content.Intent
import com.looker.droidify.database.DatabaseX import com.looker.droidify.database.DatabaseX
import com.looker.droidify.utility.Utils.toInstalledItem import com.looker.droidify.utility.Utils.toInstalledItem
import com.looker.droidify.utility.extension.android.Android import com.looker.droidify.utility.extension.android.Android
import com.looker.droidify.utility.getLaunchActivities
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@ -29,8 +30,10 @@ class PackageChangedReceiver : BroadcastReceiver() {
} catch (e: Exception) { } catch (e: Exception) {
null null
} }
val launcherActivities = context.packageManager.getLaunchActivities(packageName)
GlobalScope.launch(Dispatchers.IO) { GlobalScope.launch(Dispatchers.IO) {
if (packageInfo != null) db.installedDao.insertReplace(packageInfo.toInstalledItem()) if (packageInfo != null) db.installedDao
.insertReplace(packageInfo.toInstalledItem(launcherActivities))
else db.installedDao.delete(packageName) else db.installedDao.delete(packageName)
} }
} }

View File

@ -1,9 +1,7 @@
package com.looker.droidify.ui.fragments package com.looker.droidify.ui.fragments
import android.content.ActivityNotFoundException import android.content.ActivityNotFoundException
import android.content.ComponentName
import android.content.Intent import android.content.Intent
import android.content.pm.ApplicationInfo
import android.net.Uri import android.net.Uri
import android.os.Bundle import android.os.Bundle
import android.provider.Settings import android.provider.Settings
@ -18,6 +16,7 @@ import androidx.recyclerview.widget.LinearLayoutManager
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.looker.droidify.R import com.looker.droidify.R
import com.looker.droidify.content.ProductPreferences import com.looker.droidify.content.ProductPreferences
import com.looker.droidify.database.entity.Installed
import com.looker.droidify.database.entity.Product import com.looker.droidify.database.entity.Product
import com.looker.droidify.database.entity.Release import com.looker.droidify.database.entity.Release
import com.looker.droidify.database.entity.Repository import com.looker.droidify.database.entity.Repository
@ -108,46 +107,7 @@ class AppSheetX() : FullscreenBottomSheetDialogFragment(), AppDetailAdapter.Call
override fun setupLayout() { override fun setupLayout() {
// TODO simplify observing and updating // TODO simplify observing and updating
viewModel.installedItem.observe(viewLifecycleOwner) { viewModel.installedItem.observe(viewLifecycleOwner) {
installed = it?.let { installed = it
val isSystem = try {
((requireContext().packageManager.getApplicationInfo(packageName, 0).flags)
and ApplicationInfo.FLAG_SYSTEM) != 0
} catch (e: Exception) {
false
}
val launcherActivities =
if (packageName != context?.packageName && context != null) {
val packageManager = requireContext().packageManager
packageManager
.queryIntentActivities(
Intent(Intent.ACTION_MAIN).addCategory(
Intent.CATEGORY_LAUNCHER
), 0
)
.asSequence()
.mapNotNull { resolveInfo -> resolveInfo.activityInfo }
.filter { activityInfo -> activityInfo.packageName == packageName }
.mapNotNull { activityInfo ->
val label = try {
activityInfo.loadLabel(packageManager).toString()
} catch (e: Exception) {
e.printStackTrace()
null
}
label?.let { labelName ->
Pair(
activityInfo.name,
labelName
)
}
}
.toList()
} else {
// Don't allow to launch self
emptyList()
}
Installed(it, isSystem, launcherActivities)
}
updateSheet() updateSheet()
} }
@ -175,7 +135,7 @@ class AppSheetX() : FullscreenBottomSheetDialogFragment(), AppDetailAdapter.Call
binding.recyclerView.context, binding.recyclerView.context,
packageName, packageName,
this, this,
installed?.data installed
) )
lifecycleScope.launch { lifecycleScope.launch {
updateButtons() updateButtons()
@ -205,12 +165,12 @@ class AppSheetX() : FullscreenBottomSheetDialogFragment(), AppDetailAdapter.Call
private suspend fun updateButtons(preference: ProductPreference) = private suspend fun updateButtons(preference: ProductPreference) =
withContext(Dispatchers.Default) { withContext(Dispatchers.Default) {
val installed = installed val installed = installed
val product = findSuggestedProduct(productRepos, installed?.data) { it.first }?.first val product = findSuggestedProduct(productRepos, installed) { it.first }?.first
val compatible = product != null && product.selectedReleases.firstOrNull() val compatible = product != null && product.selectedReleases.firstOrNull()
.let { it != null && it.incompatibilities.isEmpty() } .let { it != null && it.incompatibilities.isEmpty() }
val canInstall = product != null && installed == null && compatible val canInstall = product != null && installed == null && compatible
val canUpdate = val canUpdate =
product != null && compatible && product.canUpdate(installed?.data) && product != null && compatible && product.canUpdate(installed) &&
!preference.shouldIgnoreUpdate(product.versionCode) !preference.shouldIgnoreUpdate(product.versionCode)
val canUninstall = product != null && installed != null && !installed.isSystem val canUninstall = product != null && installed != null && !installed.isSystem
val canLaunch = val canLaunch =
@ -303,7 +263,7 @@ class AppSheetX() : FullscreenBottomSheetDialogFragment(), AppDetailAdapter.Call
AppDetailAdapter.Action.INSTALL, AppDetailAdapter.Action.INSTALL,
AppDetailAdapter.Action.UPDATE, AppDetailAdapter.Action.UPDATE,
-> { -> {
val installedItem = installed?.data val installedItem = installed
lifecycleScope.launch { lifecycleScope.launch {
startUpdate( startUpdate(
packageName, packageName,
@ -408,7 +368,7 @@ class AppSheetX() : FullscreenBottomSheetDialogFragment(), AppDetailAdapter.Call
} }
override fun onReleaseClick(release: Release) { override fun onReleaseClick(release: Release) {
val installedItem = installed?.data val installedItem = installed
when { when {
release.incompatibilities.isNotEmpty() -> { release.incompatibilities.isNotEmpty() -> {
MessageDialog( MessageDialog(
@ -494,9 +454,4 @@ class AppSheetX() : FullscreenBottomSheetDialogFragment(), AppDetailAdapter.Call
UNINSTALL(5, AppDetailAdapter.Action.UNINSTALL), UNINSTALL(5, AppDetailAdapter.Action.UNINSTALL),
SHARE(6, AppDetailAdapter.Action.SHARE) SHARE(6, AppDetailAdapter.Action.SHARE)
} }
private class Installed(
val data: com.looker.droidify.database.entity.Installed, val isSystem: Boolean,
val launcherActivities: List<Pair<String, String>>,
)
} }

View File

@ -7,7 +7,9 @@ import android.content.ActivityNotFoundException
import android.content.Context import android.content.Context
import android.content.DialogInterface import android.content.DialogInterface
import android.content.Intent import android.content.Intent
import android.content.pm.ApplicationInfo
import android.content.pm.PackageInfo import android.content.pm.PackageInfo
import android.content.pm.PackageManager
import android.content.pm.Signature import android.content.pm.Signature
import android.content.res.Configuration import android.content.res.Configuration
import android.graphics.drawable.Drawable import android.graphics.drawable.Drawable
@ -37,9 +39,16 @@ import java.security.cert.CertificateEncodingException
import java.util.* import java.util.*
object Utils { object Utils {
fun PackageInfo.toInstalledItem(): Installed { fun PackageInfo.toInstalledItem(launcherActivities: List<Pair<String, String>> = emptyList()): Installed {
val signatureString = singleSignature?.let(Utils::calculateHash).orEmpty() val signatureString = singleSignature?.let(Utils::calculateHash).orEmpty()
return Installed(packageName, versionName.orEmpty(), versionCodeCompat, signatureString) return Installed(
packageName,
versionName.orEmpty(),
versionCodeCompat,
signatureString,
applicationInfo.flags and ApplicationInfo.FLAG_SYSTEM == ApplicationInfo.FLAG_SYSTEM,
launcherActivities
)
} }
fun getDefaultApplicationIcon(context: Context): Drawable = fun getDefaultApplicationIcon(context: Context): Drawable =
@ -224,4 +233,25 @@ fun Context.showBatteryOptimizationDialog() {
Preferences[Preferences.Key.IgnoreIgnoreBatteryOptimization] = true Preferences[Preferences.Key.IgnoreIgnoreBatteryOptimization] = true
} }
.show() .show()
}
fun PackageManager.getLaunchActivities(packageName: String): List<Pair<String, String>> =
queryIntentActivities(Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_LAUNCHER), 0)
.mapNotNull { resolveInfo -> resolveInfo.activityInfo }
.filter { activityInfo -> activityInfo.packageName == packageName }
.mapNotNull { activityInfo ->
val label = try {
activityInfo.loadLabel(this).toString()
} catch (e: Exception) {
e.printStackTrace()
null
}
label?.let { labelName ->
Pair(
activityInfo.name,
labelName
)
}
}
.toList()
} }