Как запросить разрешение местоположения во время выполнения

#java #android

#java #Android

Вопрос:

В файле манифеста я добавил грубые и точные разрешения, и когда я запускаю на устройстве с Android 6, ничего не происходит! Я пробую все, но не могу получать обновления местоположения…

Что я делаю не так?

 public class MainActivity extends AppCompatActivity implements LocationListener {

    LocationManager locationManager;
    String provider;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);

        provider = locationManager.getBestProvider(new Criteria(), false);

        if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED amp;amp; ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
            // TODO: Consider calling
            //    ActivityCompat#requestPermissions
            // here to request the missing permissions, and then overriding
            //   public void onRequestPermissionsResult(int requestCode, String[] permissions,
            //                                          int[] grantResults)
            // to handle the case where the user grants the permission. See the documentation
            // for ActivityCompat#requestPermissions for more details.
            return;
        }
        Location location = locationManager.getLastKnownLocation(provider);

        if (location != null) {

            Log.i("Location Info", "Location achieved!");

        } else {

            Log.i("Location Info", "No location :(");

        }

    }


    @Override
    protected void onResume() {
        super.onResume();

        if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED amp;amp; ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
            // TODO: Consider calling
            //    ActivityCompat#requestPermissions
            // here to request the missing permissions, and then overriding
            //   public void onRequestPermissionsResult(int requestCode, String[] permissions,
            //                                          int[] grantResults)
            // to handle the case where the user grants the permission. See the documentation
            // for ActivityCompat#requestPermissions for more details.
            return;
        }
        locationManager.requestLocationUpdates(provider, 400, 1, this);

    }

    @Override
    protected void onPause() {
        super.onPause();

        if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED amp;amp; ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
            // TODO: Consider calling
            //    ActivityCompat#requestPermissions
            // here to request the missing permissions, and then overriding
            //   public void onRequestPermissionsResult(int requestCode, String[] permissions,
            //                                          int[] grantResults)
            // to handle the case where the user grants the permission. See the documentation
            // for ActivityCompat#requestPermissions for more details.
            return;
        }
        locationManager.removeUpdates(this);

    }

    @Override
    public void onLocationChanged(Location location) {

        Double lat = location.getLatitude();
        Double lng = location.getLongitude();

        Log.i("Location info: Lat", lat.toString());
        Log.i("Location info: Lng", lng.toString());

    }

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

    }

    @Override
    public void onProviderEnabled(String provider) {

    }

    @Override
    public void onProviderDisabled(String provider) {

    }

    public void getLocation(View view) {

        if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED amp;amp; ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
            // TODO: Consider calling
            //    ActivityCompat#requestPermissions
            // here to request the missing permissions, and then overriding
            //   public void onRequestPermissionsResult(int requestCode, String[] permissions,
            //                                          int[] grantResults)
            // to handle the case where the user grants the permission. See the documentation
            // for ActivityCompat#requestPermissions for more details.
            return;
        }
        Location location = locationManager.getLastKnownLocation(provider);

        onLocationChanged(location);


    }

}
  

Ответ №1:

Вам действительно нужно запросить разрешение на местоположение во время выполнения (обратите внимание на комментарии в вашем коде, указывающие на это).

Обновлено с помощью Kotlin и фонового местоположения для API 31 (Android 12):

Начиная с API 30 фоновое местоположение должно запрашиваться отдельно. В этом примере используется targetSdk 31 и compileSdk 31 . Обратите внимание, что в API 29 можно объединить фоновый запрос местоположения с основным запросом местоположения, однако для этого вам потребуется поддерживать три отдельных пути кода.
Проще просто разбить его на отдельные запросы для 29 и выше.

Обязательно включите последние службы определения местоположения в gradle уровня приложения (18.0.0 на момент написания):

 implementation "com.google.android.gms:play-services-location:18.0.0"
  

Включите разрешения на местоположение в манифест:

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

Это упрощенный пример, который обрабатывает большинство случаев, но упрощенным способом. В случае, когда пользователь выбирает «Не спрашивать снова», при следующем запуске приложения он откроет настройки для пользователя, чтобы вручную включить разрешение.

Полный код действия:

 import android.Manifest
import android.app.AlertDialog
import android.content.Intent
import android.content.pm.PackageManager
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.os.Looper
import android.provider.Settings
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import com.google.android.gms.location.*


class MainActivity : AppCompatActivity() {

    private var fusedLocationProvider: FusedLocationProviderClient? = null
    private val locationRequest: LocationRequest = LocationRequest.create().apply {
        interval = 30
        fastestInterval = 10
        priority = LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY
        maxWaitTime = 60
    }

    private var locationCallback: LocationCallback = object : LocationCallback() {
        override fun onLocationResult(locationResult: LocationResult) {
            val locationList = locationResult.locations
            if (locationList.isNotEmpty()) {
                //The last location in the list is the newest
                val location = locationList.last()
                Toast.makeText(
                    this@MainActivity,
                    "Got Location: "   location.toString(),
                    Toast.LENGTH_LONG
                )
                    .show()
            }
        }
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        fusedLocationProvider = LocationServices.getFusedLocationProviderClient(this)

        checkLocationPermission()
    }

    override fun onResume() {
        super.onResume()
        if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
            == PackageManager.PERMISSION_GRANTED
        ) {

            fusedLocationProvider?.requestLocationUpdates(
                locationRequest,
                locationCallback,
                Looper.getMainLooper()
            )
        }
    }

    override fun onPause() {
        super.onPause()
        if (ContextCompat.checkSelfPermission(
                this,
                Manifest.permission.ACCESS_FINE_LOCATION
            )
            == PackageManager.PERMISSION_GRANTED
        ) {

            fusedLocationProvider?.removeLocationUpdates(locationCallback)
        }
    }

    private fun checkLocationPermission() {
        if (ActivityCompat.checkSelfPermission(
                this,
                Manifest.permission.ACCESS_FINE_LOCATION
            ) != PackageManager.PERMISSION_GRANTED
        ) {
            // Should we show an explanation?
            if (ActivityCompat.shouldShowRequestPermissionRationale(
                    this,
                    Manifest.permission.ACCESS_FINE_LOCATION
                )
            ) {
                // Show an explanation to the user *asynchronously* -- don't block
                // this thread waiting for the user's response! After the user
                // sees the explanation, try again to request the permission.
                AlertDialog.Builder(this)
                    .setTitle("Location Permission Needed")
                    .setMessage("This app needs the Location permission, please accept to use location functionality")
                    .setPositiveButton(
                        "OK"
                    ) { _, _ ->
                        //Prompt the user once explanation has been shown
                        requestLocationPermission()
                    }
                    .create()
                    .show()
            } else {
                // No explanation needed, we can request the permission.
                requestLocationPermission()
            }
        } else {
            checkBackgroundLocation()
        }
    }

    private fun checkBackgroundLocation() {
        if (ActivityCompat.checkSelfPermission(
                this,
                Manifest.permission.ACCESS_BACKGROUND_LOCATION
            ) != PackageManager.PERMISSION_GRANTED
        ) {
            requestBackgroundLocationPermission()
        }
    }

    private fun requestLocationPermission() {
        ActivityCompat.requestPermissions(
            this,
            arrayOf(
                Manifest.permission.ACCESS_FINE_LOCATION,
            ),
            MY_PERMISSIONS_REQUEST_LOCATION
        )
    }

    private fun requestBackgroundLocationPermission() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
            ActivityCompat.requestPermissions(
                this,
                arrayOf(
                    Manifest.permission.ACCESS_BACKGROUND_LOCATION
                ),
                MY_PERMISSIONS_REQUEST_BACKGROUND_LOCATION
            )
        } else {
            ActivityCompat.requestPermissions(
                this,
                arrayOf(Manifest.permission.ACCESS_FINE_LOCATION),
                MY_PERMISSIONS_REQUEST_LOCATION
            )
        }
    }

    override fun onRequestPermissionsResult(
        requestCode: Int,
        permissions: Array<String>,
        grantResults: IntArray
    ) {
        when (requestCode) {
            MY_PERMISSIONS_REQUEST_LOCATION -> {
                // If request is cancelled, the result arrays are empty.
                if (grantResults.isNotEmpty() amp;amp; grantResults[0] == PackageManager.PERMISSION_GRANTED) {

                    // permission was granted, yay! Do the
                    // location-related task you need to do.
                    if (ContextCompat.checkSelfPermission(
                            this,
                            Manifest.permission.ACCESS_FINE_LOCATION
                        ) == PackageManager.PERMISSION_GRANTED
                    ) {
                        fusedLocationProvider?.requestLocationUpdates(
                            locationRequest,
                            locationCallback,
                            Looper.getMainLooper()
                        )

                        // Now check background location
                        checkBackgroundLocation()
                    }

                } else {

                    // permission denied, boo! Disable the
                    // functionality that depends on this permission.
                    Toast.makeText(this, "permission denied", Toast.LENGTH_LONG).show()

                    // Check if we are in a state where the user has denied the permission and
                    // selected Don't ask again
                    if (!ActivityCompat.shouldShowRequestPermissionRationale(
                            this,
                            Manifest.permission.ACCESS_FINE_LOCATION
                        )
                    ) {
                        startActivity(
                            Intent(
                                Settings.ACTION_APPLICATION_DETAILS_SETTINGS,
                                Uri.fromParts("package", this.packageName, null),
                            ),
                        )
                    }
                }
                return
            }
            MY_PERMISSIONS_REQUEST_BACKGROUND_LOCATION -> {
                // If request is cancelled, the result arrays are empty.
                if (grantResults.isNotEmpty() amp;amp; grantResults[0] == PackageManager.PERMISSION_GRANTED) {

                    // permission was granted, yay! Do the
                    // location-related task you need to do.
                    if (ContextCompat.checkSelfPermission(
                            this,
                            Manifest.permission.ACCESS_FINE_LOCATION
                        ) == PackageManager.PERMISSION_GRANTED
                    ) {
                        fusedLocationProvider?.requestLocationUpdates(
                            locationRequest,
                            locationCallback,
                            Looper.getMainLooper()
                        )

                        Toast.makeText(
                            this,
                            "Granted Background Location Permission",
                            Toast.LENGTH_LONG
                        ).show()
                    }
                } else {

                    // permission denied, boo! Disable the
                    // functionality that depends on this permission.
                    Toast.makeText(this, "permission denied", Toast.LENGTH_LONG).show()
                }
                return

            }
        }
    }

    companion object {
        private const val MY_PERMISSIONS_REQUEST_LOCATION = 99
        private const val MY_PERMISSIONS_REQUEST_BACKGROUND_LOCATION = 66
    }
}
  

На Android 10 (API 29) это даст пользователю возможность предоставить фоновое местоположение после первоначального запроса местоположения:

введите описание изображения здесь

введите описание изображения здесь

На Android 12 (API 31) он будет делать то же самое, но интерфейс отличается:

введите описание изображения здесь

введите описание изображения здесь

введите описание изображения здесь

Оригинальный ответ на Java:

Вот протестированный и рабочий код для запроса разрешения местоположения.

Поместите этот код в действие:

 public static final int MY_PERMISSIONS_REQUEST_LOCATION = 99;

public boolean checkLocationPermission() {
    if (ContextCompat.checkSelfPermission(this,
            Manifest.permission.ACCESS_FINE_LOCATION)
            != PackageManager.PERMISSION_GRANTED) {

        // Should we show an explanation?
        if (ActivityCompat.shouldShowRequestPermissionRationale(this,
                Manifest.permission.ACCESS_FINE_LOCATION)) {

            // Show an explanation to the user *asynchronously* -- don't block
            // this thread waiting for the user's response! After the user
            // sees the explanation, try again to request the permission.
            new AlertDialog.Builder(this)
                    .setTitle(R.string.title_location_permission)
                    .setMessage(R.string.text_location_permission)
                    .setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialogInterface, int i) {
                            //Prompt the user once explanation has been shown
                            ActivityCompat.requestPermissions(MainActivity.this,
                                    new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
                                    MY_PERMISSIONS_REQUEST_LOCATION);
                        }
                    })
                    .create()
                    .show();


        } else {
            // No explanation needed, we can request the permission.
            ActivityCompat.requestPermissions(this,
                    new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
                    MY_PERMISSIONS_REQUEST_LOCATION);
        }
        return false;
    } else {
        return true;
    }
}

@Override
public void onRequestPermissionsResult(int requestCode,
                                       String permissions[], int[] grantResults) {
    switch (requestCode) {
        case MY_PERMISSIONS_REQUEST_LOCATION: {
            // If request is cancelled, the result arrays are empty.
            if (grantResults.length > 0
                    amp;amp; grantResults[0] == PackageManager.PERMISSION_GRANTED) {

                // permission was granted, yay! Do the
                // location-related task you need to do.
                if (ContextCompat.checkSelfPermission(this,
                        Manifest.permission.ACCESS_FINE_LOCATION)
                        == PackageManager.PERMISSION_GRANTED) {

                    //Request location updates:
                    locationManager.requestLocationUpdates(provider, 400, 1, this);
                }

            } else {

                // permission denied, boo! Disable the
                // functionality that depends on this permission.

            }
            return;
        }

    }
}
  

Затем вызовите checkLocationPermission() метод в onCreate() :

 @Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    //.........

    checkLocationPermission();
}
  

Затем вы можете использовать onResume() и onPause() точно так, как это указано в вопросе.

Вот сокращенная версия, которая немного более чистая:

 @Override
protected void onResume() {
    super.onResume();
    if (ContextCompat.checkSelfPermission(this,
            Manifest.permission.ACCESS_FINE_LOCATION)
            == PackageManager.PERMISSION_GRANTED) {

        locationManager.requestLocationUpdates(provider, 400, 1, this);
    }
}

@Override
protected void onPause() {
    super.onPause();
    if (ContextCompat.checkSelfPermission(this,
            Manifest.permission.ACCESS_FINE_LOCATION)
            == PackageManager.PERMISSION_GRANTED) {

        locationManager.removeUpdates(this);
    }
}
  

Комментарии:

1. что такое «поставщик»? и является ли ‘MainActivity’ действием, у которого запрашивается разрешение?

2. @AbdullahUmer Вы можете увидеть, как provider используется в вопросе: String provider = locationManager.getBestProvider(new Criteria(), false);

3. И да, MainActivity — это действие, в котором этот код будет использоваться в этом примере. Взгляните на MainActivity в вопросе для получения большего контекста.

4. хороший пример. Спасибо!. Следите за ошибками.. Убедитесь, что вы используете android.Manifest . Android Studio, похоже, по умолчанию имеет значение my.app.package.Manifest вместо этого.

5. @hexicle Ты прав. Я просто запустил этот код как есть, и он определенно не работал должным образом. Самое простое решение — просто вызвать checkLocationPermission() вместо onCreate() , поэтому я изменил ответ, чтобы сделать это. С помощью этого решения вы можете использовать onResume() и onPause() точно так, как это указано в вопросе. Спасибо, что обратили на это мое внимание, приветствия!

Ответ №2:

Google создал библиотеку для простого управления разрешениями. Это называется EasyPermissions

Вот простой пример запроса разрешения местоположения с использованием этой библиотеки.

 public class MainActivity extends AppCompatActivity {

    private final int REQUEST_LOCATION_PERMISSION = 1;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        requestLocationPermission();
    }

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

        // Forward results to EasyPermissions
        EasyPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults, this);
    }

    @AfterPermissionGranted(REQUEST_LOCATION_PERMISSION)
    public void requestLocationPermission() {
        String[] perms = {Manifest.permission.ACCESS_FINE_LOCATION};
        if(EasyPermissions.hasPermissions(this, perms)) {
            Toast.makeText(this, "Permission already granted", Toast.LENGTH_SHORT).show();
        }
        else {
            EasyPermissions.requestPermissions(this, "Please grant the location permission", REQUEST_LOCATION_PERMISSION, perms);
        }
    }
}
  

@AfterPermissionsGranted(REQUEST_CODE) используется для указания метода, который необходимо выполнить после предоставления запроса разрешения с кодом запроса REQUEST_CODE .

В приведенном выше случае метод requestLocationPermission() вызывается, если пользователь предоставляет разрешение на доступ к службам определения местоположения. Таким образом, этот метод действует как обратный вызов, так и метод запроса разрешений.

Вы также можете реализовать отдельные обратные вызовы для предоставленного разрешения и разрешения, в котором отказано. Это объясняется на странице github.

Комментарии:

1. Очень полезно упомянуть эту библиотеку, большое вам спасибо

Ответ №3:

Изменение конфиденциальности разрешения местоположения в Android 10 или Android Q.

Мы должны определить дополнительное ACCESS_BACKGROUND_LOCATION разрешение, если пользователь хочет получить доступ к своему текущему местоположению в фоновом режиме, поэтому пользователю необходимо предоставить разрешение во время выполнения также в requestPermission()

Если мы используем устройство ниже Android 10, то ACCESS_BACKGROUND_LOCATION разрешение автоматически разрешается с помощью ACCESS_FINE_LOCATION разрешения или ACCESS_COARSE_LOCATION

Этот табличный формат может быть легко понять, что, если мы не указываем ACCESS_BACKGROUND_LOCATION в файле манифеста.

введите описание изображения здесь

AndroidManifest.xml

 <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" /> // here we defined ACCESS_BACKGROUND_LOCATION for Android 10 device
  

MainActivity.java
Вызов checkRunTimePermission() в onCreate() или onResume()

 public void checkRunTimePermission() {
   if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
        if (ActivityCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED || 
             ActivityCompat.checkSelfPermission(context, Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED||
             ActivityCompat.checkSelfPermission(context, Manifest.permission.ACCESS_BACKGROUND_LOCATION) == PackageManager.PERMISSION_GRANTED) {
                gpsTracker = new GPSTracker(context);

        } else {
                requestPermissions(new String[]{Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION},
                        10);
        }
   } else {
            gpsTracker = new GPSTracker(context); //GPSTracker is class that is used for retrieve user current location
   }
}

 @Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    if (requestCode == 10) {
        if (grantResults.length > 0 amp;amp; grantResults[0] == PackageManager.PERMISSION_GRANTED) {
            gpsTracker = new GPSTracker(context);
        } else {
            if (!ActivityCompat.shouldShowRequestPermissionRationale((Activity) context, Manifest.permission.ACCESS_FINE_LOCATION)) {
                // If User Checked 'Don't Show Again' checkbox for runtime permission, then navigate user to Settings
                AlertDialog.Builder dialog = new AlertDialog.Builder(context);
                dialog.setTitle("Permission Required");
                dialog.setCancelable(false);
                dialog.setMessage("You have to Allow permission to access user location");
                dialog.setPositiveButton("Settings", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        Intent i = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS, Uri.fromParts("package",
                                context.getPackageName(), null));
                        //i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                        startActivityForResult(i, 1001);
                    }
                });
                AlertDialog alertDialog = dialog.create();
                alertDialog.show();
            }
            //code for deny
        }
    }
}

@Override
public void startActivityForResult(Intent intent, int requestCode) {
    super.startActivityForResult(intent, requestCode);
    switch (requestCode) {
        case 1001:
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                if (ActivityCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED ||
                        ActivityCompat.checkSelfPermission(context, Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED 
                        || ActivityCompat.checkSelfPermission(context, Manifest.permission.ACCESS_BACKGROUND_LOCATION) == PackageManager.PERMISSION_GRANTED) {
                    gpsTracker = new GPSTracker(context);
                    if (gpsTracker.canGetLocation()) {
                        latitude = gpsTracker.getLatitude();
                        longitude = gpsTracker.getLongitude();
                    }
                } else {
                    requestPermissions(new String[]{Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION,
                        Manifest.permission.ACCESS_BACKGROUND_LOCATION},10);
                }
            }
            break;
        default:
            break;
    }
}
  

build.gradle (уровень приложения)

 android {
    compileSdkVersion 29 //should be >= 29
    buildToolsVersion "29.0.2"
    useLibrary 'org.apache.http.legacy'
    defaultConfig {
        applicationId "com.example.runtimepermission"
        minSdkVersion 21
        targetSdkVersion 29 //should be >= 29
        versionCode 1
        versionName "1.0"
        multiDexEnabled true
        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
        vectorDrawables.useSupportLibrary = true
    }
}
  

Здесь вы можете найти GPSTracker.java код файла

Комментарии:

1. Эй, чувак, спасибо за твой ответ. Есть ли у вас какие-либо идеи, как я могу определить, выбрал ли пользователь опцию «всегда разрешать» или «одноразовое использование»??

2. @MohammadZarei Рад вам помочь, если ответ полезен, пожалуйста, поддержите мой ответ, и да, вы можете отслеживать запись, есть onRequestPermissionsResult() метод, и в нем есть один аргумент, int[] grantResults в котором вы должны написать цикл for для (int grantResult : grantResults) { Log.d(«TAG», «onRequestPermissionsResult: » grantResult); } если grantResult 0 , то разрешение предоставлено, иначе -1 , тогда отказано в выдаче.

3. Как в «всегда разрешать», так и в «одноразовом использовании» разрешение предоставлено, но если пользователь выберет «одноразовое использование», в следующий раз, когда он вернется в наше приложение, он должен предоставить нам такое же разрешение. Я хочу знать, можем ли мы проверить, разрешил ли он нам постоянно или нет?

4. @MohammadZarei Итак, вы должны сохранить флаг в sharedPreference в onRequestPermissionsResult() . когда пользователь выбирает разрешение «одноразовое использование», вам нужно установить флаг на true (по умолчанию он равен false), а в MainActivity вы получаете значение из предпочтения и проверяете, является ли оно true или false

5. Да, но я не смог найти способ сказать, что пользователь выбрал «одноразовый» или всегда. Это то, что я ищу.

Ответ №4:

Ниже приведено мое решение для запроса разрешений на расположение переднего плана и фона на 28 и ниже, 29 и 30. Различия между API-интерфейсами неуловимы, но важны.

API 28 и ниже, система обрабатывает разрешения местоположения на переднем плане и в фоновом режиме как одинаковые. Если вы предоставляете разрешения местоположения, то приложению предоставляются оба неявно.

API 29, вы можете запросить разрешения как на передний план, так и на задний план одновременно.

API 30, вы должны запросить разрешения расположения на переднем плане, а затем запрашивать разрешения расположения в фоновом режиме только после предоставления разрешений расположения на переднем плане. Если вы одновременно запрашиваете разрешения как для переднего плана, так и для фона, система проигнорирует запрос. Другое отличие заключается в том, что пользователь должен разрешить фоновые разрешения местоположения в настройках разрешения местоположения приложения, а не через системный диалог.

Решение, приведенное ниже, запускает указанное действие (например, фоновое отслеживание местоположения) только после того, как пользователь принял отслеживание местоположения на переднем плане и в фоновом режиме:

LocationPermissionUtil.kt

 private const val REQUEST_CODE_FOREGROUND = 1
private const val REQUEST_CODE_FOREGROUND_AND_BACKGROUND = 2

object LocationPermissionUtil {

    private fun Context.isPermissionGranted(permission: String): Boolean = ActivityCompat
        .checkSelfPermission(this, permission) == PackageManager.PERMISSION_GRANTED

    private val Context.isFineLocationPermissionGranted
        get() = isPermissionGranted(
            Manifest.permission.ACCESS_FINE_LOCATION
        )

    private val Context.isBackgroundPermissionGranted
        get() = when {
            Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q -> ActivityCompat.checkSelfPermission(
                this,
                Manifest.permission.ACCESS_BACKGROUND_LOCATION
            ) == PackageManager.PERMISSION_GRANTED
            else -> isFineLocationPermissionGranted
        }

    private val Context.isFineAndBackgroundLocationPermissionsGranted
        get() = isFineLocationPermissionGranted amp;amp; isBackgroundPermissionGranted

    private fun Activity.checkFineLocationPermission() {
        if (isFineLocationPermissionGranted) return

        val shouldShowFineLocationPermissionRationale = ActivityCompat
            .shouldShowRequestPermissionRationale(
                this,
                Manifest.permission.ACCESS_FINE_LOCATION
            )

        if (shouldShowFineLocationPermissionRationale) {
            presentAlertDialog(
                R.string.dialog_fine_location_rationale_title,
                R.string.dialog_fine_location_rationale_description,
                R.string.yes,
            ) {
                requestLocationPermissions()
            }
        } else {
            requestLocationPermissions()
        }
    }

    private fun Activity.requestLocationPermissions() =
        if (Build.VERSION.SDK_INT == Build.VERSION_CODES.Q) {
            requestFineLocationAndBackground()
        } else {
            ActivityCompat.requestPermissions(
                this,
                arrayOf(Manifest.permission.ACCESS_FINE_LOCATION),
                REQUEST_CODE_FOREGROUND
            )
        }

    @TargetApi(29)
    private fun Activity.requestFineLocationAndBackground() {
        ActivityCompat.requestPermissions(
            this,
            arrayOf(
                Manifest.permission.ACCESS_FINE_LOCATION,
                Manifest.permission.ACCESS_BACKGROUND_LOCATION
            ),
            REQUEST_CODE_FOREGROUND_AND_BACKGROUND
        )
    }

    @TargetApi(29)
    private fun Activity.checkBackgroundLocationPermission() {
        if (isFineAndBackgroundLocationPermissionsGranted) return

        val shouldShowBackgroundPermissionRationale = ActivityCompat
            .shouldShowRequestPermissionRationale(
                this,
                Manifest.permission.ACCESS_BACKGROUND_LOCATION
            )

        if (shouldShowBackgroundPermissionRationale) {
            presentAlertDialog(
                R.string.dialog_background_location_rationale_title,
                R.string.dialog_background_location_rationale_description,
                R.string.yes,
            ) {
                requestFineLocationAndBackground()
            }
        } else {
            requestFineLocationAndBackground()
        }
    }

    fun checkLocationPermissions(activity: Activity, action: () -> Unit) = with(activity) {
        if (isFineAndBackgroundLocationPermissionsGranted) {
            action()
            return
        }

        checkFineLocationPermission()
    }

    fun onRequestPermissionsResult(
        activity: Activity,
        requestCode: Int,
        action: () -> Unit
    ) = with(activity) {
        when (requestCode) {
            REQUEST_CODE_FOREGROUND -> {
                if (!isFineLocationPermissionGranted) {
                    checkFineLocationPermission()
                    return
                }

                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
                    checkBackgroundLocationPermission()
                } else {
                    action()
                }
            }
            REQUEST_CODE_FOREGROUND_AND_BACKGROUND -> {
                if (!isFineLocationPermissionGranted) {
                    checkFineLocationPermission()
                    return
                }

                if (isBackgroundPermissionGranted) {
                    action()
                } else {
                    checkBackgroundLocationPermission()
                }
            }
        }
    }
}
  

Активность:

 class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        setContentView(R.layout.activity_main)

        LocationPermissionUtil.checkLocationPermissions(this, this::onLocationPermissionsGranted)
    }

    override fun onRequestPermissionsResult(
        requestCode: Int,
        permissions: Array<out String>,
        grantResults: IntArray
    ) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults)

        LocationPermissionUtil.onRequestPermissionsResult(
            this,
            requestCode,
            this::onLocationPermissionsGranted
        )
    }

    private fun onLocationPermissionsGranted() {
        Toast.makeText(
            this,
            "Background location permitted, starting location tracking...",
            Toast.LENGTH_LONG
        ).show()
    }

}
  

Комментарии:

1. Привет @masterwok Что такое presentAlertDialog? Android Studio не может решить эту проблему.

2. Это пользовательский диалог, который я написал, чтобы представить пользователю обоснование. Здесь подойдет любой диалог. Цель состоит в том, чтобы сообщить пользователю, почему вы запрашиваете разрешение, прежде чем запрашивать его, и дать им возможность отклонить запрос (таким образом, вы не просто запрашиваете, используя системный API, потому что после нескольких попыток система не позволит вам запрашивать пользователя).

Ответ №5:

проверьте этот код из MainActivity

  // Check location permission is granted - if it is, start
// the service, otherwise request the permission
fun checkOrAskLocationPermission(callback: () -> Unit) {
    // Check GPS is enabled
    val lm = getSystemService(Context.LOCATION_SERVICE) as LocationManager
    if (!lm.isProviderEnabled(LocationManager.GPS_PROVIDER)) {
        Toast.makeText(this, "Please enable location services", Toast.LENGTH_SHORT).show()
        buildAlertMessageNoGps(this)
        return
    }

    // Check location permission is granted - if it is, start
    // the service, otherwise request the permission
    val permission = ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
    if (permission == PackageManager.PERMISSION_GRANTED) {
        callback.invoke()
    } else {
        // callback will be inside the activity's onRequestPermissionsResult(
        ActivityCompat.requestPermissions(
            this,
            arrayOf(Manifest.permission.ACCESS_FINE_LOCATION),
            PERMISSIONS_REQUEST
        )
    }
}
  

plus

    override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults)
    if (requestCode == PERMISSIONS_REQUEST) {
        if (grantResults[0] == PackageManager.PERMISSION_GRANTED){
              // Permission ok. Do work.
         }
    }
}
  

plus

  fun buildAlertMessageNoGps(context: Context) {
    val builder = AlertDialog.Builder(context);
    builder.setMessage("Your GPS is disabled. Do you want to enable it?")
        .setCancelable(false)
        .setPositiveButton("Yes") { _, _ -> context.startActivity(Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS)) }
        .setNegativeButton("No") { dialog, _ -> dialog.cancel(); }
    val alert = builder.create();
    alert.show();
}
  

использование

 checkOrAskLocationPermission() {
            // Permission ok. Do work.
        }
  

Ответ №6:

Этот код работает для меня. Я также рассматривал случай «Никогда не спрашивай меня»

В AndroidManifest.xml

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

В build.gradle (модуль: приложение)

 dependencies {
    ....
    implementation "com.google.android.gms:play-services-location:16.0.0"
}
  

Это CurrentLocationManager.kt

 import android.Manifest
import android.app.Activity
import android.content.Context
import android.content.IntentSender
import android.content.pm.PackageManager
import android.location.Location
import android.location.LocationListener
import android.location.LocationManager
import android.os.Bundle
import android.os.CountDownTimer
import android.support.v4.app.ActivityCompat
import android.support.v4.content.ContextCompat
import android.util.Log
import com.google.android.gms.common.api.ApiException
import com.google.android.gms.common.api.CommonStatusCodes
import com.google.android.gms.common.api.ResolvableApiException
import com.google.android.gms.location.LocationRequest
import com.google.android.gms.location.LocationServices
import com.google.android.gms.location.LocationSettingsRequest
import com.google.android.gms.location.LocationSettingsStatusCodes
import java.lang.ref.WeakReference


object CurrentLocationManager : LocationListener {

    const val REQUEST_CODE_ACCESS_LOCATION = 123

    fun checkLocationPermission(activity: Activity) {
        if (ContextCompat.checkSelfPermission(
                activity,
                Manifest.permission.ACCESS_FINE_LOCATION
            ) != PackageManager.PERMISSION_GRANTED
        ) {
            ActivityCompat.requestPermissions(
                activity,
                arrayOf(Manifest.permission.ACCESS_FINE_LOCATION),
                REQUEST_CODE_ACCESS_LOCATION
            )
        } else {
            Thread(Runnable {
                // Moves the current Thread into the background
                android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_BACKGROUND)
                //
                requestLocationUpdates(activity)
            }).start()
        }
    }

    /**
     * be used in HomeActivity.
     */
    const val REQUEST_CHECK_SETTINGS = 55
    /**
     * The number of millis in the future from the call to start().
     * until the countdown is done and onFinish() is called.
     *
     *
     * It is also the interval along the way to receive onTick(long) callbacks.
     */
    private const val TWENTY_SECS: Long = 20000
    /**
     * Timer to get location from history when requestLocationUpdates don't return result.
     */
    private var mCountDownTimer: CountDownTimer? = null
    /**
     * WeakReference of current activity.
     */
    private var mWeakReferenceActivity: WeakReference<Activity>? = null
    /**
     * user's location.
     */
    var currentLocation: Location? = null

    @Synchronized
    fun requestLocationUpdates(activity: Activity) {
        if (mWeakReferenceActivity == null) {
            mWeakReferenceActivity = WeakReference(activity)
        } else {
            mWeakReferenceActivity?.clear()
            mWeakReferenceActivity = WeakReference(activity)
        }
        //create location request: https://developer.android.com/training/location/change-location-settings.html#prompt
        val mLocationRequest = LocationRequest()
        // Which your app prefers to receive location updates. Note that the location updates may be
        // faster than this rate, or slower than this rate, or there may be no updates at all
        // (if the device has no connectivity)
        mLocationRequest.interval = 20000
        //This method sets the fastest rate in milliseconds at which your app can handle location updates.
        // You need to set this rate because other apps also affect the rate at which updates are sent
        mLocationRequest.fastestInterval = 10000
        mLocationRequest.priority = LocationRequest.PRIORITY_HIGH_ACCURACY

        //Get Current Location Settings
        val builder = LocationSettingsRequest.Builder().addLocationRequest(mLocationRequest)
        //Next check whether the current location settings are satisfied
        val client = LocationServices.getSettingsClient(activity)
        val task = client.checkLocationSettings(builder.build())
        //Prompt the User to Change Location Settings
        task.addOnSuccessListener(activity) {
            Log.d("CurrentLocationManager", "OnSuccessListener")
            // All location settings are satisfied. The client can initialize location requests here.
            // If it's failed, the result after user updated setting is sent to onActivityResult of HomeActivity.
            val activity1 = mWeakReferenceActivity?.get()
            if (activity1 != null) {
                startRequestLocationUpdate(activity1.applicationContext)
            }
        }

        task.addOnFailureListener(activity) { e ->
            Log.d("CurrentLocationManager", "addOnFailureListener")
            val statusCode = (e as ApiException).statusCode
            when (statusCode) {
                CommonStatusCodes.RESOLUTION_REQUIRED ->
                    // Location settings are not satisfied, but this can be fixed
                    // by showing the user a dialog.
                    try {
                        val activity1 = mWeakReferenceActivity?.get()
                        if (activity1 != null) {
                            // Show the dialog by calling startResolutionForResult(),
                            // and check the result in onActivityResult().
                            val resolvable = e as ResolvableApiException
                            resolvable.startResolutionForResult(
                                activity1, REQUEST_CHECK_SETTINGS
                            )
                        }
                    } catch (sendEx: IntentSender.SendIntentException) {
                        // Ignore the error.
                        sendEx.printStackTrace()
                    }

                LocationSettingsStatusCodes.SETTINGS_CHANGE_UNAVAILABLE -> {
                    // Location settings are not satisfied. However, we have no way
                    // to fix the settings so we won't show the dialog.
                }
            }
        }
    }

    fun startRequestLocationUpdate(appContext: Context) {
        val mLocationManager = appContext.getSystemService(Context.LOCATION_SERVICE) as LocationManager
        if (ActivityCompat.checkSelfPermission(
                appContext.applicationContext,
                Manifest.permission.ACCESS_FINE_LOCATION
            ) == PackageManager.PERMISSION_GRANTED
        ) {
            //Utilities.showProgressDialog(mWeakReferenceActivity.get());
            if (mLocationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER)) {
                mLocationManager.requestLocationUpdates(
                    LocationManager.NETWORK_PROVIDER, 10000, 0f, this
                )
            } else {
                mLocationManager.requestLocationUpdates(
                    LocationManager.GPS_PROVIDER, 10000, 0f, this
                )
            }
        }

        /*Timer to call getLastKnownLocation() when requestLocationUpdates don 't return result*/
        countDownUpdateLocation()
    }

    override fun onLocationChanged(location: Location?) {
        if (location != null) {
            stopRequestLocationUpdates()
            currentLocation = location
        }
    }

    override fun onStatusChanged(provider: String, status: Int, extras: Bundle) {

    }

    override fun onProviderEnabled(provider: String) {

    }

    override fun onProviderDisabled(provider: String) {

    }

    /**
     * Init CountDownTimer to to get location from history when requestLocationUpdates don't return result.
     */
    @Synchronized
    private fun countDownUpdateLocation() {
        mCountDownTimer?.cancel()
        mCountDownTimer = object : CountDownTimer(TWENTY_SECS, TWENTY_SECS) {
            override fun onTick(millisUntilFinished: Long) {}

            override fun onFinish() {
                if (mWeakReferenceActivity != null) {
                    val activity = mWeakReferenceActivity?.get()
                    if (activity != null amp;amp; ActivityCompat.checkSelfPermission(
                            activity,
                            Manifest.permission.ACCESS_FINE_LOCATION
                        ) == PackageManager.PERMISSION_GRANTED
                    ) {
                        val location = (activity.applicationContext
                            .getSystemService(Context.LOCATION_SERVICE) as LocationManager)
                            .getLastKnownLocation(LocationManager.PASSIVE_PROVIDER)
                        stopRequestLocationUpdates()
                        onLocationChanged(location)
                    } else {
                        stopRequestLocationUpdates()
                    }
                } else {
                    mCountDownTimer?.cancel()
                    mCountDownTimer = null
                }
            }
        }.start()
    }

    /**
     * The method must be called in onDestroy() of activity to
     * removeUpdateLocation and cancel CountDownTimer.
     */
    fun stopRequestLocationUpdates() {
        val activity = mWeakReferenceActivity?.get()
        if (activity != null) {
            /*if (ActivityCompat.checkSelfPermission(activity,
                    Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {*/
            (activity.applicationContext
                .getSystemService(Context.LOCATION_SERVICE) as LocationManager).removeUpdates(this)
            /*}*/
        }
        mCountDownTimer?.cancel()
        mCountDownTimer = null
    }
}
  

В MainActivity.kt

 override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
...
CurrentLocationManager.checkLocationPermission(this@LoginActivity)
}

override fun onDestroy() {
        CurrentLocationManager.stopRequestLocationUpdates()
        super.onDestroy()
    }


    override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults)
        if (requestCode == CurrentLocationManager.REQUEST_CODE_ACCESS_LOCATION) {
            if (grantResults[0] == PackageManager.PERMISSION_DENIED) {
                //denied
                val builder = AlertDialog.Builder(this)
                builder.setMessage("We need permission to use your location for the purpose of finding friends near you.")
                    .setTitle("Device Location Required")
                    .setIcon(com.eswapp.R.drawable.ic_info)
                    .setPositiveButton("OK") { _, _ ->
                        if (ActivityCompat.shouldShowRequestPermissionRationale(
                                this,
                                Manifest.permission.ACCESS_FINE_LOCATION
                            )
                        ) {
                            //only deny
                            CurrentLocationManager.checkLocationPermission(this@LoginActivity)
                        } else {
                            //never ask again
                            val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
                            val uri = Uri.fromParts("package", packageName, null)
                            intent.data = uri
                            startActivityForResult(intent, CurrentLocationManager.REQUEST_CHECK_SETTINGS)
                        }
                    }
                    .setNegativeButton("Ask Me Later") { _, _ ->

                    }
                // Create the AlertDialog object and return it
                val dialog = builder.create()
                dialog.show()
            } else if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                CurrentLocationManager.requestLocationUpdates(this)
            }
        }
    }

    //Forward Login result to the CallBackManager in OnActivityResult()
    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        when (requestCode) {
            //case 1. After you allow the app access device location, Another dialog will be displayed to request you to turn on device location
            //case 2. Or You chosen Never Ask Again, you open device Setting and enable location permission
            CurrentLocationManager.REQUEST_CHECK_SETTINGS -> when (resultCode) {
                RESULT_OK -> {
                    Log.d("REQUEST_CHECK_SETTINGS", "RESULT_OK")
                    //case 1. You choose OK
                    CurrentLocationManager.startRequestLocationUpdate(applicationContext)
                }
                RESULT_CANCELED -> {
                    Log.d("REQUEST_CHECK_SETTINGS", "RESULT_CANCELED")
                    //case 1. You choose NO THANKS
                    //CurrentLocationManager.requestLocationUpdates(this)

                    //case 2. In device Setting screen: user can enable or not enable location permission,
                    // so when user back to this activity, we should re-call checkLocationPermission()
                    CurrentLocationManager.checkLocationPermission(this@LoginActivity)
                }
                else -> {
                    //do nothing
                }
            }
            else -> {
                super.onActivityResult(requestCode, resultCode, data)
            }
        }
    }
  

Комментарии:

1. чувак, в вопросе есть теги JAVA и Android… Но сотрудники Kotlin всегда должны сообщать вам

Ответ №7:

После определения его в вашем файле манифеста более удобной альтернативой собственному решению было бы использование Aaper: https://github.com/LikeTheSalad/aaper вот так:

 @EnsurePermissions(permissions = [Manifest.permission.ACCESS_FINE_LOCATION])
private fun scanForLocation() {
    // Your code that needs the location permission granted.
}
  

Отказ от ответственности, я создатель Aaper.

Ответ №8:

Ищете более простой код? Попробуйте это!

 if (ContextCompat.checkSelfPermission(LoginActivity.this,
            Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
        ActivityCompat.requestPermissions(LoginActivity.this,
                new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, REQUEST_CALL);
    }
  

Кроме того, не забудьте запросить разрешения, очевидно