Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 0 additions & 7 deletions app/src/main/baseline-prof.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11784,10 +11784,7 @@ PLcom/anod/appwatcher/database/AppListTable$DefaultImpls;->loadRowIds(Lcom/anod/
PLcom/anod/appwatcher/database/AppListTable$Queries;-><clinit>()V
PLcom/anod/appwatcher/database/AppListTable$Queries;-><init>()V
PLcom/anod/appwatcher/database/AppListTable$Queries;->changes(Lcom/anod/appwatcher/database/AppListTable;)Lkotlinx/coroutines/flow/Flow;
PLcom/anod/appwatcher/database/AppListTable$Queries;->createAppsListQuery(IZLjava/lang/Integer;Ljava/lang/String;Lcom/anod/appwatcher/database/SqlOffset;)Lkotlin/Pair;
PLcom/anod/appwatcher/database/AppListTable$Queries;->createSelection(Ljava/lang/Integer;Ljava/lang/String;Lcom/anod/appwatcher/database/SqlOffset;)Lkotlin/Pair;
PLcom/anod/appwatcher/database/AppListTable$Queries;->createSortOrder(IZ)Ljava/lang/String;
PLcom/anod/appwatcher/database/AppListTable$Queries;->loadAppList(IZLjava/lang/Integer;Ljava/lang/String;Lcom/anod/appwatcher/database/SqlOffset;Lcom/anod/appwatcher/database/AppListTable;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
PLcom/anod/appwatcher/database/AppListTable$loadRowIds$2;-><init>(Lcom/anod/appwatcher/database/AppListTable;Lkotlin/coroutines/Continuation;)V
PLcom/anod/appwatcher/database/AppListTable$loadRowIds$2;->create(Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Lkotlin/coroutines/Continuation;
PLcom/anod/appwatcher/database/AppListTable$loadRowIds$2;->invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
Expand Down Expand Up @@ -11868,10 +11865,6 @@ PLcom/anod/appwatcher/database/DbContentProvider$Companion;-><init>()V
PLcom/anod/appwatcher/database/DbContentProvider$Companion;-><init>(Lkotlin/jvm/internal/DefaultConstructorMarker;)V
PLcom/anod/appwatcher/database/DbContentProvider$dbSchemaManager$2;-><init>(Lcom/anod/appwatcher/database/DbContentProvider;)V
PLcom/anod/appwatcher/database/SchedulesTable_Impl;->getRequiredConverters()Ljava/util/List;
PLcom/anod/appwatcher/database/SqlOffset;-><clinit>()V
PLcom/anod/appwatcher/database/SqlOffset;-><init>(II)V
PLcom/anod/appwatcher/database/SqlOffset;->getLimit()I
PLcom/anod/appwatcher/database/SqlOffset;->getOffset()I
PLcom/anod/appwatcher/database/TagsTable_Impl;->-$$Nest$fget__db(Lcom/anod/appwatcher/database/TagsTable_Impl;)Landroidx/room/RoomDatabase;
PLcom/anod/appwatcher/database/TagsTable_Impl;-><init>(Landroidx/room/RoomDatabase;)V
PLcom/anod/appwatcher/database/TagsTable_Impl;->getRequiredConverters()Ljava/util/List;
Expand Down
81 changes: 46 additions & 35 deletions app/src/main/java/com/anod/appwatcher/database/AppListTable.kt
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,6 @@ import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.withContext

class SqlOffset(val offset: Int, val limit: Int)

@Dao
interface AppListTable {

Expand All @@ -40,7 +38,7 @@ interface AppListTable {
suspend fun load(query: SupportSQLiteQuery): List<AppListItem>

@RawQuery
suspend fun count(query: SupportSQLiteQuery): Int
suspend fun loadRows(query: SupportSQLiteQuery): List<Int>

@Query("SELECT * FROM $TABLE WHERE ${Columns.APP_ID} == :appId")
fun observeApp(appId: String): Flow<App?>
Expand Down Expand Up @@ -185,45 +183,36 @@ interface AppListTable {
titleFilter: String,
table: AppListTable
): Flow<List<AppListItem>> {
val query = createAppsListQuery(sortId, orderByRecentlyUpdated, tagId, titleFilter, null)
val query = createAppsListQuery(sortId, orderByRecentlyUpdated, tagId, titleFilter)
return table.observe(SimpleSQLiteQuery(query.first, query.second))
}

suspend fun loadAppList(
suspend fun loadAppListRows(
sortId: Int,
orderByRecentlyDiscovered: Boolean,
tagId: Int?,
titleFilter: String,
offset: SqlOffset?,
table: AppListTable
): List<AppListItem> {
val query = createAppsListQuery(sortId, orderByRecentlyDiscovered, tagId, titleFilter, offset)
return table.load(SimpleSQLiteQuery(query.first, query.second))
}

suspend fun countAppList(tagId: Int?, titleFilter: String, table: AppListTable): Int {
val query = createAppsListCountQuery(tagId, titleFilter)
return table.count(SimpleSQLiteQuery(query.first, query.second))
): List<Int> {
val query = createAppsListRowsQuery(sortId, orderByRecentlyDiscovered, tagId, titleFilter)
return table.loadRows(SimpleSQLiteQuery(query.first, query.second))
}

private fun createAppsListCountQuery(tagId: Int?, titleFilter: String): Pair<String, Array<String>> {
val selection = createSelection(tagId, titleFilter, null)
val sql =
"SELECT COUNT(DISTINCT $TABLE.${BaseColumns._ID}) " +
"FROM $TABLE " +
"WHERE ${selection.first} "
return Pair(sql, selection.second)
suspend fun loadAppList(rowIds: List<Int>, table: AppListTable): List<AppListItem> {
if (rowIds.isEmpty()) {
return emptyList()
}
val query = createAppsListRowsQuery(rowIds)
return table.load(SimpleSQLiteQuery(query))
}

internal fun createAppsListQuery(
sortId: Int,
orderByRecentlyDiscovered: Boolean,
tagId: Int?,
titleFilter: String,
offset: SqlOffset?
titleFilter: String
): Pair<String, Array<String>> {
val rangeSql = if (offset == null) "" else " LIMIT ? OFFSET ? "
val selection = createSelection(tagId, titleFilter, offset)
val selection = createSelection(tagId, titleFilter)

val sql =
"SELECT $TABLE.*, ${ChangelogTable.TableColumns.DETAILS}, ${ChangelogTable.TableColumns.NO_NEW_DETAILS}, " +
Expand All @@ -232,11 +221,38 @@ interface AppListTable {
"LEFT JOIN ${ChangelogTable.TABLE} ON ${TableColumns.APP_ID} == ${ChangelogTable.TableColumns.APP_ID} " +
"AND ${TableColumns.VERSION_NUMBER} == ${ChangelogTable.TableColumns.VERSION_CODE} " +
"WHERE ${selection.first} " +
"ORDER BY ${createSortOrder(sortId, orderByRecentlyDiscovered)} " +
rangeSql
"ORDER BY ${createSortOrder(sortId, orderByRecentlyDiscovered)} "
return Pair(sql, selection.second)
}

internal fun createAppsListRowsQuery(
sortId: Int,
orderByRecentlyDiscovered: Boolean,
tagId: Int?,
titleFilter: String
): Pair<String, Array<String>> {
val selection = createSelection(tagId, titleFilter)
val sql =
"SELECT $TABLE.${BaseColumns._ID} " +
"FROM $TABLE " +
"WHERE ${selection.first} " +
"ORDER BY ${createSortOrder(sortId, orderByRecentlyDiscovered)} "
return Pair(sql, selection.second)
}

private fun createAppsListRowsQuery(rowIds: List<Int>): String {
val rowIdsSql = rowIds.joinToString(",")
val orderSql = rowIds.mapIndexed { index, rowId -> "WHEN $rowId THEN $index" }.joinToString(" ")
return "SELECT $TABLE.*, ${ChangelogTable.TableColumns.DETAILS}, ${ChangelogTable.TableColumns.NO_NEW_DETAILS}, " +
"CASE WHEN ${Columns.SYNC_TIMESTAMP} > $recentTime THEN 1 ELSE 0 END ${Columns.RECENT_FLAG} " +
"FROM $TABLE " +
"LEFT JOIN ${ChangelogTable.TABLE} ON ${TableColumns.APP_ID} == ${ChangelogTable.TableColumns.APP_ID} " +
"AND ${TableColumns.VERSION_NUMBER} == ${ChangelogTable.TableColumns.VERSION_CODE} " +
"WHERE $TABLE.${BaseColumns._ID} IN ($rowIdsSql) " +
"AND ${Columns.STATUS} != ${App.STATUS_DELETED} " +
"ORDER BY CASE $TABLE.${BaseColumns._ID} $orderSql END"
}

suspend fun insert(app: App, db: AppsDatabase): Long = withContext(Dispatchers.IO) {
// Skip id to apply autoincrement
return@withContext db.runInTransaction(Callable {
Expand All @@ -263,7 +279,7 @@ interface AppListTable {
Columns.STATUS + " DESC"
)
if (orderByRecentlyUpdated) {
filter.add(Columns.RECENT_FLAG + " DESC")
filter.add("CASE WHEN ${Columns.SYNC_TIMESTAMP} > $recentTime THEN 1 ELSE 0 END DESC")
}
when (sortId) {
Preferences.SORT_NAME_DESC -> filter.add(Columns.TITLE + " COLLATE NOCASE DESC")
Expand All @@ -275,9 +291,9 @@ interface AppListTable {
return filter.joinToString(", ")
}

private fun createSelection(tagId: Int?, titleFilter: String, offset: SqlOffset?): Pair<String, Array<String>> {
private fun createSelection(tagId: Int?, titleFilter: String): Pair<String, Array<String>> {
val selc = ArrayList<String>(3)
val args = ArrayList<String>(5)
val args = ArrayList<String>(3)

selc.add(Columns.STATUS + " != ?")
args.add(App.STATUS_DELETED.toString())
Expand Down Expand Up @@ -307,11 +323,6 @@ interface AppListTable {
args.add("%$titleFilter%")
}

if (offset != null) {
args.add(offset.limit.toString())
args.add(offset.offset.toString())
}

return Pair(selc.joinToString(" AND "), args.toTypedArray())
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import androidx.compose.runtime.Immutable
import androidx.paging.PagingState
import com.anod.appwatcher.database.AppListTable
import com.anod.appwatcher.database.AppsDatabase
import com.anod.appwatcher.database.SqlOffset
import com.anod.appwatcher.database.entities.AppListItem
import com.anod.appwatcher.database.entities.packageToApp
import com.anod.appwatcher.installed.InstalledTaskWorker
Expand All @@ -16,6 +15,8 @@ import com.anod.appwatcher.preferences.Preferences
import info.anodsplace.applog.AppLog
import info.anodsplace.framework.content.InstalledApps
import kotlin.math.max
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock

class WatchListPagingSource(
private val config: Config,
Expand All @@ -25,7 +26,15 @@ class WatchListPagingSource(
private val installedApps: InstalledApps,
) : FilterablePagingSource() {
override var filterQuery: String = ""
set(value) {
if (field != value) {
rowIdSnapshot = null
}
field = value
}
private val itemFilter: AppListFilter = createFilter(config.filterId)
private val rowIdSnapshotMutex = Mutex()
private var rowIdSnapshot: List<Int>? = null

@Immutable
data class Config(val filterId: Int, val tagId: Int?, val showRecentlyDiscovered: Boolean, val showOnDevice: Boolean, val showRecentlyInstalled: Boolean,)
Expand Down Expand Up @@ -53,11 +62,15 @@ class WatchListPagingSource(
limit = max(0, limit)
}

val data = AppListTable.Queries.loadAppList(
sortId, config.showRecentlyDiscovered, config.tagId, filterQuery, SqlOffset(offset, limit), database.apps()
)
val rowIds = loadRowIdSnapshot(sortId)
val pageRowIds = if (offset >= rowIds.size) {
emptyList()
} else {
rowIds.subList(offset, minOf(rowIds.size, offset + limit))
}
val data = AppListTable.Queries.loadAppList(pageRowIds, database.apps())
val filtered = data.filter { !itemFilter.filterRecord(it) }
var totalItems = countTotalItems()
var totalItems = countTotalItems(hasMissingSnapshotRows = data.size < pageRowIds.size)

items.addAll(filtered.map {
SectionItem.App(
Expand All @@ -67,7 +80,7 @@ class WatchListPagingSource(
)
})

if (config.showOnDevice && data.size < limit) {
if (config.showOnDevice && pageRowIds.size < limit) {
items.addAll(loadOnDeviceItems(sortId))
}

Expand All @@ -80,7 +93,7 @@ class WatchListPagingSource(
key = params.key,
offset = offset,
loadSize = params.loadSize,
loadedDataSize = data.size,
loadedDataSize = pageRowIds.size,
limit = limit
)
val itemsBefore = if (totalItems == LoadResult.Page.COUNT_UNDEFINED) {
Expand All @@ -104,12 +117,34 @@ class WatchListPagingSource(
return page
}

private suspend fun countTotalItems(): Int {
if (config.filterId != Filters.ALL || config.showOnDevice) {
private fun countTotalItems(hasMissingSnapshotRows: Boolean): Int {
if (config.filterId != Filters.ALL || config.showOnDevice || hasMissingSnapshotRows) {
return LoadResult.Page.COUNT_UNDEFINED
}
val appsCount = AppListTable.Queries.countAppList(config.tagId, filterQuery, database.apps())
return appsCount + if (config.showRecentlyInstalled) 1 else 0
return (rowIdSnapshot?.size ?: 0) + if (config.showRecentlyInstalled) 1 else 0
}

private suspend fun loadRowIdSnapshot(sortId: Int): List<Int> {
val existingSnapshot = rowIdSnapshot
if (existingSnapshot != null) {
return existingSnapshot
}
return rowIdSnapshotMutex.withLock {
val lockedSnapshot = rowIdSnapshot
if (lockedSnapshot != null) {
lockedSnapshot
} else {
AppListTable.Queries.loadAppListRows(
sortId,
config.showRecentlyDiscovered,
config.tagId,
filterQuery,
database.apps()
).also {
rowIdSnapshot = it
}
}
}
}

private suspend fun loadOnDeviceItems(sortId: Int): List<SectionItem.OnDevice> {
Expand Down
12 changes: 0 additions & 12 deletions app/src/release/generated/baselineProfiles/baseline-prof.txt
Original file line number Diff line number Diff line change
Expand Up @@ -25309,12 +25309,7 @@ Lcom/anod/appwatcher/database/AppListTable$Queries;
SPLcom/anod/appwatcher/database/AppListTable$Queries;-><clinit>()V
SPLcom/anod/appwatcher/database/AppListTable$Queries;-><init>()V
SPLcom/anod/appwatcher/database/AppListTable$Queries;->changes(Lcom/anod/appwatcher/database/AppListTable;)Lkotlinx/coroutines/flow/Flow;
SPLcom/anod/appwatcher/database/AppListTable$Queries;->countAppList(Ljava/lang/Integer;Ljava/lang/String;Lcom/anod/appwatcher/database/AppListTable;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
SPLcom/anod/appwatcher/database/AppListTable$Queries;->createAppsListCountQuery(Ljava/lang/Integer;Ljava/lang/String;)Lkotlin/Pair;
SPLcom/anod/appwatcher/database/AppListTable$Queries;->createAppsListQuery$app(IZLjava/lang/Integer;Ljava/lang/String;Lcom/anod/appwatcher/database/SqlOffset;)Lkotlin/Pair;
SPLcom/anod/appwatcher/database/AppListTable$Queries;->createSelection(Ljava/lang/Integer;Ljava/lang/String;Lcom/anod/appwatcher/database/SqlOffset;)Lkotlin/Pair;
SPLcom/anod/appwatcher/database/AppListTable$Queries;->createSortOrder(IZ)Ljava/lang/String;
SPLcom/anod/appwatcher/database/AppListTable$Queries;->loadAppList(IZLjava/lang/Integer;Ljava/lang/String;Lcom/anod/appwatcher/database/SqlOffset;Lcom/anod/appwatcher/database/AppListTable;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
Lcom/anod/appwatcher/database/AppListTable$loadRowIds$2;
SPLcom/anod/appwatcher/database/AppListTable$loadRowIds$2;-><init>(Lcom/anod/appwatcher/database/AppListTable;Lkotlin/coroutines/Continuation;)V
SPLcom/anod/appwatcher/database/AppListTable$loadRowIds$2;->create(Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Lkotlin/coroutines/Continuation;
Expand All @@ -25326,8 +25321,6 @@ SPLcom/anod/appwatcher/database/AppListTable_Impl;-><clinit>()V
SPLcom/anod/appwatcher/database/AppListTable_Impl;-><init>(Landroidx/room/RoomDatabase;)V
SPLcom/anod/appwatcher/database/AppListTable_Impl;->_loadRowIds$lambda$0(Ljava/lang/String;Ljava/util/List;Landroidx/sqlite/SQLiteConnection;)Ljava/util/List;
SPLcom/anod/appwatcher/database/AppListTable_Impl;->_loadRowIds(Ljava/util/List;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
SPLcom/anod/appwatcher/database/AppListTable_Impl;->count$lambda$1(Ljava/lang/String;Landroidx/room/RoomRawQuery;Landroidx/sqlite/SQLiteConnection;)I
SPLcom/anod/appwatcher/database/AppListTable_Impl;->count(Landroidx/sqlite/db/SupportSQLiteQuery;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
SPLcom/anod/appwatcher/database/AppListTable_Impl;->load$lambda$0(Ljava/lang/String;Landroidx/room/RoomRawQuery;Landroidx/sqlite/SQLiteConnection;)Ljava/util/List;
SPLcom/anod/appwatcher/database/AppListTable_Impl;->load(Landroidx/sqlite/db/SupportSQLiteQuery;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
SPLcom/anod/appwatcher/database/AppListTable_Impl;->loadRowIds(Ljava/util/List;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
Expand Down Expand Up @@ -25468,11 +25461,6 @@ Lcom/anod/appwatcher/database/SchedulesTable_Impl$Companion;
SPLcom/anod/appwatcher/database/SchedulesTable_Impl$Companion;-><init>()V
SPLcom/anod/appwatcher/database/SchedulesTable_Impl$Companion;-><init>(Lkotlin/jvm/internal/DefaultConstructorMarker;)V
SPLcom/anod/appwatcher/database/SchedulesTable_Impl$Companion;->getRequiredConverters()Ljava/util/List;
Lcom/anod/appwatcher/database/SqlOffset;
SPLcom/anod/appwatcher/database/SqlOffset;-><clinit>()V
SPLcom/anod/appwatcher/database/SqlOffset;-><init>(II)V
SPLcom/anod/appwatcher/database/SqlOffset;->getLimit()I
SPLcom/anod/appwatcher/database/SqlOffset;->getOffset()I
Lcom/anod/appwatcher/database/TagsTable;
Lcom/anod/appwatcher/database/TagsTable_Impl;
SPLcom/anod/appwatcher/database/TagsTable_Impl;-><clinit>()V
Expand Down
Loading
Loading