株式会社ライブキャストロゴ 株式会社ライブキャスト

2011年11月3日 追記
ご注意!
Google Translate API v1が2011年12月1日にサービスが終了いたします。添付のサンプルソースでは、Google Translate API v1を利用していますので、翻訳が正常に機能しない場合がございます。ご承知おきください。

Android Market公開を目指してAndroidアプリを開発する!(TextToSpeech日本語対応編)の続きです。

今、作っているAndroidアプリのベースとしたAIRアプリ、TranslatAIRには、画面が閉じられていても、OSに常駐して、クリップボードにあるテキストデータを翻訳するという機能があります。

Androidは、マルチタスクをサポートしていてバックグラウンドでの処理が可能なので、同等の機能が実装できそうです。
※ iPhoneもiOS 4から対応されましたので、同等の機能が実装できると思います。

サービスクラスを利用して、翻訳画面が閉じられても、バックグラウンドで常駐プロセスを起動させておくようにします。

常駐アプリが作成できるAndroidの“サービス”とは (2/3) – @ITを参考にして、サービスクラスを継承したクラスの大枠を作ってみました。

package jp.flashcast.translator.android.service;

import jp.flashcast.translator.android.dao.TranslateDao;
import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;

public class TranslateService extends Service {
	private TranslateDao dao;

	public class TranslateBinder extends Binder {
		public TranslateService getService() {
			return TranslateService.this;
		}
	}

	@Override
	public IBinder onBind(Intent intent) {
		return new TranslateBinder();
	}

	@Override
	public void onCreate() {
		super.onCreate();

        if (dao == null) {
        	dao = new TranslateDao(this);
        }
	}

	@Override
	public void onDestroy() {
		super.onDestroy();
	}

	@Override
	public void onRebind(Intent intent) {

	}

	@Override
	public boolean onUnbind(Intent intent) {
		return true; // 再度クライアントから接続された際に onRebind を呼び出させる場合は true を返す
	}
}

アクティビティには、以下のようなコードをいくつか追加します。
まず、このサービスを開始する部分です。

        Intent intent = new Intent(this, TranslateService.class);
        startService(intent);

アクティビティとサービスを、ServiceConnectionクラスを用いてバインドすると、

        bindService(intent, connection, Context.BIND_AUTO_CREATE);
        binded = true;

アクティビティがサービスに接続したときと切断したときの処理を、コールバック関数のようにすることができます。

    	connection = new ServiceConnection() {

    		public void onServiceConnected(ComponentName name, IBinder binder) {
    			service = ((TranslateService.TranslateBinder)binder).getService();
    		}

    		public void onServiceDisconnected(ComponentName name) {
    			service = null;
    		}

    	};

また、アクティビティが終了する際に、サービスがバインドされていたら、不要なリソースが残らないようアンバインドするようにします。

    @Override
    protected void onDestroy() {
    	super.onDestroy();

    	if (binded) {
    		unbindService(connection);
    	}
    }

これらを翻訳画面のアクティビティに追加します。

TranslatAIRの場合には、WindowsのタスクトレイのアイコンやMac OS XのDockアイコンに常駐させて、それらのアイコンをクリックされたときにクリップボードの内容を翻訳するようにしました。

ですが、今回は、同じように常駐させるのが難しいので、クリップボードの内容を翻訳しにいくトリガーをどうするかが問題です。

そこで、考えたのが加速度センサーの利用です。とりあえず、携帯を振って、ある程度の加速度を検知した際に、クリップボードの内容を翻訳するようにしました。

常駐するサービスにSensorEventListenerを実装して、加速度センサーが変化したときのイベントリスナーを登録します。

		manager = (SensorManager)getApplicationContext().getSystemService(SENSOR_SERVICE);
		List<Sensor> sensors = manager.getSensorList(Sensor.TYPE_ACCELEROMETER);

		if (sensors.size() > 0) {
			Sensor sensor = sensors.get(0);
			registered = manager.registerListener((SensorEventListener)this, sensor, SensorManager.SENSOR_DELAY_NORMAL);
		}

加速度センサーの値が変化したときのイベントリスナーはonSensorChangedです。
※ onAccuracyChangedは加速度センサーの精度が変更されたときのイベントリスナーなので、今回は何もしていません。

	public void onAccuracyChanged(Sensor sensor, int accuracy) {

	}

	public void onSensorChanged(SensorEvent event) {
		if (!binded) {
			if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
				accelerations[0] = event.values[0] - accelerations[0];
				accelerations[1] = event.values[1] - accelerations[1];
				accelerations[2] = event.values[2] - accelerations[2];

				float acceleration =
					Math.abs(accelerations[0]) +
					Math.abs(accelerations[1]) +
					Math.abs(accelerations[2]);

				if (acceleration > 40.0f) {
					String clipboard =
						((ClipboardManager)(getApplicationContext().getSystemService(CLIPBOARD_SERVICE)))
							.getText().toString();

					if (!clipboard.equals("")) {
						// 翻訳実行
						if (model == null) {

						}
						model = new TranslateModel();
						model.setOriginal(clipboard);
						model.setSrcLanguage("en");
						model.setDstLanguage("ja");
						model.setDt(new Date().toLocaleString());

						Translate();
					}

					accelerations[0] = 0.0f;
					accelerations[1] = 0.0f;
					accelerations[2] = 0.0f;
				}
			}
		}
	}

※ 加速度センサーの部分は、センサー(Sensor)を使用するには – 逆引きAndroid入門を参考にさせていただきました。
※ 17行目の値は、何度か試して適当な値を設定したものなので、特に意味はありません。

33行目の翻訳は、今までと同じように、別スレッドで処理します。

	private void Translate() {
		new TranslateThread(new Handler(), this, model).start();
	}

加速度センサーが変化を検知し正常に翻訳が終了すると、翻訳結果が、画面上に浮き上がります。

translated

これで、クリップボードにコピーして、携帯を振ると、サクっと(?)翻訳できるようになりました。他の翻訳系のAndroidアプリにはあまりないと思われる機能なので、このアプリのウリにしていきたいと思います。是非、試してみてください!

サンプルソース

サンプルソースには、Android Market公開を目指してAndroidアプリを開発する!(TextToSpeech日本語対応編)でご紹介した日本語読み上げライブラリをlibフォルダ配下にandroid-jatts-0.0.1.jarも含んでいますが、プロジェクトへの登録が再度必要ですので、ご注意ください。