diff --git a/.github/workflows/beta.yml b/.github/workflows/beta.yml index f5c7d049..a9145485 100644 --- a/.github/workflows/beta.yml +++ b/.github/workflows/beta.yml @@ -28,11 +28,17 @@ jobs: java-version: 17 cache: gradle + - name: Decode Keystore File + run: echo "${{ secrets.KEYSTORE_FILE }}" | base64 -d > $GITHUB_WORKSPACE/key.keystore + + - name: List files in the directory + run: ls -l + - name: Make gradlew executable run: chmod +x ./gradlew - name: Build with Gradle - run: ./gradlew assembleDebug + run: ./gradlew assembleDebug -Pandroid.injected.signing.store.file=$GITHUB_WORKSPACE/key.keystore -Pandroid.injected.signing.store.password=${{ secrets.KEYSTORE_PASSWORD }} -Pandroid.injected.signing.key.alias=${{ secrets.KEY_ALIAS }} -Pandroid.injected.signing.key.password=${{ secrets.KEY_PASSWORD }} - name: Upload a Build Artifact uses: actions/upload-artifact@v3.0.0 diff --git a/app/build.gradle b/app/build.gradle index 1cde5bdb..0f029289 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -100,7 +100,7 @@ dependencies { implementation 'com.alexvasilkov:gesture-views:2.8.3' implementation 'com.github.VipulOG:ebook-reader:0.1.6' implementation 'androidx.paging:paging-runtime-ktx:3.2.1' - implementation "com.github.skydoves:colorpickerview:2.3.0" + implementation 'com.github.eltos:simpledialogfragments:v3.7' // string matching implementation 'me.xdrop:fuzzywuzzy:1.4.0' diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index a77c0d31..cd7cec87 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -17,25 +17,22 @@ - - - + - - - + - - - + - - @@ -48,6 +45,7 @@ + tools:ignore="AllowBackup"> + + + + + + + + + android:exported="true"> + @@ -71,13 +81,11 @@ - - @@ -103,9 +111,9 @@ - + - - - + - + - + - - + + + - + android:exported="false" + android:theme="@android:style/Theme.Translucent.NoTitleBar" /> + android:exported="false" + android:theme="@android:style/Theme.Translucent.NoTitleBar" /> - + @@ -258,30 +266,37 @@ android:resource="@xml/provider_paths" /> - + - - + + + - - - - - - - - - + + diff --git a/app/src/main/java/ani/dantotsu/home/AnimePageAdapter.kt b/app/src/main/java/ani/dantotsu/home/AnimePageAdapter.kt index 65b69860..4e5b23ad 100644 --- a/app/src/main/java/ani/dantotsu/home/AnimePageAdapter.kt +++ b/app/src/main/java/ani/dantotsu/home/AnimePageAdapter.kt @@ -75,6 +75,12 @@ class AnimePageAdapter : RecyclerView.Adapter("ui_settings") ?: UserInterfaceSettings() + fun load() { if (activity != null && _binding != null) lifecycleScope.launch(Dispatchers.Main) { binding.homeUserName.text = Anilist.username @@ -111,7 +115,12 @@ class HomeFragment : Fragment() { snackString(currContext()?.getString(R.string.please_reload)) } } - + val incognito = currContext()?.getSharedPreferences("Dantotsu", Context.MODE_PRIVATE) + ?.getBoolean("incognito", false) ?: false + if(incognito) { + binding.incognitoTextView.visibility = View.VISIBLE + binding.incognitoView.visibility = View.VISIBLE + } binding.homeUserAvatarContainer.setSafeOnClickListener { val dialogFragment = SettingsDialogFragment.newInstance(SettingsDialogFragment.Companion.PageType.HOME) diff --git a/app/src/main/java/ani/dantotsu/home/MangaPageAdapter.kt b/app/src/main/java/ani/dantotsu/home/MangaPageAdapter.kt index 17c0bf06..0b999b1e 100644 --- a/app/src/main/java/ani/dantotsu/home/MangaPageAdapter.kt +++ b/app/src/main/java/ani/dantotsu/home/MangaPageAdapter.kt @@ -75,6 +75,13 @@ class MangaPageAdapter : RecyclerView.Adapter { diff --git a/app/src/main/java/ani/dantotsu/media/anime/ExoplayerView.kt b/app/src/main/java/ani/dantotsu/media/anime/ExoplayerView.kt index f16f47ef..c7cd6426 100644 --- a/app/src/main/java/ani/dantotsu/media/anime/ExoplayerView.kt +++ b/app/src/main/java/ani/dantotsu/media/anime/ExoplayerView.kt @@ -1101,7 +1101,7 @@ class ExoplayerView : AppCompatActivity(), Player.Listener { if (settings.cursedSpeeds) arrayOf(1f, 1.25f, 1.5f, 1.75f, 2f, 2.5f, 3f, 4f, 5f, 10f, 25f, 50f) else - arrayOf(0.25f, 0.33f, 0.5f, 0.66f, 0.75f, 1f, 1.25f, 1.33f, 1.5f, 1.66f, 1.75f, 2f) + arrayOf(0.25f, 0.33f, 0.5f, 0.66f, 0.75f, 1f, 1.15f, 1.25f, 1.33f, 1.5f, 1.66f, 1.75f, 2f) val speedsName = speeds.map { "${it}x" }.toTypedArray() var curSpeed = loadData("${media.id}_speed", this) ?: settings.defaultSpeed @@ -1156,14 +1156,18 @@ class ExoplayerView : AppCompatActivity(), Player.Listener { } preloading = false + val incognito = currContext()?.getSharedPreferences("Dantotsu", Context.MODE_PRIVATE) + ?.getBoolean("incognito", false) ?: false val showProgressDialog = if (settings.askIndividual) loadData("${media.id}_progressDialog") ?: true else false if (showProgressDialog && Anilist.userid != null && if (media.isAdult) settings.updateForH else true) AlertDialog.Builder(this, R.style.MyPopup) .setTitle(getString(R.string.auto_update, media.userPreferredName)) - .setMessage(getString(R.string.incognito_will_not_update)) .apply { + if (incognito) { + setMessage(getString(R.string.incognito_will_not_update)) + } setOnCancelListener { hideSystemBars() } setCancelable(false) setPositiveButton(getString(R.string.yes)) { dialog, _ -> 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 30fefb26..d6d72871 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 @@ -3,6 +3,7 @@ package ani.dantotsu.media.manga.mangareader import android.animation.ObjectAnimator import android.annotation.SuppressLint import android.app.AlertDialog +import android.content.Context import android.content.Intent import android.content.res.Configuration import android.graphics.Bitmap @@ -795,7 +796,8 @@ 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) @@ -803,14 +805,20 @@ class MangaReaderActivity : AppCompatActivity() { saveData("${media.id}_progressDialog", isChecked) showProgressDialog = !isChecked } - + val incognito = currContext()?.getSharedPreferences("Dantotsu", Context.MODE_PRIVATE) + ?.getBoolean("incognito", false) ?: false AlertDialog.Builder(this, R.style.MyPopup) .setTitle(getString(R.string.title_update_progress)) + .apply { + if (incognito) { + setMessage(getString(R.string.incognito_will_not_update)) + } + } .setView(dialogView) .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/media/user/ListActivity.kt b/app/src/main/java/ani/dantotsu/media/user/ListActivity.kt index ada753a4..342a5a8f 100644 --- a/app/src/main/java/ani/dantotsu/media/user/ListActivity.kt +++ b/app/src/main/java/ani/dantotsu/media/user/ListActivity.kt @@ -62,7 +62,7 @@ class ListActivity : AppCompatActivity() { binding.listTabLayout.setTabTextColors(secondaryTextColor, primaryTextColor) binding.listTabLayout.setSelectedTabIndicatorColor(primaryTextColor) val uiSettings = loadData("ui_settings") ?: UserInterfaceSettings() - if (!uiSettings.immersiveMode) { + if (!uiSettings.immersiveModeList) { this.window.statusBarColor = ContextCompat.getColor(this, R.color.nav_bg_inv) binding.root.fitsSystemWindows = true diff --git a/app/src/main/java/ani/dantotsu/others/DisabledReports.kt b/app/src/main/java/ani/dantotsu/others/DisabledReports.kt index ea2c779a..ed8f5813 100644 --- a/app/src/main/java/ani/dantotsu/others/DisabledReports.kt +++ b/app/src/main/java/ani/dantotsu/others/DisabledReports.kt @@ -2,3 +2,4 @@ package ani.dantotsu.others const val DisabledReports = false //Setting this to false, will allow sending crash reports to Dantotsu's Firebase Crashlytics +//if you want a custom build without crash reporting, set this to true \ No newline at end of file diff --git a/app/src/main/java/ani/dantotsu/settings/PlayerSettingsActivity.kt b/app/src/main/java/ani/dantotsu/settings/PlayerSettingsActivity.kt index ff3d704d..3ed493f7 100644 --- a/app/src/main/java/ani/dantotsu/settings/PlayerSettingsActivity.kt +++ b/app/src/main/java/ani/dantotsu/settings/PlayerSettingsActivity.kt @@ -97,7 +97,7 @@ class PlayerSettingsActivity : AppCompatActivity() { val speeds = - arrayOf(0.25f, 0.33f, 0.5f, 0.66f, 0.75f, 1f, 1.25f, 1.33f, 1.5f, 1.66f, 1.75f, 2f) + arrayOf(0.25f, 0.33f, 0.5f, 0.66f, 0.75f, 1f, 1.15f, 1.25f, 1.33f, 1.5f, 1.66f, 1.75f, 2f) val cursedSpeeds = arrayOf(1f, 1.25f, 1.5f, 1.75f, 2f, 2.5f, 3f, 4f, 5f, 10f, 25f, 50f) var curSpeedArr = if (settings.cursedSpeeds) cursedSpeeds else speeds var speedsName = curSpeedArr.map { "${it}x" }.toTypedArray() diff --git a/app/src/main/java/ani/dantotsu/settings/SettingsActivity.kt b/app/src/main/java/ani/dantotsu/settings/SettingsActivity.kt index 6d1e70df..c037959a 100644 --- a/app/src/main/java/ani/dantotsu/settings/SettingsActivity.kt +++ b/app/src/main/java/ani/dantotsu/settings/SettingsActivity.kt @@ -4,6 +4,7 @@ import android.annotation.SuppressLint import android.app.AlertDialog import android.content.Context import android.content.Intent +import android.graphics.Color import android.graphics.drawable.Animatable import android.os.Build.* import android.os.Build.VERSION.* @@ -11,7 +12,6 @@ import android.os.Bundle import android.view.View import android.view.ViewGroup import android.widget.ArrayAdapter -import android.widget.LinearLayout import android.widget.TextView import android.widget.Toast import androidx.activity.OnBackPressedCallback @@ -37,7 +37,9 @@ import ani.dantotsu.subcriptions.Subscription.Companion.timeMinutes import ani.dantotsu.themes.ThemeManager import com.google.android.material.snackbar.Snackbar import com.google.android.material.textfield.TextInputEditText -import com.skydoves.colorpickerview.listeners.ColorListener +import eltos.simpledialogfragment.SimpleDialog +import eltos.simpledialogfragment.SimpleDialog.OnDialogResultListener.BUTTON_POSITIVE +import eltos.simpledialogfragment.color.SimpleColorDialog import eu.kanade.domain.base.BasePreferences import eu.kanade.tachiyomi.network.NetworkPreferences import io.noties.markwon.Markwon @@ -50,7 +52,7 @@ import uy.kohesive.injekt.api.get import kotlin.random.Random -class SettingsActivity : AppCompatActivity() { +class SettingsActivity : AppCompatActivity(), SimpleDialog.OnDialogResultListener { private val restartMainActivity = object : OnBackPressedCallback(false) { override fun handleOnBackPressed() = startMainActivity(this@SettingsActivity) } @@ -176,31 +178,21 @@ class SettingsActivity : AppCompatActivity() { binding.customTheme.setOnClickListener { - var passedColor: Int = 0 - val dialogView = layoutInflater.inflate(R.layout.dialog_color_picker, null) - val alertDialog = AlertDialog.Builder(this, R.style.MyPopup) - .setTitle("Custom Theme") - .setView(dialogView) - .setPositiveButton("OK") { dialog, _ -> - getSharedPreferences("Dantotsu", Context.MODE_PRIVATE).edit() - .putInt("custom_theme_int", passedColor).apply() - logger("Custom Theme: $passedColor") - dialog.dismiss() - restartApp() - } - .setNegativeButton("Cancel") { dialog, _ -> - dialog.dismiss() - } - .create() - val colorPickerView = - dialogView.findViewById(R.id.colorPickerView) - colorPickerView.setColorListener(ColorListener { color, fromUser -> - val linearLayout = dialogView.findViewById(R.id.linear) - passedColor = color - linearLayout.setBackgroundColor(color) - }) - alertDialog.show() - alertDialog.window?.setDimAmount(0.8f) + val originalColor = getSharedPreferences("Dantotsu", Context.MODE_PRIVATE).getInt( + "custom_theme_int", + Color.parseColor("#6200EE") + ) + val tag = "colorPicker" + SimpleColorDialog.build() + .title("Custom Theme") + .colorPreset(originalColor) + .colors(this, SimpleColorDialog.BEIGE_COLOR_PALLET) + .allowCustom(true) + .showOutline(0x46000000) + .gridNumColumn(5) + .choiceMode(SimpleColorDialog.SINGLE_CHOICE) + .neg() + .show(this, tag) } //val animeSource = loadData("settings_def_anime_source_s")?.let { if (it >= AnimeSources.names.size) 0 else it } ?: 0 @@ -416,6 +408,7 @@ class SettingsActivity : AppCompatActivity() { binding.settingsIncognito.setOnCheckedChangeListener { _, isChecked -> getSharedPreferences("Dantotsu", Context.MODE_PRIVATE).edit() .putBoolean("incognito", isChecked).apply() + restartApp() } var previousStart: View = when (uiSettings.defaultStartUpTab) { @@ -770,6 +763,18 @@ class SettingsActivity : AppCompatActivity() { } } + override fun onResult(dialogTag: String, which: Int, extras: Bundle): Boolean { + if (which == BUTTON_POSITIVE) { + if (dialogTag == "colorPicker") { + val color = extras.getInt(SimpleColorDialog.COLOR) + getSharedPreferences("Dantotsu", Context.MODE_PRIVATE).edit() + .putInt("custom_theme_int", color).apply() + logger("Custom Theme: $color") + } + } + return true + } + private fun restartApp() { Snackbar.make( binding.root, diff --git a/app/src/main/java/ani/dantotsu/settings/SettingsDialogFragment.kt b/app/src/main/java/ani/dantotsu/settings/SettingsDialogFragment.kt index 72e621b1..b94490ec 100644 --- a/app/src/main/java/ani/dantotsu/settings/SettingsDialogFragment.kt +++ b/app/src/main/java/ani/dantotsu/settings/SettingsDialogFragment.kt @@ -113,19 +113,9 @@ class SettingsDialogFragment() : BottomSheetDialogFragment() { } PageType.HOME -> { - try { - val arrayOfFiles = - ContextCompat.getExternalFilesDirs(requireContext(), null) - startActivity( - if (loadData("sd_dl") == true && arrayOfFiles.size > 1 && arrayOfFiles[0] != null && arrayOfFiles[1] != null) { - val parentDirectory = arrayOfFiles[1].toString() - val intent = Intent(Intent.ACTION_VIEW) - intent.setDataAndType(Uri.parse(parentDirectory), "resource/folder") - } else Intent(DownloadManager.ACTION_VIEW_DOWNLOADS) - ) - } catch (e: ActivityNotFoundException) { - toast(getString(R.string.file_manager_not_found)) - } + val intent = Intent(activity, DownloadContainerActivity::class.java) + intent.putExtra("FRAGMENT_CLASS_NAME", OfflineMangaFragment::class.java.name) + startActivity(intent) } } diff --git a/app/src/main/java/ani/dantotsu/settings/UserInterfaceSettings.kt b/app/src/main/java/ani/dantotsu/settings/UserInterfaceSettings.kt index 39dd18b1..15fd20a8 100644 --- a/app/src/main/java/ani/dantotsu/settings/UserInterfaceSettings.kt +++ b/app/src/main/java/ani/dantotsu/settings/UserInterfaceSettings.kt @@ -10,6 +10,7 @@ data class UserInterfaceSettings( //App var immersiveMode: Boolean = false, + var immersiveModeList: Boolean = false, var smallView: Boolean = true, var defaultStartUpTab: Int = 1, var homeLayoutShow: MutableList = mutableListOf( diff --git a/app/src/main/java/ani/dantotsu/settings/UserInterfaceSettingsActivity.kt b/app/src/main/java/ani/dantotsu/settings/UserInterfaceSettingsActivity.kt index 0012e6e8..189d691f 100644 --- a/app/src/main/java/ani/dantotsu/settings/UserInterfaceSettingsActivity.kt +++ b/app/src/main/java/ani/dantotsu/settings/UserInterfaceSettingsActivity.kt @@ -68,6 +68,12 @@ class UserInterfaceSettingsActivity : AppCompatActivity() { saveData(ui, settings) restartApp() } + binding.uiSettingsImmersiveList.isChecked = settings.immersiveModeList + binding.uiSettingsImmersiveList.setOnCheckedChangeListener { _, isChecked -> + settings.immersiveModeList = isChecked + saveData(ui, settings) + restartApp() + } binding.uiSettingsBannerAnimation.isChecked = settings.bannerAnimations binding.uiSettingsBannerAnimation.setOnCheckedChangeListener { _, isChecked -> 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 00000000..9c0f2ceb --- /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 00000000..859570e4 --- /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 00000000..f9a868d6 --- /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 00000000..1bd85648 --- /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-nodpi/example_appwidget_preview.png b/app/src/main/res/drawable-nodpi/example_appwidget_preview.png new file mode 100644 index 00000000..52c4ef28 Binary files /dev/null and b/app/src/main/res/drawable-nodpi/example_appwidget_preview.png differ 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 00000000..785445c6 --- /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 00000000..007e2872 --- /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 00000000..20e9ffd6 --- /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 00000000..5b43bb34 --- /dev/null +++ b/app/src/main/res/drawable/ic_dantotsu_round.xml @@ -0,0 +1,42 @@ + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_round_delete_24.xml b/app/src/main/res/drawable/ic_round_delete_24.xml index 95e9928f..9dd59ef9 100644 --- a/app/src/main/res/drawable/ic_round_delete_24.xml +++ b/app/src/main/res/drawable/ic_round_delete_24.xml @@ -1,4 +1,4 @@ + android:viewportWidth="24" android:width="24dp" android:tint="?attr/colorControlNormal" xmlns:android="http://schemas.android.com/apk/res/android"> diff --git a/app/src/main/res/layout/activity_list.xml b/app/src/main/res/layout/activity_list.xml index 20d3d0ab..395c1e92 100644 --- a/app/src/main/res/layout/activity_list.xml +++ b/app/src/main/res/layout/activity_list.xml @@ -28,21 +28,19 @@ - - + + + + + + + + + + + + + + + + + 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 00000000..af0b9d72 --- /dev/null +++ b/app/src/main/res/layout/currently_airing_widget_configure.xml @@ -0,0 +1,28 @@ + + + + + + + +