您的观察非常正确。Android 系统的回收站实现通常是通过以下几种方式:
-
文件名添加 .Trash 前缀,使文件在文件浏览器中被隐藏
-
在 MediaStore 中将文件标记为 IS_TRASHED = 1
-
有些设备会将文件移动到特定的 .Trash 目录
// 创建文件条目时使用 .Trash 前缀 put(MediaStore.MediaColumns.DISPLAY_NAME, ".Trash-"+System.currentTimeMillis()/1000+"-"+fileName)
// 然后在所有 URI 上设置 IS_TRASHED 标记 val values = ContentValues().apply { put(MediaStore.MediaColumns.IS_TRASHED, 1) } contentResolver.update(uri, values, null, null)
回收站实现的关键点
-
文件名前缀:.Trash-[时间戳]-[原文件名] 是一种常见的格式
-
IS_TRASHED 标记:在 Android 11+ 上,这个标记告诉系统该文件已被移到回收站
-
相对路径:将文件放在应用专属目录下,如 Pictures/应用名称/
-
保留元数据:保存原始文件名和删除时间,以便将来可以恢复
/**
-
从回收站恢复文件 */ @RequiresApi(Build.VERSION_CODES.Q) suspend fun restoreFromTrash(context: Context, uri: Uri) = withContext(Dispatchers.IO) { try { val contentResolver = context.contentResolver
kotlin// 1. 获取当前文件名 var fileName = "" contentResolver.query( uri, arrayOf(MediaStore.MediaColumns.DISPLAY_NAME), null, null, null )?.use { cursor -> if (cursor.moveToFirst()) { fileName = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.DISPLAY_NAME)) } } if (fileName.startsWith(".Trash-")) { // 2. 提取原始文件名 val originalFileName = fileName.substringAfter("-", "").substringAfter("-", "") // 3. 更新文件属性 val values = ContentValues().apply { put(MediaStore.MediaColumns.DISPLAY_NAME, originalFileName) put(MediaStore.MediaColumns.IS_TRASHED, 0) } // 4. 应用更新 contentResolver.update(uri, values, null, null) LogMG.e("tagyy", "文件已从回收站恢复: $originalFileName") return@withContext true }
} catch (e: Exception) { LogMG.e("tagyy", "从回收站恢复失败: ${e.message}") e.printStackTrace() } return@withContext false }
/**
-
获取回收站中的所有文件 */ @RequiresApi(Build.VERSION_CODES.Q) suspend fun getTrashFiles(context: Context) = withContext(Dispatchers.IO) { val trashFiles = mutableListOf() val contentResolver = context.contentResolver
// 查询所有已标记为回收站的文件 val selection = " <math xmlns="http://www.w3.org/1998/Math/MathML"> M e d i a S t o r e . M e d i a C o l u m n s . I S T R A S H E D = ? O R {MediaStore.MediaColumns.IS_TRASHED} = ? OR </math>MediaStore.MediaColumns.ISTRASHED=?OR{MediaStore.MediaColumns.DISPLAY_NAME} LIKE ?" val selectionArgs = arrayOf("1", ".Trash-%")
// 查询不同类型的媒体 val uriTypes = listOf( MediaStore.Images.Media.EXTERNAL_CONTENT_URI, MediaStore.Video.Media.EXTERNAL_CONTENT_URI, MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, MediaStore.Downloads.EXTERNAL_CONTENT_URI )
for (uri in uriTypes) { contentResolver.query( uri, arrayOf(MediaStore.MediaColumns._ID), selection, selectionArgs, null )?.use { cursor -> while (cursor.moveToNext()) { val id = cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.MediaColumns._ID)) trashFiles.add(ContentUris.withAppendedId(uri, id)) } } }
return@withContext trashFiles }
-
总结
Android 系统回收站的实现主要依赖于:
-
文件名前缀(.Trash-)
-
MediaStore 中的 IS_TRASHED 标记
-
将文件放在特定目录
您的实现已经涵盖了这些关键点,应该能够正常工作。记住,不同厂商的 Android 设备可能对回收站有不同的实现,但您的方法是最通用的解决方案。