Google Glassでカメラを使う

こんにちは、KID.Aです。

前回の更新からだいぶ期間が経ってしまいました。
今回もGoogle Glassの開発に関しての記事で、アプリからカメラを使用します。

通常のAndroidの場合ですとIntentを使用してカメラを呼び出しますが、Google Glassでも同じです。
異なる箇所は、結果がonActivityResultに返却された場合に、Intentsの定数を使用してデータを受け取る箇所です。

 

実装方法

今回はgoogle のサンプルソースを動かします。
https://developers.google.com/glass/develop/gdk/camera

※コメント追加や、CameraManager.EXTRA_PICTURE_FILE_PATHをIntents.EXTRA_PICTURE_FILE_PATHに変更したり(Depricatedになっているため)、若干修正は加えています。

package com.example.glasscamera;

import java.io.File;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.os.FileObserver;
import android.provider.MediaStore;
import android.util.Log;
import android.view.KeyEvent;

import com.google.android.glass.app.Card;
import com.google.android.glass.content.Intents;

public class MainActivity extends Activity {

	// 写真撮影時の通知用の値
	private static final int TAKE_PICTURE_REQUEST = 1;

	@Override
	protected void onCreate(Bundle bundle) {
		super.onCreate(bundle);
		// 画面に文字を表示するためCardを使用する
		Card card = new Card(this);
		card.setText("タッチパッドをタップすると写真を撮影できます");
		setContentView(card.getView());
	}

	@Override
	public boolean onKeyDown(int keyCode, KeyEvent event) {

		// タッチパッドがタップされた場合
		if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) {
			takePicture();
			return false;
		} else {
			return super.onKeyDown(keyCode, event);
		}
	}

	@Override
	protected void onResume() {
		super.onResume();
	}

	@Override
	protected void onPause() {
		super.onPause();
	}

	/**
	 * 写真撮影を開始する
	 */
	private void takePicture() {
		Log.d(MainActivity.class.getName(), "call takePicture");

		// Intent経由で撮影して、写真のデータをもらう
		Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
		// 第二引数のTAKE_PICTURE_REQUESTで呼び元を判定する
		startActivityForResult(intent, TAKE_PICTURE_REQUEST);
	}

	@Override
	protected void onActivityResult(int requestCode, int resultCode, Intent data) {

		Log.d(MainActivity.class.getName(), "call onActivityResult");

		// 結果がOKで、呼び元がTAKE_PICTURE_REQUESTの場合
		if (requestCode == TAKE_PICTURE_REQUEST && resultCode == RESULT_OK) {

			Log.d(MainActivity.class.getName(), "TAKE_PICTURE_REQUEST & RESULT_OK");

			// 撮影結果の保存場所を取得する
			String picturePath = data.getStringExtra(Intents.EXTRA_PICTURE_FILE_PATH);

			// 保存されているか確認する
			processPictureWhenReady(picturePath);
		}

		super.onActivityResult(requestCode, resultCode, data);
	}

	/**
	 * 写真が保存されるまで待機するメソッド
	 * @param picturePath 写真のファイルパス
	 */
	private void processPictureWhenReady(final String picturePath) {

		Log.d(MainActivity.class.getName(), "call processPictureWhenReady");

		final File pictureFile = new File(picturePath);

		if (pictureFile.exists()) {
			// 写真が存在する場合
			Log.d(MainActivity.class.getName(), "pictureFile is exists");

			// ここで写真を使用した処理を書く
			// また、進捗状況インジゲータや待ち画像を表示している場合は消す処理を書く

		} else {
			// 写真が存在しない場合
			Log.d(MainActivity.class.getName(), "pictureFile is not exists");

			// ここで進捗状況インジゲータや待ち画像などを表示する処理を書く

			// ファイルパスを取得する
			final File parentDirectory = pictureFile.getParentFile();

			// FileObserverでディレクトリを監視する
			FileObserver observer = new FileObserver(parentDirectory.getPath(),
			        FileObserver.CLOSE_WRITE | FileObserver.MOVED_TO) {
				// MOVED_TOやCLOSE_WRITE後に保留中のイベントから保護するためのフラグ
				private boolean isFileWritten;

				@Override
				public void onEvent(int event, String path) {

					Log.d(MainActivity.class.getName(), "call event");

					// 既に書き込まれている場合は何もしない
					if (!isFileWritten) {

						File affectedFile = new File(parentDirectory, path);
						// 写真ファイルが書き込まれたチェックする
						isFileWritten = affectedFile.equals(pictureFile);

						Log.d(MainActivity.class.getName(), "affectedFile = " + affectedFile.getAbsolutePath());

						if (isFileWritten) {

							Log.d(MainActivity.class.getName(), "file is written");

							// ファイルが書き込まれている場合は、FileObserverを停止する
							stopWatching();

							// ファイルが書き込まれている場合は、UI ThreadでprocessPictureWhenReadyを呼ぶ
							runOnUiThread(new Runnable() {
								@Override
								public void run() {
									Log.d(MainActivity.class.getName(), "runOnUiThread");
									processPictureWhenReady(picturePath);
								}
							});
						}
					}
				}
			};
			observer.startWatching();
		}
	}
}

 

解説

ではソースの解説をしていきます。

  1. まず、起動したらonCreateが呼ばれて、Cardが呼ばれます。Cardは文字を表示するために使用します。
  2. 次にタッチパッドがタップされると、onKeyDownが呼ばれます。キーイベントの挙動や種別はこの記事がわかりやすいと思います。その中でtakePictureを呼んでいます。
  3. takePictureは通常のAndroidアプリと同じく、MediaStore.ACTION_IMAGE_CAPTUREを使用してIntentでカメラを起動できます。
  4. カメラで撮影後、onActivityResultに結果が返ってきます。Intents.EXTRA_PICTURE_FILE_PATHで画像のファイルパスが返却されます。Google DeveloperではCameraManager.EXTRA_PICTURE_FILE_PATHが使用されていますが、既にDepricatedされているためIntents.EXTRA_PICTURE_FILE_PATHに変更しています。
  5. 画像のファイルパスが返ってきますが、実際にはまだ保存されていない状態のため、FileObserverを使用してディレクトの監視を行います。
  6. processPictureWhenReadyが呼ばれた際に、pictureFile.exists()でまだファイルがないため、結果がfalseに返却されます。その後、FileObserverでディレクトリを監視してくれます。
  7. ファイルが追加されるとonEventが呼ばれます。
  8. onEventで追加されたファイルがIntents.EXTRA_PICTURE_FILE_PATHと同じファイル名だったらprocessPictureWhenReadyを呼びます。processPictureWhenReadyが呼んだ際に、pictureFile.exists()で既にファイルが存在するため、結果にtrueが返却されます。

 

動かしてみる

実際に動かしています。

  1. 起動後、タッチパネルをタッチします。
    device-2014-07-31-164625
  2. タッチするとカメラが立ち上がります。
    device-2014-07-31-164727
  3. カメラをタップすると、写真を撮影して、元の画面に戻ります。フォルダに写真が保存されています。
    device-2014-07-31-164625

以下は実行の際のログになります。

call takePicture
call onActivityResult
TAKE_PICTURE_REQUEST & RESULT_OK
call processPictureWhenReady
pictureFile is not exists
call event
affectedFile = /storage/emulated/0/DCIM/Camera/20140731_170059_533.jpg.tmp
call event
affectedFile = /storage/emulated/0/DCIM/Camera/20140731_170059_533.jpg
file is written
call event
runOnUiThread
call processPictureWhenReady
pictureFile is exists

call eventが2度来ていて、一度目は添付ファイルが追加され、二度目に実際のファイルが追加されているのがわかります。
これでカメラを利用することができます。
 
今回はIntentを用いてカメラを利用しましたが、CameraとSurfaceHolderを使用するとリアルタイムに画像を使用することもできます。リアルタイムに画像を使えると、某マンガであったような戦闘力を表示するスカウターアプリなどを作ることができるかもしれません。
 

KID.A

KID.A の紹介

楽して生きることと一発逆転を夢見ている、ちゃきちゃきのAndroiderです。
いろいろアプリを出しているのですが、いつもリリース後にターゲットユーザ数を2桁見誤っていたことに気付くので、残念でなりません。下方修正で、ヒットがでません。おしいです。

明日から本気出します。
よろしくお願いします。

カテゴリー: Android タグ: , , , , パーマリンク

Google Glassでカメラを使う への1件のフィードバック

  1. suela のコメント:

    英語の分からない私は、ココのサイトを見てかなり勉強させていただいております。
    今、グラスをいじっているのですが、デフォルトのカメラ画像大きいのでサイズを変更しようとしているのですが
    うまくいきません、intent bitmap 等 色々やってみましたが、うまくいかず煮詰まっております。もし、よろしかったらKID.A様のコードを元に助言などいただけませんでしょうか?

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です