Merge remote-tracking branch 'upstream/master'

This commit is contained in:
jmir1 2021-10-15 23:20:58 +02:00
commit 4e114d30a6
20 changed files with 132 additions and 111 deletions

View file

@ -1,6 +1,9 @@
name: PR build check
on:
pull_request:
paths-ignore:
- '**.md'
- 'app/src/main/res/**/strings.xml'
jobs:
build:

View file

@ -29,7 +29,7 @@ android {
minSdk = AndroidConfig.minSdk
targetSdk = AndroidConfig.targetSdk
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
versionCode = 69
versionCode = 70
versionName = "0.12.3.2"
buildConfigField("String", "COMMIT_COUNT", "\"${getCommitCount()}\"")

View file

@ -8,6 +8,7 @@ import eu.kanade.tachiyomi.data.backup.BackupCreatorJob
import eu.kanade.tachiyomi.data.library.LibraryUpdateJob
import eu.kanade.tachiyomi.data.preference.PreferenceKeys
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.data.preference.plusAssign
import eu.kanade.tachiyomi.data.track.TrackManager
import eu.kanade.tachiyomi.data.updater.UpdaterJob
import eu.kanade.tachiyomi.extension.AnimeExtensionUpdateJob
@ -248,9 +249,9 @@ object Migrations {
putString(PreferenceKeys.librarySortingDirection, newSortingDirection.name)
}
}
if (oldVersion < 65) {
if (preferences.lang().get() in listOf("en-US", "en-GB")) {
preferences.lang().set("en")
if (oldVersion < 70) {
if (preferences.enabledLanguages().isSet()) {
preferences.enabledLanguages() += "all"
}
}
return true

View file

@ -574,4 +574,4 @@ class AnimelibUpdateService(
}
}
const val PER_SOURCE_QUEUE_WARNING_THRESHOLD = 50
const val PER_SOURCE_QUEUE_WARNING_THRESHOLD = 60

View file

@ -574,4 +574,4 @@ class LibraryUpdateService(
}
}
const val PER_SOURCE_QUEUE_WARNING_THRESHOLD = 50
const val PER_SOURCE_QUEUE_WARNING_THRESHOLD = 60

View file

@ -97,7 +97,6 @@ import eu.kanade.tachiyomi.util.episode.NoEpisodesException
import eu.kanade.tachiyomi.util.hasCustomCover
import eu.kanade.tachiyomi.util.lang.awaitSingle
import eu.kanade.tachiyomi.util.lang.launchIO
import eu.kanade.tachiyomi.util.lang.launchUI
import eu.kanade.tachiyomi.util.storage.getUriCompat
import eu.kanade.tachiyomi.util.system.logcat
import eu.kanade.tachiyomi.util.system.toShareIntent
@ -272,13 +271,21 @@ class AnimeController :
if (anime == null || source == null) return
// Init RecyclerView and adapter
animeInfoAdapter = AnimeInfoHeaderAdapter(this, fromSource, binding.infoRecycler != null)
episodesHeaderAdapter = AnimeEpisodesHeaderAdapter(this)
animeInfoAdapter = AnimeInfoHeaderAdapter(this, fromSource, binding.infoRecycler != null).apply {
setHasStableIds(true)
}
episodesHeaderAdapter = AnimeEpisodesHeaderAdapter(this).apply {
setHasStableIds(true)
}
episodesAdapter = EpisodesAdapter(this, view.context)
// Phone layout
binding.fullRecycler?.let {
it.adapter = ConcatAdapter(animeInfoAdapter, episodesHeaderAdapter, episodesAdapter)
val config = ConcatAdapter.Config.Builder()
.setIsolateViewTypes(true)
.setStableIdMode(ConcatAdapter.Config.StableIdMode.SHARED_STABLE_IDS)
.build()
it.adapter = ConcatAdapter(config, animeInfoAdapter, episodesHeaderAdapter, episodesAdapter)
// Skips directly to chapters list if navigated to from the library
it.post {
@ -303,7 +310,7 @@ class AnimeController :
binding.fastScroller.doOnLayout { scroller ->
scroller.updateLayoutParams<ViewGroup.MarginLayoutParams> {
topMargin = getMainAppBarHeight()
topMargin += getMainAppBarHeight()
}
}
@ -341,7 +348,6 @@ class AnimeController :
settingsSheet = EpisodesSettingsSheet(router, presenter) { group ->
if (group is EpisodesSettingsSheet.Filter.FilterGroup) {
updateFilterIconState()
episodesAdapter?.notifyDataSetChanged()
}
}
@ -641,7 +647,7 @@ class AnimeController :
}
}
}
animeInfoAdapter?.notifyDataSetChanged()
animeInfoAdapter?.update()
}
fun onCategoriesClick() {
@ -842,7 +848,7 @@ class AnimeController :
override fun deleteAnimeCover(anime: Anime) {
presenter.deleteCustomCover(anime)
animeInfoAdapter?.notifyDataSetChanged()
animeInfoAdapter?.notifyItemChanged(0, anime)
destroyActionModeIfNeeded()
}
@ -948,7 +954,7 @@ class AnimeController :
}
fun onSetCoverSuccess() {
animeInfoAdapter?.notifyDataSetChanged()
animeInfoAdapter?.notifyItemChanged(0, this)
(dialog as? AnimeFullCoverDialog)?.setImage(anime)
activity?.toast(R.string.cover_updated)
}
@ -1120,19 +1126,20 @@ class AnimeController :
val lastClickPosition = lastClickPositionStack.peek()!!
when {
lastClickPosition == -1 -> setSelection(position)
lastClickPosition > position ->
for (i in position until lastClickPosition)
setSelection(i)
lastClickPosition < position ->
for (i in lastClickPosition + 1..position)
setSelection(i)
lastClickPosition > position -> {
for (i in position until lastClickPosition) setSelection(i)
episodesAdapter?.notifyItemRangeChanged(position, lastClickPosition, position)
}
lastClickPosition < position -> {
for (i in lastClickPosition + 1..position) setSelection(i)
episodesAdapter?.notifyItemRangeChanged(lastClickPosition + 1, position, position)
}
else -> setSelection(position)
}
if (lastClickPosition != position) {
lastClickPositionStack.remove(position) // move to top if already exists
lastClickPositionStack.push(position)
}
episodesAdapter?.notifyDataSetChanged()
}
fun showSettingsSheet() {
@ -1145,7 +1152,6 @@ class AnimeController :
val adapter = episodesAdapter ?: return
val item = adapter.getItem(position) ?: return
adapter.toggleSelection(position)
adapter.notifyDataSetChanged()
if (adapter.isSelected(position)) {
selectedEpisodes.add(item)
} else {
@ -1292,11 +1298,11 @@ class AnimeController :
selectedEpisodes.clear()
for (i in 0..adapter.itemCount) {
adapter.toggleSelection(i)
adapter.notifyItemChanged(i, i)
}
selectedEpisodes.addAll(adapter.selectedPositions.mapNotNull { adapter.getItem(it) })
actionMode?.invalidate()
adapter.notifyDataSetChanged()
}
private fun markAsRead(episodes: List<EpisodeItem>) {
@ -1384,10 +1390,7 @@ class AnimeController :
fun onEpisodesDeleted(episodes: List<EpisodeItem>) {
// this is needed so the downloaded text gets removed from the item
episodes.forEach {
episodesAdapter?.updateItem(it)
}
launchUI {
episodesAdapter?.notifyDataSetChanged()
episodesAdapter?.updateItem(it, it)
}
}

View file

@ -763,6 +763,7 @@ class AnimePresenter(
fun setDisplayMode(mode: Int) {
anime.displayMode = mode
db.updateEpisodeFlags(anime).executeAsBlocking()
refreshEpisodes()
}
/**

View file

@ -30,6 +30,8 @@ class AnimeEpisodesHeaderAdapter(
override fun getItemCount(): Int = 1
override fun getItemId(position: Int): Long = hashCode().toLong()
override fun onBindViewHolder(holder: HeaderViewHolder, position: Int) {
holder.bind()
}
@ -37,13 +39,13 @@ class AnimeEpisodesHeaderAdapter(
fun setNumEpisodes(numEpisodes: Int) {
this.numEpisodes = numEpisodes
notifyDataSetChanged()
notifyItemChanged(0, this)
}
fun setHasActiveFilters(hasActiveFilters: Boolean) {
this.hasActiveFilters = hasActiveFilters
notifyDataSetChanged()
notifyItemChanged(0, this)
}
inner class HeaderViewHolder(private val view: View) : RecyclerView.ViewHolder(view) {

View file

@ -116,7 +116,7 @@ class EpisodesSettingsSheet(
}
initModels()
item.group.items.forEach { adapter.notifyItemChanged(it) }
adapter.notifyItemChanged(items.indexOf(item), item)
}
}
}
@ -158,18 +158,18 @@ class EpisodesSettingsSheet(
}
override fun onItemClicked(item: Item) {
item as Item.MultiStateGroup
val prevState = item.state
item.group.items.forEach {
(it as Item.MultiStateGroup).state =
items.forEachIndexed { i, multiSort ->
multiSort.state = if (multiSort == item) {
when (item.state) {
Item.MultiSort.SORT_NONE -> Item.MultiSort.SORT_ASC
Item.MultiSort.SORT_ASC -> Item.MultiSort.SORT_DESC
Item.MultiSort.SORT_DESC -> Item.MultiSort.SORT_ASC
else -> throw Exception("Unknown state")
}
} else {
Item.MultiSort.SORT_NONE
}
item.state = when (prevState) {
Item.MultiSort.SORT_NONE -> Item.MultiSort.SORT_ASC
Item.MultiSort.SORT_ASC -> Item.MultiSort.SORT_DESC
Item.MultiSort.SORT_DESC -> Item.MultiSort.SORT_ASC
else -> throw Exception("Unknown state")
}
adapter.notifyItemChanged(i, multiSort)
}
when (item) {
@ -180,8 +180,6 @@ class EpisodesSettingsSheet(
}
presenter.reverseSortOrder()
item.group.items.forEach { adapter.notifyItemChanged(it) }
}
}
}
@ -215,16 +213,16 @@ class EpisodesSettingsSheet(
item as Item.Radio
if (item.checked) return
item.group.items.forEach { (it as Item.Radio).checked = false }
item.checked = true
items.forEachIndexed { index, radio ->
radio.checked = item == radio
adapter.notifyItemChanged(index, radio)
}
when (item) {
displayTitle -> presenter.setDisplayMode(Anime.EPISODE_DISPLAY_NAME)
displayEpisodeNum -> presenter.setDisplayMode(Anime.EPISODE_DISPLAY_NUMBER)
else -> throw NotImplementedError("Unknown display mode")
}
item.group.items.forEach { adapter.notifyItemChanged(it) }
}
}
}

View file

@ -56,6 +56,8 @@ class AnimeInfoHeaderAdapter(
override fun getItemCount(): Int = 1
override fun getItemId(position: Int): Long = hashCode().toLong()
override fun onBindViewHolder(holder: HeaderViewHolder, position: Int) {
holder.bind()
}
@ -69,14 +71,16 @@ class AnimeInfoHeaderAdapter(
fun update(anime: Anime, source: AnimeSource) {
this.anime = anime
this.source = source
update()
}
notifyDataSetChanged()
fun update() {
notifyItemChanged(0, this)
}
fun setTrackingCount(trackCount: Int) {
this.trackCount = trackCount
notifyDataSetChanged()
update()
}
private fun updateCoverPosition() {

View file

@ -87,7 +87,6 @@ import eu.kanade.tachiyomi.ui.webview.WebViewActivity
import eu.kanade.tachiyomi.util.chapter.NoChaptersException
import eu.kanade.tachiyomi.util.hasCustomCover
import eu.kanade.tachiyomi.util.lang.launchIO
import eu.kanade.tachiyomi.util.lang.launchUI
import eu.kanade.tachiyomi.util.storage.getUriCompat
import eu.kanade.tachiyomi.util.system.logcat
import eu.kanade.tachiyomi.util.system.toShareIntent
@ -251,13 +250,21 @@ class MangaController :
if (manga == null || source == null) return
// Init RecyclerView and adapter
mangaInfoAdapter = MangaInfoHeaderAdapter(this, fromSource, binding.infoRecycler != null)
chaptersHeaderAdapter = MangaChaptersHeaderAdapter(this)
mangaInfoAdapter = MangaInfoHeaderAdapter(this, fromSource, binding.infoRecycler != null).apply {
setHasStableIds(true)
}
chaptersHeaderAdapter = MangaChaptersHeaderAdapter(this).apply {
setHasStableIds(true)
}
chaptersAdapter = ChaptersAdapter(this, view.context)
// Phone layout
binding.fullRecycler?.let {
it.adapter = ConcatAdapter(mangaInfoAdapter, chaptersHeaderAdapter, chaptersAdapter)
val config = ConcatAdapter.Config.Builder()
.setIsolateViewTypes(true)
.setStableIdMode(ConcatAdapter.Config.StableIdMode.SHARED_STABLE_IDS)
.build()
it.adapter = ConcatAdapter(config, mangaInfoAdapter, chaptersHeaderAdapter, chaptersAdapter)
// Skips directly to chapters list if navigated to from the library
it.post {
@ -282,7 +289,7 @@ class MangaController :
binding.fastScroller.doOnLayout { scroller ->
scroller.updateLayoutParams<ViewGroup.MarginLayoutParams> {
topMargin = getMainAppBarHeight()
topMargin += getMainAppBarHeight()
}
}
@ -320,7 +327,6 @@ class MangaController :
settingsSheet = ChaptersSettingsSheet(router, presenter) { group ->
if (group is ChaptersSettingsSheet.Filter.FilterGroup) {
updateFilterIconState()
chaptersAdapter?.notifyDataSetChanged()
}
}
@ -622,7 +628,7 @@ class MangaController :
}
}
}
mangaInfoAdapter?.notifyDataSetChanged()
mangaInfoAdapter?.update()
}
fun onCategoriesClick() {
@ -822,7 +828,7 @@ class MangaController :
override fun deleteMangaCover(manga: Manga) {
presenter.deleteCustomCover(manga)
mangaInfoAdapter?.notifyDataSetChanged()
mangaInfoAdapter?.notifyItemChanged(0, manga)
destroyActionModeIfNeeded()
}
@ -836,7 +842,7 @@ class MangaController :
}
fun onSetCoverSuccess() {
mangaInfoAdapter?.notifyDataSetChanged()
mangaInfoAdapter?.notifyItemChanged(0, this)
(dialog as? MangaFullCoverDialog)?.setImage(manga)
activity?.toast(R.string.cover_updated)
}
@ -947,19 +953,20 @@ class MangaController :
val lastClickPosition = lastClickPositionStack.peek()!!
when {
lastClickPosition == -1 -> setSelection(position)
lastClickPosition > position ->
for (i in position until lastClickPosition)
setSelection(i)
lastClickPosition < position ->
for (i in lastClickPosition + 1..position)
setSelection(i)
lastClickPosition > position -> {
for (i in position until lastClickPosition) setSelection(i)
chaptersAdapter?.notifyItemRangeChanged(position, lastClickPosition, position)
}
lastClickPosition < position -> {
for (i in lastClickPosition + 1..position) setSelection(i)
chaptersAdapter?.notifyItemRangeChanged(lastClickPosition + 1, position, position)
}
else -> setSelection(position)
}
if (lastClickPosition != position) {
lastClickPositionStack.remove(position) // move to top if already exists
lastClickPositionStack.push(position)
}
chaptersAdapter?.notifyDataSetChanged()
}
fun showSettingsSheet() {
@ -972,7 +979,6 @@ class MangaController :
val adapter = chaptersAdapter ?: return
val item = adapter.getItem(position) ?: return
adapter.toggleSelection(position)
adapter.notifyDataSetChanged()
if (adapter.isSelected(position)) {
selectedChapters.add(item)
} else {
@ -1105,11 +1111,11 @@ class MangaController :
selectedChapters.clear()
for (i in 0..adapter.itemCount) {
adapter.toggleSelection(i)
adapter.notifyItemChanged(i, i)
}
selectedChapters.addAll(adapter.selectedPositions.mapNotNull { adapter.getItem(it) })
actionMode?.invalidate()
adapter.notifyDataSetChanged()
}
private fun markAsRead(chapters: List<ChapterItem>) {
@ -1176,10 +1182,7 @@ class MangaController :
fun onChaptersDeleted(chapters: List<ChapterItem>) {
// this is needed so the downloaded text gets removed from the item
chapters.forEach {
chaptersAdapter?.updateItem(it)
}
launchUI {
chaptersAdapter?.notifyDataSetChanged()
chaptersAdapter?.updateItem(it, it)
}
}

View file

@ -718,6 +718,7 @@ class MangaPresenter(
fun setDisplayMode(mode: Int) {
manga.displayMode = mode
db.updateChapterFlags(manga).executeAsBlocking()
refreshChapters()
}
/**

View file

@ -116,7 +116,7 @@ class ChaptersSettingsSheet(
}
initModels()
item.group.items.forEach { adapter.notifyItemChanged(it) }
adapter.notifyItemChanged(items.indexOf(item), item)
}
}
}
@ -158,18 +158,18 @@ class ChaptersSettingsSheet(
}
override fun onItemClicked(item: Item) {
item as Item.MultiStateGroup
val prevState = item.state
item.group.items.forEach {
(it as Item.MultiStateGroup).state =
items.forEachIndexed { i, multiSort ->
multiSort.state = if (multiSort == item) {
when (item.state) {
Item.MultiSort.SORT_NONE -> Item.MultiSort.SORT_ASC
Item.MultiSort.SORT_ASC -> Item.MultiSort.SORT_DESC
Item.MultiSort.SORT_DESC -> Item.MultiSort.SORT_ASC
else -> throw Exception("Unknown state")
}
} else {
Item.MultiSort.SORT_NONE
}
item.state = when (prevState) {
Item.MultiSort.SORT_NONE -> Item.MultiSort.SORT_ASC
Item.MultiSort.SORT_ASC -> Item.MultiSort.SORT_DESC
Item.MultiSort.SORT_DESC -> Item.MultiSort.SORT_ASC
else -> throw Exception("Unknown state")
}
adapter.notifyItemChanged(i, multiSort)
}
when (item) {
@ -180,8 +180,6 @@ class ChaptersSettingsSheet(
}
presenter.reverseSortOrder()
item.group.items.forEach { adapter.notifyItemChanged(it) }
}
}
}
@ -215,16 +213,16 @@ class ChaptersSettingsSheet(
item as Item.Radio
if (item.checked) return
item.group.items.forEach { (it as Item.Radio).checked = false }
item.checked = true
items.forEachIndexed { index, radio ->
radio.checked = item == radio
adapter.notifyItemChanged(index, radio)
}
when (item) {
displayTitle -> presenter.setDisplayMode(Manga.CHAPTER_DISPLAY_NAME)
displayChapterNum -> presenter.setDisplayMode(Manga.CHAPTER_DISPLAY_NUMBER)
else -> throw NotImplementedError("Unknown display mode")
}
item.group.items.forEach { adapter.notifyItemChanged(it) }
}
}
}

View file

@ -30,20 +30,20 @@ class MangaChaptersHeaderAdapter(
override fun getItemCount(): Int = 1
override fun getItemId(position: Int): Long = hashCode().toLong()
override fun onBindViewHolder(holder: HeaderViewHolder, position: Int) {
holder.bind()
}
fun setNumChapters(numChapters: Int) {
this.numChapters = numChapters
notifyDataSetChanged()
notifyItemChanged(0, this)
}
fun setHasActiveFilters(hasActiveFilters: Boolean) {
this.hasActiveFilters = hasActiveFilters
notifyDataSetChanged()
notifyItemChanged(0, this)
}
inner class HeaderViewHolder(private val view: View) : RecyclerView.ViewHolder(view) {

View file

@ -56,6 +56,8 @@ class MangaInfoHeaderAdapter(
override fun getItemCount(): Int = 1
override fun getItemId(position: Int): Long = hashCode().toLong()
override fun onBindViewHolder(holder: HeaderViewHolder, position: Int) {
holder.bind()
}
@ -69,14 +71,16 @@ class MangaInfoHeaderAdapter(
fun update(manga: Manga, source: Source) {
this.manga = manga
this.source = source
update()
}
notifyDataSetChanged()
fun update() {
notifyItemChanged(0, this)
}
fun setTrackingCount(trackCount: Int) {
this.trackCount = trackCount
notifyDataSetChanged()
update()
}
private fun updateCoverPosition() {

View file

@ -105,7 +105,7 @@ class ReaderPresenter(
val chaptersForReader = when {
(preferences.skipRead() || preferences.skipFiltered()) -> {
val list = dbChapters.filterNot {
val filteredChapters = dbChapters.filterNot {
when {
preferences.skipRead() && it.read -> true
preferences.skipFiltered() -> {
@ -119,13 +119,12 @@ class ReaderPresenter(
else -> false
}
}
.toMutableList()
val find = list.find { it.id == chapterId }
if (find == null) {
list.add(selectedChapter)
if (filteredChapters.any { it.id == chapterId }) {
filteredChapters
} else {
filteredChapters + listOf(selectedChapter)
}
list
}
else -> dbChapters
}

View file

@ -167,17 +167,20 @@ inline fun ExtendedFloatingActionButton.shrinkOnScroll(recycler: RecyclerView):
*
* @param items List of strings that are shown as individual chips.
* @param onClick Optional on click listener for each chip.
* @param onLongClick Optional on long click listener for each chip.
*/
inline fun ChipGroup.setChips(
items: List<String>?,
noinline onClick: (item: String) -> Unit = {}
noinline onClick: ((item: String) -> Unit)? = null,
noinline onLongClick: ((item: String) -> Unit)? = null
) {
removeAllViews()
items?.forEach { item ->
val chip = Chip(context).apply {
text = item
setOnClickListener { onClick(item) }
if (onClick != null) { setOnClickListener { onClick(item) } }
if (onLongClick != null) { setOnLongClickListener { onLongClick(item); true } }
}
addView(chip)

View file

@ -64,8 +64,9 @@ class MangaSummaryView @JvmOverloads constructor(
}
fun setTags(items: List<String>?, onClick: (item: String) -> Unit) {
binding.tagChipsShrunk.setChips(items, onClick)
binding.tagChipsExpanded.setChips(items, onClick)
listOfNotNull(binding.tagChipsShrunk, binding.tagChipsExpanded).forEach { chips ->
chips.setChips(items, onClick) { tag -> context.copyToClipboard(tag, tag) }
}
}
private fun updateExpandState() = binding.apply {

View file

@ -774,8 +774,8 @@
<string name="page_list_empty_error">No pages found</string>
<string name="loader_not_implemented_error">Source not found</string>
<plurals name="missing_chapters_warning">
<item quantity="one">Skipping %d chapter, this could be because the source does not have them, or you are filtering them out</item>
<item quantity="other">Skipping %d chapters, this could be because the source does not have them, or you are filtering them out</item>
<item quantity="one">Skipping 1 chapter, either the source is missing it or it has been filtered out</item>
<item quantity="other">Skipping %d chapters, either the source is missing them or they have been filtered out</item>
</plurals>
<!-- Updates fragment -->

View file

@ -1,5 +1,5 @@
object BuildPluginsVersion {
const val AGP = "7.0.2"
const val AGP = "7.0.3"
const val KOTLIN = "1.5.31"
const val KOTLINTER = "3.6.0"
const val VERSIONS_PLUGIN = "0.39.0"