見出し画像

Androidアプリ開発入門 (2) - RecognizerIntent

Androidアプリでの「RecognizerIntent」による「音声認識」の実装方法をまとめました。

・API 31: Android 12 (S)

前回

1. RecognizerIntent

Androidアプリで「音声認識」を実装するには、「RecognizerIntent」を使います。

・ACTION_RECOGNIZE_SPEECH : 音声認識
・ACTION_VOICE_SEARCH_HANDS_FREE : ハンズフリー音声認識
・ACTION_WEB_SEARCH : 音声認識によるWEB検索

2. AndroidManifest.xml

AndroidManifest.xml」に以下の項目を設定します。

・android.permission.RECORD_AUDIO : マイクのパーミッション
<uses-permission android:name="android.permission.RECORD_AUDIO" />​

ユーザー権限の許可には「PermissionsDispatcher」を使っています。

3. UI

今回は、「TextView」(id@label)と「Button」(id@button)を1つずつ配置します。

画像1

4. 音声認識

音声認識のコードは、次のとおりです。

package net.npaka.speechrecognizerex
import android.Manifest
import android.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.TextView
import android.widget.Toast
import android.speech.RecognizerIntent
import android.content.Intent
import android.view.View
import android.widget.Button
import permissions.dispatcher.*
import android.content.ActivityNotFoundException

@RuntimePermissions
class MainActivity : AppCompatActivity(), View.OnClickListener {
    // 音声認識
    private val REQUEST_RECOGNIZE_SPEECH = 100;

    // 参照
    private var label: TextView? = null
    private var button: Button? = null


//====================
// ライフサイクル
//====================
    // 起動時に呼ばれる
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        this.supportActionBar?.hide()

        // 参照
        this.label = findViewById(R.id.label)
        this.label!!.setText("")
        this.button = findViewById(R.id.button)
        this.button!!.setOnClickListener(this)
        setUiActive(false)

        // 音声認識の開始
        setupRecognizerWithPermissionCheck()
    }


//====================
// 操作
//====================
    // UIの有効化・無効化
    private fun setUiActive(active: Boolean) {
        runOnUiThread {
            this.button!!.isEnabled = active
        }
    }

    // テキストの指定
    private fun setText(text: String) {
        runOnUiThread {
            this.label!!.setText(text)
        }
    }

    // クリック時に呼ばれる
    override fun onClick(v: View) {
        // 音声認識の開始
        startRecognizer()
    }


//====================
// パーミッション
//====================
    // 許可された時に呼ばれる
    @NeedsPermission(Manifest.permission.RECORD_AUDIO)
    fun setupRecognizer() {
        setUiActive(true)
    }

    // 説明が必要な時に呼ばれる
    @OnShowRationale(Manifest.permission.RECORD_AUDIO)
    fun onCameraShowRationale(request: PermissionRequest) {
        AlertDialog.Builder(this)
            .setPositiveButton("許可") { _, _ -> request.proceed() }
            .setNegativeButton("許可しない") { _, _ -> request.cancel() }
            .setCancelable(false)
            .setMessage("マイクを利用します")
            .show()
    }

    // 拒否された時に呼ばれる
    @OnPermissionDenied(Manifest.permission.RECORD_AUDIO)
    fun onCameraPermissionDenied() {
        Toast.makeText(this, "拒否されました", Toast.LENGTH_SHORT).show()
    }

    // 「今後表示しない」が選択された時に呼ばれる
    @OnNeverAskAgain(Manifest.permission.RECORD_AUDIO)
    fun onCameraNeverAskAgain() {
        Toast.makeText(this, "「今後表示しない」が選択されました", Toast.LENGTH_SHORT).show()
    }


//====================
// 音声認識
//====================
    // 音声認識の開始
    private fun startRecognizer() {
        // RecognizerIntentの生成
        val intent = Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH)
        intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL, RecognizerIntent.LANGUAGE_MODEL_FREE_FORM)

        // アクティビティの起動
        try {
            startActivityForResult(intent, REQUEST_RECOGNIZE_SPEECH)
        } catch (e: ActivityNotFoundException) {
            println(e)
        }
    }

    // アクティビティ結果の取得
    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)
        when (requestCode) {
            // 音声認識結果の取得
            REQUEST_RECOGNIZE_SPEECH -> {
                if (resultCode == RESULT_OK && null != data) {
                    val text = data.getStringArrayListExtra(RecognizerIntent.EXTRA_RESULTS)!![0]
                    setText(text)
                }
            }
        }
    }
}

「音声認識を開始」ボタンを押すと、音声認識アクティビティが起動し、認識したテキストをアクティビティ結果として返し、画面に表示します。

画像2


ACTION_RECOGNIZE_SPEECH」のExtraは、次のとおりです。

・必須Extra
 ・EXTRA_LANGUAGE_MODEL

・オプションExtra
 ・EXTRA_PROMPT
 ・EXTRA_LANGUAGE
 ・EXTRA_MAX_RESULTS
 ・EXTRA_RESULTS_PENDINGINTENT
 ・EXTRA_RESULTS_PENDINGINTENT_BUNDLE

・結果Extra
 ・EXTRA_RESULTS

◎ EXTRA_LANGUAGE_MODEL
どの音声モデルを優先するかを指定します。 

・LANGUAGE_MODEL_FREE_FORM : 自由形式
・LANGUAGE_MODEL_WEB_SEARCH : WEB検索

◎ EXTRA_PROMPT
ユーザーに発話を求めるテキストを指定します(音声認識Activityで利用)。

◎ EXTRA_LANGUAGE
言語を「en-US」の形式で指定します。Locale.getDefault()の言語以外で音声認識を行う時に利用します。

◎ EXTRA_MAX_RESULTS
返される結果の最大数を指定します。

EXTRA_RESULTS_PENDINGINTENT
PendingIntentを指定します。

EXTRA_RESULTS_PENDINGINTENT_BUNDLE
PendingIntentに渡す追加のExtraを指定します。

5. ハンズフリー音声認識

ハンズフリー音声認識の使い方は調査中です。

◎ ハンズフリー音声認識アクティビティ
このアクティビティは、デバイスがセキュアモードでロックされている時に起動される場合があります。ハンズフリー中に実行される音声アクションがデバイスのセキュリティを危険にさらさないように、注意を払う必要があります。

ロック画面時にアクティビティのUIを表示するには、WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKEDを設定する必要があります。

ACTION_VOICE_SEARCH_HANDS_FREE」のExtraは、次のとおりです。

・オプションExtra
 ・EXTRA_SECURE

◎ EXTRA_SECURE
デバイスのセキュアモード時にハンズフリー音声認識が実行されたことを示すboll値です。セキュアモードの例は、画面ロックがアクティブで、ロック解除のために認証が必要な場合です。ロックされている場合、音声認識アクティビティは、許可される一連のアクションを制限するか、続行する前に何らかの認証を要求する必要があります。

6. WEB検索

音声認識によるWEB検索を行うには、startRecognizer()を以下のように書き換えます。

    // 音声認識によるWEB検索の開始
    private fun startRecognizer() {
        // RecognizerIntentの生成
        val intent = Intent(RecognizerIntent.ACTION_WEB_SEARCH)
        intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL, RecognizerIntent.LANGUAGE_MODEL_WEB_SEARCH)

        // アクティビティの起動
        try {
            startActivityForResult(intent, REQUEST_RECOGNIZE_SPEECH)
        } catch (e: ActivityNotFoundException) {
            println(e)
        }
    }

「音声認識を開始」ボタンを押すと、Googleアシスタントが起動し、WEB検索を行います。

画像3


ACTION_WEB_SEARCH」のExtraは、次のとおりです。

・必須Extra
 ・EXTRA_LANGUAGE_MODEL

・オプションExtra
 ・EXTRA_PROMPT
 ・EXTRA_LANGUAGE
 ・EXTRA_MAX_RESULTS
 ・EXTRA_PARTIAL_RESULTS
 ・EXTRA_WEB_SEARCH_ONLY
 ・EXTRA_ORIGIN

・結果Extra
 ・EXTRA_RESULTS
 ・EXTRA_CONFIDENCE_SCORES (オプション)

◎ EXTRA_WEB_SEARCH_ONLY
WEB検索のみを実行するかどうかを指定します。 デフォルトはfalseで、ユーザーのスピーチに応じて他の種別のアクションを実行できることを意味します。

◎ EXTRA_ORIGIN
音声が要求されたページのリファラーURLを指定します。Webブラウザは、特定のページでの音声の使用に、これを提供することを選択できます。

7. サポート言語の確認

サポート言語の確認するには、ACTION_GET_LANGUAGE_DETAILSのブロードキャストを投げます。

// サポート言語の確認の実行
val detailsIntent = Intent(RecognizerIntent.ACTION_GET_LANGUAGE_DETAILS)
detailsIntent.setPackage("com.google.android.googlequicksearchbox")
sendOrderedBroadcast(
    detailsIntent, null, LanguageDetailsChecker(),
    null, RESULT_OK, null, null)

// サポート言語の確認結果を取得するブロードキャストレシーバ
class LanguageDetailsChecker : BroadcastReceiver() {
    private var supportedLanguages: List<String>? = null
    private var languagePreference: String? = null
    override fun onReceive(context: Context, intent: Intent) {
        val results = getResultExtras(true)

        // 言語
        if (results.containsKey(RecognizerIntent.EXTRA_LANGUAGE_PREFERENCE)) {
            languagePreference = results.getString(RecognizerIntent.EXTRA_LANGUAGE_PREFERENCE)
            print("languagePreference : "+languagePreference+"\n")
        }

        // サポート言語
        if (results.containsKey(RecognizerIntent.EXTRA_SUPPORTED_LANGUAGES)) {
            supportedLanguages = results.getStringArrayList(
               RecognizerIntent.EXTRA_SUPPORTED_LANGUAGES
            )
            print("supportedLanguages : "+supportedLanguages+"\n")
        }
    }
}
languagePreference : ja-JP
supportedLanguages : [af-ZA, az-AZ, id-ID, ms-MY, jv-ID, su-ID, ca-ES, cs-CZ, da-DK, de-DE, de-AT, et-EE, en-AU, en-CA, en-001, en-GH, en-IN, en-IE, en-KE, en-NZ, en-NG, en-PH, en-SG, en-ZA, en-TZ, en-GB, en-US, es-AR, es-BO, es-CL, es-CO, es-CR, es-EC, es-US, es-SV, es-ES, es-GT, es-HN, es-MX, es-NI, es-PA, es-PY, es-PE, es-PR, es-DO, es-UY, es-VE, eu-ES, fil-PH, fr-FR, fr-CA, gl-ES, hr-HR, zu-ZA, is-IS, it-IT, sw, sw-TZ, lv-LV, lt-LT, hu-HU, nl-NL, nb-NO, uz-UZ, pl-PL, pt-BR, pt-PT, ro-RO, sl-SI, sk-SK, fi-FI, sv-SE, vi-VN, tr-TR, el-GR, bg-BG, ru-RU, sr-RS, uk-UA, ka-GE, hy-AM, he-IL, ar-IL, ar-JO, ar-AE, ar-BH, ar-DZ, ar-SA, ar-KW, ar-MA, ar-TN, ar-OM, ar-PS, ar-QA, ar-LB, ar-EG, fa-IR, ur-PK, ur-IN, am-ET, hi-IN, ta-IN, ta-LK, ta-SG, ta-MY, bn-BD, bn-IN, km-KH, kn-IN, mr-IN, gu-IN, si-LK, te-IN, ml-IN, ne-NP, lo-LA, th-TH, my-MM, ko-KR, cmn-Hans-CN, cmn-Hans-HK, cmn-Hant-TW, yue-Hant-HK, ja-JP, en-ID, en-TH]

次回



この記事が気に入ったらサポートをしてみませんか?