Skip to content

Commit

Permalink
fix: deleteWithIds method not working (#1038)
Browse files Browse the repository at this point in the history
Signed-off-by: CaiJingLong <cjl_spy@163.com>
  • Loading branch information
CaiJingLong authored Nov 17, 2023
1 parent 5f7fe25 commit ba15446
Show file tree
Hide file tree
Showing 6 changed files with 142 additions and 9 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@ that can be found in the LICENSE file. -->

# CHANGELOG

## 3.0.0-dev.2

Fix:

- Fix `PhotoManager.editor.deleteWithIds` method not working on Android API 29.

## 3.0.0-dev.1

***Breaking changes*** for remove some methods and classes.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.fluttercandies.photo_manager.core

import android.app.Activity
import android.app.RecoverableSecurityException
import android.content.ContentResolver
import android.content.Context
import android.content.Intent
Expand All @@ -9,8 +10,10 @@ import android.os.Build
import android.provider.MediaStore
import androidx.annotation.RequiresApi
import com.fluttercandies.photo_manager.core.utils.IDBUtils
import com.fluttercandies.photo_manager.util.LogUtils
import com.fluttercandies.photo_manager.util.ResultHandler
import io.flutter.plugin.common.PluginRegistry
import java.util.LinkedList

class PhotoManagerDeleteManager(val context: Context, private var activity: Activity?) :
PluginRegistry.ActivityResultListener {
Expand All @@ -19,17 +22,59 @@ class PhotoManagerDeleteManager(val context: Context, private var activity: Acti
this.activity = activity
}

private var androidQDeleteRequestCode = 40070
private val androidQUriMap = mutableMapOf<String, Uri?>()
private val androidQSuccessIds = mutableListOf<String>()

@RequiresApi(Build.VERSION_CODES.Q)
inner class AndroidQDeleteTask(
val id: String,
val uri: Uri,
private val exception: RecoverableSecurityException
) {
fun requestPermission() {
val intent = Intent().apply {
data = uri
}
activity?.startIntentSenderForResult(
exception.userAction.actionIntent.intentSender,
androidQDeleteRequestCode,
intent,
0,
0,
0
)
}

fun handleResult(resultCode: Int) {
if (resultCode == Activity.RESULT_OK) {
androidQSuccessIds.add(id)
}
requestAndroidQNextPermission()
}

}

private var waitPermissionQueue = LinkedList<AndroidQDeleteTask>()
private var currentTask: AndroidQDeleteTask? = null

private var androidRDeleteRequestCode = 40069

private val cr: ContentResolver
get() = context.contentResolver

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?): Boolean {
override fun onActivityResult(requestCode: Int, resultCode: Int, intent: Intent?): Boolean {
if (requestCode == androidRDeleteRequestCode) {
handleAndroidRDelete(resultCode)
return true
}
return true
if (requestCode == androidQDeleteRequestCode) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
currentTask?.handleResult(resultCode)
}
return true
}
return false
}

private fun handleAndroidRDelete(resultCode: Int) {
Expand Down Expand Up @@ -58,6 +103,7 @@ class PhotoManagerDeleteManager(val context: Context, private var activity: Acti
// }

private var androidRHandler: ResultHandler? = null
private var androidQHandler: ResultHandler? = null

@RequiresApi(Build.VERSION_CODES.R)
fun deleteInApi30(uris: List<Uri?>, resultHandler: ResultHandler) {
Expand All @@ -73,6 +119,73 @@ class PhotoManagerDeleteManager(val context: Context, private var activity: Acti
)
}

private fun findIdByUriInApi29(uri: Uri): String? {
for (entry in androidQUriMap) {
if (entry.value == uri) {
return entry.key
}
}
return null
}

@RequiresApi(Build.VERSION_CODES.Q)
private fun requestAndroidQNextPermission() {
val task = waitPermissionQueue.poll()

if (task == null) {
// all permission is granted or denied
replyAndroidQDeleteResult()
return
}

currentTask = task
task.requestPermission()
}

@RequiresApi(Build.VERSION_CODES.Q)
fun deleteJustInApi29(uris: HashMap<String, Uri?>, resultHandler: ResultHandler) {
this.androidQHandler = resultHandler

androidQUriMap.clear()
androidQUriMap.putAll(uris)
androidQSuccessIds.clear()
waitPermissionQueue.clear()

for (entry in uris) {
val uri = entry.value ?: continue
val id = entry.key
try {
cr.delete(uri, null, null)
} catch (e: Exception) {
// request delete permission
if (e is RecoverableSecurityException) {
val task = AndroidQDeleteTask(id, uri, e)
waitPermissionQueue.add(task)
} else {
LogUtils.error("delete assets error in api 29", e)
replyAndroidQDeleteResult()
return
}
}
}

requestAndroidQNextPermission()
}

private fun replyAndroidQDeleteResult() {
if (androidQSuccessIds.isNotEmpty()) {
// execute real delete
for (id in androidQSuccessIds) {
val uri = androidQUriMap[id] ?: continue
cr.delete(uri, null, null)
}
}

androidQHandler?.reply(androidQSuccessIds.toList())
androidQSuccessIds.clear()
androidQHandler = null
}

@RequiresApi(Build.VERSION_CODES.R)
fun moveToTrashInApi30(uris: List<Uri?>, resultHandler: ResultHandler) {
this.androidRHandler = resultHandler
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package com.fluttercandies.photo_manager.core

import android.app.Activity
import android.content.Context
import android.net.Uri
import android.os.Build
import android.os.Handler
import android.os.Looper
Expand Down Expand Up @@ -537,6 +538,13 @@ class PhotoManagerPlugin(
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
val uris = ids.map { photoManager.getUri(it) }.toList()
deleteManager.deleteInApi30(uris, resultHandler)
} else if (Build.VERSION.SDK_INT == Build.VERSION_CODES.Q) {
val idUriMap = HashMap<String, Uri?>()
for (id in ids) {
val uri = photoManager.getUri(id)
idUriMap[id] = uri
}
deleteManager.deleteJustInApi29(idUriMap, resultHandler)
} else {
deleteManager.deleteInApi28(ids)
resultHandler.reply(ids)
Expand Down
6 changes: 5 additions & 1 deletion example/lib/page/developer/issues_page/issue_988.dart
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,11 @@ class __DeleteAssetImageListState extends State<_DeleteAssetImageList> {
.deleteWithIds(checked.map((e) => e.id).toList());

showToast('Delete success, ids: $ids');


if (ids.isNotEmpty) {
checked.removeWhere((element) => ids.contains(element.id));
}

// refresh the list
loadAssets();
},
Expand Down
12 changes: 7 additions & 5 deletions example/lib/widget/nav_column.dart
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,13 @@ class NavColumn extends StatelessWidget {
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(8.0),
child: Center(
child: Column(
children: <Widget>[
for (final Widget item in children) buildItem(context, item),
],
child: SingleChildScrollView(
child: Center(
child: Column(
children: <Widget>[
for (final Widget item in children) buildItem(context, item),
],
),
),
),
);
Expand Down
2 changes: 1 addition & 1 deletion pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name: photo_manager
description: A Flutter plugin that provides assets abstraction management APIs on Android, iOS, and macOS.
repository: https://github.com/fluttercandies/flutter_photo_manager
version: 3.0.0-dev.1
version: 3.0.0-dev.2

environment:
sdk: ">=2.13.0 <4.0.0"
Expand Down

0 comments on commit ba15446

Please sign in to comment.