diff --git a/README.md b/README.md index b4e2183..d2d63df 100644 --- a/README.md +++ b/README.md @@ -65,9 +65,13 @@ protected List getPackages() { #### Methods +Open the system Settings to enable user to toggle Location on. + +The parameter `openInDetails` (android only) is used to open app details screen (android M+ only), so the user can toggle the permission in `Permissions` tab. + ```javascript -//Open the system Settings to enable user to toggle Location on -GPSState.openSettings(); +//openInDetails defaults to true +GPSState.openSettings(openInDetails:boolean); ``` ```javascript diff --git a/android/src/main/java/br/com/dopaminamob/gpsstate/GPSStateModule.java b/android/src/main/java/br/com/dopaminamob/gpsstate/GPSStateModule.java index ccfabad..ed7936b 100644 --- a/android/src/main/java/br/com/dopaminamob/gpsstate/GPSStateModule.java +++ b/android/src/main/java/br/com/dopaminamob/gpsstate/GPSStateModule.java @@ -12,8 +12,10 @@ import android.location.Location; import android.location.LocationListener; import android.location.LocationManager; +import android.net.Uri; import android.os.Build; import android.os.Bundle; +import android.provider.Settings; import android.support.annotation.Nullable; import android.support.v4.app.ActivityCompat; import android.support.v4.content.PermissionChecker; @@ -33,10 +35,12 @@ import java.util.HashMap; import java.util.Map; + /** * Created by neuber on 14/08/17. */ -public class GPSStateModule extends ReactContextBaseJavaModule implements ActivityCompat.OnRequestPermissionsResultCallback/*, ActivityEventListener, LocationListener, GpsStatus.Listener*/ { + +public class GPSStateModule extends ReactContextBaseJavaModule implements ActivityCompat.OnRequestPermissionsResultCallback /*, ActivityEventListener, LocationListener, GpsStatus.Listener*/ { private static final int STATUS_NOT_DETERMINED = 0; private static final int STATUS_RESTRICTED = 1; private static final int STATUS_DENIED = 2; @@ -47,12 +51,14 @@ public class GPSStateModule extends ReactContextBaseJavaModule implements Activi private static final int REQUEST_CODE_AUTHORIZATION = 1; private static final String EVENT_STATUS_CHANGE = "OnStatusChange"; - + private boolean isListen = false; private int targetSdkVersion = -1; - private BroadcastReceiver mGpsSwitchStateReceiver = null; - private LocationManager locationManager; - + private int currentStatus = STATUS_NOT_DETERMINED; + + private BroadcastReceiver mGpsSwitchStateReceiver = null; + private LocationManager locationManager; + public GPSStateModule(ReactApplicationContext reactContext) { super(reactContext); locationManager = (LocationManager) reactContext.getSystemService(reactContext.LOCATION_SERVICE); @@ -82,8 +88,22 @@ public Map getConstants() { constants.put("AUTHORIZED_WHENINUSE", STATUS_AUTHORIZED_WHENINUSE); return constants; } - - + + @Override + public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults){ + if(requestCode==REQUEST_CODE_AUTHORIZATION){ + int status = STATUS_NOT_DETERMINED; + + if(grantResults.length>0) { + int result = grantResults[0]; + status = (result == PackageManager.PERMISSION_GRANTED) ? STATUS_AUTHORIZED : STATUS_DENIED; + } + sendEvent(status); + } + } + + + @ReactMethod public void _startListen() { _stopListen(); @@ -112,8 +132,20 @@ public void _getStatus(Promise promise) { } @ReactMethod - public void _openSettings(){ - Intent callGPSSettingIntent = new Intent(android.provider.Settings.ACTION_LOCATION_SOURCE_SETTINGS); + public void _openSettings(boolean openDetails){ + Intent callGPSSettingIntent = new Intent(); + String packageName = getReactApplicationContext().getPackageName(); + String intentAction = Settings.ACTION_APPLICATION_DETAILS_SETTINGS; + if(openDetails && isMarshmallowOrAbove()){ + intentAction = android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS; + + Uri uri = Uri.fromParts("package", packageName, null); + callGPSSettingIntent.setData(uri); + + waitForPermissionBecomeGranted(); + } + + callGPSSettingIntent.setAction(intentAction); getCurrentActivity().startActivityForResult(callGPSSettingIntent, 0); } @@ -125,13 +157,12 @@ public void _requestAuthorization(){ int getGpsState(){ - int status = STATUS_NOT_DETERMINED; - boolean enabled = locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER); - - //TODO check permission to inform the correct status - if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M || targetSdkVersion >= Build.VERSION_CODES.M) { - int permission = ActivityCompat.checkSelfPermission(getReactApplicationContext(), Manifest.permission.ACCESS_FINE_LOCATION); - boolean isGranted = permission == PackageManager.PERMISSION_GRANTED; + int status; + boolean enabled = isGpsEnabled(); + + if(isMarshmallowOrAbove()) { + boolean isGranted = isPermissionGranted(); + if(enabled) { if(isGranted){ status = STATUS_AUTHORIZED; @@ -144,9 +175,23 @@ int getGpsState(){ }else{ status = (enabled ? STATUS_AUTHORIZED : STATUS_RESTRICTED); } - + + currentStatus = status; return status; } + + int getPermission(){ + return ActivityCompat.checkSelfPermission(getReactApplicationContext(), Manifest.permission.ACCESS_FINE_LOCATION); + } + + boolean isPermissionGranted(){ + int permission = getPermission(); + return permission == PackageManager.PERMISSION_GRANTED; + } + + boolean isGpsEnabled(){ + return locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER); + } void sendEvent(int status){ @@ -156,54 +201,45 @@ void sendEvent(int status){ reactContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class).emit(EVENT_STATUS_CHANGE, params); } - - /* - @Override - public void onLocationChanged(Location location) {} - - @Override - public void onStatusChanged(String provider, int status, Bundle extras) { - Toast.makeText(getReactApplicationContext(), "onStatusChanged: ["+provider+"]"+status, Toast.LENGTH_LONG).show(); - } - - @Override - public void onGpsStatusChanged(int event) { - Toast.makeText(getReactApplicationContext(), "onGpsStatusChanged: "+event, Toast.LENGTH_LONG).show(); - } - - @Override - public void onProviderEnabled(String provider) { - Toast.makeText(getReactApplicationContext(), "onProviderEnabled: "+provider, Toast.LENGTH_LONG).show(); - } - - @Override - public void onProviderDisabled(String provider) { - Toast.makeText(getReactApplicationContext(), "onProviderDisabled: "+provider, Toast.LENGTH_LONG).show(); - } - - @Override - public void onActivityResult(Activity activity, int requestCode, int resultCode, Intent data) { - int rc = resultCode; - Toast.makeText(getReactApplicationContext(), "onActivityResult: "+rc, Toast.LENGTH_LONG).show(); - } - - @Override - public void onNewIntent(Intent intent) {} - */ - - @Override - public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults){ - if(requestCode==REQUEST_CODE_AUTHORIZATION){ - int status = STATUS_NOT_DETERMINED; - - if(grantResults.length>0) { - int result = grantResults[0]; - status = (result == PackageManager.PERMISSION_GRANTED) ? STATUS_AUTHORIZED : STATUS_DENIED; - } - sendEvent(status); - } + + boolean isMarshmallowOrAbove(){ + return Build.VERSION.SDK_INT >= Build.VERSION_CODES.M || targetSdkVersion >= Build.VERSION_CODES.M; } - + + boolean isPermissionEquals(int expectedPerm){ + return currentStatus == expectedPerm; + } + + boolean isAuthorized(){ + return isPermissionEquals(STATUS_AUTHORIZED); + } + + boolean isDenied(){ + return isPermissionEquals(STATUS_DENIED); + } + + boolean isUnknow(){ + return isPermissionEquals(STATUS_NOT_DETERMINED); + } + + void waitForPermissionBecomeGranted(){ + final Ticker ticker = new Ticker(); + ticker.setInterval(3000); + ticker.setMaxTicks(30); + ticker.startTick(new TickerCallBack() { + @Override + public void tick() { + if(isPermissionGranted()){ + ticker.stopTick(); + sendEvent(getGpsState()); + } + } + }); + } + + + + private final class GPSProvideChangeReceiver extends BroadcastReceiver { @Override diff --git a/android/src/main/java/br/com/dopaminamob/gpsstate/Ticker.java b/android/src/main/java/br/com/dopaminamob/gpsstate/Ticker.java new file mode 100644 index 0000000..2b1a463 --- /dev/null +++ b/android/src/main/java/br/com/dopaminamob/gpsstate/Ticker.java @@ -0,0 +1,72 @@ +package br.com.dopaminamob.gpsstate; + +public class Ticker { + private int maxTicks = 10; + private int interval = 1; + private int ticksCount = 0; + private boolean running = false; + private TickerCallBack callBack; + + public void startTick(TickerCallBack cb){ + running = true; + callBack = cb; + tick(); + } + + public void stopTick(){ + running = false; + ticksCount = 0; + } + + private void tick(){ + if(!reachedMaxTicks() && hasCallback() && isRunning()){ + new java.util.Timer().schedule( + new java.util.TimerTask() { + @Override + public void run() { + callBack.tick(); + ticksCount ++; + + tick(); + } + }, + getInterval() + ); + }else{ + stopTick(); + } + } + + + public int getMaxTicks() { + return maxTicks; + } + + public void setMaxTicks(int maxTicks) { + this.maxTicks = maxTicks; + } + + public int getInterval() { + return interval; + } + + public void setInterval(int interval) { + this.interval = interval; + } + + public boolean isRunning(){ + return running; + } + + public boolean hasCallback(){ + return callBack!=null; + } + + public boolean reachedMaxTicks(){ + return ticksCount >= getMaxTicks(); + } +} + +interface TickerCallBack { + void tick(); +} diff --git a/index.js b/index.js index 1d3b00b..00fb171 100644 --- a/index.js +++ b/index.js @@ -3,11 +3,12 @@ const {NativeModules, NativeEventEmitter, Platform} = require('react-native'); const {GPSState} = NativeModules; +const isDroid = Platform.OS=='android'; +const isIOS = Platform.OS=='ios'; const gpsStateEmitter = new NativeEventEmitter(GPSState); var subscription = null; var listener = null; -const isDroid = Platform.OS=='android'; -const isIOS = Platform.OS=='ios'; +var isListening = true; subscription = gpsStateEmitter.addListener('OnStatusChange', (response)=>{ if(listener){ @@ -18,32 +19,30 @@ subscription = gpsStateEmitter.addListener('OnStatusChange', (response)=>{ status = response.status; } - if(status) + if(status && isListening) listener.apply(this, [status]); } }); GPSState.addListener = (callback)=>{ if(typeof callback == 'function'){ + isListening = true; listener = callback; GPSState._startListen(); } } GPSState.removeListener = (callback)=>{ - if(subscription){ - GPSState._stopListen(); - subscription.remove(); - subscription = null; - } + isListening = false + GPSState._stopListen(); } GPSState.getStatus = ()=>{ return GPSState._getStatus(); } -GPSState.openSettings = ()=>{ - GPSState._openSettings(); +GPSState.openSettings = (openInDetails=true)=>{ + GPSState._openSettings(openInDetails); } GPSState.requestAuthorization = (authType)=>{ diff --git a/package.json b/package.json index 58087a2..c981835 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "react-native-gps-state", "description": "React Native Listener for GPS status changes", - "version": "0.0.2", + "version": "1.2.0", "main": "index.js", "repository": { "type": "git",