From f96d2ffaa55227eb95807fae47cb8ef4e7d1496e Mon Sep 17 00:00:00 2001 From: TwistedUmbrellaX <1173913+AbandonedCart@users.noreply.github.com> Date: Sun, 7 Apr 2024 22:21:24 -0400 Subject: [PATCH] feat: add per-widget configuration (#333) * feat: add per-widget configuration * fix: no need to overengineer it * feat: add cache to bitmap download dfgdfg * fix: elvis has left the operation --- .../main/java/ani/dantotsu/util/BitmapUtil.kt | 67 +++++++++++++------ .../statistics/ProfileStatsConfigure.kt | 32 ++++----- .../widgets/statistics/ProfileStatsWidget.kt | 46 +++---------- .../upcoming/UpcomingRemoteViewsFactory.kt | 28 +------- .../widgets/upcoming/UpcomingWidget.kt | 4 +- 5 files changed, 74 insertions(+), 103 deletions(-) diff --git a/app/src/main/java/ani/dantotsu/util/BitmapUtil.kt b/app/src/main/java/ani/dantotsu/util/BitmapUtil.kt index a4c4e7b6cc..54a7072f0e 100644 --- a/app/src/main/java/ani/dantotsu/util/BitmapUtil.kt +++ b/app/src/main/java/ani/dantotsu/util/BitmapUtil.kt @@ -1,33 +1,62 @@ package ani.dantotsu.util import android.graphics.Bitmap +import android.graphics.BitmapFactory import android.graphics.BitmapShader import android.graphics.Canvas import android.graphics.Paint import android.graphics.RectF import android.graphics.Shader -import android.graphics.drawable.Drawable +import androidx.collection.LruCache +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.runBlocking +import java.io.InputStream +import java.net.HttpURLConnection +import java.net.URL -class BitmapUtil { - companion object { - fun roundCorners(bitmap: Bitmap, cornerRadius: Float = 20f): Bitmap { - val output = Bitmap.createBitmap(bitmap.width, bitmap.height, Bitmap.Config.ARGB_8888) - val canvas = Canvas(output) - val paint = Paint() - paint.isAntiAlias = true - paint.shader = BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP) - val rect = RectF(0f, 0f, bitmap.width.toFloat(), bitmap.height.toFloat()) - canvas.drawRoundRect(rect, cornerRadius, cornerRadius, paint) +object BitmapUtil { + private fun roundCorners(bitmap: Bitmap, cornerRadius: Float = 20f): Bitmap { + val output = Bitmap.createBitmap(bitmap.width, bitmap.height, Bitmap.Config.ARGB_8888) + val canvas = Canvas(output) + val paint = Paint() + paint.isAntiAlias = true + paint.shader = BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP) + val rect = RectF(0f, 0f, bitmap.width.toFloat(), bitmap.height.toFloat()) + canvas.drawRoundRect(rect, cornerRadius, cornerRadius, paint) - return output - } + return output + } + + private val cacheSize = (Runtime.getRuntime().maxMemory() / 1024 / 16).toInt() + private val bitmapCache = LruCache(cacheSize) + + fun downloadImageAsBitmap(imageUrl: String): Bitmap? { + var bitmap: Bitmap? = null + + runBlocking(Dispatchers.IO) { + val cacheName = imageUrl.substringAfterLast("/") + bitmap = bitmapCache[cacheName] + if (bitmap != null) return@runBlocking + var inputStream: InputStream? = null + var urlConnection: HttpURLConnection? = null + try { + val url = URL(imageUrl) + urlConnection = url.openConnection() as HttpURLConnection + urlConnection.requestMethod = "GET" + urlConnection.connect() - fun convertDrawableToBitmap(drawable: Drawable, width: Int, height: Int): Bitmap { - val bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888) - val canvas = Canvas(bitmap) - drawable.setBounds(0, 0, canvas.width, canvas.height) - drawable.draw(canvas) - return bitmap + if (urlConnection.responseCode == HttpURLConnection.HTTP_OK) { + inputStream = urlConnection.inputStream + bitmap = BitmapFactory.decodeStream(inputStream) + bitmap?.let { bitmapCache.put(cacheName, it) } + } + } catch (e: Exception) { + e.printStackTrace() + } finally { + inputStream?.close() + urlConnection?.disconnect() + } } + return bitmap?.let { roundCorners(it) } } } \ No newline at end of file diff --git a/app/src/main/java/ani/dantotsu/widgets/statistics/ProfileStatsConfigure.kt b/app/src/main/java/ani/dantotsu/widgets/statistics/ProfileStatsConfigure.kt index b3ddec4611..3850947700 100644 --- a/app/src/main/java/ani/dantotsu/widgets/statistics/ProfileStatsConfigure.kt +++ b/app/src/main/java/ani/dantotsu/widgets/statistics/ProfileStatsConfigure.kt @@ -60,7 +60,11 @@ class ProfileStatsConfigure : AppCompatActivity(), binding = StatisticsWidgetConfigureBinding.inflate(layoutInflater) setContentView(binding.root) - val prefs = getSharedPreferences(ProfileStatsWidget.PREFS_NAME, Context.MODE_PRIVATE) + appWidgetId = intent.getIntExtra( + AppWidgetManager.EXTRA_APPWIDGET_ID, + AppWidgetManager.INVALID_APPWIDGET_ID + ) + val prefs = getSharedPreferences(ProfileStatsWidget.getPrefsName(appWidgetId), Context.MODE_PRIVATE) val topBackground = prefs.getInt(ProfileStatsWidget.PREF_BACKGROUND_COLOR, Color.parseColor("#80000000")) (binding.topBackgroundButton as MaterialButton).iconTint = ColorStateList.valueOf(topBackground) binding.topBackgroundButton.setOnClickListener { @@ -192,7 +196,7 @@ class ProfileStatsConfigure : AppCompatActivity(), ) val subTextColor = typedValueOutline.data - getSharedPreferences(ProfileStatsWidget.PREFS_NAME, Context.MODE_PRIVATE).edit().apply { + getSharedPreferences(ProfileStatsWidget.getPrefsName(appWidgetId), Context.MODE_PRIVATE).edit().apply { putInt(ProfileStatsWidget.PREF_BACKGROUND_COLOR, backgroundColor) putInt(ProfileStatsWidget.PREF_BACKGROUND_FADE, backgroundColor) putInt(ProfileStatsWidget.PREF_TITLE_TEXT_COLOR, textColor) @@ -204,12 +208,13 @@ class ProfileStatsConfigure : AppCompatActivity(), override fun onResult(dialogTag: String, which: Int, extras: Bundle): Boolean { if (which == SimpleDialog.OnDialogResultListener.BUTTON_POSITIVE) { if (!isMonetEnabled) { + val prefs = getSharedPreferences( + ProfileStatsWidget.getPrefsName(appWidgetId), + Context.MODE_PRIVATE + ) when (dialogTag) { ProfileStatsWidget.PREF_BACKGROUND_COLOR -> { - getSharedPreferences( - ProfileStatsWidget.PREFS_NAME, - Context.MODE_PRIVATE - ).edit() + prefs.edit() .putInt( ProfileStatsWidget.PREF_BACKGROUND_COLOR, extras.getInt(SimpleColorDialog.COLOR) @@ -220,10 +225,7 @@ class ProfileStatsConfigure : AppCompatActivity(), } ProfileStatsWidget.PREF_BACKGROUND_FADE -> { - getSharedPreferences( - ProfileStatsWidget.PREFS_NAME, - Context.MODE_PRIVATE - ).edit() + prefs.edit() .putInt( ProfileStatsWidget.PREF_BACKGROUND_FADE, extras.getInt(SimpleColorDialog.COLOR) @@ -234,10 +236,7 @@ class ProfileStatsConfigure : AppCompatActivity(), } ProfileStatsWidget.PREF_TITLE_TEXT_COLOR -> { - getSharedPreferences( - ProfileStatsWidget.PREFS_NAME, - Context.MODE_PRIVATE - ).edit() + prefs.edit() .putInt( ProfileStatsWidget.PREF_TITLE_TEXT_COLOR, extras.getInt(SimpleColorDialog.COLOR) @@ -248,10 +247,7 @@ class ProfileStatsConfigure : AppCompatActivity(), } ProfileStatsWidget.PREF_STATS_TEXT_COLOR -> { - getSharedPreferences( - ProfileStatsWidget.PREFS_NAME, - Context.MODE_PRIVATE - ).edit() + prefs.edit() .putInt( ProfileStatsWidget.PREF_STATS_TEXT_COLOR, extras.getInt(SimpleColorDialog.COLOR) diff --git a/app/src/main/java/ani/dantotsu/widgets/statistics/ProfileStatsWidget.kt b/app/src/main/java/ani/dantotsu/widgets/statistics/ProfileStatsWidget.kt index 24ac05c8bd..af363ae225 100644 --- a/app/src/main/java/ani/dantotsu/widgets/statistics/ProfileStatsWidget.kt +++ b/app/src/main/java/ani/dantotsu/widgets/statistics/ProfileStatsWidget.kt @@ -5,28 +5,24 @@ import android.appwidget.AppWidgetManager import android.appwidget.AppWidgetProvider import android.content.Context import android.content.Intent -import android.graphics.Bitmap -import android.graphics.BitmapFactory import android.graphics.Color import android.graphics.drawable.GradientDrawable +import android.net.Uri import android.widget.RemoteViews import androidx.core.content.res.ResourcesCompat +import androidx.core.graphics.drawable.toBitmap import ani.dantotsu.MainActivity import ani.dantotsu.R import ani.dantotsu.connections.anilist.Anilist import ani.dantotsu.profile.ProfileActivity import ani.dantotsu.settings.saving.PrefManager import ani.dantotsu.settings.saving.PrefName -import ani.dantotsu.util.BitmapUtil +import ani.dantotsu.util.BitmapUtil.Companion.downloadImageAsBitmap import ani.dantotsu.widgets.WidgetSizeProvider import kotlinx.coroutines.DelicateCoroutinesApi import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.runBlocking import kotlinx.coroutines.withContext import tachiyomi.core.util.lang.launchIO -import java.io.InputStream -import java.net.HttpURLConnection -import java.net.URL /** * Implementation of App Widget functionality. @@ -56,32 +52,6 @@ class ProfileStatsWidget : AppWidgetProvider() { } companion object { - private fun downloadImageAsBitmap(imageUrl: String): Bitmap? { - var bitmap: Bitmap? = null - - runBlocking(Dispatchers.IO) { - var inputStream: InputStream? = null - var urlConnection: HttpURLConnection? = null - try { - val url = URL(imageUrl) - urlConnection = url.openConnection() as HttpURLConnection - urlConnection.requestMethod = "GET" - urlConnection.connect() - - if (urlConnection.responseCode == HttpURLConnection.HTTP_OK) { - inputStream = urlConnection.inputStream - bitmap = BitmapFactory.decodeStream(inputStream) - } - } catch (e: Exception) { - e.printStackTrace() - } finally { - inputStream?.close() - urlConnection?.disconnect() - } - } - return bitmap?.let { BitmapUtil.roundCorners(it) } - } - @OptIn(DelicateCoroutinesApi::class) fun updateAppWidget( context: Context, @@ -89,7 +59,7 @@ class ProfileStatsWidget : AppWidgetProvider() { appWidgetId: Int ) { - val prefs = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE) + val prefs = context.getSharedPreferences(getPrefsName(appWidgetId), Context.MODE_PRIVATE) val backgroundColor = prefs.getInt(PREF_BACKGROUND_COLOR, Color.parseColor("#80000000")) val backgroundFade = prefs.getInt(PREF_BACKGROUND_FADE, Color.parseColor("#00000000")) @@ -120,8 +90,7 @@ class ProfileStatsWidget : AppWidgetProvider() { val views = RemoteViews(context.packageName, R.layout.statistics_widget).apply { setImageViewBitmap( R.id.backgroundView, - BitmapUtil.convertDrawableToBitmap( - gradientDrawable, + gradientDrawable.toBitmap( width, height ) @@ -133,6 +102,7 @@ class ProfileStatsWidget : AppWidgetProvider() { 1, Intent(context, ProfileStatsConfigure::class.java).apply { putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId) + data = Uri.parse(toUri(Intent.URI_INTENT_SCHEME)) }, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE ) @@ -248,7 +218,9 @@ class ProfileStatsWidget : AppWidgetProvider() { } } - const val PREFS_NAME = "ani.dantotsu.widgets.ResumableWidget" + fun getPrefsName(appWidgetId: Int): String { + return "ani.dantotsu.widgets.Statistics.${appWidgetId}" + } const val PREF_BACKGROUND_COLOR = "background_color" const val PREF_BACKGROUND_FADE = "background_fade" const val PREF_TITLE_TEXT_COLOR = "title_text_color" diff --git a/app/src/main/java/ani/dantotsu/widgets/upcoming/UpcomingRemoteViewsFactory.kt b/app/src/main/java/ani/dantotsu/widgets/upcoming/UpcomingRemoteViewsFactory.kt index 799b2518e8..7f499dfa30 100644 --- a/app/src/main/java/ani/dantotsu/widgets/upcoming/UpcomingRemoteViewsFactory.kt +++ b/app/src/main/java/ani/dantotsu/widgets/upcoming/UpcomingRemoteViewsFactory.kt @@ -12,6 +12,7 @@ import ani.dantotsu.connections.anilist.Anilist import ani.dantotsu.media.Media import ani.dantotsu.settings.saving.PrefManager import ani.dantotsu.settings.saving.PrefName +import ani.dantotsu.util.BitmapUtil.Companion.downloadImageAsBitmap import ani.dantotsu.util.BitmapUtil.Companion.roundCorners import ani.dantotsu.util.Logger import com.google.gson.GsonBuilder @@ -183,33 +184,6 @@ class UpcomingRemoteViewsFactory(private val context: Context) : return rv } - private fun downloadImageAsBitmap(imageUrl: String): Bitmap? { - var bitmap: Bitmap? = null - var inputStream: InputStream? = null - var urlConnection: HttpURLConnection? = null - - try { - val url = URL(imageUrl) - urlConnection = url.openConnection() as HttpURLConnection - urlConnection.requestMethod = "GET" - urlConnection.connect() - - if (urlConnection.responseCode == HttpURLConnection.HTTP_OK) { - inputStream = urlConnection.inputStream - bitmap = BitmapFactory.decodeStream(inputStream) - } - } catch (e: Exception) { - e.printStackTrace() - } finally { - inputStream?.close() - urlConnection?.disconnect() - } - return bitmap?.let { roundCorners(it) } - } - - - - override fun getLoadingView(): RemoteViews { return RemoteViews(context.packageName, R.layout.item_upcoming_widget) } diff --git a/app/src/main/java/ani/dantotsu/widgets/upcoming/UpcomingWidget.kt b/app/src/main/java/ani/dantotsu/widgets/upcoming/UpcomingWidget.kt index 62f31d053f..b18876b88c 100644 --- a/app/src/main/java/ani/dantotsu/widgets/upcoming/UpcomingWidget.kt +++ b/app/src/main/java/ani/dantotsu/widgets/upcoming/UpcomingWidget.kt @@ -11,9 +11,9 @@ import android.net.Uri import android.os.Bundle import android.widget.RemoteViews import androidx.core.content.res.ResourcesCompat +import androidx.core.graphics.drawable.toBitmap import ani.dantotsu.MainActivity import ani.dantotsu.R -import ani.dantotsu.util.BitmapUtil.Companion.convertDrawableToBitmap import ani.dantotsu.widgets.WidgetSizeProvider /** @@ -97,7 +97,7 @@ class UpcomingWidget : AppWidgetProvider() { intentTemplate.putExtra("fromWidget", true) val views = RemoteViews(context.packageName, R.layout.upcoming_widget).apply { - setImageViewBitmap(R.id.backgroundView, convertDrawableToBitmap(gradientDrawable, width, height)) + setImageViewBitmap(R.id.backgroundView, gradientDrawable.toBitmap(width, height)) setTextColor(R.id.text_show_title, titleTextColor) setTextColor(R.id.text_show_countdown, countdownTextColor) setTextColor(R.id.widgetTitle, titleTextColor)