Merge pull request #54 from rebelonion/dev

Dev
This commit is contained in:
rebelonion 2023-11-21 02:52:46 -06:00 committed by GitHub
commit 533aa9f56e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 262 additions and 75 deletions

View file

@ -1,16 +1,19 @@
package ani.dantotsu.download.manga
import android.animation.ObjectAnimator
import android.content.Context
import android.content.Intent
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.os.Environment
import android.util.TypedValue
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.animation.OvershootInterpolator
import android.widget.GridView
import androidx.appcompat.app.AppCompatActivity
import androidx.cardview.widget.CardView
import androidx.core.view.updatePaddingRelative
import androidx.fragment.app.Fragment
@ -24,9 +27,15 @@ import ani.dantotsu.download.DownloadsManager
import ani.dantotsu.logger
import ani.dantotsu.media.Media
import ani.dantotsu.media.MediaDetailsActivity
import ani.dantotsu.media.manga.MangaNameAdapter
import ani.dantotsu.navBarHeight
import ani.dantotsu.px
import ani.dantotsu.setSafeOnClickListener
import ani.dantotsu.settings.SettingsDialogFragment
import ani.dantotsu.statusBarHeight
import com.google.android.material.card.MaterialCardView
import com.google.android.material.imageview.ShapeableImageView
import com.google.android.material.textfield.TextInputLayout
import com.google.firebase.crashlytics.FirebaseCrashlytics
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
@ -46,6 +55,28 @@ class OfflineMangaFragment: Fragment() {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
val view = inflater.inflate(R.layout.fragment_manga_offline, container, false)
val textInputLayout = view.findViewById<TextInputLayout>(R.id.offlineMangaSearchBar)
val currentColor = textInputLayout.boxBackgroundColor
val semiTransparentColor = (currentColor and 0x00FFFFFF) or 0xA8000000.toInt()
textInputLayout.boxBackgroundColor = semiTransparentColor
val materialCardView = view.findViewById<MaterialCardView>(R.id.offlineMangaAvatarContainer)
materialCardView.setCardBackgroundColor(semiTransparentColor)
val typedValue = TypedValue()
requireContext().theme?.resolveAttribute(android.R.attr.windowBackground, typedValue, true)
val color = typedValue.data
val animeUserAvatar= view.findViewById<ShapeableImageView>(R.id.offlineMangaUserAvatar)
animeUserAvatar.setSafeOnClickListener {
SettingsDialogFragment(SettingsDialogFragment.Companion.PageType.HOME).show((it.context as AppCompatActivity).supportFragmentManager, "dialog")
}
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())
}
gridView = view.findViewById(R.id.gridView)
getDownloads()
adapter = OfflineMangaAdapter(requireContext(), downloads)
@ -81,13 +112,6 @@ class OfflineMangaFragment: Fragment() {
}
}
}
val refreshLayout = view.findViewById<SwipeRefreshLayout>(R.id.mangaRefresh)
refreshLayout.setSlingshotDistance(height + 128)
refreshLayout.setProgressViewEndTarget(false, height + 128)
refreshLayout.setOnRefreshListener {
Refresh.activity[this.hashCode()]!!.postValue(true)
}
val scrollTop = view.findViewById<CardView>(R.id.mangaPageScrollTop)
var visible = false
fun animate() {

View file

@ -14,4 +14,5 @@ data class Selected(
var server: String? = null,
var video: Int = 0,
var latest: Float = 0f,
var scanlators: List<String>? = null,
) : Serializable

View file

@ -12,9 +12,10 @@ data class MangaChapter(
var title: String? = null,
var description: String? = null,
var sChapter: SChapter,
var progress: String? = null,
val scanlator: String? = null,
var progress: String? = ""
) : Serializable {
constructor(chapter: MangaChapter) : this(chapter.number, chapter.link, chapter.title, chapter.description, chapter.sChapter)
constructor(chapter: MangaChapter) : this(chapter.number, chapter.link, chapter.title, chapter.description, chapter.sChapter, chapter.scanlator)
private val images = mutableListOf<MangaImage>()
fun images(): List<MangaImage> = images

View file

@ -76,6 +76,7 @@ class MangaChapterAdapter(
// Find the position of the chapter and notify only that item
val position = arr.indexOfFirst { it.number == chapterNumber }
if (position != -1) {
arr[position].progress = "Downloaded"
notifyItemChanged(position)
}
}
@ -113,8 +114,10 @@ class MangaChapterAdapter(
RecyclerView.ViewHolder(binding.root) {
fun bind(chapterNumber: String, progress: String?) {
if (progress != null) {
binding.itemChapterTitle.text = "Downloading: ${progress}%"
binding.itemChapterTitle.visibility = View.VISIBLE
binding.itemChapterTitle.text = "$progress"
}else{
binding.itemChapterTitle.visibility = View.GONE
binding.itemChapterTitle.text = ""
}
if (activeDownloads.contains(chapterNumber)) {
@ -186,16 +189,9 @@ class MangaChapterAdapter(
holder.bind(ep.number, ep.progress)
setAnimation(fragment.requireContext(), holder.binding.root, fragment.uiSettings)
binding.itemChapterNumber.text = ep.number
/*if (!ep.progress.isNullOrEmpty()) {
binding.itemChapterTitle.text = ep.progress
binding.itemChapterTitle.setOnLongClickListener {
binding.itemChapterTitle.maxLines.apply {
binding.itemChapterTitle.maxLines = if (this == 1) 3 else 1
}
true
}
binding.itemChapterTitle.visibility = View.VISIBLE
} else*/ binding.itemChapterTitle.visibility = View.VISIBLE
if (ep.progress.isNullOrEmpty()) {
binding.itemChapterTitle.visibility = View.GONE
} else binding.itemChapterTitle.visibility = View.VISIBLE
if (media.userProgress != null) {
if ((MangaNameAdapter.findChapterNumber(ep.number)

View file

@ -1,16 +1,19 @@
package ani.dantotsu.media.manga
import android.annotation.SuppressLint
import android.app.AlertDialog
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ArrayAdapter
import android.widget.CheckBox
import android.widget.ImageView
import android.widget.LinearLayout
import androidx.core.content.ContextCompat
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.RecyclerView
import ani.dantotsu.*
import ani.dantotsu.App.Companion.context
import ani.dantotsu.media.anime.handleProgress
import ani.dantotsu.databinding.ItemAnimeWatchBinding
import ani.dantotsu.databinding.ItemChipBinding
@ -37,6 +40,9 @@ class MangaReadAdapter(
var subscribe: MediaDetailsActivity.PopImageButton? = null
private var _binding: ItemAnimeWatchBinding? = null
val hiddenScanlators = mutableListOf<String>()
var scanlatorSelectionListener: ScanlatorSelectionListener? = null
var options = listOf<String>()
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val bind = ItemAnimeWatchBinding.inflate(LayoutInflater.from(parent.context), parent, false)
@ -131,6 +137,46 @@ class MangaReadAdapter(
binding.animeSourceTop.rotation = if (reversed) -90f else 90f
fragment.onIconPressed(style, reversed)
}
binding.animeScanlatorTop.setOnClickListener {
val dialogView = LayoutInflater.from(currContext()).inflate(R.layout.custom_dialog_layout, null)
val checkboxContainer = dialogView.findViewById<LinearLayout>(R.id.checkboxContainer)
// Dynamically add checkboxes
options.forEach { option ->
val checkBox = CheckBox(currContext()).apply {
text = option
}
//set checked if it's already selected
if(media.selected!!.scanlators != null){
checkBox.isChecked = media.selected!!.scanlators?.contains(option) != true
scanlatorSelectionListener?.onScanlatorsSelected()
}else{
checkBox.isChecked = true
}
checkboxContainer.addView(checkBox)
}
// Create AlertDialog
AlertDialog.Builder(currContext())
.setView(dialogView)
.setPositiveButton("OK") { dialog, which ->
//add unchecked to hidden
hiddenScanlators.clear()
for (i in 0 until checkboxContainer.childCount) {
val checkBox = checkboxContainer.getChildAt(i) as CheckBox
if (!checkBox.isChecked) {
hiddenScanlators.add(checkBox.text.toString())
}
}
media.selected!!.scanlators = hiddenScanlators
scanlatorSelectionListener?.onScanlatorsSelected()
}
.setNegativeButton("Cancel", null)
.show()
}
var selected = when (style) {
0 -> binding.animeSourceList
1 -> binding.animeSourceCompact
@ -201,6 +247,7 @@ class MangaReadAdapter(
@SuppressLint("SetTextI18n")
fun handleChapters() {
val binding = _binding
if (binding != null) {
if (media.manga?.chapters != null) {
@ -208,7 +255,11 @@ class MangaReadAdapter(
val anilistEp = (media.userProgress ?: 0).plus(1)
val appEp = loadData<String>("${media.id}_current_chp")?.toIntOrNull() ?: 1
var continueEp = (if (anilistEp > appEp) anilistEp else appEp).toString()
val formattedChapters = chapters.map { MangaNameAdapter.findChapterNumber(it)?.toInt()?.toString() }
val filteredChapters = chapters.filter { chapterKey ->
val chapter = media.manga.chapters!![chapterKey]!!
chapter.scanlator !in hiddenScanlators
}
val formattedChapters = filteredChapters.map { MangaNameAdapter.findChapterNumber(it)?.toInt()?.toString() }
if (formattedChapters.contains(continueEp)) {
continueEp = chapters[formattedChapters.indexOf(continueEp)]
binding.animeSourceContinue.visibility = View.VISIBLE
@ -279,3 +330,7 @@ class MangaReadAdapter(
inner class ViewHolder(val binding: ItemAnimeWatchBinding) : RecyclerView.ViewHolder(binding.root)
}
interface ScanlatorSelectionListener {
fun onScanlatorsSelected()
}

View file

@ -67,7 +67,7 @@ import kotlin.math.roundToInt
import android.Manifest
import androidx.core.app.ActivityCompat
open class MangaReadFragment : Fragment() {
open class MangaReadFragment : Fragment(), ScanlatorSelectionListener {
private var _binding: FragmentAnimeWatchBinding? = null
private val binding get() = _binding!!
private val model: MediaDetailsViewModel by activityViewModels()
@ -160,6 +160,7 @@ open class MangaReadFragment : Fragment() {
model.mangaReadSources = if (media.isAdult) HMangaSources else MangaSources
headerAdapter = MangaReadAdapter(it, this, model.mangaReadSources!!)
headerAdapter.scanlatorSelectionListener = this
chapterAdapter = MangaChapterAdapter(style ?: uiSettings.mangaDefaultView, media, this)
for (download in downloadManager.mangaDownloads){
@ -182,45 +183,70 @@ open class MangaReadFragment : Fragment() {
}
}
model.getMangaChapters().observe(viewLifecycleOwner) { loadedChapters ->
if (loadedChapters != null) {
val chapters = loadedChapters[media.selected!!.sourceIndex]
if (chapters != null) {
media.manga?.chapters = chapters
model.getMangaChapters().observe(viewLifecycleOwner) { _ ->
updateChapters()
}
}
//CHIP GROUP
val total = chapters.size
val divisions = total.toDouble() / 10
start = 0
end = null
val limit = when {
(divisions < 25) -> 25
(divisions < 50) -> 50
else -> 100
}
headerAdapter.clearChips()
if (total > limit) {
val arr = chapters.keys.toTypedArray()
val stored = ceil((total).toDouble() / limit).toInt()
val position = clamp(media.selected!!.chip, 0, stored - 1)
val last = if (position + 1 == stored) total else (limit * (position + 1))
start = limit * (position)
end = last - 1
headerAdapter.updateChips(
limit,
arr,
(1..stored).toList().toTypedArray(),
position
)
}
override fun onScanlatorsSelected() {
updateChapters()
}
headerAdapter.subscribeButton(true)
reload()
private fun updateChapters() {
val loadedChapters = model.getMangaChapters().value
if (loadedChapters != null) {
val chapters = loadedChapters[media.selected!!.sourceIndex]
if (chapters != null) {
headerAdapter.options = getScanlators(chapters)
val filteredChapters = chapters.filterNot { (_, chapter) ->
chapter.scanlator in headerAdapter.hiddenScanlators
}
media.manga?.chapters = filteredChapters.toMutableMap()
//CHIP GROUP
val total = filteredChapters.size
val divisions = total.toDouble() / 10
start = 0
end = null
val limit = when {
(divisions < 25) -> 25
(divisions < 50) -> 50
else -> 100
}
headerAdapter.clearChips()
if (total > limit) {
val arr = filteredChapters.keys.toTypedArray()
val stored = ceil((total).toDouble() / limit).toInt()
val position = clamp(media.selected!!.chip, 0, stored - 1)
val last = if (position + 1 == stored) total else (limit * (position + 1))
start = limit * (position)
end = last - 1
headerAdapter.updateChips(
limit,
arr,
(1..stored).toList().toTypedArray(),
position
)
}
headerAdapter.subscribeButton(true)
reload()
}
}
}
fun getScanlators(chap: MutableMap<String, MangaChapter>?): List<String> {
val scanlators = mutableListOf<String>()
if (chap != null) {
val chapters = chap.values
for (chapter in chapters) {
scanlators.add(chapter.scanlator ?: "Unknown")
}
}
return scanlators.distinct()
}
fun onSourceChange(i: Int): MangaParser {
media.manga?.chapters = null
reload()
@ -453,7 +479,7 @@ open class MangaReadFragment : Fragment() {
ACTION_DOWNLOAD_PROGRESS -> {
val chapterNumber = intent.getStringExtra(EXTRA_CHAPTER_NUMBER)
val progress = intent.getIntExtra("progress", 0)
//chapterNumber?.let { chapterAdapter.updateDownloadProgress(it, progress) }
chapterNumber?.let { chapterAdapter.updateDownloadProgress(it, progress)
}
}
}

View file

@ -532,8 +532,9 @@ class DynamicMangaParser(extension: MangaExtension.Installed) : MangaParser() {
return MangaChapter(
sChapter.name,
sChapter.url,
"",
sChapter.name,
null,
sChapter.scanlator,
sChapter
)
}

View file

@ -67,7 +67,7 @@ data class MangaChapter(
//Self-Descriptive
val title: String? = null,
val description: String? = null,
val scanlator: String? = null,
val sChapter: SChapter,
)

View file

@ -3,6 +3,7 @@ package ani.dantotsu.parsers
import android.os.Environment
import ani.dantotsu.currContext
import ani.dantotsu.download.DownloadsManager
import ani.dantotsu.media.manga.MangaNameAdapter
import eu.kanade.tachiyomi.source.model.SChapter
import eu.kanade.tachiyomi.source.model.SManga
import me.xdrop.fuzzywuzzy.FuzzySearch
@ -30,10 +31,11 @@ class OfflineMangaParser: MangaParser() {
if (directory.exists()) {
directory.listFiles()?.forEach {
if (it.isDirectory) {
val chapter = MangaChapter(it.name, "$mangaLink/${it.name}", it.name, null, SChapter.create())
val chapter = MangaChapter(it.name, "$mangaLink/${it.name}", it.name, null, null, SChapter.create())
chapters.add(chapter)
}
}
chapters.sortBy { MangaNameAdapter.findChapterNumber(it.number) }
return chapters
}
return emptyList()

View file

@ -170,7 +170,7 @@ OS Version: $CODENAME $RELEASE ($SDK_INT)
binding.skipExtensionIcons.setOnCheckedChangeListener { _, isChecked ->
saveData("skip_extension_icons", isChecked)
}
binding.NSFWExtension.isChecked = loadData("NFSWExtension") ?: false
binding.NSFWExtension.isChecked = loadData("NFSWExtension") ?: true
binding.NSFWExtension.setOnCheckedChangeListener { _, isChecked ->
saveData("NFSWExtension", isChecked)

View file

@ -78,7 +78,7 @@ class AnimeExtensionPagingSource(
val installedExtensions = installedExtensionsFlow.first().map { it.pkgName }.toSet()
val availableExtensions = availableExtensionsFlow.first().filterNot { it.pkgName in installedExtensions }
val query = searchQuery.first()
var isNsfwEnabled: Boolean = loadData("NFSWExtension") ?: false
var isNsfwEnabled: Boolean = loadData("NFSWExtension") ?: true
val filteredExtensions = if (query.isEmpty()) {
availableExtensions
} else {

View file

@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/fragment_container"
android:paddingTop="32dp"
android:layout_width="match_parent"
android:layout_height="match_parent" />

View file

@ -20,6 +20,7 @@
<FrameLayout
android:id="@+id/fragmentContainer"
android:paddingTop="32dp"
android:layout_width="match_parent"
android:layout_height="match_parent">
</FrameLayout>

View file

@ -0,0 +1,19 @@
<!-- custom_dialog_layout.xml -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="16dp">
<ScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:id="@+id/checkboxContainer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"/>
</ScrollView>
</LinearLayout>

View file

@ -6,8 +6,7 @@
android:layout_height="match_parent"
tools:context=".home.MangaFragment">
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
android:id="@+id/mangaRefresh"
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipChildren="false"
@ -19,17 +18,67 @@
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:id="@+id/mangaOfflineTitle"
<LinearLayout
android:id="@+id/animeTitleContainer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="16dp"
android:text="Offline Manga"
android:textColor="?attr/colorOnSurface"
android:textSize="24sp"
android:textStyle="bold"
android:fontFamily="@font/poppins"
android:gravity="center_horizontal" />
android:layout_margin="32dp"
android:orientation="horizontal">
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/offlineMangaSearchBar"
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox.ExposedDropdownMenu"
android:layout_width="0dp"
android:layout_height="56dp"
android:layout_marginEnd="8dp"
android:layout_weight="1"
android:hint="@string/manga"
android:textColorHint="@color/bg_opp"
android:transitionName="@string/search"
app:boxBackgroundColor="?attr/colorPrimaryContainer"
app:boxCornerRadiusBottomEnd="28dp"
app:boxCornerRadiusBottomStart="28dp"
app:boxCornerRadiusTopEnd="28dp"
app:boxCornerRadiusTopStart="28dp"
app:endIconDrawable="@drawable/ic_round_search_24"
app:endIconTint="@color/bg_opp"
app:boxStrokeColor="@color/text_input_layout_stroke_color"
app:hintAnimationEnabled="true">
<AutoCompleteTextView
android:id="@+id/animeSearchBarText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:focusable="false"
android:fontFamily="@font/poppins_bold"
android:inputType="none"
android:padding="8dp"
android:textSize="14sp"
tools:ignore="LabelFor,TextContrastCheck" />
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.card.MaterialCardView
android:id="@+id/offlineMangaAvatarContainer"
android:layout_width="52dp"
android:layout_height="match_parent"
android:layout_marginTop="4dp"
app:cardBackgroundColor="?attr/colorPrimaryContainer"
app:strokeColor="@color/text_input_layout_stroke_color"
app:cardCornerRadius="26dp">
<com.google.android.material.imageview.ShapeableImageView
android:id="@+id/offlineMangaUserAvatar"
android:layout_width="52dp"
android:layout_height="52dp"
android:scaleType="center"
android:tint="@color/bg_opp"
app:srcCompat="@drawable/ic_round_settings_24"
tools:ignore="ContentDescription,ImageContrastCheck" />
</com.google.android.material.card.MaterialCardView>
</LinearLayout>
<!-- This TextView might overlap with GridView if GridView has items -->
<TextView
@ -54,7 +103,7 @@
android:padding="10dp"
android:gravity="center" />
</LinearLayout>
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
</FrameLayout>
<androidx.cardview.widget.CardView
android:id="@+id/mangaPageScrollTop"

View file

@ -245,6 +245,16 @@
app:tint="?attr/colorOnBackground"
tools:ignore="ContentDescription,ImageContrastCheck" />
<ImageView
android:id="@+id/animeScanlatorTop"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:alpha="1"
android:padding="8dp"
app:srcCompat="@drawable/ic_round_edit_note_24"
app:tint="?attr/colorOnBackground"
tools:ignore="ContentDescription,ImageContrastCheck" />
<ImageView
android:id="@+id/animeSourceTop"
android:layout_width="wrap_content"

View file

@ -52,6 +52,7 @@
android:singleLine="true"
android:textSize="14dp"
tools:ignore="SpUsage"
android:paddingEnd="8dp"
tools:text="1" />
</LinearLayout>
@ -91,7 +92,7 @@
android:id="@+id/itemEpisodeViewed"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end"
android:layout_gravity="start"
android:layout_margin="8dp"
android:visibility="gone"
app:srcCompat="@drawable/ic_round_remove_red_eye_24"