diff --git a/app/src/main/kotlin/top/fatweb/oxygen/toolbox/ui/view/ToolViewScreen.kt b/app/src/main/kotlin/top/fatweb/oxygen/toolbox/ui/view/ToolViewScreen.kt index 8d62652..76caa0c 100644 --- a/app/src/main/kotlin/top/fatweb/oxygen/toolbox/ui/view/ToolViewScreen.kt +++ b/app/src/main/kotlin/top/fatweb/oxygen/toolbox/ui/view/ToolViewScreen.kt @@ -225,7 +225,7 @@ private fun initWebView( webview.settings.javaScriptEnabled = true webview.settings.domStorageEnabled = true webview.addJavascriptInterface( - NativeWebApi(context = context, webView = webview, permissionLauncher), + NativeWebApi(context = context, permissionLauncher = permissionLauncher), "NativeApi" ) webview.setDownloadListener { url, userAgent, _, mimetype, _ -> diff --git a/app/src/main/kotlin/top/fatweb/oxygen/toolbox/util/FileUtils.kt b/app/src/main/kotlin/top/fatweb/oxygen/toolbox/util/FileUtils.kt new file mode 100644 index 0000000..c058ed4 --- /dev/null +++ b/app/src/main/kotlin/top/fatweb/oxygen/toolbox/util/FileUtils.kt @@ -0,0 +1,95 @@ +package top.fatweb.oxygen.toolbox.util + +import android.content.ContentValues +import android.content.Context +import android.os.Build +import android.os.Environment +import android.provider.MediaStore +import androidx.activity.compose.ManagedActivityResultLauncher +import androidx.annotation.RequiresApi +import kotlinx.coroutines.runBlocking +import timber.log.Timber +import java.io.File +import java.io.FileOutputStream +import java.io.IOException +import kotlin.let + +fun saveToDownloads( + context: Context, + permissionLauncher: ManagedActivityResultLauncher, + data: ByteArray, + fileName: String +) = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + saveFileToDownloads(context = context, data = data, fileName = fileName) +} else { + saveFileToDownloads( + context = context, + permissionLauncher = permissionLauncher, + data = data, + fileName = fileName + ) +} + +@RequiresApi(Build.VERSION_CODES.Q) +private fun saveFileToDownloads( + context: Context, + data: ByteArray, + fileName: String +): Boolean { + val resolver = context.contentResolver + + val contentValues = ContentValues().apply { + put(MediaStore.Downloads.DISPLAY_NAME, fileName) + put(MediaStore.Downloads.MIME_TYPE, "application/octet-stream") + put(MediaStore.Downloads.IS_PENDING, 1) + } + + val collection = MediaStore.Downloads.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY) + val fileUri = resolver.insert(collection, contentValues) + + return fileUri?.let { uri -> + resolver.openOutputStream(uri)?.use { outputStream -> + outputStream.write(data) + outputStream.flush() + } + + contentValues.clear() + contentValues.put(MediaStore.Downloads.IS_PENDING, 0) + resolver.update(uri, contentValues, null, null) + true + } ?: run { + Timber.e("Could not save file $fileName to Downloads") + false + } +} + +private fun saveFileToDownloads( + context: Context, + permissionLauncher: ManagedActivityResultLauncher, + data: ByteArray, + fileName: String +): Boolean { + if (!runBlocking { + Permissions.requestWriteExternalStoragePermission( + context, + permissionLauncher + ) + }) { + return false + } + + val downloadsDir = + Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS) + val file = File(downloadsDir, fileName) + + return try { + FileOutputStream(file).apply { + write(data) + close() + } + true + } catch (e: IOException) { + Timber.e(e, "Could not save file $fileName to ${file.absolutePath}") + false + } +} \ No newline at end of file diff --git a/app/src/main/kotlin/top/fatweb/oxygen/toolbox/util/NativeWebApi.kt b/app/src/main/kotlin/top/fatweb/oxygen/toolbox/util/NativeWebApi.kt index cab0360..a9c3f8d 100644 --- a/app/src/main/kotlin/top/fatweb/oxygen/toolbox/util/NativeWebApi.kt +++ b/app/src/main/kotlin/top/fatweb/oxygen/toolbox/util/NativeWebApi.kt @@ -2,26 +2,14 @@ package top.fatweb.oxygen.toolbox.util import android.content.ClipData import android.content.ClipboardManager -import android.content.ContentValues import android.content.Context import android.content.Context.CLIPBOARD_SERVICE -import android.os.Build -import android.os.Environment -import android.provider.MediaStore import android.util.Base64 import android.webkit.JavascriptInterface -import android.webkit.WebView import androidx.activity.compose.ManagedActivityResultLauncher -import androidx.annotation.RequiresApi -import kotlinx.coroutines.runBlocking -import timber.log.Timber -import java.io.File -import java.io.FileOutputStream -import java.io.IOException class NativeWebApi( private val context: Context, - private val webView: WebView, private val permissionLauncher: ManagedActivityResultLauncher ) { @JavascriptInterface @@ -43,66 +31,11 @@ class NativeWebApi( fun saveToDownloads(dataBase64: String, fileName: String): Boolean { val data = Base64.decode(dataBase64, Base64.DEFAULT) - return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { - saveFileToDownloads(data = data, fileName = fileName) - } else { - saveFileToExternalDownloads(data = data, fileName = fileName) - } - } - - @RequiresApi(Build.VERSION_CODES.Q) - private fun saveFileToDownloads(data: ByteArray, fileName: String): Boolean { - val resolver = context.contentResolver - - val contentValues = ContentValues().apply { - put(MediaStore.Downloads.DISPLAY_NAME, fileName) - put(MediaStore.Downloads.MIME_TYPE, "application/octet-stream") - put(MediaStore.Downloads.IS_PENDING, 1) - } - - val collection = MediaStore.Downloads.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY) - val fileUri = resolver.insert(collection, contentValues) - - return fileUri?.let { uri -> - resolver.openOutputStream(uri)?.use { outputStream -> - outputStream.write(data) - outputStream.flush() - } - - contentValues.clear() - contentValues.put(MediaStore.Downloads.IS_PENDING, 0) - resolver.update(uri, contentValues, null, null) - true - } ?: let { - Timber.e("Could not save file $fileName to Downloads") - false - } - } - - private fun saveFileToExternalDownloads(data: ByteArray, fileName: String): Boolean { - if (!runBlocking { Permissions.requestWriteExternalStoragePermission(context, permissionLauncher) }) { - return false - } - - val downloadsDir = - Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS) - val file = File(downloadsDir, fileName) - - return try { - FileOutputStream(file).apply { - write(data) - close() - } - true - } catch (e: IOException) { - Timber.e(e, "Could not save file $fileName to ${file.absolutePath}") - false - } - } - - private fun callback(callback: String, vararg args: Any) { - val jsCode = - "$callback(${args.map { if (it is String) "'$it'" else it }.joinToString(", ")})" - webView.evaluateJavascript(jsCode, null) + return saveToDownloads( + context = context, + permissionLauncher = permissionLauncher, + data = data, + fileName = fileName + ) } } \ No newline at end of file