Migrating titles maintains custom covers

Closes #972

Co-Authored-By: Saud-97 <39028181+Saud-97@users.noreply.github.com>
This commit is contained in:
Jays2Kings 2022-08-21 03:26:05 -04:00
parent 41c085210c
commit 93e56c194a
7 changed files with 107 additions and 72 deletions

View file

@ -135,6 +135,7 @@ dependencies {
implementation("androidx.palette:palette:1.0.0") implementation("androidx.palette:palette:1.0.0")
implementation("androidx.core:core-ktx:1.8.0") implementation("androidx.core:core-ktx:1.8.0")
implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.5.0") implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.5.0")
implementation("com.google.android.flexbox:flexbox:3.0.0")
implementation("androidx.constraintlayout:constraintlayout:2.1.4") implementation("androidx.constraintlayout:constraintlayout:2.1.4")

View file

@ -1,20 +1,27 @@
package eu.kanade.tachiyomi.ui.migration package eu.kanade.tachiyomi.ui.migration
import android.content.Context
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.cache.CoverCache
import eu.kanade.tachiyomi.data.database.DatabaseHelper
import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.util.system.toInt
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import uy.kohesive.injekt.injectLazy
object MigrationFlags { object MigrationFlags {
const val CHAPTERS = 0b001 private const val CHAPTERS = 0b0001
const val CATEGORIES = 0b010 private const val CATEGORIES = 0b0010
const val TRACK = 0b100 private const val TRACK = 0b0100
private const val CUSTOM_COVER = 0b1000
private const val CHAPTERS2 = 0x1 private val coverCache: CoverCache by injectLazy()
private const val CATEGORIES2 = 0x2 private val db: DatabaseHelper = Injekt.get()
private const val TRACK2 = 0x4
val titles get() = arrayOf(R.string.chapters, R.string.categories, R.string.tracking) val titles get() = arrayOf(R.string.chapters, R.string.categories, R.string.tracking, R.string.custom_cover)
val flags get() = arrayOf(CHAPTERS, CATEGORIES, TRACK, CUSTOM_COVER)
val flags get() = arrayOf(CHAPTERS, CATEGORIES, TRACK)
fun hasChapters(value: Int): Boolean { fun hasChapters(value: Int): Boolean {
return value and CHAPTERS != 0 return value and CHAPTERS != 0
@ -28,11 +35,52 @@ object MigrationFlags {
return value and TRACK != 0 return value and TRACK != 0
} }
fun getEnabledFlagsPositions(value: Int): List<Int> { fun hasCustomCover(value: Int): Boolean {
return flags.mapIndexedNotNull { index, flag -> if (value and flag != 0) index else null } return value and CUSTOM_COVER != 0
} }
fun getFlagsFromPositions(positions: Array<Int>): Int { fun getEnabledFlags(value: Int): List<Boolean> {
return positions.fold(0) { accumulated, position -> accumulated or (1 shl position) } return flags.map { flag -> value and flag != 0 }
}
fun getFlagsFromPositions(positions: Array<Boolean>, manga: Manga?): Int {
val flags = flags(manga)
return positions.foldIndexed(0) { index, accumulated, enabled ->
accumulated or (if (enabled) flags[index] else 0)
}
}
fun getFlagsFromPositions(positions: Array<Boolean>): Int {
return positions.foldIndexed(0) { index, accumulated, enabled ->
accumulated or (enabled.toInt() shl index)
}
}
fun flags(manga: Manga?): Array<Int> {
val flags = arrayOf(CHAPTERS, CATEGORIES).toMutableList()
if (manga != null) {
if (db.getTracks(manga).executeAsBlocking().isNotEmpty()) {
flags.add(TRACK)
}
if (coverCache.getCustomCoverFile(manga).exists()) {
flags.add(CUSTOM_COVER)
}
}
return flags.toTypedArray()
}
private fun titleForFlag(flag: Int): Int {
return when (flag) {
CHAPTERS -> R.string.chapters
CATEGORIES -> R.string.categories
TRACK -> R.string.tracking
CUSTOM_COVER -> R.string.custom_cover
else -> 0
}
}
fun titles(context: Context, manga: Manga?): Array<String> {
return flags(manga).map { context.getString(titleForFlag(it)) }.toTypedArray()
} }
} }

View file

@ -4,13 +4,17 @@ import android.app.Activity
import android.content.res.Configuration import android.content.res.Configuration
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.ViewGroup
import android.widget.CheckBox
import android.widget.CompoundButton import android.widget.CompoundButton
import android.widget.LinearLayout import android.widget.LinearLayout
import android.widget.RadioButton import android.widget.RadioButton
import android.widget.RadioGroup import android.widget.RadioGroup
import android.widget.Toast import android.widget.Toast
import androidx.constraintlayout.widget.ConstraintLayout import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.view.children
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.core.view.updateLayoutParams
import com.bluelinelabs.conductor.Controller import com.bluelinelabs.conductor.Controller
import com.fredporciuncula.flow.preferences.Preference import com.fredporciuncula.flow.preferences.Preference
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
@ -92,21 +96,23 @@ class MigrationBottomSheetDialog(
private fun initPreferences() { private fun initPreferences() {
val flags = preferences.migrateFlags().get() val flags = preferences.migrateFlags().get()
binding.migChapters.isChecked = MigrationFlags.hasChapters(flags) val enabledFlags = MigrationFlags.getEnabledFlags(flags)
binding.migCategories.isChecked = MigrationFlags.hasCategories(flags) MigrationFlags.titles.forEachIndexed { index, title ->
binding.migTracking.isChecked = MigrationFlags.hasTracks(flags) val checkbox = CheckBox(context)
checkbox.id = title.hashCode()
binding.migChapters.setOnCheckedChangeListener { _, _ -> setFlags() } checkbox.text = context.getString(title)
binding.migCategories.setOnCheckedChangeListener { _, _ -> setFlags() } checkbox.isChecked = enabledFlags[index]
binding.migTracking.setOnCheckedChangeListener { _, _ -> setFlags() } binding.gridFlagsLayout.addView(checkbox)
checkbox.updateLayoutParams<ViewGroup.MarginLayoutParams> {
marginStart = 8.dpToPx
topMargin = 8.dpToPx
}
checkbox.setOnCheckedChangeListener { _, _ -> setFlags() }
}
binding.extraSearchParamText.isVisible = false binding.extraSearchParamText.isVisible = false
binding.extraSearchParam.setOnCheckedChangeListener { _, isChecked -> binding.extraSearchParam.setOnCheckedChangeListener { _, isChecked ->
if (isChecked) { binding.extraSearchParamText.isVisible = isChecked
binding.extraSearchParamText.isVisible = true
} else {
binding.extraSearchParamText.isVisible = false
}
} }
binding.sourceGroup.bindToPreference(preferences.useSourceWithMost()) binding.sourceGroup.bindToPreference(preferences.useSourceWithMost())
@ -120,10 +126,8 @@ class MigrationBottomSheetDialog(
} }
private fun setFlags() { private fun setFlags() {
var flags = 0 val enabledBoxes = binding.gridFlagsLayout.children.toList().filterIsInstance<CheckBox>().map { it.isChecked }
if (binding.migChapters.isChecked) flags = flags or MigrationFlags.CHAPTERS val flags = MigrationFlags.getFlagsFromPositions(enabledBoxes.toTypedArray())
if (binding.migCategories.isChecked) flags = flags or MigrationFlags.CATEGORIES
if (binding.migTracking.isChecked) flags = flags or MigrationFlags.TRACK
preferences.migrateFlags().set(flags) preferences.migrateFlags().set(flags)
} }

View file

@ -2,6 +2,7 @@ package eu.kanade.tachiyomi.ui.migration.manga.process
import android.view.MenuItem import android.view.MenuItem
import eu.davidea.flexibleadapter.FlexibleAdapter import eu.davidea.flexibleadapter.FlexibleAdapter
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.History import eu.kanade.tachiyomi.data.database.models.History
import eu.kanade.tachiyomi.data.database.models.Manga import eu.kanade.tachiyomi.data.database.models.Manga
@ -29,6 +30,7 @@ class MigrationProcessAdapter(
var items: List<MigrationProcessItem> = emptyList() var items: List<MigrationProcessItem> = emptyList()
val preferences: PreferencesHelper by injectLazy() val preferences: PreferencesHelper by injectLazy()
val sourceManager: SourceManager by injectLazy() val sourceManager: SourceManager by injectLazy()
val coverCache: CoverCache by injectLazy()
var showOutline = preferences.outlineOnCovers().get() var showOutline = preferences.outlineOnCovers().get()
val menuItemListener: MigrationProcessInterface = controller val menuItemListener: MigrationProcessInterface = controller
@ -130,7 +132,7 @@ class MigrationProcessAdapter(
) { ) {
if (controller.config == null) return if (controller.config == null) return
val flags = preferences.migrateFlags().get() val flags = preferences.migrateFlags().get()
migrateMangaInternal(flags, db, enhancedServices, prevSource, source, prevManga, manga, replace) migrateMangaInternal(flags, db, enhancedServices, coverCache, prevSource, source, prevManga, manga, replace)
} }
companion object { companion object {
@ -139,6 +141,7 @@ class MigrationProcessAdapter(
flags: Int, flags: Int,
db: DatabaseHelper, db: DatabaseHelper,
enhancedServices: List<EnhancedTrackService>, enhancedServices: List<EnhancedTrackService>,
coverCache: CoverCache,
prevSource: Source?, prevSource: Source?,
source: Source, source: Source,
prevManga: Manga, prevManga: Manga,
@ -206,6 +209,12 @@ class MigrationProcessAdapter(
manga.favorite = true manga.favorite = true
if (replace) manga.date_added = prevManga.date_added if (replace) manga.date_added = prevManga.date_added
else manga.date_added = Date().time else manga.date_added = Date().time
// Update custom cover
if (MigrationFlags.hasCustomCover(flags) && coverCache.getCustomCoverFile(prevManga).exists()) {
coverCache.setCustomCoverToCache(manga, coverCache.getCustomCoverFile(prevManga).inputStream())
}
db.updateMangaFavorite(manga).executeAsBlocking() db.updateMangaFavorite(manga).executeAsBlocking()
db.updateMangaAdded(manga).executeAsBlocking() db.updateMangaAdded(manga).executeAsBlocking()
db.updateMangaTitle(manga).executeAsBlocking() db.updateMangaTitle(manga).executeAsBlocking()

View file

@ -258,17 +258,17 @@ private fun showAddDuplicateDialog(
) { ) {
val source = sourceManager.getOrStub(libraryManga.source) val source = sourceManager.getOrStub(libraryManga.source)
val titles by lazy { MigrationFlags.titles(activity, libraryManga) }
fun migrateManga(mDialog: DialogInterface, replace: Boolean) { fun migrateManga(mDialog: DialogInterface, replace: Boolean) {
val listView = (mDialog as AlertDialog).listView val listView = (mDialog as AlertDialog).listView
var flags = 0 val enabled = titles.indices.map { listView.isItemChecked(it) }.toTypedArray()
if (listView.isItemChecked(0)) flags = flags or MigrationFlags.CHAPTERS val flags = MigrationFlags.getFlagsFromPositions(enabled, libraryManga)
if (listView.isItemChecked(1)) flags = flags or MigrationFlags.CATEGORIES
if (listView.isItemChecked(2)) flags = flags or MigrationFlags.TRACK
val enhancedServices by lazy { Injekt.get<TrackManager>().services.filterIsInstance<EnhancedTrackService>() } val enhancedServices by lazy { Injekt.get<TrackManager>().services.filterIsInstance<EnhancedTrackService>() }
MigrationProcessAdapter.migrateMangaInternal( MigrationProcessAdapter.migrateMangaInternal(
flags, flags,
db, db,
enhancedServices, enhancedServices,
Injekt.get(),
source, source,
sourceManager.getOrStub(newManga.source), sourceManager.getOrStub(newManga.source),
libraryManga, libraryManga,
@ -301,12 +301,9 @@ private fun showAddDuplicateDialog(
activity.materialAlertDialog().apply { activity.materialAlertDialog().apply {
setTitle(R.string.migration) setTitle(R.string.migration)
setMultiChoiceItems( setMultiChoiceItems(
arrayOf( titles,
activity.getString(R.string.chapters), titles.map { true }.toBooleanArray(),
activity.getString(R.string.categories), null,
activity.getString(R.string.tracking),
),
booleanArrayOf(true, true, true), null,
) )
setPositiveButton(R.string.migrate) { mDialog, _ -> setPositiveButton(R.string.migrate) { mDialog, _ ->
migrateManga(mDialog, true) migrateManga(mDialog, true)

View file

@ -34,40 +34,15 @@
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" /> app:layout_constraintTop_toTopOf="parent" />
<CheckBox <com.google.android.flexbox.FlexboxLayout
android:id="@+id/mig_chapters" android:id="@+id/grid_flags_layout"
android:layout_width="wrap_content" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="8dp" android:layout_marginTop="8dp"
android:checked="true" app:flexWrap="wrap"
android:text="@string/chapters"
app:layout_constraintStart_toStartOf="@+id/data_label" app:layout_constraintStart_toStartOf="@+id/data_label"
app:layout_constraintTop_toBottomOf="@+id/data_label"/> app:layout_constraintTop_toBottomOf="@+id/data_label"/>
<CheckBox
android:id="@+id/mig_categories"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:checked="true"
android:text="@string/categories"
app:layout_constraintBottom_toBottomOf="@+id/mig_chapters"
app:layout_constraintStart_toEndOf="@+id/mig_chapters"
app:layout_constraintTop_toTopOf="@+id/mig_chapters" />
<CheckBox
android:id="@+id/mig_tracking"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:checked="true"
android:text="@string/tracking"
app:layout_constraintBottom_toBottomOf="@+id/mig_categories"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toEndOf="@+id/mig_categories"
app:layout_constraintTop_toTopOf="@+id/mig_categories" />
<TextView <TextView
android:id="@+id/options_label" android:id="@+id/options_label"
android:layout_width="wrap_content" android:layout_width="wrap_content"
@ -75,8 +50,8 @@
android:layout_marginTop="8dp" android:layout_marginTop="8dp"
android:text="@string/options" android:text="@string/options"
style="?textAppearanceTitleSmall" style="?textAppearanceTitleSmall"
app:layout_constraintStart_toStartOf="@+id/mig_chapters" app:layout_constraintStart_toStartOf="@+id/grid_flags_layout"
app:layout_constraintTop_toBottomOf="@+id/mig_chapters" /> app:layout_constraintTop_toBottomOf="@+id/grid_flags_layout" />
<RadioGroup <RadioGroup
android:id="@+id/sourceGroup" android:id="@+id/sourceGroup"

View file

@ -564,6 +564,7 @@
<string name="error_saving_cover">Error saving cover</string> <string name="error_saving_cover">Error saving cover</string>
<string name="error_sharing_cover">Error sharing cover</string> <string name="error_sharing_cover">Error sharing cover</string>
<string name="custom_manga_info">Custom manga info</string> <string name="custom_manga_info">Custom manga info</string>
s<string name="custom_cover">Custom cover</string>
<string name="set_as_default">Set as default</string> <string name="set_as_default">Set as default</string>
<string name="filter_groups">Filter scanlator groups</string> <string name="filter_groups">Filter scanlator groups</string>
<plurals name="deleted_chapters"> <plurals name="deleted_chapters">