diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index fdab5a4c..e3ff41fa 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -73,12 +73,24 @@ android:resource="@xml/upcoming_widget_info" /> + + + + + + + + + + + + + + "sub" + SubDubType.DUB -> "dub" + SubDubType.NULL -> "" + } + val toggledCasePreserved = + if (subdub?.get(0)?.isUpperCase() == true || soft?.get(0) + ?.isUpperCase() == true + ) toggled.replaceFirstChar { + if (it.isLowerCase()) it.titlecase( + Locale.ROOT + ) else it.toString() + } else toggled + + subdubMatcher.replaceFirst(toggledCasePreserved + bed) + } else { + null + } + } + + fun getSubDub(text: String): SubDubType { + val subdubPattern: Pattern = Pattern.compile(REGEX_SUBDUB, Pattern.CASE_INSENSITIVE) + val subdubMatcher: Matcher = subdubPattern.matcher(text) + + return if (subdubMatcher.find()) { + val subdub = subdubMatcher.group(2)?.lowercase(Locale.ROOT) + when (subdub) { + "sub" -> SubDubType.SUB + "dub" -> SubDubType.DUB + else -> SubDubType.NULL + } + } else { + SubDubType.NULL + } + } + + enum class SubDubType { + SUB, DUB, NULL + } + + fun findSeasonNumber(text: String): Int? { + val seasonPattern: Pattern = Pattern.compile(REGEX_SEASON, Pattern.CASE_INSENSITIVE) + val seasonMatcher: Matcher = seasonPattern.matcher(text) + + return if (seasonMatcher.find()) { + seasonMatcher.group(2)?.toInt() + } else { + null + } + } + + fun findEpisodeNumber(text: String): Float? { + val episodePattern: Pattern = Pattern.compile(REGEX_EPISODE, Pattern.CASE_INSENSITIVE) + val episodeMatcher: Matcher = episodePattern.matcher(text) + + return if (episodeMatcher.find()) { + if (episodeMatcher.group(2) != null) { + episodeMatcher.group(2)?.toFloat() + } else { + val failedEpisodeNumberPattern: Pattern = + Pattern.compile(REGEX_PART_NUMBER, Pattern.CASE_INSENSITIVE) + val failedEpisodeNumberMatcher: Matcher = + failedEpisodeNumberPattern.matcher(text) + if (failedEpisodeNumberMatcher.find()) { + failedEpisodeNumberMatcher.group(1)?.toFloat() + } else { + null + } + } + } else { + null + } + } + + fun removeEpisodeNumber(text: String): String { + val regexPattern = Regex(REGEX_EPISODE, RegexOption.IGNORE_CASE) + val removedNumber = text.replace(regexPattern, "").ifEmpty { + text + } + val letterPattern = Regex("[a-zA-Z]") + return if (letterPattern.containsMatchIn(removedNumber)) { + removedNumber + } else { + text + } + } + + + fun removeEpisodeNumberCompletely(text: String): String { + val regexPattern = Regex(REGEX_EPISODE, RegexOption.IGNORE_CASE) + val removedNumber = text.replace(regexPattern, "") + return if (removedNumber.equals(text, true)) { // if nothing was removed + val failedEpisodeNumberPattern = + Regex(REGEX_PART_NUMBER, RegexOption.IGNORE_CASE) + failedEpisodeNumberPattern.replace(removedNumber) { mr -> + mr.value.replaceFirst(mr.groupValues[1], "") + } + } else { + removedNumber + } + } + + fun findChapterNumber(text: String): Float? { + val pattern: Pattern = Pattern.compile(REGEX_CHAPTER, Pattern.CASE_INSENSITIVE) + val matcher: Matcher = pattern.matcher(text) + + return if (matcher.find()) { + matcher.group(2)?.toFloat() + } else { + val failedChapterNumberPattern: Pattern = + Pattern.compile(REGEX_PART_NUMBER, Pattern.CASE_INSENSITIVE) + val failedChapterNumberMatcher: Matcher = + failedChapterNumberPattern.matcher(text) + if (failedChapterNumberMatcher.find()) { + failedChapterNumberMatcher.group(1)?.toFloat() + } else { + null + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/ani/dantotsu/media/anime/AnimeNameAdapter.kt b/app/src/main/java/ani/dantotsu/media/anime/AnimeNameAdapter.kt deleted file mode 100644 index 16142902..00000000 --- a/app/src/main/java/ani/dantotsu/media/anime/AnimeNameAdapter.kt +++ /dev/null @@ -1,127 +0,0 @@ -package ani.dantotsu.media.anime - -import java.util.Locale -import java.util.regex.Matcher -import java.util.regex.Pattern - -class AnimeNameAdapter { - companion object { - const val episodeRegex = - "(episode|episodio|ep|e)[\\s:.\\-]*(\\d+\\.?\\d*)[\\s:.\\-]*\\(?\\s*(sub|subbed|dub|dubbed)*\\s*\\)?\\s*" - const val failedEpisodeNumberRegex = - "(? "sub" - SubDubType.DUB -> "dub" - SubDubType.NULL -> "" - } - val toggledCasePreserved = - if (subdub?.get(0)?.isUpperCase() == true || soft?.get(0) - ?.isUpperCase() == true - ) toggled.replaceFirstChar { - if (it.isLowerCase()) it.titlecase( - Locale.ROOT - ) else it.toString() - } else toggled - - subdubMatcher.replaceFirst(toggledCasePreserved + bed) - } else { - null - } - } - - fun getSubDub(text: String): SubDubType { - val subdubPattern: Pattern = Pattern.compile(subdubRegex, Pattern.CASE_INSENSITIVE) - val subdubMatcher: Matcher = subdubPattern.matcher(text) - - return if (subdubMatcher.find()) { - val subdub = subdubMatcher.group(2)?.lowercase(Locale.ROOT) - when (subdub) { - "sub" -> SubDubType.SUB - "dub" -> SubDubType.DUB - else -> SubDubType.NULL - } - } else { - SubDubType.NULL - } - } - - enum class SubDubType { - SUB, DUB, NULL - } - - fun findSeasonNumber(text: String): Int? { - val seasonPattern: Pattern = Pattern.compile(seasonRegex, Pattern.CASE_INSENSITIVE) - val seasonMatcher: Matcher = seasonPattern.matcher(text) - - return if (seasonMatcher.find()) { - seasonMatcher.group(2)?.toInt() - } else { - null - } - } - - fun findEpisodeNumber(text: String): Float? { - val episodePattern: Pattern = Pattern.compile(episodeRegex, Pattern.CASE_INSENSITIVE) - val episodeMatcher: Matcher = episodePattern.matcher(text) - - return if (episodeMatcher.find()) { - if (episodeMatcher.group(2) != null) { - episodeMatcher.group(2)?.toFloat() - } else { - val failedEpisodeNumberPattern: Pattern = - Pattern.compile(failedEpisodeNumberRegex, Pattern.CASE_INSENSITIVE) - val failedEpisodeNumberMatcher: Matcher = - failedEpisodeNumberPattern.matcher(text) - if (failedEpisodeNumberMatcher.find()) { - failedEpisodeNumberMatcher.group(1)?.toFloat() - } else { - null - } - } - } else { - null - } - } - - fun removeEpisodeNumber(text: String): String { - val regexPattern = Regex(episodeRegex, RegexOption.IGNORE_CASE) - val removedNumber = text.replace(regexPattern, "").ifEmpty { - text - } - val letterPattern = Regex("[a-zA-Z]") - return if (letterPattern.containsMatchIn(removedNumber)) { - removedNumber - } else { - text - } - } - - - fun removeEpisodeNumberCompletely(text: String): String { - val regexPattern = Regex(episodeRegex, RegexOption.IGNORE_CASE) - val removedNumber = text.replace(regexPattern, "") - return if (removedNumber.equals(text, true)) { // if nothing was removed - val failedEpisodeNumberPattern = - Regex(failedEpisodeNumberRegex, RegexOption.IGNORE_CASE) - failedEpisodeNumberPattern.replace(removedNumber) { mr -> - mr.value.replaceFirst(mr.groupValues[1], "") - } - } else { - removedNumber - } - } - } -} diff --git a/app/src/main/java/ani/dantotsu/media/anime/AnimeWatchAdapter.kt b/app/src/main/java/ani/dantotsu/media/anime/AnimeWatchAdapter.kt index 792a687c..14c73770 100644 --- a/app/src/main/java/ani/dantotsu/media/anime/AnimeWatchAdapter.kt +++ b/app/src/main/java/ani/dantotsu/media/anime/AnimeWatchAdapter.kt @@ -26,6 +26,7 @@ import ani.dantotsu.isOnline import ani.dantotsu.loadImage import ani.dantotsu.media.Media import ani.dantotsu.media.MediaDetailsActivity +import ani.dantotsu.media.MediaNameAdapter import ani.dantotsu.media.SourceSearchDialogFragment import ani.dantotsu.openSettings import ani.dantotsu.others.LanguageMapper @@ -403,7 +404,7 @@ class AnimeWatchAdapter( } val ep = media.anime.episodes!![continueEp]!! - val cleanedTitle = ep.title?.let { AnimeNameAdapter.removeEpisodeNumber(it) } + val cleanedTitle = ep.title?.let { MediaNameAdapter.removeEpisodeNumber(it) } binding.itemEpisodeImage.loadImage( ep.thumb ?: FileUrl[media.banner ?: media.cover], 0 diff --git a/app/src/main/java/ani/dantotsu/media/anime/AnimeWatchFragment.kt b/app/src/main/java/ani/dantotsu/media/anime/AnimeWatchFragment.kt index f02a556d..8298318c 100644 --- a/app/src/main/java/ani/dantotsu/media/anime/AnimeWatchFragment.kt +++ b/app/src/main/java/ani/dantotsu/media/anime/AnimeWatchFragment.kt @@ -41,6 +41,7 @@ import ani.dantotsu.media.Media import ani.dantotsu.media.MediaDetailsActivity import ani.dantotsu.media.MediaDetailsViewModel import ani.dantotsu.media.MediaType +import ani.dantotsu.media.MediaNameAdapter import ani.dantotsu.navBarHeight import ani.dantotsu.notifications.subscription.SubscriptionHelper import ani.dantotsu.notifications.subscription.SubscriptionHelper.Companion.saveSubscription @@ -224,7 +225,7 @@ class AnimeWatchFragment : Fragment() { if (media.anime!!.kitsuEpisodes!!.containsKey(i)) { episode.desc = media.anime!!.kitsuEpisodes!![i]?.desc ?: episode.desc - episode.title = if (AnimeNameAdapter.removeEpisodeNumberCompletely( + episode.title = if (MediaNameAdapter.removeEpisodeNumberCompletely( episode.title ?: "" ).isBlank() ) media.anime!!.kitsuEpisodes!![i]?.title diff --git a/app/src/main/java/ani/dantotsu/media/anime/EpisodeAdapters.kt b/app/src/main/java/ani/dantotsu/media/anime/EpisodeAdapters.kt index 21f3e439..68d3aabd 100644 --- a/app/src/main/java/ani/dantotsu/media/anime/EpisodeAdapters.kt +++ b/app/src/main/java/ani/dantotsu/media/anime/EpisodeAdapters.kt @@ -21,6 +21,7 @@ import ani.dantotsu.databinding.ItemEpisodeListBinding import ani.dantotsu.download.anime.AnimeDownloaderService import ani.dantotsu.download.video.Helper import ani.dantotsu.media.Media +import ani.dantotsu.media.MediaNameAdapter import ani.dantotsu.setAnimation import ani.dantotsu.settings.saving.PrefManager import com.bumptech.glide.Glide @@ -102,7 +103,7 @@ class EpisodeAdapter( override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { val ep = arr[position] val title = if (!ep.title.isNullOrEmpty() && ep.title != "null") { - ep.title?.let { AnimeNameAdapter.removeEpisodeNumber(it) } + ep.title?.let { MediaNameAdapter.removeEpisodeNumber(it) } } else { ep.number } ?: "" 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 df031443..0ca02de7 100644 --- a/app/src/main/java/ani/dantotsu/media/anime/ExoplayerView.kt +++ b/app/src/main/java/ani/dantotsu/media/anime/ExoplayerView.kt @@ -113,6 +113,7 @@ import ani.dantotsu.isOnline import ani.dantotsu.logError import ani.dantotsu.media.Media import ani.dantotsu.media.MediaDetailsViewModel +import ani.dantotsu.media.MediaNameAdapter import ani.dantotsu.media.SubtitleDownloader import ani.dantotsu.okHttpClient import ani.dantotsu.others.AniSkip @@ -998,7 +999,7 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL episodeTitleArr = arrayListOf() episodes.forEach { val episode = it.value - val cleanedTitle = AnimeNameAdapter.removeEpisodeNumberCompletely(episode.title ?: "") + val cleanedTitle = MediaNameAdapter.removeEpisodeNumberCompletely(episode.title ?: "") episodeTitleArr.add("Episode ${episode.number}${if (episode.filler) " [Filler]" else ""}${if (cleanedTitle.isNotBlank() && cleanedTitle != "null") ": $cleanedTitle" else ""}") } diff --git a/app/src/main/java/ani/dantotsu/media/manga/MangaChapterAdapter.kt b/app/src/main/java/ani/dantotsu/media/manga/MangaChapterAdapter.kt index 19d039f5..393d87b9 100644 --- a/app/src/main/java/ani/dantotsu/media/manga/MangaChapterAdapter.kt +++ b/app/src/main/java/ani/dantotsu/media/manga/MangaChapterAdapter.kt @@ -15,6 +15,7 @@ import ani.dantotsu.currContext import ani.dantotsu.databinding.ItemChapterListBinding import ani.dantotsu.databinding.ItemEpisodeCompactBinding import ani.dantotsu.media.Media +import ani.dantotsu.media.MediaNameAdapter import ani.dantotsu.setAnimation import kotlinx.coroutines.delay import kotlinx.coroutines.launch @@ -267,10 +268,10 @@ class MangaChapterAdapter( val binding = holder.binding setAnimation(fragment.requireContext(), holder.binding.root) val ep = arr[position] - val parsedNumber = MangaNameAdapter.findChapterNumber(ep.number)?.toInt() + val parsedNumber = MediaNameAdapter.findChapterNumber(ep.number)?.toInt() binding.itemEpisodeNumber.text = parsedNumber?.toString() ?: ep.number if (media.userProgress != null) { - if ((MangaNameAdapter.findChapterNumber(ep.number) + if ((MediaNameAdapter.findChapterNumber(ep.number) ?: 9999f) <= media.userProgress!!.toFloat() ) binding.itemEpisodeViewedCover.visibility = View.VISIBLE @@ -279,7 +280,7 @@ class MangaChapterAdapter( binding.itemEpisodeCont.setOnLongClickListener { updateProgress( media, - MangaNameAdapter.findChapterNumber(ep.number).toString() + MediaNameAdapter.findChapterNumber(ep.number).toString() ) true } @@ -315,7 +316,7 @@ class MangaChapterAdapter( } else binding.itemChapterTitle.visibility = View.VISIBLE if (media.userProgress != null) { - if ((MangaNameAdapter.findChapterNumber(ep.number) + if ((MediaNameAdapter.findChapterNumber(ep.number) ?: 9999f) <= media.userProgress!!.toFloat() ) { binding.itemEpisodeViewedCover.visibility = View.VISIBLE @@ -326,7 +327,7 @@ class MangaChapterAdapter( binding.root.setOnLongClickListener { updateProgress( media, - MangaNameAdapter.findChapterNumber(ep.number).toString() + MediaNameAdapter.findChapterNumber(ep.number).toString() ) true } diff --git a/app/src/main/java/ani/dantotsu/media/manga/MangaNameAdapter.kt b/app/src/main/java/ani/dantotsu/media/manga/MangaNameAdapter.kt deleted file mode 100644 index d265b69a..00000000 --- a/app/src/main/java/ani/dantotsu/media/manga/MangaNameAdapter.kt +++ /dev/null @@ -1,29 +0,0 @@ -package ani.dantotsu.media.manga - -import java.util.regex.Matcher -import java.util.regex.Pattern - -class MangaNameAdapter { - companion object { - private const val chapterRegex = "(chapter|chap|ch|c)[\\s:.\\-]*(\\d+\\.?\\d*)[\\s:.\\-]*" - private const val filedChapterNumberRegex = "(? - return when (AnimeNameAdapter.getSubDub(value.value.toString())) { - AnimeNameAdapter.Companion.SubDubType.SUB -> false - AnimeNameAdapter.Companion.SubDubType.DUB -> true - AnimeNameAdapter.Companion.SubDubType.NULL -> false + return when (MediaNameAdapter.getSubDub(value.value.toString())) { + MediaNameAdapter.SubDubType.SUB -> false + MediaNameAdapter.SubDubType.DUB -> true + MediaNameAdapter.SubDubType.NULL -> false } } } @@ -92,8 +92,8 @@ class DynamicAnimeParser(extension: AnimeExtension.Installed) : AnimeParser() { val configurableSource = extension.sources[sourceLanguage] as? ConfigurableAnimeSource ?: return val type = when (setDub) { - true -> AnimeNameAdapter.Companion.SubDubType.DUB - false -> AnimeNameAdapter.Companion.SubDubType.SUB + true -> MediaNameAdapter.SubDubType.DUB + false -> MediaNameAdapter.SubDubType.SUB } currContext()?.let { context -> val sharedPreferences = @@ -101,9 +101,9 @@ class DynamicAnimeParser(extension: AnimeExtension.Installed) : AnimeParser() { configurableSource.getPreferenceKey(), Context.MODE_PRIVATE ) - sharedPreferences.all.filterValues { AnimeNameAdapter.getSubDub(it.toString()) != AnimeNameAdapter.Companion.SubDubType.NULL } + sharedPreferences.all.filterValues { MediaNameAdapter.getSubDub(it.toString()) != MediaNameAdapter.SubDubType.NULL } .forEach { value -> - val setValue = AnimeNameAdapter.setSubDub(value.value.toString(), type) + val setValue = MediaNameAdapter.setSubDub(value.value.toString(), type) if (setValue != null) { sharedPreferences.edit().putString(value.key, setValue).apply() } @@ -122,9 +122,9 @@ class DynamicAnimeParser(extension: AnimeExtension.Installed) : AnimeParser() { Context.MODE_PRIVATE ) sharedPreferences.all.filterValues { - AnimeNameAdapter.setSubDub( + MediaNameAdapter.setSubDub( it.toString(), - AnimeNameAdapter.Companion.SubDubType.NULL + MediaNameAdapter.SubDubType.NULL ) != null } .forEach { _ -> return true } @@ -150,7 +150,7 @@ class DynamicAnimeParser(extension: AnimeExtension.Installed) : AnimeParser() { val sortedEpisodes = if (res[0].episode_number == -1f) { // Find the number in the string and sort by that number val sortedByStringNumber = res.sortedBy { - val matchResult = AnimeNameAdapter.findEpisodeNumber(it.name) + val matchResult = MediaNameAdapter.findEpisodeNumber(it.name) val number = matchResult ?: Float.MAX_VALUE it.episode_number = number // Store the found number in episode_number number @@ -171,13 +171,13 @@ class DynamicAnimeParser(extension: AnimeExtension.Installed) : AnimeParser() { var episodeCounter = 1f // Group by season, sort within each season, and then renumber while keeping episode number 0 as is val seasonGroups = - res.groupBy { AnimeNameAdapter.findSeasonNumber(it.name) ?: 0 } + res.groupBy { MediaNameAdapter.findSeasonNumber(it.name) ?: 0 } seasonGroups.keys.sortedBy { it } .flatMap { season -> seasonGroups[season]?.sortedBy { it.episode_number }?.map { episode -> if (episode.episode_number != 0f) { // Skip renumbering for episode number 0 val potentialNumber = - AnimeNameAdapter.findEpisodeNumber(episode.name) + MediaNameAdapter.findEpisodeNumber(episode.name) if (potentialNumber != null) { episode.episode_number = potentialNumber } else { diff --git a/app/src/main/java/ani/dantotsu/parsers/MangaParser.kt b/app/src/main/java/ani/dantotsu/parsers/MangaParser.kt index cd757307..bf67a8ca 100644 --- a/app/src/main/java/ani/dantotsu/parsers/MangaParser.kt +++ b/app/src/main/java/ani/dantotsu/parsers/MangaParser.kt @@ -1,7 +1,7 @@ package ani.dantotsu.parsers import ani.dantotsu.FileUrl -import ani.dantotsu.media.manga.MangaNameAdapter +import ani.dantotsu.media.MediaNameAdapter import com.bumptech.glide.load.resource.bitmap.BitmapTransformation import eu.kanade.tachiyomi.source.model.Page import eu.kanade.tachiyomi.source.model.SChapter @@ -33,9 +33,9 @@ abstract class MangaParser : BaseParser() { ): MangaChapter? { val chapter = loadChapters(mangaLink, extra, sManga) val max = chapter - .maxByOrNull { MangaNameAdapter.findChapterNumber(it.number) ?: 0f } + .maxByOrNull { MediaNameAdapter.findChapterNumber(it.number) ?: 0f } return max - ?.takeIf { latest < (MangaNameAdapter.findChapterNumber(it.number) ?: 0.001f) } + ?.takeIf { latest < (MediaNameAdapter.findChapterNumber(it.number) ?: 0.001f) } } /** diff --git a/app/src/main/java/ani/dantotsu/parsers/OfflineAnimeParser.kt b/app/src/main/java/ani/dantotsu/parsers/OfflineAnimeParser.kt index 57098894..582419c9 100644 --- a/app/src/main/java/ani/dantotsu/parsers/OfflineAnimeParser.kt +++ b/app/src/main/java/ani/dantotsu/parsers/OfflineAnimeParser.kt @@ -5,7 +5,7 @@ import android.os.Environment import ani.dantotsu.currContext import ani.dantotsu.download.DownloadsManager import ani.dantotsu.media.MediaType -import ani.dantotsu.media.anime.AnimeNameAdapter +import ani.dantotsu.media.MediaNameAdapter import ani.dantotsu.tryWithSuspend import eu.kanade.tachiyomi.animesource.model.SAnime import eu.kanade.tachiyomi.animesource.model.SEpisode @@ -54,7 +54,7 @@ class OfflineAnimeParser : AnimeParser() { episodes.add(episode) } } - episodes.sortBy { AnimeNameAdapter.findEpisodeNumber(it.number) } + episodes.sortBy { MediaNameAdapter.findEpisodeNumber(it.number) } return episodes } return emptyList() diff --git a/app/src/main/java/ani/dantotsu/parsers/OfflineMangaParser.kt b/app/src/main/java/ani/dantotsu/parsers/OfflineMangaParser.kt index 29149873..983a53ec 100644 --- a/app/src/main/java/ani/dantotsu/parsers/OfflineMangaParser.kt +++ b/app/src/main/java/ani/dantotsu/parsers/OfflineMangaParser.kt @@ -3,7 +3,7 @@ package ani.dantotsu.parsers import android.os.Environment import ani.dantotsu.currContext import ani.dantotsu.download.DownloadsManager -import ani.dantotsu.media.manga.MangaNameAdapter +import ani.dantotsu.media.MediaNameAdapter import ani.dantotsu.util.Logger import eu.kanade.tachiyomi.source.model.SChapter import eu.kanade.tachiyomi.source.model.SManga @@ -43,7 +43,7 @@ class OfflineMangaParser : MangaParser() { chapters.add(chapter) } } - chapters.sortBy { MangaNameAdapter.findChapterNumber(it.number) } + chapters.sortBy { MediaNameAdapter.findChapterNumber(it.number) } return chapters } return emptyList() diff --git a/app/src/main/java/ani/dantotsu/parsers/OfflineNovelParser.kt b/app/src/main/java/ani/dantotsu/parsers/OfflineNovelParser.kt index 534c3ac5..11009aed 100644 --- a/app/src/main/java/ani/dantotsu/parsers/OfflineNovelParser.kt +++ b/app/src/main/java/ani/dantotsu/parsers/OfflineNovelParser.kt @@ -3,7 +3,7 @@ package ani.dantotsu.parsers import android.os.Environment import ani.dantotsu.currContext import ani.dantotsu.download.DownloadsManager -import ani.dantotsu.media.manga.MangaNameAdapter +import ani.dantotsu.media.MediaNameAdapter import me.xdrop.fuzzywuzzy.FuzzySearch import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get @@ -38,7 +38,7 @@ class OfflineNovelParser : NovelParser() { chapters.add(chapter) } } - chapters.sortBy { MangaNameAdapter.findChapterNumber(it.name) } + chapters.sortBy { MediaNameAdapter.findChapterNumber(it.name) } return chapters.first() } return Book( diff --git a/app/src/main/java/ani/dantotsu/profile/activity/FeedActivity.kt b/app/src/main/java/ani/dantotsu/profile/activity/FeedActivity.kt index 0b35f047..42ee6973 100644 --- a/app/src/main/java/ani/dantotsu/profile/activity/FeedActivity.kt +++ b/app/src/main/java/ani/dantotsu/profile/activity/FeedActivity.kt @@ -21,7 +21,7 @@ import nl.joery.animatedbottombar.AnimatedBottomBar class FeedActivity : AppCompatActivity() { private lateinit var binding: ActivityFeedBinding private var selected: Int = 0 - private lateinit var navBar: AnimatedBottomBar + lateinit var navBar: AnimatedBottomBar override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) diff --git a/app/src/main/java/ani/dantotsu/profile/activity/FeedFragment.kt b/app/src/main/java/ani/dantotsu/profile/activity/FeedFragment.kt index f50502f1..40c96829 100644 --- a/app/src/main/java/ani/dantotsu/profile/activity/FeedFragment.kt +++ b/app/src/main/java/ani/dantotsu/profile/activity/FeedFragment.kt @@ -50,16 +50,21 @@ class FeedFragment : Fragment() { super.onViewCreated(view, savedInstanceState) activity = requireActivity() - binding.listRecyclerView.setBaseline((activity as ProfileActivity).navBar) + userId = arguments?.getInt("userId", -1) + activityId = arguments?.getInt("activityId", -1) ?: -1 + if (userId == -1) userId = null + global = arguments?.getBoolean("global", false) ?: false + + val navBar = if (userId != null) + (activity as ProfileActivity).navBar + else + (activity as FeedActivity).navBar + binding.listRecyclerView.setBaseline(navBar) binding.listRecyclerView.adapter = adapter binding.listRecyclerView.layoutManager = LinearLayoutManager(requireContext(), LinearLayoutManager.VERTICAL, false) binding.listProgressBar.visibility = ViewGroup.VISIBLE - userId = arguments?.getInt("userId", -1) - activityId = arguments?.getInt("activityId", -1) ?: -1 - if (userId == -1) userId = null - global = arguments?.getBoolean("global", false) ?: false } @SuppressLint("ClickableViewAccessibility") @@ -67,7 +72,11 @@ class FeedFragment : Fragment() { super.onResume() if (this::binding.isInitialized) { binding.root.requestLayout() - binding.listRecyclerView.setBaseline((activity as ProfileActivity).navBar) + val navBar = if (userId != null) + (activity as ProfileActivity).navBar + else + (activity as FeedActivity).navBar + binding.listRecyclerView.setBaseline(navBar) if (!loadedFirstTime) { activity.lifecycleScope.launch(Dispatchers.IO) { val nulledId = if (activityId == -1) null else activityId diff --git a/app/src/main/java/ani/dantotsu/settings/AnimeExtensionsFragment.kt b/app/src/main/java/ani/dantotsu/settings/AnimeExtensionsFragment.kt index a6d6c39e..fe7b81d2 100644 --- a/app/src/main/java/ani/dantotsu/settings/AnimeExtensionsFragment.kt +++ b/app/src/main/java/ani/dantotsu/settings/AnimeExtensionsFragment.kt @@ -13,7 +13,7 @@ import androidx.lifecycle.lifecycleScope import androidx.recyclerview.widget.LinearLayoutManager import ani.dantotsu.R import ani.dantotsu.connections.crashlytics.CrashlyticsInterface -import ani.dantotsu.databinding.FragmentAnimeExtensionsBinding +import ani.dantotsu.databinding.FragmentExtensionsBinding import ani.dantotsu.settings.paging.AnimeExtensionAdapter import ani.dantotsu.settings.paging.AnimeExtensionsViewModel import ani.dantotsu.settings.paging.AnimeExtensionsViewModelFactory @@ -30,7 +30,7 @@ import uy.kohesive.injekt.api.get class AnimeExtensionsFragment : Fragment(), SearchQueryHandler, OnAnimeInstallClickListener { - private var _binding: FragmentAnimeExtensionsBinding? = null + private var _binding: FragmentExtensionsBinding? = null private val binding get() = _binding!! private val viewModel: AnimeExtensionsViewModel by viewModels { @@ -48,12 +48,12 @@ class AnimeExtensionsFragment : Fragment(), container: ViewGroup?, savedInstanceState: Bundle? ): View { - _binding = FragmentAnimeExtensionsBinding.inflate(inflater, container, false) + _binding = FragmentExtensionsBinding.inflate(inflater, container, false) - binding.allAnimeExtensionsRecyclerView.isNestedScrollingEnabled = false - binding.allAnimeExtensionsRecyclerView.adapter = adapter - binding.allAnimeExtensionsRecyclerView.layoutManager = LinearLayoutManager(context) - (binding.allAnimeExtensionsRecyclerView.layoutManager as LinearLayoutManager).isItemPrefetchEnabled = + binding.allExtensionsRecyclerView.isNestedScrollingEnabled = false + binding.allExtensionsRecyclerView.adapter = adapter + binding.allExtensionsRecyclerView.layoutManager = LinearLayoutManager(context) + (binding.allExtensionsRecyclerView.layoutManager as LinearLayoutManager).isItemPrefetchEnabled = true lifecycleScope.launch { @@ -91,8 +91,8 @@ class AnimeExtensionsFragment : Fragment(), Notifications.CHANNEL_DOWNLOADER_PROGRESS ) .setSmallIcon(R.drawable.ic_round_sync_24) - .setContentTitle("Installing extension") - .setContentText("Step: $installStep") + .setContentTitle(getString(R.string.installing_extension)) + .setContentText(getString(R.string.install_step, installStep)) .setPriority(NotificationCompat.PRIORITY_LOW) notificationManager.notify(1, builder.build()) }, @@ -103,11 +103,11 @@ class AnimeExtensionsFragment : Fragment(), Notifications.CHANNEL_DOWNLOADER_ERROR ) .setSmallIcon(R.drawable.ic_round_info_24) - .setContentTitle("Installation failed: ${error.message}") - .setContentText("Error: ${error.message}") + .setContentTitle(getString(R.string.installation_failed, error.message)) + .setContentText(getString(R.string.error_message, error.message)) .setPriority(NotificationCompat.PRIORITY_HIGH) notificationManager.notify(1, builder.build()) - snackString("Installation failed: ${error.message}") + snackString(getString(R.string.installation_failed, error.message)) }, { val builder = NotificationCompat.Builder( @@ -115,12 +115,12 @@ class AnimeExtensionsFragment : Fragment(), Notifications.CHANNEL_DOWNLOADER_PROGRESS ) .setSmallIcon(R.drawable.ic_download_24) - .setContentTitle("Installation complete") - .setContentText("The extension has been successfully installed.") + .setContentTitle(getString(R.string.installation_complete)) + .setContentText(getString(R.string.extension_has_been_installed)) .setPriority(NotificationCompat.PRIORITY_LOW) notificationManager.notify(1, builder.build()) viewModel.invalidatePager() - snackString("Extension installed") + snackString(getString(R.string.extension_installed)) } ) } diff --git a/app/src/main/java/ani/dantotsu/settings/InstalledAnimeExtensionsFragment.kt b/app/src/main/java/ani/dantotsu/settings/InstalledAnimeExtensionsFragment.kt index e25fb822..3f1a3c49 100644 --- a/app/src/main/java/ani/dantotsu/settings/InstalledAnimeExtensionsFragment.kt +++ b/app/src/main/java/ani/dantotsu/settings/InstalledAnimeExtensionsFragment.kt @@ -24,7 +24,7 @@ import androidx.recyclerview.widget.RecyclerView import androidx.viewpager2.widget.ViewPager2 import ani.dantotsu.R import ani.dantotsu.connections.crashlytics.CrashlyticsInterface -import ani.dantotsu.databinding.FragmentAnimeExtensionsBinding +import ani.dantotsu.databinding.FragmentExtensionsBinding import ani.dantotsu.others.LanguageMapper import ani.dantotsu.parsers.AnimeSources import ani.dantotsu.settings.extensionprefs.AnimeSourcePreferencesFragment @@ -49,7 +49,7 @@ import java.util.Locale class InstalledAnimeExtensionsFragment : Fragment(), SearchQueryHandler { - private var _binding: FragmentAnimeExtensionsBinding? = null + private var _binding: FragmentExtensionsBinding? = null private val binding get() = _binding!! private lateinit var extensionsRecyclerView: RecyclerView private val skipIcons: Boolean = PrefManager.getVal(PrefName.SkipExtensionIcons) @@ -183,9 +183,9 @@ class InstalledAnimeExtensionsFragment : Fragment(), SearchQueryHandler { container: ViewGroup?, savedInstanceState: Bundle? ): View { - _binding = FragmentAnimeExtensionsBinding.inflate(inflater, container, false) + _binding = FragmentExtensionsBinding.inflate(inflater, container, false) - extensionsRecyclerView = binding.allAnimeExtensionsRecyclerView + extensionsRecyclerView = binding.allExtensionsRecyclerView extensionsRecyclerView.layoutManager = LinearLayoutManager(requireContext()) extensionsRecyclerView.adapter = extensionsAdapter diff --git a/app/src/main/java/ani/dantotsu/settings/InstalledMangaExtensionsFragment.kt b/app/src/main/java/ani/dantotsu/settings/InstalledMangaExtensionsFragment.kt index 24780ef5..90bfa771 100644 --- a/app/src/main/java/ani/dantotsu/settings/InstalledMangaExtensionsFragment.kt +++ b/app/src/main/java/ani/dantotsu/settings/InstalledMangaExtensionsFragment.kt @@ -26,7 +26,7 @@ import androidx.recyclerview.widget.RecyclerView import androidx.viewpager2.widget.ViewPager2 import ani.dantotsu.R import ani.dantotsu.connections.crashlytics.CrashlyticsInterface -import ani.dantotsu.databinding.FragmentMangaExtensionsBinding +import ani.dantotsu.databinding.FragmentExtensionsBinding import ani.dantotsu.others.LanguageMapper import ani.dantotsu.parsers.MangaSources import ani.dantotsu.settings.extensionprefs.MangaSourcePreferencesFragment @@ -48,7 +48,7 @@ import java.util.Collections import java.util.Locale class InstalledMangaExtensionsFragment : Fragment(), SearchQueryHandler { - private var _binding: FragmentMangaExtensionsBinding? = null + private var _binding: FragmentExtensionsBinding? = null private val binding get() = _binding!! private lateinit var extensionsRecyclerView: RecyclerView private val skipIcons: Boolean = PrefManager.getVal(PrefName.SkipExtensionIcons) @@ -181,9 +181,9 @@ class InstalledMangaExtensionsFragment : Fragment(), SearchQueryHandler { container: ViewGroup?, savedInstanceState: Bundle? ): View { - _binding = FragmentMangaExtensionsBinding.inflate(inflater, container, false) + _binding = FragmentExtensionsBinding.inflate(inflater, container, false) - extensionsRecyclerView = binding.allMangaExtensionsRecyclerView + extensionsRecyclerView = binding.allExtensionsRecyclerView extensionsRecyclerView.layoutManager = LinearLayoutManager(requireContext()) extensionsRecyclerView.adapter = extensionsAdapter diff --git a/app/src/main/java/ani/dantotsu/settings/MangaExtensionsFragment.kt b/app/src/main/java/ani/dantotsu/settings/MangaExtensionsFragment.kt index 6f7276ad..c5118096 100644 --- a/app/src/main/java/ani/dantotsu/settings/MangaExtensionsFragment.kt +++ b/app/src/main/java/ani/dantotsu/settings/MangaExtensionsFragment.kt @@ -13,7 +13,7 @@ import androidx.lifecycle.lifecycleScope import androidx.recyclerview.widget.LinearLayoutManager import ani.dantotsu.R import ani.dantotsu.connections.crashlytics.CrashlyticsInterface -import ani.dantotsu.databinding.FragmentMangaExtensionsBinding +import ani.dantotsu.databinding.FragmentExtensionsBinding import ani.dantotsu.settings.paging.MangaExtensionAdapter import ani.dantotsu.settings.paging.MangaExtensionsViewModel import ani.dantotsu.settings.paging.MangaExtensionsViewModelFactory @@ -30,7 +30,7 @@ import uy.kohesive.injekt.api.get class MangaExtensionsFragment : Fragment(), SearchQueryHandler, OnMangaInstallClickListener { - private var _binding: FragmentMangaExtensionsBinding? = null + private var _binding: FragmentExtensionsBinding? = null private val binding get() = _binding!! private val viewModel: MangaExtensionsViewModel by viewModels { @@ -49,12 +49,12 @@ class MangaExtensionsFragment : Fragment(), container: ViewGroup?, savedInstanceState: Bundle? ): View { - _binding = FragmentMangaExtensionsBinding.inflate(inflater, container, false) + _binding = FragmentExtensionsBinding.inflate(inflater, container, false) - binding.allMangaExtensionsRecyclerView.isNestedScrollingEnabled = false - binding.allMangaExtensionsRecyclerView.adapter = adapter - binding.allMangaExtensionsRecyclerView.layoutManager = LinearLayoutManager(context) - (binding.allMangaExtensionsRecyclerView.layoutManager as LinearLayoutManager).isItemPrefetchEnabled = + binding.allExtensionsRecyclerView.isNestedScrollingEnabled = false + binding.allExtensionsRecyclerView.adapter = adapter + binding.allExtensionsRecyclerView.layoutManager = LinearLayoutManager(context) + (binding.allExtensionsRecyclerView.layoutManager as LinearLayoutManager).isItemPrefetchEnabled = true lifecycleScope.launch { @@ -92,8 +92,8 @@ class MangaExtensionsFragment : Fragment(), Notifications.CHANNEL_DOWNLOADER_PROGRESS ) .setSmallIcon(R.drawable.ic_round_sync_24) - .setContentTitle("Installing extension") - .setContentText("Step: $installStep") + .setContentTitle(getString(R.string.installing_extension)) + .setContentText(getString(R.string.install_step, installStep)) .setPriority(NotificationCompat.PRIORITY_LOW) notificationManager.notify(1, builder.build()) }, @@ -104,11 +104,11 @@ class MangaExtensionsFragment : Fragment(), Notifications.CHANNEL_DOWNLOADER_ERROR ) .setSmallIcon(R.drawable.ic_round_info_24) - .setContentTitle("Installation failed: ${error.message}") - .setContentText("Error: ${error.message}") + .setContentTitle(getString(R.string.installation_failed, error.message)) + .setContentText(getString(R.string.error_message, error.message)) .setPriority(NotificationCompat.PRIORITY_HIGH) notificationManager.notify(1, builder.build()) - snackString("Installation failed: ${error.message}") + snackString(getString(R.string.installation_failed, error.message)) }, { val builder = NotificationCompat.Builder( @@ -116,12 +116,12 @@ class MangaExtensionsFragment : Fragment(), Notifications.CHANNEL_DOWNLOADER_PROGRESS ) .setSmallIcon(R.drawable.ic_download_24) - .setContentTitle("Installation complete") - .setContentText("The extension has been successfully installed.") + .setContentTitle(getString(R.string.installation_complete)) + .setContentText(getString(R.string.extension_has_been_installed)) .setPriority(NotificationCompat.PRIORITY_LOW) notificationManager.notify(1, builder.build()) viewModel.invalidatePager() - snackString("Extension installed") + snackString(getString(R.string.extension_installed)) } ) } diff --git a/app/src/main/java/ani/dantotsu/widgets/statistics/ProfileStatsConfigure.kt b/app/src/main/java/ani/dantotsu/widgets/statistics/ProfileStatsConfigure.kt new file mode 100644 index 00000000..b3ddec46 --- /dev/null +++ b/app/src/main/java/ani/dantotsu/widgets/statistics/ProfileStatsConfigure.kt @@ -0,0 +1,268 @@ +package ani.dantotsu.widgets.statistics + +import android.app.Activity +import android.appwidget.AppWidgetManager +import android.content.Context +import android.content.Intent +import android.content.res.ColorStateList +import android.graphics.Color +import android.os.Bundle +import android.util.TypedValue +import android.view.View +import androidx.appcompat.app.AppCompatActivity +import ani.dantotsu.R +import ani.dantotsu.databinding.StatisticsWidgetConfigureBinding + +import ani.dantotsu.themes.ThemeManager +import ani.dantotsu.widgets.upcoming.UpcomingWidget +import com.google.android.material.button.MaterialButton +import eltos.simpledialogfragment.SimpleDialog +import eltos.simpledialogfragment.color.SimpleColorDialog + +/** + * The configuration screen for the [ProfileStatsWidget] AppWidget. + */ +class ProfileStatsConfigure : AppCompatActivity(), + SimpleDialog.OnDialogResultListener { + private var appWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID + private var isMonetEnabled = false + private var onClickListener = View.OnClickListener { + val context = this@ProfileStatsConfigure + + // It is the responsibility of the configuration activity to update the app widget + val appWidgetManager = AppWidgetManager.getInstance(context) + //updateAppWidget(context, appWidgetManager, appWidgetId) + + + ProfileStatsWidget.updateAppWidget( + context, + appWidgetManager, + appWidgetId + ) + + // 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: StatisticsWidgetConfigureBinding + + public override fun onCreate(icicle: Bundle?) { + + 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 = StatisticsWidgetConfigureBinding.inflate(layoutInflater) + setContentView(binding.root) + + val prefs = getSharedPreferences(ProfileStatsWidget.PREFS_NAME, Context.MODE_PRIVATE) + val topBackground = prefs.getInt(ProfileStatsWidget.PREF_BACKGROUND_COLOR, Color.parseColor("#80000000")) + (binding.topBackgroundButton as MaterialButton).iconTint = ColorStateList.valueOf(topBackground) + binding.topBackgroundButton.setOnClickListener { + val tag = ProfileStatsWidget.PREF_BACKGROUND_COLOR + SimpleColorDialog().title(R.string.custom_theme) + .colorPreset(topBackground) + .colors( + this@ProfileStatsConfigure, + SimpleColorDialog.MATERIAL_COLOR_PALLET + ) + .setupColorWheelAlpha(true) + .allowCustom(true) + .showOutline(0x46000000) + .gridNumColumn(5) + .choiceMode(SimpleColorDialog.SINGLE_CHOICE) + .neg() + .show(this@ProfileStatsConfigure, tag) + } + val bottomBackground = prefs.getInt(ProfileStatsWidget.PREF_BACKGROUND_FADE, Color.parseColor("#00000000")) + (binding.bottomBackgroundButton as MaterialButton).iconTint = ColorStateList.valueOf(bottomBackground) + binding.bottomBackgroundButton.setOnClickListener { + val tag = ProfileStatsWidget.PREF_BACKGROUND_FADE + SimpleColorDialog().title(R.string.custom_theme) + .colorPreset(bottomBackground) + .colors( + this@ProfileStatsConfigure, + SimpleColorDialog.MATERIAL_COLOR_PALLET + ) + .setupColorWheelAlpha(true) + .allowCustom(true) + .showOutline(0x46000000) + .gridNumColumn(5) + .choiceMode(SimpleColorDialog.SINGLE_CHOICE) + .neg() + .show(this@ProfileStatsConfigure, tag) + } + val titleColor = prefs.getInt(ProfileStatsWidget.PREF_TITLE_TEXT_COLOR, Color.WHITE) + (binding.titleColorButton as MaterialButton).iconTint = ColorStateList.valueOf(titleColor) + binding.titleColorButton.setOnClickListener { + val tag = ProfileStatsWidget.PREF_TITLE_TEXT_COLOR + SimpleColorDialog().title(R.string.custom_theme) + .colorPreset(titleColor) + .colors( + this@ProfileStatsConfigure, + SimpleColorDialog.MATERIAL_COLOR_PALLET + ) + .setupColorWheelAlpha(true) + .allowCustom(true) + .showOutline(0x46000000) + .gridNumColumn(5) + .choiceMode(SimpleColorDialog.SINGLE_CHOICE) + .neg() + .show(this@ProfileStatsConfigure, tag) + } + val statsColor = prefs.getInt(ProfileStatsWidget.PREF_STATS_TEXT_COLOR, Color.WHITE) + (binding.statsColorButton as MaterialButton).iconTint = ColorStateList.valueOf(statsColor) + binding.statsColorButton.setOnClickListener { + val tag = ProfileStatsWidget.PREF_STATS_TEXT_COLOR + SimpleColorDialog().title(R.string.custom_theme) + .colorPreset(statsColor) + .colors( + this@ProfileStatsConfigure, + SimpleColorDialog.MATERIAL_COLOR_PALLET + ) + .setupColorWheelAlpha(true) + .allowCustom(true) + .showOutline(0x46000000) + .gridNumColumn(5) + .choiceMode(SimpleColorDialog.SINGLE_CHOICE) + .neg() + .show(this@ProfileStatsConfigure, tag) + } + binding.useAppTheme.setOnCheckedChangeListener { _, isChecked -> + isMonetEnabled = isChecked + if (isChecked) { + binding.topBackgroundButton.visibility = View.GONE + binding.bottomBackgroundButton.visibility = View.GONE + binding.titleColorButton.visibility = View.GONE + binding.statsColorButton.visibility = View.GONE + themeColors() + + } else { + binding.topBackgroundButton.visibility = View.VISIBLE + binding.bottomBackgroundButton.visibility = View.VISIBLE + binding.titleColorButton.visibility = View.VISIBLE + binding.statsColorButton.visibility = View.VISIBLE + } + } + 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 + } + } + + private fun themeColors() { + val typedValueSurface = TypedValue() + theme.resolveAttribute( + com.google.android.material.R.attr.colorSurface, + typedValueSurface, + true + ) + val backgroundColor = typedValueSurface.data + + val typedValuePrimary = TypedValue() + theme.resolveAttribute( + com.google.android.material.R.attr.colorPrimary, + typedValuePrimary, + true + ) + val textColor = typedValuePrimary.data + + val typedValueOutline = TypedValue() + theme.resolveAttribute( + com.google.android.material.R.attr.colorOutline, + typedValueOutline, + true + ) + val subTextColor = typedValueOutline.data + + getSharedPreferences(ProfileStatsWidget.PREFS_NAME, Context.MODE_PRIVATE).edit().apply { + putInt(ProfileStatsWidget.PREF_BACKGROUND_COLOR, backgroundColor) + putInt(ProfileStatsWidget.PREF_BACKGROUND_FADE, backgroundColor) + putInt(ProfileStatsWidget.PREF_TITLE_TEXT_COLOR, textColor) + putInt(ProfileStatsWidget.PREF_STATS_TEXT_COLOR, subTextColor) + apply() + } + } + + override fun onResult(dialogTag: String, which: Int, extras: Bundle): Boolean { + if (which == SimpleDialog.OnDialogResultListener.BUTTON_POSITIVE) { + if (!isMonetEnabled) { + when (dialogTag) { + ProfileStatsWidget.PREF_BACKGROUND_COLOR -> { + getSharedPreferences( + ProfileStatsWidget.PREFS_NAME, + Context.MODE_PRIVATE + ).edit() + .putInt( + ProfileStatsWidget.PREF_BACKGROUND_COLOR, + extras.getInt(SimpleColorDialog.COLOR) + ) + .apply() + (binding.topBackgroundButton as MaterialButton).iconTint = + ColorStateList.valueOf(extras.getInt(SimpleColorDialog.COLOR)) + } + + ProfileStatsWidget.PREF_BACKGROUND_FADE -> { + getSharedPreferences( + ProfileStatsWidget.PREFS_NAME, + Context.MODE_PRIVATE + ).edit() + .putInt( + ProfileStatsWidget.PREF_BACKGROUND_FADE, + extras.getInt(SimpleColorDialog.COLOR) + ) + .apply() + (binding.bottomBackgroundButton as MaterialButton).iconTint = + ColorStateList.valueOf(extras.getInt(SimpleColorDialog.COLOR)) + } + + ProfileStatsWidget.PREF_TITLE_TEXT_COLOR -> { + getSharedPreferences( + ProfileStatsWidget.PREFS_NAME, + Context.MODE_PRIVATE + ).edit() + .putInt( + ProfileStatsWidget.PREF_TITLE_TEXT_COLOR, + extras.getInt(SimpleColorDialog.COLOR) + ) + .apply() + (binding.titleColorButton as MaterialButton).iconTint = + ColorStateList.valueOf(extras.getInt(SimpleColorDialog.COLOR)) + } + + ProfileStatsWidget.PREF_STATS_TEXT_COLOR -> { + getSharedPreferences( + ProfileStatsWidget.PREFS_NAME, + Context.MODE_PRIVATE + ).edit() + .putInt( + ProfileStatsWidget.PREF_STATS_TEXT_COLOR, + extras.getInt(SimpleColorDialog.COLOR) + ) + .apply() + (binding.statsColorButton as MaterialButton).iconTint = + ColorStateList.valueOf(extras.getInt(SimpleColorDialog.COLOR)) + } + } + } + } + return true + } +} \ No newline at end of file diff --git a/app/src/main/java/ani/dantotsu/widgets/statistics/ProfileStatsWidget.kt b/app/src/main/java/ani/dantotsu/widgets/statistics/ProfileStatsWidget.kt new file mode 100644 index 00000000..06202ea0 --- /dev/null +++ b/app/src/main/java/ani/dantotsu/widgets/statistics/ProfileStatsWidget.kt @@ -0,0 +1,259 @@ +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 statsTextColor = prefs.getInt(PREF_STATS_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 + ) + ) + setOnClickPendingIntent( + R.id.userAvatar, + PendingIntent.getActivity( + context, + 1, + Intent(context, ProfileStatsConfigure::class.java).apply { + putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId) + }, + PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE + ) + ) + setTextColor(R.id.userLabel, titleTextColor) + setTextColor(R.id.topLeftItem, statsTextColor) + setTextColor(R.id.topLeftLabel, statsTextColor) + setTextColor(R.id.topRightItem, statsTextColor) + setTextColor(R.id.topRightLabel, statsTextColor) + setTextColor(R.id.bottomLeftItem, statsTextColor) + setTextColor(R.id.bottomLeftLabel, statsTextColor) + setTextColor(R.id.bottomRightItem, statsTextColor) + setTextColor(R.id.bottomRightLabel, statsTextColor) + + 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_n) + ) + + 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_n) + ) + + 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" + const val PREF_STATS_TEXT_COLOR = "stats_text_color" + } +} \ No newline at end of file diff --git a/app/src/main/java/ani/dantotsu/widgets/upcoming/UpcomingWidget.kt b/app/src/main/java/ani/dantotsu/widgets/upcoming/UpcomingWidget.kt index a9f22130..fc8850f3 100644 --- a/app/src/main/java/ani/dantotsu/widgets/upcoming/UpcomingWidget.kt +++ b/app/src/main/java/ani/dantotsu/widgets/upcoming/UpcomingWidget.kt @@ -10,7 +10,6 @@ import android.graphics.drawable.GradientDrawable import android.net.Uri import android.os.Bundle import android.widget.RemoteViews -import androidx.core.content.ContextCompat import androidx.core.content.res.ResourcesCompat import ani.dantotsu.MainActivity import ani.dantotsu.R @@ -19,7 +18,7 @@ import ani.dantotsu.widgets.WidgetSizeProvider /** * Implementation of App Widget functionality. - * App Widget Configuration implemented in [UpcomingWidgetConfigureActivity] + * App Widget Configuration implemented in [UpcomingWidgetConfigure] */ class UpcomingWidget : AppWidgetProvider() { override fun onUpdate( @@ -69,8 +68,8 @@ class UpcomingWidget : AppWidgetProvider() { ): RemoteViews { val prefs = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE) val backgroundColor = - prefs.getInt(PREF_BACKGROUND_COLOR, ContextCompat.getColor(context, R.color.theme)) - val backgroundFade = prefs.getInt(PREF_BACKGROUND_FADE, Color.GRAY) + 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 countdownTextColor = prefs.getInt(PREF_COUNTDOWN_TEXT_COLOR, Color.WHITE) @@ -80,14 +79,14 @@ class UpcomingWidget : AppWidgetProvider() { } val gradientDrawable = ResourcesCompat.getDrawable( context.resources, - R.drawable.gradient_background, + 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 = 50f + gradientDrawable.cornerRadius = 64f } else { width = 300 height = 300 @@ -118,7 +117,7 @@ class UpcomingWidget : AppWidgetProvider() { PendingIntent.getActivity( context, 1, - Intent(context, UpcomingWidgetConfigureActivity::class.java).apply { + Intent(context, UpcomingWidgetConfigure::class.java).apply { putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId) }, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE diff --git a/app/src/main/java/ani/dantotsu/widgets/upcoming/UpcomingWidgetConfigure.kt b/app/src/main/java/ani/dantotsu/widgets/upcoming/UpcomingWidgetConfigure.kt new file mode 100644 index 00000000..a465a48a --- /dev/null +++ b/app/src/main/java/ani/dantotsu/widgets/upcoming/UpcomingWidgetConfigure.kt @@ -0,0 +1,240 @@ +package ani.dantotsu.widgets.upcoming + +import android.appwidget.AppWidgetManager +import android.content.Context +import android.content.Intent +import android.content.res.ColorStateList +import android.graphics.Color +import android.os.Bundle +import android.util.TypedValue +import android.view.View +import androidx.appcompat.app.AppCompatActivity +import ani.dantotsu.R +import ani.dantotsu.databinding.UpcomingWidgetConfigureBinding +import ani.dantotsu.themes.ThemeManager +import com.google.android.material.button.MaterialButton +import eltos.simpledialogfragment.SimpleDialog +import eltos.simpledialogfragment.color.SimpleColorDialog + +/** + * The configuration screen for the [UpcomingWidget] AppWidget. + */ +class UpcomingWidgetConfigure : AppCompatActivity(), + SimpleDialog.OnDialogResultListener { + private var appWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID + private var isMonetEnabled = false + private var onClickListener = View.OnClickListener { + val context = this@UpcomingWidgetConfigure + val appWidgetManager = AppWidgetManager.getInstance(context) + + updateAppWidget( + context, + appWidgetManager, + appWidgetId, + ) + + val resultValue = Intent() + resultValue.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId) + setResult(RESULT_OK, resultValue) + finish() + } + private lateinit var binding: UpcomingWidgetConfigureBinding + + public override fun onCreate(icicle: Bundle?) { + ThemeManager(this).applyTheme() + super.onCreate(icicle) + setResult(RESULT_CANCELED) + + binding = UpcomingWidgetConfigureBinding.inflate(layoutInflater) + setContentView(binding.root) + val prefs = getSharedPreferences(UpcomingWidget.PREFS_NAME, Context.MODE_PRIVATE) + val topBackground = prefs.getInt(UpcomingWidget.PREF_BACKGROUND_COLOR, Color.parseColor("#80000000")) + (binding.topBackgroundButton as MaterialButton).iconTint = ColorStateList.valueOf(topBackground) + binding.topBackgroundButton.setOnClickListener { + val tag = UpcomingWidget.PREF_BACKGROUND_COLOR + SimpleColorDialog().title(R.string.custom_theme) + .colorPreset(topBackground) + .colors( + this@UpcomingWidgetConfigure, + SimpleColorDialog.MATERIAL_COLOR_PALLET + ) + .setupColorWheelAlpha(true) + .allowCustom(true) + .showOutline(0x46000000) + .gridNumColumn(5) + .choiceMode(SimpleColorDialog.SINGLE_CHOICE) + .neg() + .show(this@UpcomingWidgetConfigure, tag) + } + val bottomBackground = prefs.getInt(UpcomingWidget.PREF_BACKGROUND_FADE, Color.parseColor("#00000000")) + (binding.bottomBackgroundButton as MaterialButton).iconTint = ColorStateList.valueOf(bottomBackground) + binding.bottomBackgroundButton.setOnClickListener { + val tag = UpcomingWidget.PREF_BACKGROUND_FADE + SimpleColorDialog().title(R.string.custom_theme) + .colorPreset(bottomBackground) + .colors( + this@UpcomingWidgetConfigure, + SimpleColorDialog.MATERIAL_COLOR_PALLET + ) + .setupColorWheelAlpha(true) + .allowCustom(true) + .showOutline(0x46000000) + .gridNumColumn(5) + .choiceMode(SimpleColorDialog.SINGLE_CHOICE) + .neg() + .show(this@UpcomingWidgetConfigure, tag) + } + val titleTextColor = prefs.getInt(UpcomingWidget.PREF_TITLE_TEXT_COLOR, Color.WHITE) + (binding.titleColorButton as MaterialButton).iconTint = ColorStateList.valueOf(titleTextColor) + binding.titleColorButton.setOnClickListener { + val tag = UpcomingWidget.PREF_TITLE_TEXT_COLOR + SimpleColorDialog().title(R.string.custom_theme) + .colorPreset(titleTextColor) + .colors( + this@UpcomingWidgetConfigure, + SimpleColorDialog.MATERIAL_COLOR_PALLET + ) + .allowCustom(true) + .showOutline(0x46000000) + .gridNumColumn(5) + .choiceMode(SimpleColorDialog.SINGLE_CHOICE) + .neg() + .show(this@UpcomingWidgetConfigure, tag) + } + val countdownTextColor = prefs.getInt(UpcomingWidget.PREF_COUNTDOWN_TEXT_COLOR, Color.WHITE) + (binding.countdownColorButton as MaterialButton).iconTint = ColorStateList.valueOf(countdownTextColor) + binding.countdownColorButton.setOnClickListener { + val tag = UpcomingWidget.PREF_COUNTDOWN_TEXT_COLOR + SimpleColorDialog().title(R.string.custom_theme) + .colorPreset(countdownTextColor) + .colors( + this@UpcomingWidgetConfigure, + SimpleColorDialog.MATERIAL_COLOR_PALLET + ) + .allowCustom(true) + .showOutline(0x46000000) + .gridNumColumn(5) + .choiceMode(SimpleColorDialog.SINGLE_CHOICE) + .neg() + .show(this@UpcomingWidgetConfigure, tag) + } + binding.useAppTheme.setOnCheckedChangeListener { _, isChecked -> + isMonetEnabled = isChecked + if (isChecked) { + binding.topBackgroundButton.visibility = View.GONE + binding.bottomBackgroundButton.visibility = View.GONE + binding.titleColorButton.visibility = View.GONE + binding.countdownColorButton.visibility = View.GONE + themeColors() + + } else { + binding.topBackgroundButton.visibility = View.VISIBLE + binding.bottomBackgroundButton.visibility = View.VISIBLE + binding.titleColorButton.visibility = View.VISIBLE + binding.countdownColorButton.visibility = View.VISIBLE + } + } + binding.addButton.setOnClickListener(onClickListener) + + val intent = intent + val extras = intent.extras + if (extras != null) { + appWidgetId = extras.getInt( + AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID + ) + } + + if (appWidgetId == AppWidgetManager.INVALID_APPWIDGET_ID) { + finish() + return + } + } + + private fun themeColors() { + val typedValueSurface = TypedValue() + theme.resolveAttribute(com.google.android.material.R.attr.colorSurface, typedValueSurface, true) + val backgroundColor = typedValueSurface.data + + val typedValuePrimary = TypedValue() + theme.resolveAttribute(com.google.android.material.R.attr.colorPrimary, typedValuePrimary, true) + val textColor = typedValuePrimary.data + + val typedValueOutline = TypedValue() + theme.resolveAttribute(com.google.android.material.R.attr.colorOutline, typedValueOutline, true) + val subTextColor = typedValueOutline.data + + getSharedPreferences(UpcomingWidget.PREFS_NAME, Context.MODE_PRIVATE).edit().apply { + putInt(UpcomingWidget.PREF_BACKGROUND_COLOR, backgroundColor) + putInt(UpcomingWidget.PREF_BACKGROUND_FADE, backgroundColor) + putInt(UpcomingWidget.PREF_TITLE_TEXT_COLOR, textColor) + putInt(UpcomingWidget.PREF_COUNTDOWN_TEXT_COLOR, subTextColor) + apply() + } + } + + override fun onResult(dialogTag: String, which: Int, extras: Bundle): Boolean { + if (which == SimpleDialog.OnDialogResultListener.BUTTON_POSITIVE) { + if (!isMonetEnabled) { + when (dialogTag) { + UpcomingWidget.PREF_BACKGROUND_COLOR -> { + getSharedPreferences( + UpcomingWidget.PREFS_NAME, + Context.MODE_PRIVATE + ).edit() + .putInt( + UpcomingWidget.PREF_BACKGROUND_COLOR, + extras.getInt(SimpleColorDialog.COLOR) + ) + .apply() + (binding.topBackgroundButton as MaterialButton).iconTint = + ColorStateList.valueOf(extras.getInt(SimpleColorDialog.COLOR)) + } + + UpcomingWidget.PREF_BACKGROUND_FADE -> { + getSharedPreferences( + UpcomingWidget.PREFS_NAME, + Context.MODE_PRIVATE + ).edit() + .putInt( + UpcomingWidget.PREF_BACKGROUND_FADE, + extras.getInt(SimpleColorDialog.COLOR) + ) + .apply() + (binding.bottomBackgroundButton as MaterialButton).iconTint = + ColorStateList.valueOf(extras.getInt(SimpleColorDialog.COLOR)) + } + + UpcomingWidget.PREF_TITLE_TEXT_COLOR -> { + getSharedPreferences( + UpcomingWidget.PREFS_NAME, + Context.MODE_PRIVATE + ).edit() + .putInt( + UpcomingWidget.PREF_TITLE_TEXT_COLOR, + extras.getInt(SimpleColorDialog.COLOR) + ) + .apply() + (binding.titleColorButton as MaterialButton).iconTint = + ColorStateList.valueOf(extras.getInt(SimpleColorDialog.COLOR)) + } + + UpcomingWidget.PREF_COUNTDOWN_TEXT_COLOR -> { + getSharedPreferences( + UpcomingWidget.PREFS_NAME, + Context.MODE_PRIVATE + ).edit() + .putInt( + UpcomingWidget.PREF_COUNTDOWN_TEXT_COLOR, + extras.getInt(SimpleColorDialog.COLOR) + ) + .apply() + (binding.countdownColorButton as MaterialButton).iconTint = + ColorStateList.valueOf(extras.getInt(SimpleColorDialog.COLOR)) + } + + } + } + } + return true + } +} \ No newline at end of file diff --git a/app/src/main/java/ani/dantotsu/widgets/upcoming/UpcomingWidgetConfigureActivity.kt b/app/src/main/java/ani/dantotsu/widgets/upcoming/UpcomingWidgetConfigureActivity.kt deleted file mode 100644 index f14efaa7..00000000 --- a/app/src/main/java/ani/dantotsu/widgets/upcoming/UpcomingWidgetConfigureActivity.kt +++ /dev/null @@ -1,195 +0,0 @@ -package ani.dantotsu.widgets.upcoming - -import android.appwidget.AppWidgetManager -import android.content.Context -import android.content.Intent -import android.graphics.Color -import android.os.Bundle -import android.view.View -import androidx.appcompat.app.AppCompatActivity -import androidx.core.content.ContextCompat -import ani.dantotsu.R -import ani.dantotsu.databinding.UpcomingWidgetConfigureBinding -import ani.dantotsu.themes.ThemeManager -import eltos.simpledialogfragment.SimpleDialog -import eltos.simpledialogfragment.color.SimpleColorDialog - -/** - * The configuration screen for the [UpcomingWidget] AppWidget. - */ -class UpcomingWidgetConfigureActivity : AppCompatActivity(), - SimpleDialog.OnDialogResultListener { - private var appWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID - - private var onClickListener = View.OnClickListener { - val context = this@UpcomingWidgetConfigureActivity - val appWidgetManager = AppWidgetManager.getInstance(context) - - updateAppWidget( - context, - appWidgetManager, - appWidgetId, - ) - - val resultValue = Intent() - resultValue.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId) - setResult(RESULT_OK, resultValue) - finish() - } - private lateinit var binding: UpcomingWidgetConfigureBinding - - public override fun onCreate(icicle: Bundle?) { - ThemeManager(this).applyTheme() - super.onCreate(icicle) - setResult(RESULT_CANCELED) - - binding = UpcomingWidgetConfigureBinding.inflate(layoutInflater) - setContentView(binding.root) - val prefs = getSharedPreferences(UpcomingWidget.PREFS_NAME, Context.MODE_PRIVATE) - - binding.topBackgroundButton.setOnClickListener { - val tag = UpcomingWidget.PREF_BACKGROUND_COLOR - SimpleColorDialog().title(R.string.custom_theme) - .colorPreset( - prefs.getInt( - UpcomingWidget.PREF_BACKGROUND_COLOR, - ContextCompat.getColor(this, R.color.theme) - ) - ) - .colors( - this@UpcomingWidgetConfigureActivity, - SimpleColorDialog.MATERIAL_COLOR_PALLET - ) - .setupColorWheelAlpha(true) - .allowCustom(true) - .showOutline(0x46000000) - .gridNumColumn(5) - .choiceMode(SimpleColorDialog.SINGLE_CHOICE) - .neg() - .show(this@UpcomingWidgetConfigureActivity, tag) - } - binding.bottomBackgroundButton.setOnClickListener { - val tag = UpcomingWidget.PREF_BACKGROUND_FADE - SimpleColorDialog().title(R.string.custom_theme) - .colorPreset(prefs.getInt(UpcomingWidget.PREF_BACKGROUND_FADE, Color.GRAY)) - .colors( - this@UpcomingWidgetConfigureActivity, - SimpleColorDialog.MATERIAL_COLOR_PALLET - ) - .setupColorWheelAlpha(true) - .allowCustom(true) - .showOutline(0x46000000) - .gridNumColumn(5) - .choiceMode(SimpleColorDialog.SINGLE_CHOICE) - .neg() - .show(this@UpcomingWidgetConfigureActivity, tag) - } - binding.titleColorButton.setOnClickListener { - val tag = UpcomingWidget.PREF_TITLE_TEXT_COLOR - SimpleColorDialog().title(R.string.custom_theme) - .colorPreset(prefs.getInt(UpcomingWidget.PREF_TITLE_TEXT_COLOR, Color.WHITE)) - .colors( - this@UpcomingWidgetConfigureActivity, - SimpleColorDialog.MATERIAL_COLOR_PALLET - ) - .allowCustom(true) - .showOutline(0x46000000) - .gridNumColumn(5) - .choiceMode(SimpleColorDialog.SINGLE_CHOICE) - .neg() - .show(this@UpcomingWidgetConfigureActivity, tag) - } - binding.countdownColorButton.setOnClickListener { - val tag = UpcomingWidget.PREF_COUNTDOWN_TEXT_COLOR - SimpleColorDialog().title(R.string.custom_theme) - .colorPreset( - prefs.getInt( - UpcomingWidget.PREF_COUNTDOWN_TEXT_COLOR, - Color.WHITE - ) - ) - .colors( - this@UpcomingWidgetConfigureActivity, - SimpleColorDialog.MATERIAL_COLOR_PALLET - ) - .allowCustom(true) - .showOutline(0x46000000) - .gridNumColumn(5) - .choiceMode(SimpleColorDialog.SINGLE_CHOICE) - .neg() - .show(this@UpcomingWidgetConfigureActivity, tag) - } - - binding.addButton.setOnClickListener(onClickListener) - - val intent = intent - val extras = intent.extras - if (extras != null) { - appWidgetId = extras.getInt( - AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID - ) - } - - if (appWidgetId == AppWidgetManager.INVALID_APPWIDGET_ID) { - finish() - return - } - } - - override fun onResult(dialogTag: String, which: Int, extras: Bundle): Boolean { - if (which == SimpleDialog.OnDialogResultListener.BUTTON_POSITIVE) { - when (dialogTag) { - UpcomingWidget.PREF_BACKGROUND_COLOR -> { - getSharedPreferences( - UpcomingWidget.PREFS_NAME, - Context.MODE_PRIVATE - ).edit() - .putInt( - UpcomingWidget.PREF_BACKGROUND_COLOR, - extras.getInt(SimpleColorDialog.COLOR) - ) - .apply() - } - - UpcomingWidget.PREF_BACKGROUND_FADE -> { - getSharedPreferences( - UpcomingWidget.PREFS_NAME, - Context.MODE_PRIVATE - ).edit() - .putInt( - UpcomingWidget.PREF_BACKGROUND_FADE, - extras.getInt(SimpleColorDialog.COLOR) - ) - .apply() - } - - UpcomingWidget.PREF_TITLE_TEXT_COLOR -> { - getSharedPreferences( - UpcomingWidget.PREFS_NAME, - Context.MODE_PRIVATE - ).edit() - .putInt( - UpcomingWidget.PREF_TITLE_TEXT_COLOR, - extras.getInt(SimpleColorDialog.COLOR) - ) - .apply() - } - - UpcomingWidget.PREF_COUNTDOWN_TEXT_COLOR -> { - getSharedPreferences( - UpcomingWidget.PREFS_NAME, - Context.MODE_PRIVATE - ).edit() - .putInt( - UpcomingWidget.PREF_COUNTDOWN_TEXT_COLOR, - extras.getInt(SimpleColorDialog.COLOR) - ) - .apply() - } - - } - } - return true - } - -} \ 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 deleted file mode 100644 index 52c4ef28..00000000 Binary files a/app/src/main/res/drawable-nodpi/example_appwidget_preview.png and /dev/null differ diff --git a/app/src/main/res/drawable-nodpi/statistics_widget_preview.png b/app/src/main/res/drawable-nodpi/statistics_widget_preview.png new file mode 100644 index 00000000..85d3aaef Binary files /dev/null and b/app/src/main/res/drawable-nodpi/statistics_widget_preview.png differ diff --git a/app/src/main/res/drawable-nodpi/upcoming_widget_preview.png b/app/src/main/res/drawable-nodpi/upcoming_widget_preview.png new file mode 100644 index 00000000..fcfa6e7d Binary files /dev/null and b/app/src/main/res/drawable-nodpi/upcoming_widget_preview.png differ diff --git a/app/src/main/res/drawable/ic_camera_roll_24.xml b/app/src/main/res/drawable/ic_camera_roll_24.xml new file mode 100644 index 00000000..1c4b99e3 --- /dev/null +++ b/app/src/main/res/drawable/ic_camera_roll_24.xml @@ -0,0 +1,13 @@ + + + + + diff --git a/app/src/main/res/drawable/widget_stats_rounded.xml b/app/src/main/res/drawable/widget_stats_rounded.xml new file mode 100644 index 00000000..933eab8b --- /dev/null +++ b/app/src/main/res/drawable/widget_stats_rounded.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/activity_player_settings.xml b/app/src/main/res/layout/activity_player_settings.xml index 729a4788..afbc7ee2 100644 --- a/app/src/main/res/layout/activity_player_settings.xml +++ b/app/src/main/res/layout/activity_player_settings.xml @@ -177,7 +177,8 @@ android:layout_height="64dp" android:fontFamily="@font/poppins_bold" android:gravity="center_vertical" - android:paddingHorizontal="32dp" + android:paddingStart="48dp" + android:paddingEnd="32dp" android:text="@string/sub_text_example" android:textColor="?attr/colorSecondary" app:drawableEndCompat="@drawable/ic_round_arrow_drop_down_24" diff --git a/app/src/main/res/layout/fragment_anime_extensions.xml b/app/src/main/res/layout/fragment_anime_extensions.xml deleted file mode 100644 index 2ccb2d42..00000000 --- a/app/src/main/res/layout/fragment_anime_extensions.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_manga_extensions.xml b/app/src/main/res/layout/fragment_extensions.xml similarity index 89% rename from app/src/main/res/layout/fragment_manga_extensions.xml rename to app/src/main/res/layout/fragment_extensions.xml index 99067aea..d51f19be 100644 --- a/app/src/main/res/layout/fragment_manga_extensions.xml +++ b/app/src/main/res/layout/fragment_extensions.xml @@ -7,7 +7,7 @@ android:paddingEnd="16dp"> diff --git a/app/src/main/res/layout/statistics_widget.xml b/app/src/main/res/layout/statistics_widget.xml new file mode 100644 index 00000000..d4bb070f --- /dev/null +++ b/app/src/main/res/layout/statistics_widget.xml @@ -0,0 +1,188 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/statistics_widget_configure.xml b/app/src/main/res/layout/statistics_widget_configure.xml new file mode 100644 index 00000000..6d33cc25 --- /dev/null +++ b/app/src/main/res/layout/statistics_widget_configure.xml @@ -0,0 +1,139 @@ + + + + + + + +