在SDK 29中,我无法获得“允许所有时间”的位置提示。我已经在清单中设置了这些权限:
<uses-permission android:name="com.google.android.providers.gsf.permission.READ_GSERVICES" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
并请求用户在运行时允许这些权限。但是它只返回“当应用程序打开时”和“拒绝”选项。
任何关于如何在SDK 29中显示它的想法。
发布于 2020-05-20 01:41:23
为了访问运行Android 10 (API级别29)或更高版本的设备的后台位置,还需要在清单文件中使用下面的权限。
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
有关更多信息,请参考下面的链接
https://developer.android.com/training/location/permissions?hl=fr
发布于 2020-05-20 01:30:59
在清单中添加权限ACCESS_BACKGROUND_LOCATION
。要求在android 10及更高版本上显示始终允许选项。
参见https://developer.android.com/training/location/background#evaluate中的第二点
发布于 2020-06-28 08:00:53
我发现仅仅在清单中添加权限是不够的。我必须为用户提供一个选项,以便在启动时授予它,就像我需要为ACCESS_FINE_LOCATION所做的那样。有趣的是,当我检查这个应用程序中是否已经授予了后台权限时,它从来没有给出错误。但是如果我不检查,我就得不到“允许所有时间”的选项。这是在Android像素2版本10上。
这在安卓的10+版本中是唯一必要的。请求这两种权限使用户可以在对话框中选择.当然,用户可能不会选择“所有时间”。没有人能做这件事!
我有点不好意思添加我的代码,因为我知道这很糟糕。当然还有其他人可以改进它。我无法找到避免两次遍历权限的方法。
/**
* This method lists all the permissions needed and gives reasons for each of them in a single
* dialog. When the user presses ok, Android will popup separate dialogs for each permission
* that is needed. In the case of location, the "Allow all the time" gives background permission
* "Allow only while the app is running gives just the location permission while in foreground.
* @return false if permissions are to be requested, true if they have already been granted.
*
* In Android 11 Build 30 things are a lot different. One has to ask for location first, approve
* the 'while the app is running' option and then upon return in the Activity result, ask for
* the background. Thus the new variable 'secondPassR' to handle this case. After the background
* one comes back to the Activity result and then one can go into the main activity.
*/
static boolean secondPassR = false;
private static final int REQUEST_CODE_MULTIPLE_PERMISSIONS = 57;
private boolean requestPermissions(Activity activity)
{
Log.v(TAG, "requestPermissions() called");
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
{
final List<String> permissionsList = new ArrayList<>();
final List<String> reasonList = new ArrayList<>();
if(!addPermission(permissionsList, Manifest.permission.ACCESS_FINE_LOCATION, activity))
{
reasonList.add("LOCATION PERMISSION: Needs needs to be granted for the Bluetooth " +
" LE scanner to discover devices!\n\n");
}
if ((Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) && (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) || secondPassR)
{
if (!addPermission(permissionsList, Manifest.permission.ACCESS_BACKGROUND_LOCATION, activity))
{
reasonList.add("BACKGROUND PERMISSION: Needs to be granted for the Bluetooth " +
"LE Scanner to run in the background.\n\n");
}
}
if (permissionsList.size() > 0)
{
if (reasonList.size() > 0)
{
// Need Rationale
StringBuilder message = new StringBuilder(reasonList.get(0));
for (int i = 1; i < reasonList.size(); i++)
{
message.append(" ").append(reasonList.get(i));
}
final androidx.appcompat.app.AlertDialog.Builder builder =
new androidx.appcompat.app.AlertDialog.Builder(new ContextThemeWrapper(activity, R.style.Theme_AppCompat_Light));
builder.setTitle("Demo needs the following permissions:");
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
{
builder.setMessage(Html.fromHtml(message.toString(), Html.FROM_HTML_MODE_LEGACY));
}
else
{
builder.setMessage(Html.fromHtml(message.toString()));
}
builder.setPositiveButton(android.R.string.ok, null);
builder.setOnDismissListener(dialog -> {
Log.v(TAG, "Requesting permissions");
activity.requestPermissions(permissionsList.toArray(new String[0]), // newer Java recommended
REQUEST_CODE_MULTIPLE_PERMISSIONS);
});
builder.show();
return false;
}
activity.requestPermissions(permissionsList.toArray(new String[0]), // newer Java recommended
REQUEST_CODE_MULTIPLE_PERMISSIONS);
}
else
{
return true;
}
}
else
{
return true;
}
return false;
}
@TargetApi(23)
private boolean addPermission(List<String> permissionsList, String permission, Activity activity)
{
Log.v(TAG,
"addPermission() called with: " + "permissionsList = " +
"[" + permissionsList + "], permission = [" + permission + "]");
if (ActivityCompat.checkSelfPermission(activity, permission) != PackageManager.PERMISSION_GRANTED)
{
permissionsList.add(permission);
// Check for Rationale Option
return activity.shouldShowRequestPermissionRationale(permission);
}
return true;
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults)
{
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
String permission = "";
Log.v(TAG,
"onRequestPermissionsResult() called with: " + "requestCode = [" + requestCode +
"], permissions = [" + Arrays.toString(permissions) + "]," +
" grantResults = [" + Arrays.toString(grantResults) + "]");
if (requestCode == REQUEST_CODE_MULTIPLE_PERMISSIONS)
{
for (int i = 0; i < permissions.length; i++)
{
switch (permissions[i])
{
case Manifest.permission.ACCESS_FINE_LOCATION:
if (grantResults[i] == PackageManager.PERMISSION_GRANTED)
{
Log.d(TAG, "H@H: onRequestPermissionsResult: FINE LOCATION PERMISSION");
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R)
{
Log.d(TAG, "H@H: Now requesting BACKGROUND PERMISSION for version 11+");
secondPassR = true;
requestPermissions(thisActivity);
return;
}
}
break;
case Manifest.permission.ACCESS_BACKGROUND_LOCATION:
if (grantResults[i] == PackageManager.PERMISSION_GRANTED)
{
Log.d(TAG, "H@H: onRequestPermissionsResult: BACKGROUND PERMISSION");
}
break;
}
}
}
Log.d(TAG, "Starting primary activity");
secondPassR = false;
startActivityForResult(new Intent(context, PchaDemoPhg_Activity.class), EXIT_QUIT);
}
private void showMessageOKCancel(String message, DialogInterface.OnClickListener okListener)
{
new AlertDialog.Builder(this)
.setMessage(message)
.setCancelable(false)
.setPositiveButton("OK", okListener)
.create()
.show();
}
==============更新ANDROID 12和ActivityResultLauncher =======
有了Android 12,就会出现一组新的权限,我们不必再要求使用BTLE扫描仪的位置权限。THey还有一种处理活动结果的新方法,即ActivityResultLauncher,内置特性之一是运行时权限。下面是我现在用于从ANdroid 6+请求蓝牙扫描仪和后台权限的内容。我使用单权限启动程序在每个请求之前放置一个解释对话框。用户体验比我以前要好得多。(我包括字符串资源。)
import android.Manifest;
import android.annotation.SuppressLint;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Bundle;
import android.text.Html;
import android.util.Log;
import androidx.activity.result.ActivityResultCallback;
import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.view.ContextThemeWrapper;
import androidx.core.app.ActivityCompat;
import java.util.ArrayList;
import java.util.List;
/*
resource strings used:
<string name="permissions_connect_12"><p>This app needs permission to connect to Bluetooth devices</p> </string>
<string name="permissions_scan_12"><p>To search for Bluetooth devices this app needs permission to use the BLE scanner.</p> </string>
<string name="permissions_overlay_11">READ CAREFULLY! Setting permissions in Android 11+ is much more complicated!\n\n
This app wants to popup a dialog when it discovers a PHD that it can work with.\n\n
It needs Window-Overlay permissions to do that. Android will start a Settings system activity where it will list all the installed applications.\n\n
You will need to scroll down to Health@Home PHG and tap it. That will bring you to the original pre-11 Settings system overlay permission activity.
Give the permission and use the back arrow to exit. You will need use the back arrow once more to return to Health@Home.</string>
<string name="permissions_location"><p><font color=\"#007700\"><b>LOCATION PERMISSION:</b></font> Needs to be granted in order for this app to use the Bluetooth LE scanner.
The scanner is needed to discover BLE health devices and know what they are.</p>
<p><font color=\"red\"><b>This app does NOT use location information or expose location information!</b></font>
Without this permission you will only be able to work with SPP and HDP devices.</p></string>
<string name="permissions_background"><p>BACKGROUND PERMISSION: Needs to be granted for the Bluetooth LE Scanner to run in the background
to support re-connection without user intervention.</p>
<p>Please select \'Allow all the time\'.</p></string>
*/
public class PermissionsActivity extends AppCompatActivity
{
private final static String TAG = PermissionsActivity.class.getName();
private final static boolean isVersionS_Plus = (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S); // 31
private final static boolean isVersionM_Plus = (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M); // 23 (Marshmallow)
private final static boolean isVersionN_Plus = (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N);
private static final boolean isVersionO_Plus = (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O);
private static final boolean isVersionR = (Build.VERSION.SDK_INT == Build.VERSION_CODES.R); // 30
private static final boolean isVersionR_Plus = (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R); // 30
private static final boolean isVersionOtoR = ((Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) // 26
&& (Build.VERSION.SDK_INT <= Build.VERSION_CODES.R)); // 30
private final List<String> permissionsList = new ArrayList<>();
private final List<String> reasonList = new ArrayList<>();
private int index = 0; // Keeps track of what permission is being requested
private int indexMax; // The maximum number of permissions being asked for (set below)
ActivityResultLauncher<String> activityResultLauncher =
registerForActivityResult(new ActivityResultContracts.RequestPermission(), new ActivityResultCallback<Boolean>()
{
@Override
public void onActivityResult(Boolean result)
{
Log.d(TAG, "HH2: Permission " + permissionsList.get(index) + (result ? " granted" : "rejected"));
index++;
if (index >= indexMax)
{
handlePermissionSummary();
}
else
{
requestPermissions(index);
}
}
});
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
// too lazy to make a layout; your activity very likely has one!
if (handlePermissions())
{
//setLoginScreen(); // Do what needs to be done after permissions are granted or if they already are granted
}
}
// Method checks to see if the needed permissions are granted and if they are it returns true.
// If they are not granted the method returns false but starts the process of asking the user to grant the permissions
@SuppressLint("NewApi")
private boolean handlePermissions()
{
if (isVersionS_Plus) // Android 12 + (31+) Completely new runtime permissions for Bluetooth in Android 12
{ // At least one no longer has to ask for location permissions for Bluetooth completely
// confusing the user.
// Requesting BLUETOOTH_CONNECT, BLUETOOTH_SCAN, and ACCESS_BACKGROUND_LOCATION. The latter is still needed
// to use the BTLE scanner in the background.
// There is a weird bug in Android 12 with respect to the BLUETOOTH_CONNECT and BLUETOOTH_SCAN
// permissions. If you need both, regardless of what order you ask for them in, you get only one
// dialog from Android where the user grants the permission. But you have to ask for both permissions
// (if you need both). See this bug: https://issuetracker.google.com/issues/214434187
// Thus I skip the application dialog by making it empty for the second permission and just request
// the permission. That way both permissions get granted.
if (checkSelfPermission(Manifest.permission.BLUETOOTH_CONNECT) != PackageManager.PERMISSION_GRANTED)
{
permissionsList.add(Manifest.permission.BLUETOOTH_CONNECT);
reasonList.add(getString(R.string.permissions_connect_12));
}
if (checkSelfPermission(Manifest.permission.BLUETOOTH_SCAN) != PackageManager.PERMISSION_GRANTED)
{
permissionsList.add(Manifest.permission.BLUETOOTH_SCAN);
reasonList.add(""); // Work-a-round. If empty, present no dialog explaining the request to the user
//reasonList.add(getString(R.string.permissions_scan_12));
}
if (checkSelfPermission(Manifest.permission.ACCESS_BACKGROUND_LOCATION) != PackageManager.PERMISSION_GRANTED)
{
permissionsList.add(Manifest.permission.ACCESS_BACKGROUND_LOCATION);
reasonList.add(getString(R.string.permissions_background));
}
indexMax = 3; // Need three permissions
}
else if (isVersionM_Plus)
{
// Need location permissions to use the BTLE Scanner. Some versions of Android after 6 require FINE location and
// some require only coarse and some both. TO minimize headache, I always ask for FINE and place COARSE location
// in the Manifest file. That gives you use of the BTLE scanner for all pre-12 versions of Android
if (checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) // Android 6 +
{
permissionsList.add(Manifest.permission.ACCESS_FINE_LOCATION); // Require ACCESS_COARSE_LOCATION in Manifest file as well
reasonList.add(getString(R.string.permissions_location));
indexMax = 1; // Need only one here
}
if (isVersionOtoR) // Android 8 - 11. For these versions need BACKGROUND permission to use the scanner in the background.
{
if (checkSelfPermission(Manifest.permission.ACCESS_BACKGROUND_LOCATION) != PackageManager.PERMISSION_GRANTED)
{
permissionsList.add(Manifest.permission.ACCESS_BACKGROUND_LOCATION);
reasonList.add(getString(R.string.permissions_background));
indexMax = 2; // Need two permissions here
}
}
}
// If no permissions are needed, return true.
if (permissionsList.size() == 0)
{
return true;
}
// Otherwise, begin the permission request sequence which involves launching permission requests.
// The process is asynchronous, so the launch returns immediately.
requestPermissions(index);
return false; // returning false indicates that permissions have not been granted and the request process is ongoing
}
// THis method pops up an application dialog explaining to the user why the application needs the requested permission.
// When the user clicks OK, the permission request is launched. Android pops up whatever system action it dreams up to
// handle the request. Sometimes it is a dialog, and sometimes it is an activity.
// After the user responds, the result is returned in ActivityResultCallback above. The result is a boolean - true if
// granted, false if not. Within the callback, the 'index' is checked. If there are more permissions to request,
// this method is called again. If not, the summary method below is called.
// It's ugly, but it is the only way I have been able to figure out how to place an explanation dialog before each
// Android System action for the permission. Using the multiple permission approach I could not get my dialogs to
// appear before each of the Android actions.
@SuppressLint("NewApi")
private void requestPermissions(int index)
{
if (reasonList.get(index).isEmpty()) // Work-a-round for Android 12. If explanation is empty then
{
// Skip explanation dialog but still request permission. Android pops up no dialog but auto-grants permission.
activityResultLauncher.launch(permissionsList.get(index));
return;
}
// Popup a dialog explaining why the app needs this permission and perhaps what Android is going to put you
// through to grant the permission. For example, for BLUETOOTH_CONNECT/SCAN permissions Android pops up a
// dialog. But for BACKGROUND permissions, Android presents an Activity. Exiting the dialog requires different
// behavior from the user than exiting an activity.
final androidx.appcompat.app.AlertDialog.Builder builder =
new androidx.appcompat.app.AlertDialog.Builder(new ContextThemeWrapper(this, R.style.Theme_AppCompat_Light));
builder.setTitle("Health@Home needs the following permission:");
if (isVersionN_Plus)
{
builder.setMessage(Html.fromHtml(reasonList.get(index), Html.FROM_HTML_MODE_LEGACY));
}
else
{
builder.setMessage(Html.fromHtml(reasonList.get(index)));
}
builder.setPositiveButton(android.R.string.ok, null);
builder.setOnDismissListener(dialog ->
{
Log.v(TAG, "HH2: Requesting permissions");
activityResultLauncher.launch(permissionsList.get(index));
});
builder.show();
}
// THis method just summarizes the results of the permissions.
@SuppressLint("NewApi")
private void handlePermissionSummary()
{
boolean connectOk = (ActivityCompat.checkSelfPermission(this, Manifest.permission.BLUETOOTH_CONNECT) == PackageManager.PERMISSION_GRANTED);
boolean scanOk = (ActivityCompat.checkSelfPermission(this, Manifest.permission.BLUETOOTH_SCAN) == PackageManager.PERMISSION_GRANTED);
boolean locationOk = (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED);
boolean backgroundOk = (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_BACKGROUND_LOCATION) == PackageManager.PERMISSION_GRANTED);
Log.d(TAG, "HH2: BLUETOOTH_CONNECT Permissions are " + connectOk);
Log.d(TAG, "HH2: BLUETOOTH_SCAN Permissions are " + scanOk);
Log.d(TAG, "HH2: ACCESS_FINE_LOCATION Permissions are " + locationOk);
Log.d(TAG, "HH2: ACCESS_BACKGROUND_LOCATION Permissions are " + backgroundOk);
String message = "";
if (!connectOk && isVersionS_Plus)
{
message = "<p><b>Bluetooth Connect permissions not given.</b> You will be unable to find and connect to Bluetooth Low Energy devices</p>";
}
if (!scanOk && isVersionS_Plus)
{
message = "<p><b>Bluetooth Scan permissions not given.</b> You will be unable to find and connect to Bluetooth Low Energy devices</p>";
}
if (!locationOk && !isVersionS_Plus)
{
message = "<p><b>Location permissions not given.</b> You will be unable to find and connect to Bluetooth Low Energy devices</p>";
}
if (!backgroundOk && isVersionO_Plus)
{
message = message + "<p><b>Background permissions not given.</b> Operations with Bluetooth Low Energy devices will only work " +
"while Health@Home PHG is visible on the screen.</p>";
}
if (!message.isEmpty())
{
message = message + "<p>Remedies:<br>1. Restart Health@Home PHG.<br>2. Set permissions directly in the Android Settings menu<br>" +
"3. Uninstall and re-install Health@Home PHG</p>";
final AlertDialog.Builder builder =
new AlertDialog.Builder(new ContextThemeWrapper(this, R.style.Theme_AppCompat_Light));
builder.setTitle("Health@Home not given certain permissions!");
if (isVersionN_Plus)
{
builder.setMessage(Html.fromHtml(message, Html.FROM_HTML_MODE_LEGACY));
}
else
{
builder.setMessage(Html.fromHtml(message));
}
builder.setPositiveButton(android.R.string.ok, null);
builder.setOnDismissListener(dialog ->
{
Log.d(TAG, "Starting Login");
// setLoginScreen(); // Do whatever you would do after permissions are handled
});
builder.show();
return;
}
Log.d(TAG, "Starting Login");
// setLoginScreen(); // Do whatever you would do after permissions are handled
}
}
https://stackoverflow.com/questions/61909313
复制