一、例程簡介
1、應用界面圖(主界面、設置界面)
2、實現功能:
(1)打開應用,顯示主界面,檢測藍牙功能是否打開,否則詢問打開;
(2)打開藍牙功能後,點擊“連接設備:”下的按鈕選擇已匹配的藍牙設備進行連接;
(3)若藍牙設備未匹配,可點擊旁邊的 […] 按鈕打開系統藍牙設置界面,進行藍牙匹配;
(4)點擊中央的上、下、左、右和中間按鈕,可發送不同的藍牙字符串消息;
(5)藍牙消息內容可通過點擊 [設置] 按鈕在設置界面中設置,設置的數據重啓應用後依然有效;
(6)其中,中間按鈕具有長按按下、長按釋放和點擊三種不同效果;
(7)本應用還附帶來電監聽功能,有來電時,會自動發送藍牙消息;
(8)點擊 [退出] 按鈕,關閉藍牙連接,並且關閉安卓設備藍牙功能。
二、工程目錄
三、主界面代碼
MainActivity.java
package com.example.z.buletoothdemo;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothSocket;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Handler;
import android.os.Message;
import android.provider.Settings;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.telephony.PhoneStateListener;
import android.telephony.TelephonyManager;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Set;
import java.util.UUID;
public class MainActivity extends AppCompatActivity {
public final static String TAG = "MainActivity";
private Button bdevice;
private EditText emessage;
private Button btmid;
private BluetoothAdapter ba;
private ArrayList<String> lname = new ArrayList<>();
private ArrayList<String> laddress = new ArrayList<>();
private String sdname;
private String sdaddress;
private BluetoothSocket bsocket = null;
private OutputStream ostream = null;
private InputStream istream = null;
private static final UUID SerialPort_UUID = UUID.fromString(
"00001101-0000-1000-8000-00805F9B34FB");
private Handler hd;
private ReceiveThread rt;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
bdevice = (Button) findViewById(R.id.btn_dev_name);
emessage = (EditText) findViewById(R.id.et_message);
btmid = (Button)findViewById(R.id.btn_mid);
btmid.setOnTouchListener(new MidOnTouchListener());
ba = BluetoothAdapter.getDefaultAdapter();
hd = new Handler() {
public void handleMessage(Message msg)
{
super.handleMessage(msg);
if(msg.what == 111) {
SharedPreferences preferences = getSharedPreferences(
"messages", MODE_PRIVATE);
String ms = preferences.getString("msg_mlg", "5");
SendStr(ms);
}else if(msg.what == 112) {
emessage.setText(Arrays.toString((byte[])msg.obj));
emessage.setEnabled(true);
}
}
};
// When "Device does not support Bluetooth"
if (ba == null) {
Toast.makeText(getApplicationContext(),
getResources().getString(R.string.dev_nsup_bt),
Toast.LENGTH_SHORT).show();
}
// When Bluetooth hasn't opened, enable it
if (!ba.isEnabled()) {
Intent turnOn = new Intent(
BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(turnOn, 0);
}
}
// Function to send Bluetooth message
public void SendStr(String str) {
byte[] bf = new byte[33];
bf = str.getBytes();
if((!str.equals("")) && (bsocket!=null)) {
try {
ostream = bsocket.getOutputStream();
ostream.write(bf);
ostream.write('\0'); // Send an ending sign
} catch (IOException e) {
e.printStackTrace();
}
try {
if (ostream != null) {
ostream.flush();
}
} catch (IOException e) {
e.printStackTrace();
}
emessage.setText("");
}
}
// Thread to receive Bluetooth message
public class ReceiveThread extends Thread {
BluetoothSocket tsocket;
public ReceiveThread(BluetoothSocket socket) {
this.tsocket = socket;
InputStream tistream = null;
try {
tistream = socket.getInputStream();
} catch (IOException e) {
e.printStackTrace();
}
istream = tistream;
}
@Override
public void run() {
byte[] buffer = new byte[32];
String str;
// Keep on receiving
while (true) {
try {
istream.read(buffer);
str = new String(buffer,"UTF-8");
buffer = new byte[32];
Message msg = hd.obtainMessage(112,str);
hd.sendMessage(msg);
} catch (IOException e) {
try {
if (istream != null) {
istream.close();
}
break;
} catch (IOException e1) {
e1.printStackTrace();
}
}
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
// Listener to catch the RINGING STATE
class MyPhoneStateListener extends PhoneStateListener {
@Override
public void onCallStateChanged(int state, String incomingNumber) {
Log.i(TAG, "in coming number:" + incomingNumber);
if(state == TelephonyManager.CALL_STATE_RINGING) {
SharedPreferences preferences = getSharedPreferences(
"messages", MODE_PRIVATE);
String ms = preferences.getString("msg_ring", "0");
SendStr(ms);
}
super.onCallStateChanged(state, incomingNumber);
}
}
// Function of the ChooseDev Button
public void ChooseDevOnClick(View Source) {
Set<BluetoothDevice> sdevices;
// Clear data of scanning
lname.clear();
laddress.clear();
// Get data of boned devices
sdevices = ba.getBondedDevices();
if (sdevices.size() > 0) {
for (BluetoothDevice btd : sdevices) {
lname.add(btd.getName());
laddress.add(btd.getAddress());
System.out.println(btd.getName() + btd.getAddress());
}
}
// Prepare data for the list of Bluetooth devices
int size = lname.size();
final String[] snames = new String[size];
final String[] saddresses = new String[size];
for (int i = 0; i < size; i++) {
snames[i] = lname.get(i);
saddresses[i] = laddress.get(i);
}
// Buld AlertDialog of the list of Bluetooth devices
AlertDialog.Builder ab = new AlertDialog.Builder(MainActivity.this);
ab.setTitle(getResources().getString(R.string.choose_bt_dev));
ab.setItems(snames, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
// The name and address of the chosen device
sdname = snames[which];
sdaddress = saddresses[which];
// Connect to chosen device
BluetoothDevice device = ba.getRemoteDevice(sdaddress);
try {
bsocket = device.createRfcommSocketToServiceRecord(
SerialPort_UUID);
ba.cancelDiscovery();
bsocket.connect();
// Set a phone state listener
TelephonyManager tm = (TelephonyManager)getSystemService(
Context.TELEPHONY_SERVICE);
tm.listen(new MyPhoneStateListener(),
PhoneStateListener.LISTEN_CALL_STATE);
// Start a thread to receive bluetooth message
rt = new ReceiveThread(bsocket);
rt.start();
} catch (IOException e) {
// Set fail message
sdname = getResources().getString(R.string.con_fail);
try {
bsocket.close();
bsocket = null;
} catch (IOException e2) {
e2.printStackTrace();
}
e.printStackTrace();
}
// Show result of connection
bdevice.setText(sdname);
}
});
// Pop the list AlertDialog
ab.create().show();
}
// Function of the Exit Button
public void ExitOnClick(View Source) {
if(bsocket != null)
try {
bsocket.close();
}catch (IOException e) {
e.printStackTrace();
}
// Close Bluetooth
ba.disable();
MainActivity.this.finish();
}
// OnTouchListener for Mid Button
class MidOnTouchListener implements View.OnTouchListener {
private int long_pressed = 0;
@Override
public boolean onTouch(View v, MotionEvent event) {
if(event.getAction() == MotionEvent.ACTION_DOWN){
// Thread to test long pressed
Thread t = new Thread() {
@Override
public void run() {
super.run();
try {
Thread.sleep(250);
}catch (InterruptedException e)
{
e.printStackTrace();
}
// test flag
if(long_pressed == 0) {
long_pressed = 1; // Set flag
hd.sendEmptyMessage(111); // Do long-pressed-action
}else {
long_pressed = 0;
}
}
};
t.start();
}
if(event.getAction() == MotionEvent.ACTION_UP){
if(long_pressed == 0) { // Flag not set, do click-action
long_pressed = -1;
SharedPreferences preferences = getSharedPreferences(
"messages", MODE_PRIVATE);
String ms = preferences.getString("msg_msh", "0");
SendStr(ms);
}else if(long_pressed == 1) { // Long pressed end
SharedPreferences preferences = getSharedPreferences(
"messages", MODE_PRIVATE);
String ms = preferences.getString("msg_mup", "6");
SendStr(ms);
long_pressed = 0;
}
}
return false;
}
}
// Function of Up Button
public void UpOnClick(View Source) {
SharedPreferences preferences = getSharedPreferences(
"messages", MODE_PRIVATE);
String ms = preferences.getString("msg_up", "1");
SendStr(ms);
}
// Function of Down Button
public void DownOnClick(View Source) {
SharedPreferences preferences = getSharedPreferences(
"messages", MODE_PRIVATE);
String ms = preferences.getString("msg_down", "3");
SendStr(ms);
}
// Function of Left Button
public void LeftOnClick(View Source) {
SharedPreferences preferences = getSharedPreferences(
"messages", MODE_PRIVATE);
String ms = preferences.getString("msg_left", "4");
SendStr(ms);
}
// Function of Right Button
public void RightOnClick(View Source) {
SharedPreferences preferences = getSharedPreferences(
"messages", MODE_PRIVATE);
String ms = preferences.getString("msg_right", "2");
SendStr(ms);
}
// Function of Send Button
public void BtnSendOnClick(View Source) {
String str = emessage.getText().toString();
SendStr(str);
}
// When "…" Button clicked, open system activity to Bluetooth settings
public void SearchOnClick(View Source) {
startActivity(new Intent(Settings.ACTION_BLUETOOTH_SETTINGS));
}
// When Settings Button clicked, open SettingsActivity
public void SettingsOnClick(View Source) {
Intent intent = new Intent(this, SettingsActivity.class);
startActivity(intent);
}
}
SettingsActivity.java
package com.example.z.buletoothdemo;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.EditText;
/**
* Created by Z on 2016/5/26.
*/
public class SettingsActivity extends AppCompatActivity {
private EditText etup;
private EditText etdown;
private EditText etleft;
private EditText etright;
private EditText etmsh;
private EditText etmlg;
private EditText etmup;
private EditText etring;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_settings);
etup = (EditText)findViewById(R.id.et_up);
etdown = (EditText)findViewById(R.id.et_down);
etleft = (EditText)findViewById(R.id.et_left);
etright = (EditText)findViewById(R.id.et_right);
etmsh = (EditText)findViewById(R.id.et_msh);
etmlg = (EditText)findViewById(R.id.et_mlg);
etmup = (EditText)findViewById(R.id.et_mup);
etring = (EditText)findViewById(R.id.et_ring);
SharedPreferences preferences = getSharedPreferences("messages", MODE_PRIVATE);
etup.setText(preferences.getString("msg_up","1"));
etdown.setText(preferences.getString("msg_down","3"));
etleft.setText(preferences.getString("msg_left","4"));
etright.setText(preferences.getString("msg_right","2"));
etmsh.setText(preferences.getString("msg_msh","0"));
etmlg.setText(preferences.getString("msg_mlg","5"));
etmup.setText(preferences.getString("msg_mup","6"));
etring.setText(preferences.getString("msg_ring","0"));
}
public void SaveOnClick(View Source) {
SharedPreferences preferences = getSharedPreferences("messages", MODE_PRIVATE);
SharedPreferences.Editor editor = preferences.edit();
editor.putString("msg_up",etup.getText().toString());
editor.putString("msg_down",etdown.getText().toString());
editor.putString("msg_left",etleft.getText().toString());
editor.putString("msg_right",etright.getText().toString());
editor.putString("msg_msh",etmsh.getText().toString());
editor.putString("msg_mlg",etmlg.getText().toString());
editor.putString("msg_mup",etmup.getText().toString());
editor.putString("msg_ring",etring.getText().toString());
editor.apply();
// Close SettingsActivity
finish();
}
}
五、主界面佈局
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.example.z.buletoothdemo.MainActivity">
<TextView
android:id="@+id/tv_con_dev"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/con_dev"
android:textAppearance="?android:attr/textAppearanceMedium" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<Button
android:id="@+id/btn_dev_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="9"
android:background="@drawable/btn_line"
android:gravity="start|center_vertical"
android:onClick="ChooseDevOnClick"
android:text="@string/dev_name" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:background="@drawable/bg_button"
android:onClick="SearchOnClick"
android:text="@string/dots"
android:textColor="@color/white" />
</LinearLayout>
<RelativeLayout
android:layout_width="300dp"
android:layout_height="300dp"
android:layout_gravity="center_horizontal"
android:layout_marginBottom="30dp"
android:layout_marginTop="50dp">
<ImageView
android:layout_width="300dp"
android:layout_height="300dp"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:contentDescription="@string/app_name"
android:src="@drawable/shp_around" />
<Button
android:id="@+id/btn_mid"
android:layout_width="150dp"
android:layout_height="150dp"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:background="@drawable/btn_mid"/>
<Button
android:id="@+id/btn_up"
android:layout_width="50dp"
android:layout_height="wrap_content"
android:layout_above="@id/btn_mid"
android:layout_centerHorizontal="true"
android:background="@drawable/btn_up"
android:onClick="UpOnClick" />
<Button
android:id="@+id/btn_down"
android:layout_width="50dp"
android:layout_height="wrap_content"
android:layout_below="@id/btn_mid"
android:layout_centerHorizontal="true"
android:background="@drawable/btn_down"
android:onClick="DownOnClick" />
<Button
android:id="@+id/btn_left"
android:layout_width="50dp"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_toLeftOf="@id/btn_mid"
android:layout_toStartOf="@id/btn_mid"
android:background="@drawable/btn_left"
android:onClick="LeftOnClick" />
<Button
android:id="@+id/btn_right"
android:layout_width="50dp"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_toEndOf="@id/btn_mid"
android:layout_toRightOf="@id/btn_mid"
android:background="@drawable/btn_right"
android:onClick="RightOnClick" />
</RelativeLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<EditText
android:id="@+id/et_message"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="9"
android:hint="@string/h_message"
android:maxLength="32"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:background="@drawable/bg_button"
android:onClick="BtnSendOnClick"
android:text="@string/send"
android:textColor="@color/white" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<Button
android:id="@+id/btn_settings"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/settings"
android:onClick="SettingsOnClick"/>
<Button
android:id="@+id/btn_exit"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:onClick="ExitOnClick"
android:text="@string/exit" />
</LinearLayout>
</LinearLayout>
六、設置界面佈局
activity_settings.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:layout_weight="7"
android:background="@color/colorPrimaryLight"
android:text="@string/up"/>
<EditText
android:id="@+id/et_up"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="3"
android:hint="@string/h_up"/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:layout_weight="7"
android:background="@color/colorPrimaryLight"
android:text="@string/down"/>
<EditText
android:id="@+id/et_down"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="3"
android:hint="@string/h_down"/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:layout_weight="7"
android:background="@color/colorPrimaryLight"
android:text="@string/left"/>
<EditText
android:id="@+id/et_left"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="3"
android:hint="@string/h_left"/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:layout_weight="7"
android:background="@color/colorPrimaryLight"
android:text="@string/right"/>
<EditText
android:id="@+id/et_right"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="3"
android:hint="@string/h_right"/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:layout_weight="7"
android:background="@color/colorPrimaryLight"
android:text="@string/mid_sh"/>
<EditText
android:id="@+id/et_msh"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="3"
android:hint="@string/h_mid_sh"/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:layout_weight="7"
android:background="@color/colorPrimaryLight"
android:text="@string/mid_lg"/>
<EditText
android:id="@+id/et_mlg"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="3"
android:hint="@string/h_mid_lg"/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:layout_weight="7"
android:background="@color/colorPrimaryLight"
android:text="@string/mid_up"/>
<EditText
android:id="@+id/et_mup"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="3"
android:hint="@string/h_mid_up"/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:layout_weight="7"
android:background="@color/colorPrimaryLight"
android:text="@string/call_ring"/>
<EditText
android:id="@+id/et_ring"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="3"
android:hint="@string/h_ring"/>
</LinearLayout>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/bg_button"
android:text="@string/save"
android:textColor="@color/white"
android:layout_alignParentBottom="true"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true"
android:layout_margin="25sp"
android:onClick="SaveOnClick"/>
</RelativeLayout>
</LinearLayout>
七、strings.xml
<resources>
<string name="app_name">BluetoothUnv</string>
<string name="con_dev">CONNECTED DEVICE:</string>
<string name="dev_name">(PLEASE CHOOSE)</string>
<string name="settings">SETTINGS</string>
<string name="exit">EXIT</string>
<string name="send">SEND</string>
<string name="save">SAVE</string>
<string name="dots">...</string>
<string name="h_message">32 CHARACTERS LIMITED </string>
<string name="dev_nsup_bt">Device does not support Bluetooth</string>
<string name="con_fail">(CONNECTION FAILED)</string>
<string name="choose_bt_dev">CHOOSE BLUETOOTH DEVICE</string>
<string name="up" >UP</string>
<string name="down" >DOWN</string>
<string name="left" >LEFT</string>
<string name="right" >RIGHT</string>
<string name="mid_sh">MID-CLICK</string>
<string name="mid_lg">MID-PRESS</string>
<string name="mid_up">MID-RELEASE</string>
<string name="call_ring">CALL_RINGING</string>
<string name="h_up">default "1"</string>
<string name="h_down">default "3"</string>
<string name="h_left">default "4"</string>
<string name="h_right">default "2"</string>
<string name="h_mid_sh">default "0"</string>
<string name="h_mid_lg">default "5"</string>
<string name="h_mid_up">default "6"</string>
<string name="h_ring">default "0"</string>
</resources>
八、AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.z.buletoothdemo">
<uses-permission android:name="android.permission.BLUETOOTH"/>
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
<!-- 允許讀取手機狀態的權限 -->
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<application
android:allowBackup="true"
android:icon="@drawable/icon_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity
android:name=".MainActivity"
android:label="@string/app_name"
android:theme="@style/AppTheme.NoActionBar">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".SettingsActivity"
android:label="@string/settings">
</activity>
</application>
</manifest>
九、其他res
包括drawable文件夾中的一些.xml和.png,values文件夾下的colors.xml、dimens.xml和styles.xml,等。
十、代碼下載
本項目代碼已上傳至“CODE”,地址:
https://code.csdn.net/sinat_30685475/bluetoothunv/tree/master
已上傳至“下載”,地址:
http://download.csdn.net/detail/sinat_30685475/9595483