0

Lastly I ran into problem that, I can't scan for beacons because of lack of ACCESS_COARSE_LOCATION or ACCESS_FINE_LOCATION.

I tried to fix it by using code found here, but it actually help me partially.

When this view appears enter image description here I click allow. After that I doesn't get this java.lang.SecurityException: Need ACCESS_COARSE_LOCATION or ACCESS_FINE_LOCATION permission to get scan results error anymore, but I can't still see my beacons and when I open settings view the location is turned off like on picture bellow.

enter image description here

When I turn on location by finger everything works ok, so I can see my beacons and app works as it should. And here is the question is these some kind of bug or I missed something to turn on location from code behind after access to device location is turned on?

For developing I use Nexus 5x with android 7.1.1.

EDITED: Code is copied from tutorial linked above, the fragment with button which starts beacon scanner:

 public void onBleClicked(View view)
    {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M){
            if (this.checkSelfPermission(Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {

                final AlertDialog.Builder builder = new AlertDialog.Builder(this);
                builder.setTitle("This app needs location access");
                builder.setMessage("Please grant location access so this app can detect beacons.");
                builder.setPositiveButton(android.R.string.ok, null);
                builder.setOnDismissListener(new DialogInterface.OnDismissListener() {
                    @Override
                    public void onDismiss(DialogInterface dialog) {

                            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                                requestPermissions(new String[]{Manifest.permission.ACCESS_COARSE_LOCATION}, PERMISSION_REQUEST_COARSE_LOCATION);
                            }
                    }
                });
                builder.show();
            }
    BleManager bleManager = new BleManager(this);
    bleManager.tryToTurnOnBle();
}

Fragment of manifest where permissions are declared:

<!-- app only for phones -->
<uses-feature android:name="android.hardware.telephony"/>

<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="com.google.android.providers.gsf.permission.READ_GSERVICES" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />

The bluetooth permissions are apparently in library.

What I found right now is fact that there is similar question to mine here. But this solution with redirecting user to location option screen is not seems to be clean one for me.

Community
  • 1
  • 1
MyWay
  • 1,011
  • 2
  • 14
  • 35
  • Could you please post your Manifest file and code for runtime permission check? – Madhan Dec 18 '16 at 15:17
  • @Madhan yeah of course. – MyWay Dec 18 '16 at 15:28
  • According to the documentation provided by Android you need to give the android.hardware.location.network or android.hardware.location.gps if your app targets API level 21 or higher. Please refer the Requesting User Permissions in the link: https://developer.android.com/guide/topics/location/strategies.html – Madhan Dec 19 '16 at 04:29
  • The below SO link will provide you a solution. Please try it. http://stackoverflow.com/a/33800252/6452886 – Madhan Dec 19 '16 at 04:33

2 Answers2

3

Location can be determined by two ways:

1.Using NETWORK_PROVIDER

2.Using GPS_PROVIDER

NETWORK_PROVIDER: It determines the location of the users using cell towers,wifi access points. It is commonly used for determining location inside the rooms or buildings. Here the GPS coordinates are not able to be obtained.

You can specify either

<uses-permission android:name=”android.permission.ACCESS_COARSE_LOCATION” />

or

<uses-permission android:name=”android.permission.ACCESS_FINE_LOCATION” />

in order to get location using the NETWORK_PROVIDER.

GPS_PROVIDER: It determines the location of the users using satellites. For this, the GPS coordinates are obtained and used for positioning. The GPS receiver in the smartphone receives the signals from satellites. These signals are processed and precise locations are determined.It works better in outdoors – direct sky/satellite views and communication occurs.

You need specify the permission

<uses-permission android:name=”android.permission.ACCESS_FINE_LOCATION” />

in order to use location from GPS_PROVIDER.

Fine locations: It gives better and accurate locations. So, that I recommend you to use this to get your beacon locations. It gives permission for using both GPS_PROVIDER and NETWORK_PROVIDER or GPS_PROVIDER only for determining the position.

Coarse locations: It provides less accurate locations.It gives permission for using NETWORK_PROVIDER only for determining the position.

Now, come to the implementation. - Declare the above said two permissions in the AnroidManifest.xml file: - In the java part, do the following: Request the permission if it not granted yet:

private void requestPermission(Activity activity) {
ActivityCompat.requestPermissions(activity, new String[]{Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.CALL_PHONE}, MainActivity.PERMISSION_REQUEST_CODE_LOCATION);
} 

When the above method is called, a dialog asking permission will appear. On selecting Allow or Deny, the below callback gets triggered.

In the onRequestPermissionsResult

@Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String permissions[], int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        switch (requestCode) {
            case PERMISSION_REQUEST_CODE_LOCATION:
                if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                    LocationManager locationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
                    boolean isGpsProviderEnabled = locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER);
                    boolean isNetworkProviderEnabled = locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER);
                    //Location permission is given. Check if the providers are available and start location updates.
                    if (isGpsProviderEnabled && isNetworkProviderEnabled) {
                        startLocationUpdates();
                    } else {
                        Log.d(TAG, "GPS and Network providers are disabled");
                    }
                } else if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_DENIED) {
                    boolean should = ActivityCompat.shouldShowRequestPermissionRationale(MainActivity.this, Manifest.permission.ACCESS_FINE_LOCATION);
                    if (should) {
                        ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, MainActivity.PERMISSION_REQUEST_CODE_LOCATION);
                    } else {
                        promptSettings();
                    }
                }
        }
    }

In the promptSettings() method, let the user to enable location from the Settings screen.

private void promptSettings() {
AlertDialog.Builder builder;

        builder = new AlertDialog.Builder(MainActivity.this);
        builder.setTitle(getResources().getString(R.string.unable_to_find_location));
        builder.setMessage(getResources().getString(R.string.message_denied_location_permission));
        builder.setCancelable(false);
        builder.setPositiveButton(getResources().getString(R.string.go_to_settings), (dialog, which) -> {
            dialog.dismiss();
            builder = null;
            if (!checkPermission(MainActivity.this)) {
                goToSettings();
            }
        });
        builder.show();
    }

In the check permissions method:

public boolean checkPermission(Context context) {
    int result = ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION);
    return result == PackageManager.PERMISSION_GRANTED;
}

The goToSettings() allows the user to go to Settings screen:

private void goToSettings() {
        Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
        Uri uri = Uri.fromParts("package", getPackageName(), null);
        intent.setData(uri);
        startActivityForResult(intent, 1);
    }

Note: You need to give the below permissions in the manifest to scan the beacons. I hope you are doing that, if not please do it.

<uses-permission android:name="android.permission.BLUETOOTH"/>
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
Madhan
  • 555
  • 5
  • 23
  • Thx for exhaustive answer, I already resolved problem but my solution is very similar to yours, so I'm sure that your answer would be helpful for other users. Helpful links: http://stackoverflow.com/questions/33043582/bluetooth-low-energy-startscan-on-android-6-0-does-not-find-devices/33045489#33045489 https://github.com/evothings/cordova-ble/issues/87 – MyWay Dec 30 '16 at 20:23
0

As of Android Marshmallow (6.0), Location must be turned on in settings for apps to scan for Bluetooth LE devices including beacons. This requirement is in addition to the requirement that apps get dynamic permissions. You can see code below to query for location services being turned on and to prompt the user to turn it on if needed.

private void verifyLocationServices()  {
        final LocationManager manager = (LocationManager) getSystemService(this.LOCATION_SERVICE);

        if (!manager.isProviderEnabled(LocationManager.NETWORK_PROVIDER)) {
            final AlertDialog.Builder builder = new AlertDialog.Builder(this);
            builder.setMessage("This app requires that location services be enabled.  Please enable location in settings.")
                    .setCancelable(false)
                    .setPositiveButton("OK", new DialogInterface.OnClickListener() {
                        public void onClick(final DialogInterface dialog, final int id) {
                            startActivity(new Intent(android.provider.Settings.ACTION_LOCATION_SOURCE_SETTINGS));
                        }
                    });
            final AlertDialog alert = builder.create();
            alert.show();
        }
}
davidgyoung
  • 63,876
  • 14
  • 121
  • 204