[db] Add special queries for AppListItems

This commit is contained in:
Torsten Grote
2022-04-01 17:09:24 -03:00
committed by Michael Pöhn
parent 97567a2057
commit 3cb7538fc8
4 changed files with 120 additions and 5 deletions

View File

@@ -5,7 +5,10 @@ import androidx.room.DatabaseView
import androidx.room.Embedded
import androidx.room.Entity
import androidx.room.ForeignKey
import androidx.room.Ignore
import androidx.room.Relation
import org.fdroid.database.Converters.fromStringToLocalizedTextV2
import org.fdroid.database.Converters.fromStringToMapOfLocalizedTextV2
import org.fdroid.index.v2.Author
import org.fdroid.index.v2.Donation
import org.fdroid.index.v2.FileV2
@@ -126,6 +129,48 @@ public data class AppOverviewItem(
localizedIcon?.toLocalizedFileV2().getBestLocale(localeList)?.name
}
public data class AppListItem @JvmOverloads constructor(
public val repoId: Long,
public val packageId: String,
internal val name: String?,
internal val summary: String?,
internal val antiFeatures: String?,
@Relation(
parentColumn = "packageId",
entityColumn = "packageId",
)
internal val localizedIcon: List<LocalizedIcon>?,
/**
* If true, this this app has at least one version that is compatible with this device.
*/
@Ignore // TODO actually get this from the DB (probably needs post-processing).
public val isCompatible: Boolean = true,
/**
* The name of the installed version, null if this app is not installed.
*/
@Ignore
public val installedVersionName: String? = null,
@Ignore
public val installedVersionCode: Long? = null,
) {
public fun getName(localeList: LocaleListCompat): String? {
// queries for this class return a larger number, so we convert on demand
return fromStringToLocalizedTextV2(name).getBestLocale(localeList)
}
public fun getSummary(localeList: LocaleListCompat): String? {
// queries for this class return a larger number, so we convert on demand
return fromStringToLocalizedTextV2(summary).getBestLocale(localeList)
}
public fun getAntiFeatureNames(): List<String> {
return fromStringToMapOfLocalizedTextV2(antiFeatures)?.map { it.key } ?: emptyList()
}
public fun getIcon(localeList: LocaleListCompat) =
localizedIcon?.toLocalizedFileV2().getBestLocale(localeList)?.name
}
public data class UpdatableApp(
public val packageId: String,
public val installedVersionCode: Long,

View File

@@ -1,5 +1,7 @@
package org.fdroid.database
import android.content.pm.PackageInfo
import android.content.pm.PackageManager
import androidx.annotation.VisibleForTesting
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
@@ -11,6 +13,7 @@ import androidx.room.Dao
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query
import androidx.room.RoomWarnings.CURSOR_MISMATCH
import androidx.room.Transaction
import org.fdroid.database.FDroidDatabaseHolder.dispatcher
import org.fdroid.index.v2.LocalizedFileListV2
@@ -29,6 +32,14 @@ public interface AppDao {
fun getApp(repoId: Long, packageId: String): App?
fun getAppOverviewItems(limit: Int = 200): LiveData<List<AppOverviewItem>>
fun getAppOverviewItems(category: String, limit: Int = 50): LiveData<List<AppOverviewItem>>
fun getAppListItems(packageManager: PackageManager): LiveData<List<AppListItem>>
fun getAppListItems(
packageManager: PackageManager,
category: String,
): LiveData<List<AppListItem>>
fun getInstalledAppListItems(packageManager: PackageManager): LiveData<List<AppListItem>>
fun getNumberOfAppsInCategory(category: String): Int
}
@@ -197,8 +208,69 @@ internal interface AppDaoInt : AppDao {
LIMIT :limit""")
override fun getAppOverviewItems(category: String, limit: Int): LiveData<List<AppOverviewItem>>
// FIXME don't over report the same app twice (e.g. in several repos)
@Query("""SELECT COUNT(*) FROM AppMetadata
override fun getAppListItems(packageManager: PackageManager): LiveData<List<AppListItem>> {
return getAppListItems().map(packageManager)
}
private fun LiveData<List<AppListItem>>.map(
packageManager: PackageManager,
installedPackages: Map<String, PackageInfo> = packageManager.getInstalledPackages(0)
.associateBy { packageInfo -> packageInfo.packageName },
) = map { items ->
items.map { item ->
val packageInfo = installedPackages[item.packageId]
if (packageInfo == null) item else item.copy(
installedVersionName = packageInfo.versionName,
installedVersionCode = packageInfo.getVersionCode(),
)
}
}
@Transaction
@Query("""SELECT repoId, packageId, app.name, summary, version.antiFeatures
FROM AppMetadata AS app
JOIN Version AS version USING (repoId, packageId)
JOIN RepositoryPreferences AS pref USING (repoId)
WHERE pref.enabled = 1
GROUP BY packageId HAVING MAX(pref.weight) AND MAX(version.manifest_versionCode)
ORDER BY app.lastUpdated DESC""")
fun getAppListItems(): LiveData<List<AppListItem>>
override fun getAppListItems(
packageManager: PackageManager,
category: String,
): LiveData<List<AppListItem>> {
return getAppListItems(category).map(packageManager)
}
// TODO maybe it makes sense to split categories into their own table for this?
@Transaction
@Query("""SELECT repoId, packageId, app.name, summary, version.antiFeatures
FROM AppMetadata AS app
JOIN Version AS version USING (repoId, packageId)
JOIN RepositoryPreferences AS pref USING (repoId)
WHERE pref.enabled = 1 AND categories LIKE '%' || :category || '%'
GROUP BY packageId HAVING MAX(pref.weight) AND MAX(version.manifest_versionCode)
ORDER BY app.lastUpdated DESC""")
fun getAppListItems(category: String): LiveData<List<AppListItem>>
@Transaction
@SuppressWarnings(CURSOR_MISMATCH) // no anti-features needed here
@Query("""SELECT repoId, packageId, app.name, summary
FROM AppMetadata AS app
JOIN RepositoryPreferences AS pref USING (repoId)
WHERE pref.enabled = 1 AND packageId IN (:packageNames)
GROUP BY packageId HAVING MAX(pref.weight)""")
fun getAppListItems(packageNames: List<String>): LiveData<List<AppListItem>>
override fun getInstalledAppListItems(packageManager: PackageManager): LiveData<List<AppListItem>> {
val installedPackages = packageManager.getInstalledPackages(0)
.associateBy { packageInfo -> packageInfo.packageName }
val packageNames = installedPackages.keys.toList()
return getAppListItems(packageNames).map(packageManager, installedPackages)
}
@Query("""SELECT COUNT(DISTINCT packageId) FROM AppMetadata
JOIN RepositoryPreferences AS pref USING (repoId)
WHERE pref.enabled = 1 AND categories LIKE '%' || :category || '%'""")
override fun getNumberOfAppsInCategory(category: String): Int

View File

@@ -6,7 +6,7 @@ import kotlinx.serialization.builtins.serializer
import org.fdroid.index.IndexParser.json
import org.fdroid.index.v2.LocalizedTextV2
internal class Converters {
internal object Converters {
private val localizedTextV2Serializer = MapSerializer(String.serializer(), String.serializer())
private val mapOfLocalizedTextV2Serializer =

View File

@@ -48,8 +48,6 @@ public interface RepositoryDao {
fun updateUserMirrors(repoId: Long, mirrors: List<String>)
fun updateUsernameAndPassword(repoId: Long, username: String?, password: String?)
fun updateDisabledMirrors(repoId: Long, disabledMirrors: List<String>)
// FIXME: We probably want unique categories here flattened by repo weight
fun getLiveCategories(): LiveData<List<Category>>
}