fresh digitable

めんどくさかったなってことを振り返ったり振り返らなかったりするための記録

投稿する写真をカメラアプリで撮って送るついでにギャラリーアプリから見えるようにする(API Level 22+)

今回やりたいこと

前提

  • target SDK version: 29
  • Android 10で動作を確認した
  • androidx.activity:activity-ktx:1.2.0-beta01
  • androidx.fragment:fragment-ktx:1.3.0-beta01

カメラアプリを起動するインテント

カメラアプリのインテントActivityResultContracts.TakePictureを使えばよい。

インテントを作るときに渡すUriFileProvider.getUriForFile()を使って作る。content://からはじまるUriが取得できる。カメラアプリは撮影に成功してもファイルパスを返しては来ないのでこのUriは保存しておく。FileProviderの設定はリファレンスが詳しい。

developer.android.com

今回はexternal-media-pathと、古いバージョン(API Level 21未満)のためにexternal-file-pathも指定した。また、authorityの名前にアプリケーションIDを使いたかったのでアプリモジュールで実装してDIで注入する形にした。

カメラアプリにファイルの書き込み権限を渡したい

リファレンスによると二つの方法があるらしい。

FileProvider  |  Android Developers

結論から言うと二つ目のInclude the Permission in an Intentの方法は、Intent Chooserを出したい場合にはうまく動かない様子。

stackoverflow.com

したがって一つ目のGrant Permission to a Specific Packageの方法で実現することになる*1。ただし、権限を渡したいアプリのパッケージ名が必要なので何とかして入手しなければならない。API Level 22以上であればIntent.createChooser(Intent, CharSequence, IntentSender)を使うことで、Intent Chooserで選択されたアプリがBroadcastReceiverで取得できる。 API Level21以下で同じことをしなければならない時は、選択される可能性のあるカメラアプリをPackageManager.queryIntentActivities()で取得し、それらすべてに対して許可するしかないと思われる。いずれの場合においても権限をrevokeしそびれることが無いよう注意する。

画像をギャラリーから見えるようにする

この記事を書いている現在、公式の Take photos  |  Android Developers に書いてあるIntent.ACTION_MEDIA_SCANNER_SCAN_FILEを投げる方法は非推奨になっている。なので、今回はMediaScannerConnection.scanFile()を使った。これだとContextが必要なのでActivityResultContract.parseResult()とかの中でちょろっとやってしまうのは難しいかもしれない。また、これに渡すファイルパスはcontent://の形式のものではなく、FileProvider.getUriForFile()に渡したFileのパスを渡す。

このほかにも、カスタムのContractを作ったらワケあってDI(Dagger)でFragmentにContractを注入することになり、registerForActivityResult()onAttach()のなか(というかAndroidSupportInjection.inject()のあと)で呼ぶ羽目になったとかいろいろ細かいことはあるが、細かいのでこの辺にしておく。

*1:StackOverflowの回答ではqueryIntentActivities()をやって明示的なIntentにして解決しているがこっちの方法では試していない。こっちでうまくいくならその方がいいかも。