diff --git a/android/src/main/java/com/reactlibrary/CompassHeadingModule.java b/android/src/main/java/com/reactlibrary/CompassHeadingModule.java index 6558a5c..47b421c 100644 --- a/android/src/main/java/com/reactlibrary/CompassHeadingModule.java +++ b/android/src/main/java/com/reactlibrary/CompassHeadingModule.java @@ -5,13 +5,40 @@ import com.facebook.react.bridge.ReactMethod; import com.facebook.react.bridge.Callback; -public class CompassHeadingModule extends ReactContextBaseJavaModule { +import android.hardware.Sensor; +import android.hardware.SensorEvent; +import android.hardware.SensorEventListener; +import android.hardware.SensorManager; + +import android.content.Context; + +import com.facebook.react.modules.core.DeviceEventManagerModule; + +public class CompassHeadingModule extends ReactContextBaseJavaModule implements SensorEventListener { private final ReactApplicationContext reactContext; + private static Context mApplicationContext; + + private int mAzimuth = 0; // degree + private int mFilter = 1; + + private SensorManager sensorManager; + + private Sensor gsensor; + private Sensor msensor; + + private float[] mGravity = new float[3]; + private float[] mGeomagnetic = new float[3]; + + private float[] R = new float[9]; + private float[] I = new float[9]; + public CompassHeadingModule(ReactApplicationContext reactContext) { super(reactContext); this.reactContext = reactContext; + + mApplicationContext = reactContext.getApplicationContext(); } @Override @@ -20,8 +47,80 @@ public String getName() { } @ReactMethod - public void sampleMethod(String stringArgument, int numberArgument, Callback callback) { - // TODO: Implement some actually useful functionality - callback.invoke("Received numberArgument: " + numberArgument + " stringArgument: " + stringArgument); + public void start(int filter) { + + sensorManager = (SensorManager) mApplicationContext.getSystemService(Context.SENSOR_SERVICE); + + gsensor = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER); + msensor = sensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD); + + sensorManager.registerListener(this, gsensor, SensorManager.SENSOR_DELAY_GAME); + sensorManager.registerListener(this, msensor, SensorManager.SENSOR_DELAY_GAME); + + mFilter = filter; + } + + @ReactMethod + public void stop() { + if (sensorManager != null) { + sensorManager.unregisterListener(this); + } + } + + @Override + public void onSensorChanged(SensorEvent event) { + + final float alpha = 0.97f; + + synchronized (this) { + if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) { + + mGravity[0] = alpha * mGravity[0] + (1 - alpha) + * event.values[0]; + mGravity[1] = alpha * mGravity[1] + (1 - alpha) + * event.values[1]; + mGravity[2] = alpha * mGravity[2] + (1 - alpha) + * event.values[2]; + } + + if (event.sensor.getType() == Sensor.TYPE_MAGNETIC_FIELD) { + + mGeomagnetic[0] = alpha * mGeomagnetic[0] + (1 - alpha) + * event.values[0]; + mGeomagnetic[1] = alpha * mGeomagnetic[1] + (1 - alpha) + * event.values[1]; + mGeomagnetic[2] = alpha * mGeomagnetic[2] + (1 - alpha) + * event.values[2]; + + } + + boolean success = SensorManager.getRotationMatrix(R, I, mGravity, mGeomagnetic); + + if (success) { + + float orientation[] = new float[3]; + SensorManager.getOrientation(R, orientation); + + int newAzimuth = (int) Math.toDegrees(orientation[0]); + newAzimuth = (newAzimuth + 360) % 360; + + if (Math.abs(mAzimuth - newAzimuth) > mFilter) { + + mAzimuth = newAzimuth; + + getReactApplicationContext() + .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class) + .emit("HeadingUpdated", mAzimuth); + } + } + } + + + } + + + @Override + public void onAccuracyChanged(Sensor sensor, int accuracy) { + } } diff --git a/index.js b/index.js index 00780b5..7b63513 100644 --- a/index.js +++ b/index.js @@ -1,5 +1,29 @@ -import { NativeModules } from 'react-native'; +import { NativeModules, NativeEventEmitter } from 'react-native'; const { CompassHeading } = NativeModules; +let listener; + +//Monkey patching +let _start = CompassHeading.start; +CompassHeading.start = (update_rate, callback) => { + if (listener) { + CompassHeading.stop(); + } + + const compassEventEmitter = new NativeEventEmitter(CompassHeading); + listener = compassEventEmitter.addListener('HeadingUpdated', (degree) => { + callback(degree); + }); + + _start(update_rate === null ? 0 : update_rate); +} + +let _stop = CompassHeading.stop; +CompassHeading.stop = () => { + listener && listener.remove(); + listener = null; + _stop(); +} + export default CompassHeading; diff --git a/ios/CompassHeading.h b/ios/CompassHeading.h index f39015b..fcc0a6b 100644 --- a/ios/CompassHeading.h +++ b/ios/CompassHeading.h @@ -1,5 +1,6 @@ #import +#import -@interface CompassHeading : NSObject +@interface CompassHeading : RCTEventEmitter @end diff --git a/ios/CompassHeading.m b/ios/CompassHeading.m index e42d450..128c0c6 100644 --- a/ios/CompassHeading.m +++ b/ios/CompassHeading.m @@ -1,14 +1,76 @@ #import "CompassHeading.h" +#import +#import +#define kHeadingUpdated @"HeadingUpdated" + +@interface CompassHeading() +@property (strong, nonatomic) CLLocationManager *locationManager; +@end @implementation CompassHeading RCT_EXPORT_MODULE() -RCT_EXPORT_METHOD(sampleMethod:(NSString *)stringArgument numberParameter:(nonnull NSNumber *)numberArgument callback:(RCTResponseSenderBlock)callback) +- (instancetype)init { + if (self = [super init]) { + if ([CLLocationManager headingAvailable]) { + self.locationManager = [[CLLocationManager alloc] init]; + self.locationManager.delegate = self; + } + else { + NSLog(@"Heading not available"); + } + } + + return self; +} + +#pragma mark - RCTEventEmitter + +- (NSArray *)supportedEvents { + return @[kHeadingUpdated]; +} + +#pragma mark - CLLocationManagerDelegate + +- (void)locationManager:(CLLocationManager *)manager didUpdateHeading:(CLHeading *)newHeading { + if (newHeading.headingAccuracy < 0) { + return; + } + [self sendEventWithName:kHeadingUpdated body:@(newHeading.trueHeading)]; +} + +- (void)locationManager:(CLLocationManager *)manager didChangeAuthorizationStatus:(CLAuthorizationStatus)status { + NSLog(@"AuthoriationStatus changed: %i", status); +} + +- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error { + NSLog(@"Location manager failed: %@", error); +} + +- (BOOL)locationManagerShouldDisplayHeadingCalibration:(CLLocationManager *)manager +{ + CLLocationDirection accuracy = [[manager heading] headingAccuracy]; + return false; //accuracy <= 0.0f || accuracy > 10.0f; +} + +#pragma mark - React + +RCT_EXPORT_METHOD(start: (NSInteger) headingFilter) { + self.locationManager.headingFilter = headingFilter; + [self.locationManager startUpdatingHeading]; +} + +RCT_EXPORT_METHOD(stop) { + [self.locationManager stopUpdatingHeading]; +} + +RCT_EXPORT_MODULE() + ++ (BOOL)requiresMainQueueSetup { - // TODO: Implement some actually useful functionality - callback(@[[NSString stringWithFormat: @"numberArgument: %@ stringArgument: %@", numberArgument, stringArgument]]); + return NO; } @end diff --git a/package.json b/package.json index 90e08e8..24881c6 100644 --- a/package.json +++ b/package.json @@ -9,8 +9,8 @@ }, "repository": { "type": "git", - "url": "git+https://github.com/github_account/react-native-compass-heading.git", - "baseUrl": "https://github.com/github_account/react-native-compass-heading" + "url": "git+https://github.com/firofame/react-native-compass-heading.git", + "baseUrl": "https://github.com/firofame/react-native-compass-heading" }, "keywords": [ "react-native" diff --git a/react-native-compass-heading.podspec b/react-native-compass-heading.podspec index b70e5e6..a8625cd 100644 --- a/react-native-compass-heading.podspec +++ b/react-native-compass-heading.podspec @@ -9,12 +9,12 @@ Pod::Spec.new do |s| s.description = <<-DESC react-native-compass-heading DESC - s.homepage = "https://github.com/github_account/react-native-compass-heading" + s.homepage = "https://github.com/firofame/react-native-compass-heading" s.license = "MIT" # s.license = { :type => "MIT", :file => "FILE_LICENSE" } - s.authors = { "Your Name" => "yourname@email.com" } + s.authors = { "Your Name" => "firofame@gmail.com" } s.platform = :ios, "7.0" - s.source = { :git => "https://github.com/github_account/react-native-compass-heading.git", :tag => "#{s.version}" } + s.source = { :git => "https://github.com/firofame/react-native-compass-heading.git", :tag => "#{s.version}" } s.source_files = "ios/**/*.{h,m,swift}" s.requires_arc = true