From a9e629aea6be503a05fd0a17f70e85c5032d66ae Mon Sep 17 00:00:00 2001 From: arkon Date: Tue, 3 May 2022 22:23:28 -0400 Subject: [PATCH] Minor cleanup - Remove some unused StorIO queries - Clean up tall image splitting a bit (no need for creating an unscaled scaled bitmap copy, or tracking coordinates) - Clean up library updater a bit (still needs a lot of work though) --- .../data/database/queries/ChapterQueries.kt | 4 -- .../data/database/queries/HistoryQueries.kt | 11 --- .../data/database/queries/MangaQueries.kt | 12 ---- .../tachiyomi/data/download/Downloader.kt | 68 ++++++++----------- .../data/library/LibraryUpdateService.kt | 54 ++++++--------- .../ui/reader/viewer/pager/PagerPageHolder.kt | 2 +- .../viewer/webtoon/WebtoonPageHolder.kt | 2 +- .../kanade/tachiyomi/util/system/ImageUtil.kt | 44 ++++++------ app/src/main/res/values/strings.xml | 1 + 9 files changed, 72 insertions(+), 126 deletions(-) diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/database/queries/ChapterQueries.kt b/app/src/main/java/eu/kanade/tachiyomi/data/database/queries/ChapterQueries.kt index 5fe19c895e..9e1945f85d 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/database/queries/ChapterQueries.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/database/queries/ChapterQueries.kt @@ -72,12 +72,8 @@ interface ChapterQueries : DbProvider { ) .prepare() - fun insertChapter(chapter: Chapter) = db.put().`object`(chapter).prepare() - fun insertChapters(chapters: List) = db.put().objects(chapters).prepare() - fun deleteChapter(chapter: Chapter) = db.delete().`object`(chapter).prepare() - fun deleteChapters(chapters: List) = db.delete().objects(chapters).prepare() fun updateChaptersBackup(chapters: List) = db.put() diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/database/queries/HistoryQueries.kt b/app/src/main/java/eu/kanade/tachiyomi/data/database/queries/HistoryQueries.kt index 96c23b82a7..86ee8d6045 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/database/queries/HistoryQueries.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/database/queries/HistoryQueries.kt @@ -1,6 +1,5 @@ package eu.kanade.tachiyomi.data.database.queries -import com.pushtorefresh.storio.sqlite.queries.DeleteQuery import com.pushtorefresh.storio.sqlite.queries.RawQuery import eu.kanade.tachiyomi.data.database.DbProvider import eu.kanade.tachiyomi.data.database.models.History @@ -50,14 +49,4 @@ interface HistoryQueries : DbProvider { .objects(historyList) .withPutResolver(HistoryUpsertResolver()) .prepare() - - fun deleteHistoryNoLastRead() = db.delete() - .byQuery( - DeleteQuery.builder() - .table(HistoryTable.TABLE) - .where("${HistoryTable.COL_LAST_READ} = ?") - .whereArgs(0) - .build(), - ) - .prepare() } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/database/queries/MangaQueries.kt b/app/src/main/java/eu/kanade/tachiyomi/data/database/queries/MangaQueries.kt index 04518cf546..d410a090ed 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/database/queries/MangaQueries.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/database/queries/MangaQueries.kt @@ -131,10 +131,6 @@ interface MangaQueries : DbProvider { .withPutResolver(MangaCoverLastModifiedPutResolver()) .prepare() - fun deleteManga(manga: Manga) = db.delete().`object`(manga).prepare() - - fun deleteMangas(mangas: List) = db.delete().objects(mangas).prepare() - fun deleteMangasNotInLibraryBySourceIds(sourceIds: List) = db.delete() .byQuery( DeleteQuery.builder() @@ -145,14 +141,6 @@ interface MangaQueries : DbProvider { ) .prepare() - fun deleteMangas() = db.delete() - .byQuery( - DeleteQuery.builder() - .table(MangaTable.TABLE) - .build(), - ) - .prepare() - fun getLastReadManga() = db.get() .listOfObjects(Manga::class.java) .withQuery( diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/download/Downloader.kt b/app/src/main/java/eu/kanade/tachiyomi/data/download/Downloader.kt index 422aa02df6..dafce33e70 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/download/Downloader.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/download/Downloader.kt @@ -4,7 +4,6 @@ import android.content.Context import android.graphics.Bitmap import android.graphics.BitmapFactory import android.webkit.MimeTypeMap -import androidx.core.graphics.BitmapCompat import com.hippo.unifile.UniFile import com.jakewharton.rxrelay.BehaviorRelay import com.jakewharton.rxrelay.PublishRelay @@ -44,7 +43,6 @@ import uy.kohesive.injekt.injectLazy import java.io.BufferedOutputStream import java.io.File import java.io.FileOutputStream -import java.io.OutputStream import java.util.zip.CRC32 import java.util.zip.ZipEntry import java.util.zip.ZipOutputStream @@ -354,7 +352,7 @@ class Downloader( // Do when page is downloaded. .doOnNext { page -> if (preferences.splitTallImages().get()) { - splitTallImage(page, download, tmpDir) + splitTallImage(page, tmpDir) } notifier.onProgressChange(download) } @@ -560,51 +558,39 @@ class Downloader( /** * Splits tall images to improve performance of reader */ - private fun splitTallImage(page: Page, download: Download, tmpDir: UniFile) { + private fun splitTallImage(page: Page, tmpDir: UniFile) { val filename = String.format("%03d", page.number) - val imageFile = tmpDir.listFiles()!!.find { it.name!!.startsWith("$filename.") } - if (imageFile == null) { - notifier.onError("Error: imageFile was not found", download.chapter.name, download.manga.title) + val imageFile = tmpDir.listFiles()?.find { it.name!!.startsWith("$filename.") } + ?: throw Error(context.getString(R.string.download_notifier_split_page_not_found, page.number)) + + if (isAnimatedAndSupported(imageFile.openInputStream()) || !isTallImage(imageFile.openInputStream())) { return } - if (!isAnimatedAndSupported(imageFile.openInputStream()) && isTallImage(imageFile.openInputStream())) { - // Getting the scaled bitmap of the source image - val bitmap = BitmapFactory.decodeFile(imageFile.filePath) - val scaledBitmap: Bitmap = - BitmapCompat.createScaledBitmap(bitmap, bitmap.width, bitmap.height, null, true) + val bitmap = BitmapFactory.decodeFile(imageFile.filePath) + val splitsCount = bitmap.height / context.resources.displayMetrics.heightPixels + 1 + val heightPerSplit = bitmap.height / splitsCount - val splitsCount: Int = bitmap.height / context.resources.displayMetrics.heightPixels + 1 - val splitHeight = bitmap.height / splitsCount - - // xCoord and yCoord are the pixel positions of the image splits - val xCoord = 0 - var yCoord = 0 - try { - for (i in 0 until splitsCount) { - val splitPath = imageFile.filePath!!.substringBeforeLast(".") + "__${"%03d".format(i + 1)}.jpg" - // Compress the bitmap and save in jpg format - val stream: OutputStream = FileOutputStream(splitPath) - stream.use { - Bitmap.createBitmap( - scaledBitmap, - xCoord, - yCoord, - bitmap.width, - splitHeight, - ).compress(Bitmap.CompressFormat.JPEG, 100, stream) - } - yCoord += splitHeight + try { + (0..splitsCount).forEach { split -> + val splitPath = imageFile.filePath!!.substringBeforeLast(".") + "__${"%03d".format(split + 1)}.jpg" + FileOutputStream(splitPath).use { stream -> + Bitmap.createBitmap( + bitmap, + 0, + split * heightPerSplit, + bitmap.width, + heightPerSplit, + ).compress(Bitmap.CompressFormat.JPEG, 100, stream) } - imageFile.delete() - } catch (e: Exception) { - // Image splits were not successfully saved so delete them and keep the original image - for (i in 0 until splitsCount) { - val splitPath = imageFile.filePath!!.substringBeforeLast(".") + "__${"%03d".format(i + 1)}.jpg" - File(splitPath).delete() - } - throw e } + imageFile.delete() + } catch (e: Exception) { + // Image splits were not successfully saved so delete them and keep the original image + (0..splitsCount) + .map { imageFile.filePath!!.substringBeforeLast(".") + "__${"%03d".format(it + 1)}.jpg" } + .forEach { File(it).delete() } + throw e } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateService.kt b/app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateService.kt index 3e8f188c88..796ca31bd2 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateService.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateService.kt @@ -28,6 +28,7 @@ import eu.kanade.tachiyomi.data.track.TrackService import eu.kanade.tachiyomi.source.SourceManager import eu.kanade.tachiyomi.source.UnmeteredSource import eu.kanade.tachiyomi.source.model.SManga +import eu.kanade.tachiyomi.source.model.toMangaInfo import eu.kanade.tachiyomi.source.model.toSChapter import eu.kanade.tachiyomi.source.model.toSManga import eu.kanade.tachiyomi.util.chapter.NoChaptersException @@ -80,7 +81,7 @@ class LibraryUpdateService( private lateinit var wakeLock: PowerManager.WakeLock private lateinit var notifier: LibraryUpdateNotifier - private lateinit var ioScope: CoroutineScope + private var ioScope: CoroutineScope? = null private var mangaToUpdate: List = mutableListOf() private var updateJob: Job? = null @@ -90,10 +91,8 @@ class LibraryUpdateService( */ enum class Target { CHAPTERS, // Manga chapters - COVERS, // Manga covers - - TRACKING // Tracking metadata + TRACKING, // Tracking metadata } companion object { @@ -161,7 +160,6 @@ class LibraryUpdateService( override fun onCreate() { super.onCreate() - ioScope = CoroutineScope(SupervisorJob() + Dispatchers.IO) notifier = LibraryUpdateNotifier(this) wakeLock = acquireWakeLock(javaClass.name) @@ -174,8 +172,6 @@ class LibraryUpdateService( */ override fun onDestroy() { updateJob?.cancel() - // Despite what Android Studio - // states this can be null ioScope?.cancel() if (wakeLock.isHeld) { wakeLock.release() @@ -189,9 +185,7 @@ class LibraryUpdateService( /** * This method needs to be implemented, but it's not used/needed. */ - override fun onBind(intent: Intent): IBinder? { - return null - } + override fun onBind(intent: Intent): IBinder? = null /** * Method called when the service receives an intent. @@ -210,6 +204,7 @@ class LibraryUpdateService( // Unsubscribe from any previous subscription if needed updateJob?.cancel() + ioScope?.cancel() // Update favorite manga val categoryId = intent.getIntExtra(KEY_CATEGORY, -1) @@ -220,7 +215,8 @@ class LibraryUpdateService( logcat(LogPriority.ERROR, exception) stopSelf(startId) } - updateJob = ioScope.launch(handler) { + ioScope = CoroutineScope(SupervisorJob() + Dispatchers.IO) + updateJob = ioScope?.launch(handler) { when (target) { Target.CHAPTERS -> updateChapterList() Target.COVERS -> updateCovers() @@ -344,16 +340,10 @@ class LibraryUpdateService( } } catch (e: Throwable) { val errorMessage = when (e) { - is NoChaptersException -> { - getString(R.string.no_chapters_error) - } - is SourceManager.SourceNotInstalledException -> { - // failedUpdates will already have the source, don't need to copy it into the message - getString(R.string.loader_not_implemented_error) - } - else -> { - e.message - } + is NoChaptersException -> getString(R.string.no_chapters_error) + // failedUpdates will already have the source, don't need to copy it into the message + is SourceManager.SourceNotInstalledException -> getString(R.string.loader_not_implemented_error) + else -> e.message } failedUpdates.add(mangaWithNotif to errorMessage) } @@ -407,11 +397,12 @@ class LibraryUpdateService( private suspend fun updateManga(manga: Manga): Pair, List> { val source = sourceManager.getOrStub(manga.source) - var networkSManga: SManga? = null + var updatedManga: SManga = manga + // Update manga details metadata if (preferences.autoUpdateMetadata()) { - val updatedManga = source.getMangaDetails(manga.toMangaInfo()) - val sManga = updatedManga.toSManga() + val updatedMangaDetails = source.getMangaDetails(manga.toMangaInfo()) + val sManga = updatedMangaDetails.toSManga() // Avoid "losing" existing cover if (!sManga.thumbnail_url.isNullOrEmpty()) { manga.prepUpdateCover(coverCache, sManga, false) @@ -419,25 +410,22 @@ class LibraryUpdateService( sManga.thumbnail_url = manga.thumbnail_url } - networkSManga = sManga + updatedManga = sManga } - val chapters = source.getChapterList(manga.toMangaInfo()) + val chapters = source.getChapterList(updatedManga.toMangaInfo()) .map { it.toSChapter() } - // Get manga from database to account for if it was removed - // from library or database + // Get manga from database to account for if it was removed during the update val dbManga = db.getManga(manga.id!!).executeAsBlocking() ?: return Pair(emptyList(), emptyList()) // Copy into [dbManga] to retain favourite value - networkSManga?.let { - dbManga.copyFrom(it) - db.insertManga(dbManga).executeAsBlocking() - } + dbManga.copyFrom(updatedManga) + db.insertManga(dbManga).executeAsBlocking() // [dbmanga] was used so that manga data doesn't get overwritten - // incase manga gets new chapter + // in case manga gets new chapter return syncChaptersWithSource(db, chapters, dbManga, source) } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/pager/PagerPageHolder.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/pager/PagerPageHolder.kt index 7cfea4cc92..408b0263c7 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/pager/PagerPageHolder.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/pager/PagerPageHolder.kt @@ -247,7 +247,7 @@ class PagerPageHolder( return splitInHalf(imageStream) } - val isDoublePage = ImageUtil.isDoublePage(imageStream) + val isDoublePage = ImageUtil.isWideImage(imageStream) if (!isDoublePage) { return imageStream } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonPageHolder.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonPageHolder.kt index c8b23b673c..ebeef9ecf6 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonPageHolder.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonPageHolder.kt @@ -277,7 +277,7 @@ class WebtoonPageHolder( return imageStream } - val isDoublePage = ImageUtil.isDoublePage(imageStream) + val isDoublePage = ImageUtil.isWideImage(imageStream) if (!isDoublePage) { return imageStream } diff --git a/app/src/main/java/eu/kanade/tachiyomi/util/system/ImageUtil.kt b/app/src/main/java/eu/kanade/tachiyomi/util/system/ImageUtil.kt index 8e9eeda550..f634dd5d2c 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/util/system/ImageUtil.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/util/system/ImageUtil.kt @@ -99,38 +99,24 @@ object ImageUtil { } /** - * Check whether the image is a double-page spread + * Check whether the image is wide (which we consider a double-page spread). + * * @return true if the width is greater than the height */ - fun isDoublePage(imageStream: InputStream): Boolean { - imageStream.mark(imageStream.available() + 1) - - val imageBytes = imageStream.readBytes() - - val options = BitmapFactory.Options().apply { inJustDecodeBounds = true } - BitmapFactory.decodeByteArray(imageBytes, 0, imageBytes.size, options) - + fun isWideImage(imageStream: InputStream): Boolean { + val options = extractImageOptions(imageStream) imageStream.reset() - return options.outWidth > options.outHeight } /** - * Check whether the image is considered a tall image - * @return true if the height:width ratio is greater than the 3:! + * Check whether the image is considered a tall image. + * + * @return true if the height:width ratio is greater than 3. */ fun isTallImage(imageStream: InputStream): Boolean { - imageStream.mark(imageStream.available() + 1) - - val imageBytes = imageStream.readBytes() - // Checking the image dimensions without loading it in the memory. - val options = BitmapFactory.Options().apply { inJustDecodeBounds = true } - BitmapFactory.decodeByteArray(imageBytes, 0, imageBytes.size, options) - val width = options.outWidth - val height = options.outHeight - val ratio = height / width - - return ratio > 3 + val options = extractImageOptions(imageStream) + return (options.outHeight / options.outWidth) > 3 } /** @@ -410,4 +396,16 @@ object ImageUtil { private fun Int.isWhite(): Boolean = red + blue + green > 740 + + /** + * Used to check an image's dimensions without loading it in the memory. + */ + private fun extractImageOptions(imageStream: InputStream): BitmapFactory.Options { + imageStream.mark(imageStream.available() + 1) + + val imageBytes = imageStream.readBytes() + val options = BitmapFactory.Options().apply { inJustDecodeBounds = true } + BitmapFactory.decodeByteArray(imageBytes, 0, imageBytes.size, options) + return options + } } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index c5e2813097..d7cbf76905 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -814,6 +814,7 @@ No network connection available Download paused Download completed + Page %d not found while splitting Common