Android ブロードキャストレシーバーを利用してヘッドフォンの接続を検知する

英単語ロイド ってゆーアプリをつくり、英単語の読み上げを行う機能をつけていたのですが、本体をマナーモードにすると、ヘッドフォンを差しても、ヘッドフォン自体マナーモードになってしまい、音声が聞こえないという残念な作りになっていたので修正しようと思います。

 

で、AudioManager クラスをみると、isWiredHeadsetOn() というメソッドがあるので、、、

if (audioManager.isWiredHeadsetOn() || audioManager.getRingerMode() == AudioManager.RINGER_MODE_NORMAL) {
    WordBook.Word w = getCurrentWord();
    if (w != null) {
        tts.speak(w.getKeyword(), TextToSpeech.QUEUE_FLUSH, null);
    }
}

これで、終わりやん。

 

と思って動作させてみましたが、どうもきちんと動作しない。。。

よく見たら、deprecatred でした。

 

ちょっと調べると、どうも BroadcastReceiver 使わないといけないみたいですね。

 

ということで、以下手順。

 

1.BroadcastReceiver クラスを作成する

Intent.ACTION_HEADSET_PLUG を受け取ります。Bluetooth のヘッドセットだったらどうなのよ?とか思いますが、持ってないし、今回は気にしないことに。

ブロードキャストされる、Intent の内容に、state、name、microphone があり、それぞれ、ヘッドセットのプラグが挿入されているか、ヘッドセットのタイプ、マイク付きか?を表すようです。

state は、SDK Reference には、0:挿入されていない、1:挿入されている と書いていますが、思ったように動かないので確認すると、”2” が返ってきていました。

どうも、1:挿入されている(マイク付き)、2:挿入されている(マイクなし) のような感じでしょうか。 state == 1 ではなく、state > 0 で判定しときます。

あと、結果の参照を、メンバーに持たせちゃったんですが、同期とかどうなのかな~

Activity から、登録すると、Activity スレッドで動作するようなので、問題ないのかと思います。

package info.typea.eitangoroid;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;

/**
 * ヘッドセットの状態を取得するブロードキャストレシーバー
 * @author piroto
 */
public class HeadsetStateReceiver extends BroadcastReceiver {
    private boolean isPlugged = false;
    private String headsetType = null;
    private boolean isMicrophone = false;
    
    @Override
    public void onReceive(Context context, Intent intent) {
        if (Intent.ACTION_HEADSET_PLUG.equals(intent.getAction())) {
            
            // http://developer.android.com/reference/android/content/Intent.html#ACTION_HEADSET_PLUG
            // http://stackoverflow.com/questions/7590644/action-headset-plug-not-documented-extra-strange-state
            
            int state = intent.getIntExtra("state", 0);
            setPlugged((state > 0)); // 0:unplugged,1:headset with microphone,2:a headset with no microphone
            
            String name = intent.getStringExtra("name");
            setHeadsetType(name);
            
            int microphone  = intent.getIntExtra("microphone", 0);
            setMicrophone((microphone == 1));
        }
    }

    public boolean isPlugged() {
        return isPlugged;
    }

    public void setPlugged(boolean isPlugged) {
        this.isPlugged = isPlugged;
    }

    public String getHeadsetType() {
        return headsetType;
    }

    public void setHeadsetType(String headsetType) {
        this.headsetType = headsetType;
    }

    public boolean isMicrophone() {
        return isMicrophone;
    }

    public void setMicrophone(boolean isMicrophone) {
        this.isMicrophone = isMicrophone;
    }
}

2.AndroidManifest にレシーバーを宣言

上記で作成した、レシーバーを宣言します。

<receiver android:name="info.typea.eitangoroid.HeadsetStateReceiver">
    <intent-filter>
        <action android:name="android.intent.action.HEADSET_PLUG"/>
    </intent-filter>
</receiver>

3.Activity から利用する準備

Activity のメンバーとして宣言して、生成、登録、登録解除を行います。

さて、このての登録、登録解除は、Activity ライフサイクルのどこらへんでやるのが適当なのだろう?とちょいと考えましたが、Activity ライフサイクルの記述 に、

you can register a BroadcastReceiver in onStart() to monitor for changes that impact your UI, and unregister it in onStop()  when the user an no longer see what you are displaying.

てな記述があったので、以下のようにしました。

    private HeadsetStateReceiver headsetStateReceiver = null;
    
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        headsetStateReceiver = new HeadsetStateReceiver();
    }

    @Override
    protected void onStart() {
        super.onStart();
        IntentFilter filter = new IntentFilter();
        filter.addAction(Intent.ACTION_HEADSET_PLUG);
        registerReceiver(headsetStateReceiver, filter);
    }

    @Override
    protected void onStop() {
        unregisterReceiver(headsetStateReceiver);
        super.onStop();
    }

 

4.実際の利用

でようやく利用可能になりましたので、謹んで、以下のように利用させていただきます。

/**
 * 発話
 */
public void speak() {
    if (headsetStateReceiver.isPlugged() || audioManager.getRingerMode() == AudioManager.RINGER_MODE_NORMAL) {
        WordBook.Word w = getCurrentWord();
        if (w != null) {
            tts.speak(w.getKeyword(), TextToSpeech.QUEUE_FLUSH, null);
        }
    }
}

 

最初の isWiredHeadsetOn() 追加だけで対応できれば、楽だったんですがねー。

まぁ、BroadcastReceiver使ったことなかったので、今後同様のやりかたで、ブロードキャストなIntent を取り扱えそうなので勉強になりました。なんかいろいろおいしそうな通知が並んでいるし。

また一歩野望に近づいた!