読者です 読者をやめる 読者になる 読者になる

WonderPlanet DEVELOPER BLOG

ワンダープラネットの開発者ブログです。モバイルゲーム開発情報を発信。

AndroidでBluetooth対応デバイスを検索しよう

Android Cocos2d-x Java

今回のエンジニアブログを担当する大原です。

以前のブログでiOSのMFi Game Controllersが紹介されておりましたが、
Androidでゲームコントローラーを使う場合、Bluetoothデバイスになるかと思います。
そこで今回は、AndroidにおけるBluetoothについて書こうと思います。

Bluetoothでの通信を行うためには、まず相手のデバイスを探さないと行けません。
今回は、その相手のデバイスを探すためのコードを書いていきたいと思います。

まずは、AndroidManifest.xmlにBluetoothの通信を行うためのpermissionを追加します。

<?xml version="1.0" encoding="utf-8"?>  
<manifest xmlns:android="http://schemas.android.com/apk/res/android"  
      package="jp.wonderplanet.blog"  
      android:versionCode="4"  
      android:versionName="1.1.0">  
  
    <uses-sdk android:minSdkVersion="10"/>  
    <uses-feature android:glEsVersion="0x00020000" />  
  
    <uses-permission android:name="android.permission.BLUETOOTH" />  
    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />

android.permission.BLUETOOTHとandroid.permission.BLUETOOTH_ADMINを追加します。
これでBluetoothの通信をする準備ができました。

メインのActivityのjavaファイルにBluetoothのONとペアリングさせるための実装を書き加えていきます。

public class blog_BT extends Cocos2dxActivity {  
    //ブルートゥースを扱うためのアダプタークラス  
    private final BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();  
    private static final int REQUEST_DISCOVERABLE_BT = 5000;  
    private static final int WAIT_TIME = 120;  
    private static ArrayList mCandidateServers;  
  
    //cocos2dにコールバック  
    //ペアリング待ち  
    public native static void callBackPairing();  
    //デバイス検索結果  
    public native static void callBackEndSearch(String[] devices);  

Bluetoothの接続や、BluetoothをONにするために、BluetoothAdapter.getDefaultAdapter()で
デフォルトのアダプタを保持しておきます。

   private static blog_BT _activity = null;  
    protected void onCreate(Bundle savedInstanceState) {  
        super.onCreate(savedInstanceState);  
  
        mCandidateServers = new ArrayList();  
  
        if (_activity == null) _activity = this;  
  
    }  
  
    static {  
         System.loadLibrary("game");  
    }  
  
    public static blog_BT getActivity() {  
        return _activity;  
    }  
  
    /**  
     * cocos2dからブルートゥースをONにする  
     */  
    public static void activeBluetooth() {  
        getActivity().onActiveBluetooth();  
    }  
    /**  
     * ブルートゥースをONにする  
     */  
    public void onActiveBluetooth() {  
        if (BluetoothAdapter.getDefaultAdapter() == null) {  
            // Bluetoothはサポートされていない  
            return;  
        }  
  
        if (!mBluetoothAdapter.isEnabled()) {  
            //ブルートゥースをONにする  
            mBluetoothAdapter.enable();  
        }  
    }  

BluetoothがOFFの場合、mBluetoothAdapter.enable()でONにすることができます。

    /**  
     * cocos2dからペアリング待ちを行う  
     */  
    public static void pairingBluetooth() {  
        getActivity().onPairingBluetooth();  
    }  
    /**  
     * ペアリング待ちを行う  
     */  
    public void onPairingBluetooth() {  
        Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);  
        intent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, WAIT_TIME);  
        getActivity().startActivityForResult(intent, REQUEST_DISCOVERABLE_BT);  
    }  
    /**  
     * ペアリング待ちの準備OK問い合わせ  
     */  
    public void onActivityResult(int requestCode, int resultCode, Intent data) {  
        if (requestCode == REQUEST_DISCOVERABLE_BT) {  
            if (resultCode == WAIT_TIME) {  
                callBackPairing();  
            }  
        }  
    }  

BluetoothAdapter.ACTION_REQUEST_DISCOVERABLEを呼び出すことで、
Bluetoothの検出許可にしてよいかのダイアログが表示されます。
また、WAIT_TIMEを指定することで、検出許可の時間を指定することができます。
ダイアログでOKを押された場合、onActivityResultで指定したWAIT_TIMEが戻ってきます。

    /**  
     * cocos2dからデバイスをSearchさせる  
     */  
    public static void searchDevice() {  
        getActivity().onSearchDevice();  
    }  
    /**  
     * デバイスをSearchさせる  
     */  
    public void onSearchDevice() {  
        mCandidateServers.clear();  
  
        IntentFilter filter = new IntentFilter();  
        filter.addAction(BluetoothDevice.ACTION_FOUND);  
        filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);  
        getActivity().registerReceiver(mReceiver, filter);  
        mBluetoothAdapter.startDiscovery();  
    }  
    /**  
    * デバイスの検索  
    */  
    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {  
        @Override  
        public void onReceive(Context context, Intent intent) {  
            String action = intent.getAction();  
            if (BluetoothDevice.ACTION_FOUND.equals(action)) {  
                BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);  
                // 名前とアドレスをリストに格納  
                mCandidateServers.add(device.getName() + "\n" + device.getAddress());  
            }  
            else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) {  
                context.unregisterReceiver(mReceiver);  
                //デバイス検索の終了  
                String[] ret = null;  
                ret = mCandidateServers.toArray(new String[mCandidateServers.size()]);  
                callBackEndSearch(ret);  
            }  
        }  
    };  
}  

BroadcastReceiverのonReceiveで検出できたデバイス名とデバイスのアドレスを取得することができます。
検出が終了するまで、何度もonReceiveは呼ばれます。

あとは、いつもの様にJNI経由でcocos2d-xに戻してあげます。

BluetoothJni.h

#ifndef __BLUETOOTH_JNI_H__  
#define __BLUETOOTH_JNI_H__  
  
#include "BluetoothWrapper.h"  
  
#ifdef __cplusplus  
extern "C" {  
#endif  
  
    extern void activeBluetoothJni();  
    extern void pairingBluetoothJni(BluetoothWrapper* instance);  
    extern void searchDeviceJni(BluetoothWrapper* instance);  
    // androidから呼ばれるコールバック関数  
    JNIEXPORT void JNICALL Java_jp_wonderplanet_blog_blog_1BT_callBackPairing(JNIEnv* env, jobject thiz);  
    JNIEXPORT void JNICALL Java_jp_wonderplanet_blog_blog_1BT_callBackEndSearch(JNIEnv* env, jobject thiz,jobjectArray inJNIArray);  
  
#ifdef __cplusplus  
}  
#endif  
  
#endif  

BluetoothJni.cpp

#include "BluetoothJni.h"  
#include "platform/android/jni/JniHelper.h"  
  
#define  CLASS_NAME "jp/wonderplanet/blog/blog_BT"  
  
#ifdef __cplusplus  
extern "C" {  
#endif  
  
    static BluetoothWrapper* __instance;  
  
    /**  
    * ブルートゥースをONにする  
    */  
    void activeBluetoothJni()  
    {  
        JniMethodInfo methodInfo;  
  
        if (! JniHelper::getStaticMethodInfo(methodInfo, CLASS_NAME, "activeBluetooth", "()V"))  
        {  
            return;  
        }  
        methodInfo.env->CallStaticVoidMethod(methodInfo.classID, methodInfo.methodID);  
        methodInfo.env->DeleteLocalRef(methodInfo.classID);  
    }  
  
    /**  
    * ブルートゥースでペアリング待ち  
    */  
    void pairingBluetoothJni(BluetoothWrapper* instance)  
    {  
        JniMethodInfo methodInfo;  
  
        __instance = instance;  
        __instance->retain();  
  
        if (! JniHelper::getStaticMethodInfo(methodInfo, CLASS_NAME, "pairingBluetooth", "()V"))  
        {  
            return;  
        }  
        methodInfo.env->CallStaticVoidMethod(methodInfo.classID, methodInfo.methodID);  
        methodInfo.env->DeleteLocalRef(methodInfo.classID);  
    }  
  
    /**  
    * デバイス検索  
    */  
    void searchDeviceJni(BluetoothWrapper* instance)  
    {  
        JniMethodInfo methodInfo;  
  
        __instance = instance;  
        __instance->retain();  
  
        if (! JniHelper::getStaticMethodInfo(methodInfo, CLASS_NAME, "searchDevice", "()V"))  
        {  
            return;  
        }  
        methodInfo.env->CallStaticVoidMethod(methodInfo.classID, methodInfo.methodID);  
        methodInfo.env->DeleteLocalRef(methodInfo.classID);  
    }  
  
    // androidから呼ばれるコールバック関数  
    JNIEXPORT void JNICALL Java_jp_wonderplanet_blog_blog_1BT_callBackPairing(JNIEnv* env, jobject thiz)  
    {  
        __instance->callBackPairing();  
        __instance->release();  
    }  
  
    JNIEXPORT void JNICALL Java_jp_wonderplanet_blog_blog_1BT_callBackEndSearch(JNIEnv* env, jobject thiz,jobjectArray inJNIArray)  
    {  
        std::vector ret;  
  
        jsize length = env->GetArrayLength(inJNIArray);  
        for (int i = 0; i < length; i++) {  
            jstring jstr = (jstring)env->GetObjectArrayElement( inJNIArray, i);  
            const char *inCStr = env->GetStringUTFChars( jstr, NULL);  
            if (NULL == inCStr) return;  
  
            ret.push_back(std::string(inCStr));  
        }  
  
        __instance->callBackEndSearch(ret);  
        __instance->release();  
    }  
  
#ifdef __cplusplus  
}  
#endif  

BluetoothWrapper.h

#ifndef blog_BT_BluetoothWrapper_h  
#define blog_BT_BluetoothWrapper_h  
  
#include "cocos2d.h"  
  
using namespace cocos2d;  
  
typedef void (CCObject::*SEL_DeviceList)(std::vector);  
  
#define bt_selector_deviceList(_SELECTOR) (SEL_DeviceList)(&_SELECTOR)  
  
class BluetoothWrapper : public CCObject  
{  
private:  
    CCObject* m_pListener;  
    SEL_CallFunc m_callbackPairing;  
    SEL_DeviceList m_callbackDevice;  
  
public:  
    BluetoothWrapper();  
    ~BluetoothWrapper();  
  
    /**  
     * インスタンスを取得  
     * @return 本クラスの唯一のインスタンス  
     */  
    static BluetoothWrapper* sharedInstance();  
  
    /**  
     * Bluetooth ON  
     */  
    void activeBluetooth();  
    /**  
     * ペアリング  
     */  
    void pairingBluetooth(CCObject* target, SEL_CallFunc callback);  
    void callBackPairing();  
    /**  
    * デバイスSearch  
    */  
    void searchDevice(CCObject* target, SEL_DeviceList callback);  
    void callBackEndSearch( std::vector< std::string > list );  
}  

BluetoothWrapper.cpp

#include "BluetoothWrapper.h"  
#include "BluetoothJni.h"  
  
    static BluetoothWrapper *_pInstance;  
  
    /**  
     * コンストラクタ  
     */  
    BluetoothWrapper::BluetoothWrapper()  
    {  
    }  
  
    /**  
     * デストラクタ  
     */  
    BluetoothWrapper::~BluetoothWrapper()  
    {  
    }  
  
    /**  
     * インスタンスを取得  
     * @return 本クラスの唯一のインスタンス  
     */  
    BluetoothWrapper* BluetoothWrapper::sharedInstance()  
    {  
        if (! _pInstance)  
        {  
            _pInstance = new BluetoothWrapper();  
        }  
  
        return _pInstance;  
    }  
  
    /**  
     * Bluetooth ON  
     */  
    void BluetoothWrapper::activeBluetooth()  
    {  
        activeBluetoothJni();  
    }  
    /**  
     * ペアリング  
     */  
    void BluetoothWrapper::pairingBluetooth(CCObject* target, SEL_CallFunc callback)  
    {  
        m_pListener = target;  
        m_pListener->retain();  
  
        m_callbackPairing = callback;  
  
        // JNIへ処理  
        pairingBluetoothJni(this);  
    }  
    void BluetoothWrapper::callBackPairing()  
    {  
        if (m_pListener)  
        {  
            (m_pListener->*m_callbackPairing)();  
            m_pListener->release();  
        }  
    }  
    /**  
    * デバイスSearch  
    */  
    void BluetoothWrapper::searchDevice(CCObject* target, SEL_DeviceList callback)  
    {  
        m_pListener = target;  
        m_pListener->retain();  
  
        m_callbackDevice = callback;  
  
        searchDeviceJni(this);  
    }  
    void BluetoothWrapper::callBackEndSearch( std::vector< std::string > list )  
    {  
        if (m_pListener)  
        {  
            (m_pListener->*m_callbackDevice)( list );  
            m_pListener->release();  
        }  
    }

これで、cocos2d-xからBluetoothのデバイス検索ができるようになりました。
次回は、実際のBluetoothの接続と通信ができたらと思います。