Quellcode durchsuchen

refactor. finished adding request permissions

Yonah Forst vor 8 Jahren
Ursprung
Commit
58e2d528f7

+ 0
- 26
PermissionsAsker.h Datei anzeigen

@@ -1,26 +0,0 @@
1
-//
2
-//  PermissionsAsker.h
3
-//  ReactNativePermissions
4
-//
5
-//  Created by Yonah Forst on 07/07/16.
6
-//  Copyright © 2016 Yonah Forst. All rights reserved.
7
-//
8
-
9
-#import <Foundation/Foundation.h>
10
-#import <UIKit/UIKit.h>
11
-#import "RCTConvert+RNPermissionsStatus.h"
12
-
13
-@interface PermissionsAsker : NSObject
14
-
15
-+ (instancetype)sharedInstance;
16
-- (void)location:(NSString *)type;
17
-- (void)notification:(UIUserNotificationType)types completionHandler:(void (^)(RNPermissionsStatus))completionHandler;
18
-- (void)bluetooth;
19
-- (void)camera;
20
-- (void)microphone;
21
-- (void)photo;
22
-- (void)contacts;
23
-- (void)event;
24
-- (void)reminder;
25
-- (void)backgroundRefresh;
26
-@end

+ 0
- 114
PermissionsAsker.m Datei anzeigen

@@ -1,114 +0,0 @@
1
-//
2
-//  PermissionsAsker.m
3
-//  ReactNativePermissions
4
-//
5
-//  Created by Yonah Forst on 07/07/16.
6
-//  Copyright © 2016 Yonah Forst. All rights reserved.
7
-//
8
-
9
-#import "PermissionsAsker.h"
10
-
11
-#import <AddressBook/AddressBook.h>
12
-#import <AssetsLibrary/AssetsLibrary.h>
13
-#import <EventKit/EventKit.h>
14
-#import <CoreLocation/CoreLocation.h>
15
-#import <AVFoundation/AVFoundation.h>
16
-#import <CoreBluetooth/CoreBluetooth.h>
17
-
18
-#import "PermissionsChecker.h"
19
-
20
-static PermissionsAsker *__sharedInstance;
21
-
22
-@interface PermissionsAsker() <CLLocationManagerDelegate, CBPeripheralManagerDelegate>
23
-@property (strong, nonatomic) CLLocationManager *locationManager;
24
-@property (strong, nonatomic) CBPeripheralManager *peripheralManager;
25
-@property (copy) void (^notificationCompletionBlock)(RNPermissionsStatus);
26
-
27
-@end
28
-
29
-
30
-@implementation PermissionsAsker
31
-
32
-+ (instancetype) sharedInstance
33
-{
34
-    static dispatch_once_t onceToken;
35
-    dispatch_once(&onceToken, ^{
36
-        __sharedInstance = [[PermissionsAsker alloc] init];
37
-    });
38
-    return __sharedInstance;
39
-}
40
-
41
-
42
-- (void)location:(NSString *)type
43
-{
44
-    self.locationManager = [[CLLocationManager alloc] init];
45
-    self.locationManager.delegate = self;
46
-    if ([type isEqualToString:@"always"]) {
47
-        [self.locationManager requestAlwaysAuthorization];
48
-    } else {
49
-        [self.locationManager requestWhenInUseAuthorization];
50
-    }
51
-}
52
-
53
-- (void)notification:(UIUserNotificationType)types completionHandler:(void (^)(RNPermissionsStatus))completionHandler
54
-{
55
-    BOOL didAskForPermission = [[NSUserDefaults standardUserDefaults] boolForKey:@"DidAskForNotifications"];
56
-    if (!didAskForPermission) {
57
-        self.notificationCompletionBlock = completionHandler;
58
-        
59
-        [[NSNotificationCenter defaultCenter] addObserver:self
60
-                                                 selector:@selector(applicationDidBecomeActive)
61
-                                                     name:UIApplicationDidBecomeActiveNotification
62
-                                                   object:nil];
63
-        
64
-        if ([[UIApplication sharedApplication] respondsToSelector:@selector(isRegisteredForRemoteNotifications)]) {
65
-            // iOS8+
66
-            UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:types categories:nil];
67
-            [[UIApplication sharedApplication] registerUserNotificationSettings:settings];
68
-            [[UIApplication sharedApplication] registerForRemoteNotifications];
69
-        } else {
70
-            [[UIApplication sharedApplication] registerForRemoteNotificationTypes:(UIRemoteNotificationType)types];
71
-        }
72
-        
73
-        [[NSUserDefaults standardUserDefaults] setBool:YES
74
-                                                forKey:@"DidAskForNotifications"];
75
-        [[NSUserDefaults standardUserDefaults] synchronize];
76
-    } else {
77
-        RNPermissionsStatus status = [PermissionsChecker notification];
78
-        completionHandler(status);
79
-    }
80
-
81
-}
82
-
83
-- (void)applicationDidBecomeActive
84
-{
85
-    [[NSNotificationCenter defaultCenter] removeObserver:self
86
-                                                    name:UIApplicationDidBecomeActiveNotification
87
-                                                  object:nil];
88
-
89
-    //for some reason, checking permission right away returns denied. need to wait a tiny bit
90
-    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.1 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
91
-        RNPermissionsStatus status = [PermissionsChecker notification];
92
-        self.notificationCompletionBlock(status);
93
-        self.notificationCompletionBlock = nil;
94
-    });
95
-}
96
-
97
-- (void)bluetooth
98
-{
99
-    self.peripheralManager = [[CBPeripheralManager alloc] initWithDelegate:self queue:nil];
100
-    [self.peripheralManager startAdvertising:@{}];
101
-}
102
-
103
-- (void) peripheralManagerDidUpdateState:(CBPeripheralManager *)peripheralManager
104
-{
105
-    if (self.peripheralManager) {
106
-        [self.peripheralManager stopAdvertising];
107
-        self.peripheralManager = nil;
108
-    }
109
-}
110
-
111
-
112
-
113
-
114
-@end

+ 0
- 25
PermissionsChecker.h Datei anzeigen

@@ -1,25 +0,0 @@
1
-//
2
-//  PermissionsChecker
3
-//  PermissionsChecker
4
-//
5
-//  Created by Yonah Forst on 18/02/16.
6
-//  Copyright © 2016 Yonah Forst. All rights reserved.
7
-//
8
-#import "RCTConvert+RNPermissionsStatus.h"
9
-
10
-@interface PermissionsChecker : NSObject
11
-
12
-+ (BOOL)canOpenSettings;
13
-+ (void)openSettings;
14
-+ (RNPermissionsStatus)location;
15
-+ (RNPermissionsStatus)camera;
16
-+ (RNPermissionsStatus)microphone;
17
-+ (RNPermissionsStatus)photo;
18
-+ (RNPermissionsStatus)contacts;
19
-+ (RNPermissionsStatus)event;
20
-+ (RNPermissionsStatus)reminder;
21
-+ (RNPermissionsStatus)bluetooth;
22
-+ (RNPermissionsStatus)notification;
23
-+ (RNPermissionsStatus)backgroundRefresh;
24
-
25
-@end

+ 0
- 244
PermissionsChecker.m Datei anzeigen

@@ -1,244 +0,0 @@
1
-//
2
-//  PermissionsChecker
3
-//  PermissionsChecker
4
-//
5
-//  Created by Yonah Forst on 18/02/16.
6
-//  Copyright © 2016 Yonah Forst. All rights reserved.
7
-//
8
-
9
-@import Contacts;
10
-
11
-#import "PermissionsChecker.h"
12
-
13
-#import <AddressBook/AddressBook.h>
14
-#import <AssetsLibrary/AssetsLibrary.h>
15
-#import <EventKit/EventKit.h>
16
-#import <CoreLocation/CoreLocation.h>
17
-#import <AVFoundation/AVFoundation.h>
18
-#import <CoreBluetooth/CoreBluetooth.h>
19
-
20
-#if __IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_9_0
21
-@import Contacts;
22
-@import Photos;
23
-#endif
24
-
25
-
26
-@interface PermissionsChecker()
27
-@end
28
-
29
-@implementation PermissionsChecker
30
-
31
-+ (BOOL)canOpenSettings
32
-{
33
-    return UIApplicationOpenSettingsURLString != nil;
34
-}
35
-
36
-+ (void)openSettings
37
-{
38
-    if ([self canOpenSettings]) {
39
-        NSURL *url = [NSURL URLWithString:UIApplicationOpenSettingsURLString];
40
-        [[UIApplication sharedApplication] openURL:url];
41
-    }
42
-}
43
-
44
-
45
-+ (RNPermissionsStatus)location
46
-{
47
-    int status = [CLLocationManager authorizationStatus];
48
-    switch (status) {
49
-        case kCLAuthorizationStatusAuthorizedAlways:
50
-        case kCLAuthorizationStatusAuthorizedWhenInUse:
51
-            return RNPermissionsStatusAuthorized;
52
-        case kCLAuthorizationStatusDenied:
53
-            return RNPermissionsStatusDenied;
54
-        case kCLAuthorizationStatusRestricted:
55
-            return RNPermissionsStatusRestricted;
56
-        default:
57
-            return RNPermissionsStatusUndetermined;
58
-    }
59
-}
60
-
61
-
62
-
63
-
64
-+ (RNPermissionsStatus)camera
65
-{
66
-    int status = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo];
67
-    switch (status) {
68
-        case AVAuthorizationStatusAuthorized:
69
-            return RNPermissionsStatusAuthorized;
70
-        case AVAuthorizationStatusDenied:
71
-            return RNPermissionsStatusDenied;
72
-        case AVAuthorizationStatusRestricted:
73
-            return RNPermissionsStatusRestricted;
74
-        default:
75
-            return RNPermissionsStatusUndetermined;
76
-    }
77
-}
78
-
79
-+ (RNPermissionsStatus)microphone
80
-{
81
-    int status = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeAudio];
82
-    switch (status) {
83
-        case AVAuthorizationStatusAuthorized:
84
-            return RNPermissionsStatusAuthorized;
85
-        case AVAuthorizationStatusDenied:
86
-            return RNPermissionsStatusDenied;
87
-        case AVAuthorizationStatusRestricted:
88
-            return RNPermissionsStatusRestricted;
89
-        default:
90
-            return RNPermissionsStatusUndetermined;
91
-    }
92
-}
93
-
94
-+ (RNPermissionsStatus)photo
95
-{
96
-#if __IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_9_0
97
-    int status = [PHPhotoLibrary authorizationStatus];
98
-    switch (status) {
99
-        case PHAuthorizationStatusAuthorized:
100
-            return RNPermissionsStatusAuthorized;
101
-        case PHAuthorizationStatusDenied:
102
-            return RNPermissionsStatusDenied;
103
-        case PHAuthorizationStatusRestricted:
104
-            return RNPermissionsStatusRestricted;
105
-        default:
106
-            return RNPermissionsStatusUndetermined;
107
-    }
108
-#else
109
-    int status = ABAddressBookGetAuthorizationStatus();
110
-    switch (status) {
111
-        case kABAuthorizationStatusAuthorized:
112
-            return RNPermissionsStatusAuthorized;
113
-        case kABAuthorizationStatusDenied:
114
-            return RNPermissionsStatusDenied;
115
-        case kABAuthorizationStatusRestricted:
116
-            return RNPermissionsStatusRestricted;
117
-        default:
118
-            return RNPermissionsStatusUndetermined;
119
-    }
120
-#endif
121
-}
122
-
123
-
124
-+ (RNPermissionsStatus)contacts
125
-{
126
-#if __IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_9_0
127
-    int status = [CNContactStore authorizationStatusForEntityType:CNEntityTypeContacts];
128
-    switch (status) {
129
-        case CNAuthorizationStatusAuthorized:
130
-            return RNPermissionsStatusAuthorized;
131
-        case CNAuthorizationStatusDenied:
132
-            return RNPermissionsStatusDenied;
133
-        case CNAuthorizationStatusRestricted:
134
-            return RNPermissionsStatusRestricted;
135
-        default:
136
-            return RNPermissionsStatusUndetermined;
137
-    }
138
-#else
139
-    int status = ABAddressBookGetAuthorizationStatus();
140
-    switch (status) {
141
-        case kABAuthorizationStatusAuthorized:
142
-            return RNPermissionsStatusAuthorized;
143
-        case kABAuthorizationStatusDenied:
144
-            return RNPermissionsStatusDenied;
145
-        case kABAuthorizationStatusRestricted:
146
-            return RNPermissionsStatusRestricted;
147
-        default:
148
-            return RNPermissionsStatusUndetermined;
149
-    }
150
-#endif
151
-}
152
-
153
-
154
-+ (RNPermissionsStatus)event
155
-{
156
-    int status = [EKEventStore authorizationStatusForEntityType:EKEntityTypeEvent];
157
-    switch (status) {
158
-        case EKAuthorizationStatusAuthorized:
159
-            return RNPermissionsStatusAuthorized;
160
-        case EKAuthorizationStatusDenied:
161
-            return RNPermissionsStatusDenied;
162
-        case EKAuthorizationStatusRestricted:
163
-            return RNPermissionsStatusRestricted;
164
-        default:
165
-            return RNPermissionsStatusUndetermined;
166
-    }
167
-}
168
-
169
-+ (RNPermissionsStatus)reminder
170
-{
171
-    int status = [EKEventStore authorizationStatusForEntityType:EKEntityTypeReminder];
172
-    switch (status) {
173
-        case EKAuthorizationStatusAuthorized:
174
-            return RNPermissionsStatusAuthorized;
175
-        case EKAuthorizationStatusDenied:
176
-            return RNPermissionsStatusDenied;
177
-        case EKAuthorizationStatusRestricted:
178
-            return RNPermissionsStatusRestricted;
179
-        default:
180
-            return RNPermissionsStatusUndetermined;
181
-    }
182
-}
183
-
184
-
185
-+ (RNPermissionsStatus)bluetooth
186
-{
187
-    int status = [CBPeripheralManager authorizationStatus];
188
-    switch (status) {
189
-        case CBPeripheralManagerAuthorizationStatusAuthorized:
190
-            return RNPermissionsStatusAuthorized;
191
-        case CBPeripheralManagerAuthorizationStatusDenied:
192
-            return RNPermissionsStatusDenied;
193
-        case CBPeripheralManagerAuthorizationStatusRestricted:
194
-            return RNPermissionsStatusRestricted;
195
-        default:
196
-            return RNPermissionsStatusUndetermined;
197
-    }
198
-}
199
-
200
-//problem here is that we can only return Authorized or Undetermined
201
-+ (RNPermissionsStatus)notification
202
-{
203
-    BOOL didAskForPermission = [[NSUserDefaults standardUserDefaults] boolForKey:@"DidAskForNotifications"];
204
-
205
-    if (didAskForPermission) {
206
-        if ([[UIApplication sharedApplication] respondsToSelector:@selector(isRegisteredForRemoteNotifications)]) {
207
-            // iOS8+
208
-            BOOL isRegistered = [[UIApplication sharedApplication] isRegisteredForRemoteNotifications];
209
-            BOOL isEnabled = [[[UIApplication sharedApplication] currentUserNotificationSettings] types] != UIUserNotificationTypeNone;
210
-            if (isRegistered || isEnabled) {
211
-                return isEnabled ? RNPermissionsStatusAuthorized : RNPermissionsStatusDenied;
212
-            }
213
-            else {
214
-                return RNPermissionsStatusDenied;
215
-            }
216
-        } else {
217
-            if ([[UIApplication sharedApplication] enabledRemoteNotificationTypes] == UIRemoteNotificationTypeNone) {
218
-                return RNPermissionsStatusDenied;
219
-            }
220
-            else {
221
-                return RNPermissionsStatusAuthorized;
222
-            }
223
-        }
224
-    } else {
225
-        return RNPermissionsStatusUndetermined;
226
-    }
227
-}
228
-
229
-+ (RNPermissionsStatus)backgroundRefresh
230
-{
231
-    int status = [[UIApplication sharedApplication] backgroundRefreshStatus];
232
-    switch (status) {
233
-        case UIBackgroundRefreshStatusAvailable:
234
-            return RNPermissionsStatusAuthorized;
235
-        case UIBackgroundRefreshStatusDenied:
236
-            return RNPermissionsStatusDenied;
237
-        case UIBackgroundRefreshStatusRestricted:
238
-            return RNPermissionsStatusRestricted;
239
-        default:
240
-            return RNPermissionsStatusUndetermined;
241
-    }
242
-}
243
-
244
-@end

+ 33
- 0
RCTConvert+RNPStatus.h Datei anzeigen

@@ -0,0 +1,33 @@
1
+//
2
+//  RCTConvert+RNPStatus
3
+//  ReactNativePermissions
4
+//
5
+//  Created by Yonah Forst on 23/03/16.
6
+//  Copyright © 2016 Yonah Forst. All rights reserved.
7
+//
8
+
9
+#import "RCTConvert.h"
10
+
11
+static NSString* RNPStatusUndetermined = @"undetermined";
12
+static NSString* RNPStatusDenied = @"denied";
13
+static NSString* RNPStatusAuthorized = @"authorized";
14
+static NSString* RNPStatusRestricted = @"restricted";
15
+
16
+
17
+typedef NS_ENUM(NSInteger, RNPType) {
18
+    RNPTypeUnknown,
19
+    RNPTypeLocation,
20
+    RNPTypeCamera,
21
+    RNPTypeMicrophone,
22
+    RNPTypePhoto,
23
+    RNPTypeContacts,
24
+    RNPTypeEvent,
25
+    RNPTypeReminder,
26
+    RNPTypeBluetooth,
27
+    RNPTypeNotification,
28
+    RNPTypeBackgroundRefresh
29
+};
30
+
31
+@interface RCTConvert (RNPStatus)
32
+
33
+@end

+ 27
- 0
RCTConvert+RNPStatus.m Datei anzeigen

@@ -0,0 +1,27 @@
1
+//
2
+//  RCTConvert+RNPermissionsStatus.m
3
+//  ReactNativePermissions
4
+//
5
+//  Created by Yonah Forst on 23/03/16.
6
+//  Copyright © 2016 Yonah Forst. All rights reserved.
7
+//
8
+
9
+#import "RCTConvert+RNPStatus.h"
10
+
11
+@implementation RCTConvert (RNPStatus)
12
+
13
+
14
+RCT_ENUM_CONVERTER(RNPType, (@{ @"location" : @(RNPTypeLocation),
15
+                                @"camera" : @(RNPTypeCamera),
16
+                                @"microphone" : @(RNPTypeMicrophone),
17
+                                @"photo" : @(RNPTypePhoto),
18
+                                @"contacts" : @(RNPTypeContacts),
19
+                                @"event" : @(RNPTypeEvent),
20
+                                @"reminder" : @(RNPTypeReminder),
21
+                                @"bluetooth" : @(RNPTypeBluetooth),
22
+                                @"notification" : @(RNPTypeNotification),
23
+                                @"backgroundRefresh": @(RNPTypeBackgroundRefresh)
24
+                                }),
25
+                                RNPTypeUnknown, integerValue)
26
+
27
+@end

+ 0
- 20
RCTConvert+RNPermissionsStatus.h Datei anzeigen

@@ -1,20 +0,0 @@
1
-//
2
-//  RCTConvert+RNPermissionsStatus.h
3
-//  ReactNativePermissions
4
-//
5
-//  Created by Yonah Forst on 23/03/16.
6
-//  Copyright © 2016 Yonah Forst. All rights reserved.
7
-//
8
-
9
-#import "RCTConvert.h"
10
-
11
-typedef NS_ENUM(NSInteger, RNPermissionsStatus) {
12
-    RNPermissionsStatusUndetermined,
13
-    RNPermissionsStatusDenied,
14
-    RNPermissionsStatusAuthorized,
15
-    RNPermissionsStatusRestricted
16
-};
17
-
18
-@interface RCTConvert (RNPermissionsStatus)
19
-
20
-@end

+ 0
- 21
RCTConvert+RNPermissionsStatus.m Datei anzeigen

@@ -1,21 +0,0 @@
1
-//
2
-//  RCTConvert+RNPermissionsStatus.m
3
-//  ReactNativePermissions
4
-//
5
-//  Created by Yonah Forst on 23/03/16.
6
-//  Copyright © 2016 Yonah Forst. All rights reserved.
7
-//
8
-
9
-#import "RCTConvert+RNPermissionsStatus.h"
10
-
11
-@implementation RCTConvert (RNPermissionsStatus)
12
-
13
-
14
-RCT_ENUM_CONVERTER(RNPermissionsStatus, (@{ @"StatusUndetermined" : @(RNPermissionsStatusUndetermined),
15
-                                             @"StatusDenied" : @(RNPermissionsStatusDenied),
16
-                                             @"StatusAuthorized" : @(RNPermissionsStatusAuthorized),
17
-                                             @"StatusRestricted" : @(RNPermissionsStatusRestricted)}),
18
-                   RNPermissionsStatusUndetermined, integerValue)
19
-
20
-
21
-@end

+ 25
- 24
ReactNativePermissions.ios.js Datei anzeigen

@@ -3,25 +3,6 @@
3 3
 var React = require('react-native');
4 4
 var RNPermissions = React.NativeModules.ReactNativePermissions;
5 5
 
6
-const permissionTypes = [
7
-	'location',
8
-	'camera',
9
-	'microphone',
10
-	'photo',
11
-	'contacts',
12
-	'event',
13
-	'reminder',
14
-	'bluetooth',
15
-	'notification',
16
-	'backgroundRefresh',
17
-]
18
-
19
-const permissionStatus = [
20
-	'undetermined',
21
-	'denied',
22
-	'authorized',
23
-	'restricted'
24
-]
25 6
 
26 7
 class ReactNativePermissions {
27 8
 	canOpenSettings() {
@@ -32,8 +13,12 @@ class ReactNativePermissions {
32 13
 		return RNPermissions.openSettings()
33 14
 	}
34 15
 
16
+	getPermissionTypes() {
17
+		return RNPermissions.PermissionTypes;
18
+	}
19
+
35 20
 	getPermissionStatus(permission) {
36
-		if (permissionTypes.includes(permission)) {
21
+		if (RNPermissions.PermissionTypes.includes(permission)) {
37 22
 			return RNPermissions.getPermissionStatus(permission)
38 23
 		} else {
39 24
 			return Promise.reject(`ReactNativePermissions: ${permission} is not a valid permission type`)
@@ -42,12 +27,28 @@ class ReactNativePermissions {
42 27
 
43 28
 	requestPermission(permission, type) {
44 29
 		switch (permission) {
45
-			case 'location':
30
+			case "location":
46 31
 				return RNPermissions.requestLocation(type)
47
-			case 'notification':
48
-				return RNPermissions.requestNotification(type)
49
-			case 'bluetooth':
32
+			case "camera":
33
+				return RNPermissions.requestCamera();
34
+			case "microphone":
35
+				return RNPermissions.requestMicrophone();
36
+			case "photo":
37
+				return RNPermissions.requestPhoto();
38
+			case "contacts":
39
+				return RNPermissions.requestContacts();
40
+			case "event":
41
+				return RNPermissions.requestEvent();
42
+			case "reminder":
43
+				return RNPermissions.requestReminder();
44
+			case "bluetooth":
50 45
 				return RNPermissions.requestBluetooth();
46
+			case "notification":
47
+				return RNPermissions.requestNotification(type)
48
+			case "backgroundRefresh":
49
+				return Promise.reject('You cannot request backgroundRefresh')
50
+			default:
51
+				return Promise.reject('invalid type: ' + type)
51 52
 		}
52 53
 	}
53 54
 

+ 119
- 41
ReactNativePermissions.m Datei anzeigen

@@ -14,12 +14,19 @@
14 14
 #import "RCTConvert.h"
15 15
 #import "RCTEventDispatcher.h"
16 16
 
17
-#import "RCTConvert+RNPermissionsStatus.h"
18
-
19
-#import "PermissionsChecker.h"
20
-#import "PermissionsAsker.h"
17
+#import "RNPLocation.h"
18
+#import "RNPBluetooth.h"
19
+#import "RNPNotification.h"
20
+#import "RNPAudioVideo.h"
21
+#import "RNPEvent.h"
22
+#import "RNPPhoto.h"
23
+#import "RNPContacts.h"
24
+#import "RNPBackgroundRefresh.h"
21 25
 
22 26
 @interface ReactNativePermissions()
27
+@property (strong, nonatomic) RNPLocation *locationMgr;
28
+@property (strong, nonatomic) RNPNotification *notificationMgr;
29
+@property (strong, nonatomic) RNPBluetooth *bluetoothMgr;
23 30
 @end
24 31
 
25 32
 @implementation ReactNativePermissions
@@ -40,41 +47,83 @@ RCT_EXPORT_MODULE();
40 47
 
41 48
 - (NSDictionary *)constantsToExport
42 49
 {
43
-    return @{ @"PermissionTypes" : @[
44
-                      @"location",
45
-                      @"camera",
46
-                      @"microphone",
47
-                      @"photo",
48
-                      @"contacts",
49
-                      @"event",
50
-                      @"reminder",
51
-                      @"bluetooth",
52
-                      @"notification",
53
-                      @"backgroundRefresh",
54
-                      ]};
50
+    return @{ @"PermissionTypes" : @[ @"location",
51
+                                      @"camera",
52
+                                      @"microphone",
53
+                                      @"photo",
54
+                                      @"contacts",
55
+                                      @"event",
56
+                                      @"reminder",
57
+                                      @"bluetooth",
58
+                                      @"notification",
59
+                                      @"backgroundRefresh" ]
60
+              };
55 61
 };
56 62
 
57 63
 
58 64
 RCT_REMAP_METHOD(canOpenSettings, canOpenSettings:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
59 65
 {
60
-    resolve(@([PermissionsChecker canOpenSettings]));
66
+    resolve(@(UIApplicationOpenSettingsURLString != nil));
61 67
 }
62 68
 
63 69
 RCT_EXPORT_METHOD(openSettings)
64 70
 {
65
-    [PermissionsChecker openSettings];
71
+    if (@(UIApplicationOpenSettingsURLString != nil)) {
72
+        NSURL *url = [NSURL URLWithString:UIApplicationOpenSettingsURLString];
73
+        [[UIApplication sharedApplication] openURL:url];
74
+    }
66 75
 }
67 76
 
68
-RCT_REMAP_METHOD(getPermissionStatus, getPermissionStatus:(NSString *)permission resolve:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
77
+RCT_REMAP_METHOD(getPermissionStatus, getPermissionStatus:(RNPType)type resolve:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
69 78
 {
70
-    SEL s = NSSelectorFromString(permission);
71
-    RNPermissionsStatus status = (RNPermissionsStatus)[PermissionsChecker performSelector:s];
72
-    resolve([self stringForStatus:status]);
79
+    NSString *status;
80
+    
81
+    switch (type) {
82
+            
83
+        case RNPTypeLocation:
84
+            status = [RNPLocation getStatus];
85
+            break;
86
+        case RNPTypeCamera:
87
+            status = [RNPAudioVideo getStatus:@"video"];
88
+            break;
89
+        case RNPTypeMicrophone:
90
+            status = [RNPAudioVideo getStatus:@"audio"];
91
+            break;
92
+        case RNPTypePhoto:
93
+            status = [RNPPhoto getStatus];
94
+            break;
95
+        case RNPTypeContacts:
96
+            status = [RNPContacts getStatus];
97
+            break;
98
+        case RNPTypeEvent:
99
+            status = [RNPEvent getStatus:@"event"];
100
+            break;
101
+        case RNPTypeReminder:
102
+            status = [RNPEvent getStatus:@"reminder"];
103
+            break;
104
+        case RNPTypeBluetooth:
105
+            status = [RNPBluetooth getStatus];
106
+            break;
107
+        case RNPTypeNotification:
108
+            status = [RNPNotification getStatus];
109
+            break;
110
+        case RNPTypeBackgroundRefresh:
111
+            status = [RNPBackgroundRefresh getStatus];
112
+            break;
113
+        default:
114
+            break;
115
+    }
116
+
117
+    resolve(status);
73 118
 }
74 119
 
75
-RCT_EXPORT_METHOD(requestLocation:(NSString *)type)
120
+RCT_REMAP_METHOD(requestLocation, requestLocation:(NSString *)type resolve:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
76 121
 {
77
-    [[PermissionsAsker sharedInstance] location:type];
122
+    if (self.locationMgr == nil) {
123
+        self.locationMgr = [[RNPLocation alloc] init];
124
+    }
125
+    
126
+    [self.locationMgr request:type completionHandler:resolve];
78 127
 }
79 128
 
80 129
 RCT_REMAP_METHOD(requestNotification, requestNotification:(NSArray *)typeStrings resolve:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
@@ -88,30 +137,59 @@ RCT_REMAP_METHOD(requestNotification, requestNotification:(NSArray *)typeStrings
88 137
     
89 138
     if ([typeStrings containsObject:@"sound"])
90 139
         types = types | UIUserNotificationTypeSound;
140
+    
141
+    
142
+    if (self.notificationMgr == nil) {
143
+        self.notificationMgr = [[RNPNotification alloc] init];
144
+    }
145
+    
146
+    [self.notificationMgr request:types completionHandler:resolve];
91 147
 
92
-    [[PermissionsAsker sharedInstance] notification:types completionHandler:^(RNPermissionsStatus status) {
93
-        resolve([self stringForStatus:status]);
94
-    }];
95 148
 }
96 149
 
97 150
 
151
+RCT_REMAP_METHOD(requestBluetooth, requestBluetooth:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
152
+{
153
+    if (self.bluetoothMgr == nil) {
154
+        self.bluetoothMgr = [[RNPBluetooth alloc] init];
155
+    }
156
+    
157
+    [self.bluetoothMgr request:resolve];
158
+}
98 159
 
99
-RCT_EXPORT_METHOD(requestBluetooth) {
100
-    [[PermissionsAsker sharedInstance] bluetooth];
160
+RCT_REMAP_METHOD(requestCamera, requestCamera:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
161
+{
162
+    [RNPAudioVideo request:@"video" completionHandler:resolve];
101 163
 }
102 164
 
103
-- (NSString *)stringForStatus:(RNPermissionsStatus) status{
104
-    switch (status) {
105
-        case RNPermissionsStatusAuthorized:
106
-            return @"authorized";
107
-        case RNPermissionsStatusDenied:
108
-            return @"denied";
109
-        case RNPermissionsStatusRestricted:
110
-            return @"restricted";
111
-        case RNPermissionsStatusUndetermined:
112
-        default:
113
-            return @"undetermined";
114
-    }
165
+RCT_REMAP_METHOD(requestMicrophone, requestMicrophone:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
166
+{
167
+    [RNPAudioVideo request:@"audio" completionHandler:resolve];
168
+}
169
+
170
+RCT_REMAP_METHOD(requestEvent, requestEvents:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
171
+{
172
+    [RNPEvent request:@"event" completionHandler:resolve];
173
+}
174
+
175
+RCT_REMAP_METHOD(requestReminder, requestReminders:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
176
+{
177
+    [RNPEvent request:@"reminder" completionHandler:resolve];
178
+}
179
+
180
+RCT_REMAP_METHOD(requestPhoto, requestPhoto:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
181
+{
182
+    [RNPPhoto request:resolve];
115 183
 }
116 184
 
185
+RCT_REMAP_METHOD(requestContacts, requestContacts:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
186
+{
187
+    [RNPContacts request:resolve];
188
+}
189
+
190
+
191
+
192
+
193
+
194
+
117 195
 @end

+ 63
- 19
ReactNativePermissions.xcodeproj/project.pbxproj Datei anzeigen

@@ -7,10 +7,16 @@
7 7
 	objects = {
8 8
 
9 9
 /* Begin PBXBuildFile section */
10
-		9D6F44381D2E604500BF17F4 /* PermissionsAsker.m in Sources */ = {isa = PBXBuildFile; fileRef = 9D6F44371D2E604500BF17F4 /* PermissionsAsker.m */; };
11
-		9D8FB2701D2D68C500AAFC55 /* PermissionsChecker.m in Sources */ = {isa = PBXBuildFile; fileRef = 9D8FB26F1D2D68C500AAFC55 /* PermissionsChecker.m */; };
10
+		9D46283E1D34719100346A5B /* RNPAudioVideo.m in Sources */ = {isa = PBXBuildFile; fileRef = 9D46282F1D34719100346A5B /* RNPAudioVideo.m */; };
11
+		9D46283F1D34719100346A5B /* RNPBackgroundRefresh.m in Sources */ = {isa = PBXBuildFile; fileRef = 9D4628311D34719100346A5B /* RNPBackgroundRefresh.m */; };
12
+		9D4628401D34719100346A5B /* RNPBluetooth.m in Sources */ = {isa = PBXBuildFile; fileRef = 9D4628331D34719100346A5B /* RNPBluetooth.m */; };
13
+		9D4628411D34719100346A5B /* RNPContacts.m in Sources */ = {isa = PBXBuildFile; fileRef = 9D4628351D34719100346A5B /* RNPContacts.m */; };
14
+		9D4628421D34719100346A5B /* RNPEvent.m in Sources */ = {isa = PBXBuildFile; fileRef = 9D4628371D34719100346A5B /* RNPEvent.m */; };
15
+		9D4628431D34719100346A5B /* RNPLocation.m in Sources */ = {isa = PBXBuildFile; fileRef = 9D4628391D34719100346A5B /* RNPLocation.m */; };
16
+		9D4628441D34719100346A5B /* RNPNotification.m in Sources */ = {isa = PBXBuildFile; fileRef = 9D46283B1D34719100346A5B /* RNPNotification.m */; };
17
+		9D4628451D34719100346A5B /* RNPPhoto.m in Sources */ = {isa = PBXBuildFile; fileRef = 9D46283D1D34719100346A5B /* RNPPhoto.m */; };
12 18
 		9DE8D2821CA3188D009CE8CC /* ReactNativePermissions.m in Sources */ = {isa = PBXBuildFile; fileRef = 9DE8D2811CA3188D009CE8CC /* ReactNativePermissions.m */; };
13
-		9DE8D28B1CA31E95009CE8CC /* RCTConvert+RNPermissionsStatus.m in Sources */ = {isa = PBXBuildFile; fileRef = 9DE8D28A1CA31E95009CE8CC /* RCTConvert+RNPermissionsStatus.m */; };
19
+		9DE8D28B1CA31E95009CE8CC /* RCTConvert+RNPStatus.m in Sources */ = {isa = PBXBuildFile; fileRef = 9DE8D28A1CA31E95009CE8CC /* RCTConvert+RNPStatus.m */; };
14 20
 /* End PBXBuildFile section */
15 21
 
16 22
 /* Begin PBXCopyFilesBuildPhase section */
@@ -27,14 +33,26 @@
27 33
 
28 34
 /* Begin PBXFileReference section */
29 35
 		9D23B34F1C767B80008B4819 /* libReactNativePermissions.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libReactNativePermissions.a; sourceTree = BUILT_PRODUCTS_DIR; };
30
-		9D6F44361D2E604500BF17F4 /* PermissionsAsker.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PermissionsAsker.h; sourceTree = SOURCE_ROOT; };
31
-		9D6F44371D2E604500BF17F4 /* PermissionsAsker.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PermissionsAsker.m; sourceTree = SOURCE_ROOT; };
32
-		9D8FB26E1D2D68C500AAFC55 /* PermissionsChecker.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PermissionsChecker.h; sourceTree = SOURCE_ROOT; };
33
-		9D8FB26F1D2D68C500AAFC55 /* PermissionsChecker.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PermissionsChecker.m; sourceTree = SOURCE_ROOT; };
36
+		9D46282E1D34719100346A5B /* RNPAudioVideo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RNPAudioVideo.h; path = permissions/RNPAudioVideo.h; sourceTree = SOURCE_ROOT; };
37
+		9D46282F1D34719100346A5B /* RNPAudioVideo.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RNPAudioVideo.m; path = permissions/RNPAudioVideo.m; sourceTree = SOURCE_ROOT; };
38
+		9D4628301D34719100346A5B /* RNPBackgroundRefresh.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RNPBackgroundRefresh.h; path = permissions/RNPBackgroundRefresh.h; sourceTree = SOURCE_ROOT; };
39
+		9D4628311D34719100346A5B /* RNPBackgroundRefresh.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RNPBackgroundRefresh.m; path = permissions/RNPBackgroundRefresh.m; sourceTree = SOURCE_ROOT; };
40
+		9D4628321D34719100346A5B /* RNPBluetooth.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RNPBluetooth.h; path = permissions/RNPBluetooth.h; sourceTree = SOURCE_ROOT; };
41
+		9D4628331D34719100346A5B /* RNPBluetooth.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RNPBluetooth.m; path = permissions/RNPBluetooth.m; sourceTree = SOURCE_ROOT; };
42
+		9D4628341D34719100346A5B /* RNPContacts.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RNPContacts.h; path = permissions/RNPContacts.h; sourceTree = SOURCE_ROOT; };
43
+		9D4628351D34719100346A5B /* RNPContacts.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RNPContacts.m; path = permissions/RNPContacts.m; sourceTree = SOURCE_ROOT; };
44
+		9D4628361D34719100346A5B /* RNPEvent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RNPEvent.h; path = permissions/RNPEvent.h; sourceTree = SOURCE_ROOT; };
45
+		9D4628371D34719100346A5B /* RNPEvent.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RNPEvent.m; path = permissions/RNPEvent.m; sourceTree = SOURCE_ROOT; };
46
+		9D4628381D34719100346A5B /* RNPLocation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RNPLocation.h; path = permissions/RNPLocation.h; sourceTree = SOURCE_ROOT; };
47
+		9D4628391D34719100346A5B /* RNPLocation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RNPLocation.m; path = permissions/RNPLocation.m; sourceTree = SOURCE_ROOT; };
48
+		9D46283A1D34719100346A5B /* RNPNotification.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RNPNotification.h; path = permissions/RNPNotification.h; sourceTree = SOURCE_ROOT; };
49
+		9D46283B1D34719100346A5B /* RNPNotification.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RNPNotification.m; path = permissions/RNPNotification.m; sourceTree = SOURCE_ROOT; };
50
+		9D46283C1D34719100346A5B /* RNPPhoto.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RNPPhoto.h; path = permissions/RNPPhoto.h; sourceTree = SOURCE_ROOT; };
51
+		9D46283D1D34719100346A5B /* RNPPhoto.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RNPPhoto.m; path = permissions/RNPPhoto.m; sourceTree = SOURCE_ROOT; };
34 52
 		9DE8D2801CA31888009CE8CC /* ReactNativePermissions.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ReactNativePermissions.h; sourceTree = SOURCE_ROOT; };
35
-		9DE8D2811CA3188D009CE8CC /* ReactNativePermissions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ReactNativePermissions.m; sourceTree = SOURCE_ROOT; };
36
-		9DE8D2891CA31E95009CE8CC /* RCTConvert+RNPermissionsStatus.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "RCTConvert+RNPermissionsStatus.h"; sourceTree = SOURCE_ROOT; };
37
-		9DE8D28A1CA31E95009CE8CC /* RCTConvert+RNPermissionsStatus.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "RCTConvert+RNPermissionsStatus.m"; sourceTree = SOURCE_ROOT; };
53
+		9DE8D2811CA3188D009CE8CC /* ReactNativePermissions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = ReactNativePermissions.m; sourceTree = SOURCE_ROOT; xcLanguageSpecificationIdentifier = xcode.lang.objc; };
54
+		9DE8D2891CA31E95009CE8CC /* RCTConvert+RNPStatus.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = "RCTConvert+RNPStatus.h"; sourceTree = SOURCE_ROOT; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; };
55
+		9DE8D28A1CA31E95009CE8CC /* RCTConvert+RNPStatus.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = "RCTConvert+RNPStatus.m"; sourceTree = SOURCE_ROOT; xcLanguageSpecificationIdentifier = xcode.lang.objc; };
38 56
 /* End PBXFileReference section */
39 57
 
40 58
 /* Begin PBXFrameworksBuildPhase section */
@@ -67,18 +85,38 @@
67 85
 		9D23B3511C767B80008B4819 /* ReactNativePermissions */ = {
68 86
 			isa = PBXGroup;
69 87
 			children = (
70
-				9DE8D2891CA31E95009CE8CC /* RCTConvert+RNPermissionsStatus.h */,
71
-				9DE8D28A1CA31E95009CE8CC /* RCTConvert+RNPermissionsStatus.m */,
72
-				9D8FB26E1D2D68C500AAFC55 /* PermissionsChecker.h */,
73
-				9D8FB26F1D2D68C500AAFC55 /* PermissionsChecker.m */,
74
-				9D6F44361D2E604500BF17F4 /* PermissionsAsker.h */,
75
-				9D6F44371D2E604500BF17F4 /* PermissionsAsker.m */,
88
+				9D4628091D33C1EC00346A5B /* permissions */,
89
+				9DE8D2891CA31E95009CE8CC /* RCTConvert+RNPStatus.h */,
90
+				9DE8D28A1CA31E95009CE8CC /* RCTConvert+RNPStatus.m */,
76 91
 				9DE8D2801CA31888009CE8CC /* ReactNativePermissions.h */,
77 92
 				9DE8D2811CA3188D009CE8CC /* ReactNativePermissions.m */,
78 93
 			);
79 94
 			path = ReactNativePermissions;
80 95
 			sourceTree = "<group>";
81 96
 		};
97
+		9D4628091D33C1EC00346A5B /* permissions */ = {
98
+			isa = PBXGroup;
99
+			children = (
100
+				9D46282E1D34719100346A5B /* RNPAudioVideo.h */,
101
+				9D46282F1D34719100346A5B /* RNPAudioVideo.m */,
102
+				9D4628301D34719100346A5B /* RNPBackgroundRefresh.h */,
103
+				9D4628311D34719100346A5B /* RNPBackgroundRefresh.m */,
104
+				9D4628321D34719100346A5B /* RNPBluetooth.h */,
105
+				9D4628331D34719100346A5B /* RNPBluetooth.m */,
106
+				9D4628341D34719100346A5B /* RNPContacts.h */,
107
+				9D4628351D34719100346A5B /* RNPContacts.m */,
108
+				9D4628361D34719100346A5B /* RNPEvent.h */,
109
+				9D4628371D34719100346A5B /* RNPEvent.m */,
110
+				9D4628381D34719100346A5B /* RNPLocation.h */,
111
+				9D4628391D34719100346A5B /* RNPLocation.m */,
112
+				9D46283A1D34719100346A5B /* RNPNotification.h */,
113
+				9D46283B1D34719100346A5B /* RNPNotification.m */,
114
+				9D46283C1D34719100346A5B /* RNPPhoto.h */,
115
+				9D46283D1D34719100346A5B /* RNPPhoto.m */,
116
+			);
117
+			name = permissions;
118
+			sourceTree = "<group>";
119
+		};
82 120
 /* End PBXGroup section */
83 121
 
84 122
 /* Begin PBXNativeTarget section */
@@ -135,10 +173,16 @@
135 173
 			isa = PBXSourcesBuildPhase;
136 174
 			buildActionMask = 2147483647;
137 175
 			files = (
138
-				9DE8D28B1CA31E95009CE8CC /* RCTConvert+RNPermissionsStatus.m in Sources */,
139
-				9D8FB2701D2D68C500AAFC55 /* PermissionsChecker.m in Sources */,
176
+				9D46283F1D34719100346A5B /* RNPBackgroundRefresh.m in Sources */,
177
+				9D4628451D34719100346A5B /* RNPPhoto.m in Sources */,
178
+				9D4628431D34719100346A5B /* RNPLocation.m in Sources */,
179
+				9D46283E1D34719100346A5B /* RNPAudioVideo.m in Sources */,
180
+				9D4628401D34719100346A5B /* RNPBluetooth.m in Sources */,
181
+				9DE8D28B1CA31E95009CE8CC /* RCTConvert+RNPStatus.m in Sources */,
182
+				9D4628421D34719100346A5B /* RNPEvent.m in Sources */,
140 183
 				9DE8D2821CA3188D009CE8CC /* ReactNativePermissions.m in Sources */,
141
-				9D6F44381D2E604500BF17F4 /* PermissionsAsker.m in Sources */,
184
+				9D4628411D34719100346A5B /* RNPContacts.m in Sources */,
185
+				9D4628441D34719100346A5B /* RNPNotification.m in Sources */,
142 186
 			);
143 187
 			runOnlyForDeploymentPostprocessing = 0;
144 188
 		};

+ 17
- 0
permissions/RNPAudioVideo.h Datei anzeigen

@@ -0,0 +1,17 @@
1
+//
2
+//  RNPAudioVideo.h
3
+//  ReactNativePermissions
4
+//
5
+//  Created by Yonah Forst on 11/07/16.
6
+//  Copyright © 2016 Yonah Forst. All rights reserved.
7
+//
8
+
9
+#import <Foundation/Foundation.h>
10
+#import "RCTConvert+RNPStatus.h"
11
+
12
+@interface RNPAudioVideo : NSObject
13
+
14
++ (NSString *)getStatus:(NSString *)type;
15
++ (void)request:(NSString *)type completionHandler:(void (^)(NSString *))completionHandler;
16
+
17
+@end

+ 48
- 0
permissions/RNPAudioVideo.m Datei anzeigen

@@ -0,0 +1,48 @@
1
+//
2
+//  RNPCamera.m
3
+//  ReactNativePermissions
4
+//
5
+//  Created by Yonah Forst on 11/07/16.
6
+//  Copyright © 2016 Yonah Forst. All rights reserved.
7
+//
8
+
9
+#import "RNPAudioVideo.h"
10
+
11
+#import <AVFoundation/AVFoundation.h>
12
+
13
+@implementation RNPAudioVideo
14
+
15
++ (NSString *)getStatus:(NSString *)type
16
+{
17
+    int status = [AVCaptureDevice authorizationStatusForMediaType:[self typeFromString:type]];
18
+    switch (status) {
19
+        case AVAuthorizationStatusAuthorized:
20
+            return RNPStatusAuthorized;
21
+        case AVAuthorizationStatusDenied:
22
+            return RNPStatusDenied;
23
+        case AVAuthorizationStatusRestricted:
24
+            return RNPStatusRestricted;
25
+        default:
26
+            return RNPStatusUndetermined;
27
+    }
28
+}
29
+
30
++ (void)request:(NSString *)type completionHandler:(void (^)(NSString *))completionHandler
31
+{
32
+    [AVCaptureDevice requestAccessForMediaType:[self typeFromString:type]
33
+                             completionHandler:^(BOOL granted) {
34
+                                 dispatch_async(dispatch_get_main_queue(), ^{
35
+                                     completionHandler([RNPAudioVideo getStatus:type]);
36
+                                 });
37
+                             }];
38
+}
39
+
40
++ (NSString *)typeFromString:(NSString *)string {
41
+    if ([string isEqualToString:@"audio"]) {
42
+        return AVMediaTypeAudio;
43
+    } else {
44
+        return AVMediaTypeVideo;
45
+    }
46
+}
47
+
48
+@end

+ 16
- 0
permissions/RNPBackgroundRefresh.h Datei anzeigen

@@ -0,0 +1,16 @@
1
+//
2
+//  RNPBackgroundRefresh.h
3
+//  ReactNativePermissions
4
+//
5
+//  Created by Yonah Forst on 11/07/16.
6
+//  Copyright © 2016 Yonah Forst. All rights reserved.
7
+//
8
+
9
+#import <Foundation/Foundation.h>
10
+#import "RCTConvert+RNPStatus.h"
11
+
12
+@interface RNPBackgroundRefresh : NSObject
13
+
14
++ (NSString *)getStatus;
15
+
16
+@end

+ 28
- 0
permissions/RNPBackgroundRefresh.m Datei anzeigen

@@ -0,0 +1,28 @@
1
+//
2
+//  RNPBackgroundRefresh.m
3
+//  ReactNativePermissions
4
+//
5
+//  Created by Yonah Forst on 11/07/16.
6
+//  Copyright © 2016 Yonah Forst. All rights reserved.
7
+//
8
+
9
+#import "RNPBackgroundRefresh.h"
10
+
11
+@implementation RNPBackgroundRefresh
12
+
13
++(NSString *)getStatus
14
+{
15
+    int status = [[UIApplication sharedApplication] backgroundRefreshStatus];
16
+    switch (status) {
17
+        case UIBackgroundRefreshStatusAvailable:
18
+            return RNPStatusAuthorized;
19
+        case UIBackgroundRefreshStatusDenied:
20
+            return RNPStatusDenied;
21
+        case UIBackgroundRefreshStatusRestricted:
22
+            return RNPStatusRestricted;
23
+        default:
24
+            return RNPStatusUndetermined;
25
+    }
26
+
27
+}
28
+@end

+ 17
- 0
permissions/RNPBluetooth.h Datei anzeigen

@@ -0,0 +1,17 @@
1
+//
2
+//  RNPBluetooth.h
3
+//  ReactNativePermissions
4
+//
5
+//  Created by Yonah Forst on 11/07/16.
6
+//  Copyright © 2016 Yonah Forst. All rights reserved.
7
+//
8
+
9
+#import <Foundation/Foundation.h>
10
+#import "RCTConvert+RNPStatus.h"
11
+
12
+@interface RNPBluetooth : NSObject
13
+
14
++ (NSString *)getStatus;
15
+- (void)request:(void (^)(NSString *))completionHandler;
16
+
17
+@end

+ 66
- 0
permissions/RNPBluetooth.m Datei anzeigen

@@ -0,0 +1,66 @@
1
+//
2
+//  RNPBluetooth.m
3
+//  ReactNativePermissions
4
+//
5
+//  Created by Yonah Forst on 11/07/16.
6
+//  Copyright © 2016 Yonah Forst. All rights reserved.
7
+//
8
+
9
+#import "RNPBluetooth.h"
10
+#import <CoreBluetooth/CoreBluetooth.h>
11
+
12
+@interface RNPBluetooth() <CBPeripheralDelegate>
13
+@property (strong, nonatomic) CBPeripheralManager* peripheralManager;
14
+@property (copy) void (^completionHandler)(NSString *);
15
+@end
16
+
17
+@implementation RNPBluetooth
18
+
19
++ (NSString *)getStatus
20
+{
21
+    int status = [CBPeripheralManager authorizationStatus];
22
+    switch (status) {
23
+        case CBPeripheralManagerAuthorizationStatusAuthorized:
24
+            return RNPStatusAuthorized;
25
+        case CBPeripheralManagerAuthorizationStatusDenied:
26
+            return RNPStatusDenied;
27
+        case CBPeripheralManagerAuthorizationStatusRestricted:
28
+            return RNPStatusRestricted;
29
+        default:
30
+            return RNPStatusUndetermined;
31
+    }
32
+}
33
+
34
+- (void)request:(void (^)(NSString *))completionHandler
35
+{
36
+    NSString *status = [RNPBluetooth getStatus];
37
+    
38
+    if (status == RNPStatusUndetermined) {
39
+        self.completionHandler = completionHandler;
40
+        
41
+        self.peripheralManager = [[CBPeripheralManager alloc] initWithDelegate:self queue:nil];
42
+        [self.peripheralManager startAdvertising:@{}];
43
+    } else {
44
+        completionHandler(status);
45
+    }
46
+}
47
+
48
+- (void) peripheralManagerDidUpdateState:(CBPeripheralManager *)peripheralManager
49
+{
50
+    if (self.peripheralManager) {
51
+        [self.peripheralManager stopAdvertising];
52
+        self.peripheralManager.delegate = nil;
53
+        self.peripheralManager = nil;
54
+    }
55
+    
56
+    if (self.completionHandler) {
57
+        //for some reason, checking permission right away returns denied. need to wait a tiny bit
58
+        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.1 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
59
+            self.completionHandler([self.class getStatus]);
60
+            self.completionHandler = nil;
61
+        });
62
+    }
63
+    
64
+}
65
+
66
+@end

+ 17
- 0
permissions/RNPContacts.h Datei anzeigen

@@ -0,0 +1,17 @@
1
+//
2
+//  RNPContacts.h
3
+//  ReactNativePermissions
4
+//
5
+//  Created by Yonah Forst on 11/07/16.
6
+//  Copyright © 2016 Yonah Forst. All rights reserved.
7
+//
8
+
9
+#import <Foundation/Foundation.h>
10
+#import "RCTConvert+RNPStatus.h"
11
+
12
+@interface RNPContacts : NSObject
13
+
14
++ (NSString *)getStatus;
15
++ (void)request:(void (^)(NSString *))completionHandler;
16
+
17
+@end

+ 66
- 0
permissions/RNPContacts.m Datei anzeigen

@@ -0,0 +1,66 @@
1
+//
2
+//  RNPContacts.m
3
+//  ReactNativePermissions
4
+//
5
+//  Created by Yonah Forst on 11/07/16.
6
+//  Copyright © 2016 Yonah Forst. All rights reserved.
7
+//
8
+
9
+#import "RNPContacts.h"
10
+#import <AddressBook/AddressBook.h>
11
+
12
+#if __IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_9_0
13
+@import Contacts;
14
+#endif
15
+
16
+@implementation RNPContacts
17
+
18
++ (NSString *)getStatus
19
+{
20
+#if __IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_9_0
21
+    int status = [CNContactStore authorizationStatusForEntityType:CNEntityTypeContacts];
22
+    switch (status) {
23
+        case CNAuthorizationStatusAuthorized:
24
+            return RNPStatusAuthorized;
25
+        case CNAuthorizationStatusDenied:
26
+            return RNPStatusDenied;
27
+        case CNAuthorizationStatusRestricted:
28
+            return RNPStatusRestricted;
29
+        default:
30
+            return RNPStatusUndetermined;
31
+    }
32
+#else
33
+    int status = ABAddressBookGetAuthorizationStatus();
34
+    switch (status) {
35
+        case kABAuthorizationStatusAuthorized:
36
+            return RNPStatusAuthorized;
37
+        case kABAuthorizationStatusDenied:
38
+            return RNPStatusDenied;
39
+        case kABAuthorizationStatusRestricted:
40
+            return RNPStatusRestricted;
41
+        default:
42
+            return RNPStatusUndetermined;
43
+    }
44
+#endif
45
+}
46
+
47
++ (void)request:(void (^)(NSString *))completionHandler
48
+{
49
+    void (^handler)(BOOL, NSError * _Nullable) =  ^(BOOL granted, NSError * _Nullable error) {
50
+        dispatch_async(dispatch_get_main_queue(), ^{
51
+            completionHandler([self.class getStatus]);
52
+        });
53
+    };
54
+    
55
+    
56
+#if __IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_9_0
57
+    CNContactStore *contactStore = [[CNContactStore alloc] init];
58
+    [contactStore requestAccessForEntityType:CNEntityTypeContacts completionHandler:handler];
59
+#else
60
+    CFErrorRef error = nil;
61
+    ABAddressBookRef addressBook = ABAddressBookCreateWithOptions(nil, &error);
62
+    ABAddressBookRequestAccessWithCompletion(addressBook, completionHandler);
63
+#endif
64
+}
65
+
66
+@end

+ 17
- 0
permissions/RNPEvent.h Datei anzeigen

@@ -0,0 +1,17 @@
1
+//
2
+//  RNPEvent.h
3
+//  ReactNativePermissions
4
+//
5
+//  Created by Yonah Forst on 11/07/16.
6
+//  Copyright © 2016 Yonah Forst. All rights reserved.
7
+//
8
+
9
+#import <Foundation/Foundation.h>
10
+#import "RCTConvert+RNPStatus.h"
11
+
12
+@interface RNPEvent : NSObject
13
+
14
++ (NSString *)getStatus:(NSString *)type;
15
++ (void)request:(NSString *)type completionHandler:(void (^)(NSString *))completionHandler;
16
+
17
+@end

+ 48
- 0
permissions/RNPEvent.m Datei anzeigen

@@ -0,0 +1,48 @@
1
+//
2
+//  RNPEvent.m
3
+//  ReactNativePermissions
4
+//
5
+//  Created by Yonah Forst on 11/07/16.
6
+//  Copyright © 2016 Yonah Forst. All rights reserved.
7
+//
8
+
9
+#import "RNPEvent.h"
10
+#import <EventKit/EventKit.h>
11
+
12
+@implementation RNPEvent
13
+
14
++ (NSString *)getStatus:(NSString *)type
15
+{
16
+    int status = [EKEventStore authorizationStatusForEntityType:[self typeFromString:type]];
17
+
18
+    switch (status) {
19
+        case EKAuthorizationStatusAuthorized:
20
+            return RNPStatusAuthorized;
21
+        case EKAuthorizationStatusDenied:
22
+            return RNPStatusDenied;
23
+        case EKAuthorizationStatusRestricted:
24
+            return RNPStatusRestricted;
25
+        default:
26
+            return RNPStatusUndetermined;
27
+    }
28
+}
29
+
30
++ (void)request:(NSString *)type completionHandler:(void (^)(NSString *))completionHandler
31
+{
32
+    EKEventStore *aStore = [[EKEventStore alloc] init];
33
+    [aStore requestAccessToEntityType:[self typeFromString:type] completion:^(BOOL granted, NSError *error) {
34
+        dispatch_async(dispatch_get_main_queue(), ^{
35
+            completionHandler([self getStatus:type]);
36
+        });
37
+    }];
38
+}
39
+
40
++(EKEntityType)typeFromString:(NSString *)string {
41
+    if ([string isEqualToString:@"reminder"]) {
42
+        return EKEntityTypeReminder;
43
+    } else {
44
+        return EKEntityTypeEvent;
45
+    }
46
+}
47
+
48
+@end

+ 17
- 0
permissions/RNPLocation.h Datei anzeigen

@@ -0,0 +1,17 @@
1
+//
2
+//  RNPLocation.h
3
+//  ReactNativePermissions
4
+//
5
+//  Created by Yonah Forst on 11/07/16.
6
+//  Copyright © 2016 Yonah Forst. All rights reserved.
7
+//
8
+
9
+#import <Foundation/Foundation.h>
10
+#import "RCTConvert+RNPStatus.h"
11
+
12
+@interface RNPLocation : NSObject
13
+
14
++ (NSString *)getStatus;
15
+- (void)request:(NSString *)type completionHandler:(void (^)(NSString *))completionHandler;
16
+
17
+@end

+ 68
- 0
permissions/RNPLocation.m Datei anzeigen

@@ -0,0 +1,68 @@
1
+//
2
+//  RNPLocation.m
3
+//  ReactNativePermissions
4
+//
5
+//  Created by Yonah Forst on 11/07/16.
6
+//  Copyright © 2016 Yonah Forst. All rights reserved.
7
+//
8
+
9
+#import "RNPLocation.h"
10
+#import <CoreLocation/CoreLocation.h>
11
+
12
+
13
+@interface RNPLocation() <CLLocationManagerDelegate>
14
+@property (strong, nonatomic) CLLocationManager* locationManager;
15
+@property (copy) void (^completionHandler)(NSString *);
16
+@end
17
+
18
+@implementation RNPLocation
19
+
20
++ (NSString *)getStatus
21
+{
22
+    int status = [CLLocationManager authorizationStatus];
23
+    switch (status) {
24
+        case kCLAuthorizationStatusAuthorizedAlways:
25
+        case kCLAuthorizationStatusAuthorizedWhenInUse:
26
+            return RNPStatusAuthorized;
27
+        case kCLAuthorizationStatusDenied:
28
+            return RNPStatusDenied;
29
+        case kCLAuthorizationStatusRestricted:
30
+            return RNPStatusRestricted;
31
+        default:
32
+            return RNPStatusUndetermined;
33
+    }
34
+}
35
+
36
+- (void)request:(NSString*)type completionHandler:(void (^)(NSString *))completionHandler
37
+{
38
+    NSString *status = [RNPLocation getStatus];
39
+    if (status == RNPStatusUndetermined) {
40
+        self.completionHandler = completionHandler;
41
+        
42
+        self.locationManager = [[CLLocationManager alloc] init];
43
+        self.locationManager.delegate = self;
44
+        if ([type isEqualToString:@"always"]) {
45
+            [self.locationManager requestAlwaysAuthorization];
46
+        } else {
47
+            [self.locationManager requestWhenInUseAuthorization];
48
+        }
49
+    } else {
50
+        completionHandler(status);
51
+    }
52
+}
53
+
54
+-(void)locationManager:(CLLocationManager *)manager didChangeAuthorizationStatus:(CLAuthorizationStatus)status {
55
+    if (self.locationManager) {
56
+        self.locationManager.delegate = nil;
57
+        self.locationManager = nil;
58
+    }
59
+    
60
+    if (self.completionHandler) {
61
+        //for some reason, checking permission right away returns denied. need to wait a tiny bit
62
+        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.1 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
63
+            self.completionHandler([RNPLocation getStatus]);
64
+            self.completionHandler = nil;
65
+        });
66
+    }
67
+}
68
+@end

+ 17
- 0
permissions/RNPNotification.h Datei anzeigen

@@ -0,0 +1,17 @@
1
+//
2
+//  RNPNotification.h
3
+//  ReactNativePermissions
4
+//
5
+//  Created by Yonah Forst on 11/07/16.
6
+//  Copyright © 2016 Yonah Forst. All rights reserved.
7
+//
8
+
9
+#import <Foundation/Foundation.h>
10
+#import "RCTConvert+RNPStatus.h"
11
+
12
+@interface RNPNotification : NSObject
13
+
14
++ (NSString *)getStatus;
15
+- (void)request:(UIUserNotificationType)types completionHandler:(void (^)(NSString*))completionHandler;
16
+
17
+@end

+ 89
- 0
permissions/RNPNotification.m Datei anzeigen

@@ -0,0 +1,89 @@
1
+//
2
+//  RNPNotification.m
3
+//  ReactNativePermissions
4
+//
5
+//  Created by Yonah Forst on 11/07/16.
6
+//  Copyright © 2016 Yonah Forst. All rights reserved.
7
+//
8
+
9
+#import "RNPNotification.h"
10
+
11
+static NSString* RNPDidAskForNotification = @"RNPDidAskForNotification";
12
+
13
+@interface RNPNotification()
14
+@property (copy) void (^completionHandler)(NSString*);
15
+@end
16
+
17
+@implementation RNPNotification
18
+
19
++ (NSString *)getStatus
20
+{
21
+    BOOL didAskForPermission = [[NSUserDefaults standardUserDefaults] boolForKey:RNPDidAskForNotification];
22
+    
23
+    if (didAskForPermission) {
24
+        if ([[UIApplication sharedApplication] respondsToSelector:@selector(isRegisteredForRemoteNotifications)]) {
25
+            // iOS8+
26
+            BOOL isRegistered = [[UIApplication sharedApplication] isRegisteredForRemoteNotifications];
27
+            BOOL isEnabled = [[[UIApplication sharedApplication] currentUserNotificationSettings] types] != UIUserNotificationTypeNone;
28
+            if (isRegistered || isEnabled) {
29
+                return isEnabled ? RNPStatusAuthorized : RNPStatusDenied;
30
+            }
31
+            else {
32
+                return RNPStatusDenied;
33
+            }
34
+        } else {
35
+            if ([[UIApplication sharedApplication] enabledRemoteNotificationTypes] == UIRemoteNotificationTypeNone) {
36
+                return RNPStatusDenied;
37
+            }
38
+            else {
39
+                return RNPStatusAuthorized;
40
+            }
41
+        }
42
+    } else {
43
+        return RNPStatusUndetermined;
44
+    }
45
+}
46
+
47
+- (void)request:(UIUserNotificationType)types completionHandler:(void (^)(NSString*))completionHandler
48
+{
49
+    BOOL didAskForPermission = [[NSUserDefaults standardUserDefaults] boolForKey:RNPDidAskForNotification];
50
+    if (!didAskForPermission) {
51
+        self.completionHandler = completionHandler;
52
+        
53
+        [[NSNotificationCenter defaultCenter] addObserver:self
54
+                                                 selector:@selector(applicationDidBecomeActive)
55
+                                                     name:UIApplicationDidBecomeActiveNotification
56
+                                                   object:nil];
57
+        
58
+        if ([[UIApplication sharedApplication] respondsToSelector:@selector(isRegisteredForRemoteNotifications)]) {
59
+            // iOS8+
60
+            UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:types categories:nil];
61
+            [[UIApplication sharedApplication] registerUserNotificationSettings:settings];
62
+            [[UIApplication sharedApplication] registerForRemoteNotifications];
63
+        } else {
64
+            [[UIApplication sharedApplication] registerForRemoteNotificationTypes:(UIRemoteNotificationType)types];
65
+        }
66
+        
67
+        [[NSUserDefaults standardUserDefaults] setBool:YES forKey:RNPDidAskForNotification];
68
+        [[NSUserDefaults standardUserDefaults] synchronize];
69
+    } else {
70
+        completionHandler([self.class getStatus]);
71
+    }
72
+}
73
+
74
+- (void)applicationDidBecomeActive
75
+{
76
+    [[NSNotificationCenter defaultCenter] removeObserver:self
77
+                                                    name:UIApplicationDidBecomeActiveNotification
78
+                                                  object:nil];
79
+    
80
+    if (self.completionHandler) {
81
+        //for some reason, checking permission right away returns denied. need to wait a tiny bit
82
+        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.1 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
83
+            self.completionHandler([self.class getStatus]);
84
+            self.completionHandler = nil;
85
+        });
86
+    }
87
+}
88
+
89
+@end

+ 17
- 0
permissions/RNPPhoto.h Datei anzeigen

@@ -0,0 +1,17 @@
1
+//
2
+//  RNPPhoto.h
3
+//  ReactNativePermissions
4
+//
5
+//  Created by Yonah Forst on 11/07/16.
6
+//  Copyright © 2016 Yonah Forst. All rights reserved.
7
+//
8
+
9
+#import <Foundation/Foundation.h>
10
+#import "RCTConvert+RNPStatus.h"
11
+
12
+@interface RNPPhoto : NSObject
13
+
14
++ (NSString *)getStatus;
15
++ (void)request:(void (^)(NSString *))completionHandler;
16
+
17
+@end

+ 69
- 0
permissions/RNPPhoto.m Datei anzeigen

@@ -0,0 +1,69 @@
1
+//
2
+//  RNPPhoto.m
3
+//  ReactNativePermissions
4
+//
5
+//  Created by Yonah Forst on 11/07/16.
6
+//  Copyright © 2016 Yonah Forst. All rights reserved.
7
+//
8
+
9
+#import "RNPPhoto.h"
10
+#import <AssetsLibrary/AssetsLibrary.h>
11
+
12
+#if __IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_9_0
13
+@import Photos;
14
+#endif
15
+
16
+@implementation RNPPhoto
17
+
18
++ (NSString *)getStatus
19
+{
20
+#if __IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_9_0
21
+    int status = [PHPhotoLibrary authorizationStatus];
22
+    switch (status) {
23
+        case PHAuthorizationStatusAuthorized:
24
+            return RNPStatusAuthorized;
25
+        case PHAuthorizationStatusDenied:
26
+            return RNPStatusDenied;
27
+        case PHAuthorizationStatusRestricted:
28
+            return RNPStatusRestricted;
29
+        default:
30
+            return RNPStatusUndetermined;
31
+    }
32
+#else
33
+    int status = ABAddressBookGetAuthorizationStatus();
34
+    switch (status) {
35
+        case kABAuthorizationStatusAuthorized:
36
+            return RNPStatusAuthorized;
37
+        case kABAuthorizationStatusDenied:
38
+            return RNPStatusDenied;
39
+        case kABAuthorizationStatusRestricted:
40
+            return RNPStatusRestricted;
41
+        default:
42
+            return RNPStatusUndetermined;
43
+    }
44
+#endif
45
+}
46
+
47
++ (void)request:(void (^)(NSString *))completionHandler
48
+{
49
+    void (^handler)(void) =  ^(void) {
50
+        dispatch_async(dispatch_get_main_queue(), ^{
51
+            completionHandler([self.class getStatus]);
52
+        });
53
+    };
54
+    
55
+#if __IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_9_0
56
+    [PHPhotoLibrary requestAuthorization:^(PHAuthorizationStatus status) {
57
+        handler();
58
+    }];
59
+#else
60
+    ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
61
+    [library enumerateGroupsWithTypes:ALAssetsGroupSavedPhotos usingBlock:^(ALAssetsGroup *group, BOOL *stop) {
62
+        handler()
63
+        *stop = YES;
64
+    } failureBlock:^(NSError *error) {
65
+        handler();
66
+    }];
67
+#endif
68
+}
69
+@end