How to programmatically enable and disable GPS in Ionic Framework or Cordova

20,928

Solution 1

As far as I'm aware there isn't the ability to directly disable GPS from your app.

If you are trying to allow the user to disable GPS, I achieved this by adding a setting to localStorage that the user can change on the settings page in my app. Every time I check for GPS in my location factory, I check against the setting and run the error callback if the setting was disabled.

There may be some sort of call which can automatically open the location settings page on your device as I have seen other apps do this, but I'm not sure if that is supported in Cordova/Ionic yet.

Solution 2

I wanna open GPS automatically according to my requirements.
So I used cordova-plugin-request-location-accuracy
When GPS is off, it request user to open GPS..It's very helpful for me..
Ionic1
https://github.com/dpa99c/cordova-plugin-request-location-accuracy

Ionic2
https://ionicframework.com/docs/native/location-accuracy/
Best Regards....

Solution 3

As far as I know controlling the device location is not possible as there isn't any Ionic or Cordova plugin available for this purpose. One can get the device position using the Geolocation plugin. But this doesn't work in case the device GPS is off. I have managed a work around to enable device location using a JSInterface bridge. This solution is valid only for the ionic android platform and honestly I would recommend using this only if turning on the device GPS is extremely crucial to your application.

Explanation: Google LocationServices gives us a provision of enabling device location. I have created a bridge between ionic and native platform. My ionic page requests for the device location using this bridge. ex: NativeBridge.functionNameInActivity

Native function requesting device position<----->JSInterface Bridge<---->Ionic function

1. Below is the code for the MainActivity which extends CordovaActivity:

public class MainActivity extends CordovaActivity implements GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener, ResultCallback<LocationSettingsResult> {

    public static Context mContext;
    private Intent mIntent;
    private final int REQUEST_CODE_LOCATION = 101;
    private final int REQUEST_CODE_LOCATION_SETTINGS = 102;

    protected GoogleApiClient mGoogleApiClient;
    protected LocationRequest locationRequest;
    public static double LATITUDE, LONGITUDE;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mContext = getApplicationContext();
        loadUrl(launchUrl);
        mIntent = new Intent(this, LocationService.class);
        final WebView webView = (WebView) appView.getEngine().getView();
        webView.addJavascriptInterface(this.getJavaScriptHandler(), "NativeBridge");
    }


    @Override
    public void onDestroy() {
        super.onDestroy();
        stopService(mIntent);
    }


    public void configureLocationClient() {
        final int intervalInSecs = 30;
        final int fastestIntervalInSecs = 5;
        final int milliSecondMultiplier = 1000;
        mGoogleApiClient = new GoogleApiClient.Builder(this)
                .addApi(LocationServices.API)
                .addConnectionCallbacks(this)
                .addOnConnectionFailedListener(this).build();
        mGoogleApiClient.connect();
        locationRequest = LocationRequest.create();
        locationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
        locationRequest.setInterval(intervalInSecs * milliSecondMultiplier);
        locationRequest.setFastestInterval(fastestIntervalInSecs * milliSecondMultiplier);
    }

    public void checkLocationPermission() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
            requestPermissions(new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, REQUEST_CODE_LOCATION);
        } else {
            configureLocationClient();
        }
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        if (requestCode == REQUEST_CODE_LOCATION_SETTINGS) {
            if (resultCode == RESULT_OK) {
                startService(mIntent);
            } else {
                //Display a message requesting location access for further operations
            }
        }
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);

        Map<String, Integer> perms;
        switch (requestCode) {
            case REQUEST_CODE_LOCATION:
                perms = new HashMap<String, Integer>();
                perms.put(Manifest.permission.ACCESS_FINE_LOCATION, PackageManager.PERMISSION_GRANTED);

                for (int i = 0; i < permissions.length; i++) {
                    perms.put(permissions[i], grantResults[i]);
                }

                if (perms.get(Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
                    //Start location fetching service
                    configureLocationClient();
                } else {
                    // Location Permission Denied
                    DisplayUtils.displayToast(mContext, AppConstants.MSG_PERMISSIONS_LOCATION);
                    return;
                }

                break;

            default:
                super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        }
    }

    @Override
    public void onConnected(@Nullable Bundle bundle) {

        LocationSettingsRequest.Builder builder = new LocationSettingsRequest.Builder()
                .addLocationRequest(locationRequest);
        builder.setAlwaysShow(true);
        PendingResult<LocationSettingsResult> result =
                LocationServices.SettingsApi.checkLocationSettings(
                        mGoogleApiClient,
                        builder.build()
                );

        result.setResultCallback(this);

    }

    @Override
    public void onConnectionSuspended(int i) {
    }

    @Override
    public void onConnectionFailed(@NonNull ConnectionResult connectionResult) {
    }

    @Override
    public void onResult(@NonNull LocationSettingsResult locationSettingsResult) {
        final Status status = locationSettingsResult.getStatus();
        switch (status.getStatusCode()) {
            case LocationSettingsStatusCodes.SUCCESS:
                /*Start location fetching service*/
                startService(mIntent);
                break;

            case LocationSettingsStatusCodes.RESOLUTION_REQUIRED:
                /*Location settings are not satisfied. Show the user a dialog by calling startResolutionForResult(), and check the result in onActivityResult().*/
                try {
                    status.startResolutionForResult(MainActivity.this, REQUEST_CODE_LOCATION_SETTINGS);
                } catch (IntentSender.SendIntentException e) {
                    e.printStackTrace();
                }
                break;

            case LocationSettingsStatusCodes.SETTINGS_CHANGE_UNAVAILABLE:
                /*Location settings unavailable*/
                break;
            default:
                break;
        }
    }

    public JavaScriptHandler getJavaScriptHandler() {
        return new JavaScriptHandler(this.getApplicationContext());
    }

    /***
     * Javascript handler
     ***/
    public class JavaScriptHandler {
        CordovaActivity parentActivity;
        private Context mContext;

        public JavaScriptHandler(final CordovaActivity activity) {
            this.parentActivity = activity;
        }

        public JavaScriptHandler(final Context context) {
            this.mContext = context;
        }

        @JavascriptInterface
        public boolean ifLocationPermissionGranted() {
            return ContextCompat.checkSelfPermission(mContext, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED;
        }

        @JavascriptInterface
        public boolean ifLocationAccessible() {
            LocationManager mLocationManager = (LocationManager) getSystemService(LOCATION_SERVICE);
            return mLocationManager.isProviderEnabled(LocationManager.GPS_PROVIDER);
        }

        @JavascriptInterface
        public void startLocationService() {
            checkLocationPermission();
        }

        @JavascriptInterface
        public String getLatitude() {
            return String.valueOf(LATITUDE);
        }

        @JavascriptInterface
        public String getLongitude() {
            return String.valueOf(LONGITUDE);
        }
    }
}

2. Location Service class:

public class LocationService extends Service {
    private static String TAG = "TAG-LocationService";
    private LocationManager mLocationManager;
    private static final int LOCATION_INTERVAL = 2000;
    private static final float LOCATION_DISTANCE = 0f;

    private class LocationListener implements android.location.LocationListener {
        Location mLastLocation;

        public LocationListener(String provider) {
            mLastLocation = new Location(provider);
        }

        @Override
        public void onLocationChanged(Location location) {
            mLastLocation.set(location);
            if (location != null) {
                MainActivity.LATITUDE = mLastLocation.getLatitude();
                MainActivity.LONGITUDE = mLastLocation.getLongitude();
            }
        }

        @Override
        public void onProviderDisabled(String provider) {
        }

        @Override
        public void onProviderEnabled(String provider) {
        }

        @Override
        public void onStatusChanged(String provider, int status, Bundle extras) {
        }
    }

    LocationListener[] mLocationListeners = new LocationListener[]{
            new LocationListener(LocationManager.GPS_PROVIDER), new LocationListener(LocationManager.NETWORK_PROVIDER),
    };

    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }


    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        super.onStartCommand(intent, flags, startId);
        return START_STICKY;
    }

    @Override
    public void onCreate() {
        initializeLocationManager();
        try {
            mLocationManager.requestLocationUpdates(
                    LocationManager.NETWORK_PROVIDER, LOCATION_INTERVAL, LOCATION_DISTANCE,
                    mLocationListeners[1]);

        } catch (SecurityException ex) {
            ex.printStackTrace();
        } catch (IllegalArgumentException ex) {
            ex.printStackTrace();
        }
        try {
            mLocationManager.requestLocationUpdates(
                    LocationManager.GPS_PROVIDER, LOCATION_INTERVAL, LOCATION_DISTANCE,
                    mLocationListeners[0]);
        } catch (SecurityException ex) {
            ex.printStackTrace();
        } catch (IllegalArgumentException ex) {
            ex.printStackTrace();
        }
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        if (mLocationManager != null) {
            for (int i = 0; i < mLocationListeners.length; i++) {
                if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
                    mLocationManager.removeUpdates(mLocationListeners[i]);
                }
            }
        }
    }

    private void initializeLocationManager() {
        if (mLocationManager == null) {
            mLocationManager = (LocationManager) getApplicationContext().getSystemService(Context.LOCATION_SERVICE);
        }
    }
}

3. Code for your JS controller file:

MyDemoApp.controller('LocationController', function ($scope, $cordovaToast, $ionicPopup) {
$scope.goForIt = function () {
    if (NativeBridge.ifLocationPermissionGranted()) {
        if (NativeBridge.ifLocationAccessible()) {
            $scope.getDevicePosition();
        } else {
            $scope.showLocationAlert();
        }
    } else {
        $scope.showLocationAlert();
    }
}

$scope.getDevicePosition = function () {
    var positionLatitude = parseFloat(NativeBridge.getLatitude());
    var positionLongitude = parseFloat(NativeBridge.getLongitude());
}

$scope.showLocationAlert = function () {
    var confirmPopup = $ionicPopup.confirm({
        title: 'Location Service required for further operation',
        template: 'Grant permission to access Location?'
    });
    confirmPopup.then(function (res) {
        if (res) {
            NativeBridge.startLocationService();
        } else {
            $cordovaToast.showShortCenter("Location access required");
        }
    });
};

});

4. Add below line in the dependencies of your build.gradle:

compile 'com.google.android.gms:play-services-location:10.0.1'

5. Add permission to your manifest

<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />    
<uses-feature android:name="android.hardware.location.gps" />

6. Declare Service in your manifest:

<service android:name=".LocationService"></service>

On calling this function, you will see a similar dialog asking to enable device location.

enter image description here

Again, I would recommend using this only if enabling the user location is extremely important to your app functionality because its a lot of work. Feel free to criticize :-)

Share:
20,928
balfonso
Author by

balfonso

Updated on April 17, 2020

Comments

  • balfonso
    balfonso about 4 years

    My problem is simple. I need a way to turn on and off GPS using a button inside an Ionic app. I've check the docs and read this ng-cordova plugin http://ngcordova.com/docs/plugins/geolocation/ but they don't seem have this funcction. Am I just missing something or is this even possible in Ionic Framework? Thanks!

  • balfonso
    balfonso over 8 years
    Hi @SM3RKY! From what I understand on your answer.. you have a GPS (on/off) on your app's settings page. This settings is independent from the device's GPS settings. So in short, your app's GPS settings works only if the Device's GPS settings is already on but still you won't be able to turn on the device's GPS settings. It's kind of like telling your app to use or not to use the GPS even if it's enabled. Am I correct?
  • SM3RKY
    SM3RKY over 8 years
    Yes that is correct, Is that what you are looking for, or are you trying to enable the device GPS setting from the app of it is disabled, as I am unsure as to whether you can do the latter
  • balfonso
    balfonso over 8 years
    Originally I've wanted a direct on/off for the GPS inside the app. But after @Joerg commented, I realized I needed to make some more research and I finally came to the conclusion that the latter (to open the settings page) would be the best option. I haven't applied that though on my app yet. I've moved on to other tasks in the mean time but will definitely apply it later. At this point, I feel that there might already be a plugin for this but haven't made any searches if in fact Ionic and Cordova has this. Thanks for the help btw :D
  • SM3RKY
    SM3RKY over 8 years
    Great, When you do sort out a way to open the settings page for location please post it here as I would be interested in that too. Good luck! :)
  • AbhiRam
    AbhiRam almost 6 years
  • Kapil Soni
    Kapil Soni about 5 years
    @sawpyae its working perfectly but if we have to store any value from this.locationAccuracy.request this function its not working perfectly?
  • Kapil Soni
    Kapil Soni about 5 years
    Sanket: hi sir....how to manage below scenario: suppose alert is open then we have click ok button and gps is enable but then we have disable the gps agin then click harware back button of my mobile now alert is not open again please tell me how to manage above scenario?
  • Terry Windwalker
    Terry Windwalker almost 2 years
    @AbhiRam Question seems removed.