widgets: move size fixing into a transform
This commit is contained in:
parent
7a00c3c6aa
commit
d10f84efa8
4 changed files with 70 additions and 34 deletions
|
@ -107,7 +107,10 @@ class RoundedRectTransformation(
|
|||
}
|
||||
|
||||
private fun calculateOutputSize(input: Bitmap, size: Size): Pair<Int, Int> {
|
||||
// MODIFICATION: Remove short-circuiting for original size and input size
|
||||
if (size == Size.ORIGINAL) {
|
||||
// This path only runs w/the widget code, which already normalizes widget sizes
|
||||
return input.width to input.height
|
||||
}
|
||||
val multiplier =
|
||||
DecodeUtils.computeSizeMultiplier(
|
||||
srcWidth = input.width,
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
* Copyright (c) 2024 Auxio Project
|
||||
* WidgetBitmapTransformation.kt is part of Auxio.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.oxycblt.auxio.widgets
|
||||
|
||||
import android.content.res.Resources
|
||||
import android.graphics.Bitmap
|
||||
import coil.size.Size
|
||||
import coil.transform.Transformation
|
||||
import kotlin.math.sqrt
|
||||
|
||||
class WidgetBitmapTransformation(private val reduce: Float) : Transformation {
|
||||
private val metrics = Resources.getSystem().displayMetrics
|
||||
private val sw = metrics.widthPixels
|
||||
private val sh = metrics.heightPixels
|
||||
// Cap memory usage at 1.5 times the size of the display
|
||||
// 1.5 * 4 bytes/pixel * w * h ==> 6 * w * h
|
||||
// https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
|
||||
// Of course since OEMs randomly patch this check, we give a lot of slack.
|
||||
private val maxBitmapArea = (1.5 * sw * sh / reduce).toInt()
|
||||
|
||||
override val cacheKey: String
|
||||
get() = "WidgetBitmapTransformation:${maxBitmapArea}"
|
||||
|
||||
override suspend fun transform(input: Bitmap, size: Size): Bitmap {
|
||||
if (size !== Size.ORIGINAL) {
|
||||
// The widget loading stack basically discards the size parameter since there's no
|
||||
// sane value from the get-go, all this transform does is actually dynamically apply
|
||||
// the size cap so this transform must always be zero.
|
||||
throw IllegalArgumentException("WidgetBitmapTransformation requires original size.")
|
||||
}
|
||||
val inputArea = input.width * input.height
|
||||
if (inputArea != maxBitmapArea) {
|
||||
val scale = sqrt(maxBitmapArea / inputArea.toDouble())
|
||||
val newWidth = (input.width * scale).toInt()
|
||||
val newHeight = (input.height * scale).toInt()
|
||||
return Bitmap.createScaledBitmap(input, newWidth, newHeight, true)
|
||||
}
|
||||
return input
|
||||
}
|
||||
}
|
|
@ -22,6 +22,7 @@ import android.content.Context
|
|||
import android.graphics.Bitmap
|
||||
import android.os.Build
|
||||
import coil.request.ImageRequest
|
||||
import coil.size.Size
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import javax.inject.Inject
|
||||
import org.oxycblt.auxio.R
|
||||
|
@ -96,24 +97,19 @@ constructor(
|
|||
0
|
||||
}
|
||||
|
||||
return if (cornerRadius > 0) {
|
||||
// If rounded, reduce the bitmap size further to obtain more pronounced
|
||||
// rounded corners.
|
||||
builder.size(getSafeRemoteViewsImageSize(context, 10f))
|
||||
val cornersTransformation =
|
||||
RoundedRectTransformation(cornerRadius.toFloat())
|
||||
val transformations = buildList {
|
||||
if (imageSettings.forceSquareCovers) {
|
||||
builder.transformations(
|
||||
SquareCropTransformation.INSTANCE, cornersTransformation)
|
||||
add(SquareCropTransformation.INSTANCE)
|
||||
}
|
||||
if (cornerRadius > 0) {
|
||||
add(WidgetBitmapTransformation(10f))
|
||||
add(RoundedRectTransformation(cornerRadius.toFloat()))
|
||||
} else {
|
||||
builder.transformations(cornersTransformation)
|
||||
add(WidgetBitmapTransformation(2f))
|
||||
}
|
||||
} else {
|
||||
if (imageSettings.forceSquareCovers) {
|
||||
builder.transformations(SquareCropTransformation.INSTANCE)
|
||||
}
|
||||
builder.size(getSafeRemoteViewsImageSize(context))
|
||||
}
|
||||
|
||||
return builder.size(Size.ORIGINAL).transformations(transformations)
|
||||
}
|
||||
|
||||
override fun onCompleted(bitmap: Bitmap?) {
|
||||
|
|
|
@ -27,7 +27,6 @@ import android.widget.RemoteViews
|
|||
import androidx.annotation.DrawableRes
|
||||
import androidx.annotation.IdRes
|
||||
import androidx.annotation.LayoutRes
|
||||
import kotlin.math.sqrt
|
||||
import org.oxycblt.auxio.util.isLandscape
|
||||
import org.oxycblt.auxio.util.logD
|
||||
import org.oxycblt.auxio.util.newMainPendingIntent
|
||||
|
@ -46,24 +45,6 @@ fun newRemoteViews(context: Context, @LayoutRes layoutRes: Int): RemoteViews {
|
|||
return views
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an image size guaranteed to not exceed the [RemoteViews] bitmap memory limit, assuming that
|
||||
* there is only one image.
|
||||
*
|
||||
* @param context [Context] required to perform calculation.
|
||||
* @param reduce Optional multiplier to reduce the image size. Recommended value is 2 to avoid
|
||||
* device-specific variations in memory limit.
|
||||
* @return The dimension of a bitmap that can be safely used in [RemoteViews].
|
||||
*/
|
||||
fun getSafeRemoteViewsImageSize(context: Context, reduce: Float = 2f): Int {
|
||||
val metrics = context.resources.displayMetrics
|
||||
val sw = metrics.widthPixels
|
||||
val sh = metrics.heightPixels
|
||||
// Maximum size is 1/3 total screen area * 4 bytes per pixel. Reverse
|
||||
// that to obtain the image size.
|
||||
return sqrt((6f / 4f / reduce) * sw * sh).toInt()
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the background resource of a [RemoteViews] View.
|
||||
*
|
||||
|
|
Loading…
Reference in a new issue