2012年9月19日水曜日

Android 特定のパーミションがあるか調べる

PackageManagercheckPermission() を使うと、特定のパーミションがあるかどうかチェックすることができます。

  1. int p = getPackageManager().checkPermission(Manifest.permission.READ_PHONE_STATE, getPackageName());  
  2. if(p == PackageManager.PERMISSION_GRANTED) {  
  3.     // パーミッションあり  
  4. }  
  5. if(p == PackageManager.PERMISSION_DENIED) {  
  6.     // パーミッションなし  
  7. }  


パッケージ名を指定すればいいので、他のアプリもチェックできます。

例えば、インストールされているアプリの中で READ_PHONE_STATE のパーミッションがあるアプリの一覧を出すには次のようにします。

  1. public class MainActivity extends ListActivity {  
  2.   
  3.     @Override  
  4.     public void onCreate(Bundle savedInstanceState) {  
  5.         super.onCreate(savedInstanceState);  
  6.   
  7.         List<String> list = new ArrayList<String>();  
  8.   
  9.         PackageManager manager = getPackageManager();  
  10.         List<PackageInfo> packages = manager.getInstalledPackages(0);  
  11.         for (PackageInfo pkg : packages) {  
  12.             int p = getPackageManager().checkPermission(Manifest.permission.READ_PHONE_STATE, pkg.packageName);  
  13.             if(p == PackageManager.PERMISSION_GRANTED) {  
  14.                 list.add(pkg.packageName);  
  15.             }  
  16.         }  
  17.           
  18.         ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, list);  
  19.         setListAdapter(adapter);  
  20.     }  
  21. }  

Android Movie で File をデコードする

Movie を使うとアニメーション Gif を表示できるのですが、まぁバグがあるとかなんとかは自分でぐぐってください。 

よくあるサンプルは assets や raw にある Gif を読むやつばっかりです。
  1. InputStream is = context.getResources().openRawResource(R.drawable.piggy);  
  2. Movie movie = Movie.decodeStream(is);  
http://androidosbeginning.blogspot.jp/2010/09/gif-animation-in-android.html

とか
  1. InputStream is = context.getAssets().open("piggy.gif");  
  2. Movie movie = Movie.decodeStream(is);  
とか



SD にある Gif ファイルから読もうとして
  1. try {  
  2.     InputStream is = new FileInputStream(filePath);  
  3.     Movie movie = Movie.decodeStream(is);  
  4. catch (FileNotFoundException e) {  
  5.     e.printStackTrace();  
  6. }  
とすると Native 側で IOException が起こって表示されません。

LogCat で

09-19 15:38:37.828: W/System.err(7523): java.io.IOException 09-19 15:38:37.828: W/System.err(7523): at java.io.InputStream.reset(InputStream.java:218) 09-19 15:38:37.828: W/System.err(7523): at android.graphics.Movie.decodeStream(Native Method)

と出ていて、どうも InputStream の reset() で起こっていると。

InputStream のコードをみると、サブクラスが Override しないかぎり IOException が投げられるようになっています。

http://tools.oesf.biz/android-4.0.1_r1.0/xref/libcore/luni/src/main/java/java/io/InputStream.java#217
  1. 217     public synchronized void reset() throws IOException {  
  2. 218         throw new IOException();  
  3. 219     }  


FileInputStream は Override してませんでした。
http://tools.oesf.biz/android-4.0.1_r1.0/xref/libcore/luni/src/main/java/java/io/FileInputStream.java

AssetManager の open() で返ってくる InputStream は AssetInputStream です。

http://tools.oesf.biz/android-4.0.1_r1.0/xref/frameworks/base/core/java/android/content/res/AssetManager.java#288
  1. 288     public final InputStream open(String fileName) throws IOException {  
  2. 289         return open(fileName, ACCESS_STREAMING);  
  3. 290     }  
  4.   
  5. 309     public final InputStream open(String fileName, int accessMode)  
  6. 310         throws IOException {  
  7. 311         synchronized (this) {  
  8. 312             if (!mOpen) {  
  9. 313                 throw new RuntimeException("Assetmanager has been closed");  
  10. 314             }  
  11. 315             int asset = openAsset(fileName, accessMode);  
  12. 316             if (asset != 0) {  
  13. 317                 AssetInputStream res = new AssetInputStream(asset);  
  14. 318                 incRefsLocked(res.hashCode());  
  15. 319                 return res;  
  16. 320             }  
  17. 321         }  
  18. 322         throw new FileNotFoundException("Asset file: " + fileName);  
  19. 323     }  
この AssetInputStream は reset() を Override しています。

http://tools.oesf.biz/android-4.0.1_r1.0/xref/frameworks/base/core/java/android/content/res/AssetManager.java#538
  1. 538     public final class AssetInputStream extends InputStream {  
  2.   
  3. 569         public final void reset() throws IOException {  
  4. 570             seekAsset(mAsset, mMarkPos, -1);  
  5. 571         }  
  6.   
  7. 597     }  


InputStream のサブクラスで reset() を継承しているクラスとして BufferedInputStream があります。

このクラスを使って、次のようにすると IOException が発生せずに読むことができます。
  1. try {  
  2.     InputStream stream = new BufferedInputStream(  
  3.         new FileInputStream(filePath), 16 * 1024);  
  4.     stream.mark(16 * 1024);   
  5.   mMovie = Movie.decodeStream(stream);  
  6. catch (FileNotFoundException e) {  
  7.     e.printStackTrace();  
  8. }  
ポイントは mark() メソッドを呼ぶ必要がある点です。



ちなみに Movie には deocdeFile() というメソッドもあるのですが、なかみは FileInputStream 使ってるだけなので、使えません。。。

http://tools.oesf.biz/android-4.0.1_r1.0/xref/frameworks/base/graphics/java/android/graphics/Movie.java#51
  1. 51     public static Movie decodeFile(String pathName) {  
  2. 52         InputStream is;  
  3. 53         try {  
  4. 54             is = new FileInputStream(pathName);  
  5. 55         }  
  6. 56         catch (java.io.FileNotFoundException e) {  
  7. 57             return null;  
  8. 58         }  
  9. 59         return decodeTempStream(is);  
  10. 60     }  




おまけ

Movie.cpp
http://tools.oesf.biz/android-4.0.1_r1.0/xref/frameworks/base/core/jni/android/graphics/Movie.cpp#82
  1. 82 static jobject movie_decodeStream(JNIEnv* env, jobject clazz, jobject istream) {  
  2. 83   
  3. 84     NPE_CHECK_RETURN_ZERO(env, istream);  
  4. 85   
  5. 86     // what is the lifetime of the array? Can the skstream hold onto it?  
  6. 87     jbyteArray byteArray = env->NewByteArray(16*1024);  
  7. 88     SkStream* strm = CreateJavaInputStreamAdaptor(env, istream, byteArray);  
  8. 89     if (NULL == strm) {  
  9. 90         return 0;  
  10. 91     }  
  11. 92   
  12. 93     SkMovie* moov = SkMovie::DecodeStream(strm);  
  13. 94     strm->unref();  
  14. 95     return create_jmovie(env, moov);  
  15. 96 }  
http://tools.oesf.biz/android-4.0.1_r1.0/xref/frameworks/base/core/jni/android/graphics/CreateJavaOutputStreamAdaptor.cpp#CreateJavaInputStreamAdaptor
  1.      12 class JavaInputStreamAdaptor : public SkStream {  
  2.      13 public:  
  3.      14     JavaInputStreamAdaptor(JNIEnv* env, jobject js, jbyteArray ar)  
  4.      15         : fEnv(env), fJavaInputStream(js), fJavaByteArray(ar) {  
  5.      16         SkASSERT(ar);  
  6.      17         fCapacity   = env->GetArrayLength(ar);  
  7.      18         SkASSERT(fCapacity > 0);  
  8.      19         fBytesRead  = 0;  
  9.      20     }  
  10.      21   
  11.      22  virtual bool rewind() {  
  12.      23         JNIEnv* env = fEnv;  
  13.      24   
  14.      25         fBytesRead = 0;  
  15.      26   
  16.      27         env->CallVoidMethod(fJavaInputStream, gInputStream_resetMethodID);  
  17.      28         if (env->ExceptionCheck()) {  
  18.      29             env->ExceptionDescribe();  
  19.      30             env->ExceptionClear();  
  20.      31             SkDebugf("------- reset threw an exception\n");  
  21.      32             return false;  
  22.      33         }  
  23.      34         return true;  
  24.      35     }  
  25. ...  
  26.     145 SkStream* CreateJavaInputStreamAdaptor(JNIEnv* env, jobject stream,  
  27.     146                                        jbyteArray storage, int markSize) {  
  28.     147     static bool gInited;  
  29.     148   
  30.     149     if (!gInited) {  
  31.     150         jclass inputStream_Clazz = env->FindClass("java/io/InputStream");  
  32.     151         RETURN_NULL_IF_NULL(inputStream_Clazz);  
  33.     152   
  34.     153         gInputStream_resetMethodID      = env->GetMethodID(inputStream_Clazz,  
  35.     154                                                            "reset""()V");  
  36.     155         gInputStream_markMethodID       = env->GetMethodID(inputStream_Clazz,  
  37.     156                                                            "mark""(I)V");  
  38.     157         gInputStream_availableMethodID  = env->GetMethodID(inputStream_Clazz,  
  39.     158                                                            "available""()I");  
  40.     159         gInputStream_readMethodID       = env->GetMethodID(inputStream_Clazz,  
  41.     160                                                            "read""([BII)I");  
  42.     161         gInputStream_skipMethodID       = env->GetMethodID(inputStream_Clazz,  
  43.     162                                                            "skip""(J)J");  
  44.     163   
  45.     164         RETURN_NULL_IF_NULL(gInputStream_resetMethodID);  
  46.     165         RETURN_NULL_IF_NULL(gInputStream_markMethodID);  
  47.     166         RETURN_NULL_IF_NULL(gInputStream_availableMethodID);  
  48.     167         RETURN_NULL_IF_NULL(gInputStream_availableMethodID);  
  49.     168         RETURN_NULL_IF_NULL(gInputStream_skipMethodID);  
  50.     169   
  51.     170         gInited = true;  
  52.     171     }  
  53.     172   
  54.     173     if (markSize) {  
  55.     174         env->CallVoidMethod(stream, gInputStream_markMethodID, markSize);  
  56.     175     }  
  57.     176   
  58.     177     return new JavaInputStreamAdaptor(env, stream, storage);  
  59.     178 }  

2012年9月14日金曜日

67WS でマルチデバイス対応の講座やります!

67ワークショップさんで講座を受け持つことになりました。
有料ですが、少人数(なんと定員6人)でがっつり教えます!
初回は10月16日です。
よろしくお願いします。


告知文↓

■━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
あんざいゆきの Android マルチデバイス対応講座 がはじまります
■━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

Android端末の魅力はその数と多様性です。しかし、多様性があるがゆえに直面するのが、端末間での表示の差異や動作の互換性のなさです。

この講座では、Android関連の著書も多数出版されている あんざいゆき さんを講師に、マルチデバイス対応しやすい画面設計のコツから、リソースや Fragment の使い方、9 patch の効果的な利用方法まで、実践的な手法を解説していきます。

実際にAndroidアプリ開発を行われている皆様、ご参加お待ちしています。


●カリキュラム概要
1. 様々な画面サイズ、解像度対応
 リソースの使い方
 対応しやすいレイアウトのコツ
 9 patch の使い方
 タブレットとハンドセット両対応

2. マルチバージョン対応
 Support pakcage の活かし方
 バージョン判別とクラスの分け方
 Action Bar Sherlock を使って 2.x でも Action Bar を使う方法


初回開講
2012年10月16日(火)

http://67.org/ws/workshop/detail/095android.html


2012年9月5日水曜日

Android Bluetooth でシリアル通信(SPP)する Fragment 書いた

そのうち GitHub におくかもしれない。

・Bluetooth が利用可能かチェック
・Bluetooth が有効になっていない場合は有効にするダイアログを表示
・有効になったらペアリング済みの一覧ダイアログを表示
・BLUETOOTH_ADMIN permission がある場合は、一覧ダイアログにデバイス検索開始のボタンを表示
・バックグラウンドでの通信(読み書き)

が入ってます。

・画面回転考慮してません
・デバイス検索中のクルクルをつけてません
(そのうちやる、と、思う、たぶん)

  1. /** 
  2.  * This fragment process Bluetooth connection. Host Activity have to implements 
  3.  * BluetoothCallback. 
  4.  *  
  5.  * @author yanzm 
  6.  */  
  7. public class BluetoothFragment extends Fragment {  
  8.   
  9.     public interface BluetoothCallback {  
  10.         /** 
  11.          * check whether device support Bluetooth. 
  12.          *  
  13.          * @param isAvailable 
  14.          *            true if device support Bluetooth. 
  15.          */  
  16.         public void onCheckAvailability(boolean isAvailable);  
  17.   
  18.         /** 
  19.          * called when bluetooth state be changed. 
  20.          *  
  21.          * @param enabled 
  22.          */  
  23.         public void onChangeBluetoothState(boolean enabled);  
  24.   
  25.         /** 
  26.          * called when try to connect. 
  27.          *  
  28.          * @param connected 
  29.          */  
  30.         public void onConnected(boolean connected);  
  31.   
  32.         /** 
  33.          * called when connection established 
  34.          *  
  35.          * @param in 
  36.          * @param out 
  37.          */  
  38.         public void onOpenConnection();  
  39.   
  40.         public void onDataArrived(int size, byte[] buffer);  
  41.     }  
  42.   
  43.     public static final String EXTRA_UUID = "uuid";  
  44.     public static final int SPP_MODE = 0;  
  45.     private static final UUID SPP_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");  
  46.   
  47.     private static final int REQUEST_ENABLE_BT = 11;  
  48.   
  49.     private BluetoothCallback mCallback;  
  50.     private BluetoothAdapter mBluetoothAdapter;  
  51.     private boolean mIsAvailable;  
  52.     private boolean mIsEnable;  
  53.     private UUID mUuid;  
  54.   
  55.     private ConnectThread mConnectThread;  
  56.     private TransmitThread mTransmitThread;  
  57.   
  58.     @Override  
  59.     public void onAttach(Activity activity) {  
  60.         super.onAttach(activity);  
  61.   
  62.         if (activity instanceof BluetoothCallback == false) {  
  63.             throw new ClassCastException("Activity have to implement BluetoothCallback");  
  64.         }  
  65.   
  66.         mCallback = (BluetoothCallback) activity;  
  67.     }  
  68.   
  69.     @Override  
  70.     public void onCreate(Bundle savedInstanceState) {  
  71.         super.onCreate(savedInstanceState);  
  72.     }  
  73.   
  74.     @Override  
  75.     public void onDestroy() {  
  76.         super.onDestroy();  
  77.         if (mConnectThread != null) {  
  78.             mConnectThread.cancel();  
  79.             mConnectThread = null;  
  80.         }  
  81.   
  82.         if (mTransmitThread != null) {  
  83.             mTransmitThread.cancel();  
  84.             mTransmitThread = null;  
  85.         }  
  86.     }  
  87.   
  88.     @Override  
  89.     public void onActivityCreated(Bundle savedInstanceState) {  
  90.         super.onActivityCreated(savedInstanceState);  
  91.   
  92.         Bundle args = getArguments();  
  93.         int sppMode = args == null ? 0 : args.getInt(EXTRA_UUID, 0);  
  94.         switch (sppMode) {  
  95.             case 0:  
  96.                 mUuid = SPP_UUID;  
  97.                 break;  
  98.             default:  
  99.                 mUuid = SPP_UUID;  
  100.                 break;  
  101.         }  
  102.   
  103.         // 1. Get the BluetoothAdapter  
  104.         mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();  
  105.         mIsAvailable = mBluetoothAdapter != null;  
  106.   
  107.         mCallback.onCheckAvailability(mIsAvailable);  
  108.   
  109.         // 2. Enable Bluetooth  
  110.         if (mBluetoothAdapter.isEnabled()) {  
  111.             mIsEnable = true;  
  112.             onEnabled();  
  113.         } else {  
  114.             mIsEnable = false;  
  115.             Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);  
  116.             startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);  
  117.         }  
  118.         mCallback.onChangeBluetoothState(mIsEnable);  
  119.     }  
  120.   
  121.     BroadcastReceiver mStateReceiver = new BroadcastReceiver() {  
  122.   
  123.         @Override  
  124.         public void onReceive(Context context, Intent intent) {  
  125.             int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.STATE_OFF);  
  126.             if (state == BluetoothAdapter.STATE_ON) {  
  127.                 mIsEnable = true;  
  128.                 onEnabled();  
  129.             } else {  
  130.                 mIsEnable = false;  
  131.             }  
  132.             mCallback.onChangeBluetoothState(mIsEnable);  
  133.         }  
  134.     };  
  135.   
  136.     @Override  
  137.     public void onResume() {  
  138.         super.onResume();  
  139.         IntentFilter filter = new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED);  
  140.         getActivity().registerReceiver(mStateReceiver, filter);  
  141.     }  
  142.   
  143.     @Override  
  144.     public void onPause() {  
  145.         super.onPause();  
  146.         getActivity().unregisterReceiver(mStateReceiver);  
  147.         if (mBluetoothAdapter.isDiscovering()) {  
  148.             stopDiscovery();  
  149.         }  
  150.     }  
  151.   
  152.     @Override  
  153.     public void onActivityResult(int requestCode, int resultCode, Intent data) {  
  154.         super.onActivityResult(requestCode, resultCode, data);  
  155.   
  156.         if (requestCode == REQUEST_ENABLE_BT) {  
  157.             if (resultCode == Activity.RESULT_OK) {  
  158.                 mIsEnable = true;  
  159.                 onEnabled();  
  160.             } else {  
  161.                 mIsEnable = false;  
  162.             }  
  163.             mCallback.onChangeBluetoothState(mIsEnable);  
  164.         }  
  165.     }  
  166.   
  167.     private void onEnabled() {  
  168.         selectPairedDevice();  
  169.     }  
  170.   
  171.     public void selectPairedDevice() {  
  172.         // 3. Querying paired devices  
  173.         Set<BluetoothDevice> pairedDevices = mBluetoothAdapter.getBondedDevices();  
  174.   
  175.         if (pairedDevices.size() > 0) {  
  176.             ArrayList<BluetoothDevice> deviceList = new ArrayList<BluetoothDevice>();  
  177.             for (BluetoothDevice device : pairedDevices) {  
  178.                 deviceList.add(device);  
  179.             }  
  180.             final BluetoothDeviceAdapter adapter = new BluetoothDeviceAdapter(getActivity(), deviceList);  
  181.   
  182.             AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());  
  183.             builder.setTitle("Paired devices");  
  184.             builder.setAdapter(adapter, new DialogInterface.OnClickListener() {  
  185.   
  186.                 public void onClick(DialogInterface dialog, int position) {  
  187.                     BluetoothDevice selectedDevice = adapter.getItem(position);  
  188.                     connectToDevice(selectedDevice);  
  189.                 }  
  190.             });  
  191.             builder.setNegativeButton(android.R.string.cancel, null);  
  192.   
  193.             Context context = getActivity();  
  194.   
  195.             PackageManager manager = context.getPackageManager();  
  196.             int permission = manager.checkPermission(Manifest.permission.BLUETOOTH_ADMIN, context.getPackageName());  
  197.   
  198.             if (permission == PackageManager.PERMISSION_GRANTED) {  
  199.                 builder.setPositiveButton("Search for devices"new DialogInterface.OnClickListener() {  
  200.   
  201.                     public void onClick(DialogInterface dialog, int which) {  
  202.                         discoveringDevices();  
  203.                     }  
  204.                 });  
  205.             }  
  206.             builder.show();  
  207.         }  
  208.     }  
  209.   
  210.     BluetoothDeviceAdapter mDiscoveringAdapter;  
  211.   
  212.     BroadcastReceiver mDiscoveringReceiver = new BroadcastReceiver() {  
  213.   
  214.         @Override  
  215.         public void onReceive(Context context, Intent intent) {  
  216.             BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);  
  217.             mDiscoveringAdapter.add(device);  
  218.         }  
  219.     };  
  220.   
  221.     private void discoveringDevices() {  
  222.         // 4. discovering devices  
  223.         ArrayList<BluetoothDevice> deviceList = new ArrayList<BluetoothDevice>();  
  224.         mDiscoveringAdapter = new BluetoothDeviceAdapter(getActivity(), deviceList);  
  225.   
  226.         AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());  
  227.         builder.setAdapter(mDiscoveringAdapter, new DialogInterface.OnClickListener() {  
  228.   
  229.             public void onClick(DialogInterface dialog, int position) {  
  230.                 stopDiscovery();  
  231.   
  232.                 BluetoothDevice selectedDevice = mDiscoveringAdapter.getItem(position);  
  233.                 connectToDevice(selectedDevice);  
  234.             }  
  235.         });  
  236.         builder.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {  
  237.   
  238.             public void onClick(DialogInterface dialog, int which) {  
  239.                 stopDiscovery();  
  240.             }  
  241.         });  
  242.         builder.show();  
  243.   
  244.         startDiscovery();  
  245.     }  
  246.   
  247.     private void startDiscovery() {  
  248.         IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);  
  249.         getActivity().registerReceiver(mDiscoveringReceiver, filter);  
  250.         mBluetoothAdapter.startDiscovery();  
  251.     }  
  252.   
  253.     private void stopDiscovery() {  
  254.         mBluetoothAdapter.cancelDiscovery();  
  255.         getActivity().unregisterReceiver(mDiscoveringReceiver);  
  256.     }  
  257.   
  258.     private void connectToDevice(BluetoothDevice device) {  
  259.         mConnectThread = new ConnectThread(device);  
  260.         mConnectThread.start();  
  261.     }  
  262.   
  263.     private class ConnectThread extends Thread {  
  264.         private final BluetoothSocket mSocket;  
  265.   
  266.         public ConnectThread(BluetoothDevice device) {  
  267.             BluetoothSocket tmp = null;  
  268.             try {  
  269.                 tmp = device.createRfcommSocketToServiceRecord(mUuid);  
  270.             } catch (IOException e) {  
  271.             }  
  272.             mSocket = tmp;  
  273.         }  
  274.   
  275.         public void run() {  
  276.             if (mBluetoothAdapter.isDiscovering()) {  
  277.                 mBluetoothAdapter.cancelDiscovery();  
  278.             }  
  279.   
  280.             try {  
  281.                 mSocket.connect();  
  282.   
  283.                 Message msg = mHandler.obtainMessage(MESSAGE_CONNECTED, true);  
  284.                 mHandler.sendMessage(msg);  
  285.   
  286.             } catch (IOException connectException) {  
  287.                 try {  
  288.                     mSocket.close();  
  289.                 } catch (IOException closeException) {  
  290.                 }  
  291.   
  292.                 Message msg = mHandler.obtainMessage(MESSAGE_CONNECTED, false);  
  293.                 mHandler.sendMessage(msg);  
  294.                 return;  
  295.             }  
  296.   
  297.             manageConnectedSocket(mSocket);  
  298.         }  
  299.   
  300.         public void cancel() {  
  301.             try {  
  302.                 mSocket.close();  
  303.             } catch (IOException e) {  
  304.             }  
  305.         }  
  306.     }  
  307.   
  308.     private void manageConnectedSocket(BluetoothSocket socket) {  
  309.         mTransmitThread = new TransmitThread(socket);  
  310.         mTransmitThread.start();  
  311.     }  
  312.   
  313.     private static final int MESSAGE_READ = 1;  
  314.     private static final int MESSAGE_CONNECTED = 2;  
  315.     private static final int MESSAGE_OPEN = 3;  
  316.   
  317.     final Handler mHandler = new Handler() {  
  318.   
  319.         @Override  
  320.         public void handleMessage(Message msg) {  
  321.             int what = msg.what;  
  322.             switch (what) {  
  323.                 case MESSAGE_READ:  
  324.                     int length = msg.arg1;  
  325.                     byte[] buffer = (byte[]) msg.obj;  
  326.                     mCallback.onDataArrived(length, buffer);  
  327.                     break;  
  328.   
  329.                 case MESSAGE_CONNECTED:  
  330.                     boolean connected = (Boolean) msg.obj;  
  331.                     mCallback.onConnected(connected);  
  332.                     break;  
  333.   
  334.                 case MESSAGE_OPEN:  
  335.                     mCallback.onOpenConnection();  
  336.                     break;  
  337.             }  
  338.         }  
  339.     };  
  340.   
  341.     private class TransmitThread extends Thread {  
  342.         private final BluetoothSocket mSocket;  
  343.         private final InputStream mInStream;  
  344.         private final OutputStream mOutStream;  
  345.   
  346.         public TransmitThread(BluetoothSocket socket) {  
  347.             mSocket = socket;  
  348.   
  349.             InputStream tmpIn = null;  
  350.             OutputStream tmpOut = null;  
  351.             try {  
  352.                 tmpIn = socket.getInputStream();  
  353.                 tmpOut = socket.getOutputStream();  
  354.   
  355.                 Message msg = mHandler.obtainMessage(MESSAGE_OPEN);  
  356.                 mHandler.sendMessage(msg);  
  357.   
  358.             } catch (IOException e) {  
  359.             }  
  360.   
  361.             mInStream = tmpIn;  
  362.             mOutStream = tmpOut;  
  363.         }  
  364.   
  365.         public void run() {  
  366.             byte[] buffer = new byte[1024];  
  367.             int length;  
  368.   
  369.             while (true) {  
  370.                 try {  
  371.                     length = mInStream.read(buffer);  
  372.   
  373.                     Message msg = mHandler.obtainMessage(MESSAGE_READ, length, -1, buffer);  
  374.                     mHandler.sendMessage(msg);  
  375.   
  376.                 } catch (IOException e) {  
  377.                     break;  
  378.                 }  
  379.             }  
  380.         }  
  381.   
  382.         public boolean write(byte[] bytes) {  
  383.             if (mOutStream == null) {  
  384.                 return false;  
  385.             }  
  386.   
  387.             StringBuilder sb = new StringBuilder();  
  388.             for (int i = 0; i < bytes.length; i++) {  
  389.                 sb.append(bytes[i] + " ");  
  390.             }  
  391.   
  392.             try {  
  393.                 mOutStream.write(bytes);  
  394.                 return true;  
  395.             } catch (IOException e) {  
  396.             }  
  397.   
  398.             return false;  
  399.         }  
  400.   
  401.         public void cancel() {  
  402.             try {  
  403.                 mSocket.close();  
  404.             } catch (IOException e) {  
  405.             }  
  406.             try {  
  407.                 mInStream.close();  
  408.             } catch (IOException e) {  
  409.             }  
  410.             try {  
  411.                 mOutStream.close();  
  412.             } catch (IOException e) {  
  413.             }  
  414.         }  
  415.     }  
  416.   
  417.     public boolean write(int i) {  
  418.         byte[] bytes = new byte[4];  
  419.         bytes[0] = (byte) (i);  
  420.         bytes[1] = (byte) (i >> 8);  
  421.         bytes[2] = (byte) (i >> 16);  
  422.         bytes[3] = (byte) (i >> 24);  
  423.   
  424.         return write(bytes);  
  425.     }  
  426.   
  427.     public boolean write(String s) {  
  428.         return write(s.getBytes());  
  429.     }  
  430.   
  431.     public boolean write(byte[] bytes) {  
  432.         if (mTransmitThread == null) {  
  433.             return false;  
  434.         }  
  435.   
  436.         return mTransmitThread.write(bytes);  
  437.     }  
  438.   
  439.     public static class BluetoothDeviceAdapter extends ArrayAdapter<BluetoothDevice> {  
  440.   
  441.         public BluetoothDeviceAdapter(Context context, List<BluetoothDevice> objects) {  
  442.             super(context, 0, objects);  
  443.         }  
  444.   
  445.         @Override  
  446.         public View getView(int position, View convertView, ViewGroup parent) {  
  447.             if (convertView == null) {  
  448.                 LayoutInflater inflater = LayoutInflater.from(getContext());  
  449.                 convertView = inflater.inflate(android.R.layout.simple_list_item_2, parent, false);  
  450.             }  
  451.   
  452.             BluetoothDevice device = getItem(position);  
  453.   
  454.             TextView nameView = (TextView) convertView.findViewById(android.R.id.text1);  
  455.             nameView.setText(device.getName());  
  456.               
  457.             TextView addressView = (TextView) convertView.findViewById(android.R.id.text2);  
  458.             addressView.setText(device.getAddress());  
  459.   
  460.             return convertView;  
  461.         }  
  462.     }  
  463. }  


こんな感じで Activity には BluetoothCallback を implements して利用します。

  1. public class MainActivity extends FragmentActivity implements BluetoothCallback {  
  2.   
  3.     private static final String TAG = "MainActivity";  
  4.       
  5.     BluetoothFragment mBluetoothFragment;  
  6.   
  7.     View mButton;  
  8.   
  9.     @Override  
  10.     public void onCreate(Bundle savedInstanceState) {  
  11.         super.onCreate(savedInstanceState);  
  12.         setContentView(R.layout.main);  
  13.   
  14.         Bundle args = new Bundle();  
  15.         args.putInt(BluetoothFragment.EXTRA_UUID, BluetoothFragment.SPP_MODE);  
  16.   
  17.         if (mBluetoothFragment == null) {  
  18.             mBluetoothFragment = new BluetoothFragment();  
  19.             getSupportFragmentManager().beginTransaction().add(mBluetoothFragment, "BluetoothFragment").commit();  
  20.         }  
  21.   
  22.         mButton = findViewById(R.id.button1);  
  23.         mButton.setOnClickListener(new View.OnClickListener() {  
  24.   
  25.             public void onClick(View v) {  
  26.                 write(new byte[] { 0x500x4d0x03 });  
  27.             }  
  28.         });  
  29.         mButton.setEnabled(false);  
  30.     }  
  31.   
  32.     public void onCheckAvailability(boolean isAvailable) {  
  33.         String text = "onCheckAvailability : " + isAvailable;  
  34.         Log.d(TAG, text);  
  35.         showToast(text);  
  36.     }  
  37.   
  38.     public void onChangeBluetoothState(boolean enabled) {  
  39.         String text = "onChangeBluetoothState : " + enabled;  
  40.         Log.d(TAG, text);  
  41.         showToast(text);  
  42.     }  
  43.   
  44.     public void onConnected(boolean connected) {  
  45.         String text = "onConnected : " + connected;  
  46.         Log.d(TAG, text);  
  47.         showToast(text);  
  48.     }  
  49.   
  50.     public void onOpenConnection() {  
  51.         mButton.setEnabled(true);  
  52.         Log.d(TAG, "onOpenConnection");  
  53.         showToast("onOpenConnection");  
  54.     }  
  55.   
  56.     public void onDataArrived(int length, byte[] buffer) {  
  57.         StringBuilder sb = new StringBuilder();  
  58.         for(int i = 0; i < length; i++) {  
  59.             sb.append(buffer[i] + " ");  
  60.         }  
  61.         String text = "onDataArrived : " + length + ", " + sb.toString();  
  62.         Log.d(TAG, text);  
  63.         showToast(text);  
  64.     }  
  65.   
  66.     private void write(byte[] bytes) {  
  67.         if (mBluetoothFragment != null) {  
  68.             mBluetoothFragment.write(bytes);  
  69.         }  
  70.     }  
  71.   
  72.     private void showToast(String text) {  
  73.         Toast.makeText(this, text, Toast.LENGTH_SHORT).show();  
  74.     }  
  75. }