diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 12effa462..3d12eaea6 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -165,7 +165,6 @@ dependencies { implementation(compose.ui.tooling.preview) implementation(compose.ui.util) implementation(compose.accompanist.webview) - implementation(compose.accompanist.permissions) implementation(compose.accompanist.systemuicontroller) lintChecks(compose.lintchecks) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index f75531139..ccb3b15d0 100755 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -7,9 +7,6 @@ - - - @@ -40,7 +37,6 @@ android:largeHeap="true" android:localeConfig="@xml/locales_config" android:networkSecurityConfig="@xml/network_security_config" - android:requestLegacyExternalStorage="true" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/Theme.Tachiyomi"> diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsDataScreen.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsDataScreen.kt index 0d86cabbe..4ffd15d77 100644 --- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsDataScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsDataScreen.kt @@ -35,7 +35,6 @@ import eu.kanade.presentation.more.settings.Preference import eu.kanade.presentation.more.settings.screen.data.CreateBackupScreen import eu.kanade.presentation.more.settings.widget.BasePreferenceWidget import eu.kanade.presentation.more.settings.widget.PrefsHorizontalPadding -import eu.kanade.presentation.permissions.PermissionRequestHelper import eu.kanade.presentation.util.relativeTimeSpanString import eu.kanade.tachiyomi.data.backup.BackupCreateJob import eu.kanade.tachiyomi.data.backup.BackupFileValidator @@ -73,8 +72,6 @@ object SettingsDataScreen : SearchableSettings { val backupPreferences = Injekt.get() val storagePreferences = Injekt.get() - PermissionRequestHelper.requestStoragePermission() - return listOf( getStorageLocationPref(storagePreferences = storagePreferences), Preference.PreferenceItem.InfoPreference(stringResource(MR.strings.pref_storage_location_info)), diff --git a/app/src/main/java/eu/kanade/presentation/permissions/PermissionRequestHelper.kt b/app/src/main/java/eu/kanade/presentation/permissions/PermissionRequestHelper.kt deleted file mode 100644 index 7ce28f9da..000000000 --- a/app/src/main/java/eu/kanade/presentation/permissions/PermissionRequestHelper.kt +++ /dev/null @@ -1,20 +0,0 @@ -package eu.kanade.presentation.permissions - -import android.Manifest -import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect -import com.google.accompanist.permissions.rememberPermissionState - -/** - * Launches request for [Manifest.permission.WRITE_EXTERNAL_STORAGE] permission - */ -object PermissionRequestHelper { - - @Composable - fun requestStoragePermission() { - val permissionState = rememberPermissionState(permission = Manifest.permission.WRITE_EXTERNAL_STORAGE) - LaunchedEffect(Unit) { - permissionState.launchPermissionRequest() - } - } -} diff --git a/app/src/main/java/eu/kanade/tachiyomi/App.kt b/app/src/main/java/eu/kanade/tachiyomi/App.kt index 8f103c47f..374839eea 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/App.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/App.kt @@ -27,7 +27,6 @@ import com.elvishew.xlog.XLog import com.elvishew.xlog.printer.AndroidPrinter import com.elvishew.xlog.printer.Printer import com.elvishew.xlog.printer.file.backup.NeverBackupStrategy -import com.elvishew.xlog.printer.file.clean.FileLastModifiedCleanStrategy import com.elvishew.xlog.printer.file.naming.DateFileNameGenerator import eu.kanade.domain.DomainModule import eu.kanade.domain.SYDomainModule @@ -67,7 +66,6 @@ import logcat.LogPriority import logcat.LogcatLogger import org.conscrypt.Conscrypt import tachiyomi.core.i18n.stringResource -import tachiyomi.core.storage.toFile import tachiyomi.core.util.system.logcat import tachiyomi.domain.storage.service.StorageManager import tachiyomi.i18n.MR @@ -78,7 +76,6 @@ import uy.kohesive.injekt.injectLazy import java.security.Security import java.text.SimpleDateFormat import java.util.Locale -import kotlin.time.Duration.Companion.days class App : Application(), DefaultLifecycleObserver, ImageLoaderFactory { @@ -250,8 +247,6 @@ class App : Application(), DefaultLifecycleObserver, ImageLoaderFactory { val printers = mutableListOf(AndroidPrinter()) val logFolder = Injekt.get().getLogsDirectory() - ?.toFile() - ?.absolutePath if (logFolder != null) { val dateFormat = SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS", Locale.getDefault()) @@ -269,7 +264,6 @@ class App : Application(), DefaultLifecycleObserver, ImageLoaderFactory { flattener { timeMillis, level, tag, message -> "${dateFormat.format(timeMillis)} ${LogLevel.getShortLevelName(level)}/$tag: $message" } - cleanStrategy = FileLastModifiedCleanStrategy(7.days.inWholeMilliseconds) backupStrategy = NeverBackupStrategy() } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/BrowseTab.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/BrowseTab.kt index 7624cfa0b..ec9daa07c 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/BrowseTab.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/BrowseTab.kt @@ -17,7 +17,6 @@ import cafe.adriel.voyager.navigator.tab.TabOptions import eu.kanade.core.preference.asState import eu.kanade.domain.ui.UiPreferences import eu.kanade.presentation.components.TabbedScreen -import eu.kanade.presentation.permissions.PermissionRequestHelper import eu.kanade.presentation.util.Tab import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.ui.browse.extension.ExtensionsScreenModel @@ -91,9 +90,6 @@ data class BrowseTab( onChangeSearchQuery = extensionsScreenModel::search, ) - // For local source - PermissionRequestHelper.requestStoragePermission() - LaunchedEffect(Unit) { (context as? MainActivity)?.ready = true } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/ChapterLoader.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/ChapterLoader.kt index 570cd3720..d3364677f 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/ChapterLoader.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/ChapterLoader.kt @@ -10,6 +10,7 @@ import eu.kanade.tachiyomi.source.online.all.MergedSource import eu.kanade.tachiyomi.ui.reader.model.ReaderChapter import eu.kanade.tachiyomi.ui.reader.setting.ReaderPreferences import tachiyomi.core.i18n.stringResource +import tachiyomi.core.storage.toTempFile import tachiyomi.core.util.lang.withIOContext import tachiyomi.core.util.system.logcat import tachiyomi.domain.manga.model.Manga @@ -124,13 +125,13 @@ class ChapterLoader( source is LocalSource -> source.getFormat(chapter.chapter).let { format -> when (format) { is Format.Directory -> DirectoryPageLoader(format.file) - is Format.Zip -> ZipPageLoader(format.file) + is Format.Zip -> ZipPageLoader(format.file.toTempFile(context)) is Format.Rar -> try { - RarPageLoader(format.file) + RarPageLoader(format.file.toTempFile(context)) } catch (e: UnsupportedRarV5Exception) { error(context.stringResource(MR.strings.loader_rar5_error)) } - is Format.Epub -> EpubPageLoader(format.file) + is Format.Epub -> EpubPageLoader(format.file.toTempFile(context)) } } else -> error(context.stringResource(MR.strings.loader_not_implemented_error)) @@ -141,13 +142,13 @@ class ChapterLoader( source is LocalSource -> source.getFormat(chapter.chapter).let { format -> when (format) { is Format.Directory -> DirectoryPageLoader(format.file) - is Format.Zip -> ZipPageLoader(format.file) + is Format.Zip -> ZipPageLoader(format.file.toTempFile(context)) is Format.Rar -> try { - RarPageLoader(format.file) + RarPageLoader(format.file.toTempFile(context)) } catch (e: UnsupportedRarV5Exception) { error(context.stringResource(MR.strings.loader_rar5_error)) } - is Format.Epub -> EpubPageLoader(format.file) + is Format.Epub -> EpubPageLoader(format.file.toTempFile(context)) } } source is HttpSource -> HttpPageLoader(chapter, source) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/DownloadPageLoader.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/DownloadPageLoader.kt index d01706e3f..d2fe3de04 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/DownloadPageLoader.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/DownloadPageLoader.kt @@ -10,6 +10,7 @@ import eu.kanade.tachiyomi.source.Source import eu.kanade.tachiyomi.source.model.Page import eu.kanade.tachiyomi.ui.reader.model.ReaderChapter import eu.kanade.tachiyomi.ui.reader.model.ReaderPage +import tachiyomi.core.storage.toTempFile import tachiyomi.domain.manga.model.Manga import uy.kohesive.injekt.injectLazy @@ -46,7 +47,7 @@ internal class DownloadPageLoader( } private suspend fun getPagesFromArchive(chapterPath: UniFile): List { - val loader = ZipPageLoader(chapterPath).also { zipPageLoader = it } + val loader = ZipPageLoader(chapterPath.toTempFile(context)).also { zipPageLoader = it } return loader.getPages() } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/EpubPageLoader.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/EpubPageLoader.kt index cd00e3756..324af51bf 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/EpubPageLoader.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/EpubPageLoader.kt @@ -1,14 +1,14 @@ package eu.kanade.tachiyomi.ui.reader.loader -import com.hippo.unifile.UniFile import eu.kanade.tachiyomi.source.model.Page import eu.kanade.tachiyomi.ui.reader.model.ReaderPage import eu.kanade.tachiyomi.util.storage.EpubFile +import java.io.File /** * Loader used to load a chapter from a .epub file. */ -internal class EpubPageLoader(file: UniFile) : PageLoader() { +internal class EpubPageLoader(file: File) : PageLoader() { private val epub = EpubFile(file) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/RarPageLoader.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/RarPageLoader.kt index 86c91dc7a..1d8598fe9 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/RarPageLoader.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/RarPageLoader.kt @@ -8,7 +8,6 @@ import eu.kanade.tachiyomi.source.model.Page import eu.kanade.tachiyomi.ui.reader.model.ReaderPage import eu.kanade.tachiyomi.ui.reader.setting.ReaderPreferences import eu.kanade.tachiyomi.util.lang.compareToCaseInsensitiveNaturalOrder -import tachiyomi.core.storage.toFile import tachiyomi.core.util.system.ImageUtil import uy.kohesive.injekt.injectLazy import java.io.File @@ -19,9 +18,9 @@ import java.io.PipedOutputStream /** * Loader used to load a chapter from a .rar or .cbr file. */ -internal class RarPageLoader(file: UniFile) : PageLoader() { +internal class RarPageLoader(file: File) : PageLoader() { - private val rar = Archive(file.toFile()) + private val rar = Archive(file) // SY --> private val context: Application by injectLazy() @@ -33,7 +32,7 @@ internal class RarPageLoader(file: UniFile) : PageLoader() { init { if (readerPreferences.cacheArchiveMangaOnDisk().get()) { tmpDir.mkdirs() - Archive(file.toFile()).use { rar -> + Archive(file).use { rar -> rar.fileHeaders.asSequence() .filterNot { it.isDirectory } .forEach { header -> diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/ZipPageLoader.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/ZipPageLoader.kt index f9238f453..81c4d2509 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/ZipPageLoader.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/ZipPageLoader.kt @@ -9,7 +9,6 @@ import eu.kanade.tachiyomi.ui.reader.setting.ReaderPreferences import eu.kanade.tachiyomi.util.lang.compareToCaseInsensitiveNaturalOrder import eu.kanade.tachiyomi.util.storage.CbzCrypto import tachiyomi.core.i18n.stringResource -import tachiyomi.core.storage.toFile import tachiyomi.core.util.system.ImageUtil import tachiyomi.i18n.sy.SYMR import uy.kohesive.injekt.injectLazy @@ -21,7 +20,7 @@ import net.lingala.zip4j.ZipFile as Zip4jFile /** * Loader used to load a chapter from a .zip or .cbz file. */ -internal class ZipPageLoader(file: UniFile) : PageLoader() { +internal class ZipPageLoader(file: File) : PageLoader() { // SY --> private val context: Application by injectLazy() @@ -29,12 +28,12 @@ internal class ZipPageLoader(file: UniFile) : PageLoader() { private val tmpDir = File(context.externalCacheDir, "reader_${file.hashCode()}").also { it.deleteRecursively() } - private val zip4j: Zip4jFile = Zip4jFile(file.toFile()) + private val zip4j: Zip4jFile = Zip4jFile(file) private val zip: ZipFile? = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { - if (!zip4j.isEncrypted) ZipFile(file.toFile(), StandardCharsets.ISO_8859_1) else null + if (!zip4j.isEncrypted) ZipFile(file, StandardCharsets.ISO_8859_1) else null } else { - if (!zip4j.isEncrypted) ZipFile(file.toFile()) else null + if (!zip4j.isEncrypted) ZipFile(file) else null } init { @@ -42,7 +41,7 @@ internal class ZipPageLoader(file: UniFile) : PageLoader() { zip4j.charset = StandardCharsets.ISO_8859_1 } - Zip4jFile(file.toFile()).use { zip -> + Zip4jFile(file).use { zip -> if (zip.isEncrypted) { if (!CbzCrypto.checkCbzPassword(zip, CbzCrypto.getDecryptedPasswordCbz())) { this.recycle() diff --git a/app/src/main/java/exh/log/EnhancedFilePrinter.kt b/app/src/main/java/exh/log/EnhancedFilePrinter.kt index e3269c2aa..0bd6fee20 100644 --- a/app/src/main/java/exh/log/EnhancedFilePrinter.kt +++ b/app/src/main/java/exh/log/EnhancedFilePrinter.kt @@ -3,14 +3,14 @@ package exh.log import com.elvishew.xlog.internal.DefaultsFactory import com.elvishew.xlog.printer.Printer import com.elvishew.xlog.printer.file.backup.BackupStrategy -import com.elvishew.xlog.printer.file.clean.CleanStrategy import com.elvishew.xlog.printer.file.naming.FileNameGenerator +import com.hippo.unifile.UniFile +import exh.log.EnhancedFilePrinter.Builder import java.io.BufferedWriter -import java.io.File -import java.io.FileWriter import java.io.IOException import java.util.concurrent.BlockingQueue import java.util.concurrent.LinkedBlockingQueue +import kotlin.time.Duration.Companion.days import com.elvishew.xlog.flattener.Flattener2 as Flattener /** @@ -18,7 +18,7 @@ import com.elvishew.xlog.flattener.Flattener2 as Flattener * * Use the [Builder] to construct a [EnhancedFilePrinter] object. * - * @param folderPath The folder path of log file. + * @param folder The folder path of log file. * @param fileNameGenerator the file name generator for log file. * @param backupStrategy the backup strategy for log file. * @param cleanStrategy The clean strategy for log file. @@ -27,10 +27,9 @@ import com.elvishew.xlog.flattener.Flattener2 as Flattener */ @Suppress("unused") class EnhancedFilePrinter internal constructor( - private val folderPath: String, + private val folder: UniFile, private val fileNameGenerator: FileNameGenerator, private val backupStrategy: BackupStrategy, - private val cleanStrategy: CleanStrategy, private val flattener: Flattener, ) : Printer { /** @@ -41,16 +40,6 @@ class EnhancedFilePrinter internal constructor( @Volatile private var worker: Worker? = null - /** - * Make sure the folder of log file exists. - */ - private fun checkLogFolder() { - val folder = File(folderPath) - if (!folder.exists()) { - folder.mkdirs() - } - } - override fun println(logLevel: Int, tag: String, msg: String) { val timeMillis = System.currentTimeMillis() if (USE_WORKER) { @@ -68,8 +57,8 @@ class EnhancedFilePrinter internal constructor( * Do the real job of writing log to file. */ private fun doPrintln(timeMillis: Long, logLevel: Int, tag: String, msg: String) { - var lastFileName = writer.lastFileName - if (lastFileName == null || fileNameGenerator.isFileNameChangeable) { + val lastFileName = writer.lastFileName + if (fileNameGenerator.isFileNameChangeable) { val newFileName = fileNameGenerator.generateFileName(logLevel, System.currentTimeMillis()) require(!(newFileName == null || newFileName.trim { it <= ' ' }.isEmpty())) { "File name should not be empty." } if (newFileName != lastFileName) { @@ -77,37 +66,29 @@ class EnhancedFilePrinter internal constructor( writer.close() } cleanLogFilesIfNecessary() - if (writer.open(newFileName).not()) { + if (writer.open(folder.createFile(newFileName)!!).not()) { return } - lastFileName = newFileName - } - } - val lastFile = writer.file ?: return - if (backupStrategy.shouldBackup(lastFile)) { - // Backup the log file, and create a new log file. - writer.close() - val backupFile = File(folderPath, "$lastFileName.bak") - if (backupFile.exists()) { - backupFile.delete() - } - lastFile.renameTo(backupFile) - if (writer.open(lastFileName).not()) { - return } } val flattenedLog = flattener.flatten(timeMillis, logLevel, tag, msg).toString() writer.appendLog(flattenedLog) } + private val maxTimeMillis = 7.days.inWholeMilliseconds + private fun shouldClean(file: UniFile): Boolean { + val currentTimeMillis = System.currentTimeMillis() + val lastModified = file.lastModified() + return currentTimeMillis - lastModified > maxTimeMillis + } + /** * Clean log files if should clean follow strategy */ private fun cleanLogFilesIfNecessary() { - val logDir = File(folderPath) - logDir.listFiles().orEmpty() + folder.listFiles().orEmpty() .asSequence() - .filter { cleanStrategy.shouldClean(it) } + .filter { shouldClean(it) } .forEach { it.delete() } } @@ -115,7 +96,7 @@ class EnhancedFilePrinter internal constructor( * Builder for [EnhancedFilePrinter]. * @param folderPath the folder path of log file */ - class Builder(private val folderPath: String) { + class Builder(private val folder: UniFile) { /** * The file name generator for log file. */ @@ -126,11 +107,6 @@ class EnhancedFilePrinter internal constructor( */ var backupStrategy: BackupStrategy? = null - /** - * The clean strategy for log file. - */ - var cleanStrategy: CleanStrategy? = null - /** * The flattener when print a log. */ @@ -158,17 +134,6 @@ class EnhancedFilePrinter internal constructor( return this } - /** - * Set the clean strategy for log file. - * - * @param cleanStrategy the clean strategy for log file - * @return the builder - */ - fun cleanStrategy(cleanStrategy: CleanStrategy): Builder { - this.cleanStrategy = cleanStrategy - return this - } - /** * Set the flattener when print a log. * @@ -187,17 +152,16 @@ class EnhancedFilePrinter internal constructor( */ fun build(): EnhancedFilePrinter { return EnhancedFilePrinter( - folderPath, + folder, fileNameGenerator ?: DefaultsFactory.createFileNameGenerator(), backupStrategy ?: DefaultsFactory.createBackupStrategy(), - cleanStrategy ?: DefaultsFactory.createCleanStrategy(), flattener ?: DefaultsFactory.createFlattener2(), ) } companion object { - operator fun invoke(folderPath: String, block: Builder.() -> Unit): EnhancedFilePrinter { - return Builder(folderPath).apply(block).build() + operator fun invoke(folder: UniFile, block: Builder.() -> Unit): EnhancedFilePrinter { + return Builder(folder).apply(block).build() } } } @@ -271,9 +235,6 @@ class EnhancedFilePrinter internal constructor( * Get the name of last used log file. * @return the name of last used log file, maybe null */ - /** - * The file name of last used log file. - */ var lastFileName: String? = null private set /** @@ -281,10 +242,7 @@ class EnhancedFilePrinter internal constructor( * * @return the current log file, maybe null */ - /** - * The current log file. - */ - var file: File? = null + var file: UniFile? = null private set private var bufferedWriter: BufferedWriter? = null @@ -303,15 +261,10 @@ class EnhancedFilePrinter internal constructor( * @param newFileName the specific file name * @return true if opened successfully, false otherwise */ - fun open(newFileName: String): Boolean { + fun open(file: UniFile): Boolean { return try { - val file = File(folderPath, newFileName) - if (file.exists().not()) { - (file.parentFile ?: File(file.absolutePath.substringBeforeLast(File.separatorChar))).mkdirs() - file.createNewFile() - } - bufferedWriter = FileWriter(file, true).buffered() - lastFileName = newFileName + bufferedWriter = file.openOutputStream().bufferedWriter() + lastFileName = file.name this.file = file true } catch (e: Exception) { @@ -370,6 +323,5 @@ class EnhancedFilePrinter internal constructor( if (USE_WORKER) { worker = Worker() } - checkLogFolder() } } diff --git a/core/src/main/java/eu/kanade/tachiyomi/util/storage/EpubFile.kt b/core/src/main/java/eu/kanade/tachiyomi/util/storage/EpubFile.kt index 7650f65b5..a00ee69e7 100644 --- a/core/src/main/java/eu/kanade/tachiyomi/util/storage/EpubFile.kt +++ b/core/src/main/java/eu/kanade/tachiyomi/util/storage/EpubFile.kt @@ -1,9 +1,7 @@ package eu.kanade.tachiyomi.util.storage -import com.hippo.unifile.UniFile import org.jsoup.Jsoup import org.jsoup.nodes.Document -import tachiyomi.core.storage.toFile import java.io.Closeable import java.io.File import java.io.InputStream @@ -13,12 +11,12 @@ import java.util.zip.ZipFile /** * Wrapper over ZipFile to load files in epub format. */ -class EpubFile(file: UniFile) : Closeable { +class EpubFile(file: File) : Closeable { /** * Zip file of this epub. */ - private val zip = ZipFile(file.toFile()) + private val zip = ZipFile(file) /** * Path separator used by this epub. diff --git a/core/src/main/java/tachiyomi/core/storage/UniFileExtensions.kt b/core/src/main/java/tachiyomi/core/storage/UniFileExtensions.kt index 5343dfa3f..c5c2bbbc8 100644 --- a/core/src/main/java/tachiyomi/core/storage/UniFileExtensions.kt +++ b/core/src/main/java/tachiyomi/core/storage/UniFileExtensions.kt @@ -1,6 +1,10 @@ package tachiyomi.core.storage +import android.content.Context +import android.os.Build +import android.os.FileUtils import com.hippo.unifile.UniFile +import java.io.BufferedOutputStream import java.io.File val UniFile.extension: String? @@ -9,4 +13,26 @@ val UniFile.extension: String? val UniFile.nameWithoutExtension: String? get() = name?.substringBeforeLast('.') -fun UniFile.toFile(): File? = filePath?.let { File(it) } +fun UniFile.toTempFile(context: Context): File { + val inputStream = context.contentResolver.openInputStream(uri)!! + val tempFile = File.createTempFile( + nameWithoutExtension.orEmpty(), + null, + ) + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + FileUtils.copy(inputStream, tempFile.outputStream()) + } else { + BufferedOutputStream(tempFile.outputStream()).use { tmpOut -> + inputStream.use { input -> + val buffer = ByteArray(8192) + var count: Int + while (input.read(buffer).also { count = it } > 0) { + tmpOut.write(buffer, 0, count) + } + } + } + } + + return tempFile +} diff --git a/gradle/compose.versions.toml b/gradle/compose.versions.toml index 39074cb0c..ce7b074bf 100644 --- a/gradle/compose.versions.toml +++ b/gradle/compose.versions.toml @@ -22,7 +22,6 @@ material-core = { module = "androidx.compose.material:material" } glance = "androidx.glance:glance-appwidget:1.0.0" accompanist-webview = { module = "com.google.accompanist:accompanist-webview", version.ref = "accompanist" } -accompanist-permissions = { module = "com.google.accompanist:accompanist-permissions", version.ref = "accompanist" } accompanist-systemuicontroller = { module = "com.google.accompanist:accompanist-systemuicontroller", version.ref = "accompanist" } lintchecks = { module = "com.slack.lint.compose:compose-lint-checks", version = "1.2.0" } \ No newline at end of file diff --git a/source-local/src/androidMain/kotlin/tachiyomi/source/local/LocalSource.kt b/source-local/src/androidMain/kotlin/tachiyomi/source/local/LocalSource.kt index 911ac4d93..f02760355 100755 --- a/source-local/src/androidMain/kotlin/tachiyomi/source/local/LocalSource.kt +++ b/source-local/src/androidMain/kotlin/tachiyomi/source/local/LocalSource.kt @@ -30,7 +30,7 @@ import tachiyomi.core.metadata.comicinfo.getComicInfo import tachiyomi.core.metadata.tachiyomi.MangaDetails import tachiyomi.core.storage.extension import tachiyomi.core.storage.nameWithoutExtension -import tachiyomi.core.storage.toFile +import tachiyomi.core.storage.toTempFile import tachiyomi.core.util.lang.withIOContext import tachiyomi.core.util.system.ImageUtil import tachiyomi.core.util.system.logcat @@ -154,12 +154,12 @@ actual class LocalSource( // SY --> fun updateMangaInfo(manga: SManga) { - val directory = fileSystem.getFilesInBaseDirectory().map { File(it.toFile(), manga.url) }.find { - it.exists() + val directory = fileSystem.getFilesInBaseDirectory().map { it.createDirectory(manga.url) }.find { + it?.exists() == true } ?: return - val existingFileName = directory.listFiles()?.find { it.extension == "json" }?.name - val file = File(directory, existingFileName ?: "info.json") - file.outputStream().use { + val existingFile = directory.listFiles()?.find { it.extension == "json" } + val file = existingFile ?: directory.createFile("info.json") ?: return + file.openOutputStream().use { json.encodeToStream(manga.toJson(), it) } } @@ -199,7 +199,7 @@ actual class LocalSource( } // SY --> comicInfoArchiveFile != null -> { - val comicInfoArchive = ZipFile(comicInfoArchiveFile.toFile()) + val comicInfoArchive = ZipFile(comicInfoArchiveFile.toTempFile(context)) noXmlFile?.delete() if (CbzCrypto.checkCbzPassword(comicInfoArchive, CbzCrypto.getDecryptedPasswordCbz())) { @@ -269,7 +269,7 @@ actual class LocalSource( for (chapter in chapterArchives) { when (Format.valueOf(chapter)) { is Format.Zip -> { - ZipFile(chapter.toFile()).use { zip: ZipFile -> + ZipFile(chapter.toTempFile(context)).use { zip: ZipFile -> // SY --> if (zip.isEncrypted && !CbzCrypto.checkCbzPassword(zip, CbzCrypto.getDecryptedPasswordCbz()) ) { @@ -288,7 +288,7 @@ actual class LocalSource( } } is Format.Rar -> { - JunrarArchive(chapter.toFile()).use { rar -> + JunrarArchive(chapter.toTempFile(context)).use { rar -> rar.fileHeaders.firstOrNull { it.fileName == COMIC_INFO_FILE }?.let { comicInfoFile -> rar.getInputStream(comicInfoFile).buffered().use { stream -> return copyComicInfoFile(stream, folderPath) @@ -354,7 +354,7 @@ actual class LocalSource( val format = Format.valueOf(chapterFile) if (format is Format.Epub) { - EpubFile(format.file).use { epub -> + EpubFile(format.file.toTempFile(context)).use { epub -> epub.fillMetadata(manga, this) } } @@ -413,7 +413,7 @@ actual class LocalSource( entry?.let { coverManager.update(manga, it.openInputStream()) } } is Format.Zip -> { - ZipFile(format.file.toFile()).use { zip -> + ZipFile(format.file.toTempFile(context)).use { zip -> // SY --> var encrypted = false if (zip.isEncrypted) { @@ -428,7 +428,7 @@ actual class LocalSource( } } is Format.Rar -> { - JunrarArchive(format.file.toFile()).use { archive -> + JunrarArchive(format.file.toTempFile(context)).use { archive -> val entry = archive.fileHeaders .sortedWith { f1, f2 -> f1.fileName.compareToCaseInsensitiveNaturalOrder(f2.fileName) } .find { !it.isDirectory && ImageUtil.isImage(it.fileName) { archive.getInputStream(it) } } @@ -437,7 +437,7 @@ actual class LocalSource( } } is Format.Epub -> { - EpubFile(format.file).use { epub -> + EpubFile(format.file.toTempFile(context)).use { epub -> val entry = epub.getImagesFromPages() .firstOrNull() ?.let { epub.getEntry(it) } diff --git a/source-local/src/androidMain/kotlin/tachiyomi/source/local/image/LocalCoverManager.kt b/source-local/src/androidMain/kotlin/tachiyomi/source/local/image/LocalCoverManager.kt index 3e81855a5..c78e28815 100644 --- a/source-local/src/androidMain/kotlin/tachiyomi/source/local/image/LocalCoverManager.kt +++ b/source-local/src/androidMain/kotlin/tachiyomi/source/local/image/LocalCoverManager.kt @@ -8,7 +8,6 @@ import eu.kanade.tachiyomi.util.storage.DiskUtil import net.lingala.zip4j.ZipFile import net.lingala.zip4j.model.ZipParameters import tachiyomi.core.storage.nameWithoutExtension -import tachiyomi.core.storage.toFile import tachiyomi.core.util.system.ImageUtil import tachiyomi.source.local.io.LocalSourceFileSystem import java.io.File @@ -61,12 +60,22 @@ actual class LocalCoverManager( inputStream.use { input -> // SY --> if (encrypted) { - val zip4j = ZipFile(targetFile.toFile()) + val tempFile = File.createTempFile( + targetFile.nameWithoutExtension.orEmpty(), + null, + ) + val zip4j = ZipFile(tempFile) val zipParameters = ZipParameters() zip4j.setPassword(CbzCrypto.getDecryptedPasswordCbz()) CbzCrypto.setZipParametersEncrypted(zipParameters) zipParameters.fileNameInZip = DEFAULT_COVER_NAME zip4j.addStream(input, zipParameters) + zip4j.close() + targetFile.openOutputStream().use { output -> + tempFile.inputStream().use { input -> + input.copyTo(output) + } + } DiskUtil.createNoMediaFile(directory, context)