Permiso de verificación de Android para LocationManager

97

Estoy tratando de que se muestren las coordenadas GPS cuando hago clic en un botón en el diseño de mi actividad. El siguiente es el método que se llama cuando hago clic en el botón:

public void getLocation(View view) {
    TextView tv = (TextView) findViewById(R.id.gps_coord_view);
    LocationManager lm = (LocationManager) getSystemService(LOCATION_SERVICE);
    Location loc = lm.getLastKnownLocation(LocationManager.GPS_PROVIDER);
    tv.setText("Latitude: " + loc.getLatitude() + "\nLongitude: " + loc.getLongitude());
}

Recibo un error que dice

La llamada requiere un permiso que puede ser rechazado por el usuario. El código debe verificar explícitamente si hay permiso disponible.

Ya he concedido estos permisos en mi AndroidManifest. El error se soluciona y la aplicación se compila cuando agrego lo siguiente antes de llamar lm.getLastKnownLocation:

if (checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED
        && checkSelfPermission(Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
    return;
}

Sin embargo, la aplicación se bloquea cuando presiono el botón que llama a getLocation cuando se hace clic en él. Que esta pasando? ¿Existe una forma mejor o más sencilla de obtener las coordenadas GPS del dispositivo?

usuario
fuente
Probablemente podría adjuntar un registro de excepciones.
Milán
1
Use ContextCompat.checkSelfPermission (contexto, permiso) y asegúrese de haber mencionado los permisos apropiados en el manifiesto
Snehal Poyrekar

Respuestas:

149

Con el nivel de API de Android (23), debemos verificar los permisos. https://developer.android.com/training/permissions/requesting.html

Tuve el mismo problema, pero lo siguiente funcionó para mí y puedo recuperar los datos de ubicación con éxito:

(1) Asegúrese de tener sus permisos enumerados en el manifiesto:

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

(2) Asegúrese de solicitar permisos al usuario:

if ( ContextCompat.checkSelfPermission( this, android.Manifest.permission.ACCESS_COARSE_LOCATION ) != PackageManager.PERMISSION_GRANTED ) {

            ActivityCompat.requestPermissions( this, new String[] {  android.Manifest.permission.ACCESS_COARSE_LOCATION  },
                                                LocationService.MY_PERMISSION_ACCESS_COURSE_LOCATION );
        }

(3) Asegúrese de utilizar ContextCompat, ya que es compatible con niveles de API anteriores.

(4) En su servicio de ubicación, o clase que inicializa su LocationManager y obtiene la última ubicación conocida, debemos verificar los permisos:

if ( Build.VERSION.SDK_INT >= 23 &&
             ContextCompat.checkSelfPermission( context, android.Manifest.permission.ACCESS_FINE_LOCATION ) != PackageManager.PERMISSION_GRANTED &&
             ContextCompat.checkSelfPermission( context, android.Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
            return  ;
        }

(5) Este enfoque solo me funcionó después de incluir @TargetApi (23) en la parte superior de mi método initLocationService.

(6) También agregué esto a mi compilación de gradle:

compile 'com.android.support:support-v4:23.0.1'

Aquí está mi LocationService como referencia:

public class LocationService implements LocationListener  {

    //The minimum distance to change updates in meters
    private static final long MIN_DISTANCE_CHANGE_FOR_UPDATES = 0; // 10 meters

    //The minimum time between updates in milliseconds
    private static final long MIN_TIME_BW_UPDATES = 0;//1000 * 60 * 1; // 1 minute

    private final static boolean forceNetwork = false;

    private static LocationService instance = null;

    private LocationManager locationManager;
    public Location location;
    public double longitude;
    public double latitude; 


    /**
     * Singleton implementation
     * @return
     */
    public static LocationService getLocationManager(Context context)     {
        if (instance == null) {
            instance = new LocationService(context);
        }
        return instance;
    }

    /**
     * Local constructor
     */
    private LocationService( Context context )     {

        initLocationService(context); 
        LogService.log("LocationService created");
    }



    /**
     * Sets up location service after permissions is granted
     */
    @TargetApi(23)
    private void initLocationService(Context context) {


        if ( Build.VERSION.SDK_INT >= 23 &&
             ContextCompat.checkSelfPermission( context, android.Manifest.permission.ACCESS_FINE_LOCATION ) != PackageManager.PERMISSION_GRANTED &&
             ContextCompat.checkSelfPermission( context, android.Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
            return  ;
        }

        try   {
            this.longitude = 0.0;
            this.latitude = 0.0;
            this.locationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);

            // Get GPS and network status
            this.isGPSEnabled = locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER);
            this.isNetworkEnabled = locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER);

            if (forceNetwork) isGPSEnabled = false;

            if (!isNetworkEnabled && !isGPSEnabled)    {
                // cannot get location
                this.locationServiceAvailable = false;
            }
            //else
            {
                this.locationServiceAvailable = true;

                if (isNetworkEnabled) {
                    locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER,
                            MIN_TIME_BW_UPDATES,
                            MIN_DISTANCE_CHANGE_FOR_UPDATES, this);
                    if (locationManager != null)   {
                        location = locationManager.getLastKnownLocation(LocationManager.NETWORK_PROVIDER);
                        updateCoordinates();
                    }
                }//end if

                if (isGPSEnabled)  {
                    locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER,
                            MIN_TIME_BW_UPDATES,
                            MIN_DISTANCE_CHANGE_FOR_UPDATES, this);

                    if (locationManager != null)  {
                        location = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER);
                        updateCoordinates();
                    }
                }
            }
        } catch (Exception ex)  {
            LogService.log( "Error creating location service: " + ex.getMessage() );

        }
    }       


    @Override
    public void onLocationChanged(Location location)     {
        // do stuff here with location object 
    }
}

Probé con un dispositivo Android Lollipop solo hasta ahora. Espero que esto funcione para usted.

keshav.bahadoor
fuente
2
Me gusta su solución, pero debe verificar si locationManager es nulo antes de usarlo, no más tarde.
Fer
9
LocationService.MY_PERMISSION_ACCESS_COURSE_LOCATION esta línea no funciona versión 23
4
LocationService.MY_PERMISSION_ACCESS_COURSE_LOCATION no me funciona también.
Kairi San
1
@delive, LocationService.MY_PERMISSION_ACCESS_COURSE_LOCATION -> tienes que definir este código int en tu propia actividad (que implementó ActivityCompat.OnRequestPermissionsResultCallback) clase, para obtener más detalles, consulta este enlace .
David Cheung
11
GRUESO, NO CURSO
Chris Cox
46

SOLUCIÓN SIMPLE

Quería admitir aplicaciones antes de la api 23 y, en lugar de usar checkSelfPermission, usé un try / catch

try {
   location = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER);
} catch (SecurityException e) {
   dialogGPS(this.getContext()); // lets the user know there is a problem with the gps
}
Jacksonkr
fuente
39

La última parte del mensaje de error que citó dice: ...with ("checkPermission") or explicitly handle a potential "SecurityException"

Una forma mucho más rápida / sencilla de comprobar si tiene permisos es rodear su código con try { ... } catch (SecurityException e) { [insert error handling code here] }. Si tiene permisos, la parte 'probar' se ejecutará, si no los tiene, la parte 'atrapar' lo hará.

VikingGlen
fuente
1
Útil si no tiene un objeto Actividad o Contexto para el que pueda verificar los permisos.
Nublodeveloper
5

Use mi clase personalizada para verificar o solicitar permiso

public class Permissons {

        //Request Permisson
        public static void Request_STORAGE(Activity act,int code)
        {

            ActivityCompat.requestPermissions(act, new
                    String[]{android.Manifest.permission.WRITE_EXTERNAL_STORAGE},code);
        }
        public static void Request_CAMERA(Activity act,int code)
        {
            ActivityCompat.requestPermissions(act, new
                    String[]{Manifest.permission.CAMERA},code);
        }
        public static void Request_FINE_LOCATION(Activity act,int code)
        {
            ActivityCompat.requestPermissions(act, new
                    String[]{Manifest.permission.ACCESS_FINE_LOCATION},code);
        }
        public static void Request_READ_SMS(Activity act,int code)
        {
            ActivityCompat.requestPermissions(act, new
                    String[]{Manifest.permission.READ_SMS},code);
        }
        public static void Request_READ_CONTACTS(Activity act,int code)
        {
            ActivityCompat.requestPermissions(act, new
                    String[]{Manifest.permission.READ_CONTACTS},code);
        }
        public static void Request_READ_CALENDAR(Activity act,int code)
        {
            ActivityCompat.requestPermissions(act, new
                    String[]{Manifest.permission.READ_CALENDAR},code);
        }
        public static void Request_RECORD_AUDIO(Activity act,int code)
        {
            ActivityCompat.requestPermissions(act, new
                    String[]{Manifest.permission.RECORD_AUDIO},code);
        }

        //Check Permisson
        public static boolean Check_STORAGE(Activity act)
        {
            int result = ContextCompat.checkSelfPermission(act,android.Manifest.permission.WRITE_EXTERNAL_STORAGE);
            return result == PackageManager.PERMISSION_GRANTED;
        }
        public static boolean Check_CAMERA(Activity act)
        {
            int result = ContextCompat.checkSelfPermission(act, Manifest.permission.CAMERA);
            return result == PackageManager.PERMISSION_GRANTED;
        }
        public static boolean Check_FINE_LOCATION(Activity act)
        {
            int result = ContextCompat.checkSelfPermission(act, Manifest.permission.ACCESS_FINE_LOCATION);
            return result == PackageManager.PERMISSION_GRANTED;
        }
        public static boolean Check_READ_SMS(Activity act)
        {
            int result = ContextCompat.checkSelfPermission(act, Manifest.permission.READ_SMS);
            return result == PackageManager.PERMISSION_GRANTED;
        }
        public static boolean Check_READ_CONTACTS(Activity act)
        {
            int result = ContextCompat.checkSelfPermission(act, Manifest.permission.READ_CONTACTS);
            return result == PackageManager.PERMISSION_GRANTED;
        }
        public static boolean Check_READ_CALENDAR(Activity act)
        {
            int result = ContextCompat.checkSelfPermission(act, Manifest.permission.READ_CALENDAR);
            return result == PackageManager.PERMISSION_GRANTED;
        }
        public static boolean Check_RECORD_AUDIO(Activity act)
        {
            int result = ContextCompat.checkSelfPermission(act, Manifest.permission.RECORD_AUDIO);
            return result == PackageManager.PERMISSION_GRANTED;
        }
    }

Ejemplo

if(!Permissons.Check_STORAGE(MainActivity.this))
{
   //if not permisson granted so request permisson with request code
   Permissons.Request_STORAGE(MainActivity.this,22);
}
milán pithadia
fuente
gracias, pero quiero preguntar, ¿cuál es la función del "código int"?
Hendro Pramono
este código se usa para permitir el resultado otorgado o rechazar el resultado de la actividad
milan pithadia
4

si está trabajando en permisos dinámicos y cualquier permiso como ACCESS_FINE_LOCATION, ACCESS_COARSE_LOCATION dando el error "no se puede resolver el método PERMISSION_NAME" en este caso, escriba su código con el nombre del permiso y luego reconstruya su proyecto, esto regenerará el archivo de manifiesto (Manifest.permission).

anoopbryan2
fuente
0

Si simplemente desea verificar los permisos (en lugar de solicitar permisos), escribí una extensión simple como esta:

fun BaseActivity.checkPermission(permissionName: String): Boolean {
        return if (Build.VERSION.SDK_INT >= 23) {
            val granted =
                ContextCompat.checkSelfPermission(this, permissionName)
            granted == PackageManager.PERMISSION_GRANTED
        } else {
            val granted =
                PermissionChecker.checkSelfPermission(this, permissionName)
            granted == PermissionChecker.PERMISSION_GRANTED
        }
    }

Ahora, si quiero verificar un permiso, simplemente puedo pasar un permiso como este:

checkPermission(Manifest.permission.READ_CONTACTS)
azwethinkweiz
fuente