mirror of
https://gitlab.com/nkming2/nc-photos.git
synced 2025-03-13 18:58:53 +01:00
Overhaul and migrate MediaStore
This commit is contained in:
parent
8095a15972
commit
4beb93c552
18 changed files with 354 additions and 224 deletions
|
@ -9,12 +9,6 @@ import io.flutter.plugin.common.MethodChannel
|
|||
class MainActivity : FlutterActivity() {
|
||||
override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) {
|
||||
super.configureFlutterEngine(flutterEngine)
|
||||
MethodChannel(
|
||||
flutterEngine.dartExecutor.binaryMessenger,
|
||||
MediaStoreChannelHandler.CHANNEL
|
||||
).setMethodCallHandler(
|
||||
MediaStoreChannelHandler(this)
|
||||
)
|
||||
MethodChannel(
|
||||
flutterEngine.dartExecutor.binaryMessenger,
|
||||
SelfSignedCertChannelHandler.CHANNEL
|
||||
|
|
|
@ -1,83 +0,0 @@
|
|||
package com.nkming.nc_photos
|
||||
|
||||
import android.app.Activity
|
||||
import com.nkming.nc_photos.plugin.MediaStoreUtil
|
||||
import com.nkming.nc_photos.plugin.PermissionException
|
||||
import io.flutter.plugin.common.MethodCall
|
||||
import io.flutter.plugin.common.MethodChannel
|
||||
|
||||
/*
|
||||
* Save downloaded item on device
|
||||
*
|
||||
* Methods:
|
||||
* Write binary content to a file in the Download directory. Return the Uri to
|
||||
* the file
|
||||
* fun saveFileToDownload(fileName: String, content: ByteArray): String
|
||||
*/
|
||||
class MediaStoreChannelHandler(activity: Activity) :
|
||||
MethodChannel.MethodCallHandler {
|
||||
companion object {
|
||||
@JvmStatic
|
||||
val CHANNEL = "com.nkming.nc_photos/media_store"
|
||||
}
|
||||
|
||||
override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {
|
||||
when (call.method) {
|
||||
"saveFileToDownload" -> {
|
||||
try {
|
||||
saveFileToDownload(
|
||||
call.argument("fileName")!!,
|
||||
call.argument("content")!!,
|
||||
result
|
||||
)
|
||||
} catch (e: Throwable) {
|
||||
result.error("systemException", e.message, null)
|
||||
}
|
||||
}
|
||||
|
||||
"copyFileToDownload" -> {
|
||||
try {
|
||||
copyFileToDownload(
|
||||
call.argument("toFileName")!!,
|
||||
call.argument("fromFilePath")!!,
|
||||
result
|
||||
)
|
||||
} catch (e: Throwable) {
|
||||
result.error("systemException", e.message, null)
|
||||
}
|
||||
}
|
||||
|
||||
else -> result.notImplemented()
|
||||
}
|
||||
}
|
||||
|
||||
private fun saveFileToDownload(
|
||||
fileName: String, content: ByteArray, result: MethodChannel.Result
|
||||
) {
|
||||
try {
|
||||
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(
|
||||
toFileName: String, fromFilePath: String, result: MethodChannel.Result
|
||||
) {
|
||||
try {
|
||||
val uri = MediaStoreUtil.copyFileToDownload(
|
||||
_context, toFileName, fromFilePath
|
||||
)
|
||||
result.success(uri.toString())
|
||||
} catch (e: PermissionException) {
|
||||
PermissionHandler.ensureWriteExternalStorage(_activity)
|
||||
result.error("permissionError", "Permission not granted", null)
|
||||
}
|
||||
}
|
||||
|
||||
private val _activity = activity
|
||||
private val _context get() = _activity
|
||||
}
|
|
@ -1,29 +0,0 @@
|
|||
package com.nkming.nc_photos
|
||||
|
||||
import android.Manifest
|
||||
import android.app.Activity
|
||||
import android.content.pm.PackageManager
|
||||
import androidx.core.app.ActivityCompat
|
||||
import androidx.core.content.ContextCompat
|
||||
|
||||
private const val PERMISSION_REQUEST_CODE = 11011
|
||||
|
||||
class PermissionHandler {
|
||||
companion object {
|
||||
fun ensureWriteExternalStorage(activity: Activity): Boolean {
|
||||
return if (ContextCompat.checkSelfPermission(
|
||||
activity, Manifest.permission.WRITE_EXTERNAL_STORAGE
|
||||
) != PackageManager.PERMISSION_GRANTED
|
||||
) {
|
||||
ActivityCompat.requestPermissions(
|
||||
activity,
|
||||
arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE),
|
||||
PERMISSION_REQUEST_CODE
|
||||
)
|
||||
false
|
||||
} else {
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -16,6 +16,7 @@ import 'package:nc_photos/mobile/platform.dart'
|
|||
import 'package:nc_photos/platform/k.dart' as platform_k;
|
||||
import 'package:nc_photos/snack_bar_manager.dart';
|
||||
import 'package:nc_photos/use_case/download_file.dart';
|
||||
import 'package:nc_photos_plugin/nc_photos_plugin.dart';
|
||||
import 'package:tuple/tuple.dart';
|
||||
|
||||
class DownloadHandler {
|
||||
|
|
|
@ -34,22 +34,6 @@ class ApiException implements Exception {
|
|||
final dynamic message;
|
||||
}
|
||||
|
||||
/// Platform permission is not granted by user
|
||||
class PermissionException implements Exception {
|
||||
PermissionException([this.message]);
|
||||
|
||||
@override
|
||||
toString() {
|
||||
if (message == null) {
|
||||
return "PermissionException";
|
||||
} else {
|
||||
return "PermissionException: $message";
|
||||
}
|
||||
}
|
||||
|
||||
final dynamic message;
|
||||
}
|
||||
|
||||
/// The Nextcloud base URL address is invalid
|
||||
class InvalidBaseUrlException implements Exception {
|
||||
InvalidBaseUrlException([this.message]);
|
||||
|
|
|
@ -1,44 +0,0 @@
|
|||
import 'dart:typed_data';
|
||||
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:nc_photos/exception.dart';
|
||||
|
||||
class MediaStore {
|
||||
static Future<String> saveFileToDownload(
|
||||
String fileName, Uint8List fileContent) async {
|
||||
try {
|
||||
return (await _channel
|
||||
.invokeMethod<String>("saveFileToDownload", <String, dynamic>{
|
||||
"fileName": fileName,
|
||||
"content": fileContent,
|
||||
}))!;
|
||||
} on PlatformException catch (e) {
|
||||
if (e.code == _exceptionCodePermissionError) {
|
||||
throw PermissionException();
|
||||
} else {
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static Future<String> copyFileToDownload(
|
||||
String toFileName, String fromFilePath) async {
|
||||
try {
|
||||
return (await _channel
|
||||
.invokeMethod<String>("copyFileToDownload", <String, dynamic>{
|
||||
"toFileName": toFileName,
|
||||
"fromFilePath": fromFilePath,
|
||||
}))!;
|
||||
} on PlatformException catch (e) {
|
||||
if (e.code == _exceptionCodePermissionError) {
|
||||
throw PermissionException();
|
||||
} else {
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static const _exceptionCodePermissionError = "permissionError";
|
||||
|
||||
static const _channel = MethodChannel("com.nkming.nc_photos/media_store");
|
||||
}
|
|
@ -4,9 +4,9 @@ import 'dart:io';
|
|||
import 'package:http/http.dart' as http;
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:nc_photos/exception.dart';
|
||||
import 'package:nc_photos/mobile/android/media_store.dart';
|
||||
import 'package:nc_photos/platform/download.dart' as itf;
|
||||
import 'package:nc_photos/platform/k.dart' as platform_k;
|
||||
import 'package:nc_photos_plugin/nc_photos_plugin.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:uuid/uuid.dart';
|
||||
|
||||
|
@ -92,13 +92,11 @@ class _AndroidDownload extends itf.Download {
|
|||
}
|
||||
|
||||
// copy the file to the actual dir
|
||||
final String path;
|
||||
if (parentDir?.isNotEmpty == true) {
|
||||
path = "$parentDir/$filename";
|
||||
} else {
|
||||
path = filename;
|
||||
}
|
||||
return await MediaStore.copyFileToDownload(path, file.path);
|
||||
return await MediaStore.copyFileToDownload(
|
||||
file.path,
|
||||
filename: filename,
|
||||
subDir: parentDir,
|
||||
);
|
||||
} finally {
|
||||
file.delete();
|
||||
}
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import 'dart:typed_data';
|
||||
|
||||
import 'package:nc_photos/mobile/android/media_store.dart';
|
||||
import 'package:nc_photos/platform/file_saver.dart' as itf;
|
||||
import 'package:nc_photos/platform/k.dart' as platform_k;
|
||||
import 'package:nc_photos_plugin/nc_photos_plugin.dart';
|
||||
|
||||
class FileSaver extends itf.FileSaver {
|
||||
@override
|
||||
|
@ -15,5 +15,5 @@ class FileSaver extends itf.FileSaver {
|
|||
}
|
||||
|
||||
Future<String> _saveFileAndroid(String filename, Uint8List content) =>
|
||||
MediaStore.saveFileToDownload(filename, content);
|
||||
MediaStore.saveFileToDownload(content, filename);
|
||||
}
|
||||
|
|
|
@ -11,7 +11,6 @@ import 'package:nc_photos/entity/file.dart';
|
|||
import 'package:nc_photos/entity/file/data_source.dart';
|
||||
import 'package:nc_photos/entity/share.dart';
|
||||
import 'package:nc_photos/entity/share/data_source.dart';
|
||||
import 'package:nc_photos/exception.dart';
|
||||
import 'package:nc_photos/exception_util.dart' as exception_util;
|
||||
import 'package:nc_photos/iterable_extension.dart';
|
||||
import 'package:nc_photos/k.dart' as k;
|
||||
|
@ -27,6 +26,7 @@ import 'package:nc_photos/widget/processing_dialog.dart';
|
|||
import 'package:nc_photos/widget/share_link_multiple_files_dialog.dart';
|
||||
import 'package:nc_photos/widget/share_method_dialog.dart';
|
||||
import 'package:nc_photos/widget/simple_input_dialog.dart';
|
||||
import 'package:nc_photos_plugin/nc_photos_plugin.dart';
|
||||
import 'package:tuple/tuple.dart';
|
||||
|
||||
/// Handle sharing to other apps
|
||||
|
|
|
@ -5,6 +5,7 @@ interface K {
|
|||
const val DOWNLOAD_NOTIFICATION_ID_MIN = 1000
|
||||
const val DOWNLOAD_NOTIFICATION_ID_MAX = 2000
|
||||
|
||||
const val PERMISSION_REQUEST_CODE = 11011
|
||||
const val LIB_ID = "com.nkming.nc_photos.plugin"
|
||||
|
||||
const val ACTION_DOWNLOAD_CANCEL = "${LIB_ID}.ACTION_DOWNLOAD_CANCEL"
|
||||
|
|
|
@ -0,0 +1,118 @@
|
|||
package com.nkming.nc_photos.plugin
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.net.Uri
|
||||
import io.flutter.embedding.engine.plugins.activity.ActivityAware
|
||||
import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding
|
||||
import io.flutter.plugin.common.MethodCall
|
||||
import io.flutter.plugin.common.MethodChannel
|
||||
import java.io.File
|
||||
|
||||
/*
|
||||
* Save downloaded item on device
|
||||
*
|
||||
* Methods:
|
||||
* Write binary content to a file in the Download directory. Return the Uri to
|
||||
* the file
|
||||
* fun saveFileToDownload(content: ByteArray, filename: String, subDir: String?): String
|
||||
*/
|
||||
class MediaStoreChannelHandler(context: Context) :
|
||||
MethodChannel.MethodCallHandler, ActivityAware {
|
||||
companion object {
|
||||
const val METHOD_CHANNEL = "${K.LIB_ID}/media_store_method"
|
||||
|
||||
private const val TAG = "MediaStoreChannelHandler"
|
||||
}
|
||||
|
||||
override fun onAttachedToActivity(binding: ActivityPluginBinding) {
|
||||
activity = binding.activity
|
||||
}
|
||||
|
||||
override fun onReattachedToActivityForConfigChanges(
|
||||
binding: ActivityPluginBinding
|
||||
) {
|
||||
activity = binding.activity
|
||||
}
|
||||
|
||||
override fun onDetachedFromActivity() {
|
||||
activity = null
|
||||
}
|
||||
|
||||
override fun onDetachedFromActivityForConfigChanges() {
|
||||
activity = null
|
||||
}
|
||||
|
||||
override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {
|
||||
when (call.method) {
|
||||
"saveFileToDownload" -> {
|
||||
try {
|
||||
saveFileToDownload(
|
||||
call.argument("content")!!, call.argument("filename")!!,
|
||||
call.argument("subDir"), result
|
||||
)
|
||||
} catch (e: Throwable) {
|
||||
result.error("systemException", e.message, null)
|
||||
}
|
||||
}
|
||||
|
||||
"copyFileToDownload" -> {
|
||||
try {
|
||||
copyFileToDownload(
|
||||
call.argument("fromFile")!!, call.argument("filename"),
|
||||
call.argument("subDir"), result
|
||||
)
|
||||
} catch (e: Throwable) {
|
||||
result.error("systemException", e.message, null)
|
||||
}
|
||||
}
|
||||
|
||||
else -> result.notImplemented()
|
||||
}
|
||||
}
|
||||
|
||||
private fun saveFileToDownload(
|
||||
content: ByteArray, filename: String, subDir: String?,
|
||||
result: MethodChannel.Result
|
||||
) {
|
||||
try {
|
||||
val uri = MediaStoreUtil.saveFileToDownload(
|
||||
context, content, filename, subDir
|
||||
)
|
||||
result.success(uri.toString())
|
||||
} catch (e: PermissionException) {
|
||||
activity?.let { PermissionUtil.requestWriteExternalStorage(it) }
|
||||
result.error("permissionError", "Permission not granted", null)
|
||||
}
|
||||
}
|
||||
|
||||
private fun copyFileToDownload(
|
||||
fromFile: String, filename: String?, subDir: String?,
|
||||
result: MethodChannel.Result
|
||||
) {
|
||||
try {
|
||||
val fromUri = inputToUri(fromFile)
|
||||
val uri = MediaStoreUtil.copyFileToDownload(
|
||||
context, fromUri, filename, subDir
|
||||
)
|
||||
result.success(uri.toString())
|
||||
} catch (e: PermissionException) {
|
||||
activity?.let { PermissionUtil.requestWriteExternalStorage(it) }
|
||||
result.error("permissionError", "Permission not granted", null)
|
||||
}
|
||||
}
|
||||
|
||||
private fun inputToUri(fromFile: String): Uri {
|
||||
val testUri = Uri.parse(fromFile)
|
||||
return if (testUri.scheme == null) {
|
||||
// is a file path
|
||||
Uri.fromFile(File(fromFile))
|
||||
} else {
|
||||
// is a uri
|
||||
Uri.parse(fromFile)
|
||||
}
|
||||
}
|
||||
|
||||
private val context = context
|
||||
private var activity: Activity? = null
|
||||
}
|
|
@ -1,65 +1,84 @@
|
|||
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.*
|
||||
|
||||
class MediaStoreCopyWriter(data: InputStream) {
|
||||
operator fun invoke(ostream: OutputStream) {
|
||||
data.copyTo(ostream)
|
||||
}
|
||||
|
||||
private val data = data
|
||||
}
|
||||
|
||||
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
|
||||
* @param filename Filename of the new file
|
||||
* @param subDir
|
||||
* @return Uri of the created file
|
||||
*/
|
||||
fun saveFileToDownload(
|
||||
context: Context, filename: String, content: ByteArray
|
||||
context: Context, content: ByteArray, filename: String,
|
||||
subDir: String? = null
|
||||
): Uri {
|
||||
val stream = ByteArrayInputStream(content)
|
||||
return writeFileToDownload(context, filename, stream)
|
||||
return ByteArrayInputStream(content).use {
|
||||
writeFileToDownload(
|
||||
context, MediaStoreCopyWriter(it)::invoke, filename, subDir
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @param fromFile Path of the file to be copied
|
||||
* @param filename Filename of the new file. If null, the same filename
|
||||
* @param subDir
|
||||
* will be used
|
||||
* @return Uri of the created file
|
||||
*/
|
||||
fun copyFileToDownload(
|
||||
context: Context, toFilename: String, fromFilePath: String
|
||||
context: Context, fromFile: Uri, filename: String? = null,
|
||||
subDir: String? = null
|
||||
): Uri {
|
||||
val file = File(fromFilePath)
|
||||
val stream = file.inputStream()
|
||||
return writeFileToDownload(context, toFilename, stream)
|
||||
return context.contentResolver.openInputStream(fromFile)!!.use {
|
||||
writeFileToDownload(
|
||||
context, MediaStoreCopyWriter(it)::invoke,
|
||||
filename ?: UriUtil.resolveFilename(context, fromFile)!!,
|
||||
subDir
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun writeFileToDownload(
|
||||
context: Context, filename: String, data: InputStream
|
||||
fun writeFileToDownload(
|
||||
context: Context, writer: (OutputStream) -> Unit, filename: String,
|
||||
subDir: String? = null
|
||||
): Uri {
|
||||
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||
writeFileToDownload29(context, filename, data)
|
||||
writeFileToDownload29(context, writer, filename, subDir)
|
||||
} else {
|
||||
writeFileToDownload0(context, filename, data)
|
||||
writeFileToDownload0(context, writer, filename, subDir)
|
||||
}
|
||||
}
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.Q)
|
||||
private fun writeFileToDownload29(
|
||||
context: Context, filename: String, data: InputStream
|
||||
context: Context, writer: (OutputStream) -> Unit, filename: String,
|
||||
subDir: String?
|
||||
): Uri {
|
||||
// Add a media item that other apps shouldn't see until the item is
|
||||
// fully written to the media store.
|
||||
|
@ -69,13 +88,12 @@ interface MediaStoreUtil {
|
|||
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.DISPLAY_NAME, filename)
|
||||
if (subDir != null) {
|
||||
put(
|
||||
MediaStore.Downloads.RELATIVE_PATH,
|
||||
"${Environment.DIRECTORY_DOWNLOADS}/${file.parent}"
|
||||
"${Environment.DIRECTORY_DOWNLOADS}/$subDir"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -87,19 +105,17 @@ interface MediaStoreUtil {
|
|||
BufferedOutputStream(
|
||||
FileOutputStream(pfd!!.fileDescriptor)
|
||||
).use { stream ->
|
||||
data.copyTo(stream)
|
||||
writer(stream)
|
||||
}
|
||||
}
|
||||
return contentUri
|
||||
}
|
||||
|
||||
private fun writeFileToDownload0(
|
||||
context: Context, filename: String, data: InputStream
|
||||
context: Context, writer: (OutputStream) -> Unit, filename: String,
|
||||
subDir: String?
|
||||
): Uri {
|
||||
if (ContextCompat.checkSelfPermission(
|
||||
context, Manifest.permission.WRITE_EXTERNAL_STORAGE
|
||||
) != PackageManager.PERMISSION_GRANTED
|
||||
) {
|
||||
if (!PermissionUtil.hasWriteExternalStorage(context)) {
|
||||
throw PermissionException("Permission not granted")
|
||||
}
|
||||
|
||||
|
@ -107,19 +123,19 @@ interface MediaStoreUtil {
|
|||
val path = Environment.getExternalStoragePublicDirectory(
|
||||
Environment.DIRECTORY_DOWNLOADS
|
||||
)
|
||||
var file = File(path, filename)
|
||||
val prefix = if (subDir != null) "$subDir/" else ""
|
||||
var file = File(path, prefix + filename)
|
||||
val baseFilename = file.nameWithoutExtension
|
||||
var count = 1
|
||||
while (file.exists()) {
|
||||
val f = File(filename)
|
||||
file = File(
|
||||
path,
|
||||
"${f.nameWithoutExtension} ($count).${f.extension}"
|
||||
path, prefix + "$baseFilename ($count).${file.extension}"
|
||||
)
|
||||
++count
|
||||
}
|
||||
file.parentFile?.mkdirs()
|
||||
BufferedOutputStream(FileOutputStream(file)).use { stream ->
|
||||
data.copyTo(stream)
|
||||
writer(stream)
|
||||
}
|
||||
|
||||
val fileUri = Uri.fromFile(file)
|
||||
|
@ -138,6 +154,5 @@ interface MediaStoreUtil {
|
|||
}
|
||||
context.sendBroadcast(scanIntent)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,10 +2,12 @@ package com.nkming.nc_photos.plugin
|
|||
|
||||
import androidx.annotation.NonNull
|
||||
import io.flutter.embedding.engine.plugins.FlutterPlugin
|
||||
import io.flutter.embedding.engine.plugins.activity.ActivityAware
|
||||
import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding
|
||||
import io.flutter.plugin.common.EventChannel
|
||||
import io.flutter.plugin.common.MethodChannel
|
||||
|
||||
class NcPhotosPlugin : FlutterPlugin {
|
||||
class NcPhotosPlugin : FlutterPlugin, ActivityAware {
|
||||
override fun onAttachedToEngine(
|
||||
@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding
|
||||
) {
|
||||
|
@ -36,6 +38,14 @@ class NcPhotosPlugin : FlutterPlugin {
|
|||
NativeEventChannelHandler.METHOD_CHANNEL
|
||||
)
|
||||
nativeEventMethodChannel.setMethodCallHandler(nativeEventHandler)
|
||||
|
||||
mediaStoreChannelHandler =
|
||||
MediaStoreChannelHandler(flutterPluginBinding.applicationContext)
|
||||
mediaStoreMethodChannel = MethodChannel(
|
||||
flutterPluginBinding.binaryMessenger,
|
||||
MediaStoreChannelHandler.METHOD_CHANNEL
|
||||
)
|
||||
mediaStoreMethodChannel.setMethodCallHandler(mediaStoreChannelHandler)
|
||||
}
|
||||
|
||||
override fun onDetachedFromEngine(
|
||||
|
@ -46,12 +56,33 @@ class NcPhotosPlugin : FlutterPlugin {
|
|||
notificationChannel.setMethodCallHandler(null)
|
||||
nativeEventChannel.setStreamHandler(null)
|
||||
nativeEventMethodChannel.setMethodCallHandler(null)
|
||||
mediaStoreMethodChannel.setMethodCallHandler(null)
|
||||
}
|
||||
|
||||
override fun onAttachedToActivity(binding: ActivityPluginBinding) {
|
||||
mediaStoreChannelHandler.onAttachedToActivity(binding)
|
||||
}
|
||||
|
||||
override fun onReattachedToActivityForConfigChanges(
|
||||
binding: ActivityPluginBinding
|
||||
) {
|
||||
mediaStoreChannelHandler.onReattachedToActivityForConfigChanges(binding)
|
||||
}
|
||||
|
||||
override fun onDetachedFromActivity() {
|
||||
mediaStoreChannelHandler.onDetachedFromActivity()
|
||||
}
|
||||
|
||||
override fun onDetachedFromActivityForConfigChanges() {
|
||||
mediaStoreChannelHandler.onDetachedFromActivityForConfigChanges()
|
||||
}
|
||||
|
||||
private lateinit var lockChannel: MethodChannel
|
||||
private lateinit var notificationChannel: MethodChannel
|
||||
private lateinit var nativeEventChannel: EventChannel
|
||||
private lateinit var nativeEventMethodChannel: MethodChannel
|
||||
private lateinit var mediaStoreMethodChannel: MethodChannel
|
||||
|
||||
private lateinit var lockChannelHandler: LockChannelHandler
|
||||
private lateinit var mediaStoreChannelHandler: MediaStoreChannelHandler
|
||||
}
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
package com.nkming.nc_photos.plugin
|
||||
|
||||
import android.Manifest
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.content.pm.PackageManager
|
||||
import androidx.core.app.ActivityCompat
|
||||
import androidx.core.content.ContextCompat
|
||||
|
||||
interface PermissionUtil {
|
||||
companion object {
|
||||
fun request(activity: Activity, vararg permissions: String) {
|
||||
ActivityCompat.requestPermissions(
|
||||
activity, permissions, K.PERMISSION_REQUEST_CODE
|
||||
)
|
||||
}
|
||||
|
||||
fun hasReadExternalStorage(context: Context): Boolean {
|
||||
return ContextCompat.checkSelfPermission(
|
||||
context, Manifest.permission.READ_EXTERNAL_STORAGE
|
||||
) == PackageManager.PERMISSION_GRANTED
|
||||
}
|
||||
|
||||
fun requestReadExternalStorage(activity: Activity) =
|
||||
request(activity, Manifest.permission.READ_EXTERNAL_STORAGE)
|
||||
|
||||
fun hasWriteExternalStorage(context: Context): Boolean {
|
||||
return ContextCompat.checkSelfPermission(
|
||||
context, Manifest.permission.WRITE_EXTERNAL_STORAGE
|
||||
) == PackageManager.PERMISSION_GRANTED
|
||||
}
|
||||
|
||||
fun requestWriteExternalStorage(activity: Activity) =
|
||||
request(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
package com.nkming.nc_photos.plugin
|
||||
|
||||
import android.content.Context
|
||||
import android.net.Uri
|
||||
import android.provider.MediaStore
|
||||
import android.util.Log
|
||||
|
||||
interface UriUtil {
|
||||
companion object {
|
||||
fun resolveFilename(context: Context, uri: Uri): String? {
|
||||
return if (uri.scheme == "file") {
|
||||
uri.lastPathSegment!!
|
||||
} else {
|
||||
context.contentResolver.query(
|
||||
uri, arrayOf(MediaStore.MediaColumns.DISPLAY_NAME), null,
|
||||
null, null
|
||||
).use {
|
||||
if (it == null || !it.moveToFirst()) {
|
||||
Log.i(TAG, "Uri not found: $uri")
|
||||
null
|
||||
} else {
|
||||
it.getString(
|
||||
it.getColumnIndexOrThrow(
|
||||
MediaStore.MediaColumns.DISPLAY_NAME
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private const val TAG = "UriUtil"
|
||||
}
|
||||
}
|
|
@ -1,5 +1,7 @@
|
|||
library nc_photos_plugin;
|
||||
|
||||
export 'src/exception.dart';
|
||||
export 'src/lock.dart';
|
||||
export 'src/media_store.dart';
|
||||
export 'src/native_event.dart';
|
||||
export 'src/notification.dart';
|
||||
|
|
15
plugin/lib/src/exception.dart
Normal file
15
plugin/lib/src/exception.dart
Normal file
|
@ -0,0 +1,15 @@
|
|||
/// Platform permission is not granted by user
|
||||
class PermissionException implements Exception {
|
||||
const PermissionException([this.message]);
|
||||
|
||||
@override
|
||||
toString() {
|
||||
if (message == null) {
|
||||
return "PermissionException";
|
||||
} else {
|
||||
return "PermissionException: $message";
|
||||
}
|
||||
}
|
||||
|
||||
final dynamic message;
|
||||
}
|
57
plugin/lib/src/media_store.dart
Normal file
57
plugin/lib/src/media_store.dart
Normal file
|
@ -0,0 +1,57 @@
|
|||
import 'dart:typed_data';
|
||||
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:nc_photos_plugin/src/exception.dart';
|
||||
import 'package:nc_photos_plugin/src/k.dart' as k;
|
||||
|
||||
class MediaStore {
|
||||
static Future<String> saveFileToDownload(
|
||||
Uint8List content,
|
||||
String filename, {
|
||||
String? subDir,
|
||||
}) async {
|
||||
try {
|
||||
return (await _methodChannel
|
||||
.invokeMethod<String>("saveFileToDownload", <String, dynamic>{
|
||||
"content": content,
|
||||
"filename": filename,
|
||||
"subDir": subDir,
|
||||
}))!;
|
||||
} on PlatformException catch (e) {
|
||||
if (e.code == _exceptionCodePermissionError) {
|
||||
throw const PermissionException();
|
||||
} else {
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Copy a file to the user Download dir
|
||||
///
|
||||
/// [fromFile] must be either a path or a content uri. If [filename] is not
|
||||
/// null, it will be used instead of the source filename
|
||||
static Future<String> copyFileToDownload(
|
||||
String fromFile, {
|
||||
String? filename,
|
||||
String? subDir,
|
||||
}) async {
|
||||
try {
|
||||
return (await _methodChannel
|
||||
.invokeMethod<String>("copyFileToDownload", <String, dynamic>{
|
||||
"fromFile": fromFile,
|
||||
"filename": filename,
|
||||
"subDir": subDir,
|
||||
}))!;
|
||||
} on PlatformException catch (e) {
|
||||
if (e.code == _exceptionCodePermissionError) {
|
||||
throw const PermissionException();
|
||||
} else {
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static const _methodChannel = MethodChannel("${k.libId}/media_store_method");
|
||||
|
||||
static const _exceptionCodePermissionError = "permissionError";
|
||||
}
|
Loading…
Reference in a new issue