From 70fcf54c599e936db5530adc1b51d37843380b17 Mon Sep 17 00:00:00 2001 From: Omar Estrada Date: Sun, 8 Dec 2024 17:57:18 -0600 Subject: [PATCH 1/2] Within addEntriesToZip(), which is used for merging multiple CBZ files into one, replaced usage of ZipInputStream with ZipFile. On Android 15 the usage of ZipInputStream directly as used previously, only retrieves the first file of each CBZ, making this change works both on Android 15 and the previous versions. To utilize ZipFile, I decided to reuse the copyCbzToCacheAndCloseInputStream() to make a copy of file and decided to refactor it to catch error within the method. --- .../cbzconverter/ConversionFunctions.kt | 96 +++++++++++-------- 1 file changed, 58 insertions(+), 38 deletions(-) diff --git a/app/src/main/java/com/puchunguita/cbzconverter/ConversionFunctions.kt b/app/src/main/java/com/puchunguita/cbzconverter/ConversionFunctions.kt index e3668ec..0cf30ba 100644 --- a/app/src/main/java/com/puchunguita/cbzconverter/ConversionFunctions.kt +++ b/app/src/main/java/com/puchunguita/cbzconverter/ConversionFunctions.kt @@ -9,14 +9,13 @@ import com.itextpdf.kernel.pdf.PdfWriter import com.itextpdf.layout.Document import com.itextpdf.layout.element.Image import java.io.File -import java.io.FileOutputStream +import java.io.IOException import java.io.InputStream import java.util.logging.Logger import java.util.stream.Collectors import java.util.stream.IntStream import java.util.zip.ZipEntry import java.util.zip.ZipFile -import java.util.zip.ZipInputStream import java.util.zip.ZipOutputStream import kotlin.math.ceil import kotlin.streams.asStream @@ -73,21 +72,21 @@ private fun applyEachFileAndCreatePdf( fileUri.forEachIndexed { index, uri -> val outputFileName = outputFileNames[index] - val inputStream = contextHelper.openInputStream(uri) ?: run { - subStepStatusAction("Could not copy CBZ file to cache: $outputFileName"); return@forEachIndexed + try { + val tempFile = copyCbzToCacheAndCloseInputStream(contextHelper, subStepStatusAction, uri) + + createPdfEitherSingleOrMultiple( + tempFile = tempFile, + subStepStatusAction = subStepStatusAction, + overrideSortOrderToUseOffset = overrideSortOrderToUseOffset, + outputFileName = outputFileName, + outputDirectory = outputDirectory, + maxNumberOfPages = maxNumberOfPages, + outputFiles = outputFiles + ) + } catch (ioException: IOException) { + return@forEachIndexed } - - val tempFile = copyCbzToCacheAndCloseInputStream(contextHelper, inputStream) - - createPdfEitherSingleOrMultiple( - tempFile = tempFile, - subStepStatusAction = subStepStatusAction, - overrideSortOrderToUseOffset = overrideSortOrderToUseOffset, - outputFileName = outputFileName, - outputDirectory = outputDirectory, - maxNumberOfPages = maxNumberOfPages, - outputFiles = outputFiles - ) } return outputFiles } @@ -105,14 +104,9 @@ private fun mergeFilesAndCreatePdf( subStepStatusAction("Creating combined_temp.cbz in Cache") val combinedTempFile = File(contextHelper.getCacheDir(), "combined_temp.cbz") - ZipOutputStream(FileOutputStream(combinedTempFile)).use { zipOutputStream -> + ZipOutputStream(combinedTempFile.outputStream()).use { zipOutputStream -> fileUri.forEachIndexed() { index, uri -> - val inputStream = contextHelper.openInputStream(uri) ?: run { - subStepStatusAction("Could not copy CBZ file to cache: ${uri.path}") - return@forEachIndexed - } - addEntriesToZip(inputStream, zipOutputStream, outputFileNames[index], index, subStepStatusAction) - inputStream.close() + addEntriesToZip(zipOutputStream, outputFileNames[index], index, subStepStatusAction, contextHelper, uri) } } @@ -150,26 +144,47 @@ private fun orderZipEntriesToList( } private fun addEntriesToZip( - inputStream: InputStream, zipOutputStream: ZipOutputStream, fileName: String, index: Int, subStepStatusAction: (String) -> Unit, + contextHelper: ContextHelper, + uri: Uri ) { - ZipInputStream(inputStream).use { zipInputStream -> - var zipEntry = zipInputStream.nextEntry - while (zipEntry != null) { - // Using index for ordering by name to continue functioning correctly. - // Adding padding to start, without it passing 10_ is between 0_ and 1_. - // filename is added as prefix to ensure unique naming per file, otherwise duplication error. - val formattedIndex = index.toString().padStart(4, '0') - val currentFileUniqueName = "${formattedIndex}_"+fileName+"_"+zipEntry.name - subStepStatusAction("Adding ZipEntry into combined_temp.cbz: $currentFileUniqueName") - zipOutputStream.putNextEntry(ZipEntry(currentFileUniqueName)) - zipInputStream.copyTo(zipOutputStream) - zipOutputStream.closeEntry() - zipEntry = zipInputStream.nextEntry + try { + val tempFile = copyCbzToCacheAndCloseInputStream(contextHelper, subStepStatusAction, uri) + val zipFile = ZipFile(tempFile) + subStepStatusAction("Adding ${zipFile.size()} entries from $fileName") + + zipFile.entries().asSequence().forEach { zipEntry -> + try { + // Using index for ordering by name to continue functioning correctly. + // Adding padding to start, without it passing 10_ is between 0_ and 1_. + // Padding length being 4, allows correct order when merging up to 9999 files. + // filename is added as prefix to ensure unique naming per file, otherwise duplication error. + val formattedIndex = index.toString().padStart(4, '0') + val currentFileUniqueName = "${formattedIndex}_${fileName}_${zipEntry.name}" + subStepStatusAction("Adding ZipEntry into combined_temp.cbz: $currentFileUniqueName") + + // Add entry to the output ZIP + zipOutputStream.putNextEntry(ZipEntry(currentFileUniqueName)) + + // Stream the entry's data into the output stream + zipFile.getInputStream(zipEntry).copyTo(zipOutputStream) + + // Close the current entry + zipOutputStream.closeEntry() + + } catch (e: Exception) { + subStepStatusAction("Error processing file ${zipEntry.name}") + logger.warning("Exception message: ${e.message}") + logger.warning("Exception stacktrace: ${e.stackTrace.contentToString()}") + } } + + zipFile.close() + } catch (ioException: IOException) { + return } } @@ -221,8 +236,13 @@ fun createPdfEitherSingleOrMultiple( return outputFiles } -private fun copyCbzToCacheAndCloseInputStream(contextHelper: ContextHelper, inputStream: InputStream): File { +@Throws(IOException::class) +private fun copyCbzToCacheAndCloseInputStream(contextHelper: ContextHelper, subStepStatusAction: (String) -> Unit, uri: Uri): File { val tempFile = File(contextHelper.getCacheDir(), "temp.cbz") + val inputStream = contextHelper.openInputStream(uri) ?: run { + subStepStatusAction("Could not copy CBZ file to cache: ${uri.path}") + throw IOException("Could not copy CBZ file to cache: ${uri.path}") + } tempFile.outputStream().use { outputStream -> inputStream.copyTo(outputStream) } From 06a48f3e56df316d9faf3f010409661a51bb5166 Mon Sep 17 00:00:00 2001 From: Omar Estrada Date: Sun, 8 Dec 2024 18:12:40 -0600 Subject: [PATCH 2/2] Added delete temp file when within addEntriesToZip(). Updated compileSdk and targetSdk to 35 (Android 15) --- app/build.gradle.kts | 4 ++-- .../java/com/puchunguita/cbzconverter/ConversionFunctions.kt | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index d0573a0..eee576a 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -5,12 +5,12 @@ plugins { android { namespace = "com.puchunguita.cbzconverter" - compileSdk = 34 + compileSdk = 35 defaultConfig { applicationId = "com.puchunguita.cbzconverter" minSdk = 24 - targetSdk = 34 + targetSdk = 35 versionCode = 1 versionName = "1.0" diff --git a/app/src/main/java/com/puchunguita/cbzconverter/ConversionFunctions.kt b/app/src/main/java/com/puchunguita/cbzconverter/ConversionFunctions.kt index 0cf30ba..d91568d 100644 --- a/app/src/main/java/com/puchunguita/cbzconverter/ConversionFunctions.kt +++ b/app/src/main/java/com/puchunguita/cbzconverter/ConversionFunctions.kt @@ -183,6 +183,7 @@ private fun addEntriesToZip( } zipFile.close() + tempFile.delete() } catch (ioException: IOException) { return }