Android 12 llegará pronto, pero este agosto, a partir de la versión 11, los desarrolladores tendrán que utilizar nuevos estándares para acceder a las aplicaciones a archivos externos. Si antes solo podía poner una bandera de que su aplicación no admite innovaciones, pronto serán obligatorias para todos. El objetivo principal es mejorar la seguridad.
API — , . .
, — .
Android Internal Storage (IS) External Storage (ES). SD-, ES , . — IS, ES , , . ES , , , .
IS, . . ES , , (, ).
. IS, ES — , .
Android 10- , 11- .
Google Scoped Storage (SS) ES. , — . IS 10- .
Android 11 Google SS — - SDK 30- API, SS, , . Android , . , 11, , , . , Android 11, .
SS ( 10- ), . Media Content, Storage Access Framework , 11- Android, Datasets . , . , . — . , , . , , .
Media Content, SAF Datasets Shared Storage (ShS). . , .
SS — FileProvider . , .
— , . best practice ( , ), - , . , .
. , , , , .
— , , . — , .
.
iFunny . , .
, API.
interface FilesManipulator {
fun createVideoFile(fileName: String, copy: Copier): Uri
fun createImageFile(fileName: String, copy: Copier): Uri
fun createFile(fileName: String, copy: Copier): Uri
fun getPath(uri: Uri): String
fun deleteFile(uri: Uri)
}
FilesManipulator , , API . Copier — , , . , , , . 10- Android FilesManipulator File API, 10- ( ) — MediaStore API.
.
fun getContentValuesForImageCreating(fileName: String): ContentValues {
return ContentValues().apply {
put(MediaStore.Images.Media.DISPLAY_NAME, fileName)
put(MediaStore.Images.Media.IS_PENDING, FILE_WRITING_IN_PENDING)
put(MediaStore.Images.Media.RELATIVE_PATH, Environment.DIRECTORY_PICTURES + File.separator + appFolderName)
}
}
fun createImageFile(fileName: String, copy: Copier): Uri {
val contentUri = MediaStore.Images.Media.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY)
val contentValues = getContentValuesForImageCreating(fileName)
val uri = contentResolver.insert(contentUri, contentValues)
?: throw IllegalStateException("New image file insert error")
downloadContent(uri, copy)
return uri
}
fun downloadContent(uri: Uri, copy: Copier) {
try {
contentResolver.openFileDescriptor(uri, FILE_WRITE_MODE)
.use { pfd ->
if (pfd == null) {
throw IllegalStateException("Got nullable file descriptor")
}
copy.copyTo(FileOutputStream(pfd.fileDescriptor))
}
contentResolver.update(uri, getWriteDoneContentValues(), null, null)
} catch (e: Throwable) {
deleteFile(uri)
throw e
}
}
fun getWriteDoneContentValues(): ContentValues {
return ContentValues().apply {
put(MediaStore.Images.Media.IS_PENDING, FILE_WRITING_DONE)
}
}
, MediaStore.Images.Media.IS_PENDING
, 0 , .
, . URI . SDK, File API . External Storage FileProvider API.
, 30- API, . iFunny , . , query, , SDK.
, , . , .
<manifest … >
<queries>
<intent>
<action android:name="android.intent.action.SENDTO" />
<data android:scheme="smsto, mailto" />
</intent>
…
<package android:name="com.twitter.android" />
<package android:name="com.snapchat.android" />
<package android:name="com.whatsapp" />
<package android:name="com.facebook.katana" />
<package android:name="com.instagram.android" />
<package android:name="com.facebook.orca" />
<package android:name="com.discord" />
<package android:name="com.linkedin.android" />
</queries>
</manifest>
UI- API 29-30 , .
LogCat , Orchestrator java.lang.RuntimeException: Cannot connect to androidx.test.orchestrator.OrchestratorService.
, <package android:name="androidx.test.orchestrator" />
.
, — Allure , .
- Scoped Storage , , . , , , .
, . ShellCommandExecutor, adb shell appops set --uid PACKAGE_NAME MANAGE_EXTERNAL_STORAGE allow
.
Android 11 .
10- Android , Allure . issue Allure, , , 11- . adb shell appops set --uid PACKAGE_NAME LEGACY_STORAGE allow
. , .
— . , WRITE_EXTERNAL_STORAGE
28 API, . (, debug) Allure .
debug-.
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
<uses-permission
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="29"
tools:node="replace" />
, targetSdkVersion 30, Google Play , .