Bluetoothでチャットする

別スレからUIをいじれないので
100ミリ秒おきにポーリングするという謎実装になった。(static String global_charっていう文字列を用意)
リソース気にしなければこれでいいと思うんだけど。
ハンドラ周りを勉強しなければ。

MainActivity.java

package com.example.android.btrev;

import java.text.SimpleDateFormat;
import java.util.Date;

import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.Window;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ListView;
import android.widget.TextView;

public class MainActivity extends Activity {
	private Bt mBt;
	private ArrayAdapter<String> mCandidateServers;
	private ArrayAdapter<String> mServers;
	public TextView tvRcv;
	Button btnSnd;
	SimpleDateFormat sdf;
	static String global_char = "";
	
	Handler mHandler = new Handler();
	Runnable runnable = new Runnable(){
		public void run(){
			tvRcv.setText(global_char);
			mHandler.postDelayed(runnable, 100);
		}
	};
	
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		sdf = new SimpleDateFormat("k:m:s:S");
		requestWindowFeature(Window.FEATURE_NO_TITLE);
		mCandidateServers = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1);
		mServers = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1);
		mBt = new Bt(this, mCandidateServers, mServers);
		setContentView(R.layout.activity_main);
		tvRcv = (TextView) findViewById(R.id.tvRcv);
		btnSnd = (Button) findViewById(R.id.btnSnd);
		btnSnd.setOnClickListener(new View.OnClickListener() {
			@Override
			public void onClick(View v) {
				Date date = new Date();
				String currentTime = sdf.format(date);
				mBt.sendMessage(currentTime);
				runnable.run();
			}
		});
	}

	@Override
	protected void onResume() {
		super.onResume();
		mBt.turnOn();
	}

	@Override
	protected void onPause() {
		super.onPause();
		mBt.cancel();
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		getMenuInflater().inflate(R.menu.activity_main, menu);
		return true;
	}

	@Override
	public boolean onMenuItemSelected(int featureId, MenuItem item) {
		int itemId = item.getItemId();
		if (itemId == R.id.menu_discoverable) {
			mBt.startDiscoverable();
		} else if (itemId == R.id.menu_start_server) {
			mBt.startServer();
		} else if (itemId == R.id.menu_search_server) {
			mCandidateServers.clear();
			ListView lv = new ListView(this);
			lv.setAdapter(mCandidateServers);
			lv.setScrollingCacheEnabled(false);
			final AlertDialog dialog = new AlertDialog.Builder(this).setTitle(R.string.title_dialog)
					.setPositiveButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
						@Override
						public void onClick(DialogInterface dialog, int which) {
							mBt.cancelDiscovery();
						}
					}).setView(lv).create();
			lv.setOnItemClickListener(new AdapterView.OnItemClickListener() {
				@Override
				public void onItemClick(AdapterView<?> items, View view, int position, long id) {
					dialog.dismiss();
					String address = mCandidateServers.getItem(position);
					if (mServers.getPosition(address) == -1) {
						mServers.add(address);
					}
					mBt.cancelDiscovery();
				}
			});
			dialog.show();
			mBt.searchServer();
		} else if (itemId == R.id.menu_connect) {
			ListView lv = new ListView(this);
			final AlertDialog dialog = new AlertDialog.Builder(this).setTitle(R.string.title_dialog).setPositiveButton(android.R.string.cancel, null)
					.setView(lv).create();
			lv.setAdapter(mServers);
			lv.setScrollingCacheEnabled(false);
			lv.setOnItemClickListener(new AdapterView.OnItemClickListener() {
				Handler handler = new Handler();
				@Override
				public void onItemClick(AdapterView<?> items, View view, int position, long id) {
					dialog.dismiss();
					final String address = mServers.getItem(position);
					handler.post(new Runnable(){
						@Override
						public void run(){							
							mBt.connect(address);
						}
					});
				}
			});
			dialog.show();
		}
		return true;
	}

	@Override
	public void onActivityResult(int requestCode, int resultCode, Intent data) {
		mBt.onActivityResult(requestCode, resultCode, data);
	}

}


Bt.java

package com.example.android.btrev;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.UUID;

import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothServerSocket;
import android.bluetooth.BluetoothSocket;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.util.Log;
import android.widget.ArrayAdapter;

public class Bt {
	private abstract class ReceiverThread extends Thread {
		protected BluetoothSocket mSocket;

		protected void sendMessage(String message) throws IOException {
			OutputStream os = mSocket.getOutputStream();
			os.write(message.getBytes());
			os.write("\n".getBytes());
		}

		protected void loop() throws IOException {
			BufferedReader br = new BufferedReader(new InputStreamReader(mSocket.getInputStream()));
			String message;
			while ((message = br.readLine()) != null) {
				//do something
				MainActivity.global_char = message;
			}
		}

	}

	private class ServerThread extends ReceiverThread {
		private BluetoothServerSocket mServerSocket;
		private ServerThread() {
			try {
				mServerSocket = mBluetoothAdapter.listenUsingRfcommWithServiceRecord(mActivity.getPackageName(), mUuid);
			} catch (IOException e) {
				e.printStackTrace();
			}
		}

		@Override
		public void run() {
			try {
				Log.d(TAG, "accepting...");
				mSocket = mServerSocket.accept();
				Log.d(TAG, "accepted");
				loop();
			} catch (IOException e) {
				e.printStackTrace();
			} finally {
				cancel();
			}
		}

		private void cancel() {
			try {
				mServerSocket.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}

	private class ClientThread extends ReceiverThread {
		private final BluetoothDevice mServer;

		private ClientThread(String address) {
			mServer = mBluetoothAdapter.getRemoteDevice(address);
			try {
				mSocket = mServer.createRfcommSocketToServiceRecord(mUuid);
			} catch (IOException e) {
				e.printStackTrace();
			}
		}

		@Override
		public void run() {
			// connect() の前にデバイス検出をやめる必要がある
			mBluetoothAdapter.cancelDiscovery();
			try {
				// サーバに接続する
				mSocket.connect();
				loop();
			} catch (IOException e) {
				e.printStackTrace();
			} finally {
				cancel();
			}

		}

		private void cancel() {
			try {
				mSocket.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}

	private static final int REQUEST_ENABLE_BT = 1234;
	private static final int REQUEST_DISCOVERABLE_BT = 5678;
	private static final int DURATION = 300;
	private final String TAG = getClass().getSimpleName();
	private final BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
	private final MainActivity mActivity;
	private final ArrayAdapter<String> mCandidateServers;
	private final UUID mUuid = UUID.fromString("e74c254e-db32-4bb9-8f68-3b1f7d732f21"); // このアプリ固有の値。他のアプリで使用してはならない
	private ServerThread mServerThread;
	private ClientThread mClientThread;

	private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
		@Override
		public void onReceive(Context context, Intent intent) {
			String action = intent.getAction();
			if (BluetoothDevice.ACTION_FOUND.equals(action)) {
				Log.d(TAG, "ACTION_FOUND");
				// デバイスが見つかった場合、Intent から BluetoothDevice を取り出す
				BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
				// 名前とアドレスを所定のフォーマットで ArrayAdapter に格納
				mCandidateServers.add(device.getName() + "\n" + device.getAddress());
			} else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) {
				Log.d(TAG, "ACTION_DISCOVERY_FINISHED");
				// デバイス検出が終了した場合は、BroadcastReceiver を解除
				context.unregisterReceiver(mReceiver);
			}
		}
	};

	public Bt(MainActivity context, ArrayAdapter<String> candidateServers, ArrayAdapter<String> servers) {
		mActivity = context;
		mCandidateServers = candidateServers;
		Set<BluetoothDevice> devices = mBluetoothAdapter.getBondedDevices();
		for (BluetoothDevice device : devices) {
			servers.add(device.getName() + "\n" + device.getAddress());
		}
	}

	public void turnOn() {
		if (!mBluetoothAdapter.isEnabled()) {
			Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
			mActivity.startActivityForResult(intent, REQUEST_ENABLE_BT);
		}
	}

	public void onActivityResult(int requestCode, int resultCode, Intent data) {
		System.out.println("requestCode:" + requestCode + " resultCode:" + resultCode + " data:" + data);
		if (requestCode == REQUEST_ENABLE_BT) {
			if (resultCode != 0) {
				// 「はい」が選択された
			}
		} else if (requestCode == REQUEST_DISCOVERABLE_BT) {
			if (resultCode == DURATION) {
				// 「はい」が選択された
			}
		}
	}

	public void searchServer() {
		mCandidateServers.clear();
		IntentFilter filter = new IntentFilter();
		filter.addAction(BluetoothDevice.ACTION_FOUND);
		filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
		mActivity.registerReceiver(mReceiver, filter);
		mBluetoothAdapter.startDiscovery();
	}

	public void startDiscoverable() {
		Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
		intent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, DURATION);
		mActivity.startActivityForResult(intent, REQUEST_DISCOVERABLE_BT);
	}

	public void cancelDiscovery() {
		mBluetoothAdapter.cancelDiscovery();
	}

	public void startServer() {
		if (mServerThread != null) {
			mServerThread.cancel();
		}
		mServerThread = new ServerThread();
		mServerThread.start();
	}

	public void connect(String address) {
		int index;
		if ((index = address.indexOf("\n")) != -1) {
			address = address.substring(index + 1);
		}
		// クライアント用のスレッドを生成
		mClientThread = new ClientThread(address);
		mClientThread.start();
	}

	public void sendMessage(String message) {
		try {
			if (mServerThread != null) {
				mServerThread.sendMessage(message);
			}
			if (mClientThread != null) {
				mClientThread.sendMessage(message);
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	public void cancel() {
		if (mServerThread != null) {
			mServerThread.cancel();
			mServerThread = null;
		}
		if (mClientThread != null) {
			mClientThread.cancel();
			mClientThread = null;
		}
	}
}