ヽ|∵|ゝ(Fantom) の 開発blog? ホーム »
最近の記事

【Unity】Androidのトーストやダイアログ、通知、音声認識、ハード音量操作など基本的な機能を使えるプラグインを作ってみた  


 元々は GearVR のように、Cardboard/ハコスコVR でも BlueToothコントローラ を使って手元で音量操作できたら便利だろうな~って思って色々試してたんだけど、そのついでにAndroidに標準で備わっているトースト(Toast)やダイアログ(AlertDialog)、通知(Notification)、音声認識(SpeechRecognizer)なども使える簡単なプラグインを作ってみた。単純な機能しかないが、ブラウザなどでURL先を開く機能なども付けておいたので、アプリ内に書ききれない説明ページやサポートページなどのHPを開くボタンなどを作るのも良いだろう。簡単なものだがパッケージにはサンプルも付けておいたので、コピペするなり改造するなりして自由に使って欲しい。

>>プラグイン&サンプルをダウンロード
(Google Drive を利用。画面右上にあるダウンロードアイコンを押す)



(※) Unity 5.6.3p1 / AndroidStudio 2.3.3 / Windows10(x64) / Galaxy S7 Edge (Android 7.0) で確認



■プラグインのセットアップ

 まずはアーカイブを解凍したら、「FantomPluginAndroid.unitypackage」をプロジェクトにインポートしよう。もちろん機能を使うには「Build Settings..」を「Andrdoid」にする必要があるが、どのみちエディタ上では動作確認はできないので、最終的にはビルドして実機で確認する必要がある。


 プラグインの実体は「Assets/Plugins/Android/」にある「fantomPlugin.aar」になる。プラグインは「Minimum API Level:Android 4.2 (API 17) 」で作られているので、ビルドする際には API17 以上に設定して欲しい。


 また、同じフォルダに複数の「AndroidManifest-~.xml」があるが、これは使いたい機能や画面の回転方向("_Landscape"または"_Portrait")、テーマ("_API17"または"_API21")などの設定テンプレで、ビルドする際に適切なものを選んで「AndroidManifest.xml」にリネームして使って欲しい。例えば、「ハードウェア音量のイベント取得」には "-HardVolKey" が付いたものを、「音声認識のダイアログ」を使うには "-Speech" が付いたものを使う必要がある。これら機能を利用するには元のアクティビティ「UnityPlayerActivity」をオーバーライドした、「HardVolKeyOnUnityPlayerActivity」(ハードウェア音量イベント取得用アクティビティ:プラグイン内に入っている)、「ReceiveResultsOnUnityPlayerActivity」(音声認識ダイアログ結果取得用アクティビティ:プラグイン内に入っている)を使う必要があるので、それらを指定したマニフェストファイルとなっている。また、両方使いたい場合は "-FullPlugin" の付いたもの(プラグイン内に入っている)を使えば良い(「FullPluginOnUnityPlayerActivity」にはプラグイン機能のすべてが入っている)。これらマニフェストファイルは手動で書き換えても構わない(※ただし音声認識を使う場合は、録音パーミッション「RECORD_AUDIO」も必要)。

(参考)UnityPlayerActivity Java コードの拡張

 なお、"_API17"または"_API21"の名前は使えるテーマの API Level で分類されているが、これは主に「Theme.DeviceDefault.Light.NoActionBar.Fullscreen」(API17から)、「Theme.Material.Light.NoActionBar.Fullscreen」(API21から)というだけで内容は同じだ。元の「UnityPlayerActivity」をオーバーライドして起動すると、Unityが完全に起動するまでにアクションバーなどが表示されることがあるので、それを回避するために使っているものだ。これらはダイアログにも適用されるので注意しよう(※ダイアログごとに設定することはできる)。任意に変更しても構わない。ちなみに".Light"が付いているテーマは白系ベースで、付いてないものは黒系ベースとなる。テーマの名前のアンダバー("_")をドット(".")に変更したものを指定する。

(参考)R.Style.Theme

 あと、サンプルデモをビルドするには「AndroidManifest_test.xml」をリネームして「AndroidManifest.xml」にすれば全機能使える(※"-FullPlugin" と内容的には同じ)。「BuildSettings...」に「Assets/_Test/Scenes/」にあるシーンを追加してビルドするのも忘れずに。


(※) ビルドすると警告「Unable to find unity activity in manifest. You need to make sure orientation attribute is set to sensorPortrait manually.」が出るが、独自の「AndroidManifest.xml」(UnityPlayerActivity以外のもの)を使うと出るものなので気にしなくて良い(orientation=画面回転はUnity側のアプリの設定に合わせた方が良い)。


 以下には各機能の使用する際の簡単なC#コードと解説、プラグインとして作った元のJavaコードの一部(簡略化したもの)を書いておこう。簡易説明書のように使って欲しい。JavaでAndroidのアプリを作ったことがあるなら、対応するパラメタを見た方が理解しやすく応用範囲も広いものになるだろう。



■トーストを使う(Toast)


●Unity から Android の Toast を使う C# コード
using FantomLib;

#if UNITY_ANDROID && !UNITY_EDITOR
AndroidPlugin.ShowToast(message);
#endif

●プラグイン内の対応する Java コード
import android.content.Context;
import android.widget.Toast;

Toast.makeText(context, message, duration).show();

 トースト(Toast)は画面の下の方に出る、短い時間表示されるメッセージである。

 C# 側の引数はメッセージ文字列(message)と表示長さのフラグ(longDuration)となっている。長さはJava 側で「LENGTH_SHORT」「LENGTH_LONG」しかないので、フラグで置き換えている。context は起動アクティビティのことを指していて、AndroidPlugin のメソッド内で自動取得されるので必要ない。

 サンプルコードは「Assets/_Test/Scripts/PluginTest.cs」の「ShowToast()」「TestToast()」あたりを見て欲しい。



■ダイアログを使う(AlertDialog) [Yes/No 2択]


●Unity から Android の AlertDialog を使う C# コード(Yes/No ダイアログ)
using FantomLib;

#if UNITY_ANDROID && !UNITY_EDITOR
AndroidPlugin.ShowDialog(title, message, callbackGameObject, callbackMethod, yesCaption, yesValue, noCaption, noValue, style);
#endif

●プラグイン内の対応する Java コード
import android.app.AlertDialog;
import android.content.DialogInterface;
import static com.unity3d.player.UnityPlayer.UnitySendMessage;

DialogInterface.OnClickListener listener = new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int which) {
switch(which) {
case DialogInterface.BUTTON_POSITIVE:
UnitySendMessage(callbackGameObject, callbackMethod, yesValue);
break;
case DialogInterface.BUTTON_NEGATIVE:
UnitySendMessage(callbackGameObject, callbackMethod, noValue);
break;
}
}
};

AlertDialog.Builder builder = new AlertDialog.Builder(context, styleId);

builder.setTitle(title)
.setMessage(message)
.setPositiveButton(yesCaption, listener)
.setNegativeButton(noCaption, listener)
.show();

 これは「はい/いいえ」の2択のダイアログ(AlertDialog)である。戻値は「はい」のとき「yesValue」、「いいえ」のとき「noValue」がヒエラルキーにある GameObject 名(callbackGameObject)のメソッド(callbackMethod)に文字列で返ってくる。また Andrdoid のダイアログの場合、範囲外をクリックするとダイアログが消えるが、その場合は何も返って来ない。つまり実質3択のようになる(「はい/いいえ/あとで」→「Yes/No/(なし)」みたいな)。コールバックするメソッドのシグネチャは文字列型(string)固定になる(というより基本的にネイティブ間のメッセージやりとりは全て文字列型と考えて良い→受け取り側で適切に変換して使う)。

 ダイアログにはスタイル(テーマ)を当てられるようにしてある。省略=空文字("")のときはアクティビティのテーマと同じものになる。スタイルの引数は "android:Theme.DeviceDefault.Dialog.Alert" のように、名前のアンダバー("_")をドット(".")に変えたものを指定する。端末の API Level によって使えるものとそうでないものがあるので、アプリのビルドの API Level に合わせて指定する必要があるので注意。

(参考)R.style.theme

 サンプルコードは「Assets/_Test/Scripts/PluginTest.cs」の「TestDialogYesNo()」あたりを見て欲しい。



■ダイアログを使う(AlertDialog) [OK のみ]


●Unity から Android の AlertDialog を使う C# コード(OK ダイアログ)
using FantomLib;

#if UNITY_ANDROID && !UNITY_EDITOR
AndroidPlugin.ShowDialog(title, message, callbackGameObject, callbackMethod, okCaption, resultValue, style);
#endif

●プラグイン内の対応する Java コード
import android.app.AlertDialog;
import android.content.DialogInterface;
import static com.unity3d.player.UnityPlayer.UnitySendMessage;

DialogInterface.OnDismissListener listener = new DialogInterface.OnDismissListener() {
@Override
public void onDismiss(DialogInterface dialog) {
UnitySendMessage(callbackGameObject, callbackMethod, resultValue);
}
};

AlertDialog.Builder builder = new AlertDialog.Builder(context, styleId);

builder.setTitle(title)
.setMessage(message)
.setPositiveButton(okCcaption, null)
.setOnDismissListener(listener)
.show();

 これは注意喚起を促すだけの「OK」のみのダイアログ(AlertDialog)である。戻値は必ず「resultValue」がヒエラルキーにある GameObject 名(callbackGameObject)のメソッド(callbackMethod)に文字列で返ってくる。また Andrdoid のダイアログの場合、範囲外をクリックするとダイアログが消えるが、その場合も「resultValue」が返ってくる。つまりダイアログが閉じられたタイミングを受け取るだけの値となる。コールバックするメソッドのシグネチャは文字列型(string)固定になる(というより基本的にネイティブ間のメッセージやりとりは全て文字列型と考えて良い→受け取り側で適切に変換して使う)。

 ダイアログにはスタイル(テーマ)を当てられるようにしてある。省略=空文字("")のときはアクティビティのテーマと同じものになる。スタイルの引数は "android:Theme.DeviceDefault.Dialog.Alert" のように、名前のアンダバー("_")をドット(".")に変えたものを指定する。端末の API Level によって使えるものとそうでないものがあるので、アプリのビルドの API Level に合わせて指定する必要があるので注意。

(参考)R.style.theme

 サンプルコードは「Assets/_Test/Scripts/PluginTest.cs」の「TestDialogOK()」あたりを見て欲しい。



■通知を使う(Notification) [タッチでアプリに戻る(起動してないときは起動する)]


●Unity から Android の Notification を使う C# コード(タッチでアプリに戻る(起動してないときは起動する))
using FantomLib;

#if UNITY_ANDROID && !UNITY_EDITOR
AndroidPlugin.ShowNotification(title, message, iconName, tag, showTimestamp);
#endif

●プラグイン内の対応する Java コード
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Intent;

Intent intent = new Intent(context, context.getClass());
PendingIntent pendingIntent = PendingIntent.getActivities(context, 0, new Intent[]{intent}, 0);

Notification notif = new Notification.Builder(context)
.setContentTitle(title)
.setContentText(message)
.setSmallIcon(iconId)
.setAutoCancel(true)
.setContentIntent(pendingIntent)
.setWhen(System.currentTimeMillis())
.setShowWhen(true)
.build();

NotificationManager notificationManager = (NotificationManager)context.getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(tag, 0, notif);

 これは注意喚起を促すだけの通知(Notification)である。タッチでアプリに戻るようにしてある(起動してないときは起動する)。

 Unity の場合、自分でマニフェストファイルを書き換えない限りはアイコン名(iconName)は "app_icon" が使われる。タグは複数回通知したとき、同じタグを持つものは上書きされる(つまり最新のものだけ表示する)というものである。通知時刻は showTimestamp をオフにすれば消すことができる。デフォルトはオンになる。

 サンプルコードは「Assets/_Test/Scripts/PluginTest.cs」の「TestNotification()」あたりを見て欲しい。



■通知を使う(Notification) [タッチでURLを開く]


●Unity から Android の Notification を使う C# コード(タッチでURLを開く)
using FantomLib;

#if UNITY_ANDROID && !UNITY_EDITOR
AndroidPlugin.ShowNotificationToOpenURL(title, message, url, tag, showTimestamp);
#endif

●プラグイン内の対応する Java コード
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Intent;

Intent intent = new Intent(action, Uri.parse(url));
PendingIntent pendingIntent = PendingIntent.getActivities(context, 0, new Intent[]{intent}, 0);

Notification notif = new Notification.Builder(context)
.setContentTitle(title)
.setContentText(message)
.setSmallIcon(iconId)
.setAutoCancel(true)
.setContentIntent(pendingIntent)
.setWhen(System.currentTimeMillis())
.setShowWhen(true)
.build();

NotificationManager notificationManager = (NotificationManager)context.getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(tag, 0, notif);

 これはタッチをすると指定したURLを開く(起動アプリは端末による)通知(Notification)である。AndroidPlugin.ShowNotificationToActionURI() でアクションをURLを開くこと専用にしたものでもある。Java コードの引数の「action」が「android.intent.action.VIEW」のときURLを渡すとブラウザ等で開くことになる(※端末のデフォルトアプリの設定による)。タッチによる動作以外は「通知でアプリに戻る」と変わらない。

 AndroidPlugin.ShowNotificationToActionURI() でアクションと URI を組み合わせて使う分には他の使い方もできるかも知れない。アクションについては以下を参照して欲しい(Constant Value を使う)。

(参考)Intent.ACTION~

 サンプルコードは「Assets/_Test/Scripts/PluginTest.cs」の「TestNotification()」あたりを見て欲しい。



■URLを開く [任意のアクション]


●Unity から Android 端末でURLを開く
using FantomLib;

#if UNITY_ANDROID && !UNITY_EDITOR
AndroidPlugin.StartOpenURL(url);
#endif

●プラグイン内の対応する Java コード
import android.app.Activity;
import android.content.Context;
import android.content.Intent;

Intent intent = new Intent(action, Uri.parse(url));
startActivity(intent);

 これは直接 URL を開くアクションをするメソッドである。「通知からURLを開く」と動作は変わらないが、Unity のボタンでも画像でも好きなUIなどに付けて使うものとして作ってみた。AndroidPlugin.StartActionURI() でURLを開くこと専用にしたものでもある(引数の「action」が「android.intent.action.VIEW」になっている)。

 AndroidPlugin.StartActionURI() でアクションと URI を組み合わせて使う分には他の使い方もできるかも知れない。アクションについては以下を参照して欲しい(Constant Value を使う)。

(参考)Intent.ACTION~

 サンプルコードは「Assets/_Test/Scripts/PluginTest.cs」の「TestOpenURL()」あたりを見て欲しい。



■音声認識のダイアログを使う(SpeechRecognizer)


●Unity から Android の SpeechRecognizer を使う C# コード(ダイアログを表示して取得)
using FantomLib;

#if UNITY_ANDROID && !UNITY_EDITOR
AndroidPlugin.ShowSpeechRecognizer(callbackGameObject, resultCallbackMethod, message);
#endif

●プラグイン内の対応する Java コード
//音声入力ダイアログ表示
import android.speech.RecognizerIntent;
import android.speech.SpeechRecognizer;
import android.content.Intent;

Intent intent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
intent.putExtra(RecognizerIntent.EXTRA_PROMPT, message);
startActivityForResult(intent, 0);


//認識された文字列の受信とコールバック
import android.speech.RecognizerIntent;
import android.content.Intent;
import java.util.List;
import static com.unity3d.player.UnityPlayer.UnitySendMessage;

public class ReceiveResultsOnUnityPlayerActivity extends UnityPlayerActivity {
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (resultCode == RESULT_OK) {
if (data.hasExtra(RecognizerIntent.EXTRA_RESULTS)) {
List results = data.getStringArrayListExtra(RecognizerIntent.EXTRA_RESULTS);

string str = "";
for (String s : results) {
str += s + "\n";
}

UnitySendMessage(callbackGameObject, resultCallbackMethod, str);
}
}
}
}

 これは音声入力のダイアログを表示して、認識できた文字列を受信→Unity へコールバックするやり方である。認識できた文字列またはエラーはヒエラルキーにある GameObject 名(callbackGameObject)のメソッド(resultCallbackMethod)に文字列で返ってくる。認識された文字列は改行("\n")区切りで結合されて、Unity 側へコールバックメソッドの引数で返される。1つずつ取り出すには String.Split() などを使って分割すれば良い(基本的にネイティブ間のメッセージやりとりは全て文字列型と考えて良い→受け取り側で適切に処理して使う)。

 この結果受信の実装は「ReceiveResultsOnUnityPlayerActivity」に書かれていて、元の「UnityPlayerActivity」(デフォルトでAndroidビルドに使用されているアクティビティ)をオーバーライドして使う必要がある(マニフェストファイルの「-Speech」が付いているものに設定されている)

 サンプルコードは「Assets/_Test/Scripts/SpeechRecognizerTest.cs」の「ShowSpeechRecognizer()」「ResultSpeechRecognizer()」あたりを見て欲しい。



■音声認識をダイアログなしで使う(SpeechRecognizer)


●Unity から Android の SpeechRecognizer ダイアログなしで使う C# コード(イベントを取得して自前で処理する)
using FantomLib;

#if UNITY_ANDROID && !UNITY_EDITOR
AndroidPlugin.StartSpeechRecognizer(callbackGameObject, resultCallbackMethod, errorCallbackMethod,
readyCallbackMethod, beginCallbackMethod);
#endif

●プラグイン内の対応する Java コード
import android.speech.RecognitionListener;
import android.speech.RecognizerIntent;
import android.speech.SpeechRecognizer;
import android.content.Intent;

Intent intent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL, RecognizerIntent.LANGUAGE_MODEL_FREE_FORM);
intent.putExtra(RecognizerIntent.EXTRA_CALLING_PACKAGE, context.getPackageName());

SpeechRecognizer recognizer = SpeechRecognizer.createSpeechRecognizer(context);
recognizer.setRecognitionListener(new RecognitionListener() {
@Override
public void onReadyForSpeech(Bundle params) {
UnitySendMessage(callbackGameObject, readyCallbackMethod, "onReadyForSpeech");
}

@Override
public void onBeginningOfSpeech() {
UnitySendMessage(callbackGameObject, beginCallbackMethod, "onBeginningOfSpeech");
}

@Override
public void onError(int error) {
UnitySendMessage(callbackGameObject, errorCallbackMethod, String.valueOf(error));
}

@Override
public void onResults(Bundle results) {
List list = results.getStringArrayList(android.speech.SpeechRecognizer.RESULTS_RECOGNITION);

string str = "";
for (String s : list) {
str += s + "\n";
}

UnitySendMessage(callbackGameObject, resultCallbackMethod, str);
}

//・・・(略)・・・
});

recognizer.startListening(intent);

 これは音声入力のダイアログは表示しないで、待機イベント(onReadyForSpeech()→readyCallbackMethod)、入力開始イベント(onBeginningOfSpeech()→beginCallbackMethod)、認識結果イベント(onResults()→resultCallbackMethod)またはエラー(onError()→errorCallbackMethod)のイベントをこの順にコールバックするやり方である。それぞれのイベントで Unity 側でアニメーションしたり、状態表示をすれば良い。なお、待機イベント(onReadyForSpeech)は必ず文字列 "onReadyForSpeech" を、認識開始イベント(onBeginningOfSpeech) は "onBeginningOfSpeech" を返す。これはタイミングを知るためだけのイベントであり、省略しても構わない(結果とエラーだけでも使うことはできる)。

 認識された文字列は改行("\n")区切りで結合されて、Unity 側へコールバックメソッドの引数で返される。この場合はヒエラルキーにある GameObject 名(callbackGameObject)の結果受信メソッド(resultCallbackMethod)またはエラー受信メソッド(errorCallbackMethod)に引数の文字列で返ってくる。1つずつ取り出すには String.Split() などを使って分割すれば良い(基本的にネイティブ間のメッセージやりとりは全て文字列型と考えて良い→受け取り側で適切に処理して使う)。

 サンプルコードは「Assets/_Test/Scripts/SpeechRecognizerTest.cs」の「StartSpeechRecognizer()」「OnReady()」「OnBegin()」「OnResult()」「OnError()」あたりを見て欲しい。



■ハードウェア音量の取得・設定など [メディア音量のみ]


●Unity から Android のハードウェア音量を取得・設定する(メディア音量のみ)
using FantomLib;

#if UNITY_ANDROID && !UNITY_EDITOR
int max = AndroidPlugin.GetMediaMaxVolume(); //最大音量の取得
int vol = AndroidPlugin.GetMediaVolume(); //現在音量の取得
vol = AndroidPlugin.AddMediaVolume(1, showUI); //現在の音量+1
vol = AndroidPlugin.AddMediaVolume(-1, showUI); //現在の音量-1
vol = AndroidPlugin.SetMediaVolume(0, showUI); //ミュート:音量=0
#endif

●プラグイン内の対応する Java コード
import android.media.AudioManager;

//現在音量の取得
AudioManager audioManager = (AudioManager)context.getSystemService(Context.AUDIO_SERVICE);
return audioManager.getStreamVolume(AudioManager.STREAM_MUSIC);

//現在音量の設定
AudioManager audioManager = (AudioManager)context.getSystemService(Context.AUDIO_SERVICE);
audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, volume, showUI ? AudioManager.FLAG_SHOW_UI : 0);

 これは端末のハードウェア音量(メディア音量のみ)を操作するメソッドである。BlueToothコントローラなどでも音量操作できるものは多いので、Cardboard/ハコスコVRアプリなどに実装すると便利かも知れない(Cardboard アプリを Unity5.6.3p1 以前でビルドした場合ハードウェア音量キーが無効になるので。Unity5.6.3p2 以降だとハードウェア音量キーが効く→UIが出る)。

 引数の showUI は音量操作をしたとき、端末上のUIを表示するか否かのフラグである。VRアプリでは非表示にして自前でUIを実装した方が良いだろう(端末のUIは2眼にはならないので)。元々そのために作ったものである(笑)。

 またちょっとしたことだが、ネイティブ呼び出しは非常に負荷が高い動作なので、最大音量(GetMediaMaxVolume())など通常途中で変わらないものは、値をキャッシュして利用する仕組みなどを作った方が良いだろう。こういう手法を常にやるクセを付けておくと高速な処理が書けるようになる(※ただしネイティブ関連のオブジェクトはアプリが落ちたときのことを考慮して、あまりキャッシュしない方が良いかも知れない。またある程度JNIが高速化してくれているらしい。マニュアル参照)。

(参考)Unity で Java プラグインを 使用する上でのヒント

 サンプルコードは「Assets/FantomLib/Scripts/Module/HardVolumeController.cs」の「VolumeUp()」「VolumeDown()」「VolumeMute()」「VolumeNow()」「volume プロパティ」「maxVolume プロパティ」あたりを見て欲しい。またプレファブ「Assets/FantomLib/Prefabs/System/HardVolumeControllerWithToast」にはトースト(Toast)で操作したときの音量を表示できるようにセットアップしてある(デモに使っているものと同じ)。少し改造すればVRにも応用できるだろう。



■ハードウェア音量操作のイベントを取得する [メディア音量のみ]


●Unity で Android のハードウェア音量操作のイベントを取得する(メディア音量のみ)
using FantomLib;

void OnEnable()
{
#if UNITY_ANDROID && !UNITY_EDITOR
//ハードウェア音量キーのリスナー登録
AndroidPlugin.HardKey.SetKeyVolumeUpListener(gameObject.name, "HardVolumeKeyChange", "VolumeUp");
AndroidPlugin.HardKey.SetKeyVolumeDownListener(gameObject.name, "HardVolumeKeyChange", "VolumeDown");
#endif
}

void OnDisable()
{
#if UNITY_ANDROID && !UNITY_EDITOR
//ハードウェア音量キーの全リスナー解除
AndroidPlugin.HardKey.RemoveAllListeners();
#endif
}

//ハードウェア音量キーのイベントハンドラ(Android ネイティブから呼ばれる)
void HardVolumeKeyChange(string message)
{
if (message == "VolumeUp")
{
if (OnHardVolumeKeyUp != null)
OnHardVolumeKeyUp.Invoke();
}
else if (message == "VolumeDown")
{
if (OnHardVolumeKeyDown != null)
OnHardVolumeKeyDown.Invoke();
}
}

●プラグイン内の対応する Java コード
import android.view.KeyEvent;
import com.unity3d.player.UnityPlayerActivity;
import static com.unity3d.player.UnityPlayer.UnitySendMessage;

public class HardVolKeyOnUnityPlayerActivity extends UnityPlayerActivity {
@Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
switch (event.getKeyCode()) {
case KeyEvent.KEYCODE_VOLUME_UP: //音量大キー
UnitySendMessage(callbackGameObjectUp, callbackMethodUp, returnValueUp);
break;
case KeyEvent.KEYCODE_VOLUME_DOWN: //音量小キー
UnitySendMessage(callbackGameObjectDown, callbackMethodDown, returnValueDown);
break;
}
return super.onKeyDown(keyCode, event);
}
}

 これは端末のハードウェア音量(メディア音量のみ)を操作したとき、Android → Unity へイベントメッセージを送るためのリスナー登録メソッドである。このリスナーの実装は「HardVolKeyOnUnityPlayerActivity」に書かれていて、簡単に言えばアクティビティのUIスレッドからイベントを検知したら Unity へ知らせるだけのものである。そのために元の「UnityPlayerActivity」(デフォルトでAndroidビルドで使用されている)をオーバーライドして使う必要がある(マニフェストファイルの「-HardVolKey」が付いているものに設定されている)

 受け取ったイベントは Unity 側で更に「OnHardVolumeKeyUp」「OnHardVolumeKeyDown」でコールバックされるが、これはインスペクタで登録できるようにしてあるので、元々ハードウェア音量キーが有効なアプリでは表示するメソッド(VolumeNow() 等)を登録し、VR(Cardboard/ハコスコ)など元はハードウェアキーが無効になっているアプリでは音量増減メソッド(VolumeUp()/VolumeDown())を登録すれば良い(Unity5.6.3p1 以前でビルドした場合。Unity5.6.3p2 以降だとハードウェア音量キーが効く→UIが出る。※どうやらバグだったらしい。Unity5.6.3p2のリリースノートにFixされたとあった。しかしかえってUIが出るのは良くない(笑))。VRでは2眼になるので showUI はオフにして、自前でUIを実装した方が良いだろう。

 サンプルコードは「Assets/FantomLib/Scripts/Module/HardVolumeController.cs」の「OnEnable()」「OnDisable()」「HardVolumeKeyChange()」あたりを見て欲しい。またプレファブ「Assets/FantomLib/Prefabs/System/HardVolumeControllerWithToast」にはトースト(Toast)で操作したときの音量を表示できるようにセットアップしてある(デモに使っているものと同じ)。少し改造すればVR(Cardboard/ハコスコ)にも応用できるだろう。

(動作確認した機器)





■ユニティちゃんと音声認識でじゃんけん(Android 版)

 せっかくなので、このプラグインを使って音声認識でじゃんけんゲームを作ってみた。音声認識の箇所はパッケージに入っている「Assets/_Test/Scripts/SpeechRecognizerTest.cs」をほぼそのまま使っている。具体的には、受け取った結果は改行("\n")で連結されてくるのでそれを分割し、それらの最初の1文字でグー、チョキ、パーを判定する処理(いくつか候補をデータ化してある)を追加しただけだ。

(※) Unity 2017.1.1f1 / Windows10(x64) でビルド。Galaxy S7 Edge (Android 7.0) で確認。

デモアプリのダウンロード (Google Drive を利用)


Android 4.2以上
※「提供元不明アプリのインストール」許可が必要です。


 じゃんけん自体のアルゴリズム・作成方法を知りたければ以下の書籍を参考にすると良いだろう。アイコンがそっくりなのは、実際にこの本を以前読んでいたためである(笑)(※アプリ内のアイコンは新たに作ったものです)。また音声認識は以下の本も参考にしている(※ただし少し古い。実際のビルドは AndroidStudio 2.3.3 でやった)。なのでこのサンプルは2つをミックスしただけとも言える(笑)。

(じゃんけん画像素材)
LiveMaker 指南番

(じゃんけんゲーム/音声認識参考)




 実際の Java コードは色々値をチェックしたり条件分岐したり、高速化のためにキャッシュしたり、StringBuilderを使ったりしているが、長くなるのでここでは簡略化したものを書いてあると考えて欲しい。Java を知っている人なら中身は非常に簡単なものばかりだとわかるだろう。AndroidStudio を使ったことあるなら AAR プラグインをビルドするのは簡単なので、挑戦してみるのも良いかも知れない。以下に参考資料を載せておこう。この2つの記事で基本的なプラグイン作成方法は十分だ(→実際にこれらプラグインはそのまんまの方法で作った(笑)。※AndroidStudio 2.3.3 でビルド)。

(参考)
Unity : Androidプラグインを作る一番簡単な方法
AndroidプラグインからUnityの関数を呼び出す - UnitySendMessage


(関連記事)
【Android】【Java】音声入力(音声認識)で文字列を取得する
【Android】【Java】AlertDialog を使ってみる


このコンテンツの一部には『ユニティちゃんライセンス』で許諾されたアセットが使用されています。
Portions of this work are licensed under Unity-Chan License



スポンサーサイト

category: Unity

thread: ゲーム開発

janre: コンピュータ

tag: Unityライブラリ  Unityプラグイン  Cardboard  VR 
tb: 0   cm: --

【AndroidStudio】AndroidStudio 2.x で DDMS を表示する  


 なんてことないことだけど、まぁ Android のネイティブは ADT(Eclipse)~ AndroidStudio 1.x の初期頃よくいじっていて、最近は触ってもいなかったわけですよ。で、久しぶりに最新版を触ると「DDMSどこやねん!」ってなったので(笑)、とりあえずメモ。VisualStudio なんかもそうだけど、ツールバーのデフォとかショートカットも変わっていくのは困るね。EclispeのショートカットをAndroidStudioに移行することもできるみたいだけど、結局はバージョンアップするたびに変わっていく可能性あるので、最近は常に最新のやり方に慣れる方法でやってる。インターネットのおかげでググれば大抵のことは出てくるしね。ただ英文も多いので日本語で簡単にまとめておくとイザというとき便利かと(笑)。

(※) Android Studio 2.3.3 / Windows10(x64) で確認



 AndroidStudio 2.x で DDMS を表示するには、大まかに以下のような流れになる。

1.ツールバーに「Android Device Monitor」を表示させる。

2.「Android Device Monitor」を起動して、「DDMS」のアイコンを押す。



 具体的には以下のようにする。

1.メニューから「File>Settings...」を開く。

2.左側の項目から「Appearance & Behavior>Menus and Toolbars」をクリックする(展開して選択)。

3.内容が表示されたら、「Main Toolbar>Android.Main.ToolBarActionGroup」(展開して探す)を選択し、ダイアログの右側にある「Add After...」を押す(※選択した位置の後ろにアイコンが追加される。「Add Separator」で縦線(区切り線)を入れることもできる。「Move Up/Down」で移動も可。位置は任意で良い)。


4.「Choose Actions To Add」のダイアログが開くので、「All Actions>Main menu>Tools>Android>Android Device Monitor」を選択し、「OK」を押す。


5.「Settings」のダイアログに戻ったら、下にある「OK」ボタンを押して適用する。


 これでツールバーに「Android Device Monitor」のアイコンが表示される。

 あとは「Android Device Monitor」を起動し、右上にある「DDMS」を押す。


 端末のファイルなどを見たい場合は、左ペインの「Device」で端末をクリックし、右ペインで「File Explorer」のタブを押せば、ファイルやディレクトリ構成が表示される。またファイルを選択し(アプリのデータなどは「data/data/[アプリのパッケージ名]/」以下に入っている)、「Pull」アイコンを押せばローカル(開発環境:PCなど)に落として中身を覗くこともできる。

(参考)
No shortcut for DDMS in Android Studio 2.2


(関連記事)
【Android】【Java】Preference 機能を使って設定データを読み込み・保存する
【Android】エミュレータでSDカードをマウントして使う方法 その2


category: Android

thread: プログラミング

janre: コンピュータ

tag: AndroidStudio 
tb: 0   cm: --

【Android】【Java】音声入力(音声認識)で文字列を取得する  


 いずれネイティブプラグイン作ったりするときのための布石。最近はVRでも音声入力できるものあるので Cardboard などでもできたら面白いかもね(Cardboard の場合、ハンディマイクでも付けない限り、構造的な問題が大きいが…)。とりあえずいつものように一番簡単にしたコードを書いておく。簡略のため細かいチェックなどは入れてないが、実際のアプリに使う場合は、色々と手を加えた方が良いだろう。


(※) Android Studio 2.3.3 / Windows10(x64) / Galaxy S7 Edge (Android 7.0) で確認

●簡単な構成

 例えばオブジェクト(ウィジェット)の構成が以下のようになっているものとする。配置などは任意で構わないが、とりあえず音声入力を開始するボタンと入力されたテキストを表示できるものがあれば良い。ここではそれぞれ ButtonTextView を使っている。


 仕様としては、「音声入力」のボタンを押したとき、音声入力のウィジェットが表示され入力開始→入力終了したらテキストビューに認識された文字列が列挙されるといった具合だ。その実装クラスを MainActivity とすると、コードは以下のようになる(テンプレートとしては「Empty Activity」を使っている)。

●音声入力された文字列を表示する
import android.content.Intent;
import android.os.Bundle;
import android.speech.RecognizerIntent;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.TextView;

import java.util.List;

public class MainActivity extends AppCompatActivity {

private TextView textDisplay;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

textDisplay = (TextView)findViewById(R.id.text_display);
}

public void onClick(View v) {
//音声入力がサポートされているか?
if (getPackageManager().queryIntentActivities(new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH), 0).size() == 0) {
Log.d("tag", "Voice Input is not supported.");
return; //サポートされてないとき無視
}

Intent intent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
startActivityForResult(intent, 0); //0:requestCode
}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == 0 && resultCode == RESULT_OK) {
if (data.hasExtra(RecognizerIntent.EXTRA_RESULTS)) {
//ここで認識された文字列を取得
List<String> results = data.getStringArrayListExtra(RecognizerIntent.EXTRA_RESULTS);
if (results.size() > 0) {
String str = "";
for (String s : results) {
str += s + "\n";
}
textDisplay.setText(str);
}
}
}
}
}

 ここではコードを減らすため、ボタンのクリック動作をレイアウトの「activity_main.xml」で「Button」の属性に「android:onClick="onClick"」を追加しているので注意。自分で実装するなら「View.OnClickListener」を implements し、ボタンに setOnClickListener() を付ける必要がある。

 また本来 onClick の引数の Viewid を調べて音声入力のボタンかどうかをチェックする必要もあるが、ここでは1つしかボタンがないので省略している。また、システムが音声入力をサポートしているかどうかもここで判別しているが、OnCreate() で判別して使用できなかったら、ボタンを無効にするのも良いだろう。

 ボタンを押下したときの動作は、音声入力の Intent を生成し startActivityForResult() でシステムに処理を委ねているだけである。音声が認識されたら、onActivityResult() で受信し、TextView に表示することになる。ここでの注意点は onActivityResult() は他の Intent のやり取りも含まれるので、予め音声入力用の requestCode を合わせておくことと、認識されたデータ(文字列)があるか否かというくらいだ。ここではデータがないときは単に無視している。

 この例では認識された文字列を拡張for文(foreach)で結合して TextView に表示しているだけだが、ここで各々の文字列によって処理を分岐すれば、音声認識でアクションを起こせるアプリが作れる。


(参考書籍)



(関連記事)
【Android】【Java】WIFI, Bluetooth 接続の暗黙的Intent
【Android】【Java】AlertDialog を使ってみる


category: Android

thread: プログラミング

janre: コンピュータ

tag: 音声認識 
tb: 0   cm: --


プロフィール

検索フォーム

全記事一覧

カテゴリ

ユーザータグ

最新記事

リンク

PR

▲ Pagetop