From bb50d77547fe697fd024786f6f59ae2453a94f7d Mon Sep 17 00:00:00 2001 From: kitsunyan Date: Thu, 11 Jun 2020 06:49:09 +0300 Subject: [PATCH] Display compatibility information --- .../kitsunyan/foxydroid/index/IndexHandler.kt | 12 +++--- .../foxydroid/index/IndexV1Parser.kt | 18 ++++----- .../foxydroid/index/RepositoryUpdater.kt | 26 ++++++++++++- .../foxydroid/screen/ProductAdapter.kt | 39 +++++++++++-------- .../foxydroid/utility/extension/Json.kt | 4 ++ src/main/res/layout/release_item.xml | 4 +- src/main/res/values/strings.xml | 3 +- 7 files changed, 70 insertions(+), 36 deletions(-) diff --git a/src/main/kotlin/nya/kitsunyan/foxydroid/index/IndexHandler.kt b/src/main/kotlin/nya/kitsunyan/foxydroid/index/IndexHandler.kt index 13aa518..eb8b9e0 100644 --- a/src/main/kotlin/nya/kitsunyan/foxydroid/index/IndexHandler.kt +++ b/src/main/kotlin/nya/kitsunyan/foxydroid/index/IndexHandler.kt @@ -218,9 +218,9 @@ class IndexHandler(private val repositoryId: Long, private val callback: Callbac "obbMainFileSha256" -> releaseBuilder.obbMainHash = content "obbPatchFile" -> releaseBuilder.obbPatch = content "obbPatchFileSha256" -> releaseBuilder.obbPatchHash = content - "permissions" -> releaseBuilder.permissions += content.split(',') - "features" -> releaseBuilder.features += content.split(',') - "nativecode" -> releaseBuilder.platforms += content.split(',') + "permissions" -> releaseBuilder.permissions += content.split(',').filter { it.isNotEmpty() } + "features" -> releaseBuilder.features += content.split(',').filter { it.isNotEmpty() } + "nativecode" -> releaseBuilder.platforms += content.split(',').filter { it.isNotEmpty() } } } productBuilder != null -> { @@ -239,9 +239,9 @@ class IndexHandler(private val repositoryId: Long, private val callback: Callbac "added" -> productBuilder.added = content.parseDate() "lastupdated" -> productBuilder.updated = content.parseDate() "marketvercode" -> productBuilder.suggestedVersionCode = content.toLongOrNull() ?: 0L - "categories" -> productBuilder.categories += content.split(',') - "antifeatures" -> productBuilder.antiFeatures += content.split(',') - "license" -> productBuilder.licenses += content.split(',') + "categories" -> productBuilder.categories += content.split(',').filter { it.isNotEmpty() } + "antifeatures" -> productBuilder.antiFeatures += content.split(',').filter { it.isNotEmpty() } + "license" -> productBuilder.licenses += content.split(',').filter { it.isNotEmpty() } "donate" -> productBuilder.donates += Product.Donate.Regular(content) "bitcoin" -> productBuilder.donates += Product.Donate.Bitcoin(content) "litecoin" -> productBuilder.donates += Product.Donate.Litecoin(content) diff --git a/src/main/kotlin/nya/kitsunyan/foxydroid/index/IndexV1Parser.kt b/src/main/kotlin/nya/kitsunyan/foxydroid/index/IndexV1Parser.kt index 2fb1169..2fdb656 100644 --- a/src/main/kotlin/nya/kitsunyan/foxydroid/index/IndexV1Parser.kt +++ b/src/main/kotlin/nya/kitsunyan/foxydroid/index/IndexV1Parser.kt @@ -49,7 +49,7 @@ object IndexV1Parser { forEachKey { when { it.string("address") -> address = valueAsString - it.array("mirrors") -> mirrors = collectNotNullStrings() + it.array("mirrors") -> mirrors = collectDistinctNotEmptyStrings() it.string("name") -> name = valueAsString it.string("description") -> description = valueAsString it.number("version") -> version = valueAsInt @@ -117,9 +117,9 @@ object IndexV1Parser { it.number("added") -> added = valueAsLong it.number("lastUpdated") -> updated = valueAsLong it.string("suggestedVersionCode") -> suggestedVersionCode = valueAsString.toLongOrNull() ?: 0L - it.array("categories") -> categories = collectNotNullStrings() - it.array("antiFeatures") -> antiFeatures = collectNotNullStrings() - it.string("license") -> licenses += valueAsString.split(',') + it.array("categories") -> categories = collectDistinctNotEmptyStrings() + it.array("antiFeatures") -> antiFeatures = collectDistinctNotEmptyStrings() + it.string("license") -> licenses += valueAsString.split(',').filter { it.isNotEmpty() } it.string("donate") -> donates += Product.Donate.Regular(valueAsString) it.string("bitcoin") -> donates += Product.Donate.Bitcoin(valueAsString) it.string("flattrID") -> donates += Product.Donate.Flattr(valueAsString) @@ -140,9 +140,9 @@ object IndexV1Parser { it.string("summary") -> summary = valueAsString it.string("description") -> description = valueAsString it.string("whatsNew") -> whatsNew = valueAsString - it.array("phoneScreenshots") -> phone = collectNotNullStrings() - it.array("sevenInchScreenshots") -> smallTablet = collectNotNullStrings() - it.array("tenInchScreenshots") -> largeTablet = collectNotNullStrings() + it.array("phoneScreenshots") -> phone = collectDistinctNotEmptyStrings() + it.array("sevenInchScreenshots") -> smallTablet = collectDistinctNotEmptyStrings() + it.array("tenInchScreenshots") -> largeTablet = collectDistinctNotEmptyStrings() else -> skipChildren() } } @@ -215,8 +215,8 @@ object IndexV1Parser { it.string("obbPatchFileSha256") -> obbPatchHash = valueAsString it.array("uses-permission") -> collectPermissions(permissions, 0) it.array("uses-permission-sdk-23") -> collectPermissions(permissions, 23) - it.array("features") -> features = collectNotNullStrings() - it.array("nativecode") -> platforms = collectNotNullStrings() + it.array("features") -> features = collectDistinctNotEmptyStrings() + it.array("nativecode") -> platforms = collectDistinctNotEmptyStrings() else -> skipChildren() } } diff --git a/src/main/kotlin/nya/kitsunyan/foxydroid/index/RepositoryUpdater.kt b/src/main/kotlin/nya/kitsunyan/foxydroid/index/RepositoryUpdater.kt index 06f59dd..4400061 100644 --- a/src/main/kotlin/nya/kitsunyan/foxydroid/index/RepositoryUpdater.kt +++ b/src/main/kotlin/nya/kitsunyan/foxydroid/index/RepositoryUpdater.kt @@ -332,9 +332,31 @@ object RepositoryUpdater { val predicate: (Release) -> Boolean = { unstable || product.suggestedVersionCode <= 0 || it.versionCode <= product.suggestedVersionCode } - val compatibleReleaseIndex = releasePairs.indexOfFirst { it.second.isEmpty() && predicate(it.first) } - val releaseIndex = if (compatibleReleaseIndex >= 0) compatibleReleaseIndex else + val firstCompatibleReleaseIndex = releasePairs.indexOfFirst { it.second.isEmpty() && predicate(it.first) } + val releaseIndex = if (firstCompatibleReleaseIndex >= 0) { + val versionCode = releasePairs[firstCompatibleReleaseIndex].first.versionCode + val candidates = releasePairs.mapIndexedNotNull { index, (product, incompatibilities) -> + if (product.versionCode == versionCode && incompatibilities.isEmpty()) index else null + } + if (product.packageName == "org.telegram.messenger") { + val x = candidates + .filter { releasePairs[it].first.platforms.contains(Android.primaryPlatform) } + .minBy { releasePairs[it].first.platforms.size } + val y = candidates.minBy { releasePairs[it].first.platforms.size } + debug("telegram $firstCompatibleReleaseIndex $candidates $x $y") + } + if (candidates.size <= 1) { + firstCompatibleReleaseIndex + } else { + candidates + .filter { releasePairs[it].first.platforms.contains(Android.primaryPlatform) } + .minBy { releasePairs[it].first.platforms.size } + ?: candidates.minBy { releasePairs[it].first.platforms.size } + ?: firstCompatibleReleaseIndex + } + } else { releasePairs.indexOfFirst { predicate(it.first) } + } val releases = releasePairs.mapIndexed { index, (release, incompatibilities) -> release .copy(incompatibilities = incompatibilities, selected = index == releaseIndex) } diff --git a/src/main/kotlin/nya/kitsunyan/foxydroid/screen/ProductAdapter.kt b/src/main/kotlin/nya/kitsunyan/foxydroid/screen/ProductAdapter.kt index ca0190b..bb56d8c 100644 --- a/src/main/kotlin/nya/kitsunyan/foxydroid/screen/ProductAdapter.kt +++ b/src/main/kotlin/nya/kitsunyan/foxydroid/screen/ProductAdapter.kt @@ -250,8 +250,7 @@ class ProductAdapter(private val callbacks: Callbacks, private val columns: Int) get() = ViewType.SCREENSHOT } - class ReleaseItem(val repository: Repository, val release: Release, - val selectedRepository: Boolean): Item() { + class ReleaseItem(val repository: Repository, val release: Release, val selectedRepository: Boolean): Item() { override val descriptor: String get() = "release.${repository.id}.${release.identifier}" @@ -456,10 +455,10 @@ class ProductAdapter(private val callbacks: Callbacks, private val columns: Int) val source = itemView.findViewById(R.id.source)!! val added = itemView.findViewById(R.id.added)!! val size = itemView.findViewById(R.id.size)!! - val error = itemView.findViewById(R.id.error)!! + val compatibility = itemView.findViewById(R.id.compatibility)!! val statefulViews: Sequence - get() = sequenceOf(itemView, version, status, source, added, size, error) + get() = sequenceOf(itemView, version, status, source, added, size, compatibility) val setStatusActive: (Boolean) -> Unit @@ -748,18 +747,20 @@ class ProductAdapter(private val callbacks: Callbacks, private val columns: Int) } val incompatible = Preferences[Preferences.Key.IncompatibleVersions] - val releases = products.flatMap { (product, repository) -> product.releases - .map { Item.ReleaseItem(repository, it, repository.id == productRepository?.second?.id) } } - .filter { incompatible || it.release.incompatibilities.isEmpty() } + val releaseItems = products.asSequence() + .flatMap { (product, repository) -> product.releases.asSequence() + .filter { incompatible || it.incompatibilities.isEmpty() } + .map { Item.ReleaseItem(repository, it, repository.id == productRepository?.second?.id) } } .sortedByDescending { it.release.versionCode } - if (releases.isNotEmpty()) { + .toList() + if (releaseItems.isNotEmpty()) { items += Item.SectionItem(SectionType.RELEASES) val maxReleases = 5 - if (releases.size > maxReleases && ExpandType.RELEASES !in expanded) { - items += releases.take(maxReleases) - items += Item.ExpandItem(ExpandType.RELEASES, false, releases.takeLast(releases.size - maxReleases)) + if (releaseItems.size > maxReleases && ExpandType.RELEASES !in expanded) { + items += releaseItems.take(maxReleases) + items += Item.ExpandItem(ExpandType.RELEASES, false, releaseItems.takeLast(releaseItems.size - maxReleases)) } else { - items += releases + items += releaseItems } } @@ -1129,6 +1130,7 @@ class ProductAdapter(private val callbacks: Callbacks, private val columns: Int) holder as ReleaseViewHolder item as Item.ReleaseItem val incompatibility = item.release.incompatibilities.firstOrNull() + val singlePlatform = if (item.release.platforms.size == 1) item.release.platforms.first() else null val installed = installedItem?.versionCode == item.release.versionCode && installedItem?.signature == item.release.signature val suggested = incompatibility == null && item.release.selected && item.selectedRepository @@ -1150,15 +1152,20 @@ class ProductAdapter(private val callbacks: Callbacks, private val columns: Int) holder.added.text = holder.dateFormat.format(item.release.added) holder.added.setTextColor(primarySecondaryColor) holder.size.text = item.release.size.formatSize() - holder.error.visibility = if (incompatibility != null) View.VISIBLE else View.GONE + holder.compatibility.visibility = if (incompatibility != null || singlePlatform != null) + View.VISIBLE else View.GONE if (incompatibility != null) { - holder.error.text = when (incompatibility) { + holder.compatibility.setTextColor(context.getColorFromAttr(R.attr.colorError)) + holder.compatibility.text = when (incompatibility) { is Release.Incompatibility.MinSdk, - is Release.Incompatibility.MaxSdk -> context.getString(R.string.is_not_supported_format, Android.name) - is Release.Incompatibility.Platform -> context.getString(R.string.is_not_supported_format, + is Release.Incompatibility.MaxSdk -> context.getString(R.string.incompatible_with_format, Android.name) + is Release.Incompatibility.Platform -> context.getString(R.string.incompatible_with_format, Android.primaryPlatform ?: context.getString(R.string.unknown)) is Release.Incompatibility.Feature -> context.getString(R.string.requires_format, incompatibility.feature) } + } else if (singlePlatform != null) { + holder.compatibility.setTextColor(context.getColorFromAttr(android.R.attr.textColorSecondary)) + holder.compatibility.text = context.getString(R.string.compatible_with_only_format, singlePlatform) } val enabled = status == null holder.statefulViews.forEach { it.isEnabled = enabled } diff --git a/src/main/kotlin/nya/kitsunyan/foxydroid/utility/extension/Json.kt b/src/main/kotlin/nya/kitsunyan/foxydroid/utility/extension/Json.kt index 2441f84..1123d27 100644 --- a/src/main/kotlin/nya/kitsunyan/foxydroid/utility/extension/Json.kt +++ b/src/main/kotlin/nya/kitsunyan/foxydroid/utility/extension/Json.kt @@ -77,6 +77,10 @@ fun JsonParser.collectNotNullStrings(): List { return collectNotNull(JsonToken.VALUE_STRING) { valueAsString } } +fun JsonParser.collectDistinctNotEmptyStrings(): List { + return collectNotNullStrings().asSequence().filter { it.isNotEmpty() }.distinct().toList() +} + inline fun JsonParser.parseDictionary(callback: JsonParser.() -> T): T { if (nextToken() == JsonToken.START_OBJECT) { val result = callback() diff --git a/src/main/res/layout/release_item.xml b/src/main/res/layout/release_item.xml index fafb137..b7e9923 100644 --- a/src/main/res/layout/release_item.xml +++ b/src/main/res/layout/release_item.xml @@ -96,10 +96,10 @@ diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml index a0dfd93..8778c87 100644 --- a/src/main/res/values/strings.xml +++ b/src/main/res/values/strings.xml @@ -31,6 +31,7 @@ Cannot edit repository since it\'s syncing right now. Changelog Checking repository + Compatible with %s only Confirm action Connecting Dark @@ -65,13 +66,13 @@ Incompatible version Incompatible versions Show application versions incompatible with the device + Incompatible with %s Install Installed No applications installed Invalid address Invalid fingerprint format Invalid username format - %s is not supported Last update Launch License