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) }