런타임에 위치 권한을 요청하는 방법
매니페스트 파일에 거칠고 미세하게 권한을 추가했는데 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 && 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 && 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 && 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 && 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);
}
}
실제로 실행 시 Location 권한을 요청해야 합니다(코드의 코멘트에 주의).
Kotlin 및 API 31(Android 12) 배경 위치 업데이트:
API 30 background location부터는 별도로 요청해야 합니다.이 예에서는 를 사용하고 있습니다.targetSdk 31
그리고.compileSdk 31
API 29의 메인 위치 요청과 함께 백그라운드 위치 요청을 번들할 수 있지만, 그러기 위해서는 3개의 별도 코드 경로를 유지해야 합니다.
29개 이상의 요청으로 나눠서 하는 게 더 쉬워요.
최신 위치 서비스를 앱 레벨 그래들에 포함시키십시오(작성 시 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" />
이것은 대부분의 사례를 간단하게 처리할 수 있는 간단한 예입니다.Don't ask again(다시 묻지 않음)을 선택하면 다음 앱 실행 시 사용자가 수동으로 권한을 사용하도록 설정이 열립니다.
전체 액티비티비티 코드
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() && 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() && 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
&& 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);
}
}
Google은 쉬운 권한 관리를 위해 라이브러리를 만들었습니다.Easy Permissions라고 불립니다.
다음은 이 라이브러리를 사용하여 위치 권한을 요청하는 간단한 예입니다.
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 페이지에 설명되어 있습니다.
Android 10 또는 Android Q에서 위치 권한 개인 정보 변경.
사용자가 백그라운드에서 현재 위치에 액세스하고 싶다면 추가 권한을 정의해야 합니다.따라서 사용자는 다음 사이트에서도 사용 권한 실행 시간을 허용해야 합니다.requestPermission()
Android 10보다 낮은 기기를 사용하는 경우 다음과 같은 권한이 자동으로 허용됩니다.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 && 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
코드 " " "
Main Activity에서 이 코드를 확인합니다.
// 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
)
}
}
플러스
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.
}
}
}
플러스
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.
}
아래는 28일 이하, 29일, 30일에 전경 및 백그라운드 로케이션 허가를 요청하는 저의 해결책입니다.API 간의 차이는 미묘하지만 중요합니다.
API 28 이하에서는 포그라운드 및 백그라운드 로케이션 권한이 동일하게 취급됩니다.로케이션 권한을 부여하면 응용 프로그램은 암묵적으로 둘 다 부여됩니다.
API 29는 포그라운드 권한과 백그라운드 권한을 동시에 요청할 수 있습니다.
API 30, 포그라운드 위치 권한을 요청한 다음 포그라운드 위치 권한이 부여된 후에만 백그라운드 위치 권한을 요청해야 합니다.포그라운드 권한과 백그라운드 권한을 동시에 요청하면 시스템은 요청을 무시합니다.또 다른 차이점은 사용자가 시스템 대화 상자가 아닌 응용 프로그램 위치 권한 설정 내에서 백그라운드 위치 권한을 허용해야 한다는 것입니다.
다음 솔루션은 사용자가 전경 및 배경 위치 추적을 모두 수락한 후에만 지정된 작업(예: 배경 위치 추적)을 시작합니다.
Location Permission Util.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 && 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()
}
}
이 암호는 나에게 효과가 있다.'Never Ask Me' 사건도 맡았어요
AndroidManifest.xml의 경우
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
In 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 && 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)
}
}
}
매니페스트 파일에 정의한 후 네이티브 솔루션 대신 다음과 같이 Aaper: https://github.com/LikeTheSalad/aaper를 사용합니다.
@EnsurePermissions(permissions = [Manifest.permission.ACCESS_FINE_LOCATION])
private fun scanForLocation() {
// Your code that needs the location permission granted.
}
면책 조항, 나는 에이퍼를 만든 사람이다.
더 간단한 코드를 원하십니까?이거 먹어봐!
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);
}
또, 확실히 허락을 구하는 것도 잊지 마세요.
언급URL : https://stackoverflow.com/questions/40142331/how-to-request-location-permission-at-runtime
'programing' 카테고리의 다른 글
MySQL/MariaDB가 utf8mb4를 사용할 때 고유 키를 처리할 수 없음 (0) | 2022.09.27 |
---|---|
존재하지 않는 코드 오류 - 마리아DB (0) | 2022.09.27 |
IntelliJ에서 Maven의 Java 버전을 변경하는 방법 (0) | 2022.09.27 |
VueJs exact-active 클래스 (0) | 2022.09.27 |
DEBUG = False일 때 Django가 잘못된 요청(400)을 제공합니다. (0) | 2022.09.27 |