UI/Functionality for custom info done

Local Manga is up next
This commit is contained in:
Jay 2020-01-20 01:04:36 -08:00
parent a94d455e79
commit c92b2bb203
44 changed files with 530 additions and 294 deletions

View File

@ -211,7 +211,7 @@ dependencies {
implementation 'me.zhanghai.android.systemuihelper:library:1.0.0' implementation 'me.zhanghai.android.systemuihelper:library:1.0.0'
implementation 'com.nightlynexus.viewstatepageradapter:viewstatepageradapter:1.1.0' implementation 'com.nightlynexus.viewstatepageradapter:viewstatepageradapter:1.1.0'
implementation 'com.github.mthli:Slice:v1.2' implementation 'com.github.mthli:Slice:v1.2'
implementation 'me.gujun.android.taggroup:library:1.4@aar' implementation 'com.github.kizitonwose:AndroidTagGroup:1.6.0'
implementation 'com.github.chrisbanes:PhotoView:2.3.0' implementation 'com.github.chrisbanes:PhotoView:2.3.0'
implementation 'com.github.carlosesco:DirectionalViewPager:a844dbca0a' implementation 'com.github.carlosesco:DirectionalViewPager:a844dbca0a'

View File

@ -14,7 +14,7 @@ object MangaTypeAdapter {
write { write {
beginArray() beginArray()
value(it.url) value(it.url)
value(it.trueTitle()) value(it.originalTitle())
value(it.source) value(it.source)
value(it.viewer) value(it.viewer)
value(it.chapter_flags) value(it.chapter_flags)

View File

@ -36,15 +36,12 @@ open class MangaImpl : Manga {
override var hide_title: Boolean = false override var hide_title: Boolean = false
var last_cover_fetch: Long = 0
override fun copyFrom(other: SManga) { override fun copyFrom(other: SManga) {
if (((other is MangaImpl && (other as MangaImpl)::title.isInitialized) if ((other is MangaImpl && (other as MangaImpl)::title.isInitialized && other.title != title)) {
|| other !is MangaImpl) && other.title != title) { title = if (currentTitle() != originalTitle()) {
title = if (customTitle() != trueTitle()) { val customTitle = currentTitle()
val customTitle = customTitle()
val trueTitle = other.title val trueTitle = other.title
"${customTitle}≡§${trueTitle}" "${customTitle}${SManga.splitter}${trueTitle}"
} else other.title } else other.title
} }
super.copyFrom(other) super.copyFrom(other)
@ -64,4 +61,14 @@ open class MangaImpl : Manga {
return url.hashCode() return url.hashCode()
} }
companion object {
private var lastCoverFetch:MutableMap<Long, Long> = mutableMapOf()
fun setLastCoverFetch(id: Long, time: Long) {
lastCoverFetch[id] = time
}
fun getLastCoverFetch(id: Long) = lastCoverFetch[id] ?: 0
}
} }

View File

@ -92,6 +92,16 @@ interface MangaQueries : DbProvider {
.withPutResolver(MangaTitlePutResolver()) .withPutResolver(MangaTitlePutResolver())
.prepare() .prepare()
fun updateMangaInfo(manga: Manga) = db.put()
.`object`(manga)
.withPutResolver(MangaInfoPutResolver())
.prepare()
fun resetMangaInfo(manga: Manga) = db.put()
.`object`(manga)
.withPutResolver(MangaInfoPutResolver(true))
.prepare()
fun deleteManga(manga: Manga) = db.delete().`object`(manga).prepare() fun deleteManga(manga: Manga) = db.delete().`object`(manga).prepare()
fun deleteMangas(mangas: List<Manga>) = db.delete().objects(mangas).prepare() fun deleteMangas(mangas: List<Manga>) = db.delete().objects(mangas).prepare()

View File

@ -0,0 +1,45 @@
package eu.kanade.tachiyomi.data.database.resolvers
import android.content.ContentValues
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
class MangaInfoPutResolver(val reset:Boolean = false): PutResolver<Manga>() {
override fun performPut(db: StorIOSQLite, manga: Manga) = db.inTransactionReturn {
val updateQuery = mapToUpdateQuery(manga)
val contentValues = if (reset) resetToContentValues(manga) else 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) = ContentValues(1).apply {
put(MangaTable.COL_TITLE, manga.title)
put(MangaTable.COL_GENRE, manga.genre)
put(MangaTable.COL_AUTHOR, manga.author)
put(MangaTable.COL_ARTIST, manga.artist)
put(MangaTable.COL_DESCRIPTION, manga.description)
}
fun resetToContentValues(manga: Manga) = ContentValues(1).apply {
put(MangaTable.COL_TITLE, manga.originalTitle())
put(MangaTable.COL_GENRE, manga.originalGenres())
put(MangaTable.COL_AUTHOR, manga.originalAuthor())
put(MangaTable.COL_ARTIST, manga.originalArtist())
put(MangaTable.COL_DESCRIPTION, manga.originalDesc())
}
}

View File

@ -106,7 +106,7 @@ internal class DownloadNotifier(private val context: Context) {
NotificationReceiver.pauseDownloadsPendingBroadcast(context)) NotificationReceiver.pauseDownloadsPendingBroadcast(context))
} }
val title = download.manga.customTitle().chop(15) val title = download.manga.currentTitle().chop(15)
val quotedTitle = Pattern.quote(title) val quotedTitle = Pattern.quote(title)
val chapter = download.chapter.name.replaceFirst("$quotedTitle[\\s]*[-]*[\\s]*".toRegex(RegexOption.IGNORE_CASE), "") val chapter = download.chapter.name.replaceFirst("$quotedTitle[\\s]*[-]*[\\s]*".toRegex(RegexOption.IGNORE_CASE), "")
setContentTitle("$title - $chapter".chop(30)) setContentTitle("$title - $chapter".chop(30))
@ -161,7 +161,7 @@ internal class DownloadNotifier(private val context: Context) {
} }
// Create notification. // Create notification.
with(notification) { with(notification) {
val title = download.manga.customTitle().chop(15) val title = download.manga.currentTitle().chop(15)
val quotedTitle = Pattern.quote(title) val quotedTitle = Pattern.quote(title)
val chapter = download.chapter.name.replaceFirst("$quotedTitle[\\s]*[-]*[\\s]*".toRegex(RegexOption.IGNORE_CASE), "") val chapter = download.chapter.name.replaceFirst("$quotedTitle[\\s]*[-]*[\\s]*".toRegex(RegexOption.IGNORE_CASE), "")
setContentTitle("$title - $chapter".chop(30)) setContentTitle("$title - $chapter".chop(30))

View File

@ -178,7 +178,7 @@ class DownloadProvider(private val context: Context) {
* @param manga the manga to query. * @param manga the manga to query.
*/ */
fun getMangaDirName(manga: Manga): String { fun getMangaDirName(manga: Manga): String {
return DiskUtil.buildValidFilename(manga.trueTitle()) return DiskUtil.buildValidFilename(manga.originalTitle())
} }
/** /**

View File

@ -36,7 +36,7 @@ object LibraryUpdateRanker {
fun lexicographicRanking(): Comparator<Manga> { fun lexicographicRanking(): Comparator<Manga> {
return Comparator { mangaFirst: Manga, return Comparator { mangaFirst: Manga,
mangaSecond: Manga -> mangaSecond: Manga ->
compareValues(mangaFirst.customTitle(), mangaSecond.customTitle()) compareValues(mangaFirst.currentTitle(), mangaSecond.currentTitle())
} }
} }

View File

@ -20,6 +20,7 @@ import eu.kanade.tachiyomi.data.database.models.Category
import eu.kanade.tachiyomi.data.database.models.Chapter import eu.kanade.tachiyomi.data.database.models.Chapter
import eu.kanade.tachiyomi.data.database.models.LibraryManga import eu.kanade.tachiyomi.data.database.models.LibraryManga
import eu.kanade.tachiyomi.data.database.models.Manga import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.data.database.models.MangaImpl
import eu.kanade.tachiyomi.data.download.DownloadManager import eu.kanade.tachiyomi.data.download.DownloadManager
import eu.kanade.tachiyomi.data.download.DownloadService import eu.kanade.tachiyomi.data.download.DownloadService
import eu.kanade.tachiyomi.data.glide.GlideApp import eu.kanade.tachiyomi.data.glide.GlideApp
@ -406,7 +407,7 @@ class LibraryUpdateService(
manga.copyFrom(networkManga) manga.copyFrom(networkManga)
db.insertManga(manga).executeAsBlocking() db.insertManga(manga).executeAsBlocking()
coverCache.deleteFromCache(manga.thumbnail_url) coverCache.deleteFromCache(manga.thumbnail_url)
manga.last_cover_fetch = Date().time MangaImpl.setLastCoverFetch(manga.id!!, Date().time)
manga manga
} }
.onErrorReturn { manga } .onErrorReturn { manga }
@ -461,7 +462,7 @@ class LibraryUpdateService(
*/ */
private fun showProgressNotification(manga: Manga, current: Int, total: Int) { private fun showProgressNotification(manga: Manga, current: Int, total: Int) {
notificationManager.notify(Notifications.ID_LIBRARY_PROGRESS, progressNotification notificationManager.notify(Notifications.ID_LIBRARY_PROGRESS, progressNotification
.setContentTitle(manga.customTitle()) .setContentTitle(manga.currentTitle())
.setProgress(total, current, false) .setProgress(total, current, false)
.build()) .build())
} }
@ -487,7 +488,7 @@ class LibraryUpdateService(
} }
catch (e: Exception) { } catch (e: Exception) { }
setGroupAlertBehavior(GROUP_ALERT_SUMMARY) setGroupAlertBehavior(GROUP_ALERT_SUMMARY)
setContentTitle(manga.customTitle()) setContentTitle(manga.currentTitle())
color = ContextCompat.getColor(this@LibraryUpdateService, R.color.colorAccentLight) color = ContextCompat.getColor(this@LibraryUpdateService, R.color.colorAccentLight)
val chaptersNames = if (chapterNames.size > 5) { val chaptersNames = if (chapterNames.size > 5) {
"${chapterNames.take(4).joinToString(", ")}, " + "${chapterNames.take(4).joinToString(", ")}, " +
@ -527,11 +528,11 @@ class LibraryUpdateService(
.notification_new_chapters_text, .notification_new_chapters_text,
updates.size, updates.size)) updates.size, updates.size))
setStyle(NotificationCompat.BigTextStyle().bigText(updates.joinToString("\n") { setStyle(NotificationCompat.BigTextStyle().bigText(updates.joinToString("\n") {
it.first.customTitle().chop(45) it.first.currentTitle().chop(45)
})) }))
} }
else { else {
setContentText(updates.first().first.customTitle().chop(45)) setContentText(updates.first().first.currentTitle().chop(45))
} }
priority = NotificationCompat.PRIORITY_HIGH priority = NotificationCompat.PRIORITY_HIGH
setGroup(Notifications.GROUP_NEW_CHAPTERS) setGroup(Notifications.GROUP_NEW_CHAPTERS)

View File

@ -11,7 +11,6 @@ import info.debatty.java.stringsimilarity.NormalizedLevenshtein
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
import kotlinx.coroutines.async
import kotlinx.coroutines.supervisorScope import kotlinx.coroutines.supervisorScope
import rx.schedulers.Schedulers import rx.schedulers.Schedulers
import uy.kohesive.injekt.injectLazy import uy.kohesive.injekt.injectLazy
@ -65,7 +64,7 @@ class SmartSearchEngine(parentContext: CoroutineContext,
return@supervisorScope listOf(SearchEntry(searchResults.mangas.first(), 0.0)) return@supervisorScope listOf(SearchEntry(searchResults.mangas.first(), 0.0))
searchResults.mangas.map { searchResults.mangas.map {
val normalizedDistance = normalizedLevenshtein.similarity(title, it.trueTitle()) val normalizedDistance = normalizedLevenshtein.similarity(title, it.originalTitle())
SearchEntry(it, normalizedDistance) SearchEntry(it, normalizedDistance)
}.filter { (_, normalizedDistance) -> }.filter { (_, normalizedDistance) ->
normalizedDistance >= MIN_NORMAL_ELIGIBLE_THRESHOLD normalizedDistance >= MIN_NORMAL_ELIGIBLE_THRESHOLD

View File

@ -161,7 +161,7 @@ class LocalSource(private val context: Context) : CatalogueSource {
} else { } else {
chapterFile.nameWithoutExtension chapterFile.nameWithoutExtension
} }
val chapNameCut = chapName.replace(manga.trueTitle(), "", true).trim(' ', '-', '_') val chapNameCut = chapName.replace(manga.originalTitle(), "", true).trim(' ', '-', '_')
name = if (chapNameCut.isEmpty()) chapName else chapNameCut name = if (chapNameCut.isEmpty()) chapName else chapNameCut
date_upload = chapterFile.lastModified() date_upload = chapterFile.lastModified()
ChapterRecognition.parseChapterNumber(this, manga) ChapterRecognition.parseChapterNumber(this, manga)

View File

@ -22,28 +22,84 @@ interface SManga : Serializable {
var initialized: Boolean var initialized: Boolean
fun customTitle(): String { fun currentTitle(): String {
val splitTitle = title.split("") val splitTitle = title.split(splitter)
return splitTitle.first() return splitTitle.first()
} }
fun trueTitle(): String { fun originalTitle(): String {
val splitTitle = title.split("") val splitTitle = title.split(splitter)
return splitTitle.last() return splitTitle.last()
} }
fun currentGenres(): String? {
val splitGenre = genre?.split(splitter) ?: return null
return splitGenre.first()
}
fun originalGenres(): String? {
val splitGenre = genre?.split(splitter) ?: return null
return splitGenre.last()
}
fun currentDesc(): String? {
val splitDesc = description?.split(splitter) ?: return null
return splitDesc.first()
}
fun originalDesc(): String? {
val splitDesc = description?.split(splitter) ?: return null
return splitDesc.last()
}
fun currentAuthor(): String? {
val splitAuth = author?.split(splitter) ?: return null
return splitAuth.first()
}
fun originalAuthor(): String? {
val splitAuth = author?.split(splitter) ?: return null
return splitAuth.last()
}
fun currentArtist(): String? {
val splitArtist = artist?.split(splitter) ?: return null
return splitArtist.first()
}
fun originalArtist(): String? {
val splitArtist = artist?.split(splitter) ?: return null
return splitArtist.last()
}
fun copyFrom(other: SManga) { fun copyFrom(other: SManga) {
if (other.author != null) if (other.author != null)
author = other.author author = if (currentAuthor() != originalAuthor()) {
val current = currentAuthor()
val og = other.author
"${current}$splitter${og}"
} else other.author
if (other.artist != null) if (other.artist != null)
artist = other.artist artist = if (currentArtist() != originalArtist()) {
val current = currentArtist()
val og = other.artist
"${current}$splitter${og}"
} else other.artist
if (other.description != null) if (other.description != null)
description = other.description description = if (currentDesc() != originalDesc()) {
val current = currentDesc()
val og = other.description
"${current}$splitter${og}"
} else other.description
if (other.genre != null) if (other.genre != null)
genre = other.genre genre = if (currentGenres() != originalGenres()) {
val current = currentGenres()
val og = other.genre
"${current}$splitter${og}"
} else other.genre
if (other.thumbnail_url != null) if (other.thumbnail_url != null)
thumbnail_url = other.thumbnail_url thumbnail_url = other.thumbnail_url
@ -59,6 +115,7 @@ interface SManga : Serializable {
const val ONGOING = 1 const val ONGOING = 1
const val COMPLETED = 2 const val COMPLETED = 2
const val LICENSED = 3 const val LICENSED = 3
const val splitter = "▒ ▒∩▒"
fun create(): SManga { fun create(): SManga {
return SMangaImpl() return SMangaImpl()

View File

@ -261,6 +261,7 @@ open class BrowseCataloguePresenter(
coverCache.deleteFromCache(manga.thumbnail_url) coverCache.deleteFromCache(manga.thumbnail_url)
val downloadManager: DownloadManager = Injekt.get() val downloadManager: DownloadManager = Injekt.get()
downloadManager.deleteManga(manga,source) downloadManager.deleteManga(manga,source)
db.resetMangaInfo(manga).executeAsBlocking()
} }
/** /**

View File

@ -29,7 +29,7 @@ class CatalogueGridHolder(private val view: View, private val adapter: FlexibleA
*/ */
override fun onSetValues(manga: Manga) { override fun onSetValues(manga: Manga) {
// Set manga title // Set manga title
title.text = manga.title title.text = manga.originalTitle()
// Set alpha of thumbnail. // Set alpha of thumbnail.
thumbnail.alpha = if (manga.favorite) 0.3f else 1.0f thumbnail.alpha = if (manga.favorite) 0.3f else 1.0f

View File

@ -31,7 +31,7 @@ class CatalogueListHolder(private val view: View, adapter: FlexibleAdapter<IFlex
* @param manga the manga to bind. * @param manga the manga to bind.
*/ */
override fun onSetValues(manga: Manga) { override fun onSetValues(manga: Manga) {
title.text = manga.title title.text = manga.originalTitle()
title.setTextColor(if (manga.favorite) favoriteColor else unfavoriteColor) title.setTextColor(if (manga.favorite) favoriteColor else unfavoriteColor)
setImage(manga) setImage(manga)

View File

@ -38,7 +38,7 @@ class DownloadHolder(private val view: View, val adapter: DownloadAdapter) :
chapter_title.text = download.chapter.name chapter_title.text = download.chapter.name
// Update the manga title // Update the manga title
manga_title.text = download.manga.customTitle() manga_title.text = download.manga.currentTitle()
// Update the progress bar and the number of downloaded pages // Update the progress bar and the number of downloaded pages
val pages = download.pages val pages = download.pages

View File

@ -1,25 +1,15 @@
package eu.kanade.tachiyomi.ui.library package eu.kanade.tachiyomi.ui.library
import android.graphics.Color
import android.text.format.DateUtils
import androidx.core.content.ContextCompat
import eu.davidea.flexibleadapter.FlexibleAdapter import eu.davidea.flexibleadapter.FlexibleAdapter
import eu.davidea.flexibleadapter.items.IFlexible import eu.davidea.flexibleadapter.items.IFlexible
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.database.DatabaseHelper import eu.kanade.tachiyomi.data.database.DatabaseHelper
import eu.kanade.tachiyomi.data.database.models.Manga import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.data.preference.PreferencesHelper import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.data.preference.getOrDefault import eu.kanade.tachiyomi.data.preference.getOrDefault
import eu.kanade.tachiyomi.source.SourceManager
import eu.kanade.tachiyomi.ui.category.CategoryAdapter import eu.kanade.tachiyomi.ui.category.CategoryAdapter
import eu.kanade.tachiyomi.util.getResourceColor
import eu.kanade.tachiyomi.util.removeArticles import eu.kanade.tachiyomi.util.removeArticles
import uy.kohesive.injekt.injectLazy import uy.kohesive.injekt.injectLazy
import java.text.DateFormat
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.time.LocalDate
import java.time.LocalDateTime
import java.time.Year
import java.util.* import java.util.*
@ -94,7 +84,7 @@ class LibraryCategoryAdapter(val view: LibraryCategoryView) :
"N/A" "N/A"
} }
else -> { else -> {
val title = (iFlexible as LibraryItem).manga.customTitle() val title = (iFlexible as LibraryItem).manga.currentTitle()
if (preferences.removeArticles().getOrDefault()) if (preferences.removeArticles().getOrDefault())
title.removeArticles().substring(0, 1).toUpperCase(Locale.US) title.removeArticles().substring(0, 1).toUpperCase(Locale.US)
else title.substring(0, 1).toUpperCase(Locale.US) else title.substring(0, 1).toUpperCase(Locale.US)

View File

@ -1,11 +1,8 @@
package eu.kanade.tachiyomi.ui.library package eu.kanade.tachiyomi.ui.library
import android.app.Activity
import android.content.Context import android.content.Context
import android.content.Intent
import android.content.res.Configuration import android.content.res.Configuration
import android.graphics.Color import android.graphics.Color
import android.net.Uri
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.Menu import android.view.Menu
@ -18,7 +15,6 @@ import android.view.inputmethod.InputMethodManager
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.view.ActionMode import androidx.appcompat.view.ActionMode
import androidx.appcompat.widget.SearchView import androidx.appcompat.widget.SearchView
import androidx.core.content.ContextCompat.getSystemService
import androidx.core.graphics.drawable.DrawableCompat import androidx.core.graphics.drawable.DrawableCompat
import androidx.core.view.GravityCompat import androidx.core.view.GravityCompat
import androidx.drawerlayout.widget.DrawerLayout import androidx.drawerlayout.widget.DrawerLayout
@ -57,15 +53,12 @@ import eu.kanade.tachiyomi.util.inflate
import eu.kanade.tachiyomi.util.marginBottom import eu.kanade.tachiyomi.util.marginBottom
import eu.kanade.tachiyomi.util.marginTop import eu.kanade.tachiyomi.util.marginTop
import eu.kanade.tachiyomi.util.snack import eu.kanade.tachiyomi.util.snack
import eu.kanade.tachiyomi.util.toast
import eu.kanade.tachiyomi.util.updatePaddingRelative import eu.kanade.tachiyomi.util.updatePaddingRelative
import kotlinx.android.synthetic.main.library_controller.* import kotlinx.android.synthetic.main.library_controller.*
import kotlinx.android.synthetic.main.main_activity.* import kotlinx.android.synthetic.main.main_activity.*
import rx.Subscription import rx.Subscription
import timber.log.Timber
import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get import uy.kohesive.injekt.api.get
import java.io.IOException
class LibraryController( class LibraryController(
bundle: Bundle? = null, bundle: Bundle? = null,
@ -522,7 +515,6 @@ class LibraryController(
destroyActionModeIfNeeded() destroyActionModeIfNeeded()
} else { } else {
mode.title = resources?.getString(R.string.label_selected, count) mode.title = resources?.getString(R.string.label_selected, count)
menu.findItem(R.id.action_edit_cover)?.isVisible = count == 1
menu.findItem(R.id.action_hide_title)?.isVisible = menu.findItem(R.id.action_hide_title)?.isVisible =
!preferences.libraryAsList().getOrDefault() !preferences.libraryAsList().getOrDefault()
if (!preferences.libraryAsList().getOrDefault()) { if (!preferences.libraryAsList().getOrDefault()) {
@ -544,10 +536,6 @@ class LibraryController(
override fun onActionItemClicked(mode: ActionMode, item: MenuItem): Boolean { override fun onActionItemClicked(mode: ActionMode, item: MenuItem): Boolean {
when (item.itemId) { when (item.itemId) {
R.id.action_edit_cover -> {
changeSelectedCover()
destroyActionModeIfNeeded()
}
R.id.action_move_to_category -> showChangeMangaCategoriesDialog() R.id.action_move_to_category -> showChangeMangaCategoriesDialog()
R.id.action_delete -> deleteMangasFromLibrary() R.id.action_delete -> deleteMangasFromLibrary()
R.id.action_select_all -> { R.id.action_select_all -> {
@ -672,55 +660,6 @@ class LibraryController(
presenter.moveMangasToCategories(categories, mangas) presenter.moveMangasToCategories(categories, mangas)
destroyActionModeIfNeeded() destroyActionModeIfNeeded()
} }
/**
* Changes the cover for the selected manga.
*/
private fun changeSelectedCover() {
val manga = selectedMangas.firstOrNull() ?: return
selectedCoverManga = manga
if (manga.favorite) {
val intent = Intent(Intent.ACTION_GET_CONTENT)
intent.type = "image/*"
startActivityForResult(Intent.createChooser(intent,
resources?.getString(R.string.file_select_cover)), REQUEST_IMAGE_OPEN)
} else {
activity?.toast(R.string.notification_first_add_to_library)
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
if (requestCode == REQUEST_IMAGE_OPEN) {
if (data == null || resultCode != Activity.RESULT_OK) return
val activity = activity ?: return
val manga = selectedCoverManga ?: return
try {
// Get the file's input stream from the incoming Intent
activity.contentResolver.openInputStream(data.data ?: Uri.EMPTY).use {
// Update cover to selected file, show error if something went wrong
if (it != null && presenter.editCoverWithStream(it, manga)) {
// TODO refresh cover
} else {
activity.toast(R.string.notification_cover_update_failed)
}
}
} catch (error: IOException) {
activity.toast(R.string.notification_cover_update_failed)
Timber.e(error)
}
selectedCoverManga = null
}
}
private companion object {
/**
* Key to change the cover of a manga in [onActivityResult].
*/
const val REQUEST_IMAGE_OPEN = 101
}
} }
object HeightTopWindowInsetsListener : View.OnApplyWindowInsetsListener { object HeightTopWindowInsetsListener : View.OnApplyWindowInsetsListener {

View File

@ -6,9 +6,11 @@ import com.bumptech.glide.load.engine.DiskCacheStrategy
import com.bumptech.glide.signature.ObjectKey import com.bumptech.glide.signature.ObjectKey
import eu.davidea.flexibleadapter.FlexibleAdapter import eu.davidea.flexibleadapter.FlexibleAdapter
import eu.davidea.flexibleadapter.items.IFlexible import eu.davidea.flexibleadapter.items.IFlexible
import eu.kanade.tachiyomi.data.database.models.MangaImpl
import eu.kanade.tachiyomi.data.glide.GlideApp import eu.kanade.tachiyomi.data.glide.GlideApp
import eu.kanade.tachiyomi.source.LocalSource import eu.kanade.tachiyomi.source.LocalSource
import kotlinx.android.synthetic.main.catalogue_grid_item.* import kotlinx.android.synthetic.main.catalogue_grid_item.*
import java.util.Date
/** /**
* Class used to hold the displayed data of a manga in the library, like the cover or the title. * Class used to hold the displayed data of a manga in the library, like the cover or the title.
@ -35,7 +37,7 @@ class LibraryGridHolder(
// Update the title of the manga. // Update the title of the manga.
with(title) { with(title) {
visibility = if (item.manga.hide_title) View.GONE else View.VISIBLE visibility = if (item.manga.hide_title) View.GONE else View.VISIBLE
text = item.manga.customTitle() text = item.manga.currentTitle()
} }
gradient.visibility = if (item.manga.hide_title) View.GONE else View.VISIBLE gradient.visibility = if (item.manga.hide_title) View.GONE else View.VISIBLE
@ -57,7 +59,7 @@ class LibraryGridHolder(
GlideApp.with(view.context) GlideApp.with(view.context)
.load(item.manga) .load(item.manga)
.diskCacheStrategy(DiskCacheStrategy.RESOURCE) .diskCacheStrategy(DiskCacheStrategy.RESOURCE)
.signature(ObjectKey(item.manga.last_cover_fetch.toString())) .signature(ObjectKey(MangaImpl.getLastCoverFetch(item.manga.id!!).toString()))
.centerCrop() .centerCrop()
.into(thumbnail) .into(thumbnail)
} }

View File

@ -71,13 +71,14 @@ class LibraryItem(val manga: LibraryManga, private val libraryAsList: Preference
val sourceManager by injectLazy<SourceManager>() val sourceManager by injectLazy<SourceManager>()
val sourceName = sourceManager.getOrStub(manga.source).name val sourceName = sourceManager.getOrStub(manga.source).name
return manga.title.contains(constraint, true) || return manga.title.contains(constraint, true) ||
(manga.author?.contains(constraint, true) ?: false) || (manga.currentAuthor()?.contains(constraint, true) ?: false) ||
(manga.currentArtist()?.contains(constraint, true) ?: false) ||
sourceName.contains(constraint, true) || sourceName.contains(constraint, true) ||
if (constraint.contains(",")) { if (constraint.contains(",")) {
val genres = manga.genre?.split(", ") val genres = manga.currentGenres()?.split(", ")
constraint.split(",").all { containsGenre(it.trim(), genres) } constraint.split(",").all { containsGenre(it.trim(), genres) }
} }
else containsGenre(constraint, manga.genre?.split(", ")) else containsGenre(constraint, manga.currentGenres()?.split(", "))
} }
@SuppressLint("DefaultLocale") @SuppressLint("DefaultLocale")

View File

@ -9,6 +9,7 @@ import kotlinx.android.synthetic.main.catalogue_list_item.*
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.bumptech.glide.signature.ObjectKey import com.bumptech.glide.signature.ObjectKey
import eu.davidea.flexibleadapter.items.IFlexible import eu.davidea.flexibleadapter.items.IFlexible
import eu.kanade.tachiyomi.data.database.models.MangaImpl
/** /**
* Class used to hold the displayed data of a manga in the library, like the cover or the title. * Class used to hold the displayed data of a manga in the library, like the cover or the title.
@ -33,7 +34,7 @@ class LibraryListHolder(
*/ */
override fun onSetValues(item: LibraryItem) { override fun onSetValues(item: LibraryItem) {
// Update the title of the manga. // Update the title of the manga.
title.text = item.manga.customTitle() title.text = item.manga.currentTitle()
// Update the unread count and its visibility. // Update the unread count and its visibility.
with(unread_text) { with(unread_text) {
@ -59,7 +60,7 @@ class LibraryListHolder(
GlideApp.with(itemView.context) GlideApp.with(itemView.context)
.load(item.manga) .load(item.manga)
.diskCacheStrategy(DiskCacheStrategy.RESOURCE) .diskCacheStrategy(DiskCacheStrategy.RESOURCE)
.signature(ObjectKey(item.manga.last_cover_fetch.toString())) .signature(ObjectKey(MangaImpl.getLastCoverFetch(item.manga.id!!).toString()))
.centerCrop() .centerCrop()
.circleCrop() .circleCrop()
.dontAnimate() .dontAnimate()

View File

@ -2,7 +2,6 @@ package eu.kanade.tachiyomi.ui.library
import android.os.Bundle import android.os.Bundle
import com.jakewharton.rxrelay.BehaviorRelay import com.jakewharton.rxrelay.BehaviorRelay
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.cache.CoverCache import eu.kanade.tachiyomi.data.cache.CoverCache
import eu.kanade.tachiyomi.data.database.DatabaseHelper import eu.kanade.tachiyomi.data.database.DatabaseHelper
import eu.kanade.tachiyomi.data.database.models.Category import eu.kanade.tachiyomi.data.database.models.Category
@ -280,8 +279,8 @@ class LibraryPresenter(
private fun sortAlphabetical(i1: LibraryItem, i2: LibraryItem): Int { private fun sortAlphabetical(i1: LibraryItem, i2: LibraryItem): Int {
return if (preferences.removeArticles().getOrDefault()) return if (preferences.removeArticles().getOrDefault())
i1.manga.customTitle().removeArticles().compareTo(i2.manga.customTitle().removeArticles(), true) i1.manga.currentTitle().removeArticles().compareTo(i2.manga.currentTitle().removeArticles(), true)
else i1.manga.customTitle().compareTo(i2.manga.customTitle(), true) else i1.manga.currentTitle().compareTo(i2.manga.currentTitle(), true)
} }
/** /**
@ -385,6 +384,7 @@ class LibraryPresenter(
Observable.fromCallable { Observable.fromCallable {
val mangaToDelete = mangas.distinctBy { it.id } val mangaToDelete = mangas.distinctBy { it.id }
mangaToDelete.forEach { manga -> mangaToDelete.forEach { manga ->
db.resetMangaInfo(manga).executeAsBlocking()
coverCache.deleteFromCache(manga.thumbnail_url) coverCache.deleteFromCache(manga.thumbnail_url)
val source = sourceManager.get(manga.source) as? HttpSource val source = sourceManager.get(manga.source) as? HttpSource
if (source != null) if (source != null)
@ -503,27 +503,4 @@ class LibraryPresenter(
db.updateMangaTitle(manga).executeAsBlocking() db.updateMangaTitle(manga).executeAsBlocking()
} }
} }
/**
* Update cover with local file.
*
* @param inputStream the new cover.
* @param manga the manga edited.
* @return true if the cover is updated, false otherwise
*/
@Throws(IOException::class)
fun editCoverWithStream(inputStream: InputStream, manga: Manga): Boolean {
if (manga.source == LocalSource.ID) {
LocalSource.updateCover(context, manga, inputStream)
return true
}
if (manga.thumbnail_url != null && manga.favorite) {
coverCache.copyToCache(manga.thumbnail_url!!, inputStream)
(manga as? MangaImpl)?.last_cover_fetch = Date().time
return true
}
return false
}
} }

View File

@ -27,7 +27,6 @@ import eu.kanade.tachiyomi.ui.base.controller.RxController
import eu.kanade.tachiyomi.ui.base.controller.TabbedController import eu.kanade.tachiyomi.ui.base.controller.TabbedController
import eu.kanade.tachiyomi.ui.base.controller.requestPermissionsSafe import eu.kanade.tachiyomi.ui.base.controller.requestPermissionsSafe
import eu.kanade.tachiyomi.ui.catalogue.CatalogueController import eu.kanade.tachiyomi.ui.catalogue.CatalogueController
import eu.kanade.tachiyomi.ui.main.MainActivity
import eu.kanade.tachiyomi.ui.main.SearchActivity import eu.kanade.tachiyomi.ui.main.SearchActivity
import eu.kanade.tachiyomi.ui.manga.chapter.ChaptersController import eu.kanade.tachiyomi.ui.manga.chapter.ChaptersController
import eu.kanade.tachiyomi.ui.manga.info.MangaInfoController import eu.kanade.tachiyomi.ui.manga.info.MangaInfoController
@ -116,7 +115,7 @@ class MangaController : RxController, TabbedController {
private var trackingIconSubscription: Subscription? = null private var trackingIconSubscription: Subscription? = null
override fun getTitle(): String? { override fun getTitle(): String? {
return manga?.customTitle() return manga?.currentTitle()
} }
override fun inflateView(inflater: LayoutInflater, container: ViewGroup): View { override fun inflateView(inflater: LayoutInflater, container: ViewGroup): View {
@ -156,6 +155,11 @@ class MangaController : RxController, TabbedController {
else activity?.tabs else activity?.tabs
} }
fun updateTitle(manga: Manga) {
this.manga?.title = manga.title
setTitle()
}
override fun onChangeEnded(handler: ControllerChangeHandler, type: ControllerChangeType) { override fun onChangeEnded(handler: ControllerChangeHandler, type: ControllerChangeType) {
super.onChangeEnded(handler, type) super.onChangeEnded(handler, type)
if (manga == null || source == null) { if (manga == null || source == null) {

View File

@ -1,59 +1,53 @@
package eu.kanade.tachiyomi.ui.manga.info package eu.kanade.tachiyomi.ui.manga.info
import android.app.Activity
import android.app.Dialog import android.app.Dialog
import android.content.Intent
import android.graphics.drawable.Drawable
import android.net.Uri
import android.os.Bundle import android.os.Bundle
import android.view.View import android.view.View
import android.view.WindowManager
import com.afollestad.materialdialogs.MaterialDialog import com.afollestad.materialdialogs.MaterialDialog
import com.afollestad.materialdialogs.WhichButton
import com.afollestad.materialdialogs.actions.setActionButtonEnabled
import com.afollestad.materialdialogs.customview.customView import com.afollestad.materialdialogs.customview.customView
import com.jakewharton.rxbinding.widget.itemClicks import com.bluelinelabs.conductor.Router
import com.jakewharton.rxbinding.widget.textChanges import com.bumptech.glide.load.engine.DiskCacheStrategy
import com.bumptech.glide.request.target.CustomTarget
import com.bumptech.glide.request.transition.Transition
import com.bumptech.glide.signature.ObjectKey
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.database.DatabaseHelper import eu.kanade.tachiyomi.data.database.DatabaseHelper
import eu.kanade.tachiyomi.data.database.models.Manga import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.data.database.models.Track import eu.kanade.tachiyomi.data.database.models.MangaImpl
import eu.kanade.tachiyomi.data.track.TrackManager import eu.kanade.tachiyomi.data.glide.GlideApp
import eu.kanade.tachiyomi.data.track.TrackService
import eu.kanade.tachiyomi.data.track.model.TrackSearch
import eu.kanade.tachiyomi.ui.base.controller.DialogController import eu.kanade.tachiyomi.ui.base.controller.DialogController
import eu.kanade.tachiyomi.ui.manga.track.TrackController import eu.kanade.tachiyomi.ui.library.LibraryController
import eu.kanade.tachiyomi.ui.manga.track.TrackSearchAdapter import eu.kanade.tachiyomi.util.chop
import eu.kanade.tachiyomi.ui.manga.track.TrackSearchDialog import eu.kanade.tachiyomi.util.toast
import eu.kanade.tachiyomi.util.plusAssign import kotlinx.android.synthetic.main.edit_manga_dialog.view.*
import kotlinx.android.synthetic.main.track_controller.* import kotlinx.android.synthetic.main.edit_manga_dialog.view.manga_title
import kotlinx.android.synthetic.main.track_search_dialog.view.* import me.gujun.android.taggroup.TagGroup
import rx.Subscription import timber.log.Timber
import rx.android.schedulers.AndroidSchedulers
import rx.subscriptions.CompositeSubscription
import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get import uy.kohesive.injekt.api.get
import java.util.concurrent.TimeUnit import java.io.IOException
import java.io.InputStream
class EditMangaDialog : DialogController { class EditMangaDialog : DialogController {
private var dialogView: View? = null private var dialogView: View? = null
private var adapter: TrackSearchAdapter? = null
private var selectedItem: Track? = null
private val manga: Manga private val manga: Manga
private var subscriptions = CompositeSubscription() private var customCoverUri:Uri? = null
private var searchTextSubscription: Subscription? = null private val infoController
get() = targetController as MangaInfoController
private val trackController constructor(target: MangaInfoController, manga: Manga) : super(Bundle()
get() = targetController as TrackController
private var wasPreviouslyTracked:Boolean = false
constructor(target: TrackController, manga: Manga, wasTracked:Boolean) : super(Bundle()
.apply { .apply {
putLong(KEY_MANGA, manga.id!!) putLong(KEY_MANGA, manga.id!!)
}) { }) {
wasPreviouslyTracked = wasTracked
targetController = target targetController = target
this.manga = manga this.manga = manga
} }
@ -64,101 +58,112 @@ class EditMangaDialog : DialogController {
.executeAsBlocking()!! .executeAsBlocking()!!
} }
override fun showDialog(router: Router) {
super.showDialog(router)
dialog?.window?.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN)
}
override fun onCreateDialog(savedViewState: Bundle?): Dialog { override fun onCreateDialog(savedViewState: Bundle?): Dialog {
val dialog = MaterialDialog(activity!!).apply { val dialog = MaterialDialog(activity!!).apply {
customView(viewRes = R.layout.track_search_dialog, scrollable = false) customView(viewRes = R.layout.edit_manga_dialog, scrollable = true)
negativeButton(android.R.string.cancel) negativeButton(android.R.string.cancel)
positiveButton( positiveButton(R.string.action_save) { onPositiveButtonClick() }
if (wasPreviouslyTracked) R.string.action_clear
else R.string.action_track){ onPositiveButtonClick() }
setActionButtonEnabled(WhichButton.POSITIVE, wasPreviouslyTracked)
}
if (subscriptions.isUnsubscribed) {
subscriptions = CompositeSubscription()
} }
dialogView = dialog.view dialogView = dialog.view
onViewCreated(dialog.view, savedViewState) onViewCreated(dialog.view, savedViewState)
return dialog return dialog
} }
fun onViewCreated(view: View, savedState: Bundle?) { fun onViewCreated(view: View, savedState: Bundle?) {
// Create adapter GlideApp.with(view.context)
val adapter = TrackSearchAdapter(view.context) .asDrawable()
this.adapter = adapter .load(manga)
view.track_search_list.adapter = adapter .diskCacheStrategy(DiskCacheStrategy.RESOURCE)
.signature(ObjectKey(MangaImpl.getLastCoverFetch(manga.id!!).toString()))
.dontAnimate()
.into(view.manga_cover)
// Set listeners if (manga.currentTitle() != manga.originalTitle())
selectedItem = null view.manga_title.append(manga.currentTitle())
view.manga_title.hint = "${resources?.getString(R.string.title)}: ${manga.originalTitle()}"
subscriptions += view.track_search_list.itemClicks().subscribe { position -> if (manga.currentAuthor() != manga.originalAuthor())
selectedItem = adapter.getItem(position) view.manga_author.append(manga.currentAuthor())
(dialog as? MaterialDialog)?.positiveButton(R.string.action_track) if (!manga.originalAuthor().isNullOrBlank())
(dialog as? MaterialDialog)?.setActionButtonEnabled(WhichButton.POSITIVE, true) view.manga_author.hint = "${resources?.getString(R.string.manga_info_author_label)}: ${manga.originalAuthor()}"
if (manga.currentArtist() != manga.originalArtist())
view.manga_artist.append(manga.currentArtist())
if (!manga.originalArtist().isNullOrBlank())
view.manga_artist.hint = "${resources?.getString(R.string.manga_info_artist_label)}: ${manga.originalArtist()}"
view.cover_layout.setOnClickListener {
changeCover()
} }
// Do an initial search based on the manga's title if (manga.currentArtist() != manga.originalArtist())
if (savedState == null) { view.manga_description.append(manga.currentDesc())
val title = trackController.presenter.manga.trueTitle() if (!manga.originalDesc().isNullOrBlank())
view.track_search.append(title) view.manga_description.hint = "${resources?.getString(R.string.description)}: ${manga
search(title) .originalDesc()?.chop(15)}"
if (manga.currentGenres().isNullOrBlank().not()) {
view.manga_genres_tags.setTags(manga.currentGenres()?.split(", "))
}
view.reset_tags.setOnClickListener { resetTags() }
}
private fun resetTags() {
if (manga.originalGenres().isNullOrBlank() || manga.originalGenres() == "null")
dialogView?.manga_genres_tags?.setTags(emptyList())
else
dialogView?.manga_genres_tags?.setTags(manga.originalGenres()?.split(", "))
}
private fun changeCover() {
if (manga.favorite) {
val intent = Intent(Intent.ACTION_GET_CONTENT)
intent.type = "image/*"
startActivityForResult(
Intent.createChooser(intent,
resources?.getString(R.string.file_select_cover)),
101
)
} else {
activity?.toast(R.string.notification_first_add_to_library)
} }
} }
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
if (requestCode == 101) {
if (data == null || resultCode != Activity.RESULT_OK) return
val activity = activity ?: return
try {
// Get the file's input stream from the incoming Intent
GlideApp.with(dialogView!!.context)
.load(data.data ?: Uri.EMPTY)
.into(dialogView!!.manga_cover)
customCoverUri = data.data
} catch (error: IOException) {
activity.toast(R.string.notification_cover_update_failed)
Timber.e(error)
}
}
}
override fun onDestroyView(view: View) { override fun onDestroyView(view: View) {
super.onDestroyView(view) super.onDestroyView(view)
subscriptions.unsubscribe()
dialogView = null dialogView = null
adapter = null
}
override fun onAttach(view: View) {
super.onAttach(view)
searchTextSubscription = dialogView!!.track_search.textChanges()
.skip(1)
.debounce(1, TimeUnit.SECONDS, AndroidSchedulers.mainThread())
.map { it.toString() }
.filter(String::isNotBlank)
.subscribe { search(it) }
}
override fun onDetach(view: View) {
super.onDetach(view)
searchTextSubscription?.unsubscribe()
}
private fun search(query: String) {
val view = dialogView ?: return
view.progress.visibility = View.VISIBLE
view.track_search_list.visibility = View.INVISIBLE
//trackController.presenter.search(query, service)
}
fun onSearchResults(results: List<TrackSearch>) {
selectedItem = null
val view = dialogView ?: return
view.progress.visibility = View.INVISIBLE
view.track_search_list.visibility = View.VISIBLE
adapter?.setItems(results)
if (results.size == 1 && !wasPreviouslyTracked) {
selectedItem = adapter?.getItem(0)
(dialog as? MaterialDialog)?.positiveButton(R.string.action_track)
(dialog as? MaterialDialog)?.setActionButtonEnabled(WhichButton.POSITIVE, true)
}
}
fun onSearchResultsError() {
val view = dialogView ?: return
view.progress.visibility = View.VISIBLE
view.track_search_list.visibility = View.INVISIBLE
adapter?.setItems(emptyList())
} }
private fun onPositiveButtonClick() { private fun onPositiveButtonClick() {
//trackController.swipe_refresh.isRefreshing = true infoController.presenter.updateManga(dialogView?.manga_title?.text.toString(),
//trackController.presenter.registerTracking(selectedItem, service) dialogView?.manga_author?.text.toString(), dialogView?.manga_artist?.text.toString(),
customCoverUri, dialogView?.manga_description?.text.toString(),
dialogView?.manga_genres_tags?.tags)
infoController.updateTitle()
} }
private companion object { private companion object {

View File

@ -70,8 +70,9 @@ import eu.kanade.tachiyomi.util.updateLayoutParams
import eu.kanade.tachiyomi.util.updatePaddingRelative import eu.kanade.tachiyomi.util.updatePaddingRelative
import jp.wasabeef.glide.transformations.CropSquareTransformation import jp.wasabeef.glide.transformations.CropSquareTransformation
import jp.wasabeef.glide.transformations.MaskTransformation import jp.wasabeef.glide.transformations.MaskTransformation
import kotlinx.android.synthetic.main.main_activity.*
import kotlinx.android.synthetic.main.manga_info_controller.* import kotlinx.android.synthetic.main.manga_info_controller.*
import kotlinx.android.synthetic.main.manga_info_controller.manga_cover
import kotlinx.android.synthetic.main.manga_info_controller.manga_genres_tags
import uy.kohesive.injekt.injectLazy import uy.kohesive.injekt.injectLazy
import java.io.File import java.io.File
import java.text.DateFormat import java.text.DateFormat
@ -181,7 +182,7 @@ class MangaInfoController : NucleusController<MangaInfoPresenter>(),
shortAnimationDuration = resources?.getInteger(android.R.integer.config_shortAnimTime) ?: 0 shortAnimationDuration = resources?.getInteger(android.R.integer.config_shortAnimTime) ?: 0
manga_cover.longClicks().subscribeUntilDestroy { manga_cover.longClicks().subscribeUntilDestroy {
copyToClipboard(view.context.getString(R.string.title), presenter.manga.customTitle(), R.string copyToClipboard(view.context.getString(R.string.title), presenter.manga.currentTitle(), R.string
.manga_info_full_title_label) .manga_info_full_title_label)
} }
container = (view as ViewGroup).findViewById(R.id.manga_info_layout) as? View container = (view as ViewGroup).findViewById(R.id.manga_info_layout) as? View
@ -219,11 +220,14 @@ class MangaInfoController : NucleusController<MangaInfoPresenter>(),
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
inflater.inflate(R.menu.manga_info, menu) inflater.inflate(R.menu.manga_info, menu)
val editItem = menu.findItem(R.id.action_edit)
editItem.isVisible = presenter.manga.favorite
} }
override fun onOptionsItemSelected(item: MenuItem): Boolean { override fun onOptionsItemSelected(item: MenuItem): Boolean {
when (item.itemId) { when (item.itemId) {
R.id.action_edit -> { } R.id.action_edit -> EditMangaDialog(this, presenter.manga).showDialog(router)
R.id.action_open_in_browser -> openInBrowser() R.id.action_open_in_browser -> openInBrowser()
R.id.action_open_in_web_view -> openInWebView() R.id.action_open_in_web_view -> openInWebView()
R.id.action_share -> prepareToShareManga() R.id.action_share -> prepareToShareManga()
@ -262,39 +266,39 @@ class MangaInfoController : NucleusController<MangaInfoPresenter>(),
val view = view ?: return val view = view ?: return
//update full title TextView. //update full title TextView.
manga_full_title.text = if (manga.customTitle().isBlank()) { manga_full_title.text = if (manga.currentTitle().isBlank()) {
view.context.getString(R.string.unknown) view.context.getString(R.string.unknown)
} else { } else {
manga.customTitle() manga.currentTitle()
} }
// Update artist TextView. // Update artist TextView.
manga_artist.text = if (manga.artist.isNullOrBlank()) { manga_artist.text = if (manga.currentArtist().isNullOrBlank()) {
view.context.getString(R.string.unknown) view.context.getString(R.string.unknown)
} else { } else {
manga.artist manga.currentArtist()
} }
// Update author TextView. // Update author TextView.
manga_author.text = if (manga.author.isNullOrBlank()) { manga_author.text = if (manga.currentAuthor().isNullOrBlank()) {
view.context.getString(R.string.unknown) view.context.getString(R.string.unknown)
} else { } else {
manga.author manga.currentAuthor()
} }
// If manga source is known update source TextView. // If manga source is known update source TextView.
manga_source.text = source?.toString() ?: view.context.getString(R.string.unknown) manga_source.text = source?.toString() ?: view.context.getString(R.string.unknown)
// Update genres list // Update genres list
if (manga.genre.isNullOrBlank().not()) { if (manga.currentGenres().isNullOrBlank().not()) {
manga_genres_tags.setTags(manga.genre?.split(", ")) manga_genres_tags.setTags(manga.currentGenres()?.split(", "))
} }
// Update description TextView. // Update description TextView.
manga_summary.text = if (manga.description.isNullOrBlank()) { manga_summary.text = if (manga.currentDesc().isNullOrBlank()) {
view.context.getString(R.string.unknown) view.context.getString(R.string.unknown)
} else { } else {
manga.description manga.currentDesc()
} }
// Update status TextView. // Update status TextView.
@ -307,19 +311,20 @@ class MangaInfoController : NucleusController<MangaInfoPresenter>(),
// Set the favorite drawable to the correct one. // Set the favorite drawable to the correct one.
setFavoriteDrawable(manga.favorite) setFavoriteDrawable(manga.favorite)
activity?.invalidateOptionsMenu()
// Set cover if it wasn't already. // Set cover if it wasn't already.
if (!manga.thumbnail_url.isNullOrEmpty()) { if (!manga.thumbnail_url.isNullOrEmpty()) {
GlideApp.with(view.context) GlideApp.with(view.context)
.load(manga) .load(manga)
.diskCacheStrategy(DiskCacheStrategy.RESOURCE) .diskCacheStrategy(DiskCacheStrategy.RESOURCE)
.signature(ObjectKey((manga as MangaImpl).last_cover_fetch.toString())) .signature(ObjectKey(MangaImpl.getLastCoverFetch(manga.id!!).toString()))
//.centerCrop() //.centerCrop()
.into(manga_cover) .into(manga_cover)
if (manga_cover_full != null) { if (manga_cover_full != null) {
GlideApp.with(view.context).asDrawable().load(manga) GlideApp.with(view.context).asDrawable().load(manga)
.diskCacheStrategy(DiskCacheStrategy.RESOURCE) .diskCacheStrategy(DiskCacheStrategy.RESOURCE)
.signature(ObjectKey(manga.last_cover_fetch.toString())) .signature(ObjectKey(MangaImpl.getLastCoverFetch(manga.id!!).toString()))
.override(CustomTarget.SIZE_ORIGINAL, CustomTarget.SIZE_ORIGINAL) .override(CustomTarget.SIZE_ORIGINAL, CustomTarget.SIZE_ORIGINAL)
.into(object : CustomTarget<Drawable>() { .into(object : CustomTarget<Drawable>() {
override fun onResourceReady(resource: Drawable, override fun onResourceReady(resource: Drawable,
@ -336,7 +341,7 @@ class MangaInfoController : NucleusController<MangaInfoPresenter>(),
GlideApp.with(view.context) GlideApp.with(view.context)
.load(manga) .load(manga)
.diskCacheStrategy(DiskCacheStrategy.RESOURCE) .diskCacheStrategy(DiskCacheStrategy.RESOURCE)
.signature(ObjectKey(manga.last_cover_fetch.toString())) .signature(ObjectKey(MangaImpl.getLastCoverFetch(manga.id!!).toString()))
.centerCrop() .centerCrop()
.into(backdrop) .into(backdrop)
} }
@ -397,7 +402,7 @@ class MangaInfoController : NucleusController<MangaInfoPresenter>(),
} }
val activity = activity ?: return val activity = activity ?: return
val intent = WebViewActivity.newIntent(activity, source.id, url, presenter.manga.trueTitle()) val intent = WebViewActivity.newIntent(activity, source.id, url, presenter.manga.originalTitle())
startActivity(intent) startActivity(intent)
} }
@ -433,7 +438,7 @@ class MangaInfoController : NucleusController<MangaInfoPresenter>(),
val intent = Intent(Intent.ACTION_SEND).apply { val intent = Intent(Intent.ACTION_SEND).apply {
type = "text/*" type = "text/*"
putExtra(Intent.EXTRA_TEXT, url) putExtra(Intent.EXTRA_TEXT, url)
putExtra(Intent.EXTRA_TITLE, presenter.manga.customTitle()) putExtra(Intent.EXTRA_TITLE, presenter.manga.currentTitle())
flags = Intent.FLAG_GRANT_READ_URI_PERMISSION flags = Intent.FLAG_GRANT_READ_URI_PERMISSION
if (stream != null) { if (stream != null) {
clipData = ClipData.newRawUri(null, stream) clipData = ClipData.newRawUri(null, stream)
@ -710,11 +715,11 @@ class MangaInfoController : NucleusController<MangaInfoPresenter>(),
// Check if shortcut placement is supported // Check if shortcut placement is supported
if (ShortcutManagerCompat.isRequestPinShortcutSupported(activity)) { if (ShortcutManagerCompat.isRequestPinShortcutSupported(activity)) {
val shortcutId = "manga-shortcut-${presenter.manga.trueTitle()}-${presenter.source.name}" val shortcutId = "manga-shortcut-${presenter.manga.originalTitle()}-${presenter.source.name}"
// Create shortcut info // Create shortcut info
val shortcutInfo = ShortcutInfoCompat.Builder(activity, shortcutId) val shortcutInfo = ShortcutInfoCompat.Builder(activity, shortcutId)
.setShortLabel(presenter.manga.customTitle()) .setShortLabel(presenter.manga.currentTitle())
.setIcon(IconCompat.createWithBitmap(icon)) .setIcon(IconCompat.createWithBitmap(icon))
.setIntent(shortcutIntent) .setIntent(shortcutIntent)
.build() .build()
@ -735,6 +740,11 @@ class MangaInfoController : NucleusController<MangaInfoPresenter>(),
} }
} }
fun updateTitle() {
setMangaInfo(presenter.manga, presenter.source)
(parentController as? MangaController)?.updateTitle(presenter.manga)
}
private fun setFullCoverToThumb() { private fun setFullCoverToThumb() {
if (setUpFullCover) return if (setUpFullCover) return
val expandedImageView = manga_cover_full ?: return val expandedImageView = manga_cover_full ?: return

View File

@ -2,6 +2,7 @@ package eu.kanade.tachiyomi.ui.manga.info
import android.app.Application import android.app.Application
import android.graphics.Bitmap import android.graphics.Bitmap
import android.net.Uri
import android.os.Bundle import android.os.Bundle
import com.jakewharton.rxrelay.BehaviorRelay import com.jakewharton.rxrelay.BehaviorRelay
import com.jakewharton.rxrelay.PublishRelay import com.jakewharton.rxrelay.PublishRelay
@ -12,10 +13,11 @@ import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.data.database.models.MangaCategory import eu.kanade.tachiyomi.data.database.models.MangaCategory
import eu.kanade.tachiyomi.data.database.models.MangaImpl import eu.kanade.tachiyomi.data.database.models.MangaImpl
import eu.kanade.tachiyomi.data.download.DownloadManager import eu.kanade.tachiyomi.data.download.DownloadManager
import eu.kanade.tachiyomi.source.LocalSource
import eu.kanade.tachiyomi.source.Source import eu.kanade.tachiyomi.source.Source
import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter
import eu.kanade.tachiyomi.util.DiskUtil import eu.kanade.tachiyomi.util.DiskUtil
import eu.kanade.tachiyomi.util.ImageUtil
import eu.kanade.tachiyomi.util.isNullOrUnsubscribed import eu.kanade.tachiyomi.util.isNullOrUnsubscribed
import rx.Observable import rx.Observable
import rx.Subscription import rx.Subscription
@ -24,7 +26,6 @@ import rx.schedulers.Schedulers
import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get import uy.kohesive.injekt.api.get
import java.io.File import java.io.File
import java.io.FileInputStream
import java.io.FileOutputStream import java.io.FileOutputStream
import java.io.InputStream import java.io.InputStream
import java.io.OutputStream import java.io.OutputStream
@ -94,7 +95,7 @@ class MangaInfoPresenter(
manga.initialized = true manga.initialized = true
db.insertManga(manga).executeAsBlocking() db.insertManga(manga).executeAsBlocking()
coverCache.deleteFromCache(manga.thumbnail_url) coverCache.deleteFromCache(manga.thumbnail_url)
(manga as? MangaImpl)?.last_cover_fetch = Date().time MangaImpl.setLastCoverFetch(manga.id!!, Date().time)
manga manga
} }
.subscribeOn(Schedulers.io()) .subscribeOn(Schedulers.io())
@ -119,6 +120,7 @@ class MangaInfoPresenter(
fun confirmDeletion() { fun confirmDeletion() {
coverCache.deleteFromCache(manga.thumbnail_url) coverCache.deleteFromCache(manga.thumbnail_url)
db.resetMangaInfo(manga).executeAsBlocking()
downloadManager.deleteManga(manga, source) downloadManager.deleteManga(manga, source)
} }
@ -148,7 +150,7 @@ class MangaInfoPresenter(
directory.mkdirs() directory.mkdirs()
// Build destination file. // Build destination file.
val filename = DiskUtil.buildValidFilename("${manga.trueTitle()} - Cover.jpg") val filename = DiskUtil.buildValidFilename("${manga.originalTitle()} - Cover.jpg")
val destFile = File(directory, filename) val destFile = File(directory, filename)
val stream: OutputStream = FileOutputStream(destFile) val stream: OutputStream = FileOutputStream(destFile)
@ -199,4 +201,71 @@ class MangaInfoPresenter(
moveMangaToCategories(manga, listOfNotNull(category)) moveMangaToCategories(manga, listOfNotNull(category))
} }
fun updateManga(title:String?, author:String?, artist: String?, uri: Uri?,
description: String?, tags: Array<String>?) {
var changed = false
if (title.isNullOrBlank() && manga.currentTitle() != manga.originalTitle()) {
manga.title = manga.originalTitle()
changed = true
} else if (!title.isNullOrBlank() && title != manga.currentTitle()) {
manga.title = "${title}${SManga.splitter}${manga.originalTitle()}"
changed = true
}
if (author.isNullOrBlank() && manga.currentAuthor() != manga.originalAuthor()) {
manga.author = manga.originalAuthor()
changed = true
} else if (!author.isNullOrBlank() && author != manga.currentAuthor()) {
manga.author = "${author}${SManga.splitter}${manga.originalAuthor()}"
changed = true
}
if (artist.isNullOrBlank() && manga.currentArtist() != manga.currentArtist()) {
manga.artist = manga.originalArtist()
changed = true
} else if (!artist.isNullOrBlank() && artist != manga.currentArtist()) {
manga.artist = "${artist}${SManga.splitter}${manga.originalArtist()}"
changed = true
}
if (description.isNullOrBlank() && manga.currentDesc() != manga.originalDesc()) {
manga.description = manga.originalDesc()
changed = true
} else if (!description.isNullOrBlank() && description != manga.currentDesc()) {
manga.description = "${description}${SManga.splitter}${manga.originalDesc()}"
changed = true
}
var tagsString = tags?.joinToString(", ")
if (tagsString.isNullOrBlank() && manga.currentGenres() != manga.originalGenres()) {
manga.genre = manga.originalGenres()
changed = true
} else if (!tagsString.isNullOrBlank() && tagsString != manga.currentGenres()) {
tagsString = tags?.joinToString(", ") { it.capitalize() }
manga.genre = "${tagsString}${SManga.splitter}${manga.originalGenres()}"
changed = true
}
if (uri != null) editCoverWithStream(uri)
if (changed) db.updateMangaInfo(manga).executeAsBlocking()
}
private fun editCoverWithStream(uri: Uri): Boolean {
val inputStream = downloadManager.context.contentResolver.openInputStream(uri) ?:
return false
if (manga.source == LocalSource.ID) {
LocalSource.updateCover(downloadManager.context, manga, inputStream)
return true
}
if (manga.thumbnail_url != null && manga.favorite) {
coverCache.copyToCache(manga.thumbnail_url!!, inputStream)
MangaImpl.setLastCoverFetch(manga.id!!, Date().time)
return true
}
return false
}
} }

View File

@ -95,7 +95,7 @@ class TrackSearchDialog : DialogController {
// Do an initial search based on the manga's title // Do an initial search based on the manga's title
if (savedState == null) { if (savedState == null) {
val title = trackController.presenter.manga.trueTitle() val title = trackController.presenter.manga.originalTitle()
view.track_search.append(title) view.track_search.append(title)
search(title) search(title)
} }

View File

@ -16,7 +16,7 @@ class MangaHolder(
fun bind(item: MangaItem) { fun bind(item: MangaItem) {
// Update the title of the manga. // Update the title of the manga.
title.text = item.manga.customTitle() title.text = item.manga.currentTitle()
// Create thumbnail onclick to simulate long click // Create thumbnail onclick to simulate long click
thumbnail.setOnClickListener { thumbnail.setOnClickListener {

View File

@ -23,7 +23,7 @@ import uy.kohesive.injekt.injectLazy
class SearchController( class SearchController(
private var manga: Manga? = null private var manga: Manga? = null
) : CatalogueSearchController(manga?.trueTitle()) { ) : CatalogueSearchController(manga?.originalTitle()) {
private var newManga: Manga? = null private var newManga: Manga? = null
private var progress = 1 private var progress = 1

View File

@ -14,7 +14,6 @@ import androidx.core.graphics.ColorUtils
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import androidx.vectordrawable.graphics.drawable.VectorDrawableCompat import androidx.vectordrawable.graphics.drawable.VectorDrawableCompat
import com.afollestad.materialdialogs.MaterialDialog import com.afollestad.materialdialogs.MaterialDialog
import com.bluelinelabs.conductor.ControllerChangeHandler
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.database.DatabaseHelper import eu.kanade.tachiyomi.data.database.DatabaseHelper
import eu.kanade.tachiyomi.data.database.models.Manga import eu.kanade.tachiyomi.data.database.models.Manga
@ -155,7 +154,7 @@ class MigrationListController(bundle: Bundle? = null) : BaseController(bundle),
smartSearchEngine.smartSearch(source, mangaObj.title) smartSearchEngine.smartSearch(source, mangaObj.title)
} else {*/ } else {*/
val searchResult = smartSearchEngine val searchResult = smartSearchEngine
.normalSearch(source, mangaObj.trueTitle()) .normalSearch(source, mangaObj.originalTitle())
if(searchResult != null) { if(searchResult != null) {
val localManga = smartSearchEngine.networkToLocalManga(searchResult, source.id) val localManga = smartSearchEngine.networkToLocalManga(searchResult, source.id)
@ -185,7 +184,7 @@ class MigrationListController(bundle: Bundle? = null) : BaseController(bundle),
validSources.forEachIndexed { index, source -> validSources.forEachIndexed { index, source ->
val searchResult = try { val searchResult = try {
val searchResult = smartSearchEngine val searchResult = smartSearchEngine
.normalSearch(source, mangaObj.trueTitle()) .normalSearch(source, mangaObj.originalTitle())
if (searchResult != null) { if (searchResult != null) {
val localManga = smartSearchEngine.networkToLocalManga(searchResult, source.id) val localManga = smartSearchEngine.networkToLocalManga(searchResult, source.id)

View File

@ -132,10 +132,10 @@ class MigrationProcessHolder(
.centerCrop() .centerCrop()
.into(thumbnail) .into(thumbnail)
title.text = if (manga.customTitle().isBlank()) { title.text = if (manga.currentTitle().isBlank()) {
view.context.getString(R.string.unknown) view.context.getString(R.string.unknown)
} else { } else {
manga.customTitle() manga.currentTitle()
} }
gradient.visible() gradient.visible()

View File

@ -381,7 +381,7 @@ class ReaderActivity : BaseRxActivity<ReaderPresenter>(),
viewer = newViewer viewer = newViewer
viewer_container.addView(newViewer.getView()) viewer_container.addView(newViewer.getView())
toolbar.title = manga.customTitle() toolbar.title = manga.currentTitle()
page_seekbar.isRTL = newViewer is R2LPagerViewer page_seekbar.isRTL = newViewer is R2LPagerViewer

View File

@ -444,7 +444,7 @@ class ReaderPresenter(
// Build destination file. // Build destination file.
val filename = DiskUtil.buildValidFilename( val filename = DiskUtil.buildValidFilename(
"${manga.customTitle()} - ${chapter.name}".take(225) "${manga.currentTitle()} - ${chapter.name}".take(225)
) + " - ${page.number}.${type.extension}" ) + " - ${page.number}.${type.extension}"
val destFile = File(directory, filename) val destFile = File(directory, filename)
@ -531,7 +531,7 @@ class ReaderPresenter(
val thumbUrl = manga.thumbnail_url ?: throw Exception("Image url not found") val thumbUrl = manga.thumbnail_url ?: throw Exception("Image url not found")
if (manga.favorite) { if (manga.favorite) {
coverCache.copyToCache(thumbUrl, stream()) coverCache.copyToCache(thumbUrl, stream())
(manga as? MangaImpl)?.last_cover_fetch = Date().time MangaImpl.setLastCoverFetch(manga.id!!, Date().time)
SetAsCoverResult.Success SetAsCoverResult.Success
} else { } else {
SetAsCoverResult.AddToLibraryFirst SetAsCoverResult.AddToLibraryFirst

View File

@ -61,7 +61,7 @@ class RecentChapterHolder(private val view: View, private val adapter: RecentCha
chapter_title.text = item.chapter.name chapter_title.text = item.chapter.name
// Set manga title // Set manga title
manga_title.text = item.manga.customTitle() manga_title.text = item.manga.currentTitle()
// Set the correct drawable for dropdown and update the tint to match theme. // Set the correct drawable for dropdown and update the tint to match theme.
chapter_menu_icon.setVectorCompat(R.drawable.ic_more_horiz_black_24dp, view.context.getResourceColor(R.attr.icon_color)) chapter_menu_icon.setVectorCompat(R.drawable.ic_more_horiz_black_24dp, view.context.getResourceColor(R.attr.icon_color))

View File

@ -61,7 +61,7 @@ class RecentlyReadHolder(
val (manga, chapter, history) = item val (manga, chapter, history) = item
// Set manga title // Set manga title
manga_title.text = manga.customTitle() manga_title.text = manga.currentTitle()
// Set source + chapter title // Set source + chapter title
val formattedNumber = adapter.decimalFormat.format(chapter.chapter_number.toDouble()) val formattedNumber = adapter.decimalFormat.format(chapter.chapter_number.toDouble())

View File

@ -74,7 +74,7 @@ object ChapterRecognition {
} }
// Remove manga title from chapter title. // Remove manga title from chapter title.
val nameWithoutManga = name.replace(manga.trueTitle().toLowerCase(), "").trim() val nameWithoutManga = name.replace(manga.originalTitle().toLowerCase(), "").trim()
// Check if first value is number after title remove. // Check if first value is number after title remove.
if (updateChapter(withoutManga.find(nameWithoutManga), chapter)) if (updateChapter(withoutManga.find(nameWithoutManga), chapter))

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<solid
android:color="?attr/colorPrimary"/>
</shape>

View File

@ -1,9 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FFFFFFFF"
android:pathData="M3,17.25V21h3.75L17.81,9.94l-3.75,-3.75L3,17.25zM20.71,7.04c0.39,-0.39 0.39,-1.02 0,-1.41l-2.34,-2.34c-0.39,-0.39 -1.02,-0.39 -1.41,0l-1.83,1.83 3.75,3.75 1.83,-1.83z"/>
</vector>

View File

@ -0,0 +1,7 @@
<vector android:height="24dp"
android:viewportHeight="24.0" android:viewportWidth="24.0"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#FFFFFFFF"
android:pathData="M3,17.25V21h3.75L17.81,9.94l-3.75,-3.75L3,17.25zM20.71,7.04c0.39,-0.39 0.39,-1.02 0,-1.41l-2.34,-2.34c-0.39,-0.39 -1.02,-0.39 -1.41,0l-1.83,1.83 3.75,3.75 1.83,-1.83z"
android:strokeColor="#000000" android:strokeWidth="0.5"/>
</vector>

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="@android:color/white" />
<stroke android:width="1dp" android:color="@color/snackbarBackground" />
<padding android:left="1dp" android:top="1dp" android:right="1dp"
android:bottom="1dp" />
</shape>

View File

@ -0,0 +1,112 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<FrameLayout
android:id="@+id/cover_layout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginBottom="10dp">
<ImageView
android:id="@+id/manga_cover"
android:layout_width="wrap_content"
android:adjustViewBounds="true"
android:layout_height="150dp"
android:background="@drawable/image_border_background"
android:src="@mipmap/ic_launcher"/>
<FrameLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="4dp"
android:layout_gravity="end|bottom"
android:layout_marginEnd="4dp"
android:layout_marginBottom="4dp">
<ImageView
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_gravity="center"
android:src="@drawable/ic_edit_outlined_white_24dp"/>
</FrameLayout>
</FrameLayout>
<EditText
android:id="@+id/manga_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/title"
android:layout_marginEnd="16dp"
android:layout_marginStart="16dp"
android:inputType="text"
android:maxLines="1"/>
<EditText
android:id="@+id/manga_author"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/manga_info_author_label"
android:layout_marginEnd="16dp"
android:layout_marginStart="16dp"
android:inputType="text"
android:maxLines="1"/>
<EditText
android:id="@+id/manga_artist"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/manga_info_artist_label"
android:layout_marginEnd="16dp"
android:layout_marginStart="16dp"
android:inputType="text"
android:maxLines="1"/>
<Button
android:id="@+id/reset_tags"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="16dp"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:text="@string/reset_tags" />
<me.gujun.android.taggroup.TagGroup
android:id="@+id/manga_genres_tags"
style="@style/TagGroup"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginEnd="16dp"
android:layout_marginStart="16dp"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp"
app:atg_borderStrokeWidth="1dp"
app:atg_backgroundColor="@android:color/transparent"
app:atg_isAppendMode="true"
app:atg_inputHintColor="?android:attr/textColorSecondary"
app:atg_inputTextColor="?android:attr/textColorPrimary"
app:atg_checkedBackgroundColor="@color/red_error"
app:atg_checkedBorderColor="@color/red_error"
app:atg_borderColor="@color/md_blue_A400"
app:atg_textColor="@color/md_blue_A400" />
<EditText
android:id="@+id/manga_description"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/description"
android:layout_marginEnd="16dp"
android:layout_marginStart="16dp"
android:inputType="text|textMultiLine"
android:scrollHorizontally="false"/>
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="?android:attr/divider"/>
</LinearLayout>

View File

@ -5,7 +5,7 @@
<item android:id="@+id/action_edit" <item android:id="@+id/action_edit"
android:title="@string/action_edit" android:title="@string/action_edit"
android:icon="@drawable/ic_create_white_24dp" android:icon="@drawable/ic_edit_white_24dp"
app:showAsAction="ifRoom"/> app:showAsAction="ifRoom"/>
<item android:id="@+id/action_delete" <item android:id="@+id/action_delete"

View File

@ -3,11 +3,6 @@
<menu xmlns:android="http://schemas.android.com/apk/res/android" <menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"> xmlns:app="http://schemas.android.com/apk/res-auto">
<item android:id="@+id/action_edit_cover"
android:title="@string/action_edit_cover"
android:icon="@drawable/ic_create_white_24dp"
app:showAsAction="ifRoom"/>
<item android:id="@+id/action_move_to_category" <item android:id="@+id/action_move_to_category"
android:title="@string/action_move_category" android:title="@string/action_move_category"
android:icon="@drawable/ic_label_white_24dp" android:icon="@drawable/ic_label_white_24dp"

View File

@ -644,5 +644,6 @@
<string name="use_first_source">Use first source with alternative</string> <string name="use_first_source">Use first source with alternative</string>
<string name="skip_this_step_next_time">Skip this step next time</string> <string name="skip_this_step_next_time">Skip this step next time</string>
<string name="pre_migration_skip_toast">To show this screen again, go to Settings -> Library.</string> <string name="pre_migration_skip_toast">To show this screen again, go to Settings -> Library.</string>
<string name="reset_tags">Reset Tags</string>
</resources> </resources>