Profile Stats Widget (#292)
* feat: create a statistics widget * feat: mirror app color option * fix: the minimum size cut off * feat: make the stat widget decent * fix: prevent bleeding edges * fix: PREVENT BLEEDING EDGES! * fix: we didn't really need an overlay
This commit is contained in:
parent
7bcc01b94e
commit
f83d1d8d84
16 changed files with 652 additions and 2 deletions
|
@ -0,0 +1,245 @@
|
|||
package ani.dantotsu.widgets.statistics
|
||||
|
||||
import android.app.PendingIntent
|
||||
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.widget.RemoteViews
|
||||
import androidx.core.content.res.ResourcesCompat
|
||||
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.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.
|
||||
*/
|
||||
class ProfileStatsWidget : AppWidgetProvider() {
|
||||
override fun onUpdate(
|
||||
context: Context,
|
||||
appWidgetManager: AppWidgetManager,
|
||||
appWidgetIds: IntArray
|
||||
) {
|
||||
appWidgetIds.forEach { appWidgetId ->
|
||||
updateAppWidget(context, appWidgetManager, appWidgetId)
|
||||
}
|
||||
super.onUpdate(context, appWidgetManager, appWidgetIds)
|
||||
}
|
||||
|
||||
override fun onDeleted(context: Context, appWidgetIds: IntArray) {
|
||||
super.onDeleted(context, appWidgetIds)
|
||||
}
|
||||
|
||||
override fun onEnabled(context: Context) {
|
||||
super.onEnabled(context)
|
||||
}
|
||||
|
||||
override fun onDisabled(context: Context) {
|
||||
super.onDisabled(context)
|
||||
}
|
||||
|
||||
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,
|
||||
appWidgetManager: AppWidgetManager,
|
||||
appWidgetId: Int
|
||||
) {
|
||||
|
||||
val prefs = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE)
|
||||
val backgroundColor =
|
||||
prefs.getInt(PREF_BACKGROUND_COLOR, Color.parseColor("#80000000"))
|
||||
val backgroundFade = prefs.getInt(PREF_BACKGROUND_FADE, Color.parseColor("#00000000"))
|
||||
val titleTextColor = prefs.getInt(PREF_TITLE_TEXT_COLOR, Color.WHITE)
|
||||
|
||||
val gradientDrawable = ResourcesCompat.getDrawable(
|
||||
context.resources,
|
||||
R.drawable.linear_gradient_black,
|
||||
null
|
||||
) as GradientDrawable
|
||||
gradientDrawable.colors = intArrayOf(backgroundColor, backgroundFade)
|
||||
val widgetSizeProvider = WidgetSizeProvider(context)
|
||||
var (width, height) = widgetSizeProvider.getWidgetsSize(appWidgetId)
|
||||
if (width > 0 && height > 0) {
|
||||
gradientDrawable.cornerRadius = 64f
|
||||
} else {
|
||||
width = 300
|
||||
height = 300
|
||||
}
|
||||
|
||||
launchIO {
|
||||
val userPref = PrefManager.getVal(PrefName.AnilistUserId, "")
|
||||
val userId = if (userPref.isNotEmpty()) userPref.toInt() else Anilist.userid
|
||||
?: if (Anilist.query.getUserData()) Anilist.userid else null
|
||||
userId?.let {
|
||||
val respond = Anilist.query.getUserProfile(it)
|
||||
respond?.data?.user?.let { user ->
|
||||
withContext(Dispatchers.Main) {
|
||||
val views = RemoteViews(context.packageName, R.layout.statistics_widget).apply {
|
||||
setImageViewBitmap(
|
||||
R.id.backgroundView,
|
||||
BitmapUtil.convertDrawableToBitmap(
|
||||
gradientDrawable,
|
||||
width,
|
||||
height
|
||||
)
|
||||
)
|
||||
setTextColor(R.id.topLeftItem, titleTextColor)
|
||||
setTextColor(R.id.topLeftLabel, titleTextColor)
|
||||
setTextColor(R.id.topRightItem, titleTextColor)
|
||||
setTextColor(R.id.topRightLabel, titleTextColor)
|
||||
setTextColor(R.id.bottomLeftItem, titleTextColor)
|
||||
setTextColor(R.id.bottomLeftLabel, titleTextColor)
|
||||
setTextColor(R.id.bottomRightItem, titleTextColor)
|
||||
setTextColor(R.id.bottomRightLabel, titleTextColor)
|
||||
|
||||
setImageViewBitmap(
|
||||
R.id.userAvatar,
|
||||
user.avatar?.medium?.let { it1 -> downloadImageAsBitmap(it1) }
|
||||
)
|
||||
setTextViewText(
|
||||
R.id.userLabel,
|
||||
context.getString(R.string.user_stats, user.name)
|
||||
)
|
||||
|
||||
setTextViewText(
|
||||
R.id.topLeftItem,
|
||||
user.statistics.anime.count.toString()
|
||||
)
|
||||
setTextViewText(
|
||||
R.id.topLeftLabel,
|
||||
context.getString(R.string.anime_watched)
|
||||
)
|
||||
|
||||
setTextViewText(
|
||||
R.id.topRightItem,
|
||||
user.statistics.anime.episodesWatched.toString()
|
||||
)
|
||||
setTextViewText(
|
||||
R.id.topRightLabel,
|
||||
context.getString(R.string.episodes_watched)
|
||||
)
|
||||
|
||||
setTextViewText(
|
||||
R.id.bottomLeftItem,
|
||||
user.statistics.manga.count.toString()
|
||||
)
|
||||
setTextViewText(
|
||||
R.id.bottomLeftLabel,
|
||||
context.getString(R.string.manga_read)
|
||||
)
|
||||
|
||||
setTextViewText(
|
||||
R.id.bottomRightItem,
|
||||
user.statistics.manga.chaptersRead.toString()
|
||||
)
|
||||
setTextViewText(
|
||||
R.id.bottomRightLabel,
|
||||
context.getString(R.string.chapters_read)
|
||||
)
|
||||
|
||||
val intent = Intent(context, ProfileActivity::class.java)
|
||||
.putExtra("userId", it)
|
||||
val pendingIntent = PendingIntent.getActivity(
|
||||
context, 0, intent, PendingIntent.FLAG_IMMUTABLE
|
||||
)
|
||||
setOnClickPendingIntent(R.id.widgetContainer, pendingIntent)
|
||||
}
|
||||
// Instruct the widget manager to update the widget
|
||||
appWidgetManager.updateAppWidget(appWidgetId, views)
|
||||
}
|
||||
} ?: showLoginCascade(context, appWidgetManager, appWidgetId)
|
||||
} ?: showLoginCascade(context, appWidgetManager, appWidgetId)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun showLoginCascade(
|
||||
context: Context, appWidgetManager: AppWidgetManager, appWidgetId: Int
|
||||
) {
|
||||
|
||||
withContext(Dispatchers.Main) {
|
||||
val views = RemoteViews(context.packageName, R.layout.statistics_widget)
|
||||
|
||||
views.setTextViewText(R.id.topLeftItem, "")
|
||||
views.setTextViewText(
|
||||
R.id.topLeftLabel,
|
||||
context.getString(R.string.please)
|
||||
)
|
||||
|
||||
views.setTextViewText(R.id.topRightItem, "")
|
||||
views.setTextViewText(
|
||||
R.id.topRightLabel,
|
||||
context.getString(R.string.log_in)
|
||||
)
|
||||
|
||||
views.setTextViewText(
|
||||
R.id.bottomLeftItem,
|
||||
context.getString(R.string.or_join)
|
||||
)
|
||||
views.setTextViewText(R.id.bottomLeftLabel, "")
|
||||
|
||||
views.setTextViewText(
|
||||
R.id.bottomRightItem,
|
||||
context.getString(R.string.anilist)
|
||||
)
|
||||
views.setTextViewText(R.id.bottomRightLabel, "")
|
||||
|
||||
val intent = Intent(context, MainActivity::class.java)
|
||||
val pendingIntent = PendingIntent.getActivity(
|
||||
context, 0, intent, PendingIntent.FLAG_IMMUTABLE
|
||||
)
|
||||
views.setOnClickPendingIntent(R.id.widgetContainer, pendingIntent)
|
||||
|
||||
appWidgetManager.updateAppWidget(appWidgetId, views)
|
||||
}
|
||||
}
|
||||
|
||||
const val PREFS_NAME = "ani.dantotsu.widgets.ResumableWidget"
|
||||
const val PREF_BACKGROUND_COLOR = "background_color"
|
||||
const val PREF_BACKGROUND_FADE = "background_fade"
|
||||
const val PREF_TITLE_TEXT_COLOR = "title_text_color"
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue