エンジニアの独り言

日常のことやプログラミングのことを気ままに書いています

AndroidでBluetooth デバイスを検出してみる

開発環境

パーミッション設定

AndroidBluetoothを使用するに当たり、AndroidManifest.xmlに以下の権限を追加。

    <!-- Bluetoothの機能を使用するための権限の宣言 -->
    <uses-permission android:name="android.permission.BLUETOOTH" />
    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />

    <!-- Bluetoothデバイスを検出するための権限の宣言 -->
    <uses-permission android:name="android.permission.BLUETOOTH_SCAN" />

    <!-- Bluetoothの機能を使用するための位置情報権限の宣言 -->
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>

Bluetoothバイス検出処理

MainActivityのレイアウトファイル activity_main

<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".Activity.MainActivity">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:weightSum="5"
        android:orientation="horizontal">


        <Space
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="1" />

        <Button
            android:id="@+id/scan_start_button"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="スキャン開始" />

        <Space
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="1" />

        <Button
            android:id="@+id/scan_stop_button"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="スキャン停止" />

        <Space
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="1" />

    </LinearLayout>

    <ListView
        android:id="@+id/DeviceListView"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</LinearLayout>

ListViewで使用するレイアウトファイル listitem_bluetoothdevice

<!--スキャンしたBluetoothデバイスをListviewに表示するときのitemレイアウト-->
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical">

    <TextView
        android:id="@+id/itemview_devicename"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textSize="24dp"/>

    <TextView
        android:id="@+id/itemview_deviceaddress"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textSize="12dp"/>
</LinearLayout>

ListViewへitem追加用クラス BluetoothDeviceListAdapter

package com.example.bluetoothsampleapplication.Common;

import android.app.Activity;
import android.bluetooth.BluetoothDevice;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;

import com.example.bluetoothsampleapplication.R;

import java.util.ArrayList;

public class BluetoothDeviceListAdapter extends BaseAdapter {

    private ArrayList<BluetoothDevice> mBluetoothDeviceList;
    private LayoutInflater mInflator;

    public BluetoothDeviceListAdapter( Activity activity )
    {
        super();
        mBluetoothDeviceList = new ArrayList<BluetoothDevice>();
        mInflator = activity.getLayoutInflater();
    }

    // リストへの追加
    public void addDevice( BluetoothDevice device )
    {
        if( !mBluetoothDeviceList.contains( device ) )
        {    // 加えられていなければ加える
            mBluetoothDeviceList.add( device );
            notifyDataSetChanged();    // ListViewの更新
        }
    }

    // リストのクリア
    public void clear()
    {
        mBluetoothDeviceList.clear();
        notifyDataSetChanged();    // ListViewの更新
    }

    @Override
    public int getCount() {
        return mBluetoothDeviceList.size();
    }

    @Override
    public Object getItem(int i) {
        return mBluetoothDeviceList.get(i);
    }

    @Override
    public long getItemId(int i) {
        return i;
    }


    static class ViewHolder
    {
        TextView deviceName;
        TextView deviceAddress;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent)
    {

        ViewHolder viewHolder;
        // General ListView optimization code.
        if( null == convertView )
        {
            //アイテム用レイアウトビューを取得
            convertView = mInflator.inflate( R.layout.listitem_bluetoothdevice, parent, false );
            viewHolder = new ViewHolder();
            viewHolder.deviceAddress = (TextView)convertView.findViewById( R.id.itemview_deviceaddress );
            viewHolder.deviceName = (TextView)convertView.findViewById( R.id.itemview_devicename );
            convertView.setTag( viewHolder );
        }
        else
        {
            viewHolder = (ViewHolder)convertView.getTag();
        }

        BluetoothDevice device     = mBluetoothDeviceList.get( position );
        String deviceName = device.getName();
        if( null != deviceName && 0 < deviceName.length() )
        {
            viewHolder.deviceName.setText( deviceName );
        }
        else
        {
            viewHolder.deviceName.setText( "Unknown device" );
        }
        viewHolder.deviceAddress.setText( device.getAddress() );

        return convertView;
    }
}

MainActivity

package com.example.bluetoothsampleapplication.Activity;

import static android.bluetooth.BluetoothAdapter.ACTION_DISCOVERY_FINISHED;
import static android.bluetooth.BluetoothAdapter.ACTION_DISCOVERY_STARTED;
import static android.bluetooth.BluetoothDevice.ACTION_FOUND;
import static android.bluetooth.BluetoothDevice.ACTION_NAME_CHANGED;

import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;

import android.Manifest;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.ListView;
import android.widget.Toast;

import com.example.bluetoothsampleapplication.Common.BluetoothDeviceListAdapter;
import com.example.bluetoothsampleapplication.R;

public class MainActivity extends AppCompatActivity {

    private BluetoothAdapter mBluetoothAdapter;// BluetoothAdapterオブジェクト
    private BluetoothDeviceListAdapter mBluetoothDeviceListAdapter;// リストビューの内容
    private boolean mScanning = false;                // スキャン中かどうかのフラグ

    private Button scan_start_button;//スキャン開始ボタン
    private Button scan_stop_button;//スキャン停止ボタン


    // ブロードキャストレシーバー
    private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver()
    {
        @Override
        public void onReceive(Context context, Intent intent )
        {
            String action = intent.getAction();
            BluetoothDevice device = intent.getParcelableExtra( BluetoothDevice.EXTRA_DEVICE );
            switch (action)
            {
                case ACTION_DISCOVERY_STARTED:
                    Log.d("MainActivity", "ACTION_DISCOVERY_STARTED");
                    break;

                case ACTION_FOUND://デバイスが見つかったら
                    Log.d("MainActivity", "ACTION_FOUND");
                    mBluetoothDeviceListAdapter.addDevice( device );
                    break;

                case ACTION_NAME_CHANGED:
                    Log.d("MainActivity", "ACTION_NAME_CHANGED");
                    break;

                case ACTION_DISCOVERY_FINISHED:
                    scan_start_button.setEnabled(true);//ボタンを有効化
                    scan_stop_button.setEnabled(false);//ボタンを無効化
                    Log.d("MainActivity", "ACTION_DISCOVERY_FINISHED");
                    // デバイス検出が終了した場合は、BroadcastReceiver を解除
                    context.unregisterReceiver(mBroadcastReceiver);
                    break;
            }
        }
    };


    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);//layout呼び出し

        // Bluetoothアダプタの取得
        BluetoothManager bluetoothManager = (BluetoothManager)getSystemService( Context.BLUETOOTH_SERVICE );
        mBluetoothAdapter = bluetoothManager.getAdapter();
        if( null == mBluetoothAdapter )
        {    // Android端末がBluetoothをサポートしていない
            Toast.makeText( this, "Bluetooth is not supported.", Toast.LENGTH_SHORT ).show();
            finish();    // アプリ終了宣言
            return;
        }

        //Android端末のBluetooth機能の有効化要求
        requestBluetoothFeature();

        //権限有効化要求(位置情報権限要求)
        if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.ACCESS_FINE_LOCATION,}, 1000);
            return;
        }

        //UI設定
        scan_start_button = findViewById(R.id.scan_start_button);
        scan_start_button.setOnClickListener(this::onClick);
        scan_stop_button = findViewById(R.id.scan_stop_button);
        scan_stop_button.setOnClickListener(this::onClick);
        scan_stop_button.setEnabled(false);//ボタンを無効化

        // リストビューの設定
        mBluetoothDeviceListAdapter = new BluetoothDeviceListAdapter( this ); // アダプターの初期化
        ListView listView = (ListView)findViewById( R.id.DeviceListView );    // リストビューの取得
        listView.setAdapter( mBluetoothDeviceListAdapter );    // リストビューにビューアダプターをセット

        // イベントフィルター設定 & ブロードキャストレシーバーの登録
        IntentFilter filter = new IntentFilter();
        filter.addAction(ACTION_DISCOVERY_STARTED);//Bluetoothスキャン開始検出
        filter.addAction(ACTION_FOUND);//Bluetoothデバイス検出
        filter.addAction(ACTION_DISCOVERY_FINISHED);//Bluetoothスキャン終了検出
        registerReceiver( mBroadcastReceiver, filter );


    }

    // 初回表示時、および、ポーズからの復帰時
    @Override
    protected void onResume()
    {
        super.onResume();
    }

    // 別のアクティビティ(か別のアプリ)に移行したことで、バックグラウンドに追いやられた時
    @Override
    protected void onPause() {
        super.onPause();
    }

    //Android端末のBluetooth機能の有効化要求
    private void requestBluetoothFeature()
    {
        //Android端末のBluetooth機能が有効になっている場合は何もしない
        if( mBluetoothAdapter.isEnabled() )
        {
            return;
        }
        //デバイスのBluetooth機能が有効になっていないときは、有効化要求(ダイアログ表示)
        Intent enableBtIntent = new Intent( BluetoothAdapter.ACTION_REQUEST_ENABLE );
        startActivity(enableBtIntent);
    }


    // スキャンの開始
    private void startScan()
    {
        // リストビューの内容を空にする。
        mBluetoothDeviceListAdapter.clear();

        // スキャンの開始
        mScanning = true;
        mBluetoothAdapter.startDiscovery();    // 約 12 秒間の問い合わせのスキャンが行われる
        Log.d("Bluetooth","スキャン開始");


        // メニューの更新
        invalidateOptionsMenu();
    }

    // スキャンの停止
    private void stopScan()
    {
        Log.d("Bluetooth","スキャン停止");
        // スキャンの停止
        mScanning = false;
        mBluetoothAdapter.cancelDiscovery();
    }


    public void onClick(View v)
    {
        switch (v.getId())
        {
            case R.id.scan_start_button:
                startScan();
                scan_start_button.setEnabled(false);//ボタンを無効化
                scan_stop_button.setEnabled(true);//ボタンを有効化
                break;

            case R.id.scan_stop_button:
                stopScan();
                scan_start_button.setEnabled(true);//ボタンを有効化
                scan_stop_button.setEnabled(false);//ボタンを無効化
                break;
        }

    }
}