Получение исключения отказа от разрешения
У меня есть активность в моем приложении, которая позволяет пользователю выбирать несколько файлов с устройства один за другим, я использую такое намерение:
Intent intent = new Intent();
intent.setType("*/*");
intent.setAction(Intent.ACTION_GET_CONTENT);
startActivityForResult(Intent.createChooser(intent, getString(R.string.select_attachments_activity_chooser_label)), SELECT_PICTURE);
Это работает отлично, я получаю Uri из выбранных файлов, они выглядят так:
content://com.android.providers.media.documents/document/image%3A42555
Затем, если файл является изображением, я его декодирую с помощью:
InputStream streamForDecodeBitmap = MyApp.getContext().getContentResolver().openInputStream(uri);
Bitmap bitmap = BitmapFactory.decodeStream(streamForDecodeBitmap, null, options);
Когда пользователь нажимает кнопку, я передаю список Uris другому действию с помощью намерения и в этом действии в AsyncTask, я кодирую файл в base64 для отправки по сети:
InputStream is = MyApp.getContext().getContentResolver().openInputStream(uri);
byte[] inputData = getBytes(is);
is.close();
return Base64.encodeToString(inputData, Base64.DEFAULT);
Проблема заключается в том, когда я открываю inputStream, иногда он работает, но большую часть времени я получаю это исключение:
E/AndroidRuntime(22270): Caused by: java.lang.SecurityException: Permission Denial: opening provider com.android.providers.media.MediaDocumentsProvider from ProcessRecord{42858fe0 22270:co.uk.manifesto.freeagentapp/u0a246} (pid=22270, uid=10246) requires android.permission.MANAGE_DOCUMENTS or android.permission.MANAGE_DOCUMENTS
Это все разрешения в моем манифесте:
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.GET_TASKS" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.MANAGE_DOCUMENTS"/>
Я тестирую устройство с KITKAT (API 19).
Ответы
Ответ 1
Также проверьте эти вопросы:
Android KitKat securityException при попытке чтения из MediaStore
Предотвращение отказа: открытие поставщика
Для этой же проблемы в KitKat я использовал это. Это вариант/обходной путь, который я нашел из одной из ссылок Stack Overflow, вы сможете выбирать файлы из Загрузки/Недавние.
public static final int KITKAT_VALUE = 1002;
Intent intent;
if (Build.VERSION.SDK_INT < 19) {
intent = new Intent();
intent.setAction(Intent.ACTION_GET_CONTENT);
intent.setType("*/*");
startActivityForResult(intent, KITKAT_VALUE);
} else {
intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setType("*/*");
startActivityForResult(intent, KITKAT_VALUE);
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == KITKAT_VALUE ) {
if (resultCode == Activity.RESULT_OK) {
// do something here
}
}
}
Ссылка:
Галерея Android на KitKat возвращает разные Uri для Intent.ACTION_GET_CONTENT
http://developer.android.com/guide/topics/providers/document-provider.html#client
Ответ 2
Когда управление и данные возвращаются к вашей активности в
protected void onActivityResult(int reqCode, int resultCode, Intent data)
ваша деятельность, в то же время, дает разрешения на доступ к URL-адресу изображения в переданном намерении. Ваша активность будет в состоянии прочитать URL-адрес в намерении и отобразить изображение. Но вы не сможете передавать какие-либо разрешения другим действиям или новому потоку выполнения. Вот почему вы получаете SecurityException для android.permission.MANAGE_DOCUMENTS.
Один из способов избежать вашей проблемы с разрешением - это просто получить данные в действии с разрешениями. В вашем случае, из действия, у которого есть разрешение, ваша кодировка
InputStream is = getContentResolver().openInputStream(uri);
byte[] inputData = getBytes(is);
is.close();
String encoded = Base64.encodeToString(inputData, Base64.DEFAULT);
После того, как у вас есть закодированная строка, вы можете передать ее любым другим действиям, которые могут понадобиться, или вы можете передать ее в AsyncTask для отправки в другом месте.
Ответ 3
Убедитесь, что вы используете ContentResolver из Activity, который запрашивал контент, например:
activity.getContentResolver()
Вместо MyApp.getContext().getContentResolver()
Это было очевидно для меня, и ему не нужно было добавлять android.permission.MANAGE_DOCUMENTS
в манифест.
И вы вызываете намерение, используя это:
if (android.os.Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
intent.setAction(Intent.ACTION_GET_CONTENT);
} else {
intent.setAction(Intent.ACTION_OPEN_DOCUMENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
}
Дополнительная информация: https://developer.android.com/guide/topics/providers/document-provider.html#client
Ответ 4
Написание ACTION_OPEN_DOCUMENT не помогает полностью. После перезапуска ваше разрешение URI исчезнет даже после этого действия.
if (android.os.Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
intent.setAction(Intent.ACTION_GET_CONTENT);
} else {
intent.setAction(Intent.ACTION_OPEN_DOCUMENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
}
Чтобы закончить ответ Алесио Карвальо на эту документацию на той же странице, Существует хорошее объяснение разрешений URI и их сохранение.
Просто добавьте это в свой метод onActivityResult, и ваше разрешение сохраняется через перезапуск устройства. (Протестировано с помощью Google Pixel.)
override fun onActivityResult(requestCode:Int, resultCode:Int, intent:Intent?) {
if(requestCode == SELECT_PICTURE && intent != null){
val uri = intent.getData()
val takeFlags = intent.getFlags() and (Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
activity.getContentResolver().takePersistableUriPermission(uri, takeFlags)
PreferenceHelper.setHeaderImageUri(activity, uri.toString())
}
}
(извините kotlin lover... версия java находится внутри ссылки)
Ответ 5
Следует отметить, что если пользователь вручную удаляет разрешения для Uri, которые вы ранее сохранили, вы получите это же Исключение.
Настройки Android → Приложение → Имя приложения → Хранение → Очистить доступ
Вам нужно проверить, что вы все еще имеете доступ к Uri, когда используете его.