私は同じ問題と戦っていて、質問が尋ねられたずっと後に解決策を得ましたが、これはコミュニティに役立つと思います.
Android 6.0 / API レベル 23 で導入された新しい動的アクセス許可では、実行時にアクセス許可を要求し、ユーザーの反応の受け入れと拒否の両方を処理する必要があるため、トピックの質問は特に重要になっています。カメラ アクティビティを使用するには、まず対応する許可を求める必要があります ( android.permission.CAMERA
)。次に、画像を外部ディレクトリに保存すると、対応する権限android.permission.READ_EXTERNAL_STORAGE
また、ユーザーがアプリに付与する必要があります。ユーザーが意図したアクションを実行しようとしている瞬間に、ランタイム許可要求はユーザーにとって自然に思えます (たとえば、[写真を撮る] ボタンが押された直後にカメラ アクセス許可要求が表示される場合)。ただし、外部ストレージを使用してカメラの画像を保存する場合は、アプリが写真を撮るときに、(1) カメラの使用と (2) 外部ストレージへのアクセスの 2 つの権限を同時に要求する必要があります。ユーザーが写真だけを撮られることを期待しているのに、アプリがユーザー ファイルにアクセスしようとする理由が必ずしも明確ではないため、後者はイライラする可能性があります。
外部ストレージを回避し、カメラ画像を直接保存できるようにするソリューションは、コンテンツ プロバイダーを使用することにあります。ストレージ オプションのドキュメントによると、
Android は、コンテンツ プロバイダーを使用して、プライベート データを他のアプリケーションに公開する方法を提供します。コンテンツ プロバイダーは、アプリケーション データへの読み取り/書き込みアクセスを公開するオプションのコンポーネントであり、課したい制限を条件とします。
これはまさに、アプリのローカル ストレージに写真を直接保存するためにカメラ アクティビティに許可する必要があるものです。これにより、追加のアクセス許可を要求せずに簡単にアクセスできるようになります (カメラへのアクセスのみを許可する必要がありました)。
コード例を含む優れた記事がここに提供されています。この記事に触発された次の一般化されたコードは、このトリックを実行するためにアプリで使用されます。
コンテンツ プロバイダー クラス:
/**
* A content provider that allows to store the camera image internally without requesting the
* permission to access the external storage to take shots.
*/
public class CameraPictureProvider extends ContentProvider {
private static final String FILENAME = "picture.jpg";
private static final Uri CONTENT_URI = Uri.parse("content://xyz.example.app/cameraPicture");
@Override
public boolean onCreate() {
try {
File picture = new File(getContext().getFilesDir(), FILENAME);
if (!picture.exists())
if (picture.createNewFile()) {
getContext().getContentResolver().notifyChange(CONTENT_URI, null);
return true;
}
} catch (IOException | NullPointerException e) {
e.printStackTrace();
}
return false;
}
@Nullable
@Override
public ParcelFileDescriptor openFile(@NonNull Uri uri, @NonNull String mode) throws FileNotFoundException {
try {
File picture = new File(getContext().getFilesDir(), FILENAME);
if (!picture.exists())
picture.createNewFile();
return ParcelFileDescriptor.open(picture, ParcelFileDescriptor.MODE_READ_WRITE);
} catch (IOException | NullPointerException e) {
e.printStackTrace();
}
return null;
}
@Nullable
@Override
public Cursor query(@NonNull Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
return null;
}
@Nullable
@Override
public String getType(@NonNull Uri uri) {
String lc = uri.getPath().toLowerCase();
if (lc.endsWith(".jpg") || lc.endsWith(".jpeg"))
return "image/jpeg";
return null;
}
@Nullable
@Override
public Uri insert(@NonNull Uri uri, ContentValues values) {
return null;
}
@Override
public int delete(@NonNull Uri uri, String selection, String[] selectionArgs) {
return 0;
}
@Override
public int update(@NonNull Uri uri, ContentValues values, String selection, String[] selectionArgs) {
return 0;
}
}
アプリ マニフェストでコンテンツ プロバイダーを宣言する必要があります。
<provider android:authorities="xyz.example.app"
android:enabled="true"
android:exported="true"
android:name="xyz.example.app.CameraPictureProvider" />
最後に、カメラ画像をキャプチャするためにコンテンツ プロバイダーを使用するために、次のコードが呼び出しアクティビティから呼び出されます。
Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
// Ensure that there's a camera activity to handle the intent
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, CameraPictureProvider.CONTENT_URI);
startActivityForResult(takePictureIntent, 0);
カメラの許可リクエストは個別に処理する必要があることに注意してください (提示されたコード サンプルでは処理されていません)。
また、ビルド ツール バージョン 23 以降を使用している場合にのみ、アクセス許可要求を処理する必要があることにも注意してください。同じコードは低レベルのビルド ツールと互換性があり、実行時のパーミッション リクエストは気にせず、外部ストレージの使用を避けたい場合に役立ちます。