From 3368a1bc8df2b159b7f9c374a0b84f1f6af84fed Mon Sep 17 00:00:00 2001 From: Finnley Somdahl <87634197+rebelonion@users.noreply.github.com> Date: Sun, 29 Oct 2023 19:45:11 -0500 Subject: [PATCH] extension settings --- app/build.gradle | 1 + app/src/main/ic_launcher-playstore.png | Bin 27979 -> 16204 bytes .../main/java/ani/dantotsu/MainActivity.kt | 14 +- .../aniyomi/anime/custom/InjektModules.kt | 14 +- .../ani/dantotsu/home/AnimePageAdapter.kt | 14 ++ .../ani/dantotsu/home/MangaPageAdapter.kt | 15 +- .../dantotsu/media/MediaDetailsViewModel.kt | 8 +- .../main/java/ani/dantotsu/media/Selected.kt | 1 + .../dantotsu/media/anime/AnimeWatchAdapter.kt | 71 +++++++- .../media/anime/AnimeWatchFragment.kt | 171 ++++++++++++++---- .../dantotsu/media/manga/MangaReadAdapter.kt | 55 +++++- .../dantotsu/media/manga/MangaReadFragment.kt | 95 +++++++++- .../ani/dantotsu/parsers/AniyomiAdapter.kt | 47 ++++- .../InstalledAnimeExtensionsFragment.kt | 171 +++++++++++++----- .../InstalledMangaExtensionsFragment.kt | 73 ++++++++ .../AnimePreferenceFragmentCompat.kt | 88 +++++++++ .../MangaPreferenceFragmentCompat.kt | 78 ++++++++ .../tachiyomi/animesource/UnmeteredSource.kt | 8 + .../preference/SharedPreferencesDataStore.kt | 68 +++++++ .../source/anime/AndroidAnimeSourceManager.kt | 85 +++++++++ .../source/anime/AnimeSourceExtensions.kt | 34 ++++ .../source/manga/AndroidMangaSourceManager.kt | 84 +++++++++ .../source/manga/MangaSourceExtensions.kt | 34 ++++ .../kanade/tachiyomi/util/storage/DiskUtil.kt | 117 ++++++++++++ .../util/view/EditTextPreferenceExtensions.kt | 10 + .../widget/TachiyomiTextInputEditText.kt | 62 +++++++ .../core/metadata/tachiyomi/AnimeDetails.kt | 13 ++ .../core/metadata/tachiyomi/MangaDetails.kt | 13 ++ .../domain/entries/TriStateFilter.kt | 22 +++ .../domain/entries/anime/model/Anime.kt | 134 ++++++++++++++ .../domain/entries/manga/model/Manga.kt | 115 ++++++++++++ .../episode/service/EpisodeRecognition.kt | 119 ++++++++++++ .../domain/source/anime/model/AnimeSource.kt | 25 +++ .../anime/model/AnimeSourceWithCount.kt | 13 ++ .../domain/source/anime/model/Pin.kt | 42 +++++ .../source/anime/model/StubAnimeSource.kt | 33 ++++ .../anime/repository/AnimeSourceRepository.kt | 27 +++ .../anime/service/AnimeSourceManager.kt | 22 +++ .../source/manga/model/StubMangaSource.kt | 50 +++++ .../manga/service/MangaSourceManager.kt | 22 +++ .../local/entries/anime/LocalAnimeSource.kt | 98 ++++++++++ .../local/entries/manga/LocalMangaSource.kt | 70 +++++++ .../source/local/filter/anime/AnimeOrderBy.kt | 14 ++ .../source/local/filter/manga/MangaOrderBy.kt | 13 ++ .../java/tachiyomi/source/local/io/Archive.kt | 21 +++ app/src/main/res/anim/slide_down.xml | 7 + app/src/main/res/anim/slide_up.xml | 7 + app/src/main/res/drawable/bottom_nav.xml | 2 +- app/src/main/res/drawable/bottom_nav_gray.xml | 5 + .../res/drawable/ic_launcher_background.xml | 6 +- .../res/drawable/ic_launcher_foreground.xml | 2 +- .../main/res/layout-land/activity_media.xml | 16 +- .../main/res/layout/activity_extensions.xml | 6 + app/src/main/res/layout/activity_main.xml | 8 +- .../main/res/layout/activity_manga_reader.xml | 3 +- app/src/main/res/layout/activity_media.xml | 8 + .../main/res/layout/fragment_anime_watch.xml | 2 + app/src/main/res/layout/item_anime_page.xml | 8 +- app/src/main/res/layout/item_anime_watch.xml | 53 +++++- app/src/main/res/layout/item_extension.xml | 12 ++ app/src/main/res/layout/item_manga_page.xml | 8 +- .../res/mipmap-anydpi-v26/ic_launcher.xml | 1 - app/src/main/res/mipmap-hdpi/ic_launcher.webp | Bin 0 -> 3038 bytes .../res/mipmap-hdpi/ic_launcher_round.webp | Bin 3180 -> 3038 bytes app/src/main/res/mipmap-mdpi/ic_launcher.webp | Bin 0 -> 1946 bytes .../res/mipmap-mdpi/ic_launcher_round.webp | Bin 1994 -> 1946 bytes .../main/res/mipmap-xhdpi/ic_launcher.webp | Bin 0 -> 4054 bytes .../res/mipmap-xhdpi/ic_launcher_round.webp | Bin 4166 -> 4054 bytes .../main/res/mipmap-xxhdpi/ic_launcher.webp | Bin 0 -> 6266 bytes .../res/mipmap-xxhdpi/ic_launcher_round.webp | Bin 6482 -> 6266 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.webp | Bin 0 -> 8540 bytes .../res/mipmap-xxxhdpi/ic_launcher_round.webp | Bin 8716 -> 8540 bytes app/src/main/res/values-night/colors.xml | 2 + app/src/main/res/values/colors.xml | 2 + app/src/main/res/values/themes.xml | 1 + app/src/main/res/xml/anime_preferences.xml | 8 + 76 files changed, 2320 insertions(+), 131 deletions(-) create mode 100644 app/src/main/java/ani/dantotsu/settings/extensionprefs/AnimePreferenceFragmentCompat.kt create mode 100644 app/src/main/java/ani/dantotsu/settings/extensionprefs/MangaPreferenceFragmentCompat.kt create mode 100644 app/src/main/java/eu/kanade/tachiyomi/animesource/UnmeteredSource.kt create mode 100644 app/src/main/java/eu/kanade/tachiyomi/data/preference/SharedPreferencesDataStore.kt create mode 100644 app/src/main/java/eu/kanade/tachiyomi/source/anime/AndroidAnimeSourceManager.kt create mode 100644 app/src/main/java/eu/kanade/tachiyomi/source/anime/AnimeSourceExtensions.kt create mode 100644 app/src/main/java/eu/kanade/tachiyomi/source/manga/AndroidMangaSourceManager.kt create mode 100644 app/src/main/java/eu/kanade/tachiyomi/source/manga/MangaSourceExtensions.kt create mode 100644 app/src/main/java/eu/kanade/tachiyomi/util/storage/DiskUtil.kt create mode 100644 app/src/main/java/eu/kanade/tachiyomi/util/view/EditTextPreferenceExtensions.kt create mode 100644 app/src/main/java/eu/kanade/tachiyomi/widget/TachiyomiTextInputEditText.kt create mode 100644 app/src/main/java/tachiyomi/core/metadata/tachiyomi/AnimeDetails.kt create mode 100644 app/src/main/java/tachiyomi/core/metadata/tachiyomi/MangaDetails.kt create mode 100644 app/src/main/java/tachiyomi/domain/entries/TriStateFilter.kt create mode 100644 app/src/main/java/tachiyomi/domain/entries/anime/model/Anime.kt create mode 100644 app/src/main/java/tachiyomi/domain/entries/manga/model/Manga.kt create mode 100644 app/src/main/java/tachiyomi/domain/items/episode/service/EpisodeRecognition.kt create mode 100644 app/src/main/java/tachiyomi/domain/source/anime/model/AnimeSource.kt create mode 100644 app/src/main/java/tachiyomi/domain/source/anime/model/AnimeSourceWithCount.kt create mode 100644 app/src/main/java/tachiyomi/domain/source/anime/model/Pin.kt create mode 100644 app/src/main/java/tachiyomi/domain/source/anime/model/StubAnimeSource.kt create mode 100644 app/src/main/java/tachiyomi/domain/source/anime/repository/AnimeSourceRepository.kt create mode 100644 app/src/main/java/tachiyomi/domain/source/anime/service/AnimeSourceManager.kt create mode 100644 app/src/main/java/tachiyomi/domain/source/manga/model/StubMangaSource.kt create mode 100644 app/src/main/java/tachiyomi/domain/source/manga/service/MangaSourceManager.kt create mode 100644 app/src/main/java/tachiyomi/source/local/entries/anime/LocalAnimeSource.kt create mode 100644 app/src/main/java/tachiyomi/source/local/entries/manga/LocalMangaSource.kt create mode 100644 app/src/main/java/tachiyomi/source/local/filter/anime/AnimeOrderBy.kt create mode 100644 app/src/main/java/tachiyomi/source/local/filter/manga/MangaOrderBy.kt create mode 100644 app/src/main/java/tachiyomi/source/local/io/Archive.kt create mode 100644 app/src/main/res/anim/slide_down.xml create mode 100644 app/src/main/res/anim/slide_up.xml create mode 100644 app/src/main/res/drawable/bottom_nav_gray.xml create mode 100644 app/src/main/res/mipmap-hdpi/ic_launcher.webp create mode 100644 app/src/main/res/mipmap-mdpi/ic_launcher.webp create mode 100644 app/src/main/res/mipmap-xhdpi/ic_launcher.webp create mode 100644 app/src/main/res/mipmap-xxhdpi/ic_launcher.webp create mode 100644 app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp create mode 100644 app/src/main/res/xml/anime_preferences.xml diff --git a/app/build.gradle b/app/build.gradle index 8e394e12..141d3b05 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -65,6 +65,7 @@ dependencies { implementation 'com.github.Blatzar:NiceHttp:0.4.3' implementation 'org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.0' + implementation 'androidx.preference:preference:1.2.1' // Glide ext.glide_version = '4.16.0' diff --git a/app/src/main/ic_launcher-playstore.png b/app/src/main/ic_launcher-playstore.png index 20a7f23e3518eafa6065333b91da8c0b7f901b18..158604c1cc8923fa9b5266d5303e78a96ad32eec 100644 GIT binary patch literal 16204 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 literal 27979 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 x|H(fU|2pj-&!5O2#vjuUuP(R.id.navbar) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { - val bottomBar = findViewById(R.id.navbar) - val backgroundDrawable = bottomBar.background as GradientDrawable + + val backgroundDrawable = _bottomBar.background as GradientDrawable val currentColor = backgroundDrawable.color?.defaultColor ?: 0 val semiTransparentColor = (currentColor and 0x00FFFFFF) or 0xE8000000.toInt() backgroundDrawable.setColor(semiTransparentColor) - bottomBar.background = backgroundDrawable + _bottomBar.background = backgroundDrawable + } + val colorOverflow = this.getSharedPreferences("Dantotsu", Context.MODE_PRIVATE) + .getBoolean("colorOverflow", false) + if (!colorOverflow) { + _bottomBar.background = ContextCompat.getDrawable(this, R.drawable.bottom_nav_gray) + } diff --git a/app/src/main/java/ani/dantotsu/aniyomi/anime/custom/InjektModules.kt b/app/src/main/java/ani/dantotsu/aniyomi/anime/custom/InjektModules.kt index 67ca2afa..89280bae 100644 --- a/app/src/main/java/ani/dantotsu/aniyomi/anime/custom/InjektModules.kt +++ b/app/src/main/java/ani/dantotsu/aniyomi/anime/custom/InjektModules.kt @@ -3,6 +3,7 @@ package ani.dantotsu.aniyomi.anime.custom import android.app.Application import android.content.Context +import androidx.core.content.ContextCompat import ani.dantotsu.media.manga.MangaCache import eu.kanade.tachiyomi.extension.anime.AnimeExtensionManager import tachiyomi.core.preference.PreferenceStore @@ -12,7 +13,11 @@ import eu.kanade.tachiyomi.core.preference.AndroidPreferenceStore import eu.kanade.tachiyomi.extension.manga.MangaExtensionManager import eu.kanade.tachiyomi.network.NetworkHelper import eu.kanade.tachiyomi.network.NetworkPreferences +import eu.kanade.tachiyomi.source.anime.AndroidAnimeSourceManager +import eu.kanade.tachiyomi.source.manga.AndroidMangaSourceManager import kotlinx.serialization.json.Json +import tachiyomi.domain.source.anime.service.AnimeSourceManager +import tachiyomi.domain.source.manga.service.MangaSourceManager import uy.kohesive.injekt.api.InjektModule import uy.kohesive.injekt.api.InjektRegistrar import uy.kohesive.injekt.api.addSingleton @@ -26,9 +31,11 @@ class AppModule(val app: Application) : InjektModule { addSingletonFactory { NetworkHelper(app, get()) } addSingletonFactory { AnimeExtensionManager(app) } - addSingletonFactory { MangaExtensionManager(app) } + addSingletonFactory { AndroidAnimeSourceManager(app, get()) } + addSingletonFactory { AndroidMangaSourceManager(app, get()) } + val sharedPreferences = app.getSharedPreferences("Dantotsu", Context.MODE_PRIVATE) addSingleton(sharedPreferences) @@ -40,6 +47,11 @@ class AppModule(val app: Application) : InjektModule { } addSingletonFactory { MangaCache() } + + ContextCompat.getMainExecutor(app).execute { + get() + get() + } } } diff --git a/app/src/main/java/ani/dantotsu/home/AnimePageAdapter.kt b/app/src/main/java/ani/dantotsu/home/AnimePageAdapter.kt index feba6ac9..1117cf46 100644 --- a/app/src/main/java/ani/dantotsu/home/AnimePageAdapter.kt +++ b/app/src/main/java/ani/dantotsu/home/AnimePageAdapter.kt @@ -1,8 +1,11 @@ package ani.dantotsu.home +import android.content.Context import android.content.Intent +import android.graphics.Color import android.os.Handler import android.os.Looper +import android.util.TypedValue import android.view.LayoutInflater import android.view.View import android.view.ViewGroup @@ -19,6 +22,7 @@ import ani.dantotsu.media.GenreActivity import ani.dantotsu.MediaPageTransformer import ani.dantotsu.R import ani.dantotsu.connections.anilist.Anilist +import ani.dantotsu.currContext import ani.dantotsu.databinding.ItemAnimePageBinding import ani.dantotsu.loadData import ani.dantotsu.loadImage @@ -58,6 +62,16 @@ class AnimePageAdapter : RecyclerView.Adapter(R.id.animeUserAvatarContainer) materialCardView.setCardBackgroundColor(semiTransparentColor) + val typedValue = TypedValue() + currContext()?.theme?.resolveAttribute(android.R.attr.windowBackground, typedValue, true) + val color = typedValue.data + + + val colorOverflow = currContext()?.getSharedPreferences("Dantotsu", Context.MODE_PRIVATE)?.getBoolean("colorOverflow", false) ?: false + if (!colorOverflow) { + textInputLayout.boxBackgroundColor = (color and 0x00FFFFFF) or 0x28000000.toInt() + materialCardView.setCardBackgroundColor((color and 0x00FFFFFF) or 0x28000000.toInt()) + } binding.animeTitleContainer.updatePadding(top = statusBarHeight) diff --git a/app/src/main/java/ani/dantotsu/home/MangaPageAdapter.kt b/app/src/main/java/ani/dantotsu/home/MangaPageAdapter.kt index 5422d1cb..7f91aaaf 100644 --- a/app/src/main/java/ani/dantotsu/home/MangaPageAdapter.kt +++ b/app/src/main/java/ani/dantotsu/home/MangaPageAdapter.kt @@ -1,8 +1,10 @@ package ani.dantotsu.home +import android.content.Context import android.content.Intent import android.os.Handler import android.os.Looper +import android.util.TypedValue import android.view.LayoutInflater import android.view.View import android.view.ViewGroup @@ -19,6 +21,7 @@ import ani.dantotsu.media.GenreActivity import ani.dantotsu.MediaPageTransformer import ani.dantotsu.R import ani.dantotsu.connections.anilist.Anilist +import ani.dantotsu.currContext import ani.dantotsu.databinding.ItemMangaPageBinding import ani.dantotsu.loadData import ani.dantotsu.loadImage @@ -53,10 +56,20 @@ class MangaPageAdapter : RecyclerView.Adapter(R.id.mangaSearchBar) val currentColor = textInputLayout.boxBackgroundColor - val semiTransparentColor= (currentColor and 0x00FFFFFF) or 0xA8000000.toInt() + val semiTransparentColor = (currentColor and 0x00FFFFFF) or 0xA8000000.toInt() textInputLayout.boxBackgroundColor = semiTransparentColor val materialCardView = holder.itemView.findViewById(R.id.mangaUserAvatarContainer) materialCardView.setCardBackgroundColor(semiTransparentColor) + val typedValue = TypedValue() + currContext()?.theme?.resolveAttribute(android.R.attr.windowBackground, typedValue, true) + val color = typedValue.data + + + val colorOverflow = currContext()?.getSharedPreferences("Dantotsu", Context.MODE_PRIVATE)?.getBoolean("colorOverflow", false) ?: false + if (!colorOverflow) { + textInputLayout.boxBackgroundColor = (color and 0x00FFFFFF) or 0x28000000.toInt() + materialCardView.setCardBackgroundColor((color and 0x00FFFFFF) or 0x28000000.toInt()) + } binding.mangaTitleContainer.updatePadding(top = statusBarHeight) diff --git a/app/src/main/java/ani/dantotsu/media/MediaDetailsViewModel.kt b/app/src/main/java/ani/dantotsu/media/MediaDetailsViewModel.kt index 1842a8e6..203859a8 100644 --- a/app/src/main/java/ani/dantotsu/media/MediaDetailsViewModel.kt +++ b/app/src/main/java/ani/dantotsu/media/MediaDetailsViewModel.kt @@ -120,8 +120,8 @@ class MediaDetailsViewModel : ViewModel() { private val episodes = MutableLiveData>>(null) private val epsLoaded = mutableMapOf>() fun getEpisodes(): LiveData>> = episodes - suspend fun loadEpisodes(media: Media, i: Int) { - if (!epsLoaded.containsKey(i)) { + suspend fun loadEpisodes(media: Media, i: Int, invalidate: Boolean = false) { + if (!epsLoaded.containsKey(i) || invalidate) { epsLoaded[i] = watchSources?.loadEpisodesFromMedia(i, media) ?: return } episodes.postValue(epsLoaded) @@ -240,9 +240,9 @@ class MediaDetailsViewModel : ViewModel() { private val mangaChapters = MutableLiveData>>(null) private val mangaLoaded = mutableMapOf>() fun getMangaChapters(): LiveData>> = mangaChapters - suspend fun loadMangaChapters(media: Media, i: Int) { + suspend fun loadMangaChapters(media: Media, i: Int, invalidate: Boolean = false) { logger("Loading Manga Chapters : $mangaLoaded") - if (!mangaLoaded.containsKey(i)) tryWithSuspend { + if (!mangaLoaded.containsKey(i) || invalidate) tryWithSuspend { mangaLoaded[i] = mangaReadSources?.loadChaptersFromMedia(i, media) ?: return@tryWithSuspend } mangaChapters.postValue(mangaLoaded) diff --git a/app/src/main/java/ani/dantotsu/media/Selected.kt b/app/src/main/java/ani/dantotsu/media/Selected.kt index ddb30f58..9982db51 100644 --- a/app/src/main/java/ani/dantotsu/media/Selected.kt +++ b/app/src/main/java/ani/dantotsu/media/Selected.kt @@ -9,6 +9,7 @@ data class Selected( var chip: Int = 0, //var source: String = "", var sourceIndex: Int = 0, + var langIndex: Int = 0, var preferDub: Boolean = false, var server: String? = null, var video: Int = 0, 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 feb75e7a..33dcefe5 100644 --- a/app/src/main/java/ani/dantotsu/media/anime/AnimeWatchAdapter.kt +++ b/app/src/main/java/ani/dantotsu/media/anime/AnimeWatchAdapter.kt @@ -1,6 +1,8 @@ package ani.dantotsu.media.anime import android.annotation.SuppressLint +import android.app.AlertDialog +import android.content.Context import android.content.Intent import android.net.Uri import android.util.TypedValue @@ -8,23 +10,38 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.ArrayAdapter +import android.widget.FrameLayout import android.widget.ImageView import android.widget.LinearLayout +import android.widget.Toast import androidx.core.content.ContextCompat import androidx.lifecycle.lifecycleScope import androidx.recyclerview.widget.RecyclerView +import androidx.viewpager2.widget.ViewPager2 import ani.dantotsu.* import ani.dantotsu.databinding.ItemAnimeWatchBinding import ani.dantotsu.databinding.ItemChipBinding import ani.dantotsu.media.Media import ani.dantotsu.media.MediaDetailsActivity import ani.dantotsu.media.SourceSearchDialogFragment +import ani.dantotsu.parsers.AnimeSources +import ani.dantotsu.parsers.DynamicAnimeParser import ani.dantotsu.parsers.WatchSources +import ani.dantotsu.settings.ExtensionsActivity +import ani.dantotsu.settings.extensionprefs.AnimeSourcePreferencesFragment import ani.dantotsu.subcriptions.Notifications.Companion.openSettings import ani.dantotsu.subcriptions.Subscription.Companion.getChannelId import com.google.android.material.chip.Chip +import com.google.android.material.tabs.TabLayout +import com.google.android.material.textfield.TextInputLayout +import eu.kanade.tachiyomi.animesource.ConfigurableAnimeSource +import eu.kanade.tachiyomi.extension.anime.AnimeExtensionManager +import eu.kanade.tachiyomi.extension.anime.model.AnimeExtension import kotlinx.coroutines.MainScope import kotlinx.coroutines.launch +import uy.kohesive.injekt.Injekt +import uy.kohesive.injekt.api.get +import java.lang.IndexOutOfBoundsException class AnimeWatchAdapter( private val media: Media, @@ -70,7 +87,8 @@ class AnimeWatchAdapter( } //Source Selection - val source = media.selected!!.sourceIndex.let { if (it >= watchSources.names.size) 0 else it } + var source = media.selected!!.sourceIndex.let { if (it >= watchSources.names.size) 0 else it } + setLanguageList(media.selected!!.langIndex,source) if (watchSources.names.isNotEmpty() && source in 0 until watchSources.names.size) { binding.animeSource.setText(watchSources.names[source]) watchSources[source].apply { @@ -92,11 +110,41 @@ class AnimeWatchAdapter( binding.animeSourceDubbed.isChecked = selectDub changing = false binding.animeSourceDubbedCont.visibility = if (isDubAvailableSeparately) View.VISIBLE else View.GONE + source = i + setLanguageList(0,i) } subscribeButton(false) - fragment.loadEpisodes(i) + fragment.loadEpisodes(i, false) } + binding.animeSourceLanguage.setOnItemClickListener { _, _, i, _ -> + // Check if 'extension' and 'selected' properties exist and are accessible + (watchSources[source] as? DynamicAnimeParser)?.let { ext -> + ext.sourceLanguage = i + fragment.onLangChange(i) + fragment.onSourceChange(media.selected!!.sourceIndex).apply { + binding.animeSourceTitle.text = showUserText + showUserTextListener = { MainScope().launch { binding.animeSourceTitle.text = it } } + changing = true + binding.animeSourceDubbed.isChecked = selectDub + changing = false + binding.animeSourceDubbedCont.visibility = if (isDubAvailableSeparately) View.VISIBLE else View.GONE + setLanguageList(i, source) + } + subscribeButton(false) + fragment.loadEpisodes(media.selected!!.sourceIndex, true) + } ?: run { + } + } + + //settings + binding.animeSourceSettings.setOnClickListener { + (watchSources[source] as? DynamicAnimeParser)?.let { ext -> + fragment.openSettings(ext.extension) + } + } + + //Subscription subscribe = MediaDetailsActivity.PopImageButton( fragment.lifecycleScope, @@ -263,6 +311,25 @@ class AnimeWatchAdapter( } } + fun setLanguageList(lang: Int, source: Int) { + val binding = _binding + if (watchSources is AnimeSources) { + val parser = watchSources[source] as? DynamicAnimeParser + if (parser != null) { + (watchSources[source] as? DynamicAnimeParser)?.let { ext -> + ext.sourceLanguage = lang + } + try { + binding?.animeSourceLanguage?.setText(parser.extension.sources[lang].lang) + }catch (e: IndexOutOfBoundsException) { + binding?.animeSourceLanguage?.setText(parser.extension.sources.firstOrNull()?.lang ?: "Unknown") + } + binding?.animeSourceLanguage?.setAdapter(ArrayAdapter(fragment.requireContext(), R.layout.item_dropdown, parser.extension.sources.map { it.lang })) + + } + } + } + override fun getItemCount(): Int = 1 inner class ViewHolder(val binding: ItemAnimeWatchBinding) : RecyclerView.ViewHolder(binding.root) { 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 fc3893c2..0613eceb 100644 --- a/app/src/main/java/ani/dantotsu/media/anime/AnimeWatchFragment.kt +++ b/app/src/main/java/ani/dantotsu/media/anime/AnimeWatchFragment.kt @@ -1,11 +1,17 @@ package ani.dantotsu.media.anime import android.annotation.SuppressLint +import android.app.AlertDialog import android.os.Bundle import android.os.Parcelable import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import android.widget.FrameLayout +import android.widget.ImageView +import android.widget.LinearLayout +import android.widget.Toast +import androidx.cardview.widget.CardView import androidx.core.math.MathUtils import androidx.core.view.updatePadding import androidx.fragment.app.Fragment @@ -13,24 +19,38 @@ import androidx.fragment.app.activityViewModels import androidx.lifecycle.lifecycleScope import androidx.recyclerview.widget.ConcatAdapter import androidx.recyclerview.widget.GridLayoutManager +import androidx.viewpager2.widget.ViewPager2 import ani.dantotsu.* import ani.dantotsu.databinding.FragmentAnimeWatchBinding import ani.dantotsu.media.Media +import ani.dantotsu.media.MediaDetailsActivity import ani.dantotsu.media.MediaDetailsViewModel import ani.dantotsu.parsers.AnimeParser import ani.dantotsu.parsers.AnimeSources import ani.dantotsu.parsers.HAnimeSources +import ani.dantotsu.settings.ExtensionsActivity +import ani.dantotsu.settings.InstalledAnimeExtensionsFragment import ani.dantotsu.settings.PlayerSettings import ani.dantotsu.settings.UserInterfaceSettings +import ani.dantotsu.settings.extensionprefs.AnimeSourcePreferencesFragment import ani.dantotsu.subcriptions.Notifications import ani.dantotsu.subcriptions.Notifications.Group.ANIME_GROUP import ani.dantotsu.subcriptions.Subscription.Companion.getChannelId import ani.dantotsu.subcriptions.SubscriptionHelper import ani.dantotsu.subcriptions.SubscriptionHelper.Companion.saveSubscription +import com.google.android.material.appbar.AppBarLayout +import com.google.android.material.navigationrail.NavigationRailView +import com.google.android.material.tabs.TabLayout +import com.google.android.material.textfield.TextInputLayout +import eu.kanade.tachiyomi.animesource.ConfigurableAnimeSource +import eu.kanade.tachiyomi.extension.anime.AnimeExtensionManager +import eu.kanade.tachiyomi.extension.anime.model.AnimeExtension import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.async import kotlinx.coroutines.awaitAll import kotlinx.coroutines.launch +import uy.kohesive.injekt.Injekt +import uy.kohesive.injekt.api.get import kotlin.math.ceil import kotlin.math.max import kotlin.math.roundToInt @@ -214,6 +234,13 @@ class AnimeWatchFragment : Fragment() { return model.watchSources?.get(i)!! } + fun onLangChange(i: Int) { + val selected = model.loadSelected(media) + selected.langIndex = i + model.saveSelected(media.id, selected, requireActivity()) + media.selected = selected + } + fun onDubClicked(checked: Boolean) { val selected = model.loadSelected(media) model.watchSources?.get(selected.sourceIndex)?.selectDub = checked @@ -223,8 +250,8 @@ class AnimeWatchFragment : Fragment() { lifecycleScope.launch(Dispatchers.IO) { model.forceLoadEpisode(media, selected.sourceIndex) } } - fun loadEpisodes(i: Int) { - lifecycleScope.launch(Dispatchers.IO) { model.loadEpisodes(media, i) } + fun loadEpisodes(i: Int, invalidate: Boolean) { + lifecycleScope.launch(Dispatchers.IO) { model.loadEpisodes(media, i, invalidate) } } fun onIconPressed(viewType: Int, rev: Boolean) { @@ -262,45 +289,115 @@ class AnimeWatchFragment : Fragment() { else getString(R.string.unsubscribed_notification) ) } - - fun onEpisodeClick(i: String) { - model.continueMedia = false - model.saveSelected(media.id, media.selected!!, requireActivity()) - model.onEpisodeClick(media, i, requireActivity().supportFragmentManager) - } - - @SuppressLint("NotifyDataSetChanged") - private fun reload() { - val selected = model.loadSelected(media) - - //Find latest episode for subscription - selected.latest = media.anime?.episodes?.values?.maxOfOrNull { it.number.toFloatOrNull() ?: 0f } ?: 0f - selected.latest = media.userProgress?.toFloat()?.takeIf { selected.latest < it } ?: selected.latest - - model.saveSelected(media.id, selected, requireActivity()) - headerAdapter.handleEpisodes() - episodeAdapter.notifyItemRangeRemoved(0, episodeAdapter.arr.size) - var arr: ArrayList = arrayListOf() - if (media.anime!!.episodes != null) { - val end = if (end != null && end!! < media.anime!!.episodes!!.size) end else null - arr.addAll( - media.anime!!.episodes!!.values.toList() - .slice(start..(end ?: (media.anime!!.episodes!!.size - 1))) - ) - if (reverse) - arr = (arr.reversed() as? ArrayList) ?: arr + fun openSettings(pkg: AnimeExtension.Installed){ + val changeUIVisibility: (Boolean) -> Unit = { show -> + val activity = requireActivity() as MediaDetailsActivity + val visibility = if (show) View.VISIBLE else View.GONE + activity.findViewById(R.id.mediaAppBar).visibility = visibility + activity.findViewById(R.id.mediaViewPager).visibility = visibility + activity.findViewById(R.id.mediaCover).visibility = visibility + activity.findViewById(R.id.mediaClose).visibility = visibility + try{ + activity.findViewById(R.id.mediaTab).visibility = visibility + }catch (e: ClassCastException){ + activity.findViewById(R.id.mediaTab).visibility = visibility + } + activity.findViewById(R.id.fragmentExtensionsContainer).visibility = + if (show) View.GONE else View.VISIBLE + } + val allSettings = pkg.sources.filterIsInstance() + if (allSettings.isNotEmpty()) { + var selectedSetting = allSettings[0] + if (allSettings.size > 1) { + val names = allSettings.map { it.lang }.toTypedArray() + var selectedIndex = 0 + AlertDialog.Builder(requireContext()) + .setTitle("Select a Source") + .setSingleChoiceItems(names, selectedIndex) { _, which -> + selectedIndex = which + } + .setPositiveButton("OK") { dialog, _ -> + selectedSetting = allSettings[selectedIndex] + dialog.dismiss() + + // Move the fragment transaction here + val fragment = + AnimeSourcePreferencesFragment().getInstance(selectedSetting.id){ + changeUIVisibility(true) + loadEpisodes(media.selected!!.sourceIndex, true) + } + parentFragmentManager.beginTransaction() + .setCustomAnimations(R.anim.slide_up, R.anim.slide_down) + .replace(R.id.fragmentExtensionsContainer, fragment) + .addToBackStack(null) + .commit() + } + .setNegativeButton("Cancel") { dialog, _ -> + dialog.cancel() + changeUIVisibility(true) + return@setNegativeButton + } + .show() + } else { + // If there's only one setting, proceed with the fragment transaction + val fragment = AnimeSourcePreferencesFragment().getInstance(selectedSetting.id){ + changeUIVisibility(true) + loadEpisodes(media.selected!!.sourceIndex, true) + } + parentFragmentManager.beginTransaction() + .setCustomAnimations(R.anim.slide_up, R.anim.slide_down) + .replace(R.id.fragmentExtensionsContainer, fragment) + .addToBackStack(null) + .commit() + } + + changeUIVisibility(false) + } else { + Toast.makeText(requireContext(), "Source is not configurable", Toast.LENGTH_SHORT) + .show() } - episodeAdapter.arr = arr - episodeAdapter.updateType(style ?: uiSettings.animeDefaultView) - episodeAdapter.notifyItemRangeInserted(0, arr.size) } - override fun onDestroy() { - model.watchSources?.flushText() - super.onDestroy() - } + fun onEpisodeClick(i: String) { + model.continueMedia = false + model.saveSelected(media.id, media.selected!!, requireActivity()) + model.onEpisodeClick(media, i, requireActivity().supportFragmentManager) + } - var state: Parcelable? = null + @SuppressLint("NotifyDataSetChanged") + private fun reload() { + val selected = model.loadSelected(media) + + //Find latest episode for subscription + selected.latest = + media.anime?.episodes?.values?.maxOfOrNull { it.number.toFloatOrNull() ?: 0f } ?: 0f + selected.latest = + media.userProgress?.toFloat()?.takeIf { selected.latest < it } ?: selected.latest + + model.saveSelected(media.id, selected, requireActivity()) + headerAdapter.handleEpisodes() + episodeAdapter.notifyItemRangeRemoved(0, episodeAdapter.arr.size) + var arr: ArrayList = arrayListOf() + if (media.anime!!.episodes != null) { + val end = if (end != null && end!! < media.anime!!.episodes!!.size) end else null + arr.addAll( + media.anime!!.episodes!!.values.toList() + .slice(start..(end ?: (media.anime!!.episodes!!.size - 1))) + ) + if (reverse) + arr = (arr.reversed() as? ArrayList) ?: arr + } + episodeAdapter.arr = arr + episodeAdapter.updateType(style ?: uiSettings.animeDefaultView) + episodeAdapter.notifyItemRangeInserted(0, arr.size) + } + + override fun onDestroy() { + model.watchSources?.flushText() + super.onDestroy() + } + + var state: Parcelable? = null override fun onResume() { super.onResume() binding.mediaInfoProgressBar.visibility = progress diff --git a/app/src/main/java/ani/dantotsu/media/manga/MangaReadAdapter.kt b/app/src/main/java/ani/dantotsu/media/manga/MangaReadAdapter.kt index 3579d6b9..8b64057d 100644 --- a/app/src/main/java/ani/dantotsu/media/manga/MangaReadAdapter.kt +++ b/app/src/main/java/ani/dantotsu/media/manga/MangaReadAdapter.kt @@ -17,12 +17,17 @@ import ani.dantotsu.databinding.ItemChipBinding import ani.dantotsu.media.Media import ani.dantotsu.media.MediaDetailsActivity import ani.dantotsu.media.SourceSearchDialogFragment +import ani.dantotsu.parsers.AnimeSources +import ani.dantotsu.parsers.DynamicAnimeParser +import ani.dantotsu.parsers.DynamicMangaParser import ani.dantotsu.parsers.MangaReadSources +import ani.dantotsu.parsers.MangaSources import ani.dantotsu.subcriptions.Notifications.Companion.openSettings import ani.dantotsu.subcriptions.Subscription.Companion.getChannelId import com.google.android.material.chip.Chip import kotlinx.coroutines.MainScope import kotlinx.coroutines.launch +import java.lang.IndexOutOfBoundsException class MangaReadAdapter( private val media: Media, @@ -50,10 +55,10 @@ class MangaReadAdapter( } //Source Selection - val source = media.selected!!.sourceIndex.let { if (it >= mangaReadSources.names.size) 0 else it } + var source = media.selected!!.sourceIndex.let { if (it >= mangaReadSources.names.size) 0 else it } + setLanguageList(media.selected!!.langIndex,source) if (mangaReadSources.names.isNotEmpty() && source in 0 until mangaReadSources.names.size) { binding.animeSource.setText(mangaReadSources.names[source]) - mangaReadSources[source].apply { binding.animeSourceTitle.text = showUserText showUserTextListener = { MainScope().launch { binding.animeSourceTitle.text = it } } @@ -65,9 +70,34 @@ class MangaReadAdapter( fragment.onSourceChange(i).apply { binding.animeSourceTitle.text = showUserText showUserTextListener = { MainScope().launch { binding.animeSourceTitle.text = it } } + source = i + setLanguageList(0,i) } subscribeButton(false) - fragment.loadChapters(i) + fragment.loadChapters(i, false) + } + + binding.animeSourceLanguage.setOnItemClickListener { _, _, i, _ -> + // Check if 'extension' and 'selected' properties exist and are accessible + (mangaReadSources[source] as? DynamicMangaParser)?.let { ext -> + ext.sourceLanguage = i + fragment.onLangChange(i) + fragment.onSourceChange(media.selected!!.sourceIndex).apply { + binding.animeSourceTitle.text = showUserText + showUserTextListener = { MainScope().launch { binding.animeSourceTitle.text = it } } + setLanguageList(i, source) + } + subscribeButton(false) + fragment.loadChapters(media.selected!!.sourceIndex, true) + } ?: run { + } + } + + //settings + binding.animeSourceSettings.setOnClickListener { + (mangaReadSources[source] as? DynamicMangaParser)?.let { ext -> + fragment.openSettings(ext.extension) + } } //Subscription @@ -224,6 +254,25 @@ class MangaReadAdapter( } } + fun setLanguageList(lang: Int, source: Int) { + val binding = _binding + if (mangaReadSources is MangaSources) { + val parser = mangaReadSources[source] as? DynamicMangaParser + if (parser != null) { + (mangaReadSources[source] as? DynamicMangaParser)?.let { ext -> + ext.sourceLanguage = lang + } + try { + binding?.animeSourceLanguage?.setText(parser.extension.sources[lang].lang) + }catch (e: IndexOutOfBoundsException) { + binding?.animeSourceLanguage?.setText(parser.extension.sources.firstOrNull()?.lang ?: "Unknown") + } + binding?.animeSourceLanguage?.setAdapter(ArrayAdapter(fragment.requireContext(), R.layout.item_dropdown, parser.extension.sources.map { it.lang })) + + } + } + } + override fun getItemCount(): Int = 1 inner class ViewHolder(val binding: ItemAnimeWatchBinding) : RecyclerView.ViewHolder(binding.root) diff --git a/app/src/main/java/ani/dantotsu/media/manga/MangaReadFragment.kt b/app/src/main/java/ani/dantotsu/media/manga/MangaReadFragment.kt index c8a83cca..61af0bf5 100644 --- a/app/src/main/java/ani/dantotsu/media/manga/MangaReadFragment.kt +++ b/app/src/main/java/ani/dantotsu/media/manga/MangaReadFragment.kt @@ -1,11 +1,15 @@ package ani.dantotsu.media.manga import android.annotation.SuppressLint +import android.app.AlertDialog import android.os.Bundle import android.os.Parcelable import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import android.widget.FrameLayout +import android.widget.Toast +import androidx.cardview.widget.CardView import androidx.core.math.MathUtils.clamp import androidx.core.view.updatePadding import androidx.fragment.app.Fragment @@ -13,20 +17,30 @@ import androidx.fragment.app.activityViewModels import androidx.lifecycle.lifecycleScope import androidx.recyclerview.widget.ConcatAdapter import androidx.recyclerview.widget.GridLayoutManager +import androidx.viewpager2.widget.ViewPager2 import ani.dantotsu.* import ani.dantotsu.databinding.FragmentAnimeWatchBinding import ani.dantotsu.media.manga.mangareader.ChapterLoaderDialog import ani.dantotsu.media.Media +import ani.dantotsu.media.MediaDetailsActivity import ani.dantotsu.media.MediaDetailsViewModel import ani.dantotsu.parsers.HMangaSources import ani.dantotsu.parsers.MangaParser import ani.dantotsu.parsers.MangaSources import ani.dantotsu.settings.UserInterfaceSettings +import ani.dantotsu.settings.extensionprefs.AnimeSourcePreferencesFragment +import ani.dantotsu.settings.extensionprefs.MangaSourcePreferencesFragment import ani.dantotsu.subcriptions.Notifications import ani.dantotsu.subcriptions.Notifications.Group.MANGA_GROUP import ani.dantotsu.subcriptions.Subscription.Companion.getChannelId import ani.dantotsu.subcriptions.SubscriptionHelper import ani.dantotsu.subcriptions.SubscriptionHelper.Companion.saveSubscription +import com.google.android.material.appbar.AppBarLayout +import com.google.android.material.navigationrail.NavigationRailView +import eu.kanade.tachiyomi.animesource.ConfigurableAnimeSource +import eu.kanade.tachiyomi.extension.anime.model.AnimeExtension +import eu.kanade.tachiyomi.extension.manga.model.MangaExtension +import eu.kanade.tachiyomi.source.ConfigurableSource import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlin.math.ceil @@ -185,8 +199,16 @@ open class MangaReadFragment : Fragment() { return model.mangaReadSources?.get(i)!! } - fun loadChapters(i: Int) { - lifecycleScope.launch(Dispatchers.IO) { model.loadMangaChapters(media, i) } + fun onLangChange(i: Int) { + val selected = model.loadSelected(media) + selected.langIndex = i + model.saveSelected(media.id, selected, requireActivity()) + media.selected = selected + } + + + fun loadChapters(i: Int, invalidate: Boolean) { + lifecycleScope.launch(Dispatchers.IO) { model.loadMangaChapters(media, i, invalidate) } } fun onIconPressed(viewType: Int, rev: Boolean) { @@ -225,6 +247,75 @@ open class MangaReadFragment : Fragment() { ) } + fun openSettings(pkg: MangaExtension.Installed){ + val changeUIVisibility: (Boolean) -> Unit = { show -> + val activity = requireActivity() as MediaDetailsActivity + val visibility = if (show) View.VISIBLE else View.GONE + activity.findViewById(R.id.mediaAppBar).visibility = visibility + activity.findViewById(R.id.mediaViewPager).visibility = visibility + activity.findViewById(R.id.mediaCover).visibility = visibility + activity.findViewById(R.id.mediaClose).visibility = visibility + try{ + activity.findViewById(R.id.mediaTab).visibility = visibility + }catch (e: ClassCastException){ + activity.findViewById(R.id.mediaTab).visibility = visibility + } + activity.findViewById(R.id.fragmentExtensionsContainer).visibility = + if (show) View.GONE else View.VISIBLE + } + val allSettings = pkg.sources.filterIsInstance() + if (allSettings.isNotEmpty()) { + var selectedSetting = allSettings[0] + if (allSettings.size > 1) { + val names = allSettings.map { it.lang }.toTypedArray() + var selectedIndex = 0 + AlertDialog.Builder(requireContext()) + .setTitle("Select a Source") + .setSingleChoiceItems(names, selectedIndex) { _, which -> + selectedIndex = which + } + .setPositiveButton("OK") { dialog, _ -> + selectedSetting = allSettings[selectedIndex] + dialog.dismiss() + + // Move the fragment transaction here + val fragment = + MangaSourcePreferencesFragment().getInstance(selectedSetting.id){ + changeUIVisibility(true) + loadChapters(media.selected!!.sourceIndex, true) + } + parentFragmentManager.beginTransaction() + .setCustomAnimations(R.anim.slide_up, R.anim.slide_down) + .replace(R.id.fragmentExtensionsContainer, fragment) + .addToBackStack(null) + .commit() + } + .setNegativeButton("Cancel") { dialog, _ -> + dialog.cancel() + changeUIVisibility(true) + return@setNegativeButton + } + .show() + } else { + // If there's only one setting, proceed with the fragment transaction + val fragment = MangaSourcePreferencesFragment().getInstance(selectedSetting.id){ + changeUIVisibility(true) + loadChapters(media.selected!!.sourceIndex, true) + } + parentFragmentManager.beginTransaction() + .setCustomAnimations(R.anim.slide_up, R.anim.slide_down) + .replace(R.id.fragmentExtensionsContainer, fragment) + .addToBackStack(null) + .commit() + } + + changeUIVisibility(false) + } else { + Toast.makeText(requireContext(), "Source is not configurable", Toast.LENGTH_SHORT) + .show() + } + } + fun onMangaChapterClick(i: String) { model.continueMedia = false media.manga?.chapters?.get(i)?.let { diff --git a/app/src/main/java/ani/dantotsu/parsers/AniyomiAdapter.kt b/app/src/main/java/ani/dantotsu/parsers/AniyomiAdapter.kt index 6a4a58a7..887e6608 100644 --- a/app/src/main/java/ani/dantotsu/parsers/AniyomiAdapter.kt +++ b/app/src/main/java/ani/dantotsu/parsers/AniyomiAdapter.kt @@ -18,6 +18,8 @@ import ani.dantotsu.currContext import ani.dantotsu.logger import ani.dantotsu.media.manga.ImageData import ani.dantotsu.media.manga.MangaCache +import eu.kanade.tachiyomi.animesource.ConfigurableAnimeSource +import eu.kanade.tachiyomi.animesource.model.AnimeFilter import eu.kanade.tachiyomi.animesource.model.SEpisode import eu.kanade.tachiyomi.animesource.model.AnimeFilterList import eu.kanade.tachiyomi.animesource.model.AnimesPage @@ -62,6 +64,7 @@ class AniyomiAdapter { class DynamicAnimeParser(extension: AnimeExtension.Installed) : AnimeParser() { val extension: AnimeExtension.Installed + var sourceLanguage = 0 init { this.extension = extension } @@ -71,7 +74,12 @@ class DynamicAnimeParser(extension: AnimeExtension.Installed) : AnimeParser() { override val isDubAvailableSeparately = false override val isNSFW = extension.isNsfw override suspend fun loadEpisodes(animeLink: String, extra: Map?, sAnime: SAnime): List { - val source = extension.sources.first() + val source = try{ + extension.sources[sourceLanguage] + }catch (e: Exception){ + sourceLanguage = 0 + extension.sources[sourceLanguage] + } if (source is AnimeCatalogueSource) { try { val res = source.getEpisodeList(sAnime) @@ -91,7 +99,12 @@ class DynamicAnimeParser(extension: AnimeExtension.Installed) : AnimeParser() { } override suspend fun loadVideoServers(episodeLink: String, extra: Map?, sEpisode: SEpisode): List { - val source = extension.sources.first() as? AnimeCatalogueSource ?: return emptyList() + val source = try{ + extension.sources[sourceLanguage] + }catch (e: Exception){ + sourceLanguage = 0 + extension.sources[sourceLanguage] + } as? AnimeCatalogueSource ?: return emptyList() return try { val videos = source.getVideoList(sEpisode) @@ -108,8 +121,12 @@ class DynamicAnimeParser(extension: AnimeExtension.Installed) : AnimeParser() { } override suspend fun search(query: String): List { - val source = extension.sources.first() as? AnimeCatalogueSource ?: return emptyList() - + val source = try{ + extension.sources[sourceLanguage] + }catch (e: Exception){ + sourceLanguage = 0 + extension.sources[sourceLanguage] + } as? AnimeCatalogueSource ?: return emptyList() return try { val res = source.fetchSearchAnime(1, query, AnimeFilterList()).toBlocking().first() convertAnimesPageToShowResponse(res) @@ -174,6 +191,7 @@ class DynamicAnimeParser(extension: AnimeExtension.Installed) : AnimeParser() { class DynamicMangaParser(extension: MangaExtension.Installed) : MangaParser() { val mangaCache = Injekt.get() val extension: MangaExtension.Installed + var sourceLanguage = 0 init { this.extension = extension } @@ -183,7 +201,12 @@ class DynamicMangaParser(extension: MangaExtension.Installed) : MangaParser() { override val isNSFW = extension.isNsfw override suspend fun loadChapters(mangaLink: String, extra: Map?, sManga: SManga): List { - val source = extension.sources.first() as? CatalogueSource ?: return emptyList() + val source = try{ + extension.sources[sourceLanguage] + }catch (e: Exception){ + sourceLanguage = 0 + extension.sources[sourceLanguage] + } as? HttpSource ?: return emptyList() return try { val res = source.getChapterList(sManga) @@ -201,7 +224,12 @@ class DynamicMangaParser(extension: MangaExtension.Installed) : MangaParser() { override suspend fun loadImages(chapterLink: String, sChapter: SChapter): List { - val source = extension.sources.first() as? HttpSource ?: return emptyList() + val source = try{ + extension.sources[sourceLanguage] + }catch (e: Exception){ + sourceLanguage = 0 + extension.sources[sourceLanguage] + } as? HttpSource ?: return emptyList() return coroutineScope { try { @@ -321,7 +349,12 @@ class DynamicMangaParser(extension: MangaExtension.Installed) : MangaParser() { override suspend fun search(query: String): List { - val source = extension.sources.first() as? HttpSource ?: return emptyList() + val source = try{ + extension.sources[sourceLanguage] + }catch (e: Exception){ + sourceLanguage = 0 + extension.sources[sourceLanguage] + } as? HttpSource ?: return emptyList() return try { val res = source.fetchSearchManga(1, query, FilterList()).toBlocking().first() diff --git a/app/src/main/java/ani/dantotsu/settings/InstalledAnimeExtensionsFragment.kt b/app/src/main/java/ani/dantotsu/settings/InstalledAnimeExtensionsFragment.kt index 9294bd3c..6a1bf640 100644 --- a/app/src/main/java/ani/dantotsu/settings/InstalledAnimeExtensionsFragment.kt +++ b/app/src/main/java/ani/dantotsu/settings/InstalledAnimeExtensionsFragment.kt @@ -1,5 +1,6 @@ package ani.dantotsu.settings +import android.app.AlertDialog import android.app.NotificationManager import android.content.Context import android.os.Bundle @@ -7,8 +8,10 @@ import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import android.widget.FrameLayout import android.widget.ImageView import android.widget.TextView +import android.widget.Toast import androidx.core.app.NotificationCompat import androidx.core.content.ContextCompat import androidx.fragment.app.Fragment @@ -17,10 +20,15 @@ import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.ListAdapter import androidx.recyclerview.widget.RecyclerView +import androidx.viewpager2.widget.ViewPager2 import ani.dantotsu.R import ani.dantotsu.databinding.FragmentAnimeExtensionsBinding import ani.dantotsu.loadData +import ani.dantotsu.settings.extensionprefs.AnimeSourcePreferencesFragment +import com.google.android.material.tabs.TabLayout +import com.google.android.material.textfield.TextInputLayout import com.google.firebase.crashlytics.FirebaseCrashlytics +import eu.kanade.tachiyomi.animesource.ConfigurableAnimeSource import eu.kanade.tachiyomi.data.notification.Notifications import eu.kanade.tachiyomi.extension.anime.AnimeExtensionManager import eu.kanade.tachiyomi.extension.anime.model.AnimeExtension @@ -30,62 +38,129 @@ import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get class InstalledAnimeExtensionsFragment : Fragment() { + + private var _binding: FragmentAnimeExtensionsBinding? = null private val binding get() = _binding!! private lateinit var extensionsRecyclerView: RecyclerView val skipIcons = loadData("skip_extension_icons") ?: false private val animeExtensionManager: AnimeExtensionManager = Injekt.get() private val extensionsAdapter = AnimeExtensionsAdapter({ pkg -> - if (isAdded) { // Check if the fragment is currently added to its activity - val context = requireContext() // Store context in a variable - val notificationManager = - context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager // Initialize NotificationManager once + val allSettings = pkg.sources.filterIsInstance() + if (allSettings.isNotEmpty()) { + var selectedSetting = allSettings[0] + if (allSettings.size > 1) { + val names = allSettings.map { it.lang }.toTypedArray() + var selectedIndex = 0 + AlertDialog.Builder(requireContext()) + .setTitle("Select a Source") + .setSingleChoiceItems(names, selectedIndex) { _, which -> + selectedIndex = which + } + .setPositiveButton("OK") { dialog, _ -> + selectedSetting = allSettings[selectedIndex] + dialog.dismiss() - if (pkg.hasUpdate) { - animeExtensionManager.updateExtension(pkg) - .observeOn(AndroidSchedulers.mainThread()) // Observe on main thread - .subscribe( - { installStep -> - val builder = NotificationCompat.Builder( - context, - Notifications.CHANNEL_DOWNLOADER_PROGRESS - ) - .setSmallIcon(R.drawable.ic_round_sync_24) - .setContentTitle("Updating extension") - .setContentText("Step: $installStep") - .setPriority(NotificationCompat.PRIORITY_LOW) - notificationManager.notify(1, builder.build()) - }, - { error -> - FirebaseCrashlytics.getInstance().recordException(error) - Log.e("AnimeExtensionsAdapter", "Error: ", error) // Log the error - val builder = NotificationCompat.Builder( - context, - Notifications.CHANNEL_DOWNLOADER_ERROR - ) - .setSmallIcon(R.drawable.ic_round_info_24) - .setContentTitle("Update failed: ${error.message}") - .setContentText("Error: ${error.message}") - .setPriority(NotificationCompat.PRIORITY_HIGH) - notificationManager.notify(1, builder.build()) - }, - { - val builder = NotificationCompat.Builder( - context, - Notifications.CHANNEL_DOWNLOADER_PROGRESS - ) - .setSmallIcon(androidx.media3.ui.R.drawable.exo_ic_check) - .setContentTitle("Update complete") - .setContentText("The extension has been successfully updated.") - .setPriority(NotificationCompat.PRIORITY_LOW) - notificationManager.notify(1, builder.build()) + // Move the fragment transaction here + val fragment = AnimeSourcePreferencesFragment().getInstance(selectedSetting.id){ + val activity = requireActivity() as ExtensionsActivity + activity.findViewById(R.id.viewPager).visibility = View.VISIBLE + activity.findViewById(R.id.tabLayout).visibility = View.VISIBLE + activity.findViewById(R.id.searchView).visibility = View.VISIBLE + activity.findViewById(R.id.fragmentExtensionsContainer).visibility = + View.GONE } - ) + parentFragmentManager.beginTransaction() + .setCustomAnimations(R.anim.slide_up, R.anim.slide_down) + .replace(R.id.fragmentExtensionsContainer, fragment) + .addToBackStack(null) + .commit() + } + .setNegativeButton("Cancel") { dialog, _ -> + dialog.cancel() + return@setNegativeButton + } + .show() } else { - animeExtensionManager.uninstallExtension(pkg.pkgName) + // If there's only one setting, proceed with the fragment transaction + val fragment = AnimeSourcePreferencesFragment().getInstance(selectedSetting.id){ + val activity = requireActivity() as ExtensionsActivity + activity.findViewById(R.id.viewPager).visibility = View.VISIBLE + activity.findViewById(R.id.tabLayout).visibility = View.VISIBLE + activity.findViewById(R.id.searchView).visibility = View.VISIBLE + activity.findViewById(R.id.fragmentExtensionsContainer).visibility = + View.GONE + } + parentFragmentManager.beginTransaction() + .setCustomAnimations(R.anim.slide_up, R.anim.slide_down) + .replace(R.id.fragmentExtensionsContainer, fragment) + .addToBackStack(null) + .commit() } + + // Hide ViewPager2 and TabLayout + val activity = requireActivity() as ExtensionsActivity + activity.findViewById(R.id.viewPager).visibility = View.GONE + activity.findViewById(R.id.tabLayout).visibility = View.GONE + activity.findViewById(R.id.searchView).visibility = View.GONE + activity.findViewById(R.id.fragmentExtensionsContainer).visibility = View.VISIBLE + } else { + Toast.makeText(requireContext(), "Source is not configurable", Toast.LENGTH_SHORT) + .show() } - }, skipIcons) + }, + { pkg -> + if (isAdded) { // Check if the fragment is currently added to its activity + val context = requireContext() // Store context in a variable + val notificationManager = + context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager // Initialize NotificationManager once + + if (pkg.hasUpdate) { + animeExtensionManager.updateExtension(pkg) + .observeOn(AndroidSchedulers.mainThread()) // Observe on main thread + .subscribe( + { installStep -> + val builder = NotificationCompat.Builder( + context, + Notifications.CHANNEL_DOWNLOADER_PROGRESS + ) + .setSmallIcon(R.drawable.ic_round_sync_24) + .setContentTitle("Updating extension") + .setContentText("Step: $installStep") + .setPriority(NotificationCompat.PRIORITY_LOW) + notificationManager.notify(1, builder.build()) + }, + { error -> + FirebaseCrashlytics.getInstance().recordException(error) + Log.e("AnimeExtensionsAdapter", "Error: ", error) // Log the error + val builder = NotificationCompat.Builder( + context, + Notifications.CHANNEL_DOWNLOADER_ERROR + ) + .setSmallIcon(R.drawable.ic_round_info_24) + .setContentTitle("Update failed: ${error.message}") + .setContentText("Error: ${error.message}") + .setPriority(NotificationCompat.PRIORITY_HIGH) + notificationManager.notify(1, builder.build()) + }, + { + val builder = NotificationCompat.Builder( + context, + Notifications.CHANNEL_DOWNLOADER_PROGRESS + ) + .setSmallIcon(androidx.media3.ui.R.drawable.exo_ic_check) + .setContentTitle("Update complete") + .setContentText("The extension has been successfully updated.") + .setPriority(NotificationCompat.PRIORITY_LOW) + notificationManager.notify(1, builder.build()) + } + ) + } else { + animeExtensionManager.uninstallExtension(pkg.pkgName) + } + } + }, skipIcons + ) override fun onCreateView( inflater: LayoutInflater, @@ -114,6 +189,7 @@ class InstalledAnimeExtensionsFragment : Fragment() { private class AnimeExtensionsAdapter( + private val onSettingsClicked: (AnimeExtension.Installed) -> Unit, private val onUninstallClicked: (AnimeExtension.Installed) -> Unit, skipIcons: Boolean ) : ListAdapter( @@ -152,10 +228,14 @@ class InstalledAnimeExtensionsFragment : Fragment() { holder.closeTextView.setOnClickListener { onUninstallClicked(extension) } + holder.settingsImageView.setOnClickListener { + onSettingsClicked(extension) + } } inner class ViewHolder(view: View) : RecyclerView.ViewHolder(view) { val extensionNameTextView: TextView = view.findViewById(R.id.extensionNameTextView) + val settingsImageView: ImageView = view.findViewById(R.id.settingsImageView) val extensionIconImageView: ImageView = view.findViewById(R.id.extensionIconImageView) val closeTextView: TextView = view.findViewById(R.id.closeTextView) } @@ -180,5 +260,4 @@ class InstalledAnimeExtensionsFragment : Fragment() { } } - } \ No newline at end of file diff --git a/app/src/main/java/ani/dantotsu/settings/InstalledMangaExtensionsFragment.kt b/app/src/main/java/ani/dantotsu/settings/InstalledMangaExtensionsFragment.kt index 42dd191e..51e0487d 100644 --- a/app/src/main/java/ani/dantotsu/settings/InstalledMangaExtensionsFragment.kt +++ b/app/src/main/java/ani/dantotsu/settings/InstalledMangaExtensionsFragment.kt @@ -1,6 +1,7 @@ package ani.dantotsu.settings +import android.app.AlertDialog import android.app.NotificationManager import android.content.Context import android.os.Bundle @@ -8,8 +9,10 @@ import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import android.widget.FrameLayout import android.widget.ImageView import android.widget.TextView +import android.widget.Toast import androidx.core.app.NotificationCompat import androidx.core.content.ContextCompat import androidx.fragment.app.Fragment @@ -18,13 +21,18 @@ import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.ListAdapter import androidx.recyclerview.widget.RecyclerView +import androidx.viewpager2.widget.ViewPager2 import ani.dantotsu.R import ani.dantotsu.databinding.FragmentMangaExtensionsBinding import ani.dantotsu.loadData +import ani.dantotsu.settings.extensionprefs.MangaSourcePreferencesFragment +import com.google.android.material.tabs.TabLayout +import com.google.android.material.textfield.TextInputLayout import com.google.firebase.crashlytics.FirebaseCrashlytics import eu.kanade.tachiyomi.data.notification.Notifications import eu.kanade.tachiyomi.extension.manga.MangaExtensionManager import eu.kanade.tachiyomi.extension.manga.model.MangaExtension +import eu.kanade.tachiyomi.source.ConfigurableSource import kotlinx.coroutines.launch import rx.android.schedulers.AndroidSchedulers import uy.kohesive.injekt.Injekt @@ -37,6 +45,66 @@ class InstalledMangaExtensionsFragment : Fragment() { val skipIcons = loadData("skip_extension_icons") ?: false private val mangaExtensionManager: MangaExtensionManager = Injekt.get() private val extensionsAdapter = MangaExtensionsAdapter({ pkg -> + val changeUIVisibility: (Boolean) -> Unit = { show -> + val activity = requireActivity() as ExtensionsActivity + val visibility = if (show) View.VISIBLE else View.GONE + activity.findViewById(R.id.viewPager).visibility = visibility + activity.findViewById(R.id.tabLayout).visibility = visibility + activity.findViewById(R.id.searchView).visibility = visibility + activity.findViewById(R.id.fragmentExtensionsContainer).visibility = + if (show) View.GONE else View.VISIBLE + } + val allSettings = pkg.sources.filterIsInstance() + if (allSettings.isNotEmpty()) { + var selectedSetting = allSettings[0] + if (allSettings.size > 1) { + val names = allSettings.map { it.lang }.toTypedArray() + var selectedIndex = 0 + AlertDialog.Builder(requireContext()) + .setTitle("Select a Source") + .setSingleChoiceItems(names, selectedIndex) { _, which -> + selectedIndex = which + } + .setPositiveButton("OK") { dialog, _ -> + selectedSetting = allSettings[selectedIndex] + dialog.dismiss() + + // Move the fragment transaction here + val fragment = MangaSourcePreferencesFragment().getInstance(selectedSetting.id){ + changeUIVisibility(true) + } + parentFragmentManager.beginTransaction() + .setCustomAnimations(R.anim.slide_up, R.anim.slide_down) + .replace(R.id.fragmentExtensionsContainer, fragment) + .addToBackStack(null) + .commit() + } + .setNegativeButton("Cancel") { dialog, _ -> + dialog.cancel() + changeUIVisibility(true) + return@setNegativeButton + } + .show() + } else { + // If there's only one setting, proceed with the fragment transaction + val fragment = MangaSourcePreferencesFragment().getInstance(selectedSetting.id){ + changeUIVisibility(true) + } + parentFragmentManager.beginTransaction() + .setCustomAnimations(R.anim.slide_up, R.anim.slide_down) + .replace(R.id.fragmentExtensionsContainer, fragment) + .addToBackStack(null) + .commit() + } + + // Hide ViewPager2 and TabLayout + changeUIVisibility(false) + } else { + Toast.makeText(requireContext(), "Source is not configurable", Toast.LENGTH_SHORT) + .show() + } + }, + { pkg -> if (isAdded) { // Check if the fragment is currently added to its activity val context = requireContext() // Store context in a variable val notificationManager = @@ -115,6 +183,7 @@ class InstalledMangaExtensionsFragment : Fragment() { private class MangaExtensionsAdapter( + private val onSettingsClicked: (MangaExtension.Installed) -> Unit, private val onUninstallClicked: (MangaExtension.Installed) -> Unit, skipIcons: Boolean ) : ListAdapter( @@ -153,10 +222,14 @@ class InstalledMangaExtensionsFragment : Fragment() { holder.closeTextView.setOnClickListener { onUninstallClicked(extension) } + holder.settingsImageView.setOnClickListener { + onSettingsClicked(extension) + } } inner class ViewHolder(view: View) : RecyclerView.ViewHolder(view) { val extensionNameTextView: TextView = view.findViewById(R.id.extensionNameTextView) + val settingsImageView: ImageView = view.findViewById(R.id.settingsImageView) val extensionIconImageView: ImageView = view.findViewById(R.id.extensionIconImageView) val closeTextView: TextView = view.findViewById(R.id.closeTextView) } diff --git a/app/src/main/java/ani/dantotsu/settings/extensionprefs/AnimePreferenceFragmentCompat.kt b/app/src/main/java/ani/dantotsu/settings/extensionprefs/AnimePreferenceFragmentCompat.kt new file mode 100644 index 00000000..b4f93561 --- /dev/null +++ b/app/src/main/java/ani/dantotsu/settings/extensionprefs/AnimePreferenceFragmentCompat.kt @@ -0,0 +1,88 @@ +package ani.dantotsu.settings.extensionprefs + +import android.content.Context +import android.content.SharedPreferences +import android.os.Bundle +import android.util.Log +import android.util.TypedValue +import android.view.View +import android.widget.FrameLayout +import androidx.core.os.bundleOf +import androidx.lifecycle.lifecycleScope +import androidx.preference.DialogPreference +import androidx.preference.EditTextPreference +import androidx.preference.PreferenceFragmentCompat +import androidx.preference.forEach +import androidx.preference.getOnBindEditTextListener +import androidx.viewpager2.widget.ViewPager2 +import ani.dantotsu.R +import ani.dantotsu.settings.ExtensionsActivity +import com.google.android.material.tabs.TabLayout +import com.google.android.material.textfield.TextInputLayout +import eu.kanade.tachiyomi.PreferenceScreen +import eu.kanade.tachiyomi.animesource.ConfigurableAnimeSource +import eu.kanade.tachiyomi.data.preference.SharedPreferencesDataStore +import eu.kanade.tachiyomi.source.anime.getPreferenceKey +import eu.kanade.tachiyomi.widget.TachiyomiTextInputEditText.Companion.setIncognito +import tachiyomi.domain.source.anime.service.AnimeSourceManager +import uy.kohesive.injekt.Injekt +import uy.kohesive.injekt.api.get + +class AnimeSourcePreferencesFragment : PreferenceFragmentCompat() { + override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { + preferenceScreen = populateAnimePreferenceScreen() + //set background color + val color = TypedValue() + requireContext().theme.resolveAttribute(com.google.android.material.R.attr.backgroundColor, color, true) + view?.setBackgroundColor(color.data) + } + private var onCloseAction: (() -> Unit)? = null + + + override fun onDestroyView() { + super.onDestroyView() + onCloseAction?.invoke() + } + + fun populateAnimePreferenceScreen(): PreferenceScreen { + val sourceId = requireArguments().getLong(SOURCE_ID) + val source = Injekt.get().get(sourceId)!! + check(source is ConfigurableAnimeSource) + val sharedPreferences = requireContext().getSharedPreferences(source.getPreferenceKey(), Context.MODE_PRIVATE) + val dataStore = SharedPreferencesDataStore(sharedPreferences) + preferenceManager.preferenceDataStore = dataStore + val sourceScreen = preferenceManager.createPreferenceScreen(requireContext()) + source.setupPreferenceScreen(sourceScreen) + sourceScreen.forEach { pref -> + pref.isIconSpaceReserved = false + if (pref is DialogPreference) { + pref.dialogTitle = pref.title + println("pref.dialogTitle: ${pref.dialogTitle}") + } + for (entry in sharedPreferences.all.entries) { + Log.d("Preferences", "Key: ${entry.key}, Value: ${entry.value}") + } + + // Apply incognito IME for EditTextPreference + if (pref is EditTextPreference) { + val setListener = pref.getOnBindEditTextListener() + pref.setOnBindEditTextListener { + setListener?.onBindEditText(it) + it.setIncognito(lifecycleScope) + } + } + } + + return sourceScreen + } + fun getInstance(sourceId: Long, onCloseAction: (() -> Unit)? = null): AnimeSourcePreferencesFragment { + val fragment = AnimeSourcePreferencesFragment() + fragment.arguments = bundleOf(SOURCE_ID to sourceId) + fragment.onCloseAction = onCloseAction + return fragment + } + + companion object { //idk why it needs both + private const val SOURCE_ID = "source_id" + } +} diff --git a/app/src/main/java/ani/dantotsu/settings/extensionprefs/MangaPreferenceFragmentCompat.kt b/app/src/main/java/ani/dantotsu/settings/extensionprefs/MangaPreferenceFragmentCompat.kt new file mode 100644 index 00000000..d571ef77 --- /dev/null +++ b/app/src/main/java/ani/dantotsu/settings/extensionprefs/MangaPreferenceFragmentCompat.kt @@ -0,0 +1,78 @@ +package ani.dantotsu.settings.extensionprefs + +import android.content.Context +import android.os.Bundle +import android.view.View +import android.widget.FrameLayout +import androidx.core.os.bundleOf +import androidx.lifecycle.lifecycleScope +import androidx.preference.DialogPreference +import androidx.preference.EditTextPreference +import androidx.preference.PreferenceFragmentCompat +import androidx.preference.forEach +import androidx.preference.getOnBindEditTextListener +import androidx.viewpager2.widget.ViewPager2 +import ani.dantotsu.R +import ani.dantotsu.settings.ExtensionsActivity +import com.google.android.material.tabs.TabLayout +import com.google.android.material.textfield.TextInputLayout +import eu.kanade.tachiyomi.PreferenceScreen +import eu.kanade.tachiyomi.data.preference.SharedPreferencesDataStore +import eu.kanade.tachiyomi.source.ConfigurableSource +import eu.kanade.tachiyomi.source.manga.getPreferenceKey +import eu.kanade.tachiyomi.widget.TachiyomiTextInputEditText.Companion.setIncognito +import tachiyomi.domain.source.manga.service.MangaSourceManager +import uy.kohesive.injekt.Injekt +import uy.kohesive.injekt.api.get + +class MangaSourcePreferencesFragment : PreferenceFragmentCompat() { + override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { + preferenceScreen = populateMangaPreferenceScreen() + } + private var onCloseAction: (() -> Unit)? = null + + override fun onDestroyView() { + super.onDestroyView() + onCloseAction?.invoke() + + } + + fun populateMangaPreferenceScreen(): PreferenceScreen { + val sourceId = requireArguments().getLong(SOURCE_ID) + val source = Injekt.get().get(sourceId)!! + check(source is ConfigurableSource) + val sharedPreferences = requireContext().getSharedPreferences(source.getPreferenceKey(), Context.MODE_PRIVATE) + val dataStore = SharedPreferencesDataStore(sharedPreferences) + preferenceManager.preferenceDataStore = dataStore + val sourceScreen = preferenceManager.createPreferenceScreen(requireContext()) + source.setupPreferenceScreen(sourceScreen) + sourceScreen.forEach { pref -> + pref.isIconSpaceReserved = false + if (pref is DialogPreference) { + pref.dialogTitle = pref.title + println("pref.dialogTitle: ${pref.dialogTitle}") + } + + // Apply incognito IME for EditTextPreference + if (pref is EditTextPreference) { + val setListener = pref.getOnBindEditTextListener() + pref.setOnBindEditTextListener { + setListener?.onBindEditText(it) + it.setIncognito(lifecycleScope) + } + } + } + + return sourceScreen + } + fun getInstance(sourceId: Long, onCloseAction: (() -> Unit)? = null): MangaSourcePreferencesFragment { + val fragment = MangaSourcePreferencesFragment() + fragment.arguments = bundleOf(SOURCE_ID to sourceId) + fragment.onCloseAction = onCloseAction + return fragment + } + + companion object { + private const val SOURCE_ID = "source_id" + } +} \ No newline at end of file diff --git a/app/src/main/java/eu/kanade/tachiyomi/animesource/UnmeteredSource.kt b/app/src/main/java/eu/kanade/tachiyomi/animesource/UnmeteredSource.kt new file mode 100644 index 00000000..840a223a --- /dev/null +++ b/app/src/main/java/eu/kanade/tachiyomi/animesource/UnmeteredSource.kt @@ -0,0 +1,8 @@ +package eu.kanade.tachiyomi.animesource + +/** + * A source that explicitly doesn't require traffic considerations. + * + * This typically applies for self-hosted sources. + */ +interface UnmeteredSource diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/preference/SharedPreferencesDataStore.kt b/app/src/main/java/eu/kanade/tachiyomi/data/preference/SharedPreferencesDataStore.kt new file mode 100644 index 00000000..ae11e587 --- /dev/null +++ b/app/src/main/java/eu/kanade/tachiyomi/data/preference/SharedPreferencesDataStore.kt @@ -0,0 +1,68 @@ +package eu.kanade.tachiyomi.data.preference + +import android.content.SharedPreferences +import androidx.core.content.edit +import androidx.preference.PreferenceDataStore + +class SharedPreferencesDataStore(private val prefs: SharedPreferences) : PreferenceDataStore() { + + override fun getBoolean(key: String?, defValue: Boolean): Boolean { + return prefs.getBoolean(key, defValue) + } + + override fun putBoolean(key: String?, value: Boolean) { + prefs.edit { + putBoolean(key, value) + } + } + + override fun getInt(key: String?, defValue: Int): Int { + return prefs.getInt(key, defValue) + } + + override fun putInt(key: String?, value: Int) { + prefs.edit { + putInt(key, value) + } + } + + override fun getLong(key: String?, defValue: Long): Long { + return prefs.getLong(key, defValue) + } + + override fun putLong(key: String?, value: Long) { + prefs.edit { + putLong(key, value) + } + } + + override fun getFloat(key: String?, defValue: Float): Float { + return prefs.getFloat(key, defValue) + } + + override fun putFloat(key: String?, value: Float) { + prefs.edit { + putFloat(key, value) + } + } + + override fun getString(key: String?, defValue: String?): String? { + return prefs.getString(key, defValue) + } + + override fun putString(key: String?, value: String?) { + prefs.edit { + putString(key, value) + } + } + + override fun getStringSet(key: String?, defValues: MutableSet?): MutableSet? { + return prefs.getStringSet(key, defValues) + } + + override fun putStringSet(key: String?, values: MutableSet?) { + prefs.edit { + putStringSet(key, values) + } + } +} diff --git a/app/src/main/java/eu/kanade/tachiyomi/source/anime/AndroidAnimeSourceManager.kt b/app/src/main/java/eu/kanade/tachiyomi/source/anime/AndroidAnimeSourceManager.kt new file mode 100644 index 00000000..79b30b9e --- /dev/null +++ b/app/src/main/java/eu/kanade/tachiyomi/source/anime/AndroidAnimeSourceManager.kt @@ -0,0 +1,85 @@ +package eu.kanade.tachiyomi.source.anime + +import android.content.Context +import eu.kanade.tachiyomi.animesource.AnimeCatalogueSource +import eu.kanade.tachiyomi.animesource.AnimeSource +import eu.kanade.tachiyomi.animesource.online.AnimeHttpSource +import eu.kanade.tachiyomi.extension.anime.AnimeExtensionManager +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.Job +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.collectLatest +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.launch +import kotlinx.coroutines.runBlocking +import tachiyomi.domain.source.anime.model.AnimeSourceData +import tachiyomi.domain.source.anime.model.StubAnimeSource +import tachiyomi.domain.source.anime.service.AnimeSourceManager +import tachiyomi.source.local.entries.anime.LocalAnimeSource +import java.util.concurrent.ConcurrentHashMap + +class AndroidAnimeSourceManager( + private val context: Context, + private val extensionManager: AnimeExtensionManager, +) : AnimeSourceManager { + + private val scope = CoroutineScope(Job() + Dispatchers.IO) + + private val sourcesMapFlow = MutableStateFlow(ConcurrentHashMap()) + + private val stubSourcesMap = ConcurrentHashMap() + + override val catalogueSources: Flow> = sourcesMapFlow.map { it.values.filterIsInstance() } + + init { + scope.launch { + extensionManager.installedExtensionsFlow + .collectLatest { extensions -> + val mutableMap = ConcurrentHashMap( + mapOf( + LocalAnimeSource.ID to LocalAnimeSource( + context, + ), + ), + ) + extensions.forEach { extension -> + extension.sources.forEach { + mutableMap[it.id] = it + registerStubSource(it.toSourceData()) + } + } + sourcesMapFlow.value = mutableMap + } + } + + } + + override fun get(sourceKey: Long): AnimeSource? { + return sourcesMapFlow.value[sourceKey] + } + + override fun getOrStub(sourceKey: Long): AnimeSource { + return sourcesMapFlow.value[sourceKey] ?: stubSourcesMap.getOrPut(sourceKey) { + runBlocking { createStubSource(sourceKey) } + } + } + + override fun getOnlineSources() = sourcesMapFlow.value.values.filterIsInstance() + + override fun getCatalogueSources() = sourcesMapFlow.value.values.filterIsInstance() + + override fun getStubSources(): List { + val onlineSourceIds = getOnlineSources().map { it.id } + return stubSourcesMap.values.filterNot { it.id in onlineSourceIds } + } + + private fun registerStubSource(sourceData: AnimeSourceData) { + + } + + private suspend fun createStubSource(id: Long): StubAnimeSource { + return StubAnimeSource(AnimeSourceData(id, "", "")) + } +} diff --git a/app/src/main/java/eu/kanade/tachiyomi/source/anime/AnimeSourceExtensions.kt b/app/src/main/java/eu/kanade/tachiyomi/source/anime/AnimeSourceExtensions.kt new file mode 100644 index 00000000..a359d765 --- /dev/null +++ b/app/src/main/java/eu/kanade/tachiyomi/source/anime/AnimeSourceExtensions.kt @@ -0,0 +1,34 @@ +package eu.kanade.tachiyomi.source.anime + +import android.graphics.drawable.Drawable +import eu.kanade.domain.source.service.SourcePreferences +import eu.kanade.tachiyomi.animesource.AnimeSource +import eu.kanade.tachiyomi.extension.anime.AnimeExtensionManager +import tachiyomi.domain.source.anime.model.AnimeSourceData +import tachiyomi.domain.source.anime.model.StubAnimeSource +import tachiyomi.source.local.entries.anime.isLocal +import uy.kohesive.injekt.Injekt +import uy.kohesive.injekt.api.get + +fun AnimeSource.icon(): Drawable? = Injekt.get().getAppIconForSource(this.id) + +fun AnimeSource.getPreferenceKey(): String = "source_$id" + +fun AnimeSource.toSourceData(): AnimeSourceData = AnimeSourceData(id = id, lang = lang, name = name) + +fun AnimeSource.getNameForAnimeInfo(): String { + val preferences = Injekt.get() + val enabledLanguages = preferences.enabledLanguages().get() + .filterNot { it in listOf("all", "other") } + val hasOneActiveLanguages = enabledLanguages.size == 1 + val isInEnabledLanguages = lang in enabledLanguages + return when { + // For edge cases where user disables a source they got manga of in their library. + hasOneActiveLanguages && !isInEnabledLanguages -> toString() + // Hide the language tag when only one language is used. + hasOneActiveLanguages && isInEnabledLanguages -> name + else -> toString() + } +} + +fun AnimeSource.isLocalOrStub(): Boolean = isLocal() || this is StubAnimeSource diff --git a/app/src/main/java/eu/kanade/tachiyomi/source/manga/AndroidMangaSourceManager.kt b/app/src/main/java/eu/kanade/tachiyomi/source/manga/AndroidMangaSourceManager.kt new file mode 100644 index 00000000..08bd2b61 --- /dev/null +++ b/app/src/main/java/eu/kanade/tachiyomi/source/manga/AndroidMangaSourceManager.kt @@ -0,0 +1,84 @@ +package eu.kanade.tachiyomi.source.manga + +import android.content.Context +import eu.kanade.tachiyomi.extension.manga.MangaExtensionManager +import eu.kanade.tachiyomi.source.CatalogueSource +import eu.kanade.tachiyomi.source.MangaSource +import eu.kanade.tachiyomi.source.online.HttpSource +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.Job +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.collectLatest +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.launch +import kotlinx.coroutines.runBlocking +import tachiyomi.domain.source.manga.model.MangaSourceData +import tachiyomi.domain.source.manga.model.StubMangaSource +import tachiyomi.domain.source.manga.service.MangaSourceManager +import tachiyomi.source.local.entries.manga.LocalMangaSource +import java.util.concurrent.ConcurrentHashMap + +class AndroidMangaSourceManager( + private val context: Context, + private val extensionManager: MangaExtensionManager, +) : MangaSourceManager { + + private val scope = CoroutineScope(Job() + Dispatchers.IO) + + private val sourcesMapFlow = MutableStateFlow(ConcurrentHashMap()) + + private val stubSourcesMap = ConcurrentHashMap() + + override val catalogueSources: Flow> = sourcesMapFlow.map { it.values.filterIsInstance() } + + init { + scope.launch { + extensionManager.installedExtensionsFlow + .collectLatest { extensions -> + val mutableMap = ConcurrentHashMap( + mapOf( + LocalMangaSource.ID to LocalMangaSource( + context, + ), + ), + ) + extensions.forEach { extension -> + extension.sources.forEach { + mutableMap[it.id] = it + registerStubSource(it.toSourceData()) + } + } + sourcesMapFlow.value = mutableMap + } + } + } + + override fun get(sourceKey: Long): MangaSource? { + return sourcesMapFlow.value[sourceKey] + } + + override fun getOrStub(sourceKey: Long): MangaSource { + return sourcesMapFlow.value[sourceKey] ?: stubSourcesMap.getOrPut(sourceKey) { + runBlocking { createStubSource(sourceKey) } + } + } + + override fun getOnlineSources() = sourcesMapFlow.value.values.filterIsInstance() + + override fun getCatalogueSources() = sourcesMapFlow.value.values.filterIsInstance() + + override fun getStubSources(): List { + val onlineSourceIds = getOnlineSources().map { it.id } + return stubSourcesMap.values.filterNot { it.id in onlineSourceIds } + } + + private fun registerStubSource(sourceData: MangaSourceData) { + + } + + private suspend fun createStubSource(id: Long): StubMangaSource { + return StubMangaSource(MangaSourceData(id, "", "")) + } +} diff --git a/app/src/main/java/eu/kanade/tachiyomi/source/manga/MangaSourceExtensions.kt b/app/src/main/java/eu/kanade/tachiyomi/source/manga/MangaSourceExtensions.kt new file mode 100644 index 00000000..afd17cea --- /dev/null +++ b/app/src/main/java/eu/kanade/tachiyomi/source/manga/MangaSourceExtensions.kt @@ -0,0 +1,34 @@ +package eu.kanade.tachiyomi.source.manga + +import android.graphics.drawable.Drawable +import eu.kanade.domain.source.service.SourcePreferences +import eu.kanade.tachiyomi.extension.manga.MangaExtensionManager +import eu.kanade.tachiyomi.source.MangaSource +import tachiyomi.domain.source.manga.model.MangaSourceData +import tachiyomi.domain.source.manga.model.StubMangaSource +import tachiyomi.source.local.entries.manga.isLocal +import uy.kohesive.injekt.Injekt +import uy.kohesive.injekt.api.get + +fun MangaSource.icon(): Drawable? = Injekt.get().getAppIconForSource(this.id) + +fun MangaSource.getPreferenceKey(): String = "source_$id" + +fun MangaSource.toSourceData(): MangaSourceData = MangaSourceData(id = id, lang = lang, name = name) + +fun MangaSource.getNameForMangaInfo(): String { + val preferences = Injekt.get() + val enabledLanguages = preferences.enabledLanguages().get() + .filterNot { it in listOf("all", "other") } + val hasOneActiveLanguages = enabledLanguages.size == 1 + val isInEnabledLanguages = lang in enabledLanguages + return when { + // For edge cases where user disables a source they got manga of in their library. + hasOneActiveLanguages && !isInEnabledLanguages -> toString() + // Hide the language tag when only one language is used. + hasOneActiveLanguages && isInEnabledLanguages -> name + else -> toString() + } +} + +fun MangaSource.isLocalOrStub(): Boolean = isLocal() || this is StubMangaSource diff --git a/app/src/main/java/eu/kanade/tachiyomi/util/storage/DiskUtil.kt b/app/src/main/java/eu/kanade/tachiyomi/util/storage/DiskUtil.kt new file mode 100644 index 00000000..8e3141a2 --- /dev/null +++ b/app/src/main/java/eu/kanade/tachiyomi/util/storage/DiskUtil.kt @@ -0,0 +1,117 @@ +package eu.kanade.tachiyomi.util.storage + +import android.content.Context +import android.media.MediaScannerConnection +import android.net.Uri +import android.os.Environment +import android.os.StatFs +import androidx.core.content.ContextCompat +import com.hippo.unifile.UniFile +import eu.kanade.tachiyomi.util.lang.Hash +import java.io.File + +object DiskUtil { + + fun hashKeyForDisk(key: String): String { + return Hash.md5(key) + } + + fun getDirectorySize(f: File): Long { + var size: Long = 0 + if (f.isDirectory) { + for (file in f.listFiles().orEmpty()) { + size += getDirectorySize(file) + } + } else { + size = f.length() + } + return size + } + + /** + * Gets the available space for the disk that a file path points to, in bytes. + */ + fun getAvailableStorageSpace(f: UniFile): Long { + return try { + val stat = StatFs(f.uri.path) + stat.availableBlocksLong * stat.blockSizeLong + } catch (_: Exception) { + -1L + } + } + + /** + * Returns the root folders of all the available external storages. + */ + fun getExternalStorages(context: Context): List { + return ContextCompat.getExternalFilesDirs(context, null) + .filterNotNull() + .mapNotNull { + val file = File(it.absolutePath.substringBefore("/Android/")) + val state = Environment.getExternalStorageState(file) + if (state == Environment.MEDIA_MOUNTED || state == Environment.MEDIA_MOUNTED_READ_ONLY) { + file + } else { + null + } + } + } + + /** + * Don't display downloaded chapters in gallery apps creating `.nomedia`. + */ + fun createNoMediaFile(dir: UniFile?, context: Context?) { + if (dir != null && dir.exists()) { + val nomedia = dir.findFile(NOMEDIA_FILE) + if (nomedia == null) { + dir.createFile(NOMEDIA_FILE) + context?.let { scanMedia(it, dir.uri) } + } + } + } + + /** + * Scans the given file so that it can be shown in gallery apps, for example. + */ + fun scanMedia(context: Context, uri: Uri) { + MediaScannerConnection.scanFile(context, arrayOf(uri.path), null, null) + } + + /** + * Mutate the given filename to make it valid for a FAT filesystem, + * replacing any invalid characters with "_". This method doesn't allow hidden files (starting + * with a dot), but you can manually add it later. + */ + fun buildValidFilename(origName: String): String { + val name = origName.trim('.', ' ') + if (name.isEmpty()) { + return "(invalid)" + } + val sb = StringBuilder(name.length) + name.forEach { c -> + if (isValidFatFilenameChar(c)) { + sb.append(c) + } else { + sb.append('_') + } + } + // Even though vfat allows 255 UCS-2 chars, we might eventually write to + // ext4 through a FUSE layer, so use that limit minus 15 reserved characters. + return sb.toString().take(240) + } + + /** + * Returns true if the given character is a valid filename character, false otherwise. + */ + private fun isValidFatFilenameChar(c: Char): Boolean { + if (0x00.toChar() <= c && c <= 0x1f.toChar()) { + return false + } + return when (c) { + '"', '*', '/', ':', '<', '>', '?', '\\', '|', 0x7f.toChar() -> false + else -> true + } + } + + const val NOMEDIA_FILE = ".nomedia" +} diff --git a/app/src/main/java/eu/kanade/tachiyomi/util/view/EditTextPreferenceExtensions.kt b/app/src/main/java/eu/kanade/tachiyomi/util/view/EditTextPreferenceExtensions.kt new file mode 100644 index 00000000..4428fb9a --- /dev/null +++ b/app/src/main/java/eu/kanade/tachiyomi/util/view/EditTextPreferenceExtensions.kt @@ -0,0 +1,10 @@ +@file:Suppress("PackageDirectoryMismatch") + +package androidx.preference + +/** + * Returns package-private [EditTextPreference.getOnBindEditTextListener] + */ +fun EditTextPreference.getOnBindEditTextListener(): EditTextPreference.OnBindEditTextListener? { + return onBindEditTextListener +} diff --git a/app/src/main/java/eu/kanade/tachiyomi/widget/TachiyomiTextInputEditText.kt b/app/src/main/java/eu/kanade/tachiyomi/widget/TachiyomiTextInputEditText.kt new file mode 100644 index 00000000..272ac155 --- /dev/null +++ b/app/src/main/java/eu/kanade/tachiyomi/widget/TachiyomiTextInputEditText.kt @@ -0,0 +1,62 @@ +package eu.kanade.tachiyomi.widget + +import android.content.Context +import android.util.AttributeSet +import android.widget.EditText +import androidx.core.view.inputmethod.EditorInfoCompat +import com.google.android.material.textfield.TextInputEditText +import eu.kanade.domain.base.BasePreferences +import eu.kanade.tachiyomi.widget.TachiyomiTextInputEditText.Companion.setIncognito +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.SupervisorJob +import kotlinx.coroutines.cancel +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach +import uy.kohesive.injekt.Injekt +import uy.kohesive.injekt.api.get + +/** + * A custom [TextInputEditText] that sets [EditorInfoCompat.IME_FLAG_NO_PERSONALIZED_LEARNING] to imeOptions + * if [BasePreferences.incognitoMode] is true. Some IMEs may not respect this flag. + * + * @see setIncognito + */ +class TachiyomiTextInputEditText @JvmOverloads constructor( + context: Context, + attrs: AttributeSet? = null, + defStyleAttr: Int = 0, +) : TextInputEditText(context, attrs, defStyleAttr) { + + private var scope: CoroutineScope? = null + + override fun onAttachedToWindow() { + super.onAttachedToWindow() + scope = CoroutineScope(SupervisorJob() + Dispatchers.Main) + setIncognito(scope!!) + } + + override fun onDetachedFromWindow() { + super.onDetachedFromWindow() + scope?.cancel() + scope = null + } + + companion object { + /** + * Sets Flow to this [EditText] that sets [EditorInfoCompat.IME_FLAG_NO_PERSONALIZED_LEARNING] to imeOptions + * if [BasePreferences.incognitoMode] is true. Some IMEs may not respect this flag. + */ + fun EditText.setIncognito(viewScope: CoroutineScope) { + Injekt.get().incognitoMode().changes() + .onEach { + imeOptions = if (it) { + imeOptions or EditorInfoCompat.IME_FLAG_NO_PERSONALIZED_LEARNING + } else { + imeOptions and EditorInfoCompat.IME_FLAG_NO_PERSONALIZED_LEARNING.inv() + } + } + .launchIn(viewScope) + } + } +} diff --git a/app/src/main/java/tachiyomi/core/metadata/tachiyomi/AnimeDetails.kt b/app/src/main/java/tachiyomi/core/metadata/tachiyomi/AnimeDetails.kt new file mode 100644 index 00000000..414d4873 --- /dev/null +++ b/app/src/main/java/tachiyomi/core/metadata/tachiyomi/AnimeDetails.kt @@ -0,0 +1,13 @@ +package tachiyomi.core.metadata.tachiyomi + +import kotlinx.serialization.Serializable + +@Serializable +class AnimeDetails( + val title: String? = null, + val author: String? = null, + val artist: String? = null, + val description: String? = null, + val genre: List? = null, + val status: Int? = null, +) diff --git a/app/src/main/java/tachiyomi/core/metadata/tachiyomi/MangaDetails.kt b/app/src/main/java/tachiyomi/core/metadata/tachiyomi/MangaDetails.kt new file mode 100644 index 00000000..7768986e --- /dev/null +++ b/app/src/main/java/tachiyomi/core/metadata/tachiyomi/MangaDetails.kt @@ -0,0 +1,13 @@ +package tachiyomi.core.metadata.tachiyomi + +import kotlinx.serialization.Serializable + +@Serializable +class MangaDetails( + val title: String? = null, + val author: String? = null, + val artist: String? = null, + val description: String? = null, + val genre: List? = null, + val status: Int? = null, +) diff --git a/app/src/main/java/tachiyomi/domain/entries/TriStateFilter.kt b/app/src/main/java/tachiyomi/domain/entries/TriStateFilter.kt new file mode 100644 index 00000000..f56bfbfd --- /dev/null +++ b/app/src/main/java/tachiyomi/domain/entries/TriStateFilter.kt @@ -0,0 +1,22 @@ +package tachiyomi.domain.entries + +enum class TriStateFilter { + DISABLED, // Disable filter + ENABLED_IS, // Enabled with "is" filter + ENABLED_NOT, // Enabled with "not" filter + ; + + fun next(): TriStateFilter { + return when (this) { + DISABLED -> ENABLED_IS + ENABLED_IS -> ENABLED_NOT + ENABLED_NOT -> DISABLED + } + } +} + +inline fun applyFilter(filter: TriStateFilter, predicate: () -> Boolean): Boolean = when (filter) { + TriStateFilter.DISABLED -> true + TriStateFilter.ENABLED_IS -> predicate() + TriStateFilter.ENABLED_NOT -> !predicate() +} diff --git a/app/src/main/java/tachiyomi/domain/entries/anime/model/Anime.kt b/app/src/main/java/tachiyomi/domain/entries/anime/model/Anime.kt new file mode 100644 index 00000000..ede00f27 --- /dev/null +++ b/app/src/main/java/tachiyomi/domain/entries/anime/model/Anime.kt @@ -0,0 +1,134 @@ +package tachiyomi.domain.entries.anime.model + +import eu.kanade.tachiyomi.source.model.UpdateStrategy +import tachiyomi.domain.entries.TriStateFilter +import java.io.Serializable +import kotlin.math.pow + +data class Anime( + val id: Long, + val source: Long, + val favorite: Boolean, + val lastUpdate: Long, + val nextUpdate: Long, + val calculateInterval: Int, + val dateAdded: Long, + val viewerFlags: Long, + val episodeFlags: Long, + val coverLastModified: Long, + val url: String, + val title: String, + val artist: String?, + val author: String?, + val description: String?, + val genre: List?, + val status: Long, + val thumbnailUrl: String?, + val updateStrategy: UpdateStrategy, + val initialized: Boolean, +) : Serializable { + + val sorting: Long + get() = episodeFlags and EPISODE_SORTING_MASK + + val displayMode: Long + get() = episodeFlags and EPISODE_DISPLAY_MASK + + val unseenFilterRaw: Long + get() = episodeFlags and EPISODE_UNSEEN_MASK + + val downloadedFilterRaw: Long + get() = episodeFlags and EPISODE_DOWNLOADED_MASK + + val bookmarkedFilterRaw: Long + get() = episodeFlags and EPISODE_BOOKMARKED_MASK + + val skipIntroLength: Int + get() = (viewerFlags and ANIME_INTRO_MASK).toInt() + + val nextEpisodeToAir: Int + get() = (viewerFlags and ANIME_AIRING_EPISODE_MASK).removeHexZeros(zeros = 2).toInt() + + val nextEpisodeAiringAt: Long + get() = (viewerFlags and ANIME_AIRING_TIME_MASK).removeHexZeros(zeros = 6) + + val unseenFilter: TriStateFilter + get() = when (unseenFilterRaw) { + EPISODE_SHOW_UNSEEN -> TriStateFilter.ENABLED_IS + EPISODE_SHOW_SEEN -> TriStateFilter.ENABLED_NOT + else -> TriStateFilter.DISABLED + } + + val bookmarkedFilter: TriStateFilter + get() = when (bookmarkedFilterRaw) { + EPISODE_SHOW_BOOKMARKED -> TriStateFilter.ENABLED_IS + EPISODE_SHOW_NOT_BOOKMARKED -> TriStateFilter.ENABLED_NOT + else -> TriStateFilter.DISABLED + } + + fun sortDescending(): Boolean { + return episodeFlags and EPISODE_SORT_DIR_MASK == EPISODE_SORT_DESC + } + + private fun Long.removeHexZeros(zeros: Int): Long { + val hex = 16.0 + return this.div(hex.pow(zeros)).toLong() + } + + companion object { + // Generic filter that does not filter anything + const val SHOW_ALL = 0x00000000L + + const val EPISODE_SORT_DESC = 0x00000000L + const val EPISODE_SORT_ASC = 0x00000001L + const val EPISODE_SORT_DIR_MASK = 0x00000001L + + const val EPISODE_SHOW_UNSEEN = 0x00000002L + const val EPISODE_SHOW_SEEN = 0x00000004L + const val EPISODE_UNSEEN_MASK = 0x00000006L + + const val EPISODE_SHOW_DOWNLOADED = 0x00000008L + const val EPISODE_SHOW_NOT_DOWNLOADED = 0x00000010L + const val EPISODE_DOWNLOADED_MASK = 0x00000018L + + const val EPISODE_SHOW_BOOKMARKED = 0x00000020L + const val EPISODE_SHOW_NOT_BOOKMARKED = 0x00000040L + const val EPISODE_BOOKMARKED_MASK = 0x00000060L + + const val EPISODE_SORTING_SOURCE = 0x00000000L + const val EPISODE_SORTING_NUMBER = 0x00000100L + const val EPISODE_SORTING_UPLOAD_DATE = 0x00000200L + const val EPISODE_SORTING_MASK = 0x00000300L + + const val EPISODE_DISPLAY_NAME = 0x00000000L + const val EPISODE_DISPLAY_NUMBER = 0x00100000L + const val EPISODE_DISPLAY_MASK = 0x00100000L + + const val ANIME_INTRO_MASK = 0x000000000000FFL + const val ANIME_AIRING_EPISODE_MASK = 0x00000000FFFF00L + const val ANIME_AIRING_TIME_MASK = 0xFFFFFFFF000000L + + fun create() = Anime( + id = -1L, + url = "", + title = "", + source = -1L, + favorite = false, + lastUpdate = 0L, + nextUpdate = 0L, + calculateInterval = 0, + dateAdded = 0L, + viewerFlags = 0L, + episodeFlags = 0L, + coverLastModified = 0L, + artist = null, + author = null, + description = null, + genre = null, + status = 0L, + thumbnailUrl = null, + updateStrategy = UpdateStrategy.ALWAYS_UPDATE, + initialized = false, + ) + } +} diff --git a/app/src/main/java/tachiyomi/domain/entries/manga/model/Manga.kt b/app/src/main/java/tachiyomi/domain/entries/manga/model/Manga.kt new file mode 100644 index 00000000..354abffc --- /dev/null +++ b/app/src/main/java/tachiyomi/domain/entries/manga/model/Manga.kt @@ -0,0 +1,115 @@ +package tachiyomi.domain.entries.manga.model + +import eu.kanade.tachiyomi.source.model.UpdateStrategy +import tachiyomi.domain.entries.TriStateFilter +import java.io.Serializable + +data class Manga( + val id: Long, + val source: Long, + val favorite: Boolean, + val lastUpdate: Long, + val nextUpdate: Long, + val calculateInterval: Int, + val dateAdded: Long, + val viewerFlags: Long, + val chapterFlags: Long, + val coverLastModified: Long, + val url: String, + val title: String, + val artist: String?, + val author: String?, + val description: String?, + val genre: List?, + val status: Long, + val thumbnailUrl: String?, + val updateStrategy: UpdateStrategy, + val initialized: Boolean, +) : Serializable { + + val sorting: Long + get() = chapterFlags and CHAPTER_SORTING_MASK + + val displayMode: Long + get() = chapterFlags and CHAPTER_DISPLAY_MASK + + val unreadFilterRaw: Long + get() = chapterFlags and CHAPTER_UNREAD_MASK + + val downloadedFilterRaw: Long + get() = chapterFlags and CHAPTER_DOWNLOADED_MASK + + val bookmarkedFilterRaw: Long + get() = chapterFlags and CHAPTER_BOOKMARKED_MASK + + val unreadFilter: TriStateFilter + get() = when (unreadFilterRaw) { + CHAPTER_SHOW_UNREAD -> TriStateFilter.ENABLED_IS + CHAPTER_SHOW_READ -> TriStateFilter.ENABLED_NOT + else -> TriStateFilter.DISABLED + } + + val bookmarkedFilter: TriStateFilter + get() = when (bookmarkedFilterRaw) { + CHAPTER_SHOW_BOOKMARKED -> TriStateFilter.ENABLED_IS + CHAPTER_SHOW_NOT_BOOKMARKED -> TriStateFilter.ENABLED_NOT + else -> TriStateFilter.DISABLED + } + + fun sortDescending(): Boolean { + return chapterFlags and CHAPTER_SORT_DIR_MASK == CHAPTER_SORT_DESC + } + + companion object { + // Generic filter that does not filter anything + const val SHOW_ALL = 0x00000000L + + const val CHAPTER_SORT_DESC = 0x00000000L + const val CHAPTER_SORT_ASC = 0x00000001L + const val CHAPTER_SORT_DIR_MASK = 0x00000001L + + const val CHAPTER_SHOW_UNREAD = 0x00000002L + const val CHAPTER_SHOW_READ = 0x00000004L + const val CHAPTER_UNREAD_MASK = 0x00000006L + + const val CHAPTER_SHOW_DOWNLOADED = 0x00000008L + const val CHAPTER_SHOW_NOT_DOWNLOADED = 0x00000010L + const val CHAPTER_DOWNLOADED_MASK = 0x00000018L + + const val CHAPTER_SHOW_BOOKMARKED = 0x00000020L + const val CHAPTER_SHOW_NOT_BOOKMARKED = 0x00000040L + const val CHAPTER_BOOKMARKED_MASK = 0x00000060L + + const val CHAPTER_SORTING_SOURCE = 0x00000000L + const val CHAPTER_SORTING_NUMBER = 0x00000100L + const val CHAPTER_SORTING_UPLOAD_DATE = 0x00000200L + const val CHAPTER_SORTING_MASK = 0x00000300L + + const val CHAPTER_DISPLAY_NAME = 0x00000000L + const val CHAPTER_DISPLAY_NUMBER = 0x00100000L + const val CHAPTER_DISPLAY_MASK = 0x00100000L + + fun create() = Manga( + id = -1L, + url = "", + title = "", + source = -1L, + favorite = false, + lastUpdate = 0L, + nextUpdate = 0L, + calculateInterval = 0, + dateAdded = 0L, + viewerFlags = 0L, + chapterFlags = 0L, + coverLastModified = 0L, + artist = null, + author = null, + description = null, + genre = null, + status = 0L, + thumbnailUrl = null, + updateStrategy = UpdateStrategy.ALWAYS_UPDATE, + initialized = false, + ) + } +} diff --git a/app/src/main/java/tachiyomi/domain/items/episode/service/EpisodeRecognition.kt b/app/src/main/java/tachiyomi/domain/items/episode/service/EpisodeRecognition.kt new file mode 100644 index 00000000..c40410a3 --- /dev/null +++ b/app/src/main/java/tachiyomi/domain/items/episode/service/EpisodeRecognition.kt @@ -0,0 +1,119 @@ +package tachiyomi.domain.items.episode.service + +/** + * -R> = regex conversion. + */ +object EpisodeRecognition { + + private const val NUMBER_PATTERN = """([0-9]+)(\.[0-9]+)?(\.?[a-z]+)?""" + + /** + * All cases with Ch.xx + * Mokushiroku Alice Vol.1 Ch. 4: Misrepresentation -R> 4 + */ + private val basic = Regex("""(?<=ep\.) *$NUMBER_PATTERN""") + + /** + * Example: Bleach 567: Down With Snowwhite -R> 567 + */ + private val number = Regex(NUMBER_PATTERN) + + /** + * Regex used to remove unwanted tags + * Example Prison School 12 v.1 vol004 version1243 volume64 -R> Prison School 12 + */ + private val unwanted = Regex("""\b(?:v|ver|vol|version|volume|season|s)[^a-z]?[0-9]+""") + + /** + * Regex used to remove unwanted whitespace + * Example One Piece 12 special -R> One Piece 12special + */ + private val unwantedWhiteSpace = Regex("""\s(?=extra|special|omake)""") + + fun parseEpisodeNumber(animeTitle: String, episodeName: String, episodeNumber: Float? = null): Float { + // If episode number is known return. + if (episodeNumber != null && (episodeNumber == -2f || episodeNumber > -1f)) { + return episodeNumber + } + + // Get chapter title with lower case + var name = episodeName.lowercase() + + // Remove anime title from episode title. + name = name.replace(animeTitle.lowercase(), "").trim() + + // Remove comma's or hyphens. + name = name.replace(',', '.').replace('-', '.') + + // Remove unwanted white spaces. + name = unwantedWhiteSpace.replace(name, "") + + // Remove unwanted tags. + name = unwanted.replace(name, "") + + // Check base case ch.xx + basic.find(name)?.let { return getEpisodeNumberFromMatch(it) } + + // Take the first number encountered. + number.find(name)?.let { return getEpisodeNumberFromMatch(it) } + + return episodeNumber ?: -1f + } + + /** + * Check if episode number is found and return it + * @param match result of regex + * @return chapter number if found else null + */ + private fun getEpisodeNumberFromMatch(match: MatchResult): Float { + return match.let { + val initial = it.groups[1]?.value?.toFloat()!! + val subChapterDecimal = it.groups[2]?.value + val subChapterAlpha = it.groups[3]?.value + val addition = checkForDecimal(subChapterDecimal, subChapterAlpha) + initial.plus(addition) + } + } + + /** + * Check for decimal in received strings + * @param decimal decimal value of regex + * @param alpha alpha value of regex + * @return decimal/alpha float value + */ + private fun checkForDecimal(decimal: String?, alpha: String?): Float { + if (!decimal.isNullOrEmpty()) { + return decimal.toFloat() + } + + if (!alpha.isNullOrEmpty()) { + if (alpha.contains("extra")) { + return .99f + } + + if (alpha.contains("omake")) { + return .98f + } + + if (alpha.contains("special")) { + return .97f + } + + val trimmedAlpha = alpha.trimStart('.') + if (trimmedAlpha.length == 1) { + return parseAlphaPostFix(trimmedAlpha[0]) + } + } + + return .0f + } + + /** + * x.a -> x.1, x.b -> x.2, etc + */ + private fun parseAlphaPostFix(alpha: Char): Float { + val number = alpha.code - ('a'.code - 1) + if (number >= 10) return 0f + return number / 10f + } +} diff --git a/app/src/main/java/tachiyomi/domain/source/anime/model/AnimeSource.kt b/app/src/main/java/tachiyomi/domain/source/anime/model/AnimeSource.kt new file mode 100644 index 00000000..182e9653 --- /dev/null +++ b/app/src/main/java/tachiyomi/domain/source/anime/model/AnimeSource.kt @@ -0,0 +1,25 @@ +package tachiyomi.domain.source.anime.model + +data class AnimeSource( + val id: Long, + val lang: String, + val name: String, + val supportsLatest: Boolean, + val isStub: Boolean, + val pin: Pins = Pins.unpinned, + val isUsedLast: Boolean = false, +) { + + val visualName: String + get() = when { + lang.isEmpty() -> name + else -> "$name (${lang.uppercase()})" + } + + val key: () -> String = { + when { + isUsedLast -> "$id-lastused" + else -> "$id" + } + } +} diff --git a/app/src/main/java/tachiyomi/domain/source/anime/model/AnimeSourceWithCount.kt b/app/src/main/java/tachiyomi/domain/source/anime/model/AnimeSourceWithCount.kt new file mode 100644 index 00000000..65b928bc --- /dev/null +++ b/app/src/main/java/tachiyomi/domain/source/anime/model/AnimeSourceWithCount.kt @@ -0,0 +1,13 @@ +package tachiyomi.domain.source.anime.model + +data class AnimeSourceWithCount( + val source: AnimeSource, + val count: Long, +) { + + val id: Long + get() = source.id + + val name: String + get() = source.name +} diff --git a/app/src/main/java/tachiyomi/domain/source/anime/model/Pin.kt b/app/src/main/java/tachiyomi/domain/source/anime/model/Pin.kt new file mode 100644 index 00000000..e4430d3a --- /dev/null +++ b/app/src/main/java/tachiyomi/domain/source/anime/model/Pin.kt @@ -0,0 +1,42 @@ +package tachiyomi.domain.source.anime.model + +sealed class Pin(val code: Int) { + object Unpinned : Pin(0b00) + object Pinned : Pin(0b01) + object Actual : Pin(0b10) +} + +inline fun Pins(builder: Pins.PinsBuilder.() -> Unit = {}): Pins { + return Pins.PinsBuilder().apply(builder).flags() +} + +fun Pins(vararg pins: Pin) = Pins { + pins.forEach { +it } +} + +data class Pins(val code: Int = Pin.Unpinned.code) { + + operator fun contains(pin: Pin): Boolean = pin.code and code == pin.code + + operator fun plus(pin: Pin): Pins = Pins(code or pin.code) + + operator fun minus(pin: Pin): Pins = Pins(code xor pin.code) + + companion object { + val unpinned = Pins(Pin.Unpinned) + + val pinned = Pins(Pin.Pinned, Pin.Actual) + } + + class PinsBuilder(var code: Int = 0) { + operator fun Pin.unaryPlus() { + this@PinsBuilder.code = code or this@PinsBuilder.code + } + + operator fun Pin.unaryMinus() { + this@PinsBuilder.code = code or this@PinsBuilder.code + } + + fun flags(): Pins = Pins(code) + } +} diff --git a/app/src/main/java/tachiyomi/domain/source/anime/model/StubAnimeSource.kt b/app/src/main/java/tachiyomi/domain/source/anime/model/StubAnimeSource.kt new file mode 100644 index 00000000..7631c49c --- /dev/null +++ b/app/src/main/java/tachiyomi/domain/source/anime/model/StubAnimeSource.kt @@ -0,0 +1,33 @@ +package tachiyomi.domain.source.anime.model + +import eu.kanade.tachiyomi.animesource.AnimeSource +import eu.kanade.tachiyomi.animesource.model.SAnime +import eu.kanade.tachiyomi.animesource.model.SEpisode +import eu.kanade.tachiyomi.animesource.model.Video + +@Suppress("OverridingDeprecatedMember") +class StubAnimeSource(private val sourceData: AnimeSourceData) : AnimeSource { + + override val id: Long = sourceData.id + + override val name: String = sourceData.name.ifBlank { id.toString() } + + override val lang: String = sourceData.lang + + override suspend fun getAnimeDetails(anime: SAnime): SAnime { + throw AnimeSourceNotInstalledException() + } + + override suspend fun getEpisodeList(anime: SAnime): List { + throw AnimeSourceNotInstalledException() + } + + override suspend fun getVideoList(episode: SEpisode): List diff --git a/app/src/main/res/layout/activity_media.xml b/app/src/main/res/layout/activity_media.xml index 844524a2..a6b47cdf 100644 --- a/app/src/main/res/layout/activity_media.xml +++ b/app/src/main/res/layout/activity_media.xml @@ -293,4 +293,12 @@ tools:ignore="ContentDescription,ImageContrastCheck" tools:srcCompat="@tools:sample/backgrounds/scenic[2]" /> + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_anime_watch.xml b/app/src/main/res/layout/fragment_anime_watch.xml index 54d0545b..2a65e1ea 100644 --- a/app/src/main/res/layout/fragment_anime_watch.xml +++ b/app/src/main/res/layout/fragment_anime_watch.xml @@ -39,4 +39,6 @@ android:paddingBottom="128dp" tools:itemCount="1" tools:listitem="@layout/item_anime_watch" /> + + \ No newline at end of file diff --git a/app/src/main/res/layout/item_anime_page.xml b/app/src/main/res/layout/item_anime_page.xml index 0c5ecc09..ad473996 100644 --- a/app/src/main/res/layout/item_anime_page.xml +++ b/app/src/main/res/layout/item_anime_page.xml @@ -34,7 +34,7 @@ android:layout_marginEnd="8dp" android:layout_weight="1" android:hint="@string/anime" - android:textColorHint="?attr/colorOnPrimaryContainer" + android:textColorHint="?attr/colorPrimary" android:transitionName="@string/search" app:boxBackgroundColor="?attr/colorPrimaryContainer" app:boxCornerRadiusBottomEnd="28dp" @@ -42,7 +42,7 @@ app:boxCornerRadiusTopEnd="28dp" app:boxCornerRadiusTopStart="28dp" app:endIconDrawable="@drawable/ic_round_search_24" - app:endIconTint="?attr/colorOnPrimaryContainer" + app:endIconTint="?attr/colorPrimary" app:boxStrokeColor="@color/text_input_layout_stroke_color" app:hintAnimationEnabled="true"> @@ -64,7 +64,7 @@ android:layout_width="52dp" android:layout_height="match_parent" android:layout_marginTop="4dp" - android:backgroundTint="?attr/colorPrimaryContainer" + app:cardBackgroundColor="?attr/colorPrimaryContainer" app:strokeColor="@color/text_input_layout_stroke_color" app:cardCornerRadius="26dp"> @@ -73,7 +73,7 @@ android:layout_width="52dp" android:layout_height="52dp" android:scaleType="center" - android:tint="?attr/colorOnPrimaryContainer" + android:tint="?attr/colorPrimary" app:srcCompat="@drawable/ic_round_settings_24" tools:ignore="ContentDescription,ImageContrastCheck" /> diff --git a/app/src/main/res/layout/item_anime_watch.xml b/app/src/main/res/layout/item_anime_watch.xml index 9be21673..bea04864 100644 --- a/app/src/main/res/layout/item_anime_watch.xml +++ b/app/src/main/res/layout/item_anime_watch.xml @@ -82,19 +82,70 @@ android:textAllCaps="true" android:textColor="?android:attr/textColorSecondary" android:textSize="14sp" + android:ellipsize="end" + android:maxLines="1" tools:ignore="LabelFor,TextContrastCheck,DuplicateSpeakableTextCheck" /> + + + + + + + + + + + + @@ -21,6 +22,17 @@ android:textSize="18sp" android:text="Extension Name" /> + + @@ -66,7 +66,7 @@ android:layout_width="52dp" android:layout_height="match_parent" android:layout_marginTop="4dp" - android:backgroundTint="?attr/colorPrimaryContainer" + app:cardBackgroundColor="?attr/colorPrimaryContainer" app:strokeColor="@color/text_input_layout_stroke_color" app:cardCornerRadius="26dp"> @@ -75,7 +75,7 @@ android:layout_width="52dp" android:layout_height="52dp" android:scaleType="center" - android:tint="?attr/colorOnPrimaryContainer" + android:tint="?attr/colorPrimary" app:srcCompat="@drawable/ic_round_settings_24" tools:ignore="ContentDescription,ImageContrastCheck" /> diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml index 636da759..bbd3e021 100644 --- a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml +++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml @@ -2,5 +2,4 @@ - \ No newline at end of file diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher.webp b/app/src/main/res/mipmap-hdpi/ic_launcher.webp new file mode 100644 index 0000000000000000000000000000000000000000..feb696dad8ebbd2a48e39dd52053fe03ee4324aa GIT binary patch literal 3038 zcmWIYbaT7L&A<@u>J$(bVBvF$n}I>!{eYmrS2o9OzR%bHwRSneazj+~mcgRuTROQ? z-J);(+uHkC^ibddfs+&e{hxn3`o-V)n4`a@`_(UPIDK}-la$K-uG70`1@4TxR~i!) z@=IIBQ0H*Z?Wo(=o~RwXdsoRi;GblJ?28vU{8^Iy({v|9-`U@PXDs`ZCzGTm)~Rw`ZRMJvdU9K4bYV{X?QOXrl+#eVKj;4MH{Y!iZa<65 z_Giyi)(kSLJ-mUvxPC$Q-~Up&zj{8^s~W$kFxoNof)nrBuy^4H7e2nQJNVYtjHh7- zTPK`+KYgaP$8_i1wc+a*zmER7=<2T2yV3V$JQ*0KO`M|Sdkth!`T1?{!Hm7>x3|@O zyUQ?%p&@li-NBb@a&MdEZ8irv#jY2mqHH&R#P+M(Y>&%7@%bu0HJM{-GSgbqGYJB7 zy=HD+T2y9PaQoQAH5HTJ-dkP&AU%RH>*Sg4X**Q5Y<=Y=;yma2(pBp+RI=U&T|a%l z-1V~Dqdn5!+4j5nr}$JXVHSDj&LiS^_U$LWkJd>ZUry`g#@Ds){C6jWb6%#Z@yc^Y zHXoVq+QN}7D$2?^M|k4CpOflavXw5y?>-WfXD;+Fxa?&2j}VtGCI7mQ^W9zg{vXU) zuu-SdVUN_5b)H`9SzFgSezTQhZjM|%)usMPl+()2hqDC!|2<)!bgW{E>yc`uQxhhf z;dANue*g3oyTqeuNBmPKbTfW#{PTEYW!o3aa@Plv|0Q`X1s3g(Rn_?VY5oSY9%0QR zGg<{2gM@!hp0ZGDGJ|Y?Lab}@mU-8#8Z$Q8@Oe%RsJmEcwP$AJ)93NpH4UQo0%xvy zDtwsBD~VNAdP7rijNFNz$4^zaNoKuWqolQX=A}Q^RacA5ON>g*F2}>W!#TkwjlT9LdLypi(UvUP;orwqxIm&g$vhJd>0p- zy}N8d&1}m{s}|%QTyAn$>8h&9#(A>rE?>5ENbIy%tNF3q&!tzfm@)PCJ2P*i@^cB- z9rz9}&=mLGZyP!P;Y=}pb+=`9f33N~@8&XkZ7<0?oBEkM`1XZhvsV_snEJkb?T~Q& zaU$N-P*~$$!FJIVcU<3Z%<D{TKFvZFAYVZqAr5BB(iQfwSDj zcmJ7$=h&MqboKGLc)dj=(BSs!8HaWM7`op|QH~6n8LV+v<&W@P_n7uCa*{G4ZrRTM zkzFgT=BiePM*mxprM+O)=^Y&xk1@?U@Juj7bXv%?>p>T`P5I6>-A{I^#?1=?zU)r? zCy$A`${GjltoXBX;VMH1&c#<`B88Z(L=so*Z4ddiV|V$=+rnHvN$l)QM*N&LR~}yS z*m`YC>Z_`i9vUxhg!QbFEs>wRE7)Lz+GUYPUCk$++U>TUEU^JslLvDD>r|C@`ur!^MsxOeaFj+0w?HcoNMP2qU0m?zdK!I|L2HQV2#x`NlW z^IWW}Y_ZXq&+f8+FBG_2@6|saG~a#Exr9rsKCi6W8s-X^_qOyh8O(gVy5y?K(xTZy zCv}dz*x@X@P(ysv?~OV+tu5==*abGsI(B06D+a?Or`;F*ZU|YH(sCw@CpL8_4?{*- z)&BX7|F=2MagU8<7c`iEsE64gf713~)6GkQc)eF8U0QNP)SMyhfS`5uyAwfWyZn?B z_?>4sD(z8!>b%$^R{GUD&z5MRemTZJ3=NM{&c>ePy>PWuZ1S~BJ2Y9=2rf;?XuhZH z^hH)$M#it@v}@=0$-ho9⪒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⪒cgx1m<$9%$Ra3)Lkz;ow!mdutidZD`W7-t4`{|`y znGIGQYJK!@X^gRR`1L>*AEpf&tGrH}65*S)sVr6KztDA;kNMtaJO?f)HJA9R2?T}+ z3z=(7cAoLkc8dCj($>IcYpIGFN+Be zc&B#@*{79!VPaS~`QClg>wCIfcFy+Bs+!b$S}N@e&-47k-;Q&-PlqlK2ufG~a)p^; z-~I`L=MpbjNtsV=a1AW^Xi~-Bs@SX^vGdT^>Oz_4{Hj6^ryW^*yupOwfv(IuKG)SJ z7}5nc$UjP(#PR;Q*>*$6A8MP$chsntJ$$xS(b}n_pmoQiYrhx1e|zX(+9eiufejO! zdf5%~g`^FaWIZ=5Yb!S2@l_#jf=%^`HH&?_IIJ#79pL=i7d89yt~dK%H+O7lJJ!Q$ zFmbV;LR$;l!z-%i#ioCGBQ$gEC6;Xm9bSCrxFEf`+2Q1a?#&zp_OiPb6RkP}>e97N zX$5slCv{GZad1&&3S`NB+hTptl{r9N>e_`rR;T~&>^YRvy2Dq%e71{5a0)j^wtnW) z8PeOi%*tHE&PK#kSue{CVAE${6{y^z?OIh+*PiQ`7?vcMcQ5ct*wv$sDuT--%AN>2 z+~H4X$krB3`xn1x`>xxkZ)NnkR~VeMaETB$dAh82emjH2WvgQ@?^VqO_?UTa%Bk($ zpmt5-imk)9c@>WyPnmYL)ruwURVKp~4Q>OqZ3`C6nsDL!dA4m03vQe3U+~;)KEv$r zhW7g*IgE;-m8`0@%B<1V&EJno{RsG)&C2e6@%XAf{f?`93l8zPGJLn+HQVaRee+`G zGmi{5Tl_KSK5R4NXJT633G1{a{h1F8zRJw6{Wdf6Nr0iQ&CHFGCoi*q(Jef4*81mR z&Ly25fh?PE6;7-YD6moCZV-x_KQG~OLg4-5LjQs*zG~&o?VIGbed(62BZA=!sW(zx zl^#6t;bNG&Qcv>3bM86z8(!`Y`m^-IkI2*K_h zE!K7q&NYiIn{LhZ`sSfUi#&FAdEZ}twt7C}i$a!^CC?cG4q0R$SG))gbPhoQW!DQP5__0d&$+?D5YREVPLg5k&ds|X-{-&a#eMDZn!m?3 zi=Xwqvn&19F43H~F0;0J$!vdG+@ROPQfk8}`BKxN>1&tjzIyq&|FrE6F8cVBTR;Bi z{3$!imabiOn!{g>ol&W=I^oJ zmAFIkk0- zUw<{O`R?y=MQeRp>h#S6~^{7FSnyKmnklJ;)Sh-YnEMXY-ukQdb-=S!$DH0-`#n=)P-5CD!vBH zAsn0wE?+ns{KCU{Y%5CIu`%>qQvN;^zeO&bI?*+<5-?}LO zH^gYUuki$?Sr;~KpDOICaB6~r(&lOFcvB8fJGbEJhxb({U#>l<_;q zn&PegMMmdy80^2fdgkUOyDx;Z@hm&Tdh60WcLR~6DOEG$R1ZJT$*eyWbK%poeY1?Y zE-0R6;5|^{XMTI4ekD^jo8qxh{lZ^c=Y?IAv#hIFxp3<4t!$BH?VF?DH#a`gZaBK< zrb%>E|`6ia=ejW zw=L$_+3Mce-+w+o5U#u-`u~^L_v>%$n>9V>o>0OY=9Y#M>ji%f-`f9w-QR$m`@jF~ Z<5sm#yp}4zAl{5ENbz9`da$r#52t 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 U-64J;A$jSh!uWidACs6E0Ndjd_y7O^ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher.webp b/app/src/main/res/mipmap-mdpi/ic_launcher.webp new file mode 100644 index 0000000000000000000000000000000000000000..c30ada7655379b8daf5b10db109cd0998b0f67ba GIT binary patch literal 1946 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 F830Qhz#0Gm literal 0 HcmV?d00001 diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp index 4dcbec764a606568ab17ecc96c233eef7243339d..380a6b31a44d69014a8265571c159d4e15657091 100644 GIT binary patch literal 4054 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 F830Qhz#0Gm literal 4166 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 diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp new file mode 100644 index 0000000000000000000000000000000000000000..3276f97bda40502aa6c7d39c9e67147e1cca891b GIT binary patch literal 6266 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 diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp new file mode 100644 index 0000000000000000000000000000000000000000..96a88d747d87a84fdaac0562d2898f85b701e1f7 GIT binary patch literal 8540 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 literal 0 HcmV?d00001 diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp index 5a7049b5dcf0abb83e62d2993bddc31a32376741..96a88d747d87a84fdaac0562d2898f85b701e1f7 100644 GIT binary patch literal 8540 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 literal 8716 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 #80000000 #29FF6B08 + + #E8222222 \ No newline at end of file diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index 8c98f79e..7eaa3516 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -22,6 +22,8 @@ #999999 #030201 + #E8EDEDED + #00658e #00658E diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml index a1e9864b..0058aae7 100644 --- a/app/src/main/res/values/themes.xml +++ b/app/src/main/res/values/themes.xml @@ -18,6 +18,7 @@ 1000 @drawable/anim_splash shortEdges + @color/bg_black