diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 4bba73d1de..0f0917a051 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -164,7 +164,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 442a123f38..5c903960f1 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -7,9 +7,6 @@ - - - @@ -39,7 +36,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 26f0b9b048..b1bd8879c3 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 @@ -71,8 +70,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 7ce28f9dac..0000000000 --- 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/ui/browse/BrowseTab.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/BrowseTab.kt index a05cba6202..2ce4685bb2 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 @@ -13,7 +13,6 @@ import cafe.adriel.voyager.navigator.Navigator import cafe.adriel.voyager.navigator.tab.LocalTabNavigator import cafe.adriel.voyager.navigator.tab.TabOptions 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 @@ -66,9 +65,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 4989b4704e..6a31ed0292 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 @@ -8,6 +8,7 @@ import eu.kanade.tachiyomi.source.Source import eu.kanade.tachiyomi.source.online.HttpSource import eu.kanade.tachiyomi.ui.reader.model.ReaderChapter 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 @@ -88,13 +89,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 4fb5adb6c4..3d385551d5 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 cd00e37561..324af51bf3 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 319179117d..056319d4e6 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 @@ -2,12 +2,11 @@ package eu.kanade.tachiyomi.ui.reader.loader import com.github.junrar.Archive import com.github.junrar.rarfile.FileHeader -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.lang.compareToCaseInsensitiveNaturalOrder -import tachiyomi.core.storage.toFile import tachiyomi.core.util.system.ImageUtil +import java.io.File import java.io.InputStream import java.io.PipedInputStream import java.io.PipedOutputStream @@ -15,9 +14,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) override var isLocal: Boolean = true 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 b63b325578..e04fe78e6a 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 @@ -1,24 +1,23 @@ package eu.kanade.tachiyomi.ui.reader.loader import android.os.Build -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.lang.compareToCaseInsensitiveNaturalOrder -import tachiyomi.core.storage.toFile import tachiyomi.core.util.system.ImageUtil +import java.io.File import java.nio.charset.StandardCharsets import java.util.zip.ZipFile /** * Loader used to load a chapter from a .zip or .cbz file. */ -internal class ZipPageLoader(file: UniFile) : PageLoader() { +internal class ZipPageLoader(file: File) : PageLoader() { private val zip = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { - ZipFile(file.toFile(), StandardCharsets.ISO_8859_1) + ZipFile(file, StandardCharsets.ISO_8859_1) } else { - ZipFile(file.toFile()) + ZipFile(file) } override var isLocal: Boolean = true 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 7650f65b5e..a00ee69e75 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 5343dfa3f8..c5c2bbbc86 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 39074cb0c1..ce7b074bf1 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 b2fa6731ca..1305001f50 100644 --- a/source-local/src/androidMain/kotlin/tachiyomi/source/local/LocalSource.kt +++ b/source-local/src/androidMain/kotlin/tachiyomi/source/local/LocalSource.kt @@ -26,7 +26,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 @@ -213,7 +213,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 -> zip.getEntry(COMIC_INFO_FILE)?.let { comicInfoFile -> zip.getInputStream(comicInfoFile).buffered().use { stream -> return copyComicInfoFile(stream, folderPath) @@ -222,7 +222,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) @@ -272,7 +272,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) } } @@ -331,7 +331,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 -> val entry = zip.entries().toList() .sortedWith { f1, f2 -> f1.name.compareToCaseInsensitiveNaturalOrder(f2.name) } .find { !it.isDirectory && ImageUtil.isImage(it.name) { zip.getInputStream(it) } } @@ -340,7 +340,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) } } @@ -349,7 +349,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) }