Skip to content

Commit 5ab3ffc

Browse files
committed
Implement API to request permissions on Android
1 parent afe7f77 commit 5ab3ffc

File tree

8 files changed

+474
-233
lines changed

8 files changed

+474
-233
lines changed

android/src/main/kotlin/com/baseflow/permissionhandler/PermissionHandlerPlugin.kt

+354-231
Large diffs are not rendered by default.

android/src/main/kotlin/com/baseflow/permissionhandler/utils/Codec.kt

+17-1
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,35 @@ package com.baseflow.permissionhandler.utils
33
import com.baseflow.permissionhandler.data.PermissionGroup
44
import com.baseflow.permissionhandler.data.PermissionStatus
55
import com.google.gson.Gson
6+
import com.google.gson.GsonBuilder
7+
import com.google.gson.reflect.TypeToken
68

79
class Codec {
810
companion object {
9-
@JvmStatic val gsonDecoder : Gson = Gson()
11+
@JvmStatic
12+
private val gsonDecoder : Gson = GsonBuilder().enableComplexMapKeySerialization().create()
1013

1114
@JvmStatic
1215
fun decodePermissionGroup(arguments: Any) : PermissionGroup {
1316
return Codec.gsonDecoder.fromJson(arguments.toString(), PermissionGroup::class.java)
1417
}
1518

19+
@JvmStatic
20+
fun decodePermissionGroups(arguments: Any) : Array<PermissionGroup> {
21+
22+
var permissionGroupsType = object: TypeToken<Array<PermissionGroup>>() {}.type
23+
return Codec.gsonDecoder.fromJson(arguments.toString(), permissionGroupsType)
24+
}
25+
1626
@JvmStatic
1727
fun encodePermissionStatus(permissionStatus: PermissionStatus) : String {
1828
return gsonDecoder.toJson(permissionStatus)
1929
}
30+
31+
@JvmStatic
32+
fun encodePermissionRequestResult(permissionResults: Map<PermissionGroup, PermissionStatus>) : String {
33+
val jsonString = gsonDecoder.toJson(permissionResults)
34+
return jsonString
35+
}
2036
}
2137
}
Binary file not shown.

example/ios/Runner.xcodeproj/project.pbxproj

+60
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
2121
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
2222
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
23+
F83E7534C5D827024000AB4C /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 36F84C9A972D968241208A7F /* Pods_Runner.framework */; };
2324
/* End PBXBuildFile section */
2425

2526
/* Begin PBXCopyFilesBuildPhase section */
@@ -41,6 +42,7 @@
4142
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; };
4243
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
4344
2D5378251FAA1A9400D5DBA9 /* flutter_assets */ = {isa = PBXFileReference; lastKnownFileType = folder; name = flutter_assets; path = Flutter/flutter_assets; sourceTree = SOURCE_ROOT; };
45+
36F84C9A972D968241208A7F /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
4446
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
4547
3B80C3931E831B6300D905FE /* App.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = App.framework; path = Flutter/App.framework; sourceTree = "<group>"; };
4648
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = "<group>"; };
@@ -63,12 +65,21 @@
6365
files = (
6466
9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */,
6567
3B80C3941E831B6300D905FE /* App.framework in Frameworks */,
68+
F83E7534C5D827024000AB4C /* Pods_Runner.framework in Frameworks */,
6669
);
6770
runOnlyForDeploymentPostprocessing = 0;
6871
};
6972
/* End PBXFrameworksBuildPhase section */
7073

7174
/* Begin PBXGroup section */
75+
1850378EF7B1AE3FA40E4F87 /* Frameworks */ = {
76+
isa = PBXGroup;
77+
children = (
78+
36F84C9A972D968241208A7F /* Pods_Runner.framework */,
79+
);
80+
name = Frameworks;
81+
sourceTree = "<group>";
82+
};
7283
9740EEB11CF90186004384FC /* Flutter */ = {
7384
isa = PBXGroup;
7485
children = (
@@ -89,6 +100,8 @@
89100
9740EEB11CF90186004384FC /* Flutter */,
90101
97C146F01CF9000F007C117D /* Runner */,
91102
97C146EF1CF9000F007C117D /* Products */,
103+
A2B39E4F789ABF9C57736776 /* Pods */,
104+
1850378EF7B1AE3FA40E4F87 /* Frameworks */,
92105
);
93106
sourceTree = "<group>";
94107
};
@@ -123,19 +136,28 @@
123136
name = "Supporting Files";
124137
sourceTree = "<group>";
125138
};
139+
A2B39E4F789ABF9C57736776 /* Pods */ = {
140+
isa = PBXGroup;
141+
children = (
142+
);
143+
name = Pods;
144+
sourceTree = "<group>";
145+
};
126146
/* End PBXGroup section */
127147

128148
/* Begin PBXNativeTarget section */
129149
97C146ED1CF9000F007C117D /* Runner */ = {
130150
isa = PBXNativeTarget;
131151
buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
132152
buildPhases = (
153+
E8ED1A28A9A0730EDF0565B2 /* [CP] Check Pods Manifest.lock */,
133154
9740EEB61CF901F6004384FC /* Run Script */,
134155
97C146EA1CF9000F007C117D /* Sources */,
135156
97C146EB1CF9000F007C117D /* Frameworks */,
136157
97C146EC1CF9000F007C117D /* Resources */,
137158
9705A1C41CF9048500538489 /* Embed Frameworks */,
138159
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
160+
259652FD16940B446BA7C6C6 /* [CP] Embed Pods Frameworks */,
139161
);
140162
buildRules = (
141163
);
@@ -197,6 +219,26 @@
197219
/* End PBXResourcesBuildPhase section */
198220

199221
/* Begin PBXShellScriptBuildPhase section */
222+
259652FD16940B446BA7C6C6 /* [CP] Embed Pods Frameworks */ = {
223+
isa = PBXShellScriptBuildPhase;
224+
buildActionMask = 2147483647;
225+
files = (
226+
);
227+
inputPaths = (
228+
"${SRCROOT}/Pods/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh",
229+
"${PODS_ROOT}/../.symlinks/flutter/ios/Flutter.framework",
230+
"${BUILT_PRODUCTS_DIR}/permission_handler/permission_handler.framework",
231+
);
232+
name = "[CP] Embed Pods Frameworks";
233+
outputPaths = (
234+
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Flutter.framework",
235+
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/permission_handler.framework",
236+
);
237+
runOnlyForDeploymentPostprocessing = 0;
238+
shellPath = /bin/sh;
239+
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
240+
showEnvVarsInLog = 0;
241+
};
200242
3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
201243
isa = PBXShellScriptBuildPhase;
202244
buildActionMask = 2147483647;
@@ -225,6 +267,24 @@
225267
shellPath = /bin/sh;
226268
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
227269
};
270+
E8ED1A28A9A0730EDF0565B2 /* [CP] Check Pods Manifest.lock */ = {
271+
isa = PBXShellScriptBuildPhase;
272+
buildActionMask = 2147483647;
273+
files = (
274+
);
275+
inputPaths = (
276+
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
277+
"${PODS_ROOT}/Manifest.lock",
278+
);
279+
name = "[CP] Check Pods Manifest.lock";
280+
outputPaths = (
281+
"$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt",
282+
);
283+
runOnlyForDeploymentPostprocessing = 0;
284+
shellPath = /bin/sh;
285+
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
286+
showEnvVarsInLog = 0;
287+
};
228288
/* End PBXShellScriptBuildPhase section */
229289

230290
/* Begin PBXSourcesBuildPhase section */

example/ios/Runner.xcworkspace/contents.xcworkspacedata

+3
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

example/lib/main.dart

+7
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,13 @@ class _MyAppState extends State<MyApp> {
2727
// Platform messages may fail, so we use a try/catch PlatformException.
2828
try {
2929
permissionStatus = await PermissionHandler.checkPermissionStatus(PermissionGroup.calendar);
30+
31+
if(permissionStatus != PermissionStatus.granted){
32+
var permissions = await PermissionHandler.requestPermissions([PermissionGroup.calendar]);
33+
if(permissions.containsKey(PermissionGroup.calendar)) {
34+
permissionStatus = permissions[PermissionGroup.calendar];
35+
}
36+
}
3037
} on PlatformException {
3138
permissionStatus = PermissionStatus.unknown;
3239
}

lib/permission_handler.dart

+16-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,22 @@ class PermissionHandler {
1111

1212
/// Returns a [Future] containing the current permission status for the supplied [PermissionGroup].
1313
static Future<PermissionStatus> checkPermissionStatus(PermissionGroup permission) async {
14-
final status = await _channel.invokeMethod('checkPermissionStatus', Codec.encodePermissionGroup(permission));
14+
final status = await _channel.invokeMethod(
15+
'checkPermissionStatus',
16+
Codec.encodePermissionGroup(permission));
17+
1518
return Codec.decodePermissionStatus(status);
1619
}
20+
21+
static Future<bool> openAppSettings() async =>
22+
await _channel.invokeMethod("openAppSettings");
23+
24+
static Future<Map<PermissionGroup, PermissionStatus>> requestPermissions(List<PermissionGroup> permissions) async {
25+
final jsonData = Codec.encodePermissionGroups(permissions);
26+
final status = await _channel.invokeMethod(
27+
'requestPermissions',
28+
jsonData);
29+
30+
return Codec.decodePermissionRequestResult(status);
31+
}
1732
}

lib/utils/codec.dart

+17
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,27 @@ class Codec {
88

99
return PermissionStatus.values.firstWhere((e) => e.toString().split('.').last == permission);
1010
}
11+
12+
static Map<PermissionGroup, PermissionStatus> decodePermissionRequestResult(dynamic value) {
13+
final jsonObject = json.decode(value);
14+
15+
final permissionResults = Map<PermissionGroup, PermissionStatus>();
16+
jsonObject.forEach((key, value) {
17+
final permissionGroup = PermissionGroup.values.firstWhere((e) => e.toString().split('.').last == key.toString());
18+
final permissionStatus = PermissionStatus.values.firstWhere((e) => e.toString().split('.').last == value.toString());
19+
20+
permissionResults[permissionGroup] = permissionStatus;
21+
});
22+
23+
return permissionResults;
24+
}
1125

1226
static String encodePermissionGroup(PermissionGroup permissionGroup) =>
1327
json.encode(_encodeEnum(permissionGroup));
1428

29+
static String encodePermissionGroups(List<PermissionGroup> permissions) =>
30+
json.encode(permissions.map((p) => _encodeEnum(p)).toList());
31+
1532
static String _encodeEnum(dynamic value) {
1633
return value.toString().split('.').last;
1734
}

0 commit comments

Comments
 (0)