mirror of
https://github.com/Michatec/michas-droid.git
synced 2026-05-30 18:02:43 +02:00
Add support for metadata icons as fallback
This commit is contained in:
@@ -7,7 +7,7 @@ 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,
|
||||
val description: String, val whatsNew: String, val icon: String, val author: Author,
|
||||
val description: String, val whatsNew: String, val icon: String, val metadataIcon: String, val author: Author,
|
||||
val source: String, val changelog: String, val web: String, val tracker: String,
|
||||
val added: Long, val updated: Long, val suggestedVersionCode: Long,
|
||||
val categories: List<String>, val antiFeatures: List<String>, val licenses: List<String>,
|
||||
@@ -54,7 +54,7 @@ data class Product(val repositoryId: Long, val packageName: String, val name: St
|
||||
get() = selectedReleases.mapNotNull { it.signature.nullIfEmpty() }.distinct().toList()
|
||||
|
||||
fun item(): ProductItem {
|
||||
return ProductItem(repositoryId, packageName, name, summary, icon, version, "", compatible, false, 0)
|
||||
return ProductItem(repositoryId, packageName, name, summary, icon, metadataIcon, version, "", compatible, false, 0)
|
||||
}
|
||||
|
||||
fun canUpdate(installedItem: InstalledItem?): Boolean {
|
||||
@@ -69,6 +69,7 @@ data class Product(val repositoryId: Long, val packageName: String, val name: St
|
||||
generator.writeStringField("summary", summary)
|
||||
generator.writeStringField("whatsNew", whatsNew)
|
||||
generator.writeStringField("icon", icon)
|
||||
generator.writeStringField("metadataIcon", metadataIcon)
|
||||
generator.writeStringField("authorName", author.name)
|
||||
generator.writeStringField("authorEmail", author.email)
|
||||
generator.writeStringField("authorWeb", author.web)
|
||||
@@ -138,6 +139,7 @@ data class Product(val repositoryId: Long, val packageName: String, val name: St
|
||||
var summary = ""
|
||||
var whatsNew = ""
|
||||
var icon = ""
|
||||
var metadataIcon = ""
|
||||
var authorName = ""
|
||||
var authorEmail = ""
|
||||
var authorWeb = ""
|
||||
@@ -161,6 +163,7 @@ data class Product(val repositoryId: Long, val packageName: String, val name: St
|
||||
it.string("summary") -> summary = valueAsString
|
||||
it.string("whatsNew") -> whatsNew = valueAsString
|
||||
it.string("icon") -> icon = valueAsString
|
||||
it.string("metadataIcon") -> metadataIcon = valueAsString
|
||||
it.string("authorName") -> authorName = valueAsString
|
||||
it.string("authorEmail") -> authorEmail = valueAsString
|
||||
it.string("authorWeb") -> authorWeb = valueAsString
|
||||
@@ -216,7 +219,7 @@ data class Product(val repositoryId: Long, val packageName: String, val name: St
|
||||
else -> skipChildren()
|
||||
}
|
||||
}
|
||||
return Product(repositoryId, packageName, name, summary, description, whatsNew, icon,
|
||||
return Product(repositoryId, packageName, name, summary, description, whatsNew, icon, metadataIcon,
|
||||
Author(authorName, authorEmail, authorWeb), source, changelog, web, tracker, added, updated,
|
||||
suggestedVersionCode, categories, antiFeatures, licenses, donates, screenshots, releases)
|
||||
}
|
||||
|
||||
@@ -7,8 +7,8 @@ import nya.kitsunyan.foxydroid.R
|
||||
import nya.kitsunyan.foxydroid.utility.KParcelable
|
||||
import nya.kitsunyan.foxydroid.utility.extension.json.*
|
||||
|
||||
data class ProductItem(val repositoryId: Long, val packageName: String,
|
||||
val name: String, val summary: String, val icon: String, val version: String, val installedVersion: String,
|
||||
data class ProductItem(val repositoryId: Long, val packageName: String, val name: String, val summary: String,
|
||||
val icon: String, val metadataIcon: String, val version: String, val installedVersion: String,
|
||||
val compatible: Boolean, val canUpdate: Boolean, val matchRank: Int) {
|
||||
sealed class Section: KParcelable {
|
||||
object All: Section() {
|
||||
@@ -53,6 +53,7 @@ data class ProductItem(val repositoryId: Long, val packageName: String,
|
||||
fun serialize(generator: JsonGenerator) {
|
||||
generator.writeNumberField("serialVersion", 1)
|
||||
generator.writeStringField("icon", icon)
|
||||
generator.writeStringField("metadataIcon", metadataIcon)
|
||||
generator.writeStringField("version", version)
|
||||
}
|
||||
|
||||
@@ -61,15 +62,17 @@ data class ProductItem(val repositoryId: Long, val packageName: String,
|
||||
installedVersion: String, compatible: Boolean, canUpdate: Boolean, matchRank: Int,
|
||||
parser: JsonParser): ProductItem {
|
||||
var icon = ""
|
||||
var metadataIcon = ""
|
||||
var version = ""
|
||||
parser.forEachKey {
|
||||
when {
|
||||
it.string("icon") -> icon = valueAsString
|
||||
it.string("metadataIcon") -> metadataIcon = valueAsString
|
||||
it.string("version") -> version = valueAsString
|
||||
else -> skipChildren()
|
||||
}
|
||||
}
|
||||
return ProductItem(repositoryId, packageName, name, summary, icon,
|
||||
return ProductItem(repositoryId, packageName, name, summary, icon, metadataIcon,
|
||||
version, installedVersion, compatible, canUpdate, matchRank)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,10 @@ class IndexHandler(private val repositoryId: Long, private val callback: Callbac
|
||||
0L
|
||||
}
|
||||
}
|
||||
|
||||
internal fun validateIcon(icon: String): String {
|
||||
return if (icon.endsWith(".xml")) "" else icon
|
||||
}
|
||||
}
|
||||
|
||||
interface Callback {
|
||||
@@ -76,7 +80,7 @@ class IndexHandler(private val repositoryId: Long, private val callback: Callbac
|
||||
val releases = mutableListOf<Release>()
|
||||
|
||||
fun build(): Product {
|
||||
return Product(repositoryId, packageName, name, summary, description, "", icon,
|
||||
return Product(repositoryId, packageName, name, summary, description, "", icon, "",
|
||||
Product.Author(authorName, authorEmail, ""), source, changelog, web, tracker, added, updated,
|
||||
suggestedVersionCode, categories.toList(), antiFeatures.toList(),
|
||||
licenses, donates.sortedWith(DonateComparator), emptyList(), releases)
|
||||
@@ -230,7 +234,7 @@ class IndexHandler(private val repositoryId: Long, private val callback: Callbac
|
||||
"summary" -> productBuilder.summary = content
|
||||
"description" -> productBuilder.description = "<p>$content</p>"
|
||||
"desc" -> productBuilder.description = content.replace("\n", "<br/>")
|
||||
"icon" -> productBuilder.icon = content
|
||||
"icon" -> productBuilder.icon = validateIcon(content)
|
||||
"author" -> productBuilder.authorName = content
|
||||
"email" -> productBuilder.authorEmail = content
|
||||
"source" -> productBuilder.source = content
|
||||
|
||||
@@ -18,7 +18,7 @@ object IndexV1Parser {
|
||||
|
||||
private class Screenshots(val phone: List<String>, val smallTablet: List<String>, val largeTablet: List<String>)
|
||||
private class Localized(val name: String, val summary: String, val description: String,
|
||||
val whatsNew: String, val screenshots: Screenshots?)
|
||||
val whatsNew: String, val metadataIcon: String, val screenshots: Screenshots?)
|
||||
|
||||
private fun <T> Map<String, Localized>.getAndCall(key: String, callback: (String, Localized) -> T?): T? {
|
||||
return this[key]?.let { callback(key, it) }
|
||||
@@ -106,7 +106,7 @@ object IndexV1Parser {
|
||||
it.string("name") -> nameFallback = valueAsString
|
||||
it.string("summary") -> summaryFallback = valueAsString
|
||||
it.string("description") -> descriptionFallback = valueAsString
|
||||
it.string("icon") -> icon = valueAsString
|
||||
it.string("icon") -> icon = IndexHandler.validateIcon(valueAsString)
|
||||
it.string("authorName") -> authorName = valueAsString
|
||||
it.string("authorEmail") -> authorEmail = valueAsString
|
||||
it.string("authorWebSite") -> authorWeb = valueAsString
|
||||
@@ -132,6 +132,7 @@ object IndexV1Parser {
|
||||
var summary = ""
|
||||
var description = ""
|
||||
var whatsNew = ""
|
||||
var metadataIcon = ""
|
||||
var phone = emptyList<String>()
|
||||
var smallTablet = emptyList<String>()
|
||||
var largeTablet = emptyList<String>()
|
||||
@@ -141,6 +142,7 @@ object IndexV1Parser {
|
||||
it.string("summary") -> summary = valueAsString
|
||||
it.string("description") -> description = valueAsString
|
||||
it.string("whatsNew") -> whatsNew = valueAsString
|
||||
it.string("icon") -> metadataIcon = valueAsString
|
||||
it.array("phoneScreenshots") -> phone = collectDistinctNotEmptyStrings()
|
||||
it.array("sevenInchScreenshots") -> smallTablet = collectDistinctNotEmptyStrings()
|
||||
it.array("tenInchScreenshots") -> largeTablet = collectDistinctNotEmptyStrings()
|
||||
@@ -149,7 +151,8 @@ object IndexV1Parser {
|
||||
}
|
||||
val screenshots = if (sequenceOf(phone, smallTablet, largeTablet).any { it.isNotEmpty() })
|
||||
Screenshots(phone, smallTablet, largeTablet) else null
|
||||
localizedMap[locale] = Localized(name, summary, description, whatsNew, screenshots)
|
||||
localizedMap[locale] = Localized(name, summary, description, whatsNew,
|
||||
metadataIcon.nullIfEmpty()?.let { "$locale/$it" }.orEmpty(), screenshots)
|
||||
} else {
|
||||
skipChildren()
|
||||
}
|
||||
@@ -161,6 +164,7 @@ object IndexV1Parser {
|
||||
val summary = localizedMap.findString(summaryFallback) { it.summary }
|
||||
val description = localizedMap.findString(descriptionFallback) { it.description }.replace("\n", "<br/>")
|
||||
val whatsNew = localizedMap.findString("") { it.whatsNew }.replace("\n", "<br/>")
|
||||
val metadataIcon = localizedMap.findString("") { it.metadataIcon }
|
||||
val screenshotPairs = localizedMap.find { key, localized -> localized.screenshots?.let { Pair(key, it) } }
|
||||
val screenshots = screenshotPairs
|
||||
?.let { (key, screenshots) -> screenshots.phone.asSequence()
|
||||
@@ -170,7 +174,7 @@ object IndexV1Parser {
|
||||
screenshots.largeTablet.asSequence()
|
||||
.map { Product.Screenshot(key, Product.Screenshot.Type.LARGE_TABLET, it) } }
|
||||
.orEmpty().toList()
|
||||
return Product(repositoryId, packageName, name, summary, description, whatsNew, icon,
|
||||
return Product(repositoryId, packageName, name, summary, description, whatsNew, icon, metadataIcon,
|
||||
Product.Author(authorName, authorEmail, authorWeb), source, changelog, web, tracker, added, updated,
|
||||
suggestedVersionCode, categories, antiFeatures, licenses,
|
||||
donates.sortedWith(IndexHandler.DonateComparator), screenshots, emptyList())
|
||||
|
||||
@@ -17,8 +17,9 @@ object PicassoDownloader {
|
||||
private const val HOST_SCREENSHOT = "screenshot"
|
||||
private const val QUERY_ADDRESS = "address"
|
||||
private const val QUERY_AUTHENTICATION = "authentication"
|
||||
private const val QUERY_ICON = "icon"
|
||||
private const val QUERY_PACKAGE_NAME = "packageName"
|
||||
private const val QUERY_ICON = "icon"
|
||||
private const val QUERY_METADATA_ICON = "metadataIcon"
|
||||
private const val QUERY_LOCALE = "locale"
|
||||
private const val QUERY_DEVICE = "device"
|
||||
private const val QUERY_SCREENSHOT = "screenshot"
|
||||
@@ -32,16 +33,24 @@ object PicassoDownloader {
|
||||
override fun newCall(request: okhttp3.Request): Call {
|
||||
return when (request.url.host) {
|
||||
HOST_ICON -> {
|
||||
val address = request.url.queryParameter(QUERY_ADDRESS)
|
||||
val address = request.url.queryParameter(QUERY_ADDRESS)?.nullIfEmpty()
|
||||
val authentication = request.url.queryParameter(QUERY_AUTHENTICATION)
|
||||
val icon = request.url.queryParameter(QUERY_ICON)
|
||||
val dpi = request.url.queryParameter(QUERY_DPI)?.nullIfEmpty()
|
||||
if (address.isNullOrEmpty() || icon.isNullOrEmpty()) {
|
||||
val path = run {
|
||||
val packageName = request.url.queryParameter(QUERY_PACKAGE_NAME)?.nullIfEmpty()
|
||||
val icon = request.url.queryParameter(QUERY_ICON)?.nullIfEmpty()
|
||||
val metadataIcon = request.url.queryParameter(QUERY_METADATA_ICON)?.nullIfEmpty()
|
||||
val dpi = request.url.queryParameter(QUERY_DPI)?.nullIfEmpty()
|
||||
when {
|
||||
icon != null -> "${if (dpi != null) "icons-$dpi" else "icons"}/$icon"
|
||||
packageName != null && metadataIcon != null -> "$packageName/$metadataIcon"
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
if (address == null || path == null) {
|
||||
Downloader.createCall(request.newBuilder(), "", null)
|
||||
} else {
|
||||
Downloader.createCall(request.newBuilder().url(address.toHttpUrl()
|
||||
.newBuilder().addPathSegment(if (dpi != null) "icons-$dpi" else "icons")
|
||||
.addPathSegment(icon).build()), authentication.orEmpty(), cache)
|
||||
.newBuilder().addPathSegments(path).build()), authentication.orEmpty(), cache)
|
||||
}
|
||||
}
|
||||
HOST_SCREENSHOT -> {
|
||||
@@ -82,17 +91,20 @@ object PicassoDownloader {
|
||||
.build()
|
||||
}
|
||||
|
||||
fun createIconUri(view: View, icon: String, repository: Repository): Uri {
|
||||
fun createIconUri(view: View, packageName: String, icon: String, metadataIcon: String, repository: Repository): Uri {
|
||||
val size = (view.layoutParams.let { min(it.width, it.height) } /
|
||||
view.resources.displayMetrics.density).roundToInt()
|
||||
return createIconUri(view.context, icon, size, repository)
|
||||
return createIconUri(view.context, packageName, icon, metadataIcon, size, repository)
|
||||
}
|
||||
|
||||
private fun createIconUri(context: Context, icon: String, targetSizeDp: Int, repository: Repository): Uri {
|
||||
private fun createIconUri(context: Context, packageName: String, icon: String, metadataIcon: String,
|
||||
targetSizeDp: Int, repository: Repository): Uri {
|
||||
return Uri.Builder().scheme("https").authority(HOST_ICON)
|
||||
.appendQueryParameter(QUERY_ADDRESS, repository.address)
|
||||
.appendQueryParameter(QUERY_AUTHENTICATION, repository.authentication)
|
||||
.appendQueryParameter(QUERY_PACKAGE_NAME, packageName)
|
||||
.appendQueryParameter(QUERY_ICON, icon)
|
||||
.appendQueryParameter(QUERY_METADATA_ICON, metadataIcon)
|
||||
.apply {
|
||||
if (repository.version >= 11) {
|
||||
val displayDpi = context.resources.displayMetrics.densityDpi
|
||||
|
||||
@@ -961,8 +961,9 @@ class ProductAdapter(private val callbacks: Callbacks, private val columns: Int)
|
||||
val updateStatus = Payload.STATUS in payloads
|
||||
val updateAll = !updateStatus
|
||||
if (updateAll) {
|
||||
if (item.product.icon.isNotEmpty()) {
|
||||
holder.icon.load(PicassoDownloader.createIconUri(holder.icon, item.product.icon, item.repository)) {
|
||||
if (item.product.icon.isNotEmpty() || item.product.metadataIcon.isNotEmpty()) {
|
||||
holder.icon.load(PicassoDownloader.createIconUri(holder.icon, item.product.packageName,
|
||||
item.product.icon, item.product.metadataIcon, item.repository)) {
|
||||
placeholder(holder.progressIcon)
|
||||
error(holder.defaultIcon)
|
||||
}
|
||||
|
||||
@@ -139,8 +139,9 @@ class ProductsAdapter(private val onClick: (ProductItem) -> Unit):
|
||||
holder.summary.text = if (productItem.name == productItem.summary) "" else productItem.summary
|
||||
holder.summary.visibility = if (holder.summary.text.isNotEmpty()) View.VISIBLE else View.GONE
|
||||
val repository: Repository? = repositories[productItem.repositoryId]
|
||||
if (productItem.icon.isNotEmpty() && repository != null) {
|
||||
holder.icon.load(PicassoDownloader.createIconUri(holder.icon, productItem.icon, repository)) {
|
||||
if ((productItem.icon.isNotEmpty() || productItem.metadataIcon.isNotEmpty()) && repository != null) {
|
||||
holder.icon.load(PicassoDownloader.createIconUri(holder.icon, productItem.packageName,
|
||||
productItem.icon, productItem.metadataIcon, repository)) {
|
||||
placeholder(holder.progressIcon)
|
||||
error(holder.defaultIcon)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user