diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 30ad4d4d..f22c20d3 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -5,10 +5,10 @@ plugins { id("com.mikepenz.aboutlibraries.plugin") } -val composeMaterialVersion = "1.7.0-beta05" -val composeMaterial3Version = "1.3.0-beta04" +val composeMaterialVersion = "1.7.0-beta06" +val composeMaterial3Version = "1.3.0-beta05" val composeCompilerVersion = "1.5.14" -val lifecycleVersion = "2.8.3" +val lifecycleVersion = "2.8.4" val shizukuVersion = "13.1.5" android { @@ -19,8 +19,8 @@ android { applicationId = "com.aliernfrog.pftool" minSdk = 21 targetSdk = 34 - versionCode = 18100 - versionName = "1.8.1" + versionCode = 18200 + versionName = "1.8.2" vectorDrawables { useSupportLibrary = true } } @@ -91,8 +91,8 @@ dependencies { implementation("androidx.compose.material3:material3-window-size-class:$composeMaterial3Version") implementation("androidx.lifecycle:lifecycle-runtime-ktx:$lifecycleVersion") implementation("androidx.lifecycle:lifecycle-runtime-compose:$lifecycleVersion") - implementation("androidx.activity:activity-compose:1.9.0") - implementation("androidx.navigation:navigation-compose:2.8.0-beta05") + implementation("androidx.activity:activity-compose:1.9.1") + implementation("androidx.navigation:navigation-compose:2.8.0-beta06") implementation("com.mikepenz:aboutlibraries-core:11.2.2") implementation("io.insert-koin:koin-androidx-compose:3.5.6") implementation("com.github.aliernfrog:top-toast-compose:2.1.0-alpha01") diff --git a/app/src/main/aidl/com/aliernfrog/pftool/IFileService.aidl b/app/src/main/aidl/com/aliernfrog/pftool/IFileService.aidl index 2bc21991..a65d9c7b 100644 --- a/app/src/main/aidl/com/aliernfrog/pftool/IFileService.aidl +++ b/app/src/main/aidl/com/aliernfrog/pftool/IFileService.aidl @@ -1,5 +1,6 @@ package com.aliernfrog.pftool; +import android.os.ParcelFileDescriptor; import com.aliernfrog.pftool.data.ServiceFile; interface IFileService { @@ -13,15 +14,17 @@ interface IFileService { boolean exists(String path) = 4; - byte[] getByteArray(String path) = 5; + ServiceFile getFile(String path) = 5; - ServiceFile getFile(String path) = 6; + ServiceFile[] listFiles(String path) = 6; - ServiceFile[] listFiles(String path) = 7; + void mkdirs(String path) = 7; void renameFile(String oldPath, String newPath) = 8; void unzipMap(String path, String targetPath) = 9; void zipMap(String path, String targetPath) = 10; + + ParcelFileDescriptor getFd(String path) = 11; } \ No newline at end of file diff --git a/app/src/main/java/com/aliernfrog/pftool/Constant.kt b/app/src/main/java/com/aliernfrog/pftool/Constant.kt index 98407297..7a82a2aa 100644 --- a/app/src/main/java/com/aliernfrog/pftool/Constant.kt +++ b/app/src/main/java/com/aliernfrog/pftool/Constant.kt @@ -18,7 +18,6 @@ const val documentsUIPackageName = "com.google.android.documentsui" val externalStorageRoot = Environment.getExternalStorageDirectory().toString()+"/" val supportsPerAppLanguagePreferences = Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU -val imeSupportsSyncAppContent = Build.VERSION.SDK_INT >= Build.VERSION_CODES.R val folderPickerSupportsInitialUri = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O val hasAndroidDataRestrictions = Build.VERSION.SDK_INT >= Build.VERSION_CODES.R diff --git a/app/src/main/java/com/aliernfrog/pftool/data/ServiceFile.kt b/app/src/main/java/com/aliernfrog/pftool/data/ServiceFile.kt index 366abdb0..acec04d8 100644 --- a/app/src/main/java/com/aliernfrog/pftool/data/ServiceFile.kt +++ b/app/src/main/java/com/aliernfrog/pftool/data/ServiceFile.kt @@ -29,11 +29,6 @@ fun ServiceFile.exists(): Boolean { return shizukuViewModel.fileService!!.exists(path) } -fun ServiceFile.getByteArray(): ByteArray { - val shizukuViewModel = getKoinInstance() - return shizukuViewModel.fileService!!.getByteArray(path) -} - fun ServiceFile.listFiles(): Array? { val shizukuViewModel = getKoinInstance() return shizukuViewModel.fileService!!.listFiles(path) @@ -42,4 +37,9 @@ fun ServiceFile.listFiles(): Array? { fun ServiceFile.renameTo(newPath: String) { val shizukuViewModel = getKoinInstance() shizukuViewModel.fileService!!.renameFile(path, newPath) +} + +fun ServiceFile.mkdirs() { + val shizukuViewModel = getKoinInstance() + shizukuViewModel.fileService!!.mkdirs(path) } \ No newline at end of file diff --git a/app/src/main/java/com/aliernfrog/pftool/impl/MapFile.kt b/app/src/main/java/com/aliernfrog/pftool/impl/MapFile.kt index d1e1e722..4e3590ba 100644 --- a/app/src/main/java/com/aliernfrog/pftool/impl/MapFile.kt +++ b/app/src/main/java/com/aliernfrog/pftool/impl/MapFile.kt @@ -1,6 +1,7 @@ package com.aliernfrog.pftool.impl import android.content.Context +import android.os.ParcelFileDescriptor import android.util.Log import com.aliernfrog.pftool.R import com.aliernfrog.pftool.TAG @@ -26,6 +27,7 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext import org.koin.core.component.KoinComponent import org.koin.core.component.inject +import java.io.ByteArrayOutputStream import java.io.File class MapFile( @@ -101,15 +103,31 @@ class MapFile( else if (path.startsWith(mapsViewModel.exportedMapsDir)) MapImportedState.EXPORTED else MapImportedState.NONE + private var cachedThumbnailModel: Any? = null + /** * Thumbnail model of the map. */ - val thumbnailModel: Any? = if (importedState != MapImportedState.IMPORTED) null else when (file) { - is File -> if (file.isDirectory) "$path/Thumbnail.jpg" else null - is DocumentFileCompat -> if (file.isDirectory()) file.findFile("Thumbnail.jpg")?.uri?.toString() else null - is ServiceFile -> if (!file.isFile) shizukuViewModel.fileService!!.getByteArray("$path/Thumbnail.jpg") else null - else -> null - } + val thumbnailModel: Any? + get() { + return if (importedState != MapImportedState.IMPORTED) null else when (file) { + is File -> if (file.isDirectory) "$path/Thumbnail.jpg" else null + is DocumentFileCompat -> if (file.isDirectory()) file.findFile("Thumbnail.jpg")?.uri?.toString() else null + is ServiceFile -> if (!file.isFile) { + if (cachedThumbnailModel != null) return cachedThumbnailModel + val fd = shizukuViewModel.fileService!!.getFd("$path/Thumbnail.jpg") + val input = ParcelFileDescriptor.AutoCloseInputStream(fd) + val output = ByteArrayOutputStream() + input.copyTo(output) + cachedThumbnailModel = output.toByteArray() + output.close() + input.close() + fd.close() + return cachedThumbnailModel + } else null + else -> null + } + } /** * Details of the map. Includes size (KB) and modified time. diff --git a/app/src/main/java/com/aliernfrog/pftool/service/FileService.kt b/app/src/main/java/com/aliernfrog/pftool/service/FileService.kt index 52b1e41c..656a7181 100644 --- a/app/src/main/java/com/aliernfrog/pftool/service/FileService.kt +++ b/app/src/main/java/com/aliernfrog/pftool/service/FileService.kt @@ -1,5 +1,6 @@ package com.aliernfrog.pftool.service +import android.os.ParcelFileDescriptor import com.aliernfrog.pftool.IFileService import com.aliernfrog.pftool.data.ServiceFile import com.aliernfrog.pftool.util.getServiceFile @@ -36,10 +37,6 @@ class FileService : IFileService.Stub() { return File(path).exists() } - override fun getByteArray(path: String): ByteArray { - return File(path).readBytes() - } - override fun getFile(path: String): ServiceFile { return getServiceFile(File(path)) } @@ -51,6 +48,10 @@ class FileService : IFileService.Stub() { }.toTypedArray() } + override fun mkdirs(path: String) { + File(path).mkdirs() + } + override fun renameFile(oldPath: String, newPath: String) { File(oldPath).renameTo(File(newPath)) } @@ -62,4 +63,8 @@ class FileService : IFileService.Stub() { override fun zipMap(path: String, targetPath: String) { ZipUtil.zipMap(File(path), File(targetPath)) } + + override fun getFd(path: String): ParcelFileDescriptor { + return ParcelFileDescriptor.open(File(path), ParcelFileDescriptor.MODE_READ_ONLY) + } } \ No newline at end of file diff --git a/app/src/main/java/com/aliernfrog/pftool/ui/component/AppModalBottomSheet.kt b/app/src/main/java/com/aliernfrog/pftool/ui/component/AppModalBottomSheet.kt index d5332d4e..55e66671 100644 --- a/app/src/main/java/com/aliernfrog/pftool/ui/component/AppModalBottomSheet.kt +++ b/app/src/main/java/com/aliernfrog/pftool/ui/component/AppModalBottomSheet.kt @@ -21,7 +21,6 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp -import com.aliernfrog.pftool.imeSupportsSyncAppContent import com.aliernfrog.pftool.ui.viewmodel.InsetsViewModel import kotlinx.coroutines.launch import org.koin.androidx.compose.koinViewModel @@ -76,11 +75,6 @@ fun BaseModalBottomSheet( dragHandle = dragHandle, contentWindowInsets = { WindowInsets(0.dp) } ) { - content( - insetsViewModel.bottomPadding - // If IME does not sync app content, keyboard will show over the bottom sheet - // Add IME padding to workaround this - + if (imeSupportsSyncAppContent) 0.dp else insetsViewModel.imePadding - ) + content(insetsViewModel.bottomPadding) } } \ No newline at end of file diff --git a/app/src/main/java/com/aliernfrog/pftool/ui/viewmodel/MapsViewModel.kt b/app/src/main/java/com/aliernfrog/pftool/ui/viewmodel/MapsViewModel.kt index c3d383f8..156cc5f6 100644 --- a/app/src/main/java/com/aliernfrog/pftool/ui/viewmodel/MapsViewModel.kt +++ b/app/src/main/java/com/aliernfrog/pftool/ui/viewmodel/MapsViewModel.kt @@ -17,7 +17,9 @@ import com.aliernfrog.pftool.R import com.aliernfrog.pftool.TAG import com.aliernfrog.pftool.data.MapActionResult import com.aliernfrog.pftool.data.ServiceFile +import com.aliernfrog.pftool.data.exists import com.aliernfrog.pftool.data.listFiles +import com.aliernfrog.pftool.data.mkdirs import com.aliernfrog.pftool.enum.StorageAccessType import com.aliernfrog.pftool.impl.MapFile import com.aliernfrog.pftool.impl.Progress @@ -178,6 +180,8 @@ class MapsViewModel( } StorageAccessType.SHIZUKU -> { val shizukuViewModel = getKoinInstance() + val file = shizukuViewModel.fileService!!.getFile(mapsDir)!! + if (!file.exists()) file.mkdirs() shizukuViewModel.fileService!!.getFile(mapsDir) } StorageAccessType.ALL_FILES -> { @@ -216,6 +220,8 @@ class MapsViewModel( } StorageAccessType.SHIZUKU -> { val shizukuViewModel = getKoinInstance() + val file = shizukuViewModel.fileService!!.getFile(exportedMapsDir) + if (!file.exists()) file.mkdirs() shizukuViewModel.fileService!!.getFile(exportedMapsDir) } StorageAccessType.ALL_FILES -> { diff --git a/app/src/main/java/com/aliernfrog/pftool/util/staticutil/FileUtil.kt b/app/src/main/java/com/aliernfrog/pftool/util/staticutil/FileUtil.kt index d4efbc86..2c60ecf1 100644 --- a/app/src/main/java/com/aliernfrog/pftool/util/staticutil/FileUtil.kt +++ b/app/src/main/java/com/aliernfrog/pftool/util/staticutil/FileUtil.kt @@ -4,13 +4,15 @@ import android.content.Context import android.content.Intent import android.net.Uri import android.os.Environment +import android.os.ParcelFileDescriptor import android.provider.DocumentsContract import android.text.format.DateUtils import androidx.core.content.FileProvider import com.aliernfrog.pftool.R import com.aliernfrog.pftool.data.ServiceFile -import com.aliernfrog.pftool.data.getByteArray +import com.aliernfrog.pftool.ui.viewmodel.ShizukuViewModel import com.aliernfrog.pftool.util.extension.toPath +import com.aliernfrog.pftool.util.getKoinInstance import com.lazygeniouz.dfc.file.DocumentFileCompat import java.io.File @@ -110,7 +112,11 @@ class FileUtil { val inputStream = when(file) { is DocumentFileCompat -> context.contentResolver.openInputStream(file.uri) is File -> file.inputStream() - is ServiceFile -> file.getByteArray().inputStream() + is ServiceFile -> { + val shizukuViewModel = getKoinInstance() + val fd = shizukuViewModel.fileService!!.getFd(file.path) + ParcelFileDescriptor.AutoCloseInputStream(fd) + } else -> throw IllegalArgumentException() } val targetFile = File("${context.externalCacheDir}/shared/$fileName") diff --git a/build.gradle.kts b/build.gradle.kts index ff5dd348..4d056ca3 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,6 +1,6 @@ plugins { - id("com.android.application") version "8.4.1" apply false - id("com.android.library") version "8.4.1" apply false + id("com.android.application") version "8.5.1" apply false + id("com.android.library") version "8.5.1" apply false id("org.jetbrains.kotlin.android") version "1.9.24" apply false id("org.jetbrains.kotlin.plugin.parcelize") version "1.9.24" apply false id("com.mikepenz.aboutlibraries.plugin") version "11.2.2" apply false