diff --git a/src/main/kotlin/nya/kitsunyan/foxydroid/entity/Product.kt b/src/main/kotlin/nya/kitsunyan/foxydroid/entity/Product.kt index 7d511ee..133e9b5 100644 --- a/src/main/kotlin/nya/kitsunyan/foxydroid/entity/Product.kt +++ b/src/main/kotlin/nya/kitsunyan/foxydroid/entity/Product.kt @@ -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, val antiFeatures: List, val licenses: List, @@ -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) } diff --git a/src/main/kotlin/nya/kitsunyan/foxydroid/entity/ProductItem.kt b/src/main/kotlin/nya/kitsunyan/foxydroid/entity/ProductItem.kt index 8fc028b..3f7c2c1 100644 --- a/src/main/kotlin/nya/kitsunyan/foxydroid/entity/ProductItem.kt +++ b/src/main/kotlin/nya/kitsunyan/foxydroid/entity/ProductItem.kt @@ -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) } } diff --git a/src/main/kotlin/nya/kitsunyan/foxydroid/index/IndexHandler.kt b/src/main/kotlin/nya/kitsunyan/foxydroid/index/IndexHandler.kt index b094067..6127c21 100644 --- a/src/main/kotlin/nya/kitsunyan/foxydroid/index/IndexHandler.kt +++ b/src/main/kotlin/nya/kitsunyan/foxydroid/index/IndexHandler.kt @@ -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() 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 = "

$content

" "desc" -> productBuilder.description = content.replace("\n", "
") - "icon" -> productBuilder.icon = content + "icon" -> productBuilder.icon = validateIcon(content) "author" -> productBuilder.authorName = content "email" -> productBuilder.authorEmail = content "source" -> productBuilder.source = content diff --git a/src/main/kotlin/nya/kitsunyan/foxydroid/index/IndexV1Parser.kt b/src/main/kotlin/nya/kitsunyan/foxydroid/index/IndexV1Parser.kt index eb4b4d9..00c5e0a 100644 --- a/src/main/kotlin/nya/kitsunyan/foxydroid/index/IndexV1Parser.kt +++ b/src/main/kotlin/nya/kitsunyan/foxydroid/index/IndexV1Parser.kt @@ -18,7 +18,7 @@ object IndexV1Parser { private class Screenshots(val phone: List, val smallTablet: List, val largeTablet: List) 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 Map.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() var smallTablet = emptyList() var largeTablet = emptyList() @@ -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", "
") val whatsNew = localizedMap.findString("") { it.whatsNew }.replace("\n", "
") + 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()) diff --git a/src/main/kotlin/nya/kitsunyan/foxydroid/network/PicassoDownloader.kt b/src/main/kotlin/nya/kitsunyan/foxydroid/network/PicassoDownloader.kt index f993cba..c55e1d8 100644 --- a/src/main/kotlin/nya/kitsunyan/foxydroid/network/PicassoDownloader.kt +++ b/src/main/kotlin/nya/kitsunyan/foxydroid/network/PicassoDownloader.kt @@ -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 diff --git a/src/main/kotlin/nya/kitsunyan/foxydroid/screen/ProductAdapter.kt b/src/main/kotlin/nya/kitsunyan/foxydroid/screen/ProductAdapter.kt index 0411195..7717ed8 100644 --- a/src/main/kotlin/nya/kitsunyan/foxydroid/screen/ProductAdapter.kt +++ b/src/main/kotlin/nya/kitsunyan/foxydroid/screen/ProductAdapter.kt @@ -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) } diff --git a/src/main/kotlin/nya/kitsunyan/foxydroid/screen/ProductsAdapter.kt b/src/main/kotlin/nya/kitsunyan/foxydroid/screen/ProductsAdapter.kt index e82fa6d..d72a115 100644 --- a/src/main/kotlin/nya/kitsunyan/foxydroid/screen/ProductsAdapter.kt +++ b/src/main/kotlin/nya/kitsunyan/foxydroid/screen/ProductsAdapter.kt @@ -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) }