Prep for custom titles

This commit is contained in:
Jay 2020-01-19 16:58:28 -08:00
parent fe316b5bbc
commit aa00a1bed8
31 changed files with 239 additions and 48 deletions

View File

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

View File

@ -39,8 +39,14 @@ open class MangaImpl : Manga {
var last_cover_fetch: Long = 0
override fun copyFrom(other: SManga) {
if (other is MangaImpl && (other as MangaImpl)::title.isInitialized && other.title != title)
title = other.title
if (((other is MangaImpl && (other as MangaImpl)::title.isInitialized)
|| other !is MangaImpl) && other.title != title) {
title = if (customTitle() != trueTitle()) {
val customTitle = customTitle()
val trueTitle = other.title
"${customTitle}≡§${trueTitle}"
} else other.title
}
super.copyFrom(other)
}

View File

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

View File

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

View File

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

View File

@ -461,7 +461,7 @@ class LibraryUpdateService(
*/
private fun showProgressNotification(manga: Manga, current: Int, total: Int) {
notificationManager.notify(Notifications.ID_LIBRARY_PROGRESS, progressNotification
.setContentTitle(manga.title)
.setContentTitle(manga.customTitle())
.setProgress(total, current, false)
.build())
}
@ -487,7 +487,7 @@ class LibraryUpdateService(
}
catch (e: Exception) { }
setGroupAlertBehavior(GROUP_ALERT_SUMMARY)
setContentTitle(manga.title)
setContentTitle(manga.customTitle())
color = ContextCompat.getColor(this@LibraryUpdateService, R.color.colorAccentLight)
val chaptersNames = if (chapterNames.size > 5) {
"${chapterNames.take(4).joinToString(", ")}, " +
@ -527,11 +527,11 @@ class LibraryUpdateService(
.notification_new_chapters_text,
updates.size, updates.size))
setStyle(NotificationCompat.BigTextStyle().bigText(updates.joinToString("\n") {
it.first.title.chop(45)
it.first.customTitle().chop(45)
}))
}
else {
setContentText(updates.first().first.title.chop(45))
setContentText(updates.first().first.customTitle().chop(45))
}
priority = NotificationCompat.PRIORITY_HIGH
setGroup(Notifications.GROUP_NEW_CHAPTERS)

View File

@ -65,7 +65,7 @@ class SmartSearchEngine(parentContext: CoroutineContext,
return@supervisorScope listOf(SearchEntry(searchResults.mangas.first(), 0.0))
searchResults.mangas.map {
val normalizedDistance = normalizedLevenshtein.similarity(title, it.title)
val normalizedDistance = normalizedLevenshtein.similarity(title, it.trueTitle())
SearchEntry(it, normalizedDistance)
}.filter { (_, normalizedDistance) ->
normalizedDistance >= MIN_NORMAL_ELIGIBLE_THRESHOLD

View File

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

View File

@ -22,6 +22,16 @@ interface SManga : Serializable {
var initialized: Boolean
fun customTitle(): String {
val splitTitle = title.split("")
return splitTitle.first()
}
fun trueTitle(): String {
val splitTitle = title.split("")
return splitTitle.last()
}
fun copyFrom(other: SManga) {
if (other.author != null)
author = other.author

View File

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

View File

@ -94,7 +94,7 @@ class LibraryCategoryAdapter(val view: LibraryCategoryView) :
"N/A"
}
else -> {
val title = (iFlexible as LibraryItem).manga.title
val title = (iFlexible as LibraryItem).manga.customTitle()
if (preferences.removeArticles().getOrDefault())
title.removeArticles().substring(0, 1).toUpperCase(Locale.US)
else title.substring(0, 1).toUpperCase(Locale.US)

View File

@ -35,7 +35,7 @@ class LibraryGridHolder(
// Update the title of the manga.
with(title) {
visibility = if (item.manga.hide_title) View.GONE else View.VISIBLE
text = item.manga.title
text = item.manga.customTitle()
}
gradient.visibility = if (item.manga.hide_title) View.GONE else View.VISIBLE

View File

@ -33,7 +33,7 @@ class LibraryListHolder(
*/
override fun onSetValues(item: LibraryItem) {
// Update the title of the manga.
title.text = item.manga.title
title.text = item.manga.customTitle()
// Update the unread count and its visibility.
with(unread_text) {

View File

@ -280,8 +280,8 @@ class LibraryPresenter(
private fun sortAlphabetical(i1: LibraryItem, i2: LibraryItem): Int {
return if (preferences.removeArticles().getOrDefault())
i1.manga.title.removeArticles().compareTo(i2.manga.title.removeArticles(), true)
else i1.manga.title.compareTo(i2.manga.title, true)
i1.manga.customTitle().removeArticles().compareTo(i2.manga.customTitle().removeArticles(), true)
else i1.manga.customTitle().compareTo(i2.manga.customTitle(), true)
}
/**

View File

@ -116,7 +116,7 @@ class MangaController : RxController, TabbedController {
private var trackingIconSubscription: Subscription? = null
override fun getTitle(): String? {
return manga?.title
return manga?.customTitle()
}
override fun inflateView(inflater: LayoutInflater, container: ViewGroup): View {

View File

@ -0,0 +1,168 @@
package eu.kanade.tachiyomi.ui.manga.info
import android.app.Dialog
import android.os.Bundle
import android.view.View
import com.afollestad.materialdialogs.MaterialDialog
import com.afollestad.materialdialogs.WhichButton
import com.afollestad.materialdialogs.actions.setActionButtonEnabled
import com.afollestad.materialdialogs.customview.customView
import com.jakewharton.rxbinding.widget.itemClicks
import com.jakewharton.rxbinding.widget.textChanges
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.database.DatabaseHelper
import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.data.database.models.Track
import eu.kanade.tachiyomi.data.track.TrackManager
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.manga.track.TrackController
import eu.kanade.tachiyomi.ui.manga.track.TrackSearchAdapter
import eu.kanade.tachiyomi.ui.manga.track.TrackSearchDialog
import eu.kanade.tachiyomi.util.plusAssign
import kotlinx.android.synthetic.main.track_controller.*
import kotlinx.android.synthetic.main.track_search_dialog.view.*
import rx.Subscription
import rx.android.schedulers.AndroidSchedulers
import rx.subscriptions.CompositeSubscription
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import java.util.concurrent.TimeUnit
class EditMangaDialog : DialogController {
private var dialogView: View? = null
private var adapter: TrackSearchAdapter? = null
private var selectedItem: Track? = null
private val manga: Manga
private var subscriptions = CompositeSubscription()
private var searchTextSubscription: Subscription? = null
private val trackController
get() = targetController as TrackController
private var wasPreviouslyTracked:Boolean = false
constructor(target: TrackController, manga: Manga, wasTracked:Boolean) : super(Bundle()
.apply {
putLong(KEY_MANGA, manga.id!!)
}) {
wasPreviouslyTracked = wasTracked
targetController = target
this.manga = manga
}
@Suppress("unused")
constructor(bundle: Bundle) : super(bundle) {
manga = Injekt.get<DatabaseHelper>().getManga(bundle.getLong(KEY_MANGA))
.executeAsBlocking()!!
}
override fun onCreateDialog(savedViewState: Bundle?): Dialog {
val dialog = MaterialDialog(activity!!).apply {
customView(viewRes = R.layout.track_search_dialog, scrollable = false)
negativeButton(android.R.string.cancel)
positiveButton(
if (wasPreviouslyTracked) R.string.action_clear
else R.string.action_track){ onPositiveButtonClick() }
setActionButtonEnabled(WhichButton.POSITIVE, wasPreviouslyTracked)
}
if (subscriptions.isUnsubscribed) {
subscriptions = CompositeSubscription()
}
dialogView = dialog.view
onViewCreated(dialog.view, savedViewState)
return dialog
}
fun onViewCreated(view: View, savedState: Bundle?) {
// Create adapter
val adapter = TrackSearchAdapter(view.context)
this.adapter = adapter
view.track_search_list.adapter = adapter
// Set listeners
selectedItem = null
subscriptions += view.track_search_list.itemClicks().subscribe { position ->
selectedItem = adapter.getItem(position)
(dialog as? MaterialDialog)?.positiveButton(R.string.action_track)
(dialog as? MaterialDialog)?.setActionButtonEnabled(WhichButton.POSITIVE, true)
}
// Do an initial search based on the manga's title
if (savedState == null) {
val title = trackController.presenter.manga.trueTitle()
view.track_search.append(title)
search(title)
}
}
override fun onDestroyView(view: View) {
super.onDestroyView(view)
subscriptions.unsubscribe()
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() {
//trackController.swipe_refresh.isRefreshing = true
//trackController.presenter.registerTracking(selectedItem, service)
}
private companion object {
const val KEY_MANGA = "manga_id"
}
}

View File

@ -181,7 +181,8 @@ class MangaInfoController : NucleusController<MangaInfoPresenter>(),
shortAnimationDuration = resources?.getInteger(android.R.integer.config_shortAnimTime) ?: 0
manga_cover.longClicks().subscribeUntilDestroy {
copyToClipboard(view.context.getString(R.string.title), presenter.manga.title, R.string.manga_info_full_title_label)
copyToClipboard(view.context.getString(R.string.title), presenter.manga.customTitle(), R.string
.manga_info_full_title_label)
}
container = (view as ViewGroup).findViewById(R.id.manga_info_layout) as? View
val bottomM = manga_genres_tags.marginBottom
@ -222,6 +223,7 @@ class MangaInfoController : NucleusController<MangaInfoPresenter>(),
override fun onOptionsItemSelected(item: MenuItem): Boolean {
when (item.itemId) {
R.id.action_edit -> { }
R.id.action_open_in_browser -> openInBrowser()
R.id.action_open_in_web_view -> openInWebView()
R.id.action_share -> prepareToShareManga()
@ -260,10 +262,10 @@ class MangaInfoController : NucleusController<MangaInfoPresenter>(),
val view = view ?: return
//update full title TextView.
manga_full_title.text = if (manga.title.isBlank()) {
manga_full_title.text = if (manga.customTitle().isBlank()) {
view.context.getString(R.string.unknown)
} else {
manga.title
manga.customTitle()
}
// Update artist TextView.
@ -395,7 +397,7 @@ class MangaInfoController : NucleusController<MangaInfoPresenter>(),
}
val activity = activity ?: return
val intent = WebViewActivity.newIntent(activity, source.id, url, presenter.manga.title)
val intent = WebViewActivity.newIntent(activity, source.id, url, presenter.manga.trueTitle())
startActivity(intent)
}
@ -431,7 +433,7 @@ class MangaInfoController : NucleusController<MangaInfoPresenter>(),
val intent = Intent(Intent.ACTION_SEND).apply {
type = "text/*"
putExtra(Intent.EXTRA_TEXT, url)
putExtra(Intent.EXTRA_TITLE, presenter.manga.title)
putExtra(Intent.EXTRA_TITLE, presenter.manga.customTitle())
flags = Intent.FLAG_GRANT_READ_URI_PERMISSION
if (stream != null) {
clipData = ClipData.newRawUri(null, stream)
@ -708,11 +710,11 @@ class MangaInfoController : NucleusController<MangaInfoPresenter>(),
// Check if shortcut placement is supported
if (ShortcutManagerCompat.isRequestPinShortcutSupported(activity)) {
val shortcutId = "manga-shortcut-${presenter.manga.title}-${presenter.source.name}"
val shortcutId = "manga-shortcut-${presenter.manga.trueTitle()}-${presenter.source.name}"
// Create shortcut info
val shortcutInfo = ShortcutInfoCompat.Builder(activity, shortcutId)
.setShortLabel(presenter.manga.title)
.setShortLabel(presenter.manga.customTitle())
.setIcon(IconCompat.createWithBitmap(icon))
.setIntent(shortcutIntent)
.build()

View File

@ -148,7 +148,7 @@ class MangaInfoPresenter(
directory.mkdirs()
// Build destination file.
val filename = DiskUtil.buildValidFilename("${manga.title} - Cover.jpg")
val filename = DiskUtil.buildValidFilename("${manga.trueTitle()} - Cover.jpg")
val destFile = File(directory, filename)
val stream: OutputStream = FileOutputStream(destFile)

View File

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

View File

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

View File

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

View File

@ -22,11 +22,4 @@ class SearchPresenter(
//Set the catalogue search item as highlighted if the source matches that of the selected manga
return CatalogueSearchItem(source, results, source.id == manga.source)
}
override fun networkToLocalManga(sManga: SManga, sourceId: Long): Manga {
val localManga = super.networkToLocalManga(sManga, sourceId)
// For migration, displayed title should always match source rather than local DB
localManga.title = sManga.title
return localManga
}
}

View File

@ -154,7 +154,8 @@ class MigrationListController(bundle: Bundle? = null) : BaseController(bundle),
/* val searchResult = if (useSmartSearch) {
smartSearchEngine.smartSearch(source, mangaObj.title)
} else {*/
val searchResult = smartSearchEngine.normalSearch(source, mangaObj.title)
val searchResult = smartSearchEngine
.normalSearch(source, mangaObj.trueTitle())
if(searchResult != null) {
val localManga = smartSearchEngine.networkToLocalManga(searchResult, source.id)
@ -183,8 +184,8 @@ class MigrationListController(bundle: Bundle? = null) : BaseController(bundle),
} else {
validSources.forEachIndexed { index, source ->
val searchResult = try {
val searchResult = smartSearchEngine.normalSearch(source,
mangaObj.title)
val searchResult = smartSearchEngine
.normalSearch(source, mangaObj.trueTitle())
if (searchResult != null) {
val localManga = smartSearchEngine.networkToLocalManga(searchResult, source.id)

View File

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

View File

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

View File

@ -444,7 +444,7 @@ class ReaderPresenter(
// Build destination file.
val filename = DiskUtil.buildValidFilename(
"${manga.title} - ${chapter.name}".take(225)
"${manga.customTitle()} - ${chapter.name}".take(225)
) + " - ${page.number}.${type.extension}"
val destFile = File(directory, filename)

View File

@ -61,7 +61,7 @@ class RecentChapterHolder(private val view: View, private val adapter: RecentCha
chapter_title.text = item.chapter.name
// Set manga title
manga_title.text = item.manga.title
manga_title.text = item.manga.customTitle()
// 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))

View File

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

View File

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

View File

@ -0,0 +1,5 @@
<vector android:height="24dp" android:tint="#FFFFFF"
android:viewportHeight="24.0" android:viewportWidth="24.0"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#FF000000" 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

@ -2,6 +2,12 @@
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/action_edit"
android:icon="@drawable/ic_edit_white_24dp"
android:title="@string/action_edit"
app:showAsAction="ifRoom" />
<item
android:id="@+id/action_share"
android:icon="@drawable/ic_share_white_24dp"