mirror of
https://github.com/Michatec/michas-droid.git
synced 2026-05-30 18:02:43 +02:00
Properly handle multiple suggested versions
This commit is contained in:
@@ -78,7 +78,7 @@ object Database {
|
|||||||
const val ROW_ADDED = "added"
|
const val ROW_ADDED = "added"
|
||||||
const val ROW_UPDATED = "updated"
|
const val ROW_UPDATED = "updated"
|
||||||
const val ROW_VERSION_CODE = "version_code"
|
const val ROW_VERSION_CODE = "version_code"
|
||||||
const val ROW_SIGNATURE = "signature"
|
const val ROW_SIGNATURES = "signatures"
|
||||||
const val ROW_COMPATIBLE = "compatible"
|
const val ROW_COMPATIBLE = "compatible"
|
||||||
const val ROW_DATA = "data"
|
const val ROW_DATA = "data"
|
||||||
const val ROW_DATA_ITEM = "data_item"
|
const val ROW_DATA_ITEM = "data_item"
|
||||||
@@ -93,7 +93,7 @@ object Database {
|
|||||||
$ROW_ADDED INTEGER NOT NULL,
|
$ROW_ADDED INTEGER NOT NULL,
|
||||||
$ROW_UPDATED INTEGER NOT NULL,
|
$ROW_UPDATED INTEGER NOT NULL,
|
||||||
$ROW_VERSION_CODE INTEGER NOT NULL,
|
$ROW_VERSION_CODE INTEGER NOT NULL,
|
||||||
$ROW_SIGNATURE TEXT NOT NULL,
|
$ROW_SIGNATURES TEXT NOT NULL,
|
||||||
$ROW_COMPATIBLE INTEGER NOT NULL,
|
$ROW_COMPATIBLE INTEGER NOT NULL,
|
||||||
$ROW_DATA BLOB NOT NULL,
|
$ROW_DATA BLOB NOT NULL,
|
||||||
$ROW_DATA_ITEM BLOB NOT NULL,
|
$ROW_DATA_ITEM BLOB NOT NULL,
|
||||||
@@ -393,17 +393,20 @@ object Database {
|
|||||||
category: String, order: ProductItem.Order, signal: CancellationSignal?): Cursor {
|
category: String, order: ProductItem.Order, signal: CancellationSignal?): Cursor {
|
||||||
val builder = QueryBuilder()
|
val builder = QueryBuilder()
|
||||||
|
|
||||||
|
val signatureMatches = """installed.${Schema.Installed.ROW_SIGNATURE} IS NOT NULL AND
|
||||||
|
product.${Schema.Product.ROW_SIGNATURES} LIKE ('%.' || installed.${Schema.Installed.ROW_SIGNATURE} || '.%') AND
|
||||||
|
product.${Schema.Product.ROW_SIGNATURES} != ''"""
|
||||||
|
|
||||||
builder += """SELECT product.rowid AS _id, product.${Schema.Product.ROW_REPOSITORY_ID},
|
builder += """SELECT product.rowid AS _id, product.${Schema.Product.ROW_REPOSITORY_ID},
|
||||||
product.${Schema.Product.ROW_PACKAGE_NAME}, product.${Schema.Product.ROW_NAME},
|
product.${Schema.Product.ROW_PACKAGE_NAME}, product.${Schema.Product.ROW_NAME},
|
||||||
product.${Schema.Product.ROW_SUMMARY}, installed.${Schema.Installed.ROW_VERSION},
|
product.${Schema.Product.ROW_SUMMARY}, installed.${Schema.Installed.ROW_VERSION},
|
||||||
(COALESCE(lock.${Schema.Lock.ROW_VERSION_CODE}, -1) NOT IN (0, product.${Schema.Product.ROW_VERSION_CODE}) AND
|
(COALESCE(lock.${Schema.Lock.ROW_VERSION_CODE}, -1) NOT IN (0, product.${Schema.Product.ROW_VERSION_CODE}) AND
|
||||||
product.${Schema.Product.ROW_COMPATIBLE} != 0 AND product.${Schema.Product.ROW_VERSION_CODE} >
|
product.${Schema.Product.ROW_COMPATIBLE} != 0 AND product.${Schema.Product.ROW_VERSION_CODE} >
|
||||||
COALESCE(installed.${Schema.Installed.ROW_VERSION_CODE}, 0xffffffff) AND
|
COALESCE(installed.${Schema.Installed.ROW_VERSION_CODE}, 0xffffffff) AND $signatureMatches)
|
||||||
product.${Schema.Product.ROW_SIGNATURE} = installed.${Schema.Installed.ROW_SIGNATURE} AND
|
AS ${Schema.Synthetic.ROW_CAN_UPDATE}, product.${Schema.Product.ROW_COMPATIBLE},
|
||||||
product.${Schema.Product.ROW_SIGNATURE} != '') AS ${Schema.Synthetic.ROW_CAN_UPDATE},
|
product.${Schema.Product.ROW_DATA_ITEM}, MAX((product.${Schema.Product.ROW_COMPATIBLE} AND
|
||||||
product.${Schema.Product.ROW_COMPATIBLE}, product.${Schema.Product.ROW_DATA_ITEM},
|
(installed.${Schema.Installed.ROW_SIGNATURE} IS NULL OR $signatureMatches)) ||
|
||||||
MAX((product.${Schema.Product.ROW_COMPATIBLE} << 32) | product.${Schema.Product.ROW_VERSION_CODE})
|
PRINTF('%016X', product.${Schema.Product.ROW_VERSION_CODE})) FROM ${Schema.Product.name} AS product"""
|
||||||
FROM ${Schema.Product.name} AS product"""
|
|
||||||
|
|
||||||
builder += """JOIN ${Schema.Repository.name} AS repository
|
builder += """JOIN ${Schema.Repository.name} AS repository
|
||||||
ON product.${Schema.Product.ROW_REPOSITORY_ID} = repository.${Schema.Repository.ROW_ID}"""
|
ON product.${Schema.Product.ROW_REPOSITORY_ID} = repository.${Schema.Repository.ROW_ID}"""
|
||||||
@@ -574,6 +577,9 @@ object Database {
|
|||||||
db.beginTransaction()
|
db.beginTransaction()
|
||||||
try {
|
try {
|
||||||
for (product in products) {
|
for (product in products) {
|
||||||
|
// Format signatures like ".signature1.signature2." for easier select
|
||||||
|
val signatures = product.signatures.joinToString { ".$it" }
|
||||||
|
.let { if (it.isNotEmpty()) "$it." else "" }
|
||||||
db.insertOrReplace(true, Schema.Product.temporaryName, ContentValues().apply {
|
db.insertOrReplace(true, Schema.Product.temporaryName, ContentValues().apply {
|
||||||
put(Schema.Product.ROW_REPOSITORY_ID, product.repositoryId)
|
put(Schema.Product.ROW_REPOSITORY_ID, product.repositoryId)
|
||||||
put(Schema.Product.ROW_PACKAGE_NAME, product.packageName)
|
put(Schema.Product.ROW_PACKAGE_NAME, product.packageName)
|
||||||
@@ -582,7 +588,7 @@ object Database {
|
|||||||
put(Schema.Product.ROW_ADDED, product.added)
|
put(Schema.Product.ROW_ADDED, product.added)
|
||||||
put(Schema.Product.ROW_UPDATED, product.updated)
|
put(Schema.Product.ROW_UPDATED, product.updated)
|
||||||
put(Schema.Product.ROW_VERSION_CODE, product.versionCode)
|
put(Schema.Product.ROW_VERSION_CODE, product.versionCode)
|
||||||
put(Schema.Product.ROW_SIGNATURE, product.signature)
|
put(Schema.Product.ROW_SIGNATURES, signatures)
|
||||||
put(Schema.Product.ROW_COMPATIBLE, if (product.compatible) 1 else 0)
|
put(Schema.Product.ROW_COMPATIBLE, if (product.compatible) 1 else 0)
|
||||||
put(Schema.Product.ROW_DATA, jsonGenerate(product::serialize))
|
put(Schema.Product.ROW_DATA, jsonGenerate(product::serialize))
|
||||||
put(Schema.Product.ROW_DATA_ITEM, jsonGenerate(product.item()::serialize))
|
put(Schema.Product.ROW_DATA_ITEM, jsonGenerate(product.item()::serialize))
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import com.fasterxml.jackson.core.JsonGenerator
|
|||||||
import com.fasterxml.jackson.core.JsonParser
|
import com.fasterxml.jackson.core.JsonParser
|
||||||
import com.fasterxml.jackson.core.JsonToken
|
import com.fasterxml.jackson.core.JsonToken
|
||||||
import nya.kitsunyan.foxydroid.utility.extension.json.*
|
import nya.kitsunyan.foxydroid.utility.extension.json.*
|
||||||
|
import nya.kitsunyan.foxydroid.utility.extension.text.*
|
||||||
|
|
||||||
data class Product(val repositoryId: Long, val packageName: String, val name: String, val summary: String,
|
data class Product(val repositoryId: Long, val packageName: String, val name: String, val summary: String,
|
||||||
val description: String, val whatsNew: String, val icon: String, val author: Author,
|
val description: String, val whatsNew: String, val icon: String, val author: Author,
|
||||||
@@ -33,23 +34,24 @@ data class Product(val repositoryId: Long, val packageName: String, val name: St
|
|||||||
get() = "$locale.${type.name}.$path"
|
get() = "$locale.${type.name}.$path"
|
||||||
}
|
}
|
||||||
|
|
||||||
val selectedRelease: Release?
|
// Same releases with different signatures
|
||||||
get() = releases.find { it.selected }
|
val selectedReleases: List<Release>
|
||||||
|
get() = releases.filter { it.selected }
|
||||||
|
|
||||||
val displayRelease: Release?
|
val displayRelease: Release?
|
||||||
get() = selectedRelease ?: releases.firstOrNull()
|
get() = selectedReleases.firstOrNull() ?: releases.firstOrNull()
|
||||||
|
|
||||||
val version: String
|
val version: String
|
||||||
get() = displayRelease?.version.orEmpty()
|
get() = displayRelease?.version.orEmpty()
|
||||||
|
|
||||||
val versionCode: Long
|
val versionCode: Long
|
||||||
get() = selectedRelease?.versionCode ?: 0L
|
get() = selectedReleases.firstOrNull()?.versionCode ?: 0L
|
||||||
|
|
||||||
val compatible: Boolean
|
val compatible: Boolean
|
||||||
get() = selectedRelease?.incompatibilities?.isEmpty() == true
|
get() = selectedReleases.firstOrNull()?.incompatibilities?.isEmpty() == true
|
||||||
|
|
||||||
val signature: String
|
val signatures: List<String>
|
||||||
get() = selectedRelease?.signature.orEmpty()
|
get() = selectedReleases.mapNotNull { it.signature.nullIfEmpty() }.distinct().toList()
|
||||||
|
|
||||||
fun item(): ProductItem {
|
fun item(): ProductItem {
|
||||||
return ProductItem(repositoryId, packageName, name, summary, icon, version, "", compatible, false)
|
return ProductItem(repositoryId, packageName, name, summary, icon, version, "", compatible, false)
|
||||||
@@ -57,7 +59,7 @@ data class Product(val repositoryId: Long, val packageName: String, val name: St
|
|||||||
|
|
||||||
fun canUpdate(installedItem: InstalledItem?): Boolean {
|
fun canUpdate(installedItem: InstalledItem?): Boolean {
|
||||||
return installedItem != null && compatible && versionCode > installedItem.versionCode &&
|
return installedItem != null && compatible && versionCode > installedItem.versionCode &&
|
||||||
signature.isNotEmpty() && signature == installedItem.signature
|
installedItem.signature in signatures
|
||||||
}
|
}
|
||||||
|
|
||||||
fun serialize(generator: JsonGenerator) {
|
fun serialize(generator: JsonGenerator) {
|
||||||
@@ -126,8 +128,9 @@ data class Product(val repositoryId: Long, val packageName: String, val name: St
|
|||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun <T> findSuggested(products: List<T>, extract: (T) -> Product): T? {
|
fun <T> findSuggested(products: List<T>, installedItem: InstalledItem?, extract: (T) -> Product): T? {
|
||||||
return products.maxWith(compareBy({ extract(it).compatible }, { extract(it).versionCode }))
|
return products.maxWith(compareBy({ extract(it).compatible &&
|
||||||
|
(installedItem == null || installedItem.signature in extract(it).signatures) }, { extract(it).versionCode }))
|
||||||
}
|
}
|
||||||
|
|
||||||
fun deserialize(repositoryId: Long, parser: JsonParser): Product {
|
fun deserialize(repositoryId: Long, parser: JsonParser): Product {
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ data class Release(val selected: Boolean, val version: String, val versionCode:
|
|||||||
object MinSdk: Incompatibility()
|
object MinSdk: Incompatibility()
|
||||||
object MaxSdk: Incompatibility()
|
object MaxSdk: Incompatibility()
|
||||||
object Platform: Incompatibility()
|
object Platform: Incompatibility()
|
||||||
class Feature(val feature: String): Incompatibility()
|
data class Feature(val feature: String): Incompatibility()
|
||||||
}
|
}
|
||||||
|
|
||||||
val identifier: String
|
val identifier: String
|
||||||
|
|||||||
@@ -326,40 +326,20 @@ object RepositoryUpdater {
|
|||||||
if (it.platforms.isNotEmpty() && it.platforms.intersect(Android.platforms).isEmpty()) {
|
if (it.platforms.isNotEmpty() && it.platforms.intersect(Android.platforms).isEmpty()) {
|
||||||
incompatibilities += Release.Incompatibility.Platform
|
incompatibilities += Release.Incompatibility.Platform
|
||||||
}
|
}
|
||||||
incompatibilities += (it.features - features).map { Release.Incompatibility.Feature(it) }
|
incompatibilities += (it.features - features).sorted().map { Release.Incompatibility.Feature(it) }
|
||||||
Pair(it, incompatibilities as List<Release.Incompatibility>)
|
Pair(it, incompatibilities as List<Release.Incompatibility>)
|
||||||
}.toMutableList()
|
}.toMutableList()
|
||||||
|
|
||||||
val predicate: (Release) -> Boolean = { unstable || product.suggestedVersionCode <= 0 ||
|
val predicate: (Release) -> Boolean = { unstable || product.suggestedVersionCode <= 0 ||
|
||||||
it.versionCode <= product.suggestedVersionCode }
|
it.versionCode <= product.suggestedVersionCode }
|
||||||
val firstCompatibleReleaseIndex = releasePairs.indexOfFirst { it.second.isEmpty() && predicate(it.first) }
|
val firstCompatibleReleaseIndex = releasePairs.indexOfFirst { it.second.isEmpty() && predicate(it.first) }
|
||||||
val releaseIndex = if (firstCompatibleReleaseIndex >= 0) {
|
val firstReleaseIndex = if (firstCompatibleReleaseIndex >= 0) firstCompatibleReleaseIndex else
|
||||||
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) }
|
releasePairs.indexOfFirst { predicate(it.first) }
|
||||||
}
|
val firstSelected = if (firstReleaseIndex >= 0) releasePairs[firstReleaseIndex] else null
|
||||||
|
|
||||||
val releases = releasePairs.mapIndexed { index, (release, incompatibilities) -> release
|
val releases = releasePairs.map { (release, incompatibilities) -> release
|
||||||
.copy(incompatibilities = incompatibilities, selected = index == releaseIndex) }
|
.copy(incompatibilities = incompatibilities, selected = firstSelected
|
||||||
|
?.let { it.first.versionCode == release.versionCode && it.second == incompatibilities } == true) }
|
||||||
return product.copy(releases = releases)
|
return product.copy(releases = releases)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -249,7 +249,8 @@ class ProductAdapter(private val callbacks: Callbacks, private val columns: Int)
|
|||||||
get() = ViewType.SCREENSHOT
|
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,
|
||||||
|
val showSignature: Boolean): Item() {
|
||||||
override val descriptor: String
|
override val descriptor: String
|
||||||
get() = "release.${repository.id}.${release.identifier}"
|
get() = "release.${repository.id}.${release.identifier}"
|
||||||
|
|
||||||
@@ -459,10 +460,11 @@ class ProductAdapter(private val callbacks: Callbacks, private val columns: Int)
|
|||||||
val source = itemView.findViewById<TextView>(R.id.source)!!
|
val source = itemView.findViewById<TextView>(R.id.source)!!
|
||||||
val added = itemView.findViewById<TextView>(R.id.added)!!
|
val added = itemView.findViewById<TextView>(R.id.added)!!
|
||||||
val size = itemView.findViewById<TextView>(R.id.size)!!
|
val size = itemView.findViewById<TextView>(R.id.size)!!
|
||||||
|
val signature = itemView.findViewById<TextView>(R.id.signature)!!
|
||||||
val compatibility = itemView.findViewById<TextView>(R.id.compatibility)!!
|
val compatibility = itemView.findViewById<TextView>(R.id.compatibility)!!
|
||||||
|
|
||||||
val statefulViews: Sequence<View>
|
val statefulViews: Sequence<View>
|
||||||
get() = sequenceOf(itemView, version, status, source, added, size, compatibility)
|
get() = sequenceOf(itemView, version, status, source, added, size, signature, compatibility)
|
||||||
|
|
||||||
val setStatusActive: (Boolean) -> Unit
|
val setStatusActive: (Boolean) -> Unit
|
||||||
|
|
||||||
@@ -575,14 +577,23 @@ class ProductAdapter(private val callbacks: Callbacks, private val columns: Int)
|
|||||||
private val items = mutableListOf<Item>()
|
private val items = mutableListOf<Item>()
|
||||||
private val expanded = mutableSetOf<ExpandType>()
|
private val expanded = mutableSetOf<ExpandType>()
|
||||||
private var product: Product? = null
|
private var product: Product? = null
|
||||||
|
private var installedItem: InstalledItem? = null
|
||||||
|
|
||||||
fun setProducts(context: Context, products: List<Pair<Product, Repository>>, packageName: String) {
|
fun setProducts(context: Context, packageName: String,
|
||||||
val productRepository = Product.findSuggested(products) { it.first }
|
products: List<Pair<Product, Repository>>, installedItem: InstalledItem?) {
|
||||||
|
val productRepository = Product.findSuggested(products, installedItem) { it.first }
|
||||||
items.clear()
|
items.clear()
|
||||||
|
|
||||||
if (productRepository != null) {
|
if (productRepository != null) {
|
||||||
items += Item.HeaderItem(productRepository.second, productRepository.first)
|
items += Item.HeaderItem(productRepository.second, productRepository.first)
|
||||||
|
|
||||||
|
if (installedItem != null) {
|
||||||
|
items.add(Item.SwitchItem(SwitchType.IGNORE_ALL_UPDATES, packageName, productRepository.first.versionCode))
|
||||||
|
if (productRepository.first.canUpdate(installedItem)) {
|
||||||
|
items.add(Item.SwitchItem(SwitchType.IGNORE_THIS_UPDATE, packageName, productRepository.first.versionCode))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
val textViewHolder = TextViewHolder(context)
|
val textViewHolder = TextViewHolder(context)
|
||||||
val textViewWidthSpec = context.resources.displayMetrics.widthPixels
|
val textViewWidthSpec = context.resources.displayMetrics.widthPixels
|
||||||
.let { View.MeasureSpec.makeMeasureSpec(it, View.MeasureSpec.EXACTLY) }
|
.let { View.MeasureSpec.makeMeasureSpec(it, View.MeasureSpec.EXACTLY) }
|
||||||
@@ -739,7 +750,7 @@ class ProductAdapter(private val callbacks: Callbacks, private val columns: Int)
|
|||||||
}
|
}
|
||||||
|
|
||||||
val screenshotItems = productRepository.first.screenshots
|
val screenshotItems = productRepository.first.screenshots
|
||||||
.map { Item.ScreenshotItem(productRepository.second, productRepository.first.packageName, it) }
|
.map { Item.ScreenshotItem(productRepository.second, packageName, it) }
|
||||||
if (screenshotItems.isNotEmpty()) {
|
if (screenshotItems.isNotEmpty()) {
|
||||||
if (ExpandType.SCREENSHOTS in expanded) {
|
if (ExpandType.SCREENSHOTS in expanded) {
|
||||||
items += Item.SectionItem(SectionType.SCREENSHOTS, ExpandType.SCREENSHOTS, emptyList(), screenshotItems.size)
|
items += Item.SectionItem(SectionType.SCREENSHOTS, ExpandType.SCREENSHOTS, emptyList(), screenshotItems.size)
|
||||||
@@ -751,10 +762,19 @@ class ProductAdapter(private val callbacks: Callbacks, private val columns: Int)
|
|||||||
}
|
}
|
||||||
|
|
||||||
val incompatible = Preferences[Preferences.Key.IncompatibleVersions]
|
val incompatible = Preferences[Preferences.Key.IncompatibleVersions]
|
||||||
val releaseItems = products.asSequence()
|
val compatibleReleasePairs = products.asSequence()
|
||||||
.flatMap { (product, repository) -> product.releases.asSequence()
|
.flatMap { (product, repository) -> product.releases.asSequence()
|
||||||
.filter { incompatible || it.incompatibilities.isEmpty() }
|
.filter { incompatible || it.incompatibilities.isEmpty() }
|
||||||
.map { Item.ReleaseItem(repository, it, repository.id == productRepository?.second?.id) } }
|
.map { Pair(it, repository) } }
|
||||||
|
.toList()
|
||||||
|
val signaturesForVersionCode = compatibleReleasePairs.asSequence()
|
||||||
|
.mapNotNull { (release, _) -> if (release.signature.isEmpty()) null else
|
||||||
|
Pair(release.versionCode, release.signature) }
|
||||||
|
.distinct().groupBy { it.first }.toMap()
|
||||||
|
val releaseItems = compatibleReleasePairs.asSequence()
|
||||||
|
.map { (release, repository) -> Item.ReleaseItem(repository, release,
|
||||||
|
repository.id == productRepository?.second?.id,
|
||||||
|
signaturesForVersionCode.getValue(release.versionCode).size >= 2) }
|
||||||
.sortedByDescending { it.release.versionCode }
|
.sortedByDescending { it.release.versionCode }
|
||||||
.toList()
|
.toList()
|
||||||
if (releaseItems.isNotEmpty()) {
|
if (releaseItems.isNotEmpty()) {
|
||||||
@@ -772,17 +792,10 @@ class ProductAdapter(private val callbacks: Callbacks, private val columns: Int)
|
|||||||
items += Item.EmptyItem(packageName)
|
items += Item.EmptyItem(packageName)
|
||||||
}
|
}
|
||||||
this.product = productRepository?.first
|
this.product = productRepository?.first
|
||||||
updateSwitches()
|
this.installedItem = installedItem
|
||||||
notifyDataSetChanged()
|
notifyDataSetChanged()
|
||||||
}
|
}
|
||||||
|
|
||||||
var installedItem: InstalledItem? = null
|
|
||||||
set(value) {
|
|
||||||
field = value
|
|
||||||
updateSwitches()
|
|
||||||
notifyDataSetChanged()
|
|
||||||
}
|
|
||||||
|
|
||||||
private var action: Action? = null
|
private var action: Action? = null
|
||||||
|
|
||||||
fun setAction(action: Action?) {
|
fun setAction(action: Action?) {
|
||||||
@@ -823,19 +836,6 @@ class ProductAdapter(private val callbacks: Callbacks, private val columns: Int)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updateSwitches() {
|
|
||||||
val product = product
|
|
||||||
val installedItem = installedItem
|
|
||||||
items.removeAll { it is Item.SwitchItem }
|
|
||||||
val index = items.indexOfFirst { it is Item.HeaderItem }
|
|
||||||
if (index >= 0 && product != null && installedItem != null) {
|
|
||||||
items.add(index + 1, Item.SwitchItem(SwitchType.IGNORE_ALL_UPDATES, product.packageName, product.versionCode))
|
|
||||||
if (product.canUpdate(installedItem)) {
|
|
||||||
items.add(index + 2, Item.SwitchItem(SwitchType.IGNORE_THIS_UPDATE, product.packageName, product.versionCode))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override val viewTypeClass: Class<ViewType>
|
override val viewTypeClass: Class<ViewType>
|
||||||
get() = ViewType::class.java
|
get() = ViewType::class.java
|
||||||
|
|
||||||
@@ -1167,6 +1167,19 @@ class ProductAdapter(private val callbacks: Callbacks, private val columns: Int)
|
|||||||
holder.added.text = holder.dateFormat.format(item.release.added)
|
holder.added.text = holder.dateFormat.format(item.release.added)
|
||||||
holder.added.setTextColor(primarySecondaryColor)
|
holder.added.setTextColor(primarySecondaryColor)
|
||||||
holder.size.text = item.release.size.formatSize()
|
holder.size.text = item.release.size.formatSize()
|
||||||
|
holder.signature.visibility = if (item.showSignature && item.release.signature.isNotEmpty())
|
||||||
|
View.VISIBLE else View.GONE
|
||||||
|
if (item.showSignature && item.release.signature.isNotEmpty()) {
|
||||||
|
val bytes = item.release.signature.toUpperCase(Locale.US).windowed(2, 2, false).take(8)
|
||||||
|
val signature = bytes.joinToString(separator = " ")
|
||||||
|
val builder = SpannableStringBuilder(context.getString(R.string.signature_FORMAT, signature))
|
||||||
|
val index = builder.indexOf(signature)
|
||||||
|
if (index >= 0) {
|
||||||
|
bytes.forEachIndexed { i, _ -> builder.setSpan(TypefaceSpan("monospace"),
|
||||||
|
index + 3 * i, index + 3 * i + 2, SpannableStringBuilder.SPAN_EXCLUSIVE_EXCLUSIVE) }
|
||||||
|
}
|
||||||
|
holder.signature.text = builder
|
||||||
|
}
|
||||||
holder.compatibility.visibility = if (incompatibility != null || singlePlatform != null)
|
holder.compatibility.visibility = if (incompatibility != null || singlePlatform != null)
|
||||||
View.VISIBLE else View.GONE
|
View.VISIBLE else View.GONE
|
||||||
if (incompatibility != null) {
|
if (incompatibility != null) {
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ import nya.kitsunyan.foxydroid.service.Connection
|
|||||||
import nya.kitsunyan.foxydroid.service.DownloadService
|
import nya.kitsunyan.foxydroid.service.DownloadService
|
||||||
import nya.kitsunyan.foxydroid.utility.RxUtils
|
import nya.kitsunyan.foxydroid.utility.RxUtils
|
||||||
import nya.kitsunyan.foxydroid.utility.Utils
|
import nya.kitsunyan.foxydroid.utility.Utils
|
||||||
|
import nya.kitsunyan.foxydroid.utility.extension.android.*
|
||||||
import nya.kitsunyan.foxydroid.widget.DividerItemDecoration
|
import nya.kitsunyan.foxydroid.widget.DividerItemDecoration
|
||||||
|
|
||||||
class ProductFragment(): ScreenFragment(), ProductAdapter.Callbacks {
|
class ProductFragment(): ScreenFragment(), ProductAdapter.Callbacks {
|
||||||
@@ -191,11 +192,8 @@ class ProductFragment(): ScreenFragment(), ProductAdapter.Callbacks {
|
|||||||
}
|
}
|
||||||
val recyclerView = recyclerView!!
|
val recyclerView = recyclerView!!
|
||||||
val adapter = recyclerView.adapter as ProductAdapter
|
val adapter = recyclerView.adapter as ProductAdapter
|
||||||
if (firstChanged || productChanged) {
|
if (firstChanged || productChanged || installedItemChanged) {
|
||||||
adapter.setProducts(recyclerView.context, products, packageName)
|
adapter.setProducts(recyclerView.context, packageName, products, installedItem.value)
|
||||||
}
|
|
||||||
if (installedItemChanged) {
|
|
||||||
adapter.installedItem = installedItem.value
|
|
||||||
}
|
}
|
||||||
updateButtons()
|
updateButtons()
|
||||||
}
|
}
|
||||||
@@ -231,9 +229,10 @@ class ProductFragment(): ScreenFragment(), ProductAdapter.Callbacks {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun updateButtons(preference: ProductPreference) {
|
private fun updateButtons(preference: ProductPreference) {
|
||||||
val product = Product.findSuggested(products) { it.first }?.first
|
|
||||||
val installed = installed
|
val installed = installed
|
||||||
val compatible = product != null && product.selectedRelease.let { it != null && it.incompatibilities.isEmpty() }
|
val product = Product.findSuggested(products, installed?.installedItem) { it.first }?.first
|
||||||
|
val compatible = product != null && product.selectedReleases.firstOrNull()
|
||||||
|
.let { it != null && it.incompatibilities.isEmpty() }
|
||||||
val canInstall = product != null && installed == null && compatible
|
val canInstall = product != null && installed == null && compatible
|
||||||
val canUpdate = product != null && compatible && product.canUpdate(installed?.installedItem) &&
|
val canUpdate = product != null && compatible && product.canUpdate(installed?.installedItem) &&
|
||||||
!preference.shouldIgnoreUpdate(product.versionCode)
|
!preference.shouldIgnoreUpdate(product.versionCode)
|
||||||
@@ -332,10 +331,21 @@ class ProductFragment(): ScreenFragment(), ProductAdapter.Callbacks {
|
|||||||
when (action) {
|
when (action) {
|
||||||
ProductAdapter.Action.INSTALL,
|
ProductAdapter.Action.INSTALL,
|
||||||
ProductAdapter.Action.UPDATE -> {
|
ProductAdapter.Action.UPDATE -> {
|
||||||
val productRepository = Product.findSuggested(products) { it.first }
|
val installedItem = installed?.installedItem
|
||||||
val release = productRepository?.first?.selectedRelease
|
val productRepository = Product.findSuggested(products, installedItem) { it.first }
|
||||||
|
val compatibleReleases = productRepository?.first?.selectedReleases.orEmpty()
|
||||||
|
.filter { installedItem == null || installedItem.signature == it.signature }
|
||||||
|
val release = if (compatibleReleases.size >= 2) {
|
||||||
|
compatibleReleases
|
||||||
|
.filter { it.platforms.contains(Android.primaryPlatform) }
|
||||||
|
.minBy { it.platforms.size }
|
||||||
|
?: compatibleReleases.minBy { it.platforms.size }
|
||||||
|
?: compatibleReleases.firstOrNull()
|
||||||
|
} else {
|
||||||
|
compatibleReleases.firstOrNull()
|
||||||
|
}
|
||||||
val binder = downloadConnection.binder
|
val binder = downloadConnection.binder
|
||||||
if (release != null && binder != null) {
|
if (productRepository != null && release != null && binder != null) {
|
||||||
binder.enqueue(packageName, productRepository.first.name, productRepository.second, release)
|
binder.enqueue(packageName, productRepository.first.name, productRepository.second, release)
|
||||||
}
|
}
|
||||||
Unit
|
Unit
|
||||||
|
|||||||
@@ -95,6 +95,14 @@
|
|||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/signature"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textColor="?android:attr/textColorSecondary"
|
||||||
|
android:textSize="14sp"
|
||||||
|
android:maxLines="1" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/compatibility"
|
android:id="@+id/compatibility"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
|||||||
@@ -129,6 +129,7 @@
|
|||||||
<string name="select_mirror">Select a mirror</string>
|
<string name="select_mirror">Select a mirror</string>
|
||||||
<string name="show_more">Show more</string>
|
<string name="show_more">Show more</string>
|
||||||
<string name="show_older_versions">Show older versions</string>
|
<string name="show_older_versions">Show older versions</string>
|
||||||
|
<string name="signature_FORMAT">Signature %s</string>
|
||||||
<string name="signed_using_unsafe_algorithm">Signed using an unsafe algorithm</string>
|
<string name="signed_using_unsafe_algorithm">Signed using an unsafe algorithm</string>
|
||||||
<string name="skip">Skip</string>
|
<string name="skip">Skip</string>
|
||||||
<string name="socks_proxy">SOCKS proxy</string>
|
<string name="socks_proxy">SOCKS proxy</string>
|
||||||
|
|||||||
Reference in New Issue
Block a user