Membuat App Bluetooth di Android (2): Daftar Paired Devices dan Device Discovery

Menampilkan Paired Devices

Catatan: lanjutan dari tutorial sebelumnya (menyalakan Bluetooth), code yang digunakan dalam tutorial ini adalah lanjutan dari tutorial sebelumnya.

Sebelum dapat bertukar data, device harus ditemukan dan di-pair terlebih dulu (untuk alasan keamanan). Proses ini memperlukan waktu dan daya batere besar.  Oleh karena itu Android menyimpan data device yang sudah dipair (bonded) seperti nama device, class, MAC address  sehingga dapat langsung digunakan tanpa perlu mencari dan mem-pair lagi.

Untuk mendapatkan data device yang sudah di-pair atau bonded, dapat digunakan method BluetoothAdapter.getBondedDevices().

Pada app berikut kita akan menampilkan device yang sudah di-pair dalam listview. Kita perlu siapkan listiviewnya terlebih dulu. Tambahkan widget  Listview (gambar bawah)

Rancagan UI listview menampilkan paired device

Tambahkan ArrayList untuk menyimpan data beserta adapter.


 //untuk listview
 private ArrayList<String> items = new ArrayList<>();
 ArrayAdapter adapter;

 //menyimpan daftar device yg terurut sesuai listview
 private ArrayList<BluetoothDevice> arrayPairedDevices = new ArrayList<>();

Buat method yang mengambil data paired devices dan kemudian update listview.

// cari paired device, dan tambah ke listview
private void tampilkanPaired() {
   //ambil paired/bonded devices, tampung di arraylist
   Set<BluetoothDevice> pairedDevices = mBluetoothAdapter.getBondedDevices();

   //ada?
   if (pairedDevices.size() > 0) {
       // Loop semua paired devices
       for (BluetoothDevice device : pairedDevices) {
           // tambahkan ke listview
           adapter.add(device.getName() + "\n" + device.getAddress());
           //nanti kalau user mengklik listview, kita tahu device apa yg diklik
           arrayPairedDevices.add(device);
       }
   }
   //refresh listview
   adapter.notifyDataSetChanged();
}

Panggil method tersebut pada saat onCreate.

 @Override
 protected void onCreate(Bundle savedInstanceState) {
     ... sama dengan sebelumnya

     //init listview
     ListView lv = (ListView) findViewById(R.id.listView);
     //set warna abu2, karena default font adalah putih
     lv.setBackgroundColor(Color.LTGRAY);
     adapter = new ArrayAdapter (this,android.
                                 R.layout.simple_expandable_list_item_1,items);
    lv.setAdapter(adapter);
    tampilkanPaired();

     //pindah ke bawah
     if (!mBluetoothAdapter.isEnabled()) {
         //bluetooth dalam kondisi off
         //menampilkan dialog untuk menyalakan bluetooth (jika sedang mati)
         //setelah user memilih method onActivityResult akan ditrigger
         Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
         startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
     }
 }

Dibagian onActivityResult juga tambahkan bagian untuk mengupdate listview saat Bluetooth selesai dinyalakan (bagian panah).

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
   if (requestCode == REQUEST_ENABLE_BT) {
       String pesan;
       if (resultCode == RESULT_OK) {
           //user menyalakan bluetooth
           pesan = "Bluetooth dinyalakan";
           tampilkanPaired();  // <-------------------
       } else {
           // user deny
           pesan = "Bluetooth tidak mau dinyalakan user!";
       }
       Toast toast = Toast.makeText(getApplicationContext(), 
                                    pesan, Toast.LENGTH_LONG);
       toast.show();
   }
}

Jalankan app dan hasilnya akan seperti gambar di bawah. Informasi yang ditampilkan adalah nama device dan MAC address. MAC address adalah alamat unik untuk network interface yang umumnya tersimpan di hardware. Tidak ada device yang memiliki MAC address yang sama.

tampilan paired bluetooth

Menemukan Device (Device Discovery)

PENTING: mulai Android 6 (Marshmallow, API 23), untuk melakukan device discovery diperlukan permission ACCESS_COARSE_LOCATION atau ACCESS_FINE_LOCATION (tentang permission ini lihat tutorial tentang Play Service Location API).

Secara default, umumnya device hanya visible pada device yang sudah ter-pair. Agar dapat ditemukan oleh proses scan, visibilitas device perlu diset terlebih dulu menjadi discoverable. Pada Android 5 dan 6 ini dilakukan dengan membuka setting Bluetooth dan otomatis device akan masuk mode discoverable (gambar bawah).

Menyalakan discoverable

Sedangkan untuk Android versi 4.2 diaktifkan dengan membuka setting BlueTooth lalu tap tulisan “Only Visible to paired device” (gambar bawah)

Menyalakan discoverable di Android 4

Kita akan membuat app yang mencari device baru yang belum di-pair sebelumnya. Sebelum menjalankan app, pastikan ada device lain sudah dalam kondisi discoverable sehingga dapat ditemukan oleh app kita.

Kita akan modfikasi project sebelumnya. Pertama tambahkan permission untuk lokasi ACCESS_COARSE_LOCATION. Ini disebabkan mulai Android 6 (Marshmallow, API 23), untuk dapat melakukan device discovery perlu permission ini. Jika tidak, tidak akan keluar error tetapi tidak akan mendapatkan hasil (membuat sangat sulit didebug!).

Jadi manifest akan seperti ini:

<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>

Android 6 juga menerapkan runtime permission system untuk beberapa permission yang dianggap “sensititf”. Dengan runtime permission system, user memberikan ijin bukan saat install, tetapi saat fitur akan digunakan (contoh gambar dibawah). Lokasi termasuk dalam kategori runtime permission.

run time permission untuk lokasi

Selanjutkan kita tambahkan kode untuk menangani runtime permission untuk lokasi di onCreate:

//untuk minta ijin lokasi, mulai android 6, scan bluetooth perlu ijin lokasi
//angka bebas tapi jangan lebih dari 1 byte! (<255)
private static final int MY_PERMISSIONS_REQUEST = 98;
boolean isIjinLokasi = false; //sudah mendapat ijin untuk mengakses lokasi?


@Override
 protected void onCreate(Bundle savedInstanceState) {
     ...  sama

     //mulai android 6, device discovery bluetooth perlu ijin lokasi
     //akan keluar dialog yg menanyakan apkah user memberikan ijin?
     if (ContextCompat.checkSelfPermission(this,
             //hati2, jika konstanta tidak cocok dgn manifest, tidak ada runtimeerror
             Manifest.permission.ACCESS_COARSE_LOCATION)
             != PackageManager.PERMISSION_GRANTED) {

         ActivityCompat.requestPermissions(this,
                 new String[]{Manifest.permission.ACCESS_COARSE_LOCATION},
                 MY_PERMISSIONS_REQUEST);
                 // MY_PERMISSIONS_REQUEST adalah konstanta, 
                 //nanti digunakan di  onRequestPermissionsResult
     } else {
         //sudah diijinkan, tidak perlu tanya lagi
         isIjinLokasi = true;
     }

 }

Saat pertamakali menggunakan fitur, user akan ditanya apakah memberikan ijin. Hasilnya akan ditangkap di method onRequestPermissionResult seperti dibawah

//setelah muncul dialog tentang ijin lokasi, 
//user akan merespon (allow atau deny), dan method ini akan dipanggil
@Override
public void onRequestPermissionsResult(int requestCode,
                                      String permissions[], int[] grantResults) {

   if (requestCode == MY_PERMISSIONS_REQUEST) {
       if (grantResults.length > 0
           && grantResults[0] == PackageManager.PERMISSION_GRANTED)
       {
           isIjinLokasi = true; //diijinkan oleh user
       }
       return;
   }
}

Sekarang kita mulai aksi untuk mencari device. Tambahkan button, beri label “Start Discovery”

rancangan button

Isi onClicknya dengan method klikStartDiscovery sebagai berikut. Untuk memulai proses discovery dilakukan hanya dengan memanggil method startDiscovery() yang hasilnya kemudian ditangkap dengan BroadcastReceiver.

public void klikStartDiscovery(View v) {
   //membutuhkan waktu lama (sekitar 12 detik), proses dibackground
   //hasil ditangkap dengan BroadcastReceiver


   if (!isIjinLokasi) {
       //tidak mendapat ijin lokasi. Untuk android versi dibawah 6, tidak masalah tapi
       //di android versi 6 tidak error tapi tidak akan memberikan hasil --> susah didebug!!
       Toast toast = Toast.makeText(getApplicationContext(),
               "Tidak diijinkan untuk akses lokasi. " +
               "Proses device discovery tidak akan memberi hasil!!", Toast.LENGTH_LONG);
       toast.show();
   } else {
       //dapat ijin
       if (mBluetoothAdapter.isDiscovering()) {
           //sedang proses discovery? batalkan.
           mBluetoothAdapter.cancelDiscovery();
       }
       //mulai proses discovery
       //hasilnya akan ditangkap di BroadcastReceiver
       if (mBluetoothAdapter.startDiscovery()) {
           Toast toast = Toast.makeText(getApplicationContext(),
                   "Sukses memulai proses discovery", Toast.LENGTH_LONG);
           toast.show();
       } else {
           Toast toast = Toast.makeText(getApplicationContext(),
                   "GAGAL memulai proses discovery", Toast.LENGTH_LONG);
           toast.show();
       }
   }
}

Selanjutnya kita akan siapkan BroadcastReceiver. Pertama register BroadReceiver agar dipanggil saat device ditemukan, dan saat service discovery dimulai dan selesai.

@Override
protected void onResume() {
   // onResume juga dipanggil saat app dimulai
   // daftar broadcastreciver untuk menerima intent.
   IntentFilter filter = new IntentFilter();
   filter.addAction(BluetoothDevice.ACTION_FOUND);
   filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_STARTED);
   filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
   registerReceiver(mReceiver, filter);
   super.onResume();
}

@Override
protected void onPause() {
   // Unregister karena activity dipause.
   LocalBroadcastManager.getInstance(this).unregisterReceiver(mReceiver);
   super.onPause();
}

Kemudian implementasikan BroadcastReceiver. Di method ini akan ditampilkan pesan saat pencarian dimulai dan selesai. Jika ada device baru, maka akan ditambahkan ke listview.

//untuk menangkap hasil method .startDiscovery
//tertrigger untuk setiap*device ditemukan (ACTION_FOUND)
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {

   public void onReceive(Context context, Intent intent) {
       String action = intent.getAction();

       // ada device baru
       if (BluetoothDevice.ACTION_FOUND.equals(action)) {
           // ambil objek BluetoothDevice dari Intent, tulis ke listivew
           BluetoothDevice device = 
               intent.getParcelableExtra (BluetoothDevice.EXTRA_DEVICE);
           
           arrayPairedDevices.add(device); 
    // tulis nama dan MAC address ke ListView
           adapter.add("Baru:" + device.getName() + "\n" + device.getAddress());
           //refresh listview, JANGAN LUPA!!
           adapter.notifyDataSetChanged();
       } else
       // mulai proses discovery, untuk debug saja, memastikan proses dimulai
       if (BluetoothAdapter.ACTION_DISCOVERY_STARTED.equals(action)) {
           Toast toast = Toast.makeText(getApplicationContext(),
                   "Mulai proses discovery", Toast.LENGTH_LONG);
           toast.show();
       } else
       // mulai proses discovery, untuk debug saja, memastikan proses selesai
       if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) {
           Toast toast = Toast.makeText(getApplicationContext(),
                   "Proses discovery selesai", Toast.LENGTH_LONG);
           toast.show();
       }
   }
};

Jalankan maka hasilnya akan sebagai berikut (pastikan device lain dalam kondisi ter-discoverable). Device yang baru akan diawali kata “Baru”.

tambahan device baru

 

Berlanjut ke bagian 3: komunikasi data antar device

Leave a Reply

Your email address will not be published. Required fields are marked *

Time limit is exhausted. Please reload CAPTCHA.