diff --git a/.idea/misc.xml b/.idea/misc.xml
index 56dc302cc..489f14cd9 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -4,8 +4,12 @@
diff --git a/app/build.gradle b/app/build.gradle
index d806ee47b..f16fddbc1 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -103,7 +103,7 @@ dependencies {
debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.6'
- testImplementation 'junit:junit:4.13.1'
+ testImplementation 'junit:junit:4.13.2'
testImplementation 'org.json:json:20201115'
testImplementation 'org.koin:koin-test:2.2.2'
}
\ No newline at end of file
diff --git a/app/src/main/java/org/koitharu/kotatsu/core/model/MangaChapter.kt b/app/src/main/java/org/koitharu/kotatsu/core/model/MangaChapter.kt
index 62c27c781..0682eadc6 100644
--- a/app/src/main/java/org/koitharu/kotatsu/core/model/MangaChapter.kt
+++ b/app/src/main/java/org/koitharu/kotatsu/core/model/MangaChapter.kt
@@ -9,5 +9,6 @@ data class MangaChapter(
val name: String,
val number: Int,
val url: String,
+ val branch: String? = null,
val source: MangaSource
) : Parcelable
\ No newline at end of file
diff --git a/app/src/main/java/org/koitharu/kotatsu/core/parser/site/MangaLibRepository.kt b/app/src/main/java/org/koitharu/kotatsu/core/parser/site/MangaLibRepository.kt
index 8458ed0f4..8d2c6c87f 100644
--- a/app/src/main/java/org/koitharu/kotatsu/core/parser/site/MangaLibRepository.kt
+++ b/app/src/main/java/org/koitharu/kotatsu/core/parser/site/MangaLibRepository.kt
@@ -90,6 +90,8 @@ open class MangaLibRepository(loaderContext: MangaLoaderContext) :
chapters = ArrayList(total)
for (i in 0 until total) {
val item = list.getJSONObject(i)
+ val chapterId = item.getLong("chapter_id")
+ val branchName = item.getStringOrNull("username")
val url = buildString {
append(manga.url)
append("/v")
@@ -106,9 +108,10 @@ open class MangaLibRepository(loaderContext: MangaLoaderContext) :
}
chapters.add(
MangaChapter(
- id = generateUid(url),
+ id = generateUid(chapterId),
url = url,
source = source,
+ branch = branchName,
number = total - i,
name = name
)
diff --git a/app/src/main/java/org/koitharu/kotatsu/details/ui/ChaptersFragment.kt b/app/src/main/java/org/koitharu/kotatsu/details/ui/ChaptersFragment.kt
index 8ebdf8389..447a90c72 100644
--- a/app/src/main/java/org/koitharu/kotatsu/details/ui/ChaptersFragment.kt
+++ b/app/src/main/java/org/koitharu/kotatsu/details/ui/ChaptersFragment.kt
@@ -3,13 +3,10 @@ package org.koitharu.kotatsu.details.ui
import android.app.ActivityOptions
import android.os.Bundle
import android.view.*
+import android.widget.AdapterView
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.view.ActionMode
import androidx.core.graphics.Insets
-import androidx.core.view.isVisible
-import androidx.core.view.updatePadding
-import androidx.recyclerview.widget.DividerItemDecoration
-import androidx.recyclerview.widget.RecyclerView
import org.koin.androidx.viewmodel.ext.android.sharedViewModel
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.base.ui.BaseFragment
@@ -17,6 +14,7 @@ import org.koitharu.kotatsu.base.ui.list.OnListItemClickListener
import org.koitharu.kotatsu.core.model.MangaChapter
import org.koitharu.kotatsu.core.model.MangaSource
import org.koitharu.kotatsu.databinding.FragmentChaptersBinding
+import org.koitharu.kotatsu.details.ui.adapter.BranchesAdapter
import org.koitharu.kotatsu.details.ui.adapter.ChaptersAdapter
import org.koitharu.kotatsu.details.ui.adapter.ChaptersSelectionDecoration
import org.koitharu.kotatsu.details.ui.model.ChapterListItem
@@ -25,7 +23,7 @@ import org.koitharu.kotatsu.reader.ui.ReaderActivity
import org.koitharu.kotatsu.reader.ui.ReaderState
class ChaptersFragment : BaseFragment(),
- OnListItemClickListener, ActionMode.Callback {
+ OnListItemClickListener, ActionMode.Callback, AdapterView.OnItemSelectedListener {
private val viewModel by sharedViewModel()
@@ -53,9 +51,21 @@ class ChaptersFragment : BaseFragment(),
setHasFixedSize(true)
adapter = chaptersAdapter
}
+ val branchesAdapter = BranchesAdapter()
+ binding.spinnerBranches.adapter = branchesAdapter
+ binding.spinnerBranches.onItemSelectedListener = this
viewModel.isLoading.observe(viewLifecycleOwner, this::onLoadingStateChanged)
viewModel.chapters.observe(viewLifecycleOwner, this::onChaptersChanged)
+ viewModel.branches.observe(viewLifecycleOwner) {
+ branchesAdapter.setItems(it)
+ binding.spinnerBranches.isVisible = it.size > 1
+ }
+ viewModel.selectedBranchIndex.observe(viewLifecycleOwner) {
+ if (it != -1 && it != binding.spinnerBranches.selectedItemPosition) {
+ binding.spinnerBranches.setSelection(it)
+ }
+ }
viewModel.isChaptersReversed.observe(viewLifecycleOwner) {
activity?.invalidateOptionsMenu()
}
@@ -64,6 +74,7 @@ class ChaptersFragment : BaseFragment(),
override fun onDestroyView() {
chaptersAdapter = null
selectionDecoration = null
+ binding.spinnerBranches.adapter = null
super.onDestroyView()
}
@@ -145,6 +156,12 @@ class ChaptersFragment : BaseFragment(),
}
}
+ override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
+ viewModel.setSelectedBranch(binding.spinnerBranches.selectedItem as String?)
+ }
+
+ override fun onNothingSelected(parent: AdapterView<*>?) = Unit
+
override fun onCreateActionMode(mode: ActionMode, menu: Menu): Boolean {
val manga = viewModel.manga.value
mode.menuInflater.inflate(R.menu.mode_chapters, menu)
@@ -174,7 +191,7 @@ class ChaptersFragment : BaseFragment(),
binding.recyclerViewChapters.updatePadding(
left = insets.left,
right = insets.right,
- bottom = insets.bottom
+ bottom = insets.bottom + binding.spinnerBranches.height
)
}
diff --git a/app/src/main/java/org/koitharu/kotatsu/details/ui/DetailsViewModel.kt b/app/src/main/java/org/koitharu/kotatsu/details/ui/DetailsViewModel.kt
index 247af2b9f..4c0292ad5 100644
--- a/app/src/main/java/org/koitharu/kotatsu/details/ui/DetailsViewModel.kt
+++ b/app/src/main/java/org/koitharu/kotatsu/details/ui/DetailsViewModel.kt
@@ -1,5 +1,6 @@
package org.koitharu.kotatsu.details.ui
+import androidx.lifecycle.asFlow
import androidx.lifecycle.asLiveData
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.Dispatchers
@@ -18,6 +19,7 @@ import org.koitharu.kotatsu.history.domain.HistoryRepository
import org.koitharu.kotatsu.local.domain.LocalMangaRepository
import org.koitharu.kotatsu.tracker.domain.TrackingRepository
import org.koitharu.kotatsu.utils.SingleLiveEvent
+import org.koitharu.kotatsu.utils.ext.mapToSet
import java.io.IOException
class DetailsViewModel(
@@ -31,6 +33,7 @@ class DetailsViewModel(
) : BaseViewModel() {
private val mangaData = MutableStateFlow(intent.manga)
+ private val selectedBranch = MutableStateFlow(null)
private val history = mangaData.mapNotNull { it?.id }
.distinctUntilChanged()
@@ -69,12 +72,24 @@ class DetailsViewModel(
val onMangaRemoved = SingleLiveEvent()
+ val branches = mangaData.map {
+ it?.chapters?.mapToSet { x -> x.branch }?.sortedBy { x -> x }.orEmpty()
+ }.asLiveData(viewModelScope.coroutineContext + Dispatchers.Default)
+
+ val selectedBranchIndex = combine(
+ branches.asFlow(),
+ selectedBranch
+ ) { branches, selected ->
+ branches.indexOf(selected)
+ }.asLiveData(viewModelScope.coroutineContext + Dispatchers.Default)
+
val chapters = combine(
mangaData.map { it?.chapters.orEmpty() },
history.map { it?.chapterId },
newChapters,
- chaptersReversed
- ) { chapters, currentId, newCount, reversed ->
+ chaptersReversed,
+ selectedBranch
+ ) { chapters, currentId, newCount, reversed, branch ->
val currentIndex = chapters.indexOfFirst { it.id == currentId }
val firstNewIndex = chapters.size - newCount
val res = chapters.mapIndexed { index, chapter ->
@@ -86,7 +101,7 @@ class DetailsViewModel(
else -> ChapterExtra.UNREAD
}
)
- }
+ }.filter { it.chapter.branch == branch }
if (reversed) res.asReversed() else res
}.asLiveData(viewModelScope.coroutineContext + Dispatchers.Default)
@@ -96,6 +111,15 @@ class DetailsViewModel(
?: throw MangaNotFoundException("Cannot find manga")
mangaData.value = manga
manga = manga.source.repository.getDetails(manga)
+ // find default branch
+ val hist = historyRepository.getOne(manga)
+ selectedBranch.value = if (hist != null) {
+ manga.chapters?.find { it.id == hist.chapterId }?.branch
+ } else {
+ manga.chapters
+ ?.groupBy { it.branch }
+ ?.maxByOrNull { it.value.size }?.key
+ }
mangaData.value = manga
}
}
@@ -114,4 +138,8 @@ class DetailsViewModel(
fun setChaptersReversed(newValue: Boolean) {
settings.chaptersReverse = newValue
}
+
+ fun setSelectedBranch(branch: String?) {
+ selectedBranch.value = branch
+ }
}
\ No newline at end of file
diff --git a/app/src/main/java/org/koitharu/kotatsu/details/ui/adapter/BranchesAdapter.kt b/app/src/main/java/org/koitharu/kotatsu/details/ui/adapter/BranchesAdapter.kt
new file mode 100644
index 000000000..5cee213a7
--- /dev/null
+++ b/app/src/main/java/org/koitharu/kotatsu/details/ui/adapter/BranchesAdapter.kt
@@ -0,0 +1,45 @@
+package org.koitharu.kotatsu.details.ui.adapter
+
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.BaseAdapter
+import android.widget.TextView
+import org.koitharu.kotatsu.R
+import org.koitharu.kotatsu.utils.ext.replaceWith
+
+class BranchesAdapter : BaseAdapter() {
+
+ private val dataSet = ArrayList()
+
+ override fun getCount(): Int {
+ return dataSet.size
+ }
+
+ override fun getItem(position: Int): Any? {
+ return dataSet[position]
+ }
+
+ override fun getItemId(position: Int): Long {
+ return dataSet[position].hashCode().toLong()
+ }
+
+ override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
+ val view = convertView ?: LayoutInflater.from(parent.context)
+ .inflate(R.layout.item_branch, parent, false)
+ (view as TextView).text = dataSet[position]
+ return view
+ }
+
+ override fun getDropDownView(position: Int, convertView: View?, parent: ViewGroup): View {
+ val view = convertView ?: LayoutInflater.from(parent.context)
+ .inflate(R.layout.item_branch_dropdown, parent, false)
+ (view as TextView).text = dataSet[position]
+ return view
+ }
+
+ fun setItems(items: Collection) {
+ dataSet.replaceWith(items)
+ notifyDataSetChanged()
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/org/koitharu/kotatsu/reader/ui/ReaderViewModel.kt b/app/src/main/java/org/koitharu/kotatsu/reader/ui/ReaderViewModel.kt
index 573f9c878..adb4a5914 100644
--- a/app/src/main/java/org/koitharu/kotatsu/reader/ui/ReaderViewModel.kt
+++ b/app/src/main/java/org/koitharu/kotatsu/reader/ui/ReaderViewModel.kt
@@ -40,7 +40,7 @@ class ReaderViewModel(
private var loadingJob: Job? = null
private val currentState = MutableStateFlow(null)
- private val mangaData = MutableStateFlow(intent.manga)
+ private val mangaData = MutableStateFlow(intent.manga)
private val chapters = LongSparseArray()
val readerMode = MutableLiveData()
@@ -58,7 +58,7 @@ class ReaderViewModel(
)
}.asLiveDataDistinct(viewModelScope.coroutineContext + Dispatchers.Default)
- val content = MutableLiveData(ReaderContent(emptyList(), null))
+ val content = MutableLiveData(ReaderContent(emptyList(), null))
val manga: Manga?
get() = mangaData.value
@@ -80,7 +80,6 @@ class ReaderViewModel(
manga.chapters?.forEach {
chapters.put(it.id, it)
}
- mangaData.value = manga
// determine mode
val mode =
dataRepository.getReaderMode(manga.id) ?: manga.chapters?.randomOrNull()?.let {
@@ -96,6 +95,9 @@ class ReaderViewModel(
currentState.value = state ?: historyRepository.getOne(manga)?.let {
ReaderState.from(it)
} ?: ReaderState.initial(manga)
+
+ val branch = chapters[currentState.value?.chapterId ?: 0L].branch
+ mangaData.value = manga.copy(chapters = manga.chapters?.filter { it.branch == branch })
readerMode.postValue(mode)
val pages = loadChapter(requireNotNull(currentState.value).chapterId)
diff --git a/app/src/main/res/layout/fragment_chapters.xml b/app/src/main/res/layout/fragment_chapters.xml
index c4671d8c2..50a4ed6f6 100644
--- a/app/src/main/res/layout/fragment_chapters.xml
+++ b/app/src/main/res/layout/fragment_chapters.xml
@@ -1,11 +1,29 @@
-
+
+
+
+
+
+
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/item_branch.xml b/app/src/main/res/layout/item_branch.xml
new file mode 100644
index 000000000..ee1a60392
--- /dev/null
+++ b/app/src/main/res/layout/item_branch.xml
@@ -0,0 +1,9 @@
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/item_branch_dropdown.xml b/app/src/main/res/layout/item_branch_dropdown.xml
new file mode 100644
index 000000000..3c81e914d
--- /dev/null
+++ b/app/src/main/res/layout/item_branch_dropdown.xml
@@ -0,0 +1,9 @@
+
+
\ No newline at end of file
diff --git a/build.gradle b/build.gradle
index 8c3b6cad5..4933dce61 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,6 +1,6 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
- ext.kotlin_version = '1.4.30'
+ ext.kotlin_version = '1.4.31'
repositories {
google()
jcenter()