From b03ebc1fa4d2fd4321ff736ddd9eda239fa2fdde Mon Sep 17 00:00:00 2001 From: arkon Date: Thu, 3 Jun 2021 23:00:41 -0400 Subject: [PATCH 1/4] Update tablet UI - Only used when width is >= 720dp instead of 600dp (typically 10" tablets) - Fix fastscroll in manga view (fixes #5267) --- app/build.gradle.kts | 2 +- .../util/system/ContextExtensions.kt | 4 ++-- .../main_activity.xml | 1 + .../manga_controller.xml | 21 ++++++++----------- 4 files changed, 13 insertions(+), 15 deletions(-) rename app/src/main/res/{layout-sw600dp => layout-w720dp}/main_activity.xml (99%) rename app/src/main/res/{layout-sw600dp => layout-w720dp}/manga_controller.xml (84%) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 0b4857128..aca12c678 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -156,7 +156,7 @@ dependencies { implementation("androidx.work:work-runtime-ktx:2.7.0-alpha04") // UI library - implementation("com.google.android.material:material:1.4.0-beta01") + implementation("com.google.android.material:material:1.4.0-rc01") "standardImplementation"("com.google.firebase:firebase-core:19.0.0") diff --git a/app/src/main/java/eu/kanade/tachiyomi/util/system/ContextExtensions.kt b/app/src/main/java/eu/kanade/tachiyomi/util/system/ContextExtensions.kt index 9f6ddb878..39878510f 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/util/system/ContextExtensions.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/util/system/ContextExtensions.kt @@ -271,8 +271,8 @@ fun Context.createFileInCacheDir(name: String): File { } /** - * We consider anything with a width of >= 600dp as a tablet, i.e. with layouts in layout-sw600dp. + * We consider anything with a width of >= 720dp as a tablet, i.e. with layouts in layout-w720dp. */ fun Context.isTablet(): Boolean { - return resources.configuration.screenWidthDp >= 600 + return (resources.displayMetrics.widthPixels / resources.displayMetrics.density) >= 720 } diff --git a/app/src/main/res/layout-sw600dp/main_activity.xml b/app/src/main/res/layout-w720dp/main_activity.xml similarity index 99% rename from app/src/main/res/layout-sw600dp/main_activity.xml rename to app/src/main/res/layout-w720dp/main_activity.xml index 9da7d3677..697d50fa2 100644 --- a/app/src/main/res/layout-sw600dp/main_activity.xml +++ b/app/src/main/res/layout-w720dp/main_activity.xml @@ -75,6 +75,7 @@ style="@style/Widget.MaterialComponents.NavigationRailView.Colored" android:layout_width="wrap_content" android:layout_height="0dp" + app:elevation="0dp" app:itemIconTint="@color/nav_selector" app:itemRippleColor="?attr/rippleSecondaryColor" app:itemTextColor="@color/nav_selector" diff --git a/app/src/main/res/layout-sw600dp/manga_controller.xml b/app/src/main/res/layout-w720dp/manga_controller.xml similarity index 84% rename from app/src/main/res/layout-sw600dp/manga_controller.xml rename to app/src/main/res/layout-w720dp/manga_controller.xml index c1a233365..0953a47f3 100644 --- a/app/src/main/res/layout-sw600dp/manga_controller.xml +++ b/app/src/main/res/layout-w720dp/manga_controller.xml @@ -50,18 +50,6 @@ app:layout_constraintTop_toTopOf="parent" tools:listitem="@layout/chapters_item" /> - - + + From 597cec306455678f2cfe2ad2f632688292353b9e Mon Sep 17 00:00:00 2001 From: jobobby04 Date: Fri, 4 Jun 2021 18:50:22 -0400 Subject: [PATCH 2/4] Legacy backup conversion to Kotlin Serialization (#5282) * Legacy backup conversion to Kotlin Serialization * Fix BackupTest compiling --- .../data/backup/legacy/LegacyBackupManager.kt | 61 ++++++++------- .../data/backup/legacy/LegacyBackupRestore.kt | 78 +++++++++---------- .../legacy/LegacyBackupRestoreValidator.kt | 38 +++++---- .../data/backup/legacy/models/Backup.kt | 44 +++++++---- .../legacy/serializer/CategoryTypeAdapter.kt | 31 -------- .../serializer/CategoryTypeSerializer.kt | 49 ++++++++++++ .../legacy/serializer/ChapterTypeAdapter.kt | 59 -------------- .../serializer/ChapterTypeSerializer.kt | 66 ++++++++++++++++ .../legacy/serializer/HistoryTypeAdapter.kt | 32 -------- .../serializer/HistoryTypeSerializer.kt | 41 ++++++++++ .../legacy/serializer/MangaTypeAdapter.kt | 37 --------- .../legacy/serializer/MangaTypeSerializer.kt | 56 +++++++++++++ .../legacy/serializer/TrackTypeAdapter.kt | 59 -------------- .../legacy/serializer/TrackTypeSerializer.kt | 67 ++++++++++++++++ .../tachiyomi/data/backup/BackupTest.kt | 63 ++++++--------- 15 files changed, 416 insertions(+), 365 deletions(-) delete mode 100644 app/src/main/java/eu/kanade/tachiyomi/data/backup/legacy/serializer/CategoryTypeAdapter.kt create mode 100644 app/src/main/java/eu/kanade/tachiyomi/data/backup/legacy/serializer/CategoryTypeSerializer.kt delete mode 100644 app/src/main/java/eu/kanade/tachiyomi/data/backup/legacy/serializer/ChapterTypeAdapter.kt create mode 100644 app/src/main/java/eu/kanade/tachiyomi/data/backup/legacy/serializer/ChapterTypeSerializer.kt delete mode 100644 app/src/main/java/eu/kanade/tachiyomi/data/backup/legacy/serializer/HistoryTypeAdapter.kt create mode 100644 app/src/main/java/eu/kanade/tachiyomi/data/backup/legacy/serializer/HistoryTypeSerializer.kt delete mode 100644 app/src/main/java/eu/kanade/tachiyomi/data/backup/legacy/serializer/MangaTypeAdapter.kt create mode 100644 app/src/main/java/eu/kanade/tachiyomi/data/backup/legacy/serializer/MangaTypeSerializer.kt delete mode 100644 app/src/main/java/eu/kanade/tachiyomi/data/backup/legacy/serializer/TrackTypeAdapter.kt create mode 100644 app/src/main/java/eu/kanade/tachiyomi/data/backup/legacy/serializer/TrackTypeSerializer.kt diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/backup/legacy/LegacyBackupManager.kt b/app/src/main/java/eu/kanade/tachiyomi/data/backup/legacy/LegacyBackupManager.kt index 8998f4baf..d03e91a7a 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/backup/legacy/LegacyBackupManager.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/backup/legacy/LegacyBackupManager.kt @@ -2,44 +2,52 @@ package eu.kanade.tachiyomi.data.backup.legacy import android.content.Context import android.net.Uri -import com.github.salomonbrys.kotson.fromJson -import com.github.salomonbrys.kotson.registerTypeAdapter -import com.github.salomonbrys.kotson.registerTypeHierarchyAdapter -import com.google.gson.Gson -import com.google.gson.GsonBuilder -import com.google.gson.JsonArray import eu.kanade.tachiyomi.data.backup.AbstractBackupManager -import eu.kanade.tachiyomi.data.backup.legacy.models.Backup.CURRENT_VERSION +import eu.kanade.tachiyomi.data.backup.legacy.models.Backup.Companion.CURRENT_VERSION import eu.kanade.tachiyomi.data.backup.legacy.models.DHistory -import eu.kanade.tachiyomi.data.backup.legacy.serializer.CategoryTypeAdapter -import eu.kanade.tachiyomi.data.backup.legacy.serializer.ChapterTypeAdapter -import eu.kanade.tachiyomi.data.backup.legacy.serializer.HistoryTypeAdapter -import eu.kanade.tachiyomi.data.backup.legacy.serializer.MangaTypeAdapter -import eu.kanade.tachiyomi.data.backup.legacy.serializer.TrackTypeAdapter -import eu.kanade.tachiyomi.data.database.models.CategoryImpl +import eu.kanade.tachiyomi.data.backup.legacy.serializer.CategoryImplTypeSerializer +import eu.kanade.tachiyomi.data.backup.legacy.serializer.CategoryTypeSerializer +import eu.kanade.tachiyomi.data.backup.legacy.serializer.ChapterImplTypeSerializer +import eu.kanade.tachiyomi.data.backup.legacy.serializer.ChapterTypeSerializer +import eu.kanade.tachiyomi.data.backup.legacy.serializer.HistoryTypeSerializer +import eu.kanade.tachiyomi.data.backup.legacy.serializer.MangaImplTypeSerializer +import eu.kanade.tachiyomi.data.backup.legacy.serializer.MangaTypeSerializer +import eu.kanade.tachiyomi.data.backup.legacy.serializer.TrackImplTypeSerializer +import eu.kanade.tachiyomi.data.backup.legacy.serializer.TrackTypeSerializer +import eu.kanade.tachiyomi.data.database.models.Category import eu.kanade.tachiyomi.data.database.models.Chapter -import eu.kanade.tachiyomi.data.database.models.ChapterImpl import eu.kanade.tachiyomi.data.database.models.History import eu.kanade.tachiyomi.data.database.models.Manga import eu.kanade.tachiyomi.data.database.models.MangaCategory -import eu.kanade.tachiyomi.data.database.models.MangaImpl import eu.kanade.tachiyomi.data.database.models.Track -import eu.kanade.tachiyomi.data.database.models.TrackImpl import eu.kanade.tachiyomi.data.database.models.toMangaInfo import eu.kanade.tachiyomi.source.Source import eu.kanade.tachiyomi.source.model.toSManga +import kotlinx.serialization.json.Json +import kotlinx.serialization.modules.SerializersModule +import kotlinx.serialization.modules.contextual import kotlin.math.max class LegacyBackupManager(context: Context, version: Int = CURRENT_VERSION) : AbstractBackupManager(context) { - val parser: Gson = when (version) { - 2 -> GsonBuilder() - .registerTypeAdapter(MangaTypeAdapter.build()) - .registerTypeHierarchyAdapter(ChapterTypeAdapter.build()) - .registerTypeAdapter(CategoryTypeAdapter.build()) - .registerTypeAdapter(HistoryTypeAdapter.build()) - .registerTypeHierarchyAdapter(TrackTypeAdapter.build()) - .create() + val parser: Json = when (version) { + 2 -> Json { + // Forks may have added items to backup + ignoreUnknownKeys = true + + // Register custom serializers + serializersModule = SerializersModule { + contextual(MangaTypeSerializer) + contextual(MangaImplTypeSerializer) + contextual(ChapterTypeSerializer) + contextual(ChapterImplTypeSerializer) + contextual(CategoryTypeSerializer) + contextual(CategoryImplTypeSerializer) + contextual(TrackTypeSerializer) + contextual(TrackImplTypeSerializer) + contextual(HistoryTypeSerializer) + } + } else -> throw Exception("Unknown backup version") } @@ -79,12 +87,11 @@ class LegacyBackupManager(context: Context, version: Int = CURRENT_VERSION) : Ab /** * Restore the categories from Json * - * @param jsonCategories array containing categories + * @param backupCategories array containing categories */ - internal fun restoreCategories(jsonCategories: JsonArray) { + internal fun restoreCategories(backupCategories: List) { // Get categories from file and from db val dbCategories = databaseHelper.getCategories().executeAsBlocking() - val backupCategories = parser.fromJson>(jsonCategories) // Iterate over them backupCategories.forEach { category -> diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/backup/legacy/LegacyBackupRestore.kt b/app/src/main/java/eu/kanade/tachiyomi/data/backup/legacy/LegacyBackupRestore.kt index 5d11f8c5a..19c73228f 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/backup/legacy/LegacyBackupRestore.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/backup/legacy/LegacyBackupRestore.kt @@ -2,88 +2,80 @@ package eu.kanade.tachiyomi.data.backup.legacy import android.content.Context import android.net.Uri -import com.github.salomonbrys.kotson.fromJson -import com.google.gson.JsonArray -import com.google.gson.JsonElement -import com.google.gson.JsonObject -import com.google.gson.JsonParser -import com.google.gson.stream.JsonReader import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.data.backup.AbstractBackupRestore import eu.kanade.tachiyomi.data.backup.BackupNotifier import eu.kanade.tachiyomi.data.backup.legacy.models.Backup -import eu.kanade.tachiyomi.data.backup.legacy.models.Backup.MANGAS import eu.kanade.tachiyomi.data.backup.legacy.models.DHistory +import eu.kanade.tachiyomi.data.backup.legacy.models.MangaObject +import eu.kanade.tachiyomi.data.database.models.Category import eu.kanade.tachiyomi.data.database.models.Chapter -import eu.kanade.tachiyomi.data.database.models.ChapterImpl import eu.kanade.tachiyomi.data.database.models.Manga -import eu.kanade.tachiyomi.data.database.models.MangaImpl import eu.kanade.tachiyomi.data.database.models.Track -import eu.kanade.tachiyomi.data.database.models.TrackImpl import eu.kanade.tachiyomi.source.Source +import kotlinx.serialization.decodeFromString +import kotlinx.serialization.json.Json +import kotlinx.serialization.json.JsonObject +import kotlinx.serialization.json.decodeFromJsonElement +import kotlinx.serialization.json.intOrNull +import kotlinx.serialization.json.jsonPrimitive +import okio.buffer +import okio.source import java.util.Date class LegacyBackupRestore(context: Context, notifier: BackupNotifier) : AbstractBackupRestore(context, notifier) { override suspend fun performRestore(uri: Uri): Boolean { - val reader = JsonReader(context.contentResolver.openInputStream(uri)!!.bufferedReader()) - val json = JsonParser.parseReader(reader).asJsonObject + // Read the json and create a Json Object, + // cannot use the backupManager json deserializer one because its not initialized yet + val backupObject = Json.decodeFromString( + context.contentResolver.openInputStream(uri)!!.source().buffer().use { it.readUtf8() } + ) - val version = json.get(Backup.VERSION)?.asInt ?: 1 + // Get parser version + val version = backupObject["version"]?.jsonPrimitive?.intOrNull ?: 1 + + // Initialize manager backupManager = LegacyBackupManager(context, version) - val mangasJson = json.get(MANGAS).asJsonArray - restoreAmount = mangasJson.size() + 1 // +1 for categories + // Decode the json object to a Backup object + val backup = backupManager.parser.decodeFromJsonElement(backupObject) + + restoreAmount = backup.mangas.size + 1 // +1 for categories // Restore categories - json.get(Backup.CATEGORIES)?.let { restoreCategories(it) } + backup.categories?.let { restoreCategories(it) } // Store source mapping for error messages - sourceMapping = LegacyBackupRestoreValidator.getSourceMapping(json) + sourceMapping = LegacyBackupRestoreValidator.getSourceMapping(backup.extensions ?: emptyList()) // Restore individual manga - mangasJson.forEach { + backup.mangas.forEach { if (job?.isActive != true) { return false } - restoreManga(it.asJsonObject) + restoreManga(it) } return true } - private fun restoreCategories(categoriesJson: JsonElement) { + private fun restoreCategories(categoriesJson: List) { db.inTransaction { - backupManager.restoreCategories(categoriesJson.asJsonArray) + backupManager.restoreCategories(categoriesJson) } restoreProgress += 1 showRestoreProgress(restoreProgress, restoreAmount, context.getString(R.string.categories)) } - private suspend fun restoreManga(mangaJson: JsonObject) { - val manga = backupManager.parser.fromJson( - mangaJson.get( - Backup.MANGA - ) - ) - val chapters = backupManager.parser.fromJson>( - mangaJson.get(Backup.CHAPTERS) - ?: JsonArray() - ) - val categories = backupManager.parser.fromJson>( - mangaJson.get(Backup.CATEGORIES) - ?: JsonArray() - ) - val history = backupManager.parser.fromJson>( - mangaJson.get(Backup.HISTORY) - ?: JsonArray() - ) - val tracks = backupManager.parser.fromJson>( - mangaJson.get(Backup.TRACK) - ?: JsonArray() - ) + private suspend fun restoreManga(mangaJson: MangaObject) { + val manga = mangaJson.manga + val chapters = mangaJson.chapters ?: emptyList() + val categories = mangaJson.categories ?: emptyList() + val history = mangaJson.history ?: emptyList() + val tracks = mangaJson.track ?: emptyList() val source = backupManager.sourceManager.get(manga.source) val sourceName = sourceMapping[manga.source] ?: manga.source.toString() diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/backup/legacy/LegacyBackupRestoreValidator.kt b/app/src/main/java/eu/kanade/tachiyomi/data/backup/legacy/LegacyBackupRestoreValidator.kt index ab757059c..ac6a83bc2 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/backup/legacy/LegacyBackupRestoreValidator.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/backup/legacy/LegacyBackupRestoreValidator.kt @@ -2,12 +2,12 @@ package eu.kanade.tachiyomi.data.backup.legacy import android.content.Context import android.net.Uri -import com.google.gson.JsonObject -import com.google.gson.JsonParser -import com.google.gson.stream.JsonReader import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.data.backup.AbstractBackupRestoreValidator import eu.kanade.tachiyomi.data.backup.legacy.models.Backup +import kotlinx.serialization.decodeFromString +import okio.buffer +import okio.source class LegacyBackupRestoreValidator : AbstractBackupRestoreValidator() { /** @@ -17,30 +17,30 @@ class LegacyBackupRestoreValidator : AbstractBackupRestoreValidator() { * @return List of missing sources or missing trackers. */ override fun validate(context: Context, uri: Uri): Results { - val reader = JsonReader(context.contentResolver.openInputStream(uri)!!.bufferedReader()) - val json = JsonParser.parseReader(reader).asJsonObject + val backupManager = LegacyBackupManager(context) - val version = json.get(Backup.VERSION) - val mangasJson = json.get(Backup.MANGAS) - if (version == null || mangasJson == null) { + val backup = backupManager.parser.decodeFromString( + context.contentResolver.openInputStream(uri)!!.source().buffer().use { it.readUtf8() } + ) + + if (backup.version == null) { throw Exception(context.getString(R.string.invalid_backup_file_missing_data)) } - val mangas = mangasJson.asJsonArray - if (mangas.size() == 0) { + if (backup.mangas.isEmpty()) { throw Exception(context.getString(R.string.invalid_backup_file_missing_manga)) } - val sources = getSourceMapping(json) + val sources = getSourceMapping(backup.extensions ?: emptyList()) val missingSources = sources .filter { sourceManager.get(it.key) == null } .values .sorted() - val trackers = mangas - .filter { it.asJsonObject.has("track") } - .flatMap { it.asJsonObject["track"].asJsonArray } - .map { it.asJsonObject["s"].asInt } + val trackers = backup.mangas + .filterNot { it.track.isNullOrEmpty() } + .flatMap { it.track ?: emptyList() } + .map { it.sync_id } .distinct() val missingTrackers = trackers .mapNotNull { trackManager.getService(it) } @@ -52,12 +52,10 @@ class LegacyBackupRestoreValidator : AbstractBackupRestoreValidator() { } companion object { - fun getSourceMapping(json: JsonObject): Map { - val extensionsMapping = json.get(Backup.EXTENSIONS) ?: return emptyMap() - - return extensionsMapping.asJsonArray + fun getSourceMapping(extensionsMapping: List): Map { + return extensionsMapping .map { - val items = it.asString.split(":") + val items = it.split(":") items[0].toLong() to items[1] } .toMap() diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/backup/legacy/models/Backup.kt b/app/src/main/java/eu/kanade/tachiyomi/data/backup/legacy/models/Backup.kt index 32dfa9245..2766eeecc 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/backup/legacy/models/Backup.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/backup/legacy/models/Backup.kt @@ -1,25 +1,37 @@ package eu.kanade.tachiyomi.data.backup.legacy.models +import eu.kanade.tachiyomi.data.database.models.Category +import eu.kanade.tachiyomi.data.database.models.Chapter +import eu.kanade.tachiyomi.data.database.models.Manga +import eu.kanade.tachiyomi.data.database.models.Track +import kotlinx.serialization.Contextual +import kotlinx.serialization.Serializable import java.text.SimpleDateFormat import java.util.Date import java.util.Locale -/** - * Json values - */ -object Backup { - const val CURRENT_VERSION = 2 - const val MANGA = "manga" - const val MANGAS = "mangas" - const val TRACK = "track" - const val CHAPTERS = "chapters" - const val CATEGORIES = "categories" - const val EXTENSIONS = "extensions" - const val HISTORY = "history" - const val VERSION = "version" +@Serializable +data class Backup( + val version: Int? = null, + var mangas: MutableList = mutableListOf(), + var categories: List<@Contextual Category>? = null, + var extensions: List? = null +) { + companion object { + const val CURRENT_VERSION = 2 - fun getDefaultFilename(): String { - val date = SimpleDateFormat("yyyy-MM-dd_HH-mm", Locale.getDefault()).format(Date()) - return "tachiyomi_$date.json" + fun getDefaultFilename(): String { + val date = SimpleDateFormat("yyyy-MM-dd_HH-mm", Locale.getDefault()).format(Date()) + return "tachiyomi_$date.json" + } } } + +@Serializable +data class MangaObject( + var manga: @Contextual Manga, + var chapters: List<@Contextual Chapter>? = null, + var categories: List? = null, + var track: List<@Contextual Track>? = null, + var history: List<@Contextual DHistory>? = null +) diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/backup/legacy/serializer/CategoryTypeAdapter.kt b/app/src/main/java/eu/kanade/tachiyomi/data/backup/legacy/serializer/CategoryTypeAdapter.kt deleted file mode 100644 index d346af19c..000000000 --- a/app/src/main/java/eu/kanade/tachiyomi/data/backup/legacy/serializer/CategoryTypeAdapter.kt +++ /dev/null @@ -1,31 +0,0 @@ -package eu.kanade.tachiyomi.data.backup.legacy.serializer - -import com.github.salomonbrys.kotson.typeAdapter -import com.google.gson.TypeAdapter -import eu.kanade.tachiyomi.data.database.models.CategoryImpl - -/** - * JSON Serializer used to write / read [CategoryImpl] to / from json - */ -object CategoryTypeAdapter { - - fun build(): TypeAdapter { - return typeAdapter { - write { - beginArray() - value(it.name) - value(it.order) - endArray() - } - - read { - beginArray() - val category = CategoryImpl() - category.name = nextString() - category.order = nextInt() - endArray() - category - } - } - } -} diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/backup/legacy/serializer/CategoryTypeSerializer.kt b/app/src/main/java/eu/kanade/tachiyomi/data/backup/legacy/serializer/CategoryTypeSerializer.kt new file mode 100644 index 000000000..b5f52a1bb --- /dev/null +++ b/app/src/main/java/eu/kanade/tachiyomi/data/backup/legacy/serializer/CategoryTypeSerializer.kt @@ -0,0 +1,49 @@ +package eu.kanade.tachiyomi.data.backup.legacy.serializer + +import eu.kanade.tachiyomi.data.database.models.Category +import eu.kanade.tachiyomi.data.database.models.CategoryImpl +import kotlinx.serialization.KSerializer +import kotlinx.serialization.descriptors.SerialDescriptor +import kotlinx.serialization.descriptors.buildClassSerialDescriptor +import kotlinx.serialization.encoding.Decoder +import kotlinx.serialization.encoding.Encoder +import kotlinx.serialization.json.JsonDecoder +import kotlinx.serialization.json.JsonEncoder +import kotlinx.serialization.json.add +import kotlinx.serialization.json.buildJsonArray +import kotlinx.serialization.json.int +import kotlinx.serialization.json.jsonArray +import kotlinx.serialization.json.jsonPrimitive + +/** + * JSON Serializer used to write / read [CategoryImpl] to / from json + */ +open class CategoryBaseSerializer : KSerializer { + override val descriptor: SerialDescriptor = buildClassSerialDescriptor("Category") + + override fun serialize(encoder: Encoder, value: T) { + encoder as JsonEncoder + encoder.encodeJsonElement( + buildJsonArray { + add(value.name) + add(value.order) + } + ) + } + + @Suppress("UNCHECKED_CAST") + override fun deserialize(decoder: Decoder): T { + // make a category impl and cast as T so that the serializer accepts it + return CategoryImpl().apply { + decoder as JsonDecoder + val array = decoder.decodeJsonElement().jsonArray + name = array[0].jsonPrimitive.content + order = array[1].jsonPrimitive.int + } as T + } +} + +// Allow for serialization of a category and category impl +object CategoryTypeSerializer : CategoryBaseSerializer() + +object CategoryImplTypeSerializer : CategoryBaseSerializer() diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/backup/legacy/serializer/ChapterTypeAdapter.kt b/app/src/main/java/eu/kanade/tachiyomi/data/backup/legacy/serializer/ChapterTypeAdapter.kt deleted file mode 100644 index cacc8cb25..000000000 --- a/app/src/main/java/eu/kanade/tachiyomi/data/backup/legacy/serializer/ChapterTypeAdapter.kt +++ /dev/null @@ -1,59 +0,0 @@ -package eu.kanade.tachiyomi.data.backup.legacy.serializer - -import com.github.salomonbrys.kotson.typeAdapter -import com.google.gson.TypeAdapter -import com.google.gson.stream.JsonToken -import eu.kanade.tachiyomi.data.database.models.ChapterImpl - -/** - * JSON Serializer used to write / read [ChapterImpl] to / from json - */ -object ChapterTypeAdapter { - - private const val URL = "u" - private const val READ = "r" - private const val BOOKMARK = "b" - private const val LAST_READ = "l" - - fun build(): TypeAdapter { - return typeAdapter { - write { - if (it.read || it.bookmark || it.last_page_read != 0) { - beginObject() - name(URL) - value(it.url) - if (it.read) { - name(READ) - value(1) - } - if (it.bookmark) { - name(BOOKMARK) - value(1) - } - if (it.last_page_read != 0) { - name(LAST_READ) - value(it.last_page_read) - } - endObject() - } - } - - read { - val chapter = ChapterImpl() - beginObject() - while (hasNext()) { - if (peek() == JsonToken.NAME) { - when (nextName()) { - URL -> chapter.url = nextString() - READ -> chapter.read = nextInt() == 1 - BOOKMARK -> chapter.bookmark = nextInt() == 1 - LAST_READ -> chapter.last_page_read = nextInt() - } - } - } - endObject() - chapter - } - } - } -} diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/backup/legacy/serializer/ChapterTypeSerializer.kt b/app/src/main/java/eu/kanade/tachiyomi/data/backup/legacy/serializer/ChapterTypeSerializer.kt new file mode 100644 index 000000000..02f52362f --- /dev/null +++ b/app/src/main/java/eu/kanade/tachiyomi/data/backup/legacy/serializer/ChapterTypeSerializer.kt @@ -0,0 +1,66 @@ +package eu.kanade.tachiyomi.data.backup.legacy.serializer + +import eu.kanade.tachiyomi.data.database.models.Chapter +import eu.kanade.tachiyomi.data.database.models.ChapterImpl +import kotlinx.serialization.KSerializer +import kotlinx.serialization.descriptors.buildClassSerialDescriptor +import kotlinx.serialization.encoding.Decoder +import kotlinx.serialization.encoding.Encoder +import kotlinx.serialization.json.JsonDecoder +import kotlinx.serialization.json.JsonEncoder +import kotlinx.serialization.json.buildJsonObject +import kotlinx.serialization.json.intOrNull +import kotlinx.serialization.json.jsonObject +import kotlinx.serialization.json.jsonPrimitive +import kotlinx.serialization.json.put + +/** + * JSON Serializer used to write / read [ChapterImpl] to / from json + */ +open class ChapterBaseSerializer : KSerializer { + + override val descriptor = buildClassSerialDescriptor("Chapter") + + override fun serialize(encoder: Encoder, value: T) { + encoder as JsonEncoder + encoder.encodeJsonElement( + buildJsonObject { + put(URL, value.url) + if (value.read) { + put(READ, 1) + } + if (value.bookmark) { + put(BOOKMARK, 1) + } + if (value.last_page_read != 0) { + put(LAST_READ, value.last_page_read) + } + } + ) + } + + @Suppress("UNCHECKED_CAST") + override fun deserialize(decoder: Decoder): T { + // make a chapter impl and cast as T so that the serializer accepts it + return ChapterImpl().apply { + decoder as JsonDecoder + val jsonObject = decoder.decodeJsonElement().jsonObject + url = jsonObject[URL]!!.jsonPrimitive.content + read = jsonObject[READ]?.jsonPrimitive?.intOrNull == 1 + bookmark = jsonObject[BOOKMARK]?.jsonPrimitive?.intOrNull == 1 + last_page_read = jsonObject[LAST_READ]?.jsonPrimitive?.intOrNull ?: last_page_read + } as T + } + + companion object { + private const val URL = "u" + private const val READ = "r" + private const val BOOKMARK = "b" + private const val LAST_READ = "l" + } +} + +// Allow for serialization of a chapter and chapter impl +object ChapterTypeSerializer : ChapterBaseSerializer() + +object ChapterImplTypeSerializer : ChapterBaseSerializer() diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/backup/legacy/serializer/HistoryTypeAdapter.kt b/app/src/main/java/eu/kanade/tachiyomi/data/backup/legacy/serializer/HistoryTypeAdapter.kt deleted file mode 100644 index 4f7d5d9ff..000000000 --- a/app/src/main/java/eu/kanade/tachiyomi/data/backup/legacy/serializer/HistoryTypeAdapter.kt +++ /dev/null @@ -1,32 +0,0 @@ -package eu.kanade.tachiyomi.data.backup.legacy.serializer - -import com.github.salomonbrys.kotson.typeAdapter -import com.google.gson.TypeAdapter -import eu.kanade.tachiyomi.data.backup.legacy.models.DHistory - -/** - * JSON Serializer used to write / read [DHistory] to / from json - */ -object HistoryTypeAdapter { - - fun build(): TypeAdapter { - return typeAdapter { - write { - if (it.lastRead != 0L) { - beginArray() - value(it.url) - value(it.lastRead) - endArray() - } - } - - read { - beginArray() - val url = nextString() - val lastRead = nextLong() - endArray() - DHistory(url, lastRead) - } - } - } -} diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/backup/legacy/serializer/HistoryTypeSerializer.kt b/app/src/main/java/eu/kanade/tachiyomi/data/backup/legacy/serializer/HistoryTypeSerializer.kt new file mode 100644 index 000000000..ba19803ed --- /dev/null +++ b/app/src/main/java/eu/kanade/tachiyomi/data/backup/legacy/serializer/HistoryTypeSerializer.kt @@ -0,0 +1,41 @@ +package eu.kanade.tachiyomi.data.backup.legacy.serializer + +import eu.kanade.tachiyomi.data.backup.legacy.models.DHistory +import kotlinx.serialization.KSerializer +import kotlinx.serialization.descriptors.SerialDescriptor +import kotlinx.serialization.descriptors.buildClassSerialDescriptor +import kotlinx.serialization.encoding.Decoder +import kotlinx.serialization.encoding.Encoder +import kotlinx.serialization.json.JsonDecoder +import kotlinx.serialization.json.JsonEncoder +import kotlinx.serialization.json.add +import kotlinx.serialization.json.buildJsonArray +import kotlinx.serialization.json.jsonArray +import kotlinx.serialization.json.jsonPrimitive +import kotlinx.serialization.json.long + +/** + * JSON Serializer used to write / read [DHistory] to / from json + */ +object HistoryTypeSerializer : KSerializer { + override val descriptor: SerialDescriptor = buildClassSerialDescriptor("History") + + override fun serialize(encoder: Encoder, value: DHistory) { + encoder as JsonEncoder + encoder.encodeJsonElement( + buildJsonArray { + add(value.url) + add(value.lastRead) + } + ) + } + + override fun deserialize(decoder: Decoder): DHistory { + decoder as JsonDecoder + val array = decoder.decodeJsonElement().jsonArray + return DHistory( + url = array[0].jsonPrimitive.content, + lastRead = array[1].jsonPrimitive.long + ) + } +} diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/backup/legacy/serializer/MangaTypeAdapter.kt b/app/src/main/java/eu/kanade/tachiyomi/data/backup/legacy/serializer/MangaTypeAdapter.kt deleted file mode 100644 index aaab4c76f..000000000 --- a/app/src/main/java/eu/kanade/tachiyomi/data/backup/legacy/serializer/MangaTypeAdapter.kt +++ /dev/null @@ -1,37 +0,0 @@ -package eu.kanade.tachiyomi.data.backup.legacy.serializer - -import com.github.salomonbrys.kotson.typeAdapter -import com.google.gson.TypeAdapter -import eu.kanade.tachiyomi.data.database.models.MangaImpl - -/** - * JSON Serializer used to write / read [MangaImpl] to / from json - */ -object MangaTypeAdapter { - - fun build(): TypeAdapter { - return typeAdapter { - write { - beginArray() - value(it.url) - value(it.title) - value(it.source) - value(it.viewer_flags) - value(it.chapter_flags) - endArray() - } - - read { - beginArray() - val manga = MangaImpl() - manga.url = nextString() - manga.title = nextString() - manga.source = nextLong() - manga.viewer_flags = nextInt() - manga.chapter_flags = nextInt() - endArray() - manga - } - } - } -} diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/backup/legacy/serializer/MangaTypeSerializer.kt b/app/src/main/java/eu/kanade/tachiyomi/data/backup/legacy/serializer/MangaTypeSerializer.kt new file mode 100644 index 000000000..1224b3cd5 --- /dev/null +++ b/app/src/main/java/eu/kanade/tachiyomi/data/backup/legacy/serializer/MangaTypeSerializer.kt @@ -0,0 +1,56 @@ +package eu.kanade.tachiyomi.data.backup.legacy.serializer + +import eu.kanade.tachiyomi.data.database.models.Manga +import eu.kanade.tachiyomi.data.database.models.MangaImpl +import kotlinx.serialization.KSerializer +import kotlinx.serialization.descriptors.SerialDescriptor +import kotlinx.serialization.descriptors.buildClassSerialDescriptor +import kotlinx.serialization.encoding.Decoder +import kotlinx.serialization.encoding.Encoder +import kotlinx.serialization.json.JsonDecoder +import kotlinx.serialization.json.JsonEncoder +import kotlinx.serialization.json.add +import kotlinx.serialization.json.buildJsonArray +import kotlinx.serialization.json.int +import kotlinx.serialization.json.jsonArray +import kotlinx.serialization.json.jsonPrimitive +import kotlinx.serialization.json.long + +/** + * JSON Serializer used to write / read [MangaImpl] to / from json + */ +open class MangaBaseSerializer : KSerializer { + override val descriptor: SerialDescriptor = buildClassSerialDescriptor("Manga") + + override fun serialize(encoder: Encoder, value: T) { + encoder as JsonEncoder + encoder.encodeJsonElement( + buildJsonArray { + add(value.url) + add(value.title) + add(value.source) + add(value.viewer_flags) + add(value.chapter_flags) + } + ) + } + + @Suppress("UNCHECKED_CAST") + override fun deserialize(decoder: Decoder): T { + // make a manga impl and cast as T so that the serializer accepts it + return MangaImpl().apply { + decoder as JsonDecoder + val array = decoder.decodeJsonElement().jsonArray + url = array[0].jsonPrimitive.content + title = array[1].jsonPrimitive.content + source = array[2].jsonPrimitive.long + viewer_flags = array[3].jsonPrimitive.int + chapter_flags = array[4].jsonPrimitive.int + } as T + } +} + +// Allow for serialization of a manga and manga impl +object MangaTypeSerializer : MangaBaseSerializer() + +object MangaImplTypeSerializer : MangaBaseSerializer() diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/backup/legacy/serializer/TrackTypeAdapter.kt b/app/src/main/java/eu/kanade/tachiyomi/data/backup/legacy/serializer/TrackTypeAdapter.kt deleted file mode 100644 index 84c0cd829..000000000 --- a/app/src/main/java/eu/kanade/tachiyomi/data/backup/legacy/serializer/TrackTypeAdapter.kt +++ /dev/null @@ -1,59 +0,0 @@ -package eu.kanade.tachiyomi.data.backup.legacy.serializer - -import com.github.salomonbrys.kotson.typeAdapter -import com.google.gson.TypeAdapter -import com.google.gson.stream.JsonToken -import eu.kanade.tachiyomi.data.database.models.TrackImpl - -/** - * JSON Serializer used to write / read [TrackImpl] to / from json - */ -object TrackTypeAdapter { - - private const val SYNC = "s" - private const val MEDIA = "r" - private const val LIBRARY = "ml" - private const val TITLE = "t" - private const val LAST_READ = "l" - private const val TRACKING_URL = "u" - - fun build(): TypeAdapter { - return typeAdapter { - write { - beginObject() - name(TITLE) - value(it.title) - name(SYNC) - value(it.sync_id) - name(MEDIA) - value(it.media_id) - name(LIBRARY) - value(it.library_id) - name(LAST_READ) - value(it.last_chapter_read) - name(TRACKING_URL) - value(it.tracking_url) - endObject() - } - - read { - val track = TrackImpl() - beginObject() - while (hasNext()) { - if (peek() == JsonToken.NAME) { - when (nextName()) { - TITLE -> track.title = nextString() - SYNC -> track.sync_id = nextInt() - MEDIA -> track.media_id = nextInt() - LIBRARY -> track.library_id = nextLong() - LAST_READ -> track.last_chapter_read = nextInt() - TRACKING_URL -> track.tracking_url = nextString() - } - } - } - endObject() - track - } - } - } -} diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/backup/legacy/serializer/TrackTypeSerializer.kt b/app/src/main/java/eu/kanade/tachiyomi/data/backup/legacy/serializer/TrackTypeSerializer.kt new file mode 100644 index 000000000..8a08f79ff --- /dev/null +++ b/app/src/main/java/eu/kanade/tachiyomi/data/backup/legacy/serializer/TrackTypeSerializer.kt @@ -0,0 +1,67 @@ +package eu.kanade.tachiyomi.data.backup.legacy.serializer + +import eu.kanade.tachiyomi.data.database.models.Track +import eu.kanade.tachiyomi.data.database.models.TrackImpl +import kotlinx.serialization.KSerializer +import kotlinx.serialization.descriptors.SerialDescriptor +import kotlinx.serialization.descriptors.buildClassSerialDescriptor +import kotlinx.serialization.encoding.Decoder +import kotlinx.serialization.encoding.Encoder +import kotlinx.serialization.json.JsonDecoder +import kotlinx.serialization.json.JsonEncoder +import kotlinx.serialization.json.buildJsonObject +import kotlinx.serialization.json.int +import kotlinx.serialization.json.jsonObject +import kotlinx.serialization.json.jsonPrimitive +import kotlinx.serialization.json.long +import kotlinx.serialization.json.put + +/** + * JSON Serializer used to write / read [TrackImpl] to / from json + */ +open class TrackBaseSerializer : KSerializer { + override val descriptor: SerialDescriptor = buildClassSerialDescriptor("Track") + + override fun serialize(encoder: Encoder, value: T) { + encoder as JsonEncoder + encoder.encodeJsonElement( + buildJsonObject { + put(TITLE, value.title) + put(SYNC, value.sync_id) + put(MEDIA, value.media_id) + put(LIBRARY, value.library_id) + put(LAST_READ, value.last_chapter_read) + put(TRACKING_URL, value.tracking_url) + } + ) + } + + @Suppress("UNCHECKED_CAST") + override fun deserialize(decoder: Decoder): T { + // make a track impl and cast as T so that the serializer accepts it + return TrackImpl().apply { + decoder as JsonDecoder + val jsonObject = decoder.decodeJsonElement().jsonObject + title = jsonObject[TITLE]!!.jsonPrimitive.content + sync_id = jsonObject[SYNC]!!.jsonPrimitive.int + media_id = jsonObject[MEDIA]!!.jsonPrimitive.int + library_id = jsonObject[LIBRARY]!!.jsonPrimitive.long + last_chapter_read = jsonObject[LAST_READ]!!.jsonPrimitive.int + tracking_url = jsonObject[TRACKING_URL]!!.jsonPrimitive.content + } as T + } + + companion object { + private const val SYNC = "s" + private const val MEDIA = "r" + private const val LIBRARY = "ml" + private const val TITLE = "t" + private const val LAST_READ = "l" + private const val TRACKING_URL = "u" + } +} + +// Allow for serialization of a track and track impl +object TrackTypeSerializer : TrackBaseSerializer() + +object TrackImplTypeSerializer : TrackBaseSerializer() diff --git a/app/src/test/java/eu/kanade/tachiyomi/data/backup/BackupTest.kt b/app/src/test/java/eu/kanade/tachiyomi/data/backup/BackupTest.kt index d234fa7eb..c6f415265 100644 --- a/app/src/test/java/eu/kanade/tachiyomi/data/backup/BackupTest.kt +++ b/app/src/test/java/eu/kanade/tachiyomi/data/backup/BackupTest.kt @@ -3,9 +3,6 @@ package eu.kanade.tachiyomi.data.backup import android.app.Application import android.content.Context import android.os.Build -import com.github.salomonbrys.kotson.fromJson -import com.google.gson.JsonArray -import com.google.gson.JsonObject import eu.kanade.tachiyomi.BuildConfig import eu.kanade.tachiyomi.CustomRobolectricGradleTestRunner import eu.kanade.tachiyomi.data.backup.legacy.LegacyBackupManager @@ -17,12 +14,16 @@ import eu.kanade.tachiyomi.data.database.models.Chapter import eu.kanade.tachiyomi.data.database.models.ChapterImpl import eu.kanade.tachiyomi.data.database.models.Manga import eu.kanade.tachiyomi.data.database.models.MangaImpl +import eu.kanade.tachiyomi.data.database.models.Track import eu.kanade.tachiyomi.data.database.models.TrackImpl import eu.kanade.tachiyomi.source.SourceManager import eu.kanade.tachiyomi.source.online.HttpSource import eu.kanade.tachiyomi.ui.reader.setting.OrientationType import eu.kanade.tachiyomi.ui.reader.setting.ReadingModeType import kotlinx.coroutines.runBlocking +import kotlinx.serialization.decodeFromString +import kotlinx.serialization.encodeToString +import kotlinx.serialization.json.buildJsonObject import org.assertj.core.api.Assertions.assertThat import org.junit.Before import org.junit.Test @@ -47,16 +48,10 @@ import uy.kohesive.injekt.api.addSingleton @RunWith(CustomRobolectricGradleTestRunner::class) class BackupTest { // Create root object - var root = JsonObject() + var root = Backup() // Create information object - var information = JsonObject() - - // Create manga array - var mangaEntries = JsonArray() - - // Create category array - var categoryEntries = JsonArray() + var information = buildJsonObject {} lateinit var app: Application lateinit var context: Context @@ -83,11 +78,6 @@ class BackupTest { source = mock(HttpSource::class.java) `when`(legacyBackupManager.sourceManager.get(anyLong())).thenReturn(source) - - root.add(Backup.MANGAS, mangaEntries) - root.add(Backup.CATEGORIES, categoryEntries) - - clearJson() } /** @@ -95,11 +85,8 @@ class BackupTest { */ @Test fun testRestoreEmptyCategory() { - // Create backup of empty database - legacyBackupManager.backupCategories(categoryEntries) - // Restore Json - legacyBackupManager.restoreCategories(categoryEntries) + legacyBackupManager.restoreCategories(root.categories ?: emptyList()) // Check if empty val dbCats = db.getCategories().executeAsBlocking() @@ -115,7 +102,7 @@ class BackupTest { val category = addSingleCategory("category") // Restore Json - legacyBackupManager.restoreCategories(categoryEntries) + legacyBackupManager.restoreCategories(root.categories ?: emptyList()) // Check if successful val dbCats = legacyBackupManager.databaseHelper.getCategories().executeAsBlocking() @@ -139,7 +126,7 @@ class BackupTest { db.insertCategory(category).executeAsBlocking() // Restore Json - legacyBackupManager.restoreCategories(categoryEntries) + legacyBackupManager.restoreCategories(root.categories ?: emptyList()) // Check if successful val dbCats = legacyBackupManager.databaseHelper.getCategories().executeAsBlocking() @@ -167,9 +154,6 @@ class BackupTest { assertThat(favoriteManga[0].readingModeType).isEqualTo(ReadingModeType.VERTICAL.flagValue) assertThat(favoriteManga[0].orientationType).isEqualTo(OrientationType.PORTRAIT.flagValue) - // Update json with all options enabled - mangaEntries.add(legacyBackupManager.backupMangaObject(manga, 1)) - // Change manga in database to default values val dbManga = getSingleManga("One Piece") dbManga.id = manga.id @@ -198,9 +182,9 @@ class BackupTest { // Restore Json // Create JSON from manga to test parser - val json = legacyBackupManager.parser.toJsonTree(manga) + val json = legacyBackupManager.parser.encodeToString(manga) // Restore JSON from manga to test parser - val jsonManga = legacyBackupManager.parser.fromJson(json) + val jsonManga = legacyBackupManager.parser.decodeFromString(json) // Restore manga with fetch observable val networkManga = getSingleManga("One Piece") @@ -237,8 +221,8 @@ class BackupTest { } // Check parser - val chaptersJson = legacyBackupManager.parser.toJsonTree(chapters) - val restoredChapters = legacyBackupManager.parser.fromJson>(chaptersJson) + val chaptersJson = legacyBackupManager.parser.encodeToString(chapters) + val restoredChapters = legacyBackupManager.parser.decodeFromString>(chaptersJson) // Fetch chapters from upstream // Create list @@ -275,8 +259,8 @@ class BackupTest { historyList.add(historyJson) // Check parser - val historyListJson = legacyBackupManager.parser.toJsonTree(historyList) - val history = legacyBackupManager.parser.fromJson>(historyListJson) + val historyListJson = legacyBackupManager.parser.encodeToString(historyList) + val history = legacyBackupManager.parser.decodeFromString>(historyListJson) // Restore categories legacyBackupManager.restoreHistoryForManga(history) @@ -314,8 +298,8 @@ class BackupTest { // Check parser and restore already in database var trackList = listOf(track) // Check parser - var trackListJson = legacyBackupManager.parser.toJsonTree(trackList) - var trackListRestore = legacyBackupManager.parser.fromJson>(trackListJson) + var trackListJson = legacyBackupManager.parser.encodeToString(trackList) + var trackListRestore = legacyBackupManager.parser.decodeFromString>(trackListJson) legacyBackupManager.restoreTrackForManga(manga, trackListRestore) // Assert if restore works. @@ -337,8 +321,8 @@ class BackupTest { trackList = listOf(track2) // Check parser - trackListJson = legacyBackupManager.parser.toJsonTree(trackList) - trackListRestore = legacyBackupManager.parser.fromJson>(trackListJson) + trackListJson = legacyBackupManager.parser.encodeToString(trackList) + trackListRestore = legacyBackupManager.parser.decodeFromString>(trackListJson) legacyBackupManager.restoreTrackForManga(manga2, trackListRestore) // Assert if restore works. @@ -348,16 +332,13 @@ class BackupTest { } private fun clearJson() { - root = JsonObject() - information = JsonObject() - mangaEntries = JsonArray() - categoryEntries = JsonArray() + root = Backup() + information = buildJsonObject {} } private fun addSingleCategory(name: String): Category { val category = Category.create(name) - val catJson = legacyBackupManager.parser.toJsonTree(category) - categoryEntries.add(catJson) + root.categories = listOf(category) return category } From 85a1eb75c95c1b0c1577a6ddeb6d0ee1a55a9201 Mon Sep 17 00:00:00 2001 From: Andreas Date: Sat, 5 Jun 2021 00:51:43 +0200 Subject: [PATCH 3/4] Make cover bigger on tablet UI (#5296) * Make cover bigger on tablet UI Also fix bug when opening from source * Use ISO A5 ratio on tablet UI * Change design * Fix bug that happened when refreshing --- .../ui/manga/info/MangaInfoHeaderAdapter.kt | 23 +- .../res/layout-w720dp/manga_info_header.xml | 282 ++++++++++++++++++ app/src/main/res/layout/manga_info_header.xml | 175 +++++------ .../main/res/values-sw600dp-port/dimens.xml | 4 + app/src/main/res/values/dimens.xml | 2 + .../main/res/xml/manga_info_header_scene.xml | 106 +------ .../xml/manga_info_header_scene_sw600dp.xml | 59 ++++ .../res/xml/manga_summary_section_scene.xml | 142 +++++++++ 8 files changed, 605 insertions(+), 188 deletions(-) create mode 100644 app/src/main/res/layout-w720dp/manga_info_header.xml create mode 100644 app/src/main/res/values-sw600dp-port/dimens.xml create mode 100644 app/src/main/res/xml/manga_info_header_scene_sw600dp.xml create mode 100644 app/src/main/res/xml/manga_summary_section_scene.xml diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/info/MangaInfoHeaderAdapter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/info/MangaInfoHeaderAdapter.kt index 454a1d3cb..5523906c8 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/info/MangaInfoHeaderAdapter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/info/MangaInfoHeaderAdapter.kt @@ -243,7 +243,7 @@ class MangaInfoHeaderAdapter( setFavoriteButtonState(manga.favorite) // Set cover if changed. - listOf(binding.mangaCover, binding.backdrop).forEach { + listOfNotNull(binding.mangaCover, binding.backdrop).forEach { it.loadAny(manga) } @@ -277,7 +277,8 @@ class MangaInfoHeaderAdapter( merge( binding.mangaSummaryText.clicks(), binding.mangaInfoToggleMore.clicks(), - binding.mangaInfoToggleLess.clicks() + binding.mangaInfoToggleLess.clicks(), + binding.mangaSummarySection.clicks() ) .onEach { toggleMangaInfo() } .launchIn(controller.viewScope) @@ -288,6 +289,13 @@ class MangaInfoHeaderAdapter( toggleMangaInfo() initialLoad = false } + + // Refreshes will change the state and it needs to be set to correct state to display correctly + if (binding.mangaSummaryText.maxLines == 2) { + binding.mangaSummarySection.transitionToState(R.id.start) + } else { + binding.mangaSummarySection.transitionToState(R.id.end) + } } } @@ -298,18 +306,17 @@ class MangaInfoHeaderAdapter( private fun toggleMangaInfo() { val isCurrentlyExpanded = binding.mangaSummaryText.maxLines != 2 - binding.mangaInfoToggleMoreScrim.isVisible = isCurrentlyExpanded - binding.mangaInfoToggleMore.isVisible = isCurrentlyExpanded - binding.mangaInfoToggleLess.isVisible = !isCurrentlyExpanded + if (isCurrentlyExpanded) { + binding.mangaSummarySection.transitionToStart() + } else { + binding.mangaSummarySection.transitionToEnd() + } binding.mangaSummaryText.maxLines = if (isCurrentlyExpanded) { 2 } else { Int.MAX_VALUE } - - binding.mangaGenresTagsCompact.isVisible = isCurrentlyExpanded - binding.mangaGenresTagsFullChips.isVisible = !isCurrentlyExpanded } /** diff --git a/app/src/main/res/layout-w720dp/manga_info_header.xml b/app/src/main/res/layout-w720dp/manga_info_header.xml new file mode 100644 index 000000000..299529ef1 --- /dev/null +++ b/app/src/main/res/layout-w720dp/manga_info_header.xml @@ -0,0 +1,282 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/manga_info_header.xml b/app/src/main/res/layout/manga_info_header.xml index 69f85920c..e2b80ec53 100644 --- a/app/src/main/res/layout/manga_info_header.xml +++ b/app/src/main/res/layout/manga_info_header.xml @@ -168,100 +168,107 @@ app:layout_constraintTop_toTopOf="@+id/btn_favorite" tools:visibility="visible" /> - - - - - - - - - + app:layout_constraintTop_toBottomOf="@+id/btn_favorite"> - + + + + + + + + + + + + + + + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@+id/manga_info_toggle_less" + tools:visibility="gone" /> - - - - - + diff --git a/app/src/main/res/values-sw600dp-port/dimens.xml b/app/src/main/res/values-sw600dp-port/dimens.xml new file mode 100644 index 000000000..5fb171c81 --- /dev/null +++ b/app/src/main/res/values-sw600dp-port/dimens.xml @@ -0,0 +1,4 @@ + + + 80dp + \ No newline at end of file diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml index dabe27907..fc0c8c13a 100644 --- a/app/src/main/res/values/dimens.xml +++ b/app/src/main/res/values/dimens.xml @@ -14,4 +14,6 @@ 72dp 16dp + + 128dp diff --git a/app/src/main/res/xml/manga_info_header_scene.xml b/app/src/main/res/xml/manga_info_header_scene.xml index de2df81be..047bed8ef 100644 --- a/app/src/main/res/xml/manga_info_header_scene.xml +++ b/app/src/main/res/xml/manga_info_header_scene.xml @@ -6,62 +6,15 @@ motion:constraintSetEnd="@+id/end" motion:constraintSetStart="@id/start" motion:duration="@android:integer/config_mediumAnimTime"> - - - + - - - - - + @@ -134,19 +94,10 @@ motion:layout_constraintEnd_toEndOf="parent" motion:layout_constraintStart_toStartOf="parent" motion:layout_constraintTop_toBottomOf="@id/manga_cover" /> - - - - - diff --git a/app/src/main/res/xml/manga_info_header_scene_sw600dp.xml b/app/src/main/res/xml/manga_info_header_scene_sw600dp.xml new file mode 100644 index 000000000..f05cd8c4e --- /dev/null +++ b/app/src/main/res/xml/manga_info_header_scene_sw600dp.xml @@ -0,0 +1,59 @@ + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/xml/manga_summary_section_scene.xml b/app/src/main/res/xml/manga_summary_section_scene.xml new file mode 100644 index 000000000..49ddc4381 --- /dev/null +++ b/app/src/main/res/xml/manga_summary_section_scene.xml @@ -0,0 +1,142 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 64f95af3e51bc721db1be137e34007e72ee4aeb9 Mon Sep 17 00:00:00 2001 From: Hunter Nickel Date: Fri, 4 Jun 2021 16:52:30 -0600 Subject: [PATCH 4/4] Add check for backstack size before pushing DownloadController (#5312) --- app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt index c0180966c..2427fc679 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt @@ -172,7 +172,9 @@ class MainActivity : BaseViewBindingActivity() { controller?.showSettingsSheet() } R.id.nav_updates -> { - router.pushController(DownloadController().withFadeTransaction()) + if (router.backstackSize == 1) { + router.pushController(DownloadController().withFadeTransaction()) + } } } }