ストレージ経由での連携
これで、一通りカメラアプリとの連携ができました。しかし図7を見てもわかるように、非常に小さな画像しか取得できません。そこで、撮影された画像をいったんストレージに保存したうえでそれを取得する方法をとることにします。
Uri
Androidでは端末のストレージに保存された画像や音楽、電話帳などは標準で公開されています。これらの公開されたデータを特定する際、AndroidではURIで表します。また前回と同様に、このURI文字列をオブジェクトとして扱えるUriというクラスがAndroidには用意されています。今回、カメラアプリで撮影された画像をいったんストレージに保存しますが、その保存された画像を表すURIですべてをやり取りします。そこで、UseCameraActivityを以下のように改造します。
public class UseCameraActivity extends AppCompatActivity {
private Uri _imageUri; // (1)
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if(requestCode == 200 && resultCode == RESULT_OK) {
ImageView ivCamera = (ImageView) findViewById(R.id.ivCamera); // (2)
ivCamera.setImageURI(_imageUri); // (3)
}
}
~省略~
}
まず、画像のUriオブジェクトのフィールドを追加しています。それが(1)です。さらに、onActivityResult()内を(2)と(3)の2行だけに変更しています。(2)は既に存在するコードです。画像URIを利用する場合、リスト4のようにIntentオブジェクトから画像オブジェクトを取得する必要がなく、ImageViewのsetImageURI()メソッドにUriオブジェクトを渡すだけでその画像が表示されます。それが(3)です。
これで、画像の表示部分はできました。
URI指定でのカメラ起動
では、次にこのURI指定でカメラアプリを起動するように改造していきましょう。カメラアプリの起動ですので、onCameraImageClick()メソッド内の改造です。以下のように書き換えてください。
public void onCameraImageClick(View view) {
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMddHHmmss"); // (1)
Date now = new Date(System.currentTimeMillis()); // (1)
String nowStr = dateFormat.format(now); // (1)
String fileName = "UseCameraActivityPhoto_" + nowStr +".jpg"; // (1)
ContentValues values = new ContentValues(); // (2)
values.put(MediaStore.Images.Media.TITLE, fileName); // (3)
values.put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg"); // (4)
ContentResolver resolver = getContentResolver(); // (5)
_imageUri = resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values); // (6)
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); // (7)
intent.putExtra(MediaStore.EXTRA_OUTPUT, _imageUri); // (8)
startActivityForResult(intent, 200); // (9)
}
(7)と(9)はもともと記述されていたコードです。(7)でnewしたIntentオブジェクトのExtra情報としてUriオブジェクトを渡すのが最終目標です。それが(8)です。その際、キーとして定数「MediaStore.EXTRA_OUTPUT」を渡します。ただ、このままでは_imageUriはnullのままです。ですので、あらかじめこの_imageUriの中身を生成しておく必要があります。そのソースコードが(1)~(6)です。
実際にUriオブジェクトを生成してくれるのがContentResolverオブジェクトです。これを取得しているのが(5)です。これは、ActivityのメソッドgetContentResolver()メソッドで取得できます(正確には親クラスであるContextWrapperのメソッドです)。このContentResolverオブジェクトのinsertメソッドを使うことで、新しいデータの格納先を確保し、それを表すUriオブジェクトを生成してくれます。それが(6)です。その際、引数として2個必要です。第1引数はデータの格納先を表すUriオブジェクトです。画像ファイルを格納するストレージを表すUriオブジェクトは、定数が用意されています。それが「MediaStore.Images.Media.EXTERNAL_CONTENT_URI」です。第2引数は、具体的にどういったデータなのかを表す値が格納されたContentValuesオブジェクトです。これを生成しているのが(2)~(4)です。
(2)でContentValuesをnewします。ContentValuesオブジェクトはMapオブジェクトのようにさまざまなデータを、しかもデータ型をあまり気にせずに格納できる仕組みがあります。データを格納するにはput()メソッドを使います。
(3)でそのメソッドを使い、画像のファイル名を指定しています。その際のキーは「MediaStore.Images.Media.TITLE」です。値は画像のファイル名なので任意の文字列でいいのですが、撮影のたびにファイル名が変化するようにタイムスタンプを使用してユニークになるようにしています。その文字列を生成しているのが(1)の4行です。
(4)で同様にput()メソッドを使い、ファイルの種類を指定しています。その際のキーは「MediaStore.Images.Media.MIME_TYPE」で、値は「image/jpeg」です。これでJPEG画像が生成されます。
ストレージ利用のパーミッション付与
これで一通り、ストレージ経由でのカメラアプリ連携のコーディングが終了しました。ただし、この状態でアプリ起動後にカメラアイコンをタップしてもエラーで終了します。ストレージを利用するにはパーミッションをアプリに与える必要があります。そこを記述しましょう。
まず、AndroidManifest.xmlに以下の1行を追加してください。
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
次に、onCameraImageClick()メソッドの1番初めに以下のコードを追記してください。
public void onCameraImageClick(View view) {
if(ActivityCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
String[] permissions = {Manifest.permission.WRITE_EXTERNAL_STORAGE};
ActivityCompat.requestPermissions(this, permissions, 2000);
return;
}
~省略~
}
前回解説したパーミッションチェックコードです。パーミッション名が今回はストレージ利用のため、
Manifest.permission.WRITE_EXTERNAL_STORAGE
と、なっていること以外は前回と全く同じコードなので、解説は不要でしょう。
この状態でアプリを再起動し、カメラアイコンをタップしてください。以下の許可を求めるダイアログが表示されるので「許可」をタップしてください。
カメラ連携サンプルアプリの画面に戻るので、もう一度カメラアイコンをタップしてください。無事カメラアプリが起動します。あとは、ストレージを経由しない場合と同様に操作してください。カメラアプリからこのアプリに戻ってきた時に画像が表示されますが、ストレージを経由しない場合と違い、解像度の高い画像が表示されているはずです。
なお、AVDによるカメラのエミュレーターでは、そもそもの画像解像度が高くないため、ストレージ経由にしてもあまり差を感じないかもしれませんが、実機では確実に差が出ます。
パーミッション許可後
ここまでの実装で問題なく動作しますが、1点ユーザビリティが悪い部分があります。それは、許可ダイアログで許可した後にそのままカメラアプリへ遷移しないところです。これを解決するにはUseCameraActivityに、以下のメソッドを追記する必要があります。
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
if(requestCode == 2000 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
ImageView ivCamera = (ImageView) findViewById(R.id.ivCamera); // (1)
onCameraImageClick(ivCamera); // (2)
}
}
onRequestPermissionsResult()メソッドに関しては前回解説しています。ここではパーミッションが許可された時に、もう一度onCameraImageClick()を呼び出すようにしています。それが(2)です。ただ、onCameraImageClick()は引数としてタップされた画面部品オブジェクトを渡すことになっています。このメソッドはImageViewをタップする前提なので、あらかじめその部品を取得しています。それが(1)です。
これで、パーミッション許可ダイアログで許可を与えた後、すぐカメラアプリに遷移するようになります。
まとめ
今回は前回の続きとして、暗黙的インテントを利用してのカメラアプリとの連携を扱いました。次回はマテリアルデザインとツールバーを扱います。
