Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ class PhotoManagerPlugin : FlutterPlugin, ActivityAware {
binding.addRequestPermissionsResultListener(listener)
plugin?.let {
binding.addActivityResultListener(it.deleteManager)
binding.addActivityResultListener(it.favoriteManager)
}
}

Expand All @@ -93,6 +94,7 @@ class PhotoManagerPlugin : FlutterPlugin, ActivityAware {
}
plugin?.let { p ->
oldBinding.removeActivityResultListener(p.deleteManager)
oldBinding.removeActivityResultListener(p.favoriteManager)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ class Methods {
const val saveImage = "saveImage"
const val saveImageWithPath = "saveImageWithPath"
const val saveVideo = "saveVideo"
const val favoriteAsset = "favoriteAsset"
const val copyAsset = "copyAsset"
const val moveAssetToPath = "moveAssetToPath"
const val removeNoExistsAssets = "removeNoExistsAssets"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
package com.fluttercandies.photo_manager.core

import android.app.Activity
import android.app.RecoverableSecurityException
import android.content.Context
import android.graphics.Bitmap
import android.net.Uri
import android.os.Build
import android.provider.MediaStore
import android.util.Log
import com.bumptech.glide.Glide
import com.bumptech.glide.request.FutureTarget
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.Context
import android.net.Uri
import android.os.Build
Expand Down Expand Up @@ -58,11 +59,13 @@ class PhotoManagerPlugin(
}

val deleteManager = PhotoManagerDeleteManager(applicationContext, activity)
val favoriteManager = PhotoManagerFavoriteManager(applicationContext)

fun bindActivity(activity: Activity?) {
this.activity = activity
permissionsUtils.withActivity(activity)
deleteManager.bindActivity(activity)
favoriteManager.bindActivity(activity)
}

private val notifyChannel = PhotoManagerNotifyChannel(
Expand Down Expand Up @@ -541,6 +544,17 @@ class PhotoManagerPlugin(
}
}

Methods.favoriteAsset -> {
val assetId = call.argument<String>("id")!!
val isFavorite = call.argument<Boolean>("favorite")!!
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
LogUtils.error("The API 30 or lower have no IS_FAVORITE row in MediaStore.")
resultHandler.reply(false)
return
}
favoriteManager.favoriteAsset(photoManager.getUri(assetId), isFavorite, resultHandler)
}

Methods.copyAsset -> {
val assetId = call.argument<String>("assetId")!!
val galleryId = call.argument<String>("galleryId")!!
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ data class AssetEntity(
val displayName: String,
val modifiedDate: Long,
val orientation: Int,
val isFavorite: Boolean = false,
val lat: Double? = null,
val lng: Double? = null,
val androidQRelativePath: String? = null,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ object ConvertUtils {
"width" to entity.width,
"height" to entity.height,
"orientation" to entity.orientation,
"is_favorite" to entity.isFavorite,
"modifiedDt" to entity.modifiedDate,
"lat" to entity.lat,
"lng" to entity.lng,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import android.provider.MediaStore.MediaColumns.DATE_TAKEN
import android.provider.MediaStore.MediaColumns.DISPLAY_NAME
import android.provider.MediaStore.MediaColumns.DURATION
import android.provider.MediaStore.MediaColumns.HEIGHT
import android.provider.MediaStore.MediaColumns.IS_FAVORITE
import android.provider.MediaStore.MediaColumns.MIME_TYPE
import android.provider.MediaStore.MediaColumns.ORIENTATION
import android.provider.MediaStore.MediaColumns.RELATIVE_PATH
Expand All @@ -27,6 +28,7 @@ import android.provider.MediaStore.MediaColumns.WIDTH
import android.provider.MediaStore.MediaColumns._ID
import android.provider.MediaStore.VOLUME_EXTERNAL
import androidx.annotation.ChecksSdkIntAtLeast
import androidx.annotation.RequiresApi
import androidx.exifinterface.media.ExifInterface
import com.fluttercandies.photo_manager.core.PhotoManager
import com.fluttercandies.photo_manager.core.entity.AssetEntity
Expand Down Expand Up @@ -61,6 +63,7 @@ interface IDBUtils {
DATE_TAKEN //日期
).apply {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) add(DATE_TAKEN) // 拍摄时间
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) add(IS_FAVORITE)
}

val storeVideoKeys = mutableListOf(
Expand All @@ -79,6 +82,7 @@ interface IDBUtils {
DURATION //时长
).apply {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) add(DATE_TAKEN) // 拍摄时间
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) add(IS_FAVORITE)
}

val typeKeys = arrayOf(
Expand Down Expand Up @@ -199,6 +203,7 @@ interface IDBUtils {
val displayName = getString(DISPLAY_NAME)
val modifiedDate = getLong(DATE_MODIFIED)
var orientation: Int = getInt(ORIENTATION)
val isFavorite = Build.VERSION.SDK_INT >= Build.VERSION_CODES.R && getInt(IS_FAVORITE) == 1
val relativePath: String? = if (isAboveAndroidQ) {
getString(RELATIVE_PATH)
} else null
Expand Down Expand Up @@ -240,6 +245,7 @@ interface IDBUtils {
displayName,
modifiedDate,
orientation,
isFavorite,
androidQRelativePath = relativePath,
mimeType = mimeType
)
Expand Down
65 changes: 34 additions & 31 deletions example/lib/page/image_list_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ import '../util/log.dart';
import '../widget/dialog/list_dialog.dart';
import '../widget/image_item_widget.dart';
import '../widget/loading_widget.dart';

import 'copy_to_another_gallery_example.dart';
import 'detail_page.dart';
import 'move_to_another_gallery_example.dart';
Expand Down Expand Up @@ -207,36 +206,40 @@ class _GalleryContentListPageState extends State<GalleryContentListPage> {
<int>[500, 600, 700, 1000, 1500, 2000],
),
),
if (Platform.isIOS || Platform.isMacOS || PlatformUtils.isOhos)
ElevatedButton(
child: const Text('Toggle isFavorite'),
onPressed: () async {
final bool isFavorite = entity.isFavorite;
print('Current isFavorite: $isFavorite');
if (PlatformUtils.isOhos) {
await PhotoManager.editor.ohos.favoriteAsset(
entity: entity,
favorite: !isFavorite,
);
} else {
await PhotoManager.editor.darwin.favoriteAsset(
entity: entity,
favorite: !isFavorite,
);
}
final AssetEntity? newEntity =
await entity.obtainForNewProperties();
print('New isFavorite: ${newEntity?.isFavorite}');
if (!mounted) {
return;
}
if (newEntity != null) {
entity = newEntity;
readPathProvider(context).list[index] = newEntity;
setState(() {});
}
},
),
ElevatedButton(
child: const Text('Toggle isFavorite'),
onPressed: () async {
final bool isFavorite = entity.isFavorite;
print('Current isFavorite: $isFavorite');
if (PlatformUtils.isOhos) {
await PhotoManager.editor.ohos.favoriteAsset(
entity: entity,
favorite: !isFavorite,
);
} else if (Platform.isAndroid) {
await PhotoManager.editor.android.favoriteAsset(
entity: entity,
favorite: !isFavorite,
);
} else {
await PhotoManager.editor.darwin.favoriteAsset(
entity: entity,
favorite: !isFavorite,
);
}
final AssetEntity? newEntity =
await entity.obtainForNewProperties();
print('New isFavorite: ${newEntity?.isFavorite}');
if (!mounted) {
return;
}
if (newEntity != null) {
entity = newEntity;
readPathProvider(context).list[index] = newEntity;
setState(() {});
}
},
),
if ((Platform.isIOS || Platform.isMacOS) && entity.isLivePhoto)
ElevatedButton(
onPressed: () {
Expand Down
1 change: 1 addition & 0 deletions example/lib/util/common_util.dart
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ class CommonUtil {
_buildInfoItemAsync('title', entity.titleAsync),
_buildInfoItem('lat', lat.toString()),
_buildInfoItem('lng', lng.toString()),
_buildInfoItem('is favorite', entity.isFavorite.toString()),
_buildInfoItem('relative path', entity.relativePath ?? 'null'),
_buildInfoItemAsync('mimeType', entity.mimeTypeAsync),
],
Expand Down
36 changes: 16 additions & 20 deletions lib/src/internal/editor.dart
Original file line number Diff line number Diff line change
Expand Up @@ -259,19 +259,12 @@ class DarwinEditor {

/// Sets the favorite status of the given [entity].
///
/// Returns the updated [AssetEntity] if the operation was successful; otherwise, `null`.
Future<AssetEntity> favoriteAsset({
/// Returns `true` if the operation was successful; otherwise, `false`.
Future<bool> favoriteAsset({
required AssetEntity entity,
required bool favorite,
}) async {
final bool result = await plugin.favoriteAsset(entity.id, favorite);
if (result) {
return entity.copyWith(isFavorite: favorite);
}
throw StateError(
'Failed to favorite the asset '
'${entity.id} for unknown reason',
);
return plugin.favoriteAsset(entity.id, favorite);
}

/// Save Live Photo to the gallery from the given [imageFile] and [videoFile].
Expand Down Expand Up @@ -305,6 +298,16 @@ class AndroidEditor {
/// Creates a new [AndroidEditor] object.
const AndroidEditor();

/// Sets the favorite status of the given [entity].
///
/// Returns `true` if the operation was successful; otherwise, `false`.
Future<bool> favoriteAsset({
required AssetEntity entity,
required bool favorite,
}) async {
return plugin.favoriteAsset(entity.id, favorite);
}

/// Moves the given [entity] to the specified [target] path.
///
/// Returns `true` if the move was successful; otherwise, `false`.
Expand Down Expand Up @@ -341,18 +344,11 @@ class OhosEditor {

/// Sets the favorite status of the given [entity].
///
/// Returns the updated [AssetEntity] if the operation was successful; otherwise, `null`.
Future<AssetEntity> favoriteAsset({
/// Returns `true` if the operation was successful; otherwise, `false`.
Future<bool> favoriteAsset({
required AssetEntity entity,
required bool favorite,
}) async {
final bool result = await plugin.favoriteAsset(entity.id, favorite);
if (result) {
return entity.copyWith(isFavorite: favorite);
}
throw StateError(
'Failed to favorite the asset '
'${entity.id} for unknown reason',
);
return plugin.favoriteAsset(entity.id, favorite);
}
}
4 changes: 3 additions & 1 deletion lib/src/types/entity.dart
Original file line number Diff line number Diff line change
Expand Up @@ -852,11 +852,13 @@ class AssetEntity {
final int orientation;

/// Whether the asset is favorite on the device.
/// * Android: Always false.
/// * Android 11 and above: `MediaStore.MediaColumns.IS_FAVORITE`.
/// * Android 10 and below: Always false.
/// * iOS/macOS: `PHAsset.isFavorite`.
///
/// See also:
/// * [DarwinEditor.favoriteAsset] to update the favorite status.
/// * [AndroidEditor.favoriteAsset] to update the favorite status.
final bool isFavorite;

/// The relative path abstraction of the asset.
Expand Down
3 changes: 2 additions & 1 deletion lib/src/utils/convert_utils.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import 'dart:io';
import 'package:photo_manager/platform_utils.dart';

import '../filter/base_filter.dart';
import '../filter/custom/custom_columns.dart';
import '../filter/path_filter.dart';
import '../internal/enums.dart';
import '../types/entity.dart';
Expand Down Expand Up @@ -115,7 +116,7 @@ class ConvertUtils {
height: data['height'] as int,
duration: data['duration'] as int? ?? 0,
orientation: data['orientation'] as int? ?? 0,
isFavorite: data['favorite'] as bool? ?? false,
isFavorite: data[CustomColumns.base.isFavorite] as bool? ?? false,
title: data['title'] as String? ?? title,
subtype: data['subtype'] as int? ?? 0,
createDateSecond: data['createDt'] as int?,
Expand Down