mirror of
https://gitlab.com/nkming2/nc-photos.git
synced 2025-01-22 16:56:19 +01:00
Delete files via MediaStore api
This commit is contained in:
parent
4da8b95c61
commit
d33e3af806
7 changed files with 160 additions and 4 deletions
|
@ -5,7 +5,7 @@
|
|||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
|
||||
android:maxSdkVersion="28"
|
||||
android:maxSdkVersion="29"
|
||||
tools:ignore="ScopedStorage" />
|
||||
|
||||
<application
|
||||
|
@ -13,7 +13,8 @@
|
|||
android:icon="@mipmap/ic_launcher"
|
||||
android:networkSecurityConfig="@xml/network_security_config"
|
||||
android:allowBackup="false"
|
||||
android:largeHeap="true">
|
||||
android:largeHeap="true"
|
||||
android:requestLegacyExternalStorage="true">
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:launchMode="singleTop"
|
||||
|
|
|
@ -9,6 +9,8 @@ interface K {
|
|||
const val IMAGE_PROCESSOR_SERVICE_RESULT_FAILED_NOTIFICATION_ID = 5002
|
||||
|
||||
const val PERMISSION_REQUEST_CODE = 11011
|
||||
const val MEDIA_STORE_DELETE_REQUEST_CODE = 11012
|
||||
|
||||
const val LIB_ID = "com.nkming.nc_photos.plugin"
|
||||
|
||||
const val ACTION_DOWNLOAD_CANCEL = "${LIB_ID}.ACTION_DOWNLOAD_CANCEL"
|
||||
|
|
|
@ -3,13 +3,17 @@ package com.nkming.nc_photos.plugin
|
|||
import android.app.Activity
|
||||
import android.content.ContentUris
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.provider.MediaStore
|
||||
import android.util.Log
|
||||
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.MethodCall
|
||||
import io.flutter.plugin.common.MethodChannel
|
||||
import io.flutter.plugin.common.PluginRegistry
|
||||
import java.io.File
|
||||
|
||||
/*
|
||||
|
@ -24,8 +28,10 @@ import java.io.File
|
|||
* fun queryFiles(relativePath: String): List<Map>
|
||||
*/
|
||||
class MediaStoreChannelHandler(context: Context) :
|
||||
MethodChannel.MethodCallHandler, ActivityAware {
|
||||
MethodChannel.MethodCallHandler, EventChannel.StreamHandler,
|
||||
ActivityAware, PluginRegistry.ActivityResultListener {
|
||||
companion object {
|
||||
const val EVENT_CHANNEL = "${K.LIB_ID}/media_store"
|
||||
const val METHOD_CHANNEL = "${K.LIB_ID}/media_store_method"
|
||||
|
||||
private const val TAG = "MediaStoreChannelHandler"
|
||||
|
@ -49,6 +55,27 @@ class MediaStoreChannelHandler(context: Context) :
|
|||
activity = null
|
||||
}
|
||||
|
||||
override fun onActivityResult(
|
||||
requestCode: Int, resultCode: Int, data: Intent?
|
||||
): Boolean {
|
||||
if (requestCode == K.MEDIA_STORE_DELETE_REQUEST_CODE) {
|
||||
eventSink?.success(buildMap {
|
||||
put("event", "DeleteRequestResult")
|
||||
put("resultCode", resultCode)
|
||||
})
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
override fun onListen(arguments: Any?, events: EventChannel.EventSink) {
|
||||
eventSink = events
|
||||
}
|
||||
|
||||
override fun onCancel(arguments: Any?) {
|
||||
eventSink = null
|
||||
}
|
||||
|
||||
override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {
|
||||
when (call.method) {
|
||||
"saveFileToDownload" -> {
|
||||
|
@ -81,6 +108,14 @@ class MediaStoreChannelHandler(context: Context) :
|
|||
}
|
||||
}
|
||||
|
||||
"deleteFiles" -> {
|
||||
try {
|
||||
deleteFiles(call.argument("uris")!!, result)
|
||||
} catch (e: Throwable) {
|
||||
result.error("systemException", e.message, null)
|
||||
}
|
||||
}
|
||||
|
||||
else -> result.notImplemented()
|
||||
}
|
||||
}
|
||||
|
@ -196,6 +231,37 @@ class MediaStoreChannelHandler(context: Context) :
|
|||
result.success(files)
|
||||
}
|
||||
|
||||
private fun deleteFiles(uris: List<String>, result: MethodChannel.Result) {
|
||||
val urisTyped = uris.map(Uri::parse)
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||
val pi = MediaStore.createDeleteRequest(
|
||||
context.contentResolver, urisTyped
|
||||
)
|
||||
activity!!.startIntentSenderForResult(
|
||||
pi.intentSender, K.MEDIA_STORE_DELETE_REQUEST_CODE, null, 0, 0,
|
||||
0
|
||||
)
|
||||
result.success(null)
|
||||
} else {
|
||||
if (!PermissionUtil.hasWriteExternalStorage(context)) {
|
||||
activity?.let { PermissionUtil.requestWriteExternalStorage(it) }
|
||||
result.error("permissionError", "Permission not granted", null)
|
||||
return
|
||||
}
|
||||
|
||||
val failed = mutableListOf<String>()
|
||||
for (uri in urisTyped) {
|
||||
try {
|
||||
context.contentResolver.delete(uri, null, null)
|
||||
} catch (e: Throwable) {
|
||||
Log.e(TAG, "[deleteFiles] Failed while delete", e)
|
||||
failed += uri.toString()
|
||||
}
|
||||
}
|
||||
result.success(failed)
|
||||
}
|
||||
}
|
||||
|
||||
private fun inputToUri(fromFile: String): Uri {
|
||||
val testUri = Uri.parse(fromFile)
|
||||
return if (testUri.scheme == null) {
|
||||
|
@ -209,4 +275,5 @@ class MediaStoreChannelHandler(context: Context) :
|
|||
|
||||
private val context = context
|
||||
private var activity: Activity? = null
|
||||
private var eventSink: EventChannel.EventSink? = null
|
||||
}
|
||||
|
|
|
@ -1,17 +1,23 @@
|
|||
package com.nkming.nc_photos.plugin
|
||||
|
||||
import android.content.Intent
|
||||
import android.util.Log
|
||||
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
|
||||
import io.flutter.plugin.common.PluginRegistry
|
||||
|
||||
class NcPhotosPlugin : FlutterPlugin, ActivityAware {
|
||||
class NcPhotosPlugin : FlutterPlugin, ActivityAware,
|
||||
PluginRegistry.ActivityResultListener {
|
||||
companion object {
|
||||
const val ACTION_SHOW_IMAGE_PROCESSOR_RESULT =
|
||||
K.ACTION_SHOW_IMAGE_PROCESSOR_RESULT
|
||||
const val EXTRA_IMAGE_RESULT_URI = K.EXTRA_IMAGE_RESULT_URI
|
||||
|
||||
private const val TAG = "NcPhotosPlugin"
|
||||
}
|
||||
|
||||
override fun onAttachedToEngine(
|
||||
|
@ -47,6 +53,11 @@ class NcPhotosPlugin : FlutterPlugin, ActivityAware {
|
|||
|
||||
mediaStoreChannelHandler =
|
||||
MediaStoreChannelHandler(flutterPluginBinding.applicationContext)
|
||||
mediaStoreChannel = EventChannel(
|
||||
flutterPluginBinding.binaryMessenger,
|
||||
MediaStoreChannelHandler.EVENT_CHANNEL
|
||||
)
|
||||
mediaStoreChannel.setStreamHandler(mediaStoreChannelHandler)
|
||||
mediaStoreMethodChannel = MethodChannel(
|
||||
flutterPluginBinding.binaryMessenger,
|
||||
MediaStoreChannelHandler.METHOD_CHANNEL
|
||||
|
@ -87,26 +98,56 @@ class NcPhotosPlugin : FlutterPlugin, ActivityAware {
|
|||
|
||||
override fun onAttachedToActivity(binding: ActivityPluginBinding) {
|
||||
mediaStoreChannelHandler.onAttachedToActivity(binding)
|
||||
pluginBinding = binding
|
||||
binding.addActivityResultListener(this)
|
||||
}
|
||||
|
||||
override fun onReattachedToActivityForConfigChanges(
|
||||
binding: ActivityPluginBinding
|
||||
) {
|
||||
mediaStoreChannelHandler.onReattachedToActivityForConfigChanges(binding)
|
||||
pluginBinding = binding
|
||||
binding.addActivityResultListener(this)
|
||||
}
|
||||
|
||||
override fun onDetachedFromActivity() {
|
||||
mediaStoreChannelHandler.onDetachedFromActivity()
|
||||
pluginBinding?.removeActivityResultListener(this)
|
||||
}
|
||||
|
||||
override fun onDetachedFromActivityForConfigChanges() {
|
||||
mediaStoreChannelHandler.onDetachedFromActivityForConfigChanges()
|
||||
pluginBinding?.removeActivityResultListener(this)
|
||||
}
|
||||
|
||||
override fun onActivityResult(
|
||||
requestCode: Int, resultCode: Int, data: Intent?
|
||||
): Boolean {
|
||||
return try {
|
||||
when (requestCode) {
|
||||
K.MEDIA_STORE_DELETE_REQUEST_CODE -> {
|
||||
mediaStoreChannelHandler.onActivityResult(
|
||||
requestCode, resultCode, data
|
||||
)
|
||||
}
|
||||
|
||||
else -> false
|
||||
}
|
||||
} catch (e: Throwable) {
|
||||
Log.e(
|
||||
TAG, "Failed while onActivityResult, requestCode=$requestCode"
|
||||
)
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
private var pluginBinding: ActivityPluginBinding? = null
|
||||
|
||||
private lateinit var lockChannel: MethodChannel
|
||||
private lateinit var notificationChannel: MethodChannel
|
||||
private lateinit var nativeEventChannel: EventChannel
|
||||
private lateinit var nativeEventMethodChannel: MethodChannel
|
||||
private lateinit var mediaStoreChannel: EventChannel
|
||||
private lateinit var mediaStoreMethodChannel: MethodChannel
|
||||
private lateinit var imageProcessorMethodChannel: MethodChannel
|
||||
private lateinit var contentUriMethodChannel: MethodChannel
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import 'dart:typed_data';
|
||||
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:nc_photos_plugin/src/exception.dart';
|
||||
import 'package:nc_photos_plugin/src/k.dart' as k;
|
||||
|
||||
|
@ -16,6 +17,12 @@ class MediaStoreQueryResult {
|
|||
final int? dateTaken;
|
||||
}
|
||||
|
||||
class MediaStoreDeleteRequestResultEvent {
|
||||
const MediaStoreDeleteRequestResultEvent(this.resultCode);
|
||||
|
||||
final int resultCode;
|
||||
}
|
||||
|
||||
class MediaStore {
|
||||
static Future<String> saveFileToDownload(
|
||||
Uint8List content,
|
||||
|
@ -85,7 +92,36 @@ class MediaStore {
|
|||
}
|
||||
}
|
||||
|
||||
static Future<List<String>?> deleteFiles(List<String> uris) async {
|
||||
return (await _methodChannel
|
||||
.invokeMethod<List>("deleteFiles", <String, dynamic>{
|
||||
"uris": uris,
|
||||
}))
|
||||
?.cast<String>();
|
||||
}
|
||||
|
||||
static Stream get stream => _eventStream;
|
||||
|
||||
static late final _eventStream =
|
||||
_eventChannel.receiveBroadcastStream().map((event) {
|
||||
if (event is Map) {
|
||||
switch (event["event"]) {
|
||||
case _eventDeleteRequestResult:
|
||||
return MediaStoreDeleteRequestResultEvent(event["resultCode"]);
|
||||
|
||||
default:
|
||||
_log.shout("[_eventStream] Unknown event: ${event["event"]}");
|
||||
}
|
||||
} else {
|
||||
return event;
|
||||
}
|
||||
});
|
||||
|
||||
static const _eventChannel = EventChannel("${k.libId}/media_store");
|
||||
static const _methodChannel = MethodChannel("${k.libId}/media_store_method");
|
||||
|
||||
static const _exceptionCodePermissionError = "permissionError";
|
||||
static const _eventDeleteRequestResult = "DeleteRequestResult";
|
||||
|
||||
static final _log = Logger("media_store.MediaStore");
|
||||
}
|
||||
|
|
|
@ -34,6 +34,13 @@ packages:
|
|||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.0.1"
|
||||
logging:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: logging
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.0.2"
|
||||
material_color_utilities:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
|
@ -11,6 +11,8 @@ dependencies:
|
|||
flutter:
|
||||
sdk: flutter
|
||||
|
||||
logging: ^1.0.2
|
||||
|
||||
dev_dependencies:
|
||||
flutter_lints: ^1.0.0
|
||||
|
||||
|
|
Loading…
Reference in a new issue