Skip to content

Commit

Permalink
🐛 [iOS] Fix the behavior of obtaining Live Photos (#1140)
Browse files Browse the repository at this point in the history
  • Loading branch information
AlexV525 authored Jun 7, 2024
1 parent 667f5a1 commit 2acbde4
Show file tree
Hide file tree
Showing 21 changed files with 121 additions and 122 deletions.
14 changes: 13 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,23 @@ that can be found in the LICENSE file. -->

To know more about breaking changes, see the [Migration Guide][].

## 3.2.0

### Improvements

* Restores `containsLivePhotos` to `true` by default and deprecates it.
* Use the main resource's filename for title by default on iOS.

### Fixes

* Fix obtaining the correct resource from various types of resources on iOS.
* Fix `isLocallyAvailable` for edited assets on iOS.

## 3.1.1

### Improvements

* Update plugin structure for OpenHarmony
* Update plugin structure for OpenHarmony.

## 3.1.0

Expand Down
1 change: 1 addition & 0 deletions analysis_options.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ analyzer:
avoid_void_async: error
camel_case_types: error
constant_identifier_names: error
deprecated_member_use_from_same_package: ignore
non_constant_identifier_names: error
prefer_single_quotes: warning
require_trailing_commas: warning
Expand Down
2 changes: 1 addition & 1 deletion example/ios/Flutter/AppFrameworkInfo.plist
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,6 @@
<key>CFBundleVersion</key>
<string>1.0</string>
<key>MinimumOSVersion</key>
<string>11.0</string>
<string>12.0</string>
</dict>
</plist>
2 changes: 1 addition & 1 deletion example/ios/Podfile
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Uncomment this line to define a global platform for your project
platform :ios, '11.0'
platform :ios, '12.0'

# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
Expand Down
14 changes: 7 additions & 7 deletions example/ios/Runner.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@
97C146E61CF9000F007C117D /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 1430;
LastUpgradeCheck = 1510;
ORGANIZATIONNAME = "";
TargetAttributes = {
97C146ED1CF9000F007C117D = {
Expand Down Expand Up @@ -342,7 +342,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
Expand All @@ -361,7 +361,7 @@
DEVELOPMENT_TEAM = S5GU4EMC47;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
Expand Down Expand Up @@ -421,7 +421,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
Expand Down Expand Up @@ -470,7 +470,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
Expand All @@ -491,7 +491,7 @@
DEVELOPMENT_TEAM = S5GU4EMC47;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
Expand All @@ -515,7 +515,7 @@
DEVELOPMENT_TEAM = S5GU4EMC47;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1430"
LastUpgradeVersion = "1510"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
Expand Down
14 changes: 7 additions & 7 deletions example/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -68,12 +68,6 @@ class _SimpleExamplePage extends StatefulWidget {
}

class _SimpleExamplePageState extends State<_SimpleExamplePage> {
/// Customize your own filter options.
final FilterOptionGroup _filterOptionGroup = FilterOptionGroup(
imageOption: const FilterOption(
sizeConstraint: SizeConstraint(ignoreSize: true),
),
);
final int _sizePerPage = 50;

AssetPathEntity? _path;
Expand Down Expand Up @@ -102,10 +96,16 @@ class _SimpleExamplePageState extends State<_SimpleExamplePage> {
showToast('Permission is not accessible.');
return;
}
// Customize your own filter options.
final PMFilter filter = FilterOptionGroup(
imageOption: const FilterOption(
sizeConstraint: SizeConstraint(ignoreSize: true),
),
);
// Obtain assets using the path entity.
final List<AssetPathEntity> paths = await PhotoManager.getAssetPathList(
onlyAll: true,
filterOption: _filterOptionGroup,
filterOption: filter,
);
if (!mounted) {
return;
Expand Down
1 change: 1 addition & 0 deletions example/lib/model/photo_provider.dart
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,7 @@ class PhotoProvider extends ChangeNotifier {
..setOption(AssetType.audio, option)
..createTimeCond = createDtCond
..containsPathModified = _containsPathModified
// ignore: deprecated_member_use
..containsLivePhotos = _containsLivePhotos
..onlyLivePhotos = onlyLivePhotos;
}
Expand Down
1 change: 1 addition & 0 deletions example/lib/page/developer/issues_page/issue_988.dart
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,7 @@ class __DeleteAssetImageListState extends State<_DeleteAssetImageList> {
right: 0,
top: 0,
child: Checkbox(
// ignore: deprecated_member_use
overlayColor: MaterialStateProperty.all(Colors.white),
value: checked.contains(asset),
onChanged: (bool? value) {
Expand Down
2 changes: 1 addition & 1 deletion example/pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name: photo_manager_example
description: Demonstrates how to use the photo_manager plugin.
version: 3.1.0+26
version: 3.2.0+27
publish_to: none

environment:
Expand Down
6 changes: 2 additions & 4 deletions ios/Classes/core/PHAsset+PM_COMMON.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,8 @@ NS_ASSUME_NONNULL_BEGIN
@return The MIME type of this asset if available, otherwise `nil`.
*/
- (nullable NSString*)mimeType;
- (BOOL)isAdjust;
- (PHAssetResource *)getAdjustResource;
- (PHAssetResource *)getUntouchedResource;
- (void)requestAdjustedData:(void (^)(NSData *_Nullable result))block;
- (PHAssetResource *)getCurrentResource;
- (void)requestCurrentResourceData:(void (^)(NSData *_Nullable result))block;
- (PHAssetResource *)getLivePhotosResource;

@end
Expand Down
96 changes: 48 additions & 48 deletions ios/Classes/core/PHAsset+PM_COMMON.m
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
//

#import "PHAsset+PM_COMMON.h"
#import "PHAssetResource+PM_COMMON.h"
#if TARGET_OS_IOS || TARGET_OS_WATCH || TARGET_OS_TV
#import <MobileCoreServices/MobileCoreServices.h>
#else
Expand Down Expand Up @@ -64,11 +65,12 @@ - (NSString *)title {

- (NSString *)originalFilenameWithSubtype:(int)subtype {
if (@available(iOS 9.1, *)) {
if ([self isLivePhoto] && subtype == PHAssetMediaSubtypePhotoLive) {
BOOL isLivePhotoSubtype = (subtype & PHAssetMediaSubtypePhotoLive) == PHAssetMediaSubtypePhotoLive;
if ([self isLivePhoto] && isLivePhotoSubtype) {
return [self getLivePhotosResource].originalFilename;
}
}
PHAssetResource *resource = [self getUntouchedResource];
PHAssetResource *resource = [self getRawResource];
if (resource) {
return resource.originalFilename;
}
Expand Down Expand Up @@ -123,63 +125,64 @@ - (BOOL)videoIsAdjust:(NSArray<PHAssetResource *> *)resources {
return NO;
}

- (PHAssetResource *)getUntouchedResource {
- (PHAssetResource *)getRawResource {
NSArray<PHAssetResource *> *resources = [PHAssetResource assetResourcesForAsset:self];
if (resources.count == 0) {
return nil;
}

if (resources.count == 1) {
return resources[0];
}

for (PHAssetResource *res in resources) {
if (self.mediaType == PHAssetMediaTypeImage
&& res.type == PHAssetResourceTypePhoto) {
if (self.isImage && res.isImage && res.type == PHAssetResourceTypePhoto) {
return res;
}

if (self.mediaType == PHAssetMediaTypeVideo
&& res.type == PHAssetResourceTypeVideo) {
if (self.isVideo && res.isVideo && res.type == PHAssetResourceTypeVideo) {
return res;
}
if (self.isAudio && res.isAudio && res.type == PHAssetResourceTypeAudio) {
return res;
}
}

return nil;
}

- (PHAssetResource *)getAdjustResource {
- (PHAssetResource *)getCurrentResource {
NSArray<PHAssetResource *> *resources = [PHAssetResource assetResourcesForAsset:self];
if (resources.count == 0) {
NSMutableArray<PHAssetResource *> *filtered = [NSMutableArray array];
for (PHAssetResource *res in resources) {
if (!res.isValid) {
continue;
}
BOOL isAllowedType = NO;
if (self.isImage && res.isImage) {
isAllowedType = res.type == PHAssetResourceTypePhoto ||
res.type == PHAssetResourceTypeAlternatePhoto ||
res.type == PHAssetResourceTypeFullSizePhoto;
} else if (self.isVideo && res.isVideo) {
isAllowedType = res.type == PHAssetResourceTypeVideo ||
res.type == PHAssetResourceTypeFullSizeVideo ||
res.type == PHAssetResourceTypeFullSizePairedVideo;
} else if (self.isAudio && res.isAudio) {
isAllowedType = res.type == PHAssetResourceTypeAudio;
}
if (isAllowedType) {
[filtered addObject:res];
}
}
if (filtered.count == 0) {
return nil;
}

if (resources.count == 1) {
if (filtered.count == 1) {
return resources[0];
}

if (![self isAdjust]) {
for (PHAssetResource *res in resources) {
if (self.mediaType == PHAssetMediaTypeImage
&& res.type == PHAssetResourceTypePhoto) {
return res;
}

if (self.mediaType == PHAssetMediaTypeVideo
&& res.type == PHAssetResourceTypeVideo) {
return res;
}
for (PHAssetResource *res in filtered) {
BOOL isCurrent = [[res valueForKey:@"isCurrent"] boolValue];
if (isCurrent) {
return res;
}

return nil;
}

for (PHAssetResource *res in resources) {
for (PHAssetResource *res in filtered) {
if (self.mediaType == PHAssetMediaTypeImage &&
res.type == PHAssetResourceTypeFullSizePhoto) {
return res;
}

if (self.mediaType == PHAssetMediaTypeVideo &&
res.type == PHAssetResourceTypeFullSizeVideo) {
return res;
Expand All @@ -188,8 +191,8 @@ - (PHAssetResource *)getAdjustResource {
return nil;
}

- (void)requestAdjustedData:(void (^)(NSData *_Nullable))block {
PHAssetResource *res = [self getAdjustResource];
- (void)requestCurrentResourceData:(void (^)(NSData *_Nullable))block {
PHAssetResource *res = [self getCurrentResource];

PHAssetResourceManager *manager = PHAssetResourceManager.defaultManager;
PHAssetResourceRequestOptions *opt = [PHAssetResourceRequestOptions new];
Expand Down Expand Up @@ -228,19 +231,16 @@ - (PHAssetResource *)getLivePhotosResource {

if (@available(iOS 9.1, *)) {
if (resources.lastObject && resources.lastObject.type == PHAssetResourceTypePairedVideo) {
resource = resources.lastObject;
}
if (!resource) {
for (PHAssetResource *r in resources) {
// Iterate to find the paired video.
if (r.type == PHAssetResourceTypePairedVideo) {
resource = r;
break;
}
return resources.lastObject;
}
for (PHAssetResource *r in resources) {
// Iterate to find the paired video.
if (r.type == PHAssetResourceTypePairedVideo) {
return r;
}
}
}
return resource;
return nil;
}

@end
1 change: 1 addition & 0 deletions ios/Classes/core/PHAssetResource+PM_COMMON.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,6 @@
- (bool)isVideo;
- (bool)isAudio;
- (bool)isImageOrVideo;
- (bool)isValid;

@end
8 changes: 8 additions & 0 deletions ios/Classes/core/PHAssetResource+PM_COMMON.m
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,12 @@ - (bool)isImageOrVideo{
return [self isVideo] || [self isImage];
}

- (bool)isValid {
bool isResource = self.type != PHAssetResourceTypeAdjustmentData;
if (@available(iOS 17.0, *)) {
isResource = isResource && self.type != PHAssetResourceTypePhotoProxy;
}
return isResource;
}

@end
2 changes: 1 addition & 1 deletion ios/Classes/core/PMFilterOption.h
Original file line number Diff line number Diff line change
Expand Up @@ -68,4 +68,4 @@ typedef struct PMDurationConstraint {

@interface PMCustomFilterOption : NSObject <PMBaseFilter>
@property (nonatomic, strong) NSDictionary *params;
@end
@end
15 changes: 0 additions & 15 deletions ios/Classes/core/PMFilterOption.m
Original file line number Diff line number Diff line change
Expand Up @@ -113,21 +113,6 @@ - (PHFetchOptions *)getFetchOptions:(int)type {
[cond appendString:durationCond];
[args addObjectsFromArray:durationArgs];

if (@available(iOS 9.1, *)) {
if (!containsImage && optionGroup.containsLivePhotos) {
[cond appendString:@" )"];
[cond appendString:@" OR "];
[cond appendString:@"( "];
[cond appendString:@"mediaType == %d"];
[args addObject:@(PHAssetMediaTypeImage)];
[cond appendString:@" AND "];
[cond appendString:[NSString
stringWithFormat:@"( mediaSubtype & %lu ) == 8",
(unsigned long) PHAssetMediaSubtypePhotoLive]
];
}
}

[cond appendString:@" ) "];
}

Expand Down
Loading

0 comments on commit 2acbde4

Please sign in to comment.