Przeglądaj źródła

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)
arkon 2 lat temu
rodzic
commit
a9e629aea6

+ 0 - 4
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<Chapter>) = db.put().objects(chapters).prepare()
 
-    fun deleteChapter(chapter: Chapter) = db.delete().`object`(chapter).prepare()
-
     fun deleteChapters(chapters: List<Chapter>) = db.delete().objects(chapters).prepare()
 
     fun updateChaptersBackup(chapters: List<Chapter>) = db.put()

+ 0 - 11
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()
 }

+ 0 - 12
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<Manga>) = db.delete().objects(mangas).prepare()
-
     fun deleteMangasNotInLibraryBySourceIds(sourceIds: List<Long>) = 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(

+ 28 - 42
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 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
-                }
-                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()
+        val bitmap = BitmapFactory.decodeFile(imageFile.filePath)
+        val splitsCount = bitmap.height / context.resources.displayMetrics.heightPixels + 1
+        val heightPerSplit = bitmap.height / splitsCount
+
+        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)
                 }
-                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
         }
     }
 

+ 21 - 33
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<LibraryManga> = 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<Chapter>, List<Chapter>> {
         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)
     }
 

+ 1 - 1
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
         }

+ 1 - 1
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
         }

+ 21 - 23
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
+    }
 }

+ 1 - 0
app/src/main/res/values/strings.xml

@@ -814,6 +814,7 @@
     <string name="download_notifier_no_network">No network connection available</string>
     <string name="download_notifier_download_paused">Download paused</string>
     <string name="download_notifier_download_finish">Download completed</string>
+    <string name="download_notifier_split_page_not_found">Page %d not found while splitting</string>
 
     <!-- Notification channels -->
     <string name="channel_common">Common</string>