Edit manga status + edit local manga fixes
This commit is contained in:
parent
f1cb4c38a2
commit
5a67d8169d
@ -59,8 +59,8 @@ class MangaPutResolver : DefaultPutResolver<Manga>() {
|
||||
COL_DESCRIPTION to obj.originalDescription,
|
||||
COL_GENRE to obj.originalGenre,
|
||||
COL_TITLE to obj.originalTitle,
|
||||
COL_STATUS to obj.originalStatus,
|
||||
// SY <--
|
||||
COL_STATUS to obj.status,
|
||||
COL_THUMBNAIL_URL to obj.thumbnail_url,
|
||||
COL_FAVORITE to obj.favorite,
|
||||
COL_LAST_UPDATE to obj.last_update,
|
||||
|
@ -40,9 +40,11 @@ open class MangaImpl : Manga {
|
||||
override var genre: String?
|
||||
get() = if (favorite) customMangaManager.getManga(this)?.genre ?: ogGenre else ogGenre
|
||||
set(value) { ogGenre = value }
|
||||
// SY <--
|
||||
|
||||
override var status: Int = 0
|
||||
override var status: Int
|
||||
get() = if (favorite) customMangaManager.getManga(this)?.status?.takeUnless { it == 0 } ?: ogStatus else ogStatus
|
||||
set(value) { ogStatus = value }
|
||||
// SY <--
|
||||
|
||||
override var thumbnail_url: String? = null
|
||||
|
||||
@ -71,6 +73,8 @@ open class MangaImpl : Manga {
|
||||
private set
|
||||
var ogGenre: String? = null
|
||||
private set
|
||||
var ogStatus: Int = 0
|
||||
private set
|
||||
// SY <--
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
|
@ -13,6 +13,7 @@ import eu.kanade.tachiyomi.data.database.resolvers.MangaFlagsPutResolver
|
||||
import eu.kanade.tachiyomi.data.database.resolvers.MangaInfoPutResolver
|
||||
import eu.kanade.tachiyomi.data.database.resolvers.MangaLastUpdatedPutResolver
|
||||
import eu.kanade.tachiyomi.data.database.resolvers.MangaMigrationPutResolver
|
||||
import eu.kanade.tachiyomi.data.database.resolvers.MangaThumbnailPutResolver
|
||||
import eu.kanade.tachiyomi.data.database.resolvers.MangaTitlePutResolver
|
||||
import eu.kanade.tachiyomi.data.database.resolvers.MangaViewerPutResolver
|
||||
import eu.kanade.tachiyomi.data.database.tables.CategoryTable
|
||||
@ -102,6 +103,11 @@ interface MangaQueries : DbProvider {
|
||||
.`object`(manga)
|
||||
.withPutResolver(MangaMigrationPutResolver())
|
||||
.prepare()
|
||||
|
||||
fun updateMangaThumbnail(manga: Manga) = db.put()
|
||||
.`object`(manga)
|
||||
.withPutResolver(MangaThumbnailPutResolver())
|
||||
.prepare()
|
||||
// SY <--
|
||||
|
||||
fun insertManga(manga: Manga) = db.put().`object`(manga).prepare()
|
||||
|
@ -1,6 +1,5 @@
|
||||
package eu.kanade.tachiyomi.data.database.resolvers
|
||||
|
||||
import android.content.ContentValues
|
||||
import androidx.core.content.contentValuesOf
|
||||
import com.pushtorefresh.storio.sqlite.StorIOSQLite
|
||||
import com.pushtorefresh.storio.sqlite.operations.put.PutResolver
|
||||
@ -9,6 +8,7 @@ import com.pushtorefresh.storio.sqlite.queries.UpdateQuery
|
||||
import eu.kanade.tachiyomi.data.database.inTransactionReturn
|
||||
import eu.kanade.tachiyomi.data.database.models.Manga
|
||||
import eu.kanade.tachiyomi.data.database.tables.MangaTable
|
||||
import exh.util.nullIfZero
|
||||
|
||||
class MangaInfoPutResolver(val reset: Boolean = false) : PutResolver<Manga>() {
|
||||
|
||||
@ -31,15 +31,20 @@ class MangaInfoPutResolver(val reset: Boolean = false) : PutResolver<Manga>() {
|
||||
MangaTable.COL_GENRE to manga.originalGenre,
|
||||
MangaTable.COL_AUTHOR to manga.originalAuthor,
|
||||
MangaTable.COL_ARTIST to manga.originalArtist,
|
||||
MangaTable.COL_DESCRIPTION to manga.originalDescription
|
||||
MangaTable.COL_DESCRIPTION to manga.originalDescription,
|
||||
MangaTable.COL_STATUS to manga.originalStatus
|
||||
)
|
||||
|
||||
fun resetToContentValues(manga: Manga) = ContentValues(1).apply {
|
||||
val splitter = "▒ ▒∩▒"
|
||||
put(MangaTable.COL_TITLE, manga.title.split(splitter).last())
|
||||
put(MangaTable.COL_GENRE, manga.genre?.split(splitter)?.lastOrNull())
|
||||
put(MangaTable.COL_AUTHOR, manga.author?.split(splitter)?.lastOrNull())
|
||||
put(MangaTable.COL_ARTIST, manga.artist?.split(splitter)?.lastOrNull())
|
||||
put(MangaTable.COL_DESCRIPTION, manga.description?.split(splitter)?.lastOrNull())
|
||||
private fun resetToContentValues(manga: Manga) = contentValuesOf(
|
||||
MangaTable.COL_TITLE to manga.title.split(splitter).last(),
|
||||
MangaTable.COL_GENRE to manga.genre?.split(splitter)?.lastOrNull(),
|
||||
MangaTable.COL_AUTHOR to manga.author?.split(splitter)?.lastOrNull(),
|
||||
MangaTable.COL_ARTIST to manga.artist?.split(splitter)?.lastOrNull(),
|
||||
MangaTable.COL_DESCRIPTION to manga.description?.split(splitter)?.lastOrNull(),
|
||||
MangaTable.COL_STATUS to manga.status.nullIfZero()?.toString()?.split(splitter)?.lastOrNull()
|
||||
)
|
||||
|
||||
companion object {
|
||||
const val splitter = "▒ ▒∩▒"
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,32 @@
|
||||
package eu.kanade.tachiyomi.data.database.resolvers
|
||||
|
||||
import androidx.core.content.contentValuesOf
|
||||
import com.pushtorefresh.storio.sqlite.StorIOSQLite
|
||||
import com.pushtorefresh.storio.sqlite.operations.put.PutResolver
|
||||
import com.pushtorefresh.storio.sqlite.operations.put.PutResult
|
||||
import com.pushtorefresh.storio.sqlite.queries.UpdateQuery
|
||||
import eu.kanade.tachiyomi.data.database.inTransactionReturn
|
||||
import eu.kanade.tachiyomi.data.database.models.Manga
|
||||
import eu.kanade.tachiyomi.data.database.tables.MangaTable
|
||||
|
||||
// SY
|
||||
class MangaThumbnailPutResolver : PutResolver<Manga>() {
|
||||
|
||||
override fun performPut(db: StorIOSQLite, manga: Manga) = db.inTransactionReturn {
|
||||
val updateQuery = mapToUpdateQuery(manga)
|
||||
val contentValues = mapToContentValues(manga)
|
||||
|
||||
val numberOfRowsUpdated = db.lowLevel().update(updateQuery, contentValues)
|
||||
PutResult.newUpdateResult(numberOfRowsUpdated, updateQuery.table())
|
||||
}
|
||||
|
||||
fun mapToUpdateQuery(manga: Manga) = UpdateQuery.builder()
|
||||
.table(MangaTable.TABLE)
|
||||
.where("${MangaTable.COL_ID} = ?")
|
||||
.whereArgs(manga.id)
|
||||
.build()
|
||||
|
||||
fun mapToContentValues(manga: Manga) = contentValuesOf(
|
||||
MangaTable.COL_THUMBNAIL_URL to manga.thumbnail_url
|
||||
)
|
||||
}
|
@ -23,7 +23,7 @@ import uy.kohesive.injekt.injectLazy
|
||||
*
|
||||
* @param context the application context.
|
||||
*/
|
||||
class DownloadManager(/* SY private */ val context: Context) {
|
||||
class DownloadManager(private val context: Context) {
|
||||
|
||||
private val sourceManager: SourceManager by injectLazy()
|
||||
private val preferences: PreferencesHelper by injectLazy()
|
||||
|
@ -32,30 +32,15 @@ class CustomMangaManager(val context: Context) {
|
||||
val mangasJson = json.mangas ?: return mutableMapOf()
|
||||
return mangasJson.mapNotNull { mangaJson ->
|
||||
val id = mangaJson.id ?: return@mapNotNull null
|
||||
val manga = MangaImpl().apply {
|
||||
this.id = id
|
||||
title = mangaJson.title ?: ""
|
||||
author = mangaJson.author
|
||||
artist = mangaJson.artist
|
||||
description = mangaJson.description
|
||||
genre = mangaJson.genre?.joinToString(", ")
|
||||
}
|
||||
id to manga
|
||||
id to mangaJson.toManga()
|
||||
}.toMap().toMutableMap()
|
||||
}
|
||||
|
||||
fun saveMangaInfo(manga: MangaJson) {
|
||||
if (manga.title == null && manga.author == null && manga.artist == null && manga.description == null && manga.genre == null) {
|
||||
if (manga.title == null && manga.author == null && manga.artist == null && manga.description == null && manga.genre == null && manga.status == null) {
|
||||
customMangaMap.remove(manga.id!!)
|
||||
} else {
|
||||
customMangaMap[manga.id!!] = MangaImpl().apply {
|
||||
id = manga.id
|
||||
title = manga.title ?: ""
|
||||
author = manga.author
|
||||
artist = manga.artist
|
||||
description = manga.description
|
||||
genre = manga.genre?.joinToString(", ")
|
||||
}
|
||||
customMangaMap[manga.id!!] = manga.toManga()
|
||||
}
|
||||
saveCustomInfo()
|
||||
}
|
||||
@ -75,7 +60,8 @@ class CustomMangaManager(val context: Context) {
|
||||
author,
|
||||
artist,
|
||||
description,
|
||||
genre?.split(", ")
|
||||
genre?.split(", "),
|
||||
status
|
||||
)
|
||||
}
|
||||
|
||||
@ -91,9 +77,20 @@ class CustomMangaManager(val context: Context) {
|
||||
val author: String? = null,
|
||||
val artist: String? = null,
|
||||
val description: String? = null,
|
||||
val genre: List<String>? = null
|
||||
val genre: List<String>? = null,
|
||||
val status: Int? = null
|
||||
) {
|
||||
|
||||
fun toManga() = MangaImpl().apply {
|
||||
id = this@MangaJson.id
|
||||
title = this@MangaJson.title ?: ""
|
||||
author = this@MangaJson.author
|
||||
artist = this@MangaJson.artist
|
||||
description = this@MangaJson.description
|
||||
genre = this@MangaJson.genre?.joinToString(", ")
|
||||
status = this@MangaJson.status ?: 0
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (javaClass != other?.javaClass) return false
|
||||
|
@ -64,6 +64,12 @@ class LocalSource(private val context: Context) : CatalogueSource {
|
||||
val c = context.getString(R.string.app_name) + File.separator + "local"
|
||||
return DiskUtil.getExternalStorages(context).map { File(it.absolutePath, c) }
|
||||
}
|
||||
|
||||
// SY -->
|
||||
val json = Json {
|
||||
prettyPrint = true
|
||||
}
|
||||
// SY <--
|
||||
}
|
||||
|
||||
override val id = ID
|
||||
@ -151,19 +157,16 @@ class LocalSource(private val context: Context) : CatalogueSource {
|
||||
|
||||
// SY -->
|
||||
fun updateMangaInfo(manga: SManga) {
|
||||
val directory = getBaseDirectories(context).mapNotNull { File(it, manga.url) }.find {
|
||||
val directory = getBaseDirectories(context).map { File(it, manga.url) }.find {
|
||||
it.exists()
|
||||
} ?: return
|
||||
val json = Json {
|
||||
prettyPrint = true
|
||||
}
|
||||
val existingFileName = directory.listFiles()?.find { it.extension == "json" }?.name
|
||||
val file = File(directory, existingFileName ?: "info.json")
|
||||
file.writeText(json.encodeToString(manga.toJson()))
|
||||
}
|
||||
|
||||
private fun SManga.toJson(): MangaJson {
|
||||
return MangaJson(title, author, artist, description, genre?.split(", ")?.toTypedArray())
|
||||
return MangaJson(title, author, artist, description, genre?.split(", "), status)
|
||||
}
|
||||
|
||||
@Serializable
|
||||
@ -172,7 +175,8 @@ class LocalSource(private val context: Context) : CatalogueSource {
|
||||
val author: String?,
|
||||
val artist: String?,
|
||||
val description: String?,
|
||||
val genre: Array<String>?
|
||||
val genre: List<String>?,
|
||||
val status: Int
|
||||
) {
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
|
@ -35,6 +35,8 @@ interface SManga : Serializable {
|
||||
get() = (this as? MangaImpl)?.ogDesc ?: description
|
||||
val originalGenre: String?
|
||||
get() = (this as? MangaImpl)?.ogGenre ?: genre
|
||||
val originalStatus: Int
|
||||
get() = (this as? MangaImpl)?.ogStatus ?: status
|
||||
// SY <--
|
||||
|
||||
fun copyFrom(other: SManga) {
|
||||
|
@ -5,6 +5,7 @@ import android.content.Context
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.text.InputType
|
||||
import android.widget.ArrayAdapter
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.os.bundleOf
|
||||
import androidx.core.view.children
|
||||
@ -23,10 +24,12 @@ import eu.kanade.tachiyomi.data.glide.GlideApp
|
||||
import eu.kanade.tachiyomi.data.glide.toMangaThumbnail
|
||||
import eu.kanade.tachiyomi.databinding.EditMangaDialogBinding
|
||||
import eu.kanade.tachiyomi.source.LocalSource
|
||||
import eu.kanade.tachiyomi.source.model.SManga
|
||||
import eu.kanade.tachiyomi.ui.base.controller.DialogController
|
||||
import eu.kanade.tachiyomi.util.lang.chop
|
||||
import eu.kanade.tachiyomi.util.system.getResourceColor
|
||||
import eu.kanade.tachiyomi.util.system.toast
|
||||
import exh.util.dropBlank
|
||||
import exh.util.trimOrNull
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
@ -84,6 +87,36 @@ class EditMangaDialog : DialogController {
|
||||
|
||||
val isLocal = manga.source == LocalSource.ID
|
||||
|
||||
val statusAdapter: ArrayAdapter<String> = ArrayAdapter(
|
||||
context,
|
||||
android.R.layout.simple_spinner_item,
|
||||
listOf(
|
||||
R.string.manga,
|
||||
R.string.ongoing,
|
||||
R.string.completed,
|
||||
R.string.licensed,
|
||||
R.string.publication_complete,
|
||||
R.string.hiatus,
|
||||
R.string.cancelled
|
||||
).map { context.getString(it) }
|
||||
)
|
||||
|
||||
binding.status.adapter = statusAdapter
|
||||
if (manga.status != manga.originalStatus) {
|
||||
binding.status.setSelection(
|
||||
when (manga.status) {
|
||||
SManga.UNKNOWN -> 0
|
||||
SManga.ONGOING -> 1
|
||||
SManga.COMPLETED -> 2
|
||||
SManga.LICENSED -> 3
|
||||
SManga.PUBLICATION_COMPLETE -> 4
|
||||
SManga.HIATUS -> 5
|
||||
SManga.CANCELLED -> 6
|
||||
else -> 0
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
if (isLocal) {
|
||||
if (manga.title != manga.url) {
|
||||
binding.title.setText(manga.title)
|
||||
@ -92,7 +125,7 @@ class EditMangaDialog : DialogController {
|
||||
binding.mangaAuthor.setText(manga.author.orEmpty())
|
||||
binding.mangaArtist.setText(manga.artist.orEmpty())
|
||||
binding.mangaDescription.setText(manga.description.orEmpty())
|
||||
binding.mangaGenresTags.setChips(manga.getGenres().orEmpty())
|
||||
binding.mangaGenresTags.setChips(manga.getGenres().orEmpty().dropBlank())
|
||||
} else {
|
||||
if (manga.title != manga.originalTitle) {
|
||||
binding.title.append(manga.title)
|
||||
@ -160,11 +193,23 @@ class EditMangaDialog : DialogController {
|
||||
|
||||
private fun onPositiveButtonClick() {
|
||||
infoController.presenter.updateMangaInfo(
|
||||
context,
|
||||
binding.title.text.toString(),
|
||||
binding.mangaAuthor.text.toString(),
|
||||
binding.mangaArtist.text.toString(),
|
||||
binding.mangaDescription.text.toString(),
|
||||
binding.mangaGenresTags.getTextStrings(),
|
||||
binding.status.selectedItemPosition.let {
|
||||
when (it) {
|
||||
1 -> SManga.ONGOING
|
||||
2 -> SManga.COMPLETED
|
||||
3 -> SManga.LICENSED
|
||||
4 -> SManga.PUBLICATION_COMPLETE
|
||||
5 -> SManga.HIATUS
|
||||
6 -> SManga.CANCELLED
|
||||
else -> null
|
||||
}
|
||||
},
|
||||
customCoverUri,
|
||||
willResetCover
|
||||
)
|
||||
|
@ -706,7 +706,7 @@ class MangaController :
|
||||
|
||||
// SY -->
|
||||
fun changeCover() {
|
||||
if (manga?.favorite == true) {
|
||||
if (manga?.favorite == true || source?.id == LocalSource.ID) {
|
||||
val intent = Intent(Intent.ACTION_GET_CONTENT)
|
||||
intent.type = "image/*"
|
||||
startActivityForResult(
|
||||
@ -1036,7 +1036,7 @@ class MangaController :
|
||||
val uri = data.data ?: return
|
||||
if (editMangaDialog != null) editMangaDialog?.updateCover(uri)
|
||||
else {
|
||||
presenter.editCoverWithStream(uri)
|
||||
presenter.editCoverWithStream(activity, uri)
|
||||
}
|
||||
} catch (error: IOException) {
|
||||
activity.toast(R.string.notification_cover_update_failed)
|
||||
|
@ -314,11 +314,13 @@ class MangaPresenter(
|
||||
|
||||
// SY -->
|
||||
fun updateMangaInfo(
|
||||
context: Context,
|
||||
title: String?,
|
||||
author: String?,
|
||||
artist: String?,
|
||||
description: String?,
|
||||
tags: List<String>?,
|
||||
status: Int?,
|
||||
uri: Uri?,
|
||||
resetCover: Boolean = false
|
||||
) {
|
||||
@ -329,7 +331,8 @@ class MangaPresenter(
|
||||
manga.description = description?.trimOrNull()
|
||||
val tagsString = tags?.joinToString()
|
||||
manga.genre = if (tags.isNullOrEmpty()) null else tagsString?.trim()
|
||||
LocalSource(downloadManager.context).updateMangaInfo(manga)
|
||||
manga.status = status ?: 0
|
||||
(sourceManager.get(LocalSource.ID) as LocalSource).updateMangaInfo(manga)
|
||||
db.updateMangaInfo(manga).executeAsBlocking()
|
||||
} else {
|
||||
val genre = if (!tags.isNullOrEmpty() && tags.joinToString() != manga.originalGenre) {
|
||||
@ -343,13 +346,14 @@ class MangaPresenter(
|
||||
author?.trimOrNull(),
|
||||
artist?.trimOrNull(),
|
||||
description?.trimOrNull(),
|
||||
genre
|
||||
genre,
|
||||
status.takeUnless { it == manga.originalStatus }
|
||||
)
|
||||
customMangaManager.saveMangaInfo(manga)
|
||||
}
|
||||
|
||||
if (uri != null) {
|
||||
editCoverWithStream(uri)
|
||||
editCoverWithStream(context, uri)
|
||||
} else if (resetCover) {
|
||||
coverCache.deleteCustomCover(manga)
|
||||
manga.updateCoverLastModified(db)
|
||||
@ -377,11 +381,14 @@ class MangaPresenter(
|
||||
}
|
||||
}
|
||||
|
||||
fun editCoverWithStream(uri: Uri): Boolean {
|
||||
val inputStream = downloadManager.context.contentResolver.openInputStream(uri) ?: return false
|
||||
fun editCoverWithStream(context: Context, uri: Uri): Boolean {
|
||||
val inputStream = context.contentResolver.openInputStream(uri) ?: return false
|
||||
if (manga.source == LocalSource.ID) {
|
||||
LocalSource.updateCover(downloadManager.context, manga, inputStream)
|
||||
manga.updateCoverLastModified(db)
|
||||
val cover = LocalSource.updateCover(context, manga, inputStream)
|
||||
if (manga.thumbnail_url.isNullOrBlank() && cover != null) {
|
||||
manga.thumbnail_url = cover.absolutePath
|
||||
db.updateMangaThumbnail(manga).executeAsBlocking()
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
|
@ -22,16 +22,31 @@
|
||||
android:src="@mipmap/ic_launcher"/>
|
||||
</FrameLayout>
|
||||
|
||||
<Button
|
||||
android:id="@+id/reset_cover"
|
||||
android:layout_width="wrap_content"
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
style="@style/Theme.Widget.Button.FilledAccent"
|
||||
android:textAllCaps="false"
|
||||
android:layout_gravity="center"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:layout_marginStart="16dp"
|
||||
android:text="@string/reset_cover" />
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center">
|
||||
|
||||
<Button
|
||||
android:id="@+id/reset_cover"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
style="@style/Theme.Widget.Button.FilledAccent"
|
||||
android:textAllCaps="false"
|
||||
android:layout_gravity="center"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:layout_marginStart="16dp"
|
||||
android:text="@string/reset_cover" />
|
||||
|
||||
<Spinner
|
||||
android:id="@+id/status"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
style="@style/Theme.Widget.TextInputLayout.OutlinedBox.Dense"
|
||||
|
Loading…
x
Reference in New Issue
Block a user