Skip to content

Commit

Permalink
widget outline
Browse files Browse the repository at this point in the history
  • Loading branch information
rebelonion committed Dec 9, 2023
1 parent 82bc215 commit b6be707
Show file tree
Hide file tree
Showing 23 changed files with 681 additions and 21 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -795,7 +795,7 @@ class MangaReaderActivity : AppCompatActivity() {

private fun progress(runnable: Runnable) {
if (maxChapterPage - currentChapterPage <= 1 && Anilist.userid != null) {
if (showProgressDialog) {
if (showProgressDialog) {
val dialogView = layoutInflater.inflate(R.layout.item_custom_dialog, null)
val checkbox = dialogView.findViewById<CheckBox>(R.id.dialog_checkbox)
checkbox.text = getString(R.string.dont_ask_again, media.userPreferredName)
Expand All @@ -810,7 +810,7 @@ class MangaReaderActivity : AppCompatActivity() {
.setCancelable(false)
.setPositiveButton(getString(R.string.yes)) { dialog, _ ->
saveData("${media.id}_save_progress", true)
updateProgress(
updateProgress(
media,
MangaNameAdapter.findChapterNumber(media.manga!!.selectedChapter!!)
.toString()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
package ani.dantotsu.widgets

import android.content.Context
import android.content.Intent
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.net.Uri
import android.widget.RemoteViews
import android.widget.RemoteViewsService
import androidx.core.net.toUri
import ani.dantotsu.R
import ani.dantotsu.logger
import java.io.InputStream
import java.net.HttpURLConnection
import java.net.URL

class CurrentlyAiringRemoteViewsFactory(private val context: Context, intent: Intent) : RemoteViewsService.RemoteViewsFactory {
private var widgetItems = mutableListOf<WidgetItem>()

override fun onCreate() {
// 4 items for testing
widgetItems.clear()
logger("CurrentlyAiringRemoteViewsFactory onCreate")
widgetItems = List(4) {
WidgetItem("Show $it", "$it days $it hours $it minutes", "https://s4.anilist.co/file/anilistcdn/media/anime/cover/large/bx14741-alxqoP4yx6WF.jpg")
}.toMutableList()
}

override fun onDataSetChanged() {
// 4 items for testing
logger("CurrentlyAiringRemoteViewsFactory onDataSetChanged")
widgetItems.clear()
widgetItems.add(WidgetItem("Show 1", "1 day 2 hours 3 minutes", "https://s4.anilist.co/file/anilistcdn/media/anime/cover/large/bx14741-alxqoP4yx6WF.jpg"))
widgetItems.add(WidgetItem("Show 2", "2 days 3 hours 4 minutes", "https://s4.anilist.co/file/anilistcdn/media/anime/cover/large/bx14741-alxqoP4yx6WF.jpg"))
widgetItems.add(WidgetItem("Show 3", "3 days 4 hours 5 minutes", "https://s4.anilist.co/file/anilistcdn/media/anime/cover/large/bx14741-alxqoP4yx6WF.jpg"))
widgetItems.add(WidgetItem("Show 4", "4 days 5 hours 6 minutes", "https://s4.anilist.co/file/anilistcdn/media/anime/cover/large/bx14741-alxqoP4yx6WF.jpg"))
widgetItems.add(WidgetItem("Show 5", "5 days 6 hours 7 minutes", "https://s4.anilist.co/file/anilistcdn/media/anime/cover/large/bx14741-alxqoP4yx6WF.jpg"))
}

override fun onDestroy() {
widgetItems.clear()
}

override fun getCount(): Int {
return widgetItems.size
}

override fun getViewAt(position: Int): RemoteViews {
logger("CurrentlyAiringRemoteViewsFactory getViewAt")
val item = widgetItems[position]
val rv = RemoteViews(context.packageName, R.layout.item_currently_airing_widget).apply {
setTextViewText(R.id.text_show_title, item.title)
setTextViewText(R.id.text_show_countdown, item.countdown)
val bitmap = downloadImageAsBitmap(item.image)
//setImageViewUri(R.id.image_show_icon, Uri.parse(item.image))
setImageViewBitmap(R.id.image_show_icon, bitmap)
}

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()
// Handle the error according to your needs
} finally {
// Clean up resources
inputStream?.close()
urlConnection?.disconnect()
}

return bitmap
}

override fun getLoadingView(): RemoteViews {
return RemoteViews(context.packageName, R.layout.item_currently_airing_widget)
}

override fun getViewTypeCount(): Int {
return 1
}

override fun getItemId(p0: Int): Long {
return p0.toLong()
}

override fun hasStableIds(): Boolean {
return true
}
}

data class WidgetItem(val title: String, val countdown: String, val image: String)
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package ani.dantotsu.widgets

import android.content.Intent
import android.widget.RemoteViewsService
import ani.dantotsu.logger
class CurrentlyAiringRemoteViewsService : RemoteViewsService() {
override fun onGetViewFactory(intent: Intent): RemoteViewsFactory {
logger("CurrentlyAiringRemoteViewsFactory onGetViewFactory")
return CurrentlyAiringRemoteViewsFactory(applicationContext, intent)
}
}
101 changes: 101 additions & 0 deletions app/src/main/java/ani/dantotsu/widgets/CurrentlyAiringWidget.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
package ani.dantotsu.widgets

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.Canvas
import android.graphics.Color
import android.graphics.drawable.Drawable
import android.graphics.drawable.GradientDrawable
import android.net.Uri
import android.widget.RemoteViews
import androidx.core.content.res.ResourcesCompat
import ani.dantotsu.R

/**
* Implementation of App Widget functionality.
* App Widget Configuration implemented in [CurrentlyAiringWidgetConfigureActivity]
*/
class CurrentlyAiringWidget : AppWidgetProvider() {
override fun onUpdate(context: Context, appWidgetManager: AppWidgetManager, appWidgetIds: IntArray) {
appWidgetIds.forEach { appWidgetId ->
val intent = Intent(context, CurrentlyAiringRemoteViewsService::class.java).apply {
putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId)
data = Uri.parse(toUri(Intent.URI_INTENT_SCHEME))
}

val rv = RemoteViews(context.packageName, R.layout.currently_airing_widget).apply {
setRemoteAdapter(R.id.widgetListView, intent)
setEmptyView(R.id.widgetListView, R.id.empty_view)
}

appWidgetManager.updateAppWidget(appWidgetId, rv)
}
super.onUpdate(context, appWidgetManager, appWidgetIds)
}
override fun onDeleted(context: Context, appWidgetIds: IntArray) {
// When the user deletes the widget, delete the preference associated with it.
for (appWidgetId in appWidgetIds) {
deleteTitlePref(context, appWidgetId)
}
super.onDeleted(context, appWidgetIds)
}

override fun onEnabled(context: Context) {
super.onEnabled(context)
}

override fun onDisabled(context: Context) {
super.onDisabled(context)
}
companion object {
fun updateAppWidget(
context: Context,
appWidgetManager: AppWidgetManager,
appWidgetId: Int,
color: Int
) {
// Create an intent to launch the configuration activity when the widget is clicked
val intent = Intent(context, CurrentlyAiringWidgetConfigureActivity::class.java)
val pendingIntent = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_IMMUTABLE)

// Get the gradient drawable resource and update its start color with the user-selected color
val gradientDrawable = ResourcesCompat.getDrawable(context.resources, R.drawable.gradient_background, null) as GradientDrawable
gradientDrawable.colors = intArrayOf(color, Color.GRAY) // End color is gray.

// Create the RemoteViews object and set the background
val views = RemoteViews(context.packageName, R.layout.currently_airing_widget).apply {
//setImageViewBitmap(R.id.backgroundView, convertDrawableToBitmap(gradientDrawable))
//setOnClickPendingIntent(R.id.backgroundView, pendingIntent)
}

// Instruct the widget manager to update the widget
appWidgetManager.updateAppWidget(appWidgetId, views)
}

private fun convertDrawableToBitmap(drawable: Drawable): Bitmap {
val bitmap = Bitmap.createBitmap(100, 300, Bitmap.Config.ARGB_8888)
val canvas = Canvas(bitmap)
drawable.setBounds(0, 0, canvas.width, canvas.height)
drawable.draw(canvas)
return bitmap
}
}
}

internal fun updateAppWidget(
context: Context,
appWidgetManager: AppWidgetManager,
appWidgetId: Int
) {
val widgetText = loadTitlePref(context, appWidgetId)
// Construct the RemoteViews object
val views = RemoteViews(context.packageName, R.layout.currently_airing_widget)
views.setTextViewText(R.id.appwidget_text, widgetText)

// Instruct the widget manager to update the widget
appWidgetManager.updateAppWidget(appWidgetId, views)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
package ani.dantotsu.widgets

import android.app.Activity
import android.appwidget.AppWidgetManager
import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.view.View
import android.widget.EditText
import ani.dantotsu.R
import ani.dantotsu.databinding.CurrentlyAiringWidgetConfigureBinding
import ani.dantotsu.others.LangSet
import ani.dantotsu.themes.ThemeManager

/**
* The configuration screen for the [CurrentlyAiringWidget] AppWidget.
*/
class CurrentlyAiringWidgetConfigureActivity : Activity() {
private var appWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID
private lateinit var appWidgetText: EditText
private var onClickListener = View.OnClickListener {
val context = this@CurrentlyAiringWidgetConfigureActivity

// When the button is clicked, store the string locally
val widgetText = appWidgetText.text.toString()
saveTitlePref(context, appWidgetId, widgetText)

// It is the responsibility of the configuration activity to update the app widget
val appWidgetManager = AppWidgetManager.getInstance(context)
//updateAppWidget(context, appWidgetManager, appWidgetId)


CurrentlyAiringWidget.updateAppWidget(
context,
appWidgetManager,
appWidgetId,
-1
)

// Make sure we pass back the original appWidgetId
val resultValue = Intent()
resultValue.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId)
setResult(RESULT_OK, resultValue)
finish()
}
private lateinit var binding: CurrentlyAiringWidgetConfigureBinding

public override fun onCreate(icicle: Bundle?) {
LangSet.setLocale(this)
ThemeManager(this).applyTheme()
super.onCreate(icicle)

// Set the result to CANCELED. This will cause the widget host to cancel
// out of the widget placement if the user presses the back button.
setResult(RESULT_CANCELED)

binding = CurrentlyAiringWidgetConfigureBinding.inflate(layoutInflater)
setContentView(binding.root)

appWidgetText = binding.appwidgetText as EditText
binding.addButton.setOnClickListener(onClickListener)

// Find the widget id from the intent.
val intent = intent
val extras = intent.extras
if (extras != null) {
appWidgetId = extras.getInt(
AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID
)
}

// If this activity was started with an intent without an app widget ID, finish with an error.
if (appWidgetId == AppWidgetManager.INVALID_APPWIDGET_ID) {
finish()
return
}

appWidgetText.setText(
loadTitlePref(
this@CurrentlyAiringWidgetConfigureActivity,
appWidgetId
)
)

}

}

private const val PREFS_NAME = "ani.dantotsu.parsers.CurrentlyAiringWidget"
private const val PREF_PREFIX_KEY = "appwidget_"

// Write the prefix to the SharedPreferences object for this widget
internal fun saveTitlePref(context: Context, appWidgetId: Int, text: String) {
val prefs = context.getSharedPreferences(PREFS_NAME, 0).edit()
prefs.putString(PREF_PREFIX_KEY + appWidgetId, text)
prefs.apply()
}

// Read the prefix from the SharedPreferences object for this widget.
// If there is no preference saved, get the default from a resource
internal fun loadTitlePref(context: Context, appWidgetId: Int): String {
val prefs = context.getSharedPreferences(PREFS_NAME, 0)
val titleValue = prefs.getString(PREF_PREFIX_KEY + appWidgetId, null)
return titleValue ?: context.getString(R.string.appwidget_text)
}

internal fun deleteTitlePref(context: Context, appWidgetId: Int) {
val prefs = context.getSharedPreferences(PREFS_NAME, 0).edit()
prefs.remove(PREF_PREFIX_KEY + appWidgetId)
prefs.apply()
}
10 changes: 10 additions & 0 deletions app/src/main/res/drawable-v21/app_widget_background.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?><!--
Background for widgets to make the rounded corners based on the
appWidgetRadius attribute value
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">

<corners android:radius="?attr/appWidgetRadius" />
<solid android:color="?android:attr/colorBackground" />
</shape>
10 changes: 10 additions & 0 deletions app/src/main/res/drawable-v21/app_widget_inner_view_background.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?><!--
Background for views inside widgets to make the rounded corners based on the
appWidgetInnerRadius attribute value
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">

<corners android:radius="?attr/appWidgetInnerRadius" />
<solid android:color="?android:attr/colorAccent" />
</shape>
8 changes: 8 additions & 0 deletions app/src/main/res/drawable/gradient_background.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<gradient
android:angle="270"
android:endColor="@color/grey_20"
android:startColor="#B313DC" />
</shape>
Loading

0 comments on commit b6be707

Please sign in to comment.