content: package com.syarul.rnlocation;
  
  import android.location.Location;
  import android.location.LocationManager;
  import android.location.LocationListener;
  import android.content.Context;
  import android.support.annotation.Nullable;
  import android.util.Log;
  import android.os.Bundle;
  
  import com.facebook.react.bridge.Arguments;
  import com.facebook.react.bridge.ReactApplicationContext;
  import com.facebook.react.bridge.ReactContext;
  import com.facebook.react.bridge.ReactContextBaseJavaModule;
  import com.facebook.react.bridge.ReactMethod;
  import com.facebook.react.bridge.ReadableMap;
  import com.facebook.react.bridge.WritableMap;
  import com.facebook.react.modules.core.DeviceEventManagerModule.RCTDeviceEventEmitter;
  
  public class RNLocationModule extends ReactContextBaseJavaModule{
  
    // React Class Name as called from JS
    public static final String REACT_CLASS = "RNLocation";
    // Unique Name for Log TAG
    public static final String TAG = RNLocationModule.class.getSimpleName();
  
    private static final float RCT_DEFAULT_LOCATION_ACCURACY = 1;
    public static int POSITION_UNAVAILABLE = 2;
  
    // Save last Location Provided
    private Location mLastLocation;
    private LocationListener mLocationListener;
    private LocationManager locationManager;
  
    //The React Native Context
    ReactApplicationContext mReactContext;
  
  
    // Constructor Method as called in Package
    public RNLocationModule(ReactApplicationContext reactContext) {
      super(reactContext);
      // Save Context for later use
      mReactContext = reactContext;
  
      locationManager = (LocationManager) mReactContext.getSystemService(Context.LOCATION_SERVICE);
    }
  
  
    @Override
    public String getName() {
      return REACT_CLASS;
    }
  
    private static class LocationOptions {
      private final long timeout;
      private final double maximumAge;
      private final boolean highAccuracy;
      private final float distanceFilter;
  
      private LocationOptions(
        long timeout,
        double maximumAge,
        boolean highAccuracy,
        float distanceFilter) {
        this.timeout = timeout;
        this.maximumAge = maximumAge;
        this.highAccuracy = highAccuracy;
        this.distanceFilter = distanceFilter;
      }
  
      private static LocationOptions fromReactMap(ReadableMap map) {
        // precision might be dropped on timeout (double -> int conversion), but that's OK
        long timeout =
          map.hasKey("timeout") ? (long) map.getDouble("timeout") : Long.MAX_VALUE;
        double maximumAge =
          map.hasKey("maximumAge") ? map.getDouble("maximumAge") : Double.POSITIVE_INFINITY;
        boolean highAccuracy = !map.hasKey("enableHighAccuracy") || map.getBoolean("enableHighAccuracy");
        float distanceFilter = map.hasKey("distanceFilter") ?
          (float) map.getDouble("distanceFilter") :
          RCT_DEFAULT_LOCATION_ACCURACY;
  
        return new LocationOptions(timeout, maximumAge, highAccuracy, distanceFilter);
      }
    }
  
    /*
     * Location permission request (Not implemented yet)
     */
    @ReactMethod
    public void requestWhenInUseAuthorization(){
      Log.i(TAG, "Requesting authorization");
    }
  
    @Nullable
    private static String getValidProvider(LocationManager locationManager, boolean highAccuracy) {
      String provider =
        highAccuracy ? LocationManager.GPS_PROVIDER : LocationManager.NETWORK_PROVIDER;
      if (!locationManager.isProviderEnabled(provider)) {
        provider = provider.equals(LocationManager.GPS_PROVIDER)
          ? LocationManager.NETWORK_PROVIDER
          : LocationManager.GPS_PROVIDER;
        if (!locationManager.isProviderEnabled(provider)) {
          return null;
        }
      }
      return provider;
    }
  
    private void emitError(int code, String message) {
      WritableMap error = Arguments.createMap();
      error.putInt("code", code);
  
      if (message != null) {
        error.putString("message", message);
      }
  
      getReactApplicationContext().getJSModule(RCTDeviceEventEmitter.class)
        .emit("geolocationError", error);
    }
  
    /*
     * Location Callback as called by JS
     */
    @ReactMethod
    public void startUpdatingLocation(ReadableMap options) {
      LocationOptions locationOptions = LocationOptions.fromReactMap(options);
      String provider = getValidProvider(locationManager, locationOptions.highAccuracy);
  
      if (provider == null) {
        emitError(POSITION_UNAVAILABLE, "No location provider available.");
        return;
      }
  
      mLocationListener = new LocationListener(){
        @Override
        public void onStatusChanged(String provider,int status,Bundle extras){
          WritableMap params = Arguments.createMap();
          params.putString("provider", provider);
          params.putInt("status", status);
  
          sendEvent(mReactContext, "providerStatusChanged", params);
        }
  
        @Override
        public void onProviderEnabled(String provider){
          sendEvent(mReactContext, "providerEnabled", Arguments.createMap());
        }
  
        @Override
        public void onProviderDisabled(String provider){
          sendEvent(mReactContext, "providerDisabled", Arguments.createMap());
        }
  
        @Override
        public void onLocationChanged(Location loc){
          mLastLocation = loc;
          if (mLastLocation != null) {
            try {
              double longitude;
              double latitude;
              double speed;
              double altitude;
              double accuracy;
              double course;
  
              // Receive Longitude / Latitude from (updated) Last Location
              longitude = mLastLocation.getLongitude();
              latitude = mLastLocation.getLatitude();
              speed = mLastLocation.getSpeed();
              altitude = mLastLocation.getAltitude();
              accuracy = mLastLocation.getAccuracy();
              course = mLastLocation.getBearing();
  
              Log.i(TAG, "Got new location. Lng: " +longitude+" Lat: "+latitude);
  
              // Create Map with Parameters to send to JS
              WritableMap params = Arguments.createMap();
              params.putDouble("longitude", longitude);
              params.putDouble("latitude", latitude);
              params.putDouble("speed", speed);
              params.putDouble("altitude", altitude);
              params.putDouble("accuracy", accuracy);
              params.putDouble("course", course);
  
              // Send Event to JS to update Location
              sendEvent(mReactContext, "locationUpdated", params);
            } catch (Exception e) {
              e.printStackTrace();
              Log.i(TAG, "Location services disconnected.");
            }
          }
        }
      };
  
      locationManager.requestLocationUpdates(provider, 1000, locationOptions.distanceFilter, mLocationListener);
    }
  
    @ReactMethod
    public void stopUpdatingLocation() {
      try {
        locationManager.removeUpdates(mLocationListener);
        Log.i(TAG, "Location service disabled.");
      }catch(Exception e) {
        e.printStackTrace();
      }
    }
  
    /*
     * Internal function for communicating with JS
     */
    private void sendEvent(ReactContext reactContext, String eventName, @Nullable WritableMap params) {
      if (reactContext.hasActiveCatalystInstance()) {
        reactContext
          .getJSModule(RCTDeviceEventEmitter.class)
          .emit(eventName, params);
      } else {
        Log.i(TAG, "Waiting for CatalystInstance...");
      }
    }
  }
  