Java tutorial
/* * Copyright (C) 2013 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.zpcat.ble.ui.central; import android.annotation.SuppressLint; import android.annotation.TargetApi; import android.bluetooth.BluetoothGattCharacteristic; import android.bluetooth.BluetoothGattService; //import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Intent; import android.content.ServiceConnection; import android.os.Build; import android.os.Bundle; import android.os.IBinder; import android.support.v4.app.DialogFragment; import android.support.v7.app.AppCompatActivity; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.widget.Button; import android.widget.EditText; import android.widget.ExpandableListView; import android.widget.SimpleExpandableListAdapter; import android.widget.TextView; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import org.zpcat.ble.BluetoothLeService; import org.zpcat.ble.R; import org.zpcat.ble.SampleGattAttributes; import org.zpcat.ble.utils.Log; /** * For a given BLE device, this Activity provides the user interface to connect, display data, * and display GATT services and characteristics supported by the device. The Activity * communicates with {@code BluetoothLeService}, which in turn interacts with the * Bluetooth LE API. */ public class DeviceControlActivity extends AppCompatActivity { public static final String EXTRAS_DEVICE_NAME = "DEVICE_NAME"; public static final String EXTRAS_DEVICE_ADDRESS = "DEVICE_ADDRESS"; private TextView mConnectionState; private TextView mDataField; private TextView mRssiField; private String mDeviceName; private String mDeviceAddress; private ExpandableListView mGattServicesList; private BluetoothLeService mBluetoothLeService; private ArrayList<ArrayList<BluetoothGattCharacteristic>> mGattCharacteristics = new ArrayList<ArrayList<BluetoothGattCharacteristic>>(); private boolean mConnected = false; private BluetoothGattCharacteristic mNotifyCharacteristic; private BluetoothGattCharacteristic mWriteCharacteristic; private final String LIST_NAME = "NAME"; private final String LIST_UUID = "UUID"; // Code to manage Service lifecycle. private final ServiceConnection mServiceConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName componentName, IBinder service) { mBluetoothLeService = ((BluetoothLeService.LocalBinder) service).getService(); if (!mBluetoothLeService.initialize()) { Log.e("Unable to initialize Bluetooth"); finish(); } // Automatically connects to the device upon successful start-up initialization. mBluetoothLeService.connect(mDeviceAddress); mBluetoothLeService.setBLEServiceCb(mDCServiceCb); } @Override public void onServiceDisconnected(ComponentName componentName) { mBluetoothLeService = null; } }; // If a given GATT characteristic is selected, check for supported features. This sample // demonstrates 'Read' and 'Notify' features. See // http://d.android.com/reference/android/bluetooth/BluetoothGatt.html for the complete // list of supported characteristic features. private final ExpandableListView.OnChildClickListener servicesListClickListner = new ExpandableListView.OnChildClickListener() { @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2) @Override public boolean onChildClick(ExpandableListView parent, View v, int groupPosition, int childPosition, long id) { if (mGattCharacteristics != null) { final BluetoothGattCharacteristic characteristic = mGattCharacteristics.get(groupPosition) .get(childPosition); final int charaProp = characteristic.getProperties(); if ((charaProp & BluetoothGattCharacteristic.PROPERTY_READ) > 0) { // If there is an active notification on a characteristic, clear // it first so it doesn't update the data field on the user interface. Log.d("BluetoothGattCharacteristic has PROPERTY_READ, so send read request"); if (mNotifyCharacteristic != null) { mBluetoothLeService.setCharacteristicNotification(mNotifyCharacteristic, false); mNotifyCharacteristic = null; } mBluetoothLeService.readCharacteristic(characteristic); } if ((charaProp & BluetoothGattCharacteristic.PROPERTY_NOTIFY) > 0) { Log.d("BluetoothGattCharacteristic has PROPERTY_NOTIFY, so send notify request"); mNotifyCharacteristic = characteristic; mBluetoothLeService.setCharacteristicNotification(characteristic, true); } if (((charaProp & BluetoothGattCharacteristic.PROPERTY_WRITE) | (charaProp & BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE)) > 0) { Log.d("BluetoothGattCharacteristic has PROPERY_WRITE | PROPERTY_WRITE_NO_RESPONSE"); mWriteCharacteristic = characteristic; // popup an dialog to write something. showCharactWriteDialog(); } return true; } return false; } }; private void showCharactWriteDialog() { DialogFragment newFrame = new BleCharacterDialogFragment(); newFrame.show(getSupportFragmentManager(), "blewrite"); } private void writeCharacteristic(BluetoothGattCharacteristic characteristic) { if (mBluetoothLeService != null) { mBluetoothLeService.writeCharacteristic(characteristic); } } private void clearUI() { mGattServicesList.setAdapter((SimpleExpandableListAdapter) null); mDataField.setText(R.string.no_data); } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.central_gatt_services); final Intent intent = getIntent(); mDeviceName = intent.getStringExtra(EXTRAS_DEVICE_NAME); mDeviceAddress = intent.getStringExtra(EXTRAS_DEVICE_ADDRESS); // Sets up UI references. if (mDeviceAddress != null) { ((TextView) findViewById(R.id.device_address)).setText(mDeviceAddress); } mGattServicesList = (ExpandableListView) findViewById(R.id.gatt_services_list); mGattServicesList.setOnChildClickListener(servicesListClickListner); mConnectionState = (TextView) findViewById(R.id.connection_state); mDataField = (TextView) findViewById(R.id.data_value); mRssiField = (TextView) findViewById(R.id.signal_rssi); Intent gattServiceIntent = new Intent(this, BluetoothLeService.class); bindService(gattServiceIntent, mServiceConnection, BIND_AUTO_CREATE); } @Override protected void onResume() { super.onResume(); //registerReceiver(mGattUpdateReceiver, makeGattUpdateIntentFilter()); if (mBluetoothLeService != null) { final boolean result = mBluetoothLeService.connect(mDeviceAddress); Log.d("Connect request result=" + result); } } @Override protected void onPause() { super.onPause(); //unregisterReceiver(mGattUpdateReceiver); } @Override protected void onDestroy() { super.onDestroy(); unbindService(mServiceConnection); mBluetoothLeService = null; } @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.gatt_services, menu); if (mConnected) { menu.findItem(R.id.menu_connect).setVisible(false); menu.findItem(R.id.menu_disconnect).setVisible(true); } else { menu.findItem(R.id.menu_connect).setVisible(true); menu.findItem(R.id.menu_disconnect).setVisible(false); } return true; } @TargetApi(Build.VERSION_CODES.ECLAIR) @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.menu_connect: mBluetoothLeService.connect(mDeviceAddress); return true; case R.id.menu_disconnect: mBluetoothLeService.disconnect(); return true; case android.R.id.home: onBackPressed(); return true; } return super.onOptionsItemSelected(item); } private void updateConnectionState(final int resourceId) { runOnUiThread(new Runnable() { @Override public void run() { mConnectionState.setText(resourceId); } }); } private void displayData(String data) { if (data != null) { mDataField.setText(data); } } private void displayRssi(String rssi) { if (rssi != null) { //Log.d("-- dispaly Rssi: " + rssi); mRssiField.setText(rssi); } } // Demonstrates how to iterate through the supported GATT Services/Characteristics. // In this sample, we populate the data structure that is bound to the ExpandableListView // on the UI. @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2) private void displayGattServices(List<BluetoothGattService> gattServices) { Log.d("displayGATTServices"); if (gattServices == null) return; String uuid = null; String unknownServiceString = getResources().getString(R.string.unknown_service); String unknownCharaString = getResources().getString(R.string.unknown_characteristic); ArrayList<HashMap<String, String>> gattServiceData = new ArrayList<HashMap<String, String>>(); ArrayList<ArrayList<HashMap<String, String>>> gattCharacteristicData = new ArrayList<ArrayList<HashMap<String, String>>>(); mGattCharacteristics = new ArrayList<ArrayList<BluetoothGattCharacteristic>>(); // Loops through available GATT Services. for (BluetoothGattService gattService : gattServices) { HashMap<String, String> currentServiceData = new HashMap<String, String>(); uuid = gattService.getUuid().toString(); currentServiceData.put(LIST_NAME, SampleGattAttributes.lookup(uuid, unknownServiceString)); currentServiceData.put(LIST_UUID, uuid); gattServiceData.add(currentServiceData); ArrayList<HashMap<String, String>> gattCharacteristicGroupData = new ArrayList<HashMap<String, String>>(); List<BluetoothGattCharacteristic> gattCharacteristics = gattService.getCharacteristics(); ArrayList<BluetoothGattCharacteristic> charas = new ArrayList<BluetoothGattCharacteristic>(); // Loops through available Characteristics. for (BluetoothGattCharacteristic gattCharacteristic : gattCharacteristics) { charas.add(gattCharacteristic); HashMap<String, String> currentCharaData = new HashMap<String, String>(); uuid = gattCharacteristic.getUuid().toString(); currentCharaData.put(LIST_NAME, SampleGattAttributes.lookup(uuid, unknownCharaString)); currentCharaData.put(LIST_UUID, uuid); gattCharacteristicGroupData.add(currentCharaData); } mGattCharacteristics.add(charas); gattCharacteristicData.add(gattCharacteristicGroupData); } SimpleExpandableListAdapter gattServiceAdapter = new SimpleExpandableListAdapter(this, gattServiceData, android.R.layout.simple_expandable_list_item_2, new String[] { LIST_NAME, LIST_UUID }, new int[] { android.R.id.text1, android.R.id.text2 }, gattCharacteristicData, android.R.layout.simple_expandable_list_item_2, new String[] { LIST_NAME, LIST_UUID }, new int[] { android.R.id.text1, android.R.id.text2 }); mGattServicesList.setAdapter(gattServiceAdapter); } private DCServiceCb mDCServiceCb = new DCServiceCb(); public class DCServiceCb implements BluetoothLeService.BLEServiceCallback { @Override public void displayRssi(final int rssi) { runOnUiThread(new Runnable() { @Override public void run() { DeviceControlActivity.this.displayRssi(String.valueOf(rssi)); } }); } @Override public void displayData(final String data) { runOnUiThread(new Runnable() { @Override public void run() { DeviceControlActivity.this.displayData(data); } }); } @Override public void notifyConnectedGATT() { runOnUiThread(new Runnable() { @Override public void run() { mConnected = true; updateConnectionState(R.string.connected); invalidateOptionsMenu(); } }); } @Override public void notifyDisconnectedGATT() { runOnUiThread(new Runnable() { @Override public void run() { mConnected = false; updateConnectionState(R.string.disconnected); invalidateOptionsMenu(); clearUI(); } }); } @Override public void displayGATTServices() { Log.d("displayGATTServices."); runOnUiThread(new Runnable() { @Override public void run() { if (mBluetoothLeService != null) { DeviceControlActivity.this .displayGattServices(mBluetoothLeService.getSupportedGattServices()); } } }); } } @SuppressLint("ValidFragment") public class BleCharacterDialogFragment extends DialogFragment { @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View v = inflater.inflate(R.layout.write_charact_dialog, container, false); final EditText ed = (EditText) v.findViewById(R.id.charact_value); Button ok = (Button) v.findViewById(R.id.dialog_confirm); Button cancel = (Button) v.findViewById(R.id.dialog_cancel); ok.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { // write characterist here. String str = ed.getText().toString(); //mWriteCharactristc. byte[] strBytes = str.getBytes(); byte[] bytes = DeviceControlActivity.this.mWriteCharacteristic.getValue(); //mWriteCharacteristic. if (strBytes == null) { Log.w("Cannot get Value from EditText Widget"); dismiss(); return; } if (bytes == null) { // maybe just write a byte into GATT Log.w("Cannot get Values from mWriteCharacteristic."); dismiss(); return; } else if (bytes.length <= strBytes.length) { for (int i = 0; i < bytes.length; i++) { bytes[i] = strBytes[i]; } } else { for (int i = 0; i < strBytes.length; i++) { bytes[i] = strBytes[i]; } } DeviceControlActivity.this.mWriteCharacteristic.setValue(bytes); DeviceControlActivity.this.writeCharacteristic(DeviceControlActivity.this.mWriteCharacteristic); dismiss(); return; } }); cancel.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { dismiss(); } }); return v; } } }