group categories on discover screen

in order to make them easier to parse/browse through
This commit is contained in:
Torsten Grote
2025-09-15 16:13:55 -03:00
parent 8361dc76ad
commit 7909ec46d5
7 changed files with 140 additions and 24 deletions

View File

@@ -0,0 +1,22 @@
package org.fdroid.ui.categories
import androidx.annotation.StringRes
import org.fdroid.next.R
data class CategoryGroup(
val id: String,
@get:StringRes
val name: Int,
)
object CategoryGroups {
val productivity = CategoryGroup("productivity", R.string.category_group_productivity)
val tools = CategoryGroup("tools", R.string.category_group_tools)
val wallets = CategoryGroup("wallets", R.string.category_group_wallets)
val media = CategoryGroup("media", R.string.category_group_media)
val communication = CategoryGroup("communication", R.string.category_group_communication)
val device = CategoryGroup("device", R.string.category_group_device)
val storage = CategoryGroup("storage", R.string.category_group_storage)
val interests = CategoryGroup("interests", R.string.category_group_interests)
val misc = CategoryGroup("misc", R.string.category_group_misc)
}

View File

@@ -126,4 +126,67 @@ data class CategoryItem(val id: String, val name: String) {
"Writing" -> Icons.Default.EditNote
else -> Icons.Default.Category
}
val group: CategoryGroup
get() = when (id) {
"App Store & Updater" -> CategoryGroups.device
"Bookmark" -> CategoryGroups.storage
"Browser" -> CategoryGroups.productivity
"Calculator" -> CategoryGroups.tools
"Calendar & Agenda" -> CategoryGroups.productivity
"Cloud Storage & File Sync" -> CategoryGroups.storage
"Connectivity" -> CategoryGroups.device
"Development" -> CategoryGroups.interests
"DNS & Hosts" -> CategoryGroups.device
"Draw" -> CategoryGroups.interests
"Ebook Reader" -> CategoryGroups.media
"Email" -> CategoryGroups.communication
"File Encryption & Vault" -> CategoryGroups.storage
"File Transfer" -> CategoryGroups.storage
"Finance Manager" -> CategoryGroups.wallets
"Forum" -> CategoryGroups.communication
"Gallery" -> CategoryGroups.storage
"Games" -> CategoryGroups.media
"Graphics" -> CategoryGroups.interests
"Habit Tracker" -> CategoryGroups.productivity
"Icon Pack" -> CategoryGroups.device
"Internet" -> CategoryGroups.productivity
"Keyboard & IME" -> CategoryGroups.device
"Launcher" -> CategoryGroups.device
"Local Media Player" -> CategoryGroups.media
"Messaging" -> CategoryGroups.communication
"Money" -> CategoryGroups.wallets
"Multimedia" -> CategoryGroups.media
"Music Practice Tool" -> CategoryGroups.interests
"Navigation" -> CategoryGroups.tools
"News" -> CategoryGroups.interests
"Note" -> CategoryGroups.storage
"Online Media Player" -> CategoryGroups.media
"Pass Wallet" -> CategoryGroups.wallets
"Password & 2FA" -> CategoryGroups.device
"Phone & SMS" -> CategoryGroups.communication
"Podcast" -> CategoryGroups.media
"Public Transport" -> CategoryGroups.tools
"Reading" -> CategoryGroups.media
"Recipe Manager" -> CategoryGroups.interests
"Science & Education" -> CategoryGroups.interests
"Security" -> CategoryGroups.device
"Shopping List" -> CategoryGroups.tools
"Social Network" -> CategoryGroups.communication
"Sports & Health" -> CategoryGroups.interests
"System" -> CategoryGroups.device
"Task" -> CategoryGroups.productivity
"Text Editor" -> CategoryGroups.productivity
"Theming" -> CategoryGroups.device
"Time" -> CategoryGroups.productivity
"Translation & Dictionary" -> CategoryGroups.tools
"Voice & Video Chat" -> CategoryGroups.communication
"Unit Convertor" -> CategoryGroups.tools
"VPN & Proxy" -> CategoryGroups.device
"Wallet" -> CategoryGroups.wallets
"Wallpaper" -> CategoryGroups.device
"Weather" -> CategoryGroups.tools
"Workout" -> CategoryGroups.interests
"Writing" -> CategoryGroups.productivity
else -> CategoryGroups.misc
}
}

View File

@@ -7,7 +7,9 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
@@ -19,11 +21,11 @@ import org.fdroid.ui.lists.AppListType
@Composable
fun CategoryList(
categories: List<CategoryItem>?,
categoryMap: Map<CategoryGroup, List<CategoryItem>>?,
onNav: (NavKey) -> Unit,
modifier: Modifier = Modifier
) {
if (categories != null) Column(
if (categoryMap != null) Column(
modifier = modifier
) {
Text(
@@ -31,15 +33,28 @@ fun CategoryList(
style = MaterialTheme.typography.titleLarge,
modifier = Modifier.padding(bottom = 8.dp, start = 4.dp),
)
FlowRow(
horizontalArrangement = Arrangement.Start,
) {
categories.forEach { category ->
CategoryChip(category, {
val type = AppListType.Category(category.name, category.id)
val navKey = NavigationKey.AppList(type)
onNav(navKey)
})
// we'll sort the groups here, because before we didn't have the context to get names
val context = LocalContext.current
val sortedMap = remember(categoryMap) {
val comparator = compareBy<CategoryGroup> { context.getString(it.name) }
categoryMap.toSortedMap(comparator)
}
sortedMap.forEach { (group, categories) ->
Text(
text = stringResource(group.name),
style = MaterialTheme.typography.titleMedium,
modifier = Modifier.padding(4.dp),
)
FlowRow(
horizontalArrangement = Arrangement.Start,
) {
categories.forEach { category ->
CategoryChip(category, {
val type = AppListType.Category(category.name, category.id)
val navKey = NavigationKey.AppList(type)
onNav(navKey)
})
}
}
}
}
@@ -49,14 +64,18 @@ fun CategoryList(
@Composable
fun CategoryListPreview() {
FDroidContent {
val categories = listOf(
CategoryItem("App Store & Updater", "App Store & Updater"),
CategoryItem("Browser", "Browser"),
CategoryItem("Calendar & Agenda", "Calendar & Agenda"),
CategoryItem("Cloud Storage & File Sync", "Cloud Storage & File Sync"),
CategoryItem("Connectivity", "Connectivity"),
CategoryItem("Development", "Development"),
CategoryItem("doesn't exist", "Foo bar"),
val categories = mapOf(
CategoryGroups.productivity to listOf(
CategoryItem("App Store & Updater", "App Store & Updater"),
CategoryItem("Browser", "Browser"),
CategoryItem("Calendar & Agenda", "Calendar & Agenda"),
),
CategoryGroups.media to listOf(
CategoryItem("Cloud Storage & File Sync", "Cloud Storage & File Sync"),
CategoryItem("Connectivity", "Connectivity"),
CategoryItem("Development", "Development"),
CategoryItem("doesn't exist", "Foo bar"),
)
)
CategoryList(categories, {})
}

View File

@@ -171,10 +171,11 @@ fun Discover(
}
AnimatedVisibility(discoverModel is LoadedDiscoverModel) {
CategoryList(
categories = (discoverModel as LoadedDiscoverModel).categories,
categoryMap = (discoverModel as LoadedDiscoverModel).categories,
onNav = onNav,
modifier = Modifier
.padding(16.dp)
.padding(horizontal = 16.dp)
.padding(bottom = 16.dp)
.fillMaxWidth()
)
}

View File

@@ -5,6 +5,7 @@ import androidx.compose.runtime.collectAsState
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.StateFlow
import org.fdroid.database.Repository
import org.fdroid.ui.categories.CategoryGroup
import org.fdroid.ui.categories.CategoryItem
@Composable
@@ -34,7 +35,7 @@ fun DiscoverPresenter(
LoadedDiscoverModel(
newApps = apps.filter { it.isNew },
recentlyUpdatedApps = apps.filter { !it.isNew },
categories = categories,
categories = categories?.groupBy { it.group },
searchResults = searchResults,
)
}
@@ -46,6 +47,6 @@ object NoEnabledReposDiscoverModel : DiscoverModel()
data class LoadedDiscoverModel(
val newApps: List<AppDiscoverItem>,
val recentlyUpdatedApps: List<AppDiscoverItem>,
val categories: List<CategoryItem>?,
val categories: Map<CategoryGroup, List<CategoryItem>>?,
val searchResults: SearchResults? = null,
) : DiscoverModel()

View File

@@ -61,7 +61,7 @@ class DiscoverViewModel @Inject constructor(
)
}
}
val categories = db.getRepositoryDao().getLiveCategories().asFlow().map { categories ->
private val categories = db.getRepositoryDao().getLiveCategories().asFlow().map { categories ->
categories.map { category ->
CategoryItem(
id = category.id,

View File

@@ -36,6 +36,16 @@
<string name="filter_selected">selected</string>
<string name="category">Category</string>
<string name="category_group_productivity">Productivity</string>
<string name="category_group_tools">Tools</string>
<string name="category_group_wallets">Digital Wallets</string>
<string name="category_group_media">Entertainment &amp; Media</string>
<string name="category_group_communication">Communication</string>
<string name="category_group_device">Device</string>
<string name="category_group_storage">Files &amp; Storage</string>
<string name="category_group_interests">Interests</string>
<string name="category_group_misc">Miscellaneous</string>
<string name="author_by">By %1$s</string>
<string name="last_updated">Last updated: %1$s</string>
<!-- The placeholder in round brackets will be replaced with the size of the app -->