Сообщение направляет вас посетить два веб-сайта:
Это выглядит как внутренний пример (возможно, позже будет показан в демонстрациях API), но довольно сложно понять, что происходит.
Это официальная документация по новому API, но в нем недостаточно информации о том, как ее использовать.
Вот что он вам говорит:
Если вам действительно нужен полный доступ ко всему поддереву документов, начните с запуска ACTION_OPEN_DOCUMENT_TREE, чтобы пользователь мог выбрать каталог. Затем передайте полученный getData() в fromTreeUri (Context, Uri), чтобы начать работу с выбранным пользователем деревом.
При навигации по дереву экземпляров DocumentFile вы всегда можете использовать getUri(), чтобы получить Uri, представляющий базовый документ для этот объект для использования с openInputStream (Uri) и т.д.
Чтобы упростить код на устройствах, работающих под управлением KITKAT или ранее, вы можете используйте fromFile (Файл), который эмулирует поведение DocumentProvider.
У меня есть несколько вопросов о новом API:
2 ответов
Множество хороших вопросов, дайте понять.:)
Здесь отличный учебник для взаимодействия с платформой доступа к хранилищу в KitKat:
Взаимодействие с новыми API в Lollipop очень похоже. Чтобы предложить пользователю выбрать дерево каталогов, вы можете запустить такое намерение следующим образом:
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE); startActivityForResult(intent, 42);
Затем в вашем onActivityResult() вы можете передать выбранный пользователем Uri в новый вспомогательный класс DocumentFile. Вот краткий пример, в котором перечислены файлы в выбранном каталоге, а затем создается новый файл:
Public void onActivityResult(int requestCode, int resultCode, Intent resultData) { if (resultCode == RESULT_OK) { Uri treeUri = resultData.getData(); DocumentFile pickedDir = DocumentFile.fromTreeUri(this, treeUri); // List all existing files inside picked directory for (DocumentFile file: pickedDir.listFiles()) { Log.d(TAG, "Found file " + file.getName() + " with size " + file.length()); } // Create a new file and write into it DocumentFile newFile = pickedDir.createFile("text/plain", "My Novel"); OutputStream out = getContentResolver().openOutputStream(newFile.getUri()); out.write("A long time ago...".getBytes()); out.close(); } }
Если вы хотите получить доступ к этому Uri из собственного кода, вы можете вызвать ContentResolver.openFileDescriptor() , а затем использовать ParcelFileDescriptor.getFd() или detachFd() для получения традиционного целочисленного дескриптора файла POSIX.
По умолчанию Uris, возвращаемый с помощью возможностей Storage Access Framework, не сохраняется при перезагрузках. Платформа "предлагает" возможность сохранять разрешение, но вам все равно нужно "взять" разрешение, если вы этого хотите. В нашем примере выше вы бы назвали:
GetContentResolver().takePersistableUriPermission(treeUri, Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
Вы всегда можете выяснить, какие постоянные гранты для вашего приложения доступны через API ContentResolver.getPersistedUriPermissions() . Если вам больше не нужен доступ к сохраненному Uri, вы можете освободить его с помощью ContentResolver.releasePersistableUriPermission() .
Нет, мы не можем ретроактивно добавлять новые функции к более старым версиям платформы.
В настоящее время нет пользовательского интерфейса, который показывает это, но вы можете найти информацию в разделе "Предоставленные разрешения Uri" вывода adb shell dumpsys activity providers .
Разрешения на разрешение Uri изолированы для каждого пользователя, как и все другие функции многопользовательской платформы. То есть, одно и то же приложение, работающее под двумя разными пользователями, не имеет наложенных или общих разрешений Uri.
Подпрограмма DocumentProvider может аннулировать разрешение в любое время, например, когда удаляется облачный документ. Наиболее распространенный способ обнаружения этих отозванных разрешений - это когда они исчезают из ContentResolver.getPersistedUriPermissions() , упомянутых выше.
Разрешения также отменяется всякий раз, когда данные приложения очищаются для любого приложения, участвующего в гранте.
Yep, намерение ACTION_OPEN_DOCUMENT_TREE дает вам рекурсивный доступ к существующим и вновь созданным файлам и каталогам.
Да, с KitKat поддерживался множественный выбор, и вы можете разрешить его, установив EXTRA_ALLOW_MULTIPLE при запуске вашего намерения ACTION_OPEN_DOCUMENT . Вы можете использовать Intent.setType() или EXTRA_MIME_TYPES , чтобы сузить типы файлов, которые можно выбрать:
Да, основное устройство хранения данных должно появиться в сборщике, даже на эмуляторе. Если ваше приложение использует платформу доступа к хранилищу для доступа к разделяемому хранилищу, вам больше не нужны разрешения READ/WRITE_EXTERNAL_STORAGE и вы можете удалить их или использовать функцию android:maxSdkVersion , чтобы запрашивать их только на более ранних версиях платформы.
Когда задействуются физические носители, UUID (такой как серийный номер FAT) основного носителя всегда записывается в возвращаемый Uri. Система использует это для подключения к мультимедиа, который был выбран пользователем, даже если пользователь меняет носитель между несколькими слотами.
Если пользователь поменяется на второй карте, вам необходимо запросить доступ к новой карте. Поскольку система запоминает гранты на основе UUID, вы будете продолжать иметь ранее предоставленный доступ к исходной карте, если пользователь повторно вставляет ее позже.
В моем проекте Android в Github, приведенном ниже, вы можете найти рабочий код, который позволяет писать на extSdCard на Android 5. Предполагается, что пользователь дает доступ ко всей SD-карте, а затем позволяет писать всюду на этой карте. (Если вы хотите иметь доступ только к одиночным файлам, все становится проще.)
Запуск платформы доступа к хранилищу:
@TargetApi(Build.VERSION_CODES.LOLLIPOP) private void triggerStorageAccessFramework() { Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE); startActivityForResult(intent, REQUEST_CODE_STORAGE_ACCESS); }
Обработка ответа из платформы доступа к хранилищу:
@TargetApi(Build.VERSION_CODES.LOLLIPOP) @Override public final void onActivityResult(final int requestCode, final int resultCode, final Intent resultData) { if (requestCode == SettingsFragment.REQUEST_CODE_STORAGE_ACCESS) { Uri treeUri = null; if (resultCode == Activity.RESULT_OK) { // Get Uri from Storage Access Framework. treeUri = resultData.getData(); // Persist URI in shared preference so that you can use it later. // Use your own framework here instead of PreferenceUtil. PreferenceUtil.setSharedPreferenceUri(R.string.key_internal_uri_extsdcard, treeUri); // Persist access permissions. final int takeFlags = resultData.getFlags() & (Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION); getActivity().getContentResolver().takePersistableUriPermission(treeUri, takeFlags); } } }
Получение выходного потока для файла через платформу доступа к хранилищу (с использованием сохраненного URL-адреса, предполагая, что это URL-адрес корневой папки внешней SD-карты)
DocumentFile targetDocument = getDocumentFile(file, false); OutputStream outStream = Application.getAppContext(). getContentResolver().openOutputStream(targetDocument.getUri());
Используются следующие вспомогательные методы:
Public static DocumentFile getDocumentFile(final File file, final boolean isDirectory) {
String baseFolder = getExtSdCardFolder(file);
if (baseFolder == null) {
return null;
}
String relativePath = null;
try {
String fullPath = file.getCanonicalPath();
relativePath = fullPath.substring(baseFolder.length() + 1);
}
catch (IOException e) {
return null;
}
Uri treeUri = PreferenceUtil.getSharedPreferenceUri(R.string.key_internal_uri_extsdcard);
if (treeUri == null) {
return null;
}
// start with root of SD card and then parse through document tree.
DocumentFile document = DocumentFile.fromTreeUri(Application.getAppContext(), treeUri);
String parts = relativePath.split("\\/");
for (int i = 0; i < parts.length; i++) {
DocumentFile nextDocument = document.findFile(parts[i]);
if (nextDocument == null) {
if ((i < parts.length - 1) || isDirectory) {
nextDocument = document.createDirectory(parts[i]);
}
else {
nextDocument = document.createFile("image", parts[i]);
}
}
document = nextDocument;
}
return document;
}
public static String getExtSdCardFolder(final File file) {
String extSdPaths = getExtSdCardPaths();
try {
for (int i = 0; i < extSdPaths.length; i++) {
if (file.getCanonicalPath().startsWith(extSdPaths[i])) {
return extSdPaths[i];
}
}
}
catch (IOException e) {
return null;
}
return null;
}
/**
* Get a list of external SD card paths. (Kitkat or higher.)
*
* @return A list of external SD card paths.
*/
@TargetApi(Build.VERSION_CODES.KITKAT)
private static String getExtSdCardPaths() {
List
С обновлением до пользователи получили ряд ограничений на использование SD карт. Решить эту проблему до недавних пор можно было при помощи кастомной прошивки или отката к более ранней версии ОС. Теперь же былые возможности можно вернуть и на Android 4.4 KitKat . О том, как это сделать, читайте далее.
Прежде всего, вам потребуется получить root-права для своего Android устройства. Второе важное условие – это приложение SDFix . Скачать приложение на Андроид бесплатно можно прямиком с . Для этого воспользуйтесь ссылкой, указанной далее.
Установив SDFix на свой Android , вы в несколько тапов восстановите былые возможности. Принцип работы приложения заключается в том, что SDFix добавляет Android UNIX группу "media_rw" к WRITE_EXTERNAL_STORAGE , таким образом, используемый в Android 4.4 KitKat XML файл меняется на platform.xml . При этом создается резервная копия изначальной настройки platform.xml.original-pre-sdfix , которую можно использовать для восстановления ограничений. Чтобы вернуть систему к стоковому состоянию, необходимо воспользоваться проводником с поддержкой root-доступа и заменить platform.xml на platform.xml.original-pre-sdfix.
Отдельно необходимо отметить, что приложения, которые используют SD, получат более низкий уровень безопасности. Также важным является тот факт, что SDFix приводит к перманентным изменениям, а это означает, что после восстановления старых полномочий приложение можно удалить.
Давайте поговорим о том, как снять защиту записи с SD-карты памяти на Android. Многие люди сталкиваются с этой проблемой, когда пытаются скопировать или переместить файлы на SD карту. В этой статье вы найдете несколько способов как снять защиту записи Android.
Если при попытке скопировать файлы или форматировать SD-карту вы получаете ошибку, как на изображении выше, тогда знайте, это не ваша проблема. Более того, карта памяти не повреждена и не заражена вирусом, просто на ней установлена защита от записи. Без лишней суматохи мы рассмотрим несколько методов как снять защиту записи Android.
Большинство проблем и ошибок легко решаются с помощью реестра. Также мы можем использовать его, чтобы снять защиту записи Android.
Этот способ помогает в большинстве случае, но, если по какой-либо причине вы не можете его использовать, тогда попробуйте следующие методы.
Если и этот способ не помог снять защиту записи Android, не отчаивайтесь, у нас есть еще парочка решений этой проблемы.
Этот способ позволит снять защиту записи Android.
Кроме того, вы можете внимательно посмотреть на свою SD-карту, чтобы найти кнопку блокировки. Если она есть, просто переключите ее. Кнопка блокировки используется для защиты данных от удаления или полного форматирования карты памяти.
Надеемся, вам удалось снять защиту записи Android. Если у вас остались вопросы, пожалуйста, напишите в комментариях ниже.
Разрабатывая приложение для проведения соревнований, я столкнулся с проблемой хранения базы данных. Проблема состояла в том, как мне определить внешнюю карту памяти. В целом поиск в сети точного ответа не дал. Поэтому, объединив все найденные результаты, я собрал свой класс. Если кому интересно, смотрим под катом.
Итак, начнем с теории.
До версии KitKat 4.4 API не предоставляло функционала для получения путей к внешней памяти. Начиная с этой версии (API 19) появилась функция public abstract File getExternalFilesDirs (String type), которая возвращает массив строк с путями к внутренней и внешней памяти. Но как же быть с нашей SD Card, которая вставлена в слот? Путь к ней мы опять не можем получить.
В конечном итоге я решил объединить все полученные знания и написал свой класс, который может нам вернуть пути к внешним и удаляемым устройствам.
Public enum MountDeviceType {
EXTERNAL_SD_CARD, REMOVABLE_SD_CARD
}
И был создан класс StorageHelper
, который и осуществляет поиск доступных карт памяти.
В классе StorageHelper реализовано два способа поиска - через системное окружение (Environment ) и с использованием утилиты Linux mount , а точнее результата ее выполнения.
Внешняя память всегда одна и обычно всегда есть, поэтому проверяем ее на читаемость, вычисляем хэш и запоминаем. Удаляемой памяти может быть много, поэтому необходимо полученную строку разбить по разделителю и проверять каждое значение.
Функция fillDevicesEnvirement
String path = android.os.Environment.getExternalStorageDirectory()
.getAbsolutePath();
if (!path.trim().isEmpty()
&& android.os.Environment.getExternalStorageState().equals(android.os.Environment.MEDIA_MOUNTED)) {
testAndAdd(path, MountDeviceType.EXTERNAL_SD_CARD);
}
// Получаем ремувабл
String rawSecondaryStoragesStr = System.getenv("SECONDARY_STORAGE");
if (rawSecondaryStoragesStr != null
&& !rawSecondaryStoragesStr.isEmpty()) {
// All Secondary SD-CARDs splited into array
final String rawSecondaryStorages = rawSecondaryStoragesStr
.split(File.pathSeparator);
for (String rawSecondaryStorage: rawSecondaryStorages) {
testAndAdd(rawSecondaryStorage,
MountDeviceType.REMOVABLE_SD_CARD);
}
}
Вернемся к утилите mount. При запуске без параметров команда возвращает список смонтированных файловых систем. Удаляемые устройства имеют обычно формат файловой системы FAT, то будем выделять строки, в которых есть характеристика "fat ". Внешняя память будет характеризоваться параметром "fuse ".
Примечание: при использовании такого способа не всегда корректно (скорее всего я что-то не учел) определяются типы смотнтированных устройств. Разницу замечал на разных версиях Android. Поэтому этот способ можно использовать как дополнительный.
Функция fillDevicesProcess
try {
Runtime runtime = Runtime.getRuntime();
proc = runtime.exec("mount");
try {
is = proc.getInputStream();
isr = new InputStreamReader(is);
br = new BufferedReader(isr);
while ((line = br.readLine()) != null) {
if (line.contains("secure"))
continue;
if (line.contains("asec"))
continue;
if (line.contains("fat")) {// TF card
String columns = line.split(" ");
if (columns != null && columns.length > 1) {
testAndAdd(columns,
MountDeviceType.REMOVABLE_SD_CARD);
}
} else if (line.contains("fuse")) {// internal(External)
// storage
String columns = line.split(" ");
if (columns != null && columns.length > 1) {
// mount = mount.concat(columns + "\n");
testAndAdd(columns,
MountDeviceType.EXTERNAL_SD_CARD);
}
}
}
} finally {
...
}
} catch (Exception e) {
...
}
Исходный код всего класса расположен еще нигде не расположен. На днях постараюсь разместить на gitHub.
Кто еще какими способами пользуется?
Теги:
Свершилось! На планшет, а именно, на Asus MeMO Pad 7, который я на замену своему честному трудяге, прилетело обновление до Android 5.0.1. Теперь у меня есть устройство с Леденцом, или, как пишут в интернете, с Лолипопой.
Обнова прилетела несколько неожиданно. Если честно, я ждал в апреле новую прошивку на телефон (Asus ZenFone 5) - ее, по крайней мере, обещали. Про планшет же никто ничего не говорил и тут - на тебе, получите и распишитесь.
Это, на самом деле, обескуражило меня (в хорошем, конечно, смысле), еще и потому, что планшет я приобрел в декабре, и на нем стояла версия 4.3. То есть, это второй существенный апдейт операционной системы (на самом деле, обнов было больше, но номер версии Android не менялся). Предыдущий производитель моих устройств - Samsung - не мог похвастаться такой скорострельностью, хотя, тоже дважды обновлял операционку: с Android 3.2 до Android 4.1.2 (через 4.0.1). Только вот ждать этих новых версий приходилось значительно дольше.
Что ж, да здравствуют сюрпризы! Получив уведомление о выходе новой версии прошивки, я тут же скачал ее, а это больше 700 метров, и запустил обновление. Так как качал я не через домашнюю сеть , а через мобильную, то времени на все про все потребовалось чуть больше: от начала загрузки до завершения обновления прошло около часа. Само обновление длилось где-то минут двадцать, но, потенциально, может занимать и больше времени - потому, что после установки операционки следует процесс обновления установленных программ. У меня счетчик досчитал до 205. Но вот, процесс завершился и настало время посмотреть, что же изменилось.
Первое, что бросилось в глаза - более утонченная графика при разблокировке устройства. Второе - рука в области уведомлений. Пару дней я ее терпел, надеясь на то, что случайно где-нибудь увижу что-нибудь, связанное с ней. Не увидел. Тогда стал искать целенаправленно и нашел разъяснение по ней на 4pda . Оказывается, оповещения поделили на важные и не очень, и есть возможность указать, какие оповещения вы хотите получать. Если выставить опцию Оповещать всегда , то рука пропадает, если опцию Только важные оповещения , то рука появляется. Если честно, руку я убирал несколько раз, но, почему-то, она стабильно возвращается. Почему? Пока не знаю.
Вот, собственно, сама процедура по "убиранию" этой руки:
Метод, приведенный выше, совершенно не сложен. Но можно добиться результата еще быстрее и проще: нажать на какую-то из двух кнопок управления громкостью - на экране появится окошко, позволяющее изменять уровни громкости для различных компонентов системы, а также, внимание, управлять Режимами оповещения . Правда, почему-то, эти самые Режимы оповещения выводятся в таком быстром варианте не всегда. Закономерности я пока не нашел, но, если честно, не больно-то и искал.
Официальных обновлений Андроид (для нашего региона) я не нашел на официальных сайтах.
На форумах я нашел информацию, что Google выпустила версию прошивки для России в январе 2015. Но очень медленно внедряется на аппараты клиентов.
Таким образом, я могу подождать, пока на мой аппарат не поступит сообщение о том, что есть обновление По. После этого я смогу обновить Андроид.