LoginSignup
16
15

More than 5 years have passed since last update.

ストレージ権限を付与せずにカメラ撮影のIntentを投げる

Last updated at Posted at 2017-04-05

ツイッター公式とかがランタイムパーミッション求めてくるけど、別になくてもいけるよな?って思ってやってみた。

やること

  • READ_EXTERNAL_STORAGE及びWRITE_EXTERNAL_STORAGE権限を付与しない
  • アプリからACTION_IMAGE_CAPTUREでアプリを起動する
  • 撮影された写真(Uri)をBitmapに復元する

実装

FileProviderを定義する

AndroidManigest.xml
<provider
    android:name="android.support.v4.content.FileProvider"
    android:authorities="${applicationId}.provider"
    android:grantUriPermissions="true"
    android:exported="false">

    <meta-data
        android:name="android.support.FILE_PROVIDER_PATHS"
        android:resource="@xml/file_provider" />

</provider>
res/xml/file_provider.xml
<paths xmlns:android="http://schemas.android.com/apk/res/android">

    <files-path name="image" path="temp/"/>
</paths>

出力先のUriを用意する

ファイルを作ります

private File createOutputFile() {
    File tempFile = new File(context.getFilesDir(), "temp/sample");
    if (!tempFile.exists()) {
        try {
            tempFile.getParentFile().mkdirs();
            tempFile.createNewFile();

        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    return tempFile;
}

ファイルが存在していないと、カメラ側でFileNotFoundExceptionになるので空ファイルを作ってください。

作ったファイルをFileProviderを使ってUriに変換します。

FileProvider.getUriForFile(context, BuildConfig.APPLICATION_ID + ".provider", file);

Intentを起動する

Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
intent.putExtra(MediaStore.EXTRA_OUTPUT, outputUri);
intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);

startActivityForResult(intent, REQUEST_CAPTURE);

結果を受け取る

protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);

    if (requestCode == REQUEST_CAPTURE && resultCode == RESULT_OK) {
        Uri uri = outputUri();
        Bitmap bitmap = MediaStore.Images.Media.getBitmap(getContentResolver(), uri);
    }
}

ACTION_IMAGE_CAPTUREでは、出力先Uriが返却されない糞仕様なので、出力先Uriは起動側でどうにかして保持しておく必要があります。
Uriを動的に生成する場合、カメラ起動時にメモリ食ってActivityが破棄される可能性も考えるとonSaveInstanceState()を実装したりとそこそこめんどくさいですが、頑張ってください。

仕組み

出力先ファイルはアプリ領域に用意し、FileProviderを使ってUri化しています。

このUriは通常、当該アプリしかアクセスできませんが、IntentFLAG_GRANT_WRITE_URI_PERMISSIONフラグを追加することで、一時的に外部アプリにアクセス権限を与えることができます。

課題

KitKat以前は、暗黙的IntentにFLAG_GRANT_*_PERMISSIONが付与されない

暗黙的IntentにUriのアクセス権限が付与できないようです。

以下のようにすることで、付与できるようですが、ユーザーが起動するアプリ以外にも付与される気がするのでどうなんだろ…

List<ResolveInfo> resolves = getPackageManager().queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);

for (ResolveInfo resolveInfo : resolves) {
    grantUriPermission(resolveInfo.activityInfo.packageName, outputUri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
}

ちなみにgrantUriPermission()したら、必要に応じてrevokeしてあげたほうがいいみたいです。

revokeUriPermission(outputUri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION);

参考

Setting Up File Sharing | Android Developers

Android:FileProviderでファイル共有 | Yukiの枝折

16
15
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
16
15