Skip to content

Commit 4a8753b

Browse files
toyajiAlexV525
andauthored
feat: Add relativePathAsync getter to AssetPathEntity (#1320)
Add ability to get the relative path of an album/folder through AssetPathEntity. Changes: - Add relativePathAsync getter to AssetPathEntity (Dart layer) - Implement platform channel method getPathRelativePath - Add Android native implementation: - Android 10+ (API 29+): Uses MediaStore RELATIVE_PATH column - Android 9- (API 28-): Uses File.parent from DATA column - iOS/macOS returns null (albums are logical, not physical folders) - Add test UI in example app to display relative paths Platform-specific behavior: - Android: Returns relative path for device folders (e.g., 'DCIM/Camera') - iOS: Returns null (PHAssetCollection logical albums have no file path) - Special albums (All, Recent): Returns null on all platforms --------- Co-authored-by: Alex Li <github@alexv525.com>
1 parent 35e65b8 commit 4a8753b

File tree

11 files changed

+100
-6
lines changed

11 files changed

+100
-6
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ To know more about breaking changes, see the [Migration Guide][].
1313
- Add `cancelToken` parameter to `AssetEntity.loadFile`.
1414
- Add `cancelAllRequest` method to `PhotoManager`.
1515
- The `AssetEntity.getFile` and `AssetEntity.getOriginBytes` methods are public.
16+
- Add `relativePathAsync` getter to `AssetPathEntity`.
1617
- Add iOS 18 smart album subtypes support:
1718
- `smartAlbumSpatial` - For spatial/3D photos
1819
- `smartAlbumProRes` - For ProRes videos

android/src/main/kotlin/com/fluttercandies/photo_manager/constant/Methods.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ class Methods {
7171
const val getOriginBytes = "getOriginBytes"
7272
const val getMediaUrl = "getMediaUrl"
7373
const val fetchEntityProperties = "fetchEntityProperties"
74+
const val getPathRelativePath = "getPathRelativePath"
7475

7576
const val getLatLng = "getLatLngAndroidQ"
7677
const val notify = "notify"

android/src/main/kotlin/com/fluttercandies/photo_manager/core/PhotoManager.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,10 @@ class PhotoManager(private val context: Context) {
250250
}
251251
}
252252

253+
fun getPathRelativePath(galleryId: String): String? {
254+
return dbUtils.getPathRelativePath(context, galleryId)
255+
}
256+
253257
fun getMediaUri(id: Long, type: Int): String {
254258
return dbUtils.getMediaUri(context, id, type)
255259
}

android/src/main/kotlin/com/fluttercandies/photo_manager/core/PhotoManagerPlugin.kt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -458,6 +458,12 @@ class PhotoManagerPlugin(
458458
}
459459
}
460460

461+
Methods.getPathRelativePath -> {
462+
val id = call.argument<String>("id")!!
463+
val relativePath = photoManager.getPathRelativePath(id)
464+
resultHandler.reply(relativePath)
465+
}
466+
461467
Methods.getLatLng -> {
462468
val id = call.argument<String>("id")!!
463469
// 读取id

android/src/main/kotlin/com/fluttercandies/photo_manager/core/utils/AndroidQDBUtils.kt

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -369,7 +369,7 @@ object AndroidQDBUtils : IDBUtils {
369369
}
370370

371371
val insertUri = MediaStoreUtils.getInsertUri(mediaType)
372-
val relativePath = getRelativePath(context, galleryId)
372+
val relativePath = getPathRelativePath(context, galleryId)
373373
val cv = ContentValues().apply {
374374
for (key in copyKeys) {
375375
put(key, cursor.getString(key))
@@ -405,7 +405,7 @@ object AndroidQDBUtils : IDBUtils {
405405
}
406406

407407
val cr = context.contentResolver
408-
val targetPath = getRelativePath(context, galleryId)
408+
val targetPath = getPathRelativePath(context, galleryId)
409409
val contentValues = ContentValues().apply {
410410
put(RELATIVE_PATH, targetPath)
411411
}
@@ -483,9 +483,11 @@ object AndroidQDBUtils : IDBUtils {
483483
return true
484484
}
485485

486-
private fun getRelativePath(context: Context, galleryId: String): String? {
487-
val cr = context.contentResolver
488-
val cursor = cr.logQuery(
486+
override fun getPathRelativePath(context: Context, galleryId: String): String? {
487+
if (galleryId == PhotoManager.ALL_ID) {
488+
return null
489+
}
490+
val cursor = context.contentResolver.logQuery(
489491
allUri,
490492
arrayOf(BUCKET_ID, RELATIVE_PATH),
491493
"$BUCKET_ID = ?",

android/src/main/kotlin/com/fluttercandies/photo_manager/core/utils/DBUtils.kt

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -465,5 +465,27 @@ object DBUtils : IDBUtils {
465465
}
466466
}
467467

468+
override fun getPathRelativePath(context: Context, galleryId: String): String? {
469+
if (galleryId == PhotoManager.ALL_ID) {
470+
return null
471+
}
472+
val cursor = context.contentResolver.logQuery(
473+
allUri,
474+
arrayOf(MediaStore.MediaColumns.DATA),
475+
"${MediaStore.MediaColumns.BUCKET_ID} = ?",
476+
arrayOf(galleryId),
477+
null
478+
)
479+
cursor.use {
480+
if (!it.moveToNext()) {
481+
return null
482+
}
483+
val dataPath = it.getStringOrNull(MediaStore.MediaColumns.DATA)
484+
return dataPath?.let { path ->
485+
File(path).parent
486+
}
487+
}
488+
}
489+
468490
private data class GalleryInfo(val path: String, val galleryId: String, val galleryName: String)
469491
}

android/src/main/kotlin/com/fluttercandies/photo_manager/core/utils/IDBUtils.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -727,6 +727,8 @@ interface IDBUtils {
727727
}
728728
}
729729

730+
fun getPathRelativePath(context: Context, galleryId: String): String?
731+
730732
fun getPathModifiedDate(context: Context, pathId: String): Long? {
731733
val columns = arrayOf(DATE_MODIFIED)
732734
val sortOrder = "$DATE_MODIFIED desc"

example/lib/widget/gallery_item_widget.dart

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,12 @@ class GalleryItemWidget extends StatelessWidget {
8181
sb.writeln('assetCount = ${await item.assetCountAsync}');
8282
sb.writeln('id = ${item.id}');
8383

84+
// Test relativePath
85+
final relativePath = await item.relativePathAsync;
86+
sb.writeln('relativePath = $relativePath');
87+
8488
print(sb.toString());
89+
showToast('Properties logged to console');
8590
},
8691
),
8792
],
@@ -101,7 +106,31 @@ class GalleryItemWidget extends StatelessWidget {
101106
future: item.assetCountAsync,
102107
builder: (_, AsyncSnapshot<int> data) {
103108
if (data.hasData) {
104-
return Text('count : ${data.data}');
109+
return Column(
110+
crossAxisAlignment: CrossAxisAlignment.start,
111+
mainAxisSize: MainAxisSize.min,
112+
children: [
113+
Text('count : ${data.data}'),
114+
FutureBuilder<String?>(
115+
future: item.relativePathAsync,
116+
builder: (_, AsyncSnapshot<String?> pathData) {
117+
if (pathData.connectionState == ConnectionState.done) {
118+
final path = pathData.data;
119+
if (path != null) {
120+
return Text(
121+
'path: $path',
122+
style: TextStyle(
123+
fontSize: 12,
124+
color: Colors.grey[600],
125+
),
126+
);
127+
}
128+
}
129+
return const SizedBox.shrink();
130+
},
131+
),
132+
],
133+
);
105134
}
106135
return const SizedBox.shrink();
107136
},

lib/src/internal/constants.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ class PMConstants {
4343
static const String mGetLatLngAndroidQ = 'getLatLngAndroidQ';
4444
static const String mGetTitleAsync = 'getTitleAsync';
4545
static const String mGetMimeTypeAsync = 'getMimeTypeAsync';
46+
static const String mGetPathRelativePath = 'getPathRelativePath';
4647
static const String mGetMediaUrl = 'getMediaUrl';
4748
static const String mGetSubPath = 'getSubPath';
4849
static const String mCopyAsset = 'copyAsset';

lib/src/internal/plugin.dart

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -684,6 +684,23 @@ class PhotoManagerPlugin with BasePlugin, IosPlugin, AndroidPlugin, OhosPlugin {
684684
return null;
685685
}
686686

687+
Future<String?> getPathRelativePath(AssetPathEntity pathEntity) async {
688+
if (Platform.isIOS || Platform.isMacOS) {
689+
// iOS/macOS use logical albums (PHAssetCollection) without physical paths
690+
return null;
691+
}
692+
if (Platform.isAndroid) {
693+
return _channel.invokeMethod(
694+
PMConstants.mGetPathRelativePath,
695+
<String, dynamic>{'id': pathEntity.id},
696+
);
697+
}
698+
if (PlatformUtils.isOhos) {
699+
return null;
700+
}
701+
return null;
702+
}
703+
687704
Future<int> getAssetCount({
688705
PMFilter? filterOption,
689706
RequestType type = RequestType.common,

0 commit comments

Comments
 (0)