Resolve conflicts
This commit is contained in:
@@ -84,19 +84,19 @@ afterEvaluate {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation('com.github.KotatsuApp:kotatsu-parsers:85bfe42ddf') {
|
implementation('com.github.KotatsuApp:kotatsu-parsers:e2308214a7') {
|
||||||
exclude group: 'org.json', module: 'json'
|
exclude group: 'org.json', module: 'json'
|
||||||
}
|
}
|
||||||
|
|
||||||
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4'
|
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4'
|
||||||
|
|
||||||
implementation 'androidx.core:core-ktx:1.8.0'
|
implementation 'androidx.core:core-ktx:1.8.0'
|
||||||
implementation 'androidx.activity:activity-ktx:1.5.0'
|
implementation 'androidx.activity:activity-ktx:1.5.1'
|
||||||
implementation 'androidx.fragment:fragment-ktx:1.5.0'
|
implementation 'androidx.fragment:fragment-ktx:1.5.1'
|
||||||
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.5.0'
|
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.5.1'
|
||||||
implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.5.0'
|
implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.5.1'
|
||||||
implementation 'androidx.lifecycle:lifecycle-service:2.5.0'
|
implementation 'androidx.lifecycle:lifecycle-service:2.5.1'
|
||||||
implementation 'androidx.lifecycle:lifecycle-process:2.5.0'
|
implementation 'androidx.lifecycle:lifecycle-process:2.5.1'
|
||||||
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
|
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
|
||||||
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0'
|
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0'
|
||||||
implementation 'androidx.recyclerview:recyclerview:1.2.1'
|
implementation 'androidx.recyclerview:recyclerview:1.2.1'
|
||||||
@@ -106,11 +106,11 @@ dependencies {
|
|||||||
implementation 'androidx.biometric:biometric-ktx:1.2.0-alpha04'
|
implementation 'androidx.biometric:biometric-ktx:1.2.0-alpha04'
|
||||||
implementation 'com.google.android.material:material:1.7.0-alpha03'
|
implementation 'com.google.android.material:material:1.7.0-alpha03'
|
||||||
//noinspection LifecycleAnnotationProcessorWithJava8
|
//noinspection LifecycleAnnotationProcessorWithJava8
|
||||||
kapt 'androidx.lifecycle:lifecycle-compiler:2.5.0'
|
kapt 'androidx.lifecycle:lifecycle-compiler:2.5.1'
|
||||||
|
|
||||||
implementation 'androidx.room:room-runtime:2.4.2'
|
implementation 'androidx.room:room-runtime:2.4.3'
|
||||||
implementation 'androidx.room:room-ktx:2.4.2'
|
implementation 'androidx.room:room-ktx:2.4.3'
|
||||||
kapt 'androidx.room:room-compiler:2.4.2'
|
kapt 'androidx.room:room-compiler:2.4.3'
|
||||||
|
|
||||||
implementation 'com.squareup.okhttp3:okhttp:4.10.0'
|
implementation 'com.squareup.okhttp3:okhttp:4.10.0'
|
||||||
implementation 'com.squareup.okhttp3:okhttp-dnsoverhttps:4.9.3'
|
implementation 'com.squareup.okhttp3:okhttp-dnsoverhttps:4.9.3'
|
||||||
@@ -145,7 +145,7 @@ dependencies {
|
|||||||
|
|
||||||
androidTestImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.6.4'
|
androidTestImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.6.4'
|
||||||
|
|
||||||
androidTestImplementation 'androidx.room:room-testing:2.4.2'
|
androidTestImplementation 'androidx.room:room-testing:2.4.3'
|
||||||
androidTestImplementation 'com.squareup.moshi:moshi-kotlin:1.13.0'
|
androidTestImplementation 'com.squareup.moshi:moshi-kotlin:1.13.0'
|
||||||
|
|
||||||
androidTestImplementation 'com.google.dagger:hilt-android-testing:2.42'
|
androidTestImplementation 'com.google.dagger:hilt-android-testing:2.42'
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import android.widget.Checkable
|
|||||||
import androidx.annotation.AttrRes
|
import androidx.annotation.AttrRes
|
||||||
import androidx.appcompat.widget.AppCompatImageView
|
import androidx.appcompat.widget.AppCompatImageView
|
||||||
import androidx.core.os.ParcelCompat
|
import androidx.core.os.ParcelCompat
|
||||||
|
import androidx.customview.view.AbsSavedState
|
||||||
|
|
||||||
class CheckableImageView @JvmOverloads constructor(
|
class CheckableImageView @JvmOverloads constructor(
|
||||||
context: Context,
|
context: Context,
|
||||||
@@ -73,7 +74,7 @@ class CheckableImageView @JvmOverloads constructor(
|
|||||||
fun onCheckedChanged(view: CheckableImageView, isChecked: Boolean)
|
fun onCheckedChanged(view: CheckableImageView, isChecked: Boolean)
|
||||||
}
|
}
|
||||||
|
|
||||||
private class SavedState : BaseSavedState {
|
private class SavedState : AbsSavedState {
|
||||||
|
|
||||||
val isChecked: Boolean
|
val isChecked: Boolean
|
||||||
|
|
||||||
@@ -81,7 +82,7 @@ class CheckableImageView @JvmOverloads constructor(
|
|||||||
isChecked = checked
|
isChecked = checked
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(source: Parcel) : super(source) {
|
constructor(source: Parcel, classLoader: ClassLoader?) : super(source, classLoader) {
|
||||||
isChecked = ParcelCompat.readBoolean(source)
|
isChecked = ParcelCompat.readBoolean(source)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -91,9 +92,10 @@ class CheckableImageView @JvmOverloads constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
@Suppress("unused")
|
||||||
@JvmField
|
@JvmField
|
||||||
val CREATOR: Creator<SavedState> = object : Creator<SavedState> {
|
val CREATOR: Creator<SavedState> = object : Creator<SavedState> {
|
||||||
override fun createFromParcel(`in`: Parcel) = SavedState(`in`)
|
override fun createFromParcel(`in`: Parcel) = SavedState(`in`, SavedState::class.java.classLoader)
|
||||||
|
|
||||||
override fun newArray(size: Int): Array<SavedState?> = arrayOfNulls(size)
|
override fun newArray(size: Int): Array<SavedState?> = arrayOfNulls(size)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,34 @@
|
|||||||
package org.koitharu.kotatsu.core.model
|
package org.koitharu.kotatsu.core.model
|
||||||
|
|
||||||
|
import androidx.core.os.LocaleListCompat
|
||||||
import org.koitharu.kotatsu.parsers.model.Manga
|
import org.koitharu.kotatsu.parsers.model.Manga
|
||||||
import org.koitharu.kotatsu.parsers.util.mapToSet
|
import org.koitharu.kotatsu.parsers.util.mapToSet
|
||||||
|
import org.koitharu.kotatsu.parsers.util.toTitleCase
|
||||||
|
import org.koitharu.kotatsu.utils.ext.iterator
|
||||||
|
|
||||||
fun Collection<Manga>.ids() = mapToSet { it.id }
|
fun Collection<Manga>.ids() = mapToSet { it.id }
|
||||||
|
|
||||||
|
fun Manga.getPreferredBranch(history: MangaHistory?): String? {
|
||||||
|
val ch = chapters
|
||||||
|
if (ch.isNullOrEmpty()) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
if (history != null) {
|
||||||
|
val currentChapter = ch.find { it.id == history.chapterId }
|
||||||
|
if (currentChapter != null) {
|
||||||
|
return currentChapter.branch
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val groups = ch.groupBy { it.branch }
|
||||||
|
for (locale in LocaleListCompat.getAdjustedDefault()) {
|
||||||
|
var language = locale.getDisplayLanguage(locale).toTitleCase(locale)
|
||||||
|
if (groups.containsKey(language)) {
|
||||||
|
return language
|
||||||
|
}
|
||||||
|
language = locale.getDisplayName(locale).toTitleCase(locale)
|
||||||
|
if (groups.containsKey(language)) {
|
||||||
|
return language
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return groups.maxByOrNull { it.value.size }?.key
|
||||||
|
}
|
||||||
@@ -29,6 +29,7 @@ import org.koitharu.kotatsu.bookmarks.domain.Bookmark
|
|||||||
import org.koitharu.kotatsu.bookmarks.ui.adapter.BookmarksAdapter
|
import org.koitharu.kotatsu.bookmarks.ui.adapter.BookmarksAdapter
|
||||||
import org.koitharu.kotatsu.core.model.MangaHistory
|
import org.koitharu.kotatsu.core.model.MangaHistory
|
||||||
import org.koitharu.kotatsu.databinding.FragmentDetailsBinding
|
import org.koitharu.kotatsu.databinding.FragmentDetailsBinding
|
||||||
|
import org.koitharu.kotatsu.details.ui.model.ChapterListItem
|
||||||
import org.koitharu.kotatsu.details.ui.scrobbling.ScrobblingInfoBottomSheet
|
import org.koitharu.kotatsu.details.ui.scrobbling.ScrobblingInfoBottomSheet
|
||||||
import org.koitharu.kotatsu.history.domain.PROGRESS_NONE
|
import org.koitharu.kotatsu.history.domain.PROGRESS_NONE
|
||||||
import org.koitharu.kotatsu.image.ui.ImageActivity
|
import org.koitharu.kotatsu.image.ui.ImageActivity
|
||||||
@@ -75,6 +76,7 @@ class DetailsFragment :
|
|||||||
viewModel.bookmarks.observe(viewLifecycleOwner, ::onBookmarksChanged)
|
viewModel.bookmarks.observe(viewLifecycleOwner, ::onBookmarksChanged)
|
||||||
viewModel.scrobblingInfo.observe(viewLifecycleOwner, ::onScrobblingInfoChanged)
|
viewModel.scrobblingInfo.observe(viewLifecycleOwner, ::onScrobblingInfoChanged)
|
||||||
viewModel.description.observe(viewLifecycleOwner, ::onDescriptionChanged)
|
viewModel.description.observe(viewLifecycleOwner, ::onDescriptionChanged)
|
||||||
|
viewModel.chapters.observe(viewLifecycleOwner, ::onChaptersChanged)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onItemClick(item: Bookmark, view: View) {
|
override fun onItemClick(item: Bookmark, view: View) {
|
||||||
@@ -111,18 +113,6 @@ class DetailsFragment :
|
|||||||
ratingBar.isVisible = false
|
ratingBar.isVisible = false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Info containers
|
|
||||||
val chapters = manga.chapters
|
|
||||||
if (chapters.isNullOrEmpty()) {
|
|
||||||
infoLayout.textViewChapters.isVisible = false
|
|
||||||
} else {
|
|
||||||
infoLayout.textViewChapters.isVisible = true
|
|
||||||
infoLayout.textViewChapters.text = resources.getQuantityString(
|
|
||||||
R.plurals.chapters,
|
|
||||||
chapters.size,
|
|
||||||
chapters.size,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
when (manga.state) {
|
when (manga.state) {
|
||||||
MangaState.FINISHED -> {
|
MangaState.FINISHED -> {
|
||||||
infoLayout.textViewState.apply {
|
infoLayout.textViewState.apply {
|
||||||
@@ -163,6 +153,20 @@ class DetailsFragment :
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun onChaptersChanged(chapters: List<ChapterListItem>?) {
|
||||||
|
val infoLayout = binding.infoLayout
|
||||||
|
if (chapters.isNullOrEmpty()) {
|
||||||
|
infoLayout.textViewChapters.isVisible = false
|
||||||
|
} else {
|
||||||
|
infoLayout.textViewChapters.isVisible = true
|
||||||
|
infoLayout.textViewChapters.text = resources.getQuantityString(
|
||||||
|
R.plurals.chapters,
|
||||||
|
chapters.size,
|
||||||
|
chapters.size,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun onDescriptionChanged(description: CharSequence?) {
|
private fun onDescriptionChanged(description: CharSequence?) {
|
||||||
if (description.isNullOrBlank()) {
|
if (description.isNullOrBlank()) {
|
||||||
binding.textViewDescription.setText(R.string.no_description)
|
binding.textViewDescription.setText(R.string.no_description)
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
package org.koitharu.kotatsu.details.ui
|
package org.koitharu.kotatsu.details.ui
|
||||||
|
|
||||||
import androidx.core.os.LocaleListCompat
|
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
import org.koitharu.kotatsu.base.domain.MangaDataRepository
|
import org.koitharu.kotatsu.base.domain.MangaDataRepository
|
||||||
import org.koitharu.kotatsu.base.domain.MangaIntent
|
import org.koitharu.kotatsu.base.domain.MangaIntent
|
||||||
import org.koitharu.kotatsu.core.model.MangaHistory
|
import org.koitharu.kotatsu.core.model.MangaHistory
|
||||||
|
import org.koitharu.kotatsu.core.model.getPreferredBranch
|
||||||
import org.koitharu.kotatsu.core.parser.MangaRepository
|
import org.koitharu.kotatsu.core.parser.MangaRepository
|
||||||
import org.koitharu.kotatsu.core.prefs.AppSettings
|
import org.koitharu.kotatsu.core.prefs.AppSettings
|
||||||
import org.koitharu.kotatsu.details.ui.model.ChapterListItem
|
import org.koitharu.kotatsu.details.ui.model.ChapterListItem
|
||||||
@@ -16,9 +16,6 @@ import org.koitharu.kotatsu.parsers.exception.NotFoundException
|
|||||||
import org.koitharu.kotatsu.parsers.model.Manga
|
import org.koitharu.kotatsu.parsers.model.Manga
|
||||||
import org.koitharu.kotatsu.parsers.model.MangaChapter
|
import org.koitharu.kotatsu.parsers.model.MangaChapter
|
||||||
import org.koitharu.kotatsu.parsers.model.MangaSource
|
import org.koitharu.kotatsu.parsers.model.MangaSource
|
||||||
import org.koitharu.kotatsu.parsers.util.mapToSet
|
|
||||||
import org.koitharu.kotatsu.parsers.util.toTitleCase
|
|
||||||
import org.koitharu.kotatsu.utils.ext.iterator
|
|
||||||
import org.koitharu.kotatsu.utils.ext.printStackTraceDebug
|
import org.koitharu.kotatsu.utils.ext.printStackTraceDebug
|
||||||
|
|
||||||
class MangaDetailsDelegate(
|
class MangaDetailsDelegate(
|
||||||
@@ -46,12 +43,7 @@ class MangaDetailsDelegate(
|
|||||||
manga = mangaRepositoryFactory.create(manga.source).getDetails(manga)
|
manga = mangaRepositoryFactory.create(manga.source).getDetails(manga)
|
||||||
// find default branch
|
// find default branch
|
||||||
val hist = historyRepository.getOne(manga)
|
val hist = historyRepository.getOne(manga)
|
||||||
selectedBranch.value = if (hist != null) {
|
selectedBranch.value = manga.getPreferredBranch(hist)
|
||||||
val currentChapter = manga.chapters?.find { it.id == hist.chapterId }
|
|
||||||
if (currentChapter != null) currentChapter.branch else predictBranch(manga.chapters)
|
|
||||||
} else {
|
|
||||||
predictBranch(manga.chapters)
|
|
||||||
}
|
|
||||||
mangaData.value = manga
|
mangaData.value = manga
|
||||||
relatedManga.value = runCatching {
|
relatedManga.value = runCatching {
|
||||||
if (manga.source == MangaSource.LOCAL) {
|
if (manga.source == MangaSource.LOCAL) {
|
||||||
@@ -92,7 +84,7 @@ class MangaDetailsDelegate(
|
|||||||
val dateFormat = settings.getDateFormat()
|
val dateFormat = settings.getDateFormat()
|
||||||
val currentIndex = chapters.indexOfFirst { it.id == currentId }
|
val currentIndex = chapters.indexOfFirst { it.id == currentId }
|
||||||
val firstNewIndex = chapters.size - newCount
|
val firstNewIndex = chapters.size - newCount
|
||||||
val downloadedIds = downloadedChapters?.mapToSet { it.id }
|
val downloadedIds = downloadedChapters?.mapTo(HashSet(downloadedChapters.size)) { it.id }
|
||||||
for (i in chapters.indices) {
|
for (i in chapters.indices) {
|
||||||
val chapter = chapters[i]
|
val chapter = chapters[i]
|
||||||
if (chapter.branch != branch) {
|
if (chapter.branch != branch) {
|
||||||
@@ -107,6 +99,9 @@ class MangaDetailsDelegate(
|
|||||||
dateFormat = dateFormat,
|
dateFormat = dateFormat,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
if (result.size < chapters.size / 2) {
|
||||||
|
result.trimToSize()
|
||||||
|
}
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -162,24 +157,9 @@ class MangaDetailsDelegate(
|
|||||||
}
|
}
|
||||||
result.sortBy { it.chapter.number }
|
result.sortBy { it.chapter.number }
|
||||||
}
|
}
|
||||||
|
if (result.size < sourceChapters.size / 2) {
|
||||||
|
result.trimToSize()
|
||||||
|
}
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun predictBranch(chapters: List<MangaChapter>?): String? {
|
|
||||||
if (chapters.isNullOrEmpty()) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
val groups = chapters.groupBy { it.branch }
|
|
||||||
for (locale in LocaleListCompat.getAdjustedDefault()) {
|
|
||||||
var language = locale.getDisplayLanguage(locale).toTitleCase(locale)
|
|
||||||
if (groups.containsKey(language)) {
|
|
||||||
return language
|
|
||||||
}
|
|
||||||
language = locale.getDisplayName(locale).toTitleCase(locale)
|
|
||||||
if (groups.containsKey(language)) {
|
|
||||||
return language
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return groups.maxByOrNull { it.value.size }?.key
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,24 @@
|
|||||||
package org.koitharu.kotatsu.details.ui.model
|
package org.koitharu.kotatsu.details.ui.model
|
||||||
|
|
||||||
|
import java.text.DateFormat
|
||||||
import org.koitharu.kotatsu.parsers.model.MangaChapter
|
import org.koitharu.kotatsu.parsers.model.MangaChapter
|
||||||
|
|
||||||
class ChapterListItem(
|
class ChapterListItem(
|
||||||
val chapter: MangaChapter,
|
val chapter: MangaChapter,
|
||||||
val flags: Int,
|
val flags: Int,
|
||||||
val uploadDate: String?,
|
private val uploadDateMs: Long,
|
||||||
|
private val dateFormat: DateFormat,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
|
var uploadDate: String? = null
|
||||||
|
private set
|
||||||
|
get() {
|
||||||
|
if (field != null) return field
|
||||||
|
if (uploadDateMs == 0L) return null
|
||||||
|
field = dateFormat.format(uploadDateMs)
|
||||||
|
return field
|
||||||
|
}
|
||||||
|
|
||||||
val status: Int
|
val status: Int
|
||||||
get() = flags and MASK_STATUS
|
get() = flags and MASK_STATUS
|
||||||
|
|
||||||
@@ -32,7 +43,8 @@ class ChapterListItem(
|
|||||||
|
|
||||||
if (chapter != other.chapter) return false
|
if (chapter != other.chapter) return false
|
||||||
if (flags != other.flags) return false
|
if (flags != other.flags) return false
|
||||||
if (uploadDate != other.uploadDate) return false
|
if (uploadDateMs != other.uploadDateMs) return false
|
||||||
|
if (dateFormat != other.dateFormat) return false
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@@ -40,7 +52,8 @@ class ChapterListItem(
|
|||||||
override fun hashCode(): Int {
|
override fun hashCode(): Int {
|
||||||
var result = chapter.hashCode()
|
var result = chapter.hashCode()
|
||||||
result = 31 * result + flags
|
result = 31 * result + flags
|
||||||
result = 31 * result + (uploadDate?.hashCode() ?: 0)
|
result = 31 * result + uploadDateMs.hashCode()
|
||||||
|
result = 31 * result + dateFormat.hashCode()
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
package org.koitharu.kotatsu.details.ui.model
|
package org.koitharu.kotatsu.details.ui.model
|
||||||
|
|
||||||
|
import java.text.DateFormat
|
||||||
import org.koitharu.kotatsu.details.ui.model.ChapterListItem.Companion.FLAG_CURRENT
|
import org.koitharu.kotatsu.details.ui.model.ChapterListItem.Companion.FLAG_CURRENT
|
||||||
import org.koitharu.kotatsu.details.ui.model.ChapterListItem.Companion.FLAG_DOWNLOADED
|
import org.koitharu.kotatsu.details.ui.model.ChapterListItem.Companion.FLAG_DOWNLOADED
|
||||||
import org.koitharu.kotatsu.details.ui.model.ChapterListItem.Companion.FLAG_MISSING
|
import org.koitharu.kotatsu.details.ui.model.ChapterListItem.Companion.FLAG_MISSING
|
||||||
import org.koitharu.kotatsu.details.ui.model.ChapterListItem.Companion.FLAG_NEW
|
import org.koitharu.kotatsu.details.ui.model.ChapterListItem.Companion.FLAG_NEW
|
||||||
import org.koitharu.kotatsu.details.ui.model.ChapterListItem.Companion.FLAG_UNREAD
|
import org.koitharu.kotatsu.details.ui.model.ChapterListItem.Companion.FLAG_UNREAD
|
||||||
import org.koitharu.kotatsu.parsers.model.MangaChapter
|
import org.koitharu.kotatsu.parsers.model.MangaChapter
|
||||||
import java.text.DateFormat
|
|
||||||
|
|
||||||
fun MangaChapter.toListItem(
|
fun MangaChapter.toListItem(
|
||||||
isCurrent: Boolean,
|
isCurrent: Boolean,
|
||||||
@@ -25,6 +25,7 @@ fun MangaChapter.toListItem(
|
|||||||
return ChapterListItem(
|
return ChapterListItem(
|
||||||
chapter = this,
|
chapter = this,
|
||||||
flags = flags,
|
flags = flags,
|
||||||
uploadDate = if (uploadDate != 0L) dateFormat.format(uploadDate) else null
|
uploadDateMs = uploadDate,
|
||||||
|
dateFormat = dateFormat,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -5,8 +5,8 @@ import android.content.res.TypedArray
|
|||||||
import android.os.Parcel
|
import android.os.Parcel
|
||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
import android.util.AttributeSet
|
import android.util.AttributeSet
|
||||||
import android.view.View
|
|
||||||
import androidx.core.content.withStyledAttributes
|
import androidx.core.content.withStyledAttributes
|
||||||
|
import androidx.customview.view.AbsSavedState
|
||||||
import androidx.preference.Preference
|
import androidx.preference.Preference
|
||||||
import androidx.preference.PreferenceViewHolder
|
import androidx.preference.PreferenceViewHolder
|
||||||
import com.google.android.material.slider.Slider
|
import com.google.android.material.slider.Slider
|
||||||
@@ -40,11 +40,11 @@ class SliderPreference @JvmOverloads constructor(
|
|||||||
attrs,
|
attrs,
|
||||||
R.styleable.SliderPreference,
|
R.styleable.SliderPreference,
|
||||||
defStyleAttr,
|
defStyleAttr,
|
||||||
defStyleRes
|
defStyleRes,
|
||||||
) {
|
) {
|
||||||
valueFrom = getFloat(
|
valueFrom = getFloat(
|
||||||
R.styleable.SliderPreference_android_valueFrom,
|
R.styleable.SliderPreference_android_valueFrom,
|
||||||
valueFrom.toFloat()
|
valueFrom.toFloat(),
|
||||||
).toInt()
|
).toInt()
|
||||||
valueTo =
|
valueTo =
|
||||||
getFloat(R.styleable.SliderPreference_android_valueTo, valueTo.toFloat()).toInt()
|
getFloat(R.styleable.SliderPreference_android_valueTo, valueTo.toFloat()).toInt()
|
||||||
@@ -117,7 +117,7 @@ class SliderPreference @JvmOverloads constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class SavedState : View.BaseSavedState {
|
private class SavedState : AbsSavedState {
|
||||||
|
|
||||||
val valueFrom: Int
|
val valueFrom: Int
|
||||||
val valueTo: Int
|
val valueTo: Int
|
||||||
@@ -134,7 +134,7 @@ class SliderPreference @JvmOverloads constructor(
|
|||||||
this.currentValue = currentValue
|
this.currentValue = currentValue
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(source: Parcel) : super(source) {
|
constructor(source: Parcel, classLoader: ClassLoader?) : super(source, classLoader) {
|
||||||
valueFrom = source.readInt()
|
valueFrom = source.readInt()
|
||||||
valueTo = source.readInt()
|
valueTo = source.readInt()
|
||||||
currentValue = source.readInt()
|
currentValue = source.readInt()
|
||||||
@@ -148,12 +148,13 @@ class SliderPreference @JvmOverloads constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
@Suppress("unused")
|
||||||
@JvmField
|
@JvmField
|
||||||
val CREATOR: Parcelable.Creator<SavedState> = object : Parcelable.Creator<SavedState> {
|
val CREATOR: Parcelable.Creator<SavedState> = object : Parcelable.Creator<SavedState> {
|
||||||
override fun createFromParcel(`in`: Parcel) = SavedState(`in`)
|
override fun createFromParcel(`in`: Parcel) = SavedState(`in`, SavedState::class.java.classLoader)
|
||||||
|
|
||||||
override fun newArray(size: Int): Array<SavedState?> = arrayOfNulls(size)
|
override fun newArray(size: Int): Array<SavedState?> = arrayOfNulls(size)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,8 +2,10 @@ package org.koitharu.kotatsu.tracker.domain
|
|||||||
|
|
||||||
import androidx.annotation.VisibleForTesting
|
import androidx.annotation.VisibleForTesting
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
import org.koitharu.kotatsu.core.model.getPreferredBranch
|
||||||
import org.koitharu.kotatsu.core.parser.MangaRepository
|
import org.koitharu.kotatsu.core.parser.MangaRepository
|
||||||
import org.koitharu.kotatsu.core.prefs.AppSettings
|
import org.koitharu.kotatsu.core.prefs.AppSettings
|
||||||
|
import org.koitharu.kotatsu.history.domain.HistoryRepository
|
||||||
import org.koitharu.kotatsu.parsers.model.Manga
|
import org.koitharu.kotatsu.parsers.model.Manga
|
||||||
import org.koitharu.kotatsu.tracker.domain.model.MangaTracking
|
import org.koitharu.kotatsu.tracker.domain.model.MangaTracking
|
||||||
import org.koitharu.kotatsu.tracker.domain.model.MangaUpdates
|
import org.koitharu.kotatsu.tracker.domain.model.MangaUpdates
|
||||||
@@ -13,6 +15,7 @@ import org.koitharu.kotatsu.tracker.work.TrackingItem
|
|||||||
class Tracker @Inject constructor(
|
class Tracker @Inject constructor(
|
||||||
private val settings: AppSettings,
|
private val settings: AppSettings,
|
||||||
private val repository: TrackingRepository,
|
private val repository: TrackingRepository,
|
||||||
|
private val historyRepository: HistoryRepository,
|
||||||
private val channels: TrackerNotificationChannels,
|
private val channels: TrackerNotificationChannels,
|
||||||
private val mangaRepositoryFactory: MangaRepository.Factory,
|
private val mangaRepositoryFactory: MangaRepository.Factory,
|
||||||
) {
|
) {
|
||||||
@@ -70,7 +73,7 @@ class Tracker @Inject constructor(
|
|||||||
|
|
||||||
suspend fun fetchUpdates(track: MangaTracking, commit: Boolean): MangaUpdates {
|
suspend fun fetchUpdates(track: MangaTracking, commit: Boolean): MangaUpdates {
|
||||||
val manga = mangaRepositoryFactory.create(track.manga.source).getDetails(track.manga)
|
val manga = mangaRepositoryFactory.create(track.manga.source).getDetails(track.manga)
|
||||||
val updates = compare(track, manga)
|
val updates = compare(track, manga, getBranch(manga))
|
||||||
if (commit) {
|
if (commit) {
|
||||||
repository.saveUpdates(updates)
|
repository.saveUpdates(updates)
|
||||||
}
|
}
|
||||||
@@ -80,7 +83,7 @@ class Tracker @Inject constructor(
|
|||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
suspend fun checkUpdates(manga: Manga, commit: Boolean): MangaUpdates {
|
suspend fun checkUpdates(manga: Manga, commit: Boolean): MangaUpdates {
|
||||||
val track = repository.getTrack(manga)
|
val track = repository.getTrack(manga)
|
||||||
val updates = compare(track, manga)
|
val updates = compare(track, manga, getBranch(manga))
|
||||||
if (commit) {
|
if (commit) {
|
||||||
repository.saveUpdates(updates)
|
repository.saveUpdates(updates)
|
||||||
}
|
}
|
||||||
@@ -92,15 +95,20 @@ class Tracker @Inject constructor(
|
|||||||
repository.deleteTrack(mangaId)
|
repository.deleteTrack(mangaId)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private suspend fun getBranch(manga: Manga): String? {
|
||||||
|
val history = historyRepository.getOne(manga)
|
||||||
|
return manga.getPreferredBranch(history)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The main functionality of tracker: check new chapters in [manga] comparing to the [track]
|
* The main functionality of tracker: check new chapters in [manga] comparing to the [track]
|
||||||
*/
|
*/
|
||||||
private fun compare(track: MangaTracking, manga: Manga): MangaUpdates {
|
private fun compare(track: MangaTracking, manga: Manga, branch: String?): MangaUpdates {
|
||||||
if (track.isEmpty()) {
|
if (track.isEmpty()) {
|
||||||
// first check or manga was empty on last check
|
// first check or manga was empty on last check
|
||||||
return MangaUpdates(manga, emptyList(), isValid = false)
|
return MangaUpdates(manga, emptyList(), isValid = false)
|
||||||
}
|
}
|
||||||
val chapters = requireNotNull(manga.chapters)
|
val chapters = requireNotNull(manga.getChapters(branch))
|
||||||
val newChapters = chapters.takeLastWhile { x -> x.id != track.lastChapterId }
|
val newChapters = chapters.takeLastWhile { x -> x.id != track.lastChapterId }
|
||||||
return when {
|
return when {
|
||||||
newChapters.isEmpty() -> {
|
newChapters.isEmpty() -> {
|
||||||
|
|||||||
@@ -17,7 +17,8 @@ fun Throwable.getDisplayMessage(resources: Resources): String = when (this) {
|
|||||||
is AuthRequiredException -> resources.getString(R.string.auth_required)
|
is AuthRequiredException -> resources.getString(R.string.auth_required)
|
||||||
is CloudFlareProtectedException -> resources.getString(R.string.captcha_required)
|
is CloudFlareProtectedException -> resources.getString(R.string.captcha_required)
|
||||||
is ActivityNotFoundException,
|
is ActivityNotFoundException,
|
||||||
is UnsupportedOperationException, -> resources.getString(R.string.operation_not_supported)
|
is UnsupportedOperationException,
|
||||||
|
-> resources.getString(R.string.operation_not_supported)
|
||||||
is UnsupportedFileException -> resources.getString(R.string.text_file_not_supported)
|
is UnsupportedFileException -> resources.getString(R.string.text_file_not_supported)
|
||||||
is FileNotFoundException -> resources.getString(R.string.file_not_found)
|
is FileNotFoundException -> resources.getString(R.string.file_not_found)
|
||||||
is EmptyHistoryException -> resources.getString(R.string.history_is_empty)
|
is EmptyHistoryException -> resources.getString(R.string.history_is_empty)
|
||||||
@@ -40,5 +41,6 @@ fun Throwable.isReportable(): Boolean {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun Throwable.report(message: String?) {
|
fun Throwable.report(message: String?) {
|
||||||
CaughtException(this, message).sendWithAcra()
|
val exception = CaughtException(this, message)
|
||||||
|
exception.sendWithAcra()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,17 +14,18 @@ import org.koitharu.kotatsu.R
|
|||||||
import org.koitharu.kotatsu.base.domain.MangaIntent
|
import org.koitharu.kotatsu.base.domain.MangaIntent
|
||||||
import org.koitharu.kotatsu.history.domain.HistoryRepository
|
import org.koitharu.kotatsu.history.domain.HistoryRepository
|
||||||
import org.koitharu.kotatsu.parsers.model.Manga
|
import org.koitharu.kotatsu.parsers.model.Manga
|
||||||
|
import org.koitharu.kotatsu.parsers.util.replaceWith
|
||||||
import org.koitharu.kotatsu.utils.ext.requireBitmap
|
import org.koitharu.kotatsu.utils.ext.requireBitmap
|
||||||
|
|
||||||
class RecentListFactory(
|
class RecentListFactory(
|
||||||
private val context: Context,
|
private val context: Context,
|
||||||
private val historyRepository: HistoryRepository,
|
private val historyRepository: HistoryRepository,
|
||||||
private val coil: ImageLoader
|
private val coil: ImageLoader,
|
||||||
) : RemoteViewsService.RemoteViewsFactory {
|
) : RemoteViewsService.RemoteViewsFactory {
|
||||||
|
|
||||||
private val dataSet = ArrayList<Manga>()
|
private val dataSet = ArrayList<Manga>()
|
||||||
private val transformation = RoundedCornersTransformation(
|
private val transformation = RoundedCornersTransformation(
|
||||||
context.resources.getDimension(R.dimen.appwidget_corner_radius_inner)
|
context.resources.getDimension(R.dimen.appwidget_corner_radius_inner),
|
||||||
)
|
)
|
||||||
private val coverSize = Size(
|
private val coverSize = Size(
|
||||||
context.resources.getDimensionPixelSize(R.dimen.widget_cover_width),
|
context.resources.getDimensionPixelSize(R.dimen.widget_cover_width),
|
||||||
@@ -38,9 +39,8 @@ class RecentListFactory(
|
|||||||
override fun getItemId(position: Int) = dataSet[position].id
|
override fun getItemId(position: Int) = dataSet[position].id
|
||||||
|
|
||||||
override fun onDataSetChanged() {
|
override fun onDataSetChanged() {
|
||||||
dataSet.clear()
|
|
||||||
val data = runBlocking { historyRepository.getList(0, 10) }
|
val data = runBlocking { historyRepository.getList(0, 10) }
|
||||||
dataSet.addAll(data)
|
dataSet.replaceWith(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun hasStableIds() = true
|
override fun hasStableIds() = true
|
||||||
@@ -54,7 +54,7 @@ class RecentListFactory(
|
|||||||
.data(item.coverUrl)
|
.data(item.coverUrl)
|
||||||
.size(coverSize)
|
.size(coverSize)
|
||||||
.transformations(transformation)
|
.transformations(transformation)
|
||||||
.build()
|
.build(),
|
||||||
).requireBitmap()
|
).requireBitmap()
|
||||||
}.onSuccess { cover ->
|
}.onSuccess { cover ->
|
||||||
views.setImageViewBitmap(R.id.imageView_cover, cover)
|
views.setImageViewBitmap(R.id.imageView_cover, cover)
|
||||||
@@ -72,4 +72,4 @@ class RecentListFactory(
|
|||||||
override fun getViewTypeCount() = 1
|
override fun getViewTypeCount() = 1
|
||||||
|
|
||||||
override fun onDestroy() = Unit
|
override fun onDestroy() = Unit
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import org.koitharu.kotatsu.base.domain.MangaIntent
|
|||||||
import org.koitharu.kotatsu.core.prefs.AppWidgetConfig
|
import org.koitharu.kotatsu.core.prefs.AppWidgetConfig
|
||||||
import org.koitharu.kotatsu.favourites.domain.FavouritesRepository
|
import org.koitharu.kotatsu.favourites.domain.FavouritesRepository
|
||||||
import org.koitharu.kotatsu.parsers.model.Manga
|
import org.koitharu.kotatsu.parsers.model.Manga
|
||||||
|
import org.koitharu.kotatsu.parsers.util.replaceWith
|
||||||
import org.koitharu.kotatsu.utils.ext.requireBitmap
|
import org.koitharu.kotatsu.utils.ext.requireBitmap
|
||||||
|
|
||||||
class ShelfListFactory(
|
class ShelfListFactory(
|
||||||
@@ -27,7 +28,7 @@ class ShelfListFactory(
|
|||||||
private val dataSet = ArrayList<Manga>()
|
private val dataSet = ArrayList<Manga>()
|
||||||
private val config = AppWidgetConfig(context, widgetId)
|
private val config = AppWidgetConfig(context, widgetId)
|
||||||
private val transformation = RoundedCornersTransformation(
|
private val transformation = RoundedCornersTransformation(
|
||||||
context.resources.getDimension(R.dimen.appwidget_corner_radius_inner)
|
context.resources.getDimension(R.dimen.appwidget_corner_radius_inner),
|
||||||
)
|
)
|
||||||
private val coverSize = Size(
|
private val coverSize = Size(
|
||||||
context.resources.getDimensionPixelSize(R.dimen.widget_cover_width),
|
context.resources.getDimensionPixelSize(R.dimen.widget_cover_width),
|
||||||
@@ -41,7 +42,6 @@ class ShelfListFactory(
|
|||||||
override fun getItemId(position: Int) = dataSet[position].id
|
override fun getItemId(position: Int) = dataSet[position].id
|
||||||
|
|
||||||
override fun onDataSetChanged() {
|
override fun onDataSetChanged() {
|
||||||
dataSet.clear()
|
|
||||||
val data = runBlocking {
|
val data = runBlocking {
|
||||||
val category = config.categoryId
|
val category = config.categoryId
|
||||||
if (category == 0L) {
|
if (category == 0L) {
|
||||||
@@ -50,7 +50,7 @@ class ShelfListFactory(
|
|||||||
favouritesRepository.getManga(category)
|
favouritesRepository.getManga(category)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
dataSet.addAll(data)
|
dataSet.replaceWith(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun hasStableIds() = true
|
override fun hasStableIds() = true
|
||||||
@@ -65,7 +65,7 @@ class ShelfListFactory(
|
|||||||
.data(item.coverUrl)
|
.data(item.coverUrl)
|
||||||
.size(coverSize)
|
.size(coverSize)
|
||||||
.transformations(transformation)
|
.transformations(transformation)
|
||||||
.build()
|
.build(),
|
||||||
).requireBitmap()
|
).requireBitmap()
|
||||||
}.onSuccess { cover ->
|
}.onSuccess { cover ->
|
||||||
views.setImageViewBitmap(R.id.imageView_cover, cover)
|
views.setImageViewBitmap(R.id.imageView_cover, cover)
|
||||||
|
|||||||
Reference in New Issue
Block a user