Android Gallery на Android 4.4 (KitKat) возвращает другой URI для Intent.ACTION_GET_CONTENT

До KitKat (или до новой галереи) Intent.ACTION_GET_CONTENT возвращал URI, подобный этому

content:// media/external/images/media/3951.

Использование ContentResolver и запрос MediaStore.Images.Media.DATA вернул URL-адрес файла.

Однако в KitKat галерея возвращает URI (через «Last») следующим образом:

content ://com.android.providers.media.documents/document/image: 3951

Как с этим справиться?


Попробуйте следующее:

  if (Build.VERSION.SDK_INT  

Вероятно, понадобится

@SuppressLint («NewApi»)

для

takePersistableUriPermission


Это не требует специальных разрешений и работает с Storage Access Framework, а также с неофициальным шаблоном ContentProvider (путь к файлу в поле _data ).

 /** * Получить путь к файлу из Uri.  Это получит путь к Storage Access * Framework Documents, а также поле _data для MediaStore и * других ContentProvider на основе файлов.  * * @param context Контекст.  * @param uri Uri для запроса.  * @author paulburke */public static String getPath (final Context context, final Uri uri) {final boolean isKitKat = Build.VERSION.SDK_INT> = Build.VERSION_CODES.KITKAT; //DocumentProvider if (isKitKat && DocumentsContract.isDocumentUri (context, uri)) {//ExternalStorageProvider if (isExternalStorageDocument (uri)) {final String docId = DocumentsContract.getDocumentId (uri);  последняя строка [] split = docId.split (":");  final String type = split [0];  if ("первичный" .equalsIgnoreCase (тип)) {return Environment. getExternalStorageDirectory () + «/» + разделение [1];  }//TODO обрабатывает неосновные тома}//DownloadsProvider else if (isDownloadsDocument (uri)) {final String id = DocumentsContract.getDocumentId (uri);  финальный Uri contentUri = ContentUris.withAppendedId (Uri.parse ("content://downloads/public_downloads"), Long.valueOf (id));  return getDataColumn (context, contentUri, null, null);  }//MediaProvider else if (isMediaDocument (uri)) {final String docId = DocumentsContract.getDocumentId (uri);  последняя строка [] split = docId.split (":");  final String type = split [0];  Uri contentUri = null;  if ("изображение" .equals (тип)) {contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;  } else if ("видео" .equals (тип)) {contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;  } еще если ("аудио" .equals (тип)) {contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;  } final String selection = "_id =?";  окончательная строка [] selectionArgs = новая строка [] {разделение [1]};  return getDataColumn (context, contentUri, selection, selectionArgs);  }}//MediaStore (и вообще) else if ("content" .equalsIgnoreCase (uri.getScheme ())) {//Возвращаем удаленный адрес if (isGooglePhotosUri (uri)) return uri.getLastPathSegment ();  return getDataColumn (context, uri, null, null);  }//Файл иначе if ("файл" .equalsIgnoreCase (uri.getScheme ())) {return uri.getPath ();  } return null;}/** * Получить значение столбца данных для этого Uri.  Это полезно для * MediaStore Uris и других файловых ContentProvider.  * * @param context Контекст.  * @param uri Uri для запроса.  * @param selection (Необязательно) Фильтр, используемый в запросе.  * @param selectionArgs (Необязательно) Аргументы выбора, используемые в запросе.  * @return Значение столбца _data, которое обычно является путем к файлу.  */public static String getDataColumn (Context context, Uri uri, String selection, String [] selectionArgs) {Cursor cursor = null;  последний столбец строки = "_data";  последняя строка [] проекция = {столбец};  попробуйте {курсор = context.getContentResolver (). query (uri, projection, selection, selectionArgs, null);  если (курсор! = null && cursor.moveToFirst ()) {final int index = cursor.getColumnIndexOrThrow (столбец);  return cursor.getString (индекс);  }} наконец {если (курсор! = ноль) cursor.close ();  } return null;}/** * @param uri Uri для проверки.  * @return Указывает, является ли полномочие Uri ExternalStorageProvider.  */public static boolean isExternalStorageDocument (Uri uri) {return "com.android.externalstorage.documents" .equals (uri.getAuthority ());}/** * @param uri Uri для проверки.  * @return Указывает, является ли авторитет Uri DownloadsProvider.  */public static boolean isDownloadsDocument (Uri uri) {return "com.android.providers.downloads.documents" .equals (uri.getAuthority ());}/** * @param uri Проверяемый Uri.  * @return Указывает, является ли Uri центром сертификации MediaProvider.  */public static boolean isMediaDocument (Uri uri) {return "com.android.providers.media.documents". equals (uri.getAuthority ());}/** * @param uri Uri для проверки.  * @return. Является ли авторитетным источником Uri Google Фото.  */public static boolean isGooglePhotosUri (Uri uri) {return "com.google.android.apps.photos.content" .equals (uri.getAuthority ());}  

См. последняя версия этого метода здесь.

21


Возникла та же проблема, я попробовал решение, описанное выше, но хотя в целом оно работало, по какой-то причине я получал отказ в разрешении от поставщика контента Uri для некоторых изображений, хотя у меня был android. разрешение.MANAGE_DOCUMENTS разрешение добавлено правильно.

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

 //KITKATi = новое намерение (Intent.ACTION_PICK, android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);  startActivityForResult (i, CHOOSE_IMAGE_REQUEST);  

, а затем загрузите изображение:

  Uri selectedImageURI = data.getData (); input  = c.getContentResolver (). openInputStream (selectedImageURI);  BitmapFactory.decodeStream (input, null, opts);  

EDIT

ACTION_OPEN_DOCUMENT может потребовать от вас сохранить флаги разрешений и т. д. и, как правило, часто приводит к исключениям безопасности …

Другое решение — использовать ACTION_GET_CONTENT в сочетании с c.getContentResolver (). openInputStream (selectedImageURI) , который будет работать как с pre-KK, так и с KK. Kitkat будет использовать новое представление документов, и это решение будет работать со всеми приложениями, такими как Фотографии, Галерея, Проводник, Dropbox, Google Диск и т. Д.), Но помните, что при использовании этого решения вы должны создать изображение в своем onActivityResult () и сохранить его, например, на SD-карте. Воссоздание этого изображения из сохраненного uri при следующем запуске приложения вызовет исключение безопасности на преобразователе контента, даже если вы добавите флаги разрешений, как описано в документации Google API (это произошло, когда я провел некоторое тестирование)

Кроме того, Рекомендации Android Developer API предполагают:

ACTION_OPEN_DOCUMENT не предназначен для замены ACTION_GET_CONTENT. Тот, который вы должны использовать, зависит от потребностей вашего приложения:

Используйте ACTION_GET_CONTENT, если вы хотите, чтобы ваше приложение просто считывало/импортирует данные. При таком подходе приложение импортирует копию данных, например файл изображения.

Используйте ACTION_OPEN_DOCUMENT, если вы хотите, чтобы ваше приложение имело длительный постоянный доступ к документам, принадлежащим поставщику документов. Примером может служить приложение для редактирования фотографий, которое позволяет пользователям редактировать изображения, хранящиеся в поставщике документов..

7


Как уже упоминалось Commonsware, вы не должны предполагать, что поток, который вы получаете через ContentResolver , можно преобразовать в файл.

Что вам действительно нужно сделать, так это открыть InputStream из ContentProvider , а затем создайте из него Bitmap. И он работает в 4.4 и более ранних версиях, нет необходимости в отражении.

 //cxt -> текущий контекст InputStream input;  Bitmap bmp;  попробуйте {input = cxt.getContentResolver (). openInputStream (fileUri);  bmp = BitmapFactory.decodeStream (ввод);  } catch (FileNotFoundException e1) {}  

Конечно, если вы обрабатываете большие изображения, вы должны загружать их с помощью соответствующего inSampleSize : http:// developer.android.com/training/displaying-bitmaps/load-bitmap.html. Но это уже другая тема.

10


Я считаю, что уже опубликованные ответы должны побудить людей двигаться в правильном направлении. Однако вот то, что я сделал, имело смысл для устаревшего кода, который я обновлял. В устаревшем коде URI из галереи использовался для изменения и последующего сохранения изображений.

До версии 4.4 (и Google Drive) URI выглядели следующим образом: content: //media/external/images/media/41

Как указано в вопросе, они чаще выглядят так: content://com.android. Provider.media.documents/document/image: 3951

Поскольку мне нужна была возможность сохранять изображения и не нарушать уже существующий код, я просто скопировал URI из галереи в папка с данными приложения. Затем возник новый URI из сохраненного файла изображения в папке данных.

Идея вот в чем:

  Intent intent = new Intent (Intent.  ACTION_GET_CONTENT); intent.setType ("изображение/*"); startActivityForResult (намерение), CHOOSE_IMAGE_REQUEST); public void onActivityResult (int requestCode, int resultCode, Intent data) {super.onActivityResult (requestCode, resultCode, data);  Файл tempFile = новый файл (this.getFilesDir (). GetAbsolutePath (), «temp_image»); //Копируем содержимое URI во временный файл.  попробуйте {tempFile.createNewFile ();  copyAndClose (this.getContentResolver (). openInputStream (data.getData ()), новый FileOutputStream (tempFile));  } catch (IOException e) {//Ошибка журнала}//Теперь получаем новый URI URI newUri = Uri.fromFile (tempFile); /* Используйте новый объект URI, как и раньше */}  

Примечание. CopyAndClose () просто выполняет файловый ввод-вывод для копирования InputStream в FileOutputStream. Код не размещен.

4


Сразу хотел сказать, что это гениальный ответ, и я уже давно им пользуюсь без проблем. Но некоторое время назад я столкнулся с проблемой, что DownloadsProvider возвращает URI в формате content://com.android.providers.downloads.documents/document/raw%3A%2Fstorage%2Femulated%2F0%2FDownload% 2Fdoc.pdf и, следовательно, приложение вылетает из-за NumberFormatException , так как невозможно анализировать его сегменты uri так долго. Но сегмент raw: содержит прямой uri, который можно использовать для получения файла, на который есть ссылка. Поэтому я исправил это, заменив содержимое isDownloadsDocument (uri) if на следующее:

  final  String id = DocumentsContract.getDocumentId (uri); if (! TextUtils.isEmpty (id)) {if (id.startsWith ("raw:")) {return id.replaceFirst ("raw:", "");} попробуйте  {окончательный Uri contentUri = ContentUris.withAppendedId (Uri.parse ("content://downloads/public_downloads"), Long.valueOf (id));  return getDataColumn (context, contentUri, null, null);} catch (NumberFormatException e) {Log.e ("FileUtils", "Поставщик загрузок возвратил неожиданный uri" + uri.toString (), e);  return null;}}  

2


Я объединил несколько ответов в одно рабочее решение, которое приводит к пути к файлу

Тип Mime не имеет отношения к цели примера.

  Намерение намерения;  if (Build.VERSION.SDK_INT> = 19) {намерение = новое намерение (Intent.ACTION_OPEN_DOCUMENT);  intent.putExtra (Intent.EXTRA_ALLOW_MULTIPLE, ложь);  intent.addFlags (Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION);  } else {намерение = новое намерение (Intent.ACTION_GET_CONTENT);  } intent.addFlags (Intent.FLAG_GRANT_READ_URI_PERMISSION);  intent.setType ("приложение/октет-поток");  если (isAdded ()) {startActivityForResult (намерение, RESULT_CODE);  }  

Обработка результата

  @Override public void onActivityResult (int requestCode, int resultCode, Intent data) {if (requestCode =  = RESULT_CODE && resultCode == Activity.RESULT_OK) {Uri uri = data.getData ();  if (uri! = null &&! uri.toString (). isEmpty ()) {if (Build.VERSION.SDK_INT> = 19) {final int takeFlags = data.getFlags () & Intent.FLAG_GRANT_READ_URI_PERMISSION; //noinspection ResourceType getActivity (). getContentResolver () .takePersistableUriPermission (uri, takeFlags);  } String filePath = FilePickUtils.getSmartFilePath (getActivity (), uri); //делаем с ним то, что вам нужно ...}}}  

FilePickUtils

  import android.annotation.SuppressLint; import  android.content.ContentUris; импорт android.content.Context; импорт android.database.Cursor; импорт android.net.Uri; импорт android.os. Сборка; импорт android.os.Environment; импорт android.provider.DocumentsContract; импорт android.provider.MediaStore; открытый класс FilePickUtils {частная статическая строка getPathDeprecated (Context ctx, Uri uri) {if (uri == null) {return null;  } String [] projection = {MediaStore.Images.Media.DATA};  Курсор cursor = ctx.getContentResolver (). Query (uri, projection, null, null, null);  если (курсор! = ноль) {int column_index = курсор .getColumnIndexOrThrow (MediaStore.Images.Media.DATA);  cursor.moveToFirst ();  возвратите cursor.getString (column_index);  } return uri.getPath ();  } общедоступная статическая строка getSmartFilePath (Context ctx, Uri uri) {if (Build.VERSION.SDK_INT  = Build.VERSION_CODES.KITKAT; //DocumentProvider if (isKitKat && DocumentsContract.isDocumentUri (context, uri)) {//ExternalStorageProvider if (isExternalStorageDocument (uri)) {final String docId = DocumentsContract.getDocumentId (uri);  последняя строка [] split = docId.split (":");  final String type = split [0];  if ("первичный" .equalsIgnoreCase (тип)) {return Environment.getExternalStorageDirectory () + "/" + split [1];  }//TODO обрабатывает неосновные тома}//DownloadsProvider else if (isDownloadsDocument (uri)) {final String id = DocumentsContract.getDocumentId (uri);  финальный Uri contentUri = ContentUris.withAppendedId (Uri.parse ("content://downloads/public_downloads"), Long.valueOf (id));  return getDataColumn (context, contentUri, null, null);  }//MediaProvider else if (isMediaDocument (uri)) {final String docId = DocumentsContract.getDocumentId (uri);  последняя строка [] split = docId.split (":");  final String type = split [0];  Uri contentUri = null;  if ("изображение" .equals (тип)) {contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;  } else if ("видео" .equals (тип)) {contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;  } еще если ("аудио" .equals (тип)) {contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;  } final String selection = "_id =?";  окончательная строка [] selectionArgs = новая строка [] {разделение [1]};  return getDataColumn (context, contentUri, selection, selectionArgs);  }}//MediaStore (и вообще) else if ("content" .equalsIgnoreCase (uri.getScheme ())) {return getDataColumn (context, uri, null, null);  }//Файл иначе if ("файл" .equalsIgnoreCase (uri.getScheme ())) {return uri.getPath ();  } return null;  }/** * Получить значение столбца данных для этого Uri.  Это полезно для * MediaStore Uris и других файловых ContentProvider.  * * @param context Контекст.  * @param uri Uri для запроса.  * @param selection (Необязательно) Фильтр, используемый в запросе.  * @param selectionArgs (Необязательно) Аргументы выбора, используемые в запросе. * @return Значение столбца _data, которое обычно является путем к файлу.  */public static String getDataColumn (Context context, Uri uri, String selection, String [] selectionArgs) {Cursor cursor = null;  последний столбец строки = "_data";  последняя строка [] проекция = {столбец};  попробуйте {курсор = context.getContentResolver (). query (uri, projection, selection, selectionArgs, null);  если (курсор! = null && cursor.moveToFirst ()) {final int column_index = cursor.getColumnIndexOrThrow (столбец);  возвратите cursor.getString (column_index);  }} наконец {если (курсор! = ноль) cursor.close ();  } return null;  }/** * @param uri Uri для проверки.  * @return Указывает, является ли полномочие Uri ExternalStorageProvider.  */public static boolean isExternalStorageDocument (Uri uri) {return "com.android.externalstorage.documents" .equals (uri.getAuthority ());  }/** * @param uri Uri для проверки.  * @return Указывает, является ли авторитет Uri DownloadsProvider.  */public static boolean isDownloadsDocument (Uri uri) {return "com.android.providers.downloads.documents" .equals (uri.getAuthority ());  }/** * @param uri Uri для проверки.  * @return Указывает, является ли Uri центром сертификации MediaProvider.  */public static boolean isMediaDocument (Uri uri) {return "com.android.providers.media.documents" .equals (uri.getAuthority ());  }}  

2


Вопрос

Как получить фактический путь к файлу из URI

Ответ

Насколько мне известно, нам не нужно получать путь к файлу из URI, потому что в большинстве случаев мы можем напрямую использовать URI для выполнения нашей работы (например, 1. получение растрового изображения 2. Отправка файла на сервер, и т. д.)

1. Отправка на сервер

Мы можем напрямую отправить файл на сервер, используя только URI.

Используя URI, мы можем получить InputStream, который мы можно напрямую отправлять на сервер с помощью MultiPartEntity.

Пример

 /** * Используется для формирования Multi  Сущность для URI (URI, указывающий на какой-то файл, который мы получили из другого приложения).  * * @param uri URI.  * @param context Context.  * @return Multi Part Entity.  */public MultipartEntity formMultiPartEntityForUri (конечный Uri uri, конечный контекст контекста) {MultipartEntity multipartEntity = new MultipartEntity (HttpMultipartMode.BROWSER_COMPATIBLE, null, Charset.forName ("UTF-8"));  попробуйте {InputStream inputStream = mContext.getContentResolver (). openInputStream (uri);  если (inputStream! = null) {ContentBody contentBody = новый InputStreamBody (inputStream, getFileNameFromUri (uri, context));  multipartEntity.addPart ("[ВАШ_КЛЮЧ]", contentBody);  }} catch (Exception exp) {Log.e ("ТЕГ", exp.getMessage ());  } return multipartEntity;}/** * Используется для получения имени файла из URI.  * * @param uri URI.  * @param context Context.  * @return Имя файла из URI. */public String getFileNameFromUri (конечный Uri uri, конечный контекст контекста) {String fileName = null;  if (uri! = null) {//Получаем имя файла. //Схема файла.  if (ContentResolver.SCHEME_FILE.equals (uri.getScheme ())) {Файл файл = новый файл (uri.getPath ());  fileName = file.getName ();  }//Схема содержимого.  иначе, если (ContentResolver.SCHEME_CONTENT.equals (uri.getScheme ())) {Курсор returnCursor = context.getContentResolver (). query (uri, null, null, null, null);  если (returnCursor! = null && returnCursor.moveToFirst ()) {int nameIndex = returnCursor.getColumnIndex (OpenableColumns.DISPLAY_NAME);  fileName = returnCursor.getString (nameIndex);  returnCursor.close ();  }}} return fileName;}  

2. Получение растрового изображения из URI

Если URI указывает на изображение, то мы получим растровое изображение, иначе null:

 / ** * Используется для создания растрового изображения для данного URI.  * 

* 1. Преобразуйте данный URI в растровое изображение. * 2. Рассчитайте соотношение (в зависимости от размера растрового изображения), сколько нам нужно для субдискретизации исходного растрового изображения. * 3. Создайте точечный рисунок в зависимости от соотношения из URI. * 4. Ссылка - http://stackoverflow.com/questions/3879992/how-to-get-bitmap-from-an-uri * * Контекст контекста @param. * @param uri URI файла. * @param bitmapSize Размер растрового изображения, необходимый в PX. * @return Растровое изображение, созданное для данного URI. * @throws IOException */public static Bitmap createBitmapFromUri (final Context context, Uri uri, final int bitmapSize) выдает исключение IOException {//1. Преобразование данного URI в растровое изображение. InputStream input = context.getContentResolver (). OpenInputStream (uri); BitmapFactory.Options onlyBoundsOptions = new BitmapFactory.Options (); onlyBoundsOptions.inJustDecodeBounds = true; onlyBoundsOptions.inDither = true;//необязательный onlyBoundsOptions.inPreferredConfig = Bitmap.Config.ARGB_8888;//необязательный BitmapFactory.decodeStream (input, null, onlyBoundsOptions); input.close (); if ((onlyBoundsOptions.outWidth == -1) || (onlyBoundsOptions.outHeight == -1)) {return null; }//2. Рассчитаем коэффициент. int originalSize = (onlyBoundsOptions.outHeight> onlyBoundsOptions.outWidth)? onlyBoundsOptions.outHeight: onlyBoundsOptions.outWidth; двойное соотношение = (исходный размер> размер изображения)? (originalSize/bitmapSize): 1.0; //3. Создаем растровое изображение. BitmapFactory.Options bitmapOptions = новый BitmapFactory.Options (); bitmapOptions.inSampleSize = getPowerOfTwoForSampleRatio (ratio); bitmapOptions.inDither = true;//необязательный bitmapOptions.inPreferredConfig = Bitmap.Config.ARGB_8888;//необязательный input = context.getContentResolver (). openInputStream (uri); Bitmap bitmap = BitmapFactory.decodeStream (input, null, bitmapOptions); input.close (); return bitmap;}/** * Для параметра Bitmap inSampleSize - нам нужно указать значение в степени двойки. * * @param ratio Коэффициент, округляемый до степени двойки. * @return Коэффициент округляется до ближайшей степени двойки. */private static int getPowerOfTwoForSampleRatio (окончательное двойное соотношение) {int k = Integer.highestOneBit ((int) Math. этаж (коэффициент)); если (k == 0) вернуть 1; else return k;}

Комментарии

  1. Android не предоставляет никаких методов для получить путь к файлу из URI, и в большинстве вышеперечисленных ответов мы жестко закодировали некоторые константы, которые могут не работать в выпуске функции (извините, я могу ошибаться).
  2. Перед тем, как перейти непосредственно к решение получения пути к файлу из URI, попробуйте решить свой вариант использования с помощью URI и методов по умолчанию Android.

Ссылка

  1. https://developer.android.com/guide/topics/providers/content-provider-basics.html
  2. https:// developer.android.com/reference/android/content/ContentResolver.html
  3. https://hc.apache.org/httpcomponents-client-ga/httpmime/apidocs/org/apache/http/ entity/mime/content/InputStreamBody.html

1


Эта библиотека Android обрабатывает изменения регистра в K itKat (включая старые версии — 2.1+):
https://github.com/iPaulPro/aFileChooser

Используйте String path = FileUtils.getPath ( context, uri) , чтобы преобразовать возвращенный Uri в строку пути, используемую во всех версиях ОС. Подробнее об этом здесь: https://stackoverflow.com/a/20559175/860488


Для тех, кто все еще использует код @Paul Burke с Android SDK версии 23 и выше, если в вашем проекте возникла ошибка о том, что вам не хватает EXTERNAL_PERMISSION, и вы совершенно уверены, что уже добавили пользовательское разрешение в свой файл AndroidManifest.xml. Это связано с тем, что вы можете использовать Android API 23 или более поздней версии, и Google потребует еще раз гарантировать разрешение, пока вы выполняете действие для доступа к файлу во время выполнения.

Это означает: если ваша версия SDK — 23 или выше, у вас запрашивается разрешение на ЧТЕНИЕ и ЗАПИСЬ, когда вы выбираете файл изображения и хотите узнать его URI.

А ниже приведен мой код в дополнение к решению Пола Берка. Я добавляю этот код, и мой проект начинает работать нормально.

  private static final int REQUEST_EXTERNAL_STORAGE = 1; private static final String [] PERMISSINOS_STORAGE = {Manifest.permission.READ_EXTERNAL_STORAGE, Manifest  .permission.WRITE_EXTERNAL_STORAGE}; общедоступный статический void verifyStoragePermissions (Activity activity) {int permission = ActivityCompat.checkSelfPermission (activity, Manifest.permission.WRITE_EXTERNAL_STORAGE);  если (разрешение! = PackageManager.PERMISSION_GRANTED) {ActivityCompat.requestPermissions (активность, PERMISSINOS_STORAGE, REQUEST_EXTERNAL_STORAGE);  }}  

И в вашей деятельности и фрагменте, где вы запрашиваете URI:

  private void pickPhotoFromGallery () {CompatUtils.verifyStoragePermissions  (это);  Намерение намерение = новое намерение (намерение. ACTION_GET_CONTENT);  intent.setType ("изображение/*"); //startActivityForResult (намерение, REQUEST_PHOTO_LIBRARY);  startActivityForResult (Intent.createChooser (intent, "选择 照片"), REQUEST_PHOTO_LIBRARY);}  

В моем случае CompatUtils.java — это то место, где я определяю метод verifyStoragePermissions (как статический тип, поэтому Я могу вызвать это в другом действии).

Кроме того, это должно иметь больше смысла, если вы сначала создадите состояние if, чтобы увидеть, превышает ли текущая версия SDK 23 или нет, прежде чем вызывать метод verifyStoragePermissions.


Вот что я делаю:

  Uri selectedImageURI = data.getData ();  imageFile = новый файл (getRealPathFromURI (selectedImageURI));  частная строка getRealPathFromURI (Uri contentURI) {Cursor cursor = getContentResolver (). query (contentURI, null, null, null, null);  if (cursor == null) {//Источник - Dropbox или другой аналогичный путь к локальному файлу return contentURI.getPath ();  } еще {cursor.moveToFirst ();  int idx = cursor.getColumnIndex (MediaStore.Images.ImageColumns.DATA);  возвратите cursor.getString (idx);  }}  

ПРИМЕЧАНИЕ. Метод managedQuery () устарел, поэтому я его не использую.

Этот ответ от m3n0R на вопрос, что android получает реальный путь с помощью Uri.getPath (), и я не требую кредита. Я просто подумал, что люди, которые еще не решили эту проблему, могут это использовать.

3


Я попробовал несколько ответов здесь и думаю, что у меня есть решение, которое будет работать каждый раз и управлять разрешениями.

Он основан на умном решении от LEO. Этот пост должен содержать весь код, необходимый для работы, и он должен работать на любом телефоне и на любой версии Android;)

Чтобы иметь возможность выбирать файл с SD-карты, вам понадобится это в вашем манифесте:

    

Константы:

  private static final int PICK_IMAGE = 456; //Любое число, которое вам нравится, public static final int MY_PERMISSIONS_REQUEST_READ_EXTERNAL = 28528; //Любое число, которое вам нравится, публичный статический final String FILE_TEMP_NAME = "temp_image"; //Любое имя файла, которое вам нравится  

Проверяем разрешение и запускаемImagePick, если возможно

  if (ContextCompat.checkSelfPermission (getThis (),  Manifest.permission.READ_EXTERNAL_STORAGE)! = PackageManager.PERMISSION_GRANTED) {ActivityCompat.requestPermissions (getThis (), new String [] {Manifest.permission. READ_EXTERNAL_STORAGE}, MY_PERMISSIONS_REQUEST_READ_EXTERNAL);} else {launchImagePick ();}  

Ответ на разрешение

  @Overridepublic void onRequestPermissionsResult (int requestCermissions  , @NonNull String permissions [], @NonNull int [] grantResults) {если (manageReadExternalPermissionResponse (this, requestCode, grantResults)) {launchImagePick ();  }}  

Управление ответом разрешения

  public static boolean manageReadExternalPermissionResponse (final Activity activity, int requestCode, int [] grantResults) {  if (requestCode == MY_PERMISSIONS_REQUEST_READ_EXTERNAL) {//Если запрос отменен, массивы результатов пусты.  if (grantResults.length> 0 && grantResults [0] == PackageManager.PERMISSION_GRANTED) {//Разрешение предоставлено, ура!  Выполните необходимую//задачу, связанную с контактами.  вернуть истину;  } иначе, если (grantResults.length> 0 && grantResults [0] == PackageManager.PERMISSION_DENIED) {boolean showRationale = ActivityCompat.shouldShowRequestPermissionRationale (activity, Manifest.permission.READ_EXTERNAL_STORAGE);  if (! showRationale) {//Пользователь также ПРОВЕРИЛ "никогда больше не спрашивать". //Вы можете либо включить некоторые откаты,//отключить функции вашего приложения//или открыть другой диалог,//снова объясняя разрешение и перенаправляя//на настройки приложения.  } else {//Пользователь НЕ проверял "больше не спрашивать". //Это хорошее место, чтобы объяснить пользователю,//почему вам нужно разрешение, и спросить, хочет ли он/она принять его (обоснование).  }} else {//В доступе отказано, бу!  Отключите функцию//, которая зависит от этого разрешения.  }} return false;}  

Выбор изображения для запуска

  private void launchImagePick () {Intent intent = new Intent (Intent.  ACTION_GET_CONTENT);  intent.setType ("изображение/*");  intent.addCategory (Intent.CATEGORY_OPENABLE);  startActivityForResult (намерение, PICK_IMAGE); //видим onActivityResult}  

Управление ответом на выбор изображения

  @Overridepublic void onActivityResult (int requestCode, int resultCode, Intent data)  {super.onActivityResult (код запроса, код результата, данные);  if (requestCode == PICK_IMAGE) {if (resultCode == Activity.RESULT_OK) {if (data! = null && data.getData ()! = null) {попробуйте {InputStream inputStream = getContentResolver (). openInputStream (data.getData (  )) if (inputStream! = null) {//Для хранения такого файла не требуется специального разрешения FileOutputStream fos = openFileOutput (FILE_TEMP_NAME, Context.MODE_PRIVATE);  final int BUFFER_SIZE = 1  -1) {fos.write (буфер, 0, bytesRead);  } inputStream.close ();  fos.close ();  Файл tempImageFile = новый файл (getFilesDir () + "/" + FILE_TEMP_NAME); //Делайте все, что хотите, с файлом//Удаляем, когда он больше не нужен deleteFile (FILE_TEMP_NAME);  }} catch (Exception e) {e. printStackTrace ();  }} else {//Отображение ошибки}} else {//Пользователь не выбрал изображение}}}  

Вот и все, ребята; это работает у меня на всех телефонах, которые у меня есть.


Постарайтесь избегать использования метода takePersistableUriPermission, потому что он вызвал у меня исключение времени выполнения./** * Выбрать из галереи. */

  public void selectFromGallery () {if (Build.VERSION.SDK_INT  

OnActivity для результата для обработки данных изображения:

@Override protected void onActivityResult (int requestCode, int resultCode, Intent data) {

 //обработка результата намерения галереи перед версией kit-kat if (requestCode == AppConstants.GALLERY_INTENT_CALLED && resultCode == RESULT_OK) {Uri selectedImage = data.getData ();  Строка [] filePathColumn = {MediaStore.Images.Media.DATA};  Курсор cursor = getContentResolver (). Query (selectedImage, filePathColumn, null, null, null);  cursor.moveToFirst ();  int columnIndex = cursor.getColumnIndex (filePathColumn [0]);  String filePath = cursor.getString (columnIndex);  cursor.close ();  photoFile = новый файл (filePath);  mImgCropping.startCropImage (photoFile, AppConstants.REQUEST_IMAGE_CROP);  }//обработка результата намерения галереи после версии kit-kat else if (requestCode == AppConstants.GALLERY_AFTER_KITKAT_INTENT_CALLED && resultCode == RESULT_OK) {Uri selectedImage = data.getData ();  InputStream input = null;  OutputStream output = null;  try {//преобразовываем входной поток в файл, чтобы обрезать//выбранное изображение с SD-карты.  input = getApplicationContext (). getContentResolver (). openInputStream (selectedImage);  попробуйте {photoFile = mImgCropping.createImageFile ();  } catch (IOException e) {e.printStackTrace ();  } catch (исключение e) {e.printStackTrace ();  } output = new FileOutputStream (photoFile);  int read = 0;  byte [] bytes = новый байт [1024];  while ((чтение = input.read (байты))! = -1) {попробуйте {output.write (байты, 0, чтение);  } catch (IOException e) {e.printStackTrace ();  }}} catch (FileNotFoundException e) {e.printStackTrace ();  } catch (IOException e) {e.printStackTrace ();  } catch (исключение e) {e.printStackTrace ();  }}  

3


Если кому интересно, я сделал рабочая версия Kotlin для ACTION_GET_CONTENT :

  var path: String = uri. путь//uri = любое содержимое Urival databaseUri: Urival selection: String? val selectionArgs: Array ? if (path.contains ("/document/image:")) {//файлы, выбранные из "Documents" databaseUri = MediaStore  .Images.Media.EXTERNAL_CONTENT_URI selection = "_id =?"  selectionArgs = arrayOf (DocumentsContract.getDocumentId (uri) .split (":") [1])} else {//файлы, выбранные из всех других источников, особенно на устройствах Samsung databaseUri = uri selection = null selectionArgs = null} попробуйте {val  projection = arrayOf (MediaStore.Images.Media.DATA, MediaStore.Images.Media._ID, MediaStore.Images.Media.ORIENTATION, MediaStore.Images.Media.DATE_TAKEN)//некоторые примеры данных, которые вы можете запросить val cursor = context.contentResolver  .query (databaseUri, projection, selection, selectionArgs, null) if (cursor.moveToFirst ()) {//делаем с данными все, что угодно} cursor.close ()} catch (e: Exception) {Log.e (TAG  , e.message, e)}  

1


Это полный взлом, но вот что я сделал …

Итак, играя с настройкой DocumentsProvider, я заметил, что пример кода (в getDocIdForFile , около строки 450) генерирует уникальный Идентификатор выбранного документа на основе (уникального) пути файла относительно указанного корня, который вы ему даете (то есть того, что вы установили для mBaseDir в строке 96).

Таким образом, URI выглядит примерно так:

content://com.example.provider/document/root: path/to/the/file

Как говорится в документации, предполагается только один корень (в моем случае это Environment.getExternalStorageDirectory () , но вы можете использовать где-нибудь еще … затем он берет путь к файлу, начиная с корня, и делает его уникальным идентификатором, добавляя перед ним « root: «. Поэтому я могу определить путь, исключив часть "/document/root: » из uri.getPath (), создав фактический путь к файлу, выполнив примерно следующее:

  public void onActivityResult (int requestCode, int resultCode, Intent data) {//проверяем коды результатов и тому подобное, тогда ... uri = data.getData (); if (uri.getAuthority (). equals  ("com.example.provider")) {String path = Environment.getExternalStorageDirectory (0.toString () .concat ("/") .concat (uri.getPath (). substring ("/document/root:". length  ())));  doSomethingWithThePath (путь);  } else {//другой провайдер (возможно, облачный сервис, такой как GDrive)//создал этот uri.  Так что справляйся с этим или нет.  Вы можете разрешить определенные//провайдеры локальной файловой системы, фильтровать результаты, не относящиеся к файловой системе, и т. Д.}  

Я знаю. Обидно, но сработало. Опять же, это зависит от использования вашего собственного поставщика документов в вашем приложении для генерации идентификатора документа.

(Кроме того, есть лучший способ построить путь, который не предполагайте, что «/» — это разделитель путей и т. д.. Но вы поняли идею.)

1


У меня это сработало:

  else if (requestCode == GALLERY_ACTIVITY_NEW && resultCode == Activity.RESULT_OK) {Uri uri = data.getData ();  Log.i (TAG, "old uri =" + uri);  dumpImageMetaData (uri);  попробуйте {ParcelFileDescriptor parcelFileDescriptor = getContentResolver (). openFileDescriptor (uri, "r");  FileDescriptor fileDescriptor = parcelFileDescriptor.getFileDescriptor ();  Log.i (TAG, "Файловый дескриптор" + fileDescriptor.toString ());  окончательные параметры BitmapFactory.Options = new BitmapFactory.Options ();  options.inJustDecodeBounds = true;  BitmapFactory.decodeFileDescriptor (fileDescriptor, null, параметры);  options.inSampleSize = BitmapHelper.calculateInSampleSize (параметры, User.PICTURE_MAX_WIDTH_IN_PIXELS, User.PICTURE_MAX_HEIGHT_IN_PIXELS);  options.inJustDecodeBounds = false;  Bitmap bitmap = BitmapFactory.decodeFileDescriptor (fileDescriptor, null, options);  imageViewPic.setImageBitmap (растровое изображение);  ByteArrayOutputStream stream = новый ByteArrayOutputStream ();  bitmap.compress (Bitmap.CompressFormat.JPEG, 100, поток); //получаем здесь байтовый массив byte [] picData = stream.toByteArray ();  ParseFile picFile = новый ParseFile (picData);  user.setProfilePicture (picFile);  } catch (FileNotFoundException exc) {Log.i (TAG, "Файл не найден:" + exc.toString ());  }}  

1


Опираясь на ответ Пола Берка, я столкнулся со многими проблемами при разрешении пути URI внешней SD-карты, поскольку большинство предлагаемых «встроенных» функций возвращают пути, которые не разрешаются в файлы.

Однако , это мой подход его//TODO обрабатывать неосновные тома .

  String resolvedPath = ""; File [] possibleExtSdComposites =  context.getExternalFilesDirs (null); for (File f: possibleExtSdComposites) {//Сбрасываем окончательный путь resolvedPath = ""; //Составить список папок ArrayList  extSdSplit = new ArrayList  (Arrays.asList (f.getPath (). Split ("/"))); //Ищем папку «» int idx = extSdSplit.indexOf (BuildConfig.APPLICATION_ID); //ПРЕДПОЛОЖЕНИЕ: ожидается, что его можно будет найти на глубине 2 (в данном случае корень ExtSdCard -/storage/0000-0000/) - например, /storage/0000-0000/Android/data//files ArrayList ierarchyList = new ArrayList  (extSdSplit.subList (0, idx - 2)); //Построить список, содержащий полный возможный путь к файлу иерархияList.add (tail);  String possibleFilePath = TextUtils.join ("/" ,ierarchyList); //Если файл найден -> успешно if (idx! = -1 && new File (possibleFilePath) .exists ()) {resolvedPath = possibleFilePath;  перерыв;  }} если (! resolvedPath. equals ("")) {return resolvedPath;} else {return null;}  

Обратите внимание, что это зависит от иерархии, которая может быть разной для каждого производителя телефона — я не тестировал их все (до сих пор он работал хорошо на Xperia Z3 API 23 и Samsung Galaxy A3 API 23).

Пожалуйста, подтвердите, не работает ли он хорошо где-либо еще.


Ответ @paul Burke отлично работает как для изображений камеры, так и для изображений галереи для уровня API 19 и выше, но это не работает, если минимальный SDK вашего проекта Android установлен ниже 19, а некоторые ответы, упомянутые выше, не работают как для галереи, так и для камеры. Что ж, я модифицировал код @paul Burke, который работает для уровня API ниже 19. Ниже приведен код.

  public static String getPath (final Context context, final Uri uri) {  последнее логическое значение isKitKat = Build.VERSION.SDK_INT> = Build.VERSION_CODES.KITKAT;  Log.i ("URI", uri + "");  Строка result = uri + ""; //DocumentProvider//if (isKitKat && DocumentsContract.isDocumentUri (context, uri)) {if (isKitKat && (result.contains ("media.documents"))) {String [] ary = result.split ("/")  ;  int length = ary.length;  Строка imgary = ary [длина-1];  последняя строка [] dat = imgary.split ("% 3A");  последняя строка docId = dat [1];  final String type = dat [0];  Uri contentUri = null;  if ("изображение" .equals (тип)) {contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;  } else if ("video" .equals (type)) {} else if ("audio" .equals (type)) {} final String selection = "_id =?";  окончательная строка [] selectionArgs = новая строка [] {dat [1]};  return getDataColumn (context, contentUri, selection, selectionArgs);  } else if ("содержимое" .equalsIgnoreCase (uri.getScheme ())) {return getDataColumn (context, uri, null, null);  }//Файл иначе if ("файл" .equalsIgnoreCase (uri.getScheme ())) {return uri.getPath ();  } return null;} общедоступная статическая строка getDataColumn (контекст контекста, Uri uri, выбор строки, String [] selectionArgs) {Cursor cursor = null;  последний столбец строки = "_data";  последняя строка [] проекция = {столбец};  попробуйте {курсор = context.getContentResolver (). query (uri, projection, selection, selectionArgs, null);  если (курсор! = null && cursor.moveToFirst ()) {final int column_index = cursor.getColumnIndexOrThrow (столбец);  возвратите cursor.getString (column_index);  }} наконец {если (курсор! = ноль) cursor.close ();  } return null;}  

2



Заводские изображения для Nexus и пиксельные устройства

Google стремится продвигать расовое равенство для сообществ чернокожих. Смотри как.

Эта страница содержит файлы двоичных изображений, которые позволяют восстановить исходную фабрику вашего устройства Nexusor Pixel. прошивка. Вы найдете эти файлы полезными, если вы установили на свое устройство пользовательские сборки и хотите вернуть устройство в его заводское состояние.

Обратите внимание, что обычно проще и безопаснее загружать неопубликованный образ OTA.

Если вы используете заводской образ, убедитесь, что вы повторно заблокировали загрузчик после завершения процесса.

Эти файлы предназначены только для использования на вашем личном Nexus или Устройства Pixel нельзя разбирать, декомпилировать, реконструировать, модифицировать или распространять вами или использовать каким-либо образом, кроме случаев, специально оговоренных в условиях лицензии, прилагаемой к вашему устройству.

Положения и условия

Хотя есть возможность восстановить определенные данные из резервной копии в вашем аккаунте GoogleAccount, приложения и связанные с ними данные будут удалены. Прежде чем продолжить, убедитесь, что данные, которые вы хотите сохранить, сохранены в вашей учетной записи Google.

Загрузка образа системы и использование программного обеспечения устройства регулируются Условиями использования Google. Продолжая, вы соглашаетесь с Условиями использования и Политикой конфиденциальности Google. Загрузка вами образа системы и использование программного обеспечения устройства также может регулироваться некоторыми сторонними условиями обслуживания, которые можно найти в разделе Настройки> О телефоне> Юридическая информация или иным образом.

Подтверждение Я прочитал и согласен с приведенными выше условиями.

[{«type»: «thumb-down» , «id»: «missingTheInformationINeed», «label»: «Отсутствует нужная мне информация»}, {«type»: «thumb-down», «id»: «tooComplicatedTooManySteps», «label»: «Слишком сложно/слишком много шагов «}, {» type «:» thumb-down «,» id «:» outOfDate «,» label «:» Out of date «}, {» type «:» thumb-down «,» id «: «samplesCodeIssue», «label»: «Образцы/код проблемы»}, {«type»: «thumb-down», «id»: «otherDown», «label»: «Other»}] [{«type»: «thumb-up», «id»: «easyToUnderstand», «label»: «Легко понять»}, {«type»: «thumb-up», «id»: » resolMyProblem «,» label «:» Моя проблема решена «}, {» type «:» thumb-up «,» id «:» otherUp «,» label «:» Другое «}]

Оцените статью
clickpad.ru
Добавить комментарий