Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Library Bugs and Rugs #145

Merged
merged 8 commits into from
Feb 13, 2025
Merged
4 changes: 2 additions & 2 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,8 @@ android {
minSdk = 29
targetSdk = 34

versionCode = 3
versionName = "1.2.0"
versionCode = 4
versionName = "1.3.0"

buildConfigField("boolean", "GOLD", "false")
val iconValue = "@mipmap/ic_launcher"
Expand Down
11 changes: 11 additions & 0 deletions app/src/main/java/com/OxGames/Pluvia/PrefManager.kt
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import androidx.datastore.preferences.core.stringPreferencesKey
import androidx.datastore.preferences.preferencesDataStore
import com.OxGames.Pluvia.enums.AppTheme
import com.OxGames.Pluvia.service.SteamService
import com.OxGames.Pluvia.ui.enums.AppFilter
import com.OxGames.Pluvia.ui.enums.HomeDestination
import com.OxGames.Pluvia.ui.enums.Orientation
import com.materialkolor.PaletteStyle
Expand Down Expand Up @@ -356,6 +357,16 @@ object PrefManager {
/**
* Get or Set the last known Persona State. See [EPersonaState]
*/
private val LIBRARY_FILTER = intPreferencesKey("library_filter")
var libraryFilter: EnumSet<AppFilter>
get() {
val value = getPref(LIBRARY_FILTER, AppFilter.toFlags(EnumSet.of(AppFilter.GAME)))
return AppFilter.fromFlags(value)
}
set(value) {
setPref(LIBRARY_FILTER, AppFilter.toFlags(value))
}

private val PERSONA_STATE = intPreferencesKey("persona_state")
var personaState: EPersonaState
get() {
Expand Down
7 changes: 5 additions & 2 deletions app/src/main/java/com/OxGames/Pluvia/db/dao/SteamAppDao.kt
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,11 @@ interface SteamAppDao {
invalidPkgId: Int = INVALID_PKG_ID,
): Flow<List<SteamApp>>

@Query("SELECT * FROM steam_app WHERE received_pics = 0")
fun getAllAppsWithoutPICS(): Flow<List<SteamApp>>
@Query("SELECT * FROM steam_app WHERE received_pics = 0 AND package_id != :invalidPkgId AND owner_account_id = :ownerId")
fun getAllOwnedAppsWithoutPICS(
ownerId: Int,
invalidPkgId: Int = INVALID_PKG_ID,
): Flow<List<SteamApp>>

@Query("SELECT * FROM steam_app WHERE id = :appId")
fun findApp(appId: Int): Flow<SteamApp?>
Expand Down
2 changes: 1 addition & 1 deletion app/src/main/java/com/OxGames/Pluvia/enums/AppType.kt
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ enum class AppType(val code: Int) {
}

fun toFlags(value: EnumSet<AppType>): Int {
return value.map { it.code }.reduce { first, second -> first or second }
return value.map { it.code }.reduceOrNull { first, second -> first or second } ?: invalid.code
}

fun fromCode(code: Int): AppType {
Expand Down
2 changes: 1 addition & 1 deletion app/src/main/java/com/OxGames/Pluvia/enums/OS.kt
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ enum class OS(val code: Int) {
}

fun code(value: EnumSet<OS>): Int {
return value.map { it.code }.reduce { first, second -> first or second }
return value.map { it.code }.reduceOrNull { first, second -> first or second } ?: none.code
}
}
}
35 changes: 26 additions & 9 deletions app/src/main/java/com/OxGames/Pluvia/service/SteamService.kt
Original file line number Diff line number Diff line change
Expand Up @@ -1399,12 +1399,22 @@ class SteamService : Service(), IChallengeUrlChanged {
// Timber.d("Adding ${appToAdd?.name} with appId of ${appToAdd?.id} and pkgId of ${appToAdd?.packageId}")

val appIds = picsChangesCallback.appChanges.values
.filter { it.changeNumber != appDao.findApp(it.id).first()?.lastChangeNumber }
.filter { changeData ->
// only queue PICS requests for apps existing in the db that have changed
appDao.findApp(changeData.id).first()?.let {
changeData.changeNumber != it.lastChangeNumber
} == true
}
.map { AppRequest(it.id) }.toTypedArray()
queueAppPICSRequests(*appIds)

val pkgsWithChanges = picsChangesCallback.packageChanges.values
.filter { it.changeNumber != licenseDao.findLicense(it.id).first()?.lastChangeNumber }
.filter { changeData ->
// only queue PICS requests for pkgs existing in the db that have changed
licenseDao.findLicense(changeData.id).first()?.let {
changeData.changeNumber != it.lastChangeNumber
} == true
}
val pkgsForAccessTokens = pkgsWithChanges.filter { it.isNeedsToken }.map { it.id }
val accessTokens = _steamApps?.picsGetAccessTokens(
emptyList(),
Expand Down Expand Up @@ -1624,7 +1634,10 @@ class SteamService : Service(), IChallengeUrlChanged {
if (callback.result == EResult.OK) {
dbScope.launch {
// check first if any apps already exist in the db that need PICS
val apps = appDao.getAllAppsWithoutPICS().firstOrNull()?.map { AppRequest(it.id) }?.toTypedArray()
val apps = appDao.getAllOwnedAppsWithoutPICS(userSteamId!!.accountID.toInt())
.firstOrNull()
?.map { AppRequest(it.id) }
?.toTypedArray()
Timber.d("${apps?.size ?: 0} app(s) need PICS")
if (apps?.isNotEmpty() == true) {
queueAppPICSRequests(*apps)
Expand Down Expand Up @@ -1698,13 +1711,17 @@ class SteamService : Service(), IChallengeUrlChanged {
licenseDao.updateApps(pkg.id, appIds)
licenseDao.updateDepots(pkg.id, depotIds)

val steamAppsToAdd = appIds.map { appId ->
appDao.findApp(appId).first()?.copy(packageId = pkg.id)
?: SteamApp(id = appId, packageId = pkg.id)
}.toTypedArray()
appDao.insert(*steamAppsToAdd)
val license = licenseDao.findLicense(pkg.id).first()
// only add the apps belonging to the license if the user owns it
if (license?.ownerAccountId == userSteamId?.accountID?.toInt()) {
val steamAppsToAdd = appIds.map { appId ->
appDao.findApp(appId).first()?.copy(packageId = pkg.id)
?: SteamApp(id = appId, packageId = pkg.id)
}.toTypedArray()

queueAppPICSRequests(*appIds.map { AppRequest(it) }.toTypedArray())
appDao.insert(*steamAppsToAdd)
queueAppPICSRequests(*appIds.map { AppRequest(it) }.toTypedArray())
}
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.OxGames.Pluvia.ui.component

import androidx.compose.foundation.layout.padding
import androidx.compose.material3.FilterChip
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp

@Composable
fun FlowFilterChip(
selected: Boolean,
onClick: () -> Unit,
label: @Composable (() -> Unit),
modifier: Modifier = Modifier,
leadingIcon: @Composable (() -> Unit),
) {
FilterChip(
modifier = Modifier.padding(end = 8.dp).then(modifier),
onClick = onClick,
label = label,
selected = selected,
leadingIcon = leadingIcon,
)
}
6 changes: 4 additions & 2 deletions app/src/main/java/com/OxGames/Pluvia/ui/data/LibraryState.kt
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
package com.OxGames.Pluvia.ui.data

import com.OxGames.Pluvia.PrefManager
import com.OxGames.Pluvia.data.LibraryItem
import com.OxGames.Pluvia.ui.enums.FabFilter
import com.OxGames.Pluvia.ui.enums.AppFilter
import java.util.EnumSet

data class LibraryState(
val appInfoSortType: EnumSet<FabFilter> = EnumSet.of(FabFilter.ALPHABETIC, FabFilter.GAME),
val appInfoSortType: EnumSet<AppFilter> = PrefManager.libraryFilter,
val appInfoList: List<LibraryItem> = emptyList(),
val modalBottomSheet: Boolean = false,

val isSearching: Boolean = false,
val searchQuery: String = "",
Expand Down
82 changes: 82 additions & 0 deletions app/src/main/java/com/OxGames/Pluvia/ui/enums/AppFilter.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package com.OxGames.Pluvia.ui.enums

import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.AvTimer
import androidx.compose.material.icons.filled.Build
import androidx.compose.material.icons.filled.Computer
import androidx.compose.material.icons.filled.InstallMobile
import androidx.compose.material.icons.filled.VideogameAsset
import androidx.compose.ui.graphics.vector.ImageVector
import com.OxGames.Pluvia.enums.AppType
import java.util.EnumSet

enum class AppFilter(
val code: Int,
val displayText: String,
val icon: ImageVector,
) {
INSTALLED(
code = 0x01,
displayText = "Installed",
icon = Icons.Default.InstallMobile,
),
GAME(
code = 0x02,
displayText = "Game",
icon = Icons.Default.VideogameAsset,
),
APPLICATION(
code = 0x04,
displayText = "Application",
icon = Icons.Default.Computer,
),
TOOL(
code = 0x08,
displayText = "Tool",
icon = Icons.Default.Build,
),
DEMO(
code = 0x10,
displayText = "Demo",
icon = Icons.Default.AvTimer,
),
// ALPHABETIC(
// code = 0x20,
// displayText = "Alphabetic",
// icon = Icons.Default.SortByAlpha,
// ),
;

companion object {
fun getAppType(appFilter: EnumSet<AppFilter>): EnumSet<AppType> {
val output: EnumSet<AppType> = EnumSet.noneOf(AppType::class.java)
if (appFilter.contains(GAME)) {
output.add(AppType.game)
}
if (appFilter.contains(APPLICATION)) {
output.add(AppType.application)
}
if (appFilter.contains(TOOL)) {
output.add(AppType.tool)
}
if (appFilter.contains(DEMO)) {
output.add(AppType.demo)
}
return output
}

fun fromFlags(flags: Int): EnumSet<AppFilter> {
val result = EnumSet.noneOf(AppFilter::class.java)
AppFilter.entries.forEach { appFilter ->
if (flags and appFilter.code == appFilter.code) {
result.add(appFilter)
}
}
return result
}

fun toFlags(value: EnumSet<AppFilter>): Int {
return value.map { it.code }.reduceOrNull { first, second -> first or second } ?: 0
}
}
}
33 changes: 0 additions & 33 deletions app/src/main/java/com/OxGames/Pluvia/ui/enums/FabFilter.kt

This file was deleted.

20 changes: 15 additions & 5 deletions app/src/main/java/com/OxGames/Pluvia/ui/model/LibraryViewModel.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,15 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.OxGames.Pluvia.PrefManager
import com.OxGames.Pluvia.data.LibraryItem
import com.OxGames.Pluvia.data.SteamApp
import com.OxGames.Pluvia.db.dao.SteamAppDao
import com.OxGames.Pluvia.service.SteamService
import com.OxGames.Pluvia.ui.data.LibraryState
import com.OxGames.Pluvia.ui.enums.FabFilter
import com.OxGames.Pluvia.ui.enums.AppFilter
import dagger.hilt.android.lifecycle.HiltViewModel
import java.util.EnumSet
import javax.inject.Inject
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
Expand Down Expand Up @@ -53,6 +55,10 @@ class LibraryViewModel @Inject constructor(
}
}

fun onModalBottomSheet(value: Boolean) {
_state.update { it.copy(modalBottomSheet = value) }
}

fun onIsSearching(value: Boolean) {
_state.update { it.copy(isSearching = value) }
if (!value) {
Expand All @@ -66,14 +72,18 @@ class LibraryViewModel @Inject constructor(
}

// TODO: include other sort types
fun onFabFilter(value: FabFilter) {
fun onFilterChanged(value: AppFilter) {
_state.update { currentState ->
val updatedFilter = currentState.appInfoSortType
val updatedFilter = EnumSet.copyOf(currentState.appInfoSortType)

if (updatedFilter.contains(value)) {
updatedFilter.remove(value)
} else {
updatedFilter.add(value)
}

PrefManager.libraryFilter = updatedFilter

currentState.copy(appInfoSortType = updatedFilter)
}

Expand All @@ -84,7 +94,7 @@ class LibraryViewModel @Inject constructor(
Timber.d("onFilterApps")
viewModelScope.launch {
val currentState = _state.value
val currentFilter = FabFilter.getAppType(currentState.appInfoSortType)
val currentFilter = AppFilter.getAppType(currentState.appInfoSortType)

val filteredList = appList
.asSequence()
Expand All @@ -99,7 +109,7 @@ class LibraryViewModel @Inject constructor(
}
}
.filter { item ->
if (currentState.appInfoSortType.contains(FabFilter.INSTALLED)) {
if (currentState.appInfoSortType.contains(AppFilter.INSTALLED)) {
SteamService.isAppInstalled(item.id)
} else {
true
Expand Down
Loading