Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
84 commits
Select commit Hold shift + click to select a range
a41381d
feat: add sibling app suggestions for FOSS apps in DetailsScreen
jksalcedo Mar 12, 2026
1bed5ce
feat: expand app categories and handle unset category state
jksalcedo Mar 12, 2026
0ef902d
chore: externalize version configuration to version.properties
jksalcedo Mar 12, 2026
2084b6b
feat: add voting for app alternatives
jksalcedo Mar 12, 2026
e117b75
Added translation using Weblate (Croatian)
marin-19 Mar 11, 2026
fdc857b
Update translation files
weblate Mar 11, 2026
4b40c30
Translated using Weblate (Croatian)
marin-19 Mar 11, 2026
4abeea3
feat(ui): update and expand app categories in SubmitScreen
jksalcedo Mar 12, 2026
282035e
docs: add contributing section and reformat README content
jksalcedo Mar 12, 2026
34dc121
feat(ui): replace Row with FlowRow in submission type selection
jksalcedo Mar 12, 2026
f1a18a7
fix(ui): disable default window insets in Scaffolds
jksalcedo Mar 12, 2026
8a8c1b1
docs: update registered user count in README
jksalcedo Mar 12, 2026
feb42b4
Added translation using Weblate (Latvian)
Coool Mar 13, 2026
b629e01
Update translation files
weblate Mar 13, 2026
931206e
Update README with new badge information
jksalcedo Mar 13, 2026
ca457bd
Translated using Weblate (English)
Coool Mar 13, 2026
c90d4c6
Translated using Weblate (Estonian)
Coool Mar 13, 2026
b4dc997
Translated using Weblate (Chinese (Simplified Han script))
Coool Mar 13, 2026
fd58ffd
Translated using Weblate (Slovak)
Coool Mar 13, 2026
02405e2
Translated using Weblate (German)
Coool Mar 13, 2026
eca6c93
Translated using Weblate (Latvian)
Coool Mar 13, 2026
bfbc9e4
Translated using Weblate (Russian)
Coool Mar 13, 2026
45c7ee2
Translated using Weblate (Spanish)
Coool Mar 13, 2026
ec57deb
Translated using Weblate (Italian)
Coool Mar 13, 2026
68e6860
Translated using Weblate (Turkish)
Coool Mar 13, 2026
d935b80
Translated using Weblate (Turkish)
Mar 13, 2026
396a854
Translated using Weblate (German)
CM-Lehmann Mar 14, 2026
b8916ba
Translated using Weblate (Estonian)
Wqttzicue Mar 13, 2026
2659284
Translated using Weblate (French)
Happyclem Mar 13, 2026
381c511
Translated using Weblate (Russian)
Wqttzicue Mar 13, 2026
13c0d63
feat: add sibling app suggestions for FOSS apps in DetailsScreen
jksalcedo Mar 12, 2026
9815ba3
feat: expand app categories and handle unset category state
jksalcedo Mar 12, 2026
b643c10
feat: add sibling app suggestions for FOSS apps in DetailsScreen
jksalcedo Mar 12, 2026
8bcc44d
feat: expand app categories and handle unset category state
jksalcedo Mar 12, 2026
2ad83a3
chore: externalize version configuration to version.properties
jksalcedo Mar 12, 2026
92791ee
feat: add sibling app suggestions for FOSS apps in DetailsScreen
jksalcedo Mar 12, 2026
6275945
feat: expand app categories and handle unset category state
jksalcedo Mar 12, 2026
d736152
chore: externalize version configuration to version.properties
jksalcedo Mar 12, 2026
4350b81
feat: add voting for app alternatives
jksalcedo Mar 12, 2026
201bdd7
feat: add sibling app suggestions for FOSS apps in DetailsScreen
jksalcedo Mar 12, 2026
1bcd1db
feat: expand app categories and handle unset category state
jksalcedo Mar 12, 2026
3bdf35f
chore: externalize version configuration to version.properties
jksalcedo Mar 12, 2026
7553204
feat: add voting for app alternatives
jksalcedo Mar 12, 2026
9a50b0d
feat(ui): update and expand app categories in SubmitScreen
jksalcedo Mar 12, 2026
ed95f2b
docs: add contributing section and reformat README content
jksalcedo Mar 12, 2026
70cb081
feat: add sibling app suggestions for FOSS apps in DetailsScreen
jksalcedo Mar 12, 2026
42fb602
feat: expand app categories and handle unset category state
jksalcedo Mar 12, 2026
26c757e
chore: externalize version configuration to version.properties
jksalcedo Mar 12, 2026
f1cce89
feat: add voting for app alternatives
jksalcedo Mar 12, 2026
e3321f1
feat(ui): update and expand app categories in SubmitScreen
jksalcedo Mar 12, 2026
508dff8
docs: add contributing section and reformat README content
jksalcedo Mar 12, 2026
8e4b2a5
feat(ui): replace Row with FlowRow in submission type selection
jksalcedo Mar 12, 2026
d5ec887
fix(ui): disable default window insets in Scaffolds
jksalcedo Mar 12, 2026
30e09e1
docs: update registered user count in README
jksalcedo Mar 12, 2026
47b2f8a
feat: add toggle to filter system and vendor packages
jksalcedo Mar 13, 2026
3fec691
feat: add sibling app suggestions for FOSS apps in DetailsScreen
jksalcedo Mar 12, 2026
8938323
feat: expand app categories and handle unset category state
jksalcedo Mar 12, 2026
f80b05a
chore: externalize version configuration to version.properties
jksalcedo Mar 12, 2026
a2030ae
feat: add voting for app alternatives
jksalcedo Mar 12, 2026
1fcc645
feat(ui): update and expand app categories in SubmitScreen
jksalcedo Mar 12, 2026
8d4b77b
docs: add contributing section and reformat README content
jksalcedo Mar 12, 2026
642e49d
feat(ui): replace Row with FlowRow in submission type selection
jksalcedo Mar 12, 2026
d2dc087
fix(ui): disable default window insets in Scaffolds
jksalcedo Mar 12, 2026
69be618
docs: update registered user count in README
jksalcedo Mar 12, 2026
25bc46f
feat: add toggle to filter system and vendor packages
jksalcedo Mar 13, 2026
1bda233
build: automate version code generation and update release changelog
jksalcedo Mar 14, 2026
bc35060
fix: update metadata asset paths in zapstore.yaml
jksalcedo Mar 14, 2026
870e8f2
docs: reformat Android changelog for version 1900300
jksalcedo Mar 14, 2026
5b57dc3
Merge remote-tracking branch 'origin/categories' into dev
jksalcedo Mar 14, 2026
ba7201d
Merge branch 'dev' into foss-app-siblings
jksalcedo Mar 14, 2026
e821acc
Merge branch 'dev' into new-voting-system
jksalcedo Mar 14, 2026
73a1dba
New voting system (#216)
jksalcedo Mar 14, 2026
2c0bca4
Foss app siblings (#212)
jksalcedo Mar 14, 2026
001bca2
Merge branch 'dev' into weblate-translations
jksalcedo Mar 14, 2026
6b8ea83
Weblate translations (Croatian) (#217)
jksalcedo Mar 14, 2026
b75a3de
Merge branch 'dev' into fix-UI-inconsistency-in-long-translated-texts
jksalcedo Mar 14, 2026
573ce43
Fix UI inconsistency in long translated texts (#220)
jksalcedo Mar 14, 2026
25eefdf
Update README with new badge information (#227)
jksalcedo Mar 14, 2026
b5ba03d
Merge branch 'dev' into feature/filter-system-apps
jksalcedo Mar 14, 2026
64dc8cc
filter system apps (#230)
jksalcedo Mar 14, 2026
3c79753
Merge branch 'dev' into fix/automate-versioning-properties
jksalcedo Mar 14, 2026
1e3514d
automate versioning properties (#232)
jksalcedo Mar 14, 2026
309db9d
Merge branch 'main' into dev
jksalcedo Mar 14, 2026
b197a76
chore: remove unused system package filtering strings
jksalcedo Mar 14, 2026
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
62 changes: 43 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,19 @@
<h4 align="center">Discover and replace proprietary apps with FOSS alternatives.</h4>

<h4 align=center>
<img alt="GitHub Release" src="https://img.shields.io/github/v/release/jksalcedo/librefind?include_prereleases&logo=github">
<img alt="GitHub Downloads (all assets, all releases)" src="https://img.shields.io/github/downloads/jksalcedo/librefind/total">
<img alt="GitHub repo size" src="https://img.shields.io/github/repo-size/jksalcedo/librefind">
<img alt="GitHub License" src="https://img.shields.io/github/license/jksalcedo/librefind?logo=MIT">
<a href="https://github.com/jksalcedo/librefind/actions/workflows/codeql.yml">
<img src="https://github.com/jksalcedo/librefind/actions/workflows/codeql.yml/badge.svg" alt="CodeQL Advanced">
</a> <img alt="GitHub Release Date" src="https://img.shields.io/github/release-date/jksalcedo/librefind">

<a href="https://developer.android.com">
<img src="https://img.shields.io/badge/Platform-Android-brighgtreen?logo=android" alt="Android Platform">
</a>
<img src="https://img.shields.io/badge/API-24%2B-3DDC84?logo=android&color=blue" alt="Minimum SDK">
<img alt="GitHub Release" src="https://img.shields.io/github/v/release/jksalcedo/librefind?include_prereleases&logo=github&label=Release&color=blue">
<img alt="GitHub Downloads (all assets, all releases)" src="https://img.shields.io/github/downloads/jksalcedo/librefind/total?label=APK%20Downloads&color=brightgreen">
<img alt="GitHub License" src="https://img.shields.io/github/license/jksalcedo/librefind?logo=MIT&label=License&color=blue">
<a href="https://github.com/jksalcedo/librefind/actions/workflows/codeql.yml">
<img src="https://github.com/jksalcedo/librefind/actions/workflows/codeql.yml/badge.svg" alt="CodeQL Advanced">
</a>
<img src="https://img.shields.io/github/last-commit/jksalcedo/librefind?color=blue" alt="Last Commit">
</h4>

<p align="center">
<a href="https://apt.izzysoft.de/packages/com.jksalcedo.librefind">
<img alt="Get it on IzzyOnDroid" src="https://gitlab.com/IzzyOnDroid/repo/-/raw/master/assets/IzzyOnDroid.png" width="160">
Expand All @@ -38,11 +42,11 @@
alt="Get it on F-Droid"
width="160">
</a>

<a href="https://github.com/jksalcedo/librefind/releases">
<img src="https://github.com/SilentCoderHere/aihub/blob/main/fastlane/metadata/android/en-US/images/badge_github.png" width="160" alt="Get it on GitHub">
</a>

<br>

<a href="https://apps.obtainium.imranr.dev/redirect?r=obtainium://add/https://github.com/jksalcedo/librefind">
Expand All @@ -56,7 +60,8 @@

## What is LibreFind?

LibreFind is a free and lightweight Android app that scans your installed packages locally and queries our database to identify proprietary software and find FOSS alternatives.
LibreFind is a free and lightweight Android app that scans your installed packages locally and
queries our database to identify proprietary software and find FOSS alternatives.

### Core Features

Expand All @@ -67,10 +72,10 @@ LibreFind is a free and lightweight Android app that scans your installed packag

## Screenshots

| Dashboard | Alternative List | Submission | Profile |
|--------------------------------------------------------------------|------------------------------------------------------------------------------|----------------------------------------------------------------------|----------------------------------------------------------------|
| Dashboard | Alternative List | Submission | Profile |
|-------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------|---------------------------------------------------------------------------------|
| ![Dashboard](fastlane/metadata/android/en-US/images/phoneScreenshots/dashboard.jpg) | ![Alternatives](fastlane/metadata/android/en-US/images/phoneScreenshots/alternative_list.jpg) | ![Submission](fastlane/metadata/android/en-US/images/phoneScreenshots/submission.jpg) | ![Profile](fastlane/metadata/android/en-US/images/phoneScreenshots/profile.jpg) |
| Scan your apps and view sovereignty score | Browse FOSS alternatives | Contribute new alternatives | Track your submissions |
| Scan your apps and view sovereignty score | Browse FOSS alternatives | Contribute new alternatives | Track your submissions |

### Community Contributions

Expand All @@ -94,11 +99,15 @@ Please join the Telegram channel for further discussions.

## ☕ Support

LibreFind is a free and open-source project managed independently. Our goal is to promote digital privacy by making FOSS apps mainstream. Currently, LibreFind is trusted by **1,334 registered users** globally.
LibreFind is a free and open-source project managed independently. Our goal is to promote digital
privacy by making FOSS apps mainstream. Currently, LibreFind is trusted by **1,334 registered users
** globally.

While the app is free, **infrastructure and hosting servers** cost money to keep running 24/7.

While the app is free, **infrastructure and hosting servers** cost money to keep running 24/7.
If you find this tool useful for reclaiming your digital privacy, please consider buying me a
coffee. Your support goes directly toward:

If you find this tool useful for reclaiming your digital privacy, please consider buying me a coffee. Your support goes directly toward:
* Paying monthly server bills.
* Keeping the database online and fast.
* Development of new features.
Expand All @@ -109,14 +118,29 @@ If you find this tool useful for reclaiming your digital privacy, please conside
</a>
</div>

<!-- SUGGESTION: List some ways to support the project without paying -->
## Contributing

Can't donate? You can still make a real difference:

- **Star the repo** — helps others discover LibreFind on GitHub
- **Report bugs** — open an issue if something isn't working
- **Suggest features** — share ideas through GitHub issues
- **Test & give feedback** — usability and performance reports are valuable
- **Translate** — help bring LibreFind to more languages
via [Weblate](https://hosted.weblate.org/engage/librefind/)
- **Spread the word** — share with friends or on social media
- **Contribute code** — fix bugs or add features via pull requests

Every small contribution helps the project grow.

## Translations

<a href="https://hosted.weblate.org/engage/librefind/">
<img src="https://hosted.weblate.org/widget/librefind/android-strings/multi-auto.svg" alt="Translation status" />
</a>

LibreFind uses [Weblate](https://hosted.weblate.org/engage/librefind/) to manage translations. You can help translate LibreFind into your language!
LibreFind uses [Weblate](https://hosted.weblate.org/engage/librefind/) to manage translations. You
can help translate LibreFind into your language!

## Star History

Expand Down
70 changes: 47 additions & 23 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
import com.android.build.api.artifact.SingleArtifact
import com.android.build.api.dsl.ApplicationExtension
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
import java.io.FileInputStream
import java.util.Properties

plugins {
alias(libs.plugins.android.application)
Expand All @@ -8,36 +12,56 @@ plugins {
alias(libs.plugins.kotlin.serialization)
}

val versionMajor = 1
val versionMinor = 0
val versionPatch = 0
val versionStage = "beta" // Change to "alpha", "beta", "rc", or "stable"
val versionBuild = 18
// Load the version.properties file
val versionPropsFile: File = rootProject.file("version.properties")
val versionProps = Properties()
if (versionPropsFile.exists()) {
versionProps.load(FileInputStream(versionPropsFile))
} else {
throw GradleException("version.properties file not found! Please create it in the project root.")
}

// Safely parse the values
val vMajor = versionProps.getProperty("VERSION_MAJOR", "0").toInt()
val vMinor = versionProps.getProperty("VERSION_MINOR", "1").toInt()
val vPatch = versionProps.getProperty("VERSION_PATCH", "0").toInt()
val vStage = versionProps.getProperty("VERSION_STAGE", "beta").lowercase()
val vBuild = versionProps.getProperty("VERSION_BUILD", "1").toInt()

val stageWeight = when (versionStage.lowercase()) {
val stageWeight = when (vStage) {
"alpha" -> 0
"beta" -> 1
"rc" -> 2
"stable" -> 3
else -> 0
}

val computedVersionCode = (versionMajor * 100000) +
(versionMinor * 1000) +
(versionPatch * 100) +
(stageWeight * 10) +
versionBuild
val suffix = if (vStage == "stable") "" else "-$vStage$vBuild"

// Construct the versionName dynamically
// If stable, just output "1.0.0"
// Otherwise, output "1.0.0-beta16" or "1.0.0-rc1"
val mVersionName =
"$versionMajor.$versionMinor.$versionPatch-$versionStage${
if (versionStage.lowercase() == "stable") null else versionBuild
}"
val computedVersionCode = versionProps.getProperty("VERSION_CODE")?.toInt() ?: ((vMajor * 10000000) +
(vMinor * 100000) +
(vPatch * 1000) +
(stageWeight * 100) +
vBuild)

val computedVersionName = versionProps.getProperty("VERSION_NAME") ?: "$vMajor.$vMinor.$vPatch$suffix"

configure<com.android.build.api.dsl.ApplicationExtension> {
tasks.register("updateVersionProperties") {
doLast {
val calculatedCode = (vMajor * 10000000) +
(vMinor * 100000) +
(vPatch * 1000) +
(stageWeight * 100) +
vBuild
val calculatedName = "$vMajor.$vMinor.$vPatch$suffix"

versionProps.setProperty("VERSION_CODE", calculatedCode.toString())
versionProps.setProperty("VERSION_NAME", calculatedName)
versionProps.store(versionPropsFile.outputStream(), null)
}
}

configure<ApplicationExtension> {
namespace = "com.jksalcedo.librefind"
compileSdk = 36

Expand All @@ -46,7 +70,7 @@ configure<com.android.build.api.dsl.ApplicationExtension> {
minSdk = 24
targetSdk = 36
versionCode = computedVersionCode
versionName = mVersionName
versionName = computedVersionName

testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}
Expand Down Expand Up @@ -104,11 +128,11 @@ kotlin {
androidComponents {
onVariants { variant ->
val appName = "librefind"
val versionName = mVersionName
val versionName = computedVersionName
val capitalizedName = variant.name.replaceFirstChar { it.titlecase() }

tasks.register<Copy>("rename${capitalizedName}Apk") {
from(variant.artifacts.get(com.android.build.api.artifact.SingleArtifact.APK))
from(variant.artifacts.get(SingleArtifact.APK))
into(layout.buildDirectory.dir("outputs/apk/${variant.name}"))
rename(".*\\.apk", "$appName-v$versionName-${variant.name}.apk")
}
Expand Down Expand Up @@ -176,4 +200,4 @@ dependencies {
androidTestImplementation(libs.androidx.compose.ui.test.junit4)
debugImplementation(libs.androidx.compose.ui.tooling)
debugImplementation(libs.androidx.compose.ui.test.manifest)
}
}
2 changes: 2 additions & 0 deletions app/src/main/java/com/jksalcedo/librefind/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Icon
import androidx.compose.material3.NavigationBar
Expand Down Expand Up @@ -46,6 +47,7 @@ class MainActivity : AppCompatActivity() {
currentRoute == Route.Dashboard.route || currentRoute == Route.Discover.route

Scaffold(
contentWindowInsets = WindowInsets(0),
bottomBar = {
if (showBottomBar) {
NavigationBar {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ import android.os.Build
* Wraps Android PackageManager API to extract installed packages.
*/
class InventorySource(
private val context: Context
private val context: Context,
private val preferencesManager: PreferencesManager
) {
/**
* Gets all user-installed apps
Expand All @@ -34,16 +35,18 @@ class InventorySource(
.map { it.activityInfo.packageName }
.toSet()

val hideSystemPackages = preferencesManager.shouldHideSystemPackages()

pm.getInstalledPackages(PackageManager.GET_META_DATA)
.filter { app ->
val isSystem = (app.applicationInfo?.flags?.and(ApplicationInfo.FLAG_SYSTEM) != 0)
val isUpdatedSystem = (app.applicationInfo?.flags?.and(ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0)
// Filter logic:
// 1. User apps (!isSystem)
// 2. Updated System Apps (isUpdatedSystem)
// 3. System apps that are launchable (launchablePackages.contains)
!isSystem || isUpdatedSystem || launchablePackages.contains(app.packageName)

if (hideSystemPackages) {
!isSystem || isUpdatedSystem
} else {
!isSystem || isUpdatedSystem || launchablePackages.contains(app.packageName)
}
}
} catch (_: Exception) {
emptyList()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,13 @@ package com.jksalcedo.librefind.data.local

import android.content.Context
import android.content.SharedPreferences
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.callbackFlow
import java.util.UUID

class PreferencesManager(private val context: Context) {
private val prefs: SharedPreferences =
private val prefs: SharedPreferences =
context.getSharedPreferences("librefind_prefs", Context.MODE_PRIVATE)

fun hasSeenOnboarding(): Boolean {
Expand Down Expand Up @@ -43,7 +46,7 @@ class PreferencesManager(private val context: Context) {
fun getOrCreateDeviceId(): String {
val existing = prefs.getString(KEY_DEVICE_ID, null)
if (existing != null) return existing

val newId = UUID.randomUUID().toString()
prefs.edit().putString(KEY_DEVICE_ID, newId).apply()
return newId
Expand All @@ -57,10 +60,43 @@ class PreferencesManager(private val context: Context) {
}
}

// --- System package filtering preferences ---
/**
* Returns whether system/vendor packages should be hidden from the UI.
*
* Default: true (on)
*/
fun shouldHideSystemPackages(): Boolean {
return prefs.getBoolean(KEY_HIDE_SYSTEM_PACKAGES, true)
}

/**
* Persist user preference for hiding system/vendor packages.
*/
fun setHideSystemPackages(enabled: Boolean) {
prefs.edit().putBoolean(KEY_HIDE_SYSTEM_PACKAGES, enabled).apply()
}

fun observeHideSystemPackages(): Flow<Boolean> = callbackFlow {
val listener = SharedPreferences.OnSharedPreferenceChangeListener { _, key ->
if (key == KEY_HIDE_SYSTEM_PACKAGES) {
trySend(shouldHideSystemPackages())
}
}
prefs.registerOnSharedPreferenceChangeListener(listener)
trySend(shouldHideSystemPackages())
awaitClose { prefs.unregisterOnSharedPreferenceChangeListener(listener) }
}



companion object {
private const val KEY_ONBOARDING_COMPLETE = "onboarding_complete"
private const val KEY_LAST_VERSION = "last_seen_version"
private const val KEY_TUTORIAL_COMPLETE = "tutorial_complete"
private const val KEY_DEVICE_ID = "device_id"

// New keys for system package filtering
private const val KEY_HIDE_SYSTEM_PACKAGES = "hide_system_packages"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ data class SolutionDto(
@SerialName("package_name") val packageName: String,
val name: String,
val description: String,
val category: String? = null,
val category: String = "Other",
@SerialName("icon_url") val iconUrl: String? = null,
@SerialName("fdroid_id") val fdroidId: String? = null,
@SerialName("repo_url") val repoUrl: String? = null,
Expand Down Expand Up @@ -103,4 +103,36 @@ data class AppReport(
@SerialName("issue_type") val issueType: String,
@SerialName("description") val description: String,
@SerialName("status") val status: String = "pending"
)

@Serializable
data class MatchVoteDto(
@SerialName("user_id") val userId: String,
@SerialName("target_package") val targetPackage: String,
@SerialName("solution_package") val solutionPackage: String,
val vote: Int // +1 or -1
)

/** Returned by the get_alternatives_with_match_votes RPC */
@Serializable
data class AlternativeWithVoteDto(
@SerialName("package_name") val packageName: String,
val name: String,
val license: String,
@SerialName("repo_url") val repoUrl: String? = null,
@SerialName("fdroid_id") val fdroidId: String? = null,
@SerialName("icon_url") val iconUrl: String? = null,
val description: String = "",
val category: String = "Other",
val features: List<String>? = emptyList(),
val pros: List<String>? = emptyList(),
val cons: List<String>? = emptyList(),
@SerialName("rating_privacy") val ratingPrivacy: Float? = 0f,
@SerialName("rating_usability") val ratingUsability: Float? = 0f,
@SerialName("rating_features") val ratingFeatures: Float? = 0f,
@SerialName("vote_count") val voteCount: Int? = 0,
@SerialName("match_upvotes") val matchUpvotes: Int = 0,
@SerialName("match_downvotes") val matchDownvotes: Int = 0,
@SerialName("match_score") val matchScore: Int = 0,
@SerialName("user_match_vote") val userMatchVote: Int? = null
)
Loading
Loading