Android - R / W на съемную SD-карту - PullRequest
0 голосов
/ 03 марта 2019

Я создаю приложение, которое должно перемещать файлы из внутреннего хранилища (я имею в виду / storage / emulated / 0 / *) на внешнее съемное хранилище (карта micro SD).Я нахожу путь к корню этой SD-карты, но не могу создать папку или переместить туда файл.Я понятия не имею, что нужно сделать, чтобы это сработало.Я нашел что-то под названием Storage Access Framework, но я не понял, как его использовать.Я буду признателен за любую помощь, чтобы получить разрешение на чтение / запись для определенного корневого пути SD-карты.

Заранее спасибо.

1 Ответ

0 голосов
/ 04 марта 2019

Начиная с api 19 (KitKat) практически невозможно напрямую записать содержимое SDcard как простой File, поскольку они смонтированы как ТОЛЬКО ЧТЕНИЕ, по крайней мере, для пользователя по умолчанию (система все еще может записывать в него).

DocumentsProvider API довольно запутанный (даже образец в документах трудно читать), поэтому DocumentFile был добавлен для имитации поведения File для облегчения доступа.

Предполагая, что пользователь уже предоставил разрешения на чтение / запись для хранения, нам нужно получить URI документа корневого каталога SDcard (в Activity):

var sdCardUri : Uri? = null

private fun requestSDCardPermissions(){
    if(Build.VERSION.SDK_INT < 24){
        startActivityForResult(Intent(Intent.ACTION_OPEN_DOCUMENT_TREE), REQ_PICK_DIRECTORY)
        return
    }
    // find removable device using getStorageVolumes
    val sm = getSystemService(Context.STORAGE_SERVICE) as StorageManager
    val sdCard = sm.storageVolumes.find { it.isRemovable }
    if(sdCard != null){
        startActivityForResult(sdCard.createAccessIntent(null), REQ_SD_CARD_ACCESS)
    }
}

Прежде чем пользователю API 24 потребуется открыть средство выбора документов ивручную выберите SD-карту.Это не идеально, но команда Android упустила из виду тот факт, что отсутствует API SD-карты.

В более новой версии getStorageVolumes() позволяет нам находить SD-карту с помощью кода и отображать только явное предупреждение дляПользователь, который будет доступен.Это НЕ тот же диалог, что и разрешение на чтение / запись.

Теперь для обработки полученных Uri нам нужно только взять data.data результата.Это может быть хорошим местом для сохранения его в общих настройках, чтобы не просить пользователя снова и снова:

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
    if(requestCode == REQ_SD_CARD_ACCESS || requestCode == REQ_PICK_DIRECTORY){
        if(resultCode == RESULT_OK) {
            if(data == null){
                Log.e(TAG, "Error obtaining access")
            }else{
                sdCardUri = data.data
                Log.d("StorageAccess", "obtained access to $sdCardUri")
                // optionally store uri in preferences as well here { ... }
            }
        }else
            Toast.makeText(this, "access denied", Toast.LENGTH_SHORT).show()
        return
    }
    super.onActivityResult(requestCode, resultCode, data)
}

Я пропущу большинство проверок ошибок / проверок существующих файлов, но теперь вы можете использовать полученный sdCardUri вот так (копирование файла "sample.txt" из корня внутреннего хранилища в корень SDcard):

private fun copyToSDCard(){
    val sdCardRoot = DocumentFile.fromTreeUri(this, sdCardUri)
    val internalFile = File(Environment.getExternalStorageDirectory(), "sample.txt")
    // get or create file
    val sdCardFile = sdCardRoot.findFile("sample.txt") ?: sdCardRoot.createFile(null, "sample.txt")
    val outStream = contentResolver.openOutputStream(sdCardFile.uri)
    outStream.write(internalFile.readBytes())
    outStream.flush()
    outStream.close()
    Toast.makeText(this, "copied to SDCard", Toast.LENGTH_SHORT).show()
}

И наоборот (из SDcard во внутреннее хранилище):

private fun copyToInternal(){
    val sdCardRoot = DocumentFile.fromTreeUri(this, sdCardUri)
    val internalFile = File(Environment.getExternalStorageDirectory(), "sample.txt")
    val sdCardFile = sdCardRoot.findFile("sample.txt")
    val inStream = contentResolver.openInputStream(sdCardFile.uri)
    internalFile.writeBytes(inStream.readBytes())
    inStream.close()
    Toast.makeText(this, "copied to internal", Toast.LENGTH_SHORT).show()
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...