From f5f28b891ad77ca6bd33cf1535323cff71ccb88c Mon Sep 17 00:00:00 2001 From: Alexander Capehart Date: Tue, 16 Aug 2022 12:26:08 -0600 Subject: [PATCH] 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. --- .../oxycblt/auxio/widgets/WidgetComponent.kt | 22 +++++++------- .../oxycblt/auxio/widgets/WidgetProvider.kt | 29 +++++++++---------- 2 files changed, 25 insertions(+), 26 deletions(-) diff --git a/app/src/main/java/org/oxycblt/auxio/widgets/WidgetComponent.kt b/app/src/main/java/org/oxycblt/auxio/widgets/WidgetComponent.kt index 16cfbd1ba..f07f54bdd 100644 --- a/app/src/main/java/org/oxycblt/auxio/widgets/WidgetComponent.kt +++ b/app/src/main/java/org/oxycblt/auxio/widgets/WidgetComponent.kt @@ -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 */ diff --git a/app/src/main/java/org/oxycblt/auxio/widgets/WidgetProvider.kt b/app/src/main/java/org/oxycblt/auxio/widgets/WidgetProvider.kt index d23b1cf60..a99bccfef 100644 --- a/app/src/main/java/org/oxycblt/auxio/widgets/WidgetProvider.kt +++ b/app/src/main/java/org/oxycblt/auxio/widgets/WidgetProvider.kt @@ -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) - } + logD("Using widget layout $layout") + updateAppWidget(id, views[layout]) } } }