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
This commit is contained in:
parent
47d05e737d
commit
f96d2ffaa5
5 changed files with 75 additions and 104 deletions
|
@ -1,33 +1,62 @@
|
||||||
package ani.dantotsu.util
|
package ani.dantotsu.util
|
||||||
|
|
||||||
import android.graphics.Bitmap
|
import android.graphics.Bitmap
|
||||||
|
import android.graphics.BitmapFactory
|
||||||
import android.graphics.BitmapShader
|
import android.graphics.BitmapShader
|
||||||
import android.graphics.Canvas
|
import android.graphics.Canvas
|
||||||
import android.graphics.Paint
|
import android.graphics.Paint
|
||||||
import android.graphics.RectF
|
import android.graphics.RectF
|
||||||
import android.graphics.Shader
|
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 {
|
object BitmapUtil {
|
||||||
companion object {
|
private fun roundCorners(bitmap: Bitmap, cornerRadius: Float = 20f): Bitmap {
|
||||||
fun roundCorners(bitmap: Bitmap, cornerRadius: Float = 20f): Bitmap {
|
val output = Bitmap.createBitmap(bitmap.width, bitmap.height, Bitmap.Config.ARGB_8888)
|
||||||
val output = Bitmap.createBitmap(bitmap.width, bitmap.height, Bitmap.Config.ARGB_8888)
|
val canvas = Canvas(output)
|
||||||
val canvas = Canvas(output)
|
val paint = Paint()
|
||||||
val paint = Paint()
|
paint.isAntiAlias = true
|
||||||
paint.isAntiAlias = true
|
paint.shader = BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP)
|
||||||
paint.shader = BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP)
|
val rect = RectF(0f, 0f, bitmap.width.toFloat(), bitmap.height.toFloat())
|
||||||
val rect = RectF(0f, 0f, bitmap.width.toFloat(), bitmap.height.toFloat())
|
canvas.drawRoundRect(rect, cornerRadius, cornerRadius, paint)
|
||||||
canvas.drawRoundRect(rect, cornerRadius, cornerRadius, paint)
|
|
||||||
|
|
||||||
return output
|
return output
|
||||||
}
|
}
|
||||||
|
|
||||||
fun convertDrawableToBitmap(drawable: Drawable, width: Int, height: Int): Bitmap {
|
private val cacheSize = (Runtime.getRuntime().maxMemory() / 1024 / 16).toInt()
|
||||||
val bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
|
private val bitmapCache = LruCache<String, Bitmap>(cacheSize)
|
||||||
val canvas = Canvas(bitmap)
|
|
||||||
drawable.setBounds(0, 0, canvas.width, canvas.height)
|
fun downloadImageAsBitmap(imageUrl: String): Bitmap? {
|
||||||
drawable.draw(canvas)
|
var bitmap: Bitmap? = null
|
||||||
return bitmap
|
|
||||||
|
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()
|
||||||
|
|
||||||
|
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) }
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -60,7 +60,11 @@ class ProfileStatsConfigure : AppCompatActivity(),
|
||||||
binding = StatisticsWidgetConfigureBinding.inflate(layoutInflater)
|
binding = StatisticsWidgetConfigureBinding.inflate(layoutInflater)
|
||||||
setContentView(binding.root)
|
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"))
|
val topBackground = prefs.getInt(ProfileStatsWidget.PREF_BACKGROUND_COLOR, Color.parseColor("#80000000"))
|
||||||
(binding.topBackgroundButton as MaterialButton).iconTint = ColorStateList.valueOf(topBackground)
|
(binding.topBackgroundButton as MaterialButton).iconTint = ColorStateList.valueOf(topBackground)
|
||||||
binding.topBackgroundButton.setOnClickListener {
|
binding.topBackgroundButton.setOnClickListener {
|
||||||
|
@ -192,7 +196,7 @@ class ProfileStatsConfigure : AppCompatActivity(),
|
||||||
)
|
)
|
||||||
val subTextColor = typedValueOutline.data
|
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_COLOR, backgroundColor)
|
||||||
putInt(ProfileStatsWidget.PREF_BACKGROUND_FADE, backgroundColor)
|
putInt(ProfileStatsWidget.PREF_BACKGROUND_FADE, backgroundColor)
|
||||||
putInt(ProfileStatsWidget.PREF_TITLE_TEXT_COLOR, textColor)
|
putInt(ProfileStatsWidget.PREF_TITLE_TEXT_COLOR, textColor)
|
||||||
|
@ -204,12 +208,13 @@ class ProfileStatsConfigure : AppCompatActivity(),
|
||||||
override fun onResult(dialogTag: String, which: Int, extras: Bundle): Boolean {
|
override fun onResult(dialogTag: String, which: Int, extras: Bundle): Boolean {
|
||||||
if (which == SimpleDialog.OnDialogResultListener.BUTTON_POSITIVE) {
|
if (which == SimpleDialog.OnDialogResultListener.BUTTON_POSITIVE) {
|
||||||
if (!isMonetEnabled) {
|
if (!isMonetEnabled) {
|
||||||
|
val prefs = getSharedPreferences(
|
||||||
|
ProfileStatsWidget.getPrefsName(appWidgetId),
|
||||||
|
Context.MODE_PRIVATE
|
||||||
|
)
|
||||||
when (dialogTag) {
|
when (dialogTag) {
|
||||||
ProfileStatsWidget.PREF_BACKGROUND_COLOR -> {
|
ProfileStatsWidget.PREF_BACKGROUND_COLOR -> {
|
||||||
getSharedPreferences(
|
prefs.edit()
|
||||||
ProfileStatsWidget.PREFS_NAME,
|
|
||||||
Context.MODE_PRIVATE
|
|
||||||
).edit()
|
|
||||||
.putInt(
|
.putInt(
|
||||||
ProfileStatsWidget.PREF_BACKGROUND_COLOR,
|
ProfileStatsWidget.PREF_BACKGROUND_COLOR,
|
||||||
extras.getInt(SimpleColorDialog.COLOR)
|
extras.getInt(SimpleColorDialog.COLOR)
|
||||||
|
@ -220,10 +225,7 @@ class ProfileStatsConfigure : AppCompatActivity(),
|
||||||
}
|
}
|
||||||
|
|
||||||
ProfileStatsWidget.PREF_BACKGROUND_FADE -> {
|
ProfileStatsWidget.PREF_BACKGROUND_FADE -> {
|
||||||
getSharedPreferences(
|
prefs.edit()
|
||||||
ProfileStatsWidget.PREFS_NAME,
|
|
||||||
Context.MODE_PRIVATE
|
|
||||||
).edit()
|
|
||||||
.putInt(
|
.putInt(
|
||||||
ProfileStatsWidget.PREF_BACKGROUND_FADE,
|
ProfileStatsWidget.PREF_BACKGROUND_FADE,
|
||||||
extras.getInt(SimpleColorDialog.COLOR)
|
extras.getInt(SimpleColorDialog.COLOR)
|
||||||
|
@ -234,10 +236,7 @@ class ProfileStatsConfigure : AppCompatActivity(),
|
||||||
}
|
}
|
||||||
|
|
||||||
ProfileStatsWidget.PREF_TITLE_TEXT_COLOR -> {
|
ProfileStatsWidget.PREF_TITLE_TEXT_COLOR -> {
|
||||||
getSharedPreferences(
|
prefs.edit()
|
||||||
ProfileStatsWidget.PREFS_NAME,
|
|
||||||
Context.MODE_PRIVATE
|
|
||||||
).edit()
|
|
||||||
.putInt(
|
.putInt(
|
||||||
ProfileStatsWidget.PREF_TITLE_TEXT_COLOR,
|
ProfileStatsWidget.PREF_TITLE_TEXT_COLOR,
|
||||||
extras.getInt(SimpleColorDialog.COLOR)
|
extras.getInt(SimpleColorDialog.COLOR)
|
||||||
|
@ -248,10 +247,7 @@ class ProfileStatsConfigure : AppCompatActivity(),
|
||||||
}
|
}
|
||||||
|
|
||||||
ProfileStatsWidget.PREF_STATS_TEXT_COLOR -> {
|
ProfileStatsWidget.PREF_STATS_TEXT_COLOR -> {
|
||||||
getSharedPreferences(
|
prefs.edit()
|
||||||
ProfileStatsWidget.PREFS_NAME,
|
|
||||||
Context.MODE_PRIVATE
|
|
||||||
).edit()
|
|
||||||
.putInt(
|
.putInt(
|
||||||
ProfileStatsWidget.PREF_STATS_TEXT_COLOR,
|
ProfileStatsWidget.PREF_STATS_TEXT_COLOR,
|
||||||
extras.getInt(SimpleColorDialog.COLOR)
|
extras.getInt(SimpleColorDialog.COLOR)
|
||||||
|
|
|
@ -5,28 +5,24 @@ import android.appwidget.AppWidgetManager
|
||||||
import android.appwidget.AppWidgetProvider
|
import android.appwidget.AppWidgetProvider
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.graphics.Bitmap
|
|
||||||
import android.graphics.BitmapFactory
|
|
||||||
import android.graphics.Color
|
import android.graphics.Color
|
||||||
import android.graphics.drawable.GradientDrawable
|
import android.graphics.drawable.GradientDrawable
|
||||||
|
import android.net.Uri
|
||||||
import android.widget.RemoteViews
|
import android.widget.RemoteViews
|
||||||
import androidx.core.content.res.ResourcesCompat
|
import androidx.core.content.res.ResourcesCompat
|
||||||
|
import androidx.core.graphics.drawable.toBitmap
|
||||||
import ani.dantotsu.MainActivity
|
import ani.dantotsu.MainActivity
|
||||||
import ani.dantotsu.R
|
import ani.dantotsu.R
|
||||||
import ani.dantotsu.connections.anilist.Anilist
|
import ani.dantotsu.connections.anilist.Anilist
|
||||||
import ani.dantotsu.profile.ProfileActivity
|
import ani.dantotsu.profile.ProfileActivity
|
||||||
import ani.dantotsu.settings.saving.PrefManager
|
import ani.dantotsu.settings.saving.PrefManager
|
||||||
import ani.dantotsu.settings.saving.PrefName
|
import ani.dantotsu.settings.saving.PrefName
|
||||||
import ani.dantotsu.util.BitmapUtil
|
import ani.dantotsu.util.BitmapUtil.Companion.downloadImageAsBitmap
|
||||||
import ani.dantotsu.widgets.WidgetSizeProvider
|
import ani.dantotsu.widgets.WidgetSizeProvider
|
||||||
import kotlinx.coroutines.DelicateCoroutinesApi
|
import kotlinx.coroutines.DelicateCoroutinesApi
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.runBlocking
|
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import tachiyomi.core.util.lang.launchIO
|
import tachiyomi.core.util.lang.launchIO
|
||||||
import java.io.InputStream
|
|
||||||
import java.net.HttpURLConnection
|
|
||||||
import java.net.URL
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implementation of App Widget functionality.
|
* Implementation of App Widget functionality.
|
||||||
|
@ -56,32 +52,6 @@ class ProfileStatsWidget : AppWidgetProvider() {
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
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)
|
@OptIn(DelicateCoroutinesApi::class)
|
||||||
fun updateAppWidget(
|
fun updateAppWidget(
|
||||||
context: Context,
|
context: Context,
|
||||||
|
@ -89,7 +59,7 @@ class ProfileStatsWidget : AppWidgetProvider() {
|
||||||
appWidgetId: Int
|
appWidgetId: Int
|
||||||
) {
|
) {
|
||||||
|
|
||||||
val prefs = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE)
|
val prefs = context.getSharedPreferences(getPrefsName(appWidgetId), Context.MODE_PRIVATE)
|
||||||
val backgroundColor =
|
val backgroundColor =
|
||||||
prefs.getInt(PREF_BACKGROUND_COLOR, Color.parseColor("#80000000"))
|
prefs.getInt(PREF_BACKGROUND_COLOR, Color.parseColor("#80000000"))
|
||||||
val backgroundFade = prefs.getInt(PREF_BACKGROUND_FADE, Color.parseColor("#00000000"))
|
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 {
|
val views = RemoteViews(context.packageName, R.layout.statistics_widget).apply {
|
||||||
setImageViewBitmap(
|
setImageViewBitmap(
|
||||||
R.id.backgroundView,
|
R.id.backgroundView,
|
||||||
BitmapUtil.convertDrawableToBitmap(
|
gradientDrawable.toBitmap(
|
||||||
gradientDrawable,
|
|
||||||
width,
|
width,
|
||||||
height
|
height
|
||||||
)
|
)
|
||||||
|
@ -133,6 +102,7 @@ class ProfileStatsWidget : AppWidgetProvider() {
|
||||||
1,
|
1,
|
||||||
Intent(context, ProfileStatsConfigure::class.java).apply {
|
Intent(context, ProfileStatsConfigure::class.java).apply {
|
||||||
putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId)
|
putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId)
|
||||||
|
data = Uri.parse(toUri(Intent.URI_INTENT_SCHEME))
|
||||||
},
|
},
|
||||||
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
|
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_COLOR = "background_color"
|
||||||
const val PREF_BACKGROUND_FADE = "background_fade"
|
const val PREF_BACKGROUND_FADE = "background_fade"
|
||||||
const val PREF_TITLE_TEXT_COLOR = "title_text_color"
|
const val PREF_TITLE_TEXT_COLOR = "title_text_color"
|
||||||
|
|
|
@ -12,6 +12,7 @@ import ani.dantotsu.connections.anilist.Anilist
|
||||||
import ani.dantotsu.media.Media
|
import ani.dantotsu.media.Media
|
||||||
import ani.dantotsu.settings.saving.PrefManager
|
import ani.dantotsu.settings.saving.PrefManager
|
||||||
import ani.dantotsu.settings.saving.PrefName
|
import ani.dantotsu.settings.saving.PrefName
|
||||||
|
import ani.dantotsu.util.BitmapUtil.Companion.downloadImageAsBitmap
|
||||||
import ani.dantotsu.util.BitmapUtil.Companion.roundCorners
|
import ani.dantotsu.util.BitmapUtil.Companion.roundCorners
|
||||||
import ani.dantotsu.util.Logger
|
import ani.dantotsu.util.Logger
|
||||||
import com.google.gson.GsonBuilder
|
import com.google.gson.GsonBuilder
|
||||||
|
@ -183,33 +184,6 @@ class UpcomingRemoteViewsFactory(private val context: Context) :
|
||||||
return rv
|
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 {
|
override fun getLoadingView(): RemoteViews {
|
||||||
return RemoteViews(context.packageName, R.layout.item_upcoming_widget)
|
return RemoteViews(context.packageName, R.layout.item_upcoming_widget)
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,9 +11,9 @@ import android.net.Uri
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.widget.RemoteViews
|
import android.widget.RemoteViews
|
||||||
import androidx.core.content.res.ResourcesCompat
|
import androidx.core.content.res.ResourcesCompat
|
||||||
|
import androidx.core.graphics.drawable.toBitmap
|
||||||
import ani.dantotsu.MainActivity
|
import ani.dantotsu.MainActivity
|
||||||
import ani.dantotsu.R
|
import ani.dantotsu.R
|
||||||
import ani.dantotsu.util.BitmapUtil.Companion.convertDrawableToBitmap
|
|
||||||
import ani.dantotsu.widgets.WidgetSizeProvider
|
import ani.dantotsu.widgets.WidgetSizeProvider
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -97,7 +97,7 @@ class UpcomingWidget : AppWidgetProvider() {
|
||||||
intentTemplate.putExtra("fromWidget", true)
|
intentTemplate.putExtra("fromWidget", true)
|
||||||
|
|
||||||
val views = RemoteViews(context.packageName, R.layout.upcoming_widget).apply {
|
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_title, titleTextColor)
|
||||||
setTextColor(R.id.text_show_countdown, countdownTextColor)
|
setTextColor(R.id.text_show_countdown, countdownTextColor)
|
||||||
setTextColor(R.id.widgetTitle, titleTextColor)
|
setTextColor(R.id.widgetTitle, titleTextColor)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue