diff --git a/app/src/main/java/com/junkfood/seal/Downloader.kt b/app/src/main/java/com/junkfood/seal/Downloader.kt index aa4010935d..dad8f05751 100644 --- a/app/src/main/java/com/junkfood/seal/Downloader.kt +++ b/app/src/main/java/com/junkfood/seal/Downloader.kt @@ -242,7 +242,8 @@ object Downloader { } .onSuccess { if (!isDownloadingPlaylist) finishProcessing() - FileUtil.createIntentForFile(it).run { + if(it.isEmpty()) return@onSuccess + FileUtil.createIntentForFile(it.first()).run { NotificationUtil.finishNotification( notificationId, title = videoInfo.title, @@ -370,7 +371,7 @@ object Downloader { } fun openDownloadResult() { - if (taskState.value.progress == 100f) FileUtil.openFile(downloadResultTemp) + if (taskState.value.progress == 100f) FileUtil.openFileFromResult(downloadResultTemp) } fun String.toNotificationId(): Int = this.hashCode() diff --git a/app/src/main/java/com/junkfood/seal/database/VideoInfoDao.kt b/app/src/main/java/com/junkfood/seal/database/VideoInfoDao.kt index 957653a178..d9104db046 100644 --- a/app/src/main/java/com/junkfood/seal/database/VideoInfoDao.kt +++ b/app/src/main/java/com/junkfood/seal/database/VideoInfoDao.kt @@ -6,6 +6,7 @@ import androidx.room.Insert import androidx.room.Query import androidx.room.Transaction import androidx.room.Update +import com.junkfood.seal.util.FileUtil import kotlinx.coroutines.flow.Flow import java.io.File @@ -42,7 +43,7 @@ interface VideoInfoDao { suspend fun deleteInfoListByIdList(idList: List, deleteFile: Boolean = false) { idList.forEach { id -> val info = getInfoById(id) - if (deleteFile) File(info.videoPath).delete() + if (deleteFile) FileUtil.deleteFile(info.videoPath) deleteInfo(info) } } diff --git a/app/src/main/java/com/junkfood/seal/ui/page/settings/directory/DownloadDirectoryPreferences.kt b/app/src/main/java/com/junkfood/seal/ui/page/settings/directory/DownloadDirectoryPreferences.kt index 0baa2fb969..0249edf863 100644 --- a/app/src/main/java/com/junkfood/seal/ui/page/settings/directory/DownloadDirectoryPreferences.kt +++ b/app/src/main/java/com/junkfood/seal/ui/page/settings/directory/DownloadDirectoryPreferences.kt @@ -169,7 +169,7 @@ fun DownloadDirectoryPreferences(onBackPressed: () -> Unit) { }) { it?.let { if (editingDirectory == Directory.SDCARD) { - sdcardUri = it.path.toString() + sdcardUri = it.toString() PreferenceUtil.updateString(SDCARD_URI, sdcardUri) return@let } diff --git a/app/src/main/java/com/junkfood/seal/ui/page/videolist/RemoveItemDialog.kt b/app/src/main/java/com/junkfood/seal/ui/page/videolist/RemoveItemDialog.kt index 5468ba98fe..83e8e65363 100644 --- a/app/src/main/java/com/junkfood/seal/ui/page/videolist/RemoveItemDialog.kt +++ b/app/src/main/java/com/junkfood/seal/ui/page/videolist/RemoveItemDialog.kt @@ -11,8 +11,10 @@ import androidx.compose.material3.Text import androidx.compose.material3.TextButton import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp @@ -24,7 +26,7 @@ import com.junkfood.seal.ui.component.MultiChoiceItem fun RemoveItemDialog( videoListViewModel: VideoListViewModel = hiltViewModel(), ) { - val deleteFile = remember { mutableStateOf(false) } + var deleteFile by remember { mutableStateOf(false) } val detailState = videoListViewModel.detailViewState.collectAsState() if (detailState.value.showDialog) { // deleteFile.value = false @@ -43,14 +45,14 @@ fun RemoveItemDialog( ) MultiChoiceItem( text = stringResource(R.string.delete_file), - checked = deleteFile.value - ) { deleteFile.value = !deleteFile.value } + checked = deleteFile + ) { deleteFile = !deleteFile } } }, confirmButton = { TextButton(onClick = { videoListViewModel.hideDialog() - videoListViewModel.removeItem(deleteFile.value) + videoListViewModel.removeItem(deleteFile) }) { Text(text = stringResource(R.string.confirm)) } diff --git a/app/src/main/java/com/junkfood/seal/ui/page/videolist/VideoListPage.kt b/app/src/main/java/com/junkfood/seal/ui/page/videolist/VideoListPage.kt index 0c1d7b1705..cc93d9c6e9 100644 --- a/app/src/main/java/com/junkfood/seal/ui/page/videolist/VideoListPage.kt +++ b/app/src/main/java/com/junkfood/seal/ui/page/videolist/VideoListPage.kt @@ -70,6 +70,7 @@ import com.junkfood.seal.ui.component.MultiChoiceItem import com.junkfood.seal.util.AUDIO_REGEX import com.junkfood.seal.util.DatabaseUtil import com.junkfood.seal.util.FileUtil +import com.junkfood.seal.util.FileUtil.getFileSize import kotlinx.coroutines.launch import java.io.File @@ -108,7 +109,7 @@ fun VideoListPage( val fileSizeMap = remember(videoList.size) { mutableMapOf().apply { - putAll(videoList.map { Pair(it.id, File(it.videoPath).length()) }) + putAll(videoList.map { Pair(it.id, it.videoPath.getFileSize()) }) } } @@ -333,7 +334,7 @@ fun VideoListPage( if (selectedItemIds.contains(id)) selectedItemIds.remove(id) else selectedItemIds.add(id) }, - onClick = { FileUtil.openFileInURI(videoPath) } + onClick = { FileUtil.openFile(videoPath) } ) { videoListViewModel.showDrawer(scope, info) } } } diff --git a/app/src/main/java/com/junkfood/seal/ui/page/videolist/VideoListViewModel.kt b/app/src/main/java/com/junkfood/seal/ui/page/videolist/VideoListViewModel.kt index 7e8942cf14..472e8626d0 100644 --- a/app/src/main/java/com/junkfood/seal/ui/page/videolist/VideoListViewModel.kt +++ b/app/src/main/java/com/junkfood/seal/ui/page/videolist/VideoListViewModel.kt @@ -115,11 +115,7 @@ class VideoListViewModel @Inject constructor() : ViewModel() { fun removeItem(delete: Boolean) { viewModelScope.launch { withContext(Dispatchers.IO) { - if (delete) { - val info = DatabaseUtil.getInfoById(_detailViewState.value.id) - File(info.videoPath).delete() - } - DatabaseUtil.deleteInfoById(_detailViewState.value.id) + DatabaseUtil.deleteInfoListByIdList(listOf(detailViewState.value.id), delete) } } } diff --git a/app/src/main/java/com/junkfood/seal/util/DownloadUtil.kt b/app/src/main/java/com/junkfood/seal/util/DownloadUtil.kt index f6e20fdac9..eaee354468 100644 --- a/app/src/main/java/com/junkfood/seal/util/DownloadUtil.kt +++ b/app/src/main/java/com/junkfood/seal/util/DownloadUtil.kt @@ -334,7 +334,7 @@ object DownloadUtil { addOption("-P", pathBuilder.toString()) } - if (Build.VERSION.SDK_INT > 23) addOption("-P", "temp:" + context.getTempDir()) + if (Build.VERSION.SDK_INT > 23 && !sdcard) addOption("-P", "temp:" + context.getTempDir()) if (customPath) addOption("-o", outputPathTemplate + OUTPUT_TEMPLATE) else addOption("-o", OUTPUT_TEMPLATE) diff --git a/app/src/main/java/com/junkfood/seal/util/FileUtil.kt b/app/src/main/java/com/junkfood/seal/util/FileUtil.kt index e232f2ff0a..c4af33579e 100644 --- a/app/src/main/java/com/junkfood/seal/util/FileUtil.kt +++ b/app/src/main/java/com/junkfood/seal/util/FileUtil.kt @@ -11,6 +11,7 @@ import android.util.Log import android.webkit.MimeTypeMap import androidx.annotation.CheckResult import androidx.core.content.FileProvider +import androidx.documentfile.provider.DocumentFile import com.junkfood.seal.App.Companion.context import com.junkfood.seal.R import okhttp3.internal.closeQuietly @@ -20,42 +21,48 @@ const val AUDIO_REGEX = "(mp3|aac|opus|m4a)$" const val THUMBNAIL_REGEX = "\\.(jpg|png)$" object FileUtil { - fun openFile(downloadResult: Result>) { + fun openFileFromResult(downloadResult: Result>) { val filePaths = downloadResult.getOrNull() if (filePaths.isNullOrEmpty()) return - if (Build.VERSION.SDK_INT > 23) - openFileInURI(filePaths.first()) - else context.startActivity(createIntentForFile(filePaths)) + context.startActivity(createIntentForFile(filePaths.first())) } - fun openFileInURI(path: String) { - MediaScannerConnection.scanFile(context, arrayOf(path), null) { _, uri -> - if (uri == null) { + fun openFile(path: String) = + createIntentForFile(path).runCatching { context.startActivity(this) } + .onFailure { TextUtil.makeToastSuspend(context.getString(R.string.file_unavailable)) - } else { - context.startActivity(Intent().apply { - action = (Intent.ACTION_VIEW) - data = uri - flags = Intent.FLAG_ACTIVITY_NEW_TASK - }) } - } - } - fun createIntentForFile(filePaths: List): Intent? { - if (filePaths.isEmpty()) return null - val path = filePaths.first() - return Intent().apply { - action = (Intent.ACTION_VIEW) - addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_ACTIVITY_NEW_TASK) - data = FileProvider.getUriForFile( + + fun createIntentForFile(path: String): Intent? { + val uri = path.runCatching { + FileProvider.getUriForFile( context, context.packageName + ".provider", - File(path) + File(this) ) + }.getOrNull() ?: DocumentFile.fromSingleUri(context, Uri.parse(path))?.uri ?: return null + + return Intent().apply { + action = (Intent.ACTION_VIEW) + addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_ACTIVITY_NEW_TASK) + data = uri } } + fun String.getFileSize(): Long = this.run { + val length = File(this).length() + if (length == 0L) + DocumentFile.fromSingleUri(context, Uri.parse(this))?.length() ?: 0L + else length + } + + fun deleteFile(path: String) = + path.runCatching { + if (!File(path).delete()) + DocumentFile.fromSingleUri(context, Uri.parse(this))?.delete() + } + fun scanFileToMediaLibrary(title: String, downloadDir: String): List { Log.d(TAG, "scanFileToMediaLibrary: $title") val files = mutableListOf() @@ -89,6 +96,7 @@ object FileUtil { ) } tempPath.walkTopDown().forEach { + if (it.isDirectory) return@forEach try { val mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(it.extension) ?: "*/*"