community-cordova-plugin-magnetometer 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.idea/community-cordova-plugin-magnetometer.iml +8 -0
- package/.idea/modules.xml +8 -0
- package/.idea/vcs.xml +6 -0
- package/CHANGELOG.md +27 -0
- package/README.md +383 -0
- package/package.json +48 -0
- package/plugin.xml +54 -0
- package/src/android/Magnetometer.java +524 -0
- package/src/browser/MagnetometerProxy.js +300 -0
- package/src/ios/CDVMagnetometer.h +26 -0
- package/src/ios/CDVMagnetometer.m +321 -0
- package/types/index.d.ts +150 -0
- package/www/magnetometer.js +122 -0
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
var watchInterval = null;
|
|
2
|
+
var watchHeadingInterval = null;
|
|
3
|
+
|
|
4
|
+
module.exports = {
|
|
5
|
+
isAvailable: function(successCallback, errorCallback) {
|
|
6
|
+
// Check if Generic Sensor API is available
|
|
7
|
+
var available = 'Magnetometer' in window || 'AbsoluteOrientationSensor' in window;
|
|
8
|
+
successCallback(available ? 1 : 0);
|
|
9
|
+
},
|
|
10
|
+
|
|
11
|
+
getReading: function(successCallback, errorCallback) {
|
|
12
|
+
if ('Magnetometer' in window) {
|
|
13
|
+
try {
|
|
14
|
+
var sensor = new window.Magnetometer({ frequency: 10 });
|
|
15
|
+
sensor.addEventListener('reading', function() {
|
|
16
|
+
var x = sensor.x || 0;
|
|
17
|
+
var y = sensor.y || 0;
|
|
18
|
+
var z = sensor.z || 0;
|
|
19
|
+
var magnitude = Math.sqrt(x * x + y * y + z * z);
|
|
20
|
+
|
|
21
|
+
sensor.stop();
|
|
22
|
+
successCallback({
|
|
23
|
+
x: x,
|
|
24
|
+
y: y,
|
|
25
|
+
z: z,
|
|
26
|
+
magnitude: magnitude,
|
|
27
|
+
timestamp: Date.now()
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
sensor.addEventListener('error', function(event) {
|
|
31
|
+
errorCallback(event.error.message || 'Magnetometer error');
|
|
32
|
+
});
|
|
33
|
+
sensor.start();
|
|
34
|
+
} catch (e) {
|
|
35
|
+
// Return mock data if sensor not accessible
|
|
36
|
+
successCallback({
|
|
37
|
+
x: 25.5,
|
|
38
|
+
y: -12.3,
|
|
39
|
+
z: 45.8,
|
|
40
|
+
magnitude: 54.2,
|
|
41
|
+
timestamp: Date.now()
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
} else {
|
|
45
|
+
// Return mock data for browsers without Magnetometer API
|
|
46
|
+
successCallback({
|
|
47
|
+
x: 25.5,
|
|
48
|
+
y: -12.3,
|
|
49
|
+
z: 45.8,
|
|
50
|
+
magnitude: 54.2,
|
|
51
|
+
timestamp: Date.now()
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
|
|
56
|
+
getHeading: function(successCallback, errorCallback) {
|
|
57
|
+
if ('AbsoluteOrientationSensor' in window) {
|
|
58
|
+
try {
|
|
59
|
+
var sensor = new window.AbsoluteOrientationSensor({ frequency: 10 });
|
|
60
|
+
sensor.addEventListener('reading', function() {
|
|
61
|
+
var q = sensor.quaternion;
|
|
62
|
+
var heading = quaternionToHeading(q);
|
|
63
|
+
sensor.stop();
|
|
64
|
+
successCallback({
|
|
65
|
+
magneticHeading: heading,
|
|
66
|
+
trueHeading: heading,
|
|
67
|
+
headingAccuracy: -1,
|
|
68
|
+
timestamp: Date.now()
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
sensor.addEventListener('error', function(event) {
|
|
72
|
+
errorCallback(event.error.message || 'Orientation sensor error');
|
|
73
|
+
});
|
|
74
|
+
sensor.start();
|
|
75
|
+
} catch (e) {
|
|
76
|
+
// Return mock heading
|
|
77
|
+
successCallback({
|
|
78
|
+
magneticHeading: 180,
|
|
79
|
+
trueHeading: 180,
|
|
80
|
+
headingAccuracy: -1,
|
|
81
|
+
timestamp: Date.now()
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
} else {
|
|
85
|
+
// Return mock heading
|
|
86
|
+
successCallback({
|
|
87
|
+
magneticHeading: 180,
|
|
88
|
+
trueHeading: 180,
|
|
89
|
+
headingAccuracy: -1,
|
|
90
|
+
timestamp: Date.now()
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
},
|
|
94
|
+
|
|
95
|
+
watchReadings: function(successCallback, errorCallback, args) {
|
|
96
|
+
var frequency = args && args[0] ? args[0] : 100;
|
|
97
|
+
|
|
98
|
+
if (watchInterval) {
|
|
99
|
+
clearInterval(watchInterval);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if ('Magnetometer' in window) {
|
|
103
|
+
try {
|
|
104
|
+
var sensor = new window.Magnetometer({ frequency: 1000 / frequency });
|
|
105
|
+
sensor.addEventListener('reading', function() {
|
|
106
|
+
var x = sensor.x || 0;
|
|
107
|
+
var y = sensor.y || 0;
|
|
108
|
+
var z = sensor.z || 0;
|
|
109
|
+
var magnitude = Math.sqrt(x * x + y * y + z * z);
|
|
110
|
+
|
|
111
|
+
successCallback({
|
|
112
|
+
x: x,
|
|
113
|
+
y: y,
|
|
114
|
+
z: z,
|
|
115
|
+
magnitude: magnitude,
|
|
116
|
+
timestamp: Date.now()
|
|
117
|
+
});
|
|
118
|
+
});
|
|
119
|
+
sensor.addEventListener('error', function(event) {
|
|
120
|
+
errorCallback(event.error.message || 'Magnetometer error');
|
|
121
|
+
});
|
|
122
|
+
sensor.start();
|
|
123
|
+
|
|
124
|
+
// Store reference for stopping
|
|
125
|
+
watchInterval = sensor;
|
|
126
|
+
} catch (e) {
|
|
127
|
+
// Fall back to mock data
|
|
128
|
+
startMockWatch(successCallback, frequency);
|
|
129
|
+
}
|
|
130
|
+
} else {
|
|
131
|
+
// Fall back to mock data
|
|
132
|
+
startMockWatch(successCallback, frequency);
|
|
133
|
+
}
|
|
134
|
+
},
|
|
135
|
+
|
|
136
|
+
stopWatch: function(successCallback, errorCallback) {
|
|
137
|
+
if (watchInterval) {
|
|
138
|
+
if (typeof watchInterval.stop === 'function') {
|
|
139
|
+
watchInterval.stop();
|
|
140
|
+
} else {
|
|
141
|
+
clearInterval(watchInterval);
|
|
142
|
+
}
|
|
143
|
+
watchInterval = null;
|
|
144
|
+
}
|
|
145
|
+
successCallback();
|
|
146
|
+
},
|
|
147
|
+
|
|
148
|
+
watchHeading: function(successCallback, errorCallback, args) {
|
|
149
|
+
var frequency = args && args[0] ? args[0] : 100;
|
|
150
|
+
|
|
151
|
+
if (watchHeadingInterval) {
|
|
152
|
+
if (typeof watchHeadingInterval.stop === 'function') {
|
|
153
|
+
watchHeadingInterval.stop();
|
|
154
|
+
} else {
|
|
155
|
+
clearInterval(watchHeadingInterval);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
if ('AbsoluteOrientationSensor' in window) {
|
|
160
|
+
try {
|
|
161
|
+
var sensor = new window.AbsoluteOrientationSensor({ frequency: 1000 / frequency });
|
|
162
|
+
sensor.addEventListener('reading', function() {
|
|
163
|
+
var q = sensor.quaternion;
|
|
164
|
+
var heading = quaternionToHeading(q);
|
|
165
|
+
|
|
166
|
+
successCallback({
|
|
167
|
+
magneticHeading: heading,
|
|
168
|
+
trueHeading: heading,
|
|
169
|
+
headingAccuracy: -1,
|
|
170
|
+
timestamp: Date.now()
|
|
171
|
+
});
|
|
172
|
+
});
|
|
173
|
+
sensor.addEventListener('error', function(event) {
|
|
174
|
+
errorCallback(event.error.message || 'Orientation sensor error');
|
|
175
|
+
});
|
|
176
|
+
sensor.start();
|
|
177
|
+
|
|
178
|
+
watchHeadingInterval = sensor;
|
|
179
|
+
} catch (e) {
|
|
180
|
+
startMockHeadingWatch(successCallback, frequency);
|
|
181
|
+
}
|
|
182
|
+
} else {
|
|
183
|
+
startMockHeadingWatch(successCallback, frequency);
|
|
184
|
+
}
|
|
185
|
+
},
|
|
186
|
+
|
|
187
|
+
stopWatchHeading: function(successCallback, errorCallback) {
|
|
188
|
+
if (watchHeadingInterval) {
|
|
189
|
+
if (typeof watchHeadingInterval.stop === 'function') {
|
|
190
|
+
watchHeadingInterval.stop();
|
|
191
|
+
} else {
|
|
192
|
+
clearInterval(watchHeadingInterval);
|
|
193
|
+
}
|
|
194
|
+
watchHeadingInterval = null;
|
|
195
|
+
}
|
|
196
|
+
successCallback();
|
|
197
|
+
},
|
|
198
|
+
|
|
199
|
+
getMagnetometerInfo: function(successCallback, errorCallback) {
|
|
200
|
+
var available = 'Magnetometer' in window || 'AbsoluteOrientationSensor' in window;
|
|
201
|
+
|
|
202
|
+
successCallback({
|
|
203
|
+
isAvailable: available,
|
|
204
|
+
reading: {
|
|
205
|
+
x: 25.5,
|
|
206
|
+
y: -12.3,
|
|
207
|
+
z: 45.8,
|
|
208
|
+
magnitude: 54.2,
|
|
209
|
+
timestamp: Date.now()
|
|
210
|
+
},
|
|
211
|
+
heading: {
|
|
212
|
+
magneticHeading: 180,
|
|
213
|
+
trueHeading: 180,
|
|
214
|
+
headingAccuracy: -1,
|
|
215
|
+
timestamp: Date.now()
|
|
216
|
+
},
|
|
217
|
+
accuracy: 3,
|
|
218
|
+
calibrationNeeded: false,
|
|
219
|
+
platform: 'browser'
|
|
220
|
+
});
|
|
221
|
+
},
|
|
222
|
+
|
|
223
|
+
getAccuracy: function(successCallback, errorCallback) {
|
|
224
|
+
successCallback(3); // High accuracy (mock)
|
|
225
|
+
},
|
|
226
|
+
|
|
227
|
+
isCalibrationNeeded: function(successCallback, errorCallback) {
|
|
228
|
+
successCallback(0); // No calibration needed (mock)
|
|
229
|
+
},
|
|
230
|
+
|
|
231
|
+
getFieldStrength: function(successCallback, errorCallback) {
|
|
232
|
+
if ('Magnetometer' in window) {
|
|
233
|
+
try {
|
|
234
|
+
var sensor = new window.Magnetometer({ frequency: 10 });
|
|
235
|
+
sensor.addEventListener('reading', function() {
|
|
236
|
+
var x = sensor.x || 0;
|
|
237
|
+
var y = sensor.y || 0;
|
|
238
|
+
var z = sensor.z || 0;
|
|
239
|
+
var magnitude = Math.sqrt(x * x + y * y + z * z);
|
|
240
|
+
sensor.stop();
|
|
241
|
+
successCallback(Math.round(magnitude));
|
|
242
|
+
});
|
|
243
|
+
sensor.addEventListener('error', function() {
|
|
244
|
+
successCallback(54); // Mock value
|
|
245
|
+
});
|
|
246
|
+
sensor.start();
|
|
247
|
+
} catch (e) {
|
|
248
|
+
successCallback(54); // Mock value
|
|
249
|
+
}
|
|
250
|
+
} else {
|
|
251
|
+
successCallback(54); // Mock value
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
};
|
|
255
|
+
|
|
256
|
+
function startMockWatch(successCallback, frequency) {
|
|
257
|
+
var angle = 0;
|
|
258
|
+
watchInterval = setInterval(function() {
|
|
259
|
+
angle += 0.1;
|
|
260
|
+
successCallback({
|
|
261
|
+
x: 25.5 + Math.sin(angle) * 5,
|
|
262
|
+
y: -12.3 + Math.cos(angle) * 5,
|
|
263
|
+
z: 45.8 + Math.sin(angle * 0.5) * 3,
|
|
264
|
+
magnitude: 54.2 + Math.sin(angle) * 2,
|
|
265
|
+
timestamp: Date.now()
|
|
266
|
+
});
|
|
267
|
+
}, frequency);
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
function startMockHeadingWatch(successCallback, frequency) {
|
|
271
|
+
var heading = 180;
|
|
272
|
+
watchHeadingInterval = setInterval(function() {
|
|
273
|
+
heading = (heading + 1) % 360;
|
|
274
|
+
successCallback({
|
|
275
|
+
magneticHeading: heading,
|
|
276
|
+
trueHeading: heading,
|
|
277
|
+
headingAccuracy: -1,
|
|
278
|
+
timestamp: Date.now()
|
|
279
|
+
});
|
|
280
|
+
}, frequency);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
function quaternionToHeading(q) {
|
|
284
|
+
if (!q || q.length < 4) return 0;
|
|
285
|
+
|
|
286
|
+
var x = q[0], y = q[1], z = q[2], w = q[3];
|
|
287
|
+
|
|
288
|
+
// Calculate yaw (heading) from quaternion
|
|
289
|
+
var siny_cosp = 2 * (w * z + x * y);
|
|
290
|
+
var cosy_cosp = 1 - 2 * (y * y + z * z);
|
|
291
|
+
var yaw = Math.atan2(siny_cosp, cosy_cosp);
|
|
292
|
+
|
|
293
|
+
// Convert to degrees and normalize to 0-360
|
|
294
|
+
var heading = yaw * 180 / Math.PI;
|
|
295
|
+
if (heading < 0) heading += 360;
|
|
296
|
+
|
|
297
|
+
return heading;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
require('cordova/exec/proxy').add('Magnetometer', module.exports);
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
#import <Cordova/CDVPlugin.h>
|
|
2
|
+
#import <CoreMotion/CoreMotion.h>
|
|
3
|
+
#import <CoreLocation/CoreLocation.h>
|
|
4
|
+
|
|
5
|
+
@interface CDVMagnetometer : CDVPlugin <CLLocationManagerDelegate>
|
|
6
|
+
|
|
7
|
+
@property (nonatomic, strong) CMMotionManager *motionManager;
|
|
8
|
+
@property (nonatomic, strong) CLLocationManager *locationManager;
|
|
9
|
+
@property (nonatomic, strong) NSString *watchCallbackId;
|
|
10
|
+
@property (nonatomic, strong) NSString *watchHeadingCallbackId;
|
|
11
|
+
@property (nonatomic, assign) int currentAccuracy;
|
|
12
|
+
@property (nonatomic, assign) BOOL calibrationNeeded;
|
|
13
|
+
|
|
14
|
+
- (void)isAvailable:(CDVInvokedUrlCommand *)command;
|
|
15
|
+
- (void)getReading:(CDVInvokedUrlCommand *)command;
|
|
16
|
+
- (void)getHeading:(CDVInvokedUrlCommand *)command;
|
|
17
|
+
- (void)watchReadings:(CDVInvokedUrlCommand *)command;
|
|
18
|
+
- (void)stopWatch:(CDVInvokedUrlCommand *)command;
|
|
19
|
+
- (void)watchHeading:(CDVInvokedUrlCommand *)command;
|
|
20
|
+
- (void)stopWatchHeading:(CDVInvokedUrlCommand *)command;
|
|
21
|
+
- (void)getMagnetometerInfo:(CDVInvokedUrlCommand *)command;
|
|
22
|
+
- (void)getAccuracy:(CDVInvokedUrlCommand *)command;
|
|
23
|
+
- (void)isCalibrationNeeded:(CDVInvokedUrlCommand *)command;
|
|
24
|
+
- (void)getFieldStrength:(CDVInvokedUrlCommand *)command;
|
|
25
|
+
|
|
26
|
+
@end
|
|
@@ -0,0 +1,321 @@
|
|
|
1
|
+
#import "CDVMagnetometer.h"
|
|
2
|
+
|
|
3
|
+
@implementation CDVMagnetometer
|
|
4
|
+
|
|
5
|
+
- (void)pluginInitialize {
|
|
6
|
+
self.motionManager = [[CMMotionManager alloc] init];
|
|
7
|
+
self.locationManager = [[CLLocationManager alloc] init];
|
|
8
|
+
self.locationManager.delegate = self;
|
|
9
|
+
self.currentAccuracy = 3; // Default to high
|
|
10
|
+
self.calibrationNeeded = NO;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
#pragma mark - Availability Check
|
|
14
|
+
|
|
15
|
+
- (void)isAvailable:(CDVInvokedUrlCommand *)command {
|
|
16
|
+
[self.commandDelegate runInBackground:^{
|
|
17
|
+
BOOL available = self.motionManager.magnetometerAvailable;
|
|
18
|
+
CDVPluginResult *result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsInt:available ? 1 : 0];
|
|
19
|
+
[self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
|
|
20
|
+
}];
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
#pragma mark - Single Reading
|
|
24
|
+
|
|
25
|
+
- (void)getReading:(CDVInvokedUrlCommand *)command {
|
|
26
|
+
[self.commandDelegate runInBackground:^{
|
|
27
|
+
if (!self.motionManager.magnetometerAvailable) {
|
|
28
|
+
CDVPluginResult *result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"Magnetometer not available"];
|
|
29
|
+
[self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
[self.motionManager startMagnetometerUpdates];
|
|
34
|
+
|
|
35
|
+
// Wait briefly for sensor to stabilize
|
|
36
|
+
[NSThread sleepForTimeInterval:0.1];
|
|
37
|
+
|
|
38
|
+
CMMagnetometerData *data = self.motionManager.magnetometerData;
|
|
39
|
+
[self.motionManager stopMagnetometerUpdates];
|
|
40
|
+
|
|
41
|
+
if (data) {
|
|
42
|
+
double x = data.magneticField.x;
|
|
43
|
+
double y = data.magneticField.y;
|
|
44
|
+
double z = data.magneticField.z;
|
|
45
|
+
double magnitude = sqrt(x*x + y*y + z*z);
|
|
46
|
+
|
|
47
|
+
NSDictionary *reading = @{
|
|
48
|
+
@"x": @(x),
|
|
49
|
+
@"y": @(y),
|
|
50
|
+
@"z": @(z),
|
|
51
|
+
@"magnitude": @(magnitude),
|
|
52
|
+
@"timestamp": @([[NSDate date] timeIntervalSince1970] * 1000)
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
CDVPluginResult *result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:reading];
|
|
56
|
+
[self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
|
|
57
|
+
} else {
|
|
58
|
+
CDVPluginResult *result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"Failed to get magnetometer reading"];
|
|
59
|
+
[self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
|
|
60
|
+
}
|
|
61
|
+
}];
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
#pragma mark - Heading
|
|
65
|
+
|
|
66
|
+
- (void)getHeading:(CDVInvokedUrlCommand *)command {
|
|
67
|
+
[self.commandDelegate runInBackground:^{
|
|
68
|
+
if (![CLLocationManager headingAvailable]) {
|
|
69
|
+
CDVPluginResult *result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"Heading not available"];
|
|
70
|
+
[self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
__block BOOL completed = NO;
|
|
75
|
+
__weak CDVMagnetometer *weakSelf = self;
|
|
76
|
+
|
|
77
|
+
dispatch_async(dispatch_get_main_queue(), ^{
|
|
78
|
+
[weakSelf.locationManager startUpdatingHeading];
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
// Wait for heading update
|
|
82
|
+
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
|
|
83
|
+
if (!completed) {
|
|
84
|
+
completed = YES;
|
|
85
|
+
CLHeading *heading = weakSelf.locationManager.heading;
|
|
86
|
+
[weakSelf.locationManager stopUpdatingHeading];
|
|
87
|
+
|
|
88
|
+
if (heading) {
|
|
89
|
+
NSDictionary *headingData = @{
|
|
90
|
+
@"magneticHeading": @(heading.magneticHeading),
|
|
91
|
+
@"trueHeading": @(heading.trueHeading),
|
|
92
|
+
@"headingAccuracy": @(heading.headingAccuracy),
|
|
93
|
+
@"timestamp": @([[NSDate date] timeIntervalSince1970] * 1000)
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
CDVPluginResult *result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:headingData];
|
|
97
|
+
[weakSelf.commandDelegate sendPluginResult:result callbackId:command.callbackId];
|
|
98
|
+
} else {
|
|
99
|
+
CDVPluginResult *result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"Failed to get heading"];
|
|
100
|
+
[weakSelf.commandDelegate sendPluginResult:result callbackId:command.callbackId];
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
}];
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
#pragma mark - Watch Readings
|
|
108
|
+
|
|
109
|
+
- (void)watchReadings:(CDVInvokedUrlCommand *)command {
|
|
110
|
+
if (!self.motionManager.magnetometerAvailable) {
|
|
111
|
+
CDVPluginResult *result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"Magnetometer not available"];
|
|
112
|
+
[self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Stop any existing watch
|
|
117
|
+
[self.motionManager stopMagnetometerUpdates];
|
|
118
|
+
|
|
119
|
+
self.watchCallbackId = command.callbackId;
|
|
120
|
+
|
|
121
|
+
NSNumber *frequencyArg = [command.arguments objectAtIndex:0];
|
|
122
|
+
double frequency = frequencyArg ? [frequencyArg doubleValue] : 100;
|
|
123
|
+
self.motionManager.magnetometerUpdateInterval = frequency / 1000.0;
|
|
124
|
+
|
|
125
|
+
__weak CDVMagnetometer *weakSelf = self;
|
|
126
|
+
|
|
127
|
+
[self.motionManager startMagnetometerUpdatesToQueue:[NSOperationQueue mainQueue]
|
|
128
|
+
withHandler:^(CMMagnetometerData *data, NSError *error) {
|
|
129
|
+
if (error) {
|
|
130
|
+
CDVPluginResult *result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:error.localizedDescription];
|
|
131
|
+
[result setKeepCallbackAsBool:YES];
|
|
132
|
+
[weakSelf.commandDelegate sendPluginResult:result callbackId:weakSelf.watchCallbackId];
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
if (data) {
|
|
137
|
+
double x = data.magneticField.x;
|
|
138
|
+
double y = data.magneticField.y;
|
|
139
|
+
double z = data.magneticField.z;
|
|
140
|
+
double magnitude = sqrt(x*x + y*y + z*z);
|
|
141
|
+
|
|
142
|
+
NSDictionary *reading = @{
|
|
143
|
+
@"x": @(x),
|
|
144
|
+
@"y": @(y),
|
|
145
|
+
@"z": @(z),
|
|
146
|
+
@"magnitude": @(magnitude),
|
|
147
|
+
@"timestamp": @([[NSDate date] timeIntervalSince1970] * 1000)
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
CDVPluginResult *result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:reading];
|
|
151
|
+
[result setKeepCallbackAsBool:YES];
|
|
152
|
+
[weakSelf.commandDelegate sendPluginResult:result callbackId:weakSelf.watchCallbackId];
|
|
153
|
+
}
|
|
154
|
+
}];
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
- (void)stopWatch:(CDVInvokedUrlCommand *)command {
|
|
158
|
+
[self.motionManager stopMagnetometerUpdates];
|
|
159
|
+
self.watchCallbackId = nil;
|
|
160
|
+
|
|
161
|
+
CDVPluginResult *result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
|
|
162
|
+
[self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
#pragma mark - Watch Heading
|
|
166
|
+
|
|
167
|
+
- (void)watchHeading:(CDVInvokedUrlCommand *)command {
|
|
168
|
+
if (![CLLocationManager headingAvailable]) {
|
|
169
|
+
CDVPluginResult *result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"Heading not available"];
|
|
170
|
+
[self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
self.watchHeadingCallbackId = command.callbackId;
|
|
175
|
+
|
|
176
|
+
NSNumber *filterArg = [command.arguments count] > 1 ? [command.arguments objectAtIndex:1] : nil;
|
|
177
|
+
double filter = filterArg ? [filterArg doubleValue] : 0;
|
|
178
|
+
|
|
179
|
+
if (filter > 0) {
|
|
180
|
+
self.locationManager.headingFilter = filter;
|
|
181
|
+
} else {
|
|
182
|
+
self.locationManager.headingFilter = kCLHeadingFilterNone;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
dispatch_async(dispatch_get_main_queue(), ^{
|
|
186
|
+
[self.locationManager startUpdatingHeading];
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
- (void)stopWatchHeading:(CDVInvokedUrlCommand *)command {
|
|
191
|
+
dispatch_async(dispatch_get_main_queue(), ^{
|
|
192
|
+
[self.locationManager stopUpdatingHeading];
|
|
193
|
+
});
|
|
194
|
+
self.watchHeadingCallbackId = nil;
|
|
195
|
+
|
|
196
|
+
CDVPluginResult *result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
|
|
197
|
+
[self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
#pragma mark - CLLocationManagerDelegate
|
|
201
|
+
|
|
202
|
+
- (void)locationManager:(CLLocationManager *)manager didUpdateHeading:(CLHeading *)newHeading {
|
|
203
|
+
if (self.watchHeadingCallbackId && newHeading) {
|
|
204
|
+
NSDictionary *headingData = @{
|
|
205
|
+
@"magneticHeading": @(newHeading.magneticHeading),
|
|
206
|
+
@"trueHeading": @(newHeading.trueHeading),
|
|
207
|
+
@"headingAccuracy": @(newHeading.headingAccuracy),
|
|
208
|
+
@"timestamp": @([[NSDate date] timeIntervalSince1970] * 1000)
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
CDVPluginResult *result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:headingData];
|
|
212
|
+
[result setKeepCallbackAsBool:YES];
|
|
213
|
+
[self.commandDelegate sendPluginResult:result callbackId:self.watchHeadingCallbackId];
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
- (BOOL)locationManagerShouldDisplayHeadingCalibration:(CLLocationManager *)manager {
|
|
218
|
+
self.calibrationNeeded = YES;
|
|
219
|
+
return YES;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
#pragma mark - Info Methods
|
|
223
|
+
|
|
224
|
+
- (void)getMagnetometerInfo:(CDVInvokedUrlCommand *)command {
|
|
225
|
+
[self.commandDelegate runInBackground:^{
|
|
226
|
+
BOOL available = self.motionManager.magnetometerAvailable;
|
|
227
|
+
|
|
228
|
+
NSMutableDictionary *info = [NSMutableDictionary dictionary];
|
|
229
|
+
info[@"isAvailable"] = @(available);
|
|
230
|
+
info[@"accuracy"] = @(self.currentAccuracy);
|
|
231
|
+
info[@"calibrationNeeded"] = @(self.calibrationNeeded);
|
|
232
|
+
info[@"platform"] = @"ios";
|
|
233
|
+
|
|
234
|
+
if (available) {
|
|
235
|
+
[self.motionManager startMagnetometerUpdates];
|
|
236
|
+
[NSThread sleepForTimeInterval:0.1];
|
|
237
|
+
|
|
238
|
+
CMMagnetometerData *data = self.motionManager.magnetometerData;
|
|
239
|
+
[self.motionManager stopMagnetometerUpdates];
|
|
240
|
+
|
|
241
|
+
if (data) {
|
|
242
|
+
double x = data.magneticField.x;
|
|
243
|
+
double y = data.magneticField.y;
|
|
244
|
+
double z = data.magneticField.z;
|
|
245
|
+
double magnitude = sqrt(x*x + y*y + z*z);
|
|
246
|
+
|
|
247
|
+
info[@"reading"] = @{
|
|
248
|
+
@"x": @(x),
|
|
249
|
+
@"y": @(y),
|
|
250
|
+
@"z": @(z),
|
|
251
|
+
@"magnitude": @(magnitude),
|
|
252
|
+
@"timestamp": @([[NSDate date] timeIntervalSince1970] * 1000)
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// Get heading if available
|
|
258
|
+
if ([CLLocationManager headingAvailable]) {
|
|
259
|
+
CLHeading *heading = self.locationManager.heading;
|
|
260
|
+
if (heading) {
|
|
261
|
+
info[@"heading"] = @{
|
|
262
|
+
@"magneticHeading": @(heading.magneticHeading),
|
|
263
|
+
@"trueHeading": @(heading.trueHeading),
|
|
264
|
+
@"headingAccuracy": @(heading.headingAccuracy),
|
|
265
|
+
@"timestamp": @([[NSDate date] timeIntervalSince1970] * 1000)
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
CDVPluginResult *result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:info];
|
|
271
|
+
[self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
|
|
272
|
+
}];
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
- (void)getAccuracy:(CDVInvokedUrlCommand *)command {
|
|
276
|
+
CDVPluginResult *result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsInt:self.currentAccuracy];
|
|
277
|
+
[self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
- (void)isCalibrationNeeded:(CDVInvokedUrlCommand *)command {
|
|
281
|
+
CDVPluginResult *result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsInt:self.calibrationNeeded ? 1 : 0];
|
|
282
|
+
[self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
- (void)getFieldStrength:(CDVInvokedUrlCommand *)command {
|
|
286
|
+
[self.commandDelegate runInBackground:^{
|
|
287
|
+
if (!self.motionManager.magnetometerAvailable) {
|
|
288
|
+
CDVPluginResult *result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"Magnetometer not available"];
|
|
289
|
+
[self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
|
|
290
|
+
return;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
[self.motionManager startMagnetometerUpdates];
|
|
294
|
+
[NSThread sleepForTimeInterval:0.1];
|
|
295
|
+
|
|
296
|
+
CMMagnetometerData *data = self.motionManager.magnetometerData;
|
|
297
|
+
[self.motionManager stopMagnetometerUpdates];
|
|
298
|
+
|
|
299
|
+
if (data) {
|
|
300
|
+
double x = data.magneticField.x;
|
|
301
|
+
double y = data.magneticField.y;
|
|
302
|
+
double z = data.magneticField.z;
|
|
303
|
+
double magnitude = sqrt(x*x + y*y + z*z);
|
|
304
|
+
|
|
305
|
+
CDVPluginResult *result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDouble:magnitude];
|
|
306
|
+
[self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
|
|
307
|
+
} else {
|
|
308
|
+
CDVPluginResult *result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"Failed to get field strength"];
|
|
309
|
+
[self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
|
|
310
|
+
}
|
|
311
|
+
}];
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
- (void)onReset {
|
|
315
|
+
[self.motionManager stopMagnetometerUpdates];
|
|
316
|
+
[self.locationManager stopUpdatingHeading];
|
|
317
|
+
self.watchCallbackId = nil;
|
|
318
|
+
self.watchHeadingCallbackId = nil;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
@end
|