mirror of
https://gitlab.com/nkming2/nc-photos.git
synced 2025-03-25 00:14:42 +01:00
Move native code to plugin
This commit is contained in:
parent
8176e10dbe
commit
653c981a99
3 changed files with 163 additions and 96 deletions
|
@ -1,17 +1,10 @@
|
||||||
package com.nkming.nc_photos
|
package com.nkming.nc_photos
|
||||||
|
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.content.ContentValues
|
import com.nkming.nc_photos.plugin.MediaStoreUtil
|
||||||
import android.content.Intent
|
import com.nkming.nc_photos.plugin.PermissionException
|
||||||
import android.net.Uri
|
|
||||||
import android.os.Build
|
|
||||||
import android.os.Environment
|
|
||||||
import android.provider.MediaStore
|
|
||||||
import androidx.annotation.RequiresApi
|
|
||||||
import androidx.core.content.FileProvider
|
|
||||||
import io.flutter.plugin.common.MethodCall
|
import io.flutter.plugin.common.MethodCall
|
||||||
import io.flutter.plugin.common.MethodChannel
|
import io.flutter.plugin.common.MethodChannel
|
||||||
import java.io.*
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Save downloaded item on device
|
* Save downloaded item on device
|
||||||
|
@ -55,100 +48,28 @@ class MediaStoreChannelHandler(activity: Activity) :
|
||||||
private fun saveFileToDownload(
|
private fun saveFileToDownload(
|
||||||
fileName: String, content: ByteArray, result: MethodChannel.Result
|
fileName: String, content: ByteArray, result: MethodChannel.Result
|
||||||
) {
|
) {
|
||||||
val stream = ByteArrayInputStream(content)
|
try {
|
||||||
writeFileToDownload(fileName, stream, result)
|
val uri =
|
||||||
|
MediaStoreUtil.saveFileToDownload(_context, fileName, content)
|
||||||
|
result.success(uri.toString())
|
||||||
|
} catch (e: PermissionException) {
|
||||||
|
PermissionHandler.ensureWriteExternalStorage(_activity)
|
||||||
|
result.error("permissionError", "Permission not granted", null)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun copyFileToDownload(
|
private fun copyFileToDownload(
|
||||||
toFileName: String, fromFilePath: String, result: MethodChannel.Result
|
toFileName: String, fromFilePath: String, result: MethodChannel.Result
|
||||||
) {
|
) {
|
||||||
val file = File(fromFilePath)
|
try {
|
||||||
val stream = file.inputStream()
|
val uri = MediaStoreUtil.copyFileToDownload(
|
||||||
writeFileToDownload(toFileName, stream, result)
|
_context, toFileName, fromFilePath
|
||||||
}
|
)
|
||||||
|
result.success(uri.toString())
|
||||||
private fun writeFileToDownload(
|
} catch (e: PermissionException) {
|
||||||
fileName: String, data: InputStream, result: MethodChannel.Result
|
PermissionHandler.ensureWriteExternalStorage(_activity)
|
||||||
) {
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
|
||||||
writeFileToDownload29(fileName, data, result)
|
|
||||||
} else {
|
|
||||||
writeFileToDownload0(fileName, data, result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@RequiresApi(Build.VERSION_CODES.Q)
|
|
||||||
private fun writeFileToDownload29(
|
|
||||||
fileName: String, data: InputStream, result: MethodChannel.Result
|
|
||||||
) {
|
|
||||||
// Add a media item that other apps shouldn't see until the item is
|
|
||||||
// fully written to the media store.
|
|
||||||
val resolver = _context.applicationContext.contentResolver
|
|
||||||
|
|
||||||
// Find all audio files on the primary external storage device.
|
|
||||||
val collection = MediaStore.Downloads.getContentUri(
|
|
||||||
MediaStore.VOLUME_EXTERNAL_PRIMARY
|
|
||||||
)
|
|
||||||
val file = File(fileName)
|
|
||||||
val details = ContentValues().apply {
|
|
||||||
put(MediaStore.Downloads.DISPLAY_NAME, file.name)
|
|
||||||
if (file.parent != null) {
|
|
||||||
put(
|
|
||||||
MediaStore.Downloads.RELATIVE_PATH,
|
|
||||||
"${Environment.DIRECTORY_DOWNLOADS}/${file.parent}"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val contentUri = resolver.insert(collection, details)
|
|
||||||
|
|
||||||
resolver.openFileDescriptor(contentUri!!, "w", null).use { pfd ->
|
|
||||||
// Write data into the pending audio file.
|
|
||||||
BufferedOutputStream(FileOutputStream(pfd!!.fileDescriptor)).use { stream ->
|
|
||||||
data.copyTo(stream)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
result.success(contentUri.toString())
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun writeFileToDownload0(
|
|
||||||
fileName: String, data: InputStream, result: MethodChannel.Result
|
|
||||||
) {
|
|
||||||
if (!PermissionHandler.ensureWriteExternalStorage(_activity)) {
|
|
||||||
result.error("permissionError", "Permission not granted", null)
|
result.error("permissionError", "Permission not granted", null)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val path = Environment.getExternalStoragePublicDirectory(
|
|
||||||
Environment.DIRECTORY_DOWNLOADS
|
|
||||||
)
|
|
||||||
var file = File(path, fileName)
|
|
||||||
var count = 1
|
|
||||||
while (file.exists()) {
|
|
||||||
val f = File(fileName)
|
|
||||||
file =
|
|
||||||
File(path, "${f.nameWithoutExtension} ($count).${f.extension}")
|
|
||||||
++count
|
|
||||||
}
|
|
||||||
file.parentFile?.mkdirs()
|
|
||||||
BufferedOutputStream(FileOutputStream(file)).use { stream ->
|
|
||||||
data.copyTo(stream)
|
|
||||||
}
|
|
||||||
|
|
||||||
val fileUri = Uri.fromFile(file)
|
|
||||||
triggerMediaScan(fileUri)
|
|
||||||
val contentUri = FileProvider.getUriForFile(
|
|
||||||
_context, "${BuildConfig.APPLICATION_ID}.fileprovider", file
|
|
||||||
)
|
|
||||||
result.success(contentUri.toString())
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun triggerMediaScan(uri: Uri) {
|
|
||||||
val scanIntent = Intent().apply {
|
|
||||||
action = Intent.ACTION_MEDIA_SCANNER_SCAN_FILE
|
|
||||||
data = uri
|
|
||||||
}
|
|
||||||
_context.sendBroadcast(scanIntent)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private val _activity = activity
|
private val _activity = activity
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
package com.nkming.nc_photos.plugin
|
||||||
|
|
||||||
|
class PermissionException(message: String) : Exception(message)
|
|
@ -0,0 +1,143 @@
|
||||||
|
package com.nkming.nc_photos.plugin
|
||||||
|
|
||||||
|
import android.Manifest
|
||||||
|
import android.content.ContentValues
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.content.pm.PackageManager
|
||||||
|
import android.net.Uri
|
||||||
|
import android.os.Build
|
||||||
|
import android.os.Environment
|
||||||
|
import android.provider.MediaStore
|
||||||
|
import androidx.annotation.RequiresApi
|
||||||
|
import androidx.core.content.ContextCompat
|
||||||
|
import androidx.core.content.FileProvider
|
||||||
|
import java.io.*
|
||||||
|
|
||||||
|
interface MediaStoreUtil {
|
||||||
|
companion object {
|
||||||
|
/**
|
||||||
|
* Save the @c content as a file under the user Download dir
|
||||||
|
*
|
||||||
|
* @param context
|
||||||
|
* @param filename Filename of the new file
|
||||||
|
* @param content
|
||||||
|
* @return Uri of the created file
|
||||||
|
*/
|
||||||
|
fun saveFileToDownload(
|
||||||
|
context: Context, filename: String, content: ByteArray
|
||||||
|
): Uri {
|
||||||
|
val stream = ByteArrayInputStream(content)
|
||||||
|
return writeFileToDownload(context, filename, stream)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copy a file from @c fromFilePath to the user Download dir
|
||||||
|
*
|
||||||
|
* @param context
|
||||||
|
* @param toFilename Filename of the new file
|
||||||
|
* @param fromFilePath Path of the file to be copied
|
||||||
|
* @return Uri of the created file
|
||||||
|
*/
|
||||||
|
fun copyFileToDownload(
|
||||||
|
context: Context, toFilename: String, fromFilePath: String
|
||||||
|
): Uri {
|
||||||
|
val file = File(fromFilePath)
|
||||||
|
val stream = file.inputStream()
|
||||||
|
return writeFileToDownload(context, toFilename, stream)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun writeFileToDownload(
|
||||||
|
context: Context, filename: String, data: InputStream
|
||||||
|
): Uri {
|
||||||
|
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||||
|
writeFileToDownload29(context, filename, data)
|
||||||
|
} else {
|
||||||
|
writeFileToDownload0(context, filename, data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequiresApi(Build.VERSION_CODES.Q)
|
||||||
|
private fun writeFileToDownload29(
|
||||||
|
context: Context, filename: String, data: InputStream
|
||||||
|
): Uri {
|
||||||
|
// Add a media item that other apps shouldn't see until the item is
|
||||||
|
// fully written to the media store.
|
||||||
|
val resolver = context.applicationContext.contentResolver
|
||||||
|
|
||||||
|
// Find all audio files on the primary external storage device.
|
||||||
|
val collection = MediaStore.Downloads.getContentUri(
|
||||||
|
MediaStore.VOLUME_EXTERNAL_PRIMARY
|
||||||
|
)
|
||||||
|
val file = File(filename)
|
||||||
|
val details = ContentValues().apply {
|
||||||
|
put(MediaStore.Downloads.DISPLAY_NAME, file.name)
|
||||||
|
if (file.parent != null) {
|
||||||
|
put(
|
||||||
|
MediaStore.Downloads.RELATIVE_PATH,
|
||||||
|
"${Environment.DIRECTORY_DOWNLOADS}/${file.parent}"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val contentUri = resolver.insert(collection, details)
|
||||||
|
|
||||||
|
resolver.openFileDescriptor(contentUri!!, "w", null).use { pfd ->
|
||||||
|
// Write data into the pending audio file.
|
||||||
|
BufferedOutputStream(
|
||||||
|
FileOutputStream(pfd!!.fileDescriptor)
|
||||||
|
).use { stream ->
|
||||||
|
data.copyTo(stream)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return contentUri
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun writeFileToDownload0(
|
||||||
|
context: Context, filename: String, data: InputStream
|
||||||
|
): Uri {
|
||||||
|
if (ContextCompat.checkSelfPermission(
|
||||||
|
context, Manifest.permission.WRITE_EXTERNAL_STORAGE
|
||||||
|
) != PackageManager.PERMISSION_GRANTED
|
||||||
|
) {
|
||||||
|
throw PermissionException("Permission not granted")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Suppress("Deprecation")
|
||||||
|
val path = Environment.getExternalStoragePublicDirectory(
|
||||||
|
Environment.DIRECTORY_DOWNLOADS
|
||||||
|
)
|
||||||
|
var file = File(path, filename)
|
||||||
|
var count = 1
|
||||||
|
while (file.exists()) {
|
||||||
|
val f = File(filename)
|
||||||
|
file = File(
|
||||||
|
path,
|
||||||
|
"${f.nameWithoutExtension} ($count).${f.extension}"
|
||||||
|
)
|
||||||
|
++count
|
||||||
|
}
|
||||||
|
file.parentFile?.mkdirs()
|
||||||
|
BufferedOutputStream(FileOutputStream(file)).use { stream ->
|
||||||
|
data.copyTo(stream)
|
||||||
|
}
|
||||||
|
|
||||||
|
val fileUri = Uri.fromFile(file)
|
||||||
|
triggerMediaScan(context, fileUri)
|
||||||
|
val contentUri = FileProvider.getUriForFile(
|
||||||
|
context, "${context.packageName}.fileprovider", file
|
||||||
|
)
|
||||||
|
return contentUri
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun triggerMediaScan(context: Context, uri: Uri) {
|
||||||
|
val scanIntent = Intent().apply {
|
||||||
|
@Suppress("Deprecation")
|
||||||
|
action = Intent.ACTION_MEDIA_SCANNER_SCAN_FILE
|
||||||
|
data = uri
|
||||||
|
}
|
||||||
|
context.sendBroadcast(scanIntent)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue