Skip to content

Commit

Permalink
feat: get current permission (#1184)
Browse files Browse the repository at this point in the history
Signed-off-by: Caijinglong <cjl_spy@163.com>
Co-authored-by: Alex Li <github@alexv525.com>
  • Loading branch information
CaiJingLong and AlexV525 authored Sep 22, 2024
1 parent 60037ba commit 8b7b37b
Show file tree
Hide file tree
Showing 13 changed files with 231 additions and 10 deletions.
6 changes: 5 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,11 @@ To know more about breaking changes, see the [Migration Guide][].

### Breaking changes

`saveImage` now requires `title` rather than `filename`.
`saveLivePhoto` now requires `title` rather than `filename`.

### Featuers

- Add `getPermissionState` method to `PhotoManager`.

### Improvements

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.fluttercandies.photo_manager.constant

@Suppress("MemberVisibilityCanBePrivate", "ConstPropertyName")
class Methods {
companion object {
// Not need permission methods
Expand All @@ -10,6 +11,7 @@ class Methods {
const val clearFileCache = "clearFileCache"
const val releaseMemoryCache = "releaseMemoryCache"
const val ignorePermissionCheck = "ignorePermissionCheck"
const val getPermissionState = "getPermissionState"

fun isNotNeedPermissionMethod(method: String): Boolean {
return method in arrayOf(
Expand All @@ -20,6 +22,7 @@ class Methods {
clearFileCache,
releaseMemoryCache,
ignorePermissionCheck,
getPermissionState,
)
}
// Not need permission methods end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ class PhotoManagerPlugin(

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

Expand Down Expand Up @@ -322,6 +323,15 @@ class PhotoManagerPlugin(
ignorePermissionCheck = ignore
resultHandler.reply(ignore)
}

Methods.getPermissionState -> {
val androidPermission = call.argument<Map<*, *>>("androidPermission")!!
val requestType = androidPermission["type"] as Int
val mediaLocation = androidPermission["mediaLocation"] as Boolean
permissionsUtils.getAuthValue(requestType, mediaLocation).let {
resultHandler.reply(it.value)
}
}
}
}

Expand Down
5 changes: 5 additions & 0 deletions example/lib/page/developer/develop_index_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import 'dev_title_page.dart';
import 'ios/create_folder_example.dart';
import 'ios/edit_asset.dart';
import 'issues_page/issue_index_page.dart';
import 'permission_state_page.dart';
import 'remove_all_android_not_exists_example.dart';
import 'verbose_log_page.dart';

Expand Down Expand Up @@ -78,6 +79,10 @@ class _DeveloperIndexPageState extends State<DeveloperIndexPage> {
onPressed: () => navToWidget(const ColumnNamesPage()),
child: const Text('Android: column names'),
),
ElevatedButton(
onPressed: () => navToWidget(const PermissionStatePage()),
child: const Text('Show permission state page'),
),
ElevatedButton(
child: const Text('Show iOS create folder example.'),
onPressed: () => navToWidget(const CreateFolderExample()),
Expand Down
121 changes: 121 additions & 0 deletions example/lib/page/developer/permission_state_page.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
import 'dart:io';

import 'package:flutter/material.dart';
import 'package:photo_manager/photo_manager.dart';

extension on List<Widget> {
List<Widget> paddingAll(double padding) {
return map((e) => Padding(padding: EdgeInsets.all(padding), child: e))
.toList();
}
}

class PermissionStatePage extends StatefulWidget {
const PermissionStatePage({super.key});

@override
State<PermissionStatePage> createState() => _PermissionStatePageState();
}

class _PermissionStatePageState extends State<PermissionStatePage> {
List<int> types = [
RequestType.image.value,
RequestType.video.value,
];

RequestType get _selectedType {
return RequestType.fromTypes(types.map((e) => RequestType(e)).toList());
}

PermissionState? _permissionState;

IosAccessLevel _iosAccessLevel = IosAccessLevel.readWrite; // 添加这一行

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Permission State'),
),
body: ListView(
children: [
if (Platform.isAndroid)
Column(
children: RequestType.values.map((RequestType type) {
return CheckboxListTile(
title: Text(type.toString().split('.').last),
value: types.contains(type.value),
onChanged: (bool? value) {
setState(() {
if (value == true) {
types.add(type.value);
} else {
types.remove(type.value);
}
});
},
);
}).toList(),
),
if (Platform.isIOS)
Column(
children: [
Text('iOS Access Level: $_iosAccessLevel'),
DropdownButton<IosAccessLevel>(
value: _iosAccessLevel,
onChanged: (IosAccessLevel? value) {
setState(() {
_iosAccessLevel = value!;
});
},
items: IosAccessLevel.values.map((IosAccessLevel value) {
return DropdownMenuItem<IosAccessLevel>(
value: value,
child: Text(value.toString()),
);
}).toList(),
),
],
),
ElevatedButton(
onPressed: _requestPermission,
child: const Text('Request Permission'),
),
ElevatedButton(
onPressed: _getPermissionState,
child: const Text('Get Permission State'),
),
if (_permissionState != null)
Text('Current Permission State: $_permissionState'),
].paddingAll(16),
),
);
}

Future<void> _requestPermission() async {
final PermissionRequestOption option = _option();
final result =
await PhotoManager.requestPermissionExtend(requestOption: option);
setState(() {
_permissionState = result;
});
}

PermissionRequestOption _option() {
final type = _selectedType;
final PermissionRequestOption option = PermissionRequestOption(
androidPermission: AndroidPermission(type: type, mediaLocation: false),
iosAccessLevel: IosAccessLevel.readWrite,
ohosPermissions: const [],
);
return option;
}

Future<void> _getPermissionState() async {
final PermissionRequestOption option = _option();
final state = await PhotoManager.getPermissionState(requestOption: option);
setState(() {
_permissionState = state;
});
}
}
2 changes: 1 addition & 1 deletion example/macos/Runner.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,7 @@
isa = PBXProject;
attributes = {
LastSwiftUpdateCheck = 0920;
LastUpgradeCheck = 1300;
LastUpgradeCheck = 1510;
ORGANIZATIONNAME = "";
TargetAttributes = {
33CC10EC2044A3C60003C045 = {
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 = "1300"
LastUpgradeVersion = "1510"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
Expand Down
28 changes: 27 additions & 1 deletion ios/Classes/PMPlugin.m
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,15 @@ - (void)requestPermissionForWriteAndRead:(void (^)(BOOL auth))handler {
}

- (BOOL)isNotNeedPermissionMethod:(NSString *)method {
return [@[@"log", @"openSetting", @"clearFileCache", @"releaseMemoryCache", @"ignorePermissionCheck"] indexOfObject:method] != NSNotFound;
NSArray *notNeedPermissionMethods = @[
@"log",
@"openSetting",
@"clearFileCache",
@"releaseMemoryCache",
@"ignorePermissionCheck",
@"getPermissionState"
];
return [notNeedPermissionMethods containsObject:method];
}

- (BOOL)isAboutPermissionMethod:(NSString *)method {
Expand Down Expand Up @@ -141,9 +149,27 @@ - (void)handleNotNeedPermissionMethod:(ResultHandler *)handler {
} else if ([call.method isEqualToString:@"releaseMemoryCache"]) {
[manager clearCache];
[handler reply:nil];
} else if ([method isEqualToString:@"getPermissionState"]) {
[self getPermissionState:handler];
}
}

- (void)getPermissionState:(ResultHandler *)handler {
int requestAccessLevel = [handler.call.arguments[@"iosAccessLevel"] intValue];
#if __IPHONE_14_0
if (@available(iOS 14, *)) {
PHAuthorizationStatus result = [PHPhotoLibrary authorizationStatusForAccessLevel: requestAccessLevel];
[handler reply: @(result)];
} else {
PHAuthorizationStatus status = [PHPhotoLibrary authorizationStatus];
[handler reply:@(status)];
}
#else
PHAuthorizationStatus status = [PHPhotoLibrary authorizationStatus];
[handler reply:@(status)];
#endif
}

- (void)handleAboutPermissionMethod:(ResultHandler *)handler {
FlutterMethodCall *call = handler.call;
PMManager *manager = self.manager;
Expand Down
1 change: 1 addition & 0 deletions lib/src/internal/constants.dart
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ class PMConstants {
static const String mPresentLimited = 'presentLimited';
static const String mFetchEntityProperties = 'fetchEntityProperties';
static const String mGetAssetCountFromPath = 'getAssetCountFromPath';
static const String mGetPermissionState = 'getPermissionState';

/// These methods have [RequestType] params for Android 13+ (33+).
static const String mFetchPathProperties = 'fetchPathProperties';
Expand Down
11 changes: 6 additions & 5 deletions lib/src/internal/enums.dart
Original file line number Diff line number Diff line change
Expand Up @@ -63,16 +63,19 @@ enum OrderOptionType {
enum PMRequestState { prepare, loading, success, failed }

/// Information about app’s authorization to access the user’s photo library.
/// * Android: Only [authorized] and [denied] are valid.
/// * iOS/macOS: All valid.
///
/// Possible values for platforms:
/// * Android: [authorized], [denied], and [limited].
/// * iOS/macOS: All.
///
/// See also:
/// * [Apple documentation](https://developer.apple.com/documentation/photokit/phauthorizationstatus)
enum PermissionState {
/// The user has not set the app’s authorization status.
notDetermined,

/// The app isn’t authorized to access the photo library, and the user can’t grant such permission.
/// The app isn’t authorized to access the photo library,
/// and the user can’t grant such permission.
restricted,

/// The user explicitly denied this app access to the photo library.
Expand All @@ -82,8 +85,6 @@ enum PermissionState {
authorized,

/// The user authorized this app for limited photo library access.
///
/// This state only supports iOS 14 and above.
limited,
}

Expand Down
10 changes: 10 additions & 0 deletions lib/src/internal/plugin.dart
Original file line number Diff line number Diff line change
Expand Up @@ -672,6 +672,16 @@ class PhotoManagerPlugin with BasePlugin, IosPlugin, AndroidPlugin, OhosPlugin {

return null;
}

Future<PermissionState> getPermissionState(
PermissionRequestOption requestOption,
) async {
final int result = await _channel.invokeMethod(
PMConstants.mGetPermissionState,
requestOption.toMap(),
);
return PermissionState.values[result];
}
}

mixin IosPlugin on BasePlugin {
Expand Down
28 changes: 28 additions & 0 deletions lib/src/managers/photo_manager.dart
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,34 @@ class PhotoManager {
return plugin.requestPermissionExtend(requestOption);
}

/// Get the current [PermissionState] of the photo library
/// with the given [requestOption].
///
/// Example:
/// ```dart
/// final PermissionState state = await PhotoManager.getPermissionState(
/// requestOption: const PermissionRequestOption(
/// androidPermission: AndroidPermission(
// type: RequestType.image,
// mediaLocation: false,
// ),
/// ),
/// );
/// if (state == PermissionState.authorized) {
/// print('The application has full access permission');
/// } else {
/// print('The application does not have full access permission');
/// }
/// ```
///
/// Note: On Android, this method may require an `Activity` context.
/// Call [setIgnorePermissionCheck] if the call is from background service.
static Future<PermissionState> getPermissionState({
required PermissionRequestOption requestOption,
}) {
return plugin.getPermissionState(requestOption);
}

/// Prompts the limited assets selection modal on iOS.
///
/// This method only supports from iOS 14.0, and will behave differently on
Expand Down
14 changes: 13 additions & 1 deletion lib/src/types/types.dart
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,19 @@ class RequestType {
int get hashCode => value;

@override
String toString() => '$runtimeType($value)';
String toString() {
final values = <String>[];
if (containsImage()) {
values.add('image');
}
if (containsVideo()) {
values.add('video');
}
if (containsAudio()) {
values.add('audio');
}
return 'RequestType(${values.join(', ')})';
}
}

/// See [PermissionState].
Expand Down

0 comments on commit 8b7b37b

Please sign in to comment.