widget: fix android 13 cover loading issue

Work aroudn an issue in Android 13 where bitmap pooling would not work
correctly and reduce our max memory limit by ~10x.

When I mean work around, I mean that we reduce the size of the rounded
covers further to ensure we aren't hitting the memory limit. This also
sadly blocks us from further RemoteViews instances, as the bugged
memory size will continue to increase if we do that.
This commit is contained in:
Alexander Capehart 2022-08-16 12:26:08 -06:00
parent e90983e85d
commit f5f28b891a
No known key found for this signature in database
GPG key ID: 37DBE3621FE9AD47
2 changed files with 25 additions and 26 deletions

View file

@ -95,27 +95,24 @@ class WidgetComponent(private val context: Context) :
}
// We resize the image in a such a way that we don't hit the RemoteView size
// limit, which is the size of an RGB_8888 bitmap 1.5x the screen size. When
// enabling rounded corners, we further reduce it by a factor of 8 to get 16-dp
// rounded corners, whereas we only downsize it by 2 when there is rounded
// corners just to ensure that we *really* do not hit the memory limit.
// limit, which is the size of an RGB_8888 bitmap 1.5x the screen size.
val metrics = context.resources.displayMetrics
val sw = metrics.widthPixels
val sh = metrics.heightPixels
return if (cornerRadius > 0) {
this@WidgetComponent.logD("Loading round covers: $cornerRadius")
// Reduce the size by 10x, not only to make 16dp-ish corners, but also
// to work around a bug in Android 13 where the bitmaps aren't pooled
// properly, massively reducing the memory size we can work with.
builder
.size(sqrt((6f / 4f / 8f) * sw * sh).toInt())
.size(computeSize(sw, sh, 10f))
.transformations(
SquareFrameTransform.INSTANCE,
// RoundedCornersTransformation is used instead of clipToOutline
// since our hack to get a 1:1 cover on the widget actually does
// not result in a square view, making clipToOutline not work.
RoundedCornersTransformation(cornerRadius.toFloat()))
} else {
builder.size(sqrt((6f / 4f / 2f) * sw * sh).toInt())
// Divide by two to really make sure we aren't hitting the memory limit.
builder.size(computeSize(sw, sh, 2f))
}
}
@ -126,6 +123,9 @@ class WidgetComponent(private val context: Context) :
})
}
private fun computeSize(sw: Int, sh: Int, modifier: Float) =
sqrt((6f / 4f / modifier) * sw * sh).toInt()
/*
* Release this instance, removing the callbacks and resetting all widgets
*/

View file

@ -63,7 +63,15 @@ class WidgetProvider : AppWidgetProvider() {
SizeF(180f, 272f) to createMediumWidget(context, state),
SizeF(272f, 272f) to createLargeWidget(context, state))
AppWidgetManager.getInstance(context).updateAppWidgetCompat(context, views)
val awm = AppWidgetManager.getInstance(context)
try {
awm.updateAppWidgetCompat(context, views)
} catch (e: Exception) {
logW("Unable to update widget: $e")
awm.updateAppWidget(
ComponentName(context, this::class.java), createDefaultWidget(context))
}
}
/*
@ -158,21 +166,12 @@ class WidgetProvider : AppWidgetProvider() {
}
}
val layout = candidates.maxByOrNull { it.height * it.width }
val layout =
candidates.maxByOrNull { it.height * it.width }
?: unlikelyToBeNull(views.minOfOrNull { it.key.width * it.key.height })
if (layout != null) {
logD("Using widget layout $layout")
updateAppWidget(id, views[layout])
continue
} else {
// Default to the smallest view if no layout fits
logW("No good widget layout found")
val minimum =
unlikelyToBeNull(views.minByOrNull { it.key.width * it.key.height }).value
updateAppWidget(id, minimum)
}
}
}
}