Scoped Storage と DownloadManager

4月30日木曜日、晴れ

早めに切り上げるつもりが果たせずズルズルと仕事を続けてしまう。夕飯を作るつもりが、それもできず帰宅した妻が手早く豚の生姜焼きを作ってくれるのに任せてしまう。うう、自己嫌悪。

* * *

お母さんにお弁当を持たせるんだと、こどもらは徹夜でサンドイッチ作りに勤しんだ模様。明日はハンバーガーを持たせるのだと、いまパテを作っている。なんともいじらしく可愛らしい。

* * *

Android 10 では SD カードへの保存が制限されている。(Scoped storage

昨日は MediaStore.Images 経由で行を先行して insert し、この Uri をカメラアプリに渡して画像を保存する方法を捻りだした。

この方法を援用すればよかろうと Web からの画像ダウンロードでは MediaStore.Downloads で行を insert し、これから得た OutputStream にデータを書き込む方針を立てて実装を進めてみた。

しかしながら、まず MediaStore.Downloads が API level 29、つまり Android 10 以降で追加されたものらしく、それ以前のバージョンで共用できない。 10 では Uri から取得した OutputStream 経由、それ以前のバージョンでは Downloads に作成したファイルから OutputStream を得て共通化してみた。
これなら過去のデータ書き込み処理のほとんどが再利用できる!

けれど後処理で MediaScan をかける時点ではたと困る。 MediaStore 経由で作成した OutputStream のファイル名はどうしたら得られるのか?(後知恵だけれど MediaStore.DownloadColumns.TITLE でとれそうな気もする)
メディアスキャンをかけないとファイルの種別も不明な単なるデータファイルになってしまう。

しばし呆然としてから気を取りなおし解決策を探す。

どうやら DownloadManager というありがたいものがシステムに用意されているらしい。 setDestinationUri で MediaStore.Downloads に insert して得た Uri を渡してみる。クラッシュ。
file スキーマの Uri しか受けてつけてくれない模様。 Uri を指定して query し、 TITLE を得て setDestinationInExternalPublicDir に渡してみる。ビンゴ!
いや、待て。
なにやら数字の0バイトファイル(insert 時にできたもの)と DownloadManager が生成したらしいファイルとふたつができているじゃないか。

最終的にわかったのは DownloadManager が優秀で 10 でもそれ以前でも同じに動作してくれるらしいということ。
先行してファイルを作成しなくても setDestinationInExternalPublicDir が面倒を見てくれるし、ファイル名が重複するときは適当なサフィックスをつけて避けてもくれる。
なお scoped storage は ExternalPublicDir に対しては WRITE_EXTERNAL_STORAGE 権限なしのアクセスを許可してくれるものらしく、 Android 10 では特段のパーミッション請求が不要になっている。(こんなところでバージョン差分を作らないで欲しいとも思うが)

ここに至るまでで1日を全部消尽したのだ。悔しい。

この記事が気に入ったらサポートをしてみませんか?