diff --git a/app/src/main/java/ani/dantotsu/media/manga/mangareader/MangaReaderActivity.kt b/app/src/main/java/ani/dantotsu/media/manga/mangareader/MangaReaderActivity.kt index 30fefb2652..07e355845f 100644 --- a/app/src/main/java/ani/dantotsu/media/manga/mangareader/MangaReaderActivity.kt +++ b/app/src/main/java/ani/dantotsu/media/manga/mangareader/MangaReaderActivity.kt @@ -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(R.id.dialog_checkbox) checkbox.text = getString(R.string.dont_ask_again, media.userPreferredName) @@ -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() diff --git a/app/src/main/java/ani/dantotsu/widgets/CurrentlyAiringRemoteViewsFactory.kt b/app/src/main/java/ani/dantotsu/widgets/CurrentlyAiringRemoteViewsFactory.kt new file mode 100644 index 0000000000..9c0f2ceb94 --- /dev/null +++ b/app/src/main/java/ani/dantotsu/widgets/CurrentlyAiringRemoteViewsFactory.kt @@ -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() + + 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) \ No newline at end of file diff --git a/app/src/main/java/ani/dantotsu/widgets/CurrentlyAiringRemoteViewsService.kt b/app/src/main/java/ani/dantotsu/widgets/CurrentlyAiringRemoteViewsService.kt new file mode 100644 index 0000000000..859570e428 --- /dev/null +++ b/app/src/main/java/ani/dantotsu/widgets/CurrentlyAiringRemoteViewsService.kt @@ -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) + } +} diff --git a/app/src/main/java/ani/dantotsu/widgets/CurrentlyAiringWidget.kt b/app/src/main/java/ani/dantotsu/widgets/CurrentlyAiringWidget.kt new file mode 100644 index 0000000000..f9a868d61d --- /dev/null +++ b/app/src/main/java/ani/dantotsu/widgets/CurrentlyAiringWidget.kt @@ -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) +} \ No newline at end of file diff --git a/app/src/main/java/ani/dantotsu/widgets/CurrentlyAiringWidgetConfigureActivity.kt b/app/src/main/java/ani/dantotsu/widgets/CurrentlyAiringWidgetConfigureActivity.kt new file mode 100644 index 0000000000..1bd8564830 --- /dev/null +++ b/app/src/main/java/ani/dantotsu/widgets/CurrentlyAiringWidgetConfigureActivity.kt @@ -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() +} \ No newline at end of file diff --git a/app/src/main/res/drawable-v21/app_widget_background.xml b/app/src/main/res/drawable-v21/app_widget_background.xml new file mode 100644 index 0000000000..785445c66c --- /dev/null +++ b/app/src/main/res/drawable-v21/app_widget_background.xml @@ -0,0 +1,10 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable-v21/app_widget_inner_view_background.xml b/app/src/main/res/drawable-v21/app_widget_inner_view_background.xml new file mode 100644 index 0000000000..007e2872f4 --- /dev/null +++ b/app/src/main/res/drawable-v21/app_widget_inner_view_background.xml @@ -0,0 +1,10 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/gradient_background.xml b/app/src/main/res/drawable/gradient_background.xml new file mode 100644 index 0000000000..20e9ffd6d0 --- /dev/null +++ b/app/src/main/res/drawable/gradient_background.xml @@ -0,0 +1,8 @@ + + + + diff --git a/app/src/main/res/drawable/ic_dantotsu_round.xml b/app/src/main/res/drawable/ic_dantotsu_round.xml new file mode 100644 index 0000000000..5b43bb3443 --- /dev/null +++ b/app/src/main/res/drawable/ic_dantotsu_round.xml @@ -0,0 +1,42 @@ + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/currently_airing_widget.xml b/app/src/main/res/layout/currently_airing_widget.xml new file mode 100644 index 0000000000..69d47e4866 --- /dev/null +++ b/app/src/main/res/layout/currently_airing_widget.xml @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/currently_airing_widget_configure.xml b/app/src/main/res/layout/currently_airing_widget_configure.xml new file mode 100644 index 0000000000..af0b9d7272 --- /dev/null +++ b/app/src/main/res/layout/currently_airing_widget_configure.xml @@ -0,0 +1,28 @@ + + + + + + + +