Примечание: Я прошел через другие вопросы о подобных исключениях.Это не дубликат - ни один из них не касается выбора PDF или этой конкретной ошибки.
Пряжка.
Мы работаем над проектом Xamarin.Android.Существует «прикрепить документ» активность.
Когда пользователь пытается выбрать PDF (из каталога загрузок), он выдает следующее исключение:
Java.Lang.SecurityException: Отказ в разрешении: чтение com.android.provider.downloads.DownloadStorageProvider uri content: //com.android.providers.downloads.documents/226 from pid = 13877, uid = 10282 требует, чтобы вы получили доступ с помощью ACTION_OPEN_DOCUMENT или связанных API
Назначение средства выбора файла:
ChooseFile.Click += (sender, e) =>
{
Intent intent = new Intent();
intent.SetType("*/*");
intent.SetAction(Intent.ActionOpenDocument);
intent.AddCategory(Intent.CategoryOpenable);
if (global::Android.OS.Build.VERSION.SdkInt >= global::Android.OS.BuildVersionCodes.N)
{
intent.AddFlags(ActivityFlags.GrantReadUriPermission);
intent.AddFlags(ActivityFlags.GrantWriteUriPermission);
intent.AddFlags(ActivityFlags.GrantPersistableUriPermission);
intent.PutExtra(Intent.ExtraLocalOnly, true);
intent.AddFlags(ActivityFlags.NoHistory);
}
try
{
StartActivityForResult(Intent.CreateChooser(intent, "Select File"), FILE_SELECT_CODE);
}
catch (Exception ex)
{
Toast.MakeText(this, "Please install a File Manager.", ToastLength.Short).Show();
}
};
Метод OnActivityResult:
protected override void OnActivityResult(int requestCode, [GeneratedEnum] Result resultCode, Intent data)
{
base.OnActivityResult(requestCode, resultCode, data);
if ((requestCode == FILE_SELECT_CODE) && (resultCode == Result.Ok) && (data != null))
{
// Get the Uri of the selected file
uri = data.Data;
string path = "";
bool isdoc = DocumentsContract.IsDocumentUri(this, uri);
if (isdoc)
{
if (IsDownloadsDocument(uri))
{
string id = DocumentsContract.GetDocumentId(uri);
global::Android.Net.Uri contentUri = null;
if (global::Android.OS.Build.VERSION.SdkInt >= global::Android.OS.BuildVersionCodes.N)
{
contentUri = ContentUris.WithAppendedId(global::Android.Net.Uri.Parse("content://com.android.providers.downloads.documents"), Convert.ToInt64(id));
}
else
{
contentUri = ContentUris.WithAppendedId(global::Android.Net.Uri.Parse("content://downloads/public_downloads"), Convert.ToInt64(id));
}
path = GetDataColumn(this, contentUri, null, null);
}
else if (IsMediaDocument(uri))
{
string docId = DocumentsContract.GetDocumentId(uri);
string[] split = docId.Split(':');
string type = split[0];
global::Android.Net.Uri contentUri = null;
if ("image".Equals(type))
{
contentUri = MediaStore.Images.Media.ExternalContentUri;
}
else if ("video".Equals(type))
{
contentUri = MediaStore.Video.Media.ExternalContentUri;
}
else if ("audio".Equals(type))
{
contentUri = MediaStore.Audio.Media.ExternalContentUri;
}
string selection = "_id=?";
string[] selectionArgs = new String[]
{
split[1]
};
path = GetDataColumn(this, contentUri, selection, selectionArgs);
}
}
else
{
path = GetRealPathFromURI(global::Android.Net.Uri.Parse(getImageUrlWithAuthority(this, uri)));
}
txtFileName.Text = System.IO.Path.GetFileName(path);
fileStream = File.ReadAllBytes(path);
}
}
Проверка URI каталога файлов:
private bool IsExternalStorageDocument(global::Android.Net.Uri uri)
{
return "com.android.externalstorage.documents".Equals(uri.Authority);
}
private bool IsDownloadsDocument(global::Android.Net.Uri uri)
{
return "com.android.providers.downloads.documents".Equals(uri.Authority);
}
private bool IsMediaDocument(global::Android.Net.Uri uri)
{
return "com.android.providers.media.documents".Equals(uri.Authority);
}
private bool IsGooglePhotosUri(global::Android.Net.Uri uri)
{
return "com.google.android.apps.photos.content".Equals(uri.Authority);
}
Метод GetDataColumn:
private string GetDataColumn(Context context, global::Android.Net.Uri uri, String selection, string[] selectionArgs)
{
ICursor cursor = null;
string column = "_data";
string[] projection =
{
column
};
try
{
cursor = context.ContentResolver.Query(uri, projection, selection, selectionArgs, null);
if (cursor != null && cursor.MoveToFirst())
{
int index = cursor.GetColumnIndexOrThrow(column);
return cursor.GetString(index);
}
}
finally
{
if (cursor != null)
cursor.Close();
}
return null;
}
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.jnbmedical.portal"
android:installLocation="auto"
android:versionCode="33"
android:versionName="3.3">
<uses-sdk android:minSdkVersion="19" android:targetSdkVersion="28" />
<application android:label="JandB" android:icon="@drawable/icon">
<provider android:name="android.support.v4.content.FileProvider"
android:authorities="com.jnbmedical.portal.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_provider_paths" />
</provider>
</application>
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.CALL_PHONE" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.MANAGE_DOCUMENTS" />
<uses-permission android:name="android.permission.MEDIA_CONTENT_CONTROL" />
<uses-permission android:name="android.permission.INSTALL_PACKAGES" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
<uses-permission android:name="com.jandbmedical.portal.testapp.permission.C2D_MESSAGE" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
</manifest>
file_provider_paths.xml
<?xml version="1.0" encoding="utf-8" ?>
<paths>
<external-path
name="external_files" path="." />
</paths>
Недавно мы добавили разрешения во время выполнения (в виде заставки) для работы с Android 23 (т.е. 6.0 - зефир) и выше.
Поля и массив разрешений:
const int REQUEST = 0;
readonly string[] PERMISSIONS =
{
Manifest.Permission.AccessCoarseLocation,
Manifest.Permission.AccessFineLocation,
Manifest.Permission.ReadExternalStorage,
Manifest.Permission.WriteExternalStorage,
Manifest.Permission.ReadPhoneState,
};
Метод CheckPermissions:
private void CheckPermissions()
{
if (ContextCompat.CheckSelfPermission(this, Manifest.Permission.ReadPhoneState) != Permission.Granted
|| ContextCompat.CheckSelfPermission(this, Manifest.Permission.AccessFineLocation) != Permission.Granted
|| ContextCompat.CheckSelfPermission(this, Manifest.Permission.AccessCoarseLocation) != Permission.Granted
|| ContextCompat.CheckSelfPermission(this, Manifest.Permission.ReadExternalStorage) != Permission.Granted
|| ContextCompat.CheckSelfPermission(this, Manifest.Permission.WriteExternalStorage) != Permission.Granted)
{
//Permissions have not been granted
ActivityCompat.RequestPermissions(this, PERMISSIONS, REQUEST);
}
else
{
//Permissions have been granted
TelephonyManager tm = (TelephonyManager)GetSystemService(TelephonyService);
Util.UUID = tm.DeviceId;
StartActivity(typeof(MainActivity));
Finish();
}
}
Метод OnRequestPermissionsResult:
public override void OnRequestPermissionsResult(int requestCode, string[] permissions, Permission[] grantResults)
{
switch (requestCode)
{
case REQUEST:
{
var permissionGranted = VerifyPermissions(grantResults);
if (permissionGranted)
{
TelephonyManager tm = (TelephonyManager)GetSystemService(TelephonyService);
Util.UUID = tm.DeviceId;
StartActivity(typeof(MainActivity));
Finish();
}
else
{
this.ShowInformationWithClick(Resources.GetText(Resource.String.Alert), Resources.GetText(Resource.String.OK), "App requires storage, location, and phone access to function properly. Please grant necessary permissions. App will not work if permissions are not granted.", HandleClicked);
}
}
break;
}
}
Метод VerifyPermissions:
public static bool VerifyPermissions(Permission[] grantResults)
{
// At least one result must be checked.
if (grantResults.Length < 1)
return false;
// Verify that each required permission has been granted, otherwise return false.
foreach (Permission result in grantResults)
{
if (result != Permission.Granted)
{
return false;
}
}
return true;
}
Хотелось бы, чтобы был TL; DR: версия этого, но я не знаю, в чем проблема, потому что наш код почти похож на другие примеры кода для этой операции.Я прочитал кое-что о постоянных URI и предоставлении разрешений;Я попробовал некоторые из них - они не работали.И некоторые не совсем поняли - этот является одним из них.
Спасибо.