1// Copyright 2015-present 650 Industries. All rights reserved. 2 3#import "EXKernel.h" 4#import "EXSensorManager.h" 5#import <CoreMotion/CoreMotion.h> 6 7@interface EXSensorManager () 8 9@property (nonatomic, strong) CMMotionManager *manager; 10@property (nonatomic, strong) NSMutableDictionary *accelerometerHandlers; 11@property (nonatomic, strong) NSMutableDictionary *deviceMotionHandlers; 12@property (nonatomic, strong) NSMutableDictionary *gyroscopeHandlers; 13@property (nonatomic, strong) NSMutableDictionary *magnetometerHandlers; 14@property (nonatomic, strong) NSMutableDictionary *magnetometerUncalibratedHandlers; 15 16@end 17 18@implementation EXSensorManager 19 20- (instancetype)init 21{ 22 if (self = [super init]) { 23 _accelerometerHandlers = [[NSMutableDictionary alloc] init]; 24 _deviceMotionHandlers = [[NSMutableDictionary alloc] init]; 25 _gyroscopeHandlers = [[NSMutableDictionary alloc] init]; 26 _magnetometerHandlers = [[NSMutableDictionary alloc] init]; 27 _magnetometerUncalibratedHandlers = [[NSMutableDictionary alloc] init]; 28 } 29 return self; 30} 31 32- (CMMotionManager *)manager 33{ 34 if (!_manager) { 35 _manager = [[CMMotionManager alloc] init]; 36 } 37 return _manager; 38} 39 40- (void)dealloc 41{ 42 [self.manager stopAccelerometerUpdates]; 43 [self.manager stopDeviceMotionUpdates]; 44 [self.manager stopGyroUpdates]; 45 [self.manager stopMagnetometerUpdates]; 46} 47 48- (void)sensorModuleDidSubscribeForAccelerometerUpdatesOfExperience:experienceId 49 withHandler:(void (^)(NSDictionary *event))handlerBlock 50{ 51 if ([self.manager isAccelerometerAvailable]) { 52 self.accelerometerHandlers[experienceId] = handlerBlock; 53 } 54 if (![self.manager isAccelerometerActive]) { 55 [self.manager setAccelerometerUpdateInterval:0.1f]; 56 [self.manager startAccelerometerUpdatesToQueue:[NSOperationQueue mainQueue] withHandler:^(CMAccelerometerData *data, NSError *error) { 57 for (void (^handler)(NSDictionary *) in self.accelerometerHandlers.allValues) { 58 handler(@{ 59 @"x": [NSNumber numberWithDouble:data.acceleration.x], 60 @"y": [NSNumber numberWithDouble:data.acceleration.y], 61 @"z": [NSNumber numberWithDouble:data.acceleration.z] 62 }); 63 } 64 }]; 65 } 66} 67 68- (void)sensorModuleDidUnsubscribeForAccelerometerUpdatesOfExperience:experienceId 69{ 70 [self.accelerometerHandlers removeObjectForKey:experienceId]; 71 if (self.accelerometerHandlers.count == 0) { 72 [self.manager stopAccelerometerUpdates]; 73 } 74} 75 76- (void)setAccelerometerUpdateInterval:(NSTimeInterval)intervalMs 77{ 78 [self.manager setAccelerometerUpdateInterval:intervalMs]; 79} 80 81- (void)sensorModuleDidSubscribeForDeviceMotionUpdatesOfExperience:(NSString *)experienceId 82 withHandler:(void (^)(NSDictionary *event))handlerBlock 83{ 84 if ([self.manager isDeviceMotionAvailable]) { 85 self.deviceMotionHandlers[experienceId] = handlerBlock; 86 } 87 if (![self.manager isDeviceMotionActive]) { 88 [self activateDeviceMotionUpdates]; 89 } 90} 91 92- (void)sensorModuleDidUnsubscribeForDeviceMotionUpdatesOfExperience:(NSString *)experienceId 93{ 94 [self.deviceMotionHandlers removeObjectForKey:experienceId]; 95 if (self.deviceMotionHandlers.count == 0 && self.magnetometerHandlers.count == 0) { 96 [self.manager stopDeviceMotionUpdates]; 97 } 98} 99 100- (void)setDeviceMotionUpdateInterval:(NSTimeInterval)intervalMs 101{ 102 [self.manager setDeviceMotionUpdateInterval:intervalMs]; 103} 104 105- (void)sensorModuleDidSubscribeForGyroscopeUpdatesOfExperience:(NSString *)experienceId 106 withHandler:(void (^)(NSDictionary *event))handlerBlock 107{ 108 if ([self.manager isGyroAvailable]) { 109 self.gyroscopeHandlers[experienceId] = handlerBlock; 110 } 111 if (![self.manager isGyroActive]) { 112 [self.manager setGyroUpdateInterval:0.1f]; 113 [self.manager startGyroUpdatesToQueue:[NSOperationQueue mainQueue] withHandler:^(CMGyroData *data, NSError *error) { 114 for (void (^handler)(NSDictionary *) in self.gyroscopeHandlers.allValues) { 115 handler(@{ 116 @"x": [NSNumber numberWithDouble:data.rotationRate.x], 117 @"y": [NSNumber numberWithDouble:data.rotationRate.y], 118 @"z": [NSNumber numberWithDouble:data.rotationRate.z] 119 }); 120 } 121 }]; 122 } 123} 124 125- (void)sensorModuleDidUnsubscribeForGyroscopeUpdatesOfExperience:(NSString *)experienceId 126{ 127 [self.gyroscopeHandlers removeObjectForKey:experienceId]; 128 if (self.gyroscopeHandlers.count == 0) { 129 [self.manager stopGyroUpdates]; 130 } 131} 132 133- (void)setGyroscopeUpdateInterval:(NSTimeInterval)intervalMs 134{ 135 [self.manager setGyroUpdateInterval:intervalMs]; 136} 137 138- (void)sensorModuleDidSubscribeForMagnetometerUpdatesOfExperience:(NSString *)experienceId 139 withHandler:(void (^)(NSDictionary *event))handlerBlock 140{ 141 if ([self.manager isDeviceMotionAvailable]) { 142 self.magnetometerHandlers[experienceId] = handlerBlock; 143 } 144 if (![self.manager isDeviceMotionActive]) { 145 [self activateDeviceMotionUpdates]; 146 } 147} 148 149- (void)sensorModuleDidUnsubscribeForMagnetometerUpdatesOfExperience:(NSString *)experienceId 150{ 151 [self.magnetometerHandlers removeObjectForKey:experienceId]; 152 if (self.deviceMotionHandlers.count == 0 && self.magnetometerHandlers.count == 0) { 153 [self.manager stopDeviceMotionUpdates]; 154 } 155} 156 157- (void)setMagnetometerUpdateInterval:(NSTimeInterval)intervalMs 158{ 159 [self.manager setDeviceMotionUpdateInterval:intervalMs]; 160} 161 162- (void)sensorModuleDidSubscribeForMagnetometerUncalibratedUpdatesOfExperience:(NSString *)experienceId 163 withHandler:(void (^)(NSDictionary *event))handlerBlock 164{ 165 if ([self.manager isMagnetometerAvailable]) { 166 self.magnetometerUncalibratedHandlers[experienceId] = handlerBlock; 167 } 168 if (![self.manager isMagnetometerActive]) { 169 [self.manager setMagnetometerUpdateInterval:0.1f]; 170 [self.manager startMagnetometerUpdatesToQueue:[NSOperationQueue mainQueue] withHandler:^(CMMagnetometerData *data, NSError *error) { 171 for (void (^handler)(NSDictionary *) in self.magnetometerUncalibratedHandlers.allValues) { 172 handler(@{ 173 @"x": [NSNumber numberWithDouble:data.magneticField.x], 174 @"y": [NSNumber numberWithDouble:data.magneticField.y], 175 @"z": [NSNumber numberWithDouble:data.magneticField.z] 176 }); 177 } 178 }]; 179 } 180} 181 182- (void)sensorModuleDidUnsubscribeForMagnetometerUncalibratedUpdatesOfExperience:(NSString *)experienceId 183{ 184 [self.magnetometerUncalibratedHandlers removeObjectForKey:experienceId]; 185 if (self.magnetometerUncalibratedHandlers.count == 0) { 186 [self.manager stopMagnetometerUpdates]; 187 } 188} 189 190- (void)setMagnetometerUncalibratedUpdateInterval:(NSTimeInterval)intervalMs 191{ 192 [self.manager setMagnetometerUpdateInterval:intervalMs]; 193} 194 195- (float)getGravity 196{ 197 return EXGravity; 198} 199 200- (void)activateDeviceMotionUpdates 201{ 202 [self.manager setDeviceMotionUpdateInterval:0.1f]; 203 [self.manager 204 startDeviceMotionUpdatesUsingReferenceFrame:CMAttitudeReferenceFrameXArbitraryCorrectedZVertical 205 toQueue:[NSOperationQueue mainQueue] 206 withHandler:^(CMDeviceMotion *data, NSError *error) { 207 UIDeviceOrientation orientation = [[UIDevice currentDevice] orientation]; 208 int orientationDegrees; 209 switch (orientation) { 210 case UIDeviceOrientationPortrait: 211 orientationDegrees = 0; 212 break; 213 case UIDeviceOrientationLandscapeLeft: 214 orientationDegrees = -90; 215 break; 216 case UIDeviceOrientationLandscapeRight: 217 orientationDegrees = 90; 218 break; 219 case UIDeviceOrientationPortraitUpsideDown: 220 orientationDegrees = 180; 221 break; 222 default: 223 orientationDegrees = 0; 224 break; 225 } 226 227 NSDictionary *result = @{ 228 @"acceleration": @{ 229 @"x": @(data.userAcceleration.x * EXGravity), 230 @"y": @(data.userAcceleration.y * EXGravity), 231 @"z": @(data.userAcceleration.z * EXGravity) 232 }, 233 @"accelerationIncludingGravity": @{ 234 @"x": @((data.userAcceleration.x + data.gravity.x) * EXGravity), 235 @"y": @((data.userAcceleration.y + data.gravity.y) * EXGravity), 236 @"z": @((data.userAcceleration.z + data.gravity.z) * EXGravity) 237 }, 238 @"rotation": @{ 239 @"alpha": @(data.attitude.yaw), 240 @"beta": @(data.attitude.pitch), 241 @"gamma": @(data.attitude.roll), 242 }, 243 @"rotationRate" :@{ 244 @"alpha": @(data.rotationRate.z), 245 @"beta": @(data.rotationRate.y), 246 @"gamma": @(data.rotationRate.x) 247 }, 248 @"orientation": @(orientationDegrees) 249 }; 250 251 // DeviceMotionUpdates handle DeviceMotion data as well as magnetic field 252 for (void (^handler)(NSDictionary *) in self.deviceMotionHandlers.allValues) { 253 handler(result); 254 } 255 256 for (void (^handler)(NSDictionary *) in self.magnetometerHandlers.allValues) { 257 handler(@{ 258 @"x": [NSNumber numberWithDouble:data.magneticField.field.x], 259 @"y": [NSNumber numberWithDouble:data.magneticField.field.y], 260 @"z": [NSNumber numberWithDouble:data.magneticField.field.z] 261 }); 262 } 263 }]; 264} 265 266@end 267