Controlling the Behavior
Fortunately, for Android 10 at least, your app has control over whether it has traditional (“legacy”) external storage access or has “filtered” access.
Opting Out… For Now
To stick with legacy external storage, even on Android 10 devices, add android:requestLegacyExternalStorage="true"
to your <application>
element in your manifest.
With that in place, everything will work as it did in Android 4.4 through 9.0.
Opting In
Conversely, android:requestLegacyExternalStorage="false"
opts into the “filtered” behavior. This works regardless of targetSdkVersion
, so even if your targetSdkVersion
is 28 or older, you can see how your app behaves when it is using scoped storage.
If you want, you can have android:requestLegacyExternalStorage
be controlled by a bool
resource value. The StorageExplorer
sample module in the book’s sample project does this:
<application
android:name=".KoinApp"
android:allowBackup="false"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme"
tools:ignore="GoogleAppIndexingWarning"
android:requestLegacyExternalStorage="@bool/useLegacy">
The module has three flavor dimensions. One is called legacy
, and it has two flavors: legacy
and normal
. Those drive the configuration of the useLegacy
resource, via resValue
:
normal {
dimension "legacy"
applicationIdSuffix ".normal"
resValue "bool", "useLegacy", "false"
}
legacy {
dimension "legacy"
applicationIdSuffix ".legacy"
resValue "bool", "useLegacy", "true"
}
The result: a legacy
build opts into the legacy external storage behavior, while a normal
opts into “the new normal” filtered external storage.
Default Conditions
If your targetSdkVersion
is 28 or lower, you will have legacy external storage behavior by default, as if you have opted out via android:requestLegacyExternalStorage="true"
.
However, once you set your targetSdkVersion
to 29
, you will have filtered external storage by default.
It is best if you add android:requestLegacyExternalStorage
yourself and declare, positively, what scoped storage behavior you want to have for when your app runs on Android 10 devices.
To check whether you have scoped storage or not, you can call isExternalStorageLegacy()
on Environment
:
val msg =
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && Environment.isExternalStorageLegacy())
"This app has legacy external storage"
else "This app has Q-normal external storage"
Note, though, that this will only return a valid value if you have a <uses-permission>
element for READ_EXTERNAL_STORAGE
or WRITE_EXTERNAL_STORAGE
in the manifest. Otherwise, it always returns false
, even if you have opted into legacy storage.
Prev Table of Contents Next
This book is licensed under the Creative Commons Attribution-ShareAlike 4.0 International license.