From fca260f4852b200a5b12817d986d2250be4c238f Mon Sep 17 00:00:00 2001 From: Oxters Date: Mon, 10 Feb 2025 16:24:33 -0500 Subject: [PATCH 1/8] Rebased --- app/src/main/java/com/OxGames/Pluvia/db/dao/SteamAppDao.kt | 7 +++++-- app/src/main/java/com/OxGames/Pluvia/enums/AppType.kt | 2 +- app/src/main/java/com/OxGames/Pluvia/enums/OS.kt | 2 +- .../main/java/com/OxGames/Pluvia/service/SteamService.kt | 5 ++++- 4 files changed, 11 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/com/OxGames/Pluvia/db/dao/SteamAppDao.kt b/app/src/main/java/com/OxGames/Pluvia/db/dao/SteamAppDao.kt index b95df3e..53c08fa 100644 --- a/app/src/main/java/com/OxGames/Pluvia/db/dao/SteamAppDao.kt +++ b/app/src/main/java/com/OxGames/Pluvia/db/dao/SteamAppDao.kt @@ -31,8 +31,11 @@ interface SteamAppDao { invalidPkgId: Int = INVALID_PKG_ID, ): Flow> - @Query("SELECT * FROM steam_app WHERE received_pics = 0") - fun getAllAppsWithoutPICS(): Flow> + @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> @Query("SELECT * FROM steam_app WHERE id = :appId") fun findApp(appId: Int): Flow diff --git a/app/src/main/java/com/OxGames/Pluvia/enums/AppType.kt b/app/src/main/java/com/OxGames/Pluvia/enums/AppType.kt index c7f0e11..75e5f3f 100644 --- a/app/src/main/java/com/OxGames/Pluvia/enums/AppType.kt +++ b/app/src/main/java/com/OxGames/Pluvia/enums/AppType.kt @@ -66,7 +66,7 @@ enum class AppType(val code: Int) { return result } - fun toFlags(value: EnumSet): Int { + fun code(value: EnumSet): Int { return value.map { it.code }.reduce { first, second -> first or second } } diff --git a/app/src/main/java/com/OxGames/Pluvia/enums/OS.kt b/app/src/main/java/com/OxGames/Pluvia/enums/OS.kt index f9bd4d5..c1cf58d 100644 --- a/app/src/main/java/com/OxGames/Pluvia/enums/OS.kt +++ b/app/src/main/java/com/OxGames/Pluvia/enums/OS.kt @@ -38,7 +38,7 @@ enum class OS(val code: Int) { } fun code(value: EnumSet): 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 } } } diff --git a/app/src/main/java/com/OxGames/Pluvia/service/SteamService.kt b/app/src/main/java/com/OxGames/Pluvia/service/SteamService.kt index 586cf1b..23aeb3b 100644 --- a/app/src/main/java/com/OxGames/Pluvia/service/SteamService.kt +++ b/app/src/main/java/com/OxGames/Pluvia/service/SteamService.kt @@ -1624,7 +1624,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) From 86fa015cb95a24e21478fe3febb1bb58d5d862a2 Mon Sep 17 00:00:00 2001 From: Oxters Date: Mon, 10 Feb 2025 17:05:13 -0500 Subject: [PATCH 2/8] Adjusted PICS change checker to only update apps or pkgs that already exist in db. Adjusted pkg requester to only insert apps to db if it is owned by the user. --- .../OxGames/Pluvia/service/SteamService.kt | 30 ++++++++++++++----- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/com/OxGames/Pluvia/service/SteamService.kt b/app/src/main/java/com/OxGames/Pluvia/service/SteamService.kt index 23aeb3b..ba2b5b7 100644 --- a/app/src/main/java/com/OxGames/Pluvia/service/SteamService.kt +++ b/app/src/main/java/com/OxGames/Pluvia/service/SteamService.kt @@ -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(), @@ -1701,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()) + } } } From 91db4e505a6331e3daf6664f9abf228b6748a3ad Mon Sep 17 00:00:00 2001 From: LossyDragon Date: Thu, 13 Feb 2025 00:31:34 -0600 Subject: [PATCH 3/8] Transform the filter options to a Bottom Sheet Modal. Rename FabFilter to AppFilter. App Fab is now an expanded fab, which hides on search and collapses when scrolling. --- .../OxGames/Pluvia/ui/data/LibraryState.kt | 10 +- .../ui/enums/{FabFilter.kt => AppFilter.kt} | 12 +- .../Pluvia/ui/model/LibraryViewModel.kt | 14 +- .../Pluvia/ui/screen/library/LibraryScreen.kt | 65 ++++++--- .../library/components/LibraryBottomSheet.kt | 127 ++++++++++++++++ .../screen/library/components/LibraryFab.kt | 24 +-- .../screen/library/components/LibraryList.kt | 9 +- .../library/components/LibraryListPane.kt | 138 +++++++++++++----- .../library/components/LibrarySearchBar.kt | 1 - 9 files changed, 306 insertions(+), 94 deletions(-) rename app/src/main/java/com/OxGames/Pluvia/ui/enums/{FabFilter.kt => AppFilter.kt} (68%) create mode 100644 app/src/main/java/com/OxGames/Pluvia/ui/screen/library/components/LibraryBottomSheet.kt diff --git a/app/src/main/java/com/OxGames/Pluvia/ui/data/LibraryState.kt b/app/src/main/java/com/OxGames/Pluvia/ui/data/LibraryState.kt index 6b9ff3d..823df8c 100644 --- a/app/src/main/java/com/OxGames/Pluvia/ui/data/LibraryState.kt +++ b/app/src/main/java/com/OxGames/Pluvia/ui/data/LibraryState.kt @@ -1,12 +1,18 @@ package com.OxGames.Pluvia.ui.data import com.OxGames.Pluvia.data.LibraryItem -import com.OxGames.Pluvia.ui.enums.FabFilter +import com.OxGames.Pluvia.ui.enums.AppFilter import java.util.EnumSet +// TODO: +// 1. Missing games?? Did I break something or...? +// 2. The filter chips do not change their selected color. +// 3. Close button on sheet or not? Tapping outside or swipe down dismisses it. + data class LibraryState( - val appInfoSortType: EnumSet = EnumSet.of(FabFilter.ALPHABETIC, FabFilter.GAME), + val appInfoSortType: EnumSet = EnumSet.of(AppFilter.ALPHABETIC, AppFilter.GAME), // TODO save as pref val appInfoList: List = emptyList(), + val modalBottomSheet: Boolean = false, val isSearching: Boolean = false, val searchQuery: String = "", diff --git a/app/src/main/java/com/OxGames/Pluvia/ui/enums/FabFilter.kt b/app/src/main/java/com/OxGames/Pluvia/ui/enums/AppFilter.kt similarity index 68% rename from app/src/main/java/com/OxGames/Pluvia/ui/enums/FabFilter.kt rename to app/src/main/java/com/OxGames/Pluvia/ui/enums/AppFilter.kt index b572667..5e44ddb 100644 --- a/app/src/main/java/com/OxGames/Pluvia/ui/enums/FabFilter.kt +++ b/app/src/main/java/com/OxGames/Pluvia/ui/enums/AppFilter.kt @@ -3,7 +3,7 @@ package com.OxGames.Pluvia.ui.enums import com.OxGames.Pluvia.enums.AppType import java.util.EnumSet -enum class FabFilter(val code: Int) { +enum class AppFilter(val code: Int) { INSTALLED(0x01), ALPHABETIC(0x02), GAME(0x04), @@ -13,18 +13,18 @@ enum class FabFilter(val code: Int) { ; companion object { - fun getAppType(fabFilter: EnumSet): EnumSet { + fun getAppType(appFilter: EnumSet): EnumSet { val output: EnumSet = EnumSet.noneOf(AppType::class.java) - if (fabFilter.contains(GAME)) { + if (appFilter.contains(GAME)) { output.add(AppType.game) } - if (fabFilter.contains(APPLICATION)) { + if (appFilter.contains(APPLICATION)) { output.add(AppType.application) } - if (fabFilter.contains(TOOL)) { + if (appFilter.contains(TOOL)) { output.add(AppType.tool) } - if (fabFilter.contains(DEMO)) { + if (appFilter.contains(DEMO)) { output.add(AppType.demo) } return output diff --git a/app/src/main/java/com/OxGames/Pluvia/ui/model/LibraryViewModel.kt b/app/src/main/java/com/OxGames/Pluvia/ui/model/LibraryViewModel.kt index ca7f082..e64338a 100644 --- a/app/src/main/java/com/OxGames/Pluvia/ui/model/LibraryViewModel.kt +++ b/app/src/main/java/com/OxGames/Pluvia/ui/model/LibraryViewModel.kt @@ -11,7 +11,7 @@ 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 javax.inject.Inject import kotlinx.coroutines.Dispatchers @@ -53,6 +53,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) { @@ -66,14 +70,16 @@ class LibraryViewModel @Inject constructor( } // TODO: include other sort types - fun onFabFilter(value: FabFilter) { + fun onFilterChanged(value: AppFilter) { _state.update { currentState -> val updatedFilter = currentState.appInfoSortType + if (updatedFilter.contains(value)) { updatedFilter.remove(value) } else { updatedFilter.add(value) } + currentState.copy(appInfoSortType = updatedFilter) } @@ -84,7 +90,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() @@ -99,7 +105,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 diff --git a/app/src/main/java/com/OxGames/Pluvia/ui/screen/library/LibraryScreen.kt b/app/src/main/java/com/OxGames/Pluvia/ui/screen/library/LibraryScreen.kt index ff67744..ade885a 100644 --- a/app/src/main/java/com/OxGames/Pluvia/ui/screen/library/LibraryScreen.kt +++ b/app/src/main/java/com/OxGames/Pluvia/ui/screen/library/LibraryScreen.kt @@ -5,31 +5,35 @@ import androidx.activity.compose.BackHandler import androidx.compose.foundation.layout.displayCutoutPadding import androidx.compose.foundation.lazy.LazyListState import androidx.compose.foundation.lazy.rememberLazyListState +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.SheetState import androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi import androidx.compose.material3.adaptive.layout.AnimatedPane import androidx.compose.material3.adaptive.layout.ListDetailPaneScaffold import androidx.compose.material3.adaptive.layout.ListDetailPaneScaffoldRole import androidx.compose.material3.adaptive.navigation.BackNavigationBehavior import androidx.compose.material3.adaptive.navigation.rememberListDetailPaneScaffoldNavigator +import androidx.compose.material3.rememberModalBottomSheetState import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.tooling.preview.Preview import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.OxGames.Pluvia.data.LibraryItem import com.OxGames.Pluvia.service.SteamService -import com.OxGames.Pluvia.ui.component.fabmenu.state.FloatingActionMenuState -import com.OxGames.Pluvia.ui.component.fabmenu.state.FloatingActionMenuValue -import com.OxGames.Pluvia.ui.component.fabmenu.state.rememberFloatingActionMenuState import com.OxGames.Pluvia.ui.data.LibraryState -import com.OxGames.Pluvia.ui.enums.FabFilter +import com.OxGames.Pluvia.ui.enums.AppFilter import com.OxGames.Pluvia.ui.internal.fakeAppInfo import com.OxGames.Pluvia.ui.model.LibraryViewModel import com.OxGames.Pluvia.ui.screen.library.components.LibraryDetailPane import com.OxGames.Pluvia.ui.screen.library.components.LibraryListPane import com.OxGames.Pluvia.ui.theme.PluviaTheme +@OptIn(ExperimentalMaterial3Api::class) @Composable fun HomeLibraryScreen( viewModel: LibraryViewModel = hiltViewModel(), @@ -38,13 +42,14 @@ fun HomeLibraryScreen( onLogout: () -> Unit, ) { val state by viewModel.state.collectAsStateWithLifecycle() - val fabState = rememberFloatingActionMenuState() + val sheetState = rememberModalBottomSheetState() LibraryScreenContent( state = state, listState = viewModel.listState, - fabState = fabState, - onFabFilter = viewModel::onFabFilter, + sheetState = sheetState, + onFilterChanged = viewModel::onFilterChanged, + onModalBottomSheet = viewModel::onModalBottomSheet, onIsSearching = viewModel::onIsSearching, onSearchQuery = viewModel::onSearchQuery, onClickPlay = onClickPlay, @@ -53,15 +58,16 @@ fun HomeLibraryScreen( ) } -@OptIn(ExperimentalMaterial3AdaptiveApi::class) +@OptIn(ExperimentalMaterial3AdaptiveApi::class, ExperimentalMaterial3Api::class) @Composable private fun LibraryScreenContent( state: LibraryState, listState: LazyListState, - fabState: FloatingActionMenuState, + sheetState: SheetState, + onFilterChanged: (AppFilter) -> Unit, + onModalBottomSheet: (Boolean) -> Unit, onIsSearching: (Boolean) -> Unit, onSearchQuery: (String) -> Unit, - onFabFilter: (FabFilter) -> Unit, onClickPlay: (Int, Boolean) -> Unit, onSettings: () -> Unit, onLogout: () -> Unit, @@ -82,12 +88,13 @@ private fun LibraryScreenContent( LibraryListPane( state = state, listState = listState, - fabState = fabState, + sheetState = sheetState, + onFilterChanged = onFilterChanged, + onModalBottomSheet = onModalBottomSheet, onIsSearching = onIsSearching, onSearchQuery = onSearchQuery, onSettings = onSettings, onLogout = onLogout, - onFabFilter = onFabFilter, onNavigate = { item -> navigator.navigateTo( pane = ListDetailPaneScaffoldRole.Detail, @@ -117,6 +124,7 @@ private fun LibraryScreenContent( * PREVIEW * ***********/ +@OptIn(ExperimentalMaterial3Api::class) @Preview(uiMode = Configuration.UI_MODE_NIGHT_YES or Configuration.UI_MODE_TYPE_NORMAL) @Preview( uiMode = Configuration.UI_MODE_NIGHT_YES or Configuration.UI_MODE_TYPE_NORMAL, @@ -128,11 +136,11 @@ private fun LibraryScreenContent( ) @Composable private fun Preview_LibraryScreenContent() { - PluviaTheme { - LibraryScreenContent( - listState = rememberLazyListState(), - state = LibraryState( - appInfoList = List(14) { idx -> + val sheetState = rememberModalBottomSheetState() + var state by remember { + mutableStateOf( + LibraryState( + appInfoList = List(15) { idx -> val item = fakeAppInfo(idx) LibraryItem( index = idx, @@ -142,13 +150,24 @@ private fun Preview_LibraryScreenContent() { ) }, ), - fabState = rememberFloatingActionMenuState(FloatingActionMenuValue.Open), - onIsSearching = { }, - onSearchQuery = { }, - onFabFilter = { }, + ) + } + PluviaTheme { + LibraryScreenContent( + listState = rememberLazyListState(), + state = state, + sheetState = sheetState, + onIsSearching = {}, + onSearchQuery = {}, + onFilterChanged = { }, + onModalBottomSheet = { + val currentState = state.modalBottomSheet + println("State: $currentState") + state = state.copy(modalBottomSheet = !currentState) + }, onClickPlay = { _, _ -> }, - onSettings = { }, - onLogout = { }, + onSettings = {}, + onLogout = {}, ) } } diff --git a/app/src/main/java/com/OxGames/Pluvia/ui/screen/library/components/LibraryBottomSheet.kt b/app/src/main/java/com/OxGames/Pluvia/ui/screen/library/components/LibraryBottomSheet.kt new file mode 100644 index 0000000..30d6b31 --- /dev/null +++ b/app/src/main/java/com/OxGames/Pluvia/ui/screen/library/components/LibraryBottomSheet.kt @@ -0,0 +1,127 @@ +package com.OxGames.Pluvia.ui.screen.library.components + +import android.content.res.Configuration +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.FlowRow +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +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.material3.FilterChip +import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Surface +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import com.OxGames.Pluvia.ui.enums.AppFilter +import com.OxGames.Pluvia.ui.theme.PluviaTheme +import java.util.EnumSet + +@Composable +fun LibraryBottomSheet( + selectedFilters: EnumSet, + onFilterChanged: (AppFilter) -> Unit, +) { + Column( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 32.dp), + ) { + Text(text = "Filters", style = MaterialTheme.typography.titleLarge) + + Spacer(modifier = Modifier.height(18.dp)) + + FlowRow { + FilterChip( + modifier = Modifier.padding(end = 8.dp), + onClick = { onFilterChanged(AppFilter.INSTALLED) }, + label = { Text(text = "Installed") }, + selected = selectedFilters.contains(AppFilter.INSTALLED), + leadingIcon = { Icon(imageVector = Icons.Default.InstallMobile, contentDescription = null) }, + ) + FilterChip( + modifier = Modifier.padding(end = 8.dp), + onClick = { onFilterChanged(AppFilter.GAME) }, + label = { Text(text = "Game") }, + selected = selectedFilters.contains(AppFilter.GAME), + leadingIcon = { Icon(imageVector = Icons.Default.VideogameAsset, contentDescription = null) }, + ) + FilterChip( + modifier = Modifier.padding(end = 8.dp), + onClick = { onFilterChanged(AppFilter.APPLICATION) }, + label = { Text(text = "Application") }, + selected = selectedFilters.contains(AppFilter.APPLICATION), + leadingIcon = { Icon(imageVector = Icons.Default.Computer, contentDescription = null) }, + ) + FilterChip( + modifier = Modifier.padding(end = 8.dp), + onClick = { onFilterChanged(AppFilter.TOOL) }, + label = { Text(text = "Tool") }, + selected = selectedFilters.contains(AppFilter.TOOL), + leadingIcon = { Icon(imageVector = Icons.Default.Build, contentDescription = null) }, + ) + FilterChip( + modifier = Modifier.padding(end = 8.dp), + onClick = { onFilterChanged(AppFilter.DEMO) }, + label = { Text(text = "Demo") }, + selected = selectedFilters.contains(AppFilter.DEMO), + leadingIcon = { Icon(imageVector = Icons.Default.AvTimer, contentDescription = null) }, + ) + } + + Spacer(modifier = Modifier.height(32.dp)) // A little extra padding. + } +} + +/*********** + * PREVIEW * + ***********/ + +@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES or Configuration.UI_MODE_TYPE_NORMAL) +@Preview +@Composable +private fun Preview_LibraryBottomSheet() { + PluviaTheme { + Surface { + LibraryBottomSheet( + selectedFilters = EnumSet.of(AppFilter.GAME, AppFilter.DEMO), + onFilterChanged = { }, + ) + } + } +} + +// Note: Previews seem to be broken for this, run it manually + +// @OptIn(ExperimentalMaterial3Api::class) +// @Preview(uiMode = Configuration.UI_MODE_NIGHT_YES or Configuration.UI_MODE_TYPE_NORMAL) +// @Preview +// @Composable +// private fun Preview_LibraryBottomSheet_AsSheet() { +// PluviaTheme { +// Scaffold { paddingValues -> +// Box( +// modifier = Modifier +// .fillMaxSize() +// .padding(paddingValues), +// ) { +// Text(text = "Hello World") +// +// +// ModalBottomSheet( +// onDismissRequest = { }, +// content = { LibraryBottomSheet() }, +// ) +// } +// } +// } +// } diff --git a/app/src/main/java/com/OxGames/Pluvia/ui/screen/library/components/LibraryFab.kt b/app/src/main/java/com/OxGames/Pluvia/ui/screen/library/components/LibraryFab.kt index 0394673..e9dd435 100644 --- a/app/src/main/java/com/OxGames/Pluvia/ui/screen/library/components/LibraryFab.kt +++ b/app/src/main/java/com/OxGames/Pluvia/ui/screen/library/components/LibraryFab.kt @@ -14,13 +14,13 @@ import com.OxGames.Pluvia.ui.component.fabmenu.FloatingActionMenu import com.OxGames.Pluvia.ui.component.fabmenu.FloatingActionMenuItem import com.OxGames.Pluvia.ui.component.fabmenu.state.FloatingActionMenuState import com.OxGames.Pluvia.ui.data.LibraryState -import com.OxGames.Pluvia.ui.enums.FabFilter +import com.OxGames.Pluvia.ui.enums.AppFilter @Composable internal fun LibraryFab( fabState: FloatingActionMenuState, state: LibraryState, - onFabFilter: (FabFilter) -> Unit, + onFabFilter: (AppFilter) -> Unit, ) { FloatingActionMenu( state = fabState, @@ -29,45 +29,45 @@ internal fun LibraryFab( ) { FloatingActionMenuItem( labelText = "Installed", - isSelected = state.appInfoSortType.contains(FabFilter.INSTALLED), + isSelected = state.appInfoSortType.contains(AppFilter.INSTALLED), onClick = { - onFabFilter(FabFilter.INSTALLED) + onFabFilter(AppFilter.INSTALLED) fabState.close() }, content = { Icon(Icons.Filled.InstallMobile, "Installed") }, ) FloatingActionMenuItem( labelText = "Game", - isSelected = state.appInfoSortType.contains(FabFilter.GAME), + isSelected = state.appInfoSortType.contains(AppFilter.GAME), onClick = { - onFabFilter(FabFilter.GAME) + onFabFilter(AppFilter.GAME) fabState.close() }, content = { Icon(Icons.Filled.VideogameAsset, "Game") }, ) FloatingActionMenuItem( labelText = "Application", - isSelected = state.appInfoSortType.contains(FabFilter.APPLICATION), + isSelected = state.appInfoSortType.contains(AppFilter.APPLICATION), onClick = { - onFabFilter(FabFilter.APPLICATION) + onFabFilter(AppFilter.APPLICATION) fabState.close() }, content = { Icon(Icons.Filled.Computer, "Application") }, ) FloatingActionMenuItem( labelText = "Tool", - isSelected = state.appInfoSortType.contains(FabFilter.TOOL), + isSelected = state.appInfoSortType.contains(AppFilter.TOOL), onClick = { - onFabFilter(FabFilter.TOOL) + onFabFilter(AppFilter.TOOL) fabState.close() }, content = { Icon(Icons.Filled.Build, "Tool") }, ) FloatingActionMenuItem( labelText = "Demo", - isSelected = state.appInfoSortType.contains(FabFilter.DEMO), + isSelected = state.appInfoSortType.contains(AppFilter.DEMO), onClick = { - onFabFilter(FabFilter.DEMO) + onFabFilter(AppFilter.DEMO) fabState.close() }, content = { Icon(Icons.Filled.AvTimer, "Demo") }, diff --git a/app/src/main/java/com/OxGames/Pluvia/ui/screen/library/components/LibraryList.kt b/app/src/main/java/com/OxGames/Pluvia/ui/screen/library/components/LibraryList.kt index c79a9e9..d1bf41f 100644 --- a/app/src/main/java/com/OxGames/Pluvia/ui/screen/library/components/LibraryList.kt +++ b/app/src/main/java/com/OxGames/Pluvia/ui/screen/library/components/LibraryList.kt @@ -20,7 +20,6 @@ import com.OxGames.Pluvia.data.LibraryItem @Composable internal fun LibraryList( - paddingValues: PaddingValues, contentPaddingValues: PaddingValues, listState: LazyListState, list: List, @@ -28,9 +27,7 @@ internal fun LibraryList( ) { if (list.isEmpty()) { Box( - modifier = Modifier - .padding(paddingValues) - .fillMaxSize(), + modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center, ) { Surface( @@ -47,9 +44,7 @@ internal fun LibraryList( } } else { LazyColumn( - modifier = Modifier - .padding(paddingValues) - .fillMaxSize(), + modifier = Modifier.fillMaxSize(), state = listState, contentPadding = contentPaddingValues, ) { diff --git a/app/src/main/java/com/OxGames/Pluvia/ui/screen/library/components/LibraryListPane.kt b/app/src/main/java/com/OxGames/Pluvia/ui/screen/library/components/LibraryListPane.kt index efba48b..8ee2c64 100644 --- a/app/src/main/java/com/OxGames/Pluvia/ui/screen/library/components/LibraryListPane.kt +++ b/app/src/main/java/com/OxGames/Pluvia/ui/screen/library/components/LibraryListPane.kt @@ -1,43 +1,65 @@ package com.OxGames.Pluvia.ui.screen.library.components +import androidx.compose.animation.AnimatedVisibility +import androidx.compose.animation.fadeIn +import androidx.compose.animation.fadeOut +import androidx.compose.animation.scaleIn +import androidx.compose.animation.scaleOut +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.WindowInsets import androidx.compose.foundation.layout.asPaddingValues import androidx.compose.foundation.layout.calculateEndPadding import androidx.compose.foundation.layout.calculateStartPadding +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.statusBars import androidx.compose.foundation.lazy.LazyListState import androidx.compose.foundation.lazy.rememberLazyListState +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.FilterList +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.ExtendedFloatingActionButton +import androidx.compose.material3.Icon +import androidx.compose.material3.ModalBottomSheet import androidx.compose.material3.Scaffold +import androidx.compose.material3.SheetState import androidx.compose.material3.SnackbarHost import androidx.compose.material3.SnackbarHostState import androidx.compose.material3.Surface +import androidx.compose.material3.Text +import androidx.compose.material3.rememberModalBottomSheetState import androidx.compose.runtime.Composable +import androidx.compose.runtime.derivedStateOf +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.LayoutDirection import androidx.compose.ui.unit.dp import com.OxGames.Pluvia.data.LibraryItem -import com.OxGames.Pluvia.ui.component.fabmenu.state.FloatingActionMenuState -import com.OxGames.Pluvia.ui.component.fabmenu.state.FloatingActionMenuValue -import com.OxGames.Pluvia.ui.component.fabmenu.state.rememberFloatingActionMenuState import com.OxGames.Pluvia.ui.data.LibraryState -import com.OxGames.Pluvia.ui.enums.FabFilter +import com.OxGames.Pluvia.ui.enums.AppFilter import com.OxGames.Pluvia.ui.internal.fakeAppInfo import com.OxGames.Pluvia.ui.theme.PluviaTheme +@OptIn(ExperimentalMaterial3Api::class) @Composable internal fun LibraryListPane( state: LibraryState, - fabState: FloatingActionMenuState, listState: LazyListState, - onFabFilter: (FabFilter) -> Unit, + sheetState: SheetState, + onFilterChanged: (AppFilter) -> Unit, + onModalBottomSheet: (Boolean) -> Unit, onIsSearching: (Boolean) -> Unit, onLogout: () -> Unit, onNavigate: (Int) -> Unit, onSearchQuery: (String) -> Unit, onSettings: () -> Unit, ) { + val expandedFab by remember { derivedStateOf { listState.firstVisibleItemIndex == 0 } } val snackBarHost = remember { SnackbarHostState() } Scaffold( @@ -55,29 +77,55 @@ internal fun LibraryListPane( ) }, floatingActionButton = { - LibraryFab( - fabState = fabState, - state = state, - onFabFilter = onFabFilter, - ) + AnimatedVisibility( + visible = !state.isSearching, + enter = fadeIn() + scaleIn(), + exit = fadeOut() + scaleOut(), + ) { + ExtendedFloatingActionButton( + text = { Text(text = "Filters") }, + expanded = expandedFab, + icon = { Icon(imageVector = Icons.Default.FilterList, contentDescription = null) }, + onClick = { onModalBottomSheet(true) }, + ) + } }, ) { paddingValues -> val statusBarPadding = WindowInsets.statusBars.asPaddingValues().calculateTopPadding() - LibraryList( - list = state.appInfoList, - listState = listState, - paddingValues = PaddingValues( - start = paddingValues.calculateStartPadding(LayoutDirection.Ltr), - end = paddingValues.calculateEndPadding(LayoutDirection.Ltr), - top = statusBarPadding, - bottom = paddingValues.calculateBottomPadding(), - ), - contentPaddingValues = PaddingValues( - top = paddingValues.calculateTopPadding().minus(statusBarPadding), - bottom = 72.dp, - ), - onItemClick = onNavigate, - ) + Box( + modifier = Modifier + .fillMaxSize() + .padding( + PaddingValues( + start = paddingValues.calculateStartPadding(LayoutDirection.Ltr), + end = paddingValues.calculateEndPadding(LayoutDirection.Ltr), + top = statusBarPadding, + bottom = paddingValues.calculateBottomPadding(), + ), + ), + ) { + LibraryList( + list = state.appInfoList, + listState = listState, + contentPaddingValues = PaddingValues( + top = paddingValues.calculateTopPadding().minus(statusBarPadding), + bottom = 72.dp, + ), + onItemClick = onNavigate, + ) + if (state.modalBottomSheet) { + ModalBottomSheet( + onDismissRequest = { onModalBottomSheet(false) }, + sheetState = sheetState, + content = { + LibraryBottomSheet( + selectedFilters = state.appInfoSortType, + onFilterChanged = onFilterChanged, + ) + }, + ) + } + } } } @@ -85,29 +133,41 @@ internal fun LibraryListPane( * PREVIEW * ***********/ +@OptIn(ExperimentalMaterial3Api::class) @Preview(uiMode = android.content.res.Configuration.UI_MODE_NIGHT_YES or android.content.res.Configuration.UI_MODE_TYPE_NORMAL) @Preview @Composable private fun Preview_LibraryListPane() { + val sheetState = rememberModalBottomSheetState() + var state by remember { + mutableStateOf( + LibraryState( + appInfoList = List(15) { idx -> + val item = fakeAppInfo(idx) + LibraryItem( + index = idx, + appId = item.id, + name = item.name, + iconHash = item.iconHash, + ) + }, + ), + ) + } PluviaTheme { Surface { LibraryListPane( listState = rememberLazyListState(), - state = LibraryState( - appInfoList = List(14) { idx -> - val item = fakeAppInfo(idx) - LibraryItem( - index = idx, - appId = item.id, - name = item.name, - iconHash = item.iconHash, - ) - }, - ), - fabState = rememberFloatingActionMenuState(FloatingActionMenuValue.Open), + state = state, + sheetState = sheetState, + onFilterChanged = { }, + onModalBottomSheet = { + val currentState = state.modalBottomSheet + println("State: $currentState") + state = state.copy(modalBottomSheet = !currentState) + }, onIsSearching = { }, onSearchQuery = { }, - onFabFilter = { }, onSettings = { }, onLogout = { }, onNavigate = { }, diff --git a/app/src/main/java/com/OxGames/Pluvia/ui/screen/library/components/LibrarySearchBar.kt b/app/src/main/java/com/OxGames/Pluvia/ui/screen/library/components/LibrarySearchBar.kt index 7f72b84..06b2ff8 100644 --- a/app/src/main/java/com/OxGames/Pluvia/ui/screen/library/components/LibrarySearchBar.kt +++ b/app/src/main/java/com/OxGames/Pluvia/ui/screen/library/components/LibrarySearchBar.kt @@ -107,7 +107,6 @@ internal fun LibrarySearchBar( content = { if (state.isSearching) { LibraryList( - paddingValues = PaddingValues(), contentPaddingValues = PaddingValues(bottom = 72.dp), listState = listState, list = state.appInfoList, From 756677381f4e830933d6fba0cb17d47a69a73a82 Mon Sep 17 00:00:00 2001 From: Oxters Date: Thu, 13 Feb 2025 10:20:58 -0500 Subject: [PATCH 4/8] Set library filter to be persistent through the PrefManager --- .../main/java/com/OxGames/Pluvia/PrefManager.kt | 11 +++++++++++ .../main/java/com/OxGames/Pluvia/enums/AppType.kt | 4 ++-- .../com/OxGames/Pluvia/ui/data/LibraryState.kt | 3 ++- .../java/com/OxGames/Pluvia/ui/enums/AppFilter.kt | 15 +++++++++++++++ .../OxGames/Pluvia/ui/model/LibraryViewModel.kt | 3 +++ 5 files changed, 33 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/com/OxGames/Pluvia/PrefManager.kt b/app/src/main/java/com/OxGames/Pluvia/PrefManager.kt index a7c9275..3be1dd7 100644 --- a/app/src/main/java/com/OxGames/Pluvia/PrefManager.kt +++ b/app/src/main/java/com/OxGames/Pluvia/PrefManager.kt @@ -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 @@ -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 + get() { + val value = getPref(LIBRARY_FILTER, AppFilter.toFlags(EnumSet.of(AppFilter.ALPHABETIC, 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() { diff --git a/app/src/main/java/com/OxGames/Pluvia/enums/AppType.kt b/app/src/main/java/com/OxGames/Pluvia/enums/AppType.kt index 75e5f3f..0dc5a0d 100644 --- a/app/src/main/java/com/OxGames/Pluvia/enums/AppType.kt +++ b/app/src/main/java/com/OxGames/Pluvia/enums/AppType.kt @@ -66,8 +66,8 @@ enum class AppType(val code: Int) { return result } - fun code(value: EnumSet): Int { - return value.map { it.code }.reduce { first, second -> first or second } + fun toFlags(value: EnumSet): Int { + return value.map { it.code }.reduceOrNull { first, second -> first or second } ?: invalid.code } fun fromCode(code: Int): AppType { diff --git a/app/src/main/java/com/OxGames/Pluvia/ui/data/LibraryState.kt b/app/src/main/java/com/OxGames/Pluvia/ui/data/LibraryState.kt index 823df8c..9fc7562 100644 --- a/app/src/main/java/com/OxGames/Pluvia/ui/data/LibraryState.kt +++ b/app/src/main/java/com/OxGames/Pluvia/ui/data/LibraryState.kt @@ -1,5 +1,6 @@ package com.OxGames.Pluvia.ui.data +import com.OxGames.Pluvia.PrefManager import com.OxGames.Pluvia.data.LibraryItem import com.OxGames.Pluvia.ui.enums.AppFilter import java.util.EnumSet @@ -10,7 +11,7 @@ import java.util.EnumSet // 3. Close button on sheet or not? Tapping outside or swipe down dismisses it. data class LibraryState( - val appInfoSortType: EnumSet = EnumSet.of(AppFilter.ALPHABETIC, AppFilter.GAME), // TODO save as pref + val appInfoSortType: EnumSet = PrefManager.libraryFilter, val appInfoList: List = emptyList(), val modalBottomSheet: Boolean = false, diff --git a/app/src/main/java/com/OxGames/Pluvia/ui/enums/AppFilter.kt b/app/src/main/java/com/OxGames/Pluvia/ui/enums/AppFilter.kt index 5e44ddb..0ed1b07 100644 --- a/app/src/main/java/com/OxGames/Pluvia/ui/enums/AppFilter.kt +++ b/app/src/main/java/com/OxGames/Pluvia/ui/enums/AppFilter.kt @@ -4,6 +4,7 @@ import com.OxGames.Pluvia.enums.AppType import java.util.EnumSet enum class AppFilter(val code: Int) { + NONE(0), INSTALLED(0x01), ALPHABETIC(0x02), GAME(0x04), @@ -29,5 +30,19 @@ enum class AppFilter(val code: Int) { } return output } + + fun fromFlags(flags: Int): EnumSet { + 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): Int { + return value.map { it.code }.reduceOrNull { first, second -> first or second } ?: NONE.code + } } } diff --git a/app/src/main/java/com/OxGames/Pluvia/ui/model/LibraryViewModel.kt b/app/src/main/java/com/OxGames/Pluvia/ui/model/LibraryViewModel.kt index e64338a..3c123fb 100644 --- a/app/src/main/java/com/OxGames/Pluvia/ui/model/LibraryViewModel.kt +++ b/app/src/main/java/com/OxGames/Pluvia/ui/model/LibraryViewModel.kt @@ -6,6 +6,7 @@ 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 @@ -80,6 +81,8 @@ class LibraryViewModel @Inject constructor( updatedFilter.add(value) } + PrefManager.libraryFilter = updatedFilter + currentState.copy(appInfoSortType = updatedFilter) } From 4666fa5e6d7ca049f750b16261ed2dae145800a7 Mon Sep 17 00:00:00 2001 From: Oxters Date: Thu, 13 Feb 2025 10:53:48 -0500 Subject: [PATCH 5/8] Cleaned up bottom filter sheet a bit. Commented out the alphabetic app filter for now. --- .../java/com/OxGames/Pluvia/PrefManager.kt | 2 +- .../Pluvia/ui/component/FlowFilterChip.kt | 24 +++++++++ .../com/OxGames/Pluvia/ui/enums/AppFilter.kt | 53 +++++++++++++++---- .../library/components/LibraryBottomSheet.kt | 51 ++++-------------- 4 files changed, 78 insertions(+), 52 deletions(-) create mode 100644 app/src/main/java/com/OxGames/Pluvia/ui/component/FlowFilterChip.kt diff --git a/app/src/main/java/com/OxGames/Pluvia/PrefManager.kt b/app/src/main/java/com/OxGames/Pluvia/PrefManager.kt index 3be1dd7..ab8b807 100644 --- a/app/src/main/java/com/OxGames/Pluvia/PrefManager.kt +++ b/app/src/main/java/com/OxGames/Pluvia/PrefManager.kt @@ -360,7 +360,7 @@ object PrefManager { private val LIBRARY_FILTER = intPreferencesKey("library_filter") var libraryFilter: EnumSet get() { - val value = getPref(LIBRARY_FILTER, AppFilter.toFlags(EnumSet.of(AppFilter.ALPHABETIC, AppFilter.GAME))) + val value = getPref(LIBRARY_FILTER, AppFilter.toFlags(EnumSet.of(AppFilter.GAME))) return AppFilter.fromFlags(value) } set(value) { diff --git a/app/src/main/java/com/OxGames/Pluvia/ui/component/FlowFilterChip.kt b/app/src/main/java/com/OxGames/Pluvia/ui/component/FlowFilterChip.kt new file mode 100644 index 0000000..9c8c8e7 --- /dev/null +++ b/app/src/main/java/com/OxGames/Pluvia/ui/component/FlowFilterChip.kt @@ -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, + ) +} diff --git a/app/src/main/java/com/OxGames/Pluvia/ui/enums/AppFilter.kt b/app/src/main/java/com/OxGames/Pluvia/ui/enums/AppFilter.kt index 0ed1b07..52f6644 100644 --- a/app/src/main/java/com/OxGames/Pluvia/ui/enums/AppFilter.kt +++ b/app/src/main/java/com/OxGames/Pluvia/ui/enums/AppFilter.kt @@ -1,16 +1,51 @@ 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.SortByAlpha +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) { - NONE(0), - INSTALLED(0x01), - ALPHABETIC(0x02), - GAME(0x04), - APPLICATION(0x08), - TOOL(0x10), - DEMO(0x20), +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 { @@ -42,7 +77,7 @@ enum class AppFilter(val code: Int) { } fun toFlags(value: EnumSet): Int { - return value.map { it.code }.reduceOrNull { first, second -> first or second } ?: NONE.code + return value.map { it.code }.reduceOrNull { first, second -> first or second } ?: 0 } } } diff --git a/app/src/main/java/com/OxGames/Pluvia/ui/screen/library/components/LibraryBottomSheet.kt b/app/src/main/java/com/OxGames/Pluvia/ui/screen/library/components/LibraryBottomSheet.kt index 30d6b31..4d6eb73 100644 --- a/app/src/main/java/com/OxGames/Pluvia/ui/screen/library/components/LibraryBottomSheet.kt +++ b/app/src/main/java/com/OxGames/Pluvia/ui/screen/library/components/LibraryBottomSheet.kt @@ -7,13 +7,6 @@ import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding -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.material3.FilterChip import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Surface @@ -22,6 +15,7 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp +import com.OxGames.Pluvia.ui.component.FlowFilterChip import com.OxGames.Pluvia.ui.enums.AppFilter import com.OxGames.Pluvia.ui.theme.PluviaTheme import java.util.EnumSet @@ -41,41 +35,14 @@ fun LibraryBottomSheet( Spacer(modifier = Modifier.height(18.dp)) FlowRow { - FilterChip( - modifier = Modifier.padding(end = 8.dp), - onClick = { onFilterChanged(AppFilter.INSTALLED) }, - label = { Text(text = "Installed") }, - selected = selectedFilters.contains(AppFilter.INSTALLED), - leadingIcon = { Icon(imageVector = Icons.Default.InstallMobile, contentDescription = null) }, - ) - FilterChip( - modifier = Modifier.padding(end = 8.dp), - onClick = { onFilterChanged(AppFilter.GAME) }, - label = { Text(text = "Game") }, - selected = selectedFilters.contains(AppFilter.GAME), - leadingIcon = { Icon(imageVector = Icons.Default.VideogameAsset, contentDescription = null) }, - ) - FilterChip( - modifier = Modifier.padding(end = 8.dp), - onClick = { onFilterChanged(AppFilter.APPLICATION) }, - label = { Text(text = "Application") }, - selected = selectedFilters.contains(AppFilter.APPLICATION), - leadingIcon = { Icon(imageVector = Icons.Default.Computer, contentDescription = null) }, - ) - FilterChip( - modifier = Modifier.padding(end = 8.dp), - onClick = { onFilterChanged(AppFilter.TOOL) }, - label = { Text(text = "Tool") }, - selected = selectedFilters.contains(AppFilter.TOOL), - leadingIcon = { Icon(imageVector = Icons.Default.Build, contentDescription = null) }, - ) - FilterChip( - modifier = Modifier.padding(end = 8.dp), - onClick = { onFilterChanged(AppFilter.DEMO) }, - label = { Text(text = "Demo") }, - selected = selectedFilters.contains(AppFilter.DEMO), - leadingIcon = { Icon(imageVector = Icons.Default.AvTimer, contentDescription = null) }, - ) + AppFilter.entries.forEach { appFilter -> + FlowFilterChip ( + onClick = { onFilterChanged(appFilter) }, + label = { Text(text = appFilter.displayText) }, + selected = selectedFilters.contains(appFilter), + leadingIcon = { Icon(imageVector = appFilter.icon, contentDescription = null) }, + ) + } } Spacer(modifier = Modifier.height(32.dp)) // A little extra padding. From 09279a090ea33d6b184c614f37d53935d35be171 Mon Sep 17 00:00:00 2001 From: Oxters Date: Thu, 13 Feb 2025 11:24:41 -0500 Subject: [PATCH 6/8] Fixed an issue where chips would not show as selected or deselected until after the bottom sheet is closed and opened again --- app/src/main/java/com/OxGames/Pluvia/ui/enums/AppFilter.kt | 1 - .../main/java/com/OxGames/Pluvia/ui/model/LibraryViewModel.kt | 3 ++- .../Pluvia/ui/screen/library/components/LibraryBottomSheet.kt | 2 ++ 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/OxGames/Pluvia/ui/enums/AppFilter.kt b/app/src/main/java/com/OxGames/Pluvia/ui/enums/AppFilter.kt index 52f6644..56d78a9 100644 --- a/app/src/main/java/com/OxGames/Pluvia/ui/enums/AppFilter.kt +++ b/app/src/main/java/com/OxGames/Pluvia/ui/enums/AppFilter.kt @@ -5,7 +5,6 @@ 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.SortByAlpha import androidx.compose.material.icons.filled.VideogameAsset import androidx.compose.ui.graphics.vector.ImageVector import com.OxGames.Pluvia.enums.AppType diff --git a/app/src/main/java/com/OxGames/Pluvia/ui/model/LibraryViewModel.kt b/app/src/main/java/com/OxGames/Pluvia/ui/model/LibraryViewModel.kt index 3c123fb..ffffa6c 100644 --- a/app/src/main/java/com/OxGames/Pluvia/ui/model/LibraryViewModel.kt +++ b/app/src/main/java/com/OxGames/Pluvia/ui/model/LibraryViewModel.kt @@ -22,6 +22,7 @@ import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch import timber.log.Timber +import java.util.EnumSet @HiltViewModel class LibraryViewModel @Inject constructor( @@ -73,7 +74,7 @@ class LibraryViewModel @Inject constructor( // TODO: include other sort types fun onFilterChanged(value: AppFilter) { _state.update { currentState -> - val updatedFilter = currentState.appInfoSortType + val updatedFilter = EnumSet.copyOf(currentState.appInfoSortType) if (updatedFilter.contains(value)) { updatedFilter.remove(value) diff --git a/app/src/main/java/com/OxGames/Pluvia/ui/screen/library/components/LibraryBottomSheet.kt b/app/src/main/java/com/OxGames/Pluvia/ui/screen/library/components/LibraryBottomSheet.kt index 4d6eb73..c85d031 100644 --- a/app/src/main/java/com/OxGames/Pluvia/ui/screen/library/components/LibraryBottomSheet.kt +++ b/app/src/main/java/com/OxGames/Pluvia/ui/screen/library/components/LibraryBottomSheet.kt @@ -2,6 +2,7 @@ package com.OxGames.Pluvia.ui.screen.library.components import android.content.res.Configuration import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.ExperimentalLayoutApi import androidx.compose.foundation.layout.FlowRow import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth @@ -21,6 +22,7 @@ import com.OxGames.Pluvia.ui.theme.PluviaTheme import java.util.EnumSet @Composable +@OptIn(ExperimentalLayoutApi::class) fun LibraryBottomSheet( selectedFilters: EnumSet, onFilterChanged: (AppFilter) -> Unit, From 672e408b85aabbe7b2fac158ea9cad15123df5a2 Mon Sep 17 00:00:00 2001 From: Oxters Date: Thu, 13 Feb 2025 11:43:48 -0500 Subject: [PATCH 7/8] Bumped version and removed some todos --- app/build.gradle.kts | 4 ++-- app/src/main/java/com/OxGames/Pluvia/ui/data/LibraryState.kt | 5 ----- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index c7416fc..aeddf2d 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -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" diff --git a/app/src/main/java/com/OxGames/Pluvia/ui/data/LibraryState.kt b/app/src/main/java/com/OxGames/Pluvia/ui/data/LibraryState.kt index 9fc7562..55f7a92 100644 --- a/app/src/main/java/com/OxGames/Pluvia/ui/data/LibraryState.kt +++ b/app/src/main/java/com/OxGames/Pluvia/ui/data/LibraryState.kt @@ -5,11 +5,6 @@ import com.OxGames.Pluvia.data.LibraryItem import com.OxGames.Pluvia.ui.enums.AppFilter import java.util.EnumSet -// TODO: -// 1. Missing games?? Did I break something or...? -// 2. The filter chips do not change their selected color. -// 3. Close button on sheet or not? Tapping outside or swipe down dismisses it. - data class LibraryState( val appInfoSortType: EnumSet = PrefManager.libraryFilter, val appInfoList: List = emptyList(), From 40ed0f952c88580a7138a033d36f06d2667ff67a Mon Sep 17 00:00:00 2001 From: Oxters Date: Thu, 13 Feb 2025 11:51:15 -0500 Subject: [PATCH 8/8] Ran format kotlin --- .../main/java/com/OxGames/Pluvia/ui/model/LibraryViewModel.kt | 2 +- .../Pluvia/ui/screen/library/components/LibraryBottomSheet.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/OxGames/Pluvia/ui/model/LibraryViewModel.kt b/app/src/main/java/com/OxGames/Pluvia/ui/model/LibraryViewModel.kt index ffffa6c..06384c9 100644 --- a/app/src/main/java/com/OxGames/Pluvia/ui/model/LibraryViewModel.kt +++ b/app/src/main/java/com/OxGames/Pluvia/ui/model/LibraryViewModel.kt @@ -14,6 +14,7 @@ import com.OxGames.Pluvia.service.SteamService import com.OxGames.Pluvia.ui.data.LibraryState 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 @@ -22,7 +23,6 @@ import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch import timber.log.Timber -import java.util.EnumSet @HiltViewModel class LibraryViewModel @Inject constructor( diff --git a/app/src/main/java/com/OxGames/Pluvia/ui/screen/library/components/LibraryBottomSheet.kt b/app/src/main/java/com/OxGames/Pluvia/ui/screen/library/components/LibraryBottomSheet.kt index c85d031..1d0379c 100644 --- a/app/src/main/java/com/OxGames/Pluvia/ui/screen/library/components/LibraryBottomSheet.kt +++ b/app/src/main/java/com/OxGames/Pluvia/ui/screen/library/components/LibraryBottomSheet.kt @@ -38,7 +38,7 @@ fun LibraryBottomSheet( FlowRow { AppFilter.entries.forEach { appFilter -> - FlowFilterChip ( + FlowFilterChip( onClick = { onFilterChanged(appFilter) }, label = { Text(text = appFilter.displayText) }, selected = selectedFilters.contains(appFilter),