2011年9月6日火曜日

Bluetoothで通信を行う(2)

今回は、その次の段階として、Bluetoothに接続可能なデバイスを検出し、リストにして並べるところまでを解説したいと思います。

デバイス検出時のポイントとしては、

接続履歴のあるデバイス情報を取得する場合
接続したことのないデバイスを検出する場合
の2パターンが存在することです。

今回のサンプルコードでは、DeviceListActivityを新しく作成し、前回作成・解説したBlueToothSampleのActivityから、Bluetoothの設定がONになるタイミングで画面遷移させます。

画面遷移と同時に、接続履歴のあるデバイス情報を取得してリストに表示させ、オプションメニューにて、接続可能なデバイスを検出し、別途リストに表示させるようにします。
それでは続きをどうぞ、、、


接続したことのあるデバイスの情報を取得する
それではまず、接続履歴のある(ペアになったことのある)デバイス情報(デバイス名とMACアドレス)を取得してリストに並べます。ソースコードは以下の通りです。

ポイントは、getBondedDevices()メソッドを用いて、BluetoothAdapterから接続履歴のあるデバイスのセットを取得するところです(4行目)。

DeviceListActivity.java
//接続履歴のあるデバイスを取得
pairedDeviceAdapter = new ArrayAdapter(this, R.layout.rowdata);
//BluetoothAdapterから、接続履歴のあるデバイスの情報を取得
Set pairedDevices = mBtAdapter.getBondedDevices();
if(pairedDevices.size() > 0){
//接続履歴のあるデバイスが存在する
for(BluetoothDevice device:pairedDevices){
//接続履歴のあるデバイスの情報を順に取得してアダプタに詰める
//getName()・・・デバイス名取得メソッド
//getAddress()・・・デバイスのMACアドレス取得メソッド
pairedDeviceAdapter.add(device.getName() + "\n" + device.getAddress());
}
ListView deviceList = (ListView)findViewById(R.id.pairedDeviceList);
deviceList.setAdapter(pairedDeviceAdapter);
}

前回、BluetoothAdapterのインスタンスを取得しました(Bluetoothで通信を行う(1)参照)。一度ペアになったことのあるデバイスの基本情報(デバイス名、MACアドレスなど)は、システムのBluetoothAdapterに保持されます。そのため、BluetoothAdapterのインスタンスから、接続履歴のあるデバイスの情報を取得することが可能です(4行目)。

接続可能なデバイスを検出する

次に、接続可能なデバイスを取得し、リストに表示してみます(オプションメニューとして実装)。

ポイントは、接続可能なデバイスが検出された際に、検出対象のデバイスから暗黙的Intentが発行され、それをBroadcastReceiverで受け取るところです。

なお、オプションメニューの実装方法は、「オプションメニュー(OptionsMenu)を追加する」を参照してください。

DeviceListActivity.java
@Override
public boolean onOptionsItemSelected(MenuItem item) {
//オプションメニューが選択された時の処理
TextView nonPairedListTitle = (TextView)findViewById(R.id.nonPairedListTitle);
nonPairedListTitle.setText("接続履歴なしデバイス一覧");

if(item.getItemId() == Menu.FIRST){
//インテントフィルターとBroadcastReceiverの登録
IntentFilter filter = new IntentFilter();
filter.addAction(ACTION_DISCOVERY_STARTED);
filter.addAction(ACTION_FOUND);
filter.addAction(ACTION_NAME_CHANGED);
filter.addAction(ACTION_DISCOVERY_FINISHED);
registerReceiver(DevieFoundReceiver, filter);

nonPairedDeviceAdapter = new ArrayAdapter(this, R.layout.rowdata);
//接続可能なデバイスを検出
if(mBtAdapter.isDiscovering()){
//検索中の場合は検出をキャンセルする
mBtAdapter.cancelDiscovery();
}
//デバイスを検索する
//一定時間の間検出を行う
mBtAdapter.startDiscovery();
}
return false;
}
オプションメニューが押されたとき、まず検出されたデバイスからの暗黙的Intentを受け取るためのBroadcastReceiverを登録します。

次に、デバイスの検出を開始するために、BluetoothAdapterのstartDiscovery()メソッドを使用します(24行目)。このとき、もし既にデバイスを検出中だった場合には、一旦検出を中断してから、再度開始するようにします。

なお、インテントフィルターに設定した各種アクションは、それぞれ以下のタイミングでブロードキャストされます。

ACTION_DISCOVERY_STARTED デバイス検出の開始時
ACTION_FOUND デバイス検出時
ACTION_NAME_CHANGED デバイス名の判明時(新規検出時)
ACTION_DISCOVERY_FINISHED デバイス検出終了時
最後に、BroadcastReceiverの中身を記述します。

BroadcastReceiverでは主に、検出されたデバイスからのブロードキャストを受け、そのデバイス名とMACアドレスを取得してリストを生成します。
注意しないといけないのは、ここでのデバイス検出の際に、接続履歴のあるデバイスが近くにあった場合には、それも検出してしまうことです。接続履歴のあるデバイス情報はすでに取得済みのため、ここでは必要ありません。そのため、16〜20行目にて、検出されたデバイスが既に接続履歴のあるデバイスだった場合には、リストアダプタに詰めないようにします。

DeviceListActivity.java
private final BroadcastReceiver DevieFoundReceiver = new BroadcastReceiver(){
//検出されたデバイスからのブロードキャストを受ける
@Override
public void onReceive(Context context, Intent intent){
String action = intent.getAction();
String dName = null;
BluetoothDevice foundDevice;
ListView nonpairedList = (ListView)findViewById(R.id.nonPairedDeviceList);;
if(ACTION_DISCOVERY_STARTED.equals(action)){
Log("スキャン開始");
}
if(ACTION_FOUND.equals(action)){
//デバイスが検出された
foundDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
if((dName = foundDevice.getName()) != null){
if(foundDevice.getBondState() != BluetoothDevice.BOND_BONDED){
//接続したことのないデバイスのみアダプタに詰める
<span style="color: #000000;">nonPairedDeviceAdapter.add(dName + "\n" + foundDevice.getAddress());</span>
Log.d("ACTION_FOUND", dName);
}
}
nonpairedList.setAdapter(nonPairedDeviceAdapter);
}
if(ACTION_NAME_CHANGED.equals(action)){
//名前が検出された
Log.d("ACTION_NAME_CHANGED", dName);
foundDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
if(foundDevice.getBondState() != BluetoothDevice.BOND_BONDED){
//接続したことのないデバイスのみアダプタに詰める
nonPairedDeviceAdapter.add(dName + "\n" + foundDevice.getAddress());
}
nonpairedList.setAdapter(nonPairedDeviceAdapter);
}

if(ACTION_DISCOVERY_FINISHED.equals(action)){
Log("スキャン終了");
}
}
};
接続履歴の有無を判断する際に使用するgetBondState()メソッドは、検出されたデバイスの接続状態を取得します。接続状態は次の3つが存在します。

BluetoothDevice.BOND_BONDING
デバイスは接続中
BluetoothDevice.BOND_BONDED
デバイスは接続履歴あり
BluetoothDevice.BOND_NONE
デバイスは接続履歴なし
今回はBOND_BONDEDを用いて、接続履歴の有無を判断しています。

また、ポイントとして、初めて検出されたデバイスの場合は、デバイス検出時にデバイス名が取得できていない場合があります。そのときのために、デバイス名が検出された時点でリストに登録するようにします(24〜33行目)。

以上で、接続可能なデバイス一覧を作成することができました。

自デバイスの検出を有効にする

最後に、自分のデバイスを他のデバイスから認識できるようにします。

・デバイスの設定→無線とネットワーク→Bluetooth設定→検出可能にチェックを入れる

としても同様の処理ができますが、アプリ同士で検出し合いたいときにわざわざ設定画面を開いていては面倒です。なので今回は、アプリ側でその設定を有効にしてみます。

実装箇所は、(少し解説が前後してしまいますが)先ほどのオプションメニューの中に記述することにします。オプションメニューが選択された際に、自デバイスの検出を有効にしつつ、他のデバイスの検出を開始します。
@Override
public boolean onOptionsItemSelected(MenuItem item) {
//オプションメニューが選択された時の処理
//自デバイスの検出を有効にする
Intent discoverableOn = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
discoverdOn.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300);
startActivity(discoverableIntent);


省略(Broadcastの登録と接続可能デバイスの検出)

}
6行目で、検出有効時間を300秒間に設定しています。特に検出時間を指定しない場合は、初期設定で120秒に設定されています。

これで上図のようなダイアログが表示され、「はい」を選択することで自デバイスの検出が一定時間有効になります。

0 件のコメント:

コメントを投稿