@@ -4,14 +4,23 @@ import android.content.Intent
44import android.net.Uri
55import android.os.Bundle
66import org.fossify.commons.dialogs.FilePickerDialog
7- import org.fossify.commons.extensions.*
7+ import org.fossify.commons.extensions.getDocumentFile
8+ import org.fossify.commons.extensions.getDoesFilePathExist
9+ import org.fossify.commons.extensions.getFileOutputStreamSync
10+ import org.fossify.commons.extensions.getFilenameFromContentUri
11+ import org.fossify.commons.extensions.getFilenameFromPath
12+ import org.fossify.commons.extensions.getMimeType
13+ import org.fossify.commons.extensions.needsStupidWritePermissions
14+ import org.fossify.commons.extensions.rescanPaths
15+ import org.fossify.commons.extensions.showErrorToast
16+ import org.fossify.commons.extensions.toast
17+ import org.fossify.commons.extensions.viewBinding
818import org.fossify.commons.helpers.NavigationIcon
919import org.fossify.commons.helpers.ensureBackgroundThread
1020import org.fossify.filemanager.R
1121import org.fossify.filemanager.databinding.ActivitySaveAsBinding
1222import org.fossify.filemanager.extensions.config
1323import java.io.File
14- import java.io.IOException
1524
1625class SaveAsActivity : SimpleActivity() {
1726 private val binding by viewBinding(ActivitySaveAsBinding::inflate)
@@ -34,184 +43,50 @@ class SaveAsActivity : SimpleActivity() {
3443 }
3544
3645 private fun saveAsDialog() {
37- when {
38- intent.action == Intent.ACTION_SEND && intent.extras?.containsKey(Intent.EXTRA_STREAM) == true -> {
39- handleSingleFile()
40- }
41- intent.action == Intent.ACTION_SEND_MULTIPLE && intent.extras?.containsKey(Intent.EXTRA_STREAM) == true -> {
42- handleMultipleFiles()
43- }
44- else -> {
45- toast(R.string.unknown_error_occurred)
46- finish()
47- }
48- }
49- }
50-
51- private fun handleSingleFile() {
52- FilePickerDialog(this, pickFile = false, showHidden = config.shouldShowHidden(), showFAB = true, showFavoritesButton = true) {
53- val destination = it
54- handleSAFDialog(destination) {
55- toast(R.string.saving)
56- ensureBackgroundThread {
57- try {
58- createDestinationIfNeeded(destination)
59-
60- val source = intent.getParcelableExtra<Uri>(Intent.EXTRA_STREAM)!!
61- val originalFilename = getFilenameFromContentUri(source)
62- ?: source.toString().getFilenameFromPath()
63- val filename = sanitizeFilename(originalFilename)
64- val mimeType = contentResolver.getType(source)
65- ?: intent.type?.takeIf { it != "*/*" }
66- ?: filename.getMimeType()
67- val inputStream = contentResolver.openInputStream(source)
68-
69- val destinationPath = getAvailablePath("$destination/$filename")
70- val outputStream = getFileOutputStreamSync(destinationPath, mimeType, null)!!
71- inputStream!!.copyTo(outputStream)
72- rescanPaths(arrayListOf(destinationPath))
73- toast(R.string.file_saved)
74- finish()
75- } catch (e: IOException) {
76- showErrorToast(e)
77- finish()
78- } catch (e: SecurityException) {
79- showErrorToast(e)
80- finish()
46+ if (intent.action == Intent.ACTION_SEND && intent.extras?.containsKey(Intent.EXTRA_STREAM) == true) {
47+ FilePickerDialog(this, pickFile = false, showHidden = config.shouldShowHidden(), showFAB = true, showFavoritesButton = true) {
48+ val destination = it
49+ handleSAFDialog(destination) {
50+ toast(R.string.saving)
51+ ensureBackgroundThread {
52+ try {
53+ if (!getDoesFilePathExist(destination)) {
54+ if (needsStupidWritePermissions(destination)) {
55+ val document = getDocumentFile(destination)
56+ document!!.createDirectory(destination.getFilenameFromPath())
57+ } else {
58+ File(destination).mkdirs()
59+ }
60+ }
61+
62+ val source = intent.getParcelableExtra<Uri>(Intent.EXTRA_STREAM)!!
63+ val originalFilename = getFilenameFromContentUri(source)
64+ ?: source.toString().getFilenameFromPath()
65+ val filename = sanitizeFilename(originalFilename)
66+ val mimeType = contentResolver.getType(source)
67+ ?: intent.type?.takeIf { it != "*/*" }
68+ ?: filename.getMimeType()
69+ val inputStream = contentResolver.openInputStream(source)
70+
71+ val destinationPath = getAvailablePath("$destination/$filename")
72+ val outputStream = getFileOutputStreamSync(destinationPath, mimeType, null)!!
73+ inputStream!!.copyTo(outputStream)
74+ rescanPaths(arrayListOf(destinationPath))
75+ toast(R.string.file_saved)
76+ finish()
77+ } catch (e: Exception) {
78+ showErrorToast(e)
79+ finish()
80+ }
8181 }
8282 }
8383 }
84- }
85- }
86-
87- private fun handleMultipleFiles() {
88- FilePickerDialog(this, pickFile = false, showHidden = config.shouldShowHidden(), showFAB = true, showFavoritesButton = true) { destination ->
89- handleSAFDialog(destination) {
90- toast(R.string.saving)
91- ensureBackgroundThread {
92- processMultipleFiles(destination)
93- }
94- }
95- }
96- }
97-
98- private fun processMultipleFiles(destination: String) {
99- try {
100- createDestinationIfNeeded(destination)
101-
102- val uriList = intent.getParcelableArrayListExtra<Uri>(Intent.EXTRA_STREAM)
103- if (uriList.isNullOrEmpty()) {
104- runOnUiThread {
105- toast(R.string.no_items_found)
106- finish()
107- }
108- return
109- }
110-
111- val result = saveAllFiles(destination, uriList)
112- showFinalResult(result)
113- } catch (e: IOException) {
114- runOnUiThread {
115- showErrorToast(e)
116- finish()
117- }
118- } catch (e: SecurityException) {
119- runOnUiThread {
120- showErrorToast(e)
121- finish()
122- }
123- }
124- }
125-
126- private fun saveAllFiles(destination: String, uriList: ArrayList<Uri>): SaveResult {
127- val mimeTypes = intent.getStringArrayListExtra(Intent.EXTRA_MIME_TYPES)
128- val savedPaths = mutableListOf<String>()
129- var successCount = 0
130- var errorCount = 0
131-
132- for ((index, source) in uriList.withIndex()) {
133- if (saveSingleFileItem(destination, source, index, mimeTypes)) {
134- successCount++
135- savedPaths.add(destination)
136- } else {
137- errorCount++
138- }
139- }
140-
141- if (savedPaths.isNotEmpty()) {
142- rescanPaths(ArrayList(savedPaths))
143- }
144-
145- return SaveResult(successCount, errorCount)
146- }
147-
148- private fun saveSingleFileItem(
149- destination: String,
150- source: Uri,
151- index: Int,
152- mimeTypes: ArrayList<String>?): Boolean {
153- return try {
154- val originalFilename = getFilenameFromContentUri(source)
155- ?: source.toString().getFilenameFromPath()
156- val filename = sanitizeFilename(originalFilename)
157-
158- val mimeType = contentResolver.getType(source)
159- ?: mimeTypes?.getOrNull(index)?.takeIf { it != "*/*" }
160- ?: intent.type?.takeIf { it != "*/*" }
161- ?: filename.getMimeType()
162-
163- val inputStream = contentResolver.openInputStream(source)
164- ?: throw IOException(getString(R.string.error, source))
165-
166- val destinationPath = getAvailablePath("$destination/$filename")
167- val outputStream = getFileOutputStreamSync(destinationPath, mimeType, null)
168- ?: throw IOException(getString(R.string.error, source))
169-
170- inputStream.use { input ->
171- outputStream.use { output ->
172- input.copyTo(output)
173- }
174- }
175- true
176- } catch (e: IOException) {
177- showErrorToast(e)
178- false
179- } catch (e: SecurityException) {
180- showErrorToast(e)
181- false
182- }
183- }
184-
185- private fun showFinalResult(result: SaveResult) {
186- runOnUiThread {
187- when {
188- result.successCount > 0 && result.errorCount == 0 -> {
189- val message = resources.getQuantityString(R.plurals.files_saved,result.successCount)
190- toast(message)
191- }
192- result.successCount > 0 && result.errorCount > 0 -> {
193- toast(getString(R.string.files_saved_partially))
194- }
195- else -> {
196- toast(R.string.error)
197- }
198- }
84+ } else {
85+ toast(R.string.unknown_error_occurred)
19986 finish()
20087 }
20188 }
20289
203- private data class SaveResult(val successCount: Int, val errorCount: Int)
204- private fun createDestinationIfNeeded(destination: String) {
205- if (!getDoesFilePathExist(destination)) {
206- if (needsStupidWritePermissions(destination)) {
207- val document = getDocumentFile(destination)
208- document!!.createDirectory(destination.getFilenameFromPath())
209- } else {
210- File(destination).mkdirs()
211- }
212- }
213- }
214-
21590 override fun onResume() {
21691 super.onResume()
21792 setupTopAppBar(binding.activitySaveAsAppbar, NavigationIcon.Arrow)
0 commit comments