どうもこんばんわ。
ニコ生で「とある科学の超電磁砲T」の1~12話一挙放送見ました。初見だったけど面白かったです。OPかっこいい。
1期2期もいつか見たいですね。
本題
Android 11 からまーたファイルアクセスの方法が変わるらしい。はー?
どうやらファイルマネージャーアプリとかバックアップアプリ向けのファイルアクセス権限MANAGE_EXTERNAL_STORAGEが追加されるそうな。
MANAGE_EXTERNAL_STORAGE #とは
Android 9以前のように生パス(おそらく/storage/emulated/0/Downloadみたいなパスのこと)でのアクセスができるようになる。
ACTION_OPEN_DOCUMENT_TREEでSDカードへアクセスできないしSDカードアクセスするにはこれしかない?。もうSDカード使わせる気ないなこれ。  
そんな有能そうな権限ですが  
デベロッパープレビューの今後のバージョンでは、この権限が必要なアプリ向けのガイドラインをGoogle Playに提供する予定です。
引用元
がなんとか書いてあるのでなんか追加で作業が必要なのかな。
むやみにぽんぽん打てる訳ではないっぽい?
ちなみにMANAGE_EXTERNAL_STORAGEでも外部のアプリ固有領域(Android/data)にはアクセスできません。この領域ってもうファイルマネージャーとか端末単体じゃ見れないのかな(できないのそこそこ不便)。
環境
| name | value | 
| Android | 11 DP3 | 
| Android Studio | 4.1 Canary 8 | 
| 言語 | Kotlin | 
やってみる
Android 11のAPI使えるように
Android 11のSDKは各自入れてきてね。
appフォルダの方のbuild.gradle開いてandroid{}のところを書き換えます。
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 
 | android {compileSdkVersion 'android-R'
 buildToolsVersion "29.0.3"
 
 defaultConfig {
 applicationId "com.example.manageexternalstoragesample"
 minSdkVersion "R"
 targetSdkVersion 29
 versionCode 1
 versionName "1.0"
 
 testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
 }
 
 buildTypes {
 release {
 minifyEnabled false
 proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
 }
 }
 compileOptions {
 sourceCompatibility JavaVersion.VERSION_1_8
 targetCompatibility JavaVersion.VERSION_1_8
 }
 kotlinOptions {
 jvmTarget = '1.8'
 }
 }
 
 | 
compileSdkVersion 'android-R'でいいらしい?
権限追加
何故か補充が効かないのでコピペ
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 
 | <?xml version="1.0" encoding="utf-8"?><manifest xmlns:android="http://schemas.android.com/apk/res/android"
 package="com.example.manageexternalstoragesample">
 
 <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
 
 <application
 android:allowBackup="true"
 android:icon="@mipmap/ic_launcher"
 android:label="@string/app_name"
 android:roundIcon="@mipmap/ic_launcher_round"
 android:supportsRtl="true"
 android:theme="@style/Theme.ManageExternalStorageSample">
 <activity android:name=".MainActivity">
 <intent-filter>
 <action android:name="android.intent.action.MAIN" />
 
 <category android:name="android.intent.category.LAUNCHER" />
 </intent-filter>
 </activity>
 </application>
 
 </manifest>
 
 | 
レイアウト
そろそろConstraintLayout使えるようになりたい(難しそう)
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 
 | <?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 xmlns:app="http://schemas.android.com/apk/res-auto"
 xmlns:tools="http://schemas.android.com/tools"
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 android:gravity="center"
 android:orientation="vertical"
 tools:context=".MainActivity">
 
 <Button
 android:id="@+id/button"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:text="権限付与" />
 
 <Button
 android:id="@+id/show"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:text="取得" />
 
 <Button
 android:id="@+id/read"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:text="書き込み" />
 </LinearLayout>
 
 | 
権限もらう
これも補充が効かないのでIntentの中身はコピペで
| 12
 3
 4
 
 | button.setOnClickListener {val intent = Intent("android.settings.MANAGE_ALL_FILES_ACCESS_PERMISSION")
 startActivity(intent)
 }
 
 | 
Android 11から他のアプリに重ねるの権限取得で直接アプリの設定に飛べなくなりましたが、これも直接飛べずに一覧画面から選ぶ形式になってます。
権限取得ボタン押すと一覧画面でます
 
 
生パスで読み込んでみる
Android 10から使えなくなった以下のコードも MANAGE_EXTERNAL_STORAGE権限を持っていれば・・・!
Android 11では読み込みなら読み込み権限(READ_EXTERNAL_STORAGE)すらいらないです。
ただこの仕様はAndroid 11で追加されたため、Android 10でこれ使うにはManifestにrequestLegacyExternalStorage="true"(Scoped Storage無効化)を指定する必要があります。
| 12
 3
 4
 5
 
 | show.setOnClickListener {File("/storage/emulated/0/").listFiles().forEach {
 println(it.name)
 }
 }
 
 | 
出力結果
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 
 | AndroidMusic
 Podcasts
 Ringtones
 Alarms
 Notifications
 Pictures
 Movies
 Download
 DCIM
 
 | 
以下略
生パスで書き込んで見る
Android 10から使えなくなった以下のコードもMANAGE_EXTERNAL_STORAGE権限の前では・・・!
あとボタンのIDがreadになってるけどwriteが命名的には正解ですね。プログラム的には間違ってないけど。
| 12
 3
 4
 5
 6
 
 | read.setOnClickListener {File("/storage/emulated/0/書き込みテスト.txt").apply {
 createNewFile()
 writeText("書き込めてる~?")
 }
 }
 
 | 
Kotlinの拡張関数writeText()使ってるから一行で書き込めてるけどJavaだと長くなりそう。
多分これもAndroid 10を対象にするにはScoped Storageを無効にする必要があります多分。
 
全部くっつけたコード
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 
 | class MainActivity : AppCompatActivity() {override fun onCreate(savedInstanceState: Bundle?) {
 super.onCreate(savedInstanceState)
 setContentView(R.layout.activity_main)
 
 button.setOnClickListener {
 val intent = Intent("android.settings.MANAGE_ALL_FILES_ACCESS_PERMISSION")
 startActivity(intent)
 }
 
 show.setOnClickListener {
 File("/storage/emulated/0/").listFiles().forEach {
 println(it.name)
 }
 }
 
 read.setOnClickListener {
 File("/storage/emulated/0/書き込みテスト.txt").apply {
 createNewFile()
 writeText("書き込めてる~?")
 }
 }
 
 }
 }
 
 | 
おわりに
くっそめんどくさくね?(SAFだのScopedStorageだのMediaStoreだの)
あとSDカード入る実機ほしい。Xperiaの21:9使ってみたい。
(S7 EdgeあるけどなんかSDM 820の割にNexus 7 2013のほうが動くからハズレSoC引いた?)
ソースコード
https://github.com/takusan23/ManageExternalStorageSample